1 /*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
4 This file is part of systemd.
6 Copyright 2010 ProFUSION embedded systems
8 systemd is free software; you can redistribute it and/or modify it
9 under the terms of the GNU General Public License as published by
10 the Free Software Foundation; either version 2 of the License, or
11 (at your option) any later version.
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 General Public License for more details.
18 You should have received a copy of the GNU General Public License
19 along with systemd; If not, see <http://www.gnu.org/licenses/>.
23 #include <sys/types.h>
24 #include <sys/reboot.h>
25 #include <linux/reboot.h>
39 #define TIMEOUT_USEC (5 * USEC_PER_SEC)
40 #define FINALIZE_ATTEMPTS 50
41 #define FINALIZE_CRITICAL_ATTEMPTS 10
43 static bool ignore_proc(pid_t pid
) {
47 /* TODO: add more ignore rules here: device-mapper, etc */
52 static bool is_kernel_thread(pid_t pid
)
59 snprintf(buf
, sizeof(buf
), "/proc/%lu/cmdline", (unsigned long)pid
);
62 return true; /* not really, but has the desired effect */
64 count
= fread(&c
, 1, 1, f
);
69 static int killall(int sign
) {
72 unsigned int processes
= 0;
74 if ((dir
= opendir("/proc")) == NULL
)
77 while ((d
= readdir(dir
))) {
80 if (parse_pid(d
->d_name
, &pid
) < 0)
83 if (is_kernel_thread(pid
))
89 if (kill(pid
, sign
) == 0)
92 log_warning("Could not kill %d: %m", pid
);
100 static int send_signal(int sign
) {
101 sigset_t mask
, oldmask
;
106 assert_se(sigemptyset(&mask
) == 0);
107 assert_se(sigaddset(&mask
, SIGCHLD
) == 0);
108 if (sigprocmask(SIG_BLOCK
, &mask
, &oldmask
) != 0)
111 if (kill(-1, SIGSTOP
) < 0)
112 log_warning("Failed kill(-1, SIGSTOP): %m");
114 processes
= killall(sign
);
116 if (kill(-1, SIGCONT
) < 0)
117 log_warning("Failed kill(-1, SIGCONT): %m");
122 until
= now(CLOCK_MONOTONIC
) + TIMEOUT_USEC
;
124 usec_t n
= now(CLOCK_MONOTONIC
);
126 pid_t pid
= waitpid(-1, NULL
, WNOHANG
);
129 else if (pid
< 0 && errno
== ECHILD
) {
134 if (--processes
== 0)
141 timespec_store(&ts
, until
- n
);
142 if (sigtimedwait(&mask
, NULL
, &ts
) != SIGCHLD
)
143 log_warning("Failed: sigtimedwait did not return SIGCHLD: %m");
147 sigprocmask(SIG_SETMASK
, &oldmask
, NULL
);
152 static int rescue_send_signal(int sign
) {
153 sigset_t mask
, oldmask
;
159 sigaddset(&mask
, SIGCHLD
);
160 if (sigprocmask(SIG_BLOCK
, &mask
, &oldmask
) != 0)
163 if (kill(-1, SIGSTOP
) < 0)
164 log_warning("Failed kill(-1, SIGSTOP): %m");
168 log_warning("Failed kill(-1, %d): %m", sign
);
170 if (kill(-1, SIGCONT
) < 0)
171 log_warning("Failed kill(-1, SIGCONT): %m");
176 until
= now(CLOCK_MONOTONIC
) + TIMEOUT_USEC
;
178 usec_t n
= now(CLOCK_MONOTONIC
);
180 pid_t pid
= waitpid(-1, NULL
, WNOHANG
);
183 else if (pid
< 0 && errno
== ECHILD
)
190 timespec_store(&ts
, until
- n
);
191 if (sigtimedwait(&mask
, NULL
, &ts
) != SIGCHLD
)
192 log_warning("Failed: sigtimedwait did not return SIGCHLD: %m");
196 sigprocmask(SIG_SETMASK
, &oldmask
, NULL
);
201 int main(int argc
, char *argv
[]) {
203 bool need_umount
= true, need_swapoff
= true, need_loop_detach
= true, need_dm_detach
= true;
205 log_parse_environment();
206 log_set_target(LOG_TARGET_CONSOLE
); /* syslog will die if not gone yet */
210 log_error("Not executed by init (pid 1).");
216 log_error("Invalid number of arguments.");
221 if (streq(argv
[1], "reboot"))
223 else if (streq(argv
[1], "poweroff"))
225 else if (streq(argv
[1], "halt"))
226 cmd
= RB_HALT_SYSTEM
;
227 else if (streq(argv
[1], "kexec"))
228 cmd
= LINUX_REBOOT_CMD_KEXEC
;
230 log_error("Unknown action '%s'.", argv
[1]);
235 /* lock us into memory */
236 if (mlockall(MCL_CURRENT
|MCL_FUTURE
) != 0)
237 log_warning("Cannot lock process memory: %m");
239 log_info("Sending SIGTERM to processes");
240 r
= send_signal(SIGTERM
);
242 log_warning("Cannot send SIGTERM to all process: %s", strerror(r
));
244 log_info("Sending SIGKILL to processes");
245 r
= send_signal(SIGKILL
);
247 log_warning("Cannot send SIGKILL to all process: %s", strerror(r
));
249 /* Unmount all mountpoints, swaps, and loopback devices */
250 retries
= FINALIZE_ATTEMPTS
;
253 log_info("Unmounting filesystems.");
258 log_warning("Not all filesystems unmounted, %d left.", r
);
260 log_error("Error unmounting filesystems: %s", strerror(-r
));
264 log_info("Disabling swaps.");
267 need_swapoff
= false;
269 log_warning("Not all swaps are off, %d left.", r
);
271 log_error("Error turning off swaps: %s", strerror(-r
));
274 if (need_loop_detach
) {
275 log_info("Detaching loop devices.");
276 r
= loopback_detach_all();
278 need_loop_detach
= false;
280 log_warning("Not all loop devices detached, %d left.", r
);
282 log_error("Error detaching loop devices: %s", strerror(-r
));
285 if (need_dm_detach
) {
286 log_info("Detaching DM devices.");
289 need_dm_detach
= false;
291 log_warning("Not all dm devices detached, %d left.", r
);
293 log_error("Error detaching dm devices: %s", strerror(-r
));
296 if (need_umount
|| need_swapoff
|| need_loop_detach
|| need_dm_detach
) {
299 if (retries
== FINALIZE_CRITICAL_ATTEMPTS
) {
300 log_warning("Approaching critical level to finalize filesystem and devices, try to kill all processes.");
301 rescue_send_signal(SIGTERM
);
302 rescue_send_signal(SIGKILL
);
306 log_info("Action still required, %d tries left.", retries
);
308 log_error("Giving up. Actions left: Umount=%s, Swap off=%s, Loop detach=%s, dm detach=%s",
309 yes_no(need_umount
), yes_no(need_swapoff
), yes_no(need_loop_detach
), yes_no(need_dm_detach
));
318 if (cmd
== LINUX_REBOOT_CMD_KEXEC
) {
319 /* we cheat and exec kexec to avoid doing all its work */
322 log_error("Could not fork: %m. Falling back to normal reboot.");
324 wait_for_terminate_and_warn("kexec", pid
);
325 log_warning("kexec failed. Falling back to normal reboot.");
328 const char *args
[5] = { KEXEC_BINARY_PATH
, "-e", "-f", "-x", NULL
};
329 execv(args
[0], (char * const *) args
);
337 log_error("Failed to invoke reboot(): %m");
342 log_error("Critical error while doing system shutdown: %s", strerror(-r
));