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