summaryrefslogtreecommitdiff
path: root/cdinit.c
diff options
context:
space:
mode:
Diffstat (limited to 'cdinit.c')
-rw-r--r--cdinit.c387
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;
-}