foundry
Foundry OS: the operating system built on the Rustbelt kernel
| author | Grok Bot <grokbot@localhost> | 2026-10-31 19:46:15 -0700 |
|---|---|---|
| commit | 12674c45d018a98382dd8fa646cbe0b2c184b139 | |
| parent | 55604d879b3a0a8d49b374860be8e8b2f14d2f5c | |
| tree | browse files at this commit | |
c/orphan.c: check that an orphaned grandchild is adopted and reaped by init (pid 1)
| A | c/orphan.c | +81 -0 |
| M | config.mk | +1 -1 |
2 files changed, 82 insertions, 1 deletion
@@ -0,0 +1,81 @@+/* orphan: checks that init adopts and reaps orphans (boot contract v2).+ *+ * A process whose parent exits first is an "orphan". Unix hands orphans to+ * pid 1 (init), which must wait() for them when they exit; otherwise each+ * would stay a "zombie" forever. Rustbelt does this only when pid 1 is+ * named "init" -- Foundry OS's /sbin/init.+ *+ * orphan (this process)+ * `-- middle exits at once, leaving...+ * `-- grandchild ...an orphan: its parent becomes pid 1+ *+ * Checks: the grandchild's getppid() becomes 1, and after it exits it is+ * gone completely (kill(pid, 0) fails with ESRCH): init reaped it.+ * Exit code = number of failed checks. Built static-PIE by foundry-cc.+ */+#include <errno.h>+#include <signal.h>+#include <stdio.h>+#include <sys/wait.h>+#include <time.h>+#include <unistd.h>++static int failed;++static void check(int ok, const char *what) {+ printf("orphan: %-52s %s\n", what, ok ? "ok" : "FAILED");+ if (!ok)+ failed++;+}++static void sleep_ms(int ms) {+ struct timespec ts = {ms / 1000, (ms % 1000) * 1000000L};+ nanosleep(&ts, 0);+}++int main(void) {+ int fds[2];+ if (pipe(fds) != 0) {+ perror("orphan: pipe");+ return 1;+ }+ pid_t middle = fork();+ if (middle == 0) {+ pid_t grandchild = fork();+ if (grandchild == 0) {+ /* Wait (up to 3 s) for our parent to go and init to adopt us. */+ pid_t ppid = getppid();+ for (int i = 0; i < 300 && ppid != 1; i++) {+ sleep_ms(10);+ ppid = getppid();+ }+ pid_t me = getpid();+ write(fds[1], &me, sizeof me);+ write(fds[1], &ppid, sizeof ppid);+ _exit(0);+ }+ _exit(0); /* leave the grandchild behind */+ }+ close(fds[1]);+ int st;+ check(middle > 0 && waitpid(middle, &st, 0) == middle && WIFEXITED(st), "the middle process exited");+ pid_t grandchild = 0, ppid = 0;+ int got = read(fds[0], &grandchild, sizeof grandchild) == sizeof grandchild &&+ read(fds[0], &ppid, sizeof ppid) == sizeof ppid;+ printf("orphan: grandchild %d reports parent %d\n", (int)grandchild, (int)ppid);+ check(got && ppid == 1, "orphaned grandchild adopted by pid 1 (init)");+ /* It has exited now (or is about to); init must reap it. */+ int gone = 0;+ for (int i = 0; i < 300 && !gone; i++) {+ gone = kill(grandchild, 0) == -1 && errno == ESRCH;+ if (!gone)+ sleep_ms(10);+ }+ check(got && gone, "init reaped it (kill(pid, 0) = ESRCH)");+ check(waitpid(-1, &st, WNOHANG) == -1 && errno == ECHILD, "it is not our child (waitpid = ECHILD)");+ if (failed)+ printf("orphan: %d check(s) FAILED\n", failed);+ else+ printf("orphan: all 4 checks passed\n");+ return failed;+}
@@ -57,7 +57,7 @@ BIN_ORDER := sh ls cat wc grep mkdir rm touch cp sleep \ # as static-PIEs against our musl (milestone M2). They go to /bin after the # programs above, and the self-test runs them (rootfs/scripts/selftest.sh, # the "Foundry OS" block; expected lines in scripts/expect-foundry.txt).-FOUNDRY_C := hello-musl fprobe+FOUNDRY_C := hello-musl fprobe orphan # Third-party programs from ports/NAME (pinned tarball in cache/, built # static-PIE with foundry-cc), installed as /bin/NAME. FOUNDRY_PORTS := dash