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