int main(int argc, char *argv[]) {
        int r = 0;

        if (argc > 1 && argc != 4) {
                log_error("This program takes three or no arguments.");
                return EXIT_FAILURE;
        }

        if (argc > 1)
                arg_dest = argv[1];

        log_set_target(LOG_TARGET_SAFE);
        log_parse_environment();
        log_open();

        umask(0022);

        r = parse_proc_cmdline(parse_proc_cmdline_item);
        if (r < 0)
                log_warning_errno(r, "Failed to parse kernel command line, ignoring: %m");

        /* Always honour root= and usr= in the kernel command line if we are in an initrd */
        if (in_initrd()) {
                r = add_root_mount();
                if (r == 0)
                        r = add_usr_mount();
        }

        /* Honour /etc/fstab only when that's enabled */
        if (arg_fstab_enabled) {
                int k;

                log_debug("Parsing /etc/fstab");

                /* Parse the local /etc/fstab, possibly from the initrd */
                k = parse_fstab(false);
                if (k < 0)
                        r = k;

                /* If running in the initrd also parse the /etc/fstab from the host */
                if (in_initrd()) {
                        log_debug("Parsing /sysroot/etc/fstab");

                        k = parse_fstab(true);
                        if (k < 0)
                                r = k;
                }
        }

        free(arg_root_what);
        free(arg_root_fstype);
        free(arg_root_options);

        free(arg_usr_what);
        free(arg_usr_fstype);
        free(arg_usr_options);

        return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;
}
Beispiel #2
0
static int parse_proc_cmdline(void) {
        char *line, *w, *state;
        int r;
        size_t l;

        if (detect_container(NULL) > 0)
                return 0;

        r = read_one_line_file("/proc/cmdline", &line);
        if (r < 0) {
                log_warning("Failed to read /proc/cmdline, ignoring: %s", strerror(-r));
                return 0;
        }

        FOREACH_WORD_QUOTED(w, l, line, state) {
                char *word;

                word = strndup(w, l);
                if (!word) {
                        r = log_oom();
                        goto finish;
                }

                if (startswith(word, "fstab=")) {
                        r = parse_boolean(word + 6);
                        if (r < 0)
                                log_warning("Failed to parse fstab switch %s. Ignoring.", word + 6);
                        else
                                arg_enabled = r;

                } else if (startswith(word, "rd.fstab=")) {

                        if (in_initrd()) {
                                r = parse_boolean(word + 6);
                                if (r < 0)
                                        log_warning("Failed to parse fstab switch %s. Ignoring.", word + 6);
                                else
                                        arg_enabled = r;
                        }

                } else if (startswith(word, "fstab.") ||
                           (in_initrd() && startswith(word, "rd.fstab."))) {

                        log_warning("Unknown kernel switch %s. Ignoring.", word);
                }

                free(word);
        }
int main(int argc, char *argv[]) {
        int r = 0;

        if (argc > 1 && argc != 4) {
                log_error("This program takes three or no arguments.");
                return EXIT_FAILURE;
        }

        if (argc > 1)
                arg_dest = argv[1];

        log_set_prohibit_ipc(true);
        log_set_target(LOG_TARGET_AUTO);
        log_parse_environment();
        log_open();

        umask(0022);

        /* Don't even consider resuming outside of initramfs. */
        if (!in_initrd())
                return EXIT_SUCCESS;

        r = proc_cmdline_parse(parse_proc_cmdline_item, NULL, 0);
        if (r < 0)
                log_warning_errno(r, "Failed to parse kernel command line, ignoring: %m");

        r = process_resume();
        free(arg_resume_device);

        return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;
}
Beispiel #4
0
const char* runlevel_to_target(const char *word) {
        size_t i;
        const char * const *rlmap_ptr = in_initrd() ? rlmap_initrd
                                                    : rlmap;

        if (!word)
                return NULL;

        if (in_initrd() && (word = startswith(word, "rd.")) == NULL)
                return NULL;

        for (i = 0; rlmap_ptr[i] != NULL; i += 2)
                if (streq(word, rlmap_ptr[i]))
                        return rlmap_ptr[i+1];

        return NULL;
}
Beispiel #5
0
static int killall(int sig, Set *pids, bool send_sighup) {
        _cleanup_closedir_ DIR *dir = NULL;
        struct dirent *d;

        dir = opendir("/proc");
        if (!dir)
                return -errno;

        while ((d = readdir(dir))) {
                pid_t pid;
                int r;

                if (d->d_type != DT_DIR &&
                    d->d_type != DT_UNKNOWN)
                        continue;

                if (parse_pid(d->d_name, &pid) < 0)
                        continue;

                if (ignore_proc(pid, sig == SIGKILL && !in_initrd()))
                        continue;

                if (sig == SIGKILL) {
                        _cleanup_free_ char *s = NULL;

                        get_process_comm(pid, &s);
                        log_notice("Sending SIGKILL to PID "PID_FMT" (%s).", pid, strna(s));
                }

                if (kill(pid, sig) >= 0) {
                        if (pids) {
                                r = set_put(pids, PID_TO_PTR(pid));
                                if (r < 0)
                                        log_oom();
                        }
                } else if (errno != ENOENT)
                        log_warning_errno(errno, "Could not kill %d: %m", pid);

                if (send_sighup) {
                        /* Optionally, also send a SIGHUP signal, but
                        only if the process has a controlling
                        tty. This is useful to allow handling of
                        shells which ignore SIGTERM but react to
                        SIGHUP. We do not send this to processes that
                        have no controlling TTY since we don't want to
                        trigger reloads of daemon processes. Also we
                        make sure to only send this after SIGTERM so
                        that SIGTERM is always first in the queue. */


                        if (get_ctty_devnr(pid, NULL) >= 0)
                                kill(pid, SIGHUP);
                }
        }

        return set_size(pids);
}
Beispiel #6
0
int main(int argc, char *argv[]) {
        struct stat st;
        const char *device;
        _cleanup_free_ char *major_minor = NULL;
        int r;

        if (argc != 2) {
                log_error("This program expects one argument.");
                return EXIT_FAILURE;
        }

        log_set_target(LOG_TARGET_AUTO);
        log_parse_environment();
        log_open();

        umask(0022);

        /* Refuse to run unless we are in an initrd() */
        if (!in_initrd())
                return EXIT_SUCCESS;

        device = argv[1];

        if (stat(device, &st) < 0) {
                log_error_errno(errno, "Failed to stat '%s': %m", device);
                return EXIT_FAILURE;
        }

        if (!S_ISBLK(st.st_mode)) {
                log_error("Resume device '%s' is not a block device.", device);
                return EXIT_FAILURE;
        }

        if (asprintf(&major_minor, "%d:%d", major(st.st_rdev), minor(st.st_rdev)) < 0) {
                log_oom();
                return EXIT_FAILURE;
        }

        r = write_string_file("/sys/power/resume", major_minor, WRITE_STRING_FILE_CREATE);
        if (r < 0) {
                log_error_errno(r, "Failed to write '%s' to /sys/power/resume: %m", major_minor);
                return EXIT_FAILURE;
        }

        /*
         * The write above shall not return.
         *
         * However, failed resume is a normal condition (may mean that there is
         * no hibernation image).
         */

        log_info("Could not resume from '%s' (%s).", device, major_minor);
        return EXIT_SUCCESS;
}
Beispiel #7
0
int get_proc_cmdline_key(const char *key, char **value) {
        _cleanup_free_ char *line = NULL, *ret = NULL;
        bool found = false;
        const char *p;
        int r;

        assert(key);

        r = proc_cmdline(&line);
        if (r < 0)
                return r;

        p = line;
        for (;;) {
                _cleanup_free_ char *word = NULL;
                const char *e;

                r = extract_first_word(&p, &word, NULL, EXTRACT_QUOTES|EXTRACT_RELAX);
                if (r < 0)
                        return r;
                if (r == 0)
                        break;

                /* Filter out arguments that are intended only for the
                 * initrd */
                if (!in_initrd() && startswith(word, "rd."))
                        continue;

                if (value) {
                        e = startswith(word, key);
                        if (!e)
                                continue;

                        r = free_and_strdup(&ret, e);
                        if (r < 0)
                                return r;

                        found = true;
                } else {
                        if (streq(word, key))
                                found = true;
                }
        }

        if (value) {
                *value = ret;
                ret = NULL;
        }

        return found;

}
int proc_cmdline_parse(proc_cmdline_parse_t parse_item, void *data, unsigned flags) {

        _cleanup_free_ char *line = NULL;
        const char *p;
        int r;

        assert(parse_item);

        r = proc_cmdline(&line);
        if (r < 0)
                return r;

        p = line;
        for (;;) {
                _cleanup_free_ char *word = NULL;
                char *value, *key, *q;

                r = extract_first_word(&p, &word, NULL, EXTRACT_QUOTES|EXTRACT_RELAX);
                if (r < 0)
                        return r;
                if (r == 0)
                        break;

                key = word;

                /* Filter out arguments that are intended only for the initrd */
                q = startswith(word, "rd.");
                if (q) {
                        if (!in_initrd())
                                continue;

                        if (flags & PROC_CMDLINE_STRIP_RD_PREFIX)
                                key = q;
                }

                value = strchr(key, '=');
                if (value)
                        *(value++) = 0;

                r = parse_item(key, value, data);
                if (r < 0)
                        return r;
        }

        return 0;
}
int parse_proc_cmdline(int (*parse_item)(const char *key, const char *value, void *data),
                       void *data,
                       bool strip_prefix) {
        _cleanup_free_ char *line = NULL;
        const char *p;
        int r;

        assert(parse_item);

        r = proc_cmdline(&line);
        if (r < 0)
                return r;

        p = line;
        for (;;) {
                _cleanup_free_ char *word = NULL;
                char *value = NULL, *unprefixed;

                r = extract_first_word(&p, &word, NULL, EXTRACT_QUOTES|EXTRACT_RELAX);
                if (r < 0)
                        return r;
                if (r == 0)
                        break;

                /* Filter out arguments that are intended only for the
                 * initrd */
                unprefixed = startswith(word, "rd.");
                if (unprefixed && !in_initrd())
                        continue;

                value = strchr(word, '=');
                if (value)
                        *(value++) = 0;

                r = parse_item(strip_prefix && unprefixed ? unprefixed : word, value, data);
                if (r < 0)
                        return r;
        }

        return 0;
}
Beispiel #10
0
int mac_selinux_setup(bool *loaded_policy) {

#ifdef HAVE_SELINUX
        int enforce = 0;
        usec_t before_load, after_load;
        security_context_t con;
        int r;
        union selinux_callback cb;
        bool initialized = false;

        assert(loaded_policy);

        /* Turn off all of SELinux' own logging, we want to do that */
        cb.func_log = null_log;
        selinux_set_callback(SELINUX_CB_LOG, cb);

        /* Don't load policy in the initrd if we don't appear to have
         * it.  For the real root, we check below if we've already
         * loaded policy, and return gracefully.
         */
        if (in_initrd() && access(selinux_path(), F_OK) < 0)
                return 0;

        /* Already initialized by somebody else? */
        r = getcon_raw(&con);
        if (r == 0) {
                initialized = !streq(con, "kernel");
                freecon(con);
        }

        /* Make sure we have no fds open while loading the policy and
         * transitioning */
        log_close();

        /* Now load the policy */
        before_load = now(CLOCK_MONOTONIC);
        r = selinux_init_load_policy(&enforce);
        if (r == 0) {
                _cleanup_(mac_selinux_freep) char *label = NULL;
                char timespan[FORMAT_TIMESPAN_MAX];

                mac_selinux_retest();

                /* Transition to the new context */
                r = mac_selinux_get_create_label_from_exe(SYSTEMD_BINARY_PATH, &label);
                if (r < 0 || !label) {
                        log_open();
                        log_error("Failed to compute init label, ignoring.");
                } else {
                        r = setcon_raw(label);

                        log_open();
                        if (r < 0)
                                log_error("Failed to transition into init label '%s', ignoring.", label);
                }

                after_load = now(CLOCK_MONOTONIC);

                log_info("Successfully loaded SELinux policy in %s.",
                         format_timespan(timespan, sizeof(timespan), after_load - before_load, 0));

                *loaded_policy = true;

        } else {
Beispiel #11
0
int main(int argc, char *argv[]) {
        bool need_umount, need_swapoff, need_loop_detach, need_dm_detach;
        bool in_container, use_watchdog = false, can_initrd;
        _cleanup_free_ char *cgroup = NULL;
        char *arguments[3];
        int cmd, r, umount_log_level = LOG_INFO;
        static const char* const dirs[] = {SYSTEM_SHUTDOWN_PATH, NULL};
        char *watchdog_device;

        /* The log target defaults to console, but the original systemd process will pass its log target in through a
         * command line argument, which will override this default. Also, ensure we'll never log to the journal or
         * syslog, as these logging daemons are either already dead or will die very soon. */

        log_set_target(LOG_TARGET_CONSOLE);
        log_set_prohibit_ipc(true);
        log_parse_environment();

        r = parse_argv(argc, argv);
        if (r < 0)
                goto error;

        log_open();

        umask(0022);

        if (getpid_cached() != 1) {
                log_error("Not executed by init (PID 1).");
                r = -EPERM;
                goto error;
        }

        if (streq(arg_verb, "reboot"))
                cmd = RB_AUTOBOOT;
        else if (streq(arg_verb, "poweroff"))
                cmd = RB_POWER_OFF;
        else if (streq(arg_verb, "halt"))
                cmd = RB_HALT_SYSTEM;
        else if (streq(arg_verb, "kexec"))
                cmd = LINUX_REBOOT_CMD_KEXEC;
        else if (streq(arg_verb, "exit"))
                cmd = 0; /* ignored, just checking that arg_verb is valid */
        else {
                log_error("Unknown action '%s'.", arg_verb);
                r = -EINVAL;
                goto error;
        }

        (void) cg_get_root_path(&cgroup);
        in_container = detect_container() > 0;

        use_watchdog = getenv("WATCHDOG_USEC");
        watchdog_device = getenv("WATCHDOG_DEVICE");
        if (watchdog_device) {
                r = watchdog_set_device(watchdog_device);
                if (r < 0)
                        log_warning_errno(r, "Failed to set watchdog device to %s, ignoring: %m",
                                          watchdog_device);
        }

        /* Lock us into memory */
        (void) mlockall(MCL_CURRENT|MCL_FUTURE);

        /* Synchronize everything that is not written to disk yet at this point already. This is a good idea so that
         * slow IO is processed here already and the final process killing spree is not impacted by processes
         * desperately trying to sync IO to disk within their timeout. Do not remove this sync, data corruption will
         * result. */
        if (!in_container)
                sync_with_progress();

        disable_coredumps();

        log_info("Sending SIGTERM to remaining processes...");
        broadcast_signal(SIGTERM, true, true, arg_timeout);

        log_info("Sending SIGKILL to remaining processes...");
        broadcast_signal(SIGKILL, true, false, arg_timeout);

        need_umount = !in_container;
        need_swapoff = !in_container;
        need_loop_detach = !in_container;
        need_dm_detach = !in_container;
        can_initrd = !in_container && !in_initrd() && access("/run/initramfs/shutdown", X_OK) == 0;

        /* Unmount all mountpoints, swaps, and loopback devices */
        for (;;) {
                bool changed = false;

                if (use_watchdog)
                        watchdog_ping();

                /* Let's trim the cgroup tree on each iteration so
                   that we leave an empty cgroup tree around, so that
                   container managers get a nice notify event when we
                   are down */
                if (cgroup)
                        cg_trim(SYSTEMD_CGROUP_CONTROLLER, cgroup, false);

                if (need_umount) {
                        log_info("Unmounting file systems.");
                        r = umount_all(&changed, umount_log_level);
                        if (r == 0) {
                                need_umount = false;
                                log_info("All filesystems unmounted.");
                        } else if (r > 0)
                                log_info("Not all file systems unmounted, %d left.", r);
                        else
                                log_error_errno(r, "Failed to unmount file systems: %m");
                }

                if (need_swapoff) {
                        log_info("Deactivating swaps.");
                        r = swapoff_all(&changed);
                        if (r == 0) {
                                need_swapoff = false;
                                log_info("All swaps deactivated.");
                        } else if (r > 0)
                                log_info("Not all swaps deactivated, %d left.", r);
                        else
                                log_error_errno(r, "Failed to deactivate swaps: %m");
                }

                if (need_loop_detach) {
                        log_info("Detaching loop devices.");
                        r = loopback_detach_all(&changed, umount_log_level);
                        if (r == 0) {
                                need_loop_detach = false;
                                log_info("All loop devices detached.");
                        } else if (r > 0)
                                log_info("Not all loop devices detached, %d left.", r);
                        else
                                log_error_errno(r, "Failed to detach loop devices: %m");
                }

                if (need_dm_detach) {
                        log_info("Detaching DM devices.");
                        r = dm_detach_all(&changed, umount_log_level);
                        if (r == 0) {
                                need_dm_detach = false;
                                log_info("All DM devices detached.");
                        } else if (r > 0)
                                log_info("Not all DM devices detached, %d left.", r);
                        else
                                log_error_errno(r, "Failed to detach DM devices: %m");
                }

                if (!need_umount && !need_swapoff && !need_loop_detach && !need_dm_detach) {
                        log_info("All filesystems, swaps, loop devices and DM devices detached.");
                        /* Yay, done */
                        break;
                }

                if (!changed && umount_log_level == LOG_INFO && !can_initrd) {
                        /* There are things we cannot get rid of. Loop one more time
                         * with LOG_ERR to inform the user. Note that we don't need
                         * to do this if there is a initrd to switch to, because that
                         * one is likely to get rid of the remounting mounts. If not,
                         * it will log about them. */
                        umount_log_level = LOG_ERR;
                        continue;
                }

                /* If in this iteration we didn't manage to
                 * unmount/deactivate anything, we simply give up */
                if (!changed) {
                        log_info("Cannot finalize remaining%s%s%s%s continuing.",
                                 need_umount ? " file systems," : "",
                                 need_swapoff ? " swap devices," : "",
                                 need_loop_detach ? " loop devices," : "",
                                 need_dm_detach ? " DM devices," : "");
                        break;
                }

                log_debug("Couldn't finalize remaining %s%s%s%s trying again.",
                          need_umount ? " file systems," : "",
                          need_swapoff ? " swap devices," : "",
                          need_loop_detach ? " loop devices," : "",
                          need_dm_detach ? " DM devices," : "");
        }

        /* We're done with the watchdog. */
        watchdog_free_device();

        arguments[0] = NULL;
        arguments[1] = arg_verb;
        arguments[2] = NULL;
        execute_directories(dirs, DEFAULT_TIMEOUT_USEC, NULL, NULL, arguments, NULL);

        (void) rlimit_nofile_safe();

        if (can_initrd) {
                r = switch_root_initramfs();
                if (r >= 0) {
                        argv[0] = (char*) "/shutdown";

                        (void) setsid();
                        (void) make_console_stdio();

                        log_info("Successfully changed into root pivot.\n"
                                 "Returning to initrd...");

                        execv("/shutdown", argv);
                        log_error_errno(errno, "Failed to execute shutdown binary: %m");
                } else
                        log_error_errno(r, "Failed to switch root to \"/run/initramfs\": %m");

        }

        if (need_umount || need_swapoff || need_loop_detach || need_dm_detach)
                log_error("Failed to finalize %s%s%s%s ignoring",
                          need_umount ? " file systems," : "",
                          need_swapoff ? " swap devices," : "",
                          need_loop_detach ? " loop devices," : "",
                          need_dm_detach ? " DM devices," : "");

        /* The kernel will automatically flush ATA disks and suchlike on reboot(), but the file systems need to be
         * sync'ed explicitly in advance. So let's do this here, but not needlessly slow down containers. Note that we
         * sync'ed things already once above, but we did some more work since then which might have caused IO, hence
         * let's do it once more. Do not remove this sync, data corruption will result. */
        if (!in_container)
                sync_with_progress();

        if (streq(arg_verb, "exit")) {
                if (in_container)
                        return arg_exit_code;

                cmd = RB_POWER_OFF; /* We cannot exit() on the host, fallback on another method. */
        }

        switch (cmd) {

        case LINUX_REBOOT_CMD_KEXEC:

                if (!in_container) {
                        /* We cheat and exec kexec to avoid doing all its work */
                        log_info("Rebooting with kexec.");

                        r = safe_fork("(sd-kexec)", FORK_RESET_SIGNALS|FORK_CLOSE_ALL_FDS|FORK_LOG|FORK_WAIT, NULL);
                        if (r == 0) {
                                const char * const args[] = {
                                        KEXEC, "-e", NULL
                                };

                                /* Child */

                                execv(args[0], (char * const *) args);
                                _exit(EXIT_FAILURE);
                        }

                        /* If we are still running, then the kexec can't have worked, let's fall through */
                }

                cmd = RB_AUTOBOOT;
                _fallthrough_;

        case RB_AUTOBOOT:
                (void) reboot_with_parameter(REBOOT_LOG);
                log_info("Rebooting.");
                break;

        case RB_POWER_OFF:
                log_info("Powering off.");
                break;

        case RB_HALT_SYSTEM:
                log_info("Halting system.");
                break;

        default:
                assert_not_reached("Unknown magic");
        }

        (void) reboot(cmd);
        if (errno == EPERM && in_container) {
                /* If we are in a container, and we lacked
                 * CAP_SYS_BOOT just exit, this will kill our
                 * container for good. */
                log_info("Exiting container.");
                return EXIT_SUCCESS;
        }

        r = log_error_errno(errno, "Failed to invoke reboot(): %m");

  error:
        log_emergency_errno(r, "Critical error while doing system shutdown: %m");
        freeze();
}
Beispiel #12
0
int generator_write_fsck_deps(
                FILE *f,
                const char *dir,
                const char *what,
                const char *where,
                const char *fstype) {

        int r;

        assert(f);
        assert(dir);
        assert(what);
        assert(where);

        if (!is_device_path(what)) {
                log_warning("Checking was requested for \"%s\", but it is not a device.", what);
                return 0;
        }

        if (!isempty(fstype) && !streq(fstype, "auto")) {
                r = fsck_exists(fstype);
                if (r < 0)
                        log_warning_errno(r, "Checking was requested for %s, but couldn't detect if fsck.%s may be used, proceeding: %m", what, fstype);
                else if (r == 0) {
                        /* treat missing check as essentially OK */
                        log_debug("Checking was requested for %s, but fsck.%s does not exist.", what, fstype);
                        return 0;
                }
        }

        if (path_equal(where, "/")) {
                const char *lnk;

                lnk = strjoina(dir, "/" SPECIAL_LOCAL_FS_TARGET ".wants/systemd-fsck-root.service");

                mkdir_parents(lnk, 0755);
                if (symlink(SYSTEM_DATA_UNIT_PATH "/systemd-fsck-root.service", lnk) < 0)
                        return log_error_errno(errno, "Failed to create symlink %s: %m", lnk);

        } else {
                _cleanup_free_ char *_fsck = NULL;
                const char *fsck;

                if (in_initrd() && path_equal(where, "/sysroot")) {
                        r = write_fsck_sysroot_service(dir, what);
                        if (r < 0)
                                return r;

                        fsck = "systemd-fsck-root.service";
                } else {
                        r = unit_name_from_path_instance("systemd-fsck", what, ".service", &_fsck);
                        if (r < 0)
                                return log_error_errno(r, "Failed to create fsck service name: %m");

                        fsck = _fsck;
                }

                fprintf(f,
                        "Requires=%1$s\n"
                        "After=%1$s\n",
                        fsck);
        }

        return 0;
}
Beispiel #13
0
static int builtin_firmware(struct udev_device *dev, int argc, char *argv[], bool test) {
        struct udev *udev = udev_device_get_udev(dev);
        static const char *searchpath[] = { FIRMWARE_PATH };
        char loadpath[UTIL_PATH_SIZE];
        char datapath[UTIL_PATH_SIZE];
        char fwpath[UTIL_PATH_SIZE];
        const char *firmware;
        FILE *fwfile = NULL;
        struct utsname kernel;
        struct stat statbuf;
        unsigned int i;
        int rc = EXIT_SUCCESS;

        firmware = udev_device_get_property_value(dev, "FIRMWARE");
        if (firmware == NULL) {
                log_error("firmware parameter missing");
                rc = EXIT_FAILURE;
                goto exit;
        }

        /* lookup firmware file */
        uname(&kernel);
        for (i = 0; i < ELEMENTSOF(searchpath); i++) {
                strscpyl(fwpath, sizeof(fwpath), searchpath[i], kernel.release, "/", firmware, NULL);
                fwfile = fopen(fwpath, "re");
                if (fwfile != NULL)
                        break;

                strscpyl(fwpath, sizeof(fwpath), searchpath[i], firmware, NULL);
                fwfile = fopen(fwpath, "re");
                if (fwfile != NULL)
                        break;
        }

        strscpyl(loadpath, sizeof(loadpath), udev_device_get_syspath(dev), "/loading", NULL);

        if (fwfile == NULL) {
                log_debug("did not find firmware file '%s'", firmware);
                rc = EXIT_FAILURE;
                /*
                 * Do not cancel the request in the initrd, the real root might have
                 * the firmware file and the 'coldplug' run in the real root will find
                 * this pending request and fulfill or cancel it.
                 * */
                if (!in_initrd())
                        set_loading(udev, loadpath, "-1");
                goto exit;
        }

        if (stat(fwpath, &statbuf) < 0 || statbuf.st_size == 0) {
                if (!in_initrd())
                        set_loading(udev, loadpath, "-1");
                rc = EXIT_FAILURE;
                goto exit;
        }

        if (!set_loading(udev, loadpath, "1"))
                goto exit;

        strscpyl(datapath, sizeof(datapath), udev_device_get_syspath(dev), "/data", NULL);
        if (!copy_firmware(udev, fwpath, datapath, statbuf.st_size)) {
                log_error("error sending firmware '%s' to device", firmware);
                set_loading(udev, loadpath, "-1");
                rc = EXIT_FAILURE;
                goto exit;
        };

        set_loading(udev, loadpath, "0");
exit:
        if (fwfile)
                fclose(fwfile);
        return rc;
}
int proc_cmdline_get_key(const char *key, unsigned flags, char **value) {
        _cleanup_free_ char *line = NULL, *ret = NULL;
        bool found = false;
        const char *p;
        int r;

        /* Looks for a specific key on the kernel command line. Supports two modes:
         *
         * a) The "value" parameter is used. In this case a parameter beginning with the "key" string followed by "="
         *    is searched, and the value following this is returned in "value".
         *
         * b) as above, but the PROC_CMDLINE_VALUE_OPTIONAL flag is set. In this case if the key is found as a
         *    separate word (i.e. not followed by "=" but instead by whitespace or the end of the command line), then
         *    this is also accepted, and "value" is returned as NULL.
         *
         * c) The "value" parameter is NULL. In this case a search for the exact "key" parameter is performed.
         *
         * In all three cases, > 0 is returned if the key is found, 0 if not. */

        if (isempty(key))
                return -EINVAL;

        if ((flags & PROC_CMDLINE_VALUE_OPTIONAL) && !value)
                return -EINVAL;

        r = proc_cmdline(&line);
        if (r < 0)
                return r;

        p = line;
        for (;;) {
                _cleanup_free_ char *word = NULL;
                const char *e;

                r = extract_first_word(&p, &word, NULL, EXTRACT_QUOTES|EXTRACT_RELAX);
                if (r < 0)
                        return r;
                if (r == 0)
                        break;

                /* Automatically filter out arguments that are intended only for the initrd, if we are not in the
                 * initrd. */
                if (!in_initrd() && startswith(word, "rd."))
                        continue;

                if (value) {
                        e = proc_cmdline_key_startswith(word, key);
                        if (!e)
                                continue;

                        if (*e == '=') {
                                r = free_and_strdup(&ret, e+1);
                                if (r < 0)
                                        return r;

                                found = true;

                        } else if (*e == 0 && (flags & PROC_CMDLINE_VALUE_OPTIONAL))
                                found = true;

                } else {
                        if (streq(word, key))
                                found = true;
                }
        }

        if (value)
                *value = TAKE_PTR(ret);

        return found;
}
Beispiel #15
0
int main(int argc, char *argv[])
{
	char *container_name = NULL;
	char *devnm = NULL;
	int status = 0;
	int opt;
	int all = 0;
	int takeover = 0;
	int dofork = 1;
	static struct option options[] = {
		{"all", 0, NULL, 'a'},
		{"takeover", 0, NULL, 't'},
		{"help", 0, NULL, 'h'},
		{"offroot", 0, NULL, OffRootOpt},
		{"foreground", 0, NULL, 'F'},
		{NULL, 0, NULL, 0}
	};

	if (in_initrd()) {
		/*
		 * set first char of argv[0] to @. This is used by
		 * systemd to signal that the task was launched from
		 * initrd/initramfs and should be preserved during shutdown
		 */
		argv[0][0] = '@';
	}

	while ((opt = getopt_long(argc, argv, "thaF", options, NULL)) != -1) {
		switch (opt) {
		case 'a':
			container_name = argv[optind-1];
			all = 1;
			break;
		case 't':
			takeover = 1;
			break;
		case 'F':
			dofork = 0;
			break;
		case OffRootOpt:
			argv[0][0] = '@';
			break;
		case 'h':
		default:
			usage();
			break;
		}
	}

	if (all == 0 && container_name == NULL) {
		if (argv[optind])
			container_name = argv[optind];
	}

	if (container_name == NULL)
		usage();

	if (argc - optind > 1)
		usage();

	if (strcmp(container_name, "/proc/mdstat") == 0)
		all = 1;

	if (all) {
		struct mdstat_ent *mdstat, *e;
		int container_len = strlen(container_name);

		/* launch an mdmon instance for each container found */
		mdstat = mdstat_read(0, 0);
		for (e = mdstat; e; e = e->next) {
			if (e->metadata_version &&
			    strncmp(e->metadata_version, "external:", 9) == 0 &&
			    !is_subarray(&e->metadata_version[9])) {
				/* update cmdline so this mdmon instance can be
				 * distinguished from others in a call to ps(1)
				 */
				if (strlen(e->devnm) <= (unsigned)container_len) {
					memset(container_name, 0, container_len);
					sprintf(container_name, "%s", e->devnm);
				}
				status |= mdmon(e->devnm, 1, takeover);
			}
		}
		free_mdstat(mdstat);

		return status;
	} else if (strncmp(container_name, "md", 2) == 0) {
		int id = devnm2devid(container_name);
		if (id)
			devnm = container_name;
	} else {
		struct stat st;

		if (stat(container_name, &st) == 0)
			devnm = xstrdup(stat2devnm(&st));
	}

	if (!devnm) {
		pr_err("%s is not a valid md device name\n",
			container_name);
		exit(1);
	}
	return mdmon(devnm, dofork && do_fork(), takeover);
}
Beispiel #16
0
static int add_boot(const char *what) {
        _cleanup_blkid_free_probe_ blkid_probe b = NULL;
        const char *fstype = NULL, *uuid = NULL;
        sd_id128_t id, type_id;
        int r;

        assert(what);

        if (!is_efi_boot()) {
                log_debug("Not an EFI boot, ignoring /boot.");
                return 0;
        }

        if (in_initrd()) {
                log_debug("In initrd, ignoring /boot.");
                return 0;
        }

        if (detect_container() > 0) {
                log_debug("In a container, ignoring /boot.");
                return 0;
        }

        /* We create an .automount which is not overridden by the .mount from the fstab generator. */
        if (fstab_is_mount_point("/boot")) {
                log_debug("/boot specified in fstab, ignoring.");
                return 0;
        }

        if (path_is_busy("/boot")) {
                log_debug("/boot already populated, ignoring.");
                return 0;
        }

        r = efi_loader_get_device_part_uuid(&id);
        if (r == -ENOENT) {
                log_debug("EFI loader partition unknown.");
                return 0;
        }

        if (r < 0) {
                log_error_errno(r, "Failed to read ESP partition UUID: %m");
                return r;
        }

        errno = 0;
        b = blkid_new_probe_from_filename(what);
        if (!b) {
                if (errno == 0)
                        return log_oom();
                return log_error_errno(errno, "Failed to allocate prober: %m");
        }

        blkid_probe_enable_partitions(b, 1);
        blkid_probe_set_partitions_flags(b, BLKID_PARTS_ENTRY_DETAILS);

        errno = 0;
        r = blkid_do_safeprobe(b);
        if (r == -2 || r == 1) /* no result or uncertain */
                return 0;
        else if (r != 0)
                return log_error_errno(errno ?: EIO, "Failed to probe %s: %m", what);

        (void) blkid_probe_lookup_value(b, "TYPE", &fstype, NULL);
        if (!streq(fstype, "vfat")) {
                log_debug("Partition for /boot is not a FAT filesystem, ignoring.");
                return 0;
        }

        r = blkid_probe_lookup_value(b, "PART_ENTRY_UUID", &uuid, NULL);
        if (r != 0) {
                log_debug_errno(r, "Partition for /boot does not have a UUID, ignoring. %m");
                return 0;
        }

        if (sd_id128_from_string(uuid, &type_id) < 0) {
                log_debug("Partition for /boot does not have a valid UUID, ignoring.");
                return 0;
        }

        if (!sd_id128_equal(type_id, id)) {
                log_debug("Partition for /boot does not appear to be the partition we are booted from.");
                return 0;
        }

        r = add_automount("boot",
                       what,
                       "/boot",
                       "vfat",
                       true,
                       "umask=0077",
                       "EFI System Partition Automount",
                       120 * USEC_PER_SEC);

        return r;
}
Beispiel #17
0
static int builtin_firmware(struct udev_device *dev, int argc, char *argv[], bool test)
{
        struct udev *udev = udev_device_get_udev(dev);
        static const char *searchpath[] = { FIRMWARE_PATH };
        char fwencpath[UTIL_PATH_SIZE];
        char misspath[UTIL_PATH_SIZE];
        char loadpath[UTIL_PATH_SIZE];
        char datapath[UTIL_PATH_SIZE];
        char fwpath[UTIL_PATH_SIZE];
        const char *firmware;
        FILE *fwfile = NULL;
        struct utsname kernel;
        struct stat statbuf;
        unsigned int i;
        int rc = EXIT_SUCCESS;

        firmware = udev_device_get_property_value(dev, "FIRMWARE");
        if (firmware == NULL) {
                log_error("firmware parameter missing\n\n");
                rc = EXIT_FAILURE;
                goto exit;
        }

        /* lookup firmware file */
        uname(&kernel);
        for (i = 0; i < ELEMENTSOF(searchpath); i++) {
                util_strscpyl(fwpath, sizeof(fwpath), searchpath[i], kernel.release, "/", firmware, NULL);
                fwfile = fopen(fwpath, "re");
                if (fwfile != NULL)
                        break;

                util_strscpyl(fwpath, sizeof(fwpath), searchpath[i], firmware, NULL);
                fwfile = fopen(fwpath, "re");
                if (fwfile != NULL)
                        break;
        }

        util_path_encode(firmware, fwencpath, sizeof(fwencpath));
        util_strscpyl(misspath, sizeof(misspath), "/run/udev/firmware-missing/", fwencpath, NULL);
        util_strscpyl(loadpath, sizeof(loadpath), udev_device_get_syspath(dev), "/loading", NULL);

        if (fwfile == NULL) {
                int err;

                /* This link indicates the missing firmware file and the associated device */
                log_debug("did not find firmware file '%s'\n", firmware);
                do {
                        err = mkdir_parents(misspath, 0755);
                        if (err != 0 && err != -ENOENT)
                                break;
                        err = symlink(udev_device_get_devpath(dev), misspath);
                        if (err != 0)
                                err = -errno;
                } while (err == -ENOENT);
                rc = EXIT_FAILURE;
                /*
                 * Do not cancel the request in the initrd, the real root might have
                 * the firmware file and the 'coldplug' run in the real root will find
                 * this pending request and fulfill or cancel it.
                 * */
                if (!in_initrd())
                        set_loading(udev, loadpath, "-1");
                goto exit;
        }

        if (stat(fwpath, &statbuf) < 0 || statbuf.st_size == 0) {
                if (!in_initrd())
                        set_loading(udev, loadpath, "-1");
                rc = EXIT_FAILURE;
                goto exit;
        }

        if (unlink(misspath) == 0)
                util_delete_path(udev, misspath);

        if (!set_loading(udev, loadpath, "1"))
                goto exit;

        util_strscpyl(datapath, sizeof(datapath), udev_device_get_syspath(dev), "/data", NULL);
        if (!copy_firmware(udev, fwpath, datapath, statbuf.st_size)) {
                log_error("error sending firmware '%s' to device\n", firmware);
                set_loading(udev, loadpath, "-1");
                rc = EXIT_FAILURE;
                goto exit;
        };

        set_loading(udev, loadpath, "0");
exit:
        if (fwfile)
                fclose(fwfile);
        return rc;
}
Beispiel #18
0
int main(int argc, char *argv[]) {
        int cmd, r;
        unsigned retries;
        bool need_umount = true, need_swapoff = true, need_loop_detach = true, need_dm_detach = true;
        bool in_container, use_watchdog = false;
        char *arguments[3];

        log_parse_environment();
        log_set_target(LOG_TARGET_CONSOLE); /* syslog will die if not gone yet */
        log_open();

        umask(0022);

        if (getpid() != 1) {
                log_error("Not executed by init (pid 1).");
                r = -EPERM;
                goto error;
        }

        if (argc != 2) {
                log_error("Invalid number of arguments.");
                r = -EINVAL;
                goto error;
        }

        in_container = detect_container(NULL) > 0;

        if (streq(argv[1], "reboot"))
                cmd = RB_AUTOBOOT;
        else if (streq(argv[1], "poweroff"))
                cmd = RB_POWER_OFF;
        else if (streq(argv[1], "halt"))
                cmd = RB_HALT_SYSTEM;
        else if (streq(argv[1], "kexec"))
                cmd = LINUX_REBOOT_CMD_KEXEC;
        else {
                log_error("Unknown action '%s'.", argv[1]);
                r = -EINVAL;
                goto error;
        }

        use_watchdog = !!getenv("WATCHDOG_USEC");

        /* lock us into memory */
        mlockall(MCL_CURRENT|MCL_FUTURE);

        log_info("Sending SIGTERM to remaining processes...");
        broadcast_signal(SIGTERM, true);

        log_info("Sending SIGKILL to remaining processes...");
        broadcast_signal(SIGKILL, true);

        if (in_container) {
                need_swapoff = false;
                need_dm_detach = false;
                need_loop_detach = false;
        }

        /* Unmount all mountpoints, swaps, and loopback devices */
        for (retries = 0; retries < FINALIZE_ATTEMPTS; retries++) {
                bool changed = false;

                if (use_watchdog)
                        watchdog_ping();

                if (need_umount) {
                        log_info("Unmounting file systems.");
                        r = umount_all(&changed);
                        if (r == 0) {
                                need_umount = false;
                                log_info("All filesystems unmounted.");
                        } else if (r > 0)
                                log_info("Not all file systems unmounted, %d left.", r);
                        else
                                log_error("Failed to unmount file systems: %s", strerror(-r));
                }

                if (need_swapoff) {
                        log_info("Deactivating swaps.");
                        r = swapoff_all(&changed);
                        if (r == 0) {
                                need_swapoff = false;
                                log_info("All swaps deactivated.");
                        } else if (r > 0)
                                log_info("Not all swaps deactivated, %d left.", r);
                        else
                                log_error("Failed to deactivate swaps: %s", strerror(-r));
                }

                if (need_loop_detach) {
                        log_info("Detaching loop devices.");
                        r = loopback_detach_all(&changed);
                        if (r == 0) {
                                need_loop_detach = false;
                                log_info("All loop devices detached.");
                        } else if (r > 0)
                                log_info("Not all loop devices detached, %d left.", r);
                        else
                                log_error("Failed to detach loop devices: %s", strerror(-r));
                }

                if (need_dm_detach) {
                        log_info("Detaching DM devices.");
                        r = dm_detach_all(&changed);
                        if (r == 0) {
                                need_dm_detach = false;
                                log_info("All DM devices detached.");
                        } else if (r > 0)
                                log_info("Not all DM devices detached, %d left.", r);
                        else
                                log_error("Failed to detach DM devices: %s", strerror(-r));
                }

                if (!need_umount && !need_swapoff && !need_loop_detach && !need_dm_detach) {
                        if (retries > 0)
                                log_info("All filesystems, swaps, loop devices, DM devices detached.");
                        /* Yay, done */
                        break;
                }

                /* If in this iteration we didn't manage to
                 * unmount/deactivate anything, we simply give up */
                if (!changed) {
                        log_error("Cannot finalize remaining file systems and devices, giving up.");
                        break;
                }

                log_debug("Couldn't finalize remaining file systems and devices after %u retries, trying again.", retries+1);
        }

        if (retries >= FINALIZE_ATTEMPTS)
                log_error("Too many iterations, giving up.");
        else
                log_info("Storage is finalized.");

        arguments[0] = NULL;
        arguments[1] = argv[1];
        arguments[2] = NULL;
        execute_directory(SYSTEM_SHUTDOWN_PATH, NULL, arguments);

        if (!in_container && !in_initrd() &&
            access("/run/initramfs/shutdown", X_OK) == 0) {

                if (prepare_new_root() >= 0 &&
                    pivot_to_new_root() >= 0) {

                        log_info("Returning to initrd...");

                        execv("/shutdown", argv);
                        log_error("Failed to execute shutdown binary: %m");
                }
        }

        /* The kernel will automaticall flush ATA disks and suchlike
         * on reboot(), but the file systems need to be synce'd
         * explicitly in advance. So let's do this here, but not
         * needlessly slow down containers. */
        if (!in_container)
                sync();

        if (cmd == LINUX_REBOOT_CMD_KEXEC) {

                if (!in_container) {
                        /* We cheat and exec kexec to avoid doing all its work */
                        pid_t pid = fork();

                        if (pid < 0)
                                log_error("Could not fork: %m. Falling back to normal reboot.");
                        else if (pid > 0) {
                                wait_for_terminate_and_warn("kexec", pid);
                                log_warning("kexec failed. Falling back to normal reboot.");
                        } else {
                                /* Child */
                                const char *args[3] = { "/sbin/kexec", "-e", NULL };
                                execv(args[0], (char * const *) args);
                                return EXIT_FAILURE;
                        }
                }

                cmd = RB_AUTOBOOT;
        }

        reboot(cmd);

        if (errno == EPERM && in_container) {
                /* If we are in a container, and we lacked
                 * CAP_SYS_BOOT just exit, this will kill our
                 * container for good. */
                log_error("Exiting container.");
                exit(0);
        }

        log_error("Failed to invoke reboot(): %m");
        r = -errno;

  error:
        log_error("Critical error while doing system shutdown: %s", strerror(-r));

        freeze();
        return EXIT_FAILURE;
}
Beispiel #19
0
int switch_root(const char *new_root, const char *oldroot, bool detach_oldroot,  unsigned long mountflags) {

        /*  Don't try to unmount/move the old "/", there's no way to do it. */
        static const char move_mounts[] =
                "/dev\0"
                "/proc\0"
                "/sys\0"
                "/run\0";

        _cleanup_close_ int old_root_fd = -1;
        struct stat new_root_stat;
        bool old_root_remove;
        const char *i, *temporary_old_root;

        if (path_equal(new_root, "/"))
                return 0;

        temporary_old_root = strappenda(new_root, oldroot);
        mkdir_p_label(temporary_old_root, 0755);

        old_root_remove = in_initrd();

        if (stat(new_root, &new_root_stat) < 0) {
                log_error("Failed to stat directory %s: %m", new_root);
                return -errno;
        }

        /* Work-around for kernel design: the kernel refuses switching
         * root if any file systems are mounted MS_SHARED. Hence
         * remount them MS_PRIVATE here as a work-around.
         *
         * https://bugzilla.redhat.com/show_bug.cgi?id=847418 */
        if (mount(NULL, "/", NULL, MS_REC|MS_PRIVATE, NULL) < 0)
                log_warning("Failed to make \"/\" private mount: %m");

        NULSTR_FOREACH(i, move_mounts) {
                char new_mount[PATH_MAX];
                struct stat sb;

                snprintf(new_mount, sizeof(new_mount), "%s%s", new_root, i);
                char_array_0(new_mount);

                mkdir_p_label(new_mount, 0755);

                if ((stat(new_mount, &sb) < 0) ||
                    sb.st_dev != new_root_stat.st_dev) {

                        /* Mount point seems to be mounted already or
                         * stat failed. Unmount the old mount
                         * point. */
                        if (umount2(i, MNT_DETACH) < 0)
                                log_warning("Failed to unmount %s: %m", i);
                        continue;
                }

                if (mount(i, new_mount, NULL, mountflags, NULL) < 0) {
                        if (mountflags & MS_MOVE) {
                                log_error("Failed to move mount %s to %s, forcing unmount: %m", i, new_mount);

                                if (umount2(i, MNT_FORCE) < 0)
                                        log_warning("Failed to unmount %s: %m", i);
                        }
                        if (mountflags & MS_BIND)
                                log_error("Failed to bind mount %s to %s: %m", i, new_mount);

                }
        }
Beispiel #20
0
int switch_root(const char *new_root) {

    /*  Don't try to unmount/move the old "/", there's no way to do it. */
    static const char move_mounts[] =
        "/dev\0"
        "/proc\0"
        "/sys\0"
        "/run\0";

    int r, old_root_fd = -1;
    struct stat new_root_stat;
    bool old_root_remove;
    const char *i;
    _cleanup_free_ char *temporary_old_root = NULL;

    if (path_equal(new_root, "/"))
        return 0;

    /* When using pivot_root() we assume that /mnt exists as place
     * we can temporarily move the old root to. As we immediately
     * unmount it from there it doesn't matter much which
     * directory we choose for this, but it should be more likely
     * than not that /mnt exists and is suitable as mount point
     * and is on the same fs as the old root dir */
    temporary_old_root = strappend(new_root, "/mnt");
    if (!temporary_old_root)
        return -ENOMEM;

    old_root_remove = in_initrd();

    if (stat(new_root, &new_root_stat) < 0) {
        r = -errno;
        log_error("Failed to stat directory %s: %m", new_root);
        goto fail;
    }

    /* Work-around for a kernel bug: for some reason the kernel
     * refuses switching root if any file systems are mounted
     * MS_SHARED. Hence remount them MS_PRIVATE here as a
     * work-around.
     *
     * https://bugzilla.redhat.com/show_bug.cgi?id=847418 */
    if (mount(NULL, "/", NULL, MS_REC|MS_PRIVATE, NULL) < 0)
        log_warning("Failed to make \"/\" private mount: %m");

    NULSTR_FOREACH(i, move_mounts) {
        char new_mount[PATH_MAX];
        struct stat sb;

        snprintf(new_mount, sizeof(new_mount), "%s%s", new_root, i);
        char_array_0(new_mount);

        if ((stat(new_mount, &sb) < 0) ||
                sb.st_dev != new_root_stat.st_dev) {

            /* Mount point seems to be mounted already or
             * stat failed. Unmount the old mount
             * point. */
            if (umount2(i, MNT_DETACH) < 0)
                log_warning("Failed to unmount %s: %m", i);
            continue;
        }

        if (mount(i, new_mount, NULL, MS_MOVE, NULL) < 0) {
            log_error("Failed to move mount %s to %s, forcing unmount: %m", i, new_mount);

            if (umount2(i, MNT_FORCE) < 0)
                log_warning("Failed to unmount %s: %m", i);
        }
    }
static int parse_proc_cmdline(char ***arg_proc_cmdline_disks, char ***arg_proc_cmdline_options, char **arg_proc_cmdline_keyfile) {
        _cleanup_free_ char *line = NULL;
        char *w = NULL, *state = NULL;
        int r;
        size_t l;

        if (detect_container(NULL) > 0)
                return 0;

        r = read_one_line_file("/proc/cmdline", &line);
        if (r < 0) {
                log_warning("Failed to read /proc/cmdline, ignoring: %s", strerror(-r));
                return 0;
        }

        FOREACH_WORD_QUOTED(w, l, line, state) {
                _cleanup_free_ char *word = NULL;

                word = strndup(w, l);
                if (!word)
                        return log_oom();

                if (startswith(word, "luks=")) {
                        r = parse_boolean(word + 5);
                        if (r < 0)
                                log_warning("Failed to parse luks switch %s. Ignoring.", word + 5);
                        else
                                arg_enabled = r;

                } else if (startswith(word, "rd.luks=")) {

                        if (in_initrd()) {
                                r = parse_boolean(word + 8);
                                if (r < 0)
                                        log_warning("Failed to parse luks switch %s. Ignoring.", word + 8);
                                else
                                        arg_enabled = r;
                        }

                } else if (startswith(word, "luks.crypttab=")) {
                        r = parse_boolean(word + 14);
                        if (r < 0)
                                log_warning("Failed to parse luks crypttab switch %s. Ignoring.", word + 14);
                        else
                                arg_read_crypttab = r;

                } else if (startswith(word, "rd.luks.crypttab=")) {

                        if (in_initrd()) {
                                r = parse_boolean(word + 17);
                                if (r < 0)
                                        log_warning("Failed to parse luks crypttab switch %s. Ignoring.", word + 17);
                                else
                                        arg_read_crypttab = r;
                        }

                } else if (startswith(word, "luks.uuid=")) {
                        if (strv_extend(arg_proc_cmdline_disks, word + 10) < 0)
                                return log_oom();

                } else if (startswith(word, "rd.luks.uuid=")) {

                        if (in_initrd()) {
                                if (strv_extend(arg_proc_cmdline_disks, word + 13) < 0)
                                        return log_oom();
                        }

                } else if (startswith(word, "luks.options=")) {
                        if (strv_extend(arg_proc_cmdline_options, word + 13) < 0)
                                return log_oom();

                } else if (startswith(word, "rd.luks.options=")) {

                        if (in_initrd()) {
                                if (strv_extend(arg_proc_cmdline_options, word + 16) < 0)
                                        return log_oom();
                        }

                } else if (startswith(word, "luks.key=")) {
                        if (*arg_proc_cmdline_keyfile)
                                free(*arg_proc_cmdline_keyfile);
                        *arg_proc_cmdline_keyfile = strdup(word + 9);
                        if (!*arg_proc_cmdline_keyfile)
                                return log_oom();

                } else if (startswith(word, "rd.luks.key=")) {

                        if (in_initrd()) {
                                if (*arg_proc_cmdline_keyfile)
                                        free(*arg_proc_cmdline_keyfile);
                                *arg_proc_cmdline_keyfile = strdup(word + 12);
                                if (!*arg_proc_cmdline_keyfile)
                                        return log_oom();
                        }

                } else if (startswith(word, "luks.") ||
                           (in_initrd() && startswith(word, "rd.luks."))) {

                        log_warning("Unknown kernel switch %s. Ignoring.", word);
                }
        }
Beispiel #22
0
int main(int argc, char *argv[]) {
        bool need_umount, need_swapoff, need_loop_detach, need_dm_detach;
        bool in_container, use_watchdog = false;
        _cleanup_free_ char *cgroup = NULL;
        char *arguments[3];
        unsigned retries;
        int cmd, r;
        static const char* const dirs[] = {SYSTEM_SHUTDOWN_PATH, NULL};

        log_parse_environment();
        r = parse_argv(argc, argv);
        if (r < 0)
                goto error;

        /* journald will die if not gone yet. The log target defaults
         * to console, but may have been changed by command line options. */

        log_close_console(); /* force reopen of /dev/console */
        log_open();

        umask(0022);

        if (getpid() != 1) {
                log_error("Not executed by init (PID 1).");
                r = -EPERM;
                goto error;
        }

        if (streq(arg_verb, "reboot"))
                cmd = RB_AUTOBOOT;
        else if (streq(arg_verb, "poweroff"))
                cmd = RB_POWER_OFF;
        else if (streq(arg_verb, "halt"))
                cmd = RB_HALT_SYSTEM;
        else if (streq(arg_verb, "kexec"))
                cmd = LINUX_REBOOT_CMD_KEXEC;
        else if (streq(arg_verb, "exit"))
                cmd = 0; /* ignored, just checking that arg_verb is valid */
        else {
                r = -EINVAL;
                log_error("Unknown action '%s'.", arg_verb);
                goto error;
        }

        cg_get_root_path(&cgroup);

        use_watchdog = !!getenv("WATCHDOG_USEC");

        /* lock us into memory */
        mlockall(MCL_CURRENT|MCL_FUTURE);

        log_info("Sending SIGTERM to remaining processes...");
        broadcast_signal(SIGTERM, true, true);

        log_info("Sending SIGKILL to remaining processes...");
        broadcast_signal(SIGKILL, true, false);

        in_container = detect_container() > 0;

        need_umount = !in_container;
        need_swapoff = !in_container;
        need_loop_detach = !in_container;
        need_dm_detach = !in_container;

        /* Unmount all mountpoints, swaps, and loopback devices */
        for (retries = 0; retries < FINALIZE_ATTEMPTS; retries++) {
                bool changed = false;

                if (use_watchdog)
                        watchdog_ping();

                /* Let's trim the cgroup tree on each iteration so
                   that we leave an empty cgroup tree around, so that
                   container managers get a nice notify event when we
                   are down */
                if (cgroup)
                        cg_trim(SYSTEMD_CGROUP_CONTROLLER, cgroup, false);

                if (need_umount) {
                        log_info("Unmounting file systems.");
                        r = umount_all(&changed);
                        if (r == 0) {
                                need_umount = false;
                                log_info("All filesystems unmounted.");
                        } else if (r > 0)
                                log_info("Not all file systems unmounted, %d left.", r);
                        else
                                log_error_errno(r, "Failed to unmount file systems: %m");
                }

                if (need_swapoff) {
                        log_info("Deactivating swaps.");
                        r = swapoff_all(&changed);
                        if (r == 0) {
                                need_swapoff = false;
                                log_info("All swaps deactivated.");
                        } else if (r > 0)
                                log_info("Not all swaps deactivated, %d left.", r);
                        else
                                log_error_errno(r, "Failed to deactivate swaps: %m");
                }

                if (need_loop_detach) {
                        log_info("Detaching loop devices.");
                        r = loopback_detach_all(&changed);
                        if (r == 0) {
                                need_loop_detach = false;
                                log_info("All loop devices detached.");
                        } else if (r > 0)
                                log_info("Not all loop devices detached, %d left.", r);
                        else
                                log_error_errno(r, "Failed to detach loop devices: %m");
                }

                if (need_dm_detach) {
                        log_info("Detaching DM devices.");
                        r = dm_detach_all(&changed);
                        if (r == 0) {
                                need_dm_detach = false;
                                log_info("All DM devices detached.");
                        } else if (r > 0)
                                log_info("Not all DM devices detached, %d left.", r);
                        else
                                log_error_errno(r, "Failed to detach DM devices: %m");
                }

                if (!need_umount && !need_swapoff && !need_loop_detach && !need_dm_detach) {
                        if (retries > 0)
                                log_info("All filesystems, swaps, loop devices, DM devices detached.");
                        /* Yay, done */
                        goto initrd_jump;
                }

                /* If in this iteration we didn't manage to
                 * unmount/deactivate anything, we simply give up */
                if (!changed) {
                        log_info("Cannot finalize remaining%s%s%s%s continuing.",
                                 need_umount ? " file systems," : "",
                                 need_swapoff ? " swap devices," : "",
                                 need_loop_detach ? " loop devices," : "",
                                 need_dm_detach ? " DM devices," : "");
                        goto initrd_jump;
                }

                log_debug("After %u retries, couldn't finalize remaining %s%s%s%s trying again.",
                          retries + 1,
                          need_umount ? " file systems," : "",
                          need_swapoff ? " swap devices," : "",
                          need_loop_detach ? " loop devices," : "",
                          need_dm_detach ? " DM devices," : "");
        }

        log_error("Too many iterations, giving up.");

 initrd_jump:

        arguments[0] = NULL;
        arguments[1] = arg_verb;
        arguments[2] = NULL;
        execute_directories(dirs, DEFAULT_TIMEOUT_USEC, arguments);

        if (!in_container && !in_initrd() &&
            access("/run/initramfs/shutdown", X_OK) == 0) {
                r = switch_root_initramfs();
                if (r >= 0) {
                        argv[0] = (char*) "/shutdown";

                        setsid();
                        make_console_stdio();

                        log_info("Successfully changed into root pivot.\n"
                                 "Returning to initrd...");

                        execv("/shutdown", argv);
                        log_error_errno(errno, "Failed to execute shutdown binary: %m");
                } else
                        log_error_errno(r, "Failed to switch root to \"/run/initramfs\": %m");

        }

        if (need_umount || need_swapoff || need_loop_detach || need_dm_detach)
                log_error("Failed to finalize %s%s%s%s ignoring",
                          need_umount ? " file systems," : "",
                          need_swapoff ? " swap devices," : "",
                          need_loop_detach ? " loop devices," : "",
                          need_dm_detach ? " DM devices," : "");

        /* The kernel will automaticall flush ATA disks and suchlike
         * on reboot(), but the file systems need to be synce'd
         * explicitly in advance. So let's do this here, but not
         * needlessly slow down containers. */
        if (!in_container)
                sync();

        if (streq(arg_verb, "exit")) {
                if (in_container)
                        exit(arg_exit_code);
                else {
                        /* We cannot exit() on the host, fallback on another
                         * method. */
                        cmd = RB_POWER_OFF;
                }
        }

        switch (cmd) {

        case LINUX_REBOOT_CMD_KEXEC:

                if (!in_container) {
                        /* We cheat and exec kexec to avoid doing all its work */
                        pid_t pid;

                        log_info("Rebooting with kexec.");

                        pid = fork();
                        if (pid < 0)
                                log_error_errno(errno, "Failed to fork: %m");
                        else if (pid == 0) {

                                const char * const args[] = {
                                        KEXEC, "-e", NULL
                                };

                                /* Child */

                                execv(args[0], (char * const *) args);
                                _exit(EXIT_FAILURE);
                        } else
                                wait_for_terminate_and_warn("kexec", pid, true);
                }

                cmd = RB_AUTOBOOT;
                /* Fall through */

        case RB_AUTOBOOT:

                if (!in_container) {
                        _cleanup_free_ char *param = NULL;

                        if (read_one_line_file(REBOOT_PARAM_FILE, &param) >= 0) {
                                log_info("Rebooting with argument '%s'.", param);
                                syscall(SYS_reboot, LINUX_REBOOT_MAGIC1, LINUX_REBOOT_MAGIC2, LINUX_REBOOT_CMD_RESTART2, param);
                        }
                }

                log_info("Rebooting.");
                break;

        case RB_POWER_OFF:
                log_info("Powering off.");
                break;

        case RB_HALT_SYSTEM:
                log_info("Halting system.");
                break;

        default:
                assert_not_reached("Unknown magic");
        }

        reboot(cmd);
        if (errno == EPERM && in_container) {
                /* If we are in a container, and we lacked
                 * CAP_SYS_BOOT just exit, this will kill our
                 * container for good. */
                log_info("Exiting container.");
                exit(0);
        }

        r = log_error_errno(errno, "Failed to invoke reboot(): %m");

  error:
        log_emergency_errno(r, "Critical error while doing system shutdown: %m");
        freeze();
}
int main(int argc, char *argv[]) {
    _cleanup_free_ char *what = NULL;
    _cleanup_fclose_ FILE *f = NULL;
    int r = EXIT_SUCCESS;
    sd_id128_t id;
    char *name;

    if (argc > 1 && argc != 4) {
        log_error("This program takes three or no arguments.");
        return EXIT_FAILURE;
    }

    if (argc > 1)
        arg_dest = argv[3];

    log_set_target(LOG_TARGET_SAFE);
    log_parse_environment();
    log_open();

    umask(0022);

    if (in_initrd()) {
        log_debug("In initrd, exiting.");
        return EXIT_SUCCESS;
    }

    if (detect_container(NULL) > 0) {
        log_debug("In a container, exiting.");
        return EXIT_SUCCESS;
    }

    if (!is_efi_boot()) {
        log_debug("Not an EFI boot, exiting.");
        return EXIT_SUCCESS;
    }

    if (path_is_mount_point("/boot", true) <= 0 &&
            dir_is_empty("/boot") <= 0) {
        log_debug("/boot already populated, exiting.");
        return EXIT_SUCCESS;
    }

    r = efi_loader_get_device_part_uuid(&id);
    if (r == -ENOENT) {
        log_debug("EFI loader partition unknown, exiting.");
        return EXIT_SUCCESS;
    } else if (r < 0) {
        log_error_errno(r, "Failed to read ESP partition UUID: %m");
        return EXIT_FAILURE;
    }

    name = strjoina(arg_dest, "/boot.mount");
    f = fopen(name, "wxe");
    if (!f) {
        log_error_errno(errno, "Failed to create mount unit file %s: %m", name);
        return EXIT_FAILURE;
    }

    r = asprintf(&what,
                 "/dev/disk/by-partuuid/%02x%02x%02x%02x-%02x%02x-%02x%02x-%02x%02x-%02x%02x%02x%02x%02x%02x",
                 SD_ID128_FORMAT_VAL(id));
    if (r < 0) {
        log_oom();
        return EXIT_FAILURE;
    }

    fprintf(f,
            "# Automatially generated by systemd-efi-boot-generator\n\n"
            "[Unit]\n"
            "Description=EFI System Partition\n"
            "Documentation=man:systemd-efi-boot-generator(8)\n");

    r = generator_write_fsck_deps(f, arg_dest, what, "/boot", "vfat");
    if (r < 0)
        return EXIT_FAILURE;

    fprintf(f,
            "\n"
            "[Mount]\n"
            "What=%s\n"
            "Where=/boot\n"
            "Type=vfat\n"
            "Options=umask=0077,noauto\n",
            what);

    fflush(f);
    if (ferror(f)) {
        log_error_errno(errno, "Failed to write mount unit file: %m");
        return EXIT_FAILURE;
    }

    name = strjoina(arg_dest, "/boot.automount");
    fclose(f);
    f = fopen(name, "wxe");
    if (!f) {
        log_error_errno(errno, "Failed to create automount unit file %s: %m", name);
        return EXIT_FAILURE;
    }

    fputs("# Automatially generated by systemd-efi-boot-generator\n\n"
          "[Unit]\n"
          "Description=EFI System Partition Automount\n\n"
          "[Automount]\n"
          "Where=/boot\n", f);

    fflush(f);
    if (ferror(f)) {
        log_error_errno(errno, "Failed to write automount unit file: %m");
        return EXIT_FAILURE;
    }

    name = strjoina(arg_dest, "/" SPECIAL_LOCAL_FS_TARGET ".wants/boot.automount");
    mkdir_parents(name, 0755);

    if (symlink("../boot.automount", name) < 0) {
        log_error_errno(errno, "Failed to create symlink %s: %m", name);
        return EXIT_FAILURE;
    }

    return EXIT_SUCCESS;
}