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