示例#1
0
文件: shutdown.c 项目: rowhit/systemd
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();
}
示例#2
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();
}