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