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