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