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