]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/core/shutdown.c
fde3ce9c27b92239dd93fbcacff88a311dd79770
[thirdparty/systemd.git] / src / core / shutdown.c
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
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.
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
16 Lesser General Public License for more details.
17
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/>.
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>
27 #include <sys/stat.h>
28 #include <sys/mount.h>
29 #include <sys/syscall.h>
30 #include <fcntl.h>
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>
38 #include <getopt.h>
39
40 #include "missing.h"
41 #include "log.h"
42 #include "fileio.h"
43 #include "umount.h"
44 #include "util.h"
45 #include "mkdir.h"
46 #include "virt.h"
47 #include "watchdog.h"
48 #include "killall.h"
49 #include "cgroup-util.h"
50 #include "def.h"
51
52 #define FINALIZE_ATTEMPTS 50
53
54 static char* arg_verb;
55
56 static int parse_argv(int argc, char *argv[]) {
57 enum {
58 ARG_LOG_LEVEL = 0x100,
59 ARG_LOG_TARGET,
60 ARG_LOG_COLOR,
61 ARG_LOG_LOCATION,
62 };
63
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 },
69 {}
70 };
71
72 int c, r;
73
74 assert(argc >= 1);
75 assert(argv);
76
77 opterr = 0;
78
79 while ((c = getopt_long(argc, argv, ":", options, NULL)) >= 0)
80 switch (c) {
81
82 case ARG_LOG_LEVEL:
83 r = log_set_max_level_from_string(optarg);
84 if (r < 0)
85 log_error("Failed to parse log level %s, ignoring.", optarg);
86
87 break;
88
89 case ARG_LOG_TARGET:
90 r = log_set_target_from_string(optarg);
91 if (r < 0)
92 log_error("Failed to parse log target %s, ignoring", optarg);
93
94 break;
95
96 case ARG_LOG_COLOR:
97
98 if (optarg) {
99 r = log_show_color_from_string(optarg);
100 if (r < 0)
101 log_error("Failed to parse log color setting %s, ignoring", optarg);
102 } else
103 log_show_color(true);
104
105 break;
106
107 case ARG_LOG_LOCATION:
108 if (optarg) {
109 r = log_show_location_from_string(optarg);
110 if (r < 0)
111 log_error("Failed to parse log location setting %s, ignoring", optarg);
112 } else
113 log_show_location(true);
114
115 break;
116
117 case '?':
118 log_error("Unknown option %s.", argv[optind-1]);
119 return -EINVAL;
120
121 case ':':
122 log_error("Missing argument to %s.", argv[optind-1]);
123 return -EINVAL;
124
125 default:
126 assert_not_reached("Unhandled option code.");
127 }
128
129 if (optind >= argc) {
130 log_error("Verb argument missing.");
131 return -EINVAL;
132 }
133
134 arg_verb = argv[optind];
135
136 if (optind + 1 < argc)
137 log_error("Excess arguments, ignoring");
138 return 0;
139 }
140
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";
148
149 const char *dir;
150
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");
153 return -errno;
154 }
155
156 if (mount(NULL, "/run/initramfs", NULL, MS_PRIVATE, NULL) < 0) {
157 log_error("Failed to make /run/initramfs private mount: %m");
158 return -errno;
159 }
160
161 NULSTR_FOREACH(dir, dirs)
162 if (mkdir_p_label(dir, 0755) < 0 && errno != EEXIST) {
163 log_error("Failed to mkdir %s: %m", dir);
164 return -errno;
165 }
166
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");
169 return -errno;
170 }
171
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");
174 return -errno;
175 }
176
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");
179 return -errno;
180 }
181
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");
184 return -errno;
185 }
186
187 return 0;
188 }
189
190 static int pivot_to_new_root(void) {
191
192 if (chdir("/run/initramfs") < 0) {
193 log_error("Failed to change directory to /run/initramfs: %m");
194 return -errno;
195 }
196
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
200 * work-around.
201 *
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");
205
206 if (pivot_root(".", "oldroot") < 0) {
207 log_error("pivot failed: %m");
208 /* only chroot if pivot root succeeded */
209 return -errno;
210 }
211
212 chroot(".");
213
214 setsid();
215 make_console_stdio();
216
217 log_info("Successfully changed into root pivot.");
218
219 return 0;
220 }
221
222 int main(int argc, char *argv[]) {
223 bool need_umount, need_swapoff, need_loop_detach, need_dm_detach;
224 bool in_container, use_watchdog = false;
225 _cleanup_free_ char *cgroup = NULL;
226 char *arguments[3];
227 unsigned retries;
228 int cmd, r;
229
230 log_parse_environment();
231 r = parse_argv(argc, argv);
232 if (r < 0)
233 goto error;
234
235 /* journald will die if not gone yet. The log target defaults
236 * to console, but may have been changed by commandline options. */
237
238 log_close_console(); /* force reopen of /dev/console */
239 log_open();
240
241 umask(0022);
242
243 if (getpid() != 1) {
244 log_error("Not executed by init (PID 1).");
245 r = -EPERM;
246 goto error;
247 }
248
249 if (streq(arg_verb, "reboot"))
250 cmd = RB_AUTOBOOT;
251 else if (streq(arg_verb, "poweroff"))
252 cmd = RB_POWER_OFF;
253 else if (streq(arg_verb, "halt"))
254 cmd = RB_HALT_SYSTEM;
255 else if (streq(arg_verb, "kexec"))
256 cmd = LINUX_REBOOT_CMD_KEXEC;
257 else {
258 r = -EINVAL;
259 log_error("Unknown action '%s'.", arg_verb);
260 goto error;
261 }
262
263 cg_get_root_path(&cgroup);
264
265 use_watchdog = !!getenv("WATCHDOG_USEC");
266
267 /* lock us into memory */
268 mlockall(MCL_CURRENT|MCL_FUTURE);
269
270 log_info("Sending SIGTERM to remaining processes...");
271 broadcast_signal(SIGTERM, true, true);
272
273 log_info("Sending SIGKILL to remaining processes...");
274 broadcast_signal(SIGKILL, true, false);
275
276 in_container = detect_container(NULL) > 0;
277
278 need_umount = true;
279 need_swapoff = !in_container;
280 need_loop_detach = !in_container;
281 need_dm_detach = !in_container;
282
283 /* Unmount all mountpoints, swaps, and loopback devices */
284 for (retries = 0; retries < FINALIZE_ATTEMPTS; retries++) {
285 bool changed = false;
286
287 if (use_watchdog)
288 watchdog_ping();
289
290 /* Let's trim the cgroup tree on each iteration so
291 that we leave an empty cgroup tree around, so that
292 container managers get a nice notify event when we
293 are down */
294 if (cgroup)
295 cg_trim(SYSTEMD_CGROUP_CONTROLLER, cgroup, false);
296
297 if (need_umount) {
298 log_info("Unmounting file systems.");
299 r = umount_all(&changed);
300 if (r == 0) {
301 need_umount = false;
302 log_info("All filesystems unmounted.");
303 } else if (r > 0)
304 log_info("Not all file systems unmounted, %d left.", r);
305 else
306 log_error("Failed to unmount file systems: %s", strerror(-r));
307 }
308
309 if (need_swapoff) {
310 log_info("Deactivating swaps.");
311 r = swapoff_all(&changed);
312 if (r == 0) {
313 need_swapoff = false;
314 log_info("All swaps deactivated.");
315 } else if (r > 0)
316 log_info("Not all swaps deactivated, %d left.", r);
317 else
318 log_error("Failed to deactivate swaps: %s", strerror(-r));
319 }
320
321 if (need_loop_detach) {
322 log_info("Detaching loop devices.");
323 r = loopback_detach_all(&changed);
324 if (r == 0) {
325 need_loop_detach = false;
326 log_info("All loop devices detached.");
327 } else if (r > 0)
328 log_info("Not all loop devices detached, %d left.", r);
329 else
330 log_error("Failed to detach loop devices: %s", strerror(-r));
331 }
332
333 if (need_dm_detach) {
334 log_info("Detaching DM devices.");
335 r = dm_detach_all(&changed);
336 if (r == 0) {
337 need_dm_detach = false;
338 log_info("All DM devices detached.");
339 } else if (r > 0)
340 log_info("Not all DM devices detached, %d left.", r);
341 else
342 log_error("Failed to detach DM devices: %s", strerror(-r));
343 }
344
345 if (!need_umount && !need_swapoff && !need_loop_detach && !need_dm_detach) {
346 if (retries > 0)
347 log_info("All filesystems, swaps, loop devices, DM devices detached.");
348 /* Yay, done */
349 goto initrd_jump;
350 }
351
352 /* If in this iteration we didn't manage to
353 * unmount/deactivate anything, we simply give up */
354 if (!changed) {
355 log_info("Cannot finalize remaining%s%s%s%s continuing.",
356 need_umount ? " file systems," : "",
357 need_swapoff ? " swap devices," : "",
358 need_loop_detach ? " loop devices," : "",
359 need_dm_detach ? " DM devices," : "");
360 goto initrd_jump;
361 }
362
363 log_debug("After %u retries, couldn't finalize remaining %s%s%s%s trying again.",
364 retries + 1,
365 need_umount ? " file systems," : "",
366 need_swapoff ? " swap devices," : "",
367 need_loop_detach ? " loop devices," : "",
368 need_dm_detach ? " DM devices," : "");
369 }
370
371 log_error("Too many iterations, giving up.");
372
373 initrd_jump:
374
375 arguments[0] = NULL;
376 arguments[1] = arg_verb;
377 arguments[2] = NULL;
378 execute_directory(SYSTEM_SHUTDOWN_PATH, NULL, DEFAULT_TIMEOUT_USEC, arguments);
379
380 if (!in_container && !in_initrd() &&
381 access("/run/initramfs/shutdown", X_OK) == 0) {
382
383 if (prepare_new_root() >= 0 &&
384 pivot_to_new_root() >= 0) {
385 arguments[0] = (char*) "/shutdown";
386
387 log_info("Returning to initrd...");
388
389 execv("/shutdown", arguments);
390 log_error("Failed to execute shutdown binary: %m");
391 }
392 }
393
394 if (need_umount || need_swapoff || need_loop_detach || need_dm_detach)
395 log_error("Failed to finalize %s%s%s%s ignoring",
396 need_umount ? " file systems," : "",
397 need_swapoff ? " swap devices," : "",
398 need_loop_detach ? " loop devices," : "",
399 need_dm_detach ? " DM devices," : "");
400
401 /* The kernel will automaticall flush ATA disks and suchlike
402 * on reboot(), but the file systems need to be synce'd
403 * explicitly in advance. So let's do this here, but not
404 * needlessly slow down containers. */
405 if (!in_container)
406 sync();
407
408 switch (cmd) {
409
410 case LINUX_REBOOT_CMD_KEXEC:
411
412 if (!in_container) {
413 /* We cheat and exec kexec to avoid doing all its work */
414 pid_t pid;
415
416 log_info("Rebooting with kexec.");
417
418 pid = fork();
419 if (pid < 0)
420 log_error("Failed to fork: %m");
421 else if (pid == 0) {
422
423 const char * const args[] = {
424 KEXEC, "-e", NULL
425 };
426
427 /* Child */
428
429 execv(args[0], (char * const *) args);
430 _exit(EXIT_FAILURE);
431 } else
432 wait_for_terminate_and_warn("kexec", pid);
433 }
434
435 cmd = RB_AUTOBOOT;
436 /* Fall through */
437
438 case RB_AUTOBOOT:
439
440 if (!in_container) {
441 _cleanup_free_ char *param = NULL;
442
443 if (read_one_line_file(REBOOT_PARAM_FILE, &param) >= 0) {
444 log_info("Rebooting with argument '%s'.", param);
445 syscall(SYS_reboot, LINUX_REBOOT_MAGIC1, LINUX_REBOOT_MAGIC2,
446 LINUX_REBOOT_CMD_RESTART2, param);
447 }
448 }
449
450 log_info("Rebooting.");
451 break;
452
453 case RB_POWER_OFF:
454 log_info("Powering off.");
455 break;
456
457 case RB_HALT_SYSTEM:
458 log_info("Halting system.");
459 break;
460
461 default:
462 assert_not_reached("Unknown magic");
463 }
464
465 reboot(cmd);
466 if (errno == EPERM && in_container) {
467 /* If we are in a container, and we lacked
468 * CAP_SYS_BOOT just exit, this will kill our
469 * container for good. */
470 log_info("Exiting container.");
471 exit(0);
472 }
473
474 log_error("Failed to invoke reboot(): %m");
475 r = -errno;
476
477 error:
478 log_error("Critical error while doing system shutdown: %s", strerror(-r));
479
480 freeze();
481 }