1 /*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
4 This file is part of systemd.
6 Copyright 2010 ProFUSION embedded systems
8 systemd is free software; you can redistribute it and/or modify it
9 under the terms of the GNU Lesser General Public License as published by
10 the Free Software Foundation; either version 2.1 of the License, or
11 (at your option) any later version.
13 systemd is distributed in the hope that it will be useful, but
14 WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 Lesser General Public License for more details.
18 You should have received a copy of the GNU Lesser General Public License
19 along with systemd; If not, see <http://www.gnu.org/licenses/>.
23 #include <sys/types.h>
24 #include <sys/reboot.h>
25 #include <linux/reboot.h>
28 #include <sys/mount.h>
29 #include <sys/syscall.h>
49 #include "cgroup-util.h"
52 #define FINALIZE_ATTEMPTS 50
54 static char* arg_verb;
56 static int parse_argv(int argc, char *argv[]) {
58 ARG_LOG_LEVEL = 0x100,
64 static const struct option options[] = {
65 { "log-level", required_argument, NULL, ARG_LOG_LEVEL },
66 { "log-target", required_argument, NULL, ARG_LOG_TARGET },
67 { "log-color", optional_argument, NULL, ARG_LOG_COLOR },
68 { "log-location", optional_argument, NULL, ARG_LOG_LOCATION },
79 while ((c = getopt_long(argc, argv, ":", options, NULL)) >= 0)
83 r = log_set_max_level_from_string(optarg);
85 log_error("Failed to parse log level %s, ignoring.", optarg);
90 r = log_set_target_from_string(optarg);
92 log_error("Failed to parse log target %s, ignoring", optarg);
99 r = log_show_color_from_string(optarg);
101 log_error("Failed to parse log color setting %s, ignoring", optarg);
103 log_show_color(true);
107 case ARG_LOG_LOCATION:
109 r = log_show_location_from_string(optarg);
111 log_error("Failed to parse log location setting %s, ignoring", optarg);
113 log_show_location(true);
118 log_error("Unknown option %s.", argv[optind-1]);
122 log_error("Missing argument to %s.", argv[optind-1]);
126 assert_not_reached("Unhandled option code.");
129 if (optind >= argc) {
130 log_error("Verb argument missing.");
134 arg_verb = argv[optind];
136 if (optind + 1 < argc)
137 log_error("Excess arguments, ignoring");
141 static int prepare_new_root(void) {
142 static const char dirs[] =
143 "/run/initramfs/oldroot\0"
144 "/run/initramfs/proc\0"
145 "/run/initramfs/sys\0"
146 "/run/initramfs/dev\0"
147 "/run/initramfs/run\0";
151 if (mount("/run/initramfs", "/run/initramfs", NULL, MS_BIND, NULL) < 0) {
152 log_error("Failed to mount bind /run/initramfs on /run/initramfs: %m");
156 if (mount(NULL, "/run/initramfs", NULL, MS_PRIVATE, NULL) < 0) {
157 log_error("Failed to make /run/initramfs private mount: %m");
161 NULSTR_FOREACH(dir, dirs)
162 if (mkdir_p_label(dir, 0755) < 0 && errno != EEXIST) {
163 log_error("Failed to mkdir %s: %m", dir);
167 if (mount("/sys", "/run/initramfs/sys", NULL, MS_BIND, NULL) < 0) {
168 log_error("Failed to mount bind /sys on /run/initramfs/sys: %m");
172 if (mount("/proc", "/run/initramfs/proc", NULL, MS_BIND, NULL) < 0) {
173 log_error("Failed to mount bind /proc on /run/initramfs/proc: %m");
177 if (mount("/dev", "/run/initramfs/dev", NULL, MS_BIND, NULL) < 0) {
178 log_error("Failed to mount bind /dev on /run/initramfs/dev: %m");
182 if (mount("/run", "/run/initramfs/run", NULL, MS_BIND, NULL) < 0) {
183 log_error("Failed to mount bind /run on /run/initramfs/run: %m");
190 static int pivot_to_new_root(void) {
192 if (chdir("/run/initramfs") < 0) {
193 log_error("Failed to change directory to /run/initramfs: %m");
197 /* Work-around for a kernel bug: for some reason the kernel
198 * refuses switching root if any file systems are mounted
199 * MS_SHARED. Hence remount them MS_PRIVATE here as a
202 * https://bugzilla.redhat.com/show_bug.cgi?id=847418 */
203 if (mount(NULL, "/", NULL, MS_REC|MS_PRIVATE, NULL) < 0)
204 log_warning("Failed to make \"/\" private mount: %m");
206 if (pivot_root(".", "oldroot") < 0) {
207 log_error("pivot failed: %m");
208 /* only chroot if pivot root succeeded */
215 make_console_stdio();
217 log_info("Successfully changed into root pivot.");
222 int main(int argc, char *argv[]) {
223 bool need_umount = true, need_swapoff = true, need_loop_detach = true, need_dm_detach = true;
224 bool in_container, use_watchdog = false;
225 _cleanup_free_ char *cgroup = NULL;
230 log_parse_environment();
231 r = parse_argv(argc, argv);
235 /* journald will die if not gone yet. The log target defaults
236 * to console, but may have been changed by commandline options. */
238 log_close_console(); /* force reopen of /dev/console */
244 log_error("Not executed by init (PID 1).");
249 in_container = detect_container(NULL) > 0;
251 if (streq(arg_verb, "reboot"))
253 else if (streq(arg_verb, "poweroff"))
255 else if (streq(arg_verb, "halt"))
256 cmd = RB_HALT_SYSTEM;
257 else if (streq(arg_verb, "kexec"))
258 cmd = LINUX_REBOOT_CMD_KEXEC;
261 log_error("Unknown action '%s'.", arg_verb);
265 cg_get_root_path(&cgroup);
267 use_watchdog = !!getenv("WATCHDOG_USEC");
269 /* lock us into memory */
270 mlockall(MCL_CURRENT|MCL_FUTURE);
272 log_info("Sending SIGTERM to remaining processes...");
273 broadcast_signal(SIGTERM, true, true);
275 log_info("Sending SIGKILL to remaining processes...");
276 broadcast_signal(SIGKILL, true, false);
279 need_swapoff = false;
280 need_dm_detach = false;
281 need_loop_detach = false;
284 /* Unmount all mountpoints, swaps, and loopback devices */
285 for (retries = 0; retries < FINALIZE_ATTEMPTS; retries++) {
286 bool changed = false;
291 /* Let's trim the cgroup tree on each iteration so
292 that we leave an empty cgroup tree around, so that
293 container managers get a nice notify event when we
296 cg_trim(SYSTEMD_CGROUP_CONTROLLER, cgroup, false);
299 log_info("Unmounting file systems.");
300 r = umount_all(&changed);
303 log_info("All filesystems unmounted.");
305 log_info("Not all file systems unmounted, %d left.", r);
307 log_error("Failed to unmount file systems: %s", strerror(-r));
311 log_info("Deactivating swaps.");
312 r = swapoff_all(&changed);
314 need_swapoff = false;
315 log_info("All swaps deactivated.");
317 log_info("Not all swaps deactivated, %d left.", r);
319 log_error("Failed to deactivate swaps: %s", strerror(-r));
322 if (need_loop_detach) {
323 log_info("Detaching loop devices.");
324 r = loopback_detach_all(&changed);
326 need_loop_detach = false;
327 log_info("All loop devices detached.");
329 log_info("Not all loop devices detached, %d left.", r);
331 log_error("Failed to detach loop devices: %s", strerror(-r));
334 if (need_dm_detach) {
335 log_info("Detaching DM devices.");
336 r = dm_detach_all(&changed);
338 need_dm_detach = false;
339 log_info("All DM devices detached.");
341 log_info("Not all DM devices detached, %d left.", r);
343 log_error("Failed to detach DM devices: %s", strerror(-r));
346 if (!need_umount && !need_swapoff && !need_loop_detach && !need_dm_detach) {
348 log_info("All filesystems, swaps, loop devices, DM devices detached.");
353 /* If in this iteration we didn't manage to
354 * unmount/deactivate anything, we simply give up */
356 log_error("Cannot finalize remaining file systems and devices, giving up.");
360 log_debug("Couldn't finalize remaining file systems and devices after %u retries, trying again.", retries+1);
363 if (retries >= FINALIZE_ATTEMPTS)
364 log_error("Too many iterations, giving up.");
366 log_info("Storage is finalized.");
369 arguments[1] = arg_verb;
371 execute_directory(SYSTEM_SHUTDOWN_PATH, NULL, DEFAULT_TIMEOUT_USEC, arguments);
373 if (!in_container && !in_initrd() &&
374 access("/run/initramfs/shutdown", X_OK) == 0) {
376 if (prepare_new_root() >= 0 &&
377 pivot_to_new_root() >= 0) {
378 arguments[0] = (char*) "/shutdown";
380 log_info("Returning to initrd...");
382 execv("/shutdown", arguments);
383 log_error("Failed to execute shutdown binary: %m");
387 /* The kernel will automaticall flush ATA disks and suchlike
388 * on reboot(), but the file systems need to be synce'd
389 * explicitly in advance. So let's do this here, but not
390 * needlessly slow down containers. */
396 case LINUX_REBOOT_CMD_KEXEC:
399 /* We cheat and exec kexec to avoid doing all its work */
402 log_info("Rebooting with kexec.");
406 log_error("Failed to fork: %m");
409 const char * const args[] = {
415 execv(args[0], (char * const *) args);
418 wait_for_terminate_and_warn("kexec", pid);
427 _cleanup_free_ char *param = NULL;
429 if (read_one_line_file(REBOOT_PARAM_FILE, ¶m) >= 0) {
430 log_info("Rebooting with argument '%s'.", param);
431 syscall(SYS_reboot, LINUX_REBOOT_MAGIC1, LINUX_REBOOT_MAGIC2,
432 LINUX_REBOOT_CMD_RESTART2, param);
436 log_info("Rebooting.");
440 log_info("Powering off.");
444 log_info("Halting system.");
448 assert_not_reached("Unknown magic");
452 if (errno == EPERM && in_container) {
453 /* If we are in a container, and we lacked
454 * CAP_SYS_BOOT just exit, this will kill our
455 * container for good. */
456 log_info("Exiting container.");
460 log_error("Failed to invoke reboot(): %m");
464 log_error("Critical error while doing system shutdown: %s", strerror(-r));