]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/core/shutdown.c
shutdown: unify handling of reboot() syscall a bit
[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/types.h>
28 #include <sys/stat.h>
29 #include <sys/mount.h>
30 #include <sys/syscall.h>
31 #include <fcntl.h>
32 #include <dirent.h>
33 #include <errno.h>
34 #include <unistd.h>
35 #include <signal.h>
36 #include <stdbool.h>
37 #include <stdlib.h>
38 #include <string.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
52 #define FINALIZE_ATTEMPTS 50
53
54 static int prepare_new_root(void) {
55 static const char dirs[] =
56 "/run/initramfs/oldroot\0"
57 "/run/initramfs/proc\0"
58 "/run/initramfs/sys\0"
59 "/run/initramfs/dev\0"
60 "/run/initramfs/run\0";
61
62 const char *dir;
63
64 if (mount("/run/initramfs", "/run/initramfs", NULL, MS_BIND, NULL) < 0) {
65 log_error("Failed to mount bind /run/initramfs on /run/initramfs: %m");
66 return -errno;
67 }
68
69 if (mount(NULL, "/run/initramfs", NULL, MS_PRIVATE, NULL) < 0) {
70 log_error("Failed to make /run/initramfs private mount: %m");
71 return -errno;
72 }
73
74 NULSTR_FOREACH(dir, dirs)
75 if (mkdir_p_label(dir, 0755) < 0 && errno != EEXIST) {
76 log_error("Failed to mkdir %s: %m", dir);
77 return -errno;
78 }
79
80 if (mount("/sys", "/run/initramfs/sys", NULL, MS_BIND, NULL) < 0) {
81 log_error("Failed to mount bind /sys on /run/initramfs/sys: %m");
82 return -errno;
83 }
84
85 if (mount("/proc", "/run/initramfs/proc", NULL, MS_BIND, NULL) < 0) {
86 log_error("Failed to mount bind /proc on /run/initramfs/proc: %m");
87 return -errno;
88 }
89
90 if (mount("/dev", "/run/initramfs/dev", NULL, MS_BIND, NULL) < 0) {
91 log_error("Failed to mount bind /dev on /run/initramfs/dev: %m");
92 return -errno;
93 }
94
95 if (mount("/run", "/run/initramfs/run", NULL, MS_BIND, NULL) < 0) {
96 log_error("Failed to mount bind /run on /run/initramfs/run: %m");
97 return -errno;
98 }
99
100 return 0;
101 }
102
103 static int pivot_to_new_root(void) {
104
105 if (chdir("/run/initramfs") < 0) {
106 log_error("Failed to change directory to /run/initramfs: %m");
107 return -errno;
108 }
109
110 /* Work-around for a kernel bug: for some reason the kernel
111 * refuses switching root if any file systems are mounted
112 * MS_SHARED. Hence remount them MS_PRIVATE here as a
113 * work-around.
114 *
115 * https://bugzilla.redhat.com/show_bug.cgi?id=847418 */
116 if (mount(NULL, "/", NULL, MS_REC|MS_PRIVATE, NULL) < 0)
117 log_warning("Failed to make \"/\" private mount: %m");
118
119 if (pivot_root(".", "oldroot") < 0) {
120 log_error("pivot failed: %m");
121 /* only chroot if pivot root succeeded */
122 return -errno;
123 }
124
125 chroot(".");
126
127 setsid();
128 make_console_stdio();
129
130 log_info("Successfully changed into root pivot.");
131
132 return 0;
133 }
134
135 int main(int argc, char *argv[]) {
136 bool need_umount = true, need_swapoff = true, need_loop_detach = true, need_dm_detach = true;
137 bool in_container, use_watchdog = false;
138 _cleanup_free_ char *line = NULL, *cgroup = NULL;
139 char *arguments[3];
140 unsigned retries;
141 int cmd, r;
142
143 /* suppress shutdown status output if 'quiet' is used */
144 r = proc_cmdline(&line);
145 if (r > 0) {
146 char *w, *state;
147 size_t l;
148
149 FOREACH_WORD_QUOTED(w, l, line, state) {
150 if (l == 5 && memcmp(w, "quiet", 5) == 0) {
151 log_set_max_level(LOG_WARNING);
152 break;
153 }
154 }
155 }
156
157 log_parse_environment();
158 log_set_target(LOG_TARGET_CONSOLE); /* syslog will die if not gone yet */
159 log_open();
160
161 umask(0022);
162
163 if (getpid() != 1) {
164 log_error("Not executed by init (pid 1).");
165 r = -EPERM;
166 goto error;
167 }
168
169 if (argc != 2) {
170 log_error("Invalid number of arguments.");
171 r = -EINVAL;
172 goto error;
173 }
174
175 in_container = detect_container(NULL) > 0;
176
177 if (streq(argv[1], "reboot"))
178 cmd = RB_AUTOBOOT;
179 else if (streq(argv[1], "poweroff"))
180 cmd = RB_POWER_OFF;
181 else if (streq(argv[1], "halt"))
182 cmd = RB_HALT_SYSTEM;
183 else if (streq(argv[1], "kexec"))
184 cmd = LINUX_REBOOT_CMD_KEXEC;
185 else {
186 log_error("Unknown action '%s'.", argv[1]);
187 r = -EINVAL;
188 goto error;
189 }
190
191 cg_get_root_path(&cgroup);
192
193 use_watchdog = !!getenv("WATCHDOG_USEC");
194
195 /* lock us into memory */
196 mlockall(MCL_CURRENT|MCL_FUTURE);
197
198 log_info("Sending SIGTERM to remaining processes...");
199 broadcast_signal(SIGTERM, true);
200
201 log_info("Sending SIGKILL to remaining processes...");
202 broadcast_signal(SIGKILL, true);
203
204 if (in_container) {
205 need_swapoff = false;
206 need_dm_detach = false;
207 need_loop_detach = false;
208 }
209
210 /* Unmount all mountpoints, swaps, and loopback devices */
211 for (retries = 0; retries < FINALIZE_ATTEMPTS; retries++) {
212 bool changed = false;
213
214 if (use_watchdog)
215 watchdog_ping();
216
217 /* Let's trim the cgroup tree on each iteration so
218 that we leave an empty cgroup tree around, so that
219 container managers get a nice notify event when we
220 are down */
221 if (cgroup)
222 cg_trim(SYSTEMD_CGROUP_CONTROLLER, cgroup, false);
223
224 if (need_umount) {
225 log_info("Unmounting file systems.");
226 r = umount_all(&changed);
227 if (r == 0) {
228 need_umount = false;
229 log_info("All filesystems unmounted.");
230 } else if (r > 0)
231 log_info("Not all file systems unmounted, %d left.", r);
232 else
233 log_error("Failed to unmount file systems: %s", strerror(-r));
234 }
235
236 if (need_swapoff) {
237 log_info("Deactivating swaps.");
238 r = swapoff_all(&changed);
239 if (r == 0) {
240 need_swapoff = false;
241 log_info("All swaps deactivated.");
242 } else if (r > 0)
243 log_info("Not all swaps deactivated, %d left.", r);
244 else
245 log_error("Failed to deactivate swaps: %s", strerror(-r));
246 }
247
248 if (need_loop_detach) {
249 log_info("Detaching loop devices.");
250 r = loopback_detach_all(&changed);
251 if (r == 0) {
252 need_loop_detach = false;
253 log_info("All loop devices detached.");
254 } else if (r > 0)
255 log_info("Not all loop devices detached, %d left.", r);
256 else
257 log_error("Failed to detach loop devices: %s", strerror(-r));
258 }
259
260 if (need_dm_detach) {
261 log_info("Detaching DM devices.");
262 r = dm_detach_all(&changed);
263 if (r == 0) {
264 need_dm_detach = false;
265 log_info("All DM devices detached.");
266 } else if (r > 0)
267 log_info("Not all DM devices detached, %d left.", r);
268 else
269 log_error("Failed to detach DM devices: %s", strerror(-r));
270 }
271
272 if (!need_umount && !need_swapoff && !need_loop_detach && !need_dm_detach) {
273 if (retries > 0)
274 log_info("All filesystems, swaps, loop devices, DM devices detached.");
275 /* Yay, done */
276 break;
277 }
278
279 /* If in this iteration we didn't manage to
280 * unmount/deactivate anything, we simply give up */
281 if (!changed) {
282 log_error("Cannot finalize remaining file systems and devices, giving up.");
283 break;
284 }
285
286 log_debug("Couldn't finalize remaining file systems and devices after %u retries, trying again.", retries+1);
287 }
288
289 if (retries >= FINALIZE_ATTEMPTS)
290 log_error("Too many iterations, giving up.");
291 else
292 log_info("Storage is finalized.");
293
294 arguments[0] = NULL;
295 arguments[1] = argv[1];
296 arguments[2] = NULL;
297 execute_directory(SYSTEM_SHUTDOWN_PATH, NULL, arguments);
298
299 if (!in_container && !in_initrd() &&
300 access("/run/initramfs/shutdown", X_OK) == 0) {
301
302 if (prepare_new_root() >= 0 &&
303 pivot_to_new_root() >= 0) {
304
305 log_info("Returning to initrd...");
306
307 execv("/shutdown", argv);
308 log_error("Failed to execute shutdown binary: %m");
309 }
310 }
311
312 /* The kernel will automaticall flush ATA disks and suchlike
313 * on reboot(), but the file systems need to be synce'd
314 * explicitly in advance. So let's do this here, but not
315 * needlessly slow down containers. */
316 if (!in_container)
317 sync();
318
319 switch (cmd) {
320
321 case LINUX_REBOOT_CMD_KEXEC:
322
323 if (!in_container) {
324 /* We cheat and exec kexec to avoid doing all its work */
325 pid_t pid;
326
327 log_info("Rebooting with kexec.");
328
329 pid = fork();
330 if (pid < 0)
331 log_error("Failed to fork: %m");
332 else if (pid == 0) {
333
334 const char * const args[] = {
335 KEXEC, "-e", NULL
336 };
337
338 /* Child */
339
340 execv(args[0], (char * const *) args);
341 _exit(EXIT_FAILURE);
342 } else
343 wait_for_terminate_and_warn("kexec", pid);
344 }
345
346 cmd = RB_AUTOBOOT;
347 /* Fall through */
348
349 case RB_AUTOBOOT:
350
351 if (!in_container) {
352 _cleanup_free_ char *param = NULL;
353
354 if (read_one_line_file(REBOOT_PARAM_FILE, &param) >= 0) {
355 log_info("Rebooting with argument '%s'.", param);
356 syscall(SYS_reboot, LINUX_REBOOT_MAGIC1, LINUX_REBOOT_MAGIC2,
357 LINUX_REBOOT_CMD_RESTART2, param);
358 }
359 }
360
361 log_info("Rebooting.");
362 break;
363
364 case RB_POWER_OFF:
365 log_info("Powering off.");
366 break;
367
368 case RB_HALT_SYSTEM:
369 log_info("Halting system.");
370 break;
371
372 default:
373 assert_not_reached("Unknown magic");
374 }
375
376 reboot(cmd);
377 if (errno == EPERM && in_container) {
378 /* If we are in a container, and we lacked
379 * CAP_SYS_BOOT just exit, this will kill our
380 * container for good. */
381 log_info("Exiting container.");
382 exit(0);
383 }
384
385 log_error("Failed to invoke reboot(): %m");
386 r = -errno;
387
388 error:
389 log_error("Critical error while doing system shutdown: %s", strerror(-r));
390
391 freeze();
392 return EXIT_FAILURE;
393 }