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