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