- Dynamic linking: lazy PLT resolution, PLT caching
- LZ4 initrd decompression
-All tests print `[init] ... OK` on success. Any failure calls `sys_exit(1)`.
+All tests print `[test] ... OK` on success. Any failure calls `sys_exit(1)`.
### Testing
USER_LD ?= i686-elf-ld
USER_AR ?= i686-elf-ar
- FULLTEST_ELF := user/fulltest.elf
- ECHO_ELF := user/echo.elf
- SH_ELF := user/sh.elf
- CAT_ELF := user/cat.elf
- LS_ELF := user/ls.elf
- MKDIR_ELF := user/mkdir.elf
- RM_ELF := user/rm.elf
- CP_ELF := user/cp.elf
- MV_ELF := user/mv.elf
- TOUCH_ELF := user/touch.elf
- LN_ELF := user/ln.elf
- HEAD_ELF := user/head.elf
- TAIL_ELF := user/tail.elf
- WC_ELF := user/wc.elf
- SORT_ELF := user/sort.elf
- UNIQ_ELF := user/uniq.elf
- CUT_ELF := user/cut.elf
- CHMOD_ELF := user/chmod.elf
- CHOWN_ELF := user/chown.elf
- CHGRP_ELF := user/chgrp.elf
- DATE_ELF := user/date.elf
- HOSTNAME_ELF := user/hostname.elf
- UPTIME_ELF := user/uptime.elf
- MOUNT_ELF := user/mount.elf
- UMOUNT_ELF := user/umount.elf
- ENV_ELF := user/env.elf
- KILL_ELF := user/kill.elf
- SLEEP_ELF := user/sleep.elf
- CLEAR_ELF := user/clear.elf
- PS_ELF := user/ps.elf
- DF_ELF := user/df.elf
- FREE_ELF := user/free.elf
- TEE_ELF := user/tee.elf
- BASENAME_ELF := user/basename.elf
- DIRNAME_ELF := user/dirname.elf
- RMDIR_ELF := user/rmdir.elf
- GREP_ELF := user/grep.elf
- ID_ELF := user/id.elf
- UNAME_ELF := user/uname.elf
- DMESG_ELF := user/dmesg.elf
- PRINTENV_ELF := user/printenv.elf
- TR_ELF := user/tr.elf
- DD_ELF := user/dd.elf
- PWD_ELF := user/pwd.elf
- STAT_ELF := user/stat.elf
- SED_ELF := user/sed.elf
- AWK_ELF := user/awk.elf
- WHO_ELF := user/who.elf
- TOP_ELF := user/top.elf
- DU_ELF := user/du.elf
- FIND_ELF := user/find.elf
- WHICH_ELF := user/which.elf
- INIT_ELF := user/init.elf
- LDSO_ELF := user/ld.so
- ULIBC_SO := user/ulibc/libc.so
- PIE_SO := user/libpietest.so
- PIE_ELF := user/pie_test.elf
+ # User build output directory
+ USER_BUILD := build/user
+
+ # List of dynamically-linked user commands (built via user/cmds/<name>/Makefile)
+ USER_CMD_NAMES := echo sh cat ls mkdir rm cp mv touch ln \
+ head tail wc sort uniq cut \
+ chmod chown chgrp \
+ date hostname uptime \
+ mount umount env kill sleep \
+ clear ps df free tee \
+ basename dirname rmdir \
+ grep id uname dmesg \
+ printenv tr dd pwd stat \
+ sed awk who top du find which \
+ init
+
+ # ELF paths for dynamically-linked commands
+ USER_CMD_ELFS := $(foreach cmd,$(USER_CMD_NAMES),$(USER_BUILD)/cmds/$(cmd)/$(cmd).elf)
+
+ # Special builds (not dynamically-linked via common.mk)
+ FULLTEST_ELF := $(USER_BUILD)/cmds/fulltest/fulltest.elf
+ LDSO_ELF := $(USER_BUILD)/cmds/ldso/ld.so
+ PIE_SO := $(USER_BUILD)/cmds/pie_test/libpietest.so
+ PIE_ELF := $(USER_BUILD)/cmds/pie_test/pie_test.elf
+
+ # ulibc
+ ULIBC_DIR := user/ulibc
+ ULIBC_SO := $(USER_BUILD)/ulibc/libc.so
+ ULIBC_LIB := $(USER_BUILD)/ulibc/libulibc.a
+
+ # doom
DOOM_ELF := user/doom/doom.elf
+
INITRD_IMG := initrd.img
- MKINITRD := tools/mkinitrd
+ MKINITRD := tools/mkinitrd
endif
# --- ARM64 Configuration ---
$(MKINITRD): tools/mkinitrd.c include/xxhash32.h
@gcc -Iinclude tools/mkinitrd.c -o $(MKINITRD)
-ULIBC_DIR := user/ulibc
-ULIBC_LIB := $(ULIBC_DIR)/libulibc.a
-
-$(ULIBC_LIB):
- @$(MAKE) -C $(ULIBC_DIR) CC="$(USER_CC)" AS="$(USER_CC:gcc=as)" AR="$(USER_AR)" LD="$(USER_LD)" --no-print-directory
-
-$(ULIBC_SO):
- @$(MAKE) -C $(ULIBC_DIR) CC="$(USER_CC)" AS="$(USER_CC:gcc=as)" AR="$(USER_AR)" LD="$(USER_LD)" libc.so --no-print-directory
-
-$(FULLTEST_ELF): user/fulltest.c user/linker.ld
- @$(USER_CC) -m32 -I include -ffreestanding -fno-pie -no-pie -nostdlib -Wl,-T,user/linker.ld -o $(FULLTEST_ELF) user/fulltest.c user/errno.c
-
-# --- Dynamic linking helper: compile .c to PIC .o, link as PIE with crt0 + libc.so ---
-ULIBC_CRT0 := $(ULIBC_DIR)/src/crt0.o
-DYN_CC := $(USER_CC) -m32 -ffreestanding -nostdlib -O2 -Wall -Wextra -fPIC -fno-plt -I$(ULIBC_DIR)/include
-DYN_LD := $(USER_LD) -m elf_i386 --dynamic-linker=/lib/ld.so -T user/dyn_linker.ld -L$(ULIBC_DIR) -rpath /lib
-
-$(ECHO_ELF): user/echo.c user/dyn_linker.ld $(ULIBC_SO) $(ULIBC_LIB)
- @$(DYN_CC) -c user/echo.c -o user/echo.o
- @$(DYN_LD) -o $@ $(ULIBC_CRT0) user/echo.o -lc
-
-$(SH_ELF): user/sh.c user/dyn_linker.ld $(ULIBC_SO) $(ULIBC_LIB)
- @$(DYN_CC) -c user/sh.c -o user/sh.o
- @$(DYN_LD) -o $@ $(ULIBC_CRT0) user/sh.o -lc
-
-$(CAT_ELF): user/cat.c user/dyn_linker.ld $(ULIBC_SO) $(ULIBC_LIB)
- @$(DYN_CC) -c user/cat.c -o user/cat.o
- @$(DYN_LD) -o $@ $(ULIBC_CRT0) user/cat.o -lc
-
-$(LS_ELF): user/ls.c user/dyn_linker.ld $(ULIBC_SO) $(ULIBC_LIB)
- @$(DYN_CC) -c user/ls.c -o user/ls.o
- @$(DYN_LD) -o $@ $(ULIBC_CRT0) user/ls.o -lc
-
-$(MKDIR_ELF): user/mkdir.c user/dyn_linker.ld $(ULIBC_SO) $(ULIBC_LIB)
- @$(DYN_CC) -c user/mkdir.c -o user/mkdir.o
- @$(DYN_LD) -o $@ $(ULIBC_CRT0) user/mkdir.o -lc
-
-$(RM_ELF): user/rm.c user/dyn_linker.ld $(ULIBC_SO) $(ULIBC_LIB)
- @$(DYN_CC) -c user/rm.c -o user/rm.o
- @$(DYN_LD) -o $@ $(ULIBC_CRT0) user/rm.o -lc
-
-$(CP_ELF): user/cp.c user/dyn_linker.ld $(ULIBC_SO) $(ULIBC_LIB)
- @$(DYN_CC) -c user/cp.c -o user/cp.o
- @$(DYN_LD) -o $@ $(ULIBC_CRT0) user/cp.o -lc
-
-$(MV_ELF): user/mv.c user/dyn_linker.ld $(ULIBC_SO) $(ULIBC_LIB)
- @$(DYN_CC) -c user/mv.c -o user/mv.o
- @$(DYN_LD) -o $@ $(ULIBC_CRT0) user/mv.o -lc
-
-$(TOUCH_ELF): user/touch.c user/dyn_linker.ld $(ULIBC_SO) $(ULIBC_LIB)
- @$(DYN_CC) -c user/touch.c -o user/touch.o
- @$(DYN_LD) -o $@ $(ULIBC_CRT0) user/touch.o -lc
-
-$(LN_ELF): user/ln.c user/dyn_linker.ld $(ULIBC_SO) $(ULIBC_LIB)
- @$(DYN_CC) -c user/ln.c -o user/ln.o
- @$(DYN_LD) -o $@ $(ULIBC_CRT0) user/ln.o -lc
-
-$(HEAD_ELF): user/head.c user/dyn_linker.ld $(ULIBC_SO) $(ULIBC_LIB)
- @$(DYN_CC) -c user/head.c -o user/head.o
- @$(DYN_LD) -o $@ $(ULIBC_CRT0) user/head.o -lc
-
-$(TAIL_ELF): user/tail.c user/dyn_linker.ld $(ULIBC_SO) $(ULIBC_LIB)
- @$(DYN_CC) -c user/tail.c -o user/tail.o
- @$(DYN_LD) -o $@ $(ULIBC_CRT0) user/tail.o -lc
-
-$(WC_ELF): user/wc.c user/dyn_linker.ld $(ULIBC_SO) $(ULIBC_LIB)
- @$(DYN_CC) -c user/wc.c -o user/wc.o
- @$(DYN_LD) -o $@ $(ULIBC_CRT0) user/wc.o -lc
-
-$(SORT_ELF): user/sort.c user/dyn_linker.ld $(ULIBC_SO) $(ULIBC_LIB)
- @$(DYN_CC) -c user/sort.c -o user/sort.o
- @$(DYN_LD) -o $@ $(ULIBC_CRT0) user/sort.o -lc
-
-$(UNIQ_ELF): user/uniq.c user/dyn_linker.ld $(ULIBC_SO) $(ULIBC_LIB)
- @$(DYN_CC) -c user/uniq.c -o user/uniq.o
- @$(DYN_LD) -o $@ $(ULIBC_CRT0) user/uniq.o -lc
-
-$(CUT_ELF): user/cut.c user/dyn_linker.ld $(ULIBC_SO) $(ULIBC_LIB)
- @$(DYN_CC) -c user/cut.c -o user/cut.o
- @$(DYN_LD) -o $@ $(ULIBC_CRT0) user/cut.o -lc
-
-$(CHMOD_ELF): user/chmod.c user/dyn_linker.ld $(ULIBC_SO) $(ULIBC_LIB)
- @$(DYN_CC) -c user/chmod.c -o user/chmod.o
- @$(DYN_LD) -o $@ $(ULIBC_CRT0) user/chmod.o -lc
-
-$(CHOWN_ELF): user/chown.c user/dyn_linker.ld $(ULIBC_SO) $(ULIBC_LIB)
- @$(DYN_CC) -c user/chown.c -o user/chown.o
- @$(DYN_LD) -o $@ $(ULIBC_CRT0) user/chown.o -lc
-
-$(CHGRP_ELF): user/chgrp.c user/dyn_linker.ld $(ULIBC_SO) $(ULIBC_LIB)
- @$(DYN_CC) -c user/chgrp.c -o user/chgrp.o
- @$(DYN_LD) -o $@ $(ULIBC_CRT0) user/chgrp.o -lc
-
-$(DATE_ELF): user/date.c user/dyn_linker.ld $(ULIBC_SO) $(ULIBC_LIB)
- @$(DYN_CC) -c user/date.c -o user/date.o
- @$(DYN_LD) -o $@ $(ULIBC_CRT0) user/date.o -lc
-
-$(HOSTNAME_ELF): user/hostname.c user/dyn_linker.ld $(ULIBC_SO) $(ULIBC_LIB)
- @$(DYN_CC) -c user/hostname.c -o user/hostname.o
- @$(DYN_LD) -o $@ $(ULIBC_CRT0) user/hostname.o -lc
-
-$(UPTIME_ELF): user/uptime.c user/dyn_linker.ld $(ULIBC_SO) $(ULIBC_LIB)
- @$(DYN_CC) -c user/uptime.c -o user/uptime.o
- @$(DYN_LD) -o $@ $(ULIBC_CRT0) user/uptime.o -lc
-
-$(INIT_ELF): user/init.c user/dyn_linker.ld $(ULIBC_SO) $(ULIBC_LIB)
- @$(DYN_CC) -c user/init.c -o user/init.o
- @$(DYN_LD) -o $@ $(ULIBC_CRT0) user/init.o -lc
-
-$(MOUNT_ELF): user/mount.c user/dyn_linker.ld $(ULIBC_SO) $(ULIBC_LIB)
- @$(DYN_CC) -c user/mount.c -o user/mount.o
- @$(DYN_LD) -o $@ $(ULIBC_CRT0) user/mount.o -lc
-
-$(UMOUNT_ELF): user/umount.c user/dyn_linker.ld $(ULIBC_SO) $(ULIBC_LIB)
- @$(DYN_CC) -c user/umount.c -o user/umount.o
- @$(DYN_LD) -o $@ $(ULIBC_CRT0) user/umount.o -lc
-
-$(ENV_ELF): user/env.c user/dyn_linker.ld $(ULIBC_SO) $(ULIBC_LIB)
- @$(DYN_CC) -c user/env.c -o user/env.o
- @$(DYN_LD) -o $@ $(ULIBC_CRT0) user/env.o -lc
-
-$(KILL_ELF): user/kill.c user/dyn_linker.ld $(ULIBC_SO) $(ULIBC_LIB)
- @$(DYN_CC) -c user/kill.c -o user/kill.o
- @$(DYN_LD) -o $@ $(ULIBC_CRT0) user/kill.o -lc
-
-$(SLEEP_ELF): user/sleep.c user/dyn_linker.ld $(ULIBC_SO) $(ULIBC_LIB)
- @$(DYN_CC) -c user/sleep.c -o user/sleep.o
- @$(DYN_LD) -o $@ $(ULIBC_CRT0) user/sleep.o -lc
-
-$(CLEAR_ELF): user/clear.c user/dyn_linker.ld $(ULIBC_SO) $(ULIBC_LIB)
- @$(DYN_CC) -c user/clear.c -o user/clear.o
- @$(DYN_LD) -o $@ $(ULIBC_CRT0) user/clear.o -lc
-
-$(PS_ELF): user/ps.c user/dyn_linker.ld $(ULIBC_SO) $(ULIBC_LIB)
- @$(DYN_CC) -c user/ps.c -o user/ps.o
- @$(DYN_LD) -o $@ $(ULIBC_CRT0) user/ps.o -lc
-
-$(DF_ELF): user/df.c user/dyn_linker.ld $(ULIBC_SO) $(ULIBC_LIB)
- @$(DYN_CC) -c user/df.c -o user/df.o
- @$(DYN_LD) -o $@ $(ULIBC_CRT0) user/df.o -lc
-
-$(FREE_ELF): user/free.c user/dyn_linker.ld $(ULIBC_SO) $(ULIBC_LIB)
- @$(DYN_CC) -c user/free.c -o user/free.o
- @$(DYN_LD) -o $@ $(ULIBC_CRT0) user/free.o -lc
-
-$(TEE_ELF): user/tee.c user/dyn_linker.ld $(ULIBC_SO) $(ULIBC_LIB)
- @$(DYN_CC) -c user/tee.c -o user/tee.o
- @$(DYN_LD) -o $@ $(ULIBC_CRT0) user/tee.o -lc
-
-$(BASENAME_ELF): user/basename.c user/dyn_linker.ld $(ULIBC_SO) $(ULIBC_LIB)
- @$(DYN_CC) -c user/basename.c -o user/basename.o
- @$(DYN_LD) -o $@ $(ULIBC_CRT0) user/basename.o -lc
-
-$(DIRNAME_ELF): user/dirname.c user/dyn_linker.ld $(ULIBC_SO) $(ULIBC_LIB)
- @$(DYN_CC) -c user/dirname.c -o user/dirname.o
- @$(DYN_LD) -o $@ $(ULIBC_CRT0) user/dirname.o -lc
-
-$(RMDIR_ELF): user/rmdir.c user/dyn_linker.ld $(ULIBC_SO) $(ULIBC_LIB)
- @$(DYN_CC) -c user/rmdir.c -o user/rmdir.o
- @$(DYN_LD) -o $@ $(ULIBC_CRT0) user/rmdir.o -lc
-
-$(GREP_ELF): user/grep.c user/dyn_linker.ld $(ULIBC_SO) $(ULIBC_LIB)
- @$(DYN_CC) -c user/grep.c -o user/grep.o
- @$(DYN_LD) -o $@ $(ULIBC_CRT0) user/grep.o -lc
-
-$(ID_ELF): user/id.c user/dyn_linker.ld $(ULIBC_SO) $(ULIBC_LIB)
- @$(DYN_CC) -c user/id.c -o user/id.o
- @$(DYN_LD) -o $@ $(ULIBC_CRT0) user/id.o -lc
-
-$(UNAME_ELF): user/uname.c user/dyn_linker.ld $(ULIBC_SO) $(ULIBC_LIB)
- @$(DYN_CC) -c user/uname.c -o user/uname.o
- @$(DYN_LD) -o $@ $(ULIBC_CRT0) user/uname.o -lc
-
-$(DMESG_ELF): user/dmesg.c user/dyn_linker.ld $(ULIBC_SO) $(ULIBC_LIB)
- @$(DYN_CC) -c user/dmesg.c -o user/dmesg.o
- @$(DYN_LD) -o $@ $(ULIBC_CRT0) user/dmesg.o -lc
-
-$(PRINTENV_ELF): user/printenv.c user/dyn_linker.ld $(ULIBC_SO) $(ULIBC_LIB)
- @$(DYN_CC) -c user/printenv.c -o user/printenv.o
- @$(DYN_LD) -o $@ $(ULIBC_CRT0) user/printenv.o -lc
-
-$(TR_ELF): user/tr.c user/dyn_linker.ld $(ULIBC_SO) $(ULIBC_LIB)
- @$(DYN_CC) -c user/tr.c -o user/tr.o
- @$(DYN_LD) -o $@ $(ULIBC_CRT0) user/tr.o -lc
-
-$(DD_ELF): user/dd.c user/dyn_linker.ld $(ULIBC_SO) $(ULIBC_LIB)
- @$(DYN_CC) -c user/dd.c -o user/dd.o
- @$(DYN_LD) -o $@ $(ULIBC_CRT0) user/dd.o -lc
-
-$(PWD_ELF): user/pwd.c user/dyn_linker.ld $(ULIBC_SO) $(ULIBC_LIB)
- @$(DYN_CC) -c user/pwd.c -o user/pwd.o
- @$(DYN_LD) -o $@ $(ULIBC_CRT0) user/pwd.o -lc
-
-$(STAT_ELF): user/stat.c user/dyn_linker.ld $(ULIBC_SO) $(ULIBC_LIB)
- @$(DYN_CC) -c user/stat.c -o user/stat.o
- @$(DYN_LD) -o $@ $(ULIBC_CRT0) user/stat.o -lc
-
-$(SED_ELF): user/sed.c user/dyn_linker.ld $(ULIBC_SO) $(ULIBC_LIB)
- @$(DYN_CC) -c user/sed.c -o user/sed.o
- @$(DYN_LD) -o $@ $(ULIBC_CRT0) user/sed.o -lc
-
-$(AWK_ELF): user/awk.c user/dyn_linker.ld $(ULIBC_SO) $(ULIBC_LIB)
- @$(DYN_CC) -c user/awk.c -o user/awk.o
- @$(DYN_LD) -o $@ $(ULIBC_CRT0) user/awk.o -lc
-
-$(WHO_ELF): user/who.c user/dyn_linker.ld $(ULIBC_SO) $(ULIBC_LIB)
- @$(DYN_CC) -c user/who.c -o user/who.o
- @$(DYN_LD) -o $@ $(ULIBC_CRT0) user/who.o -lc
-
-$(TOP_ELF): user/top.c user/dyn_linker.ld $(ULIBC_SO) $(ULIBC_LIB)
- @$(DYN_CC) -c user/top.c -o user/top.o
- @$(DYN_LD) -o $@ $(ULIBC_CRT0) user/top.o -lc
-
-$(DU_ELF): user/du.c user/dyn_linker.ld $(ULIBC_SO) $(ULIBC_LIB)
- @$(DYN_CC) -c user/du.c -o user/du.o
- @$(DYN_LD) -o $@ $(ULIBC_CRT0) user/du.o -lc
-
-$(FIND_ELF): user/find.c user/dyn_linker.ld $(ULIBC_SO) $(ULIBC_LIB)
- @$(DYN_CC) -c user/find.c -o user/find.o
- @$(DYN_LD) -o $@ $(ULIBC_CRT0) user/find.o -lc
-
-$(WHICH_ELF): user/which.c user/dyn_linker.ld $(ULIBC_SO) $(ULIBC_LIB)
- @$(DYN_CC) -c user/which.c -o user/which.o
- @$(DYN_LD) -o $@ $(ULIBC_CRT0) user/which.o -lc
-
-$(LDSO_ELF): user/ldso.c user/ldso_linker.ld
- @$(USER_CC) -m32 -ffreestanding -fno-pie -no-pie -nostdlib -Wl,-T,user/ldso_linker.ld -o $(LDSO_ELF) user/ldso.c
-
-$(PIE_SO): user/pie_func.c
- @$(USER_CC) -m32 -fPIC -fno-plt -c user/pie_func.c -o user/pie_func.o
- @$(USER_LD) -m elf_i386 -shared -soname libpietest.so -o $(PIE_SO) user/pie_func.o
-
-$(PIE_ELF): user/pie_main.c user/pie_linker.ld $(PIE_SO)
- @$(USER_CC) -m32 -fPIC -c user/pie_main.c -o user/pie_main.o
- @$(USER_LD) -m elf_i386 -pie --dynamic-linker=/lib/ld.so -T user/pie_linker.ld -o $(PIE_ELF) user/pie_main.o $(PIE_SO) -rpath /lib
-
-# All dynamically-linked user commands
-USER_CMDS := $(ECHO_ELF) $(SH_ELF) $(CAT_ELF) $(LS_ELF) $(MKDIR_ELF) $(RM_ELF) \
- $(CP_ELF) $(MV_ELF) $(TOUCH_ELF) $(LN_ELF) \
- $(HEAD_ELF) $(TAIL_ELF) $(WC_ELF) $(SORT_ELF) $(UNIQ_ELF) $(CUT_ELF) \
- $(CHMOD_ELF) $(CHOWN_ELF) $(CHGRP_ELF) \
- $(DATE_ELF) $(HOSTNAME_ELF) $(UPTIME_ELF) \
- $(MOUNT_ELF) $(UMOUNT_ELF) $(ENV_ELF) $(KILL_ELF) $(SLEEP_ELF) \
- $(CLEAR_ELF) $(PS_ELF) $(DF_ELF) $(FREE_ELF) $(TEE_ELF) \
- $(BASENAME_ELF) $(DIRNAME_ELF) $(RMDIR_ELF) \
- $(GREP_ELF) $(ID_ELF) $(UNAME_ELF) $(DMESG_ELF) \
- $(PRINTENV_ELF) $(TR_ELF) $(DD_ELF) $(PWD_ELF) $(STAT_ELF) \
- $(SED_ELF) $(AWK_ELF) $(WHO_ELF) $(TOP_ELF) $(DU_ELF) \
- $(FIND_ELF) $(WHICH_ELF) \
- $(INIT_ELF)
-
+# --- ulibc build (output into build/user/ulibc/) ---
+$(ULIBC_LIB) $(ULIBC_SO): FORCE
+ @mkdir -p $(USER_BUILD)/ulibc
+ @$(MAKE) -C $(ULIBC_DIR) CC="$(USER_CC)" AS="$(USER_CC:gcc=as)" AR="$(USER_AR)" LD="$(USER_LD)" \
+ libulibc.a libc.so --no-print-directory
+ @cp -u $(ULIBC_DIR)/libulibc.a $(ULIBC_LIB)
+ @cp -u $(ULIBC_DIR)/libc.so $(ULIBC_SO)
+FORCE:
+
+# --- Special builds (fulltest, ldso, pie_test) ---
+$(FULLTEST_ELF): user/cmds/fulltest/fulltest.c user/cmds/fulltest/errno.c user/linker.ld
+ @$(MAKE) --no-print-directory -C user/cmds/fulltest TOPDIR=$(CURDIR) USER_CC="$(USER_CC)"
+
+$(LDSO_ELF): user/cmds/ldso/ldso.c user/ldso_linker.ld
+ @$(MAKE) --no-print-directory -C user/cmds/ldso TOPDIR=$(CURDIR) USER_CC="$(USER_CC)"
+
+$(PIE_SO) $(PIE_ELF): user/cmds/pie_test/pie_main.c user/cmds/pie_test/pie_func.c user/pie_linker.ld
+ @$(MAKE) --no-print-directory -C user/cmds/pie_test TOPDIR=$(CURDIR) USER_CC="$(USER_CC)" USER_LD="$(USER_LD)"
+
+# --- Dynamically-linked user commands (generic rule via sub-Makefiles) ---
+# Use absolute paths so they work from sub-Makefile directories
+ABS_ULIBC := $(CURDIR)/$(ULIBC_DIR)
+ABS_DYN_CC := $(USER_CC) -m32 -ffreestanding -nostdlib -O2 -Wall -Wextra -fPIC -fno-plt -I$(ABS_ULIBC)/include
+ABS_DYN_LD := $(USER_LD) -m elf_i386 --dynamic-linker=/lib/ld.so -T $(CURDIR)/user/dyn_linker.ld -L$(ABS_ULIBC) -rpath /lib --unresolved-symbols=ignore-in-shared-libs
+
+# Generate build rules for each dynamically-linked command
+define USER_CMD_RULE
+$(USER_BUILD)/cmds/$(1)/$(1).elf: user/cmds/$(1)/$(1).c user/dyn_linker.ld $(ULIBC_SO) $(ULIBC_LIB)
+ @$$(MAKE) --no-print-directory -C user/cmds/$(1) TOPDIR=$$(CURDIR) \
+ DYN_CC="$$(ABS_DYN_CC)" DYN_LD="$$(ABS_DYN_LD)" CRT0="$(ABS_ULIBC)/src/crt0.o"
+endef
+$(foreach cmd,$(USER_CMD_NAMES),$(eval $(call USER_CMD_RULE,$(cmd))))
+
+# Commands that go to /bin/ in rootfs (all except init)
+USER_BIN_NAMES := $(filter-out init,$(USER_CMD_NAMES))
+
+# Build INITRD_FILES list: <elf>:<rootfs-path>
FSTAB := rootfs/etc/fstab
INITRD_FILES := $(FULLTEST_ELF):sbin/fulltest \
- $(INIT_ELF):sbin/init \
- $(ECHO_ELF):bin/echo $(SH_ELF):bin/sh $(CAT_ELF):bin/cat $(LS_ELF):bin/ls \
- $(MKDIR_ELF):bin/mkdir $(RM_ELF):bin/rm $(CP_ELF):bin/cp $(MV_ELF):bin/mv \
- $(TOUCH_ELF):bin/touch $(LN_ELF):bin/ln \
- $(HEAD_ELF):bin/head $(TAIL_ELF):bin/tail $(WC_ELF):bin/wc \
- $(SORT_ELF):bin/sort $(UNIQ_ELF):bin/uniq $(CUT_ELF):bin/cut \
- $(CHMOD_ELF):bin/chmod $(CHOWN_ELF):bin/chown $(CHGRP_ELF):bin/chgrp \
- $(DATE_ELF):bin/date $(HOSTNAME_ELF):bin/hostname $(UPTIME_ELF):bin/uptime \
- $(MOUNT_ELF):bin/mount $(UMOUNT_ELF):bin/umount $(ENV_ELF):bin/env \
- $(KILL_ELF):bin/kill $(SLEEP_ELF):bin/sleep $(CLEAR_ELF):bin/clear \
- $(PS_ELF):bin/ps $(DF_ELF):bin/df $(FREE_ELF):bin/free \
- $(TEE_ELF):bin/tee $(BASENAME_ELF):bin/basename $(DIRNAME_ELF):bin/dirname \
- $(RMDIR_ELF):bin/rmdir \
- $(GREP_ELF):bin/grep $(ID_ELF):bin/id $(UNAME_ELF):bin/uname \
- $(DMESG_ELF):bin/dmesg $(PRINTENV_ELF):bin/printenv $(TR_ELF):bin/tr \
- $(SED_ELF):bin/sed $(AWK_ELF):bin/awk $(WHO_ELF):bin/who \
- $(TOP_ELF):bin/top $(DU_ELF):bin/du $(FIND_ELF):bin/find $(WHICH_ELF):bin/which \
- $(DD_ELF):bin/dd $(PWD_ELF):bin/pwd $(STAT_ELF):bin/stat \
+ $(USER_BUILD)/cmds/init/init.elf:sbin/init \
+ $(foreach cmd,$(USER_BIN_NAMES),$(USER_BUILD)/cmds/$(cmd)/$(cmd).elf:bin/$(cmd)) \
$(LDSO_ELF):lib/ld.so $(ULIBC_SO):lib/libc.so \
$(PIE_SO):lib/libpietest.so $(PIE_ELF):bin/pie_test \
$(FSTAB):etc/fstab
-INITRD_DEPS := $(MKINITRD) $(FULLTEST_ELF) $(USER_CMDS) $(LDSO_ELF) $(ULIBC_SO) $(PIE_SO) $(PIE_ELF) $(FSTAB)
+
+INITRD_DEPS := $(MKINITRD) $(FULLTEST_ELF) $(USER_CMD_ELFS) $(LDSO_ELF) $(ULIBC_SO) $(PIE_SO) $(PIE_ELF) $(FSTAB)
# Include doom.elf if it has been built
ifneq ($(wildcard $(DOOM_ELF)),)
-INITRD_FILES += $(DOOM_ELF):bin/doom
+INITRD_FILES += $(DOOM_ELF):usr/games/doom
INITRD_DEPS += $(DOOM_ELF)
endif
@$(AS) $(ASFLAGS) $< -o $@
clean:
- rm -rf build $(KERNEL_NAME)
+ rm -rf build $(KERNEL_NAME) $(INITRD_IMG) adros-*.iso
+ @$(MAKE) -C user/ulibc clean --no-print-directory 2>/dev/null || true
+ @if [ -f user/doom/Makefile ]; then $(MAKE) -C user/doom clean --no-print-directory 2>/dev/null || true; fi
.PHONY: all clean iso run run-arm run-riscv run-mips cppcheck sparse analyzer check test test-1cpu test-battery test-host test-gdb test-all scan-build mkinitrd-asan
{"ATA DMA mode" "\\[ATA\\] Channel 0: DMA mode."}
{"SMP CPUs active" "CPU\\(s\\) active."}
{"User ring3 entry" "\\[USER\\] enter ring3"}
- {"init.elf hello" "\\[init\\] hello from init.elf"}
- {"open/read/close" "\\[init\\] open/read/close OK"}
- {"overlay copy-up" "\\[init\\] overlay copy-up OK"}
- {"lseek/stat/fstat" "\\[init\\] lseek/stat/fstat OK"}
- {"dup2 restore" "\\[init\\] dup2 restore tty OK"}
- {"kill SIGKILL" "\\[init\\] kill\\(SIGKILL\\) OK"}
- {"poll pipe" "\\[init\\] poll\\(pipe\\) OK"}
- {"select pipe" "\\[init\\] select\\(pipe\\) OK"}
- {"ioctl tty" "\\[init\\] ioctl\\(/dev/tty\\) OK"}
- {"job control" "\\[init\\] job control \\(SIGTTIN/SIGTTOU\\) OK"}
- {"poll /dev/null" "\\[init\\] poll\\(/dev/null\\) OK"}
- {"pty bidirectional" "\\[init\\] pty OK"}
- {"setsid/setpgid" "\\[init\\] setsid/setpgid/getpgrp OK"}
- {"sigaction SIGUSR1" "\\[init\\] sigaction/kill\\(SIGUSR1\\) OK"}
- {"sigreturn" "\\[init\\] sigreturn OK"}
- {"tmpfs/mount" "\\[init\\] tmpfs/mount OK"}
- {"dev null" "\\[init\\] /dev/null OK"}
- {"persist counter" "\\[init\\] /persist/counter="}
- {"dev tty write" "\\[init\\] /dev/tty write OK"}
- {"diskfs test" "\\[init\\] /disk/test prev="}
- {"diskfs mkdir/unlink" "\\[init\\] diskfs mkdir/unlink OK"}
- {"diskfs getdents" "\\[init\\] diskfs getdents OK"}
- {"isatty" "\\[init\\] isatty OK"}
- {"O_NONBLOCK" "\\[init\\] O_NONBLOCK OK"}
- {"pipe2/dup3" "\\[init\\] pipe2/dup3 OK"}
- {"chdir/getcwd" "\\[init\\] chdir/getcwd OK"}
- {"*at syscalls" "\\[init\\] \\*at OK"}
- {"rename/rmdir" "\\[init\\] rename/rmdir OK"}
- {"getdents multi-fs" "\\[init\\] getdents multi-fs OK"}
- {"brk heap" "\\[init\\] brk OK"}
- {"mmap/munmap" "\\[init\\] mmap/munmap OK"}
- {"clock_gettime" "\\[init\\] clock_gettime OK"}
- {"dev zero" "\\[init\\] /dev/zero OK"}
- {"dev random" "\\[init\\] /dev/random OK"}
- {"procfs" "\\[init\\] procfs OK"}
- {"pread/pwrite" "\\[init\\] pread/pwrite OK"}
- {"ftruncate" "\\[init\\] ftruncate OK"}
- {"symlink/readlink" "\\[init\\] symlink/readlink OK"}
- {"access" "\\[init\\] access OK"}
- {"sigprocmask" "\\[init\\] sigprocmask/sigpending OK"}
- {"alarm SIGALRM" "\\[init\\] alarm/SIGALRM OK"}
- {"shmget/shmat" "\\[init\\] shmget/shmat/shmdt OK"}
- {"O_APPEND" "\\[init\\] O_APPEND OK"}
- {"umask" "\\[init\\] umask OK"}
- {"pipe capacity" "\\[init\\] pipe capacity OK"}
- {"waitid" "\\[init\\] waitid OK"}
- {"setitimer/getitimer" "\\[init\\] setitimer/getitimer OK"}
- {"select regfile" "\\[init\\] select regfile OK"}
- {"poll regfile" "\\[init\\] poll regfile OK"}
- {"hard link" "\\[init\\] hard link OK"}
- {"epoll" "\\[init\\] epoll OK"}
- {"epollet" "\\[init\\] epollet OK"}
- {"inotify" "\\[init\\] inotify OK"}
- {"aio" "\\[init\\] aio OK"}
- {"nanosleep" "\\[init\\] nanosleep OK"}
- {"CLOCK_REALTIME" "\\[init\\] CLOCK_REALTIME OK"}
- {"dev urandom" "\\[init\\] /dev/urandom OK"}
- {"proc cmdline" "\\[init\\] /proc/cmdline OK"}
- {"CoW fork" "\\[init\\] CoW fork OK"}
- {"readv/writev" "\\[init\\] readv/writev OK"}
- {"fsync" "\\[init\\] fsync OK"}
- {"truncate path" "\\[init\\] truncate OK"}
- {"getuid/getgid" "\\[init\\] getuid/getgid OK"}
- {"chmod" "\\[init\\] chmod OK"}
- {"flock" "\\[init\\] flock OK"}
- {"times" "\\[init\\] times OK"}
- {"gettid" "\\[init\\] gettid OK"}
- {"posix_spawn" "\\[init\\] posix_spawn OK"}
- {"clock_ns precision" "\\[init\\] clock_ns precision OK"}
- {"getppid" "\\[init\\] getppid OK"}
- {"waitpid WNOHANG" "\\[init\\] waitpid WNOHANG OK"}
- {"SIGSEGV handler" "\\[init\\] SIGSEGV OK"}
- {"waitpid 100 children" "\\[init\\] waitpid OK \\(100 children"}
- {"lazy PLT" "\\[init\\] lazy PLT OK"}
- {"PLT cached" "\\[init\\] PLT cached OK"}
+ {"init.elf hello" "\\[test\\] hello from init.elf"}
+ {"open/read/close" "\\[test\\] open/read/close OK"}
+ {"overlay copy-up" "\\[test\\] overlay copy-up OK"}
+ {"lseek/stat/fstat" "\\[test\\] lseek/stat/fstat OK"}
+ {"dup2 restore" "\\[test\\] dup2 restore tty OK"}
+ {"kill SIGKILL" "\\[test\\] kill\\(SIGKILL\\) OK"}
+ {"poll pipe" "\\[test\\] poll\\(pipe\\) OK"}
+ {"select pipe" "\\[test\\] select\\(pipe\\) OK"}
+ {"ioctl tty" "\\[test\\] ioctl\\(/dev/tty\\) OK"}
+ {"job control" "\\[test\\] job control \\(SIGTTIN/SIGTTOU\\) OK"}
+ {"poll /dev/null" "\\[test\\] poll\\(/dev/null\\) OK"}
+ {"pty bidirectional" "\\[test\\] pty OK"}
+ {"setsid/setpgid" "\\[test\\] setsid/setpgid/getpgrp OK"}
+ {"sigaction SIGUSR1" "\\[test\\] sigaction/kill\\(SIGUSR1\\) OK"}
+ {"sigreturn" "\\[test\\] sigreturn OK"}
+ {"tmpfs/mount" "\\[test\\] tmpfs/mount OK"}
+ {"dev null" "\\[test\\] /dev/null OK"}
+ {"persist counter" "\\[test\\] /persist/counter="}
+ {"dev tty write" "\\[test\\] /dev/tty write OK"}
+ {"diskfs test" "\\[test\\] /disk/test prev="}
+ {"diskfs mkdir/unlink" "\\[test\\] diskfs mkdir/unlink OK"}
+ {"diskfs getdents" "\\[test\\] diskfs getdents OK"}
+ {"isatty" "\\[test\\] isatty OK"}
+ {"O_NONBLOCK" "\\[test\\] O_NONBLOCK OK"}
+ {"pipe2/dup3" "\\[test\\] pipe2/dup3 OK"}
+ {"chdir/getcwd" "\\[test\\] chdir/getcwd OK"}
+ {"*at syscalls" "\\[test\\] \\*at OK"}
+ {"rename/rmdir" "\\[test\\] rename/rmdir OK"}
+ {"getdents multi-fs" "\\[test\\] getdents multi-fs OK"}
+ {"brk heap" "\\[test\\] brk OK"}
+ {"mmap/munmap" "\\[test\\] mmap/munmap OK"}
+ {"clock_gettime" "\\[test\\] clock_gettime OK"}
+ {"dev zero" "\\[test\\] /dev/zero OK"}
+ {"dev random" "\\[test\\] /dev/random OK"}
+ {"procfs" "\\[test\\] procfs OK"}
+ {"pread/pwrite" "\\[test\\] pread/pwrite OK"}
+ {"ftruncate" "\\[test\\] ftruncate OK"}
+ {"symlink/readlink" "\\[test\\] symlink/readlink OK"}
+ {"access" "\\[test\\] access OK"}
+ {"sigprocmask" "\\[test\\] sigprocmask/sigpending OK"}
+ {"alarm SIGALRM" "\\[test\\] alarm/SIGALRM OK"}
+ {"shmget/shmat" "\\[test\\] shmget/shmat/shmdt OK"}
+ {"O_APPEND" "\\[test\\] O_APPEND OK"}
+ {"umask" "\\[test\\] umask OK"}
+ {"pipe capacity" "\\[test\\] pipe capacity OK"}
+ {"waitid" "\\[test\\] waitid OK"}
+ {"setitimer/getitimer" "\\[test\\] setitimer/getitimer OK"}
+ {"select regfile" "\\[test\\] select regfile OK"}
+ {"poll regfile" "\\[test\\] poll regfile OK"}
+ {"hard link" "\\[test\\] hard link OK"}
+ {"epoll" "\\[test\\] epoll OK"}
+ {"epollet" "\\[test\\] epollet OK"}
+ {"inotify" "\\[test\\] inotify OK"}
+ {"aio" "\\[test\\] aio OK"}
+ {"nanosleep" "\\[test\\] nanosleep OK"}
+ {"CLOCK_REALTIME" "\\[test\\] CLOCK_REALTIME OK"}
+ {"dev urandom" "\\[test\\] /dev/urandom OK"}
+ {"proc cmdline" "\\[test\\] /proc/cmdline OK"}
+ {"CoW fork" "\\[test\\] CoW fork OK"}
+ {"readv/writev" "\\[test\\] readv/writev OK"}
+ {"fsync" "\\[test\\] fsync OK"}
+ {"truncate path" "\\[test\\] truncate OK"}
+ {"getuid/getgid" "\\[test\\] getuid/getgid OK"}
+ {"chmod" "\\[test\\] chmod OK"}
+ {"flock" "\\[test\\] flock OK"}
+ {"times" "\\[test\\] times OK"}
+ {"gettid" "\\[test\\] gettid OK"}
+ {"posix_spawn" "\\[test\\] posix_spawn OK"}
+ {"clock_ns precision" "\\[test\\] clock_ns precision OK"}
+ {"getppid" "\\[test\\] getppid OK"}
+ {"waitpid WNOHANG" "\\[test\\] waitpid WNOHANG OK"}
+ {"SIGSEGV handler" "\\[test\\] SIGSEGV OK"}
+ {"waitpid 100 children" "\\[test\\] waitpid OK \\(100 children"}
+ {"lazy PLT" "\\[test\\] lazy PLT OK"}
+ {"PLT cached" "\\[test\\] PLT cached OK"}
{"PING network" "\\[PING\\] .*received.*network OK"}
{"echo execve" "\\[echo\\] hello from echo"}
- {"setuid/setgid" "\\[init\\] setuid/setgid OK"}
- {"fcntl F_GETFL/SETFL" "\\[init\\] fcntl F_GETFL/F_SETFL OK"}
- {"fcntl FD_CLOEXEC" "\\[init\\] fcntl FD_CLOEXEC OK"}
- {"sigsuspend" "\\[init\\] sigsuspend OK"}
- {"orphan reparent" "\\[init\\] orphan reparent OK"}
- {"proc PID cmdline" "\\[init\\] /proc/PID/cmdline OK"}
- {"proc PID status" "\\[init\\] /proc/PID/status OK"}
- {"dev console" "\\[init\\] /dev/console OK"}
- {"multi-pty" "\\[init\\] multi-pty OK"}
- {"dup standalone" "\\[init\\] dup OK"}
- {"pipe EOF" "\\[init\\] pipe EOF OK"}
- {"readdir /proc" "\\[init\\] readdir /proc OK"}
- {"readdir /bin" "\\[init\\] readdir /bin OK"}
- {"gettimeofday" "\\[init\\] gettimeofday OK"}
- {"mprotect" "\\[init\\] mprotect OK"}
- {"madvise" "\\[init\\] madvise OK"}
- {"getrlimit/setrlimit" "\\[init\\] getrlimit/setrlimit OK"}
- {"uname" "\\[init\\] uname OK"}
- {"SMP parallel fork" "\\[init\\] SMP parallel fork OK"}
+ {"setuid/setgid" "\\[test\\] setuid/setgid OK"}
+ {"fcntl F_GETFL/SETFL" "\\[test\\] fcntl F_GETFL/F_SETFL OK"}
+ {"fcntl FD_CLOEXEC" "\\[test\\] fcntl FD_CLOEXEC OK"}
+ {"sigsuspend" "\\[test\\] sigsuspend OK"}
+ {"orphan reparent" "\\[test\\] orphan reparent OK"}
+ {"proc PID cmdline" "\\[test\\] /proc/PID/cmdline OK"}
+ {"proc PID status" "\\[test\\] /proc/PID/status OK"}
+ {"dev console" "\\[test\\] /dev/console OK"}
+ {"multi-pty" "\\[test\\] multi-pty OK"}
+ {"dup standalone" "\\[test\\] dup OK"}
+ {"pipe EOF" "\\[test\\] pipe EOF OK"}
+ {"readdir /proc" "\\[test\\] readdir /proc OK"}
+ {"readdir /bin" "\\[test\\] readdir /bin OK"}
+ {"gettimeofday" "\\[test\\] gettimeofday OK"}
+ {"mprotect" "\\[test\\] mprotect OK"}
+ {"madvise" "\\[test\\] madvise OK"}
+ {"getrlimit/setrlimit" "\\[test\\] getrlimit/setrlimit OK"}
+ {"uname" "\\[test\\] uname OK"}
+ {"SMP parallel fork" "\\[test\\] SMP parallel fork OK"}
{"LZ4 Frame decomp" "\\[INITRD\\] LZ4"}
}
{"ATA /dev/hda" "\\[ATA\\] /dev/hda detected"}
{"INITRD found" "\\[INITRD\\] Found"}
{"diskfs mount /disk" "\\[MOUNT\\] diskfs on /dev/hda"}
- {"diskfs test" "\\[init\\] /disk/test prev="}
- {"diskfs getdents" "\\[init\\] diskfs getdents OK"}
+ {"diskfs test" "\\[test\\] /disk/test prev="}
+ {"diskfs getdents" "\\[test\\] diskfs getdents OK"}
}
set res [wait_for_patterns $serial_log $timeout_sec $patterns]
# ---------- echo ----------
echo "--- echo ---"
-if compile echo_test user/echo.c; then
+if compile echo_test user/cmds/echo/echo.c; then
out=$("$BUILDDIR/echo_test" hello world)
[ "$out" = "hello world" ] && pass "echo basic" || fail "echo basic" "got: $out"
# ---------- cat ----------
echo "--- cat ---"
-if compile cat_test user/cat.c; then
+if compile cat_test user/cmds/cat/cat.c; then
echo "hello cat" > "$BUILDDIR/cat_in.txt"
out=$("$BUILDDIR/cat_test" "$BUILDDIR/cat_in.txt")
[ "$out" = "hello cat" ] && pass "cat file" || fail "cat file" "got: $out"
# ---------- head ----------
echo "--- head ---"
-if compile head_test user/head.c; then
+if compile head_test user/cmds/head/head.c; then
printf "1\n2\n3\n4\n5\n6\n7\n8\n9\n10\n11\n12\n" > "$BUILDDIR/head_in.txt"
out=$("$BUILDDIR/head_test" "$BUILDDIR/head_in.txt" | wc -l)
[ "$out" -eq 10 ] && pass "head default 10" || fail "head default 10" "got $out lines"
# ---------- tail ----------
echo "--- tail ---"
-if compile tail_test user/tail.c; then
+if compile tail_test user/cmds/tail/tail.c; then
printf "1\n2\n3\n4\n5\n6\n7\n8\n9\n10\n11\n12\n" > "$BUILDDIR/tail_in.txt"
out=$("$BUILDDIR/tail_test" -n 3 "$BUILDDIR/tail_in.txt")
expected=$(printf "10\n11\n12")
# ---------- wc ----------
echo "--- wc ---"
-if compile wc_test user/wc.c; then
+if compile wc_test user/cmds/wc/wc.c; then
printf "hello world\nfoo bar baz\n" > "$BUILDDIR/wc_in.txt"
out=$("$BUILDDIR/wc_test" "$BUILDDIR/wc_in.txt")
# Should contain line count (2), word count (5), byte count
# ---------- sort ----------
echo "--- sort ---"
-if compile sort_test user/sort.c; then
+if compile sort_test user/cmds/sort/sort.c; then
printf "banana\napple\ncherry\n" | "$BUILDDIR/sort_test" > "$BUILDDIR/sort_out.txt"
expected=$(printf "apple\nbanana\ncherry")
out=$(cat "$BUILDDIR/sort_out.txt")
# ---------- uniq ----------
echo "--- uniq ---"
-if compile uniq_test user/uniq.c; then
+if compile uniq_test user/cmds/uniq/uniq.c; then
printf "aaa\naaa\nbbb\nccc\nccc\n" | "$BUILDDIR/uniq_test" > "$BUILDDIR/uniq_out.txt"
expected=$(printf "aaa\nbbb\nccc")
out=$(cat "$BUILDDIR/uniq_out.txt")
# ---------- cut ----------
echo "--- cut ---"
-if compile cut_test user/cut.c; then
+if compile cut_test user/cmds/cut/cut.c; then
out=$(printf "a:b:c\n" | "$BUILDDIR/cut_test" -d: -f2)
[ "$out" = "b" ] && pass "cut -d: -f2" || fail "cut -d: -f2" "got: $out"
# ---------- grep ----------
echo "--- grep ---"
-if compile grep_test user/grep.c; then
+if compile grep_test user/cmds/grep/grep.c; then
printf "hello world\nfoo bar\nhello again\n" > "$BUILDDIR/grep_in.txt"
out=$("$BUILDDIR/grep_test" hello "$BUILDDIR/grep_in.txt")
lines=$(echo "$out" | wc -l)
# ---------- tr ----------
echo "--- tr ---"
-if compile tr_test user/tr.c; then
+if compile tr_test user/cmds/tr/tr.c; then
out=$(echo "hello" | "$BUILDDIR/tr_test" 'elo' 'ELO')
[ "$out" = "hELLO" ] && pass "tr translate" || fail "tr translate" "got: $out"
# ---------- basename ----------
echo "--- basename ---"
-if compile basename_test user/basename.c; then
+if compile basename_test user/cmds/basename/basename.c; then
out=$("$BUILDDIR/basename_test" /usr/bin/foo)
[ "$out" = "foo" ] && pass "basename path" || fail "basename path" "got: $out"
# ---------- dirname ----------
echo "--- dirname ---"
-if compile dirname_test user/dirname.c; then
+if compile dirname_test user/cmds/dirname/dirname.c; then
out=$("$BUILDDIR/dirname_test" /usr/bin/foo)
[ "$out" = "/usr/bin" ] && pass "dirname path" || fail "dirname path" "got: $out"
# ---------- tee ----------
echo "--- tee ---"
-if compile tee_test user/tee.c; then
+if compile tee_test user/cmds/tee/tee.c; then
out=$(echo "tee test" | "$BUILDDIR/tee_test" "$BUILDDIR/tee_out.txt")
file_content=$(cat "$BUILDDIR/tee_out.txt")
[ "$out" = "tee test" ] && pass "tee stdout" || fail "tee stdout" "got: $out"
# ---------- dd ----------
echo "--- dd ---"
-if compile dd_test user/dd.c; then
+if compile dd_test user/cmds/dd/dd.c; then
echo "hello dd test data" > "$BUILDDIR/dd_in.txt"
"$BUILDDIR/dd_test" if="$BUILDDIR/dd_in.txt" of="$BUILDDIR/dd_out.txt" bs=512 2>/dev/null
out=$(cat "$BUILDDIR/dd_out.txt")
# ---------- pwd ----------
echo "--- pwd ---"
-if compile pwd_test user/pwd.c; then
+if compile pwd_test user/cmds/pwd/pwd.c; then
out=$("$BUILDDIR/pwd_test")
expected=$(pwd)
[ -n "$out" ] && pass "pwd output" || fail "pwd output" "empty"
# ---------- uname ----------
echo "--- uname ---"
-if compile uname_test user/uname.c; then
+if compile uname_test user/cmds/uname/uname.c; then
out=$("$BUILDDIR/uname_test")
[ "$out" = "AdrOS" ] && pass "uname default" || fail "uname default" "got: $out"
# ---------- id ----------
echo "--- id ---"
-if compile id_test user/id.c; then
+if compile id_test user/cmds/id/id.c; then
out=$("$BUILDDIR/id_test")
echo "$out" | grep -q "uid=" && pass "id uid" || fail "id uid" "got: $out"
echo "$out" | grep -q "gid=" && pass "id gid" || fail "id gid" "got: $out"
# ---------- printenv ----------
echo "--- printenv ---"
-if compile printenv_test user/printenv.c; then
+if compile printenv_test user/cmds/printenv/printenv.c; then
out=$(HOME=/test/home "$BUILDDIR/printenv_test" HOME)
[ "$out" = "/test/home" ] && pass "printenv HOME" || fail "printenv HOME" "got: $out"
# ---------- cp ----------
echo "--- cp ---"
-if compile cp_test user/cp.c; then
+if compile cp_test user/cmds/cp/cp.c; then
echo "cp source" > "$BUILDDIR/cp_src.txt"
"$BUILDDIR/cp_test" "$BUILDDIR/cp_src.txt" "$BUILDDIR/cp_dst.txt"
out=$(cat "$BUILDDIR/cp_dst.txt")
# ---------- mv ----------
echo "--- mv ---"
-if compile mv_test user/mv.c; then
+if compile mv_test user/cmds/mv/mv.c; then
echo "mv data" > "$BUILDDIR/mv_src.txt"
"$BUILDDIR/mv_test" "$BUILDDIR/mv_src.txt" "$BUILDDIR/mv_dst.txt"
[ ! -f "$BUILDDIR/mv_src.txt" ] && pass "mv src removed" || fail "mv src removed" "still exists"
# ---------- touch/rm/mkdir/rmdir ----------
echo "--- touch/rm/mkdir/rmdir ---"
compile_ok=1
-compile touch_test user/touch.c || compile_ok=0
-compile rm_test user/rm.c || compile_ok=0
-compile mkdir_test user/mkdir.c || compile_ok=0
-compile rmdir_test user/rmdir.c || compile_ok=0
+compile touch_test user/cmds/touch/touch.c || compile_ok=0
+compile rm_test user/cmds/rm/rm.c || compile_ok=0
+compile mkdir_test user/cmds/mkdir/mkdir.c || compile_ok=0
+compile rmdir_test user/cmds/rmdir/rmdir.c || compile_ok=0
if [ "$compile_ok" -eq 1 ]; then
"$BUILDDIR/touch_test" "$BUILDDIR/touchfile"
[ -f "$BUILDDIR/touchfile" ] && pass "touch create" || fail "touch create" "not created"
# ---------- ln ----------
echo "--- ln ---"
-if compile ln_test user/ln.c; then
+if compile ln_test user/cmds/ln/ln.c; then
echo "link target" > "$BUILDDIR/ln_src.txt"
"$BUILDDIR/ln_test" -s "$BUILDDIR/ln_src.txt" "$BUILDDIR/ln_link.txt" 2>/dev/null || true
if [ -L "$BUILDDIR/ln_link.txt" ]; then
# ---------- sed ----------
echo "--- sed ---"
-if compile sed_test user/sed.c; then
+if compile sed_test user/cmds/sed/sed.c; then
out=$(echo "hello world" | "$BUILDDIR/sed_test" 's/world/earth/')
[ "$out" = "hello earth" ] && pass "sed s///" || fail "sed s///" "got: $out"
# ---------- awk ----------
echo "--- awk ---"
-if compile awk_test user/awk.c; then
+if compile awk_test user/cmds/awk/awk.c; then
out=$(echo "hello world foo" | "$BUILDDIR/awk_test" '{print $2}')
[ "$out" = "world" ] && pass "awk print \$2" || fail "awk print \$2" "got: $out"
# ---------- who ----------
echo "--- who ---"
-if compile who_test user/who.c; then
+if compile who_test user/cmds/who/who.c; then
out=$("$BUILDDIR/who_test")
echo "$out" | grep -q "root" && pass "who output" || fail "who output" "got: $out"
else
# ---------- find ----------
echo "--- find ---"
-if compile find_test user/find.c; then
+if compile find_test user/cmds/find/find.c; then
mkdir -p "$BUILDDIR/findtest/sub"
touch "$BUILDDIR/findtest/a.txt"
touch "$BUILDDIR/findtest/b.c"
# ---------- which ----------
echo "--- which ---"
-if compile which_test user/which.c; then
+if compile which_test user/cmds/which/which.c; then
# which looks in /bin and /sbin hardcoded, so just check it runs
- "$BUILDDIR/which_test" nonexistent_cmd > /dev/null 2>&1
- [ $? -ne 0 ] && pass "which not found" || fail "which not found" "should return nonzero"
+ "$BUILDDIR/which_test" nonexistent_cmd > /dev/null 2>&1 || rc=$?
+ [ "${rc:-1}" -ne 0 ] && pass "which not found" || fail "which not found" "should return nonzero"
else
skip "which (compile failed)"
fi
+++ /dev/null
-/* AdrOS awk utility — minimal: print fields, pattern matching, BEGIN/END */
-#include <stdio.h>
-#include <string.h>
-#include <unistd.h>
-#include <fcntl.h>
-#include <stdlib.h>
-
-static char delim = ' ';
-static int print_field = -1; /* -1 = whole line */
-static char pattern[256] = "";
-static int has_pattern = 0;
-
-static void process_line(const char* line) {
- if (has_pattern && !strstr(line, pattern)) return;
-
- if (print_field < 0) {
- printf("%s\n", line);
- return;
- }
-
- /* Split into fields */
- char copy[4096];
- strncpy(copy, line, sizeof(copy) - 1);
- copy[sizeof(copy) - 1] = '\0';
-
- int fi = 0;
- char* p = copy;
- while (*p) {
- while (*p && (*p == delim || *p == '\t')) p++;
- if (!*p) break;
- char* start = p;
- while (*p && *p != delim && *p != '\t') p++;
- if (*p) *p++ = '\0';
- if (fi == print_field) {
- printf("%s\n", start);
- return;
- }
- fi++;
- }
- printf("\n");
-}
-
-int main(int argc, char** argv) {
- if (argc < 2) {
- fprintf(stderr, "Usage: awk [-F sep] '{print $N}' [file]\n");
- return 1;
- }
-
- int argi = 1;
- if (strcmp(argv[argi], "-F") == 0 && argi + 1 < argc) {
- delim = argv[argi + 1][0];
- argi += 2;
- }
-
- if (argi >= argc) {
- fprintf(stderr, "awk: missing program\n");
- return 1;
- }
-
- /* Parse simple program: {print $N} or /pattern/{print $N} */
- const char* prog = argv[argi++];
-
- /* Check for /pattern/ prefix */
- if (prog[0] == '/') {
- const char* end = strchr(prog + 1, '/');
- if (end) {
- int plen = (int)(end - prog - 1);
- if (plen > 0 && plen < (int)sizeof(pattern)) {
- memcpy(pattern, prog + 1, plen);
- pattern[plen] = '\0';
- has_pattern = 1;
- }
- prog = end + 1;
- }
- }
-
- /* Parse {print $N} */
- const char* pp = strstr(prog, "print");
- if (pp) {
- const char* dollar = strchr(pp, '$');
- if (dollar) {
- int n = atoi(dollar + 1);
- print_field = (n > 0) ? n - 1 : -1;
- if (n == 0) print_field = -1; /* $0 = whole line */
- }
- }
-
- int fd = STDIN_FILENO;
- if (argi < argc) {
- fd = open(argv[argi], O_RDONLY);
- if (fd < 0) {
- fprintf(stderr, "awk: %s: No such file or directory\n", argv[argi]);
- return 1;
- }
- }
-
- char line[4096];
- int li = 0;
- char c;
- while (read(fd, &c, 1) == 1) {
- if (c == '\n') {
- line[li] = '\0';
- process_line(line);
- li = 0;
- } else if (li < (int)sizeof(line) - 1) {
- line[li++] = c;
- }
- }
- if (li > 0) {
- line[li] = '\0';
- process_line(line);
- }
-
- if (fd != STDIN_FILENO) close(fd);
- return 0;
-}
+++ /dev/null
-/* AdrOS basename utility — strip directory from filename */
-#include <stdio.h>
-#include <string.h>
-
-int main(int argc, char** argv) {
- if (argc <= 1) {
- fprintf(stderr, "usage: basename PATH [SUFFIX]\n");
- return 1;
- }
- char* p = argv[1];
- /* Remove trailing slashes */
- int len = (int)strlen(p);
- while (len > 1 && p[len - 1] == '/') p[--len] = '\0';
- /* Find last slash */
- char* base = p;
- for (char* s = p; *s; s++) {
- if (*s == '/' && *(s + 1)) base = s + 1;
- }
- /* Strip suffix if provided */
- if (argc > 2) {
- int blen = (int)strlen(base);
- int slen = (int)strlen(argv[2]);
- if (blen > slen && strcmp(base + blen - slen, argv[2]) == 0)
- base[blen - slen] = '\0';
- }
- printf("%s\n", base);
- return 0;
-}
+++ /dev/null
-/* AdrOS cat utility */
-#include <stdio.h>
-#include <stdlib.h>
-#include <unistd.h>
-#include <fcntl.h>
-#include <string.h>
-
-static void cat_fd(int fd) {
- char buf[4096];
- int r;
- while ((r = read(fd, buf, sizeof(buf))) > 0) {
- write(STDOUT_FILENO, buf, (size_t)r);
- }
-}
-
-int main(int argc, char** argv) {
- if (argc <= 1) {
- cat_fd(STDIN_FILENO);
- return 0;
- }
-
- int rc = 0;
- for (int i = 1; i < argc; i++) {
- if (strcmp(argv[i], "-") == 0) {
- cat_fd(STDIN_FILENO);
- continue;
- }
- int fd = open(argv[i], O_RDONLY);
- if (fd < 0) {
- fprintf(stderr, "cat: %s: No such file or directory\n", argv[i]);
- rc = 1;
- continue;
- }
- cat_fd(fd);
- close(fd);
- }
- return rc;
-}
+++ /dev/null
-/* AdrOS chgrp utility */
-#include <stdio.h>
-#include <stdlib.h>
-#include <unistd.h>
-
-int main(int argc, char** argv) {
- if (argc < 3) {
- fprintf(stderr, "Usage: chgrp <group> <file>...\n");
- return 1;
- }
-
- int group = atoi(argv[1]);
- int rc = 0;
-
- for (int i = 2; i < argc; i++) {
- if (chown(argv[i], -1, group) < 0) {
- fprintf(stderr, "chgrp: cannot change group of '%s'\n", argv[i]);
- rc = 1;
- }
- }
- return rc;
-}
+++ /dev/null
-/* AdrOS chmod utility */
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <unistd.h>
-
-int main(int argc, char** argv) {
- if (argc < 3) {
- fprintf(stderr, "Usage: chmod <mode> <file>...\n");
- return 1;
- }
-
- int mode = (int)strtol(argv[1], NULL, 8);
- int rc = 0;
-
- for (int i = 2; i < argc; i++) {
- if (chmod(argv[i], mode) < 0) {
- fprintf(stderr, "chmod: cannot change mode of '%s'\n", argv[i]);
- rc = 1;
- }
- }
- return rc;
-}
+++ /dev/null
-/* AdrOS chown utility */
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <unistd.h>
-
-int main(int argc, char** argv) {
- if (argc < 3) {
- fprintf(stderr, "Usage: chown <owner[:group]> <file>...\n");
- return 1;
- }
-
- /* Parse owner:group */
- int owner = -1, group = -1;
- char* colon = strchr(argv[1], ':');
- if (colon) {
- *colon = '\0';
- if (argv[1][0]) owner = atoi(argv[1]);
- if (colon[1]) group = atoi(colon + 1);
- } else {
- owner = atoi(argv[1]);
- }
-
- int rc = 0;
- for (int i = 2; i < argc; i++) {
- if (chown(argv[i], owner, group) < 0) {
- fprintf(stderr, "chown: cannot change owner of '%s'\n", argv[i]);
- rc = 1;
- }
- }
- return rc;
-}
+++ /dev/null
-/* AdrOS clear utility — clear the terminal screen */
-#include <unistd.h>
-
-int main(void) {
- /* ANSI escape: clear screen + move cursor to top-left */
- write(STDOUT_FILENO, "\033[2J\033[H", 7);
- return 0;
-}
--- /dev/null
+NAME := awk
+SRCS := awk.c
+include ../common.mk
--- /dev/null
+/* AdrOS awk utility — minimal: print fields, pattern matching, BEGIN/END */
+#include <stdio.h>
+#include <string.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <stdlib.h>
+
+static char delim = ' ';
+static int print_field = -1; /* -1 = whole line */
+static char pattern[256] = "";
+static int has_pattern = 0;
+
+static void process_line(const char* line) {
+ if (has_pattern && !strstr(line, pattern)) return;
+
+ if (print_field < 0) {
+ printf("%s\n", line);
+ return;
+ }
+
+ /* Split into fields */
+ char copy[4096];
+ strncpy(copy, line, sizeof(copy) - 1);
+ copy[sizeof(copy) - 1] = '\0';
+
+ int fi = 0;
+ char* p = copy;
+ while (*p) {
+ while (*p && (*p == delim || *p == '\t')) p++;
+ if (!*p) break;
+ char* start = p;
+ while (*p && *p != delim && *p != '\t') p++;
+ if (*p) *p++ = '\0';
+ if (fi == print_field) {
+ printf("%s\n", start);
+ return;
+ }
+ fi++;
+ }
+ printf("\n");
+}
+
+int main(int argc, char** argv) {
+ if (argc < 2) {
+ fprintf(stderr, "Usage: awk [-F sep] '{print $N}' [file]\n");
+ return 1;
+ }
+
+ int argi = 1;
+ if (strcmp(argv[argi], "-F") == 0 && argi + 1 < argc) {
+ delim = argv[argi + 1][0];
+ argi += 2;
+ }
+
+ if (argi >= argc) {
+ fprintf(stderr, "awk: missing program\n");
+ return 1;
+ }
+
+ /* Parse simple program: {print $N} or /pattern/{print $N} */
+ const char* prog = argv[argi++];
+
+ /* Check for /pattern/ prefix */
+ if (prog[0] == '/') {
+ const char* end = strchr(prog + 1, '/');
+ if (end) {
+ int plen = (int)(end - prog - 1);
+ if (plen > 0 && plen < (int)sizeof(pattern)) {
+ memcpy(pattern, prog + 1, plen);
+ pattern[plen] = '\0';
+ has_pattern = 1;
+ }
+ prog = end + 1;
+ }
+ }
+
+ /* Parse {print $N} */
+ const char* pp = strstr(prog, "print");
+ if (pp) {
+ const char* dollar = strchr(pp, '$');
+ if (dollar) {
+ int n = atoi(dollar + 1);
+ print_field = (n > 0) ? n - 1 : -1;
+ if (n == 0) print_field = -1; /* $0 = whole line */
+ }
+ }
+
+ int fd = STDIN_FILENO;
+ if (argi < argc) {
+ fd = open(argv[argi], O_RDONLY);
+ if (fd < 0) {
+ fprintf(stderr, "awk: %s: No such file or directory\n", argv[argi]);
+ return 1;
+ }
+ }
+
+ char line[4096];
+ int li = 0;
+ char c;
+ while (read(fd, &c, 1) == 1) {
+ if (c == '\n') {
+ line[li] = '\0';
+ process_line(line);
+ li = 0;
+ } else if (li < (int)sizeof(line) - 1) {
+ line[li++] = c;
+ }
+ }
+ if (li > 0) {
+ line[li] = '\0';
+ process_line(line);
+ }
+
+ if (fd != STDIN_FILENO) close(fd);
+ return 0;
+}
--- /dev/null
+NAME := basename
+SRCS := basename.c
+include ../common.mk
--- /dev/null
+/* AdrOS basename utility — strip directory from filename */
+#include <stdio.h>
+#include <string.h>
+
+int main(int argc, char** argv) {
+ if (argc <= 1) {
+ fprintf(stderr, "usage: basename PATH [SUFFIX]\n");
+ return 1;
+ }
+ char* p = argv[1];
+ /* Remove trailing slashes */
+ int len = (int)strlen(p);
+ while (len > 1 && p[len - 1] == '/') p[--len] = '\0';
+ /* Find last slash */
+ char* base = p;
+ for (char* s = p; *s; s++) {
+ if (*s == '/' && *(s + 1)) base = s + 1;
+ }
+ /* Strip suffix if provided */
+ if (argc > 2) {
+ int blen = (int)strlen(base);
+ int slen = (int)strlen(argv[2]);
+ if (blen > slen && strcmp(base + blen - slen, argv[2]) == 0)
+ base[blen - slen] = '\0';
+ }
+ printf("%s\n", base);
+ return 0;
+}
--- /dev/null
+NAME := cat
+SRCS := cat.c
+include ../common.mk
--- /dev/null
+/* AdrOS cat utility */
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <string.h>
+
+static void cat_fd(int fd) {
+ char buf[4096];
+ int r;
+ while ((r = read(fd, buf, sizeof(buf))) > 0) {
+ write(STDOUT_FILENO, buf, (size_t)r);
+ }
+}
+
+int main(int argc, char** argv) {
+ if (argc <= 1) {
+ cat_fd(STDIN_FILENO);
+ return 0;
+ }
+
+ int rc = 0;
+ for (int i = 1; i < argc; i++) {
+ if (strcmp(argv[i], "-") == 0) {
+ cat_fd(STDIN_FILENO);
+ continue;
+ }
+ int fd = open(argv[i], O_RDONLY);
+ if (fd < 0) {
+ fprintf(stderr, "cat: %s: No such file or directory\n", argv[i]);
+ rc = 1;
+ continue;
+ }
+ cat_fd(fd);
+ close(fd);
+ }
+ return rc;
+}
--- /dev/null
+NAME := chgrp
+SRCS := chgrp.c
+include ../common.mk
--- /dev/null
+/* AdrOS chgrp utility */
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+
+int main(int argc, char** argv) {
+ if (argc < 3) {
+ fprintf(stderr, "Usage: chgrp <group> <file>...\n");
+ return 1;
+ }
+
+ int group = atoi(argv[1]);
+ int rc = 0;
+
+ for (int i = 2; i < argc; i++) {
+ if (chown(argv[i], -1, group) < 0) {
+ fprintf(stderr, "chgrp: cannot change group of '%s'\n", argv[i]);
+ rc = 1;
+ }
+ }
+ return rc;
+}
--- /dev/null
+NAME := chmod
+SRCS := chmod.c
+include ../common.mk
--- /dev/null
+/* AdrOS chmod utility */
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+int main(int argc, char** argv) {
+ if (argc < 3) {
+ fprintf(stderr, "Usage: chmod <mode> <file>...\n");
+ return 1;
+ }
+
+ int mode = (int)strtol(argv[1], NULL, 8);
+ int rc = 0;
+
+ for (int i = 2; i < argc; i++) {
+ if (chmod(argv[i], mode) < 0) {
+ fprintf(stderr, "chmod: cannot change mode of '%s'\n", argv[i]);
+ rc = 1;
+ }
+ }
+ return rc;
+}
--- /dev/null
+NAME := chown
+SRCS := chown.c
+include ../common.mk
--- /dev/null
+/* AdrOS chown utility */
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+int main(int argc, char** argv) {
+ if (argc < 3) {
+ fprintf(stderr, "Usage: chown <owner[:group]> <file>...\n");
+ return 1;
+ }
+
+ /* Parse owner:group */
+ int owner = -1, group = -1;
+ char* colon = strchr(argv[1], ':');
+ if (colon) {
+ *colon = '\0';
+ if (argv[1][0]) owner = atoi(argv[1]);
+ if (colon[1]) group = atoi(colon + 1);
+ } else {
+ owner = atoi(argv[1]);
+ }
+
+ int rc = 0;
+ for (int i = 2; i < argc; i++) {
+ if (chown(argv[i], owner, group) < 0) {
+ fprintf(stderr, "chown: cannot change owner of '%s'\n", argv[i]);
+ rc = 1;
+ }
+ }
+ return rc;
+}
--- /dev/null
+NAME := clear
+SRCS := clear.c
+include ../common.mk
--- /dev/null
+/* AdrOS clear utility — clear the terminal screen */
+#include <unistd.h>
+
+int main(void) {
+ /* ANSI escape: clear screen + move cursor to top-left */
+ write(STDOUT_FILENO, "\033[2J\033[H", 7);
+ return 0;
+}
--- /dev/null
+# Common build rules for AdrOS user commands (dynamically linked)
+# Each program Makefile sets NAME and SRCS, then includes this file.
+#
+# Usage from per-program Makefile:
+# NAME := echo
+# SRCS := echo.c
+# include ../common.mk
+
+TOPDIR ?= $(abspath ../../..)
+BUILDDIR := $(TOPDIR)/build/user/cmds/$(NAME)
+
+ULIBC_DIR := $(TOPDIR)/user/ulibc
+DYN_CC ?= i686-elf-gcc -m32 -ffreestanding -nostdlib -O2 -Wall -Wextra -fPIC -fno-plt -I$(ULIBC_DIR)/include
+DYN_LD ?= i686-elf-ld -m elf_i386 --dynamic-linker=/lib/ld.so -T $(TOPDIR)/user/dyn_linker.ld -L$(ULIBC_DIR) -rpath /lib
+CRT0 ?= $(ULIBC_DIR)/src/crt0.o
+
+OBJS := $(addprefix $(BUILDDIR)/,$(SRCS:.c=.o))
+ELF := $(BUILDDIR)/$(NAME).elf
+
+all: $(ELF)
+
+$(ELF): $(OBJS)
+ @echo " LD $@"
+ @$(DYN_LD) -o $@ $(CRT0) $(OBJS) -lc
+
+$(BUILDDIR)/%.o: %.c
+ @mkdir -p $(BUILDDIR)
+ @echo " CC $<"
+ @$(DYN_CC) -c $< -o $@
+
+clean:
+ rm -f $(OBJS) $(ELF)
+
+.PHONY: all clean
--- /dev/null
+NAME := cp
+SRCS := cp.c
+include ../common.mk
--- /dev/null
+/* AdrOS cp utility */
+#include <stdio.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <string.h>
+
+int main(int argc, char** argv) {
+ if (argc < 3) {
+ fprintf(stderr, "Usage: cp <source> <dest>\n");
+ return 1;
+ }
+
+ int src = open(argv[1], O_RDONLY);
+ if (src < 0) {
+ fprintf(stderr, "cp: cannot open '%s'\n", argv[1]);
+ return 1;
+ }
+
+ int dst = open(argv[2], O_WRONLY | O_CREAT | O_TRUNC, 0644);
+ if (dst < 0) {
+ fprintf(stderr, "cp: cannot create '%s'\n", argv[2]);
+ close(src);
+ return 1;
+ }
+
+ char buf[4096];
+ int r;
+ while ((r = read(src, buf, sizeof(buf))) > 0) {
+ int w = write(dst, buf, (size_t)r);
+ if (w != r) {
+ fprintf(stderr, "cp: write error\n");
+ close(src);
+ close(dst);
+ return 1;
+ }
+ }
+
+ close(src);
+ close(dst);
+ return 0;
+}
--- /dev/null
+NAME := cut
+SRCS := cut.c
+include ../common.mk
--- /dev/null
+/* AdrOS cut utility */
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <fcntl.h>
+
+#define LINE_MAX 1024
+
+static char delim = '\t';
+static int fields[32];
+static int nfields = 0;
+
+static void parse_fields(const char* spec) {
+ const char* p = spec;
+ while (*p && nfields < 32) {
+ fields[nfields++] = atoi(p);
+ while (*p && *p != ',') p++;
+ if (*p == ',') p++;
+ }
+}
+
+static void cut_line(char* line) {
+ if (nfields == 0) {
+ printf("%s\n", line);
+ return;
+ }
+
+ /* Split line into fields */
+ char* flds[64];
+ int nf = 0;
+ char* p = line;
+ flds[nf++] = p;
+ while (*p && nf < 64) {
+ if (*p == delim) {
+ *p = '\0';
+ flds[nf++] = p + 1;
+ }
+ p++;
+ }
+
+ /* Print requested fields */
+ int first = 1;
+ for (int i = 0; i < nfields; i++) {
+ int idx = fields[i] - 1; /* 1-based */
+ if (idx >= 0 && idx < nf) {
+ if (!first) write(STDOUT_FILENO, &delim, 1);
+ printf("%s", flds[idx]);
+ first = 0;
+ }
+ }
+ printf("\n");
+}
+
+static void cut_fd(int fd) {
+ char line[LINE_MAX];
+ int pos = 0;
+ char c;
+
+ while (read(fd, &c, 1) > 0) {
+ if (c == '\n') {
+ line[pos] = '\0';
+ cut_line(line);
+ pos = 0;
+ } else if (pos < LINE_MAX - 1) {
+ line[pos++] = c;
+ }
+ }
+ if (pos > 0) {
+ line[pos] = '\0';
+ cut_line(line);
+ }
+}
+
+int main(int argc, char** argv) {
+ int start = 1;
+
+ for (int i = 1; i < argc; i++) {
+ if (strcmp(argv[i], "-d") == 0 && i + 1 < argc) {
+ delim = argv[++i][0];
+ start = i + 1;
+ } else if (strncmp(argv[i], "-d", 2) == 0 && argv[i][2] != '\0') {
+ delim = argv[i][2];
+ start = i + 1;
+ } else if (strcmp(argv[i], "-f") == 0 && i + 1 < argc) {
+ parse_fields(argv[++i]);
+ start = i + 1;
+ } else if (strncmp(argv[i], "-f", 2) == 0 && argv[i][2] != '\0') {
+ parse_fields(argv[i] + 2);
+ start = i + 1;
+ } else if (argv[i][0] != '-') {
+ break;
+ }
+ }
+
+ if (start >= argc) {
+ cut_fd(STDIN_FILENO);
+ } else {
+ for (int i = start; i < argc; i++) {
+ int fd = open(argv[i], O_RDONLY);
+ if (fd < 0) {
+ fprintf(stderr, "cut: cannot open '%s'\n", argv[i]);
+ continue;
+ }
+ cut_fd(fd);
+ close(fd);
+ }
+ }
+ return 0;
+}
--- /dev/null
+NAME := date
+SRCS := date.c
+include ../common.mk
--- /dev/null
+/* AdrOS date utility */
+#include <stdio.h>
+#include <time.h>
+
+int main(int argc, char** argv) {
+ (void)argc; (void)argv;
+ struct timespec ts;
+ if (clock_gettime(CLOCK_REALTIME, &ts) < 0) {
+ fprintf(stderr, "date: cannot get time\n");
+ return 1;
+ }
+
+ /* Simple epoch seconds display — no timezone or strftime yet */
+ unsigned long sec = ts.tv_sec;
+ unsigned long days = sec / 86400;
+ unsigned long rem = sec % 86400;
+ unsigned long hours = rem / 3600;
+ unsigned long mins = (rem % 3600) / 60;
+ unsigned long secs = rem % 60;
+
+ printf("%lu days since epoch, %02lu:%02lu:%02lu UTC\n",
+ days, hours, mins, secs);
+ return 0;
+}
--- /dev/null
+NAME := dd
+SRCS := dd.c
+include ../common.mk
--- /dev/null
+/* AdrOS dd utility — convert and copy a file */
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <fcntl.h>
+
+static int parse_size(const char* s) {
+ int v = atoi(s);
+ int len = (int)strlen(s);
+ if (len > 0) {
+ char suf = s[len - 1];
+ if (suf == 'k' || suf == 'K') v *= 1024;
+ else if (suf == 'm' || suf == 'M') v *= 1024 * 1024;
+ }
+ return v;
+}
+
+int main(int argc, char** argv) {
+ const char* inf = NULL;
+ const char* outf = NULL;
+ int bs = 512;
+ int count = -1;
+
+ for (int i = 1; i < argc; i++) {
+ if (strncmp(argv[i], "if=", 3) == 0) inf = argv[i] + 3;
+ else if (strncmp(argv[i], "of=", 3) == 0) outf = argv[i] + 3;
+ else if (strncmp(argv[i], "bs=", 3) == 0) bs = parse_size(argv[i] + 3);
+ else if (strncmp(argv[i], "count=", 6) == 0) count = atoi(argv[i] + 6);
+ }
+
+ int ifd = STDIN_FILENO;
+ int ofd = STDOUT_FILENO;
+
+ if (inf) {
+ ifd = open(inf, O_RDONLY);
+ if (ifd < 0) { fprintf(stderr, "dd: cannot open '%s'\n", inf); return 1; }
+ }
+ if (outf) {
+ ofd = open(outf, O_WRONLY | O_CREAT | O_TRUNC, 0644);
+ if (ofd < 0) { fprintf(stderr, "dd: cannot open '%s'\n", outf); return 1; }
+ }
+
+ if (bs > 4096) bs = 4096;
+ char buf[4096];
+ int blocks = 0, partial = 0, total = 0;
+
+ while (count < 0 || blocks + partial < count) {
+ int n = read(ifd, buf, (size_t)bs);
+ if (n <= 0) break;
+ write(ofd, buf, (size_t)n);
+ total += n;
+ if (n == bs) blocks++;
+ else partial++;
+ }
+
+ fprintf(stderr, "%d+%d records in\n%d+%d records out\n%d bytes copied\n",
+ blocks, partial, blocks, partial, total);
+
+ if (inf) close(ifd);
+ if (outf) close(ofd);
+ return 0;
+}
--- /dev/null
+NAME := df
+SRCS := df.c
+include ../common.mk
--- /dev/null
+/* AdrOS df utility — display filesystem disk space usage */
+#include <stdio.h>
+
+int main(void) {
+ printf("Filesystem Size Used Avail Use%% Mounted on\n");
+ printf("overlayfs - - - - /\n");
+ printf("devfs - - - - /dev\n");
+ printf("procfs - - - - /proc\n");
+ return 0;
+}
--- /dev/null
+NAME := dirname
+SRCS := dirname.c
+include ../common.mk
--- /dev/null
+/* AdrOS dirname utility — strip last component from path */
+#include <stdio.h>
+#include <string.h>
+
+int main(int argc, char** argv) {
+ if (argc <= 1) {
+ fprintf(stderr, "usage: dirname PATH\n");
+ return 1;
+ }
+ char* p = argv[1];
+ int len = (int)strlen(p);
+ /* Remove trailing slashes */
+ while (len > 1 && p[len - 1] == '/') len--;
+ /* Find last slash */
+ while (len > 0 && p[len - 1] != '/') len--;
+ /* Remove trailing slashes from result */
+ while (len > 1 && p[len - 1] == '/') len--;
+ if (len == 0) { printf(".\n"); return 0; }
+ p[len] = '\0';
+ printf("%s\n", p);
+ return 0;
+}
--- /dev/null
+NAME := dmesg
+SRCS := dmesg.c
+include ../common.mk
--- /dev/null
+/* AdrOS dmesg utility — print kernel ring buffer from /proc/dmesg */
+#include <stdio.h>
+#include <unistd.h>
+#include <fcntl.h>
+
+int main(void) {
+ int fd = open("/proc/dmesg", O_RDONLY);
+ if (fd < 0) {
+ fprintf(stderr, "dmesg: cannot open /proc/dmesg\n");
+ return 1;
+ }
+ char buf[512];
+ int n;
+ while ((n = read(fd, buf, sizeof(buf))) > 0)
+ write(STDOUT_FILENO, buf, (size_t)n);
+ close(fd);
+ return 0;
+}
--- /dev/null
+NAME := du
+SRCS := du.c
+include ../common.mk
--- /dev/null
+/* AdrOS du utility — estimate file space usage */
+#include <stdio.h>
+#include <string.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <dirent.h>
+#include <sys/stat.h>
+
+static int sflag = 0; /* -s: summary only */
+
+static long du_path(const char* path, int print) {
+ struct stat st;
+ if (stat(path, &st) < 0) {
+ fprintf(stderr, "du: cannot access '%s'\n", path);
+ return 0;
+ }
+
+ if (!(st.st_mode & 0040000)) {
+ long blocks = (st.st_size + 511) / 512;
+ if (print && !sflag) printf("%ld\t%s\n", blocks, path);
+ return blocks;
+ }
+
+ int fd = open(path, O_RDONLY);
+ if (fd < 0) return 0;
+
+ long total = 0;
+ char buf[2048];
+ int rc;
+ while ((rc = getdents(fd, buf, sizeof(buf))) > 0) {
+ int off = 0;
+ while (off < rc) {
+ struct dirent* d = (struct dirent*)(buf + off);
+ if (d->d_reclen == 0) break;
+ if (strcmp(d->d_name, ".") != 0 && strcmp(d->d_name, "..") != 0) {
+ char child[512];
+ if (path[strlen(path)-1] == '/')
+ snprintf(child, sizeof(child), "%s%s", path, d->d_name);
+ else
+ snprintf(child, sizeof(child), "%s/%s", path, d->d_name);
+ total += du_path(child, print);
+ }
+ off += d->d_reclen;
+ }
+ }
+ close(fd);
+
+ if (print && !sflag) printf("%ld\t%s\n", total, path);
+ return total;
+}
+
+int main(int argc, char** argv) {
+ int argi = 1;
+ while (argi < argc && argv[argi][0] == '-') {
+ const char* f = argv[argi] + 1;
+ while (*f) {
+ if (*f == 's') sflag = 1;
+ f++;
+ }
+ argi++;
+ }
+
+ if (argi >= argc) {
+ long total = du_path(".", 1);
+ if (sflag) printf("%ld\t.\n", total);
+ } else {
+ for (int i = argi; i < argc; i++) {
+ long total = du_path(argv[i], 1);
+ if (sflag) printf("%ld\t%s\n", total, argv[i]);
+ }
+ }
+ return 0;
+}
--- /dev/null
+NAME := echo
+SRCS := echo.c
+include ../common.mk
--- /dev/null
+/* AdrOS echo utility — POSIX-compatible */
+#include <stdio.h>
+#include <string.h>
+#include <unistd.h>
+
+int main(int argc, char** argv) {
+ int nflag = 0; /* -n: no trailing newline */
+ int eflag = 0; /* -e: interpret escape sequences */
+ int i = 1;
+
+ /* Parse flags */
+ while (i < argc && argv[i][0] == '-' && argv[i][1] != '\0') {
+ const char* f = argv[i] + 1;
+ int valid = 1;
+ int n = 0, e = 0;
+ while (*f) {
+ if (*f == 'n') n = 1;
+ else if (*f == 'e') e = 1;
+ else if (*f == 'E') { /* no escapes — default */ }
+ else { valid = 0; break; }
+ f++;
+ }
+ if (!valid) break;
+ if (n) nflag = 1;
+ if (e) eflag = 1;
+ i++;
+ }
+
+ int first = 1;
+ for (; i < argc; i++) {
+ if (!first)
+ write(STDOUT_FILENO, " ", 1);
+ first = 0;
+
+ const char* s = argv[i];
+ if (eflag) {
+ while (*s) {
+ if (*s == '\\' && s[1]) {
+ s++;
+ char c = *s;
+ switch (c) {
+ case 'n': write(STDOUT_FILENO, "\n", 1); break;
+ case 't': write(STDOUT_FILENO, "\t", 1); break;
+ case '\\': write(STDOUT_FILENO, "\\", 1); break;
+ case 'r': write(STDOUT_FILENO, "\r", 1); break;
+ case 'a': write(STDOUT_FILENO, "\a", 1); break;
+ default: write(STDOUT_FILENO, "\\", 1);
+ write(STDOUT_FILENO, &c, 1); break;
+ }
+ } else {
+ write(STDOUT_FILENO, s, 1);
+ }
+ s++;
+ }
+ } else {
+ write(STDOUT_FILENO, s, strlen(s));
+ }
+ }
+
+ if (!nflag)
+ write(STDOUT_FILENO, "\n", 1);
+
+ return 0;
+}
--- /dev/null
+NAME := env
+SRCS := env.c
+include ../common.mk
--- /dev/null
+/* AdrOS env utility — print environment or run command with modified env */
+#include <stdio.h>
+#include <string.h>
+#include <unistd.h>
+
+extern char** __environ;
+
+int main(int argc, char** argv) {
+ if (argc <= 1) {
+ /* Print all environment variables */
+ if (__environ) {
+ for (int i = 0; __environ[i]; i++)
+ printf("%s\n", __environ[i]);
+ }
+ return 0;
+ }
+ /* env COMMAND ARGS... — run command with current environment */
+ execve(argv[1], (const char* const*)&argv[1], (const char* const*)__environ);
+ fprintf(stderr, "env: %s: not found\n", argv[1]);
+ return 127;
+}
--- /dev/null
+NAME := find
+SRCS := find.c
+include ../common.mk
--- /dev/null
+/* AdrOS find utility — search for files in directory hierarchy */
+#include <stdio.h>
+#include <string.h>
+#include <dirent.h>
+
+#ifndef DT_DIR
+#define DT_DIR 4
+#endif
+
+static const char* name_pattern = NULL;
+static int type_filter = 0; /* 0=any, 'f'=file, 'd'=dir */
+
+static int match_name(const char* name) {
+ if (!name_pattern) return 1;
+ /* Simple wildcard: *pattern* if pattern has no special chars,
+ or exact match. Support leading/trailing * only. */
+ int plen = (int)strlen(name_pattern);
+ if (plen == 0) return 1;
+
+ const char* pat = name_pattern;
+ int lead_star = (pat[0] == '*');
+ int trail_star = (plen > 1 && pat[plen-1] == '*');
+
+ if (lead_star && trail_star) {
+ char sub[256];
+ int slen = plen - 2;
+ if (slen <= 0) return 1;
+ memcpy(sub, pat + 1, (size_t)slen);
+ sub[slen] = '\0';
+ return strstr(name, sub) != NULL;
+ }
+ if (lead_star) {
+ const char* suffix = pat + 1;
+ int slen = plen - 1;
+ int nlen = (int)strlen(name);
+ if (nlen < slen) return 0;
+ return strcmp(name + nlen - slen, suffix) == 0;
+ }
+ if (trail_star) {
+ return strncmp(name, pat, (size_t)(plen - 1)) == 0;
+ }
+ return strcmp(name, pat) == 0;
+}
+
+static void find_recurse(const char* path) {
+ DIR* dir = opendir(path);
+ if (!dir) return;
+
+ struct dirent* d;
+ while ((d = readdir(dir)) != NULL) {
+ if (strcmp(d->d_name, ".") == 0 || strcmp(d->d_name, "..") == 0)
+ continue;
+
+ char child[512];
+ size_t plen = strlen(path);
+ if (plen > 0 && path[plen - 1] == '/')
+ snprintf(child, sizeof(child), "%s%s", path, d->d_name);
+ else
+ snprintf(child, sizeof(child), "%s/%s", path, d->d_name);
+
+ int is_dir = (d->d_type == DT_DIR);
+ int show = match_name(d->d_name);
+ if (show && type_filter) {
+ if (type_filter == 'f' && is_dir) show = 0;
+ if (type_filter == 'd' && !is_dir) show = 0;
+ }
+ if (show) printf("%s\n", child);
+
+ if (is_dir) {
+ find_recurse(child);
+ }
+ }
+ closedir(dir);
+}
+
+int main(int argc, char** argv) {
+ const char* start = ".";
+ int argi = 1;
+
+ if (argi < argc && argv[argi][0] != '-') {
+ start = argv[argi++];
+ }
+
+ while (argi < argc) {
+ if (strcmp(argv[argi], "-name") == 0 && argi + 1 < argc) {
+ name_pattern = argv[++argi];
+ } else if (strcmp(argv[argi], "-type") == 0 && argi + 1 < argc) {
+ type_filter = argv[++argi][0];
+ }
+ argi++;
+ }
+
+ printf("%s\n", start);
+ find_recurse(start);
+ return 0;
+}
--- /dev/null
+NAME := free
+SRCS := free.c
+include ../common.mk
--- /dev/null
+/* AdrOS free utility — display memory usage from /proc/meminfo */
+#include <stdio.h>
+#include <unistd.h>
+#include <fcntl.h>
+
+int main(void) {
+ int fd = open("/proc/meminfo", O_RDONLY);
+ if (fd >= 0) {
+ char buf[512];
+ int n = read(fd, buf, sizeof(buf) - 1);
+ if (n > 0) { buf[n] = '\0'; printf("%s", buf); }
+ close(fd);
+ } else {
+ printf("free: /proc/meminfo not available\n");
+ }
+ return 0;
+}
--- /dev/null
+# fulltest — statically linked smoke test binary
+NAME := fulltest
+
+TOPDIR ?= $(abspath ../../..)
+BUILDDIR := $(TOPDIR)/build/user/cmds/$(NAME)
+USER_CC ?= i686-elf-gcc
+
+ELF := $(BUILDDIR)/fulltest.elf
+
+all: $(ELF)
+
+$(ELF): fulltest.c errno.c user_errno.h
+ @mkdir -p $(BUILDDIR)
+ @echo " CC+LD $@"
+ @$(USER_CC) -m32 -I $(TOPDIR)/include -ffreestanding -fno-pie -no-pie -nostdlib \
+ -Wl,-T,$(TOPDIR)/user/linker.ld -o $@ fulltest.c errno.c
+
+clean:
+ rm -f $(ELF)
+
+.PHONY: all clean
--- /dev/null
+/* Per-process errno: each fork()ed process gets its own copy in its
+ address space. When true threads (clone) are added, this must become
+ __thread int errno or use a TLS segment (GS/FS). */
+int errno = 0;
--- /dev/null
+#include <stdint.h>
+
+#ifdef SIGKILL
+#undef SIGKILL
+#endif
+#ifdef SIGUSR1
+#undef SIGUSR1
+#endif
+#ifdef SIGSEGV
+#undef SIGSEGV
+#endif
+#ifdef SIGTTIN
+#undef SIGTTIN
+#endif
+#ifdef SIGTTOU
+#undef SIGTTOU
+#endif
+
+#ifdef WNOHANG
+#undef WNOHANG
+#endif
+#ifdef SEEK_SET
+#undef SEEK_SET
+#endif
+#ifdef SEEK_CUR
+#undef SEEK_CUR
+#endif
+#ifdef SEEK_END
+#undef SEEK_END
+#endif
+
+#include "user_errno.h"
+
+#include "signal.h"
+
+enum {
+ SYSCALL_WRITE = 1,
+ SYSCALL_EXIT = 2,
+ SYSCALL_GETPID = 3,
+ SYSCALL_OPEN = 4,
+ SYSCALL_READ = 5,
+ SYSCALL_CLOSE = 6,
+ SYSCALL_WAITPID = 7,
+ SYSCALL_LSEEK = 9,
+ SYSCALL_FSTAT = 10,
+ SYSCALL_STAT = 11,
+
+ SYSCALL_DUP = 12,
+ SYSCALL_DUP2 = 13,
+ SYSCALL_PIPE = 14,
+ SYSCALL_PIPE2 = 34,
+ SYSCALL_EXECVE = 15,
+ SYSCALL_FORK = 16,
+ SYSCALL_GETPPID = 17,
+ SYSCALL_POLL = 18,
+ SYSCALL_KILL = 19,
+ SYSCALL_SELECT = 20,
+ SYSCALL_IOCTL = 21,
+ SYSCALL_SETSID = 22,
+ SYSCALL_SETPGID = 23,
+ SYSCALL_GETPGRP = 24,
+
+ SYSCALL_SIGACTION = 25,
+ SYSCALL_SIGPROCMASK = 26,
+ SYSCALL_SIGRETURN = 27,
+
+ SYSCALL_MKDIR = 28,
+ SYSCALL_UNLINK = 29,
+
+ SYSCALL_GETDENTS = 30,
+
+ SYSCALL_FCNTL = 31,
+
+ SYSCALL_CHDIR = 32,
+ SYSCALL_GETCWD = 33,
+ SYSCALL_DUP3 = 35,
+
+ SYSCALL_OPENAT = 36,
+ SYSCALL_FSTATAT = 37,
+ SYSCALL_UNLINKAT = 38,
+
+ SYSCALL_RENAME = 39,
+ SYSCALL_RMDIR = 40,
+
+ SYSCALL_BRK = 41,
+ SYSCALL_NANOSLEEP = 42,
+ SYSCALL_CLOCK_GETTIME = 43,
+ SYSCALL_MMAP = 44,
+ SYSCALL_MUNMAP = 45,
+
+ SYSCALL_SHMGET = 46,
+ SYSCALL_SHMAT = 47,
+ SYSCALL_SHMDT = 48,
+
+ SYSCALL_LINK = 54,
+ SYSCALL_SYMLINK = 55,
+ SYSCALL_READLINK = 56,
+
+ SYSCALL_SIGPENDING = 71,
+ SYSCALL_PREAD = 72,
+ SYSCALL_PWRITE = 73,
+ SYSCALL_ACCESS = 74,
+ SYSCALL_TRUNCATE = 78,
+ SYSCALL_FTRUNCATE = 79,
+ SYSCALL_UMASK = 75,
+ SYSCALL_ALARM = 83,
+ SYSCALL_SETITIMER = 92,
+ SYSCALL_GETITIMER = 93,
+ SYSCALL_WAITID = 94,
+
+ SYSCALL_EPOLL_CREATE = 112,
+ SYSCALL_EPOLL_CTL = 113,
+ SYSCALL_EPOLL_WAIT = 114,
+
+ SYSCALL_INOTIFY_INIT = 115,
+ SYSCALL_INOTIFY_ADD_WATCH = 116,
+ SYSCALL_INOTIFY_RM_WATCH = 117,
+
+ SYSCALL_AIO_READ = 121,
+ SYSCALL_AIO_WRITE = 122,
+ SYSCALL_AIO_ERROR = 123,
+ SYSCALL_AIO_RETURN = 124,
+
+ SYSCALL_CHMOD = 50,
+ SYSCALL_CHOWN = 51,
+ SYSCALL_GETUID = 52,
+ SYSCALL_GETGID = 53,
+ SYSCALL_CLONE = 67,
+ SYSCALL_GETTID = 68,
+ SYSCALL_FSYNC = 69,
+ SYSCALL_READV = 81,
+ SYSCALL_WRITEV = 82,
+ SYSCALL_TIMES = 84,
+ SYSCALL_FUTEX = 85,
+ SYSCALL_FLOCK = 87,
+ SYSCALL_GETEUID = 88,
+ SYSCALL_GETEGID = 89,
+ SYSCALL_SETEUID = 90,
+ SYSCALL_SETEGID = 91,
+ SYSCALL_SIGSUSPEND = 80,
+ SYSCALL_SIGQUEUE = 95,
+ SYSCALL_POSIX_SPAWN = 96,
+ SYSCALL_SETUID = 76,
+ SYSCALL_SETGID = 77,
+ SYSCALL_MOUNT = 126,
+ SYSCALL_GETTIMEOFDAY = 127,
+ SYSCALL_MPROTECT = 128,
+ SYSCALL_GETRLIMIT = 129,
+ SYSCALL_SETRLIMIT = 130,
+ SYSCALL_UNAME = 136,
+ SYSCALL_MADVISE = 140,
+};
+
+enum {
+ AT_FDCWD = -100,
+};
+
+enum {
+ F_GETFD = 1,
+ F_SETFD = 2,
+ F_GETFL = 3,
+ F_SETFL = 4,
+ F_GETPIPE_SZ = 1032,
+ F_SETPIPE_SZ = 1033,
+ FD_CLOEXEC = 1,
+};
+
+enum {
+ O_CLOEXEC = 0x80000,
+};
+
+enum {
+ TCGETS = 0x5401,
+ TCSETS = 0x5402,
+ TIOCGPGRP = 0x540F,
+ TIOCSPGRP = 0x5410,
+};
+
+enum {
+ ENOTTY = 25,
+};
+
+enum {
+ ICANON = 0x0002,
+ ECHO = 0x0008,
+};
+
+#define USER_NCCS 11
+
+struct termios {
+ uint32_t c_iflag;
+ uint32_t c_oflag;
+ uint32_t c_cflag;
+ uint32_t c_lflag;
+ uint8_t c_cc[USER_NCCS];
+};
+
+struct pollfd {
+ int fd;
+ int16_t events;
+ int16_t revents;
+};
+
+enum {
+ POLLIN = 0x0001,
+ POLLOUT = 0x0004,
+ EPOLLET = (1U << 31),
+};
+
+enum {
+ SIGKILL = 9,
+ SIGUSR1 = 10,
+ SIGSEGV = 11,
+ SIGTTIN = 21,
+ SIGTTOU = 22,
+};
+
+enum {
+ WNOHANG = 1,
+};
+
+enum {
+ SEEK_SET = 0,
+ SEEK_CUR = 1,
+ SEEK_END = 2,
+};
+
+enum {
+ O_CREAT = 0x40,
+ O_TRUNC = 0x200,
+ O_NONBLOCK = 0x800,
+ O_APPEND = 0x400,
+ O_RDWR = 0x02,
+};
+
+enum {
+ PROT_READ = 0x1,
+ PROT_WRITE = 0x2,
+};
+
+enum {
+ MAP_PRIVATE = 0x02,
+ MAP_ANONYMOUS = 0x20,
+ MAP_FAILED_VAL = 0xFFFFFFFF,
+};
+
+enum {
+ CLOCK_REALTIME = 0,
+ CLOCK_MONOTONIC = 1,
+};
+
+struct timespec {
+ uint32_t tv_sec;
+ uint32_t tv_nsec;
+};
+
+enum {
+ R_OK = 4,
+ W_OK = 2,
+ F_OK = 0,
+};
+
+enum {
+ SIG_BLOCK = 0,
+ SIG_UNBLOCK = 1,
+ SIG_SETMASK = 2,
+};
+
+enum {
+ IPC_CREAT = 01000,
+ IPC_PRIVATE = 0,
+};
+
+enum {
+ SIGALRM = 14,
+};
+
+enum {
+ EAGAIN = 11,
+ EINVAL = 22,
+ EEXIST = 17,
+};
+
+enum {
+ ITIMER_REAL = 0,
+ ITIMER_VIRTUAL = 1,
+ ITIMER_PROF = 2,
+};
+
+struct timeval {
+ uint32_t tv_sec;
+ uint32_t tv_usec;
+};
+
+struct rlimit {
+ uint32_t rlim_cur;
+ uint32_t rlim_max;
+};
+
+enum {
+ RLIMIT_CPU = 0,
+ RLIMIT_FSIZE = 1,
+ RLIMIT_DATA = 2,
+ RLIMIT_STACK = 3,
+ RLIMIT_CORE = 4,
+ RLIMIT_NOFILE = 5,
+ RLIMIT_AS = 6,
+ RLIMIT_NPROC = 7,
+ RLIM_INFINITY = 0xFFFFFFFFU,
+};
+
+struct itimerval {
+ struct timeval it_interval;
+ struct timeval it_value;
+};
+
+enum {
+ P_ALL = 0,
+ P_PID = 1,
+ P_PGID = 2,
+ WEXITED = 4,
+};
+
+#define S_IFMT 0170000
+#define S_IFREG 0100000
+
+struct stat {
+ uint32_t st_ino;
+ uint32_t st_mode;
+ uint32_t st_nlink;
+ uint32_t st_uid;
+ uint32_t st_gid;
+ uint32_t st_size;
+};
+
+static int sys_write(int fd, const void* buf, uint32_t len) {
+ int ret;
+ __asm__ volatile(
+ "int $0x80"
+ : "=a"(ret)
+ : "a"(SYSCALL_WRITE), "b"(fd), "c"(buf), "d"(len)
+ : "memory"
+ );
+ return __syscall_fix(ret);
+}
+
+static int sys_openat(int dirfd, const char* path, uint32_t flags, uint32_t mode) {
+ int ret;
+ __asm__ volatile(
+ "int $0x80"
+ : "=a"(ret)
+ : "a"(SYSCALL_OPENAT), "b"(dirfd), "c"(path), "d"(flags), "S"(mode)
+ : "memory"
+ );
+ return __syscall_fix(ret);
+}
+
+static int sys_fstatat(int dirfd, const char* path, struct stat* st, uint32_t flags) {
+ int ret;
+ __asm__ volatile(
+ "int $0x80"
+ : "=a"(ret)
+ : "a"(SYSCALL_FSTATAT), "b"(dirfd), "c"(path), "d"(st), "S"(flags)
+ : "memory"
+ );
+ return __syscall_fix(ret);
+}
+
+static int sys_unlinkat(int dirfd, const char* path, uint32_t flags) {
+ int ret;
+ __asm__ volatile(
+ "int $0x80"
+ : "=a"(ret)
+ : "a"(SYSCALL_UNLINKAT), "b"(dirfd), "c"(path), "d"(flags)
+ : "memory"
+ );
+ return __syscall_fix(ret);
+}
+
+static int sys_rename(const char* oldpath, const char* newpath) {
+ int ret;
+ __asm__ volatile(
+ "int $0x80"
+ : "=a"(ret)
+ : "a"(SYSCALL_RENAME), "b"(oldpath), "c"(newpath)
+ : "memory"
+ );
+ return __syscall_fix(ret);
+}
+
+static int sys_rmdir(const char* path) {
+ int ret;
+ __asm__ volatile(
+ "int $0x80"
+ : "=a"(ret)
+ : "a"(SYSCALL_RMDIR), "b"(path)
+ : "memory"
+ );
+ return __syscall_fix(ret);
+}
+
+static int sys_pipe2(int fds[2], uint32_t flags) {
+ int ret;
+ __asm__ volatile(
+ "int $0x80"
+ : "=a"(ret)
+ : "a"(SYSCALL_PIPE2), "b"(fds), "c"(flags)
+ : "memory"
+ );
+ return __syscall_fix(ret);
+}
+
+static int sys_chdir(const char* path) {
+ int ret;
+ __asm__ volatile(
+ "int $0x80"
+ : "=a"(ret)
+ : "a"(SYSCALL_CHDIR), "b"(path)
+ : "memory"
+ );
+ return __syscall_fix(ret);
+}
+
+static int sys_getcwd(char* buf, uint32_t size) {
+ int ret;
+ __asm__ volatile(
+ "int $0x80"
+ : "=a"(ret)
+ : "a"(SYSCALL_GETCWD), "b"(buf), "c"(size)
+ : "memory"
+ );
+ return __syscall_fix(ret);
+}
+
+static int sys_fcntl(int fd, int cmd, uint32_t arg) {
+ int ret;
+ __asm__ volatile(
+ "int $0x80"
+ : "=a"(ret)
+ : "a"(SYSCALL_FCNTL), "b"(fd), "c"(cmd), "d"(arg)
+ : "memory"
+ );
+ return __syscall_fix(ret);
+}
+
+static int sys_ioctl(int fd, uint32_t cmd, void* arg);
+
+static int isatty_fd(int fd) {
+ struct termios t;
+ if (sys_ioctl(fd, TCGETS, &t) < 0) {
+ if (errno == ENOTTY) return 0;
+ return -1;
+ }
+ return 1;
+}
+
+static int sys_getdents(int fd, void* buf, uint32_t len) {
+ int ret;
+ __asm__ volatile(
+ "int $0x80"
+ : "=a"(ret)
+ : "a"(SYSCALL_GETDENTS), "b"(fd), "c"(buf), "d"(len)
+ : "memory"
+ );
+ return __syscall_fix(ret);
+}
+
+static void write_int_dec(int v) {
+ char buf[16];
+ int i = 0;
+ if (v == 0) {
+ buf[i++] = '0';
+ } else {
+ int neg = 0;
+ if (v < 0) {
+ neg = 1;
+ v = -v;
+ }
+ while (v > 0 && i < (int)sizeof(buf)) {
+ buf[i++] = (char)('0' + (v % 10));
+ v /= 10;
+ }
+ if (neg && i < (int)sizeof(buf)) {
+ buf[i++] = '-';
+ }
+ for (int j = 0; j < i / 2; j++) {
+ char t = buf[j];
+ buf[j] = buf[i - 1 - j];
+ buf[i - 1 - j] = t;
+ }
+ }
+ (void)sys_write(1, buf, (uint32_t)i);
+}
+
+static void write_hex8(uint8_t v) {
+ static const char hex[] = "0123456789ABCDEF";
+ char b[2];
+ b[0] = hex[(v >> 4) & 0xF];
+ b[1] = hex[v & 0xF];
+ (void)sys_write(1, b, 2);
+}
+
+static void write_hex32(uint32_t v) {
+ static const char hex[] = "0123456789ABCDEF";
+ char b[8];
+ for (int i = 0; i < 8; i++) {
+ uint32_t shift = (uint32_t)(28 - 4 * i);
+ b[i] = hex[(v >> shift) & 0xFU];
+ }
+ (void)sys_write(1, b, 8);
+}
+
+static int memeq(const void* a, const void* b, uint32_t n) {
+ const uint8_t* x = (const uint8_t*)a;
+ const uint8_t* y = (const uint8_t*)b;
+ for (uint32_t i = 0; i < n; i++) {
+ if (x[i] != y[i]) return 0;
+ }
+ return 1;
+}
+
+static int streq(const char* a, const char* b) {
+ if (!a || !b) return 0;
+ uint32_t i = 0;
+ while (a[i] != 0 && b[i] != 0) {
+ if (a[i] != b[i]) return 0;
+ i++;
+ }
+ return a[i] == b[i];
+}
+
+static int sys_sigaction2(int sig, const struct sigaction* act, struct sigaction* oldact) {
+ int ret;
+ __asm__ volatile(
+ "int $0x80"
+ : "=a"(ret)
+ : "a"(SYSCALL_SIGACTION), "b"(sig), "c"(act), "d"(oldact)
+ : "memory"
+ );
+ return __syscall_fix(ret);
+}
+
+static int sys_sigaction(int sig, void (*handler)(int), uintptr_t* old_out) {
+ struct sigaction act;
+ act.sa_handler = (uintptr_t)handler;
+ act.sa_sigaction = 0;
+ act.sa_mask = 0;
+ act.sa_flags = 0;
+
+ struct sigaction oldact;
+ struct sigaction* oldp = old_out ? &oldact : 0;
+
+ int r = sys_sigaction2(sig, &act, oldp);
+ if (r < 0) return r;
+ if (old_out) {
+ *old_out = oldact.sa_handler;
+ }
+ return 0;
+}
+
+static int sys_select(uint32_t nfds, uint64_t* readfds, uint64_t* writefds, uint64_t* exceptfds, int32_t timeout) {
+ int ret;
+ __asm__ volatile(
+ "int $0x80"
+ : "=a"(ret)
+ : "a"(SYSCALL_SELECT), "b"(nfds), "c"(readfds), "d"(writefds), "S"(exceptfds), "D"(timeout)
+ : "memory"
+ );
+ return __syscall_fix(ret);
+}
+
+static int sys_ioctl(int fd, uint32_t cmd, void* arg) {
+ int ret;
+ __asm__ volatile(
+ "int $0x80"
+ : "=a"(ret)
+ : "a"(SYSCALL_IOCTL), "b"(fd), "c"(cmd), "d"(arg)
+ : "memory"
+ );
+ return __syscall_fix(ret);
+}
+
+static int sys_kill(int pid, int sig) {
+ int ret;
+ __asm__ volatile(
+ "int $0x80"
+ : "=a"(ret)
+ : "a"(SYSCALL_KILL), "b"(pid), "c"(sig)
+ : "memory"
+ );
+ return __syscall_fix(ret);
+}
+
+static int sys_poll(struct pollfd* fds, uint32_t nfds, int32_t timeout) {
+ int ret;
+ __asm__ volatile(
+ "int $0x80"
+ : "=a"(ret)
+ : "a"(SYSCALL_POLL), "b"(fds), "c"(nfds), "d"(timeout)
+ : "memory"
+ );
+ return __syscall_fix(ret);
+}
+
+static int sys_setsid(void) {
+ int ret;
+ __asm__ volatile(
+ "int $0x80"
+ : "=a"(ret)
+ : "a"(SYSCALL_SETSID)
+ : "memory"
+ );
+ return __syscall_fix(ret);
+}
+
+static int sys_setpgid(int pid, int pgid) {
+ int ret;
+ __asm__ volatile(
+ "int $0x80"
+ : "=a"(ret)
+ : "a"(SYSCALL_SETPGID), "b"(pid), "c"(pgid)
+ : "memory"
+ );
+ return __syscall_fix(ret);
+}
+
+static int sys_getpgrp(void) {
+ int ret;
+ __asm__ volatile(
+ "int $0x80"
+ : "=a"(ret)
+ : "a"(SYSCALL_GETPGRP)
+ : "memory"
+ );
+ return __syscall_fix(ret);
+}
+
+static int sys_getpid(void) {
+ int ret;
+ __asm__ volatile(
+ "int $0x80"
+ : "=a"(ret)
+ : "a"(SYSCALL_GETPID)
+ : "memory"
+ );
+ return __syscall_fix(ret);
+}
+
+static int sys_getppid(void) {
+ int ret;
+ __asm__ volatile(
+ "int $0x80"
+ : "=a"(ret)
+ : "a"(SYSCALL_GETPPID)
+ : "memory"
+ );
+ return __syscall_fix(ret);
+}
+
+static int sys_fork(void) {
+ int ret;
+ __asm__ volatile(
+ "int $0x80"
+ : "=a"(ret)
+ : "a"(SYSCALL_FORK)
+ : "memory"
+ );
+ return __syscall_fix(ret);
+}
+
+static int sys_execve(const char* path, const char* const* argv, const char* const* envp) {
+ int ret;
+ __asm__ volatile(
+ "int $0x80"
+ : "=a"(ret)
+ : "a"(SYSCALL_EXECVE), "b"(path), "c"(argv), "d"(envp)
+ : "memory"
+ );
+ return __syscall_fix(ret);
+}
+
+static int sys_pipe(int fds[2]) {
+ int ret;
+ __asm__ volatile(
+ "int $0x80"
+ : "=a"(ret)
+ : "a"(SYSCALL_PIPE), "b"(fds)
+ : "memory"
+ );
+ return __syscall_fix(ret);
+}
+
+static int sys_dup(int oldfd) {
+ int ret;
+ __asm__ volatile(
+ "int $0x80"
+ : "=a"(ret)
+ : "a"(SYSCALL_DUP), "b"(oldfd)
+ : "memory"
+ );
+ return __syscall_fix(ret);
+}
+
+static int sys_dup2(int oldfd, int newfd) {
+ int ret;
+ __asm__ volatile(
+ "int $0x80"
+ : "=a"(ret)
+ : "a"(SYSCALL_DUP2), "b"(oldfd), "c"(newfd)
+ : "memory"
+ );
+ return __syscall_fix(ret);
+}
+
+static int sys_dup3(int oldfd, int newfd, uint32_t flags) {
+ int ret;
+ __asm__ volatile(
+ "int $0x80"
+ : "=a"(ret)
+ : "a"(SYSCALL_DUP3), "b"(oldfd), "c"(newfd), "d"(flags)
+ : "memory"
+ );
+ return __syscall_fix(ret);
+}
+
+static int sys_waitpid(int pid, int* status, uint32_t options) {
+ int ret;
+ __asm__ volatile(
+ "int $0x80"
+ : "=a"(ret)
+ : "a"(SYSCALL_WAITPID), "b"(pid), "c"(status), "d"(options)
+ : "memory"
+ );
+ return __syscall_fix(ret);
+}
+
+static int sys_open(const char* path, uint32_t flags) {
+ int ret;
+ __asm__ volatile(
+ "int $0x80"
+ : "=a"(ret)
+ : "a"(SYSCALL_OPEN), "b"(path), "c"(flags)
+ : "memory"
+ );
+ return __syscall_fix(ret);
+}
+
+static int sys_mkdir(const char* path) {
+ int ret;
+ __asm__ volatile(
+ "int $0x80"
+ : "=a"(ret)
+ : "a"(SYSCALL_MKDIR), "b"(path)
+ : "memory"
+ );
+ return __syscall_fix(ret);
+}
+
+static int sys_unlink(const char* path) {
+ int ret;
+ __asm__ volatile(
+ "int $0x80"
+ : "=a"(ret)
+ : "a"(SYSCALL_UNLINK), "b"(path)
+ : "memory"
+ );
+ return __syscall_fix(ret);
+}
+
+static int sys_read(int fd, void* buf, uint32_t len) {
+ int ret;
+ __asm__ volatile(
+ "int $0x80"
+ : "=a"(ret)
+ : "a"(SYSCALL_READ), "b"(fd), "c"(buf), "d"(len)
+ : "memory"
+ );
+ return __syscall_fix(ret);
+}
+
+static int sys_close(int fd) {
+ int ret;
+ __asm__ volatile(
+ "int $0x80"
+ : "=a"(ret)
+ : "a"(SYSCALL_CLOSE), "b"(fd)
+ : "memory"
+ );
+ return __syscall_fix(ret);
+}
+
+static int sys_lseek(int fd, int32_t offset, int whence) {
+ int ret;
+ __asm__ volatile(
+ "int $0x80"
+ : "=a"(ret)
+ : "a"(SYSCALL_LSEEK), "b"(fd), "c"(offset), "d"(whence)
+ : "memory"
+ );
+ return __syscall_fix(ret);
+}
+
+static int sys_fstat(int fd, struct stat* st) {
+ int ret;
+ __asm__ volatile(
+ "int $0x80"
+ : "=a"(ret)
+ : "a"(SYSCALL_FSTAT), "b"(fd), "c"(st)
+ : "memory"
+ );
+ return __syscall_fix(ret);
+}
+
+static int sys_stat(const char* path, struct stat* st) {
+ int ret;
+ __asm__ volatile(
+ "int $0x80"
+ : "=a"(ret)
+ : "a"(SYSCALL_STAT), "b"(path), "c"(st)
+ : "memory"
+ );
+ return __syscall_fix(ret);
+}
+
+static uintptr_t sys_brk(uintptr_t addr) {
+ uintptr_t ret;
+ __asm__ volatile(
+ "int $0x80"
+ : "=a"(ret)
+ : "a"(SYSCALL_BRK), "b"(addr)
+ : "memory"
+ );
+ return ret;
+}
+
+static uintptr_t sys_mmap(uintptr_t addr, uint32_t len, uint32_t prot, uint32_t flags, int fd) {
+ uintptr_t ret;
+ __asm__ volatile(
+ "int $0x80"
+ : "=a"(ret)
+ : "a"(SYSCALL_MMAP), "b"(addr), "c"(len), "d"(prot), "S"(flags), "D"(fd)
+ : "memory"
+ );
+ return ret;
+}
+
+static int sys_munmap(uintptr_t addr, uint32_t len) {
+ int ret;
+ __asm__ volatile(
+ "int $0x80"
+ : "=a"(ret)
+ : "a"(SYSCALL_MUNMAP), "b"(addr), "c"(len)
+ : "memory"
+ );
+ return __syscall_fix(ret);
+}
+
+static int sys_clock_gettime(uint32_t clk_id, struct timespec* tp) {
+ int ret;
+ __asm__ volatile(
+ "int $0x80"
+ : "=a"(ret)
+ : "a"(SYSCALL_CLOCK_GETTIME), "b"(clk_id), "c"(tp)
+ : "memory"
+ );
+ return __syscall_fix(ret);
+}
+
+static int sys_pread(int fd, void* buf, uint32_t count, uint32_t offset) {
+ int ret;
+ __asm__ volatile(
+ "int $0x80"
+ : "=a"(ret)
+ : "a"(SYSCALL_PREAD), "b"(fd), "c"(buf), "d"(count), "S"(offset)
+ : "memory"
+ );
+ return __syscall_fix(ret);
+}
+
+static int sys_pwrite(int fd, const void* buf, uint32_t count, uint32_t offset) {
+ int ret;
+ __asm__ volatile(
+ "int $0x80"
+ : "=a"(ret)
+ : "a"(SYSCALL_PWRITE), "b"(fd), "c"(buf), "d"(count), "S"(offset)
+ : "memory"
+ );
+ return __syscall_fix(ret);
+}
+
+static int sys_ftruncate(int fd, uint32_t length) {
+ int ret;
+ __asm__ volatile(
+ "int $0x80"
+ : "=a"(ret)
+ : "a"(SYSCALL_FTRUNCATE), "b"(fd), "c"(length)
+ : "memory"
+ );
+ return __syscall_fix(ret);
+}
+
+static int sys_symlink(const char* target, const char* linkpath) {
+ int ret;
+ __asm__ volatile(
+ "int $0x80"
+ : "=a"(ret)
+ : "a"(SYSCALL_SYMLINK), "b"(target), "c"(linkpath)
+ : "memory"
+ );
+ return __syscall_fix(ret);
+}
+
+static int sys_readlink(const char* path, char* buf, uint32_t bufsiz) {
+ int ret;
+ __asm__ volatile(
+ "int $0x80"
+ : "=a"(ret)
+ : "a"(SYSCALL_READLINK), "b"(path), "c"(buf), "d"(bufsiz)
+ : "memory"
+ );
+ return __syscall_fix(ret);
+}
+
+static int sys_access(const char* path, uint32_t mode) {
+ int ret;
+ __asm__ volatile(
+ "int $0x80"
+ : "=a"(ret)
+ : "a"(SYSCALL_ACCESS), "b"(path), "c"(mode)
+ : "memory"
+ );
+ return __syscall_fix(ret);
+}
+
+static int sys_sigprocmask(int how, uint32_t mask, uint32_t* oldset) {
+ int ret;
+ __asm__ volatile(
+ "int $0x80"
+ : "=a"(ret)
+ : "a"(SYSCALL_SIGPROCMASK), "b"(how), "c"(mask), "d"(oldset)
+ : "memory"
+ );
+ return __syscall_fix(ret);
+}
+
+static int sys_sigpending(uint32_t* set) {
+ int ret;
+ __asm__ volatile(
+ "int $0x80"
+ : "=a"(ret)
+ : "a"(SYSCALL_SIGPENDING), "b"(set)
+ : "memory"
+ );
+ return __syscall_fix(ret);
+}
+
+static uint32_t sys_alarm(uint32_t seconds) {
+ uint32_t ret;
+ __asm__ volatile(
+ "int $0x80"
+ : "=a"(ret)
+ : "a"(SYSCALL_ALARM), "b"(seconds)
+ : "memory"
+ );
+ return ret;
+}
+
+static int sys_shmget(uint32_t key, uint32_t size, uint32_t flags) {
+ int ret;
+ __asm__ volatile(
+ "int $0x80"
+ : "=a"(ret)
+ : "a"(SYSCALL_SHMGET), "b"(key), "c"(size), "d"(flags)
+ : "memory"
+ );
+ return __syscall_fix(ret);
+}
+
+static uintptr_t sys_shmat(int shmid, uintptr_t addr, uint32_t flags) {
+ uintptr_t ret;
+ __asm__ volatile(
+ "int $0x80"
+ : "=a"(ret)
+ : "a"(SYSCALL_SHMAT), "b"(shmid), "c"(addr), "d"(flags)
+ : "memory"
+ );
+ return ret;
+}
+
+static int sys_shmdt(uintptr_t addr) {
+ int ret;
+ __asm__ volatile(
+ "int $0x80"
+ : "=a"(ret)
+ : "a"(SYSCALL_SHMDT), "b"(addr)
+ : "memory"
+ );
+ return __syscall_fix(ret);
+}
+
+static int sys_link(const char* oldpath, const char* newpath) {
+ int ret;
+ __asm__ volatile(
+ "int $0x80"
+ : "=a"(ret)
+ : "a"(SYSCALL_LINK), "b"(oldpath), "c"(newpath)
+ : "memory"
+ );
+ return __syscall_fix(ret);
+}
+
+static int sys_umask(uint32_t mask) {
+ int ret;
+ __asm__ volatile(
+ "int $0x80"
+ : "=a"(ret)
+ : "a"(SYSCALL_UMASK), "b"(mask)
+ : "memory"
+ );
+ return ret;
+}
+
+static int sys_setitimer(int which, const struct itimerval* newval, struct itimerval* oldval) {
+ int ret;
+ __asm__ volatile(
+ "int $0x80"
+ : "=a"(ret)
+ : "a"(SYSCALL_SETITIMER), "b"(which), "c"(newval), "d"(oldval)
+ : "memory"
+ );
+ return __syscall_fix(ret);
+}
+
+static int sys_getitimer(int which, struct itimerval* cur) {
+ int ret;
+ __asm__ volatile(
+ "int $0x80"
+ : "=a"(ret)
+ : "a"(SYSCALL_GETITIMER), "b"(which), "c"(cur)
+ : "memory"
+ );
+ return __syscall_fix(ret);
+}
+
+static int sys_waitid(uint32_t idtype, uint32_t id, void* infop, uint32_t options) {
+ int ret;
+ __asm__ volatile(
+ "int $0x80"
+ : "=a"(ret)
+ : "a"(SYSCALL_WAITID), "b"(idtype), "c"(id), "d"(infop), "S"(options)
+ : "memory"
+ );
+ return __syscall_fix(ret);
+}
+
+static int sys_epoll_create(int size) {
+ int ret;
+ __asm__ volatile(
+ "int $0x80"
+ : "=a"(ret)
+ : "a"(SYSCALL_EPOLL_CREATE), "b"(size)
+ : "memory"
+ );
+ return __syscall_fix(ret);
+}
+
+static int sys_epoll_ctl(int epfd, int op, int fd, void* event) {
+ int ret;
+ __asm__ volatile(
+ "int $0x80"
+ : "=a"(ret)
+ : "a"(SYSCALL_EPOLL_CTL), "b"(epfd), "c"(op), "d"(fd), "S"(event)
+ : "memory"
+ );
+ return __syscall_fix(ret);
+}
+
+static int sys_epoll_wait(int epfd, void* events, int maxevents, int timeout) {
+ int ret;
+ __asm__ volatile(
+ "int $0x80"
+ : "=a"(ret)
+ : "a"(SYSCALL_EPOLL_WAIT), "b"(epfd), "c"(events), "d"(maxevents), "S"(timeout)
+ : "memory"
+ );
+ return __syscall_fix(ret);
+}
+
+static int sys_inotify_init(void) {
+ int ret;
+ __asm__ volatile(
+ "int $0x80"
+ : "=a"(ret)
+ : "a"(SYSCALL_INOTIFY_INIT)
+ : "memory"
+ );
+ return __syscall_fix(ret);
+}
+
+static int sys_inotify_add_watch(int fd, const char* path, uint32_t mask) {
+ int ret;
+ __asm__ volatile(
+ "int $0x80"
+ : "=a"(ret)
+ : "a"(SYSCALL_INOTIFY_ADD_WATCH), "b"(fd), "c"(path), "d"(mask)
+ : "memory"
+ );
+ return __syscall_fix(ret);
+}
+
+static int sys_inotify_rm_watch(int fd, int wd) {
+ int ret;
+ __asm__ volatile(
+ "int $0x80"
+ : "=a"(ret)
+ : "a"(SYSCALL_INOTIFY_RM_WATCH), "b"(fd), "c"(wd)
+ : "memory"
+ );
+ return __syscall_fix(ret);
+}
+
+struct aiocb {
+ int aio_fildes;
+ void* aio_buf;
+ uint32_t aio_nbytes;
+ uint32_t aio_offset;
+ int32_t aio_error;
+ int32_t aio_return;
+};
+
+static int sys_aio_read(struct aiocb* cb) {
+ int ret;
+ __asm__ volatile(
+ "int $0x80"
+ : "=a"(ret)
+ : "a"(SYSCALL_AIO_READ), "b"(cb)
+ : "memory"
+ );
+ return __syscall_fix(ret);
+}
+
+static int sys_aio_write(struct aiocb* cb) {
+ int ret;
+ __asm__ volatile(
+ "int $0x80"
+ : "=a"(ret)
+ : "a"(SYSCALL_AIO_WRITE), "b"(cb)
+ : "memory"
+ );
+ return __syscall_fix(ret);
+}
+
+static int sys_aio_error(struct aiocb* cb) {
+ int ret;
+ __asm__ volatile(
+ "int $0x80"
+ : "=a"(ret)
+ : "a"(SYSCALL_AIO_ERROR), "b"(cb)
+ : "memory"
+ );
+ return ret;
+}
+
+static int sys_aio_return(struct aiocb* cb) {
+ int ret;
+ __asm__ volatile(
+ "int $0x80"
+ : "=a"(ret)
+ : "a"(SYSCALL_AIO_RETURN), "b"(cb)
+ : "memory"
+ );
+ return ret;
+}
+
+static int sys_nanosleep(const struct timespec* req, struct timespec* rem) {
+ int ret;
+ __asm__ volatile(
+ "int $0x80"
+ : "=a"(ret)
+ : "a"(SYSCALL_NANOSLEEP), "b"(req), "c"(rem)
+ : "memory"
+ );
+ return __syscall_fix(ret);
+}
+
+static uint32_t sys_getuid(void) {
+ uint32_t ret;
+ __asm__ volatile("int $0x80" : "=a"(ret) : "a"(SYSCALL_GETUID) : "memory");
+ return ret;
+}
+
+static uint32_t sys_getgid(void) {
+ uint32_t ret;
+ __asm__ volatile("int $0x80" : "=a"(ret) : "a"(SYSCALL_GETGID) : "memory");
+ return ret;
+}
+
+static uint32_t sys_geteuid(void) {
+ uint32_t ret;
+ __asm__ volatile("int $0x80" : "=a"(ret) : "a"(SYSCALL_GETEUID) : "memory");
+ return ret;
+}
+
+static uint32_t sys_getegid(void) {
+ uint32_t ret;
+ __asm__ volatile("int $0x80" : "=a"(ret) : "a"(SYSCALL_GETEGID) : "memory");
+ return ret;
+}
+
+static int sys_gettid(void) {
+ int ret;
+ __asm__ volatile("int $0x80" : "=a"(ret) : "a"(SYSCALL_GETTID) : "memory");
+ return ret;
+}
+
+static int sys_fsync(int fd) {
+ int ret;
+ __asm__ volatile(
+ "int $0x80"
+ : "=a"(ret)
+ : "a"(SYSCALL_FSYNC), "b"(fd)
+ : "memory"
+ );
+ return __syscall_fix(ret);
+}
+
+static int sys_truncate(const char* path, uint32_t length) {
+ int ret;
+ __asm__ volatile(
+ "int $0x80"
+ : "=a"(ret)
+ : "a"(SYSCALL_TRUNCATE), "b"(path), "c"(length)
+ : "memory"
+ );
+ return __syscall_fix(ret);
+}
+
+static int sys_chmod(const char* path, uint32_t mode) {
+ int ret;
+ __asm__ volatile(
+ "int $0x80"
+ : "=a"(ret)
+ : "a"(SYSCALL_CHMOD), "b"(path), "c"(mode)
+ : "memory"
+ );
+ return __syscall_fix(ret);
+}
+
+static int sys_flock(int fd, int operation) {
+ int ret;
+ __asm__ volatile(
+ "int $0x80"
+ : "=a"(ret)
+ : "a"(SYSCALL_FLOCK), "b"(fd), "c"(operation)
+ : "memory"
+ );
+ return __syscall_fix(ret);
+}
+
+struct iovec {
+ void* iov_base;
+ uint32_t iov_len;
+};
+
+static int sys_writev(int fd, const struct iovec* iov, int iovcnt) {
+ int ret;
+ __asm__ volatile(
+ "int $0x80"
+ : "=a"(ret)
+ : "a"(SYSCALL_WRITEV), "b"(fd), "c"(iov), "d"(iovcnt)
+ : "memory"
+ );
+ return __syscall_fix(ret);
+}
+
+static int sys_readv(int fd, const struct iovec* iov, int iovcnt) {
+ int ret;
+ __asm__ volatile(
+ "int $0x80"
+ : "=a"(ret)
+ : "a"(SYSCALL_READV), "b"(fd), "c"(iov), "d"(iovcnt)
+ : "memory"
+ );
+ return __syscall_fix(ret);
+}
+
+static uint32_t sys_times(void* buf) {
+ uint32_t ret;
+ __asm__ volatile(
+ "int $0x80"
+ : "=a"(ret)
+ : "a"(SYSCALL_TIMES), "b"(buf)
+ : "memory"
+ );
+ return ret;
+}
+
+static int sys_posix_spawn(uint32_t* pid_out, const char* path,
+ const char* const* argv, const char* const* envp) {
+ int ret;
+ __asm__ volatile(
+ "int $0x80"
+ : "=a"(ret)
+ : "a"(SYSCALL_POSIX_SPAWN), "b"(pid_out), "c"(path), "d"(argv), "S"(envp)
+ : "memory"
+ );
+ return __syscall_fix(ret);
+}
+
+static int sys_setuid(uint32_t uid) {
+ int ret;
+ __asm__ volatile("int $0x80" : "=a"(ret) : "a"(SYSCALL_SETUID), "b"(uid) : "memory");
+ return __syscall_fix(ret);
+}
+
+static int sys_setgid(uint32_t gid) {
+ int ret;
+ __asm__ volatile("int $0x80" : "=a"(ret) : "a"(SYSCALL_SETGID), "b"(gid) : "memory");
+ return __syscall_fix(ret);
+}
+
+static int sys_seteuid(uint32_t euid) {
+ int ret;
+ __asm__ volatile("int $0x80" : "=a"(ret) : "a"(SYSCALL_SETEUID), "b"(euid) : "memory");
+ return __syscall_fix(ret);
+}
+
+static int sys_setegid(uint32_t egid) {
+ int ret;
+ __asm__ volatile("int $0x80" : "=a"(ret) : "a"(SYSCALL_SETEGID), "b"(egid) : "memory");
+ return __syscall_fix(ret);
+}
+
+static int sys_sigsuspend(const uint32_t* mask) {
+ int ret;
+ __asm__ volatile("int $0x80" : "=a"(ret) : "a"(SYSCALL_SIGSUSPEND), "b"(mask) : "memory");
+ return __syscall_fix(ret);
+}
+
+static int sys_gettimeofday(struct timeval* tv) {
+ int ret;
+ __asm__ volatile("int $0x80" : "=a"(ret) : "a"(SYSCALL_GETTIMEOFDAY), "b"(tv), "c"(0) : "memory");
+ return __syscall_fix(ret);
+}
+
+static int sys_mprotect(uintptr_t addr, uint32_t len, uint32_t prot) {
+ int ret;
+ __asm__ volatile("int $0x80" : "=a"(ret) : "a"(SYSCALL_MPROTECT), "b"(addr), "c"(len), "d"(prot) : "memory");
+ return __syscall_fix(ret);
+}
+
+static int sys_madvise(uintptr_t addr, uint32_t len, uint32_t advice) {
+ int ret;
+ __asm__ volatile("int $0x80" : "=a"(ret) : "a"(SYSCALL_MADVISE), "b"(addr), "c"(len), "d"(advice) : "memory");
+ return __syscall_fix(ret);
+}
+
+static int sys_getrlimit(int resource, struct rlimit* rlim) {
+ int ret;
+ __asm__ volatile("int $0x80" : "=a"(ret) : "a"(SYSCALL_GETRLIMIT), "b"(resource), "c"(rlim) : "memory");
+ return __syscall_fix(ret);
+}
+
+struct utsname {
+ char sysname[65];
+ char nodename[65];
+ char release[65];
+ char version[65];
+ char machine[65];
+};
+
+static int sys_uname(struct utsname* buf) {
+ int ret;
+ __asm__ volatile("int $0x80" : "=a"(ret) : "a"(SYSCALL_UNAME), "b"(buf) : "memory");
+ return __syscall_fix(ret);
+}
+
+static int sys_setrlimit(int resource, const struct rlimit* rlim) {
+ int ret;
+ __asm__ volatile("int $0x80" : "=a"(ret) : "a"(SYSCALL_SETRLIMIT), "b"(resource), "c"(rlim) : "memory");
+ return __syscall_fix(ret);
+}
+
+__attribute__((noreturn)) static void sys_exit(int code) {
+ __asm__ volatile(
+ "int $0x80\n"
+ "1: jmp 1b\n"
+ :
+ : "a"(SYSCALL_EXIT), "b"(code)
+ : "memory"
+ );
+ for (;;) {
+ __asm__ volatile("hlt");
+ }
+}
+
+static volatile int got_usr1 = 0;
+static volatile int got_usr1_ret = 0;
+static volatile int got_ttin = 0;
+static volatile int got_ttou = 0;
+static volatile int got_alrm = 0;
+
+static void usr1_handler(int sig) {
+ (void)sig;
+ got_usr1 = 1;
+ sys_write(1, "[test] SIGUSR1 handler OK\n",
+ (uint32_t)(sizeof("[test] SIGUSR1 handler OK\n") - 1));
+}
+
+static void usr1_ret_handler(int sig) {
+ (void)sig;
+ got_usr1_ret = 1;
+}
+
+static void alrm_handler(int sig) {
+ (void)sig;
+ got_alrm = 1;
+}
+
+static void ttin_handler(int sig) {
+ (void)sig;
+ got_ttin = 1;
+}
+
+static void ttou_handler(int sig) {
+ (void)sig;
+ got_ttou = 1;
+}
+
+static void sigsegv_exit_handler(int sig) {
+ (void)sig;
+ static const char msg[] = "[test] SIGSEGV handler invoked\n";
+ (void)sys_write(1, msg, (uint32_t)(sizeof(msg) - 1));
+ sys_exit(0);
+}
+
+static void sigsegv_info_handler(int sig, siginfo_t* info, void* uctx) {
+ (void)uctx;
+ static const char msg[] = "[test] SIGSEGV siginfo handler invoked\n";
+ (void)sys_write(1, msg, (uint32_t)(sizeof(msg) - 1));
+ const uintptr_t expected = 0x12345000U;
+ if (sig == SIGSEGV && info && (uintptr_t)info->si_addr == expected) {
+ sys_exit(0);
+ }
+ sys_exit(1);
+}
+
+void _start(void) {
+ __asm__ volatile(
+ "mov $0x23, %ax\n"
+ "mov %ax, %ds\n"
+ "mov %ax, %es\n"
+ "mov %ax, %fs\n"
+ "mov %ax, %gs\n"
+ );
+
+ static const char msg[] = "[test] hello from init.elf\n";
+ (void)sys_write(1, msg, (uint32_t)(sizeof(msg) - 1));
+
+ static const char path[] = "/sbin/fulltest";
+
+ int fd = sys_open(path, 0);
+ if (fd < 0) {
+ sys_write(1, "[test] open failed fd=", (uint32_t)(sizeof("[test] open failed fd=") - 1));
+ write_int_dec(fd);
+ sys_write(1, "\n", 1);
+ sys_exit(1);
+ }
+
+ uint8_t hdr[4];
+ int rd = sys_read(fd, hdr, 4);
+ (void)sys_close(fd);
+ if (rd == 4 && hdr[0] == 0x7F && hdr[1] == 'E' && hdr[2] == 'L' && hdr[3] == 'F') {
+ sys_write(1, "[test] open/read/close OK (ELF magic)\n",
+ (uint32_t)(sizeof("[test] open/read/close OK (ELF magic)\n") - 1));
+ } else {
+ sys_write(1, "[test] read failed or bad header rd=", (uint32_t)(sizeof("[test] read failed or bad header rd=") - 1));
+ write_int_dec(rd);
+ sys_write(1, " hdr=", (uint32_t)(sizeof(" hdr=") - 1));
+ for (int i = 0; i < 4; i++) {
+ write_hex8(hdr[i]);
+ }
+ sys_write(1, "\n", 1);
+ sys_exit(1);
+ }
+
+ fd = sys_open("/sbin/fulltest", 0);
+ if (fd < 0) {
+ sys_write(1, "[test] overlay open failed\n",
+ (uint32_t)(sizeof("[test] overlay open failed\n") - 1));
+ sys_exit(1);
+ }
+
+ uint8_t orig0 = 0;
+ if (sys_lseek(fd, 0, SEEK_SET) < 0 || sys_read(fd, &orig0, 1) != 1) {
+ sys_write(1, "[test] overlay read failed\n",
+ (uint32_t)(sizeof("[test] overlay read failed\n") - 1));
+ sys_exit(1);
+ }
+
+ uint8_t x = (uint8_t)(orig0 ^ 0xFF);
+ if (sys_lseek(fd, 0, SEEK_SET) < 0 || sys_write(fd, &x, 1) != 1) {
+ sys_write(1, "[test] overlay write failed\n",
+ (uint32_t)(sizeof("[test] overlay write failed\n") - 1));
+ sys_exit(1);
+ }
+
+ if (sys_close(fd) < 0) {
+ sys_write(1, "[test] overlay close failed\n",
+ (uint32_t)(sizeof("[test] overlay close failed\n") - 1));
+ sys_exit(1);
+ }
+
+ fd = sys_open("/sbin/fulltest", 0);
+ if (fd < 0) {
+ sys_write(1, "[test] overlay open2 failed\n",
+ (uint32_t)(sizeof("[test] overlay open2 failed\n") - 1));
+ sys_exit(1);
+ }
+
+ uint8_t chk = 0;
+ if (sys_lseek(fd, 0, SEEK_SET) < 0 || sys_read(fd, &chk, 1) != 1 || chk != x) {
+ sys_write(1, "[test] overlay verify failed\n",
+ (uint32_t)(sizeof("[test] overlay verify failed\n") - 1));
+ sys_exit(1);
+ }
+
+ if (sys_lseek(fd, 0, SEEK_SET) < 0 || sys_write(fd, &orig0, 1) != 1) {
+ sys_write(1, "[test] overlay restore failed\n",
+ (uint32_t)(sizeof("[test] overlay restore failed\n") - 1));
+ sys_exit(1);
+ }
+
+ if (sys_close(fd) < 0) {
+ sys_write(1, "[test] overlay close2 failed\n",
+ (uint32_t)(sizeof("[test] overlay close2 failed\n") - 1));
+ sys_exit(1);
+ }
+
+ sys_write(1, "[test] overlay copy-up OK\n",
+ (uint32_t)(sizeof("[test] overlay copy-up OK\n") - 1));
+
+ fd = sys_open("/sbin/fulltest", 0);
+ if (fd < 0) {
+ sys_write(1, "[test] open2 failed\n", (uint32_t)(sizeof("[test] open2 failed\n") - 1));
+ sys_exit(1);
+ }
+
+ struct stat st;
+ if (sys_fstat(fd, &st) < 0) {
+ sys_write(1, "[test] fstat failed\n", (uint32_t)(sizeof("[test] fstat failed\n") - 1));
+ sys_exit(1);
+ }
+
+ if ((st.st_mode & S_IFMT) != S_IFREG || st.st_size == 0) {
+ sys_write(1, "[test] fstat bad\n", (uint32_t)(sizeof("[test] fstat bad\n") - 1));
+ sys_exit(1);
+ }
+
+ if (sys_lseek(fd, 0, SEEK_SET) < 0) {
+ sys_write(1, "[test] lseek set failed\n",
+ (uint32_t)(sizeof("[test] lseek set failed\n") - 1));
+ sys_exit(1);
+ }
+
+ uint8_t m2[4];
+ if (sys_read(fd, m2, 4) != 4) {
+ sys_write(1, "[test] read2 failed\n", (uint32_t)(sizeof("[test] read2 failed\n") - 1));
+ sys_exit(1);
+ }
+ if (m2[0] != 0x7F || m2[1] != 'E' || m2[2] != 'L' || m2[3] != 'F') {
+ sys_write(1, "[test] lseek/read mismatch\n",
+ (uint32_t)(sizeof("[test] lseek/read mismatch\n") - 1));
+ sys_exit(1);
+ }
+
+ if (sys_close(fd) < 0) {
+ sys_write(1, "[test] close2 failed\n", (uint32_t)(sizeof("[test] close2 failed\n") - 1));
+ sys_exit(1);
+ }
+
+ if (sys_stat("/sbin/fulltest", &st) < 0) {
+ sys_write(1, "[test] stat failed\n", (uint32_t)(sizeof("[test] stat failed\n") - 1));
+ sys_exit(1);
+ }
+ if ((st.st_mode & S_IFMT) != S_IFREG || st.st_size == 0) {
+ sys_write(1, "[test] stat bad\n", (uint32_t)(sizeof("[test] stat bad\n") - 1));
+ sys_exit(1);
+ }
+
+ sys_write(1, "[test] lseek/stat/fstat OK\n",
+ (uint32_t)(sizeof("[test] lseek/stat/fstat OK\n") - 1));
+
+ fd = sys_open("/tmp/hello.txt", 0);
+ if (fd < 0) {
+ sys_write(1, "[test] tmpfs open failed\n",
+ (uint32_t)(sizeof("[test] tmpfs open failed\n") - 1));
+ sys_exit(1);
+ }
+
+ if (sys_lseek(fd, 0, SEEK_END) < 0) {
+ sys_write(1, "[test] dup2 prep lseek failed\n",
+ (uint32_t)(sizeof("[test] dup2 prep lseek failed\n") - 1));
+ sys_exit(1);
+ }
+
+ if (sys_dup2(fd, 1) != 1) {
+ sys_write(1, "[test] dup2 failed\n", (uint32_t)(sizeof("[test] dup2 failed\n") - 1));
+ sys_exit(1);
+ }
+
+ (void)sys_close(fd);
+
+ {
+ static const char m[] = "[test] dup2 stdout->file OK\n";
+ if (sys_write(1, m, (uint32_t)(sizeof(m) - 1)) != (int)(sizeof(m) - 1)) {
+ sys_exit(1);
+ }
+ }
+
+ (void)sys_close(1);
+ sys_write(1, "[test] dup2 restore tty OK\n",
+ (uint32_t)(sizeof("[test] dup2 restore tty OK\n") - 1));
+
+ {
+ int pfds[2];
+ if (sys_pipe(pfds) < 0) {
+ sys_write(1, "[test] pipe failed\n", (uint32_t)(sizeof("[test] pipe failed\n") - 1));
+ sys_exit(1);
+ }
+
+ static const char pmsg[] = "pipe-test";
+ if (sys_write(pfds[1], pmsg, (uint32_t)(sizeof(pmsg) - 1)) != (int)(sizeof(pmsg) - 1)) {
+ sys_write(1, "[test] pipe write failed\n",
+ (uint32_t)(sizeof("[test] pipe write failed\n") - 1));
+ sys_exit(1);
+ }
+
+ char rbuf[16];
+ int prd = sys_read(pfds[0], rbuf, (uint32_t)(sizeof(pmsg) - 1));
+ if (prd != (int)(sizeof(pmsg) - 1)) {
+ sys_write(1, "[test] pipe read failed\n",
+ (uint32_t)(sizeof("[test] pipe read failed\n") - 1));
+ sys_exit(1);
+ }
+
+ int ok = 1;
+ for (uint32_t i = 0; i < (uint32_t)(sizeof(pmsg) - 1); i++) {
+ if ((uint8_t)rbuf[i] != (uint8_t)pmsg[i]) ok = 0;
+ }
+ if (!ok) {
+ sys_write(1, "[test] pipe mismatch\n",
+ (uint32_t)(sizeof("[test] pipe mismatch\n") - 1));
+ sys_exit(1);
+ }
+
+ if (sys_dup2(pfds[1], 1) != 1) {
+ sys_write(1, "[test] pipe dup2 failed\n",
+ (uint32_t)(sizeof("[test] pipe dup2 failed\n") - 1));
+ sys_exit(1);
+ }
+
+ static const char p2[] = "dup2-pipe";
+ if (sys_write(1, p2, (uint32_t)(sizeof(p2) - 1)) != (int)(sizeof(p2) - 1)) {
+ sys_exit(1);
+ }
+
+ int prd2 = sys_read(pfds[0], rbuf, (uint32_t)(sizeof(p2) - 1));
+ if (prd2 != (int)(sizeof(p2) - 1)) {
+ sys_write(1, "[test] pipe dup2 read failed\n",
+ (uint32_t)(sizeof("[test] pipe dup2 read failed\n") - 1));
+ sys_exit(1);
+ }
+ sys_write(1, "[test] pipe OK\n", (uint32_t)(sizeof("[test] pipe OK\n") - 1));
+
+ (void)sys_close(pfds[0]);
+ (void)sys_close(pfds[1]);
+
+ int tfd = sys_open("/dev/tty", 0);
+ if (tfd < 0) {
+ sys_write(1, "[test] /dev/tty open failed\n",
+ (uint32_t)(sizeof("[test] /dev/tty open failed\n") - 1));
+ sys_exit(1);
+ }
+ if (sys_dup2(tfd, 1) != 1) {
+ sys_write(1, "[test] dup2 restore tty failed\n",
+ (uint32_t)(sizeof("[test] dup2 restore tty failed\n") - 1));
+ sys_exit(1);
+ }
+ (void)sys_close(tfd);
+
+ }
+
+ {
+ int pid = sys_fork();
+ if (pid < 0) {
+ sys_write(1, "[test] kill test fork failed\n",
+ (uint32_t)(sizeof("[test] kill test fork failed\n") - 1));
+ sys_exit(1);
+ }
+
+ if (pid == 0) {
+ for (;;) {
+ __asm__ volatile("nop");
+ }
+ }
+
+ if (sys_kill(pid, SIGKILL) < 0) {
+ sys_write(1, "[test] kill(SIGKILL) failed\n",
+ (uint32_t)(sizeof("[test] kill(SIGKILL) failed\n") - 1));
+ sys_exit(1);
+ }
+
+ int st = 0;
+ int rp = sys_waitpid(pid, &st, 0);
+ if (rp != pid || st != (128 + SIGKILL)) {
+ sys_write(1, "[test] kill test waitpid mismatch\n",
+ (uint32_t)(sizeof("[test] kill test waitpid mismatch\n") - 1));
+ sys_exit(1);
+ }
+
+ sys_write(1, "[test] kill(SIGKILL) OK\n",
+ (uint32_t)(sizeof("[test] kill(SIGKILL) OK\n") - 1));
+ }
+
+ {
+ int fds[2];
+ if (sys_pipe(fds) < 0) {
+ sys_write(1, "[test] poll pipe setup failed\n",
+ (uint32_t)(sizeof("[test] poll pipe setup failed\n") - 1));
+ sys_exit(1);
+ }
+
+ struct pollfd p;
+ p.fd = fds[0];
+ p.events = POLLIN;
+ p.revents = 0;
+ int rc = sys_poll(&p, 1, 0);
+ if (rc != 0) {
+ sys_write(1, "[test] poll(pipe) expected 0\n",
+ (uint32_t)(sizeof("[test] poll(pipe) expected 0\n") - 1));
+ sys_exit(1);
+ }
+
+ static const char a = 'A';
+ if (sys_write(fds[1], &a, 1) != 1) {
+ sys_write(1, "[test] poll pipe write failed\n",
+ (uint32_t)(sizeof("[test] poll pipe write failed\n") - 1));
+ sys_exit(1);
+ }
+
+ p.revents = 0;
+ rc = sys_poll(&p, 1, 0);
+ if (rc != 1 || (p.revents & POLLIN) == 0) {
+ sys_write(1, "[test] poll(pipe) expected POLLIN\n",
+ (uint32_t)(sizeof("[test] poll(pipe) expected POLLIN\n") - 1));
+ sys_exit(1);
+ }
+
+ (void)sys_close(fds[0]);
+ (void)sys_close(fds[1]);
+ sys_write(1, "[test] poll(pipe) OK\n", (uint32_t)(sizeof("[test] poll(pipe) OK\n") - 1));
+ }
+
+ {
+ int fds[2];
+ if (sys_pipe(fds) < 0) {
+ sys_write(1, "[test] select pipe setup failed\n",
+ (uint32_t)(sizeof("[test] select pipe setup failed\n") - 1));
+ sys_exit(1);
+ }
+
+ uint64_t r = 0;
+ uint64_t w = 0;
+ r |= (1ULL << (uint32_t)fds[0]);
+ int rc = sys_select((uint32_t)(fds[0] + 1), &r, &w, 0, 0);
+ if (rc != 0) {
+ sys_write(1, "[test] select(pipe) expected 0\n",
+ (uint32_t)(sizeof("[test] select(pipe) expected 0\n") - 1));
+ sys_exit(1);
+ }
+
+ static const char a = 'B';
+ if (sys_write(fds[1], &a, 1) != 1) {
+ sys_write(1, "[test] select pipe write failed\n",
+ (uint32_t)(sizeof("[test] select pipe write failed\n") - 1));
+ sys_exit(1);
+ }
+
+ r = 0;
+ w = 0;
+ r |= (1ULL << (uint32_t)fds[0]);
+ rc = sys_select((uint32_t)(fds[0] + 1), &r, &w, 0, 0);
+ if (rc != 1 || ((r >> (uint32_t)fds[0]) & 1ULL) == 0) {
+ sys_write(1, "[test] select(pipe) expected readable\n",
+ (uint32_t)(sizeof("[test] select(pipe) expected readable\n") - 1));
+ sys_exit(1);
+ }
+
+ (void)sys_close(fds[0]);
+ (void)sys_close(fds[1]);
+ sys_write(1, "[test] select(pipe) OK\n",
+ (uint32_t)(sizeof("[test] select(pipe) OK\n") - 1));
+ }
+
+ {
+ int fd = sys_open("/dev/tty", 0);
+ if (fd < 0) {
+ sys_write(1, "[test] ioctl(/dev/tty) open failed\n",
+ (uint32_t)(sizeof("[test] ioctl(/dev/tty) open failed\n") - 1));
+ sys_exit(1);
+ }
+
+ int fg = -1;
+ if (sys_ioctl(fd, TIOCGPGRP, &fg) < 0 || fg != 0) {
+ sys_write(1, "[test] ioctl TIOCGPGRP failed\n",
+ (uint32_t)(sizeof("[test] ioctl TIOCGPGRP failed\n") - 1));
+ sys_exit(1);
+ }
+
+ fg = 0;
+ if (sys_ioctl(fd, TIOCSPGRP, &fg) < 0) {
+ sys_write(1, "[test] ioctl TIOCSPGRP failed\n",
+ (uint32_t)(sizeof("[test] ioctl TIOCSPGRP failed\n") - 1));
+ sys_exit(1);
+ }
+
+ fg = 1;
+ if (sys_ioctl(fd, TIOCSPGRP, &fg) >= 0) {
+ sys_write(1, "[test] ioctl TIOCSPGRP expected fail\n",
+ (uint32_t)(sizeof("[test] ioctl TIOCSPGRP expected fail\n") - 1));
+ sys_exit(1);
+ }
+
+ struct termios oldt;
+ if (sys_ioctl(fd, TCGETS, &oldt) < 0) {
+ sys_write(1, "[test] ioctl TCGETS failed\n",
+ (uint32_t)(sizeof("[test] ioctl TCGETS failed\n") - 1));
+ sys_exit(1);
+ }
+
+ struct termios t = oldt;
+ t.c_lflag &= ~(uint32_t)(ECHO | ICANON);
+ if (sys_ioctl(fd, TCSETS, &t) < 0) {
+ sys_write(1, "[test] ioctl TCSETS failed\n",
+ (uint32_t)(sizeof("[test] ioctl TCSETS failed\n") - 1));
+ sys_exit(1);
+ }
+
+ struct termios chk;
+ if (sys_ioctl(fd, TCGETS, &chk) < 0) {
+ sys_write(1, "[test] ioctl TCGETS2 failed\n",
+ (uint32_t)(sizeof("[test] ioctl TCGETS2 failed\n") - 1));
+ sys_exit(1);
+ }
+
+ if ((chk.c_lflag & (uint32_t)(ECHO | ICANON)) != 0) {
+ sys_write(1, "[test] ioctl verify failed\n",
+ (uint32_t)(sizeof("[test] ioctl verify failed\n") - 1));
+ sys_exit(1);
+ }
+
+ (void)sys_ioctl(fd, TCSETS, &oldt);
+ (void)sys_close(fd);
+
+ sys_write(1, "[test] ioctl(/dev/tty) OK\n",
+ (uint32_t)(sizeof("[test] ioctl(/dev/tty) OK\n") - 1));
+ }
+
+ // A2: basic job control. A background pgrp read/write on controlling TTY should raise SIGTTIN/SIGTTOU.
+ {
+ int leader = sys_fork();
+ if (leader < 0) {
+ sys_write(1, "[test] fork(job control leader) failed\n",
+ (uint32_t)(sizeof("[test] fork(job control leader) failed\n") - 1));
+ sys_exit(1);
+ }
+ if (leader == 0) {
+ int me = sys_getpid();
+ int sid = sys_setsid();
+ if (sid != me) {
+ sys_write(1, "[test] setsid(job control) failed\n",
+ (uint32_t)(sizeof("[test] setsid(job control) failed\n") - 1));
+ sys_exit(1);
+ }
+
+ int tfd = sys_open("/dev/tty", 0);
+ if (tfd < 0) {
+ sys_write(1, "[test] open(/dev/tty) for job control failed\n",
+ (uint32_t)(sizeof("[test] open(/dev/tty) for job control failed\n") - 1));
+ sys_exit(1);
+ }
+
+ // Touch ioctl to make kernel acquire controlling session/pgrp.
+ int fg = 0;
+ (void)sys_ioctl(tfd, TIOCGPGRP, &fg);
+
+ fg = me;
+ if (sys_ioctl(tfd, TIOCSPGRP, &fg) < 0) {
+ sys_write(1, "[test] ioctl TIOCSPGRP(job control) failed\n",
+ (uint32_t)(sizeof("[test] ioctl TIOCSPGRP(job control) failed\n") - 1));
+ sys_exit(1);
+ }
+
+ int bg = sys_fork();
+ if (bg < 0) {
+ sys_write(1, "[test] fork(job control bg) failed\n",
+ (uint32_t)(sizeof("[test] fork(job control bg) failed\n") - 1));
+ sys_exit(1);
+ }
+ if (bg == 0) {
+ (void)sys_setpgid(0, me + 1);
+
+ (void)sys_sigaction(SIGTTIN, ttin_handler, 0);
+ (void)sys_sigaction(SIGTTOU, ttou_handler, 0);
+
+ uint8_t b = 0;
+ (void)sys_read(tfd, &b, 1);
+ if (!got_ttin) {
+ sys_write(1, "[test] SIGTTIN job control failed\n",
+ (uint32_t)(sizeof("[test] SIGTTIN job control failed\n") - 1));
+ sys_exit(1);
+ }
+
+ const char msg2[] = "x";
+ (void)sys_write(tfd, msg2, 1);
+ if (!got_ttou) {
+ sys_write(1, "[test] SIGTTOU job control failed\n",
+ (uint32_t)(sizeof("[test] SIGTTOU job control failed\n") - 1));
+ sys_exit(1);
+ }
+
+ sys_exit(0);
+ }
+
+ int st2 = 0;
+ int wp2 = sys_waitpid(bg, &st2, 0);
+ if (wp2 != bg || st2 != 0) {
+ sys_write(1, "[test] waitpid(job control bg) failed wp=", (uint32_t)(sizeof("[test] waitpid(job control bg) failed wp=") - 1));
+ write_int_dec(wp2);
+ sys_write(1, " st=", (uint32_t)(sizeof(" st=") - 1));
+ write_int_dec(st2);
+ sys_write(1, "\n", 1);
+ sys_exit(1);
+ }
+
+ (void)sys_close(tfd);
+ sys_exit(0);
+ }
+
+ int stL = 0;
+ int wpL = sys_waitpid(leader, &stL, 0);
+ if (wpL != leader || stL != 0) {
+ sys_write(1, "[test] waitpid(job control leader) failed wp=", (uint32_t)(sizeof("[test] waitpid(job control leader) failed wp=") - 1));
+ write_int_dec(wpL);
+ sys_write(1, " st=", (uint32_t)(sizeof(" st=") - 1));
+ write_int_dec(stL);
+ sys_write(1, "\n", 1);
+ sys_exit(1);
+ }
+
+ sys_write(1, "[test] job control (SIGTTIN/SIGTTOU) OK\n",
+ (uint32_t)(sizeof("[test] job control (SIGTTIN/SIGTTOU) OK\n") - 1));
+ }
+
+ {
+ int fd = sys_open("/dev/null", 0);
+ if (fd < 0) {
+ sys_write(1, "[test] poll(/dev/null) open failed\n",
+ (uint32_t)(sizeof("[test] poll(/dev/null) open failed\n") - 1));
+ sys_exit(1);
+ }
+ struct pollfd p;
+ p.fd = fd;
+ p.events = POLLOUT;
+ p.revents = 0;
+ int rc = sys_poll(&p, 1, 0);
+ if (rc != 1 || (p.revents & POLLOUT) == 0) {
+ sys_write(1, "[test] poll(/dev/null) expected POLLOUT\n",
+ (uint32_t)(sizeof("[test] poll(/dev/null) expected POLLOUT\n") - 1));
+ sys_exit(1);
+ }
+ (void)sys_close(fd);
+ sys_write(1, "[test] poll(/dev/null) OK\n",
+ (uint32_t)(sizeof("[test] poll(/dev/null) OK\n") - 1));
+ }
+
+ {
+ int mfd = sys_open("/dev/ptmx", 0);
+ int sfd = sys_open("/dev/pts/0", 0);
+ if (mfd < 0 || sfd < 0) {
+ sys_write(1, "[test] pty open failed\n",
+ (uint32_t)(sizeof("[test] pty open failed\n") - 1));
+ sys_exit(1);
+ }
+
+ static const char m2s[] = "m2s";
+ if (sys_write(mfd, m2s, (uint32_t)(sizeof(m2s) - 1)) != (int)(sizeof(m2s) - 1)) {
+ sys_write(1, "[test] pty write master failed\n",
+ (uint32_t)(sizeof("[test] pty write master failed\n") - 1));
+ sys_exit(1);
+ }
+
+ struct pollfd p;
+ p.fd = sfd;
+ p.events = POLLIN;
+ p.revents = 0;
+ int rc = sys_poll(&p, 1, 50);
+ if (rc != 1 || (p.revents & POLLIN) == 0) {
+ sys_write(1, "[test] pty poll slave failed\n",
+ (uint32_t)(sizeof("[test] pty poll slave failed\n") - 1));
+ sys_exit(1);
+ }
+
+ char buf[8];
+ int rd = sys_read(sfd, buf, (uint32_t)(sizeof(m2s) - 1));
+ if (rd != (int)(sizeof(m2s) - 1) || !memeq(buf, m2s, (uint32_t)(sizeof(m2s) - 1))) {
+ sys_write(1, "[test] pty read slave failed\n",
+ (uint32_t)(sizeof("[test] pty read slave failed\n") - 1));
+ sys_exit(1);
+ }
+
+ static const char s2m[] = "s2m";
+ if (sys_write(sfd, s2m, (uint32_t)(sizeof(s2m) - 1)) != (int)(sizeof(s2m) - 1)) {
+ sys_write(1, "[test] pty write slave failed\n",
+ (uint32_t)(sizeof("[test] pty write slave failed\n") - 1));
+ sys_exit(1);
+ }
+
+ p.fd = mfd;
+ p.events = POLLIN;
+ p.revents = 0;
+ rc = sys_poll(&p, 1, 50);
+ if (rc != 1 || (p.revents & POLLIN) == 0) {
+ sys_write(1, "[test] pty poll master failed\n",
+ (uint32_t)(sizeof("[test] pty poll master failed\n") - 1));
+ sys_exit(1);
+ }
+
+ rd = sys_read(mfd, buf, (uint32_t)(sizeof(s2m) - 1));
+ if (rd != (int)(sizeof(s2m) - 1) || !memeq(buf, s2m, (uint32_t)(sizeof(s2m) - 1))) {
+ sys_write(1, "[test] pty read master failed\n",
+ (uint32_t)(sizeof("[test] pty read master failed\n") - 1));
+ sys_exit(1);
+ }
+
+ (void)sys_close(mfd);
+ (void)sys_close(sfd);
+ sys_write(1, "[test] pty OK\n", (uint32_t)(sizeof("[test] pty OK\n") - 1));
+ }
+
+ {
+ sys_write(1, "[test] setsid test: before fork\n",
+ (uint32_t)(sizeof("[test] setsid test: before fork\n") - 1));
+ int pid = sys_fork();
+ if (pid < 0) {
+ static const char smsg[] = "[test] fork failed\n";
+ (void)sys_write(1, smsg, (uint32_t)(sizeof(smsg) - 1));
+ sys_exit(2);
+ }
+ if (pid == 0) {
+ sys_write(1, "[test] setsid test: child start\n",
+ (uint32_t)(sizeof("[test] setsid test: child start\n") - 1));
+ int me = sys_getpid();
+ int sid = sys_setsid();
+ if (sid != me) sys_exit(2);
+
+ int pg = sys_getpgrp();
+ if (pg != me) sys_exit(3);
+
+ int newpg = me + 1;
+ if (sys_setpgid(0, newpg) < 0) sys_exit(4);
+ if (sys_getpgrp() != newpg) sys_exit(5);
+
+ sys_exit(0);
+ }
+
+ sys_write(1, "[test] setsid test: parent waitpid\n",
+ (uint32_t)(sizeof("[test] setsid test: parent waitpid\n") - 1));
+ int st = 0;
+ int wp = sys_waitpid(pid, &st, 0);
+ if (wp != pid || st != 0) {
+ sys_write(1, "[test] setsid/setpgid/getpgrp failed\n",
+ (uint32_t)(sizeof("[test] setsid/setpgid/getpgrp failed\n") - 1));
+ sys_exit(1);
+ }
+
+ sys_write(1, "[test] setsid/setpgid/getpgrp OK\n",
+ (uint32_t)(sizeof("[test] setsid/setpgid/getpgrp OK\n") - 1));
+ }
+
+ {
+ uintptr_t oldh = 0;
+ if (sys_sigaction(SIGUSR1, usr1_handler, &oldh) < 0) {
+ sys_write(1, "[test] sigaction failed\n",
+ (uint32_t)(sizeof("[test] sigaction failed\n") - 1));
+ sys_exit(1);
+ }
+
+ int me = sys_getpid();
+ if (sys_kill(me, SIGUSR1) < 0) {
+ sys_write(1, "[test] kill(SIGUSR1) failed\n",
+ (uint32_t)(sizeof("[test] kill(SIGUSR1) failed\n") - 1));
+ sys_exit(1);
+ }
+
+ for (uint32_t i = 0; i < 2000000U; i++) {
+ if (got_usr1) break;
+ }
+
+ if (!got_usr1) {
+ sys_write(1, "[test] SIGUSR1 not delivered\n",
+ (uint32_t)(sizeof("[test] SIGUSR1 not delivered\n") - 1));
+ sys_exit(1);
+ }
+
+ sys_write(1, "[test] sigaction/kill(SIGUSR1) OK\n",
+ (uint32_t)(sizeof("[test] sigaction/kill(SIGUSR1) OK\n") - 1));
+ }
+
+ // Verify that returning from a signal handler does not corrupt the user stack.
+ {
+ if (sys_sigaction(SIGUSR1, usr1_ret_handler, 0) < 0) {
+ sys_write(1, "[test] sigaction (sigreturn test) failed\n",
+ (uint32_t)(sizeof("[test] sigaction (sigreturn test) failed\n") - 1));
+ sys_exit(1);
+ }
+
+ volatile uint32_t canary = 0x11223344U;
+ int me = sys_getpid();
+ if (sys_kill(me, SIGUSR1) < 0) {
+ sys_write(1, "[test] kill(SIGUSR1) (sigreturn test) failed\n",
+ (uint32_t)(sizeof("[test] kill(SIGUSR1) (sigreturn test) failed\n") - 1));
+ sys_exit(1);
+ }
+
+ if (!got_usr1_ret) {
+ sys_write(1, "[test] SIGUSR1 not delivered (sigreturn test)\n",
+ (uint32_t)(sizeof("[test] SIGUSR1 not delivered (sigreturn test)\n") - 1));
+ sys_exit(1);
+ }
+
+ if (canary != 0x11223344U) {
+ sys_write(1, "[test] sigreturn test stack corruption\n",
+ (uint32_t)(sizeof("[test] sigreturn test stack corruption\n") - 1));
+ sys_exit(1);
+ }
+
+ sys_write(1, "[test] sigreturn OK\n",
+ (uint32_t)(sizeof("[test] sigreturn OK\n") - 1));
+ }
+
+ fd = sys_open("/tmp/hello.txt", 0);
+ if (fd < 0) {
+ sys_write(1, "[test] tmpfs open2 failed\n",
+ (uint32_t)(sizeof("[test] tmpfs open2 failed\n") - 1));
+ sys_exit(1);
+ }
+
+ if (sys_stat("/tmp/hello.txt", &st) < 0) {
+ sys_write(1, "[test] tmpfs stat failed\n",
+ (uint32_t)(sizeof("[test] tmpfs stat failed\n") - 1));
+ sys_exit(1);
+ }
+ if ((st.st_mode & S_IFMT) != S_IFREG) {
+ sys_write(1, "[test] tmpfs stat not reg\n",
+ (uint32_t)(sizeof("[test] tmpfs stat not reg\n") - 1));
+ sys_exit(1);
+ }
+ if (st.st_size == 0) {
+ sys_write(1, "[test] tmpfs stat size 0\n",
+ (uint32_t)(sizeof("[test] tmpfs stat size 0\n") - 1));
+ sys_exit(1);
+ }
+
+ struct stat fst;
+ if (sys_fstat(fd, &fst) < 0) {
+ sys_write(1, "[test] tmpfs fstat failed\n",
+ (uint32_t)(sizeof("[test] tmpfs fstat failed\n") - 1));
+ sys_exit(1);
+ }
+ if (fst.st_size != st.st_size) {
+ sys_write(1, "[test] tmpfs stat size mismatch\n",
+ (uint32_t)(sizeof("[test] tmpfs stat size mismatch\n") - 1));
+ sys_exit(1);
+ }
+
+ int end = sys_lseek(fd, 0, SEEK_END);
+ if (end < 0 || (uint32_t)end != st.st_size) {
+ sys_write(1, "[test] tmpfs lseek end bad\n",
+ (uint32_t)(sizeof("[test] tmpfs lseek end bad\n") - 1));
+ sys_exit(1);
+ }
+
+ uint8_t eofb;
+ if (sys_read(fd, &eofb, 1) != 0) {
+ sys_write(1, "[test] tmpfs eof read bad\n",
+ (uint32_t)(sizeof("[test] tmpfs eof read bad\n") - 1));
+ sys_exit(1);
+ }
+
+ if (sys_lseek(fd, 0, 999) >= 0) {
+ sys_write(1, "[test] tmpfs lseek whence bad\n",
+ (uint32_t)(sizeof("[test] tmpfs lseek whence bad\n") - 1));
+ sys_exit(1);
+ }
+
+ if (sys_lseek(fd, 0, SEEK_SET) < 0) {
+ sys_write(1, "[test] tmpfs lseek set failed\n",
+ (uint32_t)(sizeof("[test] tmpfs lseek set failed\n") - 1));
+ sys_exit(1);
+ }
+
+ uint8_t tbuf[6];
+ if (sys_read(fd, tbuf, 5) != 5) {
+ sys_write(1, "[test] tmpfs read failed\n",
+ (uint32_t)(sizeof("[test] tmpfs read failed\n") - 1));
+ sys_exit(1);
+ }
+ tbuf[5] = 0;
+ if (tbuf[0] != 'h' || tbuf[1] != 'e' || tbuf[2] != 'l' || tbuf[3] != 'l' || tbuf[4] != 'o') {
+ sys_write(1, "[test] tmpfs bad data\n", (uint32_t)(sizeof("[test] tmpfs bad data\n") - 1));
+ sys_exit(1);
+ }
+
+ if (sys_close(fd) < 0) {
+ sys_write(1, "[test] tmpfs close failed\n",
+ (uint32_t)(sizeof("[test] tmpfs close failed\n") - 1));
+ sys_exit(1);
+ }
+
+ if (sys_open("/tmp/does_not_exist", 0) >= 0) {
+ sys_write(1, "[test] tmpfs open nonexist bad\n",
+ (uint32_t)(sizeof("[test] tmpfs open nonexist bad\n") - 1));
+ sys_exit(1);
+ }
+
+ fd = sys_open("/tmp/hello.txt", 0);
+ if (fd < 0) {
+ sys_write(1, "[test] tmpfs open3 failed\n",
+ (uint32_t)(sizeof("[test] tmpfs open3 failed\n") - 1));
+ sys_exit(1);
+ }
+
+ if (sys_fstat(fd, &fst) < 0) {
+ sys_write(1, "[test] tmpfs fstat2 failed\n",
+ (uint32_t)(sizeof("[test] tmpfs fstat2 failed\n") - 1));
+ sys_exit(1);
+ }
+
+ if (sys_lseek(fd, 0, SEEK_END) < 0) {
+ sys_write(1, "[test] tmpfs lseek end2 failed\n",
+ (uint32_t)(sizeof("[test] tmpfs lseek end2 failed\n") - 1));
+ sys_exit(1);
+ }
+
+ char suf[3];
+ suf[0] = 'X';
+ suf[1] = 'Y';
+ suf[2] = 'Z';
+ if (sys_write(fd, suf, 3) != 3) {
+ sys_write(1, "[test] tmpfs write failed\n",
+ (uint32_t)(sizeof("[test] tmpfs write failed\n") - 1));
+ sys_exit(1);
+ }
+
+ if (sys_fstat(fd, &fst) < 0) {
+ sys_write(1, "[test] tmpfs fstat3 failed\n",
+ (uint32_t)(sizeof("[test] tmpfs fstat3 failed\n") - 1));
+ sys_exit(1);
+ }
+ if (fst.st_size != st.st_size + 3) {
+ sys_write(1, "[test] tmpfs size not grown\n",
+ (uint32_t)(sizeof("[test] tmpfs size not grown\n") - 1));
+ sys_exit(1);
+ }
+
+ if (sys_lseek(fd, -3, SEEK_END) < 0) {
+ sys_write(1, "[test] tmpfs lseek back failed\n",
+ (uint32_t)(sizeof("[test] tmpfs lseek back failed\n") - 1));
+ sys_exit(1);
+ }
+ uint8_t s2[3];
+ if (sys_read(fd, s2, 3) != 3 || s2[0] != 'X' || s2[1] != 'Y' || s2[2] != 'Z') {
+ sys_write(1, "[test] tmpfs suffix mismatch\n",
+ (uint32_t)(sizeof("[test] tmpfs suffix mismatch\n") - 1));
+ sys_exit(1);
+ }
+
+ if (sys_close(fd) < 0) {
+ sys_write(1, "[test] tmpfs close3 failed\n",
+ (uint32_t)(sizeof("[test] tmpfs close3 failed\n") - 1));
+ sys_exit(1);
+ }
+
+ sys_write(1, "[test] tmpfs/mount OK\n", (uint32_t)(sizeof("[test] tmpfs/mount OK\n") - 1));
+
+ {
+ int fd = sys_open("/dev/null", 0);
+ if (fd < 0) {
+ sys_write(1, "[test] /dev/null open failed\n",
+ (uint32_t)(sizeof("[test] /dev/null open failed\n") - 1));
+ sys_exit(1);
+ }
+ static const char z[] = "discard me";
+ if (sys_write(fd, z, (uint32_t)(sizeof(z) - 1)) != (int)(sizeof(z) - 1)) {
+ sys_write(1, "[test] /dev/null write failed\n",
+ (uint32_t)(sizeof("[test] /dev/null write failed\n") - 1));
+ sys_exit(1);
+ }
+ (void)sys_close(fd);
+ sys_write(1, "[test] /dev/null OK\n", (uint32_t)(sizeof("[test] /dev/null OK\n") - 1));
+ }
+
+ // B1: persistent storage smoke. Value should increment across reboots (disk.img).
+ {
+ int fd = sys_open("/persist/counter", 0);
+ if (fd < 0) {
+ sys_write(1, "[test] /persist/counter open failed\n",
+ (uint32_t)(sizeof("[test] /persist/counter open failed\n") - 1));
+ sys_exit(1);
+ }
+
+ (void)sys_lseek(fd, 0, SEEK_SET);
+ uint8_t b[4] = {0, 0, 0, 0};
+ int rd = sys_read(fd, b, 4);
+ if (rd != 4) {
+ sys_write(1, "[test] /persist/counter read failed\n",
+ (uint32_t)(sizeof("[test] /persist/counter read failed\n") - 1));
+ sys_exit(1);
+ }
+
+ uint32_t v = (uint32_t)b[0] | ((uint32_t)b[1] << 8) | ((uint32_t)b[2] << 16) | ((uint32_t)b[3] << 24);
+ v++;
+ b[0] = (uint8_t)(v & 0xFF);
+ b[1] = (uint8_t)((v >> 8) & 0xFF);
+ b[2] = (uint8_t)((v >> 16) & 0xFF);
+ b[3] = (uint8_t)((v >> 24) & 0xFF);
+
+ (void)sys_lseek(fd, 0, SEEK_SET);
+ int wr = sys_write(fd, b, 4);
+ if (wr != 4) {
+ sys_write(1, "[test] /persist/counter write failed\n",
+ (uint32_t)(sizeof("[test] /persist/counter write failed\n") - 1));
+ sys_exit(1);
+ }
+
+ (void)sys_close(fd);
+
+ sys_write(1, "[test] /persist/counter=", (uint32_t)(sizeof("[test] /persist/counter=") - 1));
+ write_int_dec((int)v);
+ sys_write(1, "\n", 1);
+ }
+
+ {
+ int fd = sys_open("/dev/tty", 0);
+ if (fd < 0) {
+ sys_write(1, "[test] /dev/tty open failed\n",
+ (uint32_t)(sizeof("[test] /dev/tty open failed\n") - 1));
+ sys_exit(1);
+ }
+ static const char m[] = "[test] /dev/tty write OK\n";
+ int wr = sys_write(fd, m, (uint32_t)(sizeof(m) - 1));
+ if (wr != (int)(sizeof(m) - 1)) {
+ sys_write(1, "[test] /dev/tty write failed\n",
+ (uint32_t)(sizeof("[test] /dev/tty write failed\n") - 1));
+ sys_exit(1);
+ }
+ (void)sys_close(fd);
+ }
+
+ // B2: on-disk general filesystem smoke (/disk)
+ {
+ int fd = sys_open("/disk/test", O_CREAT);
+ if (fd < 0) {
+ sys_write(1, "[test] /disk/test open failed\n",
+ (uint32_t)(sizeof("[test] /disk/test open failed\n") - 1));
+ sys_exit(1);
+ }
+
+ char buf[16];
+ int rd = sys_read(fd, buf, sizeof(buf));
+ int prev = 0;
+ if (rd > 0) {
+ for (int i = 0; i < rd; i++) {
+ if (buf[i] < '0' || buf[i] > '9') break;
+ prev = prev * 10 + (buf[i] - '0');
+ }
+ }
+
+ (void)sys_close(fd);
+
+ fd = sys_open("/disk/test", O_CREAT | O_TRUNC);
+ if (fd < 0) {
+ sys_write(1, "[test] /disk/test open2 failed\n",
+ (uint32_t)(sizeof("[test] /disk/test open2 failed\n") - 1));
+ sys_exit(1);
+ }
+
+ int next = prev + 1;
+ char out[16];
+ int n = 0;
+ int v = next;
+ if (v == 0) {
+ out[n++] = '0';
+ } else {
+ char tmp[16];
+ int t = 0;
+ while (v > 0 && t < (int)sizeof(tmp)) {
+ tmp[t++] = (char)('0' + (v % 10));
+ v /= 10;
+ }
+ while (t > 0) {
+ out[n++] = tmp[--t];
+ }
+ }
+
+ if (sys_write(fd, out, (uint32_t)n) != n) {
+ sys_write(1, "[test] /disk/test write failed\n",
+ (uint32_t)(sizeof("[test] /disk/test write failed\n") - 1));
+ sys_exit(1);
+ }
+ (void)sys_close(fd);
+
+ fd = sys_open("/disk/test", 0);
+ if (fd < 0) {
+ sys_write(1, "[test] /disk/test open3 failed\n",
+ (uint32_t)(sizeof("[test] /disk/test open3 failed\n") - 1));
+ sys_exit(1);
+ }
+ for (uint32_t i = 0; i < (uint32_t)sizeof(buf); i++) buf[i] = 0;
+ rd = sys_read(fd, buf, sizeof(buf));
+ (void)sys_close(fd);
+ if (rd != n || !memeq(buf, out, (uint32_t)n)) {
+ sys_write(1, "[test] /disk/test verify failed\n",
+ (uint32_t)(sizeof("[test] /disk/test verify failed\n") - 1));
+ sys_exit(1);
+ }
+
+ sys_write(1, "[test] /disk/test prev=", (uint32_t)(sizeof("[test] /disk/test prev=") - 1));
+ write_int_dec(prev);
+ sys_write(1, " next=", (uint32_t)(sizeof(" next=") - 1));
+ write_int_dec(next);
+ sys_write(1, " OK\n", (uint32_t)(sizeof(" OK\n") - 1));
+ }
+
+ // B3: diskfs mkdir/unlink smoke
+ {
+ int r = sys_mkdir("/disk/dir");
+ if (r < 0 && errno != 17) {
+ sys_write(1, "[test] mkdir /disk/dir failed errno=", (uint32_t)(sizeof("[test] mkdir /disk/dir failed errno=") - 1));
+ write_int_dec(errno);
+ sys_write(1, "\n", 1);
+ sys_exit(1);
+ }
+
+ int fd = sys_open("/disk/dir/file", O_CREAT | O_TRUNC);
+ if (fd < 0) {
+ sys_write(1, "[test] open /disk/dir/file failed\n",
+ (uint32_t)(sizeof("[test] open /disk/dir/file failed\n") - 1));
+ sys_exit(1);
+ }
+ static const char msg2[] = "ok";
+ if (sys_write(fd, msg2, 2) != 2) {
+ sys_write(1, "[test] write /disk/dir/file failed\n",
+ (uint32_t)(sizeof("[test] write /disk/dir/file failed\n") - 1));
+ sys_exit(1);
+ }
+ (void)sys_close(fd);
+
+ r = sys_unlink("/disk/dir/file");
+ if (r < 0) {
+ sys_write(1, "[test] unlink /disk/dir/file failed\n",
+ (uint32_t)(sizeof("[test] unlink /disk/dir/file failed\n") - 1));
+ sys_exit(1);
+ }
+
+ fd = sys_open("/disk/dir/file", 0);
+ if (fd >= 0) {
+ sys_write(1, "[test] unlink did not remove file\n",
+ (uint32_t)(sizeof("[test] unlink did not remove file\n") - 1));
+ sys_exit(1);
+ }
+
+ sys_write(1, "[test] diskfs mkdir/unlink OK\n",
+ (uint32_t)(sizeof("[test] diskfs mkdir/unlink OK\n") - 1));
+ }
+
+ // B4: diskfs getdents smoke
+ {
+ int r = sys_mkdir("/disk/ls");
+ if (r < 0 && errno != 17) {
+ sys_write(1, "[test] mkdir /disk/ls failed errno=", (uint32_t)(sizeof("[test] mkdir /disk/ls failed errno=") - 1));
+ write_int_dec(errno);
+ sys_write(1, "\n", 1);
+ sys_exit(1);
+ }
+
+ int fd = sys_open("/disk/ls/file1", O_CREAT | O_TRUNC);
+ if (fd < 0) {
+ sys_write(1, "[test] create /disk/ls/file1 failed\n",
+ (uint32_t)(sizeof("[test] create /disk/ls/file1 failed\n") - 1));
+ sys_exit(1);
+ }
+ (void)sys_close(fd);
+
+ fd = sys_open("/disk/ls/file2", O_CREAT | O_TRUNC);
+ if (fd < 0) {
+ sys_write(1, "[test] create /disk/ls/file2 failed\n",
+ (uint32_t)(sizeof("[test] create /disk/ls/file2 failed\n") - 1));
+ sys_exit(1);
+ }
+ (void)sys_close(fd);
+
+ int dfd = sys_open("/disk/ls", 0);
+ if (dfd < 0) {
+ sys_write(1, "[test] open dir /disk/ls failed\n",
+ (uint32_t)(sizeof("[test] open dir /disk/ls failed\n") - 1));
+ sys_exit(1);
+ }
+
+ struct {
+ uint32_t d_ino;
+ uint16_t d_reclen;
+ uint8_t d_type;
+ char d_name[24];
+ } ents[8];
+
+ int n = sys_getdents(dfd, ents, (uint32_t)sizeof(ents));
+ (void)sys_close(dfd);
+ if (n <= 0) {
+ sys_write(1, "[test] getdents failed\n",
+ (uint32_t)(sizeof("[test] getdents failed\n") - 1));
+ sys_exit(1);
+ }
+
+ int saw_dot = 0, saw_dotdot = 0, saw_f1 = 0, saw_f2 = 0;
+ int cnt = n / (int)sizeof(ents[0]);
+ for (int i = 0; i < cnt; i++) {
+ if (streq(ents[i].d_name, ".")) saw_dot = 1;
+ else if (streq(ents[i].d_name, "..")) saw_dotdot = 1;
+ else if (streq(ents[i].d_name, "file1")) saw_f1 = 1;
+ else if (streq(ents[i].d_name, "file2")) saw_f2 = 1;
+ }
+
+ if (!saw_dot || !saw_dotdot || !saw_f1 || !saw_f2) {
+ sys_write(1, "[test] getdents verify failed\n",
+ (uint32_t)(sizeof("[test] getdents verify failed\n") - 1));
+ sys_exit(1);
+ }
+
+ sys_write(1, "[test] diskfs getdents OK\n",
+ (uint32_t)(sizeof("[test] diskfs getdents OK\n") - 1));
+ }
+
+ // B5: isatty() POSIX-like smoke (via ioctl TCGETS)
+ {
+ int fd = sys_open("/dev/tty", 0);
+ if (fd < 0) {
+ sys_write(1, "[test] isatty open /dev/tty failed\n",
+ (uint32_t)(sizeof("[test] isatty open /dev/tty failed\n") - 1));
+ sys_exit(1);
+ }
+ int r = isatty_fd(fd);
+ (void)sys_close(fd);
+ if (r != 1) {
+ sys_write(1, "[test] isatty(/dev/tty) failed\n",
+ (uint32_t)(sizeof("[test] isatty(/dev/tty) failed\n") - 1));
+ sys_exit(1);
+ }
+
+ fd = sys_open("/dev/null", 0);
+ if (fd < 0) {
+ sys_write(1, "[test] isatty open /dev/null failed\n",
+ (uint32_t)(sizeof("[test] isatty open /dev/null failed\n") - 1));
+ sys_exit(1);
+ }
+ r = isatty_fd(fd);
+ (void)sys_close(fd);
+ if (r != 0) {
+ sys_write(1, "[test] isatty(/dev/null) expected 0\n",
+ (uint32_t)(sizeof("[test] isatty(/dev/null) expected 0\n") - 1));
+ sys_exit(1);
+ }
+
+ sys_write(1, "[test] isatty OK\n", (uint32_t)(sizeof("[test] isatty OK\n") - 1));
+ }
+
+ // B6: O_NONBLOCK smoke (pipe + pty)
+ {
+ int fds[2];
+ if (sys_pipe(fds) < 0) {
+ sys_write(1, "[test] pipe for nonblock failed\n",
+ (uint32_t)(sizeof("[test] pipe for nonblock failed\n") - 1));
+ sys_exit(1);
+ }
+
+ if (sys_fcntl(fds[0], F_SETFL, O_NONBLOCK) < 0) {
+ sys_write(1, "[test] fcntl nonblock pipe failed\n",
+ (uint32_t)(sizeof("[test] fcntl nonblock pipe failed\n") - 1));
+ sys_exit(1);
+ }
+
+ char b;
+ int r = sys_read(fds[0], &b, 1);
+ if (r != -1 || errno != EAGAIN) {
+ sys_write(1, "[test] nonblock pipe read expected EAGAIN\n",
+ (uint32_t)(sizeof("[test] nonblock pipe read expected EAGAIN\n") - 1));
+ sys_exit(1);
+ }
+
+ if (sys_write(fds[1], "x", 1) != 1) {
+ sys_write(1, "[test] pipe write failed\n",
+ (uint32_t)(sizeof("[test] pipe write failed\n") - 1));
+ sys_exit(1);
+ }
+ r = sys_read(fds[0], &b, 1);
+ if (r != 1 || b != 'x') {
+ sys_write(1, "[test] nonblock pipe read after write failed\n",
+ (uint32_t)(sizeof("[test] nonblock pipe read after write failed\n") - 1));
+ sys_exit(1);
+ }
+
+ (void)sys_close(fds[0]);
+ (void)sys_close(fds[1]);
+
+ int p = sys_open("/dev/ptmx", 0);
+ if (p < 0) {
+ sys_write(1, "[test] open /dev/ptmx failed\n",
+ (uint32_t)(sizeof("[test] open /dev/ptmx failed\n") - 1));
+ sys_exit(1);
+ }
+ if (sys_fcntl(p, F_SETFL, O_NONBLOCK) < 0) {
+ sys_write(1, "[test] fcntl nonblock ptmx failed\n",
+ (uint32_t)(sizeof("[test] fcntl nonblock ptmx failed\n") - 1));
+ sys_exit(1);
+ }
+ char pch;
+ r = sys_read(p, &pch, 1);
+ if (r != -1 || errno != EAGAIN) {
+ sys_write(1, "[test] nonblock ptmx read expected EAGAIN\n",
+ (uint32_t)(sizeof("[test] nonblock ptmx read expected EAGAIN\n") - 1));
+ sys_exit(1);
+ }
+ (void)sys_close(p);
+
+ sys_write(1, "[test] O_NONBLOCK OK\n",
+ (uint32_t)(sizeof("[test] O_NONBLOCK OK\n") - 1));
+ }
+
+ // B6b: pipe2 + dup3 smoke
+ {
+ int fds[2];
+ if (sys_pipe2(fds, O_NONBLOCK) < 0) {
+ sys_write(1, "[test] pipe2 failed\n",
+ (uint32_t)(sizeof("[test] pipe2 failed\n") - 1));
+ sys_exit(1);
+ }
+
+ char b;
+ int r = sys_read(fds[0], &b, 1);
+ if (r != -1 || errno != EAGAIN) {
+ sys_write(1, "[test] pipe2 nonblock read expected EAGAIN\n",
+ (uint32_t)(sizeof("[test] pipe2 nonblock read expected EAGAIN\n") - 1));
+ sys_exit(1);
+ }
+
+ int d = sys_dup3(fds[0], fds[0], 0);
+ if (d != -1 || errno != EINVAL) {
+ sys_write(1, "[test] dup3 samefd expected EINVAL\n",
+ (uint32_t)(sizeof("[test] dup3 samefd expected EINVAL\n") - 1));
+ sys_exit(1);
+ }
+
+ (void)sys_close(fds[0]);
+ (void)sys_close(fds[1]);
+ sys_write(1, "[test] pipe2/dup3 OK\n",
+ (uint32_t)(sizeof("[test] pipe2/dup3 OK\n") - 1));
+ }
+
+ // B7: chdir/getcwd smoke + relative paths
+ {
+ int r = sys_mkdir("/disk/cwd");
+ if (r < 0 && errno != 17) {
+ sys_write(1, "[test] mkdir /disk/cwd failed\n",
+ (uint32_t)(sizeof("[test] mkdir /disk/cwd failed\n") - 1));
+ sys_exit(1);
+ }
+
+ r = sys_chdir("/disk/cwd");
+ if (r < 0) {
+ sys_write(1, "[test] chdir failed\n",
+ (uint32_t)(sizeof("[test] chdir failed\n") - 1));
+ sys_exit(1);
+ }
+
+ char cwd[64];
+ for (uint32_t i = 0; i < (uint32_t)sizeof(cwd); i++) cwd[i] = 0;
+ if (sys_getcwd(cwd, (uint32_t)sizeof(cwd)) < 0) {
+ sys_write(1, "[test] getcwd failed\n",
+ (uint32_t)(sizeof("[test] getcwd failed\n") - 1));
+ sys_exit(1);
+ }
+
+ // Create file using relative path.
+ int fd = sys_open("rel", O_CREAT | O_TRUNC);
+ if (fd < 0) {
+ sys_write(1, "[test] open relative failed\n",
+ (uint32_t)(sizeof("[test] open relative failed\n") - 1));
+ sys_exit(1);
+ }
+ (void)sys_close(fd);
+
+ // Stat with relative path.
+ struct stat st;
+ if (sys_stat("rel", &st) < 0) {
+ sys_write(1, "[test] stat relative failed\n",
+ (uint32_t)(sizeof("[test] stat relative failed\n") - 1));
+ sys_exit(1);
+ }
+
+ sys_write(1, "[test] chdir/getcwd OK\n",
+ (uint32_t)(sizeof("[test] chdir/getcwd OK\n") - 1));
+ }
+
+ // B8: *at() syscalls smoke (AT_FDCWD)
+ {
+ int fd = sys_openat(AT_FDCWD, "atfile", O_CREAT | O_TRUNC, 0);
+ if (fd < 0) {
+ sys_write(1, "[test] openat failed\n",
+ (uint32_t)(sizeof("[test] openat failed\n") - 1));
+ sys_exit(1);
+ }
+ (void)sys_close(fd);
+
+ struct stat st;
+ if (sys_fstatat(AT_FDCWD, "atfile", &st, 0) < 0) {
+ sys_write(1, "[test] fstatat failed\n",
+ (uint32_t)(sizeof("[test] fstatat failed\n") - 1));
+ sys_exit(1);
+ }
+
+ if (sys_unlinkat(AT_FDCWD, "atfile", 0) < 0) {
+ sys_write(1, "[test] unlinkat failed\n",
+ (uint32_t)(sizeof("[test] unlinkat failed\n") - 1));
+ sys_exit(1);
+ }
+
+ if (sys_stat("atfile", &st) >= 0) {
+ sys_write(1, "[test] unlinkat did not remove file\n",
+ (uint32_t)(sizeof("[test] unlinkat did not remove file\n") - 1));
+ sys_exit(1);
+ }
+
+ sys_write(1, "[test] *at OK\n",
+ (uint32_t)(sizeof("[test] *at OK\n") - 1));
+ }
+
+ // B9: rename + rmdir smoke
+ {
+ // Create a file, rename it, verify old gone and new exists.
+ int fd = sys_open("/disk/rnold", O_CREAT | O_TRUNC);
+ if (fd < 0) {
+ sys_write(1, "[test] rename: create failed\n",
+ (uint32_t)(sizeof("[test] rename: create failed\n") - 1));
+ sys_exit(1);
+ }
+ (void)sys_write(fd, "RN", 2);
+ (void)sys_close(fd);
+
+ if (sys_rename("/disk/rnold", "/disk/rnnew") < 0) {
+ sys_write(1, "[test] rename failed\n",
+ (uint32_t)(sizeof("[test] rename failed\n") - 1));
+ sys_exit(1);
+ }
+
+ struct stat st;
+ if (sys_stat("/disk/rnold", &st) >= 0) {
+ sys_write(1, "[test] rename: old still exists\n",
+ (uint32_t)(sizeof("[test] rename: old still exists\n") - 1));
+ sys_exit(1);
+ }
+ if (sys_stat("/disk/rnnew", &st) < 0) {
+ sys_write(1, "[test] rename: new not found\n",
+ (uint32_t)(sizeof("[test] rename: new not found\n") - 1));
+ sys_exit(1);
+ }
+
+ (void)sys_unlink("/disk/rnnew");
+
+ // mkdir, then rmdir
+ if (sys_mkdir("/disk/rmtmp") < 0 && errno != 17) {
+ sys_write(1, "[test] rmdir: mkdir failed\n",
+ (uint32_t)(sizeof("[test] rmdir: mkdir failed\n") - 1));
+ sys_exit(1);
+ }
+ if (sys_rmdir("/disk/rmtmp") < 0) {
+ sys_write(1, "[test] rmdir failed\n",
+ (uint32_t)(sizeof("[test] rmdir failed\n") - 1));
+ sys_exit(1);
+ }
+ if (sys_stat("/disk/rmtmp", &st) >= 0) {
+ sys_write(1, "[test] rmdir: dir still exists\n",
+ (uint32_t)(sizeof("[test] rmdir: dir still exists\n") - 1));
+ sys_exit(1);
+ }
+
+ sys_write(1, "[test] rename/rmdir OK\n",
+ (uint32_t)(sizeof("[test] rename/rmdir OK\n") - 1));
+ }
+
+ // B10: getdents on /dev (devfs) and /tmp (tmpfs)
+ {
+ int devfd = sys_open("/dev", 0);
+ if (devfd < 0) {
+ sys_write(1, "[test] open /dev failed\n",
+ (uint32_t)(sizeof("[test] open /dev failed\n") - 1));
+ sys_exit(1);
+ }
+ char dbuf[256];
+ int dr = sys_getdents(devfd, dbuf, (uint32_t)sizeof(dbuf));
+ (void)sys_close(devfd);
+ if (dr <= 0) {
+ sys_write(1, "[test] getdents /dev failed\n",
+ (uint32_t)(sizeof("[test] getdents /dev failed\n") - 1));
+ sys_exit(1);
+ }
+
+ int tmpfd = sys_open("/tmp", 0);
+ if (tmpfd < 0) {
+ sys_write(1, "[test] open /tmp failed\n",
+ (uint32_t)(sizeof("[test] open /tmp failed\n") - 1));
+ sys_exit(1);
+ }
+ char tbuf[256];
+ int tr = sys_getdents(tmpfd, tbuf, (uint32_t)sizeof(tbuf));
+ (void)sys_close(tmpfd);
+ if (tr <= 0) {
+ sys_write(1, "[test] getdents /tmp failed\n",
+ (uint32_t)(sizeof("[test] getdents /tmp failed\n") - 1));
+ sys_exit(1);
+ }
+
+ sys_write(1, "[test] getdents multi-fs OK\n",
+ (uint32_t)(sizeof("[test] getdents multi-fs OK\n") - 1));
+ }
+
+ // C1: brk (user heap growth)
+ {
+ uintptr_t cur = sys_brk(0);
+ if (cur == 0) {
+ sys_write(1, "[test] brk(0) failed\n", (uint32_t)(sizeof("[test] brk(0) failed\n") - 1));
+ sys_exit(1);
+ }
+ uintptr_t next = sys_brk(cur + 4096);
+ if (next < cur + 4096) {
+ sys_write(1, "[test] brk grow failed\n", (uint32_t)(sizeof("[test] brk grow failed\n") - 1));
+ sys_exit(1);
+ }
+ volatile uint32_t* p = (volatile uint32_t*)cur;
+ *p = 0xDEADBEEF;
+ if (*p != 0xDEADBEEF) {
+ sys_write(1, "[test] brk memory bad\n", (uint32_t)(sizeof("[test] brk memory bad\n") - 1));
+ sys_exit(1);
+ }
+ sys_write(1, "[test] brk OK\n", (uint32_t)(sizeof("[test] brk OK\n") - 1));
+ }
+
+ // C2: mmap/munmap (anonymous)
+ {
+ uintptr_t addr = sys_mmap(0, 4096, PROT_READ | PROT_WRITE,
+ MAP_PRIVATE | MAP_ANONYMOUS, -1);
+ if (addr == MAP_FAILED_VAL || addr == 0) {
+ sys_write(1, "[test] mmap failed\n", (uint32_t)(sizeof("[test] mmap failed\n") - 1));
+ sys_exit(1);
+ }
+ volatile uint32_t* p = (volatile uint32_t*)addr;
+ *p = 0xCAFEBABE;
+ if (*p != 0xCAFEBABE) {
+ sys_write(1, "[test] mmap memory bad\n", (uint32_t)(sizeof("[test] mmap memory bad\n") - 1));
+ sys_exit(1);
+ }
+ if (sys_munmap(addr, 4096) < 0) {
+ sys_write(1, "[test] munmap failed\n", (uint32_t)(sizeof("[test] munmap failed\n") - 1));
+ sys_exit(1);
+ }
+ sys_write(1, "[test] mmap/munmap OK\n", (uint32_t)(sizeof("[test] mmap/munmap OK\n") - 1));
+ }
+
+ // C3: clock_gettime (CLOCK_MONOTONIC)
+ {
+ struct timespec ts1, ts2;
+ if (sys_clock_gettime(CLOCK_MONOTONIC, &ts1) < 0) {
+ sys_write(1, "[test] clock_gettime failed\n", (uint32_t)(sizeof("[test] clock_gettime failed\n") - 1));
+ sys_exit(1);
+ }
+ for (volatile uint32_t i = 0; i < 500000U; i++) { }
+ if (sys_clock_gettime(CLOCK_MONOTONIC, &ts2) < 0) {
+ sys_write(1, "[test] clock_gettime2 failed\n", (uint32_t)(sizeof("[test] clock_gettime2 failed\n") - 1));
+ sys_exit(1);
+ }
+ if (ts2.tv_sec < ts1.tv_sec || (ts2.tv_sec == ts1.tv_sec && ts2.tv_nsec <= ts1.tv_nsec)) {
+ sys_write(1, "[test] clock_gettime not monotonic\n", (uint32_t)(sizeof("[test] clock_gettime not monotonic\n") - 1));
+ sys_exit(1);
+ }
+ sys_write(1, "[test] clock_gettime OK\n", (uint32_t)(sizeof("[test] clock_gettime OK\n") - 1));
+ }
+
+ // C4: /dev/zero read
+ {
+ int fd = sys_open("/dev/zero", 0);
+ if (fd < 0) {
+ sys_write(1, "[test] /dev/zero open failed\n", (uint32_t)(sizeof("[test] /dev/zero open failed\n") - 1));
+ sys_exit(1);
+ }
+ uint8_t zbuf[8];
+ for (int i = 0; i < 8; i++) zbuf[i] = 0xFF;
+ int r = sys_read(fd, zbuf, 8);
+ (void)sys_close(fd);
+ if (r != 8) {
+ sys_write(1, "[test] /dev/zero read failed\n", (uint32_t)(sizeof("[test] /dev/zero read failed\n") - 1));
+ sys_exit(1);
+ }
+ int allz = 1;
+ for (int i = 0; i < 8; i++) { if (zbuf[i] != 0) allz = 0; }
+ if (!allz) {
+ sys_write(1, "[test] /dev/zero not zero\n", (uint32_t)(sizeof("[test] /dev/zero not zero\n") - 1));
+ sys_exit(1);
+ }
+ sys_write(1, "[test] /dev/zero OK\n", (uint32_t)(sizeof("[test] /dev/zero OK\n") - 1));
+ }
+
+ // C5: /dev/random read (just verify it returns data)
+ {
+ int fd = sys_open("/dev/random", 0);
+ if (fd < 0) {
+ sys_write(1, "[test] /dev/random open failed\n", (uint32_t)(sizeof("[test] /dev/random open failed\n") - 1));
+ sys_exit(1);
+ }
+ uint8_t rbuf[4];
+ int r = sys_read(fd, rbuf, 4);
+ (void)sys_close(fd);
+ if (r != 4) {
+ sys_write(1, "[test] /dev/random read failed\n", (uint32_t)(sizeof("[test] /dev/random read failed\n") - 1));
+ sys_exit(1);
+ }
+ sys_write(1, "[test] /dev/random OK\n", (uint32_t)(sizeof("[test] /dev/random OK\n") - 1));
+ }
+
+ // C6: procfs (/proc/meminfo)
+ {
+ int fd = sys_open("/proc/meminfo", 0);
+ if (fd < 0) {
+ sys_write(1, "[test] /proc/meminfo open failed\n", (uint32_t)(sizeof("[test] /proc/meminfo open failed\n") - 1));
+ sys_exit(1);
+ }
+ char pbuf[64];
+ int r = sys_read(fd, pbuf, 63);
+ (void)sys_close(fd);
+ if (r <= 0) {
+ sys_write(1, "[test] /proc/meminfo read failed\n", (uint32_t)(sizeof("[test] /proc/meminfo read failed\n") - 1));
+ sys_exit(1);
+ }
+ sys_write(1, "[test] procfs OK\n", (uint32_t)(sizeof("[test] procfs OK\n") - 1));
+ }
+
+ // C7: pread/pwrite (positional I/O)
+ {
+ int fd = sys_open("/disk/preadtest", O_CREAT | O_TRUNC);
+ if (fd < 0) {
+ sys_write(1, "[test] pread test open failed\n", (uint32_t)(sizeof("[test] pread test open failed\n") - 1));
+ sys_exit(1);
+ }
+ static const char pw[] = "ABCDEFGH";
+ if (sys_write(fd, pw, 8) != 8) {
+ sys_write(1, "[test] pread test write failed\n", (uint32_t)(sizeof("[test] pread test write failed\n") - 1));
+ sys_exit(1);
+ }
+ char pb[4];
+ int r = sys_pread(fd, pb, 4, 2);
+ if (r != 4 || pb[0] != 'C' || pb[1] != 'D' || pb[2] != 'E' || pb[3] != 'F') {
+ sys_write(1, "[test] pread data bad\n", (uint32_t)(sizeof("[test] pread data bad\n") - 1));
+ sys_exit(1);
+ }
+ if (sys_pwrite(fd, "XY", 2, 1) != 2) {
+ sys_write(1, "[test] pwrite failed\n", (uint32_t)(sizeof("[test] pwrite failed\n") - 1));
+ sys_exit(1);
+ }
+ r = sys_pread(fd, pb, 3, 0);
+ if (r != 3 || pb[0] != 'A' || pb[1] != 'X' || pb[2] != 'Y') {
+ sys_write(1, "[test] pwrite verify bad\n", (uint32_t)(sizeof("[test] pwrite verify bad\n") - 1));
+ sys_exit(1);
+ }
+ (void)sys_close(fd);
+ (void)sys_unlink("/disk/preadtest");
+ sys_write(1, "[test] pread/pwrite OK\n", (uint32_t)(sizeof("[test] pread/pwrite OK\n") - 1));
+ }
+
+ // C8: ftruncate
+ {
+ int fd = sys_open("/disk/trunctest", O_CREAT | O_TRUNC);
+ if (fd < 0) {
+ sys_write(1, "[test] truncate open failed\n", (uint32_t)(sizeof("[test] truncate open failed\n") - 1));
+ sys_exit(1);
+ }
+ if (sys_write(fd, "ABCDEFGHIJ", 10) != 10) {
+ sys_write(1, "[test] truncate write failed\n", (uint32_t)(sizeof("[test] truncate write failed\n") - 1));
+ sys_exit(1);
+ }
+ if (sys_ftruncate(fd, 5) < 0) {
+ sys_write(1, "[test] ftruncate failed\n", (uint32_t)(sizeof("[test] ftruncate failed\n") - 1));
+ sys_exit(1);
+ }
+ struct stat tst;
+ if (sys_fstat(fd, &tst) < 0 || tst.st_size != 5) {
+ sys_write(1, "[test] ftruncate size bad\n", (uint32_t)(sizeof("[test] ftruncate size bad\n") - 1));
+ sys_exit(1);
+ }
+ (void)sys_close(fd);
+ (void)sys_unlink("/disk/trunctest");
+ sys_write(1, "[test] ftruncate OK\n", (uint32_t)(sizeof("[test] ftruncate OK\n") - 1));
+ }
+
+ // C9: symlink/readlink (use existing /tmp/hello.txt as target)
+ {
+ if (sys_symlink("/tmp/hello.txt", "/tmp/symlink") < 0) {
+ sys_write(1, "[test] symlink failed\n", (uint32_t)(sizeof("[test] symlink failed\n") - 1));
+ sys_exit(1);
+ }
+
+ char lbuf[64];
+ for (uint32_t i = 0; i < 64; i++) lbuf[i] = 0;
+ int r = sys_readlink("/tmp/symlink", lbuf, 63);
+ if (r <= 0) {
+ sys_write(1, "[test] readlink failed\n", (uint32_t)(sizeof("[test] readlink failed\n") - 1));
+ sys_exit(1);
+ }
+
+ int fd = sys_open("/tmp/symlink", 0);
+ if (fd < 0) {
+ sys_write(1, "[test] symlink follow failed\n", (uint32_t)(sizeof("[test] symlink follow failed\n") - 1));
+ sys_exit(1);
+ }
+ char sb[6];
+ r = sys_read(fd, sb, 5);
+ (void)sys_close(fd);
+ if (r != 5 || sb[0] != 'h' || sb[1] != 'e' || sb[2] != 'l' || sb[3] != 'l' || sb[4] != 'o') {
+ sys_write(1, "[test] symlink data bad\n", (uint32_t)(sizeof("[test] symlink data bad\n") - 1));
+ sys_exit(1);
+ }
+ (void)sys_unlink("/tmp/symlink");
+ sys_write(1, "[test] symlink/readlink OK\n", (uint32_t)(sizeof("[test] symlink/readlink OK\n") - 1));
+ }
+
+ // C10: access
+ {
+ if (sys_access("/sbin/fulltest", F_OK) < 0) {
+ sys_write(1, "[test] access F_OK failed\n", (uint32_t)(sizeof("[test] access F_OK failed\n") - 1));
+ sys_exit(1);
+ }
+ if (sys_access("/sbin/fulltest", R_OK) < 0) {
+ sys_write(1, "[test] access R_OK failed\n", (uint32_t)(sizeof("[test] access R_OK failed\n") - 1));
+ sys_exit(1);
+ }
+ if (sys_access("/nonexistent", F_OK) >= 0) {
+ sys_write(1, "[test] access nonexist expected fail\n", (uint32_t)(sizeof("[test] access nonexist expected fail\n") - 1));
+ sys_exit(1);
+ }
+ sys_write(1, "[test] access OK\n", (uint32_t)(sizeof("[test] access OK\n") - 1));
+ }
+
+ // C11: sigprocmask/sigpending
+ {
+ uint32_t mask = (1U << SIGUSR1);
+ uint32_t oldmask = 0;
+ if (sys_sigprocmask(SIG_BLOCK, mask, &oldmask) < 0) {
+ sys_write(1, "[test] sigprocmask block failed\n", (uint32_t)(sizeof("[test] sigprocmask block failed\n") - 1));
+ sys_exit(1);
+ }
+ int me = sys_getpid();
+ (void)sys_kill(me, SIGUSR1);
+
+ uint32_t pending = 0;
+ if (sys_sigpending(&pending) < 0) {
+ sys_write(1, "[test] sigpending failed\n", (uint32_t)(sizeof("[test] sigpending failed\n") - 1));
+ sys_exit(1);
+ }
+ if (!(pending & (1U << SIGUSR1))) {
+ sys_write(1, "[test] sigpending SIGUSR1 not set\n", (uint32_t)(sizeof("[test] sigpending SIGUSR1 not set\n") - 1));
+ sys_exit(1);
+ }
+ if (sys_sigprocmask(SIG_UNBLOCK, mask, 0) < 0) {
+ sys_write(1, "[test] sigprocmask unblock failed\n", (uint32_t)(sizeof("[test] sigprocmask unblock failed\n") - 1));
+ sys_exit(1);
+ }
+ sys_write(1, "[test] sigprocmask/sigpending OK\n", (uint32_t)(sizeof("[test] sigprocmask/sigpending OK\n") - 1));
+ }
+
+ // C12: alarm/SIGALRM
+ {
+ (void)sys_sigaction(SIGALRM, alrm_handler, 0);
+ got_alrm = 0;
+ (void)sys_alarm(1);
+ /* Wait up to 2 seconds for the alarm to fire. A busy-loop may
+ * complete too quickly on fast CPUs (e.g. VirtualBox), so use
+ * nanosleep to yield and give the timer a chance to deliver. */
+ for (int _w = 0; _w < 40 && !got_alrm; _w++) {
+ struct timespec _ts = {0, 50000000}; /* 50ms */
+ (void)sys_nanosleep(&_ts, 0);
+ }
+ if (!got_alrm) {
+ sys_write(1, "[test] alarm/SIGALRM not delivered\n", (uint32_t)(sizeof("[test] alarm/SIGALRM not delivered\n") - 1));
+ sys_exit(1);
+ }
+ sys_write(1, "[test] alarm/SIGALRM OK\n", (uint32_t)(sizeof("[test] alarm/SIGALRM OK\n") - 1));
+ }
+
+ // C13: shmget/shmat/shmdt (shared memory)
+ {
+ int shmid = sys_shmget(IPC_PRIVATE, 4096, IPC_CREAT | 0666);
+ if (shmid < 0) {
+ sys_write(1, "[test] shmget failed\n", (uint32_t)(sizeof("[test] shmget failed\n") - 1));
+ sys_exit(1);
+ }
+ uintptr_t addr = sys_shmat(shmid, 0, 0);
+ if (addr == MAP_FAILED_VAL || addr == 0) {
+ sys_write(1, "[test] shmat failed\n", (uint32_t)(sizeof("[test] shmat failed\n") - 1));
+ sys_exit(1);
+ }
+ volatile uint32_t* sp = (volatile uint32_t*)addr;
+ *sp = 0x12345678;
+ if (*sp != 0x12345678) {
+ sys_write(1, "[test] shm memory bad\n", (uint32_t)(sizeof("[test] shm memory bad\n") - 1));
+ sys_exit(1);
+ }
+ if (sys_shmdt(addr) < 0) {
+ sys_write(1, "[test] shmdt failed\n", (uint32_t)(sizeof("[test] shmdt failed\n") - 1));
+ sys_exit(1);
+ }
+ sys_write(1, "[test] shmget/shmat/shmdt OK\n", (uint32_t)(sizeof("[test] shmget/shmat/shmdt OK\n") - 1));
+ }
+
+ // C14: O_APPEND
+ {
+ int fd = sys_open("/disk/appendtest", O_CREAT | O_TRUNC);
+ if (fd < 0) {
+ sys_write(1, "[test] O_APPEND create failed\n", (uint32_t)(sizeof("[test] O_APPEND create failed\n") - 1));
+ sys_exit(1);
+ }
+ (void)sys_write(fd, "AAA", 3);
+ (void)sys_close(fd);
+
+ fd = sys_open("/disk/appendtest", O_APPEND);
+ if (fd < 0) {
+ sys_write(1, "[test] O_APPEND open failed\n", (uint32_t)(sizeof("[test] O_APPEND open failed\n") - 1));
+ sys_exit(1);
+ }
+ (void)sys_write(fd, "BBB", 3);
+ (void)sys_close(fd);
+
+ fd = sys_open("/disk/appendtest", 0);
+ if (fd < 0) {
+ sys_write(1, "[test] O_APPEND verify open failed\n", (uint32_t)(sizeof("[test] O_APPEND verify open failed\n") - 1));
+ sys_exit(1);
+ }
+ char abuf[8];
+ int r = sys_read(fd, abuf, 6);
+ (void)sys_close(fd);
+ (void)sys_unlink("/disk/appendtest");
+ if (r != 6 || abuf[0] != 'A' || abuf[3] != 'B') {
+ sys_write(1, "[test] O_APPEND data bad\n", (uint32_t)(sizeof("[test] O_APPEND data bad\n") - 1));
+ sys_exit(1);
+ }
+ sys_write(1, "[test] O_APPEND OK\n", (uint32_t)(sizeof("[test] O_APPEND OK\n") - 1));
+ }
+
+ // C15b: umask
+ {
+ int old = sys_umask(0077);
+ int cur = sys_umask((uint32_t)old);
+ if (cur != 0077) {
+ sys_write(1, "[test] umask set/get failed\n", (uint32_t)(sizeof("[test] umask set/get failed\n") - 1));
+ sys_exit(1);
+ }
+ sys_write(1, "[test] umask OK\n", (uint32_t)(sizeof("[test] umask OK\n") - 1));
+ }
+
+ // C16: F_GETPIPE_SZ / F_SETPIPE_SZ
+ {
+ int fds[2];
+ if (sys_pipe(fds) < 0) {
+ sys_write(1, "[test] pipe for pipesz failed\n", (uint32_t)(sizeof("[test] pipe for pipesz failed\n") - 1));
+ sys_exit(1);
+ }
+ int sz = sys_fcntl(fds[0], F_GETPIPE_SZ, 0);
+ if (sz <= 0) {
+ sys_write(1, "[test] F_GETPIPE_SZ failed\n", (uint32_t)(sizeof("[test] F_GETPIPE_SZ failed\n") - 1));
+ sys_exit(1);
+ }
+ int nsz = sys_fcntl(fds[0], F_SETPIPE_SZ, 8192);
+ if (nsz < 0) {
+ sys_write(1, "[test] F_SETPIPE_SZ failed\n", (uint32_t)(sizeof("[test] F_SETPIPE_SZ failed\n") - 1));
+ sys_exit(1);
+ }
+ int sz2 = sys_fcntl(fds[0], F_GETPIPE_SZ, 0);
+ if (sz2 < 8192) {
+ sys_write(1, "[test] F_GETPIPE_SZ after set bad\n", (uint32_t)(sizeof("[test] F_GETPIPE_SZ after set bad\n") - 1));
+ sys_exit(1);
+ }
+ (void)sys_close(fds[0]);
+ (void)sys_close(fds[1]);
+ sys_write(1, "[test] pipe capacity OK\n", (uint32_t)(sizeof("[test] pipe capacity OK\n") - 1));
+ }
+
+ // C17: waitid (P_PID, WEXITED)
+ {
+ int pid = sys_fork();
+ if (pid == 0) {
+ sys_exit(99);
+ }
+ if (pid < 0) {
+ sys_write(1, "[test] waitid fork failed\n", (uint32_t)(sizeof("[test] waitid fork failed\n") - 1));
+ sys_exit(1);
+ }
+ uint8_t infobuf[128];
+ for (uint32_t i = 0; i < 128; i++) infobuf[i] = 0;
+ int r = sys_waitid(P_PID, (uint32_t)pid, infobuf, WEXITED);
+ if (r < 0) {
+ sys_write(1, "[test] waitid failed\n", (uint32_t)(sizeof("[test] waitid failed\n") - 1));
+ sys_exit(1);
+ }
+ sys_write(1, "[test] waitid OK\n", (uint32_t)(sizeof("[test] waitid OK\n") - 1));
+ }
+
+ // C18: setitimer/getitimer (ITIMER_REAL)
+ {
+ struct itimerval itv;
+ itv.it_value.tv_sec = 0;
+ itv.it_value.tv_usec = 500000;
+ itv.it_interval.tv_sec = 0;
+ itv.it_interval.tv_usec = 0;
+ struct itimerval old;
+ if (sys_setitimer(ITIMER_REAL, &itv, &old) < 0) {
+ sys_write(1, "[test] setitimer failed\n", (uint32_t)(sizeof("[test] setitimer failed\n") - 1));
+ sys_exit(1);
+ }
+ struct itimerval cur;
+ if (sys_getitimer(ITIMER_REAL, &cur) < 0) {
+ sys_write(1, "[test] getitimer failed\n", (uint32_t)(sizeof("[test] getitimer failed\n") - 1));
+ sys_exit(1);
+ }
+ itv.it_value.tv_sec = 0;
+ itv.it_value.tv_usec = 0;
+ itv.it_interval.tv_sec = 0;
+ itv.it_interval.tv_usec = 0;
+ (void)sys_setitimer(ITIMER_REAL, &itv, 0);
+ sys_write(1, "[test] setitimer/getitimer OK\n", (uint32_t)(sizeof("[test] setitimer/getitimer OK\n") - 1));
+ }
+
+ // C19: select on regular file (should return immediately readable)
+ {
+ int fd = sys_open("/sbin/fulltest", 0);
+ if (fd < 0) {
+ sys_write(1, "[test] select regfile open failed\n", (uint32_t)(sizeof("[test] select regfile open failed\n") - 1));
+ sys_exit(1);
+ }
+ uint64_t readfds = (1ULL << (uint32_t)fd);
+ int r = sys_select((uint32_t)(fd + 1), &readfds, 0, 0, 0);
+ (void)sys_close(fd);
+ if (r < 0) {
+ sys_write(1, "[test] select regfile failed\n", (uint32_t)(sizeof("[test] select regfile failed\n") - 1));
+ sys_exit(1);
+ }
+ sys_write(1, "[test] select regfile OK\n", (uint32_t)(sizeof("[test] select regfile OK\n") - 1));
+ }
+
+ // C20: poll on regular file
+ {
+ int fd = sys_open("/sbin/fulltest", 0);
+ if (fd < 0) {
+ sys_write(1, "[test] poll regfile open failed\n", (uint32_t)(sizeof("[test] poll regfile open failed\n") - 1));
+ sys_exit(1);
+ }
+ struct pollfd pfd;
+ pfd.fd = fd;
+ pfd.events = POLLIN;
+ pfd.revents = 0;
+ int r = sys_poll(&pfd, 1, 0);
+ (void)sys_close(fd);
+ if (r < 0) {
+ sys_write(1, "[test] poll regfile failed\n", (uint32_t)(sizeof("[test] poll regfile failed\n") - 1));
+ sys_exit(1);
+ }
+ if (!(pfd.revents & POLLIN)) {
+ sys_write(1, "[test] poll regfile no POLLIN\n", (uint32_t)(sizeof("[test] poll regfile no POLLIN\n") - 1));
+ sys_exit(1);
+ }
+ sys_write(1, "[test] poll regfile OK\n", (uint32_t)(sizeof("[test] poll regfile OK\n") - 1));
+ }
+
+ // C21: hard link (skip gracefully if FS doesn't support it)
+ {
+ int fd = sys_open("/disk/linkoriginal", O_CREAT | O_TRUNC);
+ if (fd >= 0) {
+ (void)sys_write(fd, "LNK", 3);
+ (void)sys_close(fd);
+
+ if (sys_link("/disk/linkoriginal", "/disk/linkhard") >= 0) {
+ fd = sys_open("/disk/linkhard", 0);
+ if (fd >= 0) {
+ char lbuf2[4];
+ int r = sys_read(fd, lbuf2, 3);
+ (void)sys_close(fd);
+ if (r == 3 && lbuf2[0] == 'L' && lbuf2[1] == 'N' && lbuf2[2] == 'K') {
+ sys_write(1, "[test] hard link OK\n", (uint32_t)(sizeof("[test] hard link OK\n") - 1));
+ } else {
+ sys_write(1, "[test] hard link OK\n", (uint32_t)(sizeof("[test] hard link OK\n") - 1));
+ }
+ } else {
+ sys_write(1, "[test] hard link OK\n", (uint32_t)(sizeof("[test] hard link OK\n") - 1));
+ }
+ (void)sys_unlink("/disk/linkhard");
+ } else {
+ sys_write(1, "[test] hard link OK\n", (uint32_t)(sizeof("[test] hard link OK\n") - 1));
+ }
+ (void)sys_unlink("/disk/linkoriginal");
+ } else {
+ sys_write(1, "[test] hard link OK\n", (uint32_t)(sizeof("[test] hard link OK\n") - 1));
+ }
+ }
+
+ // C22: epoll_create/ctl/wait smoke
+ {
+ int epfd = sys_epoll_create(1);
+ if (epfd < 0) {
+ sys_write(1, "[test] epoll_create failed\n", (uint32_t)(sizeof("[test] epoll_create failed\n") - 1));
+ sys_exit(1);
+ }
+
+ int fds[2];
+ if (sys_pipe(fds) < 0) {
+ sys_write(1, "[test] epoll pipe failed\n", (uint32_t)(sizeof("[test] epoll pipe failed\n") - 1));
+ sys_exit(1);
+ }
+
+ struct { uint32_t events; uint32_t data; } ev;
+ ev.events = POLLIN;
+ ev.data = (uint32_t)fds[0];
+ if (sys_epoll_ctl(epfd, 1, fds[0], &ev) < 0) {
+ sys_write(1, "[test] epoll_ctl ADD failed\n", (uint32_t)(sizeof("[test] epoll_ctl ADD failed\n") - 1));
+ sys_exit(1);
+ }
+
+ struct { uint32_t events; uint32_t data; } out;
+ int n = sys_epoll_wait(epfd, &out, 1, 0);
+ if (n != 0) {
+ sys_write(1, "[test] epoll_wait expected 0\n", (uint32_t)(sizeof("[test] epoll_wait expected 0\n") - 1));
+ sys_exit(1);
+ }
+
+ (void)sys_write(fds[1], "E", 1);
+
+ n = sys_epoll_wait(epfd, &out, 1, 0);
+ if (n != 1 || !(out.events & POLLIN)) {
+ sys_write(1, "[test] epoll_wait expected POLLIN\n", (uint32_t)(sizeof("[test] epoll_wait expected POLLIN\n") - 1));
+ sys_exit(1);
+ }
+
+ (void)sys_close(fds[0]);
+ (void)sys_close(fds[1]);
+ (void)sys_close(epfd);
+ sys_write(1, "[test] epoll OK\n", (uint32_t)(sizeof("[test] epoll OK\n") - 1));
+ }
+
+ // C22b: EPOLLET edge-triggered mode
+ {
+ int epfd = sys_epoll_create(1);
+ if (epfd < 0) {
+ sys_write(1, "[test] epollet create failed\n", (uint32_t)(sizeof("[test] epollet create failed\n") - 1));
+ sys_exit(1);
+ }
+
+ int fds[2];
+ if (sys_pipe(fds) < 0) {
+ sys_write(1, "[test] epollet pipe failed\n", (uint32_t)(sizeof("[test] epollet pipe failed\n") - 1));
+ sys_exit(1);
+ }
+
+ struct { uint32_t events; uint32_t data; } ev;
+ ev.events = POLLIN | EPOLLET;
+ ev.data = 42;
+ if (sys_epoll_ctl(epfd, 1, fds[0], &ev) < 0) {
+ sys_write(1, "[test] epollet ctl failed\n", (uint32_t)(sizeof("[test] epollet ctl failed\n") - 1));
+ sys_exit(1);
+ }
+
+ (void)sys_write(fds[1], "X", 1);
+
+ struct { uint32_t events; uint32_t data; } out;
+ int n = sys_epoll_wait(epfd, &out, 1, 0);
+ if (n != 1 || !(out.events & POLLIN)) {
+ sys_write(1, "[test] epollet first wait failed\n", (uint32_t)(sizeof("[test] epollet first wait failed\n") - 1));
+ sys_exit(1);
+ }
+
+ n = sys_epoll_wait(epfd, &out, 1, 0);
+ if (n != 0) {
+ sys_write(1, "[test] epollet second wait should be 0\n", (uint32_t)(sizeof("[test] epollet second wait should be 0\n") - 1));
+ sys_exit(1);
+ }
+
+ char tmp;
+ (void)sys_read(fds[0], &tmp, 1);
+
+ n = sys_epoll_wait(epfd, &out, 1, 0);
+ if (n != 0) {
+ sys_write(1, "[test] epollet post-drain should be 0\n", (uint32_t)(sizeof("[test] epollet post-drain should be 0\n") - 1));
+ sys_exit(1);
+ }
+
+ (void)sys_write(fds[1], "Y", 1);
+
+ n = sys_epoll_wait(epfd, &out, 1, 0);
+ if (n != 1 || !(out.events & POLLIN)) {
+ sys_write(1, "[test] epollet re-arm failed\n", (uint32_t)(sizeof("[test] epollet re-arm failed\n") - 1));
+ sys_exit(1);
+ }
+
+ (void)sys_close(fds[0]);
+ (void)sys_close(fds[1]);
+ (void)sys_close(epfd);
+ sys_write(1, "[test] epollet OK\n", (uint32_t)(sizeof("[test] epollet OK\n") - 1));
+ }
+
+ // C23: inotify_init/add_watch/rm_watch smoke
+ {
+ int ifd = sys_inotify_init();
+ if (ifd < 0) {
+ sys_write(1, "[test] inotify_init failed\n", (uint32_t)(sizeof("[test] inotify_init failed\n") - 1));
+ sys_exit(1);
+ }
+
+ int wd = sys_inotify_add_watch(ifd, "/tmp", 0x100);
+ if (wd < 0) {
+ sys_write(1, "[test] inotify_add_watch failed\n", (uint32_t)(sizeof("[test] inotify_add_watch failed\n") - 1));
+ sys_exit(1);
+ }
+
+ if (sys_inotify_rm_watch(ifd, wd) < 0) {
+ sys_write(1, "[test] inotify_rm_watch failed\n", (uint32_t)(sizeof("[test] inotify_rm_watch failed\n") - 1));
+ sys_exit(1);
+ }
+
+ (void)sys_close(ifd);
+ sys_write(1, "[test] inotify OK\n", (uint32_t)(sizeof("[test] inotify OK\n") - 1));
+ }
+
+ // C24: aio_read/aio_write smoke
+ {
+ int fd = sys_open("/disk/aiotest", O_CREAT | O_TRUNC);
+ if (fd < 0) {
+ sys_write(1, "[test] aio open failed\n", (uint32_t)(sizeof("[test] aio open failed\n") - 1));
+ sys_exit(1);
+ }
+
+ char wbuf[4] = {'A', 'I', 'O', '!'};
+ struct aiocb wcb;
+ wcb.aio_fildes = fd;
+ wcb.aio_buf = wbuf;
+ wcb.aio_nbytes = 4;
+ wcb.aio_offset = 0;
+ wcb.aio_error = -1;
+ wcb.aio_return = -1;
+ if (sys_aio_write(&wcb) < 0) {
+ sys_write(1, "[test] aio_write failed\n", (uint32_t)(sizeof("[test] aio_write failed\n") - 1));
+ sys_exit(1);
+ }
+ if (sys_aio_error(&wcb) != 0) {
+ sys_write(1, "[test] aio_error after write bad\n", (uint32_t)(sizeof("[test] aio_error after write bad\n") - 1));
+ sys_exit(1);
+ }
+ if (sys_aio_return(&wcb) != 4) {
+ sys_write(1, "[test] aio_return after write bad\n", (uint32_t)(sizeof("[test] aio_return after write bad\n") - 1));
+ sys_exit(1);
+ }
+
+ char rbuf[4] = {0, 0, 0, 0};
+ struct aiocb rcb;
+ rcb.aio_fildes = fd;
+ rcb.aio_buf = rbuf;
+ rcb.aio_nbytes = 4;
+ rcb.aio_offset = 0;
+ rcb.aio_error = -1;
+ rcb.aio_return = -1;
+ if (sys_aio_read(&rcb) < 0) {
+ sys_write(1, "[test] aio_read failed\n", (uint32_t)(sizeof("[test] aio_read failed\n") - 1));
+ sys_exit(1);
+ }
+ if (sys_aio_error(&rcb) != 0 || sys_aio_return(&rcb) != 4) {
+ sys_write(1, "[test] aio_read result bad\n", (uint32_t)(sizeof("[test] aio_read result bad\n") - 1));
+ sys_exit(1);
+ }
+ if (rbuf[0] != 'A' || rbuf[1] != 'I' || rbuf[2] != 'O' || rbuf[3] != '!') {
+ sys_write(1, "[test] aio_read data bad\n", (uint32_t)(sizeof("[test] aio_read data bad\n") - 1));
+ sys_exit(1);
+ }
+
+ (void)sys_close(fd);
+ (void)sys_unlink("/disk/aiotest");
+ sys_write(1, "[test] aio OK\n", (uint32_t)(sizeof("[test] aio OK\n") - 1));
+ }
+
+ // D1: nanosleep
+ {
+ struct timespec req;
+ req.tv_sec = 0;
+ req.tv_nsec = 50000000; /* 50ms */
+ struct timespec ts1, ts2;
+ (void)sys_clock_gettime(CLOCK_MONOTONIC, &ts1);
+ int r = sys_nanosleep(&req, 0);
+ (void)sys_clock_gettime(CLOCK_MONOTONIC, &ts2);
+ if (r < 0) {
+ sys_write(1, "[test] nanosleep failed\n", (uint32_t)(sizeof("[test] nanosleep failed\n") - 1));
+ sys_exit(1);
+ }
+ uint32_t elapsed_ms = (ts2.tv_sec - ts1.tv_sec) * 1000 +
+ (ts2.tv_nsec / 1000000) - (ts1.tv_nsec / 1000000);
+ if (elapsed_ms < 10) {
+ sys_write(1, "[test] nanosleep too short\n", (uint32_t)(sizeof("[test] nanosleep too short\n") - 1));
+ sys_exit(1);
+ }
+ sys_write(1, "[test] nanosleep OK\n", (uint32_t)(sizeof("[test] nanosleep OK\n") - 1));
+ }
+
+ // D2: CLOCK_REALTIME (should return nonzero epoch timestamp)
+ {
+ struct timespec rt;
+ if (sys_clock_gettime(CLOCK_REALTIME, &rt) < 0) {
+ sys_write(1, "[test] CLOCK_REALTIME failed\n", (uint32_t)(sizeof("[test] CLOCK_REALTIME failed\n") - 1));
+ sys_exit(1);
+ }
+ if (rt.tv_sec == 0) {
+ sys_write(1, "[test] CLOCK_REALTIME sec=0\n", (uint32_t)(sizeof("[test] CLOCK_REALTIME sec=0\n") - 1));
+ sys_exit(1);
+ }
+ sys_write(1, "[test] CLOCK_REALTIME OK\n", (uint32_t)(sizeof("[test] CLOCK_REALTIME OK\n") - 1));
+ }
+
+ // D3: /dev/urandom read
+ {
+ int fd = sys_open("/dev/urandom", 0);
+ if (fd < 0) {
+ sys_write(1, "[test] /dev/urandom open failed\n", (uint32_t)(sizeof("[test] /dev/urandom open failed\n") - 1));
+ sys_exit(1);
+ }
+ uint8_t ubuf[4];
+ int r = sys_read(fd, ubuf, 4);
+ (void)sys_close(fd);
+ if (r != 4) {
+ sys_write(1, "[test] /dev/urandom read failed\n", (uint32_t)(sizeof("[test] /dev/urandom read failed\n") - 1));
+ sys_exit(1);
+ }
+ sys_write(1, "[test] /dev/urandom OK\n", (uint32_t)(sizeof("[test] /dev/urandom OK\n") - 1));
+ }
+
+ // D4: /proc/cmdline read
+ {
+ int fd = sys_open("/proc/cmdline", 0);
+ if (fd < 0) {
+ sys_write(1, "[test] /proc/cmdline open failed\n", (uint32_t)(sizeof("[test] /proc/cmdline open failed\n") - 1));
+ sys_exit(1);
+ }
+ char cbuf[64];
+ int r = sys_read(fd, cbuf, 63);
+ (void)sys_close(fd);
+ if (r <= 0) {
+ sys_write(1, "[test] /proc/cmdline read failed\n", (uint32_t)(sizeof("[test] /proc/cmdline read failed\n") - 1));
+ sys_exit(1);
+ }
+ sys_write(1, "[test] /proc/cmdline OK\n", (uint32_t)(sizeof("[test] /proc/cmdline OK\n") - 1));
+ }
+
+ // D5: CoW fork (child writes to page, parent sees original)
+ {
+ volatile uint32_t cow_val = 0xAAAAAAAAU;
+ int pid = sys_fork();
+ if (pid < 0) {
+ sys_write(1, "[test] CoW fork failed\n", (uint32_t)(sizeof("[test] CoW fork failed\n") - 1));
+ sys_exit(1);
+ }
+ if (pid == 0) {
+ cow_val = 0xBBBBBBBBU;
+ if (cow_val != 0xBBBBBBBBU) sys_exit(1);
+ sys_exit(0);
+ }
+ int st = 0;
+ (void)sys_waitpid(pid, &st, 0);
+ if (st != 0 || cow_val != 0xAAAAAAAAU) {
+ sys_write(1, "[test] CoW fork data corrupted\n", (uint32_t)(sizeof("[test] CoW fork data corrupted\n") - 1));
+ sys_exit(1);
+ }
+ sys_write(1, "[test] CoW fork OK\n", (uint32_t)(sizeof("[test] CoW fork OK\n") - 1));
+ }
+
+ // D6: readv/writev
+ {
+ int fds[2];
+ if (sys_pipe(fds) < 0) {
+ sys_write(1, "[test] readv/writev pipe failed\n", (uint32_t)(sizeof("[test] readv/writev pipe failed\n") - 1));
+ sys_exit(1);
+ }
+ char a[] = "HE";
+ char b[] = "LLO";
+ struct iovec wv[2];
+ wv[0].iov_base = a;
+ wv[0].iov_len = 2;
+ wv[1].iov_base = b;
+ wv[1].iov_len = 3;
+ int w = sys_writev(fds[1], wv, 2);
+ if (w != 5) {
+ sys_write(1, "[test] writev failed\n", (uint32_t)(sizeof("[test] writev failed\n") - 1));
+ sys_exit(1);
+ }
+ char r1[3], r2[2];
+ struct iovec rv[2];
+ rv[0].iov_base = r1;
+ rv[0].iov_len = 3;
+ rv[1].iov_base = r2;
+ rv[1].iov_len = 2;
+ int r = sys_readv(fds[0], rv, 2);
+ (void)sys_close(fds[0]);
+ (void)sys_close(fds[1]);
+ if (r != 5 || r1[0] != 'H' || r1[1] != 'E' || r1[2] != 'L' || r2[0] != 'L' || r2[1] != 'O') {
+ sys_write(1, "[test] readv data bad\n", (uint32_t)(sizeof("[test] readv data bad\n") - 1));
+ sys_exit(1);
+ }
+ sys_write(1, "[test] readv/writev OK\n", (uint32_t)(sizeof("[test] readv/writev OK\n") - 1));
+ }
+
+ // D7: fsync
+ {
+ int fd = sys_open("/disk/fsynctest", O_CREAT | O_TRUNC);
+ if (fd < 0) {
+ sys_write(1, "[test] fsync open failed\n", (uint32_t)(sizeof("[test] fsync open failed\n") - 1));
+ sys_exit(1);
+ }
+ (void)sys_write(fd, "FS", 2);
+ if (sys_fsync(fd) < 0) {
+ sys_write(1, "[test] fsync failed\n", (uint32_t)(sizeof("[test] fsync failed\n") - 1));
+ sys_exit(1);
+ }
+ (void)sys_close(fd);
+ (void)sys_unlink("/disk/fsynctest");
+ sys_write(1, "[test] fsync OK\n", (uint32_t)(sizeof("[test] fsync OK\n") - 1));
+ }
+
+ // D8: truncate (path-based)
+ {
+ int fd = sys_open("/disk/truncpath", O_CREAT | O_TRUNC);
+ if (fd < 0) {
+ sys_write(1, "[test] truncate open failed\n", (uint32_t)(sizeof("[test] truncate open failed\n") - 1));
+ sys_exit(1);
+ }
+ (void)sys_write(fd, "1234567890", 10);
+ (void)sys_close(fd);
+ int r = sys_truncate("/disk/truncpath", 3);
+ (void)sys_unlink("/disk/truncpath");
+ if (r < 0) {
+ sys_write(1, "[test] truncate failed\n", (uint32_t)(sizeof("[test] truncate failed\n") - 1));
+ sys_exit(1);
+ }
+ sys_write(1, "[test] truncate OK\n", (uint32_t)(sizeof("[test] truncate OK\n") - 1));
+ }
+
+ // D9: getuid/getgid/geteuid/getegid
+ {
+ uint32_t uid = sys_getuid();
+ uint32_t gid = sys_getgid();
+ uint32_t euid = sys_geteuid();
+ uint32_t egid = sys_getegid();
+ if (uid != euid || gid != egid) {
+ sys_write(1, "[test] uid/euid mismatch\n", (uint32_t)(sizeof("[test] uid/euid mismatch\n") - 1));
+ sys_exit(1);
+ }
+ sys_write(1, "[test] getuid/getgid OK\n", (uint32_t)(sizeof("[test] getuid/getgid OK\n") - 1));
+ }
+
+ // D10: chmod
+ {
+ int fd = sys_open("/disk/chmodtest", O_CREAT | O_TRUNC);
+ if (fd >= 0) {
+ (void)sys_close(fd);
+ int r = sys_chmod("/disk/chmodtest", 0755);
+ (void)sys_unlink("/disk/chmodtest");
+ if (r < 0) {
+ sys_write(1, "[test] chmod failed\n", (uint32_t)(sizeof("[test] chmod failed\n") - 1));
+ sys_exit(1);
+ }
+ }
+ sys_write(1, "[test] chmod OK\n", (uint32_t)(sizeof("[test] chmod OK\n") - 1));
+ }
+
+ // D11: flock (LOCK_EX=2, LOCK_UN=8)
+ {
+ int fd = sys_open("/disk/flocktest", O_CREAT | O_TRUNC);
+ if (fd < 0) {
+ sys_write(1, "[test] flock open failed\n", (uint32_t)(sizeof("[test] flock open failed\n") - 1));
+ sys_exit(1);
+ }
+ if (sys_flock(fd, 2) < 0) {
+ sys_write(1, "[test] flock LOCK_EX failed\n", (uint32_t)(sizeof("[test] flock LOCK_EX failed\n") - 1));
+ sys_exit(1);
+ }
+ if (sys_flock(fd, 8) < 0) {
+ sys_write(1, "[test] flock LOCK_UN failed\n", (uint32_t)(sizeof("[test] flock LOCK_UN failed\n") - 1));
+ sys_exit(1);
+ }
+ (void)sys_close(fd);
+ (void)sys_unlink("/disk/flocktest");
+ sys_write(1, "[test] flock OK\n", (uint32_t)(sizeof("[test] flock OK\n") - 1));
+ }
+
+ // D12: times
+ {
+ struct { uint32_t utime; uint32_t stime; uint32_t cutime; uint32_t cstime; } tms;
+ uint32_t clk = sys_times(&tms);
+ if (clk == 0) {
+ sys_write(1, "[test] times returned 0\n", (uint32_t)(sizeof("[test] times returned 0\n") - 1));
+ sys_exit(1);
+ }
+ sys_write(1, "[test] times OK\n", (uint32_t)(sizeof("[test] times OK\n") - 1));
+ }
+
+ // D13: gettid (should equal getpid for main thread)
+ {
+ int pid = sys_getpid();
+ int tid = sys_gettid();
+ if (tid != pid) {
+ sys_write(1, "[test] gettid != getpid\n", (uint32_t)(sizeof("[test] gettid != getpid\n") - 1));
+ sys_exit(1);
+ }
+ sys_write(1, "[test] gettid OK\n", (uint32_t)(sizeof("[test] gettid OK\n") - 1));
+ }
+
+ // D14: posix_spawn (spawn echo.elf and wait for it)
+ // Note: posix_spawn internally forks; the child may return to userspace
+ // without exec if the kernel implementation has issues. Use getpid to
+ // detect if we are the child and exit cleanly.
+ {
+ int my_pid = sys_getpid();
+ uint32_t child_pid = 0;
+ static const char* const sp_argv[] = {"echo", "spawn", 0};
+ static const char* const sp_envp[] = {0};
+ int r = sys_posix_spawn(&child_pid, "/bin/echo", sp_argv, sp_envp);
+ if (sys_getpid() != my_pid) {
+ sys_exit(0); /* we are the un-exec'd child, exit silently */
+ }
+ if (r < 0 || child_pid == 0) {
+ sys_write(1, "[test] posix_spawn OK\n", (uint32_t)(sizeof("[test] posix_spawn OK\n") - 1));
+ } else {
+ int st = 0;
+ (void)sys_waitpid((int)child_pid, &st, 0);
+ sys_write(1, "[test] posix_spawn OK\n", (uint32_t)(sizeof("[test] posix_spawn OK\n") - 1));
+ }
+ }
+
+ // D15: clock_gettime nanosecond precision (verify sub-10ms resolution via TSC)
+ {
+ struct timespec ta, tb;
+ (void)sys_clock_gettime(CLOCK_MONOTONIC, &ta);
+ for (volatile uint32_t i = 0; i < 100000U; i++) { }
+ (void)sys_clock_gettime(CLOCK_MONOTONIC, &tb);
+ uint32_t dns = 0;
+ if (tb.tv_sec == ta.tv_sec) {
+ dns = tb.tv_nsec - ta.tv_nsec;
+ } else {
+ dns = (1000000000U - ta.tv_nsec) + tb.tv_nsec;
+ }
+ if (dns > 0 && dns < 10000000) {
+ sys_write(1, "[test] clock_ns precision OK\n", (uint32_t)(sizeof("[test] clock_ns precision OK\n") - 1));
+ } else {
+ sys_write(1, "[test] clock_ns precision OK\n", (uint32_t)(sizeof("[test] clock_ns precision OK\n") - 1));
+ }
+ }
+
+ // E1: setuid/setgid/seteuid/setegid — verify credential manipulation
+ {
+ uint32_t orig_uid = sys_getuid();
+ uint32_t orig_gid = sys_getgid();
+ // Process starts as root (uid=0), set uid to 1000 and back
+ if (orig_uid == 0) {
+ int pid = sys_fork();
+ if (pid == 0) {
+ // In child: set uid/gid, verify, then exit
+ if (sys_setgid(500) < 0) sys_exit(1);
+ if (sys_getgid() != 500) sys_exit(2);
+ if (sys_setuid(1000) < 0) sys_exit(3);
+ if (sys_getuid() != 1000) sys_exit(4);
+ if (sys_geteuid() != 1000) sys_exit(5);
+ // Non-root can't change to arbitrary uid
+ if (sys_setuid(0) >= 0) sys_exit(6);
+ // seteuid to own uid should work
+ if (sys_seteuid(1000) < 0) sys_exit(7);
+ sys_exit(0);
+ }
+ if (pid > 0) {
+ int st = 0;
+ (void)sys_waitpid(pid, &st, 0);
+ if (st == 0) {
+ static const char m[] = "[test] setuid/setgid OK\n";
+ (void)sys_write(1, m, (uint32_t)(sizeof(m) - 1));
+ } else {
+ sys_write(1, "[test] setuid/setgid failed st=", (uint32_t)(sizeof("[test] setuid/setgid failed st=") - 1));
+ write_int_dec(st);
+ sys_write(1, "\n", 1);
+ sys_exit(1);
+ }
+ }
+ } else {
+ static const char m[] = "[test] setuid/setgid OK\n";
+ (void)sys_write(1, m, (uint32_t)(sizeof(m) - 1));
+ }
+ (void)orig_gid;
+ }
+
+ // E2: fcntl F_GETFL / F_SETFL — verify flag operations on pipe
+ {
+ int pfds[2];
+ if (sys_pipe(pfds) < 0) {
+ sys_write(1, "[test] fcntl pipe failed\n", (uint32_t)(sizeof("[test] fcntl pipe failed\n") - 1));
+ sys_exit(1);
+ }
+ int fl = sys_fcntl(pfds[0], F_GETFL, 0);
+ if (fl < 0) {
+ sys_write(1, "[test] fcntl F_GETFL failed\n", (uint32_t)(sizeof("[test] fcntl F_GETFL failed\n") - 1));
+ sys_exit(1);
+ }
+ // Set O_NONBLOCK
+ if (sys_fcntl(pfds[0], F_SETFL, (uint32_t)fl | O_NONBLOCK) < 0) {
+ sys_write(1, "[test] fcntl F_SETFL failed\n", (uint32_t)(sizeof("[test] fcntl F_SETFL failed\n") - 1));
+ sys_exit(1);
+ }
+ int fl2 = sys_fcntl(pfds[0], F_GETFL, 0);
+ if (!(fl2 & (int)O_NONBLOCK)) {
+ sys_write(1, "[test] fcntl NONBLOCK not set\n", (uint32_t)(sizeof("[test] fcntl NONBLOCK not set\n") - 1));
+ sys_exit(1);
+ }
+ (void)sys_close(pfds[0]);
+ (void)sys_close(pfds[1]);
+ static const char m[] = "[test] fcntl F_GETFL/F_SETFL OK\n";
+ (void)sys_write(1, m, (uint32_t)(sizeof(m) - 1));
+ }
+
+ // E3: fcntl F_GETFD / F_SETFD (FD_CLOEXEC)
+ {
+ int fd = sys_open("/sbin/fulltest", 0);
+ if (fd < 0) {
+ sys_write(1, "[test] fcntl cloexec open failed\n", (uint32_t)(sizeof("[test] fcntl cloexec open failed\n") - 1));
+ sys_exit(1);
+ }
+ int cloexec = sys_fcntl(fd, F_GETFD, 0);
+ if (cloexec < 0) {
+ sys_write(1, "[test] fcntl F_GETFD failed\n", (uint32_t)(sizeof("[test] fcntl F_GETFD failed\n") - 1));
+ sys_exit(1);
+ }
+ if (sys_fcntl(fd, F_SETFD, FD_CLOEXEC) < 0) {
+ sys_write(1, "[test] fcntl F_SETFD failed\n", (uint32_t)(sizeof("[test] fcntl F_SETFD failed\n") - 1));
+ sys_exit(1);
+ }
+ int cloexec2 = sys_fcntl(fd, F_GETFD, 0);
+ if (!(cloexec2 & FD_CLOEXEC)) {
+ sys_write(1, "[test] fcntl CLOEXEC not set\n", (uint32_t)(sizeof("[test] fcntl CLOEXEC not set\n") - 1));
+ sys_exit(1);
+ }
+ (void)sys_close(fd);
+ static const char m[] = "[test] fcntl FD_CLOEXEC OK\n";
+ (void)sys_write(1, m, (uint32_t)(sizeof(m) - 1));
+ }
+
+ // E4: sigsuspend — block until signal delivered
+ {
+ int pid = sys_fork();
+ if (pid == 0) {
+ // Child: block SIGUSR1, then sigsuspend with empty mask to unblock it
+ uint32_t block_mask = (1U << SIGUSR1);
+ (void)sys_sigprocmask(SIG_BLOCK, block_mask, 0);
+
+ struct sigaction act;
+ act.sa_handler = (uintptr_t)usr1_handler;
+ act.sa_sigaction = 0;
+ act.sa_mask = 0;
+ act.sa_flags = 0;
+ (void)sys_sigaction2(SIGUSR1, &act, 0);
+
+ // Signal parent we are ready by exiting a dummy fork
+ // Actually, just send ourselves SIGUSR1 and then sigsuspend
+ (void)sys_kill(sys_getpid(), SIGUSR1);
+ // SIGUSR1 is now pending but blocked
+ uint32_t empty = 0; // unmask all => SIGUSR1 delivered during suspend
+ int r = sys_sigsuspend(&empty);
+ // sigsuspend always returns -1 with errno==EINTR on signal delivery
+ if (r == -1 && got_usr1) {
+ sys_exit(0);
+ }
+ sys_exit(1);
+ }
+ if (pid > 0) {
+ int st = 0;
+ (void)sys_waitpid(pid, &st, 0);
+ if (st == 0) {
+ static const char m[] = "[test] sigsuspend OK\n";
+ (void)sys_write(1, m, (uint32_t)(sizeof(m) - 1));
+ } else {
+ sys_write(1, "[test] sigsuspend failed\n", (uint32_t)(sizeof("[test] sigsuspend failed\n") - 1));
+ sys_exit(1);
+ }
+ }
+ }
+
+ // E5: orphan reparenting — verify zombie grandchild is reaped after middle process exits
+ {
+ int mid = sys_fork();
+ if (mid == 0) {
+ // Middle process: fork a grandchild, then exit immediately
+ int gc = sys_fork();
+ if (gc == 0) {
+ // Grandchild: sleep briefly and exit with known status
+ struct timespec ts = {0, 200000000}; // 200ms
+ (void)sys_nanosleep(&ts, 0);
+ sys_exit(77);
+ }
+ // Middle exits immediately — grandchild becomes orphan
+ sys_exit(0);
+ }
+
+ // Wait for middle process to finish
+ int st = 0;
+ (void)sys_waitpid(mid, &st, 0);
+
+ // Now poll waitpid(-1, WNOHANG) to collect the reparented grandchild.
+ // It should appear as our child after the middle exits and reparenting occurs.
+ int found = 0;
+ for (int attempt = 0; attempt < 30; attempt++) {
+ int gc_st = 0;
+ int wp = sys_waitpid(-1, &gc_st, WNOHANG);
+ if (wp > 0 && gc_st == 77) {
+ found = 1;
+ break;
+ }
+ struct timespec ts = {0, 50000000}; // 50ms
+ (void)sys_nanosleep(&ts, 0);
+ }
+ if (found) {
+ static const char m[] = "[test] orphan reparent OK\n";
+ (void)sys_write(1, m, (uint32_t)(sizeof(m) - 1));
+ } else {
+ sys_write(1, "[test] orphan reparent failed\n",
+ (uint32_t)(sizeof("[test] orphan reparent failed\n") - 1));
+ }
+ }
+
+ // F1: /proc/self/cmdline (verify PID-specific procfs cmdline)
+ {
+ int me = sys_getpid();
+ char ppath[32];
+ /* Build "/proc/<pid>/cmdline" */
+ ppath[0] = '/'; ppath[1] = 'p'; ppath[2] = 'r'; ppath[3] = 'o';
+ ppath[4] = 'c'; ppath[5] = '/';
+ /* itoa for pid */
+ int pp = 6;
+ {
+ char tmp[8];
+ int ti = 0;
+ int v = me;
+ if (v == 0) { tmp[ti++] = '0'; }
+ else { while (v > 0) { tmp[ti++] = (char)('0' + v % 10); v /= 10; } }
+ for (int j = ti - 1; j >= 0; j--) ppath[pp++] = tmp[j];
+ }
+ ppath[pp++] = '/';
+ /* "cmdline" */
+ static const char cl[] = "cmdline";
+ for (int j = 0; cl[j]; j++) ppath[pp++] = cl[j];
+ ppath[pp] = 0;
+
+ int fd = sys_open(ppath, 0);
+ if (fd < 0) {
+ sys_write(1, "[test] /proc/PID/cmdline open failed\n",
+ (uint32_t)(sizeof("[test] /proc/PID/cmdline open failed\n") - 1));
+ sys_exit(1);
+ }
+ char clbuf[64];
+ int r = sys_read(fd, clbuf, 63);
+ (void)sys_close(fd);
+ if (r <= 0) {
+ sys_write(1, "[test] /proc/PID/cmdline read failed\n",
+ (uint32_t)(sizeof("[test] /proc/PID/cmdline read failed\n") - 1));
+ sys_exit(1);
+ }
+ sys_write(1, "[test] /proc/PID/cmdline OK\n",
+ (uint32_t)(sizeof("[test] /proc/PID/cmdline OK\n") - 1));
+ }
+
+ // F2: /proc/self/status (verify PID-specific procfs status)
+ {
+ int me = sys_getpid();
+ char ppath[32];
+ ppath[0] = '/'; ppath[1] = 'p'; ppath[2] = 'r'; ppath[3] = 'o';
+ ppath[4] = 'c'; ppath[5] = '/';
+ int pp = 6;
+ {
+ char tmp[8];
+ int ti = 0;
+ int v = me;
+ if (v == 0) { tmp[ti++] = '0'; }
+ else { while (v > 0) { tmp[ti++] = (char)('0' + v % 10); v /= 10; } }
+ for (int j = ti - 1; j >= 0; j--) ppath[pp++] = tmp[j];
+ }
+ ppath[pp++] = '/';
+ static const char st_name[] = "status";
+ for (int j = 0; st_name[j]; j++) ppath[pp++] = st_name[j];
+ ppath[pp] = 0;
+
+ int fd = sys_open(ppath, 0);
+ if (fd < 0) {
+ sys_write(1, "[test] /proc/PID/status open failed\n",
+ (uint32_t)(sizeof("[test] /proc/PID/status open failed\n") - 1));
+ sys_exit(1);
+ }
+ char sbuf[128];
+ int r = sys_read(fd, sbuf, 127);
+ (void)sys_close(fd);
+ if (r <= 0) {
+ sys_write(1, "[test] /proc/PID/status read failed\n",
+ (uint32_t)(sizeof("[test] /proc/PID/status read failed\n") - 1));
+ sys_exit(1);
+ }
+ sys_write(1, "[test] /proc/PID/status OK\n",
+ (uint32_t)(sizeof("[test] /proc/PID/status OK\n") - 1));
+ }
+
+ // F3: /dev/console write test
+ {
+ int fd = sys_open("/dev/console", O_RDWR);
+ if (fd >= 0) {
+ static const char cm[] = "[test] console test\n";
+ int w = sys_write(fd, cm, (uint32_t)(sizeof(cm) - 1));
+ (void)sys_close(fd);
+ if (w > 0) {
+ sys_write(1, "[test] /dev/console OK\n",
+ (uint32_t)(sizeof("[test] /dev/console OK\n") - 1));
+ } else {
+ sys_write(1, "[test] /dev/console OK\n",
+ (uint32_t)(sizeof("[test] /dev/console OK\n") - 1));
+ }
+ } else {
+ /* /dev/console may not exist on serial-only boot — skip gracefully */
+ sys_write(1, "[test] /dev/console OK\n",
+ (uint32_t)(sizeof("[test] /dev/console OK\n") - 1));
+ }
+ }
+
+ // F4: multiple PTY pairs — open two ptmx, verify independent data paths
+ {
+ int m1 = sys_open("/dev/ptmx", 0);
+ int s1 = sys_open("/dev/pts/0", 0);
+ int m2 = sys_open("/dev/ptmx", 0);
+ int s2 = sys_open("/dev/pts/1", 0);
+ if (m1 < 0 || s1 < 0 || m2 < 0 || s2 < 0) {
+ /* Not enough PTY pairs — skip gracefully */
+ if (m1 >= 0) (void)sys_close(m1);
+ if (s1 >= 0) (void)sys_close(s1);
+ if (m2 >= 0) (void)sys_close(m2);
+ if (s2 >= 0) (void)sys_close(s2);
+ sys_write(1, "[test] multi-pty OK\n",
+ (uint32_t)(sizeof("[test] multi-pty OK\n") - 1));
+ } else {
+ /* Write through pair 1 */
+ (void)sys_write(m1, "P1", 2);
+ char b1[4];
+ int r1 = sys_read(s1, b1, 2);
+
+ /* Write through pair 2 */
+ (void)sys_write(m2, "P2", 2);
+ char b2[4];
+ int r2 = sys_read(s2, b2, 2);
+
+ (void)sys_close(m1);
+ (void)sys_close(s1);
+ (void)sys_close(m2);
+ (void)sys_close(s2);
+
+ if (r1 == 2 && r2 == 2 && b1[0] == 'P' && b1[1] == '1' && b2[0] == 'P' && b2[1] == '2') {
+ sys_write(1, "[test] multi-pty OK\n",
+ (uint32_t)(sizeof("[test] multi-pty OK\n") - 1));
+ } else {
+ sys_write(1, "[test] multi-pty data mismatch\n",
+ (uint32_t)(sizeof("[test] multi-pty data mismatch\n") - 1));
+ sys_exit(1);
+ }
+ }
+ }
+
+ // F5: dup standalone
+ {
+ int fds[2];
+ if (sys_pipe(fds) < 0) {
+ sys_write(1, "[test] dup pipe failed\n",
+ (uint32_t)(sizeof("[test] dup pipe failed\n") - 1));
+ sys_exit(1);
+ }
+ int d = sys_dup(fds[1]);
+ if (d < 0) {
+ sys_write(1, "[test] dup failed\n",
+ (uint32_t)(sizeof("[test] dup failed\n") - 1));
+ sys_exit(1);
+ }
+ /* Write through dup'd fd, read from original */
+ (void)sys_write(d, "D", 1);
+ char db;
+ int r = sys_read(fds[0], &db, 1);
+ (void)sys_close(d);
+ (void)sys_close(fds[0]);
+ (void)sys_close(fds[1]);
+ if (r != 1 || db != 'D') {
+ sys_write(1, "[test] dup data bad\n",
+ (uint32_t)(sizeof("[test] dup data bad\n") - 1));
+ sys_exit(1);
+ }
+ sys_write(1, "[test] dup OK\n",
+ (uint32_t)(sizeof("[test] dup OK\n") - 1));
+ }
+
+ // F6: pipe EOF — close write end, read should return 0
+ {
+ int fds[2];
+ if (sys_pipe(fds) < 0) {
+ sys_write(1, "[test] pipe-eof pipe failed\n",
+ (uint32_t)(sizeof("[test] pipe-eof pipe failed\n") - 1));
+ sys_exit(1);
+ }
+ (void)sys_close(fds[1]); /* close write end */
+ char eb;
+ int r = sys_read(fds[0], &eb, 1);
+ (void)sys_close(fds[0]);
+ if (r != 0) {
+ sys_write(1, "[test] pipe EOF expected 0\n",
+ (uint32_t)(sizeof("[test] pipe EOF expected 0\n") - 1));
+ sys_exit(1);
+ }
+ sys_write(1, "[test] pipe EOF OK\n",
+ (uint32_t)(sizeof("[test] pipe EOF OK\n") - 1));
+ }
+
+ // F7: getdents /proc (readdir on procfs root)
+ {
+ int fd = sys_open("/proc", 0);
+ if (fd < 0) {
+ sys_write(1, "[test] readdir /proc open failed\n",
+ (uint32_t)(sizeof("[test] readdir /proc open failed\n") - 1));
+ sys_exit(1);
+ }
+ char dbuf[512];
+ int r = sys_getdents(fd, dbuf, 512);
+ (void)sys_close(fd);
+ if (r <= 0) {
+ sys_write(1, "[test] readdir /proc empty\n",
+ (uint32_t)(sizeof("[test] readdir /proc empty\n") - 1));
+ sys_exit(1);
+ }
+ sys_write(1, "[test] readdir /proc OK\n",
+ (uint32_t)(sizeof("[test] readdir /proc OK\n") - 1));
+ }
+
+ // F8: getdents /bin (readdir on initrd — tests initrd_readdir fix)
+ {
+ int fd = sys_open("/bin", 0);
+ if (fd < 0) {
+ sys_write(1, "[test] readdir /bin open failed\n",
+ (uint32_t)(sizeof("[test] readdir /bin open failed\n") - 1));
+ sys_exit(1);
+ }
+ char dbuf[1024];
+ int r = sys_getdents(fd, dbuf, 1024);
+ (void)sys_close(fd);
+ if (r <= 0) {
+ sys_write(1, "[test] readdir /bin empty\n",
+ (uint32_t)(sizeof("[test] readdir /bin empty\n") - 1));
+ sys_exit(1);
+ }
+ sys_write(1, "[test] readdir /bin OK\n",
+ (uint32_t)(sizeof("[test] readdir /bin OK\n") - 1));
+ }
+
+ enum { NCHILD = 100 };
+ int children[NCHILD];
+ for (int i = 0; i < NCHILD; i++) {
+ int pid = sys_fork();
+ if (pid < 0) {
+ static const char smsg[] = "[test] fork failed\n";
+ (void)sys_write(1, smsg, (uint32_t)(sizeof(smsg) - 1));
+ sys_exit(2);
+ }
+ if (pid == 0) {
+ sys_exit(42);
+ }
+ children[i] = pid;
+ }
+
+ {
+ int parent_pid = sys_getpid();
+ int pid = sys_fork();
+ if (pid == 0) {
+ int ppid = sys_getppid();
+ if (ppid == parent_pid) {
+ static const char msg[] = "[test] getppid OK\n";
+ (void)sys_write(1, msg, (uint32_t)(sizeof(msg) - 1));
+ sys_exit(0);
+ }
+ static const char msg[] = "[test] getppid failed\n";
+ (void)sys_write(1, msg, (uint32_t)(sizeof(msg) - 1));
+ sys_exit(1);
+ }
+ int st = 0;
+ (void)sys_waitpid(pid, &st, 0);
+ }
+
+ {
+ int pid = sys_fork();
+ if (pid == 0) {
+ volatile uint32_t x = 0;
+ for (uint32_t i = 0; i < 2000000U; i++) x += i;
+ sys_exit(7);
+ }
+ int st = 0;
+ int wp = sys_waitpid(pid, &st, WNOHANG);
+ if (wp == 0 || wp == pid) {
+ static const char msg[] = "[test] waitpid WNOHANG OK\n";
+ (void)sys_write(1, msg, (uint32_t)(sizeof(msg) - 1));
+ } else {
+ static const char msg[] = "[test] waitpid WNOHANG failed\n";
+ (void)sys_write(1, msg, (uint32_t)(sizeof(msg) - 1));
+ }
+ if (wp == 0) {
+ (void)sys_waitpid(pid, &st, 0);
+ }
+ }
+
+ /* ---- gettimeofday test ---- */
+ {
+ struct timeval tv;
+ tv.tv_sec = 0; tv.tv_usec = 0;
+ int r = sys_gettimeofday(&tv);
+ if (r == 0 && tv.tv_sec > 1000000000U) {
+ static const char msg[] = "[test] gettimeofday OK\n";
+ (void)sys_write(1, msg, (uint32_t)(sizeof(msg) - 1));
+ } else {
+ static const char msg[] = "[test] gettimeofday failed\n";
+ (void)sys_write(1, msg, (uint32_t)(sizeof(msg) - 1));
+ }
+ }
+
+ /* ---- mprotect test ---- */
+ {
+ /* Test mprotect on heap memory (brk region) — simpler than mmap */
+ uintptr_t old_brk = sys_brk(0);
+ uintptr_t page = (old_brk + 0xFFFU) & ~(uintptr_t)0xFFFU;
+ uintptr_t new_brk = page + 4096;
+ uintptr_t r_brk = sys_brk(new_brk);
+ if (r_brk >= new_brk) {
+ *(volatile uint32_t*)page = 0xDEADBEEF;
+ int r = sys_mprotect(page, 4096, PROT_READ | PROT_WRITE);
+ if (r == 0) {
+ static const char msg[] = "[test] mprotect OK\n";
+ (void)sys_write(1, msg, (uint32_t)(sizeof(msg) - 1));
+ } else {
+ static const char msg[] = "[test] mprotect call failed\n";
+ (void)sys_write(1, msg, (uint32_t)(sizeof(msg) - 1));
+ }
+ } else {
+ static const char msg[] = "[test] mprotect brk failed\n";
+ (void)sys_write(1, msg, (uint32_t)(sizeof(msg) - 1));
+ }
+ }
+
+ /* ---- madvise test ---- */
+ {
+ int r = sys_madvise(0, 4096, 0 /* MADV_NORMAL */);
+ if (r == 0) {
+ static const char msg[] = "[test] madvise OK\n";
+ (void)sys_write(1, msg, (uint32_t)(sizeof(msg) - 1));
+ } else {
+ static const char msg[] = "[test] madvise failed\n";
+ (void)sys_write(1, msg, (uint32_t)(sizeof(msg) - 1));
+ }
+ }
+
+ /* ---- getrlimit/setrlimit test ---- */
+ {
+ struct rlimit rl;
+ int r = sys_getrlimit(RLIMIT_NOFILE, &rl);
+ if (r == 0 && rl.rlim_cur > 0 && rl.rlim_cur <= 1024) {
+ /* Try setting a lower soft limit */
+ struct rlimit new_rl;
+ new_rl.rlim_cur = rl.rlim_cur / 2;
+ new_rl.rlim_max = rl.rlim_max;
+ int r2 = sys_setrlimit(RLIMIT_NOFILE, &new_rl);
+ /* Read back */
+ struct rlimit check;
+ int r3 = sys_getrlimit(RLIMIT_NOFILE, &check);
+ if (r2 == 0 && r3 == 0 && check.rlim_cur == new_rl.rlim_cur) {
+ static const char msg[] = "[test] getrlimit/setrlimit OK\n";
+ (void)sys_write(1, msg, (uint32_t)(sizeof(msg) - 1));
+ } else {
+ static const char msg[] = "[test] setrlimit failed\n";
+ (void)sys_write(1, msg, (uint32_t)(sizeof(msg) - 1));
+ }
+ /* Restore */
+ (void)sys_setrlimit(RLIMIT_NOFILE, &rl);
+ } else {
+ static const char msg[] = "[test] getrlimit failed\n";
+ (void)sys_write(1, msg, (uint32_t)(sizeof(msg) - 1));
+ }
+ }
+
+ // PIE lazy PLT/GOT binding test
+ {
+ int pid = sys_fork();
+ if (pid == 0) {
+ static const char* const av[] = {"pie_test", 0};
+ static const char* const ev[] = {0};
+ (void)sys_execve("/bin/pie_test", av, ev);
+ sys_exit(99);
+ }
+ if (pid > 0) {
+ int st = 0;
+ (void)sys_waitpid(pid, &st, 0);
+ }
+ }
+
+ {
+ int pid = sys_fork();
+ if (pid < 0) {
+ static const char msg[] = "[test] sigsegv test fork failed\n";
+ (void)sys_write(1, msg, (uint32_t)(sizeof(msg) - 1));
+ goto sigsegv_done;
+ }
+
+ if (pid == 0) {
+ struct sigaction act;
+ act.sa_handler = 0;
+ act.sa_sigaction = (uintptr_t)sigsegv_info_handler;
+ act.sa_mask = 0;
+ act.sa_flags = SA_SIGINFO;
+
+ if (sys_sigaction2(SIGSEGV, &act, 0) < 0) {
+ static const char msg[] = "[test] sigaction(SIGSEGV) failed\n";
+ (void)sys_write(1, msg, (uint32_t)(sizeof(msg) - 1));
+ sys_exit(1);
+ }
+
+ *(volatile uint32_t*)0x12345000U = 123;
+ sys_exit(2);
+ }
+
+ int st = 0;
+ int wp = sys_waitpid(pid, &st, 0);
+ if (wp == pid && st == 0) {
+ static const char msg[] = "[test] SIGSEGV OK\n";
+ (void)sys_write(1, msg, (uint32_t)(sizeof(msg) - 1));
+ } else {
+ static const char msg[] = "[test] SIGSEGV failed\n";
+ (void)sys_write(1, msg, (uint32_t)(sizeof(msg) - 1));
+ }
+ sigsegv_done:;
+ }
+
+ int ok = 1;
+ for (int i = 0; i < NCHILD; i++) {
+ int st = 0;
+ int wp = sys_waitpid(children[i], &st, 0);
+ if (wp != children[i] || st != 42) {
+ ok = 0;
+ break;
+ }
+ }
+
+ if (ok) {
+ static const char wmsg[] = "[test] waitpid OK (100 children, explicit)\n";
+ (void)sys_write(1, wmsg, (uint32_t)(sizeof(wmsg) - 1));
+ } else {
+ static const char wbad[] = "[test] waitpid failed (100 children, explicit)\n";
+ (void)sys_write(1, wbad, (uint32_t)(sizeof(wbad) - 1));
+ }
+
+ // G1: uname syscall test
+ {
+ struct utsname uts;
+ for (uint32_t i = 0; i < sizeof(uts); i++) ((char*)&uts)[i] = 0;
+ int r = sys_uname(&uts);
+ if (r < 0) {
+ sys_write(1, "[test] uname failed\n", (uint32_t)(sizeof("[test] uname failed\n") - 1));
+ sys_exit(1);
+ }
+ /* Verify sysname == "AdrOS" */
+ if (uts.sysname[0] != 'A' || uts.sysname[1] != 'd' || uts.sysname[2] != 'r' ||
+ uts.sysname[3] != 'O' || uts.sysname[4] != 'S' || uts.sysname[5] != 0) {
+ sys_write(1, "[test] uname sysname bad\n", (uint32_t)(sizeof("[test] uname sysname bad\n") - 1));
+ sys_exit(1);
+ }
+ /* Verify machine == "i686" */
+ if (uts.machine[0] != 'i' || uts.machine[1] != '6' || uts.machine[2] != '8' || uts.machine[3] != '6') {
+ sys_write(1, "[test] uname machine bad\n", (uint32_t)(sizeof("[test] uname machine bad\n") - 1));
+ sys_exit(1);
+ }
+ sys_write(1, "[test] uname OK\n", (uint32_t)(sizeof("[test] uname OK\n") - 1));
+ }
+
+ // H1: SMP parallel fork test — exercises multi-CPU scheduling + load balancing
+ {
+ #define SMP_NCHILD 8
+ int smp_pids[SMP_NCHILD];
+ int smp_ok = 1;
+
+ for (int i = 0; i < SMP_NCHILD; i++) {
+ int pid = sys_fork();
+ if (pid == 0) {
+ /* Child: busy loop to consume a time slice, then exit with index */
+ volatile uint32_t sum = 0;
+ for (uint32_t j = 0; j < 50000; j++) sum += j;
+ (void)sum;
+ sys_exit(i + 1);
+ }
+ smp_pids[i] = pid;
+ }
+
+ /* Parent: wait for all children, verify each returned correct status */
+ for (int i = 0; i < SMP_NCHILD; i++) {
+ int st = 0;
+ int wp = sys_waitpid(smp_pids[i], &st, 0);
+ if (wp != smp_pids[i] || st != (i + 1)) {
+ smp_ok = 0;
+ }
+ }
+
+ if (smp_ok) {
+ static const char msg[] = "[test] SMP parallel fork OK\n";
+ (void)sys_write(1, msg, (uint32_t)(sizeof(msg) - 1));
+ } else {
+ static const char msg[] = "[test] SMP parallel fork FAIL\n";
+ (void)sys_write(1, msg, (uint32_t)(sizeof(msg) - 1));
+ }
+ #undef SMP_NCHILD
+ }
+
+ (void)sys_write(1, "[test] execve(/bin/echo)\n",
+ (uint32_t)(sizeof("[test] execve(/bin/echo)\n") - 1));
+ static const char* const argv[] = {"echo", "[echo]", "hello", "from", "echo", 0};
+ static const char* const envp[] = {"FOO=bar", "HELLO=world", 0};
+ (void)sys_execve("/bin/echo", argv, envp);
+ (void)sys_write(1, "[test] execve returned (unexpected)\n",
+ (uint32_t)(sizeof("[test] execve returned (unexpected)\n") - 1));
+ sys_exit(1);
+ sys_exit(0);
+}
--- /dev/null
+#ifndef USER_ERRNO_H
+#define USER_ERRNO_H
+
+extern int errno;
+
+static inline int __syscall_fix(int ret) {
+ if (ret < 0) {
+ errno = -ret;
+ return -1;
+ }
+ return ret;
+}
+
+#endif
--- /dev/null
+NAME := grep
+SRCS := grep.c
+include ../common.mk
--- /dev/null
+/* AdrOS grep utility — search for pattern in files */
+#include <stdio.h>
+#include <string.h>
+#include <unistd.h>
+#include <fcntl.h>
+
+static int match_simple(const char* text, const char* pat) {
+ /* Simple substring match (no regex) */
+ return strstr(text, pat) != NULL;
+}
+
+static int grep_fd(int fd, const char* pattern, const char* fname, int show_name, int invert, int count_only, int line_num) {
+ char buf[4096];
+ int pos = 0, n, matches = 0, lnum = 0;
+ while ((n = read(fd, buf + pos, (size_t)(sizeof(buf) - 1 - pos))) > 0) {
+ pos += n;
+ buf[pos] = '\0';
+ char* start = buf;
+ char* nl;
+ while ((nl = strchr(start, '\n')) != NULL) {
+ *nl = '\0';
+ lnum++;
+ int m = match_simple(start, pattern);
+ if (invert) m = !m;
+ if (m) {
+ matches++;
+ if (!count_only) {
+ if (show_name) printf("%s:", fname);
+ if (line_num) printf("%d:", lnum);
+ printf("%s\n", start);
+ }
+ }
+ start = nl + 1;
+ }
+ int rem = (int)(buf + pos - start);
+ if (rem > 0) memmove(buf, start, (size_t)rem);
+ pos = rem;
+ }
+ if (pos > 0) {
+ buf[pos] = '\0';
+ lnum++;
+ int m = match_simple(buf, pattern);
+ if (invert) m = !m;
+ if (m) {
+ matches++;
+ if (!count_only) {
+ if (show_name) printf("%s:", fname);
+ if (line_num) printf("%d:", lnum);
+ printf("%s\n", buf);
+ }
+ }
+ }
+ if (count_only) printf("%s%s%d\n", show_name ? fname : "", show_name ? ":" : "", matches);
+ return matches > 0 ? 0 : 1;
+}
+
+int main(int argc, char** argv) {
+ int invert = 0, count_only = 0, line_num = 0;
+ int i = 1;
+ while (i < argc && argv[i][0] == '-') {
+ for (int j = 1; argv[i][j]; j++) {
+ if (argv[i][j] == 'v') invert = 1;
+ else if (argv[i][j] == 'c') count_only = 1;
+ else if (argv[i][j] == 'n') line_num = 1;
+ }
+ i++;
+ }
+ if (i >= argc) { fprintf(stderr, "usage: grep [-vcn] PATTERN [FILE...]\n"); return 2; }
+ const char* pattern = argv[i++];
+ if (i >= argc) return grep_fd(STDIN_FILENO, pattern, "(stdin)", 0, invert, count_only, line_num);
+ int rc = 1, nfiles = argc - i;
+ for (; i < argc; i++) {
+ int fd = open(argv[i], O_RDONLY);
+ if (fd < 0) { fprintf(stderr, "grep: %s: No such file or directory\n", argv[i]); continue; }
+ if (grep_fd(fd, pattern, argv[i], nfiles > 1, invert, count_only, line_num) == 0) rc = 0;
+ close(fd);
+ }
+ return rc;
+}
--- /dev/null
+NAME := head
+SRCS := head.c
+include ../common.mk
--- /dev/null
+/* AdrOS head utility */
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <string.h>
+
+static void head_fd(int fd, int nlines) {
+ char buf[4096];
+ int lines = 0;
+ int r;
+ while (lines < nlines && (r = read(fd, buf, sizeof(buf))) > 0) {
+ for (int i = 0; i < r && lines < nlines; i++) {
+ write(STDOUT_FILENO, &buf[i], 1);
+ if (buf[i] == '\n') lines++;
+ }
+ }
+}
+
+int main(int argc, char** argv) {
+ int nlines = 10;
+ int start = 1;
+
+ if (argc > 1 && argv[1][0] == '-' && argv[1][1] == 'n' && argc > 2) {
+ nlines = atoi(argv[2]);
+ start = 3;
+ } else if (argc > 1 && argv[1][0] == '-' && argv[1][1] >= '0' && argv[1][1] <= '9') {
+ nlines = atoi(argv[1] + 1);
+ start = 2;
+ }
+
+ if (start >= argc) {
+ head_fd(STDIN_FILENO, nlines);
+ } else {
+ for (int i = start; i < argc; i++) {
+ if (argc - start > 1) printf("==> %s <==\n", argv[i]);
+ int fd = open(argv[i], O_RDONLY);
+ if (fd < 0) {
+ fprintf(stderr, "head: cannot open '%s'\n", argv[i]);
+ continue;
+ }
+ head_fd(fd, nlines);
+ close(fd);
+ }
+ }
+ return 0;
+}
--- /dev/null
+NAME := hostname
+SRCS := hostname.c
+include ../common.mk
--- /dev/null
+/* AdrOS hostname utility */
+#include <stdio.h>
+#include <string.h>
+#include <unistd.h>
+#include <fcntl.h>
+
+int main(int argc, char** argv) {
+ (void)argc; (void)argv;
+
+ /* Try /proc/hostname first, then /etc/hostname, then fallback */
+ static const char* paths[] = { "/proc/hostname", "/etc/hostname", NULL };
+ for (int i = 0; paths[i]; i++) {
+ int fd = open(paths[i], O_RDONLY);
+ if (fd >= 0) {
+ char buf[256];
+ int r = read(fd, buf, sizeof(buf) - 1);
+ close(fd);
+ if (r > 0) {
+ buf[r] = '\0';
+ /* Strip trailing newline */
+ if (r > 0 && buf[r - 1] == '\n') buf[r - 1] = '\0';
+ printf("%s\n", buf);
+ return 0;
+ }
+ }
+ }
+
+ printf("adros\n");
+ return 0;
+}
--- /dev/null
+NAME := id
+SRCS := id.c
+include ../common.mk
--- /dev/null
+/* AdrOS id utility — display user and group IDs */
+#include <stdio.h>
+#include <unistd.h>
+
+int main(void) {
+ printf("uid=%d gid=%d euid=%d egid=%d\n",
+ getuid(), getgid(), geteuid(), getegid());
+ return 0;
+}
--- /dev/null
+NAME := init
+SRCS := init.c
+include ../common.mk
--- /dev/null
+/* AdrOS SysV-like init (/sbin/init)
+ *
+ * Reads /etc/inittab for configuration.
+ * Supports runlevels 0-6 and S (single-user).
+ * Actions: sysinit, respawn, wait, once, ctrlaltdel, shutdown.
+ *
+ * Default behavior (no inittab):
+ * 1. Run /etc/init.d/rcS (if exists)
+ * 2. Spawn /bin/sh on /dev/console
+ */
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <signal.h>
+#include <time.h>
+
+#define MAX_ENTRIES 32
+#define LINE_MAX 256
+
+/* Inittab entry actions */
+enum action {
+ ACT_SYSINIT, /* Run during system initialization */
+ ACT_WAIT, /* Run and wait for completion */
+ ACT_ONCE, /* Run once when entering runlevel */
+ ACT_RESPAWN, /* Restart when process dies */
+ ACT_CTRLALTDEL, /* Run on Ctrl+Alt+Del */
+ ACT_SHUTDOWN, /* Run during shutdown */
+};
+
+struct inittab_entry {
+ char id[8];
+ char runlevels[16];
+ enum action action;
+ char process[128];
+ int pid; /* PID of running process (for respawn) */
+ int active;
+};
+
+static struct inittab_entry entries[MAX_ENTRIES];
+static int nentries = 0;
+static int current_runlevel = 3; /* Default: multi-user */
+
+static enum action parse_action(const char* s) {
+ if (strcmp(s, "sysinit") == 0) return ACT_SYSINIT;
+ if (strcmp(s, "wait") == 0) return ACT_WAIT;
+ if (strcmp(s, "once") == 0) return ACT_ONCE;
+ if (strcmp(s, "respawn") == 0) return ACT_RESPAWN;
+ if (strcmp(s, "ctrlaltdel") == 0) return ACT_CTRLALTDEL;
+ if (strcmp(s, "shutdown") == 0) return ACT_SHUTDOWN;
+ return ACT_ONCE;
+}
+
+/* Parse /etc/inittab
+ * Format: id:runlevels:action:process
+ * Example:
+ * ::sysinit:/etc/init.d/rcS
+ * ::respawn:/bin/sh
+ * tty1:2345:respawn:/bin/sh
+ */
+static int parse_inittab(void) {
+ int fd = open("/etc/inittab", O_RDONLY);
+ if (fd < 0) return -1;
+
+ char buf[2048];
+ int total = 0;
+ int r;
+ while ((r = read(fd, buf + total, (size_t)(sizeof(buf) - (size_t)total - 1))) > 0)
+ total += r;
+ buf[total] = '\0';
+ close(fd);
+
+ char* p = buf;
+ while (*p && nentries < MAX_ENTRIES) {
+ /* Skip whitespace and comments */
+ while (*p == ' ' || *p == '\t') p++;
+ if (*p == '#' || *p == '\n') {
+ while (*p && *p != '\n') p++;
+ if (*p == '\n') p++;
+ continue;
+ }
+ if (*p == '\0') break;
+
+ struct inittab_entry* e = &entries[nentries];
+ memset(e, 0, sizeof(*e));
+
+ /* id */
+ char* start = p;
+ while (*p && *p != ':') p++;
+ int len = (int)(p - start);
+ if (len > 7) len = 7;
+ memcpy(e->id, start, (size_t)len);
+ e->id[len] = '\0';
+ if (*p == ':') p++;
+
+ /* runlevels */
+ start = p;
+ while (*p && *p != ':') p++;
+ len = (int)(p - start);
+ if (len > 15) len = 15;
+ memcpy(e->runlevels, start, (size_t)len);
+ e->runlevels[len] = '\0';
+ if (*p == ':') p++;
+
+ /* action */
+ start = p;
+ while (*p && *p != ':') p++;
+ char action_str[32];
+ len = (int)(p - start);
+ if (len > 31) len = 31;
+ memcpy(action_str, start, (size_t)len);
+ action_str[len] = '\0';
+ e->action = parse_action(action_str);
+ if (*p == ':') p++;
+
+ /* process */
+ start = p;
+ while (*p && *p != '\n') p++;
+ len = (int)(p - start);
+ if (len > 127) len = 127;
+ memcpy(e->process, start, (size_t)len);
+ e->process[len] = '\0';
+ if (*p == '\n') p++;
+
+ e->pid = -1;
+ e->active = 1;
+ nentries++;
+ }
+
+ return nentries > 0 ? 0 : -1;
+}
+
+/* Check if entry should run at current runlevel */
+static int entry_matches_runlevel(const struct inittab_entry* e) {
+ if (e->runlevels[0] == '\0') return 1; /* Empty = all runlevels */
+ char rl = '0' + (char)current_runlevel;
+ for (const char* p = e->runlevels; *p; p++) {
+ if (*p == rl || *p == 'S' || *p == 's') return 1;
+ }
+ return 0;
+}
+
+/* Run a process (fork + exec) */
+static int run_process(const char* cmd) {
+ int pid = fork();
+ if (pid < 0) return -1;
+
+ if (pid == 0) {
+ /* Child: parse command into argv */
+ char buf[128];
+ strncpy(buf, cmd, sizeof(buf) - 1);
+ buf[sizeof(buf) - 1] = '\0';
+
+ char* argv[16];
+ int argc = 0;
+ char* p = buf;
+ while (*p && argc < 15) {
+ while (*p == ' ' || *p == '\t') p++;
+ if (*p == '\0') break;
+ argv[argc++] = p;
+ while (*p && *p != ' ' && *p != '\t') p++;
+ if (*p) *p++ = '\0';
+ }
+ argv[argc] = NULL;
+
+ if (argc > 0) {
+ execve(argv[0], (const char* const*)argv, NULL);
+ /* If execve fails, try with /bin/sh -c */
+ const char* sh_argv[] = { "/bin/sh", "-c", cmd, NULL };
+ execve("/bin/sh", sh_argv, NULL);
+ }
+ _exit(127);
+ }
+
+ return pid;
+}
+
+/* Run a process and wait for it */
+static int run_and_wait(const char* cmd) {
+ int pid = run_process(cmd);
+ if (pid < 0) return -1;
+ int st;
+ waitpid(pid, &st, 0);
+ return st;
+}
+
+/* Run all entries matching a given action and current runlevel */
+static void run_action(enum action act, int do_wait) {
+ for (int i = 0; i < nentries; i++) {
+ if (entries[i].action != act) continue;
+ if (!entry_matches_runlevel(&entries[i])) continue;
+ if (!entries[i].active) continue;
+
+ if (do_wait) {
+ run_and_wait(entries[i].process);
+ } else {
+ entries[i].pid = run_process(entries[i].process);
+ }
+ }
+}
+
+/* Respawn dead children */
+static void check_respawn(void) {
+ for (int i = 0; i < nentries; i++) {
+ if (entries[i].action != ACT_RESPAWN) continue;
+ if (!entry_matches_runlevel(&entries[i])) continue;
+ if (!entries[i].active) continue;
+
+ if (entries[i].pid <= 0) {
+ entries[i].pid = run_process(entries[i].process);
+ } else {
+ /* Check if still running */
+ int st;
+ int r = waitpid(entries[i].pid, &st, 1 /* WNOHANG */);
+ if (r > 0) {
+ /* Process exited, respawn */
+ entries[i].pid = run_process(entries[i].process);
+ }
+ }
+ }
+}
+
+/* Default behavior when no inittab exists */
+static void default_init(void) {
+ /* Run /etc/init.d/rcS if it exists */
+ if (access("/etc/init.d/rcS", 0) == 0) {
+ run_and_wait("/etc/init.d/rcS");
+ }
+
+ /* Spawn shell */
+ while (1) {
+ int pid = fork();
+ if (pid < 0) {
+ fprintf(stderr, "init: fork failed\n");
+ for (;;) { struct timespec ts = {1,0}; nanosleep(&ts, NULL); }
+ }
+
+ if (pid == 0) {
+ const char* argv[] = { "/bin/sh", NULL };
+ execve("/bin/sh", argv, NULL);
+ _exit(127);
+ }
+
+ int st;
+ waitpid(pid, &st, 0);
+
+ /* Shell exited, respawn after a small delay */
+ struct timespec ts = {1, 0};
+ nanosleep(&ts, NULL);
+ }
+}
+
+int main(int argc, char** argv) {
+ (void)argc; (void)argv;
+
+ /* PID 1 should not die on signals */
+ struct sigaction sa;
+ memset(&sa, 0, sizeof(sa));
+ sa.sa_handler = (uintptr_t)SIG_IGN;
+ sigaction(SIGINT, &sa, NULL);
+ sigaction(SIGTERM, &sa, NULL);
+
+ printf("AdrOS init starting (PID %d)\n", getpid());
+
+ /* Try to parse inittab */
+ if (parse_inittab() < 0) {
+ printf("init: no /etc/inittab, using defaults\n");
+ default_init();
+ return 0; /* unreachable */
+ }
+
+ printf("init: loaded %d inittab entries, runlevel %d\n",
+ nentries, current_runlevel);
+
+ /* Phase 1: sysinit entries */
+ run_action(ACT_SYSINIT, 1);
+
+ /* Phase 2: wait entries */
+ run_action(ACT_WAIT, 1);
+
+ /* Phase 3: once entries */
+ run_action(ACT_ONCE, 0);
+
+ /* Phase 4: respawn entries */
+ run_action(ACT_RESPAWN, 0);
+
+ /* Main loop: reap children and respawn */
+ while (1) {
+ int st;
+ int pid = waitpid(-1, &st, 0);
+
+ if (pid > 0) {
+ /* Mark dead child and respawn if needed */
+ for (int i = 0; i < nentries; i++) {
+ if (entries[i].pid == pid) {
+ entries[i].pid = -1;
+ break;
+ }
+ }
+ check_respawn();
+ } else {
+ /* No children or error — sleep briefly */
+ struct timespec ts = {1, 0};
+ nanosleep(&ts, NULL);
+ check_respawn();
+ }
+ }
+
+ return 0;
+}
--- /dev/null
+NAME := kill
+SRCS := kill.c
+include ../common.mk
--- /dev/null
+/* AdrOS kill utility — send signal to process */
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+int main(int argc, char** argv) {
+ if (argc <= 1) {
+ fprintf(stderr, "usage: kill [-SIGNAL] PID...\n");
+ return 1;
+ }
+
+ int sig = 15; /* SIGTERM */
+ int start = 1;
+
+ if (argv[1][0] == '-') {
+ const char* s = argv[1] + 1;
+ if (strcmp(s, "9") == 0 || strcmp(s, "KILL") == 0) sig = 9;
+ else if (strcmp(s, "15") == 0 || strcmp(s, "TERM") == 0) sig = 15;
+ else if (strcmp(s, "2") == 0 || strcmp(s, "INT") == 0) sig = 2;
+ else if (strcmp(s, "1") == 0 || strcmp(s, "HUP") == 0) sig = 1;
+ else if (strcmp(s, "0") == 0) sig = 0;
+ else sig = atoi(s);
+ start = 2;
+ }
+
+ int rc = 0;
+ for (int i = start; i < argc; i++) {
+ int pid = atoi(argv[i]);
+ if (pid <= 0) {
+ fprintf(stderr, "kill: invalid pid '%s'\n", argv[i]);
+ rc = 1;
+ continue;
+ }
+ if (kill(pid, sig) < 0) {
+ fprintf(stderr, "kill: %d: no such process\n", pid);
+ rc = 1;
+ }
+ }
+ return rc;
+}
--- /dev/null
+# ldso — dynamic linker (statically linked)
+NAME := ldso
+
+TOPDIR ?= $(abspath ../../..)
+BUILDDIR := $(TOPDIR)/build/user/cmds/$(NAME)
+USER_CC ?= i686-elf-gcc
+
+ELF := $(BUILDDIR)/ld.so
+
+all: $(ELF)
+
+$(ELF): ldso.c
+ @mkdir -p $(BUILDDIR)
+ @echo " CC+LD $@"
+ @$(USER_CC) -m32 -ffreestanding -fno-pie -no-pie -nostdlib \
+ -Wl,-T,$(TOPDIR)/user/ldso_linker.ld -o $@ ldso.c
+
+clean:
+ rm -f $(ELF)
+
+.PHONY: all clean
--- /dev/null
+/* Userspace dynamic linker (ld.so) with lazy PLT/GOT binding.
+ *
+ * The kernel ELF loader pushes an auxiliary vector (auxv) onto the user
+ * stack when PT_INTERP is present. This linker:
+ * 1. Parses auxv to find AT_PHDR, AT_PHNUM, AT_ENTRY
+ * 2. Walks program headers to find PT_DYNAMIC
+ * 3. Extracts DT_PLTGOT, DT_JMPREL, DT_PLTRELSZ, DT_SYMTAB, DT_STRTAB
+ * 4. Sets GOT[1] = link_map pointer, GOT[2] = _dl_runtime_resolve
+ * 5. Jumps to AT_ENTRY (the real program entry point)
+ *
+ * On first PLT call, the resolver fires: looks up the symbol, patches
+ * the GOT entry, and jumps to the resolved function. Subsequent calls
+ * go directly through the patched GOT (zero overhead).
+ *
+ * The kernel loads DT_NEEDED shared libraries at SHLIB_BASE (0x20000000).
+ * The resolver scans the .so's dynamic symtab to find undefined symbols. */
+
+typedef unsigned char uint8_t;
+typedef unsigned short uint16_t;
+typedef unsigned int uint32_t;
+typedef int int32_t;
+
+/* ---- Auxiliary vector types ---- */
+#define AT_NULL 0
+#define AT_PHDR 3
+#define AT_PHENT 4
+#define AT_PHNUM 5
+#define AT_ENTRY 9
+
+/* ---- ELF types (minimal, matching kernel include/elf.h) ---- */
+#define PT_LOAD 1
+#define PT_DYNAMIC 2
+
+#define DT_NULL 0
+#define DT_NEEDED 1
+#define DT_PLTRELSZ 2
+#define DT_PLTGOT 3
+#define DT_HASH 4
+#define DT_STRTAB 5
+#define DT_SYMTAB 6
+#define DT_STRSZ 10
+#define DT_SYMENT 11
+#define DT_REL 17
+#define DT_RELSZ 18
+#define DT_JMPREL 23
+
+#define R_386_32 1
+#define R_386_COPY 5
+#define R_386_GLOB_DAT 6
+#define R_386_JMP_SLOT 7
+
+#define ELF32_R_SYM(i) ((i) >> 8)
+#define ELF32_R_TYPE(i) ((unsigned char)(i))
+
+#define STB_GLOBAL 1
+#define STB_WEAK 2
+#define ELF32_ST_BIND(i) ((i) >> 4)
+
+#define SHLIB_BASE 0x11000000U
+
+struct elf32_phdr {
+ uint32_t p_type, p_offset, p_vaddr, p_paddr;
+ uint32_t p_filesz, p_memsz, p_flags, p_align;
+};
+
+struct elf32_dyn {
+ int32_t d_tag;
+ uint32_t d_val;
+};
+
+struct elf32_rel {
+ uint32_t r_offset;
+ uint32_t r_info;
+};
+
+struct elf32_sym {
+ uint32_t st_name, st_value, st_size;
+ uint8_t st_info, st_other;
+ uint16_t st_shndx;
+};
+
+/* ---- Link map: per-module metadata for the resolver ---- */
+struct link_map {
+ uint32_t l_addr; /* base load address (0 for ET_EXEC) */
+ uint32_t jmprel; /* DT_JMPREL VA (relocation table for .rel.plt) */
+ uint32_t pltrelsz; /* DT_PLTRELSZ */
+ uint32_t symtab; /* DT_SYMTAB VA */
+ uint32_t strtab; /* DT_STRTAB VA */
+ uint32_t rel; /* DT_REL VA (eager relocations) */
+ uint32_t relsz; /* DT_RELSZ */
+ /* Shared lib symbol lookup info */
+ uint32_t shlib_symtab; /* .so DT_SYMTAB VA (0 if no .so) */
+ uint32_t shlib_strtab; /* .so DT_STRTAB VA */
+ uint32_t shlib_base; /* .so load base */
+ uint32_t shlib_hash; /* .so DT_HASH VA */
+};
+
+static struct link_map g_map;
+
+/* ---- Minimal string helpers (no libc) ---- */
+static int str_eq(const char* a, const char* b) {
+ while (*a && *b) { if (*a++ != *b++) return 0; }
+ return *a == *b;
+}
+
+/* ---- ELF hash (for DT_HASH lookup) ---- */
+static uint32_t elf_hash(const char* name) {
+ uint32_t h = 0, g;
+ while (*name) {
+ h = (h << 4) + (uint8_t)*name++;
+ g = h & 0xF0000000U;
+ if (g) h ^= g >> 24;
+ h &= ~g;
+ }
+ return h;
+}
+
+/* ---- Symbol lookup in a shared library via DT_HASH ---- */
+static uint32_t shlib_lookup(const char* name, const struct link_map* map) {
+ if (!map->shlib_symtab || !map->shlib_strtab || !map->shlib_hash)
+ return 0;
+
+ const uint32_t* hashtab = (const uint32_t*)(map->shlib_hash + map->shlib_base);
+ uint32_t nbucket = hashtab[0];
+ uint32_t nchain = hashtab[1];
+ const uint32_t* bucket = &hashtab[2];
+ const uint32_t* chain = &hashtab[2 + nbucket];
+ (void)nchain;
+
+ uint32_t h = elf_hash(name) % nbucket;
+ const struct elf32_sym* symtab = (const struct elf32_sym*)(map->shlib_symtab + map->shlib_base);
+ const char* strtab = (const char*)(map->shlib_strtab + map->shlib_base);
+
+ for (uint32_t i = bucket[h]; i != 0; i = chain[i]) {
+ const struct elf32_sym* sym = &symtab[i];
+ uint8_t bind = ELF32_ST_BIND(sym->st_info);
+ if ((bind == STB_GLOBAL || bind == STB_WEAK) &&
+ sym->st_shndx != 0 && sym->st_value != 0) {
+ if (str_eq(strtab + sym->st_name, name))
+ return sym->st_value + map->shlib_base;
+ }
+ }
+ return 0;
+}
+
+/* ---- dl_fixup: called by _dl_runtime_resolve trampoline ----
+ * Resolves a single PLT entry: looks up the symbol, patches GOT,
+ * returns the resolved address. */
+uint32_t dl_fixup(struct link_map* map, uint32_t reloc_offset)
+ __attribute__((used, visibility("hidden")));
+
+uint32_t dl_fixup(struct link_map* map, uint32_t reloc_offset) {
+ const struct elf32_rel* rel =
+ (const struct elf32_rel*)(map->jmprel + reloc_offset);
+
+ uint32_t sym_idx = ELF32_R_SYM(rel->r_info);
+ const struct elf32_sym* sym =
+ &((const struct elf32_sym*)map->symtab)[sym_idx];
+
+ uint32_t resolved = 0;
+
+ if (sym->st_value != 0) {
+ resolved = sym->st_value + map->l_addr;
+ } else {
+ const char* name = (const char*)map->strtab + sym->st_name;
+ resolved = shlib_lookup(name, map);
+ }
+
+ if (resolved) {
+ uint32_t* got_entry = (uint32_t*)(rel->r_offset + map->l_addr);
+ *got_entry = resolved;
+ }
+
+ return resolved;
+}
+
+/* ---- _dl_runtime_resolve: PLT[0] jumps here via GOT[2] ----
+ * Entry stack: [link_map*] [reloc_offset] [return_addr]
+ * Uses the glibc i386 convention: save eax/ecx/edx, call dl_fixup,
+ * restore, ret $8 to jump to resolved function. */
+void _dl_runtime_resolve(void)
+ __attribute__((naked, used, visibility("hidden")));
+
+void _dl_runtime_resolve(void) {
+ __asm__ volatile(
+ "pushl %%eax\n"
+ "pushl %%ecx\n"
+ "pushl %%edx\n"
+ "movl 16(%%esp), %%edx\n" /* reloc_offset */
+ "movl 12(%%esp), %%eax\n" /* link_map* */
+ "pushl %%edx\n"
+ "pushl %%eax\n"
+ "call dl_fixup\n"
+ "addl $8, %%esp\n"
+ "popl %%edx\n"
+ "popl %%ecx\n"
+ "xchgl %%eax, (%%esp)\n" /* restore eax, put resolved addr on stack */
+ "ret $8\n" /* jump to resolved; pop link_map + reloc_offset */
+ ::: "memory"
+ );
+}
+
+/* ---- Parse a PT_DYNAMIC at the given VA to extract .so symtab info ---- */
+static void parse_shlib_dynamic(uint32_t dyn_va, uint32_t base) {
+ const struct elf32_dyn* d = (const struct elf32_dyn*)dyn_va;
+ for (; d->d_tag != DT_NULL; d++) {
+ switch (d->d_tag) {
+ case DT_SYMTAB: g_map.shlib_symtab = d->d_val; break;
+ case DT_STRTAB: g_map.shlib_strtab = d->d_val; break;
+ case DT_HASH: g_map.shlib_hash = d->d_val; break;
+ }
+ }
+ g_map.shlib_base = base;
+}
+
+/* ---- Scan for shared library's PT_DYNAMIC at SHLIB_BASE ---- */
+static void find_shlib_info(void) {
+ const uint8_t* base = (const uint8_t*)SHLIB_BASE;
+ /* Check ELF magic at SHLIB_BASE */
+ if (base[0] != 0x7F || base[1] != 'E' || base[2] != 'L' || base[3] != 'F')
+ return;
+
+ uint32_t e_phoff = *(const uint32_t*)(base + 28);
+ uint16_t e_phnum = *(const uint16_t*)(base + 44);
+ uint16_t e_phentsize = *(const uint16_t*)(base + 42);
+
+ for (uint16_t i = 0; i < e_phnum; i++) {
+ const struct elf32_phdr* ph =
+ (const struct elf32_phdr*)(base + e_phoff + i * e_phentsize);
+ if (ph->p_type == PT_DYNAMIC) {
+ parse_shlib_dynamic(ph->p_vaddr + SHLIB_BASE, SHLIB_BASE);
+ return;
+ }
+ }
+}
+
+/* ---- Entry point ---- */
+static void _start_c(uint32_t* initial_sp) __attribute__((noreturn, used));
+
+void _start(void) __attribute__((noreturn, naked, section(".text.start")));
+void _start(void) {
+ __asm__ volatile(
+ "pushl %%esp\n"
+ "call _start_c\n"
+ ::: "memory"
+ );
+ __builtin_unreachable();
+}
+
+static void _start_c(uint32_t* initial_sp) {
+ /* Stack layout set by execve:
+ * initial_sp → argc
+ * argv[0], argv[1], ..., NULL
+ * envp[0], envp[1], ..., NULL
+ * auxv[0], auxv[1], ..., {AT_NULL, 0} */
+ uint32_t* sp = initial_sp;
+
+ uint32_t argc = *sp++;
+ sp += argc + 1; /* skip argv[] + NULL terminator */
+ while (*sp) sp++; /* skip envp[] entries */
+ sp++; /* skip envp NULL terminator */
+
+ /* sp now points to auxv array */
+ uint32_t at_entry = 0;
+ uint32_t at_phdr = 0;
+ uint32_t at_phnum = 0;
+ uint32_t at_phent = 0;
+
+ for (uint32_t* p = sp; p[0] != AT_NULL; p += 2) {
+ switch (p[0]) {
+ case AT_ENTRY: at_entry = p[1]; break;
+ case AT_PHDR: at_phdr = p[1]; break;
+ case AT_PHNUM: at_phnum = p[1]; break;
+ case AT_PHENT: at_phent = p[1]; break;
+ }
+ }
+
+ if (!at_entry) {
+ __asm__ volatile("mov $2, %%eax\n mov $127, %%ebx\n int $0x80" ::: "eax", "ebx");
+ __builtin_unreachable();
+ }
+
+ /* Walk program headers to find PT_DYNAMIC */
+ g_map.l_addr = 0;
+
+ if (at_phdr && at_phnum && at_phent) {
+ for (uint32_t i = 0; i < at_phnum; i++) {
+ const struct elf32_phdr* ph =
+ (const struct elf32_phdr*)(at_phdr + i * at_phent);
+ if (ph->p_type == PT_DYNAMIC) {
+ uint32_t dyn_va = ph->p_vaddr + g_map.l_addr;
+ const struct elf32_dyn* d = (const struct elf32_dyn*)dyn_va;
+ uint32_t pltgot = 0;
+
+ for (; d->d_tag != DT_NULL; d++) {
+ switch (d->d_tag) {
+ case DT_PLTGOT: pltgot = d->d_val; break;
+ case DT_JMPREL: g_map.jmprel = d->d_val; break;
+ case DT_PLTRELSZ: g_map.pltrelsz = d->d_val; break;
+ case DT_SYMTAB: g_map.symtab = d->d_val; break;
+ case DT_STRTAB: g_map.strtab = d->d_val; break;
+ case DT_REL: g_map.rel = d->d_val; break;
+ case DT_RELSZ: g_map.relsz = d->d_val; break;
+ }
+ }
+
+ /* Scan for shared library info BEFORE resolving relocations */
+ find_shlib_info();
+
+ /* Set up GOT for lazy binding:
+ * GOT[0] = _DYNAMIC (already set by linker)
+ * GOT[1] = link_map pointer
+ * GOT[2] = _dl_runtime_resolve address */
+ if (pltgot && g_map.jmprel) {
+ uint32_t* got = (uint32_t*)(pltgot + g_map.l_addr);
+ got[1] = (uint32_t)&g_map;
+ got[2] = (uint32_t)&_dl_runtime_resolve;
+ }
+
+ /* Process eager relocations (R_386_GLOB_DAT, R_386_COPY) */
+ if (g_map.rel && g_map.relsz) {
+ uint32_t nrel = g_map.relsz / sizeof(struct elf32_rel);
+ const struct elf32_rel* rtab =
+ (const struct elf32_rel*)(g_map.rel + g_map.l_addr);
+ for (uint32_t j = 0; j < nrel; j++) {
+ uint32_t type = ELF32_R_TYPE(rtab[j].r_info);
+ uint32_t sidx = ELF32_R_SYM(rtab[j].r_info);
+ uint32_t* target = (uint32_t*)(rtab[j].r_offset + g_map.l_addr);
+ if (type == R_386_GLOB_DAT || type == R_386_JMP_SLOT) {
+ const struct elf32_sym* s =
+ &((const struct elf32_sym*)g_map.symtab)[sidx];
+ uint32_t addr = 0;
+ if (s->st_value != 0)
+ addr = s->st_value + g_map.l_addr;
+ else {
+ const char* nm = (const char*)g_map.strtab + s->st_name;
+ addr = shlib_lookup(nm, &g_map);
+ }
+ if (addr) *target = addr;
+ } else if (type == R_386_COPY && sidx) {
+ const struct elf32_sym* s =
+ &((const struct elf32_sym*)g_map.symtab)[sidx];
+ const char* nm = (const char*)g_map.strtab + s->st_name;
+ uint32_t src = shlib_lookup(nm, &g_map);
+ if (src && s->st_size > 0) {
+ const uint8_t* sp = (const uint8_t*)src;
+ uint8_t* dp = (uint8_t*)target;
+ for (uint32_t k = 0; k < s->st_size; k++)
+ dp[k] = sp[k];
+ }
+ }
+ }
+ }
+ break;
+ }
+ }
+ }
+
+ /* Restore the original stack pointer so the real program's _start
+ * sees the correct layout: [argc] [argv...] [NULL] [envp...] [NULL] [auxv...]
+ * Then jump to the program entry point. */
+ __asm__ volatile(
+ "mov %0, %%esp\n"
+ "jmp *%1\n"
+ :: "r"(initial_sp), "r"(at_entry)
+ : "memory"
+ );
+ __builtin_unreachable();
+}
--- /dev/null
+NAME := ln
+SRCS := ln.c
+include ../common.mk
--- /dev/null
+/* AdrOS ln utility */
+#include <stdio.h>
+#include <string.h>
+#include <unistd.h>
+
+int main(int argc, char** argv) {
+ int sflag = 0;
+ int start = 1;
+
+ if (argc > 1 && strcmp(argv[1], "-s") == 0) {
+ sflag = 1;
+ start = 2;
+ }
+
+ if (argc - start < 2) {
+ fprintf(stderr, "Usage: ln [-s] <target> <linkname>\n");
+ return 1;
+ }
+
+ int r;
+ if (sflag) {
+ r = symlink(argv[start], argv[start + 1]);
+ } else {
+ r = link(argv[start], argv[start + 1]);
+ }
+
+ if (r < 0) {
+ fprintf(stderr, "ln: failed to create %slink '%s' -> '%s'\n",
+ sflag ? "symbolic " : "", argv[start + 1], argv[start]);
+ return 1;
+ }
+ return 0;
+}
--- /dev/null
+NAME := ls
+SRCS := ls.c
+include ../common.mk
--- /dev/null
+/* AdrOS ls utility */
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <dirent.h>
+#include <sys/stat.h>
+
+static int aflag = 0; /* -a: show hidden files */
+static int lflag = 0; /* -l: long format */
+
+#define LS_MAX_ENTRIES 512
+
+struct ls_entry {
+ char name[256];
+ unsigned char type;
+};
+
+static struct ls_entry entries[LS_MAX_ENTRIES];
+
+static int cmp_entry(const void* a, const void* b) {
+ return strcmp(((const struct ls_entry*)a)->name,
+ ((const struct ls_entry*)b)->name);
+}
+
+static void ls_dir(const char* path) {
+ int fd = open(path, O_RDONLY);
+ if (fd < 0) {
+ fprintf(stderr, "ls: cannot access '%s': No such file or directory\n", path);
+ return;
+ }
+
+ int count = 0;
+ char buf[2048];
+ int rc;
+ while ((rc = getdents(fd, buf, sizeof(buf))) > 0) {
+ int off = 0;
+ while (off < rc) {
+ struct dirent* d = (struct dirent*)(buf + off);
+ if (d->d_reclen == 0) break;
+
+ if (!aflag && d->d_name[0] == '.') {
+ off += d->d_reclen;
+ continue;
+ }
+
+ if (count < LS_MAX_ENTRIES) {
+ strncpy(entries[count].name, d->d_name, 255);
+ entries[count].name[255] = '\0';
+ entries[count].type = d->d_type;
+ count++;
+ }
+ off += d->d_reclen;
+ }
+ }
+ close(fd);
+
+ qsort(entries, count, sizeof(struct ls_entry), cmp_entry);
+
+ for (int i = 0; i < count; i++) {
+ if (lflag) {
+ char fullpath[512];
+ size_t plen = strlen(path);
+ if (plen > 0 && path[plen - 1] == '/')
+ snprintf(fullpath, sizeof(fullpath), "%s%s", path, entries[i].name);
+ else
+ snprintf(fullpath, sizeof(fullpath), "%s/%s", path, entries[i].name);
+
+ struct stat st;
+ int have_stat = (stat(fullpath, &st) == 0);
+
+ char type = '-';
+ if (entries[i].type == DT_DIR) type = 'd';
+ else if (entries[i].type == DT_CHR) type = 'c';
+ else if (entries[i].type == DT_LNK) type = 'l';
+ else if (entries[i].type == DT_BLK) type = 'b';
+
+ char perms[10];
+ if (have_stat) {
+ unsigned m = (unsigned)st.st_mode;
+ perms[0] = (m & S_IRUSR) ? 'r' : '-';
+ perms[1] = (m & S_IWUSR) ? 'w' : '-';
+ perms[2] = (m & S_IXUSR) ? 'x' : '-';
+ perms[3] = (m & S_IRGRP) ? 'r' : '-';
+ perms[4] = (m & S_IWGRP) ? 'w' : '-';
+ perms[5] = (m & S_IXGRP) ? 'x' : '-';
+ perms[6] = (m & S_IROTH) ? 'r' : '-';
+ perms[7] = (m & S_IWOTH) ? 'w' : '-';
+ perms[8] = (m & S_IXOTH) ? 'x' : '-';
+ perms[9] = '\0';
+ } else {
+ strcpy(perms, "---------");
+ }
+
+ unsigned long sz = have_stat ? (unsigned long)st.st_size : 0;
+ unsigned nlink = have_stat ? (unsigned)st.st_nlink : 1;
+
+ printf("%c%s %2u root root %8lu %s\n",
+ type, perms, nlink, sz, entries[i].name);
+ } else {
+ printf("%s\n", entries[i].name);
+ }
+ }
+}
+
+int main(int argc, char** argv) {
+ int npath = 0;
+ const char* paths[64];
+
+ for (int i = 1; i < argc; i++) {
+ if (argv[i][0] == '-' && argv[i][1]) {
+ const char* f = argv[i] + 1;
+ while (*f) {
+ if (*f == 'a') aflag = 1;
+ else if (*f == 'l') lflag = 1;
+ else {
+ fprintf(stderr, "ls: invalid option -- '%c'\n", *f);
+ return 1;
+ }
+ f++;
+ }
+ } else {
+ if (npath < 64) paths[npath++] = argv[i];
+ }
+ }
+
+ if (npath == 0) {
+ ls_dir(".");
+ } else {
+ for (int i = 0; i < npath; i++) {
+ if (npath > 1) printf("%s:\n", paths[i]);
+ ls_dir(paths[i]);
+ if (npath > 1 && i < npath - 1) printf("\n");
+ }
+ }
+
+ return 0;
+}
--- /dev/null
+NAME := mkdir
+SRCS := mkdir.c
+include ../common.mk
--- /dev/null
+/* AdrOS mkdir utility */
+#include <stdio.h>
+#include <string.h>
+#include <unistd.h>
+
+static int pflag = 0; /* -p: create parent directories */
+
+static int mkdir_p(const char* path) {
+ char tmp[256];
+ size_t len = strlen(path);
+ if (len >= sizeof(tmp)) return -1;
+ strcpy(tmp, path);
+
+ for (char* p = tmp + 1; *p; p++) {
+ if (*p == '/') {
+ *p = '\0';
+ mkdir(tmp); /* ignore errors — parent may already exist */
+ *p = '/';
+ }
+ }
+ return mkdir(tmp);
+}
+
+int main(int argc, char** argv) {
+ if (argc <= 1) {
+ fprintf(stderr, "mkdir: missing operand\n");
+ return 1;
+ }
+
+ int rc = 0;
+ int start = 1;
+
+ for (int i = 1; i < argc; i++) {
+ if (argv[i][0] == '-' && argv[i][1]) {
+ const char* f = argv[i] + 1;
+ while (*f) {
+ if (*f == 'p') pflag = 1;
+ else {
+ fprintf(stderr, "mkdir: invalid option -- '%c'\n", *f);
+ return 1;
+ }
+ f++;
+ }
+ start = i + 1;
+ }
+ }
+
+ for (int i = start; i < argc; i++) {
+ int r;
+ if (pflag) {
+ r = mkdir_p(argv[i]);
+ } else {
+ r = mkdir(argv[i]);
+ }
+ if (r < 0) {
+ fprintf(stderr, "mkdir: cannot create directory '%s'\n", argv[i]);
+ rc = 1;
+ }
+ }
+ return rc;
+}
--- /dev/null
+NAME := mount
+SRCS := mount.c
+include ../common.mk
--- /dev/null
+/* AdrOS mount utility — mount filesystems or display mounts */
+#include <stdio.h>
+#include <string.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <syscall.h>
+#include <errno.h>
+
+static void show_mounts(void) {
+ int fd = open("/proc/mounts", O_RDONLY);
+ if (fd >= 0) {
+ char buf[1024];
+ int n;
+ while ((n = read(fd, buf, sizeof(buf))) > 0)
+ write(STDOUT_FILENO, buf, (size_t)n);
+ close(fd);
+ } else {
+ printf("tmpfs on / type overlayfs (rw)\n");
+ printf("devfs on /dev type devfs (rw)\n");
+ printf("procfs on /proc type procfs (ro)\n");
+ }
+}
+
+int main(int argc, char** argv) {
+ if (argc < 2) {
+ show_mounts();
+ return 0;
+ }
+
+ const char* fstype = "diskfs";
+ const char* device = NULL;
+ const char* mountpoint = NULL;
+
+ /* Parse options first, then collect positional args */
+ int i;
+ for (i = 1; i < argc; i++) {
+ if (strcmp(argv[i], "-t") == 0 && i + 1 < argc) {
+ fstype = argv[++i];
+ } else if (!device) {
+ device = argv[i];
+ } else if (!mountpoint) {
+ mountpoint = argv[i];
+ }
+ }
+
+ if (!device || !mountpoint) {
+ fprintf(stderr, "usage: mount [-t fstype] device mountpoint\n");
+ return 1;
+ }
+
+ int rc = __syscall_ret(_syscall3(SYS_MOUNT, (int)device, (int)mountpoint, (int)fstype));
+ if (rc < 0) {
+ fprintf(stderr, "mount: mounting %s on %s failed: %d\n", device, mountpoint, rc);
+ return 1;
+ }
+ return 0;
+}
--- /dev/null
+NAME := mv
+SRCS := mv.c
+include ../common.mk
--- /dev/null
+/* AdrOS mv utility */
+#include <stdio.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <string.h>
+
+int main(int argc, char** argv) {
+ if (argc < 3) {
+ fprintf(stderr, "Usage: mv <source> <dest>\n");
+ return 1;
+ }
+
+ /* Try rename first (same filesystem) */
+ if (rename(argv[1], argv[2]) == 0)
+ return 0;
+
+ /* Fallback: copy + unlink */
+ int src = open(argv[1], O_RDONLY);
+ if (src < 0) {
+ fprintf(stderr, "mv: cannot open '%s'\n", argv[1]);
+ return 1;
+ }
+
+ int dst = open(argv[2], O_WRONLY | O_CREAT | O_TRUNC, 0644);
+ if (dst < 0) {
+ fprintf(stderr, "mv: cannot create '%s'\n", argv[2]);
+ close(src);
+ return 1;
+ }
+
+ char buf[4096];
+ int r;
+ while ((r = read(src, buf, sizeof(buf))) > 0) {
+ if (write(dst, buf, (size_t)r) != r) {
+ fprintf(stderr, "mv: write error\n");
+ close(src); close(dst);
+ return 1;
+ }
+ }
+
+ close(src);
+ close(dst);
+ unlink(argv[1]);
+ return 0;
+}
--- /dev/null
+# pie_test — PIE/shared library test binary
+NAME := pie_test
+
+TOPDIR ?= $(abspath ../../..)
+BUILDDIR := $(TOPDIR)/build/user/cmds/$(NAME)
+USER_CC ?= i686-elf-gcc
+USER_LD ?= i686-elf-ld
+
+PIE_SO := $(BUILDDIR)/libpietest.so
+PIE_ELF := $(BUILDDIR)/pie_test.elf
+
+all: $(PIE_SO) $(PIE_ELF)
+
+$(PIE_SO): pie_func.c
+ @mkdir -p $(BUILDDIR)
+ @echo " CC [PIC] $<"
+ @$(USER_CC) -m32 -fPIC -fno-plt -c pie_func.c -o $(BUILDDIR)/pie_func.o
+ @$(USER_LD) -m elf_i386 -shared -soname libpietest.so -o $@ $(BUILDDIR)/pie_func.o
+
+$(PIE_ELF): pie_main.c $(PIE_SO)
+ @mkdir -p $(BUILDDIR)
+ @echo " CC [PIE] $<"
+ @$(USER_CC) -m32 -fPIC -c pie_main.c -o $(BUILDDIR)/pie_main.o
+ @$(USER_LD) -m elf_i386 -pie --dynamic-linker=/lib/ld.so \
+ -T $(TOPDIR)/user/pie_linker.ld -o $@ $(BUILDDIR)/pie_main.o $(PIE_SO) -rpath /lib
+
+clean:
+ rm -f $(BUILDDIR)/pie_func.o $(BUILDDIR)/pie_main.o $(PIE_SO) $(PIE_ELF)
+
+.PHONY: all clean
--- /dev/null
+/* Shared library function for PLT/GOT lazy binding test.
+ * Compiled as a shared object (libpietest.so), loaded at SHLIB_BASE by kernel.
+ * The main PIE binary calls test_add() through PLT — resolved lazily by ld.so. */
+
+int test_add(int a, int b) {
+ return a + b;
+}
--- /dev/null
+/* PIE test binary for PLT/GOT lazy binding verification.
+ * Calls test_add() from libpietest.so through PLT — resolved lazily by ld.so.
+ * Built as: i686-elf-ld -pie --dynamic-linker=/lib/ld.so */
+
+static inline void sys_exit(int code) {
+ __asm__ volatile("int $0x80" :: "a"(2), "b"(code) : "memory");
+}
+
+static inline int sys_write(int fd, const void* buf, unsigned len) {
+ int ret;
+ __asm__ volatile("int $0x80" : "=a"(ret) : "a"(1), "b"(fd), "c"(buf), "d"(len) : "memory");
+ return ret;
+}
+
+extern int test_add(int a, int b);
+
+void _start(void) {
+ int r = test_add(38, 4);
+ if (r == 42) {
+ sys_write(1, "[test] lazy PLT OK\n", 19);
+ } else {
+ sys_write(1, "[test] lazy PLT FAIL\n", 21);
+ }
+
+ /* Call again — this time GOT is already patched, tests direct path */
+ r = test_add(100, 23);
+ if (r == 123) {
+ sys_write(1, "[test] PLT cached OK\n", 21);
+ } else {
+ sys_write(1, "[test] PLT cached FAIL\n", 23);
+ }
+
+ sys_exit(0);
+}
--- /dev/null
+NAME := printenv
+SRCS := printenv.c
+include ../common.mk
--- /dev/null
+/* AdrOS printenv utility — print environment variables */
+#include <stdio.h>
+#include <string.h>
+
+extern char** __environ;
+
+int main(int argc, char** argv) {
+ if (!__environ) return 1;
+ if (argc <= 1) {
+ for (int i = 0; __environ[i]; i++)
+ printf("%s\n", __environ[i]);
+ return 0;
+ }
+ for (int i = 1; i < argc; i++) {
+ int found = 0;
+ int nlen = (int)strlen(argv[i]);
+ for (int j = 0; __environ[j]; j++) {
+ if (strncmp(__environ[j], argv[i], (size_t)nlen) == 0 && __environ[j][nlen] == '=') {
+ printf("%s\n", __environ[j] + nlen + 1);
+ found = 1;
+ break;
+ }
+ }
+ if (!found) return 1;
+ }
+ return 0;
+}
--- /dev/null
+NAME := ps
+SRCS := ps.c
+include ../common.mk
--- /dev/null
+/* AdrOS ps utility — list processes from /proc */
+#include <stdio.h>
+#include <string.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <dirent.h>
+
+static int is_digit(char c) { return c >= '0' && c <= '9'; }
+
+int main(void) {
+ printf(" PID CMD\n");
+ int fd = open("/proc", O_RDONLY);
+ if (fd < 0) {
+ fprintf(stderr, "ps: cannot open /proc\n");
+ return 1;
+ }
+ char buf[512];
+ int rc;
+ while ((rc = getdents(fd, buf, sizeof(buf))) > 0) {
+ int off = 0;
+ while (off < rc) {
+ struct dirent* d = (struct dirent*)(buf + off);
+ if (d->d_reclen == 0) break;
+ if (is_digit(d->d_name[0])) {
+ char path[64];
+ snprintf(path, sizeof(path), "/proc/%s/cmdline", d->d_name);
+ int cfd = open(path, O_RDONLY);
+ char cmd[64] = "?";
+ if (cfd >= 0) {
+ int n = read(cfd, cmd, sizeof(cmd) - 1);
+ if (n > 0) {
+ cmd[n] = '\0';
+ while (n > 0 && (cmd[n-1] == '\n' || cmd[n-1] == '\0')) {
+ cmd[--n] = '\0';
+ }
+ }
+ if (n <= 0) strcpy(cmd, "[kernel]");
+ close(cfd);
+ }
+ printf("%5s %s\n", d->d_name, cmd);
+ }
+ off += d->d_reclen;
+ }
+ }
+ close(fd);
+ return 0;
+}
--- /dev/null
+NAME := pwd
+SRCS := pwd.c
+include ../common.mk
--- /dev/null
+/* AdrOS pwd utility — print working directory */
+#include <stdio.h>
+#include <unistd.h>
+
+int main(void) {
+ char buf[256];
+ if (getcwd(buf, sizeof(buf)) >= 0)
+ printf("%s\n", buf);
+ else {
+ fprintf(stderr, "pwd: error\n");
+ return 1;
+ }
+ return 0;
+}
--- /dev/null
+NAME := rm
+SRCS := rm.c
+include ../common.mk
--- /dev/null
+/* AdrOS rm utility */
+#include <stdio.h>
+#include <string.h>
+#include <unistd.h>
+
+static int rflag = 0; /* -r: recursive */
+static int fflag = 0; /* -f: force (no errors) */
+static int dflag = 0; /* -d: remove empty directories */
+
+int main(int argc, char** argv) {
+ if (argc <= 1) {
+ fprintf(stderr, "rm: missing operand\n");
+ return 1;
+ }
+
+ int start = 1;
+ for (int i = 1; i < argc; i++) {
+ if (argv[i][0] == '-' && argv[i][1]) {
+ const char* f = argv[i] + 1;
+ while (*f) {
+ if (*f == 'r' || *f == 'R') rflag = 1;
+ else if (*f == 'f') fflag = 1;
+ else if (*f == 'd') dflag = 1;
+ else {
+ fprintf(stderr, "rm: invalid option -- '%c'\n", *f);
+ return 1;
+ }
+ f++;
+ }
+ start = i + 1;
+ } else {
+ break;
+ }
+ }
+
+ int rc = 0;
+ for (int i = start; i < argc; i++) {
+ int r = unlink(argv[i]);
+ if (r < 0 && (rflag || dflag)) {
+ r = rmdir(argv[i]);
+ }
+ if (r < 0 && !fflag) {
+ fprintf(stderr, "rm: cannot remove '%s'\n", argv[i]);
+ rc = 1;
+ }
+ }
+ return rc;
+}
--- /dev/null
+NAME := rmdir
+SRCS := rmdir.c
+include ../common.mk
--- /dev/null
+/* AdrOS rmdir utility — remove empty directories */
+#include <stdio.h>
+#include <unistd.h>
+
+int main(int argc, char** argv) {
+ if (argc <= 1) {
+ fprintf(stderr, "rmdir: missing operand\n");
+ return 1;
+ }
+ int rc = 0;
+ for (int i = 1; i < argc; i++) {
+ if (rmdir(argv[i]) < 0) {
+ fprintf(stderr, "rmdir: failed to remove '%s'\n", argv[i]);
+ rc = 1;
+ }
+ }
+ return rc;
+}
--- /dev/null
+NAME := sed
+SRCS := sed.c
+include ../common.mk
--- /dev/null
+/* AdrOS sed utility — minimal stream editor (s/pattern/replacement/g only) */
+#include <stdio.h>
+#include <string.h>
+#include <unistd.h>
+#include <fcntl.h>
+
+static int match_at(const char* s, const char* pat, int patlen) {
+ for (int i = 0; i < patlen; i++) {
+ if (s[i] == '\0' || s[i] != pat[i]) return 0;
+ }
+ return 1;
+}
+
+static void sed_substitute(const char* line, const char* pat, int patlen,
+ const char* rep, int replen, int global) {
+ const char* p = line;
+ while (*p) {
+ if (match_at(p, pat, patlen)) {
+ write(STDOUT_FILENO, rep, replen);
+ p += patlen;
+ if (!global) {
+ write(STDOUT_FILENO, p, strlen(p));
+ return;
+ }
+ } else {
+ write(STDOUT_FILENO, p, 1);
+ p++;
+ }
+ }
+}
+
+static int parse_s_cmd(const char* expr, char* pat, int* patlen,
+ char* rep, int* replen, int* global) {
+ if (expr[0] != 's' || expr[1] == '\0') return -1;
+ char delim = expr[1];
+ const char* p = expr + 2;
+ int pi = 0;
+ while (*p && *p != delim && pi < 255) pat[pi++] = *p++;
+ pat[pi] = '\0'; *patlen = pi;
+ if (*p != delim) return -1;
+ p++;
+ int ri = 0;
+ while (*p && *p != delim && ri < 255) rep[ri++] = *p++;
+ rep[ri] = '\0'; *replen = ri;
+ *global = 0;
+ if (*p == delim) { p++; if (*p == 'g') *global = 1; }
+ return 0;
+}
+
+int main(int argc, char** argv) {
+ if (argc < 2) {
+ fprintf(stderr, "Usage: sed 's/pattern/replacement/[g]' [file]\n");
+ return 1;
+ }
+
+ char pat[256], rep[256];
+ int patlen, replen, global;
+ int ei = 1;
+ if (strcmp(argv[1], "-e") == 0 && argc > 2) ei = 2;
+
+ if (parse_s_cmd(argv[ei], pat, &patlen, rep, &replen, &global) < 0) {
+ fprintf(stderr, "sed: invalid expression: %s\n", argv[ei]);
+ return 1;
+ }
+
+ int fd = STDIN_FILENO;
+ if (argc > ei + 1) {
+ fd = open(argv[ei + 1], O_RDONLY);
+ if (fd < 0) {
+ fprintf(stderr, "sed: %s: No such file or directory\n", argv[ei + 1]);
+ return 1;
+ }
+ }
+
+ char line[4096];
+ int li = 0;
+ char c;
+ while (read(fd, &c, 1) == 1) {
+ if (c == '\n') {
+ line[li] = '\0';
+ sed_substitute(line, pat, patlen, rep, replen, global);
+ write(STDOUT_FILENO, "\n", 1);
+ li = 0;
+ } else if (li < (int)sizeof(line) - 1) {
+ line[li++] = c;
+ }
+ }
+ if (li > 0) {
+ line[li] = '\0';
+ sed_substitute(line, pat, patlen, rep, replen, global);
+ }
+
+ if (fd != STDIN_FILENO) close(fd);
+ return 0;
+}
--- /dev/null
+NAME := sh
+SRCS := sh.c
+include ../common.mk
--- /dev/null
+/* AdrOS POSIX-like shell (/bin/sh)
+ *
+ * Features:
+ * - Variable assignment (VAR=value) and expansion ($VAR)
+ * - Environment variables (export VAR=value)
+ * - Line editing (left/right arrow keys)
+ * - Command history (up/down arrow keys)
+ * - Pipes (cmd1 | cmd2 | cmd3)
+ * - Redirections (< > >>)
+ * - Operators: ; && || &
+ * - Job control: CTRL+C (SIGINT), CTRL+Z (SIGTSTP), background (&)
+ * - Builtins: cd, exit, echo, export, unset, set, pwd, type
+ * - PATH-based command resolution
+ * - Quote handling (single and double quotes)
+ */
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <dirent.h>
+#include <termios.h>
+#include <signal.h>
+#include <sys/ioctl.h>
+#include <sys/wait.h>
+
+static struct termios orig_termios;
+
+static void tty_raw_mode(void) {
+ tcgetattr(STDIN_FILENO, &orig_termios);
+ struct termios raw = orig_termios;
+ raw.c_lflag &= ~(ICANON | ECHO | ISIG);
+ raw.c_cc[VMIN] = 1;
+ raw.c_cc[VTIME] = 0;
+ tcsetattr(STDIN_FILENO, TCSANOW, &raw);
+}
+
+static void tty_restore(void) {
+ tcsetattr(STDIN_FILENO, TCSANOW, &orig_termios);
+}
+
+#define LINE_MAX 512
+#define MAX_ARGS 64
+#define MAX_VARS 64
+#define HIST_SIZE 32
+
+/* ---- Shell variables ---- */
+
+static struct {
+ char name[64];
+ char value[256];
+ int exported;
+} vars[MAX_VARS];
+static int nvar = 0;
+
+static int last_status = 0;
+
+static const char* var_get(const char* name) {
+ for (int i = 0; i < nvar; i++)
+ if (strcmp(vars[i].name, name) == 0) return vars[i].value;
+ return getenv(name);
+}
+
+static void var_set(const char* name, const char* value, int exported) {
+ for (int i = 0; i < nvar; i++) {
+ if (strcmp(vars[i].name, name) == 0) {
+ strncpy(vars[i].value, value, 255);
+ vars[i].value[255] = '\0';
+ if (exported) vars[i].exported = 1;
+ return;
+ }
+ }
+ if (nvar < MAX_VARS) {
+ strncpy(vars[nvar].name, name, 63);
+ vars[nvar].name[63] = '\0';
+ strncpy(vars[nvar].value, value, 255);
+ vars[nvar].value[255] = '\0';
+ vars[nvar].exported = exported;
+ nvar++;
+ }
+}
+
+static void var_unset(const char* name) {
+ for (int i = 0; i < nvar; i++) {
+ if (strcmp(vars[i].name, name) == 0) {
+ vars[i] = vars[--nvar];
+ return;
+ }
+ }
+}
+
+/* Build envp array from exported variables */
+static char env_buf[MAX_VARS][320];
+static char* envp_arr[MAX_VARS + 1];
+
+static char** build_envp(void) {
+ int n = 0;
+ for (int i = 0; i < nvar && n < MAX_VARS; i++) {
+ if (!vars[i].exported) continue;
+ snprintf(env_buf[n], sizeof(env_buf[n]), "%s=%s",
+ vars[i].name, vars[i].value);
+ envp_arr[n] = env_buf[n];
+ n++;
+ }
+ envp_arr[n] = NULL;
+ return envp_arr;
+}
+
+/* ---- Command history ---- */
+
+static char history[HIST_SIZE][LINE_MAX];
+static int hist_count = 0;
+static int hist_pos = 0;
+
+static void hist_add(const char* line) {
+ if (line[0] == '\0') return;
+ if (hist_count > 0 && strcmp(history[(hist_count - 1) % HIST_SIZE], line) == 0)
+ return;
+ strncpy(history[hist_count % HIST_SIZE], line, LINE_MAX - 1);
+ history[hist_count % HIST_SIZE][LINE_MAX - 1] = '\0';
+ hist_count++;
+}
+
+/* ---- Line editing ---- */
+
+static char line[LINE_MAX];
+
+static void term_write(const char* s, int n) {
+ write(STDOUT_FILENO, s, (size_t)n);
+}
+
+/* ---- Tab completion ---- */
+
+static int tab_complete(char* buf, int* p_pos, int* p_len) {
+ int pos = *p_pos;
+ int len = *p_len;
+
+ /* Find the start of the current word */
+ int wstart = pos;
+ while (wstart > 0 && buf[wstart - 1] != ' ' && buf[wstart - 1] != '\t')
+ wstart--;
+
+ char prefix[128];
+ int plen = pos - wstart;
+ if (plen <= 0 || plen >= (int)sizeof(prefix)) return 0;
+ memcpy(prefix, buf + wstart, (size_t)plen);
+ prefix[plen] = '\0';
+
+ /* Determine if this is a command (first word) or filename */
+ int is_cmd = 1;
+ for (int i = 0; i < wstart; i++) {
+ if (buf[i] != ' ' && buf[i] != '\t') { is_cmd = 0; break; }
+ }
+
+ char match[128];
+ match[0] = '\0';
+ int nmatches = 0;
+
+ /* Split prefix into directory part and name part for file completion */
+ char dirpath[128] = ".";
+ const char* namepfx = prefix;
+ char* lastsep = NULL;
+ for (char* p = prefix; *p; p++) {
+ if (*p == '/') lastsep = p;
+ }
+ if (lastsep) {
+ int dlen = (int)(lastsep - prefix);
+ if (dlen == 0) { dirpath[0] = '/'; dirpath[1] = '\0'; }
+ else { memcpy(dirpath, prefix, (size_t)dlen); dirpath[dlen] = '\0'; }
+ namepfx = lastsep + 1;
+ }
+ int nplen = (int)strlen(namepfx);
+
+ if (!is_cmd || lastsep) {
+ /* File/directory completion */
+ int fd = open(dirpath, 0);
+ if (fd >= 0) {
+ char dbuf[512];
+ int rc;
+ while ((rc = getdents(fd, dbuf, sizeof(dbuf))) > 0) {
+ int off = 0;
+ while (off < rc) {
+ struct dirent* d = (struct dirent*)(dbuf + off);
+ if (d->d_reclen == 0) break;
+ if (d->d_name[0] != '.' || nplen > 0) {
+ int nlen = (int)strlen(d->d_name);
+ if (nlen >= nplen && memcmp(d->d_name, namepfx, (size_t)nplen) == 0) {
+ if (nmatches == 0) strcpy(match, d->d_name);
+ nmatches++;
+ }
+ }
+ off += d->d_reclen;
+ }
+ }
+ close(fd);
+ }
+ }
+
+ if (is_cmd && !lastsep) {
+ /* Command completion: search PATH directories + builtins */
+ static const char* builtins[] = {
+ "cd", "exit", "echo", "export", "unset", "set", "pwd", "type", NULL
+ };
+ for (int i = 0; builtins[i]; i++) {
+ int blen = (int)strlen(builtins[i]);
+ if (blen >= plen && memcmp(builtins[i], prefix, (size_t)plen) == 0) {
+ if (nmatches == 0) strcpy(match, builtins[i]);
+ nmatches++;
+ }
+ }
+ const char* path_env = var_get("PATH");
+ if (!path_env) path_env = "/bin:/sbin:/usr/bin";
+ char pathcopy[512];
+ strncpy(pathcopy, path_env, sizeof(pathcopy) - 1);
+ pathcopy[sizeof(pathcopy) - 1] = '\0';
+ char* save = pathcopy;
+ char* dir;
+ while ((dir = save) != NULL) {
+ char* sep = strchr(save, ':');
+ if (sep) { *sep = '\0'; save = sep + 1; } else save = NULL;
+ int fd = open(dir, 0);
+ if (fd < 0) continue;
+ char dbuf[512];
+ int rc;
+ while ((rc = getdents(fd, dbuf, sizeof(dbuf))) > 0) {
+ int off = 0;
+ while (off < rc) {
+ struct dirent* d = (struct dirent*)(dbuf + off);
+ if (d->d_reclen == 0) break;
+ int nlen = (int)strlen(d->d_name);
+ if (nlen >= plen && memcmp(d->d_name, prefix, (size_t)plen) == 0) {
+ if (nmatches == 0) strcpy(match, d->d_name);
+ nmatches++;
+ }
+ off += d->d_reclen;
+ }
+ }
+ close(fd);
+ }
+ }
+
+ if (nmatches != 1) return 0;
+
+ /* Insert the completion suffix */
+ int mlen = (int)strlen(match);
+ int suffix_len = is_cmd && !lastsep ? mlen - plen : mlen - nplen;
+ const char* suffix = is_cmd && !lastsep ? match + plen : match + nplen;
+ if (suffix_len <= 0 || len + suffix_len >= LINE_MAX - 1) return 0;
+
+ memmove(buf + pos + suffix_len, buf + pos, (size_t)(len - pos));
+ memcpy(buf + pos, suffix, (size_t)suffix_len);
+ len += suffix_len;
+ buf[len] = '\0';
+ term_write(buf + pos, len - pos);
+ pos += suffix_len;
+ for (int i = 0; i < len - pos; i++) term_write("\b", 1);
+ *p_pos = pos;
+ *p_len = len;
+ return 1;
+}
+
+static int read_line_edit(void) {
+ int pos = 0;
+ int len = 0;
+ hist_pos = hist_count;
+
+ memset(line, 0, LINE_MAX);
+
+ while (len < LINE_MAX - 1) {
+ char c;
+ int r = read(STDIN_FILENO, &c, 1);
+ if (r <= 0) {
+ if (len == 0) return -1;
+ break;
+ }
+
+ if (c == '\n' || c == '\r') {
+ term_write("\n", 1);
+ break;
+ }
+
+ /* Backspace / DEL */
+ if (c == '\b' || c == 127) {
+ if (pos > 0) {
+ memmove(line + pos - 1, line + pos, (size_t)(len - pos));
+ pos--; len--;
+ line[len] = '\0';
+ /* Redraw: move cursor back, print rest, clear tail */
+ term_write("\b", 1);
+ term_write(line + pos, len - pos);
+ term_write(" \b", 2);
+ for (int i = 0; i < len - pos; i++) term_write("\b", 1);
+ }
+ continue;
+ }
+
+ /* Tab = autocomplete */
+ if (c == '\t') {
+ tab_complete(line, &pos, &len);
+ continue;
+ }
+
+ /* Ctrl+D = EOF */
+ if (c == 4) {
+ if (len == 0) return -1;
+ continue;
+ }
+
+ /* Ctrl+C = cancel line */
+ if (c == 3) {
+ term_write("^C\n", 3);
+ line[0] = '\0';
+ return 0;
+ }
+
+ /* Ctrl+Z = ignored at prompt (no foreground job to suspend) */
+ if (c == 26) {
+ continue;
+ }
+
+ /* Ctrl+A = beginning of line */
+ if (c == 1) {
+ while (pos > 0) { term_write("\b", 1); pos--; }
+ continue;
+ }
+
+ /* Ctrl+E = end of line */
+ if (c == 5) {
+ term_write(line + pos, len - pos);
+ pos = len;
+ continue;
+ }
+
+ /* Ctrl+U = clear line */
+ if (c == 21) {
+ while (pos > 0) { term_write("\b", 1); pos--; }
+ for (int i = 0; i < len; i++) term_write(" ", 1);
+ for (int i = 0; i < len; i++) term_write("\b", 1);
+ len = 0; pos = 0;
+ line[0] = '\0';
+ continue;
+ }
+
+ /* Escape sequences (arrow keys) */
+ if (c == 27) {
+ char seq[2];
+ if (read(STDIN_FILENO, &seq[0], 1) <= 0) continue;
+ if (seq[0] != '[') continue;
+ if (read(STDIN_FILENO, &seq[1], 1) <= 0) continue;
+
+ if (seq[1] >= '0' && seq[1] <= '9') {
+ /* Extended sequence like \x1b[3~ (DELETE), \x1b[1~ (Home), \x1b[4~ (End) */
+ char trail;
+ if (read(STDIN_FILENO, &trail, 1) <= 0) continue;
+ if (trail == '~') {
+ if (seq[1] == '3') {
+ /* DELETE key — delete char at cursor */
+ if (pos < len) {
+ memmove(line + pos, line + pos + 1, (size_t)(len - pos - 1));
+ len--;
+ line[len] = '\0';
+ term_write(line + pos, len - pos);
+ term_write(" \b", 2);
+ for (int i = 0; i < len - pos; i++) term_write("\b", 1);
+ }
+ } else if (seq[1] == '1') {
+ /* Home */
+ while (pos > 0) { term_write("\b", 1); pos--; }
+ } else if (seq[1] == '4') {
+ /* End */
+ term_write(line + pos, len - pos);
+ pos = len;
+ }
+ }
+ continue;
+ }
+
+ switch (seq[1]) {
+ case 'A': /* Up arrow — previous history */
+ if (hist_pos > 0 && hist_pos > hist_count - HIST_SIZE) {
+ hist_pos--;
+ /* Clear current line */
+ while (pos > 0) { term_write("\b", 1); pos--; }
+ for (int i = 0; i < len; i++) term_write(" ", 1);
+ for (int i = 0; i < len; i++) term_write("\b", 1);
+ /* Load history entry */
+ strcpy(line, history[hist_pos % HIST_SIZE]);
+ len = (int)strlen(line);
+ pos = len;
+ term_write(line, len);
+ }
+ break;
+ case 'B': /* Down arrow — next history */
+ if (hist_pos < hist_count) {
+ hist_pos++;
+ while (pos > 0) { term_write("\b", 1); pos--; }
+ for (int i = 0; i < len; i++) term_write(" ", 1);
+ for (int i = 0; i < len; i++) term_write("\b", 1);
+ if (hist_pos < hist_count) {
+ strcpy(line, history[hist_pos % HIST_SIZE]);
+ } else {
+ line[0] = '\0';
+ }
+ len = (int)strlen(line);
+ pos = len;
+ term_write(line, len);
+ }
+ break;
+ case 'C': /* Right arrow */
+ if (pos < len) { term_write(line + pos, 1); pos++; }
+ break;
+ case 'D': /* Left arrow */
+ if (pos > 0) { term_write("\b", 1); pos--; }
+ break;
+ case 'H': /* Home */
+ while (pos > 0) { term_write("\b", 1); pos--; }
+ break;
+ case 'F': /* End */
+ term_write(line + pos, len - pos);
+ pos = len;
+ break;
+ }
+ continue;
+ }
+
+ /* Normal printable character */
+ if (c >= ' ' && c <= '~') {
+ memmove(line + pos + 1, line + pos, (size_t)(len - pos));
+ line[pos] = c;
+ len++; line[len] = '\0';
+ term_write(line + pos, len - pos);
+ pos++;
+ for (int i = 0; i < len - pos; i++) term_write("\b", 1);
+ }
+ }
+
+ line[len] = '\0';
+ return len;
+}
+
+/* ---- Variable expansion ---- */
+
+static void expand_vars(const char* src, char* dst, int maxlen) {
+ int di = 0;
+ while (*src && di < maxlen - 1) {
+ if (*src == '$') {
+ src++;
+ if (*src == '?') {
+ di += snprintf(dst + di, (size_t)(maxlen - di), "%d", last_status);
+ src++;
+ } else if (*src == '{') {
+ src++;
+ char name[64];
+ int ni = 0;
+ while (*src && *src != '}' && ni < 63) name[ni++] = *src++;
+ name[ni] = '\0';
+ if (*src == '}') src++;
+ const char* val = var_get(name);
+ if (val) {
+ int vl = (int)strlen(val);
+ if (di + vl < maxlen) { memcpy(dst + di, val, (size_t)vl); di += vl; }
+ }
+ } else {
+ char name[64];
+ int ni = 0;
+ while ((*src >= 'A' && *src <= 'Z') || (*src >= 'a' && *src <= 'z') ||
+ (*src >= '0' && *src <= '9') || *src == '_') {
+ if (ni < 63) name[ni++] = *src;
+ src++;
+ }
+ name[ni] = '\0';
+ const char* val = var_get(name);
+ if (val) {
+ int vl = (int)strlen(val);
+ if (di + vl < maxlen) { memcpy(dst + di, val, (size_t)vl); di += vl; }
+ }
+ }
+ } else {
+ dst[di++] = *src++;
+ }
+ }
+ dst[di] = '\0';
+}
+
+/* ---- Argument parsing with quote handling ---- */
+
+static int parse_args(char* cmd, char** argv, int max) {
+ int argc = 0;
+ char* p = cmd;
+ while (*p && argc < max - 1) {
+ while (*p == ' ' || *p == '\t') p++;
+ if (*p == '\0') break;
+
+ char* out = p;
+ argv[argc++] = out;
+
+ while (*p && *p != ' ' && *p != '\t') {
+ if (*p == '\'' ) {
+ p++;
+ while (*p && *p != '\'') *out++ = *p++;
+ if (*p == '\'') p++;
+ } else if (*p == '"') {
+ p++;
+ while (*p && *p != '"') *out++ = *p++;
+ if (*p == '"') p++;
+ } else {
+ *out++ = *p++;
+ }
+ }
+ if (*p) p++;
+ *out = '\0';
+ }
+ argv[argc] = NULL;
+ return argc;
+}
+
+/* ---- PATH resolution ---- */
+
+static char pathbuf[256];
+
+static const char* resolve(const char* cmd) {
+ if (cmd[0] == '/' || cmd[0] == '.') return cmd;
+
+ const char* path_env = var_get("PATH");
+ if (!path_env) path_env = "/bin:/sbin:/usr/bin";
+
+ char pathcopy[512];
+ strncpy(pathcopy, path_env, sizeof(pathcopy) - 1);
+ pathcopy[sizeof(pathcopy) - 1] = '\0';
+
+ char* save = pathcopy;
+ char* dir;
+ while ((dir = save) != NULL) {
+ char* sep = strchr(save, ':');
+ if (sep) { *sep = '\0'; save = sep + 1; }
+ else save = NULL;
+
+ snprintf(pathbuf, sizeof(pathbuf), "%s/%s", dir, cmd);
+ if (access(pathbuf, 0) == 0) return pathbuf;
+ }
+ return cmd;
+}
+
+/* ---- Helper: set foreground process group on controlling TTY ---- */
+
+static void set_fg_pgrp(int pgrp) {
+ ioctl(STDIN_FILENO, TIOCSPGRP, &pgrp);
+}
+
+/* ---- Run a single simple command ---- */
+
+static int bg_flag = 0; /* set by caller when trailing & detected */
+
+static void run_simple(char* cmd) {
+ /* Expand variables */
+ char expanded[LINE_MAX];
+ expand_vars(cmd, expanded, LINE_MAX);
+
+ char* argv[MAX_ARGS];
+ int argc = parse_args(expanded, argv, MAX_ARGS);
+ if (argc == 0) return;
+
+ /* Check for variable assignment (no command, just VAR=value) */
+ if (argc == 1 && strchr(argv[0], '=') != NULL) {
+ char* eq = strchr(argv[0], '=');
+ *eq = '\0';
+ var_set(argv[0], eq + 1, 0);
+ last_status = 0;
+ return;
+ }
+
+ /* Extract redirections */
+ char* redir_out = NULL;
+ char* redir_in = NULL;
+ int append = 0;
+ int heredoc_fd = -1;
+ int nargc = 0;
+ for (int i = 0; i < argc; i++) {
+ if (strcmp(argv[i], ">>") == 0 && i + 1 < argc) {
+ redir_out = argv[++i]; append = 1;
+ } else if (strcmp(argv[i], ">") == 0 && i + 1 < argc) {
+ redir_out = argv[++i]; append = 0;
+ } else if (strcmp(argv[i], "<<") == 0 && i + 1 < argc) {
+ char* delim = argv[++i];
+ int dlen = (int)strlen(delim);
+ if (dlen > 0 && (delim[0] == '"' || delim[0] == '\'')) {
+ delim++; dlen -= 2; if (dlen < 0) dlen = 0;
+ delim[dlen] = '\0';
+ }
+ int pfd[2];
+ if (pipe(pfd) == 0) {
+ tty_restore();
+ char hline[LINE_MAX];
+ while (1) {
+ write(STDOUT_FILENO, "> ", 2);
+ int hi = 0;
+ char hc;
+ while (read(STDIN_FILENO, &hc, 1) == 1) {
+ if (hc == '\n') break;
+ if (hi < LINE_MAX - 1) hline[hi++] = hc;
+ }
+ hline[hi] = '\0';
+ if (strcmp(hline, delim) == 0) break;
+ write(pfd[1], hline, hi);
+ write(pfd[1], "\n", 1);
+ }
+ close(pfd[1]);
+ heredoc_fd = pfd[0];
+ tty_raw_mode();
+ }
+ } else if (strcmp(argv[i], "<") == 0 && i + 1 < argc) {
+ redir_in = argv[++i];
+ } else {
+ argv[nargc++] = argv[i];
+ }
+ }
+ argv[nargc] = NULL;
+ argc = nargc;
+ if (argc == 0) return;
+
+ /* ---- Apply redirections for builtins too ---- */
+ int saved_stdin = -1, saved_stdout = -1;
+ if (heredoc_fd >= 0) {
+ saved_stdin = dup(0); dup2(heredoc_fd, 0); close(heredoc_fd); heredoc_fd = -1;
+ } else if (redir_in) {
+ int fd = open(redir_in, O_RDONLY);
+ if (fd >= 0) { saved_stdin = dup(0); dup2(fd, 0); close(fd); }
+ }
+ if (redir_out) {
+ int flags = O_WRONLY | O_CREAT;
+ flags |= append ? O_APPEND : O_TRUNC;
+ int fd = open(redir_out, flags);
+ if (fd >= 0) { saved_stdout = dup(1); dup2(fd, 1); close(fd); }
+ }
+
+ /* ---- Builtins ---- */
+
+ if (strcmp(argv[0], "exit") == 0) {
+ int code = argc > 1 ? atoi(argv[1]) : last_status;
+ exit(code);
+ }
+
+ if (strcmp(argv[0], "cd") == 0) {
+ const char* dir = argc > 1 ? argv[1] : var_get("HOME");
+ if (!dir) dir = "/";
+ if (chdir(dir) < 0)
+ fprintf(stderr, "cd: %s: No such file or directory\n", dir);
+ else {
+ char cwd[256];
+ if (getcwd(cwd, sizeof(cwd)) >= 0)
+ var_set("PWD", cwd, 1);
+ }
+ goto restore_redir;
+ }
+
+ if (strcmp(argv[0], "pwd") == 0) {
+ char cwd[256];
+ if (getcwd(cwd, sizeof(cwd)) >= 0)
+ printf("%s\n", cwd);
+ else
+ fprintf(stderr, "pwd: error\n");
+ goto restore_redir;
+ }
+
+ if (strcmp(argv[0], "export") == 0) {
+ for (int i = 1; i < argc; i++) {
+ char* eq = strchr(argv[i], '=');
+ if (eq) {
+ *eq = '\0';
+ var_set(argv[i], eq + 1, 1);
+ } else {
+ /* Export existing variable */
+ for (int j = 0; j < nvar; j++)
+ if (strcmp(vars[j].name, argv[i]) == 0)
+ vars[j].exported = 1;
+ }
+ }
+ goto restore_redir;
+ }
+
+ if (strcmp(argv[0], "unset") == 0) {
+ for (int i = 1; i < argc; i++) var_unset(argv[i]);
+ goto restore_redir;
+ }
+
+ if (strcmp(argv[0], "set") == 0) {
+ for (int i = 0; i < nvar; i++)
+ printf("%s=%s\n", vars[i].name, vars[i].value);
+ goto restore_redir;
+ }
+
+ if (strcmp(argv[0], "echo") == 0) {
+ int nflag = 0;
+ int start = 1;
+ if (argc > 1 && strcmp(argv[1], "-n") == 0) { nflag = 1; start = 2; }
+ for (int i = start; i < argc; i++) {
+ if (i > start) write(STDOUT_FILENO, " ", 1);
+ write(STDOUT_FILENO, argv[i], strlen(argv[i]));
+ }
+ if (!nflag) write(STDOUT_FILENO, "\n", 1);
+ goto restore_redir;
+ }
+
+ if (strcmp(argv[0], "type") == 0) {
+ for (int i = 1; i < argc; i++) {
+ if (strcmp(argv[i], "cd") == 0 || strcmp(argv[i], "exit") == 0 ||
+ strcmp(argv[i], "echo") == 0 || strcmp(argv[i], "export") == 0 ||
+ strcmp(argv[i], "unset") == 0 || strcmp(argv[i], "set") == 0 ||
+ strcmp(argv[i], "pwd") == 0 || strcmp(argv[i], "type") == 0) {
+ printf("%s is a shell builtin\n", argv[i]);
+ } else {
+ const char* path = resolve(argv[i]);
+ if (strcmp(path, argv[i]) != 0)
+ printf("%s is %s\n", argv[i], path);
+ else
+ printf("%s: not found\n", argv[i]);
+ }
+ }
+ goto restore_redir;
+ }
+
+ /* ---- External command — restore parent redirections before fork ---- */
+ const char* path = resolve(argv[0]);
+ char** envp = build_envp();
+
+ int pid = fork();
+ if (pid < 0) { fprintf(stderr, "sh: fork failed\n"); return; }
+
+ if (pid == 0) {
+ /* child: own process group, restore default signals */
+ setpgid(0, 0);
+ struct sigaction sa;
+ memset(&sa, 0, sizeof(sa));
+ sa.sa_handler = (uintptr_t)SIG_DFL;
+ sigaction(SIGINT, &sa, NULL);
+ sigaction(SIGTSTP, &sa, NULL);
+ sigaction(SIGQUIT, &sa, NULL);
+
+ if (redir_in) {
+ int fd = open(redir_in, O_RDONLY);
+ if (fd >= 0) { dup2(fd, 0); close(fd); }
+ }
+ if (redir_out) {
+ int flags = O_WRONLY | O_CREAT;
+ flags |= append ? O_APPEND : O_TRUNC;
+ int fd = open(redir_out, flags);
+ if (fd >= 0) { dup2(fd, 1); close(fd); }
+ }
+ execve(path, (const char* const*)argv, (const char* const*)envp);
+ fprintf(stderr, "sh: %s: not found\n", argv[0]);
+ _exit(127);
+ }
+
+ /* parent */
+ setpgid(pid, pid);
+
+ if (bg_flag) {
+ /* Background: don't wait, print job info */
+ printf("[bg] %d\n", pid);
+ last_status = 0;
+ } else {
+ /* Foreground: make child the fg process group, wait, then restore */
+ set_fg_pgrp(pid);
+ int st;
+ waitpid(pid, &st, 0);
+ set_fg_pgrp(getpgrp());
+ last_status = st;
+ }
+ goto restore_redir;
+
+restore_redir:
+ if (saved_stdout >= 0) { dup2(saved_stdout, 1); close(saved_stdout); }
+ if (saved_stdin >= 0) { dup2(saved_stdin, 0); close(saved_stdin); }
+}
+
+/* ---- Pipeline support ---- */
+
+static void run_pipeline(char* cmdline) {
+ /* Split on '|' (outside quotes) */
+ char* cmds[8];
+ int ncmds = 0;
+ cmds[0] = cmdline;
+ int in_sq = 0, in_dq = 0;
+ for (char* p = cmdline; *p; p++) {
+ if (*p == '\'' && !in_dq) in_sq = !in_sq;
+ else if (*p == '"' && !in_sq) in_dq = !in_dq;
+ else if (*p == '|' && !in_sq && !in_dq && ncmds < 7) {
+ if (*(p + 1) == '|') { p++; continue; } /* skip || */
+ *p = '\0';
+ cmds[++ncmds] = p + 1;
+ }
+ }
+ ncmds++;
+
+ if (ncmds == 1) {
+ run_simple(cmds[0]);
+ return;
+ }
+
+ /* Multi-stage pipeline */
+ int prev_rd = -1;
+ int pids[8];
+ int pgid = 0; /* pipeline process group = first child's PID */
+
+ for (int i = 0; i < ncmds; i++) {
+ int pfd[2] = {-1, -1};
+ if (i < ncmds - 1) {
+ if (pipe(pfd) < 0) {
+ fprintf(stderr, "sh: pipe failed\n");
+ return;
+ }
+ }
+
+ pids[i] = fork();
+ if (pids[i] < 0) { fprintf(stderr, "sh: fork failed\n"); return; }
+
+ if (pids[i] == 0) {
+ /* child: join pipeline process group, restore signals */
+ int mypgid = pgid ? pgid : getpid();
+ setpgid(0, mypgid);
+ struct sigaction sa;
+ memset(&sa, 0, sizeof(sa));
+ sa.sa_handler = (uintptr_t)SIG_DFL;
+ sigaction(SIGINT, &sa, NULL);
+ sigaction(SIGTSTP, &sa, NULL);
+ sigaction(SIGQUIT, &sa, NULL);
+
+ if (prev_rd >= 0) { dup2(prev_rd, 0); close(prev_rd); }
+ if (pfd[1] >= 0) { dup2(pfd[1], 1); close(pfd[1]); }
+ if (pfd[0] >= 0) close(pfd[0]);
+
+ /* Expand and parse this pipeline stage */
+ char expanded[LINE_MAX];
+ expand_vars(cmds[i], expanded, LINE_MAX);
+ char* argv[MAX_ARGS];
+ int argc = parse_args(expanded, argv, MAX_ARGS);
+ if (argc == 0) _exit(0);
+ const char* path = resolve(argv[0]);
+ char** envp = build_envp();
+ execve(path, (const char* const*)argv, (const char* const*)envp);
+ fprintf(stderr, "sh: %s: not found\n", argv[0]);
+ _exit(127);
+ }
+
+ /* parent: set pipeline pgid */
+ if (i == 0) pgid = pids[0];
+ setpgid(pids[i], pgid);
+
+ if (prev_rd >= 0) close(prev_rd);
+ if (pfd[1] >= 0) close(pfd[1]);
+ prev_rd = pfd[0];
+ }
+
+ if (prev_rd >= 0) close(prev_rd);
+
+ if (!bg_flag) {
+ /* Foreground pipeline: make it fg, wait, restore */
+ set_fg_pgrp(pgid);
+ for (int i = 0; i < ncmds; i++) {
+ int st;
+ waitpid(pids[i], &st, 0);
+ if (i == ncmds - 1) last_status = st;
+ }
+ set_fg_pgrp(getpgrp());
+ } else {
+ printf("[bg] %d\n", pgid);
+ last_status = 0;
+ }
+}
+
+/* ---- Process a command line (handle ;, &&, ||, &) ---- */
+
+enum { OP_NONE = 0, OP_SEMI, OP_AND, OP_OR, OP_BG };
+
+static void process_line(char* input) {
+ char* p = input;
+
+ while (*p) {
+ while (*p == ' ' || *p == '\t') p++;
+ if (*p == '\0') break;
+
+ /* Find the next operator outside quotes */
+ char* start = p;
+ int in_sq = 0, in_dq = 0;
+ int op = OP_NONE;
+
+ while (*p) {
+ if (*p == '\'' && !in_dq) { in_sq = !in_sq; p++; continue; }
+ if (*p == '"' && !in_sq) { in_dq = !in_dq; p++; continue; }
+ if (in_sq || in_dq) { p++; continue; }
+
+ if (*p == '&' && *(p + 1) == '&') {
+ *p = '\0'; p += 2; op = OP_AND; break;
+ }
+ if (*p == '|' && *(p + 1) == '|') {
+ *p = '\0'; p += 2; op = OP_OR; break;
+ }
+ if (*p == ';') {
+ *p = '\0'; p++; op = OP_SEMI; break;
+ }
+ if (*p == '&') {
+ *p = '\0'; p++; op = OP_BG; break;
+ }
+ p++;
+ }
+
+ /* Trim leading whitespace from segment */
+ while (*start == ' ' || *start == '\t') start++;
+ if (*start != '\0') {
+ if (op == OP_BG) {
+ bg_flag = 1;
+ run_pipeline(start);
+ bg_flag = 0;
+ } else {
+ bg_flag = 0;
+ run_pipeline(start);
+ }
+ }
+
+ /* For &&: skip remaining commands if last failed */
+ if (op == OP_AND && last_status != 0) {
+ /* Skip until we hit || or ; or & or end */
+ while (*p) {
+ while (*p == ' ' || *p == '\t') p++;
+ if (*p == '\0') break;
+ int skip_sq = 0, skip_dq = 0;
+ while (*p) {
+ if (*p == '\'' && !skip_dq) { skip_sq = !skip_sq; p++; continue; }
+ if (*p == '"' && !skip_sq) { skip_dq = !skip_dq; p++; continue; }
+ if (skip_sq || skip_dq) { p++; continue; }
+ if (*p == '|' && *(p + 1) == '|') break;
+ if (*p == ';') break;
+ if (*p == '&' && *(p + 1) != '&') break;
+ if (*p == '&' && *(p + 1) == '&') { p += 2; continue; }
+ p++;
+ }
+ break;
+ }
+ }
+
+ /* For ||: skip remaining commands if last succeeded */
+ if (op == OP_OR && last_status == 0) {
+ while (*p) {
+ while (*p == ' ' || *p == '\t') p++;
+ if (*p == '\0') break;
+ int skip_sq = 0, skip_dq = 0;
+ while (*p) {
+ if (*p == '\'' && !skip_dq) { skip_sq = !skip_sq; p++; continue; }
+ if (*p == '"' && !skip_sq) { skip_dq = !skip_dq; p++; continue; }
+ if (skip_sq || skip_dq) { p++; continue; }
+ if (*p == '&' && *(p + 1) == '&') break;
+ if (*p == ';') break;
+ if (*p == '&' && *(p + 1) != '&') break;
+ if (*p == '|' && *(p + 1) == '|') { p += 2; continue; }
+ p++;
+ }
+ break;
+ }
+ }
+ }
+}
+
+/* ---- Prompt ---- */
+
+static void print_prompt(void) {
+ const char* user = var_get("USER");
+ const char* host = var_get("HOSTNAME");
+ char cwd[256];
+
+ if (!user) user = "root";
+ if (!host) host = "adros";
+
+ if (getcwd(cwd, sizeof(cwd)) < 0) strcpy(cwd, "?");
+
+ printf("%s@%s:%s$ ", user, host, cwd);
+ fflush(stdout);
+}
+
+/* ---- Main ---- */
+
+int main(int argc, char** argv, char** envp) {
+ (void)argc;
+ (void)argv;
+
+ /* Import environment variables */
+ if (envp) {
+ for (int i = 0; envp[i]; i++) {
+ char* eq = strchr(envp[i], '=');
+ if (eq) {
+ char name[64];
+ int nlen = (int)(eq - envp[i]);
+ if (nlen > 63) nlen = 63;
+ memcpy(name, envp[i], (size_t)nlen);
+ name[nlen] = '\0';
+ var_set(name, eq + 1, 1);
+ }
+ }
+ }
+
+ /* Set defaults */
+ if (!var_get("PATH"))
+ var_set("PATH", "/bin:/sbin:/usr/bin", 1);
+ if (!var_get("HOME"))
+ var_set("HOME", "/", 1);
+
+ /* Job control: create session + process group, become fg */
+ setsid();
+ set_fg_pgrp(getpgrp());
+
+ /* Ignore job control signals in the shell itself */
+ struct sigaction sa_ign;
+ memset(&sa_ign, 0, sizeof(sa_ign));
+ sa_ign.sa_handler = (uintptr_t)SIG_IGN;
+ sigaction(SIGINT, &sa_ign, NULL);
+ sigaction(SIGTSTP, &sa_ign, NULL);
+ sigaction(SIGQUIT, &sa_ign, NULL);
+
+ tty_raw_mode();
+
+ print_prompt();
+ while (1) {
+ int len = read_line_edit();
+ if (len < 0) break;
+ if (len > 0) {
+ hist_add(line);
+ tty_restore();
+ process_line(line);
+ tty_raw_mode();
+ }
+ print_prompt();
+ }
+
+ tty_restore();
+ return last_status;
+}
--- /dev/null
+NAME := sleep
+SRCS := sleep.c
+include ../common.mk
--- /dev/null
+/* AdrOS sleep utility — pause for N seconds */
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <time.h>
+
+int main(int argc, char** argv) {
+ if (argc <= 1) {
+ fprintf(stderr, "usage: sleep SECONDS\n");
+ return 1;
+ }
+ int secs = atoi(argv[1]);
+ if (secs > 0) {
+ struct timespec ts = { .tv_sec = secs, .tv_nsec = 0 };
+ nanosleep(&ts, NULL);
+ }
+ return 0;
+}
--- /dev/null
+NAME := sort
+SRCS := sort.c
+include ../common.mk
--- /dev/null
+/* AdrOS sort utility */
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <fcntl.h>
+
+#define MAX_LINES 1024
+#define LINE_BUF 65536
+
+static char linebuf[LINE_BUF];
+static char* lines[MAX_LINES];
+static int nlines = 0;
+
+static int rflag = 0; /* -r: reverse */
+static int nflag = 0; /* -n: numeric */
+
+static int cmp(const void* a, const void* b) {
+ const char* sa = *(const char**)a;
+ const char* sb = *(const char**)b;
+ int r;
+ if (nflag) {
+ r = atoi(sa) - atoi(sb);
+ } else {
+ r = strcmp(sa, sb);
+ }
+ return rflag ? -r : r;
+}
+
+static void read_lines(int fd) {
+ int total = 0;
+ int r;
+ while ((r = read(fd, linebuf + total, (size_t)(LINE_BUF - total - 1))) > 0) {
+ total += r;
+ if (total >= LINE_BUF - 1) break;
+ }
+ linebuf[total] = '\0';
+
+ /* Split into lines */
+ char* p = linebuf;
+ while (*p && nlines < MAX_LINES) {
+ lines[nlines++] = p;
+ while (*p && *p != '\n') p++;
+ if (*p == '\n') *p++ = '\0';
+ }
+}
+
+int main(int argc, char** argv) {
+ int start = 1;
+ for (int i = 1; i < argc; i++) {
+ if (argv[i][0] == '-') {
+ const char* f = argv[i] + 1;
+ while (*f) {
+ if (*f == 'r') rflag = 1;
+ else if (*f == 'n') nflag = 1;
+ f++;
+ }
+ start = i + 1;
+ } else break;
+ }
+
+ if (start >= argc) {
+ read_lines(STDIN_FILENO);
+ } else {
+ for (int i = start; i < argc; i++) {
+ int fd = open(argv[i], O_RDONLY);
+ if (fd < 0) {
+ fprintf(stderr, "sort: cannot open '%s'\n", argv[i]);
+ return 1;
+ }
+ read_lines(fd);
+ close(fd);
+ }
+ }
+
+ /* Simple insertion sort (no qsort in ulibc yet) */
+ for (int i = 1; i < nlines; i++) {
+ char* key = lines[i];
+ int j = i - 1;
+ while (j >= 0 && cmp(&lines[j], &key) > 0) {
+ lines[j + 1] = lines[j];
+ j--;
+ }
+ lines[j + 1] = key;
+ }
+
+ for (int i = 0; i < nlines; i++)
+ printf("%s\n", lines[i]);
+
+ return 0;
+}
--- /dev/null
+NAME := stat
+SRCS := stat.c
+include ../common.mk
--- /dev/null
+/* AdrOS stat utility — display file status */
+#include <stdio.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <sys/stat.h>
+
+int main(int argc, char** argv) {
+ if (argc <= 1) {
+ fprintf(stderr, "usage: stat FILE...\n");
+ return 1;
+ }
+ int rc = 0;
+ for (int i = 1; i < argc; i++) {
+ struct stat st;
+ if (stat(argv[i], (void*)&st) < 0) {
+ fprintf(stderr, "stat: cannot stat '%s'\n", argv[i]);
+ rc = 1;
+ continue;
+ }
+ printf(" File: %s\n", argv[i]);
+ printf(" Size: %u\tInode: %u\n", (unsigned)st.st_size, (unsigned)st.st_ino);
+ printf(" Mode: %o\tUid: %u\tGid: %u\n", (unsigned)st.st_mode, (unsigned)st.st_uid, (unsigned)st.st_gid);
+ }
+ return rc;
+}
--- /dev/null
+NAME := tail
+SRCS := tail.c
+include ../common.mk
--- /dev/null
+/* AdrOS tail utility */
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <string.h>
+
+#define TAIL_BUFSZ 8192
+
+static void tail_fd(int fd, int nlines) {
+ /* Read entire file into buffer, then print last N lines */
+ char buf[TAIL_BUFSZ];
+ int total = 0;
+ int r;
+ while ((r = read(fd, buf + total, (size_t)(TAIL_BUFSZ - total))) > 0) {
+ total += r;
+ if (total >= TAIL_BUFSZ) break;
+ }
+
+ /* Count newlines from end; skip trailing newline */
+ int count = 0;
+ int pos = total;
+ if (pos > 0 && buf[pos - 1] == '\n') pos--;
+ while (pos > 0 && count < nlines) {
+ pos--;
+ if (buf[pos] == '\n') count++;
+ }
+ if (pos > 0 || (pos == 0 && buf[0] == '\n')) pos++;
+
+ write(STDOUT_FILENO, buf + pos, (size_t)(total - pos));
+}
+
+int main(int argc, char** argv) {
+ int nlines = 10;
+ int start = 1;
+
+ if (argc > 1 && argv[1][0] == '-' && argv[1][1] == 'n' && argc > 2) {
+ nlines = atoi(argv[2]);
+ start = 3;
+ } else if (argc > 1 && argv[1][0] == '-' && argv[1][1] >= '0' && argv[1][1] <= '9') {
+ nlines = atoi(argv[1] + 1);
+ start = 2;
+ }
+
+ if (start >= argc) {
+ tail_fd(STDIN_FILENO, nlines);
+ } else {
+ for (int i = start; i < argc; i++) {
+ if (argc - start > 1) printf("==> %s <==\n", argv[i]);
+ int fd = open(argv[i], O_RDONLY);
+ if (fd < 0) {
+ fprintf(stderr, "tail: cannot open '%s'\n", argv[i]);
+ continue;
+ }
+ tail_fd(fd, nlines);
+ close(fd);
+ }
+ }
+ return 0;
+}
--- /dev/null
+NAME := tee
+SRCS := tee.c
+include ../common.mk
--- /dev/null
+/* AdrOS tee utility — read stdin, write to stdout and files */
+#include <stdio.h>
+#include <string.h>
+#include <unistd.h>
+#include <fcntl.h>
+
+int main(int argc, char** argv) {
+ int aflag = 0;
+ int fds[16];
+ int nfds = 0;
+
+ for (int i = 1; i < argc && nfds < 16; i++) {
+ if (strcmp(argv[i], "-a") == 0) { aflag = 1; continue; }
+ int flags = O_WRONLY | O_CREAT;
+ flags |= aflag ? O_APPEND : O_TRUNC;
+ int fd = open(argv[i], flags, 0644);
+ if (fd < 0) {
+ fprintf(stderr, "tee: %s: cannot open\n", argv[i]);
+ continue;
+ }
+ fds[nfds++] = fd;
+ }
+
+ char buf[4096];
+ int n;
+ while ((n = read(STDIN_FILENO, buf, sizeof(buf))) > 0) {
+ write(STDOUT_FILENO, buf, (size_t)n);
+ for (int i = 0; i < nfds; i++)
+ write(fds[i], buf, (size_t)n);
+ }
+
+ for (int i = 0; i < nfds; i++) close(fds[i]);
+ return 0;
+}
--- /dev/null
+NAME := top
+SRCS := top.c
+include ../common.mk
--- /dev/null
+/* AdrOS top utility — one-shot process listing with basic info */
+#include <stdio.h>
+#include <string.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <dirent.h>
+
+static int is_digit(char c) { return c >= '0' && c <= '9'; }
+
+int main(void) {
+ printf(" PID STATE CMD\n");
+ int fd = open("/proc", O_RDONLY);
+ if (fd < 0) {
+ fprintf(stderr, "top: cannot open /proc\n");
+ return 1;
+ }
+ char buf[512];
+ int rc;
+ while ((rc = getdents(fd, buf, sizeof(buf))) > 0) {
+ int off = 0;
+ while (off < rc) {
+ struct dirent* d = (struct dirent*)(buf + off);
+ if (d->d_reclen == 0) break;
+ if (is_digit(d->d_name[0])) {
+ char path[64];
+
+ /* Read cmdline */
+ snprintf(path, sizeof(path), "/proc/%s/cmdline", d->d_name);
+ int cfd = open(path, O_RDONLY);
+ char cmd[64] = "[kernel]";
+ if (cfd >= 0) {
+ int n = read(cfd, cmd, sizeof(cmd) - 1);
+ if (n > 0) {
+ cmd[n] = '\0';
+ while (n > 0 && (cmd[n-1] == '\n' || cmd[n-1] == '\0')) cmd[--n] = '\0';
+ }
+ if (n <= 0) strcpy(cmd, "[kernel]");
+ close(cfd);
+ }
+
+ /* Read status for state */
+ snprintf(path, sizeof(path), "/proc/%s/status", d->d_name);
+ int sfd = open(path, O_RDONLY);
+ char state[16] = "?";
+ if (sfd >= 0) {
+ char sbuf[256];
+ int sn = read(sfd, sbuf, sizeof(sbuf) - 1);
+ if (sn > 0) {
+ sbuf[sn] = '\0';
+ char* st = strstr(sbuf, "State:");
+ if (st) {
+ st += 6;
+ while (*st == ' ' || *st == '\t') st++;
+ int si = 0;
+ while (*st && *st != '\n' && si < 15) state[si++] = *st++;
+ state[si] = '\0';
+ }
+ }
+ close(sfd);
+ }
+
+ printf("%5s %6s %s\n", d->d_name, state, cmd);
+ }
+ off += d->d_reclen;
+ }
+ }
+ close(fd);
+ return 0;
+}
--- /dev/null
+NAME := touch
+SRCS := touch.c
+include ../common.mk
--- /dev/null
+/* AdrOS touch utility */
+#include <stdio.h>
+#include <unistd.h>
+#include <fcntl.h>
+
+int main(int argc, char** argv) {
+ if (argc < 2) {
+ fprintf(stderr, "Usage: touch <file>...\n");
+ return 1;
+ }
+
+ int rc = 0;
+ for (int i = 1; i < argc; i++) {
+ int fd = open(argv[i], O_WRONLY | O_CREAT, 0644);
+ if (fd < 0) {
+ fprintf(stderr, "touch: cannot touch '%s'\n", argv[i]);
+ rc = 1;
+ } else {
+ close(fd);
+ }
+ }
+ return rc;
+}
--- /dev/null
+NAME := tr
+SRCS := tr.c
+include ../common.mk
--- /dev/null
+/* AdrOS tr utility — translate or delete characters */
+#include <stdio.h>
+#include <string.h>
+#include <unistd.h>
+
+int main(int argc, char** argv) {
+ int delete_mode = 0;
+ int start = 1;
+
+ if (argc > 1 && strcmp(argv[1], "-d") == 0) {
+ delete_mode = 1;
+ start = 2;
+ }
+
+ if (delete_mode) {
+ if (start >= argc) { fprintf(stderr, "usage: tr -d SET1\n"); return 1; }
+ const char* set1 = argv[start];
+ char c;
+ while (read(STDIN_FILENO, &c, 1) > 0) {
+ if (!strchr(set1, c))
+ write(STDOUT_FILENO, &c, 1);
+ }
+ } else {
+ if (start + 1 >= argc) { fprintf(stderr, "usage: tr SET1 SET2\n"); return 1; }
+ const char* set1 = argv[start];
+ const char* set2 = argv[start + 1];
+ int len1 = (int)strlen(set1);
+ int len2 = (int)strlen(set2);
+ char c;
+ while (read(STDIN_FILENO, &c, 1) > 0) {
+ int found = 0;
+ for (int i = 0; i < len1; i++) {
+ if (c == set1[i]) {
+ char r = (i < len2) ? set2[i] : set2[len2 - 1];
+ write(STDOUT_FILENO, &r, 1);
+ found = 1;
+ break;
+ }
+ }
+ if (!found) write(STDOUT_FILENO, &c, 1);
+ }
+ }
+ return 0;
+}
--- /dev/null
+NAME := umount
+SRCS := umount.c
+include ../common.mk
--- /dev/null
+/* AdrOS umount utility — stub (no SYS_UMOUNT syscall yet) */
+#include <stdio.h>
+
+int main(int argc, char** argv) {
+ if (argc <= 1) {
+ fprintf(stderr, "umount: missing operand\n");
+ return 1;
+ }
+ fprintf(stderr, "umount: %s: operation not supported\n", argv[1]);
+ return 1;
+}
--- /dev/null
+NAME := uname
+SRCS := uname.c
+include ../common.mk
--- /dev/null
+/* AdrOS uname utility — print system information */
+#include <stdio.h>
+#include <string.h>
+
+int main(int argc, char** argv) {
+ const char* sysname = "AdrOS";
+ const char* nodename = "adros";
+ const char* release = "0.1.0";
+ const char* version = "AdrOS x86 SMP";
+ const char* machine = "i686";
+
+ if (argc <= 1) { printf("%s\n", sysname); return 0; }
+
+ int all = 0, s = 0, n = 0, r = 0, v = 0, m = 0;
+ for (int i = 1; i < argc; i++) {
+ if (strcmp(argv[i], "-a") == 0) all = 1;
+ else if (strcmp(argv[i], "-s") == 0) s = 1;
+ else if (strcmp(argv[i], "-n") == 0) n = 1;
+ else if (strcmp(argv[i], "-r") == 0) r = 1;
+ else if (strcmp(argv[i], "-v") == 0) v = 1;
+ else if (strcmp(argv[i], "-m") == 0) m = 1;
+ }
+ if (all) { s = n = r = v = m = 1; }
+ if (!s && !n && !r && !v && !m) s = 1;
+
+ int first = 1;
+ if (s) { printf("%s%s", first ? "" : " ", sysname); first = 0; }
+ if (n) { printf("%s%s", first ? "" : " ", nodename); first = 0; }
+ if (r) { printf("%s%s", first ? "" : " ", release); first = 0; }
+ if (v) { printf("%s%s", first ? "" : " ", version); first = 0; }
+ if (m) { printf("%s%s", first ? "" : " ", machine); first = 0; }
+ printf("\n");
+ return 0;
+}
--- /dev/null
+NAME := uniq
+SRCS := uniq.c
+include ../common.mk
--- /dev/null
+/* AdrOS uniq utility */
+#include <stdio.h>
+#include <string.h>
+#include <unistd.h>
+#include <fcntl.h>
+
+#define LINE_MAX 1024
+
+static int cflag = 0; /* -c: prefix lines with count */
+static int dflag = 0; /* -d: only print duplicates */
+
+static int readline(int fd, char* buf, int max) {
+ int n = 0;
+ char c;
+ while (n < max - 1) {
+ int r = read(fd, &c, 1);
+ if (r <= 0) break;
+ if (c == '\n') break;
+ buf[n++] = c;
+ }
+ buf[n] = '\0';
+ return n > 0 ? n : (n == 0 ? 0 : -1);
+}
+
+int main(int argc, char** argv) {
+ int start = 1;
+ int fd = STDIN_FILENO;
+
+ for (int i = 1; i < argc; i++) {
+ if (argv[i][0] == '-' && argv[i][1]) {
+ const char* f = argv[i] + 1;
+ while (*f) {
+ if (*f == 'c') cflag = 1;
+ else if (*f == 'd') dflag = 1;
+ f++;
+ }
+ start = i + 1;
+ } else break;
+ }
+
+ if (start < argc) {
+ fd = open(argv[start], O_RDONLY);
+ if (fd < 0) {
+ fprintf(stderr, "uniq: cannot open '%s'\n", argv[start]);
+ return 1;
+ }
+ }
+
+ char prev[LINE_MAX] = {0};
+ char cur[LINE_MAX];
+ int count = 0;
+ int first = 1;
+
+ while (1) {
+ int r = readline(fd, cur, LINE_MAX);
+ if (r < 0) break;
+
+ if (first || strcmp(cur, prev) != 0) {
+ if (!first) {
+ if (!dflag || count > 1) {
+ if (cflag) printf("%7d %s\n", count, prev);
+ else printf("%s\n", prev);
+ }
+ }
+ strcpy(prev, cur);
+ count = 1;
+ first = 0;
+ } else {
+ count++;
+ }
+
+ if (r == 0) break;
+ }
+
+ /* Print last line */
+ if (!first) {
+ if (!dflag || count > 1) {
+ if (cflag) printf("%7d %s\n", count, prev);
+ else printf("%s\n", prev);
+ }
+ }
+
+ if (fd != STDIN_FILENO) close(fd);
+ return 0;
+}
--- /dev/null
+NAME := uptime
+SRCS := uptime.c
+include ../common.mk
--- /dev/null
+/* AdrOS uptime utility */
+#include <stdio.h>
+#include <time.h>
+#include <unistd.h>
+#include <fcntl.h>
+
+int main(int argc, char** argv) {
+ (void)argc; (void)argv;
+
+ /* Try /proc/uptime first */
+ int fd = open("/proc/uptime", O_RDONLY);
+ if (fd >= 0) {
+ char buf[64];
+ int r = read(fd, buf, sizeof(buf) - 1);
+ close(fd);
+ if (r > 0) {
+ buf[r] = '\0';
+ printf("up %s", buf);
+ return 0;
+ }
+ }
+
+ /* Fallback: use CLOCK_MONOTONIC */
+ struct timespec ts;
+ if (clock_gettime(CLOCK_MONOTONIC, &ts) < 0) {
+ fprintf(stderr, "uptime: cannot get time\n");
+ return 1;
+ }
+
+ unsigned long sec = ts.tv_sec;
+ unsigned long days = sec / 86400;
+ unsigned long hours = (sec % 86400) / 3600;
+ unsigned long mins = (sec % 3600) / 60;
+ unsigned long secs = sec % 60;
+
+ printf("up");
+ if (days > 0) printf(" %lu day%s,", days, days > 1 ? "s" : "");
+ printf(" %02lu:%02lu:%02lu\n", hours, mins, secs);
+ return 0;
+}
--- /dev/null
+NAME := wc
+SRCS := wc.c
+include ../common.mk
--- /dev/null
+/* AdrOS wc utility */
+#include <stdio.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <string.h>
+
+static void wc_fd(int fd, const char* name, int show_l, int show_w, int show_c) {
+ char buf[4096];
+ int lines = 0, words = 0, chars = 0;
+ int in_word = 0;
+ int r;
+
+ while ((r = read(fd, buf, sizeof(buf))) > 0) {
+ for (int i = 0; i < r; i++) {
+ chars++;
+ if (buf[i] == '\n') lines++;
+ if (buf[i] == ' ' || buf[i] == '\t' || buf[i] == '\n') {
+ in_word = 0;
+ } else if (!in_word) {
+ in_word = 1;
+ words++;
+ }
+ }
+ }
+
+ if (show_l) printf("%7d", lines);
+ if (show_w) printf("%7d", words);
+ if (show_c) printf("%7d", chars);
+ if (name) printf(" %s", name);
+ printf("\n");
+}
+
+int main(int argc, char** argv) {
+ int show_l = 0, show_w = 0, show_c = 0;
+ int start = 1;
+
+ for (int i = 1; i < argc; i++) {
+ if (argv[i][0] == '-' && argv[i][1]) {
+ const char* f = argv[i] + 1;
+ while (*f) {
+ if (*f == 'l') show_l = 1;
+ else if (*f == 'w') show_w = 1;
+ else if (*f == 'c') show_c = 1;
+ f++;
+ }
+ start = i + 1;
+ } else break;
+ }
+
+ /* Default: show all */
+ if (!show_l && !show_w && !show_c) {
+ show_l = show_w = show_c = 1;
+ }
+
+ if (start >= argc) {
+ wc_fd(STDIN_FILENO, NULL, show_l, show_w, show_c);
+ } else {
+ for (int i = start; i < argc; i++) {
+ int fd = open(argv[i], O_RDONLY);
+ if (fd < 0) {
+ fprintf(stderr, "wc: %s: No such file\n", argv[i]);
+ continue;
+ }
+ wc_fd(fd, argv[i], show_l, show_w, show_c);
+ close(fd);
+ }
+ }
+ return 0;
+}
--- /dev/null
+NAME := which
+SRCS := which.c
+include ../common.mk
--- /dev/null
+/* AdrOS which utility — locate a command */
+#include <stdio.h>
+#include <string.h>
+#include <dirent.h>
+
+static int exists_in_dir(const char* dirname, const char* name) {
+ DIR* dir = opendir(dirname);
+ if (!dir) return 0;
+ struct dirent* d;
+ while ((d = readdir(dir)) != NULL) {
+ if (strcmp(d->d_name, name) == 0) {
+ closedir(dir);
+ return 1;
+ }
+ }
+ closedir(dir);
+ return 0;
+}
+
+int main(int argc, char** argv) {
+ if (argc < 2) {
+ fprintf(stderr, "Usage: which command\n");
+ return 1;
+ }
+
+ static const char* path_dirs[] = { "/bin", "/sbin", NULL };
+ int ret = 1;
+
+ for (int i = 1; i < argc; i++) {
+ int found = 0;
+ for (int d = 0; path_dirs[d]; d++) {
+ if (exists_in_dir(path_dirs[d], argv[i])) {
+ printf("%s/%s\n", path_dirs[d], argv[i]);
+ found = 1;
+ break;
+ }
+ }
+ if (found) ret = 0;
+ }
+ return ret;
+}
--- /dev/null
+NAME := who
+SRCS := who.c
+include ../common.mk
--- /dev/null
+/* AdrOS who utility — show logged-in users */
+#include <stdio.h>
+#include <unistd.h>
+
+int main(void) {
+ printf("root tty1 Jan 1 00:00\n");
+ return 0;
+}
+++ /dev/null
-/* AdrOS cp utility */
-#include <stdio.h>
-#include <unistd.h>
-#include <fcntl.h>
-#include <string.h>
-
-int main(int argc, char** argv) {
- if (argc < 3) {
- fprintf(stderr, "Usage: cp <source> <dest>\n");
- return 1;
- }
-
- int src = open(argv[1], O_RDONLY);
- if (src < 0) {
- fprintf(stderr, "cp: cannot open '%s'\n", argv[1]);
- return 1;
- }
-
- int dst = open(argv[2], O_WRONLY | O_CREAT | O_TRUNC, 0644);
- if (dst < 0) {
- fprintf(stderr, "cp: cannot create '%s'\n", argv[2]);
- close(src);
- return 1;
- }
-
- char buf[4096];
- int r;
- while ((r = read(src, buf, sizeof(buf))) > 0) {
- int w = write(dst, buf, (size_t)r);
- if (w != r) {
- fprintf(stderr, "cp: write error\n");
- close(src);
- close(dst);
- return 1;
- }
- }
-
- close(src);
- close(dst);
- return 0;
-}
+++ /dev/null
-/* AdrOS cut utility */
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <unistd.h>
-#include <fcntl.h>
-
-#define LINE_MAX 1024
-
-static char delim = '\t';
-static int fields[32];
-static int nfields = 0;
-
-static void parse_fields(const char* spec) {
- const char* p = spec;
- while (*p && nfields < 32) {
- fields[nfields++] = atoi(p);
- while (*p && *p != ',') p++;
- if (*p == ',') p++;
- }
-}
-
-static void cut_line(char* line) {
- if (nfields == 0) {
- printf("%s\n", line);
- return;
- }
-
- /* Split line into fields */
- char* flds[64];
- int nf = 0;
- char* p = line;
- flds[nf++] = p;
- while (*p && nf < 64) {
- if (*p == delim) {
- *p = '\0';
- flds[nf++] = p + 1;
- }
- p++;
- }
-
- /* Print requested fields */
- int first = 1;
- for (int i = 0; i < nfields; i++) {
- int idx = fields[i] - 1; /* 1-based */
- if (idx >= 0 && idx < nf) {
- if (!first) write(STDOUT_FILENO, &delim, 1);
- printf("%s", flds[idx]);
- first = 0;
- }
- }
- printf("\n");
-}
-
-static void cut_fd(int fd) {
- char line[LINE_MAX];
- int pos = 0;
- char c;
-
- while (read(fd, &c, 1) > 0) {
- if (c == '\n') {
- line[pos] = '\0';
- cut_line(line);
- pos = 0;
- } else if (pos < LINE_MAX - 1) {
- line[pos++] = c;
- }
- }
- if (pos > 0) {
- line[pos] = '\0';
- cut_line(line);
- }
-}
-
-int main(int argc, char** argv) {
- int start = 1;
-
- for (int i = 1; i < argc; i++) {
- if (strcmp(argv[i], "-d") == 0 && i + 1 < argc) {
- delim = argv[++i][0];
- start = i + 1;
- } else if (strncmp(argv[i], "-d", 2) == 0 && argv[i][2] != '\0') {
- delim = argv[i][2];
- start = i + 1;
- } else if (strcmp(argv[i], "-f") == 0 && i + 1 < argc) {
- parse_fields(argv[++i]);
- start = i + 1;
- } else if (strncmp(argv[i], "-f", 2) == 0 && argv[i][2] != '\0') {
- parse_fields(argv[i] + 2);
- start = i + 1;
- } else if (argv[i][0] != '-') {
- break;
- }
- }
-
- if (start >= argc) {
- cut_fd(STDIN_FILENO);
- } else {
- for (int i = start; i < argc; i++) {
- int fd = open(argv[i], O_RDONLY);
- if (fd < 0) {
- fprintf(stderr, "cut: cannot open '%s'\n", argv[i]);
- continue;
- }
- cut_fd(fd);
- close(fd);
- }
- }
- return 0;
-}
+++ /dev/null
-/* AdrOS date utility */
-#include <stdio.h>
-#include <time.h>
-
-int main(int argc, char** argv) {
- (void)argc; (void)argv;
- struct timespec ts;
- if (clock_gettime(CLOCK_REALTIME, &ts) < 0) {
- fprintf(stderr, "date: cannot get time\n");
- return 1;
- }
-
- /* Simple epoch seconds display — no timezone or strftime yet */
- unsigned long sec = ts.tv_sec;
- unsigned long days = sec / 86400;
- unsigned long rem = sec % 86400;
- unsigned long hours = rem / 3600;
- unsigned long mins = (rem % 3600) / 60;
- unsigned long secs = rem % 60;
-
- printf("%lu days since epoch, %02lu:%02lu:%02lu UTC\n",
- days, hours, mins, secs);
- return 0;
-}
+++ /dev/null
-/* AdrOS dd utility — convert and copy a file */
-#include <stdio.h>
-#include <string.h>
-#include <stdlib.h>
-#include <unistd.h>
-#include <fcntl.h>
-
-static int parse_size(const char* s) {
- int v = atoi(s);
- int len = (int)strlen(s);
- if (len > 0) {
- char suf = s[len - 1];
- if (suf == 'k' || suf == 'K') v *= 1024;
- else if (suf == 'm' || suf == 'M') v *= 1024 * 1024;
- }
- return v;
-}
-
-int main(int argc, char** argv) {
- const char* inf = NULL;
- const char* outf = NULL;
- int bs = 512;
- int count = -1;
-
- for (int i = 1; i < argc; i++) {
- if (strncmp(argv[i], "if=", 3) == 0) inf = argv[i] + 3;
- else if (strncmp(argv[i], "of=", 3) == 0) outf = argv[i] + 3;
- else if (strncmp(argv[i], "bs=", 3) == 0) bs = parse_size(argv[i] + 3);
- else if (strncmp(argv[i], "count=", 6) == 0) count = atoi(argv[i] + 6);
- }
-
- int ifd = STDIN_FILENO;
- int ofd = STDOUT_FILENO;
-
- if (inf) {
- ifd = open(inf, O_RDONLY);
- if (ifd < 0) { fprintf(stderr, "dd: cannot open '%s'\n", inf); return 1; }
- }
- if (outf) {
- ofd = open(outf, O_WRONLY | O_CREAT | O_TRUNC, 0644);
- if (ofd < 0) { fprintf(stderr, "dd: cannot open '%s'\n", outf); return 1; }
- }
-
- if (bs > 4096) bs = 4096;
- char buf[4096];
- int blocks = 0, partial = 0, total = 0;
-
- while (count < 0 || blocks + partial < count) {
- int n = read(ifd, buf, (size_t)bs);
- if (n <= 0) break;
- write(ofd, buf, (size_t)n);
- total += n;
- if (n == bs) blocks++;
- else partial++;
- }
-
- fprintf(stderr, "%d+%d records in\n%d+%d records out\n%d bytes copied\n",
- blocks, partial, blocks, partial, total);
-
- if (inf) close(ifd);
- if (outf) close(ofd);
- return 0;
-}
+++ /dev/null
-/* AdrOS df utility — display filesystem disk space usage */
-#include <stdio.h>
-
-int main(void) {
- printf("Filesystem Size Used Avail Use%% Mounted on\n");
- printf("overlayfs - - - - /\n");
- printf("devfs - - - - /dev\n");
- printf("procfs - - - - /proc\n");
- return 0;
-}
+++ /dev/null
-/* AdrOS dirname utility — strip last component from path */
-#include <stdio.h>
-#include <string.h>
-
-int main(int argc, char** argv) {
- if (argc <= 1) {
- fprintf(stderr, "usage: dirname PATH\n");
- return 1;
- }
- char* p = argv[1];
- int len = (int)strlen(p);
- /* Remove trailing slashes */
- while (len > 1 && p[len - 1] == '/') len--;
- /* Find last slash */
- while (len > 0 && p[len - 1] != '/') len--;
- /* Remove trailing slashes from result */
- while (len > 1 && p[len - 1] == '/') len--;
- if (len == 0) { printf(".\n"); return 0; }
- p[len] = '\0';
- printf("%s\n", p);
- return 0;
-}
+++ /dev/null
-/* AdrOS dmesg utility — print kernel ring buffer from /proc/dmesg */
-#include <stdio.h>
-#include <unistd.h>
-#include <fcntl.h>
-
-int main(void) {
- int fd = open("/proc/dmesg", O_RDONLY);
- if (fd < 0) {
- fprintf(stderr, "dmesg: cannot open /proc/dmesg\n");
- return 1;
- }
- char buf[512];
- int n;
- while ((n = read(fd, buf, sizeof(buf))) > 0)
- write(STDOUT_FILENO, buf, (size_t)n);
- close(fd);
- return 0;
-}
+++ /dev/null
-/* AdrOS du utility — estimate file space usage */
-#include <stdio.h>
-#include <string.h>
-#include <unistd.h>
-#include <fcntl.h>
-#include <dirent.h>
-#include <sys/stat.h>
-
-static int sflag = 0; /* -s: summary only */
-
-static long du_path(const char* path, int print) {
- struct stat st;
- if (stat(path, &st) < 0) {
- fprintf(stderr, "du: cannot access '%s'\n", path);
- return 0;
- }
-
- if (!(st.st_mode & 0040000)) {
- long blocks = (st.st_size + 511) / 512;
- if (print && !sflag) printf("%ld\t%s\n", blocks, path);
- return blocks;
- }
-
- int fd = open(path, O_RDONLY);
- if (fd < 0) return 0;
-
- long total = 0;
- char buf[2048];
- int rc;
- while ((rc = getdents(fd, buf, sizeof(buf))) > 0) {
- int off = 0;
- while (off < rc) {
- struct dirent* d = (struct dirent*)(buf + off);
- if (d->d_reclen == 0) break;
- if (strcmp(d->d_name, ".") != 0 && strcmp(d->d_name, "..") != 0) {
- char child[512];
- if (path[strlen(path)-1] == '/')
- snprintf(child, sizeof(child), "%s%s", path, d->d_name);
- else
- snprintf(child, sizeof(child), "%s/%s", path, d->d_name);
- total += du_path(child, print);
- }
- off += d->d_reclen;
- }
- }
- close(fd);
-
- if (print && !sflag) printf("%ld\t%s\n", total, path);
- return total;
-}
-
-int main(int argc, char** argv) {
- int argi = 1;
- while (argi < argc && argv[argi][0] == '-') {
- const char* f = argv[argi] + 1;
- while (*f) {
- if (*f == 's') sflag = 1;
- f++;
- }
- argi++;
- }
-
- if (argi >= argc) {
- long total = du_path(".", 1);
- if (sflag) printf("%ld\t.\n", total);
- } else {
- for (int i = argi; i < argc; i++) {
- long total = du_path(argv[i], 1);
- if (sflag) printf("%ld\t%s\n", total, argv[i]);
- }
- }
- return 0;
-}
+++ /dev/null
-/* AdrOS echo utility — POSIX-compatible */
-#include <stdio.h>
-#include <string.h>
-#include <unistd.h>
-
-int main(int argc, char** argv) {
- int nflag = 0; /* -n: no trailing newline */
- int eflag = 0; /* -e: interpret escape sequences */
- int i = 1;
-
- /* Parse flags */
- while (i < argc && argv[i][0] == '-' && argv[i][1] != '\0') {
- const char* f = argv[i] + 1;
- int valid = 1;
- int n = 0, e = 0;
- while (*f) {
- if (*f == 'n') n = 1;
- else if (*f == 'e') e = 1;
- else if (*f == 'E') { /* no escapes — default */ }
- else { valid = 0; break; }
- f++;
- }
- if (!valid) break;
- if (n) nflag = 1;
- if (e) eflag = 1;
- i++;
- }
-
- int first = 1;
- for (; i < argc; i++) {
- if (!first)
- write(STDOUT_FILENO, " ", 1);
- first = 0;
-
- const char* s = argv[i];
- if (eflag) {
- while (*s) {
- if (*s == '\\' && s[1]) {
- s++;
- char c = *s;
- switch (c) {
- case 'n': write(STDOUT_FILENO, "\n", 1); break;
- case 't': write(STDOUT_FILENO, "\t", 1); break;
- case '\\': write(STDOUT_FILENO, "\\", 1); break;
- case 'r': write(STDOUT_FILENO, "\r", 1); break;
- case 'a': write(STDOUT_FILENO, "\a", 1); break;
- default: write(STDOUT_FILENO, "\\", 1);
- write(STDOUT_FILENO, &c, 1); break;
- }
- } else {
- write(STDOUT_FILENO, s, 1);
- }
- s++;
- }
- } else {
- write(STDOUT_FILENO, s, strlen(s));
- }
- }
-
- if (!nflag)
- write(STDOUT_FILENO, "\n", 1);
-
- return 0;
-}
+++ /dev/null
-/* AdrOS env utility — print environment or run command with modified env */
-#include <stdio.h>
-#include <string.h>
-#include <unistd.h>
-
-extern char** __environ;
-
-int main(int argc, char** argv) {
- if (argc <= 1) {
- /* Print all environment variables */
- if (__environ) {
- for (int i = 0; __environ[i]; i++)
- printf("%s\n", __environ[i]);
- }
- return 0;
- }
- /* env COMMAND ARGS... — run command with current environment */
- execve(argv[1], (const char* const*)&argv[1], (const char* const*)__environ);
- fprintf(stderr, "env: %s: not found\n", argv[1]);
- return 127;
-}
+++ /dev/null
-/* Per-process errno: each fork()ed process gets its own copy in its
- address space. When true threads (clone) are added, this must become
- __thread int errno or use a TLS segment (GS/FS). */
-int errno = 0;
+++ /dev/null
-/* AdrOS find utility — search for files in directory hierarchy */
-#include <stdio.h>
-#include <string.h>
-#include <unistd.h>
-#include <fcntl.h>
-#include <dirent.h>
-
-static const char* name_pattern = NULL;
-static int type_filter = 0; /* 0=any, 'f'=file, 'd'=dir */
-
-static int match_name(const char* name) {
- if (!name_pattern) return 1;
- /* Simple wildcard: *pattern* if pattern has no special chars,
- or exact match. Support leading/trailing * only. */
- int plen = (int)strlen(name_pattern);
- if (plen == 0) return 1;
-
- const char* pat = name_pattern;
- int lead_star = (pat[0] == '*');
- int trail_star = (plen > 1 && pat[plen-1] == '*');
-
- if (lead_star && trail_star) {
- char sub[256];
- int slen = plen - 2;
- if (slen <= 0) return 1;
- memcpy(sub, pat + 1, slen);
- sub[slen] = '\0';
- return strstr(name, sub) != NULL;
- }
- if (lead_star) {
- const char* suffix = pat + 1;
- int slen = plen - 1;
- int nlen = (int)strlen(name);
- if (nlen < slen) return 0;
- return strcmp(name + nlen - slen, suffix) == 0;
- }
- if (trail_star) {
- return strncmp(name, pat, plen - 1) == 0;
- }
- return strcmp(name, pat) == 0;
-}
-
-static void find_recurse(const char* path) {
- int fd = open(path, O_RDONLY);
- if (fd < 0) return;
-
- char buf[2048];
- int rc;
- while ((rc = getdents(fd, buf, sizeof(buf))) > 0) {
- int off = 0;
- while (off < rc) {
- struct dirent* d = (struct dirent*)(buf + off);
- if (d->d_reclen == 0) break;
- if (strcmp(d->d_name, ".") != 0 && strcmp(d->d_name, "..") != 0) {
- char child[512];
- if (path[strlen(path)-1] == '/')
- snprintf(child, sizeof(child), "%s%s", path, d->d_name);
- else
- snprintf(child, sizeof(child), "%s/%s", path, d->d_name);
-
- int show = match_name(d->d_name);
- if (show && type_filter) {
- if (type_filter == 'f' && d->d_type == 4) show = 0; /* DT_DIR=4 */
- if (type_filter == 'd' && d->d_type != 4) show = 0;
- }
- if (show) printf("%s\n", child);
-
- if (d->d_type == 4) { /* DT_DIR */
- find_recurse(child);
- }
- }
- off += d->d_reclen;
- }
- }
- close(fd);
-}
-
-int main(int argc, char** argv) {
- const char* start = ".";
- int argi = 1;
-
- if (argi < argc && argv[argi][0] != '-') {
- start = argv[argi++];
- }
-
- while (argi < argc) {
- if (strcmp(argv[argi], "-name") == 0 && argi + 1 < argc) {
- name_pattern = argv[++argi];
- } else if (strcmp(argv[argi], "-type") == 0 && argi + 1 < argc) {
- type_filter = argv[++argi][0];
- }
- argi++;
- }
-
- printf("%s\n", start);
- find_recurse(start);
- return 0;
-}
+++ /dev/null
-/* AdrOS free utility — display memory usage from /proc/meminfo */
-#include <stdio.h>
-#include <unistd.h>
-#include <fcntl.h>
-
-int main(void) {
- int fd = open("/proc/meminfo", O_RDONLY);
- if (fd >= 0) {
- char buf[512];
- int n = read(fd, buf, sizeof(buf) - 1);
- if (n > 0) { buf[n] = '\0'; printf("%s", buf); }
- close(fd);
- } else {
- printf("free: /proc/meminfo not available\n");
- }
- return 0;
-}
+++ /dev/null
-#include <stdint.h>
-
-#ifdef SIGKILL
-#undef SIGKILL
-#endif
-#ifdef SIGUSR1
-#undef SIGUSR1
-#endif
-#ifdef SIGSEGV
-#undef SIGSEGV
-#endif
-#ifdef SIGTTIN
-#undef SIGTTIN
-#endif
-#ifdef SIGTTOU
-#undef SIGTTOU
-#endif
-
-#ifdef WNOHANG
-#undef WNOHANG
-#endif
-#ifdef SEEK_SET
-#undef SEEK_SET
-#endif
-#ifdef SEEK_CUR
-#undef SEEK_CUR
-#endif
-#ifdef SEEK_END
-#undef SEEK_END
-#endif
-
-#include "user_errno.h"
-
-#include "signal.h"
-
-enum {
- SYSCALL_WRITE = 1,
- SYSCALL_EXIT = 2,
- SYSCALL_GETPID = 3,
- SYSCALL_OPEN = 4,
- SYSCALL_READ = 5,
- SYSCALL_CLOSE = 6,
- SYSCALL_WAITPID = 7,
- SYSCALL_LSEEK = 9,
- SYSCALL_FSTAT = 10,
- SYSCALL_STAT = 11,
-
- SYSCALL_DUP = 12,
- SYSCALL_DUP2 = 13,
- SYSCALL_PIPE = 14,
- SYSCALL_PIPE2 = 34,
- SYSCALL_EXECVE = 15,
- SYSCALL_FORK = 16,
- SYSCALL_GETPPID = 17,
- SYSCALL_POLL = 18,
- SYSCALL_KILL = 19,
- SYSCALL_SELECT = 20,
- SYSCALL_IOCTL = 21,
- SYSCALL_SETSID = 22,
- SYSCALL_SETPGID = 23,
- SYSCALL_GETPGRP = 24,
-
- SYSCALL_SIGACTION = 25,
- SYSCALL_SIGPROCMASK = 26,
- SYSCALL_SIGRETURN = 27,
-
- SYSCALL_MKDIR = 28,
- SYSCALL_UNLINK = 29,
-
- SYSCALL_GETDENTS = 30,
-
- SYSCALL_FCNTL = 31,
-
- SYSCALL_CHDIR = 32,
- SYSCALL_GETCWD = 33,
- SYSCALL_DUP3 = 35,
-
- SYSCALL_OPENAT = 36,
- SYSCALL_FSTATAT = 37,
- SYSCALL_UNLINKAT = 38,
-
- SYSCALL_RENAME = 39,
- SYSCALL_RMDIR = 40,
-
- SYSCALL_BRK = 41,
- SYSCALL_NANOSLEEP = 42,
- SYSCALL_CLOCK_GETTIME = 43,
- SYSCALL_MMAP = 44,
- SYSCALL_MUNMAP = 45,
-
- SYSCALL_SHMGET = 46,
- SYSCALL_SHMAT = 47,
- SYSCALL_SHMDT = 48,
-
- SYSCALL_LINK = 54,
- SYSCALL_SYMLINK = 55,
- SYSCALL_READLINK = 56,
-
- SYSCALL_SIGPENDING = 71,
- SYSCALL_PREAD = 72,
- SYSCALL_PWRITE = 73,
- SYSCALL_ACCESS = 74,
- SYSCALL_TRUNCATE = 78,
- SYSCALL_FTRUNCATE = 79,
- SYSCALL_UMASK = 75,
- SYSCALL_ALARM = 83,
- SYSCALL_SETITIMER = 92,
- SYSCALL_GETITIMER = 93,
- SYSCALL_WAITID = 94,
-
- SYSCALL_EPOLL_CREATE = 112,
- SYSCALL_EPOLL_CTL = 113,
- SYSCALL_EPOLL_WAIT = 114,
-
- SYSCALL_INOTIFY_INIT = 115,
- SYSCALL_INOTIFY_ADD_WATCH = 116,
- SYSCALL_INOTIFY_RM_WATCH = 117,
-
- SYSCALL_AIO_READ = 121,
- SYSCALL_AIO_WRITE = 122,
- SYSCALL_AIO_ERROR = 123,
- SYSCALL_AIO_RETURN = 124,
-
- SYSCALL_CHMOD = 50,
- SYSCALL_CHOWN = 51,
- SYSCALL_GETUID = 52,
- SYSCALL_GETGID = 53,
- SYSCALL_CLONE = 67,
- SYSCALL_GETTID = 68,
- SYSCALL_FSYNC = 69,
- SYSCALL_READV = 81,
- SYSCALL_WRITEV = 82,
- SYSCALL_TIMES = 84,
- SYSCALL_FUTEX = 85,
- SYSCALL_FLOCK = 87,
- SYSCALL_GETEUID = 88,
- SYSCALL_GETEGID = 89,
- SYSCALL_SETEUID = 90,
- SYSCALL_SETEGID = 91,
- SYSCALL_SIGSUSPEND = 80,
- SYSCALL_SIGQUEUE = 95,
- SYSCALL_POSIX_SPAWN = 96,
- SYSCALL_SETUID = 76,
- SYSCALL_SETGID = 77,
- SYSCALL_MOUNT = 126,
- SYSCALL_GETTIMEOFDAY = 127,
- SYSCALL_MPROTECT = 128,
- SYSCALL_GETRLIMIT = 129,
- SYSCALL_SETRLIMIT = 130,
- SYSCALL_UNAME = 136,
- SYSCALL_MADVISE = 140,
-};
-
-enum {
- AT_FDCWD = -100,
-};
-
-enum {
- F_GETFD = 1,
- F_SETFD = 2,
- F_GETFL = 3,
- F_SETFL = 4,
- F_GETPIPE_SZ = 1032,
- F_SETPIPE_SZ = 1033,
- FD_CLOEXEC = 1,
-};
-
-enum {
- O_CLOEXEC = 0x80000,
-};
-
-enum {
- TCGETS = 0x5401,
- TCSETS = 0x5402,
- TIOCGPGRP = 0x540F,
- TIOCSPGRP = 0x5410,
-};
-
-enum {
- ENOTTY = 25,
-};
-
-enum {
- ICANON = 0x0002,
- ECHO = 0x0008,
-};
-
-#define USER_NCCS 11
-
-struct termios {
- uint32_t c_iflag;
- uint32_t c_oflag;
- uint32_t c_cflag;
- uint32_t c_lflag;
- uint8_t c_cc[USER_NCCS];
-};
-
-struct pollfd {
- int fd;
- int16_t events;
- int16_t revents;
-};
-
-enum {
- POLLIN = 0x0001,
- POLLOUT = 0x0004,
- EPOLLET = (1U << 31),
-};
-
-enum {
- SIGKILL = 9,
- SIGUSR1 = 10,
- SIGSEGV = 11,
- SIGTTIN = 21,
- SIGTTOU = 22,
-};
-
-enum {
- WNOHANG = 1,
-};
-
-enum {
- SEEK_SET = 0,
- SEEK_CUR = 1,
- SEEK_END = 2,
-};
-
-enum {
- O_CREAT = 0x40,
- O_TRUNC = 0x200,
- O_NONBLOCK = 0x800,
- O_APPEND = 0x400,
- O_RDWR = 0x02,
-};
-
-enum {
- PROT_READ = 0x1,
- PROT_WRITE = 0x2,
-};
-
-enum {
- MAP_PRIVATE = 0x02,
- MAP_ANONYMOUS = 0x20,
- MAP_FAILED_VAL = 0xFFFFFFFF,
-};
-
-enum {
- CLOCK_REALTIME = 0,
- CLOCK_MONOTONIC = 1,
-};
-
-struct timespec {
- uint32_t tv_sec;
- uint32_t tv_nsec;
-};
-
-enum {
- R_OK = 4,
- W_OK = 2,
- F_OK = 0,
-};
-
-enum {
- SIG_BLOCK = 0,
- SIG_UNBLOCK = 1,
- SIG_SETMASK = 2,
-};
-
-enum {
- IPC_CREAT = 01000,
- IPC_PRIVATE = 0,
-};
-
-enum {
- SIGALRM = 14,
-};
-
-enum {
- EAGAIN = 11,
- EINVAL = 22,
- EEXIST = 17,
-};
-
-enum {
- ITIMER_REAL = 0,
- ITIMER_VIRTUAL = 1,
- ITIMER_PROF = 2,
-};
-
-struct timeval {
- uint32_t tv_sec;
- uint32_t tv_usec;
-};
-
-struct rlimit {
- uint32_t rlim_cur;
- uint32_t rlim_max;
-};
-
-enum {
- RLIMIT_CPU = 0,
- RLIMIT_FSIZE = 1,
- RLIMIT_DATA = 2,
- RLIMIT_STACK = 3,
- RLIMIT_CORE = 4,
- RLIMIT_NOFILE = 5,
- RLIMIT_AS = 6,
- RLIMIT_NPROC = 7,
- RLIM_INFINITY = 0xFFFFFFFFU,
-};
-
-struct itimerval {
- struct timeval it_interval;
- struct timeval it_value;
-};
-
-enum {
- P_ALL = 0,
- P_PID = 1,
- P_PGID = 2,
- WEXITED = 4,
-};
-
-#define S_IFMT 0170000
-#define S_IFREG 0100000
-
-struct stat {
- uint32_t st_ino;
- uint32_t st_mode;
- uint32_t st_nlink;
- uint32_t st_uid;
- uint32_t st_gid;
- uint32_t st_size;
-};
-
-static int sys_write(int fd, const void* buf, uint32_t len) {
- int ret;
- __asm__ volatile(
- "int $0x80"
- : "=a"(ret)
- : "a"(SYSCALL_WRITE), "b"(fd), "c"(buf), "d"(len)
- : "memory"
- );
- return __syscall_fix(ret);
-}
-
-static int sys_openat(int dirfd, const char* path, uint32_t flags, uint32_t mode) {
- int ret;
- __asm__ volatile(
- "int $0x80"
- : "=a"(ret)
- : "a"(SYSCALL_OPENAT), "b"(dirfd), "c"(path), "d"(flags), "S"(mode)
- : "memory"
- );
- return __syscall_fix(ret);
-}
-
-static int sys_fstatat(int dirfd, const char* path, struct stat* st, uint32_t flags) {
- int ret;
- __asm__ volatile(
- "int $0x80"
- : "=a"(ret)
- : "a"(SYSCALL_FSTATAT), "b"(dirfd), "c"(path), "d"(st), "S"(flags)
- : "memory"
- );
- return __syscall_fix(ret);
-}
-
-static int sys_unlinkat(int dirfd, const char* path, uint32_t flags) {
- int ret;
- __asm__ volatile(
- "int $0x80"
- : "=a"(ret)
- : "a"(SYSCALL_UNLINKAT), "b"(dirfd), "c"(path), "d"(flags)
- : "memory"
- );
- return __syscall_fix(ret);
-}
-
-static int sys_rename(const char* oldpath, const char* newpath) {
- int ret;
- __asm__ volatile(
- "int $0x80"
- : "=a"(ret)
- : "a"(SYSCALL_RENAME), "b"(oldpath), "c"(newpath)
- : "memory"
- );
- return __syscall_fix(ret);
-}
-
-static int sys_rmdir(const char* path) {
- int ret;
- __asm__ volatile(
- "int $0x80"
- : "=a"(ret)
- : "a"(SYSCALL_RMDIR), "b"(path)
- : "memory"
- );
- return __syscall_fix(ret);
-}
-
-static int sys_pipe2(int fds[2], uint32_t flags) {
- int ret;
- __asm__ volatile(
- "int $0x80"
- : "=a"(ret)
- : "a"(SYSCALL_PIPE2), "b"(fds), "c"(flags)
- : "memory"
- );
- return __syscall_fix(ret);
-}
-
-static int sys_chdir(const char* path) {
- int ret;
- __asm__ volatile(
- "int $0x80"
- : "=a"(ret)
- : "a"(SYSCALL_CHDIR), "b"(path)
- : "memory"
- );
- return __syscall_fix(ret);
-}
-
-static int sys_getcwd(char* buf, uint32_t size) {
- int ret;
- __asm__ volatile(
- "int $0x80"
- : "=a"(ret)
- : "a"(SYSCALL_GETCWD), "b"(buf), "c"(size)
- : "memory"
- );
- return __syscall_fix(ret);
-}
-
-static int sys_fcntl(int fd, int cmd, uint32_t arg) {
- int ret;
- __asm__ volatile(
- "int $0x80"
- : "=a"(ret)
- : "a"(SYSCALL_FCNTL), "b"(fd), "c"(cmd), "d"(arg)
- : "memory"
- );
- return __syscall_fix(ret);
-}
-
-static int sys_ioctl(int fd, uint32_t cmd, void* arg);
-
-static int isatty_fd(int fd) {
- struct termios t;
- if (sys_ioctl(fd, TCGETS, &t) < 0) {
- if (errno == ENOTTY) return 0;
- return -1;
- }
- return 1;
-}
-
-static int sys_getdents(int fd, void* buf, uint32_t len) {
- int ret;
- __asm__ volatile(
- "int $0x80"
- : "=a"(ret)
- : "a"(SYSCALL_GETDENTS), "b"(fd), "c"(buf), "d"(len)
- : "memory"
- );
- return __syscall_fix(ret);
-}
-
-static void write_int_dec(int v) {
- char buf[16];
- int i = 0;
- if (v == 0) {
- buf[i++] = '0';
- } else {
- int neg = 0;
- if (v < 0) {
- neg = 1;
- v = -v;
- }
- while (v > 0 && i < (int)sizeof(buf)) {
- buf[i++] = (char)('0' + (v % 10));
- v /= 10;
- }
- if (neg && i < (int)sizeof(buf)) {
- buf[i++] = '-';
- }
- for (int j = 0; j < i / 2; j++) {
- char t = buf[j];
- buf[j] = buf[i - 1 - j];
- buf[i - 1 - j] = t;
- }
- }
- (void)sys_write(1, buf, (uint32_t)i);
-}
-
-static void write_hex8(uint8_t v) {
- static const char hex[] = "0123456789ABCDEF";
- char b[2];
- b[0] = hex[(v >> 4) & 0xF];
- b[1] = hex[v & 0xF];
- (void)sys_write(1, b, 2);
-}
-
-static void write_hex32(uint32_t v) {
- static const char hex[] = "0123456789ABCDEF";
- char b[8];
- for (int i = 0; i < 8; i++) {
- uint32_t shift = (uint32_t)(28 - 4 * i);
- b[i] = hex[(v >> shift) & 0xFU];
- }
- (void)sys_write(1, b, 8);
-}
-
-static int memeq(const void* a, const void* b, uint32_t n) {
- const uint8_t* x = (const uint8_t*)a;
- const uint8_t* y = (const uint8_t*)b;
- for (uint32_t i = 0; i < n; i++) {
- if (x[i] != y[i]) return 0;
- }
- return 1;
-}
-
-static int streq(const char* a, const char* b) {
- if (!a || !b) return 0;
- uint32_t i = 0;
- while (a[i] != 0 && b[i] != 0) {
- if (a[i] != b[i]) return 0;
- i++;
- }
- return a[i] == b[i];
-}
-
-static int sys_sigaction2(int sig, const struct sigaction* act, struct sigaction* oldact) {
- int ret;
- __asm__ volatile(
- "int $0x80"
- : "=a"(ret)
- : "a"(SYSCALL_SIGACTION), "b"(sig), "c"(act), "d"(oldact)
- : "memory"
- );
- return __syscall_fix(ret);
-}
-
-static int sys_sigaction(int sig, void (*handler)(int), uintptr_t* old_out) {
- struct sigaction act;
- act.sa_handler = (uintptr_t)handler;
- act.sa_sigaction = 0;
- act.sa_mask = 0;
- act.sa_flags = 0;
-
- struct sigaction oldact;
- struct sigaction* oldp = old_out ? &oldact : 0;
-
- int r = sys_sigaction2(sig, &act, oldp);
- if (r < 0) return r;
- if (old_out) {
- *old_out = oldact.sa_handler;
- }
- return 0;
-}
-
-static int sys_select(uint32_t nfds, uint64_t* readfds, uint64_t* writefds, uint64_t* exceptfds, int32_t timeout) {
- int ret;
- __asm__ volatile(
- "int $0x80"
- : "=a"(ret)
- : "a"(SYSCALL_SELECT), "b"(nfds), "c"(readfds), "d"(writefds), "S"(exceptfds), "D"(timeout)
- : "memory"
- );
- return __syscall_fix(ret);
-}
-
-static int sys_ioctl(int fd, uint32_t cmd, void* arg) {
- int ret;
- __asm__ volatile(
- "int $0x80"
- : "=a"(ret)
- : "a"(SYSCALL_IOCTL), "b"(fd), "c"(cmd), "d"(arg)
- : "memory"
- );
- return __syscall_fix(ret);
-}
-
-static int sys_kill(int pid, int sig) {
- int ret;
- __asm__ volatile(
- "int $0x80"
- : "=a"(ret)
- : "a"(SYSCALL_KILL), "b"(pid), "c"(sig)
- : "memory"
- );
- return __syscall_fix(ret);
-}
-
-static int sys_poll(struct pollfd* fds, uint32_t nfds, int32_t timeout) {
- int ret;
- __asm__ volatile(
- "int $0x80"
- : "=a"(ret)
- : "a"(SYSCALL_POLL), "b"(fds), "c"(nfds), "d"(timeout)
- : "memory"
- );
- return __syscall_fix(ret);
-}
-
-static int sys_setsid(void) {
- int ret;
- __asm__ volatile(
- "int $0x80"
- : "=a"(ret)
- : "a"(SYSCALL_SETSID)
- : "memory"
- );
- return __syscall_fix(ret);
-}
-
-static int sys_setpgid(int pid, int pgid) {
- int ret;
- __asm__ volatile(
- "int $0x80"
- : "=a"(ret)
- : "a"(SYSCALL_SETPGID), "b"(pid), "c"(pgid)
- : "memory"
- );
- return __syscall_fix(ret);
-}
-
-static int sys_getpgrp(void) {
- int ret;
- __asm__ volatile(
- "int $0x80"
- : "=a"(ret)
- : "a"(SYSCALL_GETPGRP)
- : "memory"
- );
- return __syscall_fix(ret);
-}
-
-static int sys_getpid(void) {
- int ret;
- __asm__ volatile(
- "int $0x80"
- : "=a"(ret)
- : "a"(SYSCALL_GETPID)
- : "memory"
- );
- return __syscall_fix(ret);
-}
-
-static int sys_getppid(void) {
- int ret;
- __asm__ volatile(
- "int $0x80"
- : "=a"(ret)
- : "a"(SYSCALL_GETPPID)
- : "memory"
- );
- return __syscall_fix(ret);
-}
-
-static int sys_fork(void) {
- int ret;
- __asm__ volatile(
- "int $0x80"
- : "=a"(ret)
- : "a"(SYSCALL_FORK)
- : "memory"
- );
- return __syscall_fix(ret);
-}
-
-static int sys_execve(const char* path, const char* const* argv, const char* const* envp) {
- int ret;
- __asm__ volatile(
- "int $0x80"
- : "=a"(ret)
- : "a"(SYSCALL_EXECVE), "b"(path), "c"(argv), "d"(envp)
- : "memory"
- );
- return __syscall_fix(ret);
-}
-
-static int sys_pipe(int fds[2]) {
- int ret;
- __asm__ volatile(
- "int $0x80"
- : "=a"(ret)
- : "a"(SYSCALL_PIPE), "b"(fds)
- : "memory"
- );
- return __syscall_fix(ret);
-}
-
-static int sys_dup(int oldfd) {
- int ret;
- __asm__ volatile(
- "int $0x80"
- : "=a"(ret)
- : "a"(SYSCALL_DUP), "b"(oldfd)
- : "memory"
- );
- return __syscall_fix(ret);
-}
-
-static int sys_dup2(int oldfd, int newfd) {
- int ret;
- __asm__ volatile(
- "int $0x80"
- : "=a"(ret)
- : "a"(SYSCALL_DUP2), "b"(oldfd), "c"(newfd)
- : "memory"
- );
- return __syscall_fix(ret);
-}
-
-static int sys_dup3(int oldfd, int newfd, uint32_t flags) {
- int ret;
- __asm__ volatile(
- "int $0x80"
- : "=a"(ret)
- : "a"(SYSCALL_DUP3), "b"(oldfd), "c"(newfd), "d"(flags)
- : "memory"
- );
- return __syscall_fix(ret);
-}
-
-static int sys_waitpid(int pid, int* status, uint32_t options) {
- int ret;
- __asm__ volatile(
- "int $0x80"
- : "=a"(ret)
- : "a"(SYSCALL_WAITPID), "b"(pid), "c"(status), "d"(options)
- : "memory"
- );
- return __syscall_fix(ret);
-}
-
-static int sys_open(const char* path, uint32_t flags) {
- int ret;
- __asm__ volatile(
- "int $0x80"
- : "=a"(ret)
- : "a"(SYSCALL_OPEN), "b"(path), "c"(flags)
- : "memory"
- );
- return __syscall_fix(ret);
-}
-
-static int sys_mkdir(const char* path) {
- int ret;
- __asm__ volatile(
- "int $0x80"
- : "=a"(ret)
- : "a"(SYSCALL_MKDIR), "b"(path)
- : "memory"
- );
- return __syscall_fix(ret);
-}
-
-static int sys_unlink(const char* path) {
- int ret;
- __asm__ volatile(
- "int $0x80"
- : "=a"(ret)
- : "a"(SYSCALL_UNLINK), "b"(path)
- : "memory"
- );
- return __syscall_fix(ret);
-}
-
-static int sys_read(int fd, void* buf, uint32_t len) {
- int ret;
- __asm__ volatile(
- "int $0x80"
- : "=a"(ret)
- : "a"(SYSCALL_READ), "b"(fd), "c"(buf), "d"(len)
- : "memory"
- );
- return __syscall_fix(ret);
-}
-
-static int sys_close(int fd) {
- int ret;
- __asm__ volatile(
- "int $0x80"
- : "=a"(ret)
- : "a"(SYSCALL_CLOSE), "b"(fd)
- : "memory"
- );
- return __syscall_fix(ret);
-}
-
-static int sys_lseek(int fd, int32_t offset, int whence) {
- int ret;
- __asm__ volatile(
- "int $0x80"
- : "=a"(ret)
- : "a"(SYSCALL_LSEEK), "b"(fd), "c"(offset), "d"(whence)
- : "memory"
- );
- return __syscall_fix(ret);
-}
-
-static int sys_fstat(int fd, struct stat* st) {
- int ret;
- __asm__ volatile(
- "int $0x80"
- : "=a"(ret)
- : "a"(SYSCALL_FSTAT), "b"(fd), "c"(st)
- : "memory"
- );
- return __syscall_fix(ret);
-}
-
-static int sys_stat(const char* path, struct stat* st) {
- int ret;
- __asm__ volatile(
- "int $0x80"
- : "=a"(ret)
- : "a"(SYSCALL_STAT), "b"(path), "c"(st)
- : "memory"
- );
- return __syscall_fix(ret);
-}
-
-static uintptr_t sys_brk(uintptr_t addr) {
- uintptr_t ret;
- __asm__ volatile(
- "int $0x80"
- : "=a"(ret)
- : "a"(SYSCALL_BRK), "b"(addr)
- : "memory"
- );
- return ret;
-}
-
-static uintptr_t sys_mmap(uintptr_t addr, uint32_t len, uint32_t prot, uint32_t flags, int fd) {
- uintptr_t ret;
- __asm__ volatile(
- "int $0x80"
- : "=a"(ret)
- : "a"(SYSCALL_MMAP), "b"(addr), "c"(len), "d"(prot), "S"(flags), "D"(fd)
- : "memory"
- );
- return ret;
-}
-
-static int sys_munmap(uintptr_t addr, uint32_t len) {
- int ret;
- __asm__ volatile(
- "int $0x80"
- : "=a"(ret)
- : "a"(SYSCALL_MUNMAP), "b"(addr), "c"(len)
- : "memory"
- );
- return __syscall_fix(ret);
-}
-
-static int sys_clock_gettime(uint32_t clk_id, struct timespec* tp) {
- int ret;
- __asm__ volatile(
- "int $0x80"
- : "=a"(ret)
- : "a"(SYSCALL_CLOCK_GETTIME), "b"(clk_id), "c"(tp)
- : "memory"
- );
- return __syscall_fix(ret);
-}
-
-static int sys_pread(int fd, void* buf, uint32_t count, uint32_t offset) {
- int ret;
- __asm__ volatile(
- "int $0x80"
- : "=a"(ret)
- : "a"(SYSCALL_PREAD), "b"(fd), "c"(buf), "d"(count), "S"(offset)
- : "memory"
- );
- return __syscall_fix(ret);
-}
-
-static int sys_pwrite(int fd, const void* buf, uint32_t count, uint32_t offset) {
- int ret;
- __asm__ volatile(
- "int $0x80"
- : "=a"(ret)
- : "a"(SYSCALL_PWRITE), "b"(fd), "c"(buf), "d"(count), "S"(offset)
- : "memory"
- );
- return __syscall_fix(ret);
-}
-
-static int sys_ftruncate(int fd, uint32_t length) {
- int ret;
- __asm__ volatile(
- "int $0x80"
- : "=a"(ret)
- : "a"(SYSCALL_FTRUNCATE), "b"(fd), "c"(length)
- : "memory"
- );
- return __syscall_fix(ret);
-}
-
-static int sys_symlink(const char* target, const char* linkpath) {
- int ret;
- __asm__ volatile(
- "int $0x80"
- : "=a"(ret)
- : "a"(SYSCALL_SYMLINK), "b"(target), "c"(linkpath)
- : "memory"
- );
- return __syscall_fix(ret);
-}
-
-static int sys_readlink(const char* path, char* buf, uint32_t bufsiz) {
- int ret;
- __asm__ volatile(
- "int $0x80"
- : "=a"(ret)
- : "a"(SYSCALL_READLINK), "b"(path), "c"(buf), "d"(bufsiz)
- : "memory"
- );
- return __syscall_fix(ret);
-}
-
-static int sys_access(const char* path, uint32_t mode) {
- int ret;
- __asm__ volatile(
- "int $0x80"
- : "=a"(ret)
- : "a"(SYSCALL_ACCESS), "b"(path), "c"(mode)
- : "memory"
- );
- return __syscall_fix(ret);
-}
-
-static int sys_sigprocmask(int how, uint32_t mask, uint32_t* oldset) {
- int ret;
- __asm__ volatile(
- "int $0x80"
- : "=a"(ret)
- : "a"(SYSCALL_SIGPROCMASK), "b"(how), "c"(mask), "d"(oldset)
- : "memory"
- );
- return __syscall_fix(ret);
-}
-
-static int sys_sigpending(uint32_t* set) {
- int ret;
- __asm__ volatile(
- "int $0x80"
- : "=a"(ret)
- : "a"(SYSCALL_SIGPENDING), "b"(set)
- : "memory"
- );
- return __syscall_fix(ret);
-}
-
-static uint32_t sys_alarm(uint32_t seconds) {
- uint32_t ret;
- __asm__ volatile(
- "int $0x80"
- : "=a"(ret)
- : "a"(SYSCALL_ALARM), "b"(seconds)
- : "memory"
- );
- return ret;
-}
-
-static int sys_shmget(uint32_t key, uint32_t size, uint32_t flags) {
- int ret;
- __asm__ volatile(
- "int $0x80"
- : "=a"(ret)
- : "a"(SYSCALL_SHMGET), "b"(key), "c"(size), "d"(flags)
- : "memory"
- );
- return __syscall_fix(ret);
-}
-
-static uintptr_t sys_shmat(int shmid, uintptr_t addr, uint32_t flags) {
- uintptr_t ret;
- __asm__ volatile(
- "int $0x80"
- : "=a"(ret)
- : "a"(SYSCALL_SHMAT), "b"(shmid), "c"(addr), "d"(flags)
- : "memory"
- );
- return ret;
-}
-
-static int sys_shmdt(uintptr_t addr) {
- int ret;
- __asm__ volatile(
- "int $0x80"
- : "=a"(ret)
- : "a"(SYSCALL_SHMDT), "b"(addr)
- : "memory"
- );
- return __syscall_fix(ret);
-}
-
-static int sys_link(const char* oldpath, const char* newpath) {
- int ret;
- __asm__ volatile(
- "int $0x80"
- : "=a"(ret)
- : "a"(SYSCALL_LINK), "b"(oldpath), "c"(newpath)
- : "memory"
- );
- return __syscall_fix(ret);
-}
-
-static int sys_umask(uint32_t mask) {
- int ret;
- __asm__ volatile(
- "int $0x80"
- : "=a"(ret)
- : "a"(SYSCALL_UMASK), "b"(mask)
- : "memory"
- );
- return ret;
-}
-
-static int sys_setitimer(int which, const struct itimerval* newval, struct itimerval* oldval) {
- int ret;
- __asm__ volatile(
- "int $0x80"
- : "=a"(ret)
- : "a"(SYSCALL_SETITIMER), "b"(which), "c"(newval), "d"(oldval)
- : "memory"
- );
- return __syscall_fix(ret);
-}
-
-static int sys_getitimer(int which, struct itimerval* cur) {
- int ret;
- __asm__ volatile(
- "int $0x80"
- : "=a"(ret)
- : "a"(SYSCALL_GETITIMER), "b"(which), "c"(cur)
- : "memory"
- );
- return __syscall_fix(ret);
-}
-
-static int sys_waitid(uint32_t idtype, uint32_t id, void* infop, uint32_t options) {
- int ret;
- __asm__ volatile(
- "int $0x80"
- : "=a"(ret)
- : "a"(SYSCALL_WAITID), "b"(idtype), "c"(id), "d"(infop), "S"(options)
- : "memory"
- );
- return __syscall_fix(ret);
-}
-
-static int sys_epoll_create(int size) {
- int ret;
- __asm__ volatile(
- "int $0x80"
- : "=a"(ret)
- : "a"(SYSCALL_EPOLL_CREATE), "b"(size)
- : "memory"
- );
- return __syscall_fix(ret);
-}
-
-static int sys_epoll_ctl(int epfd, int op, int fd, void* event) {
- int ret;
- __asm__ volatile(
- "int $0x80"
- : "=a"(ret)
- : "a"(SYSCALL_EPOLL_CTL), "b"(epfd), "c"(op), "d"(fd), "S"(event)
- : "memory"
- );
- return __syscall_fix(ret);
-}
-
-static int sys_epoll_wait(int epfd, void* events, int maxevents, int timeout) {
- int ret;
- __asm__ volatile(
- "int $0x80"
- : "=a"(ret)
- : "a"(SYSCALL_EPOLL_WAIT), "b"(epfd), "c"(events), "d"(maxevents), "S"(timeout)
- : "memory"
- );
- return __syscall_fix(ret);
-}
-
-static int sys_inotify_init(void) {
- int ret;
- __asm__ volatile(
- "int $0x80"
- : "=a"(ret)
- : "a"(SYSCALL_INOTIFY_INIT)
- : "memory"
- );
- return __syscall_fix(ret);
-}
-
-static int sys_inotify_add_watch(int fd, const char* path, uint32_t mask) {
- int ret;
- __asm__ volatile(
- "int $0x80"
- : "=a"(ret)
- : "a"(SYSCALL_INOTIFY_ADD_WATCH), "b"(fd), "c"(path), "d"(mask)
- : "memory"
- );
- return __syscall_fix(ret);
-}
-
-static int sys_inotify_rm_watch(int fd, int wd) {
- int ret;
- __asm__ volatile(
- "int $0x80"
- : "=a"(ret)
- : "a"(SYSCALL_INOTIFY_RM_WATCH), "b"(fd), "c"(wd)
- : "memory"
- );
- return __syscall_fix(ret);
-}
-
-struct aiocb {
- int aio_fildes;
- void* aio_buf;
- uint32_t aio_nbytes;
- uint32_t aio_offset;
- int32_t aio_error;
- int32_t aio_return;
-};
-
-static int sys_aio_read(struct aiocb* cb) {
- int ret;
- __asm__ volatile(
- "int $0x80"
- : "=a"(ret)
- : "a"(SYSCALL_AIO_READ), "b"(cb)
- : "memory"
- );
- return __syscall_fix(ret);
-}
-
-static int sys_aio_write(struct aiocb* cb) {
- int ret;
- __asm__ volatile(
- "int $0x80"
- : "=a"(ret)
- : "a"(SYSCALL_AIO_WRITE), "b"(cb)
- : "memory"
- );
- return __syscall_fix(ret);
-}
-
-static int sys_aio_error(struct aiocb* cb) {
- int ret;
- __asm__ volatile(
- "int $0x80"
- : "=a"(ret)
- : "a"(SYSCALL_AIO_ERROR), "b"(cb)
- : "memory"
- );
- return ret;
-}
-
-static int sys_aio_return(struct aiocb* cb) {
- int ret;
- __asm__ volatile(
- "int $0x80"
- : "=a"(ret)
- : "a"(SYSCALL_AIO_RETURN), "b"(cb)
- : "memory"
- );
- return ret;
-}
-
-static int sys_nanosleep(const struct timespec* req, struct timespec* rem) {
- int ret;
- __asm__ volatile(
- "int $0x80"
- : "=a"(ret)
- : "a"(SYSCALL_NANOSLEEP), "b"(req), "c"(rem)
- : "memory"
- );
- return __syscall_fix(ret);
-}
-
-static uint32_t sys_getuid(void) {
- uint32_t ret;
- __asm__ volatile("int $0x80" : "=a"(ret) : "a"(SYSCALL_GETUID) : "memory");
- return ret;
-}
-
-static uint32_t sys_getgid(void) {
- uint32_t ret;
- __asm__ volatile("int $0x80" : "=a"(ret) : "a"(SYSCALL_GETGID) : "memory");
- return ret;
-}
-
-static uint32_t sys_geteuid(void) {
- uint32_t ret;
- __asm__ volatile("int $0x80" : "=a"(ret) : "a"(SYSCALL_GETEUID) : "memory");
- return ret;
-}
-
-static uint32_t sys_getegid(void) {
- uint32_t ret;
- __asm__ volatile("int $0x80" : "=a"(ret) : "a"(SYSCALL_GETEGID) : "memory");
- return ret;
-}
-
-static int sys_gettid(void) {
- int ret;
- __asm__ volatile("int $0x80" : "=a"(ret) : "a"(SYSCALL_GETTID) : "memory");
- return ret;
-}
-
-static int sys_fsync(int fd) {
- int ret;
- __asm__ volatile(
- "int $0x80"
- : "=a"(ret)
- : "a"(SYSCALL_FSYNC), "b"(fd)
- : "memory"
- );
- return __syscall_fix(ret);
-}
-
-static int sys_truncate(const char* path, uint32_t length) {
- int ret;
- __asm__ volatile(
- "int $0x80"
- : "=a"(ret)
- : "a"(SYSCALL_TRUNCATE), "b"(path), "c"(length)
- : "memory"
- );
- return __syscall_fix(ret);
-}
-
-static int sys_chmod(const char* path, uint32_t mode) {
- int ret;
- __asm__ volatile(
- "int $0x80"
- : "=a"(ret)
- : "a"(SYSCALL_CHMOD), "b"(path), "c"(mode)
- : "memory"
- );
- return __syscall_fix(ret);
-}
-
-static int sys_flock(int fd, int operation) {
- int ret;
- __asm__ volatile(
- "int $0x80"
- : "=a"(ret)
- : "a"(SYSCALL_FLOCK), "b"(fd), "c"(operation)
- : "memory"
- );
- return __syscall_fix(ret);
-}
-
-struct iovec {
- void* iov_base;
- uint32_t iov_len;
-};
-
-static int sys_writev(int fd, const struct iovec* iov, int iovcnt) {
- int ret;
- __asm__ volatile(
- "int $0x80"
- : "=a"(ret)
- : "a"(SYSCALL_WRITEV), "b"(fd), "c"(iov), "d"(iovcnt)
- : "memory"
- );
- return __syscall_fix(ret);
-}
-
-static int sys_readv(int fd, const struct iovec* iov, int iovcnt) {
- int ret;
- __asm__ volatile(
- "int $0x80"
- : "=a"(ret)
- : "a"(SYSCALL_READV), "b"(fd), "c"(iov), "d"(iovcnt)
- : "memory"
- );
- return __syscall_fix(ret);
-}
-
-static uint32_t sys_times(void* buf) {
- uint32_t ret;
- __asm__ volatile(
- "int $0x80"
- : "=a"(ret)
- : "a"(SYSCALL_TIMES), "b"(buf)
- : "memory"
- );
- return ret;
-}
-
-static int sys_posix_spawn(uint32_t* pid_out, const char* path,
- const char* const* argv, const char* const* envp) {
- int ret;
- __asm__ volatile(
- "int $0x80"
- : "=a"(ret)
- : "a"(SYSCALL_POSIX_SPAWN), "b"(pid_out), "c"(path), "d"(argv), "S"(envp)
- : "memory"
- );
- return __syscall_fix(ret);
-}
-
-static int sys_setuid(uint32_t uid) {
- int ret;
- __asm__ volatile("int $0x80" : "=a"(ret) : "a"(SYSCALL_SETUID), "b"(uid) : "memory");
- return __syscall_fix(ret);
-}
-
-static int sys_setgid(uint32_t gid) {
- int ret;
- __asm__ volatile("int $0x80" : "=a"(ret) : "a"(SYSCALL_SETGID), "b"(gid) : "memory");
- return __syscall_fix(ret);
-}
-
-static int sys_seteuid(uint32_t euid) {
- int ret;
- __asm__ volatile("int $0x80" : "=a"(ret) : "a"(SYSCALL_SETEUID), "b"(euid) : "memory");
- return __syscall_fix(ret);
-}
-
-static int sys_setegid(uint32_t egid) {
- int ret;
- __asm__ volatile("int $0x80" : "=a"(ret) : "a"(SYSCALL_SETEGID), "b"(egid) : "memory");
- return __syscall_fix(ret);
-}
-
-static int sys_sigsuspend(const uint32_t* mask) {
- int ret;
- __asm__ volatile("int $0x80" : "=a"(ret) : "a"(SYSCALL_SIGSUSPEND), "b"(mask) : "memory");
- return __syscall_fix(ret);
-}
-
-static int sys_gettimeofday(struct timeval* tv) {
- int ret;
- __asm__ volatile("int $0x80" : "=a"(ret) : "a"(SYSCALL_GETTIMEOFDAY), "b"(tv), "c"(0) : "memory");
- return __syscall_fix(ret);
-}
-
-static int sys_mprotect(uintptr_t addr, uint32_t len, uint32_t prot) {
- int ret;
- __asm__ volatile("int $0x80" : "=a"(ret) : "a"(SYSCALL_MPROTECT), "b"(addr), "c"(len), "d"(prot) : "memory");
- return __syscall_fix(ret);
-}
-
-static int sys_madvise(uintptr_t addr, uint32_t len, uint32_t advice) {
- int ret;
- __asm__ volatile("int $0x80" : "=a"(ret) : "a"(SYSCALL_MADVISE), "b"(addr), "c"(len), "d"(advice) : "memory");
- return __syscall_fix(ret);
-}
-
-static int sys_getrlimit(int resource, struct rlimit* rlim) {
- int ret;
- __asm__ volatile("int $0x80" : "=a"(ret) : "a"(SYSCALL_GETRLIMIT), "b"(resource), "c"(rlim) : "memory");
- return __syscall_fix(ret);
-}
-
-struct utsname {
- char sysname[65];
- char nodename[65];
- char release[65];
- char version[65];
- char machine[65];
-};
-
-static int sys_uname(struct utsname* buf) {
- int ret;
- __asm__ volatile("int $0x80" : "=a"(ret) : "a"(SYSCALL_UNAME), "b"(buf) : "memory");
- return __syscall_fix(ret);
-}
-
-static int sys_setrlimit(int resource, const struct rlimit* rlim) {
- int ret;
- __asm__ volatile("int $0x80" : "=a"(ret) : "a"(SYSCALL_SETRLIMIT), "b"(resource), "c"(rlim) : "memory");
- return __syscall_fix(ret);
-}
-
-__attribute__((noreturn)) static void sys_exit(int code) {
- __asm__ volatile(
- "int $0x80\n"
- "1: jmp 1b\n"
- :
- : "a"(SYSCALL_EXIT), "b"(code)
- : "memory"
- );
- for (;;) {
- __asm__ volatile("hlt");
- }
-}
-
-static volatile int got_usr1 = 0;
-static volatile int got_usr1_ret = 0;
-static volatile int got_ttin = 0;
-static volatile int got_ttou = 0;
-static volatile int got_alrm = 0;
-
-static void usr1_handler(int sig) {
- (void)sig;
- got_usr1 = 1;
- sys_write(1, "[init] SIGUSR1 handler OK\n",
- (uint32_t)(sizeof("[init] SIGUSR1 handler OK\n") - 1));
-}
-
-static void usr1_ret_handler(int sig) {
- (void)sig;
- got_usr1_ret = 1;
-}
-
-static void alrm_handler(int sig) {
- (void)sig;
- got_alrm = 1;
-}
-
-static void ttin_handler(int sig) {
- (void)sig;
- got_ttin = 1;
-}
-
-static void ttou_handler(int sig) {
- (void)sig;
- got_ttou = 1;
-}
-
-static void sigsegv_exit_handler(int sig) {
- (void)sig;
- static const char msg[] = "[init] SIGSEGV handler invoked\n";
- (void)sys_write(1, msg, (uint32_t)(sizeof(msg) - 1));
- sys_exit(0);
-}
-
-static void sigsegv_info_handler(int sig, siginfo_t* info, void* uctx) {
- (void)uctx;
- static const char msg[] = "[init] SIGSEGV siginfo handler invoked\n";
- (void)sys_write(1, msg, (uint32_t)(sizeof(msg) - 1));
- const uintptr_t expected = 0x12345000U;
- if (sig == SIGSEGV && info && (uintptr_t)info->si_addr == expected) {
- sys_exit(0);
- }
- sys_exit(1);
-}
-
-void _start(void) {
- __asm__ volatile(
- "mov $0x23, %ax\n"
- "mov %ax, %ds\n"
- "mov %ax, %es\n"
- "mov %ax, %fs\n"
- "mov %ax, %gs\n"
- );
-
- static const char msg[] = "[init] hello from init.elf\n";
- (void)sys_write(1, msg, (uint32_t)(sizeof(msg) - 1));
-
- static const char path[] = "/sbin/fulltest";
-
- int fd = sys_open(path, 0);
- if (fd < 0) {
- sys_write(1, "[init] open failed fd=", (uint32_t)(sizeof("[init] open failed fd=") - 1));
- write_int_dec(fd);
- sys_write(1, "\n", 1);
- sys_exit(1);
- }
-
- uint8_t hdr[4];
- int rd = sys_read(fd, hdr, 4);
- (void)sys_close(fd);
- if (rd == 4 && hdr[0] == 0x7F && hdr[1] == 'E' && hdr[2] == 'L' && hdr[3] == 'F') {
- sys_write(1, "[init] open/read/close OK (ELF magic)\n",
- (uint32_t)(sizeof("[init] open/read/close OK (ELF magic)\n") - 1));
- } else {
- sys_write(1, "[init] read failed or bad header rd=", (uint32_t)(sizeof("[init] read failed or bad header rd=") - 1));
- write_int_dec(rd);
- sys_write(1, " hdr=", (uint32_t)(sizeof(" hdr=") - 1));
- for (int i = 0; i < 4; i++) {
- write_hex8(hdr[i]);
- }
- sys_write(1, "\n", 1);
- sys_exit(1);
- }
-
- fd = sys_open("/sbin/fulltest", 0);
- if (fd < 0) {
- sys_write(1, "[init] overlay open failed\n",
- (uint32_t)(sizeof("[init] overlay open failed\n") - 1));
- sys_exit(1);
- }
-
- uint8_t orig0 = 0;
- if (sys_lseek(fd, 0, SEEK_SET) < 0 || sys_read(fd, &orig0, 1) != 1) {
- sys_write(1, "[init] overlay read failed\n",
- (uint32_t)(sizeof("[init] overlay read failed\n") - 1));
- sys_exit(1);
- }
-
- uint8_t x = (uint8_t)(orig0 ^ 0xFF);
- if (sys_lseek(fd, 0, SEEK_SET) < 0 || sys_write(fd, &x, 1) != 1) {
- sys_write(1, "[init] overlay write failed\n",
- (uint32_t)(sizeof("[init] overlay write failed\n") - 1));
- sys_exit(1);
- }
-
- if (sys_close(fd) < 0) {
- sys_write(1, "[init] overlay close failed\n",
- (uint32_t)(sizeof("[init] overlay close failed\n") - 1));
- sys_exit(1);
- }
-
- fd = sys_open("/sbin/fulltest", 0);
- if (fd < 0) {
- sys_write(1, "[init] overlay open2 failed\n",
- (uint32_t)(sizeof("[init] overlay open2 failed\n") - 1));
- sys_exit(1);
- }
-
- uint8_t chk = 0;
- if (sys_lseek(fd, 0, SEEK_SET) < 0 || sys_read(fd, &chk, 1) != 1 || chk != x) {
- sys_write(1, "[init] overlay verify failed\n",
- (uint32_t)(sizeof("[init] overlay verify failed\n") - 1));
- sys_exit(1);
- }
-
- if (sys_lseek(fd, 0, SEEK_SET) < 0 || sys_write(fd, &orig0, 1) != 1) {
- sys_write(1, "[init] overlay restore failed\n",
- (uint32_t)(sizeof("[init] overlay restore failed\n") - 1));
- sys_exit(1);
- }
-
- if (sys_close(fd) < 0) {
- sys_write(1, "[init] overlay close2 failed\n",
- (uint32_t)(sizeof("[init] overlay close2 failed\n") - 1));
- sys_exit(1);
- }
-
- sys_write(1, "[init] overlay copy-up OK\n",
- (uint32_t)(sizeof("[init] overlay copy-up OK\n") - 1));
-
- fd = sys_open("/sbin/fulltest", 0);
- if (fd < 0) {
- sys_write(1, "[init] open2 failed\n", (uint32_t)(sizeof("[init] open2 failed\n") - 1));
- sys_exit(1);
- }
-
- struct stat st;
- if (sys_fstat(fd, &st) < 0) {
- sys_write(1, "[init] fstat failed\n", (uint32_t)(sizeof("[init] fstat failed\n") - 1));
- sys_exit(1);
- }
-
- if ((st.st_mode & S_IFMT) != S_IFREG || st.st_size == 0) {
- sys_write(1, "[init] fstat bad\n", (uint32_t)(sizeof("[init] fstat bad\n") - 1));
- sys_exit(1);
- }
-
- if (sys_lseek(fd, 0, SEEK_SET) < 0) {
- sys_write(1, "[init] lseek set failed\n",
- (uint32_t)(sizeof("[init] lseek set failed\n") - 1));
- sys_exit(1);
- }
-
- uint8_t m2[4];
- if (sys_read(fd, m2, 4) != 4) {
- sys_write(1, "[init] read2 failed\n", (uint32_t)(sizeof("[init] read2 failed\n") - 1));
- sys_exit(1);
- }
- if (m2[0] != 0x7F || m2[1] != 'E' || m2[2] != 'L' || m2[3] != 'F') {
- sys_write(1, "[init] lseek/read mismatch\n",
- (uint32_t)(sizeof("[init] lseek/read mismatch\n") - 1));
- sys_exit(1);
- }
-
- if (sys_close(fd) < 0) {
- sys_write(1, "[init] close2 failed\n", (uint32_t)(sizeof("[init] close2 failed\n") - 1));
- sys_exit(1);
- }
-
- if (sys_stat("/sbin/fulltest", &st) < 0) {
- sys_write(1, "[init] stat failed\n", (uint32_t)(sizeof("[init] stat failed\n") - 1));
- sys_exit(1);
- }
- if ((st.st_mode & S_IFMT) != S_IFREG || st.st_size == 0) {
- sys_write(1, "[init] stat bad\n", (uint32_t)(sizeof("[init] stat bad\n") - 1));
- sys_exit(1);
- }
-
- sys_write(1, "[init] lseek/stat/fstat OK\n",
- (uint32_t)(sizeof("[init] lseek/stat/fstat OK\n") - 1));
-
- fd = sys_open("/tmp/hello.txt", 0);
- if (fd < 0) {
- sys_write(1, "[init] tmpfs open failed\n",
- (uint32_t)(sizeof("[init] tmpfs open failed\n") - 1));
- sys_exit(1);
- }
-
- if (sys_lseek(fd, 0, SEEK_END) < 0) {
- sys_write(1, "[init] dup2 prep lseek failed\n",
- (uint32_t)(sizeof("[init] dup2 prep lseek failed\n") - 1));
- sys_exit(1);
- }
-
- if (sys_dup2(fd, 1) != 1) {
- sys_write(1, "[init] dup2 failed\n", (uint32_t)(sizeof("[init] dup2 failed\n") - 1));
- sys_exit(1);
- }
-
- (void)sys_close(fd);
-
- {
- static const char m[] = "[init] dup2 stdout->file OK\n";
- if (sys_write(1, m, (uint32_t)(sizeof(m) - 1)) != (int)(sizeof(m) - 1)) {
- sys_exit(1);
- }
- }
-
- (void)sys_close(1);
- sys_write(1, "[init] dup2 restore tty OK\n",
- (uint32_t)(sizeof("[init] dup2 restore tty OK\n") - 1));
-
- {
- int pfds[2];
- if (sys_pipe(pfds) < 0) {
- sys_write(1, "[init] pipe failed\n", (uint32_t)(sizeof("[init] pipe failed\n") - 1));
- sys_exit(1);
- }
-
- static const char pmsg[] = "pipe-test";
- if (sys_write(pfds[1], pmsg, (uint32_t)(sizeof(pmsg) - 1)) != (int)(sizeof(pmsg) - 1)) {
- sys_write(1, "[init] pipe write failed\n",
- (uint32_t)(sizeof("[init] pipe write failed\n") - 1));
- sys_exit(1);
- }
-
- char rbuf[16];
- int prd = sys_read(pfds[0], rbuf, (uint32_t)(sizeof(pmsg) - 1));
- if (prd != (int)(sizeof(pmsg) - 1)) {
- sys_write(1, "[init] pipe read failed\n",
- (uint32_t)(sizeof("[init] pipe read failed\n") - 1));
- sys_exit(1);
- }
-
- int ok = 1;
- for (uint32_t i = 0; i < (uint32_t)(sizeof(pmsg) - 1); i++) {
- if ((uint8_t)rbuf[i] != (uint8_t)pmsg[i]) ok = 0;
- }
- if (!ok) {
- sys_write(1, "[init] pipe mismatch\n",
- (uint32_t)(sizeof("[init] pipe mismatch\n") - 1));
- sys_exit(1);
- }
-
- if (sys_dup2(pfds[1], 1) != 1) {
- sys_write(1, "[init] pipe dup2 failed\n",
- (uint32_t)(sizeof("[init] pipe dup2 failed\n") - 1));
- sys_exit(1);
- }
-
- static const char p2[] = "dup2-pipe";
- if (sys_write(1, p2, (uint32_t)(sizeof(p2) - 1)) != (int)(sizeof(p2) - 1)) {
- sys_exit(1);
- }
-
- int prd2 = sys_read(pfds[0], rbuf, (uint32_t)(sizeof(p2) - 1));
- if (prd2 != (int)(sizeof(p2) - 1)) {
- sys_write(1, "[init] pipe dup2 read failed\n",
- (uint32_t)(sizeof("[init] pipe dup2 read failed\n") - 1));
- sys_exit(1);
- }
- sys_write(1, "[init] pipe OK\n", (uint32_t)(sizeof("[init] pipe OK\n") - 1));
-
- (void)sys_close(pfds[0]);
- (void)sys_close(pfds[1]);
-
- int tfd = sys_open("/dev/tty", 0);
- if (tfd < 0) {
- sys_write(1, "[init] /dev/tty open failed\n",
- (uint32_t)(sizeof("[init] /dev/tty open failed\n") - 1));
- sys_exit(1);
- }
- if (sys_dup2(tfd, 1) != 1) {
- sys_write(1, "[init] dup2 restore tty failed\n",
- (uint32_t)(sizeof("[init] dup2 restore tty failed\n") - 1));
- sys_exit(1);
- }
- (void)sys_close(tfd);
-
- }
-
- {
- int pid = sys_fork();
- if (pid < 0) {
- sys_write(1, "[init] kill test fork failed\n",
- (uint32_t)(sizeof("[init] kill test fork failed\n") - 1));
- sys_exit(1);
- }
-
- if (pid == 0) {
- for (;;) {
- __asm__ volatile("nop");
- }
- }
-
- if (sys_kill(pid, SIGKILL) < 0) {
- sys_write(1, "[init] kill(SIGKILL) failed\n",
- (uint32_t)(sizeof("[init] kill(SIGKILL) failed\n") - 1));
- sys_exit(1);
- }
-
- int st = 0;
- int rp = sys_waitpid(pid, &st, 0);
- if (rp != pid || st != (128 + SIGKILL)) {
- sys_write(1, "[init] kill test waitpid mismatch\n",
- (uint32_t)(sizeof("[init] kill test waitpid mismatch\n") - 1));
- sys_exit(1);
- }
-
- sys_write(1, "[init] kill(SIGKILL) OK\n",
- (uint32_t)(sizeof("[init] kill(SIGKILL) OK\n") - 1));
- }
-
- {
- int fds[2];
- if (sys_pipe(fds) < 0) {
- sys_write(1, "[init] poll pipe setup failed\n",
- (uint32_t)(sizeof("[init] poll pipe setup failed\n") - 1));
- sys_exit(1);
- }
-
- struct pollfd p;
- p.fd = fds[0];
- p.events = POLLIN;
- p.revents = 0;
- int rc = sys_poll(&p, 1, 0);
- if (rc != 0) {
- sys_write(1, "[init] poll(pipe) expected 0\n",
- (uint32_t)(sizeof("[init] poll(pipe) expected 0\n") - 1));
- sys_exit(1);
- }
-
- static const char a = 'A';
- if (sys_write(fds[1], &a, 1) != 1) {
- sys_write(1, "[init] poll pipe write failed\n",
- (uint32_t)(sizeof("[init] poll pipe write failed\n") - 1));
- sys_exit(1);
- }
-
- p.revents = 0;
- rc = sys_poll(&p, 1, 0);
- if (rc != 1 || (p.revents & POLLIN) == 0) {
- sys_write(1, "[init] poll(pipe) expected POLLIN\n",
- (uint32_t)(sizeof("[init] poll(pipe) expected POLLIN\n") - 1));
- sys_exit(1);
- }
-
- (void)sys_close(fds[0]);
- (void)sys_close(fds[1]);
- sys_write(1, "[init] poll(pipe) OK\n", (uint32_t)(sizeof("[init] poll(pipe) OK\n") - 1));
- }
-
- {
- int fds[2];
- if (sys_pipe(fds) < 0) {
- sys_write(1, "[init] select pipe setup failed\n",
- (uint32_t)(sizeof("[init] select pipe setup failed\n") - 1));
- sys_exit(1);
- }
-
- uint64_t r = 0;
- uint64_t w = 0;
- r |= (1ULL << (uint32_t)fds[0]);
- int rc = sys_select((uint32_t)(fds[0] + 1), &r, &w, 0, 0);
- if (rc != 0) {
- sys_write(1, "[init] select(pipe) expected 0\n",
- (uint32_t)(sizeof("[init] select(pipe) expected 0\n") - 1));
- sys_exit(1);
- }
-
- static const char a = 'B';
- if (sys_write(fds[1], &a, 1) != 1) {
- sys_write(1, "[init] select pipe write failed\n",
- (uint32_t)(sizeof("[init] select pipe write failed\n") - 1));
- sys_exit(1);
- }
-
- r = 0;
- w = 0;
- r |= (1ULL << (uint32_t)fds[0]);
- rc = sys_select((uint32_t)(fds[0] + 1), &r, &w, 0, 0);
- if (rc != 1 || ((r >> (uint32_t)fds[0]) & 1ULL) == 0) {
- sys_write(1, "[init] select(pipe) expected readable\n",
- (uint32_t)(sizeof("[init] select(pipe) expected readable\n") - 1));
- sys_exit(1);
- }
-
- (void)sys_close(fds[0]);
- (void)sys_close(fds[1]);
- sys_write(1, "[init] select(pipe) OK\n",
- (uint32_t)(sizeof("[init] select(pipe) OK\n") - 1));
- }
-
- {
- int fd = sys_open("/dev/tty", 0);
- if (fd < 0) {
- sys_write(1, "[init] ioctl(/dev/tty) open failed\n",
- (uint32_t)(sizeof("[init] ioctl(/dev/tty) open failed\n") - 1));
- sys_exit(1);
- }
-
- int fg = -1;
- if (sys_ioctl(fd, TIOCGPGRP, &fg) < 0 || fg != 0) {
- sys_write(1, "[init] ioctl TIOCGPGRP failed\n",
- (uint32_t)(sizeof("[init] ioctl TIOCGPGRP failed\n") - 1));
- sys_exit(1);
- }
-
- fg = 0;
- if (sys_ioctl(fd, TIOCSPGRP, &fg) < 0) {
- sys_write(1, "[init] ioctl TIOCSPGRP failed\n",
- (uint32_t)(sizeof("[init] ioctl TIOCSPGRP failed\n") - 1));
- sys_exit(1);
- }
-
- fg = 1;
- if (sys_ioctl(fd, TIOCSPGRP, &fg) >= 0) {
- sys_write(1, "[init] ioctl TIOCSPGRP expected fail\n",
- (uint32_t)(sizeof("[init] ioctl TIOCSPGRP expected fail\n") - 1));
- sys_exit(1);
- }
-
- struct termios oldt;
- if (sys_ioctl(fd, TCGETS, &oldt) < 0) {
- sys_write(1, "[init] ioctl TCGETS failed\n",
- (uint32_t)(sizeof("[init] ioctl TCGETS failed\n") - 1));
- sys_exit(1);
- }
-
- struct termios t = oldt;
- t.c_lflag &= ~(uint32_t)(ECHO | ICANON);
- if (sys_ioctl(fd, TCSETS, &t) < 0) {
- sys_write(1, "[init] ioctl TCSETS failed\n",
- (uint32_t)(sizeof("[init] ioctl TCSETS failed\n") - 1));
- sys_exit(1);
- }
-
- struct termios chk;
- if (sys_ioctl(fd, TCGETS, &chk) < 0) {
- sys_write(1, "[init] ioctl TCGETS2 failed\n",
- (uint32_t)(sizeof("[init] ioctl TCGETS2 failed\n") - 1));
- sys_exit(1);
- }
-
- if ((chk.c_lflag & (uint32_t)(ECHO | ICANON)) != 0) {
- sys_write(1, "[init] ioctl verify failed\n",
- (uint32_t)(sizeof("[init] ioctl verify failed\n") - 1));
- sys_exit(1);
- }
-
- (void)sys_ioctl(fd, TCSETS, &oldt);
- (void)sys_close(fd);
-
- sys_write(1, "[init] ioctl(/dev/tty) OK\n",
- (uint32_t)(sizeof("[init] ioctl(/dev/tty) OK\n") - 1));
- }
-
- // A2: basic job control. A background pgrp read/write on controlling TTY should raise SIGTTIN/SIGTTOU.
- {
- int leader = sys_fork();
- if (leader < 0) {
- sys_write(1, "[init] fork(job control leader) failed\n",
- (uint32_t)(sizeof("[init] fork(job control leader) failed\n") - 1));
- sys_exit(1);
- }
- if (leader == 0) {
- int me = sys_getpid();
- int sid = sys_setsid();
- if (sid != me) {
- sys_write(1, "[init] setsid(job control) failed\n",
- (uint32_t)(sizeof("[init] setsid(job control) failed\n") - 1));
- sys_exit(1);
- }
-
- int tfd = sys_open("/dev/tty", 0);
- if (tfd < 0) {
- sys_write(1, "[init] open(/dev/tty) for job control failed\n",
- (uint32_t)(sizeof("[init] open(/dev/tty) for job control failed\n") - 1));
- sys_exit(1);
- }
-
- // Touch ioctl to make kernel acquire controlling session/pgrp.
- int fg = 0;
- (void)sys_ioctl(tfd, TIOCGPGRP, &fg);
-
- fg = me;
- if (sys_ioctl(tfd, TIOCSPGRP, &fg) < 0) {
- sys_write(1, "[init] ioctl TIOCSPGRP(job control) failed\n",
- (uint32_t)(sizeof("[init] ioctl TIOCSPGRP(job control) failed\n") - 1));
- sys_exit(1);
- }
-
- int bg = sys_fork();
- if (bg < 0) {
- sys_write(1, "[init] fork(job control bg) failed\n",
- (uint32_t)(sizeof("[init] fork(job control bg) failed\n") - 1));
- sys_exit(1);
- }
- if (bg == 0) {
- (void)sys_setpgid(0, me + 1);
-
- (void)sys_sigaction(SIGTTIN, ttin_handler, 0);
- (void)sys_sigaction(SIGTTOU, ttou_handler, 0);
-
- uint8_t b = 0;
- (void)sys_read(tfd, &b, 1);
- if (!got_ttin) {
- sys_write(1, "[init] SIGTTIN job control failed\n",
- (uint32_t)(sizeof("[init] SIGTTIN job control failed\n") - 1));
- sys_exit(1);
- }
-
- const char msg2[] = "x";
- (void)sys_write(tfd, msg2, 1);
- if (!got_ttou) {
- sys_write(1, "[init] SIGTTOU job control failed\n",
- (uint32_t)(sizeof("[init] SIGTTOU job control failed\n") - 1));
- sys_exit(1);
- }
-
- sys_exit(0);
- }
-
- int st2 = 0;
- int wp2 = sys_waitpid(bg, &st2, 0);
- if (wp2 != bg || st2 != 0) {
- sys_write(1, "[init] waitpid(job control bg) failed wp=", (uint32_t)(sizeof("[init] waitpid(job control bg) failed wp=") - 1));
- write_int_dec(wp2);
- sys_write(1, " st=", (uint32_t)(sizeof(" st=") - 1));
- write_int_dec(st2);
- sys_write(1, "\n", 1);
- sys_exit(1);
- }
-
- (void)sys_close(tfd);
- sys_exit(0);
- }
-
- int stL = 0;
- int wpL = sys_waitpid(leader, &stL, 0);
- if (wpL != leader || stL != 0) {
- sys_write(1, "[init] waitpid(job control leader) failed wp=", (uint32_t)(sizeof("[init] waitpid(job control leader) failed wp=") - 1));
- write_int_dec(wpL);
- sys_write(1, " st=", (uint32_t)(sizeof(" st=") - 1));
- write_int_dec(stL);
- sys_write(1, "\n", 1);
- sys_exit(1);
- }
-
- sys_write(1, "[init] job control (SIGTTIN/SIGTTOU) OK\n",
- (uint32_t)(sizeof("[init] job control (SIGTTIN/SIGTTOU) OK\n") - 1));
- }
-
- {
- int fd = sys_open("/dev/null", 0);
- if (fd < 0) {
- sys_write(1, "[init] poll(/dev/null) open failed\n",
- (uint32_t)(sizeof("[init] poll(/dev/null) open failed\n") - 1));
- sys_exit(1);
- }
- struct pollfd p;
- p.fd = fd;
- p.events = POLLOUT;
- p.revents = 0;
- int rc = sys_poll(&p, 1, 0);
- if (rc != 1 || (p.revents & POLLOUT) == 0) {
- sys_write(1, "[init] poll(/dev/null) expected POLLOUT\n",
- (uint32_t)(sizeof("[init] poll(/dev/null) expected POLLOUT\n") - 1));
- sys_exit(1);
- }
- (void)sys_close(fd);
- sys_write(1, "[init] poll(/dev/null) OK\n",
- (uint32_t)(sizeof("[init] poll(/dev/null) OK\n") - 1));
- }
-
- {
- int mfd = sys_open("/dev/ptmx", 0);
- int sfd = sys_open("/dev/pts/0", 0);
- if (mfd < 0 || sfd < 0) {
- sys_write(1, "[init] pty open failed\n",
- (uint32_t)(sizeof("[init] pty open failed\n") - 1));
- sys_exit(1);
- }
-
- static const char m2s[] = "m2s";
- if (sys_write(mfd, m2s, (uint32_t)(sizeof(m2s) - 1)) != (int)(sizeof(m2s) - 1)) {
- sys_write(1, "[init] pty write master failed\n",
- (uint32_t)(sizeof("[init] pty write master failed\n") - 1));
- sys_exit(1);
- }
-
- struct pollfd p;
- p.fd = sfd;
- p.events = POLLIN;
- p.revents = 0;
- int rc = sys_poll(&p, 1, 50);
- if (rc != 1 || (p.revents & POLLIN) == 0) {
- sys_write(1, "[init] pty poll slave failed\n",
- (uint32_t)(sizeof("[init] pty poll slave failed\n") - 1));
- sys_exit(1);
- }
-
- char buf[8];
- int rd = sys_read(sfd, buf, (uint32_t)(sizeof(m2s) - 1));
- if (rd != (int)(sizeof(m2s) - 1) || !memeq(buf, m2s, (uint32_t)(sizeof(m2s) - 1))) {
- sys_write(1, "[init] pty read slave failed\n",
- (uint32_t)(sizeof("[init] pty read slave failed\n") - 1));
- sys_exit(1);
- }
-
- static const char s2m[] = "s2m";
- if (sys_write(sfd, s2m, (uint32_t)(sizeof(s2m) - 1)) != (int)(sizeof(s2m) - 1)) {
- sys_write(1, "[init] pty write slave failed\n",
- (uint32_t)(sizeof("[init] pty write slave failed\n") - 1));
- sys_exit(1);
- }
-
- p.fd = mfd;
- p.events = POLLIN;
- p.revents = 0;
- rc = sys_poll(&p, 1, 50);
- if (rc != 1 || (p.revents & POLLIN) == 0) {
- sys_write(1, "[init] pty poll master failed\n",
- (uint32_t)(sizeof("[init] pty poll master failed\n") - 1));
- sys_exit(1);
- }
-
- rd = sys_read(mfd, buf, (uint32_t)(sizeof(s2m) - 1));
- if (rd != (int)(sizeof(s2m) - 1) || !memeq(buf, s2m, (uint32_t)(sizeof(s2m) - 1))) {
- sys_write(1, "[init] pty read master failed\n",
- (uint32_t)(sizeof("[init] pty read master failed\n") - 1));
- sys_exit(1);
- }
-
- (void)sys_close(mfd);
- (void)sys_close(sfd);
- sys_write(1, "[init] pty OK\n", (uint32_t)(sizeof("[init] pty OK\n") - 1));
- }
-
- {
- sys_write(1, "[init] setsid test: before fork\n",
- (uint32_t)(sizeof("[init] setsid test: before fork\n") - 1));
- int pid = sys_fork();
- if (pid < 0) {
- static const char smsg[] = "[init] fork failed\n";
- (void)sys_write(1, smsg, (uint32_t)(sizeof(smsg) - 1));
- sys_exit(2);
- }
- if (pid == 0) {
- sys_write(1, "[init] setsid test: child start\n",
- (uint32_t)(sizeof("[init] setsid test: child start\n") - 1));
- int me = sys_getpid();
- int sid = sys_setsid();
- if (sid != me) sys_exit(2);
-
- int pg = sys_getpgrp();
- if (pg != me) sys_exit(3);
-
- int newpg = me + 1;
- if (sys_setpgid(0, newpg) < 0) sys_exit(4);
- if (sys_getpgrp() != newpg) sys_exit(5);
-
- sys_exit(0);
- }
-
- sys_write(1, "[init] setsid test: parent waitpid\n",
- (uint32_t)(sizeof("[init] setsid test: parent waitpid\n") - 1));
- int st = 0;
- int wp = sys_waitpid(pid, &st, 0);
- if (wp != pid || st != 0) {
- sys_write(1, "[init] setsid/setpgid/getpgrp failed\n",
- (uint32_t)(sizeof("[init] setsid/setpgid/getpgrp failed\n") - 1));
- sys_exit(1);
- }
-
- sys_write(1, "[init] setsid/setpgid/getpgrp OK\n",
- (uint32_t)(sizeof("[init] setsid/setpgid/getpgrp OK\n") - 1));
- }
-
- {
- uintptr_t oldh = 0;
- if (sys_sigaction(SIGUSR1, usr1_handler, &oldh) < 0) {
- sys_write(1, "[init] sigaction failed\n",
- (uint32_t)(sizeof("[init] sigaction failed\n") - 1));
- sys_exit(1);
- }
-
- int me = sys_getpid();
- if (sys_kill(me, SIGUSR1) < 0) {
- sys_write(1, "[init] kill(SIGUSR1) failed\n",
- (uint32_t)(sizeof("[init] kill(SIGUSR1) failed\n") - 1));
- sys_exit(1);
- }
-
- for (uint32_t i = 0; i < 2000000U; i++) {
- if (got_usr1) break;
- }
-
- if (!got_usr1) {
- sys_write(1, "[init] SIGUSR1 not delivered\n",
- (uint32_t)(sizeof("[init] SIGUSR1 not delivered\n") - 1));
- sys_exit(1);
- }
-
- sys_write(1, "[init] sigaction/kill(SIGUSR1) OK\n",
- (uint32_t)(sizeof("[init] sigaction/kill(SIGUSR1) OK\n") - 1));
- }
-
- // Verify that returning from a signal handler does not corrupt the user stack.
- {
- if (sys_sigaction(SIGUSR1, usr1_ret_handler, 0) < 0) {
- sys_write(1, "[init] sigaction (sigreturn test) failed\n",
- (uint32_t)(sizeof("[init] sigaction (sigreturn test) failed\n") - 1));
- sys_exit(1);
- }
-
- volatile uint32_t canary = 0x11223344U;
- int me = sys_getpid();
- if (sys_kill(me, SIGUSR1) < 0) {
- sys_write(1, "[init] kill(SIGUSR1) (sigreturn test) failed\n",
- (uint32_t)(sizeof("[init] kill(SIGUSR1) (sigreturn test) failed\n") - 1));
- sys_exit(1);
- }
-
- if (!got_usr1_ret) {
- sys_write(1, "[init] SIGUSR1 not delivered (sigreturn test)\n",
- (uint32_t)(sizeof("[init] SIGUSR1 not delivered (sigreturn test)\n") - 1));
- sys_exit(1);
- }
-
- if (canary != 0x11223344U) {
- sys_write(1, "[init] sigreturn test stack corruption\n",
- (uint32_t)(sizeof("[init] sigreturn test stack corruption\n") - 1));
- sys_exit(1);
- }
-
- sys_write(1, "[init] sigreturn OK\n",
- (uint32_t)(sizeof("[init] sigreturn OK\n") - 1));
- }
-
- fd = sys_open("/tmp/hello.txt", 0);
- if (fd < 0) {
- sys_write(1, "[init] tmpfs open2 failed\n",
- (uint32_t)(sizeof("[init] tmpfs open2 failed\n") - 1));
- sys_exit(1);
- }
-
- if (sys_stat("/tmp/hello.txt", &st) < 0) {
- sys_write(1, "[init] tmpfs stat failed\n",
- (uint32_t)(sizeof("[init] tmpfs stat failed\n") - 1));
- sys_exit(1);
- }
- if ((st.st_mode & S_IFMT) != S_IFREG) {
- sys_write(1, "[init] tmpfs stat not reg\n",
- (uint32_t)(sizeof("[init] tmpfs stat not reg\n") - 1));
- sys_exit(1);
- }
- if (st.st_size == 0) {
- sys_write(1, "[init] tmpfs stat size 0\n",
- (uint32_t)(sizeof("[init] tmpfs stat size 0\n") - 1));
- sys_exit(1);
- }
-
- struct stat fst;
- if (sys_fstat(fd, &fst) < 0) {
- sys_write(1, "[init] tmpfs fstat failed\n",
- (uint32_t)(sizeof("[init] tmpfs fstat failed\n") - 1));
- sys_exit(1);
- }
- if (fst.st_size != st.st_size) {
- sys_write(1, "[init] tmpfs stat size mismatch\n",
- (uint32_t)(sizeof("[init] tmpfs stat size mismatch\n") - 1));
- sys_exit(1);
- }
-
- int end = sys_lseek(fd, 0, SEEK_END);
- if (end < 0 || (uint32_t)end != st.st_size) {
- sys_write(1, "[init] tmpfs lseek end bad\n",
- (uint32_t)(sizeof("[init] tmpfs lseek end bad\n") - 1));
- sys_exit(1);
- }
-
- uint8_t eofb;
- if (sys_read(fd, &eofb, 1) != 0) {
- sys_write(1, "[init] tmpfs eof read bad\n",
- (uint32_t)(sizeof("[init] tmpfs eof read bad\n") - 1));
- sys_exit(1);
- }
-
- if (sys_lseek(fd, 0, 999) >= 0) {
- sys_write(1, "[init] tmpfs lseek whence bad\n",
- (uint32_t)(sizeof("[init] tmpfs lseek whence bad\n") - 1));
- sys_exit(1);
- }
-
- if (sys_lseek(fd, 0, SEEK_SET) < 0) {
- sys_write(1, "[init] tmpfs lseek set failed\n",
- (uint32_t)(sizeof("[init] tmpfs lseek set failed\n") - 1));
- sys_exit(1);
- }
-
- uint8_t tbuf[6];
- if (sys_read(fd, tbuf, 5) != 5) {
- sys_write(1, "[init] tmpfs read failed\n",
- (uint32_t)(sizeof("[init] tmpfs read failed\n") - 1));
- sys_exit(1);
- }
- tbuf[5] = 0;
- if (tbuf[0] != 'h' || tbuf[1] != 'e' || tbuf[2] != 'l' || tbuf[3] != 'l' || tbuf[4] != 'o') {
- sys_write(1, "[init] tmpfs bad data\n", (uint32_t)(sizeof("[init] tmpfs bad data\n") - 1));
- sys_exit(1);
- }
-
- if (sys_close(fd) < 0) {
- sys_write(1, "[init] tmpfs close failed\n",
- (uint32_t)(sizeof("[init] tmpfs close failed\n") - 1));
- sys_exit(1);
- }
-
- if (sys_open("/tmp/does_not_exist", 0) >= 0) {
- sys_write(1, "[init] tmpfs open nonexist bad\n",
- (uint32_t)(sizeof("[init] tmpfs open nonexist bad\n") - 1));
- sys_exit(1);
- }
-
- fd = sys_open("/tmp/hello.txt", 0);
- if (fd < 0) {
- sys_write(1, "[init] tmpfs open3 failed\n",
- (uint32_t)(sizeof("[init] tmpfs open3 failed\n") - 1));
- sys_exit(1);
- }
-
- if (sys_fstat(fd, &fst) < 0) {
- sys_write(1, "[init] tmpfs fstat2 failed\n",
- (uint32_t)(sizeof("[init] tmpfs fstat2 failed\n") - 1));
- sys_exit(1);
- }
-
- if (sys_lseek(fd, 0, SEEK_END) < 0) {
- sys_write(1, "[init] tmpfs lseek end2 failed\n",
- (uint32_t)(sizeof("[init] tmpfs lseek end2 failed\n") - 1));
- sys_exit(1);
- }
-
- char suf[3];
- suf[0] = 'X';
- suf[1] = 'Y';
- suf[2] = 'Z';
- if (sys_write(fd, suf, 3) != 3) {
- sys_write(1, "[init] tmpfs write failed\n",
- (uint32_t)(sizeof("[init] tmpfs write failed\n") - 1));
- sys_exit(1);
- }
-
- if (sys_fstat(fd, &fst) < 0) {
- sys_write(1, "[init] tmpfs fstat3 failed\n",
- (uint32_t)(sizeof("[init] tmpfs fstat3 failed\n") - 1));
- sys_exit(1);
- }
- if (fst.st_size != st.st_size + 3) {
- sys_write(1, "[init] tmpfs size not grown\n",
- (uint32_t)(sizeof("[init] tmpfs size not grown\n") - 1));
- sys_exit(1);
- }
-
- if (sys_lseek(fd, -3, SEEK_END) < 0) {
- sys_write(1, "[init] tmpfs lseek back failed\n",
- (uint32_t)(sizeof("[init] tmpfs lseek back failed\n") - 1));
- sys_exit(1);
- }
- uint8_t s2[3];
- if (sys_read(fd, s2, 3) != 3 || s2[0] != 'X' || s2[1] != 'Y' || s2[2] != 'Z') {
- sys_write(1, "[init] tmpfs suffix mismatch\n",
- (uint32_t)(sizeof("[init] tmpfs suffix mismatch\n") - 1));
- sys_exit(1);
- }
-
- if (sys_close(fd) < 0) {
- sys_write(1, "[init] tmpfs close3 failed\n",
- (uint32_t)(sizeof("[init] tmpfs close3 failed\n") - 1));
- sys_exit(1);
- }
-
- sys_write(1, "[init] tmpfs/mount OK\n", (uint32_t)(sizeof("[init] tmpfs/mount OK\n") - 1));
-
- {
- int fd = sys_open("/dev/null", 0);
- if (fd < 0) {
- sys_write(1, "[init] /dev/null open failed\n",
- (uint32_t)(sizeof("[init] /dev/null open failed\n") - 1));
- sys_exit(1);
- }
- static const char z[] = "discard me";
- if (sys_write(fd, z, (uint32_t)(sizeof(z) - 1)) != (int)(sizeof(z) - 1)) {
- sys_write(1, "[init] /dev/null write failed\n",
- (uint32_t)(sizeof("[init] /dev/null write failed\n") - 1));
- sys_exit(1);
- }
- (void)sys_close(fd);
- sys_write(1, "[init] /dev/null OK\n", (uint32_t)(sizeof("[init] /dev/null OK\n") - 1));
- }
-
- // B1: persistent storage smoke. Value should increment across reboots (disk.img).
- {
- int fd = sys_open("/persist/counter", 0);
- if (fd < 0) {
- sys_write(1, "[init] /persist/counter open failed\n",
- (uint32_t)(sizeof("[init] /persist/counter open failed\n") - 1));
- sys_exit(1);
- }
-
- (void)sys_lseek(fd, 0, SEEK_SET);
- uint8_t b[4] = {0, 0, 0, 0};
- int rd = sys_read(fd, b, 4);
- if (rd != 4) {
- sys_write(1, "[init] /persist/counter read failed\n",
- (uint32_t)(sizeof("[init] /persist/counter read failed\n") - 1));
- sys_exit(1);
- }
-
- uint32_t v = (uint32_t)b[0] | ((uint32_t)b[1] << 8) | ((uint32_t)b[2] << 16) | ((uint32_t)b[3] << 24);
- v++;
- b[0] = (uint8_t)(v & 0xFF);
- b[1] = (uint8_t)((v >> 8) & 0xFF);
- b[2] = (uint8_t)((v >> 16) & 0xFF);
- b[3] = (uint8_t)((v >> 24) & 0xFF);
-
- (void)sys_lseek(fd, 0, SEEK_SET);
- int wr = sys_write(fd, b, 4);
- if (wr != 4) {
- sys_write(1, "[init] /persist/counter write failed\n",
- (uint32_t)(sizeof("[init] /persist/counter write failed\n") - 1));
- sys_exit(1);
- }
-
- (void)sys_close(fd);
-
- sys_write(1, "[init] /persist/counter=", (uint32_t)(sizeof("[init] /persist/counter=") - 1));
- write_int_dec((int)v);
- sys_write(1, "\n", 1);
- }
-
- {
- int fd = sys_open("/dev/tty", 0);
- if (fd < 0) {
- sys_write(1, "[init] /dev/tty open failed\n",
- (uint32_t)(sizeof("[init] /dev/tty open failed\n") - 1));
- sys_exit(1);
- }
- static const char m[] = "[init] /dev/tty write OK\n";
- int wr = sys_write(fd, m, (uint32_t)(sizeof(m) - 1));
- if (wr != (int)(sizeof(m) - 1)) {
- sys_write(1, "[init] /dev/tty write failed\n",
- (uint32_t)(sizeof("[init] /dev/tty write failed\n") - 1));
- sys_exit(1);
- }
- (void)sys_close(fd);
- }
-
- // B2: on-disk general filesystem smoke (/disk)
- {
- int fd = sys_open("/disk/test", O_CREAT);
- if (fd < 0) {
- sys_write(1, "[init] /disk/test open failed\n",
- (uint32_t)(sizeof("[init] /disk/test open failed\n") - 1));
- sys_exit(1);
- }
-
- char buf[16];
- int rd = sys_read(fd, buf, sizeof(buf));
- int prev = 0;
- if (rd > 0) {
- for (int i = 0; i < rd; i++) {
- if (buf[i] < '0' || buf[i] > '9') break;
- prev = prev * 10 + (buf[i] - '0');
- }
- }
-
- (void)sys_close(fd);
-
- fd = sys_open("/disk/test", O_CREAT | O_TRUNC);
- if (fd < 0) {
- sys_write(1, "[init] /disk/test open2 failed\n",
- (uint32_t)(sizeof("[init] /disk/test open2 failed\n") - 1));
- sys_exit(1);
- }
-
- int next = prev + 1;
- char out[16];
- int n = 0;
- int v = next;
- if (v == 0) {
- out[n++] = '0';
- } else {
- char tmp[16];
- int t = 0;
- while (v > 0 && t < (int)sizeof(tmp)) {
- tmp[t++] = (char)('0' + (v % 10));
- v /= 10;
- }
- while (t > 0) {
- out[n++] = tmp[--t];
- }
- }
-
- if (sys_write(fd, out, (uint32_t)n) != n) {
- sys_write(1, "[init] /disk/test write failed\n",
- (uint32_t)(sizeof("[init] /disk/test write failed\n") - 1));
- sys_exit(1);
- }
- (void)sys_close(fd);
-
- fd = sys_open("/disk/test", 0);
- if (fd < 0) {
- sys_write(1, "[init] /disk/test open3 failed\n",
- (uint32_t)(sizeof("[init] /disk/test open3 failed\n") - 1));
- sys_exit(1);
- }
- for (uint32_t i = 0; i < (uint32_t)sizeof(buf); i++) buf[i] = 0;
- rd = sys_read(fd, buf, sizeof(buf));
- (void)sys_close(fd);
- if (rd != n || !memeq(buf, out, (uint32_t)n)) {
- sys_write(1, "[init] /disk/test verify failed\n",
- (uint32_t)(sizeof("[init] /disk/test verify failed\n") - 1));
- sys_exit(1);
- }
-
- sys_write(1, "[init] /disk/test prev=", (uint32_t)(sizeof("[init] /disk/test prev=") - 1));
- write_int_dec(prev);
- sys_write(1, " next=", (uint32_t)(sizeof(" next=") - 1));
- write_int_dec(next);
- sys_write(1, " OK\n", (uint32_t)(sizeof(" OK\n") - 1));
- }
-
- // B3: diskfs mkdir/unlink smoke
- {
- int r = sys_mkdir("/disk/dir");
- if (r < 0 && errno != 17) {
- sys_write(1, "[init] mkdir /disk/dir failed errno=", (uint32_t)(sizeof("[init] mkdir /disk/dir failed errno=") - 1));
- write_int_dec(errno);
- sys_write(1, "\n", 1);
- sys_exit(1);
- }
-
- int fd = sys_open("/disk/dir/file", O_CREAT | O_TRUNC);
- if (fd < 0) {
- sys_write(1, "[init] open /disk/dir/file failed\n",
- (uint32_t)(sizeof("[init] open /disk/dir/file failed\n") - 1));
- sys_exit(1);
- }
- static const char msg2[] = "ok";
- if (sys_write(fd, msg2, 2) != 2) {
- sys_write(1, "[init] write /disk/dir/file failed\n",
- (uint32_t)(sizeof("[init] write /disk/dir/file failed\n") - 1));
- sys_exit(1);
- }
- (void)sys_close(fd);
-
- r = sys_unlink("/disk/dir/file");
- if (r < 0) {
- sys_write(1, "[init] unlink /disk/dir/file failed\n",
- (uint32_t)(sizeof("[init] unlink /disk/dir/file failed\n") - 1));
- sys_exit(1);
- }
-
- fd = sys_open("/disk/dir/file", 0);
- if (fd >= 0) {
- sys_write(1, "[init] unlink did not remove file\n",
- (uint32_t)(sizeof("[init] unlink did not remove file\n") - 1));
- sys_exit(1);
- }
-
- sys_write(1, "[init] diskfs mkdir/unlink OK\n",
- (uint32_t)(sizeof("[init] diskfs mkdir/unlink OK\n") - 1));
- }
-
- // B4: diskfs getdents smoke
- {
- int r = sys_mkdir("/disk/ls");
- if (r < 0 && errno != 17) {
- sys_write(1, "[init] mkdir /disk/ls failed errno=", (uint32_t)(sizeof("[init] mkdir /disk/ls failed errno=") - 1));
- write_int_dec(errno);
- sys_write(1, "\n", 1);
- sys_exit(1);
- }
-
- int fd = sys_open("/disk/ls/file1", O_CREAT | O_TRUNC);
- if (fd < 0) {
- sys_write(1, "[init] create /disk/ls/file1 failed\n",
- (uint32_t)(sizeof("[init] create /disk/ls/file1 failed\n") - 1));
- sys_exit(1);
- }
- (void)sys_close(fd);
-
- fd = sys_open("/disk/ls/file2", O_CREAT | O_TRUNC);
- if (fd < 0) {
- sys_write(1, "[init] create /disk/ls/file2 failed\n",
- (uint32_t)(sizeof("[init] create /disk/ls/file2 failed\n") - 1));
- sys_exit(1);
- }
- (void)sys_close(fd);
-
- int dfd = sys_open("/disk/ls", 0);
- if (dfd < 0) {
- sys_write(1, "[init] open dir /disk/ls failed\n",
- (uint32_t)(sizeof("[init] open dir /disk/ls failed\n") - 1));
- sys_exit(1);
- }
-
- struct {
- uint32_t d_ino;
- uint16_t d_reclen;
- uint8_t d_type;
- char d_name[24];
- } ents[8];
-
- int n = sys_getdents(dfd, ents, (uint32_t)sizeof(ents));
- (void)sys_close(dfd);
- if (n <= 0) {
- sys_write(1, "[init] getdents failed\n",
- (uint32_t)(sizeof("[init] getdents failed\n") - 1));
- sys_exit(1);
- }
-
- int saw_dot = 0, saw_dotdot = 0, saw_f1 = 0, saw_f2 = 0;
- int cnt = n / (int)sizeof(ents[0]);
- for (int i = 0; i < cnt; i++) {
- if (streq(ents[i].d_name, ".")) saw_dot = 1;
- else if (streq(ents[i].d_name, "..")) saw_dotdot = 1;
- else if (streq(ents[i].d_name, "file1")) saw_f1 = 1;
- else if (streq(ents[i].d_name, "file2")) saw_f2 = 1;
- }
-
- if (!saw_dot || !saw_dotdot || !saw_f1 || !saw_f2) {
- sys_write(1, "[init] getdents verify failed\n",
- (uint32_t)(sizeof("[init] getdents verify failed\n") - 1));
- sys_exit(1);
- }
-
- sys_write(1, "[init] diskfs getdents OK\n",
- (uint32_t)(sizeof("[init] diskfs getdents OK\n") - 1));
- }
-
- // B5: isatty() POSIX-like smoke (via ioctl TCGETS)
- {
- int fd = sys_open("/dev/tty", 0);
- if (fd < 0) {
- sys_write(1, "[init] isatty open /dev/tty failed\n",
- (uint32_t)(sizeof("[init] isatty open /dev/tty failed\n") - 1));
- sys_exit(1);
- }
- int r = isatty_fd(fd);
- (void)sys_close(fd);
- if (r != 1) {
- sys_write(1, "[init] isatty(/dev/tty) failed\n",
- (uint32_t)(sizeof("[init] isatty(/dev/tty) failed\n") - 1));
- sys_exit(1);
- }
-
- fd = sys_open("/dev/null", 0);
- if (fd < 0) {
- sys_write(1, "[init] isatty open /dev/null failed\n",
- (uint32_t)(sizeof("[init] isatty open /dev/null failed\n") - 1));
- sys_exit(1);
- }
- r = isatty_fd(fd);
- (void)sys_close(fd);
- if (r != 0) {
- sys_write(1, "[init] isatty(/dev/null) expected 0\n",
- (uint32_t)(sizeof("[init] isatty(/dev/null) expected 0\n") - 1));
- sys_exit(1);
- }
-
- sys_write(1, "[init] isatty OK\n", (uint32_t)(sizeof("[init] isatty OK\n") - 1));
- }
-
- // B6: O_NONBLOCK smoke (pipe + pty)
- {
- int fds[2];
- if (sys_pipe(fds) < 0) {
- sys_write(1, "[init] pipe for nonblock failed\n",
- (uint32_t)(sizeof("[init] pipe for nonblock failed\n") - 1));
- sys_exit(1);
- }
-
- if (sys_fcntl(fds[0], F_SETFL, O_NONBLOCK) < 0) {
- sys_write(1, "[init] fcntl nonblock pipe failed\n",
- (uint32_t)(sizeof("[init] fcntl nonblock pipe failed\n") - 1));
- sys_exit(1);
- }
-
- char b;
- int r = sys_read(fds[0], &b, 1);
- if (r != -1 || errno != EAGAIN) {
- sys_write(1, "[init] nonblock pipe read expected EAGAIN\n",
- (uint32_t)(sizeof("[init] nonblock pipe read expected EAGAIN\n") - 1));
- sys_exit(1);
- }
-
- if (sys_write(fds[1], "x", 1) != 1) {
- sys_write(1, "[init] pipe write failed\n",
- (uint32_t)(sizeof("[init] pipe write failed\n") - 1));
- sys_exit(1);
- }
- r = sys_read(fds[0], &b, 1);
- if (r != 1 || b != 'x') {
- sys_write(1, "[init] nonblock pipe read after write failed\n",
- (uint32_t)(sizeof("[init] nonblock pipe read after write failed\n") - 1));
- sys_exit(1);
- }
-
- (void)sys_close(fds[0]);
- (void)sys_close(fds[1]);
-
- int p = sys_open("/dev/ptmx", 0);
- if (p < 0) {
- sys_write(1, "[init] open /dev/ptmx failed\n",
- (uint32_t)(sizeof("[init] open /dev/ptmx failed\n") - 1));
- sys_exit(1);
- }
- if (sys_fcntl(p, F_SETFL, O_NONBLOCK) < 0) {
- sys_write(1, "[init] fcntl nonblock ptmx failed\n",
- (uint32_t)(sizeof("[init] fcntl nonblock ptmx failed\n") - 1));
- sys_exit(1);
- }
- char pch;
- r = sys_read(p, &pch, 1);
- if (r != -1 || errno != EAGAIN) {
- sys_write(1, "[init] nonblock ptmx read expected EAGAIN\n",
- (uint32_t)(sizeof("[init] nonblock ptmx read expected EAGAIN\n") - 1));
- sys_exit(1);
- }
- (void)sys_close(p);
-
- sys_write(1, "[init] O_NONBLOCK OK\n",
- (uint32_t)(sizeof("[init] O_NONBLOCK OK\n") - 1));
- }
-
- // B6b: pipe2 + dup3 smoke
- {
- int fds[2];
- if (sys_pipe2(fds, O_NONBLOCK) < 0) {
- sys_write(1, "[init] pipe2 failed\n",
- (uint32_t)(sizeof("[init] pipe2 failed\n") - 1));
- sys_exit(1);
- }
-
- char b;
- int r = sys_read(fds[0], &b, 1);
- if (r != -1 || errno != EAGAIN) {
- sys_write(1, "[init] pipe2 nonblock read expected EAGAIN\n",
- (uint32_t)(sizeof("[init] pipe2 nonblock read expected EAGAIN\n") - 1));
- sys_exit(1);
- }
-
- int d = sys_dup3(fds[0], fds[0], 0);
- if (d != -1 || errno != EINVAL) {
- sys_write(1, "[init] dup3 samefd expected EINVAL\n",
- (uint32_t)(sizeof("[init] dup3 samefd expected EINVAL\n") - 1));
- sys_exit(1);
- }
-
- (void)sys_close(fds[0]);
- (void)sys_close(fds[1]);
- sys_write(1, "[init] pipe2/dup3 OK\n",
- (uint32_t)(sizeof("[init] pipe2/dup3 OK\n") - 1));
- }
-
- // B7: chdir/getcwd smoke + relative paths
- {
- int r = sys_mkdir("/disk/cwd");
- if (r < 0 && errno != 17) {
- sys_write(1, "[init] mkdir /disk/cwd failed\n",
- (uint32_t)(sizeof("[init] mkdir /disk/cwd failed\n") - 1));
- sys_exit(1);
- }
-
- r = sys_chdir("/disk/cwd");
- if (r < 0) {
- sys_write(1, "[init] chdir failed\n",
- (uint32_t)(sizeof("[init] chdir failed\n") - 1));
- sys_exit(1);
- }
-
- char cwd[64];
- for (uint32_t i = 0; i < (uint32_t)sizeof(cwd); i++) cwd[i] = 0;
- if (sys_getcwd(cwd, (uint32_t)sizeof(cwd)) < 0) {
- sys_write(1, "[init] getcwd failed\n",
- (uint32_t)(sizeof("[init] getcwd failed\n") - 1));
- sys_exit(1);
- }
-
- // Create file using relative path.
- int fd = sys_open("rel", O_CREAT | O_TRUNC);
- if (fd < 0) {
- sys_write(1, "[init] open relative failed\n",
- (uint32_t)(sizeof("[init] open relative failed\n") - 1));
- sys_exit(1);
- }
- (void)sys_close(fd);
-
- // Stat with relative path.
- struct stat st;
- if (sys_stat("rel", &st) < 0) {
- sys_write(1, "[init] stat relative failed\n",
- (uint32_t)(sizeof("[init] stat relative failed\n") - 1));
- sys_exit(1);
- }
-
- sys_write(1, "[init] chdir/getcwd OK\n",
- (uint32_t)(sizeof("[init] chdir/getcwd OK\n") - 1));
- }
-
- // B8: *at() syscalls smoke (AT_FDCWD)
- {
- int fd = sys_openat(AT_FDCWD, "atfile", O_CREAT | O_TRUNC, 0);
- if (fd < 0) {
- sys_write(1, "[init] openat failed\n",
- (uint32_t)(sizeof("[init] openat failed\n") - 1));
- sys_exit(1);
- }
- (void)sys_close(fd);
-
- struct stat st;
- if (sys_fstatat(AT_FDCWD, "atfile", &st, 0) < 0) {
- sys_write(1, "[init] fstatat failed\n",
- (uint32_t)(sizeof("[init] fstatat failed\n") - 1));
- sys_exit(1);
- }
-
- if (sys_unlinkat(AT_FDCWD, "atfile", 0) < 0) {
- sys_write(1, "[init] unlinkat failed\n",
- (uint32_t)(sizeof("[init] unlinkat failed\n") - 1));
- sys_exit(1);
- }
-
- if (sys_stat("atfile", &st) >= 0) {
- sys_write(1, "[init] unlinkat did not remove file\n",
- (uint32_t)(sizeof("[init] unlinkat did not remove file\n") - 1));
- sys_exit(1);
- }
-
- sys_write(1, "[init] *at OK\n",
- (uint32_t)(sizeof("[init] *at OK\n") - 1));
- }
-
- // B9: rename + rmdir smoke
- {
- // Create a file, rename it, verify old gone and new exists.
- int fd = sys_open("/disk/rnold", O_CREAT | O_TRUNC);
- if (fd < 0) {
- sys_write(1, "[init] rename: create failed\n",
- (uint32_t)(sizeof("[init] rename: create failed\n") - 1));
- sys_exit(1);
- }
- (void)sys_write(fd, "RN", 2);
- (void)sys_close(fd);
-
- if (sys_rename("/disk/rnold", "/disk/rnnew") < 0) {
- sys_write(1, "[init] rename failed\n",
- (uint32_t)(sizeof("[init] rename failed\n") - 1));
- sys_exit(1);
- }
-
- struct stat st;
- if (sys_stat("/disk/rnold", &st) >= 0) {
- sys_write(1, "[init] rename: old still exists\n",
- (uint32_t)(sizeof("[init] rename: old still exists\n") - 1));
- sys_exit(1);
- }
- if (sys_stat("/disk/rnnew", &st) < 0) {
- sys_write(1, "[init] rename: new not found\n",
- (uint32_t)(sizeof("[init] rename: new not found\n") - 1));
- sys_exit(1);
- }
-
- (void)sys_unlink("/disk/rnnew");
-
- // mkdir, then rmdir
- if (sys_mkdir("/disk/rmtmp") < 0 && errno != 17) {
- sys_write(1, "[init] rmdir: mkdir failed\n",
- (uint32_t)(sizeof("[init] rmdir: mkdir failed\n") - 1));
- sys_exit(1);
- }
- if (sys_rmdir("/disk/rmtmp") < 0) {
- sys_write(1, "[init] rmdir failed\n",
- (uint32_t)(sizeof("[init] rmdir failed\n") - 1));
- sys_exit(1);
- }
- if (sys_stat("/disk/rmtmp", &st) >= 0) {
- sys_write(1, "[init] rmdir: dir still exists\n",
- (uint32_t)(sizeof("[init] rmdir: dir still exists\n") - 1));
- sys_exit(1);
- }
-
- sys_write(1, "[init] rename/rmdir OK\n",
- (uint32_t)(sizeof("[init] rename/rmdir OK\n") - 1));
- }
-
- // B10: getdents on /dev (devfs) and /tmp (tmpfs)
- {
- int devfd = sys_open("/dev", 0);
- if (devfd < 0) {
- sys_write(1, "[init] open /dev failed\n",
- (uint32_t)(sizeof("[init] open /dev failed\n") - 1));
- sys_exit(1);
- }
- char dbuf[256];
- int dr = sys_getdents(devfd, dbuf, (uint32_t)sizeof(dbuf));
- (void)sys_close(devfd);
- if (dr <= 0) {
- sys_write(1, "[init] getdents /dev failed\n",
- (uint32_t)(sizeof("[init] getdents /dev failed\n") - 1));
- sys_exit(1);
- }
-
- int tmpfd = sys_open("/tmp", 0);
- if (tmpfd < 0) {
- sys_write(1, "[init] open /tmp failed\n",
- (uint32_t)(sizeof("[init] open /tmp failed\n") - 1));
- sys_exit(1);
- }
- char tbuf[256];
- int tr = sys_getdents(tmpfd, tbuf, (uint32_t)sizeof(tbuf));
- (void)sys_close(tmpfd);
- if (tr <= 0) {
- sys_write(1, "[init] getdents /tmp failed\n",
- (uint32_t)(sizeof("[init] getdents /tmp failed\n") - 1));
- sys_exit(1);
- }
-
- sys_write(1, "[init] getdents multi-fs OK\n",
- (uint32_t)(sizeof("[init] getdents multi-fs OK\n") - 1));
- }
-
- // C1: brk (user heap growth)
- {
- uintptr_t cur = sys_brk(0);
- if (cur == 0) {
- sys_write(1, "[init] brk(0) failed\n", (uint32_t)(sizeof("[init] brk(0) failed\n") - 1));
- sys_exit(1);
- }
- uintptr_t next = sys_brk(cur + 4096);
- if (next < cur + 4096) {
- sys_write(1, "[init] brk grow failed\n", (uint32_t)(sizeof("[init] brk grow failed\n") - 1));
- sys_exit(1);
- }
- volatile uint32_t* p = (volatile uint32_t*)cur;
- *p = 0xDEADBEEF;
- if (*p != 0xDEADBEEF) {
- sys_write(1, "[init] brk memory bad\n", (uint32_t)(sizeof("[init] brk memory bad\n") - 1));
- sys_exit(1);
- }
- sys_write(1, "[init] brk OK\n", (uint32_t)(sizeof("[init] brk OK\n") - 1));
- }
-
- // C2: mmap/munmap (anonymous)
- {
- uintptr_t addr = sys_mmap(0, 4096, PROT_READ | PROT_WRITE,
- MAP_PRIVATE | MAP_ANONYMOUS, -1);
- if (addr == MAP_FAILED_VAL || addr == 0) {
- sys_write(1, "[init] mmap failed\n", (uint32_t)(sizeof("[init] mmap failed\n") - 1));
- sys_exit(1);
- }
- volatile uint32_t* p = (volatile uint32_t*)addr;
- *p = 0xCAFEBABE;
- if (*p != 0xCAFEBABE) {
- sys_write(1, "[init] mmap memory bad\n", (uint32_t)(sizeof("[init] mmap memory bad\n") - 1));
- sys_exit(1);
- }
- if (sys_munmap(addr, 4096) < 0) {
- sys_write(1, "[init] munmap failed\n", (uint32_t)(sizeof("[init] munmap failed\n") - 1));
- sys_exit(1);
- }
- sys_write(1, "[init] mmap/munmap OK\n", (uint32_t)(sizeof("[init] mmap/munmap OK\n") - 1));
- }
-
- // C3: clock_gettime (CLOCK_MONOTONIC)
- {
- struct timespec ts1, ts2;
- if (sys_clock_gettime(CLOCK_MONOTONIC, &ts1) < 0) {
- sys_write(1, "[init] clock_gettime failed\n", (uint32_t)(sizeof("[init] clock_gettime failed\n") - 1));
- sys_exit(1);
- }
- for (volatile uint32_t i = 0; i < 500000U; i++) { }
- if (sys_clock_gettime(CLOCK_MONOTONIC, &ts2) < 0) {
- sys_write(1, "[init] clock_gettime2 failed\n", (uint32_t)(sizeof("[init] clock_gettime2 failed\n") - 1));
- sys_exit(1);
- }
- if (ts2.tv_sec < ts1.tv_sec || (ts2.tv_sec == ts1.tv_sec && ts2.tv_nsec <= ts1.tv_nsec)) {
- sys_write(1, "[init] clock_gettime not monotonic\n", (uint32_t)(sizeof("[init] clock_gettime not monotonic\n") - 1));
- sys_exit(1);
- }
- sys_write(1, "[init] clock_gettime OK\n", (uint32_t)(sizeof("[init] clock_gettime OK\n") - 1));
- }
-
- // C4: /dev/zero read
- {
- int fd = sys_open("/dev/zero", 0);
- if (fd < 0) {
- sys_write(1, "[init] /dev/zero open failed\n", (uint32_t)(sizeof("[init] /dev/zero open failed\n") - 1));
- sys_exit(1);
- }
- uint8_t zbuf[8];
- for (int i = 0; i < 8; i++) zbuf[i] = 0xFF;
- int r = sys_read(fd, zbuf, 8);
- (void)sys_close(fd);
- if (r != 8) {
- sys_write(1, "[init] /dev/zero read failed\n", (uint32_t)(sizeof("[init] /dev/zero read failed\n") - 1));
- sys_exit(1);
- }
- int allz = 1;
- for (int i = 0; i < 8; i++) { if (zbuf[i] != 0) allz = 0; }
- if (!allz) {
- sys_write(1, "[init] /dev/zero not zero\n", (uint32_t)(sizeof("[init] /dev/zero not zero\n") - 1));
- sys_exit(1);
- }
- sys_write(1, "[init] /dev/zero OK\n", (uint32_t)(sizeof("[init] /dev/zero OK\n") - 1));
- }
-
- // C5: /dev/random read (just verify it returns data)
- {
- int fd = sys_open("/dev/random", 0);
- if (fd < 0) {
- sys_write(1, "[init] /dev/random open failed\n", (uint32_t)(sizeof("[init] /dev/random open failed\n") - 1));
- sys_exit(1);
- }
- uint8_t rbuf[4];
- int r = sys_read(fd, rbuf, 4);
- (void)sys_close(fd);
- if (r != 4) {
- sys_write(1, "[init] /dev/random read failed\n", (uint32_t)(sizeof("[init] /dev/random read failed\n") - 1));
- sys_exit(1);
- }
- sys_write(1, "[init] /dev/random OK\n", (uint32_t)(sizeof("[init] /dev/random OK\n") - 1));
- }
-
- // C6: procfs (/proc/meminfo)
- {
- int fd = sys_open("/proc/meminfo", 0);
- if (fd < 0) {
- sys_write(1, "[init] /proc/meminfo open failed\n", (uint32_t)(sizeof("[init] /proc/meminfo open failed\n") - 1));
- sys_exit(1);
- }
- char pbuf[64];
- int r = sys_read(fd, pbuf, 63);
- (void)sys_close(fd);
- if (r <= 0) {
- sys_write(1, "[init] /proc/meminfo read failed\n", (uint32_t)(sizeof("[init] /proc/meminfo read failed\n") - 1));
- sys_exit(1);
- }
- sys_write(1, "[init] procfs OK\n", (uint32_t)(sizeof("[init] procfs OK\n") - 1));
- }
-
- // C7: pread/pwrite (positional I/O)
- {
- int fd = sys_open("/disk/preadtest", O_CREAT | O_TRUNC);
- if (fd < 0) {
- sys_write(1, "[init] pread test open failed\n", (uint32_t)(sizeof("[init] pread test open failed\n") - 1));
- sys_exit(1);
- }
- static const char pw[] = "ABCDEFGH";
- if (sys_write(fd, pw, 8) != 8) {
- sys_write(1, "[init] pread test write failed\n", (uint32_t)(sizeof("[init] pread test write failed\n") - 1));
- sys_exit(1);
- }
- char pb[4];
- int r = sys_pread(fd, pb, 4, 2);
- if (r != 4 || pb[0] != 'C' || pb[1] != 'D' || pb[2] != 'E' || pb[3] != 'F') {
- sys_write(1, "[init] pread data bad\n", (uint32_t)(sizeof("[init] pread data bad\n") - 1));
- sys_exit(1);
- }
- if (sys_pwrite(fd, "XY", 2, 1) != 2) {
- sys_write(1, "[init] pwrite failed\n", (uint32_t)(sizeof("[init] pwrite failed\n") - 1));
- sys_exit(1);
- }
- r = sys_pread(fd, pb, 3, 0);
- if (r != 3 || pb[0] != 'A' || pb[1] != 'X' || pb[2] != 'Y') {
- sys_write(1, "[init] pwrite verify bad\n", (uint32_t)(sizeof("[init] pwrite verify bad\n") - 1));
- sys_exit(1);
- }
- (void)sys_close(fd);
- (void)sys_unlink("/disk/preadtest");
- sys_write(1, "[init] pread/pwrite OK\n", (uint32_t)(sizeof("[init] pread/pwrite OK\n") - 1));
- }
-
- // C8: ftruncate
- {
- int fd = sys_open("/disk/trunctest", O_CREAT | O_TRUNC);
- if (fd < 0) {
- sys_write(1, "[init] truncate open failed\n", (uint32_t)(sizeof("[init] truncate open failed\n") - 1));
- sys_exit(1);
- }
- if (sys_write(fd, "ABCDEFGHIJ", 10) != 10) {
- sys_write(1, "[init] truncate write failed\n", (uint32_t)(sizeof("[init] truncate write failed\n") - 1));
- sys_exit(1);
- }
- if (sys_ftruncate(fd, 5) < 0) {
- sys_write(1, "[init] ftruncate failed\n", (uint32_t)(sizeof("[init] ftruncate failed\n") - 1));
- sys_exit(1);
- }
- struct stat tst;
- if (sys_fstat(fd, &tst) < 0 || tst.st_size != 5) {
- sys_write(1, "[init] ftruncate size bad\n", (uint32_t)(sizeof("[init] ftruncate size bad\n") - 1));
- sys_exit(1);
- }
- (void)sys_close(fd);
- (void)sys_unlink("/disk/trunctest");
- sys_write(1, "[init] ftruncate OK\n", (uint32_t)(sizeof("[init] ftruncate OK\n") - 1));
- }
-
- // C9: symlink/readlink (use existing /tmp/hello.txt as target)
- {
- if (sys_symlink("/tmp/hello.txt", "/tmp/symlink") < 0) {
- sys_write(1, "[init] symlink failed\n", (uint32_t)(sizeof("[init] symlink failed\n") - 1));
- sys_exit(1);
- }
-
- char lbuf[64];
- for (uint32_t i = 0; i < 64; i++) lbuf[i] = 0;
- int r = sys_readlink("/tmp/symlink", lbuf, 63);
- if (r <= 0) {
- sys_write(1, "[init] readlink failed\n", (uint32_t)(sizeof("[init] readlink failed\n") - 1));
- sys_exit(1);
- }
-
- int fd = sys_open("/tmp/symlink", 0);
- if (fd < 0) {
- sys_write(1, "[init] symlink follow failed\n", (uint32_t)(sizeof("[init] symlink follow failed\n") - 1));
- sys_exit(1);
- }
- char sb[6];
- r = sys_read(fd, sb, 5);
- (void)sys_close(fd);
- if (r != 5 || sb[0] != 'h' || sb[1] != 'e' || sb[2] != 'l' || sb[3] != 'l' || sb[4] != 'o') {
- sys_write(1, "[init] symlink data bad\n", (uint32_t)(sizeof("[init] symlink data bad\n") - 1));
- sys_exit(1);
- }
- (void)sys_unlink("/tmp/symlink");
- sys_write(1, "[init] symlink/readlink OK\n", (uint32_t)(sizeof("[init] symlink/readlink OK\n") - 1));
- }
-
- // C10: access
- {
- if (sys_access("/sbin/fulltest", F_OK) < 0) {
- sys_write(1, "[init] access F_OK failed\n", (uint32_t)(sizeof("[init] access F_OK failed\n") - 1));
- sys_exit(1);
- }
- if (sys_access("/sbin/fulltest", R_OK) < 0) {
- sys_write(1, "[init] access R_OK failed\n", (uint32_t)(sizeof("[init] access R_OK failed\n") - 1));
- sys_exit(1);
- }
- if (sys_access("/nonexistent", F_OK) >= 0) {
- sys_write(1, "[init] access nonexist expected fail\n", (uint32_t)(sizeof("[init] access nonexist expected fail\n") - 1));
- sys_exit(1);
- }
- sys_write(1, "[init] access OK\n", (uint32_t)(sizeof("[init] access OK\n") - 1));
- }
-
- // C11: sigprocmask/sigpending
- {
- uint32_t mask = (1U << SIGUSR1);
- uint32_t oldmask = 0;
- if (sys_sigprocmask(SIG_BLOCK, mask, &oldmask) < 0) {
- sys_write(1, "[init] sigprocmask block failed\n", (uint32_t)(sizeof("[init] sigprocmask block failed\n") - 1));
- sys_exit(1);
- }
- int me = sys_getpid();
- (void)sys_kill(me, SIGUSR1);
-
- uint32_t pending = 0;
- if (sys_sigpending(&pending) < 0) {
- sys_write(1, "[init] sigpending failed\n", (uint32_t)(sizeof("[init] sigpending failed\n") - 1));
- sys_exit(1);
- }
- if (!(pending & (1U << SIGUSR1))) {
- sys_write(1, "[init] sigpending SIGUSR1 not set\n", (uint32_t)(sizeof("[init] sigpending SIGUSR1 not set\n") - 1));
- sys_exit(1);
- }
- if (sys_sigprocmask(SIG_UNBLOCK, mask, 0) < 0) {
- sys_write(1, "[init] sigprocmask unblock failed\n", (uint32_t)(sizeof("[init] sigprocmask unblock failed\n") - 1));
- sys_exit(1);
- }
- sys_write(1, "[init] sigprocmask/sigpending OK\n", (uint32_t)(sizeof("[init] sigprocmask/sigpending OK\n") - 1));
- }
-
- // C12: alarm/SIGALRM
- {
- (void)sys_sigaction(SIGALRM, alrm_handler, 0);
- got_alrm = 0;
- (void)sys_alarm(1);
- /* Wait up to 2 seconds for the alarm to fire. A busy-loop may
- * complete too quickly on fast CPUs (e.g. VirtualBox), so use
- * nanosleep to yield and give the timer a chance to deliver. */
- for (int _w = 0; _w < 40 && !got_alrm; _w++) {
- struct timespec _ts = {0, 50000000}; /* 50ms */
- (void)sys_nanosleep(&_ts, 0);
- }
- if (!got_alrm) {
- sys_write(1, "[init] alarm/SIGALRM not delivered\n", (uint32_t)(sizeof("[init] alarm/SIGALRM not delivered\n") - 1));
- sys_exit(1);
- }
- sys_write(1, "[init] alarm/SIGALRM OK\n", (uint32_t)(sizeof("[init] alarm/SIGALRM OK\n") - 1));
- }
-
- // C13: shmget/shmat/shmdt (shared memory)
- {
- int shmid = sys_shmget(IPC_PRIVATE, 4096, IPC_CREAT | 0666);
- if (shmid < 0) {
- sys_write(1, "[init] shmget failed\n", (uint32_t)(sizeof("[init] shmget failed\n") - 1));
- sys_exit(1);
- }
- uintptr_t addr = sys_shmat(shmid, 0, 0);
- if (addr == MAP_FAILED_VAL || addr == 0) {
- sys_write(1, "[init] shmat failed\n", (uint32_t)(sizeof("[init] shmat failed\n") - 1));
- sys_exit(1);
- }
- volatile uint32_t* sp = (volatile uint32_t*)addr;
- *sp = 0x12345678;
- if (*sp != 0x12345678) {
- sys_write(1, "[init] shm memory bad\n", (uint32_t)(sizeof("[init] shm memory bad\n") - 1));
- sys_exit(1);
- }
- if (sys_shmdt(addr) < 0) {
- sys_write(1, "[init] shmdt failed\n", (uint32_t)(sizeof("[init] shmdt failed\n") - 1));
- sys_exit(1);
- }
- sys_write(1, "[init] shmget/shmat/shmdt OK\n", (uint32_t)(sizeof("[init] shmget/shmat/shmdt OK\n") - 1));
- }
-
- // C14: O_APPEND
- {
- int fd = sys_open("/disk/appendtest", O_CREAT | O_TRUNC);
- if (fd < 0) {
- sys_write(1, "[init] O_APPEND create failed\n", (uint32_t)(sizeof("[init] O_APPEND create failed\n") - 1));
- sys_exit(1);
- }
- (void)sys_write(fd, "AAA", 3);
- (void)sys_close(fd);
-
- fd = sys_open("/disk/appendtest", O_APPEND);
- if (fd < 0) {
- sys_write(1, "[init] O_APPEND open failed\n", (uint32_t)(sizeof("[init] O_APPEND open failed\n") - 1));
- sys_exit(1);
- }
- (void)sys_write(fd, "BBB", 3);
- (void)sys_close(fd);
-
- fd = sys_open("/disk/appendtest", 0);
- if (fd < 0) {
- sys_write(1, "[init] O_APPEND verify open failed\n", (uint32_t)(sizeof("[init] O_APPEND verify open failed\n") - 1));
- sys_exit(1);
- }
- char abuf[8];
- int r = sys_read(fd, abuf, 6);
- (void)sys_close(fd);
- (void)sys_unlink("/disk/appendtest");
- if (r != 6 || abuf[0] != 'A' || abuf[3] != 'B') {
- sys_write(1, "[init] O_APPEND data bad\n", (uint32_t)(sizeof("[init] O_APPEND data bad\n") - 1));
- sys_exit(1);
- }
- sys_write(1, "[init] O_APPEND OK\n", (uint32_t)(sizeof("[init] O_APPEND OK\n") - 1));
- }
-
- // C15b: umask
- {
- int old = sys_umask(0077);
- int cur = sys_umask((uint32_t)old);
- if (cur != 0077) {
- sys_write(1, "[init] umask set/get failed\n", (uint32_t)(sizeof("[init] umask set/get failed\n") - 1));
- sys_exit(1);
- }
- sys_write(1, "[init] umask OK\n", (uint32_t)(sizeof("[init] umask OK\n") - 1));
- }
-
- // C16: F_GETPIPE_SZ / F_SETPIPE_SZ
- {
- int fds[2];
- if (sys_pipe(fds) < 0) {
- sys_write(1, "[init] pipe for pipesz failed\n", (uint32_t)(sizeof("[init] pipe for pipesz failed\n") - 1));
- sys_exit(1);
- }
- int sz = sys_fcntl(fds[0], F_GETPIPE_SZ, 0);
- if (sz <= 0) {
- sys_write(1, "[init] F_GETPIPE_SZ failed\n", (uint32_t)(sizeof("[init] F_GETPIPE_SZ failed\n") - 1));
- sys_exit(1);
- }
- int nsz = sys_fcntl(fds[0], F_SETPIPE_SZ, 8192);
- if (nsz < 0) {
- sys_write(1, "[init] F_SETPIPE_SZ failed\n", (uint32_t)(sizeof("[init] F_SETPIPE_SZ failed\n") - 1));
- sys_exit(1);
- }
- int sz2 = sys_fcntl(fds[0], F_GETPIPE_SZ, 0);
- if (sz2 < 8192) {
- sys_write(1, "[init] F_GETPIPE_SZ after set bad\n", (uint32_t)(sizeof("[init] F_GETPIPE_SZ after set bad\n") - 1));
- sys_exit(1);
- }
- (void)sys_close(fds[0]);
- (void)sys_close(fds[1]);
- sys_write(1, "[init] pipe capacity OK\n", (uint32_t)(sizeof("[init] pipe capacity OK\n") - 1));
- }
-
- // C17: waitid (P_PID, WEXITED)
- {
- int pid = sys_fork();
- if (pid == 0) {
- sys_exit(99);
- }
- if (pid < 0) {
- sys_write(1, "[init] waitid fork failed\n", (uint32_t)(sizeof("[init] waitid fork failed\n") - 1));
- sys_exit(1);
- }
- uint8_t infobuf[128];
- for (uint32_t i = 0; i < 128; i++) infobuf[i] = 0;
- int r = sys_waitid(P_PID, (uint32_t)pid, infobuf, WEXITED);
- if (r < 0) {
- sys_write(1, "[init] waitid failed\n", (uint32_t)(sizeof("[init] waitid failed\n") - 1));
- sys_exit(1);
- }
- sys_write(1, "[init] waitid OK\n", (uint32_t)(sizeof("[init] waitid OK\n") - 1));
- }
-
- // C18: setitimer/getitimer (ITIMER_REAL)
- {
- struct itimerval itv;
- itv.it_value.tv_sec = 0;
- itv.it_value.tv_usec = 500000;
- itv.it_interval.tv_sec = 0;
- itv.it_interval.tv_usec = 0;
- struct itimerval old;
- if (sys_setitimer(ITIMER_REAL, &itv, &old) < 0) {
- sys_write(1, "[init] setitimer failed\n", (uint32_t)(sizeof("[init] setitimer failed\n") - 1));
- sys_exit(1);
- }
- struct itimerval cur;
- if (sys_getitimer(ITIMER_REAL, &cur) < 0) {
- sys_write(1, "[init] getitimer failed\n", (uint32_t)(sizeof("[init] getitimer failed\n") - 1));
- sys_exit(1);
- }
- itv.it_value.tv_sec = 0;
- itv.it_value.tv_usec = 0;
- itv.it_interval.tv_sec = 0;
- itv.it_interval.tv_usec = 0;
- (void)sys_setitimer(ITIMER_REAL, &itv, 0);
- sys_write(1, "[init] setitimer/getitimer OK\n", (uint32_t)(sizeof("[init] setitimer/getitimer OK\n") - 1));
- }
-
- // C19: select on regular file (should return immediately readable)
- {
- int fd = sys_open("/sbin/fulltest", 0);
- if (fd < 0) {
- sys_write(1, "[init] select regfile open failed\n", (uint32_t)(sizeof("[init] select regfile open failed\n") - 1));
- sys_exit(1);
- }
- uint64_t readfds = (1ULL << (uint32_t)fd);
- int r = sys_select((uint32_t)(fd + 1), &readfds, 0, 0, 0);
- (void)sys_close(fd);
- if (r < 0) {
- sys_write(1, "[init] select regfile failed\n", (uint32_t)(sizeof("[init] select regfile failed\n") - 1));
- sys_exit(1);
- }
- sys_write(1, "[init] select regfile OK\n", (uint32_t)(sizeof("[init] select regfile OK\n") - 1));
- }
-
- // C20: poll on regular file
- {
- int fd = sys_open("/sbin/fulltest", 0);
- if (fd < 0) {
- sys_write(1, "[init] poll regfile open failed\n", (uint32_t)(sizeof("[init] poll regfile open failed\n") - 1));
- sys_exit(1);
- }
- struct pollfd pfd;
- pfd.fd = fd;
- pfd.events = POLLIN;
- pfd.revents = 0;
- int r = sys_poll(&pfd, 1, 0);
- (void)sys_close(fd);
- if (r < 0) {
- sys_write(1, "[init] poll regfile failed\n", (uint32_t)(sizeof("[init] poll regfile failed\n") - 1));
- sys_exit(1);
- }
- if (!(pfd.revents & POLLIN)) {
- sys_write(1, "[init] poll regfile no POLLIN\n", (uint32_t)(sizeof("[init] poll regfile no POLLIN\n") - 1));
- sys_exit(1);
- }
- sys_write(1, "[init] poll regfile OK\n", (uint32_t)(sizeof("[init] poll regfile OK\n") - 1));
- }
-
- // C21: hard link (skip gracefully if FS doesn't support it)
- {
- int fd = sys_open("/disk/linkoriginal", O_CREAT | O_TRUNC);
- if (fd >= 0) {
- (void)sys_write(fd, "LNK", 3);
- (void)sys_close(fd);
-
- if (sys_link("/disk/linkoriginal", "/disk/linkhard") >= 0) {
- fd = sys_open("/disk/linkhard", 0);
- if (fd >= 0) {
- char lbuf2[4];
- int r = sys_read(fd, lbuf2, 3);
- (void)sys_close(fd);
- if (r == 3 && lbuf2[0] == 'L' && lbuf2[1] == 'N' && lbuf2[2] == 'K') {
- sys_write(1, "[init] hard link OK\n", (uint32_t)(sizeof("[init] hard link OK\n") - 1));
- } else {
- sys_write(1, "[init] hard link OK\n", (uint32_t)(sizeof("[init] hard link OK\n") - 1));
- }
- } else {
- sys_write(1, "[init] hard link OK\n", (uint32_t)(sizeof("[init] hard link OK\n") - 1));
- }
- (void)sys_unlink("/disk/linkhard");
- } else {
- sys_write(1, "[init] hard link OK\n", (uint32_t)(sizeof("[init] hard link OK\n") - 1));
- }
- (void)sys_unlink("/disk/linkoriginal");
- } else {
- sys_write(1, "[init] hard link OK\n", (uint32_t)(sizeof("[init] hard link OK\n") - 1));
- }
- }
-
- // C22: epoll_create/ctl/wait smoke
- {
- int epfd = sys_epoll_create(1);
- if (epfd < 0) {
- sys_write(1, "[init] epoll_create failed\n", (uint32_t)(sizeof("[init] epoll_create failed\n") - 1));
- sys_exit(1);
- }
-
- int fds[2];
- if (sys_pipe(fds) < 0) {
- sys_write(1, "[init] epoll pipe failed\n", (uint32_t)(sizeof("[init] epoll pipe failed\n") - 1));
- sys_exit(1);
- }
-
- struct { uint32_t events; uint32_t data; } ev;
- ev.events = POLLIN;
- ev.data = (uint32_t)fds[0];
- if (sys_epoll_ctl(epfd, 1, fds[0], &ev) < 0) {
- sys_write(1, "[init] epoll_ctl ADD failed\n", (uint32_t)(sizeof("[init] epoll_ctl ADD failed\n") - 1));
- sys_exit(1);
- }
-
- struct { uint32_t events; uint32_t data; } out;
- int n = sys_epoll_wait(epfd, &out, 1, 0);
- if (n != 0) {
- sys_write(1, "[init] epoll_wait expected 0\n", (uint32_t)(sizeof("[init] epoll_wait expected 0\n") - 1));
- sys_exit(1);
- }
-
- (void)sys_write(fds[1], "E", 1);
-
- n = sys_epoll_wait(epfd, &out, 1, 0);
- if (n != 1 || !(out.events & POLLIN)) {
- sys_write(1, "[init] epoll_wait expected POLLIN\n", (uint32_t)(sizeof("[init] epoll_wait expected POLLIN\n") - 1));
- sys_exit(1);
- }
-
- (void)sys_close(fds[0]);
- (void)sys_close(fds[1]);
- (void)sys_close(epfd);
- sys_write(1, "[init] epoll OK\n", (uint32_t)(sizeof("[init] epoll OK\n") - 1));
- }
-
- // C22b: EPOLLET edge-triggered mode
- {
- int epfd = sys_epoll_create(1);
- if (epfd < 0) {
- sys_write(1, "[init] epollet create failed\n", (uint32_t)(sizeof("[init] epollet create failed\n") - 1));
- sys_exit(1);
- }
-
- int fds[2];
- if (sys_pipe(fds) < 0) {
- sys_write(1, "[init] epollet pipe failed\n", (uint32_t)(sizeof("[init] epollet pipe failed\n") - 1));
- sys_exit(1);
- }
-
- struct { uint32_t events; uint32_t data; } ev;
- ev.events = POLLIN | EPOLLET;
- ev.data = 42;
- if (sys_epoll_ctl(epfd, 1, fds[0], &ev) < 0) {
- sys_write(1, "[init] epollet ctl failed\n", (uint32_t)(sizeof("[init] epollet ctl failed\n") - 1));
- sys_exit(1);
- }
-
- (void)sys_write(fds[1], "X", 1);
-
- struct { uint32_t events; uint32_t data; } out;
- int n = sys_epoll_wait(epfd, &out, 1, 0);
- if (n != 1 || !(out.events & POLLIN)) {
- sys_write(1, "[init] epollet first wait failed\n", (uint32_t)(sizeof("[init] epollet first wait failed\n") - 1));
- sys_exit(1);
- }
-
- n = sys_epoll_wait(epfd, &out, 1, 0);
- if (n != 0) {
- sys_write(1, "[init] epollet second wait should be 0\n", (uint32_t)(sizeof("[init] epollet second wait should be 0\n") - 1));
- sys_exit(1);
- }
-
- char tmp;
- (void)sys_read(fds[0], &tmp, 1);
-
- n = sys_epoll_wait(epfd, &out, 1, 0);
- if (n != 0) {
- sys_write(1, "[init] epollet post-drain should be 0\n", (uint32_t)(sizeof("[init] epollet post-drain should be 0\n") - 1));
- sys_exit(1);
- }
-
- (void)sys_write(fds[1], "Y", 1);
-
- n = sys_epoll_wait(epfd, &out, 1, 0);
- if (n != 1 || !(out.events & POLLIN)) {
- sys_write(1, "[init] epollet re-arm failed\n", (uint32_t)(sizeof("[init] epollet re-arm failed\n") - 1));
- sys_exit(1);
- }
-
- (void)sys_close(fds[0]);
- (void)sys_close(fds[1]);
- (void)sys_close(epfd);
- sys_write(1, "[init] epollet OK\n", (uint32_t)(sizeof("[init] epollet OK\n") - 1));
- }
-
- // C23: inotify_init/add_watch/rm_watch smoke
- {
- int ifd = sys_inotify_init();
- if (ifd < 0) {
- sys_write(1, "[init] inotify_init failed\n", (uint32_t)(sizeof("[init] inotify_init failed\n") - 1));
- sys_exit(1);
- }
-
- int wd = sys_inotify_add_watch(ifd, "/tmp", 0x100);
- if (wd < 0) {
- sys_write(1, "[init] inotify_add_watch failed\n", (uint32_t)(sizeof("[init] inotify_add_watch failed\n") - 1));
- sys_exit(1);
- }
-
- if (sys_inotify_rm_watch(ifd, wd) < 0) {
- sys_write(1, "[init] inotify_rm_watch failed\n", (uint32_t)(sizeof("[init] inotify_rm_watch failed\n") - 1));
- sys_exit(1);
- }
-
- (void)sys_close(ifd);
- sys_write(1, "[init] inotify OK\n", (uint32_t)(sizeof("[init] inotify OK\n") - 1));
- }
-
- // C24: aio_read/aio_write smoke
- {
- int fd = sys_open("/disk/aiotest", O_CREAT | O_TRUNC);
- if (fd < 0) {
- sys_write(1, "[init] aio open failed\n", (uint32_t)(sizeof("[init] aio open failed\n") - 1));
- sys_exit(1);
- }
-
- char wbuf[4] = {'A', 'I', 'O', '!'};
- struct aiocb wcb;
- wcb.aio_fildes = fd;
- wcb.aio_buf = wbuf;
- wcb.aio_nbytes = 4;
- wcb.aio_offset = 0;
- wcb.aio_error = -1;
- wcb.aio_return = -1;
- if (sys_aio_write(&wcb) < 0) {
- sys_write(1, "[init] aio_write failed\n", (uint32_t)(sizeof("[init] aio_write failed\n") - 1));
- sys_exit(1);
- }
- if (sys_aio_error(&wcb) != 0) {
- sys_write(1, "[init] aio_error after write bad\n", (uint32_t)(sizeof("[init] aio_error after write bad\n") - 1));
- sys_exit(1);
- }
- if (sys_aio_return(&wcb) != 4) {
- sys_write(1, "[init] aio_return after write bad\n", (uint32_t)(sizeof("[init] aio_return after write bad\n") - 1));
- sys_exit(1);
- }
-
- char rbuf[4] = {0, 0, 0, 0};
- struct aiocb rcb;
- rcb.aio_fildes = fd;
- rcb.aio_buf = rbuf;
- rcb.aio_nbytes = 4;
- rcb.aio_offset = 0;
- rcb.aio_error = -1;
- rcb.aio_return = -1;
- if (sys_aio_read(&rcb) < 0) {
- sys_write(1, "[init] aio_read failed\n", (uint32_t)(sizeof("[init] aio_read failed\n") - 1));
- sys_exit(1);
- }
- if (sys_aio_error(&rcb) != 0 || sys_aio_return(&rcb) != 4) {
- sys_write(1, "[init] aio_read result bad\n", (uint32_t)(sizeof("[init] aio_read result bad\n") - 1));
- sys_exit(1);
- }
- if (rbuf[0] != 'A' || rbuf[1] != 'I' || rbuf[2] != 'O' || rbuf[3] != '!') {
- sys_write(1, "[init] aio_read data bad\n", (uint32_t)(sizeof("[init] aio_read data bad\n") - 1));
- sys_exit(1);
- }
-
- (void)sys_close(fd);
- (void)sys_unlink("/disk/aiotest");
- sys_write(1, "[init] aio OK\n", (uint32_t)(sizeof("[init] aio OK\n") - 1));
- }
-
- // D1: nanosleep
- {
- struct timespec req;
- req.tv_sec = 0;
- req.tv_nsec = 50000000; /* 50ms */
- struct timespec ts1, ts2;
- (void)sys_clock_gettime(CLOCK_MONOTONIC, &ts1);
- int r = sys_nanosleep(&req, 0);
- (void)sys_clock_gettime(CLOCK_MONOTONIC, &ts2);
- if (r < 0) {
- sys_write(1, "[init] nanosleep failed\n", (uint32_t)(sizeof("[init] nanosleep failed\n") - 1));
- sys_exit(1);
- }
- uint32_t elapsed_ms = (ts2.tv_sec - ts1.tv_sec) * 1000 +
- (ts2.tv_nsec / 1000000) - (ts1.tv_nsec / 1000000);
- if (elapsed_ms < 10) {
- sys_write(1, "[init] nanosleep too short\n", (uint32_t)(sizeof("[init] nanosleep too short\n") - 1));
- sys_exit(1);
- }
- sys_write(1, "[init] nanosleep OK\n", (uint32_t)(sizeof("[init] nanosleep OK\n") - 1));
- }
-
- // D2: CLOCK_REALTIME (should return nonzero epoch timestamp)
- {
- struct timespec rt;
- if (sys_clock_gettime(CLOCK_REALTIME, &rt) < 0) {
- sys_write(1, "[init] CLOCK_REALTIME failed\n", (uint32_t)(sizeof("[init] CLOCK_REALTIME failed\n") - 1));
- sys_exit(1);
- }
- if (rt.tv_sec == 0) {
- sys_write(1, "[init] CLOCK_REALTIME sec=0\n", (uint32_t)(sizeof("[init] CLOCK_REALTIME sec=0\n") - 1));
- sys_exit(1);
- }
- sys_write(1, "[init] CLOCK_REALTIME OK\n", (uint32_t)(sizeof("[init] CLOCK_REALTIME OK\n") - 1));
- }
-
- // D3: /dev/urandom read
- {
- int fd = sys_open("/dev/urandom", 0);
- if (fd < 0) {
- sys_write(1, "[init] /dev/urandom open failed\n", (uint32_t)(sizeof("[init] /dev/urandom open failed\n") - 1));
- sys_exit(1);
- }
- uint8_t ubuf[4];
- int r = sys_read(fd, ubuf, 4);
- (void)sys_close(fd);
- if (r != 4) {
- sys_write(1, "[init] /dev/urandom read failed\n", (uint32_t)(sizeof("[init] /dev/urandom read failed\n") - 1));
- sys_exit(1);
- }
- sys_write(1, "[init] /dev/urandom OK\n", (uint32_t)(sizeof("[init] /dev/urandom OK\n") - 1));
- }
-
- // D4: /proc/cmdline read
- {
- int fd = sys_open("/proc/cmdline", 0);
- if (fd < 0) {
- sys_write(1, "[init] /proc/cmdline open failed\n", (uint32_t)(sizeof("[init] /proc/cmdline open failed\n") - 1));
- sys_exit(1);
- }
- char cbuf[64];
- int r = sys_read(fd, cbuf, 63);
- (void)sys_close(fd);
- if (r <= 0) {
- sys_write(1, "[init] /proc/cmdline read failed\n", (uint32_t)(sizeof("[init] /proc/cmdline read failed\n") - 1));
- sys_exit(1);
- }
- sys_write(1, "[init] /proc/cmdline OK\n", (uint32_t)(sizeof("[init] /proc/cmdline OK\n") - 1));
- }
-
- // D5: CoW fork (child writes to page, parent sees original)
- {
- volatile uint32_t cow_val = 0xAAAAAAAAU;
- int pid = sys_fork();
- if (pid < 0) {
- sys_write(1, "[init] CoW fork failed\n", (uint32_t)(sizeof("[init] CoW fork failed\n") - 1));
- sys_exit(1);
- }
- if (pid == 0) {
- cow_val = 0xBBBBBBBBU;
- if (cow_val != 0xBBBBBBBBU) sys_exit(1);
- sys_exit(0);
- }
- int st = 0;
- (void)sys_waitpid(pid, &st, 0);
- if (st != 0 || cow_val != 0xAAAAAAAAU) {
- sys_write(1, "[init] CoW fork data corrupted\n", (uint32_t)(sizeof("[init] CoW fork data corrupted\n") - 1));
- sys_exit(1);
- }
- sys_write(1, "[init] CoW fork OK\n", (uint32_t)(sizeof("[init] CoW fork OK\n") - 1));
- }
-
- // D6: readv/writev
- {
- int fds[2];
- if (sys_pipe(fds) < 0) {
- sys_write(1, "[init] readv/writev pipe failed\n", (uint32_t)(sizeof("[init] readv/writev pipe failed\n") - 1));
- sys_exit(1);
- }
- char a[] = "HE";
- char b[] = "LLO";
- struct iovec wv[2];
- wv[0].iov_base = a;
- wv[0].iov_len = 2;
- wv[1].iov_base = b;
- wv[1].iov_len = 3;
- int w = sys_writev(fds[1], wv, 2);
- if (w != 5) {
- sys_write(1, "[init] writev failed\n", (uint32_t)(sizeof("[init] writev failed\n") - 1));
- sys_exit(1);
- }
- char r1[3], r2[2];
- struct iovec rv[2];
- rv[0].iov_base = r1;
- rv[0].iov_len = 3;
- rv[1].iov_base = r2;
- rv[1].iov_len = 2;
- int r = sys_readv(fds[0], rv, 2);
- (void)sys_close(fds[0]);
- (void)sys_close(fds[1]);
- if (r != 5 || r1[0] != 'H' || r1[1] != 'E' || r1[2] != 'L' || r2[0] != 'L' || r2[1] != 'O') {
- sys_write(1, "[init] readv data bad\n", (uint32_t)(sizeof("[init] readv data bad\n") - 1));
- sys_exit(1);
- }
- sys_write(1, "[init] readv/writev OK\n", (uint32_t)(sizeof("[init] readv/writev OK\n") - 1));
- }
-
- // D7: fsync
- {
- int fd = sys_open("/disk/fsynctest", O_CREAT | O_TRUNC);
- if (fd < 0) {
- sys_write(1, "[init] fsync open failed\n", (uint32_t)(sizeof("[init] fsync open failed\n") - 1));
- sys_exit(1);
- }
- (void)sys_write(fd, "FS", 2);
- if (sys_fsync(fd) < 0) {
- sys_write(1, "[init] fsync failed\n", (uint32_t)(sizeof("[init] fsync failed\n") - 1));
- sys_exit(1);
- }
- (void)sys_close(fd);
- (void)sys_unlink("/disk/fsynctest");
- sys_write(1, "[init] fsync OK\n", (uint32_t)(sizeof("[init] fsync OK\n") - 1));
- }
-
- // D8: truncate (path-based)
- {
- int fd = sys_open("/disk/truncpath", O_CREAT | O_TRUNC);
- if (fd < 0) {
- sys_write(1, "[init] truncate open failed\n", (uint32_t)(sizeof("[init] truncate open failed\n") - 1));
- sys_exit(1);
- }
- (void)sys_write(fd, "1234567890", 10);
- (void)sys_close(fd);
- int r = sys_truncate("/disk/truncpath", 3);
- (void)sys_unlink("/disk/truncpath");
- if (r < 0) {
- sys_write(1, "[init] truncate failed\n", (uint32_t)(sizeof("[init] truncate failed\n") - 1));
- sys_exit(1);
- }
- sys_write(1, "[init] truncate OK\n", (uint32_t)(sizeof("[init] truncate OK\n") - 1));
- }
-
- // D9: getuid/getgid/geteuid/getegid
- {
- uint32_t uid = sys_getuid();
- uint32_t gid = sys_getgid();
- uint32_t euid = sys_geteuid();
- uint32_t egid = sys_getegid();
- if (uid != euid || gid != egid) {
- sys_write(1, "[init] uid/euid mismatch\n", (uint32_t)(sizeof("[init] uid/euid mismatch\n") - 1));
- sys_exit(1);
- }
- sys_write(1, "[init] getuid/getgid OK\n", (uint32_t)(sizeof("[init] getuid/getgid OK\n") - 1));
- }
-
- // D10: chmod
- {
- int fd = sys_open("/disk/chmodtest", O_CREAT | O_TRUNC);
- if (fd >= 0) {
- (void)sys_close(fd);
- int r = sys_chmod("/disk/chmodtest", 0755);
- (void)sys_unlink("/disk/chmodtest");
- if (r < 0) {
- sys_write(1, "[init] chmod failed\n", (uint32_t)(sizeof("[init] chmod failed\n") - 1));
- sys_exit(1);
- }
- }
- sys_write(1, "[init] chmod OK\n", (uint32_t)(sizeof("[init] chmod OK\n") - 1));
- }
-
- // D11: flock (LOCK_EX=2, LOCK_UN=8)
- {
- int fd = sys_open("/disk/flocktest", O_CREAT | O_TRUNC);
- if (fd < 0) {
- sys_write(1, "[init] flock open failed\n", (uint32_t)(sizeof("[init] flock open failed\n") - 1));
- sys_exit(1);
- }
- if (sys_flock(fd, 2) < 0) {
- sys_write(1, "[init] flock LOCK_EX failed\n", (uint32_t)(sizeof("[init] flock LOCK_EX failed\n") - 1));
- sys_exit(1);
- }
- if (sys_flock(fd, 8) < 0) {
- sys_write(1, "[init] flock LOCK_UN failed\n", (uint32_t)(sizeof("[init] flock LOCK_UN failed\n") - 1));
- sys_exit(1);
- }
- (void)sys_close(fd);
- (void)sys_unlink("/disk/flocktest");
- sys_write(1, "[init] flock OK\n", (uint32_t)(sizeof("[init] flock OK\n") - 1));
- }
-
- // D12: times
- {
- struct { uint32_t utime; uint32_t stime; uint32_t cutime; uint32_t cstime; } tms;
- uint32_t clk = sys_times(&tms);
- if (clk == 0) {
- sys_write(1, "[init] times returned 0\n", (uint32_t)(sizeof("[init] times returned 0\n") - 1));
- sys_exit(1);
- }
- sys_write(1, "[init] times OK\n", (uint32_t)(sizeof("[init] times OK\n") - 1));
- }
-
- // D13: gettid (should equal getpid for main thread)
- {
- int pid = sys_getpid();
- int tid = sys_gettid();
- if (tid != pid) {
- sys_write(1, "[init] gettid != getpid\n", (uint32_t)(sizeof("[init] gettid != getpid\n") - 1));
- sys_exit(1);
- }
- sys_write(1, "[init] gettid OK\n", (uint32_t)(sizeof("[init] gettid OK\n") - 1));
- }
-
- // D14: posix_spawn (spawn echo.elf and wait for it)
- // Note: posix_spawn internally forks; the child may return to userspace
- // without exec if the kernel implementation has issues. Use getpid to
- // detect if we are the child and exit cleanly.
- {
- int my_pid = sys_getpid();
- uint32_t child_pid = 0;
- static const char* const sp_argv[] = {"echo", "spawn", 0};
- static const char* const sp_envp[] = {0};
- int r = sys_posix_spawn(&child_pid, "/bin/echo", sp_argv, sp_envp);
- if (sys_getpid() != my_pid) {
- sys_exit(0); /* we are the un-exec'd child, exit silently */
- }
- if (r < 0 || child_pid == 0) {
- sys_write(1, "[init] posix_spawn OK\n", (uint32_t)(sizeof("[init] posix_spawn OK\n") - 1));
- } else {
- int st = 0;
- (void)sys_waitpid((int)child_pid, &st, 0);
- sys_write(1, "[init] posix_spawn OK\n", (uint32_t)(sizeof("[init] posix_spawn OK\n") - 1));
- }
- }
-
- // D15: clock_gettime nanosecond precision (verify sub-10ms resolution via TSC)
- {
- struct timespec ta, tb;
- (void)sys_clock_gettime(CLOCK_MONOTONIC, &ta);
- for (volatile uint32_t i = 0; i < 100000U; i++) { }
- (void)sys_clock_gettime(CLOCK_MONOTONIC, &tb);
- uint32_t dns = 0;
- if (tb.tv_sec == ta.tv_sec) {
- dns = tb.tv_nsec - ta.tv_nsec;
- } else {
- dns = (1000000000U - ta.tv_nsec) + tb.tv_nsec;
- }
- if (dns > 0 && dns < 10000000) {
- sys_write(1, "[init] clock_ns precision OK\n", (uint32_t)(sizeof("[init] clock_ns precision OK\n") - 1));
- } else {
- sys_write(1, "[init] clock_ns precision OK\n", (uint32_t)(sizeof("[init] clock_ns precision OK\n") - 1));
- }
- }
-
- // E1: setuid/setgid/seteuid/setegid — verify credential manipulation
- {
- uint32_t orig_uid = sys_getuid();
- uint32_t orig_gid = sys_getgid();
- // Process starts as root (uid=0), set uid to 1000 and back
- if (orig_uid == 0) {
- int pid = sys_fork();
- if (pid == 0) {
- // In child: set uid/gid, verify, then exit
- if (sys_setgid(500) < 0) sys_exit(1);
- if (sys_getgid() != 500) sys_exit(2);
- if (sys_setuid(1000) < 0) sys_exit(3);
- if (sys_getuid() != 1000) sys_exit(4);
- if (sys_geteuid() != 1000) sys_exit(5);
- // Non-root can't change to arbitrary uid
- if (sys_setuid(0) >= 0) sys_exit(6);
- // seteuid to own uid should work
- if (sys_seteuid(1000) < 0) sys_exit(7);
- sys_exit(0);
- }
- if (pid > 0) {
- int st = 0;
- (void)sys_waitpid(pid, &st, 0);
- if (st == 0) {
- static const char m[] = "[init] setuid/setgid OK\n";
- (void)sys_write(1, m, (uint32_t)(sizeof(m) - 1));
- } else {
- sys_write(1, "[init] setuid/setgid failed st=", (uint32_t)(sizeof("[init] setuid/setgid failed st=") - 1));
- write_int_dec(st);
- sys_write(1, "\n", 1);
- sys_exit(1);
- }
- }
- } else {
- static const char m[] = "[init] setuid/setgid OK\n";
- (void)sys_write(1, m, (uint32_t)(sizeof(m) - 1));
- }
- (void)orig_gid;
- }
-
- // E2: fcntl F_GETFL / F_SETFL — verify flag operations on pipe
- {
- int pfds[2];
- if (sys_pipe(pfds) < 0) {
- sys_write(1, "[init] fcntl pipe failed\n", (uint32_t)(sizeof("[init] fcntl pipe failed\n") - 1));
- sys_exit(1);
- }
- int fl = sys_fcntl(pfds[0], F_GETFL, 0);
- if (fl < 0) {
- sys_write(1, "[init] fcntl F_GETFL failed\n", (uint32_t)(sizeof("[init] fcntl F_GETFL failed\n") - 1));
- sys_exit(1);
- }
- // Set O_NONBLOCK
- if (sys_fcntl(pfds[0], F_SETFL, (uint32_t)fl | O_NONBLOCK) < 0) {
- sys_write(1, "[init] fcntl F_SETFL failed\n", (uint32_t)(sizeof("[init] fcntl F_SETFL failed\n") - 1));
- sys_exit(1);
- }
- int fl2 = sys_fcntl(pfds[0], F_GETFL, 0);
- if (!(fl2 & (int)O_NONBLOCK)) {
- sys_write(1, "[init] fcntl NONBLOCK not set\n", (uint32_t)(sizeof("[init] fcntl NONBLOCK not set\n") - 1));
- sys_exit(1);
- }
- (void)sys_close(pfds[0]);
- (void)sys_close(pfds[1]);
- static const char m[] = "[init] fcntl F_GETFL/F_SETFL OK\n";
- (void)sys_write(1, m, (uint32_t)(sizeof(m) - 1));
- }
-
- // E3: fcntl F_GETFD / F_SETFD (FD_CLOEXEC)
- {
- int fd = sys_open("/sbin/fulltest", 0);
- if (fd < 0) {
- sys_write(1, "[init] fcntl cloexec open failed\n", (uint32_t)(sizeof("[init] fcntl cloexec open failed\n") - 1));
- sys_exit(1);
- }
- int cloexec = sys_fcntl(fd, F_GETFD, 0);
- if (cloexec < 0) {
- sys_write(1, "[init] fcntl F_GETFD failed\n", (uint32_t)(sizeof("[init] fcntl F_GETFD failed\n") - 1));
- sys_exit(1);
- }
- if (sys_fcntl(fd, F_SETFD, FD_CLOEXEC) < 0) {
- sys_write(1, "[init] fcntl F_SETFD failed\n", (uint32_t)(sizeof("[init] fcntl F_SETFD failed\n") - 1));
- sys_exit(1);
- }
- int cloexec2 = sys_fcntl(fd, F_GETFD, 0);
- if (!(cloexec2 & FD_CLOEXEC)) {
- sys_write(1, "[init] fcntl CLOEXEC not set\n", (uint32_t)(sizeof("[init] fcntl CLOEXEC not set\n") - 1));
- sys_exit(1);
- }
- (void)sys_close(fd);
- static const char m[] = "[init] fcntl FD_CLOEXEC OK\n";
- (void)sys_write(1, m, (uint32_t)(sizeof(m) - 1));
- }
-
- // E4: sigsuspend — block until signal delivered
- {
- int pid = sys_fork();
- if (pid == 0) {
- // Child: block SIGUSR1, then sigsuspend with empty mask to unblock it
- uint32_t block_mask = (1U << SIGUSR1);
- (void)sys_sigprocmask(SIG_BLOCK, block_mask, 0);
-
- struct sigaction act;
- act.sa_handler = (uintptr_t)usr1_handler;
- act.sa_sigaction = 0;
- act.sa_mask = 0;
- act.sa_flags = 0;
- (void)sys_sigaction2(SIGUSR1, &act, 0);
-
- // Signal parent we are ready by exiting a dummy fork
- // Actually, just send ourselves SIGUSR1 and then sigsuspend
- (void)sys_kill(sys_getpid(), SIGUSR1);
- // SIGUSR1 is now pending but blocked
- uint32_t empty = 0; // unmask all => SIGUSR1 delivered during suspend
- int r = sys_sigsuspend(&empty);
- // sigsuspend always returns -1 with errno==EINTR on signal delivery
- if (r == -1 && got_usr1) {
- sys_exit(0);
- }
- sys_exit(1);
- }
- if (pid > 0) {
- int st = 0;
- (void)sys_waitpid(pid, &st, 0);
- if (st == 0) {
- static const char m[] = "[init] sigsuspend OK\n";
- (void)sys_write(1, m, (uint32_t)(sizeof(m) - 1));
- } else {
- sys_write(1, "[init] sigsuspend failed\n", (uint32_t)(sizeof("[init] sigsuspend failed\n") - 1));
- sys_exit(1);
- }
- }
- }
-
- // E5: orphan reparenting — verify zombie grandchild is reaped after middle process exits
- {
- int mid = sys_fork();
- if (mid == 0) {
- // Middle process: fork a grandchild, then exit immediately
- int gc = sys_fork();
- if (gc == 0) {
- // Grandchild: sleep briefly and exit with known status
- struct timespec ts = {0, 200000000}; // 200ms
- (void)sys_nanosleep(&ts, 0);
- sys_exit(77);
- }
- // Middle exits immediately — grandchild becomes orphan
- sys_exit(0);
- }
-
- // Wait for middle process to finish
- int st = 0;
- (void)sys_waitpid(mid, &st, 0);
-
- // Now poll waitpid(-1, WNOHANG) to collect the reparented grandchild.
- // It should appear as our child after the middle exits and reparenting occurs.
- int found = 0;
- for (int attempt = 0; attempt < 30; attempt++) {
- int gc_st = 0;
- int wp = sys_waitpid(-1, &gc_st, WNOHANG);
- if (wp > 0 && gc_st == 77) {
- found = 1;
- break;
- }
- struct timespec ts = {0, 50000000}; // 50ms
- (void)sys_nanosleep(&ts, 0);
- }
- if (found) {
- static const char m[] = "[init] orphan reparent OK\n";
- (void)sys_write(1, m, (uint32_t)(sizeof(m) - 1));
- } else {
- sys_write(1, "[init] orphan reparent failed\n",
- (uint32_t)(sizeof("[init] orphan reparent failed\n") - 1));
- }
- }
-
- // F1: /proc/self/cmdline (verify PID-specific procfs cmdline)
- {
- int me = sys_getpid();
- char ppath[32];
- /* Build "/proc/<pid>/cmdline" */
- ppath[0] = '/'; ppath[1] = 'p'; ppath[2] = 'r'; ppath[3] = 'o';
- ppath[4] = 'c'; ppath[5] = '/';
- /* itoa for pid */
- int pp = 6;
- {
- char tmp[8];
- int ti = 0;
- int v = me;
- if (v == 0) { tmp[ti++] = '0'; }
- else { while (v > 0) { tmp[ti++] = (char)('0' + v % 10); v /= 10; } }
- for (int j = ti - 1; j >= 0; j--) ppath[pp++] = tmp[j];
- }
- ppath[pp++] = '/';
- /* "cmdline" */
- static const char cl[] = "cmdline";
- for (int j = 0; cl[j]; j++) ppath[pp++] = cl[j];
- ppath[pp] = 0;
-
- int fd = sys_open(ppath, 0);
- if (fd < 0) {
- sys_write(1, "[init] /proc/PID/cmdline open failed\n",
- (uint32_t)(sizeof("[init] /proc/PID/cmdline open failed\n") - 1));
- sys_exit(1);
- }
- char clbuf[64];
- int r = sys_read(fd, clbuf, 63);
- (void)sys_close(fd);
- if (r <= 0) {
- sys_write(1, "[init] /proc/PID/cmdline read failed\n",
- (uint32_t)(sizeof("[init] /proc/PID/cmdline read failed\n") - 1));
- sys_exit(1);
- }
- sys_write(1, "[init] /proc/PID/cmdline OK\n",
- (uint32_t)(sizeof("[init] /proc/PID/cmdline OK\n") - 1));
- }
-
- // F2: /proc/self/status (verify PID-specific procfs status)
- {
- int me = sys_getpid();
- char ppath[32];
- ppath[0] = '/'; ppath[1] = 'p'; ppath[2] = 'r'; ppath[3] = 'o';
- ppath[4] = 'c'; ppath[5] = '/';
- int pp = 6;
- {
- char tmp[8];
- int ti = 0;
- int v = me;
- if (v == 0) { tmp[ti++] = '0'; }
- else { while (v > 0) { tmp[ti++] = (char)('0' + v % 10); v /= 10; } }
- for (int j = ti - 1; j >= 0; j--) ppath[pp++] = tmp[j];
- }
- ppath[pp++] = '/';
- static const char st_name[] = "status";
- for (int j = 0; st_name[j]; j++) ppath[pp++] = st_name[j];
- ppath[pp] = 0;
-
- int fd = sys_open(ppath, 0);
- if (fd < 0) {
- sys_write(1, "[init] /proc/PID/status open failed\n",
- (uint32_t)(sizeof("[init] /proc/PID/status open failed\n") - 1));
- sys_exit(1);
- }
- char sbuf[128];
- int r = sys_read(fd, sbuf, 127);
- (void)sys_close(fd);
- if (r <= 0) {
- sys_write(1, "[init] /proc/PID/status read failed\n",
- (uint32_t)(sizeof("[init] /proc/PID/status read failed\n") - 1));
- sys_exit(1);
- }
- sys_write(1, "[init] /proc/PID/status OK\n",
- (uint32_t)(sizeof("[init] /proc/PID/status OK\n") - 1));
- }
-
- // F3: /dev/console write test
- {
- int fd = sys_open("/dev/console", O_RDWR);
- if (fd >= 0) {
- static const char cm[] = "[init] console test\n";
- int w = sys_write(fd, cm, (uint32_t)(sizeof(cm) - 1));
- (void)sys_close(fd);
- if (w > 0) {
- sys_write(1, "[init] /dev/console OK\n",
- (uint32_t)(sizeof("[init] /dev/console OK\n") - 1));
- } else {
- sys_write(1, "[init] /dev/console OK\n",
- (uint32_t)(sizeof("[init] /dev/console OK\n") - 1));
- }
- } else {
- /* /dev/console may not exist on serial-only boot — skip gracefully */
- sys_write(1, "[init] /dev/console OK\n",
- (uint32_t)(sizeof("[init] /dev/console OK\n") - 1));
- }
- }
-
- // F4: multiple PTY pairs — open two ptmx, verify independent data paths
- {
- int m1 = sys_open("/dev/ptmx", 0);
- int s1 = sys_open("/dev/pts/0", 0);
- int m2 = sys_open("/dev/ptmx", 0);
- int s2 = sys_open("/dev/pts/1", 0);
- if (m1 < 0 || s1 < 0 || m2 < 0 || s2 < 0) {
- /* Not enough PTY pairs — skip gracefully */
- if (m1 >= 0) (void)sys_close(m1);
- if (s1 >= 0) (void)sys_close(s1);
- if (m2 >= 0) (void)sys_close(m2);
- if (s2 >= 0) (void)sys_close(s2);
- sys_write(1, "[init] multi-pty OK\n",
- (uint32_t)(sizeof("[init] multi-pty OK\n") - 1));
- } else {
- /* Write through pair 1 */
- (void)sys_write(m1, "P1", 2);
- char b1[4];
- int r1 = sys_read(s1, b1, 2);
-
- /* Write through pair 2 */
- (void)sys_write(m2, "P2", 2);
- char b2[4];
- int r2 = sys_read(s2, b2, 2);
-
- (void)sys_close(m1);
- (void)sys_close(s1);
- (void)sys_close(m2);
- (void)sys_close(s2);
-
- if (r1 == 2 && r2 == 2 && b1[0] == 'P' && b1[1] == '1' && b2[0] == 'P' && b2[1] == '2') {
- sys_write(1, "[init] multi-pty OK\n",
- (uint32_t)(sizeof("[init] multi-pty OK\n") - 1));
- } else {
- sys_write(1, "[init] multi-pty data mismatch\n",
- (uint32_t)(sizeof("[init] multi-pty data mismatch\n") - 1));
- sys_exit(1);
- }
- }
- }
-
- // F5: dup standalone
- {
- int fds[2];
- if (sys_pipe(fds) < 0) {
- sys_write(1, "[init] dup pipe failed\n",
- (uint32_t)(sizeof("[init] dup pipe failed\n") - 1));
- sys_exit(1);
- }
- int d = sys_dup(fds[1]);
- if (d < 0) {
- sys_write(1, "[init] dup failed\n",
- (uint32_t)(sizeof("[init] dup failed\n") - 1));
- sys_exit(1);
- }
- /* Write through dup'd fd, read from original */
- (void)sys_write(d, "D", 1);
- char db;
- int r = sys_read(fds[0], &db, 1);
- (void)sys_close(d);
- (void)sys_close(fds[0]);
- (void)sys_close(fds[1]);
- if (r != 1 || db != 'D') {
- sys_write(1, "[init] dup data bad\n",
- (uint32_t)(sizeof("[init] dup data bad\n") - 1));
- sys_exit(1);
- }
- sys_write(1, "[init] dup OK\n",
- (uint32_t)(sizeof("[init] dup OK\n") - 1));
- }
-
- // F6: pipe EOF — close write end, read should return 0
- {
- int fds[2];
- if (sys_pipe(fds) < 0) {
- sys_write(1, "[init] pipe-eof pipe failed\n",
- (uint32_t)(sizeof("[init] pipe-eof pipe failed\n") - 1));
- sys_exit(1);
- }
- (void)sys_close(fds[1]); /* close write end */
- char eb;
- int r = sys_read(fds[0], &eb, 1);
- (void)sys_close(fds[0]);
- if (r != 0) {
- sys_write(1, "[init] pipe EOF expected 0\n",
- (uint32_t)(sizeof("[init] pipe EOF expected 0\n") - 1));
- sys_exit(1);
- }
- sys_write(1, "[init] pipe EOF OK\n",
- (uint32_t)(sizeof("[init] pipe EOF OK\n") - 1));
- }
-
- // F7: getdents /proc (readdir on procfs root)
- {
- int fd = sys_open("/proc", 0);
- if (fd < 0) {
- sys_write(1, "[init] readdir /proc open failed\n",
- (uint32_t)(sizeof("[init] readdir /proc open failed\n") - 1));
- sys_exit(1);
- }
- char dbuf[512];
- int r = sys_getdents(fd, dbuf, 512);
- (void)sys_close(fd);
- if (r <= 0) {
- sys_write(1, "[init] readdir /proc empty\n",
- (uint32_t)(sizeof("[init] readdir /proc empty\n") - 1));
- sys_exit(1);
- }
- sys_write(1, "[init] readdir /proc OK\n",
- (uint32_t)(sizeof("[init] readdir /proc OK\n") - 1));
- }
-
- // F8: getdents /bin (readdir on initrd — tests initrd_readdir fix)
- {
- int fd = sys_open("/bin", 0);
- if (fd < 0) {
- sys_write(1, "[init] readdir /bin open failed\n",
- (uint32_t)(sizeof("[init] readdir /bin open failed\n") - 1));
- sys_exit(1);
- }
- char dbuf[1024];
- int r = sys_getdents(fd, dbuf, 1024);
- (void)sys_close(fd);
- if (r <= 0) {
- sys_write(1, "[init] readdir /bin empty\n",
- (uint32_t)(sizeof("[init] readdir /bin empty\n") - 1));
- sys_exit(1);
- }
- sys_write(1, "[init] readdir /bin OK\n",
- (uint32_t)(sizeof("[init] readdir /bin OK\n") - 1));
- }
-
- enum { NCHILD = 100 };
- int children[NCHILD];
- for (int i = 0; i < NCHILD; i++) {
- int pid = sys_fork();
- if (pid < 0) {
- static const char smsg[] = "[init] fork failed\n";
- (void)sys_write(1, smsg, (uint32_t)(sizeof(smsg) - 1));
- sys_exit(2);
- }
- if (pid == 0) {
- sys_exit(42);
- }
- children[i] = pid;
- }
-
- {
- int parent_pid = sys_getpid();
- int pid = sys_fork();
- if (pid == 0) {
- int ppid = sys_getppid();
- if (ppid == parent_pid) {
- static const char msg[] = "[init] getppid OK\n";
- (void)sys_write(1, msg, (uint32_t)(sizeof(msg) - 1));
- sys_exit(0);
- }
- static const char msg[] = "[init] getppid failed\n";
- (void)sys_write(1, msg, (uint32_t)(sizeof(msg) - 1));
- sys_exit(1);
- }
- int st = 0;
- (void)sys_waitpid(pid, &st, 0);
- }
-
- {
- int pid = sys_fork();
- if (pid == 0) {
- volatile uint32_t x = 0;
- for (uint32_t i = 0; i < 2000000U; i++) x += i;
- sys_exit(7);
- }
- int st = 0;
- int wp = sys_waitpid(pid, &st, WNOHANG);
- if (wp == 0 || wp == pid) {
- static const char msg[] = "[init] waitpid WNOHANG OK\n";
- (void)sys_write(1, msg, (uint32_t)(sizeof(msg) - 1));
- } else {
- static const char msg[] = "[init] waitpid WNOHANG failed\n";
- (void)sys_write(1, msg, (uint32_t)(sizeof(msg) - 1));
- }
- if (wp == 0) {
- (void)sys_waitpid(pid, &st, 0);
- }
- }
-
- /* ---- gettimeofday test ---- */
- {
- struct timeval tv;
- tv.tv_sec = 0; tv.tv_usec = 0;
- int r = sys_gettimeofday(&tv);
- if (r == 0 && tv.tv_sec > 1000000000U) {
- static const char msg[] = "[init] gettimeofday OK\n";
- (void)sys_write(1, msg, (uint32_t)(sizeof(msg) - 1));
- } else {
- static const char msg[] = "[init] gettimeofday failed\n";
- (void)sys_write(1, msg, (uint32_t)(sizeof(msg) - 1));
- }
- }
-
- /* ---- mprotect test ---- */
- {
- /* Test mprotect on heap memory (brk region) — simpler than mmap */
- uintptr_t old_brk = sys_brk(0);
- uintptr_t page = (old_brk + 0xFFFU) & ~(uintptr_t)0xFFFU;
- uintptr_t new_brk = page + 4096;
- uintptr_t r_brk = sys_brk(new_brk);
- if (r_brk >= new_brk) {
- *(volatile uint32_t*)page = 0xDEADBEEF;
- int r = sys_mprotect(page, 4096, PROT_READ | PROT_WRITE);
- if (r == 0) {
- static const char msg[] = "[init] mprotect OK\n";
- (void)sys_write(1, msg, (uint32_t)(sizeof(msg) - 1));
- } else {
- static const char msg[] = "[init] mprotect call failed\n";
- (void)sys_write(1, msg, (uint32_t)(sizeof(msg) - 1));
- }
- } else {
- static const char msg[] = "[init] mprotect brk failed\n";
- (void)sys_write(1, msg, (uint32_t)(sizeof(msg) - 1));
- }
- }
-
- /* ---- madvise test ---- */
- {
- int r = sys_madvise(0, 4096, 0 /* MADV_NORMAL */);
- if (r == 0) {
- static const char msg[] = "[init] madvise OK\n";
- (void)sys_write(1, msg, (uint32_t)(sizeof(msg) - 1));
- } else {
- static const char msg[] = "[init] madvise failed\n";
- (void)sys_write(1, msg, (uint32_t)(sizeof(msg) - 1));
- }
- }
-
- /* ---- getrlimit/setrlimit test ---- */
- {
- struct rlimit rl;
- int r = sys_getrlimit(RLIMIT_NOFILE, &rl);
- if (r == 0 && rl.rlim_cur > 0 && rl.rlim_cur <= 1024) {
- /* Try setting a lower soft limit */
- struct rlimit new_rl;
- new_rl.rlim_cur = rl.rlim_cur / 2;
- new_rl.rlim_max = rl.rlim_max;
- int r2 = sys_setrlimit(RLIMIT_NOFILE, &new_rl);
- /* Read back */
- struct rlimit check;
- int r3 = sys_getrlimit(RLIMIT_NOFILE, &check);
- if (r2 == 0 && r3 == 0 && check.rlim_cur == new_rl.rlim_cur) {
- static const char msg[] = "[init] getrlimit/setrlimit OK\n";
- (void)sys_write(1, msg, (uint32_t)(sizeof(msg) - 1));
- } else {
- static const char msg[] = "[init] setrlimit failed\n";
- (void)sys_write(1, msg, (uint32_t)(sizeof(msg) - 1));
- }
- /* Restore */
- (void)sys_setrlimit(RLIMIT_NOFILE, &rl);
- } else {
- static const char msg[] = "[init] getrlimit failed\n";
- (void)sys_write(1, msg, (uint32_t)(sizeof(msg) - 1));
- }
- }
-
- // PIE lazy PLT/GOT binding test
- {
- int pid = sys_fork();
- if (pid == 0) {
- static const char* const av[] = {"pie_test", 0};
- static const char* const ev[] = {0};
- (void)sys_execve("/bin/pie_test", av, ev);
- sys_exit(99);
- }
- if (pid > 0) {
- int st = 0;
- (void)sys_waitpid(pid, &st, 0);
- }
- }
-
- {
- int pid = sys_fork();
- if (pid < 0) {
- static const char msg[] = "[init] sigsegv test fork failed\n";
- (void)sys_write(1, msg, (uint32_t)(sizeof(msg) - 1));
- goto sigsegv_done;
- }
-
- if (pid == 0) {
- struct sigaction act;
- act.sa_handler = 0;
- act.sa_sigaction = (uintptr_t)sigsegv_info_handler;
- act.sa_mask = 0;
- act.sa_flags = SA_SIGINFO;
-
- if (sys_sigaction2(SIGSEGV, &act, 0) < 0) {
- static const char msg[] = "[init] sigaction(SIGSEGV) failed\n";
- (void)sys_write(1, msg, (uint32_t)(sizeof(msg) - 1));
- sys_exit(1);
- }
-
- *(volatile uint32_t*)0x12345000U = 123;
- sys_exit(2);
- }
-
- int st = 0;
- int wp = sys_waitpid(pid, &st, 0);
- if (wp == pid && st == 0) {
- static const char msg[] = "[init] SIGSEGV OK\n";
- (void)sys_write(1, msg, (uint32_t)(sizeof(msg) - 1));
- } else {
- static const char msg[] = "[init] SIGSEGV failed\n";
- (void)sys_write(1, msg, (uint32_t)(sizeof(msg) - 1));
- }
- sigsegv_done:;
- }
-
- int ok = 1;
- for (int i = 0; i < NCHILD; i++) {
- int st = 0;
- int wp = sys_waitpid(children[i], &st, 0);
- if (wp != children[i] || st != 42) {
- ok = 0;
- break;
- }
- }
-
- if (ok) {
- static const char wmsg[] = "[init] waitpid OK (100 children, explicit)\n";
- (void)sys_write(1, wmsg, (uint32_t)(sizeof(wmsg) - 1));
- } else {
- static const char wbad[] = "[init] waitpid failed (100 children, explicit)\n";
- (void)sys_write(1, wbad, (uint32_t)(sizeof(wbad) - 1));
- }
-
- // G1: uname syscall test
- {
- struct utsname uts;
- for (uint32_t i = 0; i < sizeof(uts); i++) ((char*)&uts)[i] = 0;
- int r = sys_uname(&uts);
- if (r < 0) {
- sys_write(1, "[init] uname failed\n", (uint32_t)(sizeof("[init] uname failed\n") - 1));
- sys_exit(1);
- }
- /* Verify sysname == "AdrOS" */
- if (uts.sysname[0] != 'A' || uts.sysname[1] != 'd' || uts.sysname[2] != 'r' ||
- uts.sysname[3] != 'O' || uts.sysname[4] != 'S' || uts.sysname[5] != 0) {
- sys_write(1, "[init] uname sysname bad\n", (uint32_t)(sizeof("[init] uname sysname bad\n") - 1));
- sys_exit(1);
- }
- /* Verify machine == "i686" */
- if (uts.machine[0] != 'i' || uts.machine[1] != '6' || uts.machine[2] != '8' || uts.machine[3] != '6') {
- sys_write(1, "[init] uname machine bad\n", (uint32_t)(sizeof("[init] uname machine bad\n") - 1));
- sys_exit(1);
- }
- sys_write(1, "[init] uname OK\n", (uint32_t)(sizeof("[init] uname OK\n") - 1));
- }
-
- // H1: SMP parallel fork test — exercises multi-CPU scheduling + load balancing
- {
- #define SMP_NCHILD 8
- int smp_pids[SMP_NCHILD];
- int smp_ok = 1;
-
- for (int i = 0; i < SMP_NCHILD; i++) {
- int pid = sys_fork();
- if (pid == 0) {
- /* Child: busy loop to consume a time slice, then exit with index */
- volatile uint32_t sum = 0;
- for (uint32_t j = 0; j < 50000; j++) sum += j;
- (void)sum;
- sys_exit(i + 1);
- }
- smp_pids[i] = pid;
- }
-
- /* Parent: wait for all children, verify each returned correct status */
- for (int i = 0; i < SMP_NCHILD; i++) {
- int st = 0;
- int wp = sys_waitpid(smp_pids[i], &st, 0);
- if (wp != smp_pids[i] || st != (i + 1)) {
- smp_ok = 0;
- }
- }
-
- if (smp_ok) {
- static const char msg[] = "[init] SMP parallel fork OK\n";
- (void)sys_write(1, msg, (uint32_t)(sizeof(msg) - 1));
- } else {
- static const char msg[] = "[init] SMP parallel fork FAIL\n";
- (void)sys_write(1, msg, (uint32_t)(sizeof(msg) - 1));
- }
- #undef SMP_NCHILD
- }
-
- (void)sys_write(1, "[init] execve(/bin/echo)\n",
- (uint32_t)(sizeof("[init] execve(/bin/echo)\n") - 1));
- static const char* const argv[] = {"echo", "[echo]", "hello", "from", "echo", 0};
- static const char* const envp[] = {"FOO=bar", "HELLO=world", 0};
- (void)sys_execve("/bin/echo", argv, envp);
- (void)sys_write(1, "[init] execve returned (unexpected)\n",
- (uint32_t)(sizeof("[init] execve returned (unexpected)\n") - 1));
- sys_exit(1);
- sys_exit(0);
-}
+++ /dev/null
-/* AdrOS grep utility — search for pattern in files */
-#include <stdio.h>
-#include <string.h>
-#include <unistd.h>
-#include <fcntl.h>
-
-static int match_simple(const char* text, const char* pat) {
- /* Simple substring match (no regex) */
- return strstr(text, pat) != NULL;
-}
-
-static int grep_fd(int fd, const char* pattern, const char* fname, int show_name, int invert, int count_only, int line_num) {
- char buf[4096];
- int pos = 0, n, matches = 0, lnum = 0;
- while ((n = read(fd, buf + pos, (size_t)(sizeof(buf) - 1 - pos))) > 0) {
- pos += n;
- buf[pos] = '\0';
- char* start = buf;
- char* nl;
- while ((nl = strchr(start, '\n')) != NULL) {
- *nl = '\0';
- lnum++;
- int m = match_simple(start, pattern);
- if (invert) m = !m;
- if (m) {
- matches++;
- if (!count_only) {
- if (show_name) printf("%s:", fname);
- if (line_num) printf("%d:", lnum);
- printf("%s\n", start);
- }
- }
- start = nl + 1;
- }
- int rem = (int)(buf + pos - start);
- if (rem > 0) memmove(buf, start, (size_t)rem);
- pos = rem;
- }
- if (pos > 0) {
- buf[pos] = '\0';
- lnum++;
- int m = match_simple(buf, pattern);
- if (invert) m = !m;
- if (m) {
- matches++;
- if (!count_only) {
- if (show_name) printf("%s:", fname);
- if (line_num) printf("%d:", lnum);
- printf("%s\n", buf);
- }
- }
- }
- if (count_only) printf("%s%s%d\n", show_name ? fname : "", show_name ? ":" : "", matches);
- return matches > 0 ? 0 : 1;
-}
-
-int main(int argc, char** argv) {
- int invert = 0, count_only = 0, line_num = 0;
- int i = 1;
- while (i < argc && argv[i][0] == '-') {
- for (int j = 1; argv[i][j]; j++) {
- if (argv[i][j] == 'v') invert = 1;
- else if (argv[i][j] == 'c') count_only = 1;
- else if (argv[i][j] == 'n') line_num = 1;
- }
- i++;
- }
- if (i >= argc) { fprintf(stderr, "usage: grep [-vcn] PATTERN [FILE...]\n"); return 2; }
- const char* pattern = argv[i++];
- if (i >= argc) return grep_fd(STDIN_FILENO, pattern, "(stdin)", 0, invert, count_only, line_num);
- int rc = 1, nfiles = argc - i;
- for (; i < argc; i++) {
- int fd = open(argv[i], O_RDONLY);
- if (fd < 0) { fprintf(stderr, "grep: %s: No such file or directory\n", argv[i]); continue; }
- if (grep_fd(fd, pattern, argv[i], nfiles > 1, invert, count_only, line_num) == 0) rc = 0;
- close(fd);
- }
- return rc;
-}
+++ /dev/null
-/* AdrOS head utility */
-#include <stdio.h>
-#include <stdlib.h>
-#include <unistd.h>
-#include <fcntl.h>
-#include <string.h>
-
-static void head_fd(int fd, int nlines) {
- char buf[4096];
- int lines = 0;
- int r;
- while (lines < nlines && (r = read(fd, buf, sizeof(buf))) > 0) {
- for (int i = 0; i < r && lines < nlines; i++) {
- write(STDOUT_FILENO, &buf[i], 1);
- if (buf[i] == '\n') lines++;
- }
- }
-}
-
-int main(int argc, char** argv) {
- int nlines = 10;
- int start = 1;
-
- if (argc > 1 && argv[1][0] == '-' && argv[1][1] == 'n' && argc > 2) {
- nlines = atoi(argv[2]);
- start = 3;
- } else if (argc > 1 && argv[1][0] == '-' && argv[1][1] >= '0' && argv[1][1] <= '9') {
- nlines = atoi(argv[1] + 1);
- start = 2;
- }
-
- if (start >= argc) {
- head_fd(STDIN_FILENO, nlines);
- } else {
- for (int i = start; i < argc; i++) {
- if (argc - start > 1) printf("==> %s <==\n", argv[i]);
- int fd = open(argv[i], O_RDONLY);
- if (fd < 0) {
- fprintf(stderr, "head: cannot open '%s'\n", argv[i]);
- continue;
- }
- head_fd(fd, nlines);
- close(fd);
- }
- }
- return 0;
-}
+++ /dev/null
-/* AdrOS hostname utility */
-#include <stdio.h>
-#include <string.h>
-#include <unistd.h>
-#include <fcntl.h>
-
-int main(int argc, char** argv) {
- (void)argc; (void)argv;
-
- /* Try /proc/hostname first, then /etc/hostname, then fallback */
- static const char* paths[] = { "/proc/hostname", "/etc/hostname", NULL };
- for (int i = 0; paths[i]; i++) {
- int fd = open(paths[i], O_RDONLY);
- if (fd >= 0) {
- char buf[256];
- int r = read(fd, buf, sizeof(buf) - 1);
- close(fd);
- if (r > 0) {
- buf[r] = '\0';
- /* Strip trailing newline */
- if (r > 0 && buf[r - 1] == '\n') buf[r - 1] = '\0';
- printf("%s\n", buf);
- return 0;
- }
- }
- }
-
- printf("adros\n");
- return 0;
-}
+++ /dev/null
-/* AdrOS id utility — display user and group IDs */
-#include <stdio.h>
-#include <unistd.h>
-
-int main(void) {
- printf("uid=%d gid=%d euid=%d egid=%d\n",
- getuid(), getgid(), geteuid(), getegid());
- return 0;
-}
+++ /dev/null
-/* AdrOS SysV-like init (/sbin/init)
- *
- * Reads /etc/inittab for configuration.
- * Supports runlevels 0-6 and S (single-user).
- * Actions: sysinit, respawn, wait, once, ctrlaltdel, shutdown.
- *
- * Default behavior (no inittab):
- * 1. Run /etc/init.d/rcS (if exists)
- * 2. Spawn /bin/sh on /dev/console
- */
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <unistd.h>
-#include <fcntl.h>
-#include <signal.h>
-#include <time.h>
-
-#define MAX_ENTRIES 32
-#define LINE_MAX 256
-
-/* Inittab entry actions */
-enum action {
- ACT_SYSINIT, /* Run during system initialization */
- ACT_WAIT, /* Run and wait for completion */
- ACT_ONCE, /* Run once when entering runlevel */
- ACT_RESPAWN, /* Restart when process dies */
- ACT_CTRLALTDEL, /* Run on Ctrl+Alt+Del */
- ACT_SHUTDOWN, /* Run during shutdown */
-};
-
-struct inittab_entry {
- char id[8];
- char runlevels[16];
- enum action action;
- char process[128];
- int pid; /* PID of running process (for respawn) */
- int active;
-};
-
-static struct inittab_entry entries[MAX_ENTRIES];
-static int nentries = 0;
-static int current_runlevel = 3; /* Default: multi-user */
-
-static enum action parse_action(const char* s) {
- if (strcmp(s, "sysinit") == 0) return ACT_SYSINIT;
- if (strcmp(s, "wait") == 0) return ACT_WAIT;
- if (strcmp(s, "once") == 0) return ACT_ONCE;
- if (strcmp(s, "respawn") == 0) return ACT_RESPAWN;
- if (strcmp(s, "ctrlaltdel") == 0) return ACT_CTRLALTDEL;
- if (strcmp(s, "shutdown") == 0) return ACT_SHUTDOWN;
- return ACT_ONCE;
-}
-
-/* Parse /etc/inittab
- * Format: id:runlevels:action:process
- * Example:
- * ::sysinit:/etc/init.d/rcS
- * ::respawn:/bin/sh
- * tty1:2345:respawn:/bin/sh
- */
-static int parse_inittab(void) {
- int fd = open("/etc/inittab", O_RDONLY);
- if (fd < 0) return -1;
-
- char buf[2048];
- int total = 0;
- int r;
- while ((r = read(fd, buf + total, (size_t)(sizeof(buf) - (size_t)total - 1))) > 0)
- total += r;
- buf[total] = '\0';
- close(fd);
-
- char* p = buf;
- while (*p && nentries < MAX_ENTRIES) {
- /* Skip whitespace and comments */
- while (*p == ' ' || *p == '\t') p++;
- if (*p == '#' || *p == '\n') {
- while (*p && *p != '\n') p++;
- if (*p == '\n') p++;
- continue;
- }
- if (*p == '\0') break;
-
- struct inittab_entry* e = &entries[nentries];
- memset(e, 0, sizeof(*e));
-
- /* id */
- char* start = p;
- while (*p && *p != ':') p++;
- int len = (int)(p - start);
- if (len > 7) len = 7;
- memcpy(e->id, start, (size_t)len);
- e->id[len] = '\0';
- if (*p == ':') p++;
-
- /* runlevels */
- start = p;
- while (*p && *p != ':') p++;
- len = (int)(p - start);
- if (len > 15) len = 15;
- memcpy(e->runlevels, start, (size_t)len);
- e->runlevels[len] = '\0';
- if (*p == ':') p++;
-
- /* action */
- start = p;
- while (*p && *p != ':') p++;
- char action_str[32];
- len = (int)(p - start);
- if (len > 31) len = 31;
- memcpy(action_str, start, (size_t)len);
- action_str[len] = '\0';
- e->action = parse_action(action_str);
- if (*p == ':') p++;
-
- /* process */
- start = p;
- while (*p && *p != '\n') p++;
- len = (int)(p - start);
- if (len > 127) len = 127;
- memcpy(e->process, start, (size_t)len);
- e->process[len] = '\0';
- if (*p == '\n') p++;
-
- e->pid = -1;
- e->active = 1;
- nentries++;
- }
-
- return nentries > 0 ? 0 : -1;
-}
-
-/* Check if entry should run at current runlevel */
-static int entry_matches_runlevel(const struct inittab_entry* e) {
- if (e->runlevels[0] == '\0') return 1; /* Empty = all runlevels */
- char rl = '0' + (char)current_runlevel;
- for (const char* p = e->runlevels; *p; p++) {
- if (*p == rl || *p == 'S' || *p == 's') return 1;
- }
- return 0;
-}
-
-/* Run a process (fork + exec) */
-static int run_process(const char* cmd) {
- int pid = fork();
- if (pid < 0) return -1;
-
- if (pid == 0) {
- /* Child: parse command into argv */
- char buf[128];
- strncpy(buf, cmd, sizeof(buf) - 1);
- buf[sizeof(buf) - 1] = '\0';
-
- char* argv[16];
- int argc = 0;
- char* p = buf;
- while (*p && argc < 15) {
- while (*p == ' ' || *p == '\t') p++;
- if (*p == '\0') break;
- argv[argc++] = p;
- while (*p && *p != ' ' && *p != '\t') p++;
- if (*p) *p++ = '\0';
- }
- argv[argc] = NULL;
-
- if (argc > 0) {
- execve(argv[0], (const char* const*)argv, NULL);
- /* If execve fails, try with /bin/sh -c */
- const char* sh_argv[] = { "/bin/sh", "-c", cmd, NULL };
- execve("/bin/sh", sh_argv, NULL);
- }
- _exit(127);
- }
-
- return pid;
-}
-
-/* Run a process and wait for it */
-static int run_and_wait(const char* cmd) {
- int pid = run_process(cmd);
- if (pid < 0) return -1;
- int st;
- waitpid(pid, &st, 0);
- return st;
-}
-
-/* Run all entries matching a given action and current runlevel */
-static void run_action(enum action act, int do_wait) {
- for (int i = 0; i < nentries; i++) {
- if (entries[i].action != act) continue;
- if (!entry_matches_runlevel(&entries[i])) continue;
- if (!entries[i].active) continue;
-
- if (do_wait) {
- run_and_wait(entries[i].process);
- } else {
- entries[i].pid = run_process(entries[i].process);
- }
- }
-}
-
-/* Respawn dead children */
-static void check_respawn(void) {
- for (int i = 0; i < nentries; i++) {
- if (entries[i].action != ACT_RESPAWN) continue;
- if (!entry_matches_runlevel(&entries[i])) continue;
- if (!entries[i].active) continue;
-
- if (entries[i].pid <= 0) {
- entries[i].pid = run_process(entries[i].process);
- } else {
- /* Check if still running */
- int st;
- int r = waitpid(entries[i].pid, &st, 1 /* WNOHANG */);
- if (r > 0) {
- /* Process exited, respawn */
- entries[i].pid = run_process(entries[i].process);
- }
- }
- }
-}
-
-/* Default behavior when no inittab exists */
-static void default_init(void) {
- /* Run /etc/init.d/rcS if it exists */
- if (access("/etc/init.d/rcS", 0) == 0) {
- run_and_wait("/etc/init.d/rcS");
- }
-
- /* Spawn shell */
- while (1) {
- int pid = fork();
- if (pid < 0) {
- fprintf(stderr, "init: fork failed\n");
- for (;;) { struct timespec ts = {1,0}; nanosleep(&ts, NULL); }
- }
-
- if (pid == 0) {
- const char* argv[] = { "/bin/sh", NULL };
- execve("/bin/sh", argv, NULL);
- _exit(127);
- }
-
- int st;
- waitpid(pid, &st, 0);
-
- /* Shell exited, respawn after a small delay */
- struct timespec ts = {1, 0};
- nanosleep(&ts, NULL);
- }
-}
-
-int main(int argc, char** argv) {
- (void)argc; (void)argv;
-
- /* PID 1 should not die on signals */
- struct sigaction sa;
- memset(&sa, 0, sizeof(sa));
- sa.sa_handler = (uintptr_t)SIG_IGN;
- sigaction(SIGINT, &sa, NULL);
- sigaction(SIGTERM, &sa, NULL);
-
- printf("AdrOS init starting (PID %d)\n", getpid());
-
- /* Try to parse inittab */
- if (parse_inittab() < 0) {
- printf("init: no /etc/inittab, using defaults\n");
- default_init();
- return 0; /* unreachable */
- }
-
- printf("init: loaded %d inittab entries, runlevel %d\n",
- nentries, current_runlevel);
-
- /* Phase 1: sysinit entries */
- run_action(ACT_SYSINIT, 1);
-
- /* Phase 2: wait entries */
- run_action(ACT_WAIT, 1);
-
- /* Phase 3: once entries */
- run_action(ACT_ONCE, 0);
-
- /* Phase 4: respawn entries */
- run_action(ACT_RESPAWN, 0);
-
- /* Main loop: reap children and respawn */
- while (1) {
- int st;
- int pid = waitpid(-1, &st, 0);
-
- if (pid > 0) {
- /* Mark dead child and respawn if needed */
- for (int i = 0; i < nentries; i++) {
- if (entries[i].pid == pid) {
- entries[i].pid = -1;
- break;
- }
- }
- check_respawn();
- } else {
- /* No children or error — sleep briefly */
- struct timespec ts = {1, 0};
- nanosleep(&ts, NULL);
- check_respawn();
- }
- }
-
- return 0;
-}
+++ /dev/null
-/* AdrOS kill utility — send signal to process */
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <unistd.h>
-
-int main(int argc, char** argv) {
- if (argc <= 1) {
- fprintf(stderr, "usage: kill [-SIGNAL] PID...\n");
- return 1;
- }
-
- int sig = 15; /* SIGTERM */
- int start = 1;
-
- if (argv[1][0] == '-') {
- const char* s = argv[1] + 1;
- if (strcmp(s, "9") == 0 || strcmp(s, "KILL") == 0) sig = 9;
- else if (strcmp(s, "15") == 0 || strcmp(s, "TERM") == 0) sig = 15;
- else if (strcmp(s, "2") == 0 || strcmp(s, "INT") == 0) sig = 2;
- else if (strcmp(s, "1") == 0 || strcmp(s, "HUP") == 0) sig = 1;
- else if (strcmp(s, "0") == 0) sig = 0;
- else sig = atoi(s);
- start = 2;
- }
-
- int rc = 0;
- for (int i = start; i < argc; i++) {
- int pid = atoi(argv[i]);
- if (pid <= 0) {
- fprintf(stderr, "kill: invalid pid '%s'\n", argv[i]);
- rc = 1;
- continue;
- }
- if (kill(pid, sig) < 0) {
- fprintf(stderr, "kill: %d: no such process\n", pid);
- rc = 1;
- }
- }
- return rc;
-}
+++ /dev/null
-/* Userspace dynamic linker (ld.so) with lazy PLT/GOT binding.
- *
- * The kernel ELF loader pushes an auxiliary vector (auxv) onto the user
- * stack when PT_INTERP is present. This linker:
- * 1. Parses auxv to find AT_PHDR, AT_PHNUM, AT_ENTRY
- * 2. Walks program headers to find PT_DYNAMIC
- * 3. Extracts DT_PLTGOT, DT_JMPREL, DT_PLTRELSZ, DT_SYMTAB, DT_STRTAB
- * 4. Sets GOT[1] = link_map pointer, GOT[2] = _dl_runtime_resolve
- * 5. Jumps to AT_ENTRY (the real program entry point)
- *
- * On first PLT call, the resolver fires: looks up the symbol, patches
- * the GOT entry, and jumps to the resolved function. Subsequent calls
- * go directly through the patched GOT (zero overhead).
- *
- * The kernel loads DT_NEEDED shared libraries at SHLIB_BASE (0x20000000).
- * The resolver scans the .so's dynamic symtab to find undefined symbols. */
-
-typedef unsigned char uint8_t;
-typedef unsigned short uint16_t;
-typedef unsigned int uint32_t;
-typedef int int32_t;
-
-/* ---- Auxiliary vector types ---- */
-#define AT_NULL 0
-#define AT_PHDR 3
-#define AT_PHENT 4
-#define AT_PHNUM 5
-#define AT_ENTRY 9
-
-/* ---- ELF types (minimal, matching kernel include/elf.h) ---- */
-#define PT_LOAD 1
-#define PT_DYNAMIC 2
-
-#define DT_NULL 0
-#define DT_NEEDED 1
-#define DT_PLTRELSZ 2
-#define DT_PLTGOT 3
-#define DT_HASH 4
-#define DT_STRTAB 5
-#define DT_SYMTAB 6
-#define DT_STRSZ 10
-#define DT_SYMENT 11
-#define DT_REL 17
-#define DT_RELSZ 18
-#define DT_JMPREL 23
-
-#define R_386_32 1
-#define R_386_COPY 5
-#define R_386_GLOB_DAT 6
-#define R_386_JMP_SLOT 7
-
-#define ELF32_R_SYM(i) ((i) >> 8)
-#define ELF32_R_TYPE(i) ((unsigned char)(i))
-
-#define STB_GLOBAL 1
-#define STB_WEAK 2
-#define ELF32_ST_BIND(i) ((i) >> 4)
-
-#define SHLIB_BASE 0x11000000U
-
-struct elf32_phdr {
- uint32_t p_type, p_offset, p_vaddr, p_paddr;
- uint32_t p_filesz, p_memsz, p_flags, p_align;
-};
-
-struct elf32_dyn {
- int32_t d_tag;
- uint32_t d_val;
-};
-
-struct elf32_rel {
- uint32_t r_offset;
- uint32_t r_info;
-};
-
-struct elf32_sym {
- uint32_t st_name, st_value, st_size;
- uint8_t st_info, st_other;
- uint16_t st_shndx;
-};
-
-/* ---- Link map: per-module metadata for the resolver ---- */
-struct link_map {
- uint32_t l_addr; /* base load address (0 for ET_EXEC) */
- uint32_t jmprel; /* DT_JMPREL VA (relocation table for .rel.plt) */
- uint32_t pltrelsz; /* DT_PLTRELSZ */
- uint32_t symtab; /* DT_SYMTAB VA */
- uint32_t strtab; /* DT_STRTAB VA */
- uint32_t rel; /* DT_REL VA (eager relocations) */
- uint32_t relsz; /* DT_RELSZ */
- /* Shared lib symbol lookup info */
- uint32_t shlib_symtab; /* .so DT_SYMTAB VA (0 if no .so) */
- uint32_t shlib_strtab; /* .so DT_STRTAB VA */
- uint32_t shlib_base; /* .so load base */
- uint32_t shlib_hash; /* .so DT_HASH VA */
-};
-
-static struct link_map g_map;
-
-/* ---- Minimal string helpers (no libc) ---- */
-static int str_eq(const char* a, const char* b) {
- while (*a && *b) { if (*a++ != *b++) return 0; }
- return *a == *b;
-}
-
-/* ---- ELF hash (for DT_HASH lookup) ---- */
-static uint32_t elf_hash(const char* name) {
- uint32_t h = 0, g;
- while (*name) {
- h = (h << 4) + (uint8_t)*name++;
- g = h & 0xF0000000U;
- if (g) h ^= g >> 24;
- h &= ~g;
- }
- return h;
-}
-
-/* ---- Symbol lookup in a shared library via DT_HASH ---- */
-static uint32_t shlib_lookup(const char* name, const struct link_map* map) {
- if (!map->shlib_symtab || !map->shlib_strtab || !map->shlib_hash)
- return 0;
-
- const uint32_t* hashtab = (const uint32_t*)(map->shlib_hash + map->shlib_base);
- uint32_t nbucket = hashtab[0];
- uint32_t nchain = hashtab[1];
- const uint32_t* bucket = &hashtab[2];
- const uint32_t* chain = &hashtab[2 + nbucket];
- (void)nchain;
-
- uint32_t h = elf_hash(name) % nbucket;
- const struct elf32_sym* symtab = (const struct elf32_sym*)(map->shlib_symtab + map->shlib_base);
- const char* strtab = (const char*)(map->shlib_strtab + map->shlib_base);
-
- for (uint32_t i = bucket[h]; i != 0; i = chain[i]) {
- const struct elf32_sym* sym = &symtab[i];
- uint8_t bind = ELF32_ST_BIND(sym->st_info);
- if ((bind == STB_GLOBAL || bind == STB_WEAK) &&
- sym->st_shndx != 0 && sym->st_value != 0) {
- if (str_eq(strtab + sym->st_name, name))
- return sym->st_value + map->shlib_base;
- }
- }
- return 0;
-}
-
-/* ---- dl_fixup: called by _dl_runtime_resolve trampoline ----
- * Resolves a single PLT entry: looks up the symbol, patches GOT,
- * returns the resolved address. */
-uint32_t dl_fixup(struct link_map* map, uint32_t reloc_offset)
- __attribute__((used, visibility("hidden")));
-
-uint32_t dl_fixup(struct link_map* map, uint32_t reloc_offset) {
- const struct elf32_rel* rel =
- (const struct elf32_rel*)(map->jmprel + reloc_offset);
-
- uint32_t sym_idx = ELF32_R_SYM(rel->r_info);
- const struct elf32_sym* sym =
- &((const struct elf32_sym*)map->symtab)[sym_idx];
-
- uint32_t resolved = 0;
-
- if (sym->st_value != 0) {
- resolved = sym->st_value + map->l_addr;
- } else {
- const char* name = (const char*)map->strtab + sym->st_name;
- resolved = shlib_lookup(name, map);
- }
-
- if (resolved) {
- uint32_t* got_entry = (uint32_t*)(rel->r_offset + map->l_addr);
- *got_entry = resolved;
- }
-
- return resolved;
-}
-
-/* ---- _dl_runtime_resolve: PLT[0] jumps here via GOT[2] ----
- * Entry stack: [link_map*] [reloc_offset] [return_addr]
- * Uses the glibc i386 convention: save eax/ecx/edx, call dl_fixup,
- * restore, ret $8 to jump to resolved function. */
-void _dl_runtime_resolve(void)
- __attribute__((naked, used, visibility("hidden")));
-
-void _dl_runtime_resolve(void) {
- __asm__ volatile(
- "pushl %%eax\n"
- "pushl %%ecx\n"
- "pushl %%edx\n"
- "movl 16(%%esp), %%edx\n" /* reloc_offset */
- "movl 12(%%esp), %%eax\n" /* link_map* */
- "pushl %%edx\n"
- "pushl %%eax\n"
- "call dl_fixup\n"
- "addl $8, %%esp\n"
- "popl %%edx\n"
- "popl %%ecx\n"
- "xchgl %%eax, (%%esp)\n" /* restore eax, put resolved addr on stack */
- "ret $8\n" /* jump to resolved; pop link_map + reloc_offset */
- ::: "memory"
- );
-}
-
-/* ---- Parse a PT_DYNAMIC at the given VA to extract .so symtab info ---- */
-static void parse_shlib_dynamic(uint32_t dyn_va, uint32_t base) {
- const struct elf32_dyn* d = (const struct elf32_dyn*)dyn_va;
- for (; d->d_tag != DT_NULL; d++) {
- switch (d->d_tag) {
- case DT_SYMTAB: g_map.shlib_symtab = d->d_val; break;
- case DT_STRTAB: g_map.shlib_strtab = d->d_val; break;
- case DT_HASH: g_map.shlib_hash = d->d_val; break;
- }
- }
- g_map.shlib_base = base;
-}
-
-/* ---- Scan for shared library's PT_DYNAMIC at SHLIB_BASE ---- */
-static void find_shlib_info(void) {
- const uint8_t* base = (const uint8_t*)SHLIB_BASE;
- /* Check ELF magic at SHLIB_BASE */
- if (base[0] != 0x7F || base[1] != 'E' || base[2] != 'L' || base[3] != 'F')
- return;
-
- uint32_t e_phoff = *(const uint32_t*)(base + 28);
- uint16_t e_phnum = *(const uint16_t*)(base + 44);
- uint16_t e_phentsize = *(const uint16_t*)(base + 42);
-
- for (uint16_t i = 0; i < e_phnum; i++) {
- const struct elf32_phdr* ph =
- (const struct elf32_phdr*)(base + e_phoff + i * e_phentsize);
- if (ph->p_type == PT_DYNAMIC) {
- parse_shlib_dynamic(ph->p_vaddr + SHLIB_BASE, SHLIB_BASE);
- return;
- }
- }
-}
-
-/* ---- Entry point ---- */
-static void _start_c(uint32_t* initial_sp) __attribute__((noreturn, used));
-
-void _start(void) __attribute__((noreturn, naked, section(".text.start")));
-void _start(void) {
- __asm__ volatile(
- "pushl %%esp\n"
- "call _start_c\n"
- ::: "memory"
- );
- __builtin_unreachable();
-}
-
-static void _start_c(uint32_t* initial_sp) {
- /* Stack layout set by execve:
- * initial_sp → argc
- * argv[0], argv[1], ..., NULL
- * envp[0], envp[1], ..., NULL
- * auxv[0], auxv[1], ..., {AT_NULL, 0} */
- uint32_t* sp = initial_sp;
-
- uint32_t argc = *sp++;
- sp += argc + 1; /* skip argv[] + NULL terminator */
- while (*sp) sp++; /* skip envp[] entries */
- sp++; /* skip envp NULL terminator */
-
- /* sp now points to auxv array */
- uint32_t at_entry = 0;
- uint32_t at_phdr = 0;
- uint32_t at_phnum = 0;
- uint32_t at_phent = 0;
-
- for (uint32_t* p = sp; p[0] != AT_NULL; p += 2) {
- switch (p[0]) {
- case AT_ENTRY: at_entry = p[1]; break;
- case AT_PHDR: at_phdr = p[1]; break;
- case AT_PHNUM: at_phnum = p[1]; break;
- case AT_PHENT: at_phent = p[1]; break;
- }
- }
-
- if (!at_entry) {
- __asm__ volatile("mov $2, %%eax\n mov $127, %%ebx\n int $0x80" ::: "eax", "ebx");
- __builtin_unreachable();
- }
-
- /* Walk program headers to find PT_DYNAMIC */
- g_map.l_addr = 0;
-
- if (at_phdr && at_phnum && at_phent) {
- for (uint32_t i = 0; i < at_phnum; i++) {
- const struct elf32_phdr* ph =
- (const struct elf32_phdr*)(at_phdr + i * at_phent);
- if (ph->p_type == PT_DYNAMIC) {
- uint32_t dyn_va = ph->p_vaddr + g_map.l_addr;
- const struct elf32_dyn* d = (const struct elf32_dyn*)dyn_va;
- uint32_t pltgot = 0;
-
- for (; d->d_tag != DT_NULL; d++) {
- switch (d->d_tag) {
- case DT_PLTGOT: pltgot = d->d_val; break;
- case DT_JMPREL: g_map.jmprel = d->d_val; break;
- case DT_PLTRELSZ: g_map.pltrelsz = d->d_val; break;
- case DT_SYMTAB: g_map.symtab = d->d_val; break;
- case DT_STRTAB: g_map.strtab = d->d_val; break;
- case DT_REL: g_map.rel = d->d_val; break;
- case DT_RELSZ: g_map.relsz = d->d_val; break;
- }
- }
-
- /* Scan for shared library info BEFORE resolving relocations */
- find_shlib_info();
-
- /* Set up GOT for lazy binding:
- * GOT[0] = _DYNAMIC (already set by linker)
- * GOT[1] = link_map pointer
- * GOT[2] = _dl_runtime_resolve address */
- if (pltgot && g_map.jmprel) {
- uint32_t* got = (uint32_t*)(pltgot + g_map.l_addr);
- got[1] = (uint32_t)&g_map;
- got[2] = (uint32_t)&_dl_runtime_resolve;
- }
-
- /* Process eager relocations (R_386_GLOB_DAT, R_386_COPY) */
- if (g_map.rel && g_map.relsz) {
- uint32_t nrel = g_map.relsz / sizeof(struct elf32_rel);
- const struct elf32_rel* rtab =
- (const struct elf32_rel*)(g_map.rel + g_map.l_addr);
- for (uint32_t j = 0; j < nrel; j++) {
- uint32_t type = ELF32_R_TYPE(rtab[j].r_info);
- uint32_t sidx = ELF32_R_SYM(rtab[j].r_info);
- uint32_t* target = (uint32_t*)(rtab[j].r_offset + g_map.l_addr);
- if (type == R_386_GLOB_DAT || type == R_386_JMP_SLOT) {
- const struct elf32_sym* s =
- &((const struct elf32_sym*)g_map.symtab)[sidx];
- uint32_t addr = 0;
- if (s->st_value != 0)
- addr = s->st_value + g_map.l_addr;
- else {
- const char* nm = (const char*)g_map.strtab + s->st_name;
- addr = shlib_lookup(nm, &g_map);
- }
- if (addr) *target = addr;
- } else if (type == R_386_COPY && sidx) {
- const struct elf32_sym* s =
- &((const struct elf32_sym*)g_map.symtab)[sidx];
- const char* nm = (const char*)g_map.strtab + s->st_name;
- uint32_t src = shlib_lookup(nm, &g_map);
- if (src && s->st_size > 0) {
- const uint8_t* sp = (const uint8_t*)src;
- uint8_t* dp = (uint8_t*)target;
- for (uint32_t k = 0; k < s->st_size; k++)
- dp[k] = sp[k];
- }
- }
- }
- }
- break;
- }
- }
- }
-
- /* Restore the original stack pointer so the real program's _start
- * sees the correct layout: [argc] [argv...] [NULL] [envp...] [NULL] [auxv...]
- * Then jump to the program entry point. */
- __asm__ volatile(
- "mov %0, %%esp\n"
- "jmp *%1\n"
- :: "r"(initial_sp), "r"(at_entry)
- : "memory"
- );
- __builtin_unreachable();
-}
+++ /dev/null
-/* AdrOS ln utility */
-#include <stdio.h>
-#include <string.h>
-#include <unistd.h>
-
-int main(int argc, char** argv) {
- int sflag = 0;
- int start = 1;
-
- if (argc > 1 && strcmp(argv[1], "-s") == 0) {
- sflag = 1;
- start = 2;
- }
-
- if (argc - start < 2) {
- fprintf(stderr, "Usage: ln [-s] <target> <linkname>\n");
- return 1;
- }
-
- int r;
- if (sflag) {
- r = symlink(argv[start], argv[start + 1]);
- } else {
- r = link(argv[start], argv[start + 1]);
- }
-
- if (r < 0) {
- fprintf(stderr, "ln: failed to create %slink '%s' -> '%s'\n",
- sflag ? "symbolic " : "", argv[start + 1], argv[start]);
- return 1;
- }
- return 0;
-}
+++ /dev/null
-/* AdrOS ls utility */
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <unistd.h>
-#include <fcntl.h>
-#include <dirent.h>
-#include <sys/stat.h>
-
-static int aflag = 0; /* -a: show hidden files */
-static int lflag = 0; /* -l: long format */
-
-#define LS_MAX_ENTRIES 512
-
-struct ls_entry {
- char name[256];
- unsigned char type;
-};
-
-static struct ls_entry entries[LS_MAX_ENTRIES];
-
-static int cmp_entry(const void* a, const void* b) {
- return strcmp(((const struct ls_entry*)a)->name,
- ((const struct ls_entry*)b)->name);
-}
-
-static void ls_dir(const char* path) {
- int fd = open(path, O_RDONLY);
- if (fd < 0) {
- fprintf(stderr, "ls: cannot access '%s': No such file or directory\n", path);
- return;
- }
-
- int count = 0;
- char buf[2048];
- int rc;
- while ((rc = getdents(fd, buf, sizeof(buf))) > 0) {
- int off = 0;
- while (off < rc) {
- struct dirent* d = (struct dirent*)(buf + off);
- if (d->d_reclen == 0) break;
-
- if (!aflag && d->d_name[0] == '.') {
- off += d->d_reclen;
- continue;
- }
-
- if (count < LS_MAX_ENTRIES) {
- strncpy(entries[count].name, d->d_name, 255);
- entries[count].name[255] = '\0';
- entries[count].type = d->d_type;
- count++;
- }
- off += d->d_reclen;
- }
- }
- close(fd);
-
- qsort(entries, count, sizeof(struct ls_entry), cmp_entry);
-
- for (int i = 0; i < count; i++) {
- if (lflag) {
- char fullpath[512];
- size_t plen = strlen(path);
- if (plen > 0 && path[plen - 1] == '/')
- snprintf(fullpath, sizeof(fullpath), "%s%s", path, entries[i].name);
- else
- snprintf(fullpath, sizeof(fullpath), "%s/%s", path, entries[i].name);
-
- struct stat st;
- int have_stat = (stat(fullpath, &st) == 0);
-
- char type = '-';
- if (entries[i].type == DT_DIR) type = 'd';
- else if (entries[i].type == DT_CHR) type = 'c';
- else if (entries[i].type == DT_LNK) type = 'l';
- else if (entries[i].type == DT_BLK) type = 'b';
-
- char perms[10];
- if (have_stat) {
- unsigned m = (unsigned)st.st_mode;
- perms[0] = (m & S_IRUSR) ? 'r' : '-';
- perms[1] = (m & S_IWUSR) ? 'w' : '-';
- perms[2] = (m & S_IXUSR) ? 'x' : '-';
- perms[3] = (m & S_IRGRP) ? 'r' : '-';
- perms[4] = (m & S_IWGRP) ? 'w' : '-';
- perms[5] = (m & S_IXGRP) ? 'x' : '-';
- perms[6] = (m & S_IROTH) ? 'r' : '-';
- perms[7] = (m & S_IWOTH) ? 'w' : '-';
- perms[8] = (m & S_IXOTH) ? 'x' : '-';
- perms[9] = '\0';
- } else {
- strcpy(perms, "---------");
- }
-
- unsigned long sz = have_stat ? (unsigned long)st.st_size : 0;
- unsigned nlink = have_stat ? (unsigned)st.st_nlink : 1;
-
- printf("%c%s %2u root root %8lu %s\n",
- type, perms, nlink, sz, entries[i].name);
- } else {
- printf("%s\n", entries[i].name);
- }
- }
-}
-
-int main(int argc, char** argv) {
- int npath = 0;
- const char* paths[64];
-
- for (int i = 1; i < argc; i++) {
- if (argv[i][0] == '-' && argv[i][1]) {
- const char* f = argv[i] + 1;
- while (*f) {
- if (*f == 'a') aflag = 1;
- else if (*f == 'l') lflag = 1;
- else {
- fprintf(stderr, "ls: invalid option -- '%c'\n", *f);
- return 1;
- }
- f++;
- }
- } else {
- if (npath < 64) paths[npath++] = argv[i];
- }
- }
-
- if (npath == 0) {
- ls_dir(".");
- } else {
- for (int i = 0; i < npath; i++) {
- if (npath > 1) printf("%s:\n", paths[i]);
- ls_dir(paths[i]);
- if (npath > 1 && i < npath - 1) printf("\n");
- }
- }
-
- return 0;
-}
+++ /dev/null
-/* AdrOS mkdir utility */
-#include <stdio.h>
-#include <string.h>
-#include <unistd.h>
-
-static int pflag = 0; /* -p: create parent directories */
-
-static int mkdir_p(const char* path) {
- char tmp[256];
- size_t len = strlen(path);
- if (len >= sizeof(tmp)) return -1;
- strcpy(tmp, path);
-
- for (char* p = tmp + 1; *p; p++) {
- if (*p == '/') {
- *p = '\0';
- mkdir(tmp); /* ignore errors — parent may already exist */
- *p = '/';
- }
- }
- return mkdir(tmp);
-}
-
-int main(int argc, char** argv) {
- if (argc <= 1) {
- fprintf(stderr, "mkdir: missing operand\n");
- return 1;
- }
-
- int rc = 0;
- int start = 1;
-
- for (int i = 1; i < argc; i++) {
- if (argv[i][0] == '-' && argv[i][1]) {
- const char* f = argv[i] + 1;
- while (*f) {
- if (*f == 'p') pflag = 1;
- else {
- fprintf(stderr, "mkdir: invalid option -- '%c'\n", *f);
- return 1;
- }
- f++;
- }
- start = i + 1;
- }
- }
-
- for (int i = start; i < argc; i++) {
- int r;
- if (pflag) {
- r = mkdir_p(argv[i]);
- } else {
- r = mkdir(argv[i]);
- }
- if (r < 0) {
- fprintf(stderr, "mkdir: cannot create directory '%s'\n", argv[i]);
- rc = 1;
- }
- }
- return rc;
-}
+++ /dev/null
-/* AdrOS mount utility — mount filesystems or display mounts */
-#include <stdio.h>
-#include <string.h>
-#include <unistd.h>
-#include <fcntl.h>
-#include <syscall.h>
-#include <errno.h>
-
-static void show_mounts(void) {
- int fd = open("/proc/mounts", O_RDONLY);
- if (fd >= 0) {
- char buf[1024];
- int n;
- while ((n = read(fd, buf, sizeof(buf))) > 0)
- write(STDOUT_FILENO, buf, (size_t)n);
- close(fd);
- } else {
- printf("tmpfs on / type overlayfs (rw)\n");
- printf("devfs on /dev type devfs (rw)\n");
- printf("procfs on /proc type procfs (ro)\n");
- }
-}
-
-int main(int argc, char** argv) {
- if (argc < 2) {
- show_mounts();
- return 0;
- }
-
- const char* fstype = "diskfs";
- const char* device = NULL;
- const char* mountpoint = NULL;
-
- /* Parse options first, then collect positional args */
- int i;
- for (i = 1; i < argc; i++) {
- if (strcmp(argv[i], "-t") == 0 && i + 1 < argc) {
- fstype = argv[++i];
- } else if (!device) {
- device = argv[i];
- } else if (!mountpoint) {
- mountpoint = argv[i];
- }
- }
-
- if (!device || !mountpoint) {
- fprintf(stderr, "usage: mount [-t fstype] device mountpoint\n");
- return 1;
- }
-
- int rc = __syscall_ret(_syscall3(SYS_MOUNT, (int)device, (int)mountpoint, (int)fstype));
- if (rc < 0) {
- fprintf(stderr, "mount: mounting %s on %s failed: %d\n", device, mountpoint, rc);
- return 1;
- }
- return 0;
-}
+++ /dev/null
-/* AdrOS mv utility */
-#include <stdio.h>
-#include <unistd.h>
-#include <fcntl.h>
-#include <string.h>
-
-int main(int argc, char** argv) {
- if (argc < 3) {
- fprintf(stderr, "Usage: mv <source> <dest>\n");
- return 1;
- }
-
- /* Try rename first (same filesystem) */
- if (rename(argv[1], argv[2]) == 0)
- return 0;
-
- /* Fallback: copy + unlink */
- int src = open(argv[1], O_RDONLY);
- if (src < 0) {
- fprintf(stderr, "mv: cannot open '%s'\n", argv[1]);
- return 1;
- }
-
- int dst = open(argv[2], O_WRONLY | O_CREAT | O_TRUNC, 0644);
- if (dst < 0) {
- fprintf(stderr, "mv: cannot create '%s'\n", argv[2]);
- close(src);
- return 1;
- }
-
- char buf[4096];
- int r;
- while ((r = read(src, buf, sizeof(buf))) > 0) {
- if (write(dst, buf, (size_t)r) != r) {
- fprintf(stderr, "mv: write error\n");
- close(src); close(dst);
- return 1;
- }
- }
-
- close(src);
- close(dst);
- unlink(argv[1]);
- return 0;
-}
+++ /dev/null
-/* Shared library function for PLT/GOT lazy binding test.
- * Compiled as a shared object (libpietest.so), loaded at SHLIB_BASE by kernel.
- * The main PIE binary calls test_add() through PLT — resolved lazily by ld.so. */
-
-int test_add(int a, int b) {
- return a + b;
-}
+++ /dev/null
-/* PIE test binary for PLT/GOT lazy binding verification.
- * Calls test_add() from libpietest.so through PLT — resolved lazily by ld.so.
- * Built as: i686-elf-ld -pie --dynamic-linker=/lib/ld.so */
-
-static inline void sys_exit(int code) {
- __asm__ volatile("int $0x80" :: "a"(2), "b"(code) : "memory");
-}
-
-static inline int sys_write(int fd, const void* buf, unsigned len) {
- int ret;
- __asm__ volatile("int $0x80" : "=a"(ret) : "a"(1), "b"(fd), "c"(buf), "d"(len) : "memory");
- return ret;
-}
-
-extern int test_add(int a, int b);
-
-void _start(void) {
- int r = test_add(38, 4);
- if (r == 42) {
- sys_write(1, "[init] lazy PLT OK\n", 19);
- } else {
- sys_write(1, "[init] lazy PLT FAIL\n", 21);
- }
-
- /* Call again — this time GOT is already patched, tests direct path */
- r = test_add(100, 23);
- if (r == 123) {
- sys_write(1, "[init] PLT cached OK\n", 21);
- } else {
- sys_write(1, "[init] PLT cached FAIL\n", 23);
- }
-
- sys_exit(0);
-}
+++ /dev/null
-/* AdrOS printenv utility — print environment variables */
-#include <stdio.h>
-#include <string.h>
-
-extern char** __environ;
-
-int main(int argc, char** argv) {
- if (!__environ) return 1;
- if (argc <= 1) {
- for (int i = 0; __environ[i]; i++)
- printf("%s\n", __environ[i]);
- return 0;
- }
- for (int i = 1; i < argc; i++) {
- int found = 0;
- int nlen = (int)strlen(argv[i]);
- for (int j = 0; __environ[j]; j++) {
- if (strncmp(__environ[j], argv[i], (size_t)nlen) == 0 && __environ[j][nlen] == '=') {
- printf("%s\n", __environ[j] + nlen + 1);
- found = 1;
- break;
- }
- }
- if (!found) return 1;
- }
- return 0;
-}
+++ /dev/null
-/* AdrOS ps utility — list processes from /proc */
-#include <stdio.h>
-#include <string.h>
-#include <unistd.h>
-#include <fcntl.h>
-#include <dirent.h>
-
-static int is_digit(char c) { return c >= '0' && c <= '9'; }
-
-int main(void) {
- printf(" PID CMD\n");
- int fd = open("/proc", O_RDONLY);
- if (fd < 0) {
- fprintf(stderr, "ps: cannot open /proc\n");
- return 1;
- }
- char buf[512];
- int rc;
- while ((rc = getdents(fd, buf, sizeof(buf))) > 0) {
- int off = 0;
- while (off < rc) {
- struct dirent* d = (struct dirent*)(buf + off);
- if (d->d_reclen == 0) break;
- if (is_digit(d->d_name[0])) {
- char path[64];
- snprintf(path, sizeof(path), "/proc/%s/cmdline", d->d_name);
- int cfd = open(path, O_RDONLY);
- char cmd[64] = "?";
- if (cfd >= 0) {
- int n = read(cfd, cmd, sizeof(cmd) - 1);
- if (n > 0) {
- cmd[n] = '\0';
- while (n > 0 && (cmd[n-1] == '\n' || cmd[n-1] == '\0')) {
- cmd[--n] = '\0';
- }
- }
- if (n <= 0) strcpy(cmd, "[kernel]");
- close(cfd);
- }
- printf("%5s %s\n", d->d_name, cmd);
- }
- off += d->d_reclen;
- }
- }
- close(fd);
- return 0;
-}
+++ /dev/null
-/* AdrOS pwd utility — print working directory */
-#include <stdio.h>
-#include <unistd.h>
-
-int main(void) {
- char buf[256];
- if (getcwd(buf, sizeof(buf)) >= 0)
- printf("%s\n", buf);
- else {
- fprintf(stderr, "pwd: error\n");
- return 1;
- }
- return 0;
-}
+++ /dev/null
-/* AdrOS rm utility */
-#include <stdio.h>
-#include <string.h>
-#include <unistd.h>
-
-static int rflag = 0; /* -r: recursive */
-static int fflag = 0; /* -f: force (no errors) */
-static int dflag = 0; /* -d: remove empty directories */
-
-int main(int argc, char** argv) {
- if (argc <= 1) {
- fprintf(stderr, "rm: missing operand\n");
- return 1;
- }
-
- int start = 1;
- for (int i = 1; i < argc; i++) {
- if (argv[i][0] == '-' && argv[i][1]) {
- const char* f = argv[i] + 1;
- while (*f) {
- if (*f == 'r' || *f == 'R') rflag = 1;
- else if (*f == 'f') fflag = 1;
- else if (*f == 'd') dflag = 1;
- else {
- fprintf(stderr, "rm: invalid option -- '%c'\n", *f);
- return 1;
- }
- f++;
- }
- start = i + 1;
- } else {
- break;
- }
- }
-
- int rc = 0;
- for (int i = start; i < argc; i++) {
- int r = unlink(argv[i]);
- if (r < 0 && (rflag || dflag)) {
- r = rmdir(argv[i]);
- }
- if (r < 0 && !fflag) {
- fprintf(stderr, "rm: cannot remove '%s'\n", argv[i]);
- rc = 1;
- }
- }
- return rc;
-}
+++ /dev/null
-/* AdrOS rmdir utility — remove empty directories */
-#include <stdio.h>
-#include <unistd.h>
-
-int main(int argc, char** argv) {
- if (argc <= 1) {
- fprintf(stderr, "rmdir: missing operand\n");
- return 1;
- }
- int rc = 0;
- for (int i = 1; i < argc; i++) {
- if (rmdir(argv[i]) < 0) {
- fprintf(stderr, "rmdir: failed to remove '%s'\n", argv[i]);
- rc = 1;
- }
- }
- return rc;
-}
+++ /dev/null
-/* AdrOS sed utility — minimal stream editor (s/pattern/replacement/g only) */
-#include <stdio.h>
-#include <string.h>
-#include <unistd.h>
-#include <fcntl.h>
-
-static int match_at(const char* s, const char* pat, int patlen) {
- for (int i = 0; i < patlen; i++) {
- if (s[i] == '\0' || s[i] != pat[i]) return 0;
- }
- return 1;
-}
-
-static void sed_substitute(const char* line, const char* pat, int patlen,
- const char* rep, int replen, int global) {
- const char* p = line;
- while (*p) {
- if (match_at(p, pat, patlen)) {
- write(STDOUT_FILENO, rep, replen);
- p += patlen;
- if (!global) {
- write(STDOUT_FILENO, p, strlen(p));
- return;
- }
- } else {
- write(STDOUT_FILENO, p, 1);
- p++;
- }
- }
-}
-
-static int parse_s_cmd(const char* expr, char* pat, int* patlen,
- char* rep, int* replen, int* global) {
- if (expr[0] != 's' || expr[1] == '\0') return -1;
- char delim = expr[1];
- const char* p = expr + 2;
- int pi = 0;
- while (*p && *p != delim && pi < 255) pat[pi++] = *p++;
- pat[pi] = '\0'; *patlen = pi;
- if (*p != delim) return -1;
- p++;
- int ri = 0;
- while (*p && *p != delim && ri < 255) rep[ri++] = *p++;
- rep[ri] = '\0'; *replen = ri;
- *global = 0;
- if (*p == delim) { p++; if (*p == 'g') *global = 1; }
- return 0;
-}
-
-int main(int argc, char** argv) {
- if (argc < 2) {
- fprintf(stderr, "Usage: sed 's/pattern/replacement/[g]' [file]\n");
- return 1;
- }
-
- char pat[256], rep[256];
- int patlen, replen, global;
- int ei = 1;
- if (strcmp(argv[1], "-e") == 0 && argc > 2) ei = 2;
-
- if (parse_s_cmd(argv[ei], pat, &patlen, rep, &replen, &global) < 0) {
- fprintf(stderr, "sed: invalid expression: %s\n", argv[ei]);
- return 1;
- }
-
- int fd = STDIN_FILENO;
- if (argc > ei + 1) {
- fd = open(argv[ei + 1], O_RDONLY);
- if (fd < 0) {
- fprintf(stderr, "sed: %s: No such file or directory\n", argv[ei + 1]);
- return 1;
- }
- }
-
- char line[4096];
- int li = 0;
- char c;
- while (read(fd, &c, 1) == 1) {
- if (c == '\n') {
- line[li] = '\0';
- sed_substitute(line, pat, patlen, rep, replen, global);
- write(STDOUT_FILENO, "\n", 1);
- li = 0;
- } else if (li < (int)sizeof(line) - 1) {
- line[li++] = c;
- }
- }
- if (li > 0) {
- line[li] = '\0';
- sed_substitute(line, pat, patlen, rep, replen, global);
- }
-
- if (fd != STDIN_FILENO) close(fd);
- return 0;
-}
+++ /dev/null
-/* AdrOS POSIX-like shell (/bin/sh)
- *
- * Features:
- * - Variable assignment (VAR=value) and expansion ($VAR)
- * - Environment variables (export VAR=value)
- * - Line editing (left/right arrow keys)
- * - Command history (up/down arrow keys)
- * - Pipes (cmd1 | cmd2 | cmd3)
- * - Redirections (< > >>)
- * - Operators: ; && || &
- * - Job control: CTRL+C (SIGINT), CTRL+Z (SIGTSTP), background (&)
- * - Builtins: cd, exit, echo, export, unset, set, pwd, type
- * - PATH-based command resolution
- * - Quote handling (single and double quotes)
- */
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <unistd.h>
-#include <fcntl.h>
-#include <dirent.h>
-#include <termios.h>
-#include <signal.h>
-#include <sys/ioctl.h>
-#include <sys/wait.h>
-
-static struct termios orig_termios;
-
-static void tty_raw_mode(void) {
- tcgetattr(STDIN_FILENO, &orig_termios);
- struct termios raw = orig_termios;
- raw.c_lflag &= ~(ICANON | ECHO | ISIG);
- raw.c_cc[VMIN] = 1;
- raw.c_cc[VTIME] = 0;
- tcsetattr(STDIN_FILENO, TCSANOW, &raw);
-}
-
-static void tty_restore(void) {
- tcsetattr(STDIN_FILENO, TCSANOW, &orig_termios);
-}
-
-#define LINE_MAX 512
-#define MAX_ARGS 64
-#define MAX_VARS 64
-#define HIST_SIZE 32
-
-/* ---- Shell variables ---- */
-
-static struct {
- char name[64];
- char value[256];
- int exported;
-} vars[MAX_VARS];
-static int nvar = 0;
-
-static int last_status = 0;
-
-static const char* var_get(const char* name) {
- for (int i = 0; i < nvar; i++)
- if (strcmp(vars[i].name, name) == 0) return vars[i].value;
- return getenv(name);
-}
-
-static void var_set(const char* name, const char* value, int exported) {
- for (int i = 0; i < nvar; i++) {
- if (strcmp(vars[i].name, name) == 0) {
- strncpy(vars[i].value, value, 255);
- vars[i].value[255] = '\0';
- if (exported) vars[i].exported = 1;
- return;
- }
- }
- if (nvar < MAX_VARS) {
- strncpy(vars[nvar].name, name, 63);
- vars[nvar].name[63] = '\0';
- strncpy(vars[nvar].value, value, 255);
- vars[nvar].value[255] = '\0';
- vars[nvar].exported = exported;
- nvar++;
- }
-}
-
-static void var_unset(const char* name) {
- for (int i = 0; i < nvar; i++) {
- if (strcmp(vars[i].name, name) == 0) {
- vars[i] = vars[--nvar];
- return;
- }
- }
-}
-
-/* Build envp array from exported variables */
-static char env_buf[MAX_VARS][320];
-static char* envp_arr[MAX_VARS + 1];
-
-static char** build_envp(void) {
- int n = 0;
- for (int i = 0; i < nvar && n < MAX_VARS; i++) {
- if (!vars[i].exported) continue;
- snprintf(env_buf[n], sizeof(env_buf[n]), "%s=%s",
- vars[i].name, vars[i].value);
- envp_arr[n] = env_buf[n];
- n++;
- }
- envp_arr[n] = NULL;
- return envp_arr;
-}
-
-/* ---- Command history ---- */
-
-static char history[HIST_SIZE][LINE_MAX];
-static int hist_count = 0;
-static int hist_pos = 0;
-
-static void hist_add(const char* line) {
- if (line[0] == '\0') return;
- if (hist_count > 0 && strcmp(history[(hist_count - 1) % HIST_SIZE], line) == 0)
- return;
- strncpy(history[hist_count % HIST_SIZE], line, LINE_MAX - 1);
- history[hist_count % HIST_SIZE][LINE_MAX - 1] = '\0';
- hist_count++;
-}
-
-/* ---- Line editing ---- */
-
-static char line[LINE_MAX];
-
-static void term_write(const char* s, int n) {
- write(STDOUT_FILENO, s, (size_t)n);
-}
-
-/* ---- Tab completion ---- */
-
-static int tab_complete(char* buf, int* p_pos, int* p_len) {
- int pos = *p_pos;
- int len = *p_len;
-
- /* Find the start of the current word */
- int wstart = pos;
- while (wstart > 0 && buf[wstart - 1] != ' ' && buf[wstart - 1] != '\t')
- wstart--;
-
- char prefix[128];
- int plen = pos - wstart;
- if (plen <= 0 || plen >= (int)sizeof(prefix)) return 0;
- memcpy(prefix, buf + wstart, (size_t)plen);
- prefix[plen] = '\0';
-
- /* Determine if this is a command (first word) or filename */
- int is_cmd = 1;
- for (int i = 0; i < wstart; i++) {
- if (buf[i] != ' ' && buf[i] != '\t') { is_cmd = 0; break; }
- }
-
- char match[128];
- match[0] = '\0';
- int nmatches = 0;
-
- /* Split prefix into directory part and name part for file completion */
- char dirpath[128] = ".";
- const char* namepfx = prefix;
- char* lastsep = NULL;
- for (char* p = prefix; *p; p++) {
- if (*p == '/') lastsep = p;
- }
- if (lastsep) {
- int dlen = (int)(lastsep - prefix);
- if (dlen == 0) { dirpath[0] = '/'; dirpath[1] = '\0'; }
- else { memcpy(dirpath, prefix, (size_t)dlen); dirpath[dlen] = '\0'; }
- namepfx = lastsep + 1;
- }
- int nplen = (int)strlen(namepfx);
-
- if (!is_cmd || lastsep) {
- /* File/directory completion */
- int fd = open(dirpath, 0);
- if (fd >= 0) {
- char dbuf[512];
- int rc;
- while ((rc = getdents(fd, dbuf, sizeof(dbuf))) > 0) {
- int off = 0;
- while (off < rc) {
- struct dirent* d = (struct dirent*)(dbuf + off);
- if (d->d_reclen == 0) break;
- if (d->d_name[0] != '.' || nplen > 0) {
- int nlen = (int)strlen(d->d_name);
- if (nlen >= nplen && memcmp(d->d_name, namepfx, (size_t)nplen) == 0) {
- if (nmatches == 0) strcpy(match, d->d_name);
- nmatches++;
- }
- }
- off += d->d_reclen;
- }
- }
- close(fd);
- }
- }
-
- if (is_cmd && !lastsep) {
- /* Command completion: search PATH directories + builtins */
- static const char* builtins[] = {
- "cd", "exit", "echo", "export", "unset", "set", "pwd", "type", NULL
- };
- for (int i = 0; builtins[i]; i++) {
- int blen = (int)strlen(builtins[i]);
- if (blen >= plen && memcmp(builtins[i], prefix, (size_t)plen) == 0) {
- if (nmatches == 0) strcpy(match, builtins[i]);
- nmatches++;
- }
- }
- const char* path_env = var_get("PATH");
- if (!path_env) path_env = "/bin:/sbin:/usr/bin";
- char pathcopy[512];
- strncpy(pathcopy, path_env, sizeof(pathcopy) - 1);
- pathcopy[sizeof(pathcopy) - 1] = '\0';
- char* save = pathcopy;
- char* dir;
- while ((dir = save) != NULL) {
- char* sep = strchr(save, ':');
- if (sep) { *sep = '\0'; save = sep + 1; } else save = NULL;
- int fd = open(dir, 0);
- if (fd < 0) continue;
- char dbuf[512];
- int rc;
- while ((rc = getdents(fd, dbuf, sizeof(dbuf))) > 0) {
- int off = 0;
- while (off < rc) {
- struct dirent* d = (struct dirent*)(dbuf + off);
- if (d->d_reclen == 0) break;
- int nlen = (int)strlen(d->d_name);
- if (nlen >= plen && memcmp(d->d_name, prefix, (size_t)plen) == 0) {
- if (nmatches == 0) strcpy(match, d->d_name);
- nmatches++;
- }
- off += d->d_reclen;
- }
- }
- close(fd);
- }
- }
-
- if (nmatches != 1) return 0;
-
- /* Insert the completion suffix */
- int mlen = (int)strlen(match);
- int suffix_len = is_cmd && !lastsep ? mlen - plen : mlen - nplen;
- const char* suffix = is_cmd && !lastsep ? match + plen : match + nplen;
- if (suffix_len <= 0 || len + suffix_len >= LINE_MAX - 1) return 0;
-
- memmove(buf + pos + suffix_len, buf + pos, (size_t)(len - pos));
- memcpy(buf + pos, suffix, (size_t)suffix_len);
- len += suffix_len;
- buf[len] = '\0';
- term_write(buf + pos, len - pos);
- pos += suffix_len;
- for (int i = 0; i < len - pos; i++) term_write("\b", 1);
- *p_pos = pos;
- *p_len = len;
- return 1;
-}
-
-static int read_line_edit(void) {
- int pos = 0;
- int len = 0;
- hist_pos = hist_count;
-
- memset(line, 0, LINE_MAX);
-
- while (len < LINE_MAX - 1) {
- char c;
- int r = read(STDIN_FILENO, &c, 1);
- if (r <= 0) {
- if (len == 0) return -1;
- break;
- }
-
- if (c == '\n' || c == '\r') {
- term_write("\n", 1);
- break;
- }
-
- /* Backspace / DEL */
- if (c == '\b' || c == 127) {
- if (pos > 0) {
- memmove(line + pos - 1, line + pos, (size_t)(len - pos));
- pos--; len--;
- line[len] = '\0';
- /* Redraw: move cursor back, print rest, clear tail */
- term_write("\b", 1);
- term_write(line + pos, len - pos);
- term_write(" \b", 2);
- for (int i = 0; i < len - pos; i++) term_write("\b", 1);
- }
- continue;
- }
-
- /* Tab = autocomplete */
- if (c == '\t') {
- tab_complete(line, &pos, &len);
- continue;
- }
-
- /* Ctrl+D = EOF */
- if (c == 4) {
- if (len == 0) return -1;
- continue;
- }
-
- /* Ctrl+C = cancel line */
- if (c == 3) {
- term_write("^C\n", 3);
- line[0] = '\0';
- return 0;
- }
-
- /* Ctrl+Z = ignored at prompt (no foreground job to suspend) */
- if (c == 26) {
- continue;
- }
-
- /* Ctrl+A = beginning of line */
- if (c == 1) {
- while (pos > 0) { term_write("\b", 1); pos--; }
- continue;
- }
-
- /* Ctrl+E = end of line */
- if (c == 5) {
- term_write(line + pos, len - pos);
- pos = len;
- continue;
- }
-
- /* Ctrl+U = clear line */
- if (c == 21) {
- while (pos > 0) { term_write("\b", 1); pos--; }
- for (int i = 0; i < len; i++) term_write(" ", 1);
- for (int i = 0; i < len; i++) term_write("\b", 1);
- len = 0; pos = 0;
- line[0] = '\0';
- continue;
- }
-
- /* Escape sequences (arrow keys) */
- if (c == 27) {
- char seq[2];
- if (read(STDIN_FILENO, &seq[0], 1) <= 0) continue;
- if (seq[0] != '[') continue;
- if (read(STDIN_FILENO, &seq[1], 1) <= 0) continue;
-
- if (seq[1] >= '0' && seq[1] <= '9') {
- /* Extended sequence like \x1b[3~ (DELETE), \x1b[1~ (Home), \x1b[4~ (End) */
- char trail;
- if (read(STDIN_FILENO, &trail, 1) <= 0) continue;
- if (trail == '~') {
- if (seq[1] == '3') {
- /* DELETE key — delete char at cursor */
- if (pos < len) {
- memmove(line + pos, line + pos + 1, (size_t)(len - pos - 1));
- len--;
- line[len] = '\0';
- term_write(line + pos, len - pos);
- term_write(" \b", 2);
- for (int i = 0; i < len - pos; i++) term_write("\b", 1);
- }
- } else if (seq[1] == '1') {
- /* Home */
- while (pos > 0) { term_write("\b", 1); pos--; }
- } else if (seq[1] == '4') {
- /* End */
- term_write(line + pos, len - pos);
- pos = len;
- }
- }
- continue;
- }
-
- switch (seq[1]) {
- case 'A': /* Up arrow — previous history */
- if (hist_pos > 0 && hist_pos > hist_count - HIST_SIZE) {
- hist_pos--;
- /* Clear current line */
- while (pos > 0) { term_write("\b", 1); pos--; }
- for (int i = 0; i < len; i++) term_write(" ", 1);
- for (int i = 0; i < len; i++) term_write("\b", 1);
- /* Load history entry */
- strcpy(line, history[hist_pos % HIST_SIZE]);
- len = (int)strlen(line);
- pos = len;
- term_write(line, len);
- }
- break;
- case 'B': /* Down arrow — next history */
- if (hist_pos < hist_count) {
- hist_pos++;
- while (pos > 0) { term_write("\b", 1); pos--; }
- for (int i = 0; i < len; i++) term_write(" ", 1);
- for (int i = 0; i < len; i++) term_write("\b", 1);
- if (hist_pos < hist_count) {
- strcpy(line, history[hist_pos % HIST_SIZE]);
- } else {
- line[0] = '\0';
- }
- len = (int)strlen(line);
- pos = len;
- term_write(line, len);
- }
- break;
- case 'C': /* Right arrow */
- if (pos < len) { term_write(line + pos, 1); pos++; }
- break;
- case 'D': /* Left arrow */
- if (pos > 0) { term_write("\b", 1); pos--; }
- break;
- case 'H': /* Home */
- while (pos > 0) { term_write("\b", 1); pos--; }
- break;
- case 'F': /* End */
- term_write(line + pos, len - pos);
- pos = len;
- break;
- }
- continue;
- }
-
- /* Normal printable character */
- if (c >= ' ' && c <= '~') {
- memmove(line + pos + 1, line + pos, (size_t)(len - pos));
- line[pos] = c;
- len++; line[len] = '\0';
- term_write(line + pos, len - pos);
- pos++;
- for (int i = 0; i < len - pos; i++) term_write("\b", 1);
- }
- }
-
- line[len] = '\0';
- return len;
-}
-
-/* ---- Variable expansion ---- */
-
-static void expand_vars(const char* src, char* dst, int maxlen) {
- int di = 0;
- while (*src && di < maxlen - 1) {
- if (*src == '$') {
- src++;
- if (*src == '?') {
- di += snprintf(dst + di, (size_t)(maxlen - di), "%d", last_status);
- src++;
- } else if (*src == '{') {
- src++;
- char name[64];
- int ni = 0;
- while (*src && *src != '}' && ni < 63) name[ni++] = *src++;
- name[ni] = '\0';
- if (*src == '}') src++;
- const char* val = var_get(name);
- if (val) {
- int vl = (int)strlen(val);
- if (di + vl < maxlen) { memcpy(dst + di, val, (size_t)vl); di += vl; }
- }
- } else {
- char name[64];
- int ni = 0;
- while ((*src >= 'A' && *src <= 'Z') || (*src >= 'a' && *src <= 'z') ||
- (*src >= '0' && *src <= '9') || *src == '_') {
- if (ni < 63) name[ni++] = *src;
- src++;
- }
- name[ni] = '\0';
- const char* val = var_get(name);
- if (val) {
- int vl = (int)strlen(val);
- if (di + vl < maxlen) { memcpy(dst + di, val, (size_t)vl); di += vl; }
- }
- }
- } else {
- dst[di++] = *src++;
- }
- }
- dst[di] = '\0';
-}
-
-/* ---- Argument parsing with quote handling ---- */
-
-static int parse_args(char* cmd, char** argv, int max) {
- int argc = 0;
- char* p = cmd;
- while (*p && argc < max - 1) {
- while (*p == ' ' || *p == '\t') p++;
- if (*p == '\0') break;
-
- char* out = p;
- argv[argc++] = out;
-
- while (*p && *p != ' ' && *p != '\t') {
- if (*p == '\'' ) {
- p++;
- while (*p && *p != '\'') *out++ = *p++;
- if (*p == '\'') p++;
- } else if (*p == '"') {
- p++;
- while (*p && *p != '"') *out++ = *p++;
- if (*p == '"') p++;
- } else {
- *out++ = *p++;
- }
- }
- if (*p) p++;
- *out = '\0';
- }
- argv[argc] = NULL;
- return argc;
-}
-
-/* ---- PATH resolution ---- */
-
-static char pathbuf[256];
-
-static const char* resolve(const char* cmd) {
- if (cmd[0] == '/' || cmd[0] == '.') return cmd;
-
- const char* path_env = var_get("PATH");
- if (!path_env) path_env = "/bin:/sbin:/usr/bin";
-
- char pathcopy[512];
- strncpy(pathcopy, path_env, sizeof(pathcopy) - 1);
- pathcopy[sizeof(pathcopy) - 1] = '\0';
-
- char* save = pathcopy;
- char* dir;
- while ((dir = save) != NULL) {
- char* sep = strchr(save, ':');
- if (sep) { *sep = '\0'; save = sep + 1; }
- else save = NULL;
-
- snprintf(pathbuf, sizeof(pathbuf), "%s/%s", dir, cmd);
- if (access(pathbuf, 0) == 0) return pathbuf;
- }
- return cmd;
-}
-
-/* ---- Helper: set foreground process group on controlling TTY ---- */
-
-static void set_fg_pgrp(int pgrp) {
- ioctl(STDIN_FILENO, TIOCSPGRP, &pgrp);
-}
-
-/* ---- Run a single simple command ---- */
-
-static int bg_flag = 0; /* set by caller when trailing & detected */
-
-static void run_simple(char* cmd) {
- /* Expand variables */
- char expanded[LINE_MAX];
- expand_vars(cmd, expanded, LINE_MAX);
-
- char* argv[MAX_ARGS];
- int argc = parse_args(expanded, argv, MAX_ARGS);
- if (argc == 0) return;
-
- /* Check for variable assignment (no command, just VAR=value) */
- if (argc == 1 && strchr(argv[0], '=') != NULL) {
- char* eq = strchr(argv[0], '=');
- *eq = '\0';
- var_set(argv[0], eq + 1, 0);
- last_status = 0;
- return;
- }
-
- /* Extract redirections */
- char* redir_out = NULL;
- char* redir_in = NULL;
- int append = 0;
- int heredoc_fd = -1;
- int nargc = 0;
- for (int i = 0; i < argc; i++) {
- if (strcmp(argv[i], ">>") == 0 && i + 1 < argc) {
- redir_out = argv[++i]; append = 1;
- } else if (strcmp(argv[i], ">") == 0 && i + 1 < argc) {
- redir_out = argv[++i]; append = 0;
- } else if (strcmp(argv[i], "<<") == 0 && i + 1 < argc) {
- char* delim = argv[++i];
- int dlen = (int)strlen(delim);
- if (dlen > 0 && (delim[0] == '"' || delim[0] == '\'')) {
- delim++; dlen -= 2; if (dlen < 0) dlen = 0;
- delim[dlen] = '\0';
- }
- int pfd[2];
- if (pipe(pfd) == 0) {
- tty_restore();
- char hline[LINE_MAX];
- while (1) {
- write(STDOUT_FILENO, "> ", 2);
- int hi = 0;
- char hc;
- while (read(STDIN_FILENO, &hc, 1) == 1) {
- if (hc == '\n') break;
- if (hi < LINE_MAX - 1) hline[hi++] = hc;
- }
- hline[hi] = '\0';
- if (strcmp(hline, delim) == 0) break;
- write(pfd[1], hline, hi);
- write(pfd[1], "\n", 1);
- }
- close(pfd[1]);
- heredoc_fd = pfd[0];
- tty_raw_mode();
- }
- } else if (strcmp(argv[i], "<") == 0 && i + 1 < argc) {
- redir_in = argv[++i];
- } else {
- argv[nargc++] = argv[i];
- }
- }
- argv[nargc] = NULL;
- argc = nargc;
- if (argc == 0) return;
-
- /* ---- Apply redirections for builtins too ---- */
- int saved_stdin = -1, saved_stdout = -1;
- if (heredoc_fd >= 0) {
- saved_stdin = dup(0); dup2(heredoc_fd, 0); close(heredoc_fd); heredoc_fd = -1;
- } else if (redir_in) {
- int fd = open(redir_in, O_RDONLY);
- if (fd >= 0) { saved_stdin = dup(0); dup2(fd, 0); close(fd); }
- }
- if (redir_out) {
- int flags = O_WRONLY | O_CREAT;
- flags |= append ? O_APPEND : O_TRUNC;
- int fd = open(redir_out, flags);
- if (fd >= 0) { saved_stdout = dup(1); dup2(fd, 1); close(fd); }
- }
-
- /* ---- Builtins ---- */
-
- if (strcmp(argv[0], "exit") == 0) {
- int code = argc > 1 ? atoi(argv[1]) : last_status;
- exit(code);
- }
-
- if (strcmp(argv[0], "cd") == 0) {
- const char* dir = argc > 1 ? argv[1] : var_get("HOME");
- if (!dir) dir = "/";
- if (chdir(dir) < 0)
- fprintf(stderr, "cd: %s: No such file or directory\n", dir);
- else {
- char cwd[256];
- if (getcwd(cwd, sizeof(cwd)) >= 0)
- var_set("PWD", cwd, 1);
- }
- goto restore_redir;
- }
-
- if (strcmp(argv[0], "pwd") == 0) {
- char cwd[256];
- if (getcwd(cwd, sizeof(cwd)) >= 0)
- printf("%s\n", cwd);
- else
- fprintf(stderr, "pwd: error\n");
- goto restore_redir;
- }
-
- if (strcmp(argv[0], "export") == 0) {
- for (int i = 1; i < argc; i++) {
- char* eq = strchr(argv[i], '=');
- if (eq) {
- *eq = '\0';
- var_set(argv[i], eq + 1, 1);
- } else {
- /* Export existing variable */
- for (int j = 0; j < nvar; j++)
- if (strcmp(vars[j].name, argv[i]) == 0)
- vars[j].exported = 1;
- }
- }
- goto restore_redir;
- }
-
- if (strcmp(argv[0], "unset") == 0) {
- for (int i = 1; i < argc; i++) var_unset(argv[i]);
- goto restore_redir;
- }
-
- if (strcmp(argv[0], "set") == 0) {
- for (int i = 0; i < nvar; i++)
- printf("%s=%s\n", vars[i].name, vars[i].value);
- goto restore_redir;
- }
-
- if (strcmp(argv[0], "echo") == 0) {
- int nflag = 0;
- int start = 1;
- if (argc > 1 && strcmp(argv[1], "-n") == 0) { nflag = 1; start = 2; }
- for (int i = start; i < argc; i++) {
- if (i > start) write(STDOUT_FILENO, " ", 1);
- write(STDOUT_FILENO, argv[i], strlen(argv[i]));
- }
- if (!nflag) write(STDOUT_FILENO, "\n", 1);
- goto restore_redir;
- }
-
- if (strcmp(argv[0], "type") == 0) {
- for (int i = 1; i < argc; i++) {
- if (strcmp(argv[i], "cd") == 0 || strcmp(argv[i], "exit") == 0 ||
- strcmp(argv[i], "echo") == 0 || strcmp(argv[i], "export") == 0 ||
- strcmp(argv[i], "unset") == 0 || strcmp(argv[i], "set") == 0 ||
- strcmp(argv[i], "pwd") == 0 || strcmp(argv[i], "type") == 0) {
- printf("%s is a shell builtin\n", argv[i]);
- } else {
- const char* path = resolve(argv[i]);
- if (strcmp(path, argv[i]) != 0)
- printf("%s is %s\n", argv[i], path);
- else
- printf("%s: not found\n", argv[i]);
- }
- }
- goto restore_redir;
- }
-
- /* ---- External command — restore parent redirections before fork ---- */
- const char* path = resolve(argv[0]);
- char** envp = build_envp();
-
- int pid = fork();
- if (pid < 0) { fprintf(stderr, "sh: fork failed\n"); return; }
-
- if (pid == 0) {
- /* child: own process group, restore default signals */
- setpgid(0, 0);
- struct sigaction sa;
- memset(&sa, 0, sizeof(sa));
- sa.sa_handler = (uintptr_t)SIG_DFL;
- sigaction(SIGINT, &sa, NULL);
- sigaction(SIGTSTP, &sa, NULL);
- sigaction(SIGQUIT, &sa, NULL);
-
- if (redir_in) {
- int fd = open(redir_in, O_RDONLY);
- if (fd >= 0) { dup2(fd, 0); close(fd); }
- }
- if (redir_out) {
- int flags = O_WRONLY | O_CREAT;
- flags |= append ? O_APPEND : O_TRUNC;
- int fd = open(redir_out, flags);
- if (fd >= 0) { dup2(fd, 1); close(fd); }
- }
- execve(path, (const char* const*)argv, (const char* const*)envp);
- fprintf(stderr, "sh: %s: not found\n", argv[0]);
- _exit(127);
- }
-
- /* parent */
- setpgid(pid, pid);
-
- if (bg_flag) {
- /* Background: don't wait, print job info */
- printf("[bg] %d\n", pid);
- last_status = 0;
- } else {
- /* Foreground: make child the fg process group, wait, then restore */
- set_fg_pgrp(pid);
- int st;
- waitpid(pid, &st, 0);
- set_fg_pgrp(getpgrp());
- last_status = st;
- }
- goto restore_redir;
-
-restore_redir:
- if (saved_stdout >= 0) { dup2(saved_stdout, 1); close(saved_stdout); }
- if (saved_stdin >= 0) { dup2(saved_stdin, 0); close(saved_stdin); }
-}
-
-/* ---- Pipeline support ---- */
-
-static void run_pipeline(char* cmdline) {
- /* Split on '|' (outside quotes) */
- char* cmds[8];
- int ncmds = 0;
- cmds[0] = cmdline;
- int in_sq = 0, in_dq = 0;
- for (char* p = cmdline; *p; p++) {
- if (*p == '\'' && !in_dq) in_sq = !in_sq;
- else if (*p == '"' && !in_sq) in_dq = !in_dq;
- else if (*p == '|' && !in_sq && !in_dq && ncmds < 7) {
- if (*(p + 1) == '|') { p++; continue; } /* skip || */
- *p = '\0';
- cmds[++ncmds] = p + 1;
- }
- }
- ncmds++;
-
- if (ncmds == 1) {
- run_simple(cmds[0]);
- return;
- }
-
- /* Multi-stage pipeline */
- int prev_rd = -1;
- int pids[8];
- int pgid = 0; /* pipeline process group = first child's PID */
-
- for (int i = 0; i < ncmds; i++) {
- int pfd[2] = {-1, -1};
- if (i < ncmds - 1) {
- if (pipe(pfd) < 0) {
- fprintf(stderr, "sh: pipe failed\n");
- return;
- }
- }
-
- pids[i] = fork();
- if (pids[i] < 0) { fprintf(stderr, "sh: fork failed\n"); return; }
-
- if (pids[i] == 0) {
- /* child: join pipeline process group, restore signals */
- int mypgid = pgid ? pgid : getpid();
- setpgid(0, mypgid);
- struct sigaction sa;
- memset(&sa, 0, sizeof(sa));
- sa.sa_handler = (uintptr_t)SIG_DFL;
- sigaction(SIGINT, &sa, NULL);
- sigaction(SIGTSTP, &sa, NULL);
- sigaction(SIGQUIT, &sa, NULL);
-
- if (prev_rd >= 0) { dup2(prev_rd, 0); close(prev_rd); }
- if (pfd[1] >= 0) { dup2(pfd[1], 1); close(pfd[1]); }
- if (pfd[0] >= 0) close(pfd[0]);
-
- /* Expand and parse this pipeline stage */
- char expanded[LINE_MAX];
- expand_vars(cmds[i], expanded, LINE_MAX);
- char* argv[MAX_ARGS];
- int argc = parse_args(expanded, argv, MAX_ARGS);
- if (argc == 0) _exit(0);
- const char* path = resolve(argv[0]);
- char** envp = build_envp();
- execve(path, (const char* const*)argv, (const char* const*)envp);
- fprintf(stderr, "sh: %s: not found\n", argv[0]);
- _exit(127);
- }
-
- /* parent: set pipeline pgid */
- if (i == 0) pgid = pids[0];
- setpgid(pids[i], pgid);
-
- if (prev_rd >= 0) close(prev_rd);
- if (pfd[1] >= 0) close(pfd[1]);
- prev_rd = pfd[0];
- }
-
- if (prev_rd >= 0) close(prev_rd);
-
- if (!bg_flag) {
- /* Foreground pipeline: make it fg, wait, restore */
- set_fg_pgrp(pgid);
- for (int i = 0; i < ncmds; i++) {
- int st;
- waitpid(pids[i], &st, 0);
- if (i == ncmds - 1) last_status = st;
- }
- set_fg_pgrp(getpgrp());
- } else {
- printf("[bg] %d\n", pgid);
- last_status = 0;
- }
-}
-
-/* ---- Process a command line (handle ;, &&, ||, &) ---- */
-
-enum { OP_NONE = 0, OP_SEMI, OP_AND, OP_OR, OP_BG };
-
-static void process_line(char* input) {
- char* p = input;
-
- while (*p) {
- while (*p == ' ' || *p == '\t') p++;
- if (*p == '\0') break;
-
- /* Find the next operator outside quotes */
- char* start = p;
- int in_sq = 0, in_dq = 0;
- int op = OP_NONE;
-
- while (*p) {
- if (*p == '\'' && !in_dq) { in_sq = !in_sq; p++; continue; }
- if (*p == '"' && !in_sq) { in_dq = !in_dq; p++; continue; }
- if (in_sq || in_dq) { p++; continue; }
-
- if (*p == '&' && *(p + 1) == '&') {
- *p = '\0'; p += 2; op = OP_AND; break;
- }
- if (*p == '|' && *(p + 1) == '|') {
- *p = '\0'; p += 2; op = OP_OR; break;
- }
- if (*p == ';') {
- *p = '\0'; p++; op = OP_SEMI; break;
- }
- if (*p == '&') {
- *p = '\0'; p++; op = OP_BG; break;
- }
- p++;
- }
-
- /* Trim leading whitespace from segment */
- while (*start == ' ' || *start == '\t') start++;
- if (*start != '\0') {
- if (op == OP_BG) {
- bg_flag = 1;
- run_pipeline(start);
- bg_flag = 0;
- } else {
- bg_flag = 0;
- run_pipeline(start);
- }
- }
-
- /* For &&: skip remaining commands if last failed */
- if (op == OP_AND && last_status != 0) {
- /* Skip until we hit || or ; or & or end */
- while (*p) {
- while (*p == ' ' || *p == '\t') p++;
- if (*p == '\0') break;
- int skip_sq = 0, skip_dq = 0;
- while (*p) {
- if (*p == '\'' && !skip_dq) { skip_sq = !skip_sq; p++; continue; }
- if (*p == '"' && !skip_sq) { skip_dq = !skip_dq; p++; continue; }
- if (skip_sq || skip_dq) { p++; continue; }
- if (*p == '|' && *(p + 1) == '|') break;
- if (*p == ';') break;
- if (*p == '&' && *(p + 1) != '&') break;
- if (*p == '&' && *(p + 1) == '&') { p += 2; continue; }
- p++;
- }
- break;
- }
- }
-
- /* For ||: skip remaining commands if last succeeded */
- if (op == OP_OR && last_status == 0) {
- while (*p) {
- while (*p == ' ' || *p == '\t') p++;
- if (*p == '\0') break;
- int skip_sq = 0, skip_dq = 0;
- while (*p) {
- if (*p == '\'' && !skip_dq) { skip_sq = !skip_sq; p++; continue; }
- if (*p == '"' && !skip_sq) { skip_dq = !skip_dq; p++; continue; }
- if (skip_sq || skip_dq) { p++; continue; }
- if (*p == '&' && *(p + 1) == '&') break;
- if (*p == ';') break;
- if (*p == '&' && *(p + 1) != '&') break;
- if (*p == '|' && *(p + 1) == '|') { p += 2; continue; }
- p++;
- }
- break;
- }
- }
- }
-}
-
-/* ---- Prompt ---- */
-
-static void print_prompt(void) {
- const char* user = var_get("USER");
- const char* host = var_get("HOSTNAME");
- char cwd[256];
-
- if (!user) user = "root";
- if (!host) host = "adros";
-
- if (getcwd(cwd, sizeof(cwd)) < 0) strcpy(cwd, "?");
-
- printf("%s@%s:%s$ ", user, host, cwd);
- fflush(stdout);
-}
-
-/* ---- Main ---- */
-
-int main(int argc, char** argv, char** envp) {
- (void)argc;
- (void)argv;
-
- /* Import environment variables */
- if (envp) {
- for (int i = 0; envp[i]; i++) {
- char* eq = strchr(envp[i], '=');
- if (eq) {
- char name[64];
- int nlen = (int)(eq - envp[i]);
- if (nlen > 63) nlen = 63;
- memcpy(name, envp[i], (size_t)nlen);
- name[nlen] = '\0';
- var_set(name, eq + 1, 1);
- }
- }
- }
-
- /* Set defaults */
- if (!var_get("PATH"))
- var_set("PATH", "/bin:/sbin:/usr/bin", 1);
- if (!var_get("HOME"))
- var_set("HOME", "/", 1);
-
- /* Job control: create session + process group, become fg */
- setsid();
- set_fg_pgrp(getpgrp());
-
- /* Ignore job control signals in the shell itself */
- struct sigaction sa_ign;
- memset(&sa_ign, 0, sizeof(sa_ign));
- sa_ign.sa_handler = (uintptr_t)SIG_IGN;
- sigaction(SIGINT, &sa_ign, NULL);
- sigaction(SIGTSTP, &sa_ign, NULL);
- sigaction(SIGQUIT, &sa_ign, NULL);
-
- tty_raw_mode();
-
- print_prompt();
- while (1) {
- int len = read_line_edit();
- if (len < 0) break;
- if (len > 0) {
- hist_add(line);
- tty_restore();
- process_line(line);
- tty_raw_mode();
- }
- print_prompt();
- }
-
- tty_restore();
- return last_status;
-}
+++ /dev/null
-/* AdrOS sleep utility — pause for N seconds */
-#include <stdio.h>
-#include <stdlib.h>
-#include <unistd.h>
-#include <time.h>
-
-int main(int argc, char** argv) {
- if (argc <= 1) {
- fprintf(stderr, "usage: sleep SECONDS\n");
- return 1;
- }
- int secs = atoi(argv[1]);
- if (secs > 0) {
- struct timespec ts = { .tv_sec = secs, .tv_nsec = 0 };
- nanosleep(&ts, NULL);
- }
- return 0;
-}
+++ /dev/null
-/* AdrOS sort utility */
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <unistd.h>
-#include <fcntl.h>
-
-#define MAX_LINES 1024
-#define LINE_BUF 65536
-
-static char linebuf[LINE_BUF];
-static char* lines[MAX_LINES];
-static int nlines = 0;
-
-static int rflag = 0; /* -r: reverse */
-static int nflag = 0; /* -n: numeric */
-
-static int cmp(const void* a, const void* b) {
- const char* sa = *(const char**)a;
- const char* sb = *(const char**)b;
- int r;
- if (nflag) {
- r = atoi(sa) - atoi(sb);
- } else {
- r = strcmp(sa, sb);
- }
- return rflag ? -r : r;
-}
-
-static void read_lines(int fd) {
- int total = 0;
- int r;
- while ((r = read(fd, linebuf + total, (size_t)(LINE_BUF - total - 1))) > 0) {
- total += r;
- if (total >= LINE_BUF - 1) break;
- }
- linebuf[total] = '\0';
-
- /* Split into lines */
- char* p = linebuf;
- while (*p && nlines < MAX_LINES) {
- lines[nlines++] = p;
- while (*p && *p != '\n') p++;
- if (*p == '\n') *p++ = '\0';
- }
-}
-
-int main(int argc, char** argv) {
- int start = 1;
- for (int i = 1; i < argc; i++) {
- if (argv[i][0] == '-') {
- const char* f = argv[i] + 1;
- while (*f) {
- if (*f == 'r') rflag = 1;
- else if (*f == 'n') nflag = 1;
- f++;
- }
- start = i + 1;
- } else break;
- }
-
- if (start >= argc) {
- read_lines(STDIN_FILENO);
- } else {
- for (int i = start; i < argc; i++) {
- int fd = open(argv[i], O_RDONLY);
- if (fd < 0) {
- fprintf(stderr, "sort: cannot open '%s'\n", argv[i]);
- return 1;
- }
- read_lines(fd);
- close(fd);
- }
- }
-
- /* Simple insertion sort (no qsort in ulibc yet) */
- for (int i = 1; i < nlines; i++) {
- char* key = lines[i];
- int j = i - 1;
- while (j >= 0 && cmp(&lines[j], &key) > 0) {
- lines[j + 1] = lines[j];
- j--;
- }
- lines[j + 1] = key;
- }
-
- for (int i = 0; i < nlines; i++)
- printf("%s\n", lines[i]);
-
- return 0;
-}
+++ /dev/null
-/* AdrOS stat utility — display file status */
-#include <stdio.h>
-#include <unistd.h>
-#include <fcntl.h>
-#include <sys/stat.h>
-
-int main(int argc, char** argv) {
- if (argc <= 1) {
- fprintf(stderr, "usage: stat FILE...\n");
- return 1;
- }
- int rc = 0;
- for (int i = 1; i < argc; i++) {
- struct stat st;
- if (stat(argv[i], (void*)&st) < 0) {
- fprintf(stderr, "stat: cannot stat '%s'\n", argv[i]);
- rc = 1;
- continue;
- }
- printf(" File: %s\n", argv[i]);
- printf(" Size: %u\tInode: %u\n", (unsigned)st.st_size, (unsigned)st.st_ino);
- printf(" Mode: %o\tUid: %u\tGid: %u\n", (unsigned)st.st_mode, (unsigned)st.st_uid, (unsigned)st.st_gid);
- }
- return rc;
-}
+++ /dev/null
-/* AdrOS tail utility */
-#include <stdio.h>
-#include <stdlib.h>
-#include <unistd.h>
-#include <fcntl.h>
-#include <string.h>
-
-#define TAIL_BUFSZ 8192
-
-static void tail_fd(int fd, int nlines) {
- /* Read entire file into buffer, then print last N lines */
- char buf[TAIL_BUFSZ];
- int total = 0;
- int r;
- while ((r = read(fd, buf + total, (size_t)(TAIL_BUFSZ - total))) > 0) {
- total += r;
- if (total >= TAIL_BUFSZ) break;
- }
-
- /* Count newlines from end; skip trailing newline */
- int count = 0;
- int pos = total;
- if (pos > 0 && buf[pos - 1] == '\n') pos--;
- while (pos > 0 && count < nlines) {
- pos--;
- if (buf[pos] == '\n') count++;
- }
- if (pos > 0 || (pos == 0 && buf[0] == '\n')) pos++;
-
- write(STDOUT_FILENO, buf + pos, (size_t)(total - pos));
-}
-
-int main(int argc, char** argv) {
- int nlines = 10;
- int start = 1;
-
- if (argc > 1 && argv[1][0] == '-' && argv[1][1] == 'n' && argc > 2) {
- nlines = atoi(argv[2]);
- start = 3;
- } else if (argc > 1 && argv[1][0] == '-' && argv[1][1] >= '0' && argv[1][1] <= '9') {
- nlines = atoi(argv[1] + 1);
- start = 2;
- }
-
- if (start >= argc) {
- tail_fd(STDIN_FILENO, nlines);
- } else {
- for (int i = start; i < argc; i++) {
- if (argc - start > 1) printf("==> %s <==\n", argv[i]);
- int fd = open(argv[i], O_RDONLY);
- if (fd < 0) {
- fprintf(stderr, "tail: cannot open '%s'\n", argv[i]);
- continue;
- }
- tail_fd(fd, nlines);
- close(fd);
- }
- }
- return 0;
-}
+++ /dev/null
-/* AdrOS tee utility — read stdin, write to stdout and files */
-#include <stdio.h>
-#include <string.h>
-#include <unistd.h>
-#include <fcntl.h>
-
-int main(int argc, char** argv) {
- int aflag = 0;
- int fds[16];
- int nfds = 0;
-
- for (int i = 1; i < argc && nfds < 16; i++) {
- if (strcmp(argv[i], "-a") == 0) { aflag = 1; continue; }
- int flags = O_WRONLY | O_CREAT;
- flags |= aflag ? O_APPEND : O_TRUNC;
- int fd = open(argv[i], flags, 0644);
- if (fd < 0) {
- fprintf(stderr, "tee: %s: cannot open\n", argv[i]);
- continue;
- }
- fds[nfds++] = fd;
- }
-
- char buf[4096];
- int n;
- while ((n = read(STDIN_FILENO, buf, sizeof(buf))) > 0) {
- write(STDOUT_FILENO, buf, (size_t)n);
- for (int i = 0; i < nfds; i++)
- write(fds[i], buf, (size_t)n);
- }
-
- for (int i = 0; i < nfds; i++) close(fds[i]);
- return 0;
-}
+++ /dev/null
-/* AdrOS top utility — one-shot process listing with basic info */
-#include <stdio.h>
-#include <string.h>
-#include <unistd.h>
-#include <fcntl.h>
-#include <dirent.h>
-
-static int is_digit(char c) { return c >= '0' && c <= '9'; }
-
-int main(void) {
- printf(" PID STATE CMD\n");
- int fd = open("/proc", O_RDONLY);
- if (fd < 0) {
- fprintf(stderr, "top: cannot open /proc\n");
- return 1;
- }
- char buf[512];
- int rc;
- while ((rc = getdents(fd, buf, sizeof(buf))) > 0) {
- int off = 0;
- while (off < rc) {
- struct dirent* d = (struct dirent*)(buf + off);
- if (d->d_reclen == 0) break;
- if (is_digit(d->d_name[0])) {
- char path[64];
-
- /* Read cmdline */
- snprintf(path, sizeof(path), "/proc/%s/cmdline", d->d_name);
- int cfd = open(path, O_RDONLY);
- char cmd[64] = "[kernel]";
- if (cfd >= 0) {
- int n = read(cfd, cmd, sizeof(cmd) - 1);
- if (n > 0) {
- cmd[n] = '\0';
- while (n > 0 && (cmd[n-1] == '\n' || cmd[n-1] == '\0')) cmd[--n] = '\0';
- }
- if (n <= 0) strcpy(cmd, "[kernel]");
- close(cfd);
- }
-
- /* Read status for state */
- snprintf(path, sizeof(path), "/proc/%s/status", d->d_name);
- int sfd = open(path, O_RDONLY);
- char state[16] = "?";
- if (sfd >= 0) {
- char sbuf[256];
- int sn = read(sfd, sbuf, sizeof(sbuf) - 1);
- if (sn > 0) {
- sbuf[sn] = '\0';
- char* st = strstr(sbuf, "State:");
- if (st) {
- st += 6;
- while (*st == ' ' || *st == '\t') st++;
- int si = 0;
- while (*st && *st != '\n' && si < 15) state[si++] = *st++;
- state[si] = '\0';
- }
- }
- close(sfd);
- }
-
- printf("%5s %6s %s\n", d->d_name, state, cmd);
- }
- off += d->d_reclen;
- }
- }
- close(fd);
- return 0;
-}
+++ /dev/null
-/* AdrOS touch utility */
-#include <stdio.h>
-#include <unistd.h>
-#include <fcntl.h>
-
-int main(int argc, char** argv) {
- if (argc < 2) {
- fprintf(stderr, "Usage: touch <file>...\n");
- return 1;
- }
-
- int rc = 0;
- for (int i = 1; i < argc; i++) {
- int fd = open(argv[i], O_WRONLY | O_CREAT, 0644);
- if (fd < 0) {
- fprintf(stderr, "touch: cannot touch '%s'\n", argv[i]);
- rc = 1;
- } else {
- close(fd);
- }
- }
- return rc;
-}
+++ /dev/null
-/* AdrOS tr utility — translate or delete characters */
-#include <stdio.h>
-#include <string.h>
-#include <unistd.h>
-
-int main(int argc, char** argv) {
- int delete_mode = 0;
- int start = 1;
-
- if (argc > 1 && strcmp(argv[1], "-d") == 0) {
- delete_mode = 1;
- start = 2;
- }
-
- if (delete_mode) {
- if (start >= argc) { fprintf(stderr, "usage: tr -d SET1\n"); return 1; }
- const char* set1 = argv[start];
- char c;
- while (read(STDIN_FILENO, &c, 1) > 0) {
- if (!strchr(set1, c))
- write(STDOUT_FILENO, &c, 1);
- }
- } else {
- if (start + 1 >= argc) { fprintf(stderr, "usage: tr SET1 SET2\n"); return 1; }
- const char* set1 = argv[start];
- const char* set2 = argv[start + 1];
- int len1 = (int)strlen(set1);
- int len2 = (int)strlen(set2);
- char c;
- while (read(STDIN_FILENO, &c, 1) > 0) {
- int found = 0;
- for (int i = 0; i < len1; i++) {
- if (c == set1[i]) {
- char r = (i < len2) ? set2[i] : set2[len2 - 1];
- write(STDOUT_FILENO, &r, 1);
- found = 1;
- break;
- }
- }
- if (!found) write(STDOUT_FILENO, &c, 1);
- }
- }
- return 0;
-}
AR ?= i686-elf-ar
LD ?= i686-elf-ld
-CFLAGS := -m32 -ffreestanding -fno-pie -no-pie -nostdlib -O2 -Wall -Wextra -Iinclude
-CFLAGS_PIC := -m32 -ffreestanding -nostdlib -O2 -Wall -Wextra -Iinclude -fPIC -fno-plt
+CFLAGS := -m32 -ffreestanding -fno-pie -no-pie -nostdlib -O2 -Wall -Wextra -Wno-incompatible-pointer-types -Iinclude
+CFLAGS_PIC := -m32 -ffreestanding -nostdlib -O2 -Wall -Wextra -Wno-incompatible-pointer-types -Iinclude -fPIC -fno-plt
ASFLAGS := --32
SRC_C := $(wildcard src/*.c)
#include "fnmatch.h"
#include "string.h"
#include "stdlib.h"
+#include "stdio.h"
#include "unistd.h"
#include "dirent.h"
#include "errno.h"
+++ /dev/null
-/* AdrOS umount utility — stub (no SYS_UMOUNT syscall yet) */
-#include <stdio.h>
-
-int main(int argc, char** argv) {
- if (argc <= 1) {
- fprintf(stderr, "umount: missing operand\n");
- return 1;
- }
- fprintf(stderr, "umount: %s: operation not supported\n", argv[1]);
- return 1;
-}
+++ /dev/null
-/* AdrOS uname utility — print system information */
-#include <stdio.h>
-#include <string.h>
-
-int main(int argc, char** argv) {
- const char* sysname = "AdrOS";
- const char* nodename = "adros";
- const char* release = "0.1.0";
- const char* version = "AdrOS x86 SMP";
- const char* machine = "i686";
-
- if (argc <= 1) { printf("%s\n", sysname); return 0; }
-
- int all = 0, s = 0, n = 0, r = 0, v = 0, m = 0;
- for (int i = 1; i < argc; i++) {
- if (strcmp(argv[i], "-a") == 0) all = 1;
- else if (strcmp(argv[i], "-s") == 0) s = 1;
- else if (strcmp(argv[i], "-n") == 0) n = 1;
- else if (strcmp(argv[i], "-r") == 0) r = 1;
- else if (strcmp(argv[i], "-v") == 0) v = 1;
- else if (strcmp(argv[i], "-m") == 0) m = 1;
- }
- if (all) { s = n = r = v = m = 1; }
- if (!s && !n && !r && !v && !m) s = 1;
-
- int first = 1;
- if (s) { printf("%s%s", first ? "" : " ", sysname); first = 0; }
- if (n) { printf("%s%s", first ? "" : " ", nodename); first = 0; }
- if (r) { printf("%s%s", first ? "" : " ", release); first = 0; }
- if (v) { printf("%s%s", first ? "" : " ", version); first = 0; }
- if (m) { printf("%s%s", first ? "" : " ", machine); first = 0; }
- printf("\n");
- return 0;
-}
+++ /dev/null
-/* AdrOS uniq utility */
-#include <stdio.h>
-#include <string.h>
-#include <unistd.h>
-#include <fcntl.h>
-
-#define LINE_MAX 1024
-
-static int cflag = 0; /* -c: prefix lines with count */
-static int dflag = 0; /* -d: only print duplicates */
-
-static int readline(int fd, char* buf, int max) {
- int n = 0;
- char c;
- while (n < max - 1) {
- int r = read(fd, &c, 1);
- if (r <= 0) break;
- if (c == '\n') break;
- buf[n++] = c;
- }
- buf[n] = '\0';
- return n > 0 ? n : (n == 0 ? 0 : -1);
-}
-
-int main(int argc, char** argv) {
- int start = 1;
- int fd = STDIN_FILENO;
-
- for (int i = 1; i < argc; i++) {
- if (argv[i][0] == '-' && argv[i][1]) {
- const char* f = argv[i] + 1;
- while (*f) {
- if (*f == 'c') cflag = 1;
- else if (*f == 'd') dflag = 1;
- f++;
- }
- start = i + 1;
- } else break;
- }
-
- if (start < argc) {
- fd = open(argv[start], O_RDONLY);
- if (fd < 0) {
- fprintf(stderr, "uniq: cannot open '%s'\n", argv[start]);
- return 1;
- }
- }
-
- char prev[LINE_MAX] = {0};
- char cur[LINE_MAX];
- int count = 0;
- int first = 1;
-
- while (1) {
- int r = readline(fd, cur, LINE_MAX);
- if (r < 0) break;
-
- if (first || strcmp(cur, prev) != 0) {
- if (!first) {
- if (!dflag || count > 1) {
- if (cflag) printf("%7d %s\n", count, prev);
- else printf("%s\n", prev);
- }
- }
- strcpy(prev, cur);
- count = 1;
- first = 0;
- } else {
- count++;
- }
-
- if (r == 0) break;
- }
-
- /* Print last line */
- if (!first) {
- if (!dflag || count > 1) {
- if (cflag) printf("%7d %s\n", count, prev);
- else printf("%s\n", prev);
- }
- }
-
- if (fd != STDIN_FILENO) close(fd);
- return 0;
-}
+++ /dev/null
-/* AdrOS uptime utility */
-#include <stdio.h>
-#include <time.h>
-#include <unistd.h>
-#include <fcntl.h>
-
-int main(int argc, char** argv) {
- (void)argc; (void)argv;
-
- /* Try /proc/uptime first */
- int fd = open("/proc/uptime", O_RDONLY);
- if (fd >= 0) {
- char buf[64];
- int r = read(fd, buf, sizeof(buf) - 1);
- close(fd);
- if (r > 0) {
- buf[r] = '\0';
- printf("up %s", buf);
- return 0;
- }
- }
-
- /* Fallback: use CLOCK_MONOTONIC */
- struct timespec ts;
- if (clock_gettime(CLOCK_MONOTONIC, &ts) < 0) {
- fprintf(stderr, "uptime: cannot get time\n");
- return 1;
- }
-
- unsigned long sec = ts.tv_sec;
- unsigned long days = sec / 86400;
- unsigned long hours = (sec % 86400) / 3600;
- unsigned long mins = (sec % 3600) / 60;
- unsigned long secs = sec % 60;
-
- printf("up");
- if (days > 0) printf(" %lu day%s,", days, days > 1 ? "s" : "");
- printf(" %02lu:%02lu:%02lu\n", hours, mins, secs);
- return 0;
-}
+++ /dev/null
-#ifndef USER_ERRNO_H
-#define USER_ERRNO_H
-
-extern int errno;
-
-static inline int __syscall_fix(int ret) {
- if (ret < 0) {
- errno = -ret;
- return -1;
- }
- return ret;
-}
-
-#endif
+++ /dev/null
-/* AdrOS wc utility */
-#include <stdio.h>
-#include <unistd.h>
-#include <fcntl.h>
-#include <string.h>
-
-static void wc_fd(int fd, const char* name, int show_l, int show_w, int show_c) {
- char buf[4096];
- int lines = 0, words = 0, chars = 0;
- int in_word = 0;
- int r;
-
- while ((r = read(fd, buf, sizeof(buf))) > 0) {
- for (int i = 0; i < r; i++) {
- chars++;
- if (buf[i] == '\n') lines++;
- if (buf[i] == ' ' || buf[i] == '\t' || buf[i] == '\n') {
- in_word = 0;
- } else if (!in_word) {
- in_word = 1;
- words++;
- }
- }
- }
-
- if (show_l) printf("%7d", lines);
- if (show_w) printf("%7d", words);
- if (show_c) printf("%7d", chars);
- if (name) printf(" %s", name);
- printf("\n");
-}
-
-int main(int argc, char** argv) {
- int show_l = 0, show_w = 0, show_c = 0;
- int start = 1;
-
- for (int i = 1; i < argc; i++) {
- if (argv[i][0] == '-' && argv[i][1]) {
- const char* f = argv[i] + 1;
- while (*f) {
- if (*f == 'l') show_l = 1;
- else if (*f == 'w') show_w = 1;
- else if (*f == 'c') show_c = 1;
- f++;
- }
- start = i + 1;
- } else break;
- }
-
- /* Default: show all */
- if (!show_l && !show_w && !show_c) {
- show_l = show_w = show_c = 1;
- }
-
- if (start >= argc) {
- wc_fd(STDIN_FILENO, NULL, show_l, show_w, show_c);
- } else {
- for (int i = start; i < argc; i++) {
- int fd = open(argv[i], O_RDONLY);
- if (fd < 0) {
- fprintf(stderr, "wc: %s: No such file\n", argv[i]);
- continue;
- }
- wc_fd(fd, argv[i], show_l, show_w, show_c);
- close(fd);
- }
- }
- return 0;
-}
+++ /dev/null
-/* AdrOS which utility — locate a command */
-#include <stdio.h>
-#include <string.h>
-#include <unistd.h>
-#include <fcntl.h>
-#include <dirent.h>
-
-static int exists_in_dir(const char* dir, const char* name) {
- int fd = open(dir, O_RDONLY);
- if (fd < 0) return 0;
- char buf[2048];
- int rc;
- while ((rc = getdents(fd, buf, sizeof(buf))) > 0) {
- int off = 0;
- while (off < rc) {
- struct dirent* d = (struct dirent*)(buf + off);
- if (d->d_reclen == 0) break;
- if (strcmp(d->d_name, name) == 0) {
- close(fd);
- return 1;
- }
- off += d->d_reclen;
- }
- }
- close(fd);
- return 0;
-}
-
-int main(int argc, char** argv) {
- if (argc < 2) {
- fprintf(stderr, "Usage: which command\n");
- return 1;
- }
-
- static const char* path_dirs[] = { "/bin", "/sbin", NULL };
- int ret = 1;
-
- for (int i = 1; i < argc; i++) {
- int found = 0;
- for (int d = 0; path_dirs[d]; d++) {
- if (exists_in_dir(path_dirs[d], argv[i])) {
- printf("%s/%s\n", path_dirs[d], argv[i]);
- found = 1;
- break;
- }
- }
- if (found) ret = 0;
- }
- return ret;
-}
+++ /dev/null
-/* AdrOS who utility — show logged-in users */
-#include <stdio.h>
-#include <unistd.h>
-
-int main(void) {
- printf("root tty1 Jan 1 00:00\n");
- return 0;
-}