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