main.c revision ffbd2c4d45787ba5ba85a32db6551efba66a1ee6
/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
/***
This file is part of systemd.
Copyright 2010 Lennart Poettering
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
(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
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
#include <stdio.h>
#include <errno.h>
#include <string.h>
#include <unistd.h>
#include <getopt.h>
#include <signal.h>
#include <fcntl.h>
#include "manager.h"
#include "log.h"
#include "load-fragment.h"
#include "fdset.h"
#include "special.h"
#include "conf-parser.h"
#include "bus-errors.h"
#include "missing.h"
#include "label.h"
#include "build.h"
#include "strv.h"
#include "def.h"
#include "virt.h"
#include "watchdog.h"
#include "path-util.h"
#include "switch-root.h"
#include "capability.h"
#include "killall.h"
#include "env-util.h"
#include "hwclock.h"
#include "sd-daemon.h"
#include "mount-setup.h"
#include "loopback-setup.h"
#ifdef HAVE_KMOD
#include "kmod-setup.h"
#endif
#include "hostname-setup.h"
#include "machine-id-setup.h"
#include "locale-setup.h"
#include "selinux-setup.h"
#include "ima-setup.h"
#include "fileio.h"
#include "smack-setup.h"
static enum {
} arg_action = ACTION_RUN;
static char *arg_default_unit = NULL;
static bool arg_dump_core = true;
static bool arg_crash_shell = false;
static int arg_crash_chvt = -1;
static bool arg_confirm_spawn = false;
static bool arg_show_status = true;
static bool arg_switched_root = false;
static char **arg_default_controllers = NULL;
static char ***arg_join_controllers = NULL;
static usec_t arg_runtime_watchdog = 0;
static uint64_t arg_capability_bounding_set_drop = 0;
static void nop_handler(int sig) {
}
if (!arg_dump_core)
else {
/* We want to wait for the core process, hence let's enable SIGCHLD */
else if (pid == 0) {
/* Enable default signal handler for core dump */
/* Don't limit the core dump size */
/* Just to be sure... */
/* Raise the signal again */
assert_not_reached("We shouldn't be here...");
_exit(1);
} else {
int r;
/* Order things nicely. */
else
}
}
if (arg_crash_chvt)
if (arg_crash_shell) {
log_info("Executing crash shell in 10s...");
sleep(10);
/* Let the kernel reap children for us */
if (pid < 0)
log_error("Failed to fork off crash shell: %m");
else if (pid == 0) {
log_error("execl() failed: %m");
_exit(1);
}
}
log_info("Freezing execution.");
freeze();
}
static void install_crash_handler(void) {
}
static int console_setup(bool do_reset) {
int tty_fd, r;
* and make sure we don't have a controlling tty. */
if (!do_reset)
return 0;
if (tty_fd < 0) {
return -tty_fd;
}
/* We don't want to force text mode.
* plymouth may be showing pictures already from initrd. */
r = reset_terminal_fd(tty_fd, false);
if (r < 0)
return r;
}
static int set_default_unit(const char *u) {
char *c;
assert(u);
c = strdup(u);
if (!c)
return -ENOMEM;
arg_default_unit = c;
return 0;
}
static int parse_proc_cmdline_word(const char *word) {
static const char * const rlmap[] = {
"emergency", SPECIAL_EMERGENCY_TARGET,
"-b", SPECIAL_EMERGENCY_TARGET,
"single", SPECIAL_RESCUE_TARGET,
"-s", SPECIAL_RESCUE_TARGET,
"s", SPECIAL_RESCUE_TARGET,
"S", SPECIAL_RESCUE_TARGET,
"1", SPECIAL_RESCUE_TARGET,
"2", SPECIAL_RUNLEVEL2_TARGET,
"3", SPECIAL_RUNLEVEL3_TARGET,
"4", SPECIAL_RUNLEVEL4_TARGET,
"5", SPECIAL_RUNLEVEL5_TARGET,
};
if (!in_initrd())
if (in_initrd())
int r;
else
arg_dump_core = r;
int r;
else
arg_crash_shell = r;
int r;
else
arg_confirm_spawn = r;
int k;
else
arg_crash_chvt = k;
int r;
else
arg_show_status = r;
int r;
else
int r;
else
char *eq;
int r;
if (!cenv)
return -ENOMEM;
if (!eq) {
if (!env_name_is_valid(cenv))
else {
if (r < 0)
}
} else {
if (!env_assignment_is_valid(cenv))
else {
*eq = 0;
if (r < 0)
}
}
const char *c;
/* Ignore systemd.journald.xyz and friends */
c = word;
if (startswith(c, "rd."))
c += 3;
if (startswith(c, "systemd."))
c += 8;
log_info("Supported kernel switches:\n"
"systemd.unit=UNIT Default unit to start\n"
"rd.systemd.unit=UNIT Default unit to start when run in initrd\n"
"systemd.dump_core=0|1 Dump core on crash\n"
"systemd.crash_shell=0|1 Run shell on crash\n"
"systemd.crash_chvt=N Change to VT #N on crash\n"
"systemd.confirm_spawn=0|1 Confirm every process spawn\n"
"systemd.show_status=0|1 Show status updates on the console during bootup\n"
"systemd.log_target=console|kmsg|journal|journal-or-kmsg|syslog|syslog-or-kmsg|null\n"
" Log target\n"
"systemd.log_level=LEVEL Log level\n"
"systemd.log_color=0|1 Highlight important log messages\n"
"systemd.log_location=0|1 Include code location in log messages\n"
"systemd.default_standard_output=null|tty|syslog|syslog+console|kmsg|kmsg+console|journal|journal+console\n"
" Set default log output for services\n"
"systemd.default_standard_error=null|tty|syslog|syslog+console|kmsg|kmsg+console|journal|journal+console\n"
" Set default log error output for services\n"
"systemd.setenv=ASSIGNMENT Set an environment variable for all spawned processes\n");
}
arg_show_status = false;
else if (!in_initrd()) {
unsigned i;
/* SysV compatibility */
}
return 0;
}
static int config_parse_level2(
const char *filename,
unsigned line,
const char *section,
const char *lvalue,
int ltype,
const char *rvalue,
void *data,
void *userdata) {
return 0;
}
static int config_parse_target(
const char *filename,
unsigned line,
const char *section,
const char *lvalue,
int ltype,
const char *rvalue,
void *data,
void *userdata) {
return 0;
}
static int config_parse_color(
const char *filename,
unsigned line,
const char *section,
const char *lvalue,
int ltype,
const char *rvalue,
void *data,
void *userdata) {
return 0;
}
static int config_parse_location(
const char *filename,
unsigned line,
const char *section,
const char *lvalue,
int ltype,
const char *rvalue,
void *data,
void *userdata) {
return 0;
}
static int config_parse_cpu_affinity2(
const char *filename,
unsigned line,
const char *section,
const char *lvalue,
int ltype,
const char *rvalue,
void *data,
void *userdata) {
char *w;
size_t l;
char *state;
unsigned ncpus = 0;
char *t;
int r;
unsigned cpu;
if (!(t = strndup(w, l)))
return log_oom();
free(t);
if (!c)
if (!(c = cpu_set_malloc(&ncpus)))
return log_oom();
CPU_FREE(c);
return -EBADMSG;
}
}
if (c) {
log_warning("Failed to set CPU affinity: %m");
CPU_FREE(c);
}
return 0;
}
static void strv_free_free(char ***l) {
char ***i;
if (!l)
return;
for (i = l; *i; i++)
strv_free(*i);
free(l);
}
static void free_join_controllers(void) {
if (!arg_join_controllers)
return;
}
static int config_parse_join_controllers(
const char *filename,
unsigned line,
const char *section,
const char *lvalue,
int ltype,
const char *rvalue,
void *data,
void *userdata) {
unsigned n = 0;
char *state, *w;
char *s, **l;
if (!s)
return log_oom();
l = strv_split(s, ",");
free(s);
strv_uniq(l);
if (strv_length(l) <= 1) {
strv_free(l);
continue;
}
if (!arg_join_controllers) {
if (!arg_join_controllers) {
strv_free(l);
return log_oom();
}
arg_join_controllers[0] = l;
n = 1;
} else {
char ***a;
char ***t;
t = new0(char**, n+2);
if (!t) {
strv_free(l);
return log_oom();
}
n = 0;
for (a = arg_join_controllers; *a; a++) {
if (strv_overlap(*a, l)) {
char **c;
c = strv_merge(*a, l);
if (!c) {
strv_free(l);
strv_free_free(t);
return log_oom();
}
strv_free(l);
l = c;
} else {
char **c;
c = strv_copy(*a);
if (!c) {
strv_free(l);
strv_free_free(t);
return log_oom();
}
t[n++] = c;
}
}
t[n++] = strv_uniq(l);
arg_join_controllers = t;
}
}
return 0;
}
static int parse_config_file(void) {
const ConfigTableItem items[] = {
{ "Manager", "CapabilityBoundingSet", config_parse_bounding_set, 0, &arg_capability_bounding_set_drop },
{ "Manager", "DefaultLimitSIGPENDING",config_parse_limit, 0, &arg_default_rlimit[RLIMIT_SIGPENDING]},
};
FILE *f;
const char *fn;
int r;
if (!f) {
return 0;
return 0;
}
if (r < 0)
fclose(f);
return 0;
}
static int parse_proc_cmdline(void) {
int r;
size_t l;
* that is only relevant for the host system */
if (detect_container(NULL) > 0)
return 0;
return 0;
}
char *word;
r = -ENOMEM;
goto finish;
}
r = parse_proc_cmdline_word(word);
if (r < 0) {
goto finish;
}
}
r = 0;
return r;
}
enum {
ARG_LOG_LEVEL = 0x100,
};
};
int c, r;
if (getpid() == 1)
opterr = 0;
switch (c) {
case ARG_LOG_LEVEL:
if ((r = log_set_max_level_from_string(optarg)) < 0) {
return r;
}
break;
case ARG_LOG_TARGET:
if ((r = log_set_target_from_string(optarg)) < 0) {
return r;
}
break;
case ARG_LOG_COLOR:
if (optarg) {
if ((r = log_show_color_from_string(optarg)) < 0) {
return r;
}
} else
log_show_color(true);
break;
case ARG_LOG_LOCATION:
if (optarg) {
if ((r = log_show_location_from_string(optarg)) < 0) {
return r;
}
} else
log_show_location(true);
break;
case ARG_DEFAULT_STD_OUTPUT:
if ((r = exec_output_from_string(optarg)) < 0) {
return r;
} else
break;
case ARG_DEFAULT_STD_ERROR:
if ((r = exec_output_from_string(optarg)) < 0) {
return r;
} else
break;
case ARG_UNIT:
if ((r = set_default_unit(optarg)) < 0) {
return r;
}
break;
case ARG_SYSTEM:
break;
case ARG_USER:
break;
case ARG_TEST:
break;
case ARG_VERSION:
break;
break;
case ARG_DUMP_CORE:
if (r < 0) {
return r;
}
arg_dump_core = r;
break;
case ARG_CRASH_SHELL:
if (r < 0) {
return r;
}
arg_crash_shell = r;
break;
case ARG_CONFIRM_SPAWN:
if (r < 0) {
return r;
}
arg_confirm_spawn = r;
break;
case ARG_SHOW_STATUS:
if (r < 0) {
return r;
}
arg_show_status = r;
break;
case ARG_DESERIALIZE: {
int fd;
FILE *f;
if (r < 0 || fd < 0) {
return r < 0 ? r : -EINVAL;
}
fd_cloexec(fd, true);
if (!f) {
log_error("Failed to open serialization fd: %m");
return -errno;
}
if (serialization)
serialization = f;
break;
}
case ARG_SWITCHED_ROOT:
arg_switched_root = true;
break;
case ARG_INTROSPECT: {
const char * const * i = NULL;
for (i = bus_interface_table; *i; i += 2)
"<node>\n", stdout);
if (optarg)
break;
}
if (!i[0] && optarg)
break;
}
case 'h':
break;
case 'D':
break;
case 'b':
case 's':
case 'z':
/* Just to eat away the sysvinit kernel
* cmdline args without getopt() error
* messages that we'll parse in
* parse_proc_cmdline_word() or ignore. */
case '?':
default:
if (getpid() != 1) {
log_error("Unknown option code %c", c);
return -EINVAL;
}
break;
}
/* Hmm, when we aren't run as init system
* let's complain about excess arguments */
log_error("Excess arguments.");
return -EINVAL;
}
if (detect_container(NULL) > 0) {
char **a;
* understand it passed to us. We're not really
* more interesting and complete. With one exception:
* relevant for the container, hence we rely on argv[]
* instead. */
if ((r = parse_proc_cmdline_word(*a)) < 0) {
return r;
}
}
return 0;
}
static int help(void) {
printf("%s [OPTIONS...]\n\n"
"Starts up and maintains the system or user services.\n\n"
" -h --help Show this help\n"
" --test Determine startup sequence, dump it and exit\n"
" --dump-configuration-items Dump understood unit configuration items\n"
" --introspect[=INTERFACE] Extract D-Bus interface data\n"
" --unit=UNIT Set default unit\n"
" --system Run a system instance, even if PID != 1\n"
" --user Run a user instance\n"
" --dump-core[=0|1] Dump core on crash\n"
" --crash-shell[=0|1] Run shell on crash\n"
" --confirm-spawn[=0|1] Ask for confirmation when spawning processes\n"
" --show-status[=0|1] Show status updates on the console during bootup\n"
" --log-target=TARGET Set log target (console, journal, syslog, kmsg, journal-or-kmsg, syslog-or-kmsg, null)\n"
" --log-level=LEVEL Set log level (debug, info, notice, warning, err, crit, alert, emerg)\n"
" --log-color[=0|1] Highlight important log messages\n"
" --log-location[=0|1] Include code location in log messages\n"
" --default-standard-output= Set default standard output for services\n"
" --default-standard-error= Set default standard error output for services\n",
return 0;
}
static int version(void) {
return 0;
}
int r;
assert(m);
/* Make sure nothing is really destructed when we shut down */
m->n_reloading ++;
r = manager_open_serialization(m, &f);
if (r < 0) {
goto fail;
}
if (!fds) {
r = -ENOMEM;
goto fail;
}
if (r < 0) {
goto fail;
}
log_error("Failed to rewind serialization fd: %m");
goto fail;
}
r = fd_cloexec(fileno(f), false);
if (r < 0) {
goto fail;
}
r = fdset_cloexec(fds, false);
if (r < 0) {
goto fail;
}
*_f = f;
return 0;
fail:
if (f)
fclose(f);
return r;
}
int r;
/* Save the original RLIMIT_NOFILE so that we can reset it
* later when transitioning from the initrd to the main
* systemd or suchlike. */
log_error("Reading RLIMIT_NOFILE failed: %m");
return -errno;
}
/* Make sure forked processes get the default kernel setting */
if (!arg_default_rlimit[RLIMIT_NOFILE]) {
if (!rl)
return log_oom();
}
/* Bump up the resource limit for ourselves substantially */
if (r < 0) {
return r;
}
return 0;
}
const char *e;
unsigned long long a, b;
assert(t);
e = getenv("RD_TIMESTAMP");
if (!e)
return NULL;
return NULL;
return t;
}
static void test_mtab(void) {
char *p;
if (readlink_malloc("/etc/mtab", &p) >= 0) {
bool b;
free(p);
if (b)
return;
}
"This is not supported anymore. "
"Please make sure to replace this file by a symlink to avoid incorrect or misleading mount(8) output.");
}
static void test_usr(void) {
/* Check that /usr is not a separate fs */
if (dir_is_empty("/usr") <= 0)
return;
log_warning("/usr appears to be on its own filesytem and is not already mounted. This is not a supported setup. "
"Some things will probably break (sometimes even silently) in mysterious ways. "
"Consult http://freedesktop.org/wiki/Software/systemd/separate-usr-is-broken for more information.");
}
static void test_cgroups(void) {
return;
log_warning("CONFIG_CGROUPS was not set when your kernel was compiled. "
"Systems without control groups are not supported. "
"We will now sleep for 10s, and then continue boot-up. "
"Expect breakage and please do not file bugs. "
"Instead fix your kernel and enable CONFIG_CGROUPS. "
"Consult http://0pointer.de/blog/projects/cgroups-vs-cgroups.html for more information.");
sleep(10);
}
static int initialize_join_controllers(void) {
/* By default, mount "cpu" + "cpuacct" together, and "net_cls"
* + "net_prio". We'd like to add "cpuset" to the mix, but
* "cpuset" does't really work for groups with no initialized
* attributes. */
if (!arg_join_controllers)
return -ENOMEM;
if (!arg_join_controllers[0])
return -ENOMEM;
if (!arg_join_controllers[1])
return -ENOMEM;
return 0;
}
int r, retval = EXIT_FAILURE;
char timespan[FORMAT_TIMESPAN_MAX];
bool reexecute = false;
const char *shutdown_verb = NULL;
static char systemd[] = "systemd";
bool skip_setup = false;
int j;
bool loaded_policy = false;
bool arm_reboot_watchdog = false;
bool queue_default_job = false;
static struct rlimit saved_rlimit_nofile = { 0, 0 };
#ifdef HAVE_SYSV_COMPAT
/* This is compatibility support for SysV, where
* calling init as a user is identical to telinit. */
return 1;
}
#endif
/* Determine if this is a reexecution or normal bootup. We do
* the full command line parsing much later, so let's just
* have a quick peek here. */
for (j = 1; j < argc; j++)
skip_setup = true;
break;
}
/* If we have switched root, do all the special setup
* things */
for (j = 1; j < argc; j++)
skip_setup = false;
break;
}
called 'init'. After a subsequent reexecution we are then
called 'systemd'. That is confusing, hence let's call us
systemd right-away. */
saved_argv = argv;
saved_argc = argc;
/* Running outside of a container as PID 1 */
log_open();
if (in_initrd()) {
char *rd_timestamp = NULL;
(unsigned long long) initrd_timestamp.realtime,
(unsigned long long) initrd_timestamp.monotonic);
if (rd_timestamp) {
}
}
if (!skip_setup) {
if (selinux_setup(&loaded_policy) < 0)
goto finish;
if (ima_setup() < 0)
goto finish;
if (smack_setup() < 0)
goto finish;
}
if (label_init(NULL) < 0)
goto finish;
if (!skip_setup) {
if (hwclock_is_localtime() > 0) {
int min;
/* The first-time call to settimeofday() does a time warp in the kernel */
r = hwclock_set_timezone(&min);
if (r < 0)
else
} else if (!in_initrd()) {
/*
* Do dummy first-time call to seal the kernel's time warp magic
*
* Do not call this this from inside the initrd. The initrd might not
* that way. In such case, we need to delay the time-warp or the sealing
* until we reach the real system.
*/
/* Tell the kernel our time zone */
r = hwclock_set_timezone(NULL);
if (r < 0)
}
}
/* Set the default for later on, but don't actually
* open the logs like this for now. Note that if we
* are transitioning from the initrd there might still
* be journal fd open, and we shouldn't attempt
* might redirect output elsewhere. */
} else if (getpid() == 1) {
/* Running inside a container, as PID 1 */
log_open();
/* For the later on, see above... */
} else {
/* Running as user instance */
log_open();
}
/* Initialize default unit */
if (r < 0) {
goto finish;
}
r = initialize_join_controllers();
if (r < 0)
goto finish;
r = mount_setup(loaded_policy);
if (r < 0)
goto finish;
}
/* Reset all signal handlers. */
assert_se(reset_all_signal_handlers() == 0);
/* If we are init, we can block sigkill. Yay. */
if (parse_config_file() < 0)
goto finish;
if (arg_running_as == SYSTEMD_SYSTEM)
if (parse_proc_cmdline() < 0)
goto finish;
goto finish;
if (arg_action == ACTION_TEST &&
geteuid() == 0) {
log_error("Don't run test mode as root.");
goto finish;
}
if (arg_running_as == SYSTEMD_USER &&
arg_action == ACTION_RUN &&
sd_booted() <= 0) {
log_error("Trying to run as user instance, but the system has not been booted with systemd.");
goto finish;
}
if (arg_running_as == SYSTEMD_SYSTEM &&
arg_action == ACTION_RUN &&
running_in_chroot() > 0) {
log_error("Cannot be run in a chroot() environment.");
goto finish;
}
if (arg_action == ACTION_HELP) {
goto finish;
} else if (arg_action == ACTION_VERSION) {
goto finish;
} else if (arg_action == ACTION_DUMP_CONFIGURATION_ITEMS) {
goto finish;
} else if (arg_action == ACTION_DONE) {
goto finish;
}
/* Close logging fds, in order not to confuse fdset below */
log_close();
/* Remember open file descriptors for later deserialization */
r = fdset_new_fill(&fds);
if (r < 0) {
goto finish;
} else
fdset_cloexec(fds, true);
if (serialization)
/* Set up PATH unless it is already set */
setenv("PATH",
#ifdef HAVE_SPLIT_USR
#else
#endif
if (arg_running_as == SYSTEMD_SYSTEM) {
/* Parse the data passed to us. We leave this
* variables set, but the manager later on will not
* pass them on to our children. */
if (!in_initrd())
/* Unset some environment variables passed in from the
* kernel that don't really make sense for us. */
unsetenv("HOME");
unsetenv("TERM");
/* When we are invoked by a shell, these might be set,
* but make little sense to pass on */
unsetenv("PWD");
unsetenv("SHLVL");
unsetenv("_");
/* When we are invoked by a chroot-like tool such as
* nspawn, these might be set, but make little sense
* to pass on */
unsetenv("USER");
unsetenv("LOGNAME");
/* We suppress the socket activation env vars, as
* we'll try to match *any* open fd to units if
* possible. */
unsetenv("LISTEN_FDS");
unsetenv("LISTEN_PID");
/* All other variables are left as is, so that clients
* can still read them via /proc/1/environ */
}
/* Move out of the way, so that we won't block unmounts */
if (arg_running_as == SYSTEMD_SYSTEM) {
/* Become a session leader if we aren't one yet. */
setsid();
/* Disable the umask logic */
umask(0);
}
/* Make sure D-Bus doesn't fiddle with the SIGPIPE handlers */
/* Reset the console, but only if this is really init and we
* are freshly booted */
/* Open the logging devices, if possible and necessary */
log_open();
/* Make sure we leave a core dump without panicing the
* kernel. */
if (getpid() == 1)
if (r < 0)
goto finish;
}
if (arg_running_as == SYSTEMD_SYSTEM) {
const char *virtualization = NULL;
if (virtualization)
if (in_initrd())
log_info("Running in initial RAM disk.");
} else
locale_setup();
if (arg_show_status || plymouth_running())
#ifdef HAVE_KMOD
kmod_setup();
#endif
test_mtab();
test_usr();
test_cgroups();
}
log_error("Failed to adjust timer slack: %m");
if (r < 0) {
goto finish;
}
if (r < 0) {
goto finish;
}
}
if (arg_running_as == SYSTEMD_USER) {
/* Become reaper of our children */
log_warning("Failed to make us a subreaper: %m");
log_info("Perhaps the kernel version is too old (< 3.4?)");
}
}
if (arg_running_as == SYSTEMD_SYSTEM)
r = manager_new(arg_running_as, &m);
if (r < 0) {
goto finish;
}
/* Remember whether we should queue the default job */
if (r < 0)
/* This will close all file descriptors that were opened, but
* not claimed by any unit. */
if (serialization) {
}
if (queue_default_job) {
if (r < 0) {
log_error("Default target masked.");
log_info("Trying to load rescue target...");
if (r < 0) {
goto finish;
goto finish;
log_error("Rescue target masked.");
goto finish;
}
}
if (arg_action == ACTION_TEST) {
printf("-> By units:\n");
}
if (r < 0) {
goto finish;
}
"Loaded units and determined initial transaction in %s.",
if (arg_action == ACTION_TEST) {
printf("-> By jobs:\n");
goto finish;
}
}
for (;;) {
r = manager_loop(m);
if (r < 0) {
goto finish;
}
switch (m->exit_code) {
case MANAGER_EXIT:
log_debug("Exit.");
goto finish;
case MANAGER_RELOAD:
log_info("Reloading.");
r = manager_reload(m);
if (r < 0)
break;
case MANAGER_REEXECUTE:
goto finish;
reexecute = true;
log_notice("Reexecuting.");
goto finish;
case MANAGER_SWITCH_ROOT:
/* Steal the switch root parameters */
switch_root_dir = m->switch_root;
if (!switch_root_init)
goto finish;
reexecute = true;
log_notice("Switching root.");
goto finish;
case MANAGER_REBOOT:
case MANAGER_POWEROFF:
case MANAGER_HALT:
case MANAGER_KEXEC: {
static const char * const table[_MANAGER_EXIT_CODE_MAX] = {
[MANAGER_REBOOT] = "reboot",
[MANAGER_POWEROFF] = "poweroff",
[MANAGER_HALT] = "halt",
[MANAGER_KEXEC] = "kexec"
};
log_notice("Shutting down.");
goto finish;
}
default:
assert_not_reached("Unknown exit code.");
}
}
if (m)
manager_free(m);
for (j = 0; j < RLIMIT_NLIMITS; j++)
free(arg_default_rlimit[j]);
label_finish();
if (reexecute) {
const char **args;
unsigned i, args_size;
/* Close and disarm the watchdog, so that the new
* instance can reinitialize it, but doesn't get
* rebooted while we do that */
watchdog_close(true);
/* Reset the RLIMIT_NOFILE to the kernel default, so
* that the new systemd can pass the kernel default to
* its child processes */
if (saved_rlimit_nofile.rlim_cur > 0)
if (switch_root_dir) {
/* Kill all remaining processes from the
* initrd, but don't wait for them, so that we
* can handle the SIGCHLD for them after
* deserializing. */
broadcast_signal(SIGTERM, false);
/* And switch root */
r = switch_root(switch_root_dir);
if (r < 0)
}
if (!switch_root_init) {
char sfd[16];
/* First try to spawn ourselves with the right
* path, and with full serialization. We do
* this only if the user didn't specify an
* explicit init to spawn. */
i = 0;
args[i++] = SYSTEMD_BINARY_PATH;
if (switch_root_dir)
args[i++] = "--switched-root";
args[i++] = "--deserialize";
}
/* Try the fallback, if there is any, without any
* serialization. We pass the original argv[] and
* envp[]. (Well, modulo the ordering changes due to
* getopt() in argv[], and some cleanups in envp[],
* but let's hope that doesn't matter.) */
if (serialization) {
}
if (fds) {
}
/* Reopen the console */
if (switch_root_init) {
args[0] = switch_root_init;
log_warning("Failed to execute configured init, trying fallback: %m");
}
log_warning("No /sbin/init, trying fallback");
} else
log_warning("Failed to execute /sbin/init, giving up: %m");
}
if (serialization)
if (fds)
if (shutdown_verb) {
const char * command_line[] = {
};
char **env_block;
if (arm_reboot_watchdog && arg_shutdown_watchdog > 0) {
char e[32];
/* If we reboot let's set the shutdown
* watchdog and tell the shutdown binary to
* repeatedly ping it */
watchdog_close(false);
/* Tell the binary how often to ping */
char_array_0(e);
} else {
watchdog_close(true);
}
log_error("Failed to execute shutdown binary, freezing: %m");
}
if (getpid() == 1)
freeze();
return retval;
}