RustForge

foundry

Foundry OS: the operating system built on the Rustbelt kernel

foundry / c / orphan.c
2867 bytes ยท raw
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
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;
}