diff options
author | A. Wilcox <AWilcox@Wilcox-Tech.com> | 2016-12-09 02:41:11 -0600 |
---|---|---|
committer | A. Wilcox <AWilcox@Wilcox-Tech.com> | 2016-12-09 02:41:11 -0600 |
commit | 9acabb63bef0cdf55a2bf490edef91cb8b2f7190 (patch) | |
tree | b9f476610ffae6d1fadecd6f4a2ac1712646492a /cdinit.c | |
download | image-9acabb63bef0cdf55a2bf490edef91cb8b2f7190.tar.gz image-9acabb63bef0cdf55a2bf490edef91cb8b2f7190.tar.bz2 image-9acabb63bef0cdf55a2bf490edef91cb8b2f7190.tar.xz image-9acabb63bef0cdf55a2bf490edef91cb8b2f7190.zip |
Initial commit to Git
Diffstat (limited to 'cdinit.c')
-rw-r--r-- | cdinit.c | 329 |
1 files changed, 329 insertions, 0 deletions
diff --git a/cdinit.c b/cdinit.c new file mode 100644 index 0000000..2990106 --- /dev/null +++ b/cdinit.c @@ -0,0 +1,329 @@ +/* + * cdinit - start up udev, find the squashfs, and get the hell out of dodge + * Copyright (c) 2016 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 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(stdout, "[ !! ] failed to fork: %s\n", strerror(errno)); + return false; + } + else + { + int status, code; + + waitpid(our_pid, &status, 0); + if(!WIFEXITED(status)) + { + fprintf(stdout, "[ !! ] %s caught signal\n", command); + return false; + } + + code = WEXITSTATUS(status); + if(code != 0) + { + fprintf(stdout, "[ !! ] %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 'adelie.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(void) +{ + 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 link to udev.\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(stdout, "No block devices found.\n"); + fprintf(stdout, "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 */ + } + +#ifdef DEBUG + fprintf(stderr, "cdi_find_media: testing candidate %s\n", + dev_node); +#endif + + if((fstype = blkid_get_tag_value(NULL, "TYPE", dev_node)) == NULL) + { +#ifdef DEBUG + fprintf(stderr, "cdi_find_media: %s has unknown FS\n", + dev_node); +#endif + continue; + } + + if(mount(dev_node, "/media", fstype, MS_RDONLY, NULL) != 0) + { +#ifdef DEBUG + fprintf(stderr, "cdi_find_media: %s cannot be mounted:" + " %s\n", dev_node, strerror(errno)); +#endif + free(fstype); + continue; + } + free(fstype); + + if(access("/media/adelie.squashfs", F_OK) != 0) + { +#ifdef DEBUG + fprintf(stderr, "cdi_find_media: %s has no squash root:" + " %s\n", dev_node, strerror(errno)); +#endif + umount("/media"); + continue; + } + +#ifdef DEBUG + fprintf(stderr, "cdi_find_media: LiveFS located at %s\n", + dev_node); +#endif + + int squash_fd = open("/media/adelie.squashfs", O_RDONLY); + if(squash_fd == -1) + { + umount("/media"); + continue; + } + + int dev_fd = open("/dev/loop4", O_RDWR); + if(dev_fd == -1) + { + close(squash_fd); + umount("/media"); + continue; + } + + ioctl(dev_fd, LOOP_SET_FD, squash_fd); + close(squash_fd); + + if(mount("/dev/loop4", "/newroot", "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); + umount("/media"); + continue; + } + + close(dev_fd); + udev_device_unref(device); + device = NULL; + break; + } + + udev_enumerate_unref(dev_list); + udev_unref(udev); + + return (access("/newroot/sbin/init", F_OK) == 0); +} + + + +int main(void) +{ + pid_t our_pid; + +#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(stdout, "FATAL: Can't mount devtmpfs at /dev: %s\n", + strerror(errno)); + return EXIT_FAILURE; + } + + if(mount("none", "/proc", "proc", 0, NULL) != 0) + { + fprintf(stdout, "FATAL: Can't mount early /proc: %s\n", + strerror(errno)); + return EXIT_FAILURE; + } + + if(mount("none", "/sys", "sysfs", 0, NULL) != 0) + { + fprintf(stdout, "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; + } + } + + /* Now we need to iterate over the available block devices, trying to + * find our CD media. + */ + if(!cdi_find_media()) + { + fprintf(stdout, "FATAL: no boot media found\n"); + return EXIT_FAILURE; + } + + { + char * const argv[] = {"udevadm", "control", "-e", 0}; + cdi_invoke("clean up", "/sbin/udevadm", argv); + } + + 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, "could not pivot to /newroot: %s\n", + strerror(errno)); + return EXIT_FAILURE; + } + + chroot("."); + execl("/sbin/init", "init", (char *)0); + + fprintf(stdout, "Going nowhere without my new root's init\n"); + return EXIT_FAILURE; +} |