/* Stage 21: the "init" on the boot-contract-v2 test image (build.rs puts * the same file at /sbin/init, /bin/init and /bin/sh of the root * partition in target/rootfs-gpt.img). * * It looks at AT_EXECFN (the path the kernel started it from): * /sbin/init -> checks what the contract promises (below), then exits * with the number in /etc/init-status (0 if missing), or * 1 if a check failed; * /bin/init -> exits 21; /bin/sh -> exits 22 * so the tests can tell which one of the fallback chain ran. * * The checks: pid 1, argv[0] "init", PATH, "/" is the ext2 root, the * kernel's mounts are still there, /mnt/share (the exFAT partition) is * readable and writable, and msync's argument checks. * Also runs on a normal Linux host (where it isn't pid 1). */ #include #include #include #include #include #include #include #include #include static int failed; static void check(int ok, const char *what) { printf("[inittest] %s: %s\n", ok ? "ok" : "FAILED", what); if (!ok) failed++; } static int read_file(const char *path, char *buf, size_t len) { int fd = open(path, O_RDONLY); if (fd < 0) return -1; int n = read(fd, buf, len - 1); close(fd); if (n >= 0) buf[n] = 0; return n; } static int is_dir(const char *path) { struct stat st; return stat(path, &st) == 0 && S_ISDIR(st.st_mode); } static void contract(int argc, char **argv) { const char *path = getenv("PATH"); const char *headless = getenv("RUSTBELT_HEADLESS"); printf("[inittest] pid %d, argc %d, argv[0] %s, argv[1] %s, PATH=%s, RUSTBELT_HEADLESS=%s\n", getpid(), argc, argv[0], argc > 1 ? argv[1] : "-", path ? path : "-", headless ? headless : "-"); check(getpid() == 1, "I am pid 1"); check(strcmp(argv[0], "init") == 0, "argv[0] is \"init\""); check(!headless || (argc > 1 && strcmp(argv[1], "selftest") == 0), "headless: argv[1] is \"selftest\""); check(path && strcmp(path, "/sbin:/bin:/usr/bin:/mnt/disk0/bin") == 0, "PATH=/sbin:/bin:/usr/bin:/mnt/disk0/bin"); char buf[256]; check(read_file("/etc/rootfs-v2", buf, sizeof buf) > 0 && strstr(buf, "ext2 root"), "/ is the ext2 root partition"); check(is_dir("/tmp") && is_dir("/dev") && is_dir("/proc") && is_dir("/mnt/disk0"), "/tmp /dev /proc /mnt/disk0 are there"); int fd = open("/tmp/inittest.tmp", O_CREAT | O_WRONLY | O_TRUNC, 0644); check(fd >= 0 && write(fd, "x", 1) == 1, "/tmp is writable"); close(fd); unlink("/tmp/inittest.tmp"); /* The exFAT share. */ check(read_file("/mnt/share/hello.txt", buf, sizeof buf) > 0 && strcmp(buf, "Hello from the exFAT share!\n") == 0, "/mnt/share/hello.txt reads back"); fd = open("/mnt/share/from-init.txt", O_CREAT | O_WRONLY | O_TRUNC, 0644); check(fd >= 0 && write(fd, "written by init\n", 16) == 16, "/mnt/share is writable"); close(fd); check(read_file("/mnt/share/from-init.txt", buf, sizeof buf) == 16 && strcmp(buf, "written by init\n") == 0, "...and the new file reads back"); /* B: (the FAT32 data disk, /mnt/disk0). Writing to it sets FAT's * "dirty bit"; since 0.21.1 the kernel's shutdown clears it again when * pid 1 exits (scripts/boot-v2-check.sh runs fsck.fat afterwards). */ fd = open("/mnt/disk0/from-init.txt", O_CREAT | O_WRONLY | O_TRUNC, 0644); check(fd >= 0 && write(fd, "written by init\n", 16) == 16, "/mnt/disk0 (B:, FAT32) is writable"); close(fd); check(read_file("/mnt/disk0/from-init.txt", buf, sizeof buf) == 16 && strcmp(buf, "written by init\n") == 0, "...and the new file reads back"); /* msync (26): Rustbelt has nothing to write back, but checks its * arguments like Linux. */ long pg = 4096; char *anon = mmap(0, 2 * pg, PROT_READ | PROT_WRITE, MAP_SHARED | MAP_ANONYMOUS, -1, 0); check(anon != MAP_FAILED, "mmap MAP_SHARED|MAP_ANONYMOUS"); anon[0] = 1; check(msync(anon, 2 * pg, MS_SYNC) == 0, "msync(MS_SYNC) = 0"); check(msync(anon, 1, MS_ASYNC | MS_INVALIDATE) == 0, "msync(MS_ASYNC|MS_INVALIDATE), partial page = 0"); check(msync(anon, 0, MS_SYNC) == 0, "msync(len 0) = 0"); check(msync(anon + 1, pg, MS_SYNC) == -1 && errno == EINVAL, "msync(unaligned) = EINVAL"); check(msync(anon, pg, 8) == -1 && errno == EINVAL, "msync(bad flags) = EINVAL"); check(msync(anon, pg, MS_SYNC | MS_ASYNC) == -1 && errno == EINVAL, "msync(MS_SYNC|MS_ASYNC) = EINVAL"); munmap(anon + pg, pg); check(msync(anon, 2 * pg, MS_SYNC) == -1 && errno == ENOMEM, "msync over an unmapped page = ENOMEM"); munmap(anon, pg); check(msync(anon, pg, MS_SYNC) == -1 && errno == ENOMEM, "msync(unmapped) = ENOMEM"); char *priv = mmap(0, pg, PROT_READ, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0); check(priv != MAP_FAILED && msync(priv, pg, MS_ASYNC) == 0, "msync(private mapping) = 0"); } int main(int argc, char **argv) { const char *execfn = (const char *)getauxval(AT_EXECFN); printf("[inittest] started as %s\n", execfn ? execfn : "?"); if (execfn && strcmp(execfn, "/bin/init") == 0) return 21; if (execfn && strcmp(execfn, "/bin/sh") == 0) return 22; contract(argc, argv); char buf[16]; int status = read_file("/etc/init-status", buf, sizeof buf) > 0 ? atoi(buf) : 0; printf("[inittest] %d failed\n", failed); fflush(stdout); return failed ? 1 : status; }