chiark / gitweb /
fe7a0739db8d1feaab1e8ade3cfb5f7977fa9739
[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                         _cleanup_free_ char *word;
149
150                         word = strndup(w, l);
151                         if (!word)
152                                 break;
153
154                         if (streq(word, "quiet")) {
155                                 log_set_max_level(LOG_WARNING);
156                                 break;
157                         }
158                 }
159         }
160
161         log_parse_environment();
162         log_set_target(LOG_TARGET_CONSOLE); /* syslog will die if not gone yet */
163         log_open();
164
165         umask(0022);
166
167         if (getpid() != 1) {
168                 log_error("Not executed by init (pid 1).");
169                 r = -EPERM;
170                 goto error;
171         }
172
173         if (argc != 2) {
174                 log_error("Invalid number of arguments.");
175                 r = -EINVAL;
176                 goto error;
177         }
178
179         in_container = detect_container(NULL) > 0;
180
181         if (streq(argv[1], "reboot"))
182                 cmd = RB_AUTOBOOT;
183         else if (streq(argv[1], "poweroff"))
184                 cmd = RB_POWER_OFF;
185         else if (streq(argv[1], "halt"))
186                 cmd = RB_HALT_SYSTEM;
187         else if (streq(argv[1], "kexec"))
188                 cmd = LINUX_REBOOT_CMD_KEXEC;
189         else {
190                 log_error("Unknown action '%s'.", argv[1]);
191                 r = -EINVAL;
192                 goto error;
193         }
194
195         use_watchdog = !!getenv("WATCHDOG_USEC");
196
197         /* lock us into memory */
198         mlockall(MCL_CURRENT|MCL_FUTURE);
199
200         log_info("Sending SIGTERM to remaining processes...");
201         broadcast_signal(SIGTERM, true);
202
203         log_info("Sending SIGKILL to remaining processes...");
204         broadcast_signal(SIGKILL, true);
205
206         if (in_container) {
207                 need_swapoff = false;
208                 need_dm_detach = false;
209                 need_loop_detach = false;
210         }
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                 if (need_umount) {
220                         log_info("Unmounting file systems.");
221                         r = umount_all(&changed);
222                         if (r == 0) {
223                                 need_umount = false;
224                                 log_info("All filesystems unmounted.");
225                         } else if (r > 0)
226                                 log_info("Not all file systems unmounted, %d left.", r);
227                         else
228                                 log_error("Failed to unmount file systems: %s", strerror(-r));
229                 }
230
231                 if (need_swapoff) {
232                         log_info("Deactivating swaps.");
233                         r = swapoff_all(&changed);
234                         if (r == 0) {
235                                 need_swapoff = false;
236                                 log_info("All swaps deactivated.");
237                         } else if (r > 0)
238                                 log_info("Not all swaps deactivated, %d left.", r);
239                         else
240                                 log_error("Failed to deactivate swaps: %s", strerror(-r));
241                 }
242
243                 if (need_loop_detach) {
244                         log_info("Detaching loop devices.");
245                         r = loopback_detach_all(&changed);
246                         if (r == 0) {
247                                 need_loop_detach = false;
248                                 log_info("All loop devices detached.");
249                         } else if (r > 0)
250                                 log_info("Not all loop devices detached, %d left.", r);
251                         else
252                                 log_error("Failed to detach loop devices: %s", strerror(-r));
253                 }
254
255                 if (need_dm_detach) {
256                         log_info("Detaching DM devices.");
257                         r = dm_detach_all(&changed);
258                         if (r == 0) {
259                                 need_dm_detach = false;
260                                 log_info("All DM devices detached.");
261                         } else if (r > 0)
262                                 log_info("Not all DM devices detached, %d left.", r);
263                         else
264                                 log_error("Failed to detach DM devices: %s", strerror(-r));
265                 }
266
267                 if (!need_umount && !need_swapoff && !need_loop_detach && !need_dm_detach) {
268                         if (retries > 0)
269                                 log_info("All filesystems, swaps, loop devices, DM devices detached.");
270                         /* Yay, done */
271                         break;
272                 }
273
274                 /* If in this iteration we didn't manage to
275                  * unmount/deactivate anything, we simply give up */
276                 if (!changed) {
277                         log_error("Cannot finalize remaining file systems and devices, giving up.");
278                         break;
279                 }
280
281                 log_debug("Couldn't finalize remaining file systems and devices after %u retries, trying again.", retries+1);
282         }
283
284         if (retries >= FINALIZE_ATTEMPTS)
285                 log_error("Too many iterations, giving up.");
286         else
287                 log_info("Storage is finalized.");
288
289         arguments[0] = NULL;
290         arguments[1] = argv[1];
291         arguments[2] = NULL;
292         execute_directory(SYSTEM_SHUTDOWN_PATH, NULL, arguments);
293
294         if (!in_container && !in_initrd() &&
295             access("/run/initramfs/shutdown", X_OK) == 0) {
296
297                 if (prepare_new_root() >= 0 &&
298                     pivot_to_new_root() >= 0) {
299
300                         log_info("Returning to initrd...");
301
302                         execv("/shutdown", argv);
303                         log_error("Failed to execute shutdown binary: %m");
304                 }
305         }
306
307         /* The kernel will automaticall flush ATA disks and suchlike
308          * on reboot(), but the file systems need to be synce'd
309          * explicitly in advance. So let's do this here, but not
310          * needlessly slow down containers. */
311         if (!in_container)
312                 sync();
313
314         if (cmd == LINUX_REBOOT_CMD_KEXEC) {
315
316                 if (!in_container) {
317                         /* We cheat and exec kexec to avoid doing all its work */
318                         pid_t pid = fork();
319
320                         if (pid < 0)
321                                 log_error("Could not fork: %m. Falling back to normal reboot.");
322                         else if (pid > 0) {
323                                 wait_for_terminate_and_warn("kexec", pid);
324                                 log_warning("kexec failed. Falling back to normal reboot.");
325                         } else {
326                                 /* Child */
327                                 const char *args[3] = { "/sbin/kexec", "-e", NULL };
328                                 execv(args[0], (char * const *) args);
329                                 return EXIT_FAILURE;
330                         }
331                 }
332
333                 cmd = RB_AUTOBOOT;
334         }
335
336         reboot(cmd);
337
338         if (errno == EPERM && in_container) {
339                 /* If we are in a container, and we lacked
340                  * CAP_SYS_BOOT just exit, this will kill our
341                  * container for good. */
342                 log_error("Exiting container.");
343                 exit(0);
344         }
345
346         log_error("Failed to invoke reboot(): %m");
347         r = -errno;
348
349   error:
350         log_error("Critical error while doing system shutdown: %s", strerror(-r));
351
352         freeze();
353         return EXIT_FAILURE;
354 }