]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/core/shutdown.c
Merge pull request #7569 from keszybz/doc-reverse-settings
[thirdparty/systemd.git] / src / core / shutdown.c
1 /* SPDX-License-Identifier: LGPL-2.1+ */
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
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
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
15 Lesser General Public License for more details.
16
17 You should have received a copy of the GNU Lesser General Public License
18 along with systemd; If not, see <http://www.gnu.org/licenses/>.
19 ***/
20
21 #include <errno.h>
22 #include <getopt.h>
23 #include <linux/reboot.h>
24 #include <signal.h>
25 #include <stdbool.h>
26 #include <stdlib.h>
27 #include <sys/mman.h>
28 #include <sys/mount.h>
29 #include <sys/reboot.h>
30 #include <sys/stat.h>
31 #include <unistd.h>
32
33 #include "alloc-util.h"
34 #include "cgroup-util.h"
35 #include "def.h"
36 #include "exec-util.h"
37 #include "fileio.h"
38 #include "killall.h"
39 #include "log.h"
40 #include "missing.h"
41 #include "parse-util.h"
42 #include "process-util.h"
43 #include "string-util.h"
44 #include "switch-root.h"
45 #include "terminal-util.h"
46 #include "umount.h"
47 #include "util.h"
48 #include "virt.h"
49 #include "watchdog.h"
50
51 #define FINALIZE_ATTEMPTS 50
52
53 static char* arg_verb;
54 static uint8_t arg_exit_code;
55
56 static 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,
62 ARG_EXIT_CODE,
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 },
70 { "exit-code", required_argument, NULL, ARG_EXIT_CODE },
71 {}
72 };
73
74 int c, r;
75
76 assert(argc >= 1);
77 assert(argv);
78
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)
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
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
126 case '\001':
127 if (!arg_verb)
128 arg_verb = optarg;
129 else
130 log_error("Excess arguments, ignoring");
131 break;
132
133 case '?':
134 return -EINVAL;
135
136 default:
137 assert_not_reached("Unhandled option code.");
138 }
139
140 if (!arg_verb) {
141 log_error("Verb argument missing.");
142 return -EINVAL;
143 }
144
145 return 0;
146 }
147
148 static int switch_root_initramfs(void) {
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");
151
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");
154
155 /* switch_root with MS_BIND, because there might still be processes lurking around, which have open file descriptors.
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);
160 }
161
162 int main(int argc, char *argv[]) {
163 bool need_umount, need_swapoff, need_loop_detach, need_dm_detach;
164 bool in_container, use_watchdog = false;
165 _cleanup_free_ char *cgroup = NULL;
166 char *arguments[3];
167 unsigned retries;
168 int cmd, r;
169 static const char* const dirs[] = {SYSTEM_SHUTDOWN_PATH, NULL};
170 char *watchdog_device;
171
172 log_parse_environment();
173 r = parse_argv(argc, argv);
174 if (r < 0)
175 goto error;
176
177 /* journald will die if not gone yet. The log target defaults
178 * to console, but may have been changed by command line options. */
179
180 log_close_console(); /* force reopen of /dev/console */
181 log_open();
182
183 umask(0022);
184
185 if (getpid_cached() != 1) {
186 log_error("Not executed by init (PID 1).");
187 r = -EPERM;
188 goto error;
189 }
190
191 if (streq(arg_verb, "reboot"))
192 cmd = RB_AUTOBOOT;
193 else if (streq(arg_verb, "poweroff"))
194 cmd = RB_POWER_OFF;
195 else if (streq(arg_verb, "halt"))
196 cmd = RB_HALT_SYSTEM;
197 else if (streq(arg_verb, "kexec"))
198 cmd = LINUX_REBOOT_CMD_KEXEC;
199 else if (streq(arg_verb, "exit"))
200 cmd = 0; /* ignored, just checking that arg_verb is valid */
201 else {
202 r = -EINVAL;
203 log_error("Unknown action '%s'.", arg_verb);
204 goto error;
205 }
206
207 (void) cg_get_root_path(&cgroup);
208 in_container = detect_container() > 0;
209
210 use_watchdog = !!getenv("WATCHDOG_USEC");
211 watchdog_device = getenv("WATCHDOG_DEVICE");
212 if (watchdog_device) {
213 r = watchdog_set_device(watchdog_device);
214 if (r < 0)
215 log_warning_errno(r, "Failed to set watchdog device to %s, ignoring: %m",
216 watchdog_device);
217 }
218
219 /* Lock us into memory */
220 mlockall(MCL_CURRENT|MCL_FUTURE);
221
222 /* Synchronize everything that is not written to disk yet at this point already. This is a good idea so that
223 * slow IO is processed here already and the final process killing spree is not impacted by processes
224 * desperately trying to sync IO to disk within their timeout. */
225 if (!in_container)
226 sync();
227
228 log_info("Sending SIGTERM to remaining processes...");
229 broadcast_signal(SIGTERM, true, true);
230
231 log_info("Sending SIGKILL to remaining processes...");
232 broadcast_signal(SIGKILL, true, false);
233
234 need_umount = !in_container;
235 need_swapoff = !in_container;
236 need_loop_detach = !in_container;
237 need_dm_detach = !in_container;
238
239 /* Unmount all mountpoints, swaps, and loopback devices */
240 for (retries = 0; retries < FINALIZE_ATTEMPTS; retries++) {
241 bool changed = false;
242
243 if (use_watchdog)
244 watchdog_ping();
245
246 /* Let's trim the cgroup tree on each iteration so
247 that we leave an empty cgroup tree around, so that
248 container managers get a nice notify event when we
249 are down */
250 if (cgroup)
251 cg_trim(SYSTEMD_CGROUP_CONTROLLER, cgroup, false);
252
253 if (need_umount) {
254 log_info("Unmounting file systems.");
255 r = umount_all(&changed);
256 if (r == 0) {
257 need_umount = false;
258 log_info("All filesystems unmounted.");
259 } else if (r > 0)
260 log_info("Not all file systems unmounted, %d left.", r);
261 else
262 log_error_errno(r, "Failed to unmount file systems: %m");
263 }
264
265 if (need_swapoff) {
266 log_info("Deactivating swaps.");
267 r = swapoff_all(&changed);
268 if (r == 0) {
269 need_swapoff = false;
270 log_info("All swaps deactivated.");
271 } else if (r > 0)
272 log_info("Not all swaps deactivated, %d left.", r);
273 else
274 log_error_errno(r, "Failed to deactivate swaps: %m");
275 }
276
277 if (need_loop_detach) {
278 log_info("Detaching loop devices.");
279 r = loopback_detach_all(&changed);
280 if (r == 0) {
281 need_loop_detach = false;
282 log_info("All loop devices detached.");
283 } else if (r > 0)
284 log_info("Not all loop devices detached, %d left.", r);
285 else
286 log_error_errno(r, "Failed to detach loop devices: %m");
287 }
288
289 if (need_dm_detach) {
290 log_info("Detaching DM devices.");
291 r = dm_detach_all(&changed);
292 if (r == 0) {
293 need_dm_detach = false;
294 log_info("All DM devices detached.");
295 } else if (r > 0)
296 log_info("Not all DM devices detached, %d left.", r);
297 else
298 log_error_errno(r, "Failed to detach DM devices: %m");
299 }
300
301 if (!need_umount && !need_swapoff && !need_loop_detach && !need_dm_detach) {
302 if (retries > 0)
303 log_info("All filesystems, swaps, loop devices, DM devices detached.");
304 /* Yay, done */
305 goto initrd_jump;
306 }
307
308 /* If in this iteration we didn't manage to
309 * unmount/deactivate anything, we simply give up */
310 if (!changed) {
311 log_info("Cannot finalize remaining%s%s%s%s continuing.",
312 need_umount ? " file systems," : "",
313 need_swapoff ? " swap devices," : "",
314 need_loop_detach ? " loop devices," : "",
315 need_dm_detach ? " DM devices," : "");
316 goto initrd_jump;
317 }
318
319 log_debug("After %u retries, couldn't finalize remaining %s%s%s%s trying again.",
320 retries + 1,
321 need_umount ? " file systems," : "",
322 need_swapoff ? " swap devices," : "",
323 need_loop_detach ? " loop devices," : "",
324 need_dm_detach ? " DM devices," : "");
325 }
326
327 log_error("Too many iterations, giving up.");
328
329 initrd_jump:
330
331 /* We're done with the watchdog. */
332 watchdog_free_device();
333
334 arguments[0] = NULL;
335 arguments[1] = arg_verb;
336 arguments[2] = NULL;
337 execute_directories(dirs, DEFAULT_TIMEOUT_USEC, NULL, NULL, arguments);
338
339 if (!in_container && !in_initrd() &&
340 access("/run/initramfs/shutdown", X_OK) == 0) {
341 r = switch_root_initramfs();
342 if (r >= 0) {
343 argv[0] = (char*) "/shutdown";
344
345 setsid();
346 make_console_stdio();
347
348 log_info("Successfully changed into root pivot.\n"
349 "Returning to initrd...");
350
351 execv("/shutdown", argv);
352 log_error_errno(errno, "Failed to execute shutdown binary: %m");
353 } else
354 log_error_errno(r, "Failed to switch root to \"/run/initramfs\": %m");
355
356 }
357
358 if (need_umount || need_swapoff || need_loop_detach || need_dm_detach)
359 log_error("Failed to finalize %s%s%s%s ignoring",
360 need_umount ? " file systems," : "",
361 need_swapoff ? " swap devices," : "",
362 need_loop_detach ? " loop devices," : "",
363 need_dm_detach ? " DM devices," : "");
364
365 /* The kernel will automatically flush ATA disks and suchlike on reboot(), but the file systems need to be
366 * sync'ed explicitly in advance. So let's do this here, but not needlessly slow down containers. Note that we
367 * sync'ed things already once above, but we did some more work since then which might have caused IO, hence
368 * let's doit once more. */
369 if (!in_container)
370 sync();
371
372 if (streq(arg_verb, "exit")) {
373 if (in_container)
374 exit(arg_exit_code);
375 else {
376 /* We cannot exit() on the host, fallback on another
377 * method. */
378 cmd = RB_POWER_OFF;
379 }
380 }
381
382 switch (cmd) {
383
384 case LINUX_REBOOT_CMD_KEXEC:
385
386 if (!in_container) {
387 /* We cheat and exec kexec to avoid doing all its work */
388 pid_t pid;
389
390 log_info("Rebooting with kexec.");
391
392 pid = fork();
393 if (pid < 0)
394 log_error_errno(errno, "Failed to fork: %m");
395 else if (pid == 0) {
396
397 const char * const args[] = {
398 KEXEC, "-e", NULL
399 };
400
401 /* Child */
402
403 execv(args[0], (char * const *) args);
404 _exit(EXIT_FAILURE);
405 } else
406 wait_for_terminate_and_warn("kexec", pid, true);
407 }
408
409 cmd = RB_AUTOBOOT;
410 _fallthrough_;
411 case RB_AUTOBOOT:
412
413 if (!in_container) {
414 _cleanup_free_ char *param = NULL;
415
416 r = read_one_line_file("/run/systemd/reboot-param", &param);
417 if (r < 0 && r != -ENOENT)
418 log_warning_errno(r, "Failed to read reboot parameter file: %m");
419
420 if (!isempty(param)) {
421 log_info("Rebooting with argument '%s'.", param);
422 syscall(SYS_reboot, LINUX_REBOOT_MAGIC1, LINUX_REBOOT_MAGIC2, LINUX_REBOOT_CMD_RESTART2, param);
423 log_warning_errno(errno, "Failed to reboot with parameter, retrying without: %m");
424 }
425 }
426
427 log_info("Rebooting.");
428 break;
429
430 case RB_POWER_OFF:
431 log_info("Powering off.");
432 break;
433
434 case RB_HALT_SYSTEM:
435 log_info("Halting system.");
436 break;
437
438 default:
439 assert_not_reached("Unknown magic");
440 }
441
442 reboot(cmd);
443 if (errno == EPERM && in_container) {
444 /* If we are in a container, and we lacked
445 * CAP_SYS_BOOT just exit, this will kill our
446 * container for good. */
447 log_info("Exiting container.");
448 exit(0);
449 }
450
451 r = log_error_errno(errno, "Failed to invoke reboot(): %m");
452
453 error:
454 log_emergency_errno(r, "Critical error while doing system shutdown: %m");
455 freeze();
456 }