From: Tulio A M Mendes Date: Tue, 17 Feb 2026 04:31:30 +0000 (-0300) Subject: fix: shell/command bugs, new utilities, procfs race condition X-Git-Url: https://projects.tadryanom.me/sitemap.xml?a=commitdiff_plain;h=6185473e19823a39481ea99b4fb0bc41cf6a820f;p=AdrOS.git fix: shell/command bugs, new utilities, procfs race condition Shell fixes: - Fix DELETE key showing ~ (handle \x1b[3~ escape sequence + Home/End) - Fix builtin redirections (echo > file now works via saved fd restore) - Fix initrd readdir (root cause of ls /bin empty + tab completion broken) Command fixes: - Fix cut -dX/-fN combined argument parsing (POSIX style) - Fix ps showing ? for PIDs: add cmdline[128] to process struct, populate in execve + init - Fix procfs race condition: use sched_lock for process list traversal - Make sched_lock non-static for procfs access New commands (22 total): - Previous session: mount, umount, env, kill, sleep, clear, ps, df, free, tee, basename, dirname, rmdir - This session: grep, id, uname, dmesg, printenv, tr, dd, pwd, stat Arch contamination note: vdso.c includes arch/x86/kernel_va_map.h directly (acceptable for now, only x86 target) Tests: 89/89 smoke, cppcheck clean --- diff --git a/Makefile b/Makefile index 9e264f4..68d94b5 100644 --- a/Makefile +++ b/Makefile @@ -96,6 +96,28 @@ ifeq ($(ARCH),x86) 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 INIT_ELF := user/init.elf LDSO_ELF := user/ld.so ULIBC_SO := user/ulibc/libc.so @@ -289,6 +311,94 @@ $(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 + $(LDSO_ELF): user/ldso.c user/ldso_linker.ld @i686-elf-gcc -m32 -ffreestanding -fno-pie -no-pie -nostdlib -Wl,-T,user/ldso_linker.ld -o $(LDSO_ELF) user/ldso.c @@ -306,6 +416,11 @@ USER_CMDS := $(ECHO_ELF) $(SH_ELF) $(CAT_ELF) $(LS_ELF) $(MKDIR_ELF) $(RM_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) \ $(INIT_ELF) FSTAB := rootfs/etc/fstab @@ -318,6 +433,14 @@ INITRD_FILES := $(FULLTEST_ELF):sbin/fulltest \ $(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 \ + $(DD_ELF):bin/dd $(PWD_ELF):bin/pwd $(STAT_ELF):bin/stat \ $(LDSO_ELF):lib/ld.so $(ULIBC_SO):lib/libc.so \ $(PIE_SO):lib/libpietest.so $(PIE_ELF):bin/pie_test \ $(FSTAB):etc/fstab diff --git a/include/errno.h b/include/errno.h index 77335b1..f52c2f2 100644 --- a/include/errno.h +++ b/include/errno.h @@ -38,6 +38,7 @@ #define ENOLCK 37 #define EBUSY 16 #define EMSGSIZE 90 +#define EROFS 30 #define EWOULDBLOCK EAGAIN #endif diff --git a/include/process.h b/include/process.h index 2ac7d48..89247c1 100644 --- a/include/process.h +++ b/include/process.h @@ -102,6 +102,7 @@ struct process { uintptr_t heap_break; char cwd[128]; + char cmdline[128]; uint32_t umask; int waiting; diff --git a/src/arch/x86/arch_platform.c b/src/arch/x86/arch_platform.c index e89d8bd..de8e28c 100644 --- a/src/arch/x86/arch_platform.c +++ b/src/arch/x86/arch_platform.c @@ -12,6 +12,7 @@ #include "process.h" #include "heap.h" +#include "utils.h" #include "hal/cpu.h" #include "hal/uart.h" @@ -57,6 +58,8 @@ static void userspace_init_thread(void) { current_process->addr_space = user_as; current_process->heap_start = heap_brk; current_process->heap_break = heap_brk; + strncpy(current_process->cmdline, init_path, sizeof(current_process->cmdline) - 1); + current_process->cmdline[sizeof(current_process->cmdline) - 1] = '\0'; vmm_as_activate(user_as); /* Register this process as "init" for orphan reparenting */ @@ -85,6 +88,53 @@ static void userspace_init_thread(void) { } } + /* If the binary uses a dynamic linker (PT_INTERP), ld.so needs a + * proper stack layout: [argc][argv...][NULL][envp...][NULL][auxv...] + * The execve path does this in syscall.c; for the init thread we + * must build it here since there is no prior execve. */ + { + elf32_auxv_t auxv_buf[8]; + int auxv_n = elf32_pop_pending_auxv(auxv_buf, 8); + if (auxv_n > 0) { + /* Build the stack top-down in the user address space. + * Layout (grows downward): + * auxv[] (auxv_n * 8 bytes) + * NULL (4 bytes — envp terminator) + * NULL (4 bytes — argv terminator) + * argv[0] ptr (4 bytes — points to init_path string) + * argc = 1 (4 bytes) + * [init_path string bytes at bottom] */ + size_t path_len = 0; + for (const char* s = init_path; *s; s++) path_len++; + path_len++; /* include NUL */ + + /* Copy init_path string onto user stack */ + user_sp -= (path_len + 3U) & ~3U; /* align to 4 */ + uintptr_t path_va = user_sp; + memcpy((void*)path_va, init_path, path_len); + + /* auxv array */ + user_sp -= (uintptr_t)(auxv_n * (int)sizeof(elf32_auxv_t)); + memcpy((void*)user_sp, auxv_buf, (size_t)auxv_n * sizeof(elf32_auxv_t)); + + /* envp NULL terminator */ + user_sp -= 4; + *(uint32_t*)user_sp = 0; + + /* argv NULL terminator */ + user_sp -= 4; + *(uint32_t*)user_sp = 0; + + /* argv[0] = pointer to init_path string */ + user_sp -= 4; + *(uint32_t*)user_sp = (uint32_t)path_va; + + /* argc = 1 */ + user_sp -= 4; + *(uint32_t*)user_sp = 1; + } + } + kprintf("[ELF] starting %s\n", init_path); kprintf("[ELF] user_range_ok(entry)=%c user_range_ok(stack)=%c\n", diff --git a/src/arch/x86/elf.c b/src/arch/x86/elf.c index afa3e9b..27dae9b 100644 --- a/src/arch/x86/elf.c +++ b/src/arch/x86/elf.c @@ -314,7 +314,7 @@ static int elf32_load_needed_libs(const uint8_t* file, uint32_t file_len, uintptr_t seg_end = 0; int rc = elf32_load_shared_lib_at(path, as, lib_base, &seg_end); if (rc == 0) { - kprintf("[ELF] loaded shared lib: %s at 0x%x\n", path, (unsigned)lib_base); + /* shared lib loaded silently */ lib_base = (seg_end + 0xFFFU) & ~(uintptr_t)0xFFFU; loaded++; } else { @@ -456,7 +456,7 @@ int elf32_load_user_from_initrd(const char* filename, uintptr_t* entry_out, uint if (irc == 0) { real_entry = interp_entry; has_interp = 1; - kprintf("[ELF] loaded interp: %s\n", interp_path); + /* interp loaded silently */ } break; } diff --git a/src/drivers/initrd.c b/src/drivers/initrd.c index 51a619d..9469644 100644 --- a/src/drivers/initrd.c +++ b/src/drivers/initrd.c @@ -200,8 +200,58 @@ static const struct file_operations initrd_file_ops = { static const struct file_operations initrd_dir_ops = {0}; +static int initrd_readdir(struct fs_node* node, uint32_t* inout_index, void* buf, uint32_t buf_len) { + if (!node || !inout_index || !buf) return -1; + if (node->flags != FS_DIRECTORY) return -1; + if (buf_len < sizeof(struct vfs_dirent)) return -1; + + int parent = (int)node->inode; + if (parent < 0 || parent >= entry_count) return -1; + + uint32_t idx = *inout_index; + uint32_t cap = buf_len / (uint32_t)sizeof(struct vfs_dirent); + struct vfs_dirent* ents = (struct vfs_dirent*)buf; + uint32_t written = 0; + + while (written < cap) { + struct vfs_dirent e; + memset(&e, 0, sizeof(e)); + + if (idx == 0) { + e.d_ino = node->inode; + e.d_type = FS_DIRECTORY; + strcpy(e.d_name, "."); + } else if (idx == 1) { + int pi = entries[parent].parent; + e.d_ino = (pi >= 0) ? (uint32_t)pi : node->inode; + e.d_type = FS_DIRECTORY; + strcpy(e.d_name, ".."); + } else { + /* Walk the child linked list to find the (idx-2)th child */ + uint32_t skip = idx - 2; + int c = entries[parent].first_child; + while (c != -1 && skip > 0) { + c = entries[c].next_sibling; + skip--; + } + if (c == -1) break; + e.d_ino = (uint32_t)c; + e.d_type = (uint8_t)entries[c].flags; + strcpy(e.d_name, entries[c].name); + } + + e.d_reclen = (uint16_t)sizeof(e); + ents[written++] = e; + idx++; + } + + *inout_index = idx; + return (int)(written * (uint32_t)sizeof(struct vfs_dirent)); +} + static const struct inode_operations initrd_dir_iops = { - .lookup = initrd_finddir, + .lookup = initrd_finddir, + .readdir = initrd_readdir, }; static void initrd_finalize_nodes(void) { diff --git a/src/kernel/overlayfs.c b/src/kernel/overlayfs.c index c769fbf..8a4b5b0 100644 --- a/src/kernel/overlayfs.c +++ b/src/kernel/overlayfs.c @@ -1,5 +1,6 @@ #include "overlayfs.h" +#include "errno.h" #include "heap.h" #include "utils.h" #include "tmpfs.h" @@ -23,6 +24,10 @@ struct overlay_node { static struct fs_node* overlay_finddir_impl(struct fs_node* node, const char* name); static int overlay_readdir_impl(struct fs_node* node, uint32_t* inout_index, void* buf, uint32_t buf_len); +static int overlay_mkdir_impl(struct fs_node* dir, const char* name); +static int overlay_unlink_impl(struct fs_node* dir, const char* name); +static int overlay_rmdir_impl(struct fs_node* dir, const char* name); +static int overlay_create_impl(struct fs_node* dir, const char* name, uint32_t flags, struct fs_node** out); static void overlay_str_copy_n(char* dst, size_t dst_sz, const char* src, size_t src_n) { if (!dst || dst_sz == 0) return; @@ -99,6 +104,10 @@ static const struct file_operations overlay_dir_ops = { static const struct inode_operations overlay_dir_iops = { .lookup = overlay_finddir_impl, .readdir = overlay_readdir_impl, + .mkdir = overlay_mkdir_impl, + .unlink = overlay_unlink_impl, + .rmdir = overlay_rmdir_impl, + .create = overlay_create_impl, }; static fs_node_t* overlay_wrap_child(struct overlay_node* parent, const char* name, fs_node_t* lower_child, fs_node_t* upper_child) { @@ -158,19 +167,73 @@ static fs_node_t* overlay_wrap_child(struct overlay_node* parent, const char* na return &c->vfs; } +static int overlay_count_upper(struct overlay_node* dir) { + if (!dir->upper || !dir->upper->i_ops || !dir->upper->i_ops->readdir) return 0; + int count = 0; + uint32_t idx = 0; + struct vfs_dirent tmp; + while (1) { + int rc = dir->upper->i_ops->readdir(dir->upper, &idx, &tmp, sizeof(tmp)); + if (rc <= 0) break; + count += rc / (int)sizeof(struct vfs_dirent); + } + return count; +} + +static int overlay_upper_has_name(struct overlay_node* dir, const char* name) { + if (!dir->upper || !dir->upper->i_ops || !dir->upper->i_ops->lookup) return 0; + fs_node_t* found = dir->upper->i_ops->lookup(dir->upper, name); + return found ? 1 : 0; +} + static int overlay_readdir_impl(struct fs_node* node, uint32_t* inout_index, void* buf, uint32_t buf_len) { if (!node || !inout_index || !buf) return -1; if (node->flags != FS_DIRECTORY) return -1; if (buf_len < sizeof(struct vfs_dirent)) return -1; struct overlay_node* dir = (struct overlay_node*)node; + uint32_t idx = *inout_index; + uint32_t cap = buf_len / (uint32_t)sizeof(struct vfs_dirent); + struct vfs_dirent* ents = (struct vfs_dirent*)buf; + uint32_t written = 0; + + /* Phase 1: emit upper layer entries */ + if (dir->upper && dir->upper->i_ops && dir->upper->i_ops->readdir) { + uint32_t upper_idx = idx; + int rc = dir->upper->i_ops->readdir(dir->upper, &upper_idx, ents, buf_len); + if (rc > 0) { + written = (uint32_t)rc / (uint32_t)sizeof(struct vfs_dirent); + *inout_index = upper_idx; + return rc; + } + /* Upper exhausted — switch to lower layer. + * Count total upper entries to know offset for lower phase. */ + } - // Prefer upper layer readdir; fall back to lower. - fs_node_t* src = dir->upper ? dir->upper : dir->lower; - if (!src) return 0; - if (src->i_ops && src->i_ops->readdir) - return src->i_ops->readdir(src, inout_index, buf, buf_len); - return 0; + /* Phase 2: emit lower layer entries, skipping those already in upper */ + if (!dir->lower || !dir->lower->i_ops || !dir->lower->i_ops->readdir) { + *inout_index = idx; + return 0; + } + + int upper_total = overlay_count_upper(dir); + /* Lower-phase index: subtract upper_total from our global index */ + uint32_t lower_idx = (idx >= (uint32_t)upper_total) ? idx - (uint32_t)upper_total : 0; + + while (written < cap) { + struct vfs_dirent tmp; + uint32_t tmp_idx = lower_idx; + int rc = dir->lower->i_ops->readdir(dir->lower, &tmp_idx, &tmp, sizeof(tmp)); + if (rc <= 0) break; + lower_idx = tmp_idx; + /* Skip . and .. (already emitted by upper) and entries that exist in upper */ + if (strcmp(tmp.d_name, ".") == 0 || strcmp(tmp.d_name, "..") == 0) continue; + if (overlay_upper_has_name(dir, tmp.d_name)) continue; + ents[written++] = tmp; + } + + *inout_index = (uint32_t)upper_total + lower_idx; + return (int)(written * (uint32_t)sizeof(struct vfs_dirent)); } static struct fs_node* overlay_finddir_impl(struct fs_node* node, const char* name) { @@ -191,6 +254,46 @@ static struct fs_node* overlay_finddir_impl(struct fs_node* node, const char* na return overlay_wrap_child(dir, name, lower_child, upper_child); } +static int overlay_mkdir_impl(struct fs_node* dir, const char* name) { + if (!dir || !name) return -EINVAL; + struct overlay_node* on = (struct overlay_node*)dir; + if (!on->upper) return -EROFS; + if (on->upper->i_ops && on->upper->i_ops->mkdir) + return on->upper->i_ops->mkdir(on->upper, name); + return -ENOSYS; +} + +static int overlay_unlink_impl(struct fs_node* dir, const char* name) { + if (!dir || !name) return -EINVAL; + struct overlay_node* on = (struct overlay_node*)dir; + /* Try upper layer first */ + if (on->upper && on->upper->i_ops && on->upper->i_ops->unlink) { + int rc = on->upper->i_ops->unlink(on->upper, name); + if (rc == 0 || rc != -ENOENT) return rc; + } + /* File only in lower (read-only) layer — cannot delete */ + return -EROFS; +} + +static int overlay_rmdir_impl(struct fs_node* dir, const char* name) { + if (!dir || !name) return -EINVAL; + struct overlay_node* on = (struct overlay_node*)dir; + if (on->upper && on->upper->i_ops && on->upper->i_ops->rmdir) { + int rc = on->upper->i_ops->rmdir(on->upper, name); + if (rc == 0 || rc != -ENOENT) return rc; + } + return -EROFS; +} + +static int overlay_create_impl(struct fs_node* dir, const char* name, uint32_t flags, struct fs_node** out) { + if (!dir || !name || !out) return -EINVAL; + struct overlay_node* on = (struct overlay_node*)dir; + if (!on->upper) return -EROFS; + if (on->upper->i_ops && on->upper->i_ops->create) + return on->upper->i_ops->create(on->upper, name, flags, out); + return -ENOSYS; +} + fs_node_t* overlayfs_create_root(fs_node_t* lower_root, fs_node_t* upper_root) { if (!lower_root || !upper_root) return NULL; diff --git a/src/kernel/procfs.c b/src/kernel/procfs.c index 7af3604..05cb34e 100644 --- a/src/kernel/procfs.c +++ b/src/kernel/procfs.c @@ -1,6 +1,7 @@ #include "procfs.h" #include "process.h" +#include "spinlock.h" #include "utils.h" #include "heap.h" #include "pmm.h" @@ -23,16 +24,10 @@ static fs_node_t g_pid_maps[PID_NODE_POOL]; static uint32_t g_pid_pool_idx = 0; extern struct process* ready_queue_head; +extern spinlock_t sched_lock; -static struct process* proc_find_pid(uint32_t pid) { - if (!ready_queue_head) return NULL; - struct process* it = ready_queue_head; - const struct process* start = it; - do { - if (it->pid == pid) return it; - it = it->next; - } while (it && it != start); - return NULL; +static struct process* proc_find_pid_safe(uint32_t pid) { + return process_find_by_pid(pid); } static int proc_snprintf(char* buf, uint32_t sz, const char* key, uint32_t val) { @@ -137,15 +132,19 @@ static uint32_t proc_meminfo_read(fs_node_t* node, uint32_t offset, uint32_t siz char tmp[256]; uint32_t len = 0; - /* Count processes */ + /* Count processes (under sched_lock to avoid race) */ uint32_t nprocs = 0; - if (ready_queue_head) { - struct process* it = ready_queue_head; - const struct process* start = it; - do { - nprocs++; - it = it->next; - } while (it && it != start); + { + uintptr_t fl = spin_lock_irqsave(&sched_lock); + if (ready_queue_head) { + struct process* it = ready_queue_head; + const struct process* start = it; + do { + nprocs++; + it = it->next; + } while (it && it != start); + } + spin_unlock_irqrestore(&sched_lock, fl); } len += (uint32_t)proc_snprintf(tmp + len, sizeof(tmp) - len, "Processes:\t", nprocs); @@ -162,7 +161,7 @@ static uint32_t proc_meminfo_read(fs_node_t* node, uint32_t offset, uint32_t siz static uint32_t proc_pid_status_read(fs_node_t* node, uint32_t offset, uint32_t size, uint8_t* buffer) { uint32_t pid = node->inode; - struct process* p = proc_find_pid(pid); + struct process* p = proc_find_pid_safe(pid); if (!p) return 0; char tmp[512]; @@ -198,11 +197,29 @@ static uint32_t proc_pid_status_read(fs_node_t* node, uint32_t offset, uint32_t return size; } +/* --- per-PID cmdline read (inode == target pid) --- */ + +static uint32_t proc_pid_cmdline_read(fs_node_t* node, uint32_t offset, uint32_t size, uint8_t* buffer) { + uint32_t pid = node->inode; + struct process* p = proc_find_pid_safe(pid); + if (!p) return 0; + + uint32_t len = (uint32_t)strlen(p->cmdline); + char tmp[256]; + memcpy(tmp, p->cmdline, len); + if (len + 1 < sizeof(tmp)) { tmp[len] = '\n'; len++; } + if (offset >= len) return 0; + uint32_t avail = len - offset; + if (size > avail) size = avail; + memcpy(buffer, tmp + offset, size); + return size; +} + /* --- per-PID maps read (inode == target pid) --- */ static uint32_t proc_pid_maps_read(fs_node_t* node, uint32_t offset, uint32_t size, uint8_t* buffer) { uint32_t pid = node->inode; - struct process* p = proc_find_pid(pid); + struct process* p = proc_find_pid_safe(pid); if (!p) return 0; char tmp[1024]; @@ -238,6 +255,7 @@ static const struct file_operations procfs_pid_dir_fops; static const struct inode_operations procfs_pid_dir_iops; static const struct file_operations procfs_pid_status_fops; static const struct file_operations procfs_pid_maps_fops; +static const struct file_operations procfs_pid_cmdline_fops; static fs_node_t* proc_pid_finddir(fs_node_t* node, const char* name) { uint32_t pid = node->inode; @@ -261,6 +279,15 @@ static fs_node_t* proc_pid_finddir(fs_node_t* node, const char* name) { g_pid_maps[slot].f_ops = &procfs_pid_maps_fops; return &g_pid_maps[slot]; } + if (strcmp(name, "cmdline") == 0) { + g_pid_pool_idx = (slot + 1) % PID_NODE_POOL; + memset(&g_pid_status[slot], 0, sizeof(fs_node_t)); + strcpy(g_pid_status[slot].name, "cmdline"); + g_pid_status[slot].flags = FS_FILE; + g_pid_status[slot].inode = pid; + g_pid_status[slot].f_ops = &procfs_pid_cmdline_fops; + return &g_pid_status[slot]; + } return NULL; } @@ -269,9 +296,9 @@ static int proc_pid_readdir(fs_node_t* node, uint32_t* inout_index, void* buf, u if (!inout_index || !buf) return -1; if (buf_len < sizeof(struct vfs_dirent)) return -1; - static const char* entries[] = { "status", "maps" }; + static const char* entries[] = { "status", "maps", "cmdline" }; uint32_t idx = *inout_index; - if (idx >= 2) return 0; + if (idx >= 3) return 0; struct vfs_dirent* d = (struct vfs_dirent*)buf; d->d_ino = 300 + idx; @@ -288,7 +315,7 @@ static int proc_pid_readdir(fs_node_t* node, uint32_t* inout_index, void* buf, u } static fs_node_t* proc_get_pid_dir(uint32_t pid) { - if (!proc_find_pid(pid)) return NULL; + if (!proc_find_pid_safe(pid)) return NULL; uint32_t slot = g_pid_pool_idx; g_pid_pool_idx = (slot + 1) % PID_NODE_POOL; memset(&g_pid_dir[slot], 0, sizeof(fs_node_t)); @@ -376,31 +403,37 @@ static int proc_root_readdir(fs_node_t* node, uint32_t* inout_index, void* buf, return (int)sizeof(struct vfs_dirent); } - /* After fixed entries, list numeric PIDs */ + /* After fixed entries, list numeric PIDs (under sched_lock) */ uint32_t pi = idx - 4; uint32_t count = 0; - if (ready_queue_head) { - struct process* it = ready_queue_head; - const struct process* start = it; - do { - if (count == pi) { - char num[16]; - itoa(it->pid, num, 10); - d->d_ino = 400 + it->pid; - d->d_type = FS_DIRECTORY; - d->d_reclen = sizeof(struct vfs_dirent); - uint32_t j = 0; - while (num[j] && j + 1 < sizeof(d->d_name) && j < sizeof(num) - 1) { d->d_name[j] = num[j]; j++; } - d->d_name[j] = 0; - *inout_index = idx + 1; - return (int)sizeof(struct vfs_dirent); - } - count++; - it = it->next; - } while (it && it != start); + int found = 0; + { + uintptr_t fl = spin_lock_irqsave(&sched_lock); + if (ready_queue_head) { + struct process* it = ready_queue_head; + const struct process* start = it; + do { + if (count == pi) { + char num[16]; + itoa(it->pid, num, 10); + d->d_ino = 400 + it->pid; + d->d_type = FS_DIRECTORY; + d->d_reclen = sizeof(struct vfs_dirent); + uint32_t j = 0; + while (num[j] && j + 1 < sizeof(d->d_name) && j < sizeof(num) - 1) { d->d_name[j] = num[j]; j++; } + d->d_name[j] = 0; + *inout_index = idx + 1; + found = 1; + break; + } + count++; + it = it->next; + } while (it && it != start); + } + spin_unlock_irqrestore(&sched_lock, fl); } - return 0; + return found ? (int)sizeof(struct vfs_dirent) : 0; } /* --- file_operations tables --- */ @@ -450,6 +483,10 @@ static const struct file_operations procfs_pid_maps_fops = { .read = proc_pid_maps_read, }; +static const struct file_operations procfs_pid_cmdline_fops = { + .read = proc_pid_cmdline_read, +}; + fs_node_t* procfs_create_root(void) { memset(&g_proc_root, 0, sizeof(g_proc_root)); strcpy(g_proc_root.name, "proc"); diff --git a/src/kernel/scheduler.c b/src/kernel/scheduler.c index 504a1b6..b8a5569 100644 --- a/src/kernel/scheduler.c +++ b/src/kernel/scheduler.c @@ -22,7 +22,7 @@ struct process* ready_queue_tail = NULL; static uint32_t next_pid = 1; static uint32_t init_pid = 0; /* PID of the first userspace process ("init") */ -static spinlock_t sched_lock = {0}; +spinlock_t sched_lock = {0}; static uintptr_t kernel_as = 0; /* diff --git a/src/kernel/syscall.c b/src/kernel/syscall.c index ab4b869..a9cb43e 100644 --- a/src/kernel/syscall.c +++ b/src/kernel/syscall.c @@ -1950,6 +1950,8 @@ static int syscall_execve_impl(struct registers* regs, const char* user_path, co current_process->addr_space = new_as; current_process->heap_start = heap_brk; current_process->heap_break = heap_brk; + strncpy(current_process->cmdline, path, sizeof(current_process->cmdline) - 1); + current_process->cmdline[sizeof(current_process->cmdline) - 1] = '\0'; vmm_as_activate(new_as); // Build a minimal initial user stack: argc, argv pointers, envp pointers, strings. diff --git a/src/kernel/tmpfs.c b/src/kernel/tmpfs.c index 124c277..dffc7c0 100644 --- a/src/kernel/tmpfs.c +++ b/src/kernel/tmpfs.c @@ -20,6 +20,10 @@ static struct fs_node* tmpfs_finddir_impl(struct fs_node* node, const char* name static int tmpfs_readdir_impl(struct fs_node* node, uint32_t* inout_index, void* buf, uint32_t buf_len); static uint32_t tmpfs_read_impl(fs_node_t* node, uint32_t offset, uint32_t size, uint8_t* buffer); static uint32_t tmpfs_write_impl(fs_node_t* node, uint32_t offset, uint32_t size, const uint8_t* buffer); +static int tmpfs_mkdir_impl(struct fs_node* dir, const char* name); +static int tmpfs_unlink_impl(struct fs_node* dir, const char* name); +static int tmpfs_rmdir_impl(struct fs_node* dir, const char* name); +static int tmpfs_create_impl(struct fs_node* dir, const char* name, uint32_t flags, struct fs_node** out); static const struct file_operations tmpfs_file_ops = { .read = tmpfs_read_impl, @@ -31,6 +35,10 @@ static const struct file_operations tmpfs_dir_ops = {0}; static const struct inode_operations tmpfs_dir_iops = { .lookup = tmpfs_finddir_impl, .readdir = tmpfs_readdir_impl, + .mkdir = tmpfs_mkdir_impl, + .unlink = tmpfs_unlink_impl, + .rmdir = tmpfs_rmdir_impl, + .create = tmpfs_create_impl, }; static struct tmpfs_node* tmpfs_node_alloc(const char* name, uint32_t flags) { @@ -202,6 +210,73 @@ static int tmpfs_readdir_impl(struct fs_node* node, uint32_t* inout_index, void* return (int)(written * (uint32_t)sizeof(struct vfs_dirent)); } +static int tmpfs_mkdir_impl(struct fs_node* dir, const char* name) { + if (!dir || !name || dir->flags != FS_DIRECTORY) return -EINVAL; + struct tmpfs_node* d = (struct tmpfs_node*)dir; + if (tmpfs_child_find(d, name)) return -EEXIST; + struct tmpfs_node* nd = tmpfs_node_alloc(name, FS_DIRECTORY); + if (!nd) return -ENOMEM; + nd->vfs.f_ops = &tmpfs_dir_ops; + nd->vfs.i_ops = &tmpfs_dir_iops; + tmpfs_child_add(d, nd); + return 0; +} + +static int tmpfs_unlink_impl(struct fs_node* dir, const char* name) { + if (!dir || !name || dir->flags != FS_DIRECTORY) return -EINVAL; + struct tmpfs_node* d = (struct tmpfs_node*)dir; + struct tmpfs_node* prev = NULL; + struct tmpfs_node* c = d->first_child; + while (c) { + if (strcmp(c->vfs.name, name) == 0) break; + prev = c; + c = c->next_sibling; + } + if (!c) return -ENOENT; + if (c->vfs.flags == FS_DIRECTORY) return -EISDIR; + if (prev) prev->next_sibling = c->next_sibling; + else d->first_child = c->next_sibling; + if (c->data) kfree(c->data); + kfree(c); + return 0; +} + +static int tmpfs_rmdir_impl(struct fs_node* dir, const char* name) { + if (!dir || !name || dir->flags != FS_DIRECTORY) return -EINVAL; + struct tmpfs_node* d = (struct tmpfs_node*)dir; + struct tmpfs_node* prev = NULL; + struct tmpfs_node* c = d->first_child; + while (c) { + if (strcmp(c->vfs.name, name) == 0) break; + prev = c; + c = c->next_sibling; + } + if (!c) return -ENOENT; + if (c->vfs.flags != FS_DIRECTORY) return -ENOTDIR; + if (c->first_child) return -ENOTEMPTY; + if (prev) prev->next_sibling = c->next_sibling; + else d->first_child = c->next_sibling; + kfree(c); + return 0; +} + +static int tmpfs_create_impl(struct fs_node* dir, const char* name, uint32_t flags, struct fs_node** out) { + if (!dir || !name || !out || dir->flags != FS_DIRECTORY) return -EINVAL; + struct tmpfs_node* d = (struct tmpfs_node*)dir; + struct tmpfs_node* existing = tmpfs_child_find(d, name); + if (existing) { + *out = &existing->vfs; + return 0; + } + if (!(flags & 0x40U)) return -ENOENT; /* O_CREAT */ + struct tmpfs_node* f = tmpfs_node_alloc(name, FS_FILE); + if (!f) return -ENOMEM; + f->vfs.f_ops = &tmpfs_file_ops; + tmpfs_child_add(d, f); + *out = &f->vfs; + return 0; +} + fs_node_t* tmpfs_create_root(void) { struct tmpfs_node* root = tmpfs_node_alloc("", FS_DIRECTORY); if (!root) return NULL; diff --git a/user/basename.c b/user/basename.c new file mode 100644 index 0000000..8089c29 --- /dev/null +++ b/user/basename.c @@ -0,0 +1,28 @@ +/* AdrOS basename utility — strip directory from filename */ +#include +#include + +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; +} diff --git a/user/clear.c b/user/clear.c new file mode 100644 index 0000000..619feb4 --- /dev/null +++ b/user/clear.c @@ -0,0 +1,8 @@ +/* AdrOS clear utility — clear the terminal screen */ +#include + +int main(void) { + /* ANSI escape: clear screen + move cursor to top-left */ + write(STDOUT_FILENO, "\033[2J\033[H", 7); + return 0; +} diff --git a/user/cut.c b/user/cut.c index 42cc1c2..47d4d5d 100644 --- a/user/cut.c +++ b/user/cut.c @@ -79,9 +79,15 @@ int main(int argc, char** argv) { 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; } diff --git a/user/dd.c b/user/dd.c new file mode 100644 index 0000000..eef0d8c --- /dev/null +++ b/user/dd.c @@ -0,0 +1,63 @@ +/* AdrOS dd utility — convert and copy a file */ +#include +#include +#include +#include +#include + +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); + 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; +} diff --git a/user/df.c b/user/df.c new file mode 100644 index 0000000..81d0a87 --- /dev/null +++ b/user/df.c @@ -0,0 +1,10 @@ +/* AdrOS df utility — display filesystem disk space usage */ +#include + +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; +} diff --git a/user/dirname.c b/user/dirname.c new file mode 100644 index 0000000..15e518d --- /dev/null +++ b/user/dirname.c @@ -0,0 +1,22 @@ +/* AdrOS dirname utility — strip last component from path */ +#include +#include + +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; +} diff --git a/user/dmesg.c b/user/dmesg.c new file mode 100644 index 0000000..b2aacc4 --- /dev/null +++ b/user/dmesg.c @@ -0,0 +1,18 @@ +/* AdrOS dmesg utility — print kernel ring buffer from /proc/dmesg */ +#include +#include +#include + +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; +} diff --git a/user/env.c b/user/env.c new file mode 100644 index 0000000..a7d727a --- /dev/null +++ b/user/env.c @@ -0,0 +1,21 @@ +/* AdrOS env utility — print environment or run command with modified env */ +#include +#include +#include + +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; +} diff --git a/user/free.c b/user/free.c new file mode 100644 index 0000000..b07a8bc --- /dev/null +++ b/user/free.c @@ -0,0 +1,17 @@ +/* AdrOS free utility — display memory usage from /proc/meminfo */ +#include +#include +#include + +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; +} diff --git a/user/grep.c b/user/grep.c new file mode 100644 index 0000000..1f755ee --- /dev/null +++ b/user/grep.c @@ -0,0 +1,79 @@ +/* AdrOS grep utility — search for pattern in files */ +#include +#include +#include +#include + +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; +} diff --git a/user/id.c b/user/id.c new file mode 100644 index 0000000..31b51ab --- /dev/null +++ b/user/id.c @@ -0,0 +1,9 @@ +/* AdrOS id utility — display user and group IDs */ +#include +#include + +int main(void) { + printf("uid=%d gid=%d euid=%d egid=%d\n", + getuid(), getgid(), geteuid(), getegid()); + return 0; +} diff --git a/user/kill.c b/user/kill.c new file mode 100644 index 0000000..7cc1e95 --- /dev/null +++ b/user/kill.c @@ -0,0 +1,41 @@ +/* AdrOS kill utility — send signal to process */ +#include +#include +#include +#include + +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; +} diff --git a/user/mount.c b/user/mount.c new file mode 100644 index 0000000..970e327 --- /dev/null +++ b/user/mount.c @@ -0,0 +1,23 @@ +/* AdrOS mount utility — display mounted filesystems */ +#include +#include +#include +#include + +int main(int argc, char** argv) { + (void)argc; (void)argv; + /* Read /proc/mounts if available, otherwise show static info */ + 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"); + } + return 0; +} diff --git a/user/printenv.c b/user/printenv.c new file mode 100644 index 0000000..0e3f081 --- /dev/null +++ b/user/printenv.c @@ -0,0 +1,27 @@ +/* AdrOS printenv utility — print environment variables */ +#include +#include + +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; +} diff --git a/user/ps.c b/user/ps.c new file mode 100644 index 0000000..f9d8d5b --- /dev/null +++ b/user/ps.c @@ -0,0 +1,42 @@ +/* AdrOS ps utility — list processes from /proc */ +#include +#include +#include +#include +#include + +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'; + else strcpy(cmd, "?"); + close(cfd); + } + printf("%5s %s\n", d->d_name, cmd); + } + off += d->d_reclen; + } + } + close(fd); + return 0; +} diff --git a/user/pwd.c b/user/pwd.c new file mode 100644 index 0000000..baa6134 --- /dev/null +++ b/user/pwd.c @@ -0,0 +1,14 @@ +/* AdrOS pwd utility — print working directory */ +#include +#include + +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; +} diff --git a/user/rmdir.c b/user/rmdir.c new file mode 100644 index 0000000..0d65e8a --- /dev/null +++ b/user/rmdir.c @@ -0,0 +1,18 @@ +/* AdrOS rmdir utility — remove empty directories */ +#include +#include + +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; +} diff --git a/user/sh.c b/user/sh.c index aec3d56..8df4b23 100644 --- a/user/sh.c +++ b/user/sh.c @@ -16,6 +16,23 @@ #include #include #include +#include +#include + +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 @@ -107,6 +124,136 @@ 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; @@ -142,6 +289,12 @@ static int read_line_edit(void) { continue; } + /* Tab = autocomplete */ + if (c == '\t') { + tab_complete(line, &pos, &len); + continue; + } + /* Ctrl+D = EOF */ if (c == 4) { if (len == 0) return -1; @@ -185,6 +338,33 @@ static int read_line_edit(void) { 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) { @@ -222,6 +402,13 @@ static int read_line_edit(void) { 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; } @@ -384,6 +571,19 @@ static void run_simple(char* cmd) { argc = nargc; if (argc == 0) return; + /* ---- Apply redirections for builtins too ---- */ + int saved_stdin = -1, saved_stdout = -1; + 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) { @@ -401,7 +601,7 @@ static void run_simple(char* cmd) { if (getcwd(cwd, sizeof(cwd)) >= 0) var_set("PWD", cwd, 1); } - return; + goto restore_redir; } if (strcmp(argv[0], "pwd") == 0) { @@ -410,7 +610,7 @@ static void run_simple(char* cmd) { printf("%s\n", cwd); else fprintf(stderr, "pwd: error\n"); - return; + goto restore_redir; } if (strcmp(argv[0], "export") == 0) { @@ -426,18 +626,18 @@ static void run_simple(char* cmd) { vars[j].exported = 1; } } - return; + goto restore_redir; } if (strcmp(argv[0], "unset") == 0) { for (int i = 1; i < argc; i++) var_unset(argv[i]); - return; + 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); - return; + goto restore_redir; } if (strcmp(argv[0], "echo") == 0) { @@ -449,7 +649,7 @@ static void run_simple(char* cmd) { write(STDOUT_FILENO, argv[i], strlen(argv[i])); } if (!nflag) write(STDOUT_FILENO, "\n", 1); - return; + goto restore_redir; } if (strcmp(argv[0], "type") == 0) { @@ -467,10 +667,10 @@ static void run_simple(char* cmd) { printf("%s: not found\n", argv[i]); } } - return; + goto restore_redir; } - /* ---- External command ---- */ + /* ---- External command — restore parent redirections before fork ---- */ const char* path = resolve(argv[0]); char** envp = build_envp(); @@ -498,6 +698,11 @@ static void run_simple(char* cmd) { int st; waitpid(pid, &st, 0); 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 ---- */ @@ -644,16 +849,21 @@ int main(int argc, char** argv, char** envp) { if (!var_get("HOME")) var_set("HOME", "/", 1); + 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; } diff --git a/user/sleep.c b/user/sleep.c new file mode 100644 index 0000000..9da1be1 --- /dev/null +++ b/user/sleep.c @@ -0,0 +1,18 @@ +/* AdrOS sleep utility — pause for N seconds */ +#include +#include +#include +#include + +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; +} diff --git a/user/stat.c b/user/stat.c new file mode 100644 index 0000000..7083d29 --- /dev/null +++ b/user/stat.c @@ -0,0 +1,25 @@ +/* AdrOS stat utility — display file status */ +#include +#include +#include +#include + +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; +} diff --git a/user/tee.c b/user/tee.c new file mode 100644 index 0000000..b85a018 --- /dev/null +++ b/user/tee.c @@ -0,0 +1,34 @@ +/* AdrOS tee utility — read stdin, write to stdout and files */ +#include +#include +#include +#include + +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); + 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; +} diff --git a/user/tr.c b/user/tr.c new file mode 100644 index 0000000..cf7606f --- /dev/null +++ b/user/tr.c @@ -0,0 +1,44 @@ +/* AdrOS tr utility — translate or delete characters */ +#include +#include +#include + +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; +} diff --git a/user/ulibc/include/termios.h b/user/ulibc/include/termios.h new file mode 100644 index 0000000..4fdb006 --- /dev/null +++ b/user/ulibc/include/termios.h @@ -0,0 +1,53 @@ +#ifndef ULIBC_TERMIOS_H +#define ULIBC_TERMIOS_H + +#include + +#define NCCS 11 + +/* c_lflag bits */ +#define ISIG 0x0001 +#define ICANON 0x0002 +#define ECHO 0x0008 + +/* c_iflag bits */ +#define ICRNL 0x0100 +#define IGNCR 0x0080 +#define INLCR 0x0040 + +/* c_oflag bits */ +#define OPOST 0x0001 +#define ONLCR 0x0004 + +/* c_cc indices */ +#define VINTR 0 +#define VQUIT 1 +#define VERASE 2 +#define VKILL 3 +#define VEOF 4 +#define VSUSP 7 +#define VMIN 8 +#define VTIME 9 + +/* ioctl commands */ +#define TCGETS 0x5401 +#define TCSETS 0x5402 +#define TCSETSW 0x5403 +#define TCSETSF 0x5404 + +struct termios { + uint32_t c_iflag; + uint32_t c_oflag; + uint32_t c_cflag; + uint32_t c_lflag; + uint8_t c_cc[NCCS]; +}; + +int tcgetattr(int fd, struct termios* t); +int tcsetattr(int fd, int actions, const struct termios* t); + +#define TCSANOW 0 +#define TCSADRAIN 1 +#define TCSAFLUSH 2 + +#endif diff --git a/user/ulibc/src/unistd.c b/user/ulibc/src/unistd.c index 51c34e0..7eee36f 100644 --- a/user/ulibc/src/unistd.c +++ b/user/ulibc/src/unistd.c @@ -1,6 +1,7 @@ #include "unistd.h" #include "syscall.h" #include "errno.h" +#include "termios.h" int read(int fd, void* buf, size_t count) { return __syscall_ret(_syscall3(SYS_READ, fd, (int)buf, (int)count)); @@ -215,6 +216,17 @@ int readlink(const char* path, char* buf, size_t bufsiz) { return -1; /* TODO: implement when kernel has SYS_READLINK */ } +int tcgetattr(int fd, struct termios* t) { + return __syscall_ret(_syscall3(SYS_IOCTL, fd, 0x5401 /* TCGETS */, (int)t)); +} + +int tcsetattr(int fd, int actions, const struct termios* t) { + int cmd = 0x5402; /* TCSETS */ + if (actions == 1) cmd = 0x5403; /* TCSETSW */ + else if (actions == 2) cmd = 0x5404; /* TCSETSF */ + return __syscall_ret(_syscall3(SYS_IOCTL, fd, cmd, (int)t)); +} + void _exit(int status) { _syscall1(SYS_EXIT, status); /* If exit syscall somehow returns, loop forever. diff --git a/user/umount.c b/user/umount.c new file mode 100644 index 0000000..1396c94 --- /dev/null +++ b/user/umount.c @@ -0,0 +1,11 @@ +/* AdrOS umount utility — stub (no SYS_UMOUNT syscall yet) */ +#include + +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; +} diff --git a/user/uname.c b/user/uname.c new file mode 100644 index 0000000..250fe05 --- /dev/null +++ b/user/uname.c @@ -0,0 +1,34 @@ +/* AdrOS uname utility — print system information */ +#include +#include + +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; +}