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