]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/core/shutdown.c
switch_root: do not fail, if base_filesystem_create() failed
[thirdparty/systemd.git] / src / core / shutdown.c
CommitLineData
b1b2a107
FF
1/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
2
3/***
4 This file is part of systemd.
5
6 Copyright 2010 ProFUSION embedded systems
7
8 systemd is free software; you can redistribute it and/or modify it
5430f7f2
LP
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
b1b2a107
FF
11 (at your option) any later version.
12
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
5430f7f2 16 Lesser General Public License for more details.
b1b2a107 17
5430f7f2 18 You should have received a copy of the GNU Lesser General Public License
b1b2a107
FF
19 along with systemd; If not, see <http://www.gnu.org/licenses/>.
20***/
21
22#include <sys/mman.h>
23#include <sys/types.h>
24#include <sys/reboot.h>
25#include <linux/reboot.h>
26#include <sys/wait.h>
7cb1094a
HH
27#include <sys/stat.h>
28#include <sys/mount.h>
29#include <sys/syscall.h>
30#include <fcntl.h>
b1b2a107
FF
31#include <dirent.h>
32#include <errno.h>
33#include <unistd.h>
34#include <signal.h>
35#include <stdbool.h>
36#include <stdlib.h>
37#include <string.h>
b1e90ec5 38#include <getopt.h>
b1b2a107 39
7cb1094a 40#include "missing.h"
b1b2a107 41#include "log.h"
ec26be51 42#include "fileio.h"
b1b2a107
FF
43#include "umount.h"
44#include "util.h"
49e942b2 45#include "mkdir.h"
b52aae1d 46#include "virt.h"
e96d6be7 47#include "watchdog.h"
39d6464c 48#include "killall.h"
41f85451 49#include "cgroup-util.h"
37185ec8 50#include "def.h"
5a4bf02f 51#include "switch-root.h"
b1b2a107 52
b1b2a107 53#define FINALIZE_ATTEMPTS 50
b1b2a107 54
b1e90ec5
ZJS
55static char* arg_verb;
56
57static int parse_argv(int argc, char *argv[]) {
58 enum {
59 ARG_LOG_LEVEL = 0x100,
60 ARG_LOG_TARGET,
61 ARG_LOG_COLOR,
62 ARG_LOG_LOCATION,
63 };
64
65 static const struct option options[] = {
66 { "log-level", required_argument, NULL, ARG_LOG_LEVEL },
67 { "log-target", required_argument, NULL, ARG_LOG_TARGET },
68 { "log-color", optional_argument, NULL, ARG_LOG_COLOR },
69 { "log-location", optional_argument, NULL, ARG_LOG_LOCATION },
70 {}
71 };
72
73 int c, r;
74
75 assert(argc >= 1);
76 assert(argv);
77
601185b4 78 while ((c = getopt_long(argc, argv, "", options, NULL)) >= 0)
b1e90ec5
ZJS
79 switch (c) {
80
81 case ARG_LOG_LEVEL:
82 r = log_set_max_level_from_string(optarg);
83 if (r < 0)
84 log_error("Failed to parse log level %s, ignoring.", optarg);
85
86 break;
87
88 case ARG_LOG_TARGET:
89 r = log_set_target_from_string(optarg);
90 if (r < 0)
91 log_error("Failed to parse log target %s, ignoring", optarg);
92
93 break;
94
95 case ARG_LOG_COLOR:
96
97 if (optarg) {
98 r = log_show_color_from_string(optarg);
99 if (r < 0)
100 log_error("Failed to parse log color setting %s, ignoring", optarg);
101 } else
102 log_show_color(true);
103
104 break;
105
106 case ARG_LOG_LOCATION:
107 if (optarg) {
108 r = log_show_location_from_string(optarg);
109 if (r < 0)
110 log_error("Failed to parse log location setting %s, ignoring", optarg);
111 } else
112 log_show_location(true);
113
114 break;
115
116 case '?':
b1e90ec5
ZJS
117 return -EINVAL;
118
119 default:
120 assert_not_reached("Unhandled option code.");
121 }
122
123 if (optind >= argc) {
124 log_error("Verb argument missing.");
125 return -EINVAL;
126 }
127
128 arg_verb = argv[optind];
129
130 if (optind + 1 < argc)
131 log_error("Excess arguments, ignoring");
132 return 0;
133}
134
5a4bf02f 135static int switch_root_initramfs(void) {
89d471d5
LP
136 if (mount("/run/initramfs", "/run/initramfs", NULL, MS_BIND, NULL) < 0) {
137 log_error("Failed to mount bind /run/initramfs on /run/initramfs: %m");
138 return -errno;
139 }
140
141 if (mount(NULL, "/run/initramfs", NULL, MS_PRIVATE, NULL) < 0) {
142 log_error("Failed to make /run/initramfs private mount: %m");
143 return -errno;
144 }
145
5a4bf02f
HH
146 /* switch_root with MS_BIND, because there might still be processes lurking around, which have open file desriptors.
147 * /run/initramfs/shutdown will take care of these.
148 * Also do not detach the old root, because /run/initramfs/shutdown needs to access it.
149 */
150 return switch_root("/run/initramfs", "/oldroot", false, MS_BIND);
7cb1094a
HH
151}
152
7cb1094a 153
b1b2a107 154int main(int argc, char *argv[]) {
8c977838 155 bool need_umount, need_swapoff, need_loop_detach, need_dm_detach;
bd3fa1d2 156 bool in_container, use_watchdog = false;
06beed6d 157 _cleanup_free_ char *cgroup = NULL;
6edd7d0a 158 char *arguments[3];
41f85451
LP
159 unsigned retries;
160 int cmd, r;
b1b2a107 161
b1e90ec5
ZJS
162 log_parse_environment();
163 r = parse_argv(argc, argv);
164 if (r < 0)
165 goto error;
ec26be51 166
b1e90ec5
ZJS
167 /* journald will die if not gone yet. The log target defaults
168 * to console, but may have been changed by commandline options. */
ec26be51 169
99f09825 170 log_close_console(); /* force reopen of /dev/console */
b1b2a107
FF
171 log_open();
172
4c12626c
LP
173 umask(0022);
174
b1b2a107 175 if (getpid() != 1) {
b1e90ec5 176 log_error("Not executed by init (PID 1).");
b1b2a107
FF
177 r = -EPERM;
178 goto error;
179 }
180
b1e90ec5 181 if (streq(arg_verb, "reboot"))
b1b2a107 182 cmd = RB_AUTOBOOT;
b1e90ec5 183 else if (streq(arg_verb, "poweroff"))
b1b2a107 184 cmd = RB_POWER_OFF;
b1e90ec5 185 else if (streq(arg_verb, "halt"))
b1b2a107 186 cmd = RB_HALT_SYSTEM;
b1e90ec5 187 else if (streq(arg_verb, "kexec"))
b1b2a107
FF
188 cmd = LINUX_REBOOT_CMD_KEXEC;
189 else {
b1b2a107 190 r = -EINVAL;
b1e90ec5 191 log_error("Unknown action '%s'.", arg_verb);
b1b2a107
FF
192 goto error;
193 }
194
41f85451
LP
195 cg_get_root_path(&cgroup);
196
e96d6be7
LP
197 use_watchdog = !!getenv("WATCHDOG_USEC");
198
b1b2a107 199 /* lock us into memory */
b55d0651 200 mlockall(MCL_CURRENT|MCL_FUTURE);
b1b2a107 201
ab58e291 202 log_info("Sending SIGTERM to remaining processes...");
0bee65f0 203 broadcast_signal(SIGTERM, true, true);
b1b2a107 204
ab58e291 205 log_info("Sending SIGKILL to remaining processes...");
0bee65f0 206 broadcast_signal(SIGKILL, true, false);
40e85d00 207
8c977838
ZJS
208 in_container = detect_container(NULL) > 0;
209
d89b5fed 210 need_umount = !in_container;
8c977838
ZJS
211 need_swapoff = !in_container;
212 need_loop_detach = !in_container;
213 need_dm_detach = !in_container;
b1b2a107 214
567ea02a 215 /* Unmount all mountpoints, swaps, and loopback devices */
12aad1d0
LP
216 for (retries = 0; retries < FINALIZE_ATTEMPTS; retries++) {
217 bool changed = false;
218
e96d6be7
LP
219 if (use_watchdog)
220 watchdog_ping();
221
41f85451
LP
222 /* Let's trim the cgroup tree on each iteration so
223 that we leave an empty cgroup tree around, so that
224 container managers get a nice notify event when we
225 are down */
226 if (cgroup)
227 cg_trim(SYSTEMD_CGROUP_CONTROLLER, cgroup, false);
228
b1b2a107 229 if (need_umount) {
ab58e291 230 log_info("Unmounting file systems.");
12aad1d0 231 r = umount_all(&changed);
bce93b7a 232 if (r == 0) {
b1b2a107 233 need_umount = false;
bce93b7a
MS
234 log_info("All filesystems unmounted.");
235 } else if (r > 0)
ab58e291 236 log_info("Not all file systems unmounted, %d left.", r);
b1b2a107 237 else
ab58e291 238 log_error("Failed to unmount file systems: %s", strerror(-r));
b1b2a107
FF
239 }
240
241 if (need_swapoff) {
735e0712 242 log_info("Deactivating swaps.");
12aad1d0 243 r = swapoff_all(&changed);
bce93b7a 244 if (r == 0) {
b1b2a107 245 need_swapoff = false;
735e0712 246 log_info("All swaps deactivated.");
bce93b7a 247 } else if (r > 0)
735e0712 248 log_info("Not all swaps deactivated, %d left.", r);
b1b2a107 249 else
735e0712 250 log_error("Failed to deactivate swaps: %s", strerror(-r));
b1b2a107
FF
251 }
252
253 if (need_loop_detach) {
254 log_info("Detaching loop devices.");
12aad1d0 255 r = loopback_detach_all(&changed);
bce93b7a 256 if (r == 0) {
b1b2a107 257 need_loop_detach = false;
bce93b7a
MS
258 log_info("All loop devices detached.");
259 } else if (r > 0)
ab58e291 260 log_info("Not all loop devices detached, %d left.", r);
b1b2a107 261 else
ab58e291 262 log_error("Failed to detach loop devices: %s", strerror(-r));
d48141ba 263 }
b1b2a107 264
d48141ba
LP
265 if (need_dm_detach) {
266 log_info("Detaching DM devices.");
12aad1d0 267 r = dm_detach_all(&changed);
bce93b7a 268 if (r == 0) {
d48141ba 269 need_dm_detach = false;
bce93b7a
MS
270 log_info("All DM devices detached.");
271 } else if (r > 0)
2569a5ce 272 log_info("Not all DM devices detached, %d left.", r);
d48141ba 273 else
ab58e291 274 log_error("Failed to detach DM devices: %s", strerror(-r));
b1b2a107
FF
275 }
276
a27d2184
KS
277 if (!need_umount && !need_swapoff && !need_loop_detach && !need_dm_detach) {
278 if (retries > 0)
279 log_info("All filesystems, swaps, loop devices, DM devices detached.");
12aad1d0 280 /* Yay, done */
8c977838 281 goto initrd_jump;
a27d2184 282 }
b1b2a107 283
12aad1d0 284 /* If in this iteration we didn't manage to
bd3fa1d2 285 * unmount/deactivate anything, we simply give up */
12aad1d0 286 if (!changed) {
8c977838
ZJS
287 log_info("Cannot finalize remaining%s%s%s%s continuing.",
288 need_umount ? " file systems," : "",
289 need_swapoff ? " swap devices," : "",
290 need_loop_detach ? " loop devices," : "",
291 need_dm_detach ? " DM devices," : "");
292 goto initrd_jump;
12aad1d0
LP
293 }
294
8c977838
ZJS
295 log_debug("After %u retries, couldn't finalize remaining %s%s%s%s trying again.",
296 retries + 1,
297 need_umount ? " file systems," : "",
298 need_swapoff ? " swap devices," : "",
299 need_loop_detach ? " loop devices," : "",
300 need_dm_detach ? " DM devices," : "");
b1b2a107
FF
301 }
302
8c977838
ZJS
303 log_error("Too many iterations, giving up.");
304
305 initrd_jump:
12aad1d0 306
6edd7d0a 307 arguments[0] = NULL;
b1e90ec5 308 arguments[1] = arg_verb;
6edd7d0a 309 arguments[2] = NULL;
e2680723 310 execute_directory(SYSTEM_SHUTDOWN_PATH, NULL, DEFAULT_TIMEOUT_USEC, arguments);
83cc030f 311
02eaa788 312 if (!in_container && !in_initrd() &&
cb7ec564 313 access("/run/initramfs/shutdown", X_OK) == 0) {
5a4bf02f
HH
314 r = switch_root_initramfs();
315 if (r >= 0) {
a2726e5c 316 argv[0] = (char*) "/shutdown";
30d743f4 317
5a4bf02f
HH
318 setsid();
319 make_console_stdio();
320
321 log_info("Successfully changed into root pivot.\n"
322 "Returning to initrd...");
30d743f4 323
a2726e5c 324 execv("/shutdown", argv);
7cb1094a 325 log_error("Failed to execute shutdown binary: %m");
5a4bf02f
HH
326 } else
327 log_error("Failed to switch root to \"/run/initramfs\": %s", strerror(-r));
328
7cb1094a
HH
329 }
330
8c977838
ZJS
331 if (need_umount || need_swapoff || need_loop_detach || need_dm_detach)
332 log_error("Failed to finalize %s%s%s%s ignoring",
333 need_umount ? " file systems," : "",
334 need_swapoff ? " swap devices," : "",
335 need_loop_detach ? " loop devices," : "",
336 need_dm_detach ? " DM devices," : "");
337
0049f05a
LP
338 /* The kernel will automaticall flush ATA disks and suchlike
339 * on reboot(), but the file systems need to be synce'd
340 * explicitly in advance. So let's do this here, but not
341 * needlessly slow down containers. */
342 if (!in_container)
343 sync();
344
477def80
LP
345 switch (cmd) {
346
347 case LINUX_REBOOT_CMD_KEXEC:
cb7ec564
LP
348
349 if (!in_container) {
350 /* We cheat and exec kexec to avoid doing all its work */
477def80
LP
351 pid_t pid;
352
353 log_info("Rebooting with kexec.");
cb7ec564 354
477def80 355 pid = fork();
cb7ec564 356 if (pid < 0)
477def80
LP
357 log_error("Failed to fork: %m");
358 else if (pid == 0) {
359
360 const char * const args[] = {
361 KEXEC, "-e", NULL
362 };
363
cb7ec564 364 /* Child */
477def80 365
cb7ec564 366 execv(args[0], (char * const *) args);
477def80
LP
367 _exit(EXIT_FAILURE);
368 } else
369 wait_for_terminate_and_warn("kexec", pid);
b1b2a107 370 }
e61cd186
LP
371
372 cmd = RB_AUTOBOOT;
477def80 373 /* Fall through */
b1b2a107 374
477def80
LP
375 case RB_AUTOBOOT:
376
377 if (!in_container) {
378 _cleanup_free_ char *param = NULL;
379
380 if (read_one_line_file(REBOOT_PARAM_FILE, &param) >= 0) {
381 log_info("Rebooting with argument '%s'.", param);
f07756bf 382 syscall(SYS_reboot, LINUX_REBOOT_MAGIC1, LINUX_REBOOT_MAGIC2, LINUX_REBOOT_CMD_RESTART2, param);
477def80
LP
383 }
384 }
385
386 log_info("Rebooting.");
387 break;
388
389 case RB_POWER_OFF:
390 log_info("Powering off.");
391 break;
392
393 case RB_HALT_SYSTEM:
394 log_info("Halting system.");
395 break;
396
397 default:
398 assert_not_reached("Unknown magic");
399 }
cb7ec564 400
477def80 401 reboot(cmd);
cb7ec564
LP
402 if (errno == EPERM && in_container) {
403 /* If we are in a container, and we lacked
404 * CAP_SYS_BOOT just exit, this will kill our
405 * container for good. */
477def80 406 log_info("Exiting container.");
cb7ec564
LP
407 exit(0);
408 }
409
e61cd186
LP
410 log_error("Failed to invoke reboot(): %m");
411 r = -errno;
b1b2a107
FF
412
413 error:
e61cd186
LP
414 log_error("Critical error while doing system shutdown: %s", strerror(-r));
415
b1b2a107 416 freeze();
b1b2a107 417}