chiark / gitweb /
udev: support ENV{}=="" global property matches
[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 #include <getopt.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 #include "cgroup-util.h"
50 #include "def.h"
51 #include "switch-root.h"
52
53 #define FINALIZE_ATTEMPTS 50
54
55 static char* arg_verb;
56
57 static int parse_argv(int argc, char *argv[]) {
58         enum {
59                 ARG_LOG_LEVEL = 0x100,
60                 ARG_LOG_TARGET,
61                 ARG_LOG_COLOR,
62                 ARG_LOG_LOCATION,
63         };
64
65         static const struct option options[] = {
66                 { "log-level",     required_argument, NULL, ARG_LOG_LEVEL    },
67                 { "log-target",    required_argument, NULL, ARG_LOG_TARGET   },
68                 { "log-color",     optional_argument, NULL, ARG_LOG_COLOR    },
69                 { "log-location",  optional_argument, NULL, ARG_LOG_LOCATION },
70                 {}
71         };
72
73         int c, r;
74
75         assert(argc >= 1);
76         assert(argv);
77
78         /* "-" prevents getopt from permuting argv[] and moving the verb away
79          * from argv[1]. Our interface to initrd promises it'll be there. */
80         while ((c = getopt_long(argc, argv, "-", options, NULL)) >= 0)
81                 switch (c) {
82
83                 case ARG_LOG_LEVEL:
84                         r = log_set_max_level_from_string(optarg);
85                         if (r < 0)
86                                 log_error("Failed to parse log level %s, ignoring.", optarg);
87
88                         break;
89
90                 case ARG_LOG_TARGET:
91                         r = log_set_target_from_string(optarg);
92                         if (r < 0)
93                                 log_error("Failed to parse log target %s, ignoring", optarg);
94
95                         break;
96
97                 case ARG_LOG_COLOR:
98
99                         if (optarg) {
100                                 r = log_show_color_from_string(optarg);
101                                 if (r < 0)
102                                         log_error("Failed to parse log color setting %s, ignoring", optarg);
103                         } else
104                                 log_show_color(true);
105
106                         break;
107
108                 case ARG_LOG_LOCATION:
109                         if (optarg) {
110                                 r = log_show_location_from_string(optarg);
111                                 if (r < 0)
112                                         log_error("Failed to parse log location setting %s, ignoring", optarg);
113                         } else
114                                 log_show_location(true);
115
116                         break;
117
118                 case '\001':
119                         if (!arg_verb)
120                                 arg_verb = optarg;
121                         else
122                                 log_error("Excess arguments, ignoring");
123                         break;
124
125                 case '?':
126                         return -EINVAL;
127
128                 default:
129                         assert_not_reached("Unhandled option code.");
130                 }
131
132         if (!arg_verb) {
133                 log_error("Verb argument missing.");
134                 return -EINVAL;
135         }
136
137         return 0;
138 }
139
140 static int switch_root_initramfs(void) {
141         if (mount("/run/initramfs", "/run/initramfs", NULL, MS_BIND, NULL) < 0) {
142                 log_error("Failed to mount bind /run/initramfs on /run/initramfs: %m");
143                 return -errno;
144         }
145
146         if (mount(NULL, "/run/initramfs", NULL, MS_PRIVATE, NULL) < 0) {
147                 log_error("Failed to make /run/initramfs private mount: %m");
148                 return -errno;
149         }
150
151         /* switch_root with MS_BIND, because there might still be processes lurking around, which have open file desriptors.
152          * /run/initramfs/shutdown will take care of these.
153          * Also do not detach the old root, because /run/initramfs/shutdown needs to access it.
154          */
155         return switch_root("/run/initramfs", "/oldroot", false, MS_BIND);
156 }
157
158
159 int main(int argc, char *argv[]) {
160         bool need_umount, need_swapoff, need_loop_detach, need_dm_detach;
161         bool in_container, use_watchdog = false;
162         _cleanup_free_ char *cgroup = NULL;
163         char *arguments[3];
164         unsigned retries;
165         int cmd, r;
166
167         log_parse_environment();
168         r = parse_argv(argc, argv);
169         if (r < 0)
170                 goto error;
171
172         /* journald will die if not gone yet. The log target defaults
173          * to console, but may have been changed by command line options. */
174
175         log_close_console(); /* force reopen of /dev/console */
176         log_open();
177
178         umask(0022);
179
180         if (getpid() != 1) {
181                 log_error("Not executed by init (PID 1).");
182                 r = -EPERM;
183                 goto error;
184         }
185
186         if (streq(arg_verb, "reboot"))
187                 cmd = RB_AUTOBOOT;
188         else if (streq(arg_verb, "poweroff"))
189                 cmd = RB_POWER_OFF;
190         else if (streq(arg_verb, "halt"))
191                 cmd = RB_HALT_SYSTEM;
192         else if (streq(arg_verb, "kexec"))
193                 cmd = LINUX_REBOOT_CMD_KEXEC;
194         else {
195                 r = -EINVAL;
196                 log_error("Unknown action '%s'.", arg_verb);
197                 goto error;
198         }
199
200         cg_get_root_path(&cgroup);
201
202         use_watchdog = !!getenv("WATCHDOG_USEC");
203
204         /* lock us into memory */
205         mlockall(MCL_CURRENT|MCL_FUTURE);
206
207         log_info("Sending SIGTERM to remaining processes...");
208         broadcast_signal(SIGTERM, true, true);
209
210         log_info("Sending SIGKILL to remaining processes...");
211         broadcast_signal(SIGKILL, true, false);
212
213         in_container = detect_container(NULL) > 0;
214
215         need_umount = !in_container;
216         need_swapoff = !in_container;
217         need_loop_detach = !in_container;
218         need_dm_detach = !in_container;
219
220         /* Unmount all mountpoints, swaps, and loopback devices */
221         for (retries = 0; retries < FINALIZE_ATTEMPTS; retries++) {
222                 bool changed = false;
223
224                 if (use_watchdog)
225                         watchdog_ping();
226
227                 /* Let's trim the cgroup tree on each iteration so
228                    that we leave an empty cgroup tree around, so that
229                    container managers get a nice notify event when we
230                    are down */
231                 if (cgroup)
232                         cg_trim(SYSTEMD_CGROUP_CONTROLLER, cgroup, false);
233
234                 if (need_umount) {
235                         log_info("Unmounting file systems.");
236                         r = umount_all(&changed);
237                         if (r == 0) {
238                                 need_umount = false;
239                                 log_info("All filesystems unmounted.");
240                         } else if (r > 0)
241                                 log_info("Not all file systems unmounted, %d left.", r);
242                         else
243                                 log_error("Failed to unmount file systems: %s", strerror(-r));
244                 }
245
246                 if (need_swapoff) {
247                         log_info("Deactivating swaps.");
248                         r = swapoff_all(&changed);
249                         if (r == 0) {
250                                 need_swapoff = false;
251                                 log_info("All swaps deactivated.");
252                         } else if (r > 0)
253                                 log_info("Not all swaps deactivated, %d left.", r);
254                         else
255                                 log_error("Failed to deactivate swaps: %s", strerror(-r));
256                 }
257
258                 if (need_loop_detach) {
259                         log_info("Detaching loop devices.");
260                         r = loopback_detach_all(&changed);
261                         if (r == 0) {
262                                 need_loop_detach = false;
263                                 log_info("All loop devices detached.");
264                         } else if (r > 0)
265                                 log_info("Not all loop devices detached, %d left.", r);
266                         else
267                                 log_error("Failed to detach loop devices: %s", strerror(-r));
268                 }
269
270                 if (need_dm_detach) {
271                         log_info("Detaching DM devices.");
272                         r = dm_detach_all(&changed);
273                         if (r == 0) {
274                                 need_dm_detach = false;
275                                 log_info("All DM devices detached.");
276                         } else if (r > 0)
277                                 log_info("Not all DM devices detached, %d left.", r);
278                         else
279                                 log_error("Failed to detach DM devices: %s", strerror(-r));
280                 }
281
282                 if (!need_umount && !need_swapoff && !need_loop_detach && !need_dm_detach) {
283                         if (retries > 0)
284                                 log_info("All filesystems, swaps, loop devices, DM devices detached.");
285                         /* Yay, done */
286                         goto initrd_jump;
287                 }
288
289                 /* If in this iteration we didn't manage to
290                  * unmount/deactivate anything, we simply give up */
291                 if (!changed) {
292                         log_info("Cannot finalize remaining%s%s%s%s continuing.",
293                                  need_umount ? " file systems," : "",
294                                  need_swapoff ? " swap devices," : "",
295                                  need_loop_detach ? " loop devices," : "",
296                                  need_dm_detach ? " DM devices," : "");
297                         goto initrd_jump;
298                 }
299
300                 log_debug("After %u retries, couldn't finalize remaining %s%s%s%s trying again.",
301                           retries + 1,
302                           need_umount ? " file systems," : "",
303                           need_swapoff ? " swap devices," : "",
304                           need_loop_detach ? " loop devices," : "",
305                           need_dm_detach ? " DM devices," : "");
306         }
307
308         log_error("Too many iterations, giving up.");
309
310  initrd_jump:
311
312         arguments[0] = NULL;
313         arguments[1] = arg_verb;
314         arguments[2] = NULL;
315         execute_directory(SYSTEM_SHUTDOWN_PATH, NULL, DEFAULT_TIMEOUT_USEC, arguments);
316
317         if (!in_container && !in_initrd() &&
318             access("/run/initramfs/shutdown", X_OK) == 0) {
319                 r = switch_root_initramfs();
320                 if (r >= 0) {
321                         argv[0] = (char*) "/shutdown";
322
323                         setsid();
324                         make_console_stdio();
325
326                         log_info("Successfully changed into root pivot.\n"
327                                  "Returning to initrd...");
328
329                         execv("/shutdown", argv);
330                         log_error("Failed to execute shutdown binary: %m");
331                 } else
332                         log_error("Failed to switch root to \"/run/initramfs\": %s", strerror(-r));
333
334         }
335
336         if (need_umount || need_swapoff || need_loop_detach || need_dm_detach)
337                 log_error("Failed to finalize %s%s%s%s ignoring",
338                           need_umount ? " file systems," : "",
339                           need_swapoff ? " swap devices," : "",
340                           need_loop_detach ? " loop devices," : "",
341                           need_dm_detach ? " DM devices," : "");
342
343         /* The kernel will automaticall flush ATA disks and suchlike
344          * on reboot(), but the file systems need to be synce'd
345          * explicitly in advance. So let's do this here, but not
346          * needlessly slow down containers. */
347         if (!in_container)
348                 sync();
349
350         switch (cmd) {
351
352         case LINUX_REBOOT_CMD_KEXEC:
353
354                 if (!in_container) {
355                         /* We cheat and exec kexec to avoid doing all its work */
356                         pid_t pid;
357
358                         log_info("Rebooting with kexec.");
359
360                         pid = fork();
361                         if (pid < 0)
362                                 log_error("Failed to fork: %m");
363                         else if (pid == 0) {
364
365                                 const char * const args[] = {
366                                         KEXEC, "-e", NULL
367                                 };
368
369                                 /* Child */
370
371                                 execv(args[0], (char * const *) args);
372                                 _exit(EXIT_FAILURE);
373                         } else
374                                 wait_for_terminate_and_warn("kexec", pid);
375                 }
376
377                 cmd = RB_AUTOBOOT;
378                 /* Fall through */
379
380         case RB_AUTOBOOT:
381
382                 if (!in_container) {
383                         _cleanup_free_ char *param = NULL;
384
385                         if (read_one_line_file(REBOOT_PARAM_FILE, &param) >= 0) {
386                                 log_info("Rebooting with argument '%s'.", param);
387                                 syscall(SYS_reboot, LINUX_REBOOT_MAGIC1, LINUX_REBOOT_MAGIC2, LINUX_REBOOT_CMD_RESTART2, param);
388                         }
389                 }
390
391                 log_info("Rebooting.");
392                 break;
393
394         case RB_POWER_OFF:
395                 log_info("Powering off.");
396                 break;
397
398         case RB_HALT_SYSTEM:
399                 log_info("Halting system.");
400                 break;
401
402         default:
403                 assert_not_reached("Unknown magic");
404         }
405
406         reboot(cmd);
407         if (errno == EPERM && in_container) {
408                 /* If we are in a container, and we lacked
409                  * CAP_SYS_BOOT just exit, this will kill our
410                  * container for good. */
411                 log_info("Exiting container.");
412                 exit(0);
413         }
414
415         log_error("Failed to invoke reboot(): %m");
416         r = -errno;
417
418   error:
419         log_error("Critical error while doing system shutdown: %s", strerror(-r));
420
421         freeze();
422 }