Systemd/src/main.c
Lennart Poettering b2bb3dbed9 console: rework automatic getty on kernel console logic again
It is essential that the gettys are proper dependencies from
getty.target so that they aren't killed and immediately restarted on
runlevel changes. Hence rework the logic to implicitly add console
gettys to getty.target as dependencies.

This also adds an automatic hvc console for virtualizers.

https://bugzilla.redhat.com/show_bug.cgi?id=501720
2010-08-30 23:03:58 +02:00

1189 lines
40 KiB
C

/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
/***
This file is part of systemd.
Copyright 2010 Lennart Poettering
systemd is free software; you can redistribute it and/or modify it
under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 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
General Public License for more details.
You should have received a copy of the GNU General Public License
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
#include <dbus/dbus.h>
#include <stdio.h>
#include <errno.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <getopt.h>
#include <signal.h>
#include <sys/wait.h>
#include <fcntl.h>
#include "manager.h"
#include "log.h"
#include "mount-setup.h"
#include "hostname-setup.h"
#include "loopback-setup.h"
#include "kmod-setup.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"
static enum {
ACTION_RUN,
ACTION_HELP,
ACTION_TEST,
ACTION_DUMP_CONFIGURATION_ITEMS,
ACTION_DONE
} arg_action = ACTION_RUN;
static char *arg_default_unit = NULL;
static ManagerRunningAs arg_running_as = _MANAGER_RUNNING_AS_INVALID;
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_sysv_console = true;
static bool arg_mount_auto = true;
static bool arg_swap_auto = true;
static char *arg_console = NULL;
static FILE* serialization = NULL;
_noreturn_ static void freeze(void) {
for (;;)
pause();
}
static void nop_handler(int sig) {
}
_noreturn_ static void crash(int sig) {
if (!arg_dump_core)
log_error("Caught <%s>, not dumping core.", signal_to_string(sig));
else {
struct sigaction sa;
pid_t pid;
/* We want to wait for the core process, hence let's enable SIGCHLD */
zero(sa);
sa.sa_handler = nop_handler;
sa.sa_flags = SA_NOCLDSTOP|SA_RESTART;
assert_se(sigaction(SIGCHLD, &sa, NULL) == 0);
if ((pid = fork()) < 0)
log_error("Caught <%s>, cannot fork for core dump: %s", signal_to_string(sig), strerror(errno));
else if (pid == 0) {
struct rlimit rl;
/* Enable default signal handler for core dump */
zero(sa);
sa.sa_handler = SIG_DFL;
assert_se(sigaction(sig, &sa, NULL) == 0);
/* Don't limit the core dump size */
zero(rl);
rl.rlim_cur = RLIM_INFINITY;
rl.rlim_max = RLIM_INFINITY;
setrlimit(RLIMIT_CORE, &rl);
/* Just to be sure... */
assert_se(chdir("/") == 0);
/* Raise the signal again */
raise(sig);
assert_not_reached("We shouldn't be here...");
_exit(1);
} else {
int status;
/* Order things nicely. */
if (waitpid(pid, &status, 0) < 0)
log_error("Caught <%s>, waitpid() failed: %s", signal_to_string(sig), strerror(errno));
else if (!WCOREDUMP(status))
log_error("Caught <%s>, core dump failed.", signal_to_string(sig));
else
log_error("Caught <%s>, dumped core as pid %lu.", signal_to_string(sig), (unsigned long) pid);
}
}
if (arg_crash_chvt)
chvt(arg_crash_chvt);
if (arg_crash_shell) {
struct sigaction sa;
pid_t pid;
log_info("Executing crash shell in 10s...");
sleep(10);
/* Let the kernel reap children for us */
zero(sa);
sa.sa_handler = SIG_IGN;
sa.sa_flags = SA_NOCLDSTOP|SA_NOCLDWAIT|SA_RESTART;
assert_se(sigaction(SIGCHLD, &sa, NULL) == 0);
if ((pid = fork()) < 0)
log_error("Failed to fork off crash shell: %s", strerror(errno));
else if (pid == 0) {
int fd, r;
if ((fd = acquire_terminal("/dev/console", false, true, true)) < 0)
log_error("Failed to acquire terminal: %s", strerror(-fd));
else if ((r = make_stdio(fd)) < 0)
log_error("Failed to duplicate terminal fd: %s", strerror(-r));
execl("/bin/sh", "/bin/sh", NULL);
log_error("execl() failed: %s", strerror(errno));
_exit(1);
}
log_info("Successfully spawned crash shall as pid %lu.", (unsigned long) pid);
}
log_info("Freezing execution.");
freeze();
}
static void install_crash_handler(void) {
struct sigaction sa;
zero(sa);
sa.sa_handler = crash;
sa.sa_flags = SA_NODEFER;
sigaction_many(&sa, SIGNALS_CRASH_HANDLER, -1);
}
static int make_null_stdio(void) {
int null_fd, r;
if ((null_fd = open("/dev/null", O_RDWR|O_NOCTTY)) < 0) {
log_error("Failed to open /dev/null: %m");
return -errno;
}
if ((r = make_stdio(null_fd)) < 0)
log_warning("Failed to dup2() device: %s", strerror(-r));
return r;
}
static int console_setup(bool do_reset) {
int tty_fd, r;
/* If we are init, we connect stdin/stdout/stderr to /dev/null
* and make sure we don't have a controlling tty. */
release_terminal();
if (!do_reset)
return 0;
if ((tty_fd = open_terminal("/dev/console", O_WRONLY|O_NOCTTY|O_CLOEXEC)) < 0) {
log_error("Failed to open /dev/console: %s", strerror(-tty_fd));
return -tty_fd;
}
if ((r = reset_terminal(tty_fd)) < 0)
log_error("Failed to reset /dev/console: %s", strerror(-r));
close_nointr_nofail(tty_fd);
return r;
}
static int set_default_unit(const char *u) {
char *c;
assert(u);
if (!(c = strdup(u)))
return -ENOMEM;
free(arg_default_unit);
arg_default_unit = c;
return 0;
}
static int parse_proc_cmdline_word(const char *word) {
static const char * const rlmap[] = {
"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
};
assert(word);
if (startswith(word, "systemd.unit="))
return set_default_unit(word + 13);
else if (startswith(word, "systemd.log_target=")) {
if (log_set_target_from_string(word + 19) < 0)
log_warning("Failed to parse log target %s. Ignoring.", word + 19);
} else if (startswith(word, "systemd.log_level=")) {
if (log_set_max_level_from_string(word + 18) < 0)
log_warning("Failed to parse log level %s. Ignoring.", word + 18);
} else if (startswith(word, "systemd.log_color=")) {
if (log_show_color_from_string(word + 18) < 0)
log_warning("Failed to parse log color setting %s. Ignoring.", word + 18);
} else if (startswith(word, "systemd.log_location=")) {
if (log_show_location_from_string(word + 21) < 0)
log_warning("Failed to parse log location setting %s. Ignoring.", word + 21);
} else if (startswith(word, "systemd.dump_core=")) {
int r;
if ((r = parse_boolean(word + 18)) < 0)
log_warning("Failed to parse dump core switch %s, Ignoring.", word + 18);
else
arg_dump_core = r;
} else if (startswith(word, "systemd.crash_shell=")) {
int r;
if ((r = parse_boolean(word + 20)) < 0)
log_warning("Failed to parse crash shell switch %s, Ignoring.", word + 20);
else
arg_crash_shell = r;
} else if (startswith(word, "systemd.confirm_spawn=")) {
int r;
if ((r = parse_boolean(word + 22)) < 0)
log_warning("Failed to parse confirm spawn switch %s, Ignoring.", word + 22);
else
arg_confirm_spawn = r;
} else if (startswith(word, "systemd.crash_chvt=")) {
int k;
if (safe_atoi(word + 19, &k) < 0)
log_warning("Failed to parse crash chvt switch %s, Ignoring.", word + 19);
else
arg_crash_chvt = k;
} else if (startswith(word, "systemd.show_status=")) {
int r;
if ((r = parse_boolean(word + 20)) < 0)
log_warning("Failed to parse show status switch %s, Ignoring.", word + 20);
else
arg_show_status = r;
} else if (startswith(word, "systemd.sysv_console=")) {
int r;
if ((r = parse_boolean(word + 21)) < 0)
log_warning("Failed to parse SysV console switch %s, Ignoring.", word + 20);
else
arg_sysv_console = r;
} else if (startswith(word, "systemd.")) {
log_warning("Unknown kernel switch %s. Ignoring.", word);
log_info("Supported kernel switches:\n"
"systemd.unit=UNIT Default unit to start\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.sysv_console=0|1 Connect output of SysV scripts to console\n"
"systemd.log_target=console|kmsg|syslog|syslog-org-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");
} else if (startswith(word, "console=")) {
const char *k;
size_t l;
char *w = NULL;
k = word + 8;
l = strcspn(k, ",");
/* Ignore the console setting if set to a VT */
if (l < 4 ||
!startswith(k, "tty") ||
k[3+strspn(k+3, "0123456789")] != 0) {
if (!(w = strndup(k, l)))
return -ENOMEM;
}
free(arg_console);
arg_console = w;
} else if (streq(word, "quiet")) {
arg_show_status = false;
arg_sysv_console = false;
} else {
unsigned i;
/* SysV compatibility */
for (i = 0; i < ELEMENTSOF(rlmap); i += 2)
if (streq(word, rlmap[i]))
return set_default_unit(rlmap[i+1]);
}
return 0;
}
static int config_parse_level(
const char *filename,
unsigned line,
const char *section,
const char *lvalue,
const char *rvalue,
void *data,
void *userdata) {
assert(filename);
assert(lvalue);
assert(rvalue);
log_set_max_level_from_string(rvalue);
return 0;
}
static int config_parse_target(
const char *filename,
unsigned line,
const char *section,
const char *lvalue,
const char *rvalue,
void *data,
void *userdata) {
assert(filename);
assert(lvalue);
assert(rvalue);
log_set_target_from_string(rvalue);
return 0;
}
static int config_parse_color(
const char *filename,
unsigned line,
const char *section,
const char *lvalue,
const char *rvalue,
void *data,
void *userdata) {
assert(filename);
assert(lvalue);
assert(rvalue);
log_show_color_from_string(rvalue);
return 0;
}
static int config_parse_location(
const char *filename,
unsigned line,
const char *section,
const char *lvalue,
const char *rvalue,
void *data,
void *userdata) {
assert(filename);
assert(lvalue);
assert(rvalue);
log_show_location_from_string(rvalue);
return 0;
}
static int config_parse_cpu_affinity(
const char *filename,
unsigned line,
const char *section,
const char *lvalue,
const char *rvalue,
void *data,
void *userdata) {
char *w;
size_t l;
char *state;
cpu_set_t *c = NULL;
unsigned ncpus = 0;
assert(filename);
assert(lvalue);
assert(rvalue);
FOREACH_WORD_QUOTED(w, l, rvalue, state) {
char *t;
int r;
unsigned cpu;
if (!(t = strndup(w, l)))
return -ENOMEM;
r = safe_atou(t, &cpu);
free(t);
if (!c)
if (!(c = cpu_set_malloc(&ncpus)))
return -ENOMEM;
if (r < 0 || cpu >= ncpus) {
log_error("[%s:%u] Failed to parse CPU affinity: %s", filename, line, rvalue);
CPU_FREE(c);
return -EBADMSG;
}
CPU_SET_S(cpu, CPU_ALLOC_SIZE(ncpus), c);
}
if (c) {
if (sched_setaffinity(0, CPU_ALLOC_SIZE(ncpus), c) < 0)
log_warning("Failed to set CPU affinity: %m");
CPU_FREE(c);
}
return 0;
}
static int parse_config_file(void) {
const ConfigItem items[] = {
{ "LogLevel", config_parse_level, NULL, "Manager" },
{ "LogTarget", config_parse_target, NULL, "Manager" },
{ "LogColor", config_parse_color, NULL, "Manager" },
{ "LogLocation", config_parse_location, NULL, "Manager" },
{ "DumpCore", config_parse_bool, &arg_dump_core, "Manager" },
{ "CrashShell", config_parse_bool, &arg_crash_shell, "Manager" },
{ "ShowStatus", config_parse_bool, &arg_show_status, "Manager" },
{ "SysVConsole", config_parse_bool, &arg_sysv_console, "Manager" },
{ "CrashChVT", config_parse_int, &arg_crash_chvt, "Manager" },
{ "CPUAffinity", config_parse_cpu_affinity, NULL, "Manager" },
{ "MountAuto", config_parse_bool, &arg_mount_auto, "Manager" },
{ "SwapAuto", config_parse_bool, &arg_swap_auto, "Manager" },
{ NULL, NULL, NULL, NULL }
};
static const char * const sections[] = {
"Manager",
NULL
};
FILE *f;
const char *fn;
int r;
fn = arg_running_as == MANAGER_SYSTEM ? SYSTEM_CONFIG_FILE : SESSION_CONFIG_FILE;
if (!(f = fopen(fn, "re"))) {
if (errno == ENOENT)
return 0;
log_warning("Failed to open configuration file '%s': %m", fn);
return 0;
}
if ((r = config_parse(fn, f, sections, items, false, NULL)) < 0)
log_warning("Failed to parse configuration file: %s", strerror(-r));
fclose(f);
return 0;
}
static int parse_proc_cmdline(void) {
char *line;
int r;
char *w;
size_t l;
char *state;
if ((r = read_one_line_file("/proc/cmdline", &line)) < 0) {
log_warning("Failed to read /proc/cmdline, ignoring: %s", strerror(-r));
return 0;
}
FOREACH_WORD_QUOTED(w, l, line, state) {
char *word;
if (!(word = strndup(w, l))) {
r = -ENOMEM;
goto finish;
}
r = parse_proc_cmdline_word(word);
free(word);
if (r < 0)
goto finish;
}
r = 0;
finish:
free(line);
return r;
}
static int parse_argv(int argc, char *argv[]) {
enum {
ARG_LOG_LEVEL = 0x100,
ARG_LOG_TARGET,
ARG_LOG_COLOR,
ARG_LOG_LOCATION,
ARG_UNIT,
ARG_SYSTEM,
ARG_SESSION,
ARG_TEST,
ARG_DUMP_CONFIGURATION_ITEMS,
ARG_DUMP_CORE,
ARG_CRASH_SHELL,
ARG_CONFIRM_SPAWN,
ARG_SHOW_STATUS,
ARG_SYSV_CONSOLE,
ARG_DESERIALIZE,
ARG_INTROSPECT
};
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 },
{ "unit", required_argument, NULL, ARG_UNIT },
{ "system", no_argument, NULL, ARG_SYSTEM },
{ "session", no_argument, NULL, ARG_SESSION },
{ "test", no_argument, NULL, ARG_TEST },
{ "help", no_argument, NULL, 'h' },
{ "dump-configuration-items", no_argument, NULL, ARG_DUMP_CONFIGURATION_ITEMS },
{ "dump-core", no_argument, NULL, ARG_DUMP_CORE },
{ "crash-shell", no_argument, NULL, ARG_CRASH_SHELL },
{ "confirm-spawn", no_argument, NULL, ARG_CONFIRM_SPAWN },
{ "show-status", optional_argument, NULL, ARG_SHOW_STATUS },
{ "sysv-console", optional_argument, NULL, ARG_SYSV_CONSOLE },
{ "deserialize", required_argument, NULL, ARG_DESERIALIZE },
{ "introspect", optional_argument, NULL, ARG_INTROSPECT },
{ NULL, 0, NULL, 0 }
};
int c, r;
assert(argc >= 1);
assert(argv);
while ((c = getopt_long(argc, argv, "hD", options, NULL)) >= 0)
switch (c) {
case ARG_LOG_LEVEL:
if ((r = log_set_max_level_from_string(optarg)) < 0) {
log_error("Failed to parse log level %s.", optarg);
return r;
}
break;
case ARG_LOG_TARGET:
if ((r = log_set_target_from_string(optarg)) < 0) {
log_error("Failed to parse log target %s.", optarg);
return r;
}
break;
case ARG_LOG_COLOR:
if (optarg) {
if ((r = log_show_color_from_string(optarg)) < 0) {
log_error("Failed to parse log color setting %s.", optarg);
return r;
}
} else
log_show_color(true);
break;
case ARG_LOG_LOCATION:
if (optarg) {
if ((r = log_show_location_from_string(optarg)) < 0) {
log_error("Failed to parse log location setting %s.", optarg);
return r;
}
} else
log_show_location(true);
break;
case ARG_UNIT:
if ((r = set_default_unit(optarg)) < 0) {
log_error("Failed to set default unit %s: %s", optarg, strerror(-r));
return r;
}
break;
case ARG_SYSTEM:
arg_running_as = MANAGER_SYSTEM;
break;
case ARG_SESSION:
arg_running_as = MANAGER_SESSION;
break;
case ARG_TEST:
arg_action = ACTION_TEST;
break;
case ARG_DUMP_CONFIGURATION_ITEMS:
arg_action = ACTION_DUMP_CONFIGURATION_ITEMS;
break;
case ARG_DUMP_CORE:
arg_dump_core = true;
break;
case ARG_CRASH_SHELL:
arg_crash_shell = true;
break;
case ARG_CONFIRM_SPAWN:
arg_confirm_spawn = true;
break;
case ARG_SHOW_STATUS:
if (optarg) {
if ((r = parse_boolean(optarg)) < 0) {
log_error("Failed to show status boolean %s.", optarg);
return r;
}
arg_show_status = r;
} else
arg_show_status = true;
break;
case ARG_SYSV_CONSOLE:
if (optarg) {
if ((r = parse_boolean(optarg)) < 0) {
log_error("Failed to SysV console boolean %s.", optarg);
return r;
}
arg_sysv_console = r;
} else
arg_sysv_console = true;
break;
case ARG_DESERIALIZE: {
int fd;
FILE *f;
if ((r = safe_atoi(optarg, &fd)) < 0 || fd < 0) {
log_error("Failed to parse deserialize option %s.", optarg);
return r;
}
if (!(f = fdopen(fd, "r"))) {
log_error("Failed to open serialization fd: %m");
return r;
}
if (serialization)
fclose(serialization);
serialization = f;
break;
}
case ARG_INTROSPECT: {
const char * const * i = NULL;
for (i = bus_interface_table; *i; i += 2)
if (!optarg || streq(i[0], optarg)) {
fputs(DBUS_INTROSPECT_1_0_XML_DOCTYPE_DECL_NODE
"<node>\n", stdout);
fputs(i[1], stdout);
fputs("</node>\n", stdout);
if (optarg)
break;
}
if (!i[0] && optarg)
log_error("Unknown interface %s.", optarg);
arg_action = ACTION_DONE;
break;
}
case 'h':
arg_action = ACTION_HELP;
break;
case 'D':
log_set_max_level(LOG_DEBUG);
break;
case '?':
return -EINVAL;
default:
log_error("Unknown option code %c", c);
return -EINVAL;
}
/* PID 1 will get the kernel arguments as parameters, which we
* ignore and unconditionally read from
* /proc/cmdline. However, we need to ignore those arguments
* here. */
if (arg_running_as != MANAGER_SYSTEM && optind < argc) {
log_error("Excess arguments.");
return -EINVAL;
}
return 0;
}
static int help(void) {
printf("%s [OPTIONS...]\n\n"
"Starts up and maintains the system or a session.\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"
" --session Run a session instance\n"
" --dump-core Dump core on crash\n"
" --crash-shell Run shell on crash\n"
" --confirm-spawn Ask for confirmation when spawning processes\n"
" --show-status[=0|1] Show status updates on the console during bootup\n"
" --sysv-console[=0|1] Connect output of SysV scripts to console\n"
" --log-target=TARGET Set log target (console, syslog, 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",
program_invocation_short_name);
return 0;
}
static int prepare_reexecute(Manager *m, FILE **_f, FDSet **_fds) {
FILE *f = NULL;
FDSet *fds = NULL;
int r;
assert(m);
assert(_f);
assert(_fds);
if ((r = manager_open_serialization(m, &f)) < 0) {
log_error("Failed to create serialization faile: %s", strerror(-r));
goto fail;
}
if (!(fds = fdset_new())) {
r = -ENOMEM;
log_error("Failed to allocate fd set: %s", strerror(-r));
goto fail;
}
if ((r = manager_serialize(m, f, fds)) < 0) {
log_error("Failed to serialize state: %s", strerror(-r));
goto fail;
}
if (fseeko(f, 0, SEEK_SET) < 0) {
log_error("Failed to rewind serialization fd: %m");
goto fail;
}
if ((r = fd_cloexec(fileno(f), false)) < 0) {
log_error("Failed to disable O_CLOEXEC for serialization: %s", strerror(-r));
goto fail;
}
if ((r = fdset_cloexec(fds, false)) < 0) {
log_error("Failed to disable O_CLOEXEC for serialization fds: %s", strerror(-r));
goto fail;
}
*_f = f;
*_fds = fds;
return 0;
fail:
fdset_free(fds);
if (f)
fclose(f);
return r;
}
int main(int argc, char *argv[]) {
Manager *m = NULL;
int r, retval = 1;
FDSet *fds = NULL;
bool reexecute = false;
if (getpid() != 1 && strstr(program_invocation_short_name, "init")) {
/* This is compatbility support for SysV, where
* calling init as a user is identical to telinit. */
errno = -ENOENT;
execv(SYSTEMCTL_BINARY_PATH, argv);
log_error("Failed to exec " SYSTEMCTL_BINARY_PATH ": %m");
return 1;
}
log_show_color(isatty(STDERR_FILENO) > 0);
log_show_location(false);
log_set_max_level(LOG_INFO);
if (getpid() == 1) {
arg_running_as = MANAGER_SYSTEM;
log_set_target(LOG_TARGET_SYSLOG_OR_KMSG);
if (label_init() < 0)
goto finish;
} else {
arg_running_as = MANAGER_SESSION;
log_set_target(LOG_TARGET_CONSOLE);
}
if (set_default_unit(SPECIAL_DEFAULT_TARGET) < 0)
goto finish;
/* Mount /proc, /sys and friends, so that /proc/cmdline and
* /proc/$PID/fd is available. */
if (geteuid() == 0 && !getenv("SYSTEMD_SKIP_API_MOUNTS"))
if (mount_setup() < 0)
goto finish;
/* Reset all signal handlers. */
assert_se(reset_all_signal_handlers() == 0);
/* If we are init, we can block sigkill. Yay. */
ignore_signals(SIGNALS_IGNORE, -1);
if (parse_config_file() < 0)
goto finish;
if (arg_running_as == MANAGER_SYSTEM)
if (parse_proc_cmdline() < 0)
goto finish;
log_parse_environment();
if (parse_argv(argc, argv) < 0)
goto finish;
if (arg_action == ACTION_HELP) {
retval = help();
goto finish;
} else if (arg_action == ACTION_DUMP_CONFIGURATION_ITEMS) {
unit_dump_config_items(stdout);
retval = 0;
goto finish;
} else if (arg_action == ACTION_DONE) {
retval = 0;
goto finish;
}
assert_se(arg_action == ACTION_RUN || arg_action == ACTION_TEST);
/* Remember open file descriptors for later deserialization */
if (serialization) {
if ((r = fdset_new_fill(&fds)) < 0) {
log_error("Failed to allocate fd set: %s", strerror(-r));
goto finish;
}
assert_se(fdset_remove(fds, fileno(serialization)) >= 0);
} else
close_all_fds(NULL, 0);
/* Set up PATH unless it is already set */
setenv("PATH",
"/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin",
arg_running_as == MANAGER_SYSTEM);
/* Move out of the way, so that we won't block unmounts */
assert_se(chdir("/") == 0);
if (arg_running_as == MANAGER_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 */
dbus_connection_set_change_sigpipe(FALSE);
/* Reset the console, but only if this is really init and we
* are freshly booted */
if (arg_running_as == MANAGER_SYSTEM && arg_action == ACTION_RUN) {
console_setup(getpid() == 1 && !serialization);
make_null_stdio();
}
/* Open the logging devices, if possible and necessary */
log_open();
/* Make sure we leave a core dump without panicing the
* kernel. */
if (getpid() == 1)
install_crash_handler();
log_full(arg_running_as == MANAGER_SYSTEM ? LOG_INFO : LOG_DEBUG,
PACKAGE_STRING " running in %s mode. (" SYSTEMD_FEATURES ")", manager_running_as_to_string(arg_running_as));
if (arg_running_as == MANAGER_SYSTEM && !serialization) {
if (arg_show_status)
status_welcome();
kmod_setup();
hostname_setup();
loopback_setup();
}
if ((r = manager_new(arg_running_as, &m)) < 0) {
log_error("Failed to allocate manager object: %s", strerror(-r));
goto finish;
}
m->confirm_spawn = arg_confirm_spawn;
m->show_status = arg_show_status;
m->sysv_console = arg_sysv_console;
m->mount_auto = arg_mount_auto;
m->swap_auto = arg_swap_auto;
if (arg_console)
manager_set_console(m, arg_console);
if ((r = manager_startup(m, serialization, fds)) < 0)
log_error("Failed to fully start up daemon: %s", strerror(-r));
if (fds) {
/* This will close all file descriptors that were opened, but
* not claimed by any unit. */
fdset_free(fds);
fds = NULL;
}
if (serialization) {
fclose(serialization);
serialization = NULL;
} else {
DBusError error;
Unit *target = NULL;
dbus_error_init(&error);
log_debug("Activating default unit: %s", arg_default_unit);
if ((r = manager_load_unit(m, arg_default_unit, NULL, &error, &target)) < 0) {
log_error("Failed to load default target: %s", bus_error(&error, r));
dbus_error_free(&error);
} else if (target->meta.load_state != UNIT_LOADED)
log_error("Failed to load default target: %s", strerror(-target->meta.load_error));
if (!target || target->meta.load_state != UNIT_LOADED) {
log_info("Trying to load rescue target...");
if ((r = manager_load_unit(m, SPECIAL_RESCUE_TARGET, NULL, &error, &target)) < 0) {
log_error("Failed to load rescue target: %s", bus_error(&error, r));
dbus_error_free(&error);
goto finish;
} else if (target->meta.load_state != UNIT_LOADED) {
log_error("Failed to load rescue target: %s", strerror(-target->meta.load_error));
goto finish;
}
}
if (arg_action == ACTION_TEST) {
printf("-> By units:\n");
manager_dump_units(m, stdout, "\t");
}
if ((r = manager_add_job(m, JOB_START, target, JOB_REPLACE, false, &error, NULL)) < 0) {
log_error("Failed to start default target: %s", bus_error(&error, r));
dbus_error_free(&error);
goto finish;
}
if (arg_action == ACTION_TEST) {
printf("-> By jobs:\n");
manager_dump_jobs(m, stdout, "\t");
retval = 0;
goto finish;
}
}
for (;;) {
if ((r = manager_loop(m)) < 0) {
log_error("Failed to run mainloop: %s", strerror(-r));
goto finish;
}
switch (m->exit_code) {
case MANAGER_EXIT:
retval = 0;
log_debug("Exit.");
goto finish;
case MANAGER_RELOAD:
log_info("Reloading.");
if ((r = manager_reload(m)) < 0)
log_error("Failed to reload: %s", strerror(-r));
break;
case MANAGER_REEXECUTE:
if (prepare_reexecute(m, &serialization, &fds) < 0)
goto finish;
reexecute = true;
log_notice("Reexecuting.");
goto finish;
default:
assert_not_reached("Unknown exit code.");
}
}
finish:
if (m)
manager_free(m);
free(arg_default_unit);
free(arg_console);
dbus_shutdown();
label_finish();
if (reexecute) {
const char *args[15];
unsigned i = 0;
char sfd[16];
assert(serialization);
assert(fds);
args[i++] = SYSTEMD_BINARY_PATH;
args[i++] = "--log-level";
args[i++] = log_level_to_string(log_get_max_level());
args[i++] = "--log-target";
args[i++] = log_target_to_string(log_get_target());
if (arg_running_as == MANAGER_SYSTEM)
args[i++] = "--system";
else
args[i++] = "--session";
if (arg_dump_core)
args[i++] = "--dump-core";
if (arg_crash_shell)
args[i++] = "--crash-shell";
if (arg_confirm_spawn)
args[i++] = "--confirm-spawn";
if (arg_show_status)
args[i++] = "--show-status=1";
else
args[i++] = "--show-status=0";
if (arg_sysv_console)
args[i++] = "--sysv-console=1";
else
args[i++] = "--sysv-console=0";
snprintf(sfd, sizeof(sfd), "%i", fileno(serialization));
char_array_0(sfd);
args[i++] = "--deserialize";
args[i++] = sfd;
args[i++] = NULL;
assert(i <= ELEMENTSOF(args));
execv(args[0], (char* const*) args);
log_error("Failed to reexecute: %m");
}
if (serialization)
fclose(serialization);
if (fds)
fdset_free(fds);
if (getpid() == 1)
freeze();
return retval;
}