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
@$(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
$(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
$(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
#define ENOLCK 37
#define EBUSY 16
#define EMSGSIZE 90
+#define EROFS 30
#define EWOULDBLOCK EAGAIN
#endif
uintptr_t heap_break;
char cwd[128];
+ char cmdline[128];
uint32_t umask;
int waiting;
#include "process.h"
#include "heap.h"
+#include "utils.h"
#include "hal/cpu.h"
#include "hal/uart.h"
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 */
}
}
+ /* 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",
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 {
if (irc == 0) {
real_entry = interp_entry;
has_interp = 1;
- kprintf("[ELF] loaded interp: %s\n", interp_path);
+ /* interp loaded silently */
}
break;
}
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) {
#include "overlayfs.h"
+#include "errno.h"
#include "heap.h"
#include "utils.h"
#include "tmpfs.h"
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;
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) {
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) {
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;
#include "procfs.h"
#include "process.h"
+#include "spinlock.h"
#include "utils.h"
#include "heap.h"
#include "pmm.h"
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) {
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);
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];
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];
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;
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;
}
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;
}
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));
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 --- */
.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");
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;
/*
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.
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,
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) {
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;
--- /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 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;
+}
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;
}
--- /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);
+ 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 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
+/* 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
+/* 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 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 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
+/* AdrOS mount utility — display mounted filesystems */
+#include <stdio.h>
+#include <string.h>
+#include <unistd.h>
+#include <fcntl.h>
+
+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;
+}
--- /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';
+ else strcpy(cmd, "?");
+ 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 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;
+}
#include <string.h>
#include <unistd.h>
#include <fcntl.h>
+#include <dirent.h>
+#include <termios.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
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;
continue;
}
+ /* Tab = autocomplete */
+ if (c == '\t') {
+ tab_complete(line, &pos, &len);
+ continue;
+ }
+
/* Ctrl+D = EOF */
if (c == 4) {
if (len == 0) return -1;
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) {
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;
}
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) {
if (getcwd(cwd, sizeof(cwd)) >= 0)
var_set("PWD", cwd, 1);
}
- return;
+ goto restore_redir;
}
if (strcmp(argv[0], "pwd") == 0) {
printf("%s\n", cwd);
else
fprintf(stderr, "pwd: error\n");
- return;
+ goto restore_redir;
}
if (strcmp(argv[0], "export") == 0) {
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) {
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) {
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();
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 ---- */
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;
}
--- /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 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 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);
+ 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 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
+#ifndef ULIBC_TERMIOS_H
+#define ULIBC_TERMIOS_H
+
+#include <stdint.h>
+
+#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
#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));
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.
--- /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;
+}