2017-11-18 17:09:20 +01:00
|
|
|
/* SPDX-License-Identifier: LGPL-2.1+ */
|
2010-10-07 13:24:41 +02:00
|
|
|
/***
|
|
|
|
This file is part of systemd.
|
|
|
|
|
|
|
|
Copyright 2010 ProFUSION embedded systems
|
|
|
|
|
|
|
|
systemd is free software; you can redistribute it and/or modify it
|
2012-04-12 00:20:58 +02:00
|
|
|
under the terms of the GNU Lesser General Public License as published by
|
|
|
|
the Free Software Foundation; either version 2.1 of the License, or
|
2010-10-07 13:24:41 +02:00
|
|
|
(at your option) any later version.
|
|
|
|
|
|
|
|
systemd is distributed in the hope that it will be useful, but
|
|
|
|
WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
2012-04-12 00:20:58 +02:00
|
|
|
Lesser General Public License for more details.
|
2010-10-07 13:24:41 +02:00
|
|
|
|
2012-04-12 00:20:58 +02:00
|
|
|
You should have received a copy of the GNU Lesser General Public License
|
2010-10-07 13:24:41 +02:00
|
|
|
along with systemd; If not, see <http://www.gnu.org/licenses/>.
|
|
|
|
***/
|
|
|
|
|
|
|
|
#include <errno.h>
|
2015-10-24 22:58:24 +02:00
|
|
|
#include <getopt.h>
|
|
|
|
#include <linux/reboot.h>
|
2010-10-07 13:24:41 +02:00
|
|
|
#include <signal.h>
|
|
|
|
#include <stdbool.h>
|
|
|
|
#include <stdlib.h>
|
2015-10-24 22:58:24 +02:00
|
|
|
#include <sys/mman.h>
|
|
|
|
#include <sys/mount.h>
|
|
|
|
#include <sys/reboot.h>
|
|
|
|
#include <sys/stat.h>
|
|
|
|
#include <unistd.h>
|
2010-10-07 13:24:41 +02:00
|
|
|
|
2015-10-27 03:01:06 +01:00
|
|
|
#include "alloc-util.h"
|
2017-12-22 13:19:56 +01:00
|
|
|
#include "async.h"
|
2015-10-24 22:58:24 +02:00
|
|
|
#include "cgroup-util.h"
|
|
|
|
#include "def.h"
|
2017-01-22 18:35:08 +01:00
|
|
|
#include "exec-util.h"
|
2017-12-22 13:19:56 +01:00
|
|
|
#include "fd-util.h"
|
2013-07-05 00:32:05 +02:00
|
|
|
#include "fileio.h"
|
2015-10-24 22:58:24 +02:00
|
|
|
#include "killall.h"
|
|
|
|
#include "log.h"
|
|
|
|
#include "missing.h"
|
2015-10-26 16:18:16 +01:00
|
|
|
#include "parse-util.h"
|
2015-10-24 22:58:24 +02:00
|
|
|
#include "process-util.h"
|
2017-12-14 17:46:03 +01:00
|
|
|
#include "signal-util.h"
|
2015-10-24 22:58:24 +02:00
|
|
|
#include "string-util.h"
|
|
|
|
#include "switch-root.h"
|
|
|
|
#include "terminal-util.h"
|
2010-10-07 13:24:41 +02:00
|
|
|
#include "umount.h"
|
|
|
|
#include "util.h"
|
2011-09-23 17:00:33 +02:00
|
|
|
#include "virt.h"
|
2012-04-05 22:08:10 +02:00
|
|
|
#include "watchdog.h"
|
2010-10-07 13:24:41 +02:00
|
|
|
|
|
|
|
#define FINALIZE_ATTEMPTS 50
|
|
|
|
|
2017-12-14 17:46:03 +01:00
|
|
|
#define SYNC_PROGRESS_ATTEMPTS 3
|
|
|
|
#define SYNC_TIMEOUT_USEC (10*USEC_PER_SEC)
|
|
|
|
|
2014-02-16 00:13:46 +01:00
|
|
|
static char* arg_verb;
|
2015-09-18 13:37:34 +02:00
|
|
|
static uint8_t arg_exit_code;
|
2014-02-16 00:13:46 +01:00
|
|
|
|
|
|
|
static int parse_argv(int argc, char *argv[]) {
|
|
|
|
enum {
|
|
|
|
ARG_LOG_LEVEL = 0x100,
|
|
|
|
ARG_LOG_TARGET,
|
|
|
|
ARG_LOG_COLOR,
|
|
|
|
ARG_LOG_LOCATION,
|
2015-09-18 13:37:34 +02:00
|
|
|
ARG_EXIT_CODE,
|
2014-02-16 00:13:46 +01:00
|
|
|
};
|
|
|
|
|
|
|
|
static const struct option options[] = {
|
|
|
|
{ "log-level", required_argument, NULL, ARG_LOG_LEVEL },
|
|
|
|
{ "log-target", required_argument, NULL, ARG_LOG_TARGET },
|
|
|
|
{ "log-color", optional_argument, NULL, ARG_LOG_COLOR },
|
|
|
|
{ "log-location", optional_argument, NULL, ARG_LOG_LOCATION },
|
2015-09-18 13:37:34 +02:00
|
|
|
{ "exit-code", required_argument, NULL, ARG_EXIT_CODE },
|
2014-02-16 00:13:46 +01:00
|
|
|
{}
|
|
|
|
};
|
|
|
|
|
|
|
|
int c, r;
|
|
|
|
|
|
|
|
assert(argc >= 1);
|
|
|
|
assert(argv);
|
|
|
|
|
2014-11-06 22:24:13 +01:00
|
|
|
/* "-" prevents getopt from permuting argv[] and moving the verb away
|
|
|
|
* from argv[1]. Our interface to initrd promises it'll be there. */
|
|
|
|
while ((c = getopt_long(argc, argv, "-", options, NULL)) >= 0)
|
2014-02-16 00:13:46 +01:00
|
|
|
switch (c) {
|
|
|
|
|
|
|
|
case ARG_LOG_LEVEL:
|
|
|
|
r = log_set_max_level_from_string(optarg);
|
|
|
|
if (r < 0)
|
|
|
|
log_error("Failed to parse log level %s, ignoring.", optarg);
|
|
|
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
case ARG_LOG_TARGET:
|
|
|
|
r = log_set_target_from_string(optarg);
|
|
|
|
if (r < 0)
|
|
|
|
log_error("Failed to parse log target %s, ignoring", optarg);
|
|
|
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
case ARG_LOG_COLOR:
|
|
|
|
|
|
|
|
if (optarg) {
|
|
|
|
r = log_show_color_from_string(optarg);
|
|
|
|
if (r < 0)
|
|
|
|
log_error("Failed to parse log color setting %s, ignoring", optarg);
|
|
|
|
} else
|
|
|
|
log_show_color(true);
|
|
|
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
case ARG_LOG_LOCATION:
|
|
|
|
if (optarg) {
|
|
|
|
r = log_show_location_from_string(optarg);
|
|
|
|
if (r < 0)
|
|
|
|
log_error("Failed to parse log location setting %s, ignoring", optarg);
|
|
|
|
} else
|
|
|
|
log_show_location(true);
|
|
|
|
|
|
|
|
break;
|
|
|
|
|
2015-09-18 13:37:34 +02:00
|
|
|
case ARG_EXIT_CODE:
|
|
|
|
r = safe_atou8(optarg, &arg_exit_code);
|
|
|
|
if (r < 0)
|
|
|
|
log_error("Failed to parse exit code %s, ignoring", optarg);
|
|
|
|
|
|
|
|
break;
|
|
|
|
|
2014-11-06 22:24:13 +01:00
|
|
|
case '\001':
|
|
|
|
if (!arg_verb)
|
|
|
|
arg_verb = optarg;
|
|
|
|
else
|
|
|
|
log_error("Excess arguments, ignoring");
|
|
|
|
break;
|
|
|
|
|
2014-02-16 00:13:46 +01:00
|
|
|
case '?':
|
|
|
|
return -EINVAL;
|
|
|
|
|
|
|
|
default:
|
|
|
|
assert_not_reached("Unhandled option code.");
|
|
|
|
}
|
|
|
|
|
2014-11-06 22:24:13 +01:00
|
|
|
if (!arg_verb) {
|
2014-02-16 00:13:46 +01:00
|
|
|
log_error("Verb argument missing.");
|
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2014-08-21 16:21:26 +02:00
|
|
|
static int switch_root_initramfs(void) {
|
2014-11-28 19:57:32 +01:00
|
|
|
if (mount("/run/initramfs", "/run/initramfs", NULL, MS_BIND, NULL) < 0)
|
|
|
|
return log_error_errno(errno, "Failed to mount bind /run/initramfs on /run/initramfs: %m");
|
2011-07-11 22:56:45 +02:00
|
|
|
|
2014-11-28 19:57:32 +01:00
|
|
|
if (mount(NULL, "/run/initramfs", NULL, MS_PRIVATE, NULL) < 0)
|
|
|
|
return log_error_errno(errno, "Failed to make /run/initramfs private mount: %m");
|
2011-07-11 22:56:45 +02:00
|
|
|
|
2014-12-29 10:45:58 +01:00
|
|
|
/* switch_root with MS_BIND, because there might still be processes lurking around, which have open file descriptors.
|
2014-08-21 16:21:26 +02:00
|
|
|
* /run/initramfs/shutdown will take care of these.
|
|
|
|
* Also do not detach the old root, because /run/initramfs/shutdown needs to access it.
|
|
|
|
*/
|
|
|
|
return switch_root("/run/initramfs", "/oldroot", false, MS_BIND);
|
2011-05-05 12:29:44 +02:00
|
|
|
}
|
|
|
|
|
2017-12-14 17:46:03 +01:00
|
|
|
/* Read the following fields from /proc/meminfo:
|
|
|
|
*
|
|
|
|
* NFS_Unstable
|
|
|
|
* Writeback
|
|
|
|
* Dirty
|
|
|
|
*
|
|
|
|
* Return true if the sum of these fields is greater than the previous
|
|
|
|
* value input. For all other issues, report the failure and indicate that
|
|
|
|
* the sync is not making progress.
|
|
|
|
*/
|
|
|
|
static bool sync_making_progress(unsigned long long *prev_dirty) {
|
|
|
|
_cleanup_fclose_ FILE *f = NULL;
|
|
|
|
char line[LINE_MAX];
|
|
|
|
bool r = false;
|
|
|
|
unsigned long long val = 0;
|
|
|
|
|
|
|
|
f = fopen("/proc/meminfo", "re");
|
|
|
|
if (!f)
|
|
|
|
return log_warning_errno(errno, "Failed to open /proc/meminfo: %m");
|
|
|
|
|
|
|
|
FOREACH_LINE(line, f, log_warning_errno(errno, "Failed to parse /proc/meminfo: %m")) {
|
|
|
|
unsigned long long ull = 0;
|
|
|
|
|
|
|
|
if (!first_word(line, "NFS_Unstable:") && !first_word(line, "Writeback:") && !first_word(line, "Dirty:"))
|
|
|
|
continue;
|
|
|
|
|
|
|
|
errno = 0;
|
|
|
|
if (sscanf(line, "%*s %llu %*s", &ull) != 1) {
|
|
|
|
if (errno != 0)
|
|
|
|
log_warning_errno(errno, "Failed to parse /proc/meminfo: %m");
|
|
|
|
else
|
|
|
|
log_warning("Failed to parse /proc/meminfo");
|
|
|
|
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
val += ull;
|
|
|
|
}
|
|
|
|
|
|
|
|
r = *prev_dirty > val;
|
|
|
|
|
|
|
|
*prev_dirty = val;
|
|
|
|
|
|
|
|
return r;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void sync_with_progress(void) {
|
2017-12-22 13:19:56 +01:00
|
|
|
unsigned long long dirty = ULONG_LONG_MAX;
|
2017-12-14 17:46:03 +01:00
|
|
|
unsigned checks;
|
|
|
|
pid_t pid;
|
|
|
|
int r;
|
|
|
|
|
|
|
|
BLOCK_SIGNALS(SIGCHLD);
|
|
|
|
|
2017-12-22 13:19:56 +01:00
|
|
|
/* Due to the possiblity of the sync operation hanging, we fork a child process and monitor the progress. If
|
|
|
|
* the timeout lapses, the assumption is that that particular sync stalled. */
|
|
|
|
|
|
|
|
r = asynchronous_sync(&pid);
|
2017-12-22 13:08:14 +01:00
|
|
|
if (r < 0) {
|
2017-12-22 13:19:56 +01:00
|
|
|
log_error_errno(r, "Failed to fork sync(): %m");
|
2017-12-14 17:46:03 +01:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
log_info("Syncing filesystems and block devices.");
|
|
|
|
|
|
|
|
/* Start monitoring the sync operation. If more than
|
|
|
|
* SYNC_PROGRESS_ATTEMPTS lapse without progress being made,
|
|
|
|
* we assume that the sync is stalled */
|
|
|
|
for (checks = 0; checks < SYNC_PROGRESS_ATTEMPTS; checks++) {
|
|
|
|
r = wait_for_terminate_with_timeout(pid, SYNC_TIMEOUT_USEC);
|
|
|
|
if (r == 0)
|
|
|
|
/* Sync finished without error.
|
|
|
|
* (The sync itself does not return an error code) */
|
|
|
|
return;
|
|
|
|
else if (r == -ETIMEDOUT) {
|
|
|
|
/* Reset the check counter if the "Dirty" value is
|
|
|
|
* decreasing */
|
|
|
|
if (sync_making_progress(&dirty))
|
|
|
|
checks = 0;
|
|
|
|
} else {
|
|
|
|
log_error_errno(r, "Failed to sync filesystems and block devices: %m");
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Only reached in the event of a timeout. We should issue a kill
|
|
|
|
* to the stray process. */
|
|
|
|
log_error("Syncing filesystems and block devices - timed out, issuing SIGKILL to PID "PID_FMT".", pid);
|
|
|
|
(void) kill(pid, SIGKILL);
|
|
|
|
}
|
|
|
|
|
2010-10-07 13:24:41 +02:00
|
|
|
int main(int argc, char *argv[]) {
|
2014-06-26 07:41:04 +02:00
|
|
|
bool need_umount, need_swapoff, need_loop_detach, need_dm_detach;
|
2012-07-13 14:41:57 +02:00
|
|
|
bool in_container, use_watchdog = false;
|
2014-02-18 04:25:37 +01:00
|
|
|
_cleanup_free_ char *cgroup = NULL;
|
2012-05-05 02:06:58 +02:00
|
|
|
char *arguments[3];
|
2013-11-05 22:17:03 +01:00
|
|
|
unsigned retries;
|
|
|
|
int cmd, r;
|
Implement masking and overriding of generators
Sometimes it is necessary to stop a generator from running. Either
because of a bug, or for testing, or some other reason. The only way
to do that would be to rename or chmod the generator binary, which is
inconvenient and does not survive upgrades. Allow masking and
overriding generators similarly to units and other configuration
files.
For the systemd instance, masking would be more common, rather than
overriding generators. For the user instances, it may also be useful
for users to have generators in $XDG_CONFIG_HOME to augment or
override system-wide generators.
Directories are searched according to the usual scheme (/usr/lib,
/usr/local/lib, /run, /etc), and files with the same name in higher
priority directories override files with the same name in lower
priority directories. Empty files and links to /dev/null mask a given
name.
https://bugs.freedesktop.org/show_bug.cgi?id=87230
2015-01-09 02:47:25 +01:00
|
|
|
static const char* const dirs[] = {SYSTEM_SHUTDOWN_PATH, NULL};
|
2017-12-08 18:26:44 +01:00
|
|
|
char *watchdog_device;
|
2010-10-07 13:24:41 +02:00
|
|
|
|
2014-02-16 00:13:46 +01:00
|
|
|
log_parse_environment();
|
|
|
|
r = parse_argv(argc, argv);
|
|
|
|
if (r < 0)
|
|
|
|
goto error;
|
2013-07-05 00:32:05 +02:00
|
|
|
|
2014-02-16 00:13:46 +01:00
|
|
|
/* journald will die if not gone yet. The log target defaults
|
2014-11-06 15:33:48 +01:00
|
|
|
* to console, but may have been changed by command line options. */
|
2013-07-05 00:32:05 +02:00
|
|
|
|
2013-12-18 05:07:34 +01:00
|
|
|
log_close_console(); /* force reopen of /dev/console */
|
2010-10-07 13:24:41 +02:00
|
|
|
log_open();
|
|
|
|
|
2011-08-01 20:52:18 +02:00
|
|
|
umask(0022);
|
|
|
|
|
2017-07-20 16:19:18 +02:00
|
|
|
if (getpid_cached() != 1) {
|
2014-02-16 00:13:46 +01:00
|
|
|
log_error("Not executed by init (PID 1).");
|
2010-10-07 13:24:41 +02:00
|
|
|
r = -EPERM;
|
|
|
|
goto error;
|
|
|
|
}
|
|
|
|
|
2014-02-16 00:13:46 +01:00
|
|
|
if (streq(arg_verb, "reboot"))
|
2010-10-07 13:24:41 +02:00
|
|
|
cmd = RB_AUTOBOOT;
|
2014-02-16 00:13:46 +01:00
|
|
|
else if (streq(arg_verb, "poweroff"))
|
2010-10-07 13:24:41 +02:00
|
|
|
cmd = RB_POWER_OFF;
|
2014-02-16 00:13:46 +01:00
|
|
|
else if (streq(arg_verb, "halt"))
|
2010-10-07 13:24:41 +02:00
|
|
|
cmd = RB_HALT_SYSTEM;
|
2014-02-16 00:13:46 +01:00
|
|
|
else if (streq(arg_verb, "kexec"))
|
2010-10-07 13:24:41 +02:00
|
|
|
cmd = LINUX_REBOOT_CMD_KEXEC;
|
2015-09-18 13:37:34 +02:00
|
|
|
else if (streq(arg_verb, "exit"))
|
|
|
|
cmd = 0; /* ignored, just checking that arg_verb is valid */
|
2010-10-07 13:24:41 +02:00
|
|
|
else {
|
|
|
|
r = -EINVAL;
|
2014-02-16 00:13:46 +01:00
|
|
|
log_error("Unknown action '%s'.", arg_verb);
|
2010-10-07 13:24:41 +02:00
|
|
|
goto error;
|
|
|
|
}
|
|
|
|
|
2016-04-09 03:09:09 +02:00
|
|
|
(void) cg_get_root_path(&cgroup);
|
2016-07-12 17:26:52 +02:00
|
|
|
in_container = detect_container() > 0;
|
2013-11-05 22:17:03 +01:00
|
|
|
|
2012-04-05 22:08:10 +02:00
|
|
|
use_watchdog = !!getenv("WATCHDOG_USEC");
|
2017-12-08 18:26:44 +01:00
|
|
|
watchdog_device = getenv("WATCHDOG_DEVICE");
|
|
|
|
if (watchdog_device) {
|
|
|
|
r = watchdog_set_device(watchdog_device);
|
|
|
|
if (r < 0)
|
|
|
|
log_warning_errno(r, "Failed to set watchdog device to %s, ignoring: %m",
|
|
|
|
watchdog_device);
|
|
|
|
}
|
2012-04-05 22:08:10 +02:00
|
|
|
|
2016-07-12 17:26:52 +02:00
|
|
|
/* Lock us into memory */
|
2012-04-24 16:56:06 +02:00
|
|
|
mlockall(MCL_CURRENT|MCL_FUTURE);
|
2010-10-07 13:24:41 +02:00
|
|
|
|
2016-07-12 17:26:52 +02:00
|
|
|
/* Synchronize everything that is not written to disk yet at this point already. This is a good idea so that
|
|
|
|
* slow IO is processed here already and the final process killing spree is not impacted by processes
|
2017-12-14 17:46:03 +01:00
|
|
|
* desperately trying to sync IO to disk within their timeout. Do not remove this sync, data corruption will
|
|
|
|
* result. */
|
2016-07-12 17:26:52 +02:00
|
|
|
if (!in_container)
|
2017-12-14 17:46:03 +01:00
|
|
|
sync_with_progress();
|
2016-07-12 17:26:52 +02:00
|
|
|
|
2010-10-28 22:11:26 +02:00
|
|
|
log_info("Sending SIGTERM to remaining processes...");
|
2013-11-25 18:08:02 +01:00
|
|
|
broadcast_signal(SIGTERM, true, true);
|
2010-10-07 13:24:41 +02:00
|
|
|
|
2010-10-28 22:11:26 +02:00
|
|
|
log_info("Sending SIGKILL to remaining processes...");
|
2013-11-25 18:08:02 +01:00
|
|
|
broadcast_signal(SIGKILL, true, false);
|
2011-03-14 05:40:15 +01:00
|
|
|
|
2014-10-21 18:38:42 +02:00
|
|
|
need_umount = !in_container;
|
2014-06-26 07:41:04 +02:00
|
|
|
need_swapoff = !in_container;
|
|
|
|
need_loop_detach = !in_container;
|
|
|
|
need_dm_detach = !in_container;
|
2010-10-07 13:24:41 +02:00
|
|
|
|
2010-10-14 00:49:22 +02:00
|
|
|
/* Unmount all mountpoints, swaps, and loopback devices */
|
2010-10-14 18:55:04 +02:00
|
|
|
for (retries = 0; retries < FINALIZE_ATTEMPTS; retries++) {
|
|
|
|
bool changed = false;
|
|
|
|
|
2012-04-05 22:08:10 +02:00
|
|
|
if (use_watchdog)
|
|
|
|
watchdog_ping();
|
|
|
|
|
2013-11-05 22:17:03 +01:00
|
|
|
/* Let's trim the cgroup tree on each iteration so
|
|
|
|
that we leave an empty cgroup tree around, so that
|
|
|
|
container managers get a nice notify event when we
|
|
|
|
are down */
|
|
|
|
if (cgroup)
|
|
|
|
cg_trim(SYSTEMD_CGROUP_CONTROLLER, cgroup, false);
|
|
|
|
|
2010-10-07 13:24:41 +02:00
|
|
|
if (need_umount) {
|
2010-10-28 22:11:26 +02:00
|
|
|
log_info("Unmounting file systems.");
|
2010-10-14 18:55:04 +02:00
|
|
|
r = umount_all(&changed);
|
2012-12-07 17:44:50 +01:00
|
|
|
if (r == 0) {
|
2010-10-07 13:24:41 +02:00
|
|
|
need_umount = false;
|
2012-12-07 17:44:50 +01:00
|
|
|
log_info("All filesystems unmounted.");
|
|
|
|
} else if (r > 0)
|
2010-10-28 22:11:26 +02:00
|
|
|
log_info("Not all file systems unmounted, %d left.", r);
|
2010-10-07 13:24:41 +02:00
|
|
|
else
|
2014-11-28 13:19:16 +01:00
|
|
|
log_error_errno(r, "Failed to unmount file systems: %m");
|
2010-10-07 13:24:41 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
if (need_swapoff) {
|
2012-12-07 18:02:43 +01:00
|
|
|
log_info("Deactivating swaps.");
|
2010-10-14 18:55:04 +02:00
|
|
|
r = swapoff_all(&changed);
|
2012-12-07 17:44:50 +01:00
|
|
|
if (r == 0) {
|
2010-10-07 13:24:41 +02:00
|
|
|
need_swapoff = false;
|
2012-12-07 18:02:43 +01:00
|
|
|
log_info("All swaps deactivated.");
|
2012-12-07 17:44:50 +01:00
|
|
|
} else if (r > 0)
|
2012-12-07 18:02:43 +01:00
|
|
|
log_info("Not all swaps deactivated, %d left.", r);
|
2010-10-07 13:24:41 +02:00
|
|
|
else
|
2014-11-28 13:19:16 +01:00
|
|
|
log_error_errno(r, "Failed to deactivate swaps: %m");
|
2010-10-07 13:24:41 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
if (need_loop_detach) {
|
|
|
|
log_info("Detaching loop devices.");
|
2010-10-14 18:55:04 +02:00
|
|
|
r = loopback_detach_all(&changed);
|
2012-12-07 17:44:50 +01:00
|
|
|
if (r == 0) {
|
2010-10-07 13:24:41 +02:00
|
|
|
need_loop_detach = false;
|
2012-12-07 17:44:50 +01:00
|
|
|
log_info("All loop devices detached.");
|
|
|
|
} else if (r > 0)
|
2010-10-28 22:11:26 +02:00
|
|
|
log_info("Not all loop devices detached, %d left.", r);
|
2010-10-07 13:24:41 +02:00
|
|
|
else
|
2014-11-28 13:19:16 +01:00
|
|
|
log_error_errno(r, "Failed to detach loop devices: %m");
|
2010-10-14 02:33:09 +02:00
|
|
|
}
|
2010-10-07 13:24:41 +02:00
|
|
|
|
2010-10-14 02:33:09 +02:00
|
|
|
if (need_dm_detach) {
|
|
|
|
log_info("Detaching DM devices.");
|
2010-10-14 18:55:04 +02:00
|
|
|
r = dm_detach_all(&changed);
|
2012-12-07 17:44:50 +01:00
|
|
|
if (r == 0) {
|
2010-10-14 02:33:09 +02:00
|
|
|
need_dm_detach = false;
|
2012-12-07 17:44:50 +01:00
|
|
|
log_info("All DM devices detached.");
|
|
|
|
} else if (r > 0)
|
2012-12-07 17:28:30 +01:00
|
|
|
log_info("Not all DM devices detached, %d left.", r);
|
2010-10-14 02:33:09 +02:00
|
|
|
else
|
2014-11-28 13:19:16 +01:00
|
|
|
log_error_errno(r, "Failed to detach DM devices: %m");
|
2010-10-07 13:24:41 +02:00
|
|
|
}
|
|
|
|
|
2011-07-30 20:24:40 +02:00
|
|
|
if (!need_umount && !need_swapoff && !need_loop_detach && !need_dm_detach) {
|
|
|
|
if (retries > 0)
|
|
|
|
log_info("All filesystems, swaps, loop devices, DM devices detached.");
|
2010-10-14 18:55:04 +02:00
|
|
|
/* Yay, done */
|
2014-06-26 07:41:04 +02:00
|
|
|
goto initrd_jump;
|
2011-07-30 20:24:40 +02:00
|
|
|
}
|
2010-10-07 13:24:41 +02:00
|
|
|
|
2010-10-14 18:55:04 +02:00
|
|
|
/* If in this iteration we didn't manage to
|
2012-07-13 14:41:57 +02:00
|
|
|
* unmount/deactivate anything, we simply give up */
|
2010-10-14 18:55:04 +02:00
|
|
|
if (!changed) {
|
2014-06-26 07:41:04 +02:00
|
|
|
log_info("Cannot finalize remaining%s%s%s%s continuing.",
|
|
|
|
need_umount ? " file systems," : "",
|
|
|
|
need_swapoff ? " swap devices," : "",
|
|
|
|
need_loop_detach ? " loop devices," : "",
|
|
|
|
need_dm_detach ? " DM devices," : "");
|
|
|
|
goto initrd_jump;
|
2010-10-14 18:55:04 +02:00
|
|
|
}
|
|
|
|
|
2014-06-26 07:41:04 +02:00
|
|
|
log_debug("After %u retries, couldn't finalize remaining %s%s%s%s trying again.",
|
|
|
|
retries + 1,
|
|
|
|
need_umount ? " file systems," : "",
|
|
|
|
need_swapoff ? " swap devices," : "",
|
|
|
|
need_loop_detach ? " loop devices," : "",
|
|
|
|
need_dm_detach ? " DM devices," : "");
|
2010-10-07 13:24:41 +02:00
|
|
|
}
|
|
|
|
|
2014-06-26 07:41:04 +02:00
|
|
|
log_error("Too many iterations, giving up.");
|
|
|
|
|
|
|
|
initrd_jump:
|
2010-10-14 18:55:04 +02:00
|
|
|
|
2017-12-08 18:26:44 +01:00
|
|
|
/* We're done with the watchdog. */
|
|
|
|
watchdog_free_device();
|
|
|
|
|
2012-05-05 02:06:58 +02:00
|
|
|
arguments[0] = NULL;
|
2014-02-16 00:13:46 +01:00
|
|
|
arguments[1] = arg_verb;
|
2012-05-05 02:06:58 +02:00
|
|
|
arguments[2] = NULL;
|
2017-01-22 21:22:37 +01:00
|
|
|
execute_directories(dirs, DEFAULT_TIMEOUT_USEC, NULL, NULL, arguments);
|
2011-02-15 00:30:11 +01:00
|
|
|
|
2012-11-28 17:27:17 +01:00
|
|
|
if (!in_container && !in_initrd() &&
|
2012-09-06 00:37:18 +02:00
|
|
|
access("/run/initramfs/shutdown", X_OK) == 0) {
|
2014-08-21 16:21:26 +02:00
|
|
|
r = switch_root_initramfs();
|
|
|
|
if (r >= 0) {
|
2014-08-29 17:51:45 +02:00
|
|
|
argv[0] = (char*) "/shutdown";
|
2013-04-09 18:29:24 +02:00
|
|
|
|
2014-08-21 16:21:26 +02:00
|
|
|
setsid();
|
|
|
|
make_console_stdio();
|
|
|
|
|
|
|
|
log_info("Successfully changed into root pivot.\n"
|
|
|
|
"Returning to initrd...");
|
2013-04-09 18:29:24 +02:00
|
|
|
|
2014-08-29 17:51:45 +02:00
|
|
|
execv("/shutdown", argv);
|
2014-11-28 19:29:59 +01:00
|
|
|
log_error_errno(errno, "Failed to execute shutdown binary: %m");
|
2014-08-21 16:21:26 +02:00
|
|
|
} else
|
2014-11-28 13:19:16 +01:00
|
|
|
log_error_errno(r, "Failed to switch root to \"/run/initramfs\": %m");
|
2014-08-21 16:21:26 +02:00
|
|
|
|
2011-05-05 12:29:44 +02:00
|
|
|
}
|
|
|
|
|
2014-06-26 07:41:04 +02:00
|
|
|
if (need_umount || need_swapoff || need_loop_detach || need_dm_detach)
|
|
|
|
log_error("Failed to finalize %s%s%s%s ignoring",
|
|
|
|
need_umount ? " file systems," : "",
|
|
|
|
need_swapoff ? " swap devices," : "",
|
|
|
|
need_loop_detach ? " loop devices," : "",
|
|
|
|
need_dm_detach ? " DM devices," : "");
|
|
|
|
|
2016-07-12 17:26:52 +02:00
|
|
|
/* The kernel will automatically flush ATA disks and suchlike on reboot(), but the file systems need to be
|
|
|
|
* sync'ed explicitly in advance. So let's do this here, but not needlessly slow down containers. Note that we
|
|
|
|
* sync'ed things already once above, but we did some more work since then which might have caused IO, hence
|
2017-12-14 17:46:03 +01:00
|
|
|
* let's do it once more. Do not remove this sync, data corruption will result. */
|
2012-11-16 01:30:29 +01:00
|
|
|
if (!in_container)
|
2017-12-14 17:46:03 +01:00
|
|
|
sync_with_progress();
|
2012-11-16 01:30:29 +01:00
|
|
|
|
2015-09-18 13:37:34 +02:00
|
|
|
if (streq(arg_verb, "exit")) {
|
|
|
|
if (in_container)
|
|
|
|
exit(arg_exit_code);
|
|
|
|
else {
|
|
|
|
/* We cannot exit() on the host, fallback on another
|
|
|
|
* method. */
|
|
|
|
cmd = RB_POWER_OFF;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2013-11-08 19:32:45 +01:00
|
|
|
switch (cmd) {
|
|
|
|
|
|
|
|
case LINUX_REBOOT_CMD_KEXEC:
|
2012-09-06 00:37:18 +02:00
|
|
|
|
|
|
|
if (!in_container) {
|
|
|
|
/* We cheat and exec kexec to avoid doing all its work */
|
2013-11-08 19:32:45 +01:00
|
|
|
pid_t pid;
|
|
|
|
|
|
|
|
log_info("Rebooting with kexec.");
|
2012-09-06 00:37:18 +02:00
|
|
|
|
2017-12-22 13:08:14 +01:00
|
|
|
r = safe_fork("(sd-kexec)", FORK_RESET_SIGNALS|FORK_CLOSE_ALL_FDS, &pid);
|
|
|
|
if (r < 0)
|
|
|
|
log_error_errno(r, "Failed to fork: %m");
|
|
|
|
if (r == 0) {
|
2013-11-08 19:32:45 +01:00
|
|
|
|
|
|
|
const char * const args[] = {
|
|
|
|
KEXEC, "-e", NULL
|
|
|
|
};
|
|
|
|
|
2012-09-06 00:37:18 +02:00
|
|
|
/* Child */
|
2013-11-08 19:32:45 +01:00
|
|
|
|
2012-09-06 00:37:18 +02:00
|
|
|
execv(args[0], (char * const *) args);
|
2013-11-08 19:32:45 +01:00
|
|
|
_exit(EXIT_FAILURE);
|
2017-12-22 13:08:14 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
(void) wait_for_terminate_and_warn("kexec", pid, true);
|
2010-10-07 13:24:41 +02:00
|
|
|
}
|
2010-10-14 00:50:11 +02:00
|
|
|
|
|
|
|
cmd = RB_AUTOBOOT;
|
2017-11-19 19:06:10 +01:00
|
|
|
_fallthrough_;
|
2013-11-08 19:32:45 +01:00
|
|
|
case RB_AUTOBOOT:
|
|
|
|
|
|
|
|
if (!in_container) {
|
|
|
|
_cleanup_free_ char *param = NULL;
|
|
|
|
|
2016-04-07 16:53:37 +02:00
|
|
|
r = read_one_line_file("/run/systemd/reboot-param", ¶m);
|
2017-05-31 05:14:31 +02:00
|
|
|
if (r < 0 && r != -ENOENT)
|
2016-04-07 16:53:37 +02:00
|
|
|
log_warning_errno(r, "Failed to read reboot parameter file: %m");
|
|
|
|
|
|
|
|
if (!isempty(param)) {
|
2013-11-08 19:32:45 +01:00
|
|
|
log_info("Rebooting with argument '%s'.", param);
|
2014-08-22 16:59:46 +02:00
|
|
|
syscall(SYS_reboot, LINUX_REBOOT_MAGIC1, LINUX_REBOOT_MAGIC2, LINUX_REBOOT_CMD_RESTART2, param);
|
2016-04-07 16:53:37 +02:00
|
|
|
log_warning_errno(errno, "Failed to reboot with parameter, retrying without: %m");
|
2013-11-08 19:32:45 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
log_info("Rebooting.");
|
|
|
|
break;
|
|
|
|
|
|
|
|
case RB_POWER_OFF:
|
|
|
|
log_info("Powering off.");
|
|
|
|
break;
|
|
|
|
|
|
|
|
case RB_HALT_SYSTEM:
|
|
|
|
log_info("Halting system.");
|
|
|
|
break;
|
|
|
|
|
|
|
|
default:
|
|
|
|
assert_not_reached("Unknown magic");
|
|
|
|
}
|
2012-09-06 00:37:18 +02:00
|
|
|
|
2013-11-08 19:32:45 +01:00
|
|
|
reboot(cmd);
|
2012-09-06 00:37:18 +02:00
|
|
|
if (errno == EPERM && in_container) {
|
|
|
|
/* If we are in a container, and we lacked
|
|
|
|
* CAP_SYS_BOOT just exit, this will kill our
|
|
|
|
* container for good. */
|
2013-11-08 19:32:45 +01:00
|
|
|
log_info("Exiting container.");
|
2017-12-22 13:24:40 +01:00
|
|
|
exit(EXIT_SUCCESS);
|
2012-09-06 00:37:18 +02:00
|
|
|
}
|
|
|
|
|
2015-09-08 19:30:45 +02:00
|
|
|
r = log_error_errno(errno, "Failed to invoke reboot(): %m");
|
2010-10-07 13:24:41 +02:00
|
|
|
|
|
|
|
error:
|
2014-11-28 13:19:16 +01:00
|
|
|
log_emergency_errno(r, "Critical error while doing system shutdown: %m");
|
2010-10-07 13:24:41 +02:00
|
|
|
freeze();
|
|
|
|
}
|