chiark / gitweb /
shutdown: add missing include
[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         /*
108           In case some evil process made "/" MS_SHARED
109           It works for pivot_root, but the ref count for the root device
110           is not decreasing :-/
111         */
112         if (mount(NULL, "/", NULL, MS_PRIVATE, NULL) < 0) {
113                 log_error("Failed to make \"/\" private mount %m");
114                 return -errno;
115         }
116
117         if (pivot_root(".", "oldroot") < 0) {
118                 log_error("pivot failed: %m");
119                 /* only chroot if pivot root succeded */
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         int cmd, r;
135         unsigned retries;
136         bool need_umount = true, need_swapoff = true, need_loop_detach = true, need_dm_detach = true;
137         bool in_container, use_watchdog = false;
138         char *arguments[3];
139
140         log_parse_environment();
141         log_set_target(LOG_TARGET_CONSOLE); /* syslog will die if not gone yet */
142         log_open();
143
144         umask(0022);
145
146         if (getpid() != 1) {
147                 log_error("Not executed by init (pid 1).");
148                 r = -EPERM;
149                 goto error;
150         }
151
152         if (argc != 2) {
153                 log_error("Invalid number of arguments.");
154                 r = -EINVAL;
155                 goto error;
156         }
157
158         in_container = detect_container(NULL) > 0;
159
160         if (streq(argv[1], "reboot"))
161                 cmd = RB_AUTOBOOT;
162         else if (streq(argv[1], "poweroff"))
163                 cmd = RB_POWER_OFF;
164         else if (streq(argv[1], "halt"))
165                 cmd = RB_HALT_SYSTEM;
166         else if (streq(argv[1], "kexec"))
167                 cmd = LINUX_REBOOT_CMD_KEXEC;
168         else {
169                 log_error("Unknown action '%s'.", argv[1]);
170                 r = -EINVAL;
171                 goto error;
172         }
173
174         use_watchdog = !!getenv("WATCHDOG_USEC");
175
176         /* lock us into memory */
177         mlockall(MCL_CURRENT|MCL_FUTURE);
178
179         log_info("Sending SIGTERM to remaining processes...");
180         broadcast_signal(SIGTERM);
181
182         log_info("Sending SIGKILL to remaining processes...");
183         broadcast_signal(SIGKILL);
184
185         if (in_container) {
186                 need_swapoff = false;
187                 need_dm_detach = false;
188                 need_loop_detach = false;
189         }
190
191         /* Unmount all mountpoints, swaps, and loopback devices */
192         for (retries = 0; retries < FINALIZE_ATTEMPTS; retries++) {
193                 bool changed = false;
194
195                 if (use_watchdog)
196                         watchdog_ping();
197
198                 if (need_umount) {
199                         log_info("Unmounting file systems.");
200                         r = umount_all(&changed);
201                         if (r == 0)
202                                 need_umount = false;
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                         else if (r > 0)
215                                 log_info("Not all swaps are turned off, %d left.", r);
216                         else
217                                 log_error("Failed to turn off swaps: %s", strerror(-r));
218                 }
219
220                 if (need_loop_detach) {
221                         log_info("Detaching loop devices.");
222                         r = loopback_detach_all(&changed);
223                         if (r == 0)
224                                 need_loop_detach = false;
225                         else if (r > 0)
226                                 log_info("Not all loop devices detached, %d left.", r);
227                         else
228                                 log_error("Failed to detach loop devices: %s", strerror(-r));
229                 }
230
231                 if (need_dm_detach) {
232                         log_info("Detaching DM devices.");
233                         r = dm_detach_all(&changed);
234                         if (r == 0)
235                                 need_dm_detach = false;
236                         else if (r > 0)
237                                 log_warning("Not all DM devices detached, %d left.", r);
238                         else
239                                 log_error("Failed to detach DM devices: %s", strerror(-r));
240                 }
241
242                 if (!need_umount && !need_swapoff && !need_loop_detach && !need_dm_detach) {
243                         if (retries > 0)
244                                 log_info("All filesystems, swaps, loop devices, DM devices detached.");
245                         /* Yay, done */
246                         break;
247                 }
248
249                 /* If in this iteration we didn't manage to
250                  * unmount/deactivate anything, we simply give up */
251                 if (!changed) {
252                         log_error("Cannot finalize remaining file systems and devices, giving up.");
253                         break;
254                 }
255
256                 log_debug("Couldn't finalize remaining file systems and devices after %u retries, trying again.", retries+1);
257         }
258
259         if (retries >= FINALIZE_ATTEMPTS)
260                 log_error("Too many iterations, giving up.");
261
262         arguments[0] = NULL;
263         arguments[1] = argv[1];
264         arguments[2] = NULL;
265         execute_directory(SYSTEM_SHUTDOWN_PATH, NULL, arguments);
266
267         /* If we are in a container, just exit, this will kill our
268          * container for good. */
269         if (in_container) {
270                 log_error("Exiting container.");
271                 exit(0);
272         }
273
274         if (access("/run/initramfs/shutdown", X_OK) == 0) {
275
276                 if (prepare_new_root() >= 0 &&
277                     pivot_to_new_root() >= 0) {
278                         execv("/shutdown", argv);
279                         log_error("Failed to execute shutdown binary: %m");
280                 }
281         }
282
283         sync();
284
285         if (cmd == LINUX_REBOOT_CMD_KEXEC) {
286                 /* We cheat and exec kexec to avoid doing all its work */
287                 pid_t pid = fork();
288
289                 if (pid < 0)
290                         log_error("Could not fork: %m. Falling back to normal reboot.");
291                 else if (pid > 0) {
292                         wait_for_terminate_and_warn("kexec", pid);
293                         log_warning("kexec failed. Falling back to normal reboot.");
294                 } else {
295                         /* Child */
296                         const char *args[3] = { "/sbin/kexec", "-e", NULL };
297                         execv(args[0], (char * const *) args);
298                         return EXIT_FAILURE;
299                 }
300
301                 cmd = RB_AUTOBOOT;
302         }
303
304         reboot(cmd);
305         log_error("Failed to invoke reboot(): %m");
306         r = -errno;
307
308   error:
309         log_error("Critical error while doing system shutdown: %s", strerror(-r));
310
311         freeze();
312         return EXIT_FAILURE;
313 }