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