diff options
Diffstat (limited to 'cdinit.c')
-rw-r--r-- | cdinit.c | 387 |
1 files changed, 0 insertions, 387 deletions
diff --git a/cdinit.c b/cdinit.c deleted file mode 100644 index f5ccb22..0000000 --- a/cdinit.c +++ /dev/null @@ -1,387 +0,0 @@ -/* - * cdinit - start up udev, find the squashfs, and get the hell out of dodge - * Copyright (c) 2016-2018 Adélie Linux Team. All rights reserved. - * Licensed under the NCSA open source license. - * See LICENSE file included with this source for more information. - */ - - -#include <errno.h> /* errno */ -#include <fcntl.h> /* ioctl */ -#include <linux/loop.h> /* LOOP_SET_FD */ -#include <stdbool.h> /* bool */ -#include <stdio.h> /* stderr, fprintf */ -#include <stdlib.h> /* EXIT_FAILURE */ -#include <string.h> /* strlen */ -#include <sys/mount.h> /* mount */ -#include <sys/stat.h> /* mkdir */ -#include <sys/wait.h> /* waitpid, W* macros */ -#include <unistd.h> /* exec, fork, etc */ - -#include <blkid/blkid.h>/* blkid_get_tag_value */ -#include <libudev.h> /* udev* */ - - -/*! - * @brief Grab a parameter out of /proc/cmdline, if it is set. - * @param param The parameter you wish to read. - * @param def The default value if no value is set (or NULL). - * @returns The value of the parameter as set in /proc/cmdline, or - * the value of `default` if no value is set. - * @note If a parameter is passed on the command line but has no value - * (ex: 'debug'), its value will not be returned. - */ -const char *get_param(const char *param, const char *def) -{ - char *str = getenv(param); - return str ? str : def; -} - -/*! - * @brief Invoke a specified command. Return if it is run successfully. - * @param command User-readable description of what command is to run. - * @param path The full on-disk path to the executable to run. - * @param argv The full argument list to run. - * @returns true if command runs successfully, false otherwise. - */ -bool cdi_invoke(const char *command, const char *path, char * const argv[]) -{ - pid_t our_pid; - - fprintf(stdout, " * Starting %s... ", command); - - our_pid = fork(); - if(our_pid == 0) - { - execv(path, argv); - fprintf(stderr, "could not start %s: %s\n", command, - strerror(errno)); - return 255; - } - else if(our_pid == -1) - { - fprintf(stderr, "[ !! ] failed to fork: %s\n", strerror(errno)); - return false; - } - else - { - int status, code; - - waitpid(our_pid, &status, 0); - if(!WIFEXITED(status)) - { - fprintf(stderr, "[ !! ] %s caught signal\n", command); - return false; - } - - code = WEXITSTATUS(status); - if(code != 0) - { - fprintf(stderr, "[ !! ] %s exited with error code %d\n", - command, code); - return false; - } - } - - fprintf(stdout, "[ ok ]\n"); - - return true; -} - -/* We need to test each block device in the system, in case there are - * multiple drives that all have media present. We can narrow it down, - * however: - * - * - We know that our device will have the specified squashfs. - * - We know it will be mountable without external helpers, as ISO9660, - * FAT, and HFS+ are built in to the kernel. - */ -bool cdi_find_media(const char *squash_name) -{ - struct udev *udev; - struct udev_enumerate *dev_list; - struct udev_list_entry *first, *candidate; - struct udev_device *device = NULL; - - udev = udev_new(); - if(udev == NULL) - { - fprintf(stderr, "Cannot establish udev link.\n"); - return false; - } - - dev_list = udev_enumerate_new(udev); - if(dev_list == NULL) - { - fprintf(stderr, "Cannot create enumerator (memory?)\n"); - return false; - } - - udev_enumerate_add_match_subsystem(dev_list, "block"); -#ifdef DISKONLY /* support booting off USB partition unless DISKONLY */ - udev_enumerate_add_match_property(dev_list, "DEVTYPE", "disk"); -#endif - udev_enumerate_scan_devices(dev_list); - - first = udev_enumerate_get_list_entry(dev_list); - if(first == NULL) - { - fprintf(stderr, "No block devices found.\n"); - fprintf(stderr, "This system cannot boot Adélie Linux " - "without additional drivers.\n"); - return false; - } - - udev_list_entry_foreach(candidate, first) - { - const char *path = udev_list_entry_get_name(candidate); - char *fstype = NULL; - - if(device != NULL) - { - udev_device_unref(device); - } - device = udev_device_new_from_syspath(udev, path); - errno = 0; - - const char *dev_node = udev_device_get_devnode(device); - if(dev_node == NULL) - { - continue; /* worthless */ - } - - if((fstype = blkid_get_tag_value(NULL, "TYPE", dev_node)) == NULL) - { -#ifdef DEBUG - fprintf(stderr, "cdi_find_media: %s: unknown FS\n", - dev_node); -#endif - continue; - } - - if(mount(dev_node, "/media", fstype, MS_RDONLY, NULL) != 0) - { - fprintf(stderr, "cdi_find_media: mounting %s:" - " %s\n", dev_node, strerror(errno)); - free(fstype); - continue; - } - free(fstype); - - if(chdir("/media") != 0) - { - fprintf(stderr, "cdi_find_media: error accessing %s:" - " %s\n", dev_node, strerror(errno)); - umount("/media"); - continue; - } - if(faccessat(AT_FDCWD, squash_name, F_OK, 0) != 0) - { -#ifdef DEBUG - fprintf(stderr, "cdi_find_media: %s: system not found:" - " %s\n", dev_node, strerror(errno)); -#endif - chdir("/"); - umount("/media"); - continue; - } - -#ifdef DEBUG - fprintf(stderr, "cdi_find_media: LiveFS located at %s\n", - dev_node); -#endif - - int squash_fd = openat(AT_FDCWD, squash_name, O_RDONLY); - if(squash_fd == -1) - { - chdir("/"); - umount("/media"); - continue; - } - - int dev_fd = open("/dev/loop4", O_RDWR); - if(dev_fd == -1) - { - close(squash_fd); - chdir("/"); - umount("/media"); - continue; - } - - ioctl(dev_fd, LOOP_SET_FD, squash_fd); - close(squash_fd); - - if(mount("/dev/loop4", "/lowerroot", "squashfs", MS_RDONLY, - NULL) != 0) - { -#ifdef DEBUG - fprintf(stderr, "cdi_find_media: %s contained invalid" - " LiveFS image: %s\n", dev_node, - strerror(errno)); -#endif - ioctl(dev_fd, LOOP_CLR_FD, 0); - close(dev_fd); - chdir("/"); - umount("/media"); - continue; - } - - close(dev_fd); - chdir("/"); - udev_device_unref(device); - device = NULL; - break; - } - - udev_enumerate_unref(dev_list); - udev_unref(udev); - - return (access("/lowerroot/sbin/init", F_OK) == 0); -} - - - -int main(void) -{ - pid_t our_pid; - const char *squash; - bool found = false; - unsigned char tries = 4; - -#ifndef DEBUG - if(getpid() != 1) - { - fprintf(stderr, "This application can only boot live media.\n"); - return EXIT_FAILURE; - } -#endif - - fprintf(stdout, " * Adélie Linux is starting up...\n\n"); - - if(mount("none", "/dev", "devtmpfs", 0, NULL) != 0) - { - fprintf(stderr, "FATAL: Can't mount devtmpfs at /dev: %s\n", - strerror(errno)); - return EXIT_FAILURE; - } - - if(mount("none", "/proc", "proc", 0, NULL) != 0) - { - fprintf(stderr, "FATAL: Can't mount early /proc: %s\n", - strerror(errno)); - return EXIT_FAILURE; - } - - if(mount("none", "/sys", "sysfs", 0, NULL) != 0) - { - fprintf(stderr, "FATAL: Can't mount early /sys: %s\n", - strerror(errno)); - return EXIT_FAILURE; - } - - /* Our entire goal for this system is to be small and fast, in both - * execution and code. This is not the place for bloat and this is not - * the place to do a tech demo of some shiny new API. - * - * Installers and rescue media must be very stable. The less surface - * this system has, the less can go wrong. - * - * As we are pid1, we first need to start up udev. - */ - { - char * const argv[] = {"udevd", "--daemon", -#ifdef DEBUG - "--debug", -#endif - "--event-timeout=30", - "--resolve-names=never", - (char *)0}; - if(!cdi_invoke("early udevd", "/sbin/udevd", argv)) - { - return EXIT_FAILURE; - } - } - - /* udev is started. Now we need to communicate. - * - * First, we trigger block device uevents. This will ensure that all - * block devices supported by the kernel have /dev entries that we can - * use. - */ - { - char * const argv[] = {"udevadm", "trigger", "--action=add", - "--subsystem-match=blocK", 0}; - if(!cdi_invoke("block uevents", "/sbin/udevadm", argv)) - { - return EXIT_FAILURE; - } - } - - /* Figure out what our squashfs is named. */ - squash = get_param("squashroot", "adelie.squashfs"); - - /* Now we need to iterate over the available block devices, trying to - * find our CD media. - */ - found = cdi_find_media(squash); - while(!found && tries--) - { - found = cdi_find_media(squash); - fprintf(stderr, "Attempting to acquiesce...\n"); - sleep(5); - } - - if(!found) - { - fprintf(stderr, "FATAL: no boot media found\n"); - return EXIT_FAILURE; - } - - { - char * const argv[] = {"udevadm", "control", "-e", 0}; - cdi_invoke("clean up", "/sbin/udevadm", argv); - } - - /* Create our OverlayFS mount, so everything is writable. - * TODO: USB-based persistence - */ - if(mount("tmpfs", "/upperroot", "tmpfs", 0, NULL) != 0) - { - fprintf(stderr, "FATAL: could not mount tmpfs: %s\n", - strerror(errno)); - return EXIT_FAILURE; - } - - /* If this fails, we'll find out below */ - mkdir("/upperroot/.overlay_work", S_IRWXU | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH); - mkdir("/upperroot/.root", S_IRWXU | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH); - - if(mount("overlay", "/newroot", "overlay", 0, - "lowerdir=/lowerroot," - "upperdir=/upperroot/.root," - "workdir=/upperroot/.overlay_work") != 0) - { - fprintf(stderr, "FATAL: could not mount overlayfs: %s\n", - strerror(errno)); - return EXIT_FAILURE; - } - - mount("/dev", "/newroot/dev", NULL, MS_MOVE, NULL); - mount("/media", "/newroot/media/live", NULL, MS_MOVE, NULL); - mount("/proc", "/newroot/proc", NULL, MS_MOVE, NULL); - mount("/sys", "/newroot/sys", NULL, MS_MOVE, NULL); - - chdir("/newroot"); - if(mount("/newroot", "/", NULL, MS_MOVE, NULL) != 0) - { - fprintf(stderr, "FATAL: could not pivot to /newroot: %s\n", - strerror(errno)); - return EXIT_FAILURE; - } - - chroot("."); - execl("/sbin/init", "init", (char *)0); - - fprintf(stderr, "Going nowhere without my new root's init\n"); - return EXIT_FAILURE; -} |