]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/core/shutdown.c
tree-wide: make use of log_error_errno() return value
[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
52 static 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
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)
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
113 case '\001':
114 if (!arg_verb)
115 arg_verb = optarg;
116 else
117 log_error("Excess arguments, ignoring");
118 break;
119
120 case '?':
121 return -EINVAL;
122
123 default:
124 assert_not_reached("Unhandled option code.");
125 }
126
127 if (!arg_verb) {
128 log_error("Verb argument missing.");
129 return -EINVAL;
130 }
131
132 return 0;
133 }
134
135 static int switch_root_initramfs(void) {
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");
138
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");
141
142 /* switch_root with MS_BIND, because there might still be processes lurking around, which have open file descriptors.
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);
147 }
148
149
150 int main(int argc, char *argv[]) {
151 bool need_umount, need_swapoff, need_loop_detach, need_dm_detach;
152 bool in_container, use_watchdog = false;
153 _cleanup_free_ char *cgroup = NULL;
154 char *arguments[3];
155 unsigned retries;
156 int cmd, r;
157 static const char* const dirs[] = {SYSTEM_SHUTDOWN_PATH, NULL};
158
159 log_parse_environment();
160 r = parse_argv(argc, argv);
161 if (r < 0)
162 goto error;
163
164 /* journald will die if not gone yet. The log target defaults
165 * to console, but may have been changed by command line options. */
166
167 log_close_console(); /* force reopen of /dev/console */
168 log_open();
169
170 umask(0022);
171
172 if (getpid() != 1) {
173 log_error("Not executed by init (PID 1).");
174 r = -EPERM;
175 goto error;
176 }
177
178 if (streq(arg_verb, "reboot"))
179 cmd = RB_AUTOBOOT;
180 else if (streq(arg_verb, "poweroff"))
181 cmd = RB_POWER_OFF;
182 else if (streq(arg_verb, "halt"))
183 cmd = RB_HALT_SYSTEM;
184 else if (streq(arg_verb, "kexec"))
185 cmd = LINUX_REBOOT_CMD_KEXEC;
186 else {
187 r = -EINVAL;
188 log_error("Unknown action '%s'.", arg_verb);
189 goto error;
190 }
191
192 cg_get_root_path(&cgroup);
193
194 use_watchdog = !!getenv("WATCHDOG_USEC");
195
196 /* lock us into memory */
197 mlockall(MCL_CURRENT|MCL_FUTURE);
198
199 log_info("Sending SIGTERM to remaining processes...");
200 broadcast_signal(SIGTERM, true, true);
201
202 log_info("Sending SIGKILL to remaining processes...");
203 broadcast_signal(SIGKILL, true, false);
204
205 in_container = detect_container() > 0;
206
207 need_umount = !in_container;
208 need_swapoff = !in_container;
209 need_loop_detach = !in_container;
210 need_dm_detach = !in_container;
211
212 /* Unmount all mountpoints, swaps, and loopback devices */
213 for (retries = 0; retries < FINALIZE_ATTEMPTS; retries++) {
214 bool changed = false;
215
216 if (use_watchdog)
217 watchdog_ping();
218
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
226 if (need_umount) {
227 log_info("Unmounting file systems.");
228 r = umount_all(&changed);
229 if (r == 0) {
230 need_umount = false;
231 log_info("All filesystems unmounted.");
232 } else if (r > 0)
233 log_info("Not all file systems unmounted, %d left.", r);
234 else
235 log_error_errno(r, "Failed to unmount file systems: %m");
236 }
237
238 if (need_swapoff) {
239 log_info("Deactivating swaps.");
240 r = swapoff_all(&changed);
241 if (r == 0) {
242 need_swapoff = false;
243 log_info("All swaps deactivated.");
244 } else if (r > 0)
245 log_info("Not all swaps deactivated, %d left.", r);
246 else
247 log_error_errno(r, "Failed to deactivate swaps: %m");
248 }
249
250 if (need_loop_detach) {
251 log_info("Detaching loop devices.");
252 r = loopback_detach_all(&changed);
253 if (r == 0) {
254 need_loop_detach = false;
255 log_info("All loop devices detached.");
256 } else if (r > 0)
257 log_info("Not all loop devices detached, %d left.", r);
258 else
259 log_error_errno(r, "Failed to detach loop devices: %m");
260 }
261
262 if (need_dm_detach) {
263 log_info("Detaching DM devices.");
264 r = dm_detach_all(&changed);
265 if (r == 0) {
266 need_dm_detach = false;
267 log_info("All DM devices detached.");
268 } else if (r > 0)
269 log_info("Not all DM devices detached, %d left.", r);
270 else
271 log_error_errno(r, "Failed to detach DM devices: %m");
272 }
273
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.");
277 /* Yay, done */
278 goto initrd_jump;
279 }
280
281 /* If in this iteration we didn't manage to
282 * unmount/deactivate anything, we simply give up */
283 if (!changed) {
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;
290 }
291
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," : "");
298 }
299
300 log_error("Too many iterations, giving up.");
301
302 initrd_jump:
303
304 arguments[0] = NULL;
305 arguments[1] = arg_verb;
306 arguments[2] = NULL;
307 execute_directories(dirs, DEFAULT_TIMEOUT_USEC, arguments);
308
309 if (!in_container && !in_initrd() &&
310 access("/run/initramfs/shutdown", X_OK) == 0) {
311 r = switch_root_initramfs();
312 if (r >= 0) {
313 argv[0] = (char*) "/shutdown";
314
315 setsid();
316 make_console_stdio();
317
318 log_info("Successfully changed into root pivot.\n"
319 "Returning to initrd...");
320
321 execv("/shutdown", argv);
322 log_error_errno(errno, "Failed to execute shutdown binary: %m");
323 } else
324 log_error_errno(r, "Failed to switch root to \"/run/initramfs\": %m");
325
326 }
327
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
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
342 switch (cmd) {
343
344 case LINUX_REBOOT_CMD_KEXEC:
345
346 if (!in_container) {
347 /* We cheat and exec kexec to avoid doing all its work */
348 pid_t pid;
349
350 log_info("Rebooting with kexec.");
351
352 pid = fork();
353 if (pid < 0)
354 log_error_errno(errno, "Failed to fork: %m");
355 else if (pid == 0) {
356
357 const char * const args[] = {
358 KEXEC, "-e", NULL
359 };
360
361 /* Child */
362
363 execv(args[0], (char * const *) args);
364 _exit(EXIT_FAILURE);
365 } else
366 wait_for_terminate_and_warn("kexec", pid, true);
367 }
368
369 cmd = RB_AUTOBOOT;
370 /* Fall through */
371
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);
379 syscall(SYS_reboot, LINUX_REBOOT_MAGIC1, LINUX_REBOOT_MAGIC2, LINUX_REBOOT_CMD_RESTART2, param);
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 }
397
398 reboot(cmd);
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. */
403 log_info("Exiting container.");
404 exit(0);
405 }
406
407 r = log_error_errno(errno, "Failed to invoke reboot(): %m");
408
409 error:
410 log_emergency_errno(r, "Critical error while doing system shutdown: %m");
411
412 freeze();
413 }