// SPDX-License-Identifier: Apache-2.0 // SchOS — minimal init (PID 1) #include "types.h" #include "user.h" #include "fcntl.h" static void xputs(int fd, const char *s) { int n = 0; while (s[n]) n++; write(fd, (void *)s, n); } int main(void) { if (getpid() != 1) exit(); // Open console as fd 0; dup to fd 1 (stdout) and fd 2 (stderr). if (open("console", O_RDWR) < 0) { mknod("console", 1, 1); open("console", O_RDWR); } dup(0); dup(0); // --- /dev ------------------------------------------------------------ // Minor numbers under major 1 follow Linux's mem-major layout where it // costs nothing (null=2, zero=5, full=7, random=8, urandom=9). // /dev/hole (was minor 4) is gone: it backed an abandoned experiment and // nothing has referenced it since. mkdir("/dev"); mknod("/dev/console", 1, 1); mknod("/dev/null", 1, 2); mknod("/dev/keyboard", 1, 3); mknod("/dev/zero", 1, 5); mknod("/dev/full", 1, 7); mknod("/dev/random", 1, 8); mknod("/dev/urandom", 1, 9); mknod("/dev/fb0", 2, 0); mknod("/dev/mouse", 3, 1); mknod("/dev/tty", 1, 1); // alias for console — must pass TCGETS ioctl mknod("/dev/ttyS0", 4, 64); mknod("/dev/stdin", 1, 1); mknod("/dev/stdout", 1, 1); mknod("/dev/stderr", 1, 1); // Raw input events, Linux evdev layout (kernel/dev/input.c). /dev/keyboard // and /dev/mouse above are the old character-and-position interfaces; these // are the ones with key releases and button transitions, which is what a // game or a compositor needs and what neither of the others can express. mkdir("/dev/input"); mknod("/dev/input/event0", 7, 0); // keyboard mknod("/dev/input/event1", 7, 1); // mouse // Raw disks (kernel/dev/disk.c). The minor is the IDE device number, so // hda is the disk the kernel booted from and hdb the second channel. // // Created unconditionally, the same way every other node here is: opening // one when no drive is there fails with ENXIO, which is a better answer // than a missing path, and it keeps this list a description of the device // model rather than of one machine's hardware. mknod("/dev/hda", 8, 0); mknod("/dev/hdb", 8, 1); // --- /proc ----------------------------------------------------------- // Synthesised status files, major 6 (kernel/dev/procfs.c). These are the // global ones; per-process /proc// directories need synthetic // directory support in the filesystem, which does not exist yet. mkdir("/proc"); // /proc is a real filesystem now (kernel/fs/procfs.c), not a handful of // mknod'd nodes: it has to be, because one directory per process cannot be // created ahead of time. This is what makes `ps` and `top` work. // // The mknod'd global files below still happen, and are simply covered by // the mount. They stay for now so that a failure here degrades to the old // behaviour instead of losing /proc entirely. mount("proc", "/proc", "proc", 0, 0); mknod("/proc/meminfo", 6, 1); mknod("/proc/uptime", 6, 2); mknod("/proc/cpuinfo", 6, 3); mknod("/proc/version", 6, 4); mknod("/proc/stat", 6, 5); mknod("/proc/loadavg", 6, 6); mknod("/proc/mounts", 6, 7); mknod("/proc/filesystems", 6, 8); // SchFS volume summary: label, generation, free space, and the root // directory. Reachable whether or not the volume is mounted into the // namespace, which is what makes it useful when a mount has gone wrong. mknod("/proc/schfs", 6, 9); // Write 'c' here to crash on purpose. The crash screen is otherwise the // one part of the system that can only be seen by having a bug. mknod("/proc/sysrq", 6, 10); mknod("/proc/pci", 6, 11); mknod("/proc/interrupts", 6, 12); mkdir("/sys"); xputs(1, "\nSchOS\n"); xputs(1, "init: ready (pid 1)\n"); for (;;) { int pid = fork(); if (pid < 0) { xputs(2, "init: fork failed\n"); sleep(100); continue; } if (pid == 0) { // exec.c auto-enables Linux ABI for musl binaries (entry >= 0x100000). // Do NOT call enable_linux_abi() here — it corrupts SchOS syscall // numbers for the exec/exit calls that follow in this child. // Standardized naming: the shell is always /bin/sh, regardless of // which implementation backs it (dash, mksh, etc.) — no ksh/mksh/ // other aliases are looked up here. static char *argv[] = { "sh", 0 }; exec("/bin/sh", argv); xputs(2, "init: no shell found; place sh in /bin\n"); exit(); } // Reap zombies; the loop restarts the shell when it exits. int wpid; while ((wpid = wait()) >= 0 && wpid != pid) ; // Brief pause before restarting so we don't spin on a missing shell. sleep(100); } return 0; }