X-Git-Url: http://www.chiark.greenend.org.uk/ucgi/~ianmdlvl/git?p=elogind.git;a=blobdiff_plain;f=src%2Fcore%2Fmain.c;h=546582cff02af96391f36099e386fa87bd56b5fc;hp=4c2be261f5130393be24cf8345ac729cdc2b1a7d;hb=66a78c2b95ba6cc0be15dab68c5af816fb5b7a33;hpb=78a825f216d39ee0295b00647b059d45467e1d02 diff --git a/src/core/main.c b/src/core/main.c index 4c2be261f..546582cff 100644 --- a/src/core/main.c +++ b/src/core/main.c @@ -32,17 +32,10 @@ #include #include #include +#include #include "manager.h" #include "log.h" -#include "mount-setup.h" -#include "hostname-setup.h" -#include "loopback-setup.h" -#include "kmod-setup.h" -#include "locale-setup.h" -#include "selinux-setup.h" -#include "ima-setup.h" -#include "machine-id-util.h" #include "load-fragment.h" #include "fdset.h" #include "special.h" @@ -55,6 +48,19 @@ #include "def.h" #include "virt.h" #include "watchdog.h" +#include "path-util.h" +#include "switch-root.h" +#include "capability.h" + +#include "mount-setup.h" +#include "loopback-setup.h" +#include "kmod-setup.h" +#include "hostname-setup.h" +#include "machine-id-setup.h" +#include "locale-setup.h" +#include "hwclock.h" +#include "selinux-setup.h" +#include "ima-setup.h" static enum { ACTION_RUN, @@ -72,17 +78,19 @@ 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; #ifdef HAVE_SYSV_COMPAT static bool arg_sysv_console = true; #endif -static bool arg_mount_auto = true; -static bool arg_swap_auto = true; static char **arg_default_controllers = NULL; static char ***arg_join_controllers = NULL; static ExecOutput arg_default_std_output = EXEC_OUTPUT_JOURNAL; static ExecOutput arg_default_std_error = EXEC_OUTPUT_INHERIT; static usec_t arg_runtime_watchdog = 0; static usec_t arg_shutdown_watchdog = 10 * USEC_PER_MINUTE; +static struct rlimit *arg_default_rlimit[RLIMIT_NLIMITS] = {}; +static uint64_t arg_capability_bounding_set_drop = 0; +static nsec_t arg_timer_slack_nsec = (nsec_t) -1; static FILE* serialization = NULL; @@ -225,11 +233,13 @@ static int set_default_unit(const char *u) { assert(u); - if (!(c = strdup(u))) + c = strdup(u); + if (!c) return -ENOMEM; free(arg_default_unit); arg_default_unit = c; + return 0; } @@ -251,10 +261,17 @@ static int parse_proc_cmdline_word(const char *word) { assert(word); - if (startswith(word, "systemd.unit=")) - return set_default_unit(word + 13); + if (startswith(word, "systemd.unit=")) { + + if (!in_initrd()) + return set_default_unit(word + 13); + + } else if (startswith(word, "rd.systemd.unit=")) { - else if (startswith(word, "systemd.log_target=")) { + if (in_initrd()) + return set_default_unit(word + 16); + + } 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); @@ -357,12 +374,14 @@ static int parse_proc_cmdline_word(const char *word) { arg_sysv_console = r; #endif - } else if (startswith(word, "systemd.")) { + } else if (startswith(word, "systemd.") || + (in_initrd() && startswith(word, "rd.systemd."))) { log_warning("Unknown kernel switch %s. Ignoring.", word); 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" @@ -379,14 +398,15 @@ static int parse_proc_cmdline_word(const char *word) { "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"); + " Set default log error output for services\n" + "systemd.setenv=ASSIGNMENT Set an environment variable for all spawned processes\n"); } else if (streq(word, "quiet")) { arg_show_status = false; #ifdef HAVE_SYSV_COMPAT arg_sysv_console = false; #endif - } else { + } else if (!in_initrd()) { unsigned i; /* SysV compatibility */ @@ -657,14 +677,30 @@ static int parse_config_file(void) { #endif { "Manager", "CrashChVT", config_parse_int, 0, &arg_crash_chvt }, { "Manager", "CPUAffinity", config_parse_cpu_affinity2, 0, NULL }, - { "Manager", "MountAuto", config_parse_bool, 0, &arg_mount_auto }, - { "Manager", "SwapAuto", config_parse_bool, 0, &arg_swap_auto }, { "Manager", "DefaultControllers", config_parse_strv, 0, &arg_default_controllers }, { "Manager", "DefaultStandardOutput", config_parse_output, 0, &arg_default_std_output }, { "Manager", "DefaultStandardError", config_parse_output, 0, &arg_default_std_error }, { "Manager", "JoinControllers", config_parse_join_controllers, 0, &arg_join_controllers }, { "Manager", "RuntimeWatchdogSec", config_parse_usec, 0, &arg_runtime_watchdog }, { "Manager", "ShutdownWatchdogSec", config_parse_usec, 0, &arg_shutdown_watchdog }, + { "Manager", "CapabilityBoundingSet", config_parse_bounding_set, 0, &arg_capability_bounding_set_drop }, + { "Manager", "TimerSlackNSec", config_parse_nsec, 0, &arg_timer_slack_nsec }, + { "Manager", "DefaultLimitCPU", config_parse_limit, 0, &arg_default_rlimit[RLIMIT_CPU]}, + { "Manager", "DefaultLimitFSIZE", config_parse_limit, 0, &arg_default_rlimit[RLIMIT_FSIZE]}, + { "Manager", "DefaultLimitDATA", config_parse_limit, 0, &arg_default_rlimit[RLIMIT_DATA]}, + { "Manager", "DefaultLimitSTACK", config_parse_limit, 0, &arg_default_rlimit[RLIMIT_STACK]}, + { "Manager", "DefaultLimitCORE", config_parse_limit, 0, &arg_default_rlimit[RLIMIT_CORE]}, + { "Manager", "DefaultLimitRSS", config_parse_limit, 0, &arg_default_rlimit[RLIMIT_RSS]}, + { "Manager", "DefaultLimitNOFILE", config_parse_limit, 0, &arg_default_rlimit[RLIMIT_NOFILE]}, + { "Manager", "DefaultLimitAS", config_parse_limit, 0, &arg_default_rlimit[RLIMIT_AS]}, + { "Manager", "DefaultLimitNPROC", config_parse_limit, 0, &arg_default_rlimit[RLIMIT_NPROC]}, + { "Manager", "DefaultLimitMEMLOCK", config_parse_limit, 0, &arg_default_rlimit[RLIMIT_MEMLOCK]}, + { "Manager", "DefaultLimitLOCKS", config_parse_limit, 0, &arg_default_rlimit[RLIMIT_LOCKS]}, + { "Manager", "DefaultLimitSIGPENDING",config_parse_limit, 0, &arg_default_rlimit[RLIMIT_SIGPENDING]}, + { "Manager", "DefaultLimitMSGQUEUE", config_parse_limit, 0, &arg_default_rlimit[RLIMIT_MSGQUEUE]}, + { "Manager", "DefaultLimitNICE", config_parse_limit, 0, &arg_default_rlimit[RLIMIT_NICE]}, + { "Manager", "DefaultLimitRTPRIO", config_parse_limit, 0, &arg_default_rlimit[RLIMIT_RTPRIO]}, + { "Manager", "DefaultLimitRTTIME", config_parse_limit, 0, &arg_default_rlimit[RLIMIT_RTTIME]}, { NULL, NULL, NULL, 0, NULL } }; @@ -746,6 +782,7 @@ static int parse_argv(int argc, char *argv[]) { ARG_SHOW_STATUS, ARG_SYSV_CONSOLE, ARG_DESERIALIZE, + ARG_SWITCHED_ROOT, ARG_INTROSPECT, ARG_DEFAULT_STD_OUTPUT, ARG_DEFAULT_STD_ERROR @@ -762,14 +799,15 @@ static int parse_argv(int argc, char *argv[]) { { "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 }, + { "dump-core", optional_argument, NULL, ARG_DUMP_CORE }, + { "crash-shell", optional_argument, NULL, ARG_CRASH_SHELL }, + { "confirm-spawn", optional_argument, NULL, ARG_CONFIRM_SPAWN }, { "show-status", optional_argument, NULL, ARG_SHOW_STATUS }, #ifdef HAVE_SYSV_COMPAT { "sysv-console", optional_argument, NULL, ARG_SYSV_CONSOLE }, #endif { "deserialize", required_argument, NULL, ARG_DESERIALIZE }, + { "switched-root", no_argument, NULL, ARG_SWITCHED_ROOT }, { "introspect", optional_argument, NULL, ARG_INTROSPECT }, { "default-standard-output", required_argument, NULL, ARG_DEFAULT_STD_OUTPUT, }, { "default-standard-error", required_argument, NULL, ARG_DEFAULT_STD_ERROR, }, @@ -873,39 +911,49 @@ static int parse_argv(int argc, char *argv[]) { break; case ARG_DUMP_CORE: - arg_dump_core = true; + r = optarg ? parse_boolean(optarg) : 1; + if (r < 0) { + log_error("Failed to parse dump core boolean %s.", optarg); + return r; + } + arg_dump_core = r; break; case ARG_CRASH_SHELL: - arg_crash_shell = true; + r = optarg ? parse_boolean(optarg) : 1; + if (r < 0) { + log_error("Failed to parse crash shell boolean %s.", optarg); + return r; + } + arg_crash_shell = r; break; case ARG_CONFIRM_SPAWN: - arg_confirm_spawn = true; + r = optarg ? parse_boolean(optarg) : 1; + if (r < 0) { + log_error("Failed to parse confirm spawn boolean %s.", optarg); + return r; + } + arg_confirm_spawn = r; 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; + r = optarg ? parse_boolean(optarg) : 1; + if (r < 0) { + log_error("Failed to parse show status boolean %s.", optarg); + return r; + } + arg_show_status = r; break; + #ifdef HAVE_SYSV_COMPAT 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; + r = optarg ? parse_boolean(optarg) : 1; + if (r < 0) { + log_error("Failed to parse SysV console boolean %s.", optarg); + return r; + } + arg_sysv_console = r; break; #endif @@ -931,6 +979,10 @@ static int parse_argv(int argc, char *argv[]) { break; } + case ARG_SWITCHED_ROOT: + arg_switched_root = true; + break; + case ARG_INTROSPECT: { const char * const * i = NULL; @@ -1016,9 +1068,9 @@ static int help(void) { " --unit=UNIT Set default unit\n" " --system Run a system instance, even if PID != 1\n" " --user Run a user 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" + " --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" #ifdef HAVE_SYSV_COMPAT " --sysv-console[=0|1] Connect output of SysV scripts to console\n" @@ -1167,10 +1219,12 @@ int main(int argc, char *argv[]) { const char *shutdown_verb = NULL; dual_timestamp initrd_timestamp = { 0ULL, 0ULL }; static char systemd[] = "systemd"; - bool is_reexec = false; + bool skip_setup = false; int j; bool loaded_policy = false; bool arm_reboot_watchdog = false; + bool queue_default_job = false; + char *switch_root_dir = NULL, *switch_root_init = NULL; #ifdef HAVE_SYSV_COMPAT if (getpid() != 1 && strstr(program_invocation_short_name, "init")) { @@ -1187,10 +1241,17 @@ int main(int argc, char *argv[]) { /* 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++) if (streq(argv[j], "--deserialize")) { - is_reexec = true; + skip_setup = true; + break; + } + + /* If we have switched root, do all the special setup + * things */ + for (j = 1; j < argc; j++) + if (streq(argv[j], "--switched-root")) { + skip_setup = false; break; } @@ -1209,10 +1270,23 @@ int main(int argc, char *argv[]) { log_set_max_level(LOG_INFO); if (getpid() == 1) { + if (in_initrd()) { + char *rd_timestamp = NULL; + + dual_timestamp_get(&initrd_timestamp); + asprintf(&rd_timestamp, "%llu %llu", + (unsigned long long) initrd_timestamp.realtime, + (unsigned long long) initrd_timestamp.monotonic); + if (rd_timestamp) { + setenv("RD_TIMESTAMP", rd_timestamp, 1); + free(rd_timestamp); + } + } + arg_running_as = MANAGER_SYSTEM; - log_set_target(detect_container(NULL) > 0 ? LOG_TARGET_CONSOLE : LOG_TARGET_JOURNAL_OR_KMSG); + log_set_target(detect_container(NULL) > 0 ? LOG_TARGET_JOURNAL : LOG_TARGET_JOURNAL_OR_KMSG); - if (!is_reexec) { + if (!skip_setup) { if (selinux_setup(&loaded_policy) < 0) goto finish; if (ima_setup() < 0) @@ -1221,10 +1295,10 @@ int main(int argc, char *argv[]) { log_open(); - if (label_init() < 0) + if (label_init(NULL) < 0) goto finish; - if (!is_reexec) + if (!skip_setup) if (hwclock_is_localtime() > 0) { int min; @@ -1336,7 +1410,8 @@ int main(int argc, char *argv[]) { /* Parse the data passed to us. We leave this * variables set, but the manager later on will not * pass them on to our children. */ - parse_initrd_timestamp(&initrd_timestamp); + if (!in_initrd()) + parse_initrd_timestamp(&initrd_timestamp); /* Unset some environment variables passed in from the * kernel that don't really make sense for us. */ @@ -1349,6 +1424,12 @@ int main(int argc, char *argv[]) { 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"); + /* All other variables are left as is, so that clients * can still read them via /proc/1/environ */ } @@ -1370,7 +1451,7 @@ int main(int argc, char *argv[]) { /* 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 && !is_reexec); + console_setup(getpid() == 1 && !skip_setup); make_null_stdio(); } @@ -1391,7 +1472,7 @@ int main(int argc, char *argv[]) { log_full(arg_running_as == MANAGER_SYSTEM ? LOG_INFO : LOG_DEBUG, PACKAGE_STRING " running in %s mode. (" SYSTEMD_FEATURES "; " DISTRIBUTION ")", manager_running_as_to_string(arg_running_as)); - if (arg_running_as == MANAGER_SYSTEM && !is_reexec) { + if (arg_running_as == MANAGER_SYSTEM && !skip_setup) { locale_setup(); if (arg_show_status || plymouth_running()) @@ -1410,6 +1491,23 @@ int main(int argc, char *argv[]) { if (arg_running_as == MANAGER_SYSTEM && arg_runtime_watchdog > 0) watchdog_set_timeout(&arg_runtime_watchdog); + if (arg_timer_slack_nsec != (nsec_t) -1) + if (prctl(PR_SET_TIMERSLACK, arg_timer_slack_nsec) < 0) + log_error("Failed to adjust timer slack: %m"); + + if (arg_capability_bounding_set_drop) { + r = capability_bounding_set_drop(arg_capability_bounding_set_drop, true); + if (r < 0) { + log_error("Failed to drop capability bounding set: %s", strerror(-r)); + goto finish; + } + r = capability_bounding_set_drop_usermode(arg_capability_bounding_set_drop); + if (r < 0) { + log_error("Failed to drop capability bounding set of usermode helpers: %s", strerror(-r)); + goto finish; + } + } + r = manager_new(arg_running_as, &m); if (r < 0) { log_error("Failed to allocate manager object: %s", strerror(-r)); @@ -1420,13 +1518,13 @@ int main(int argc, char *argv[]) { #ifdef HAVE_SYSV_COMPAT m->sysv_console = arg_sysv_console; #endif - m->mount_auto = arg_mount_auto; - m->swap_auto = arg_swap_auto; m->default_std_output = arg_default_std_output; m->default_std_error = arg_default_std_error; m->runtime_watchdog = arg_runtime_watchdog; m->shutdown_watchdog = arg_shutdown_watchdog; + manager_set_default_rlimits(m, arg_default_rlimit); + if (dual_timestamp_is_set(&initrd_timestamp)) m->initrd_timestamp = initrd_timestamp; @@ -1435,16 +1533,18 @@ int main(int argc, char *argv[]) { manager_set_show_status(m, arg_show_status); + /* Remember whether we should queue the default job */ + queue_default_job = !serialization || arg_switched_root; + before_startup = now(CLOCK_MONOTONIC); r = manager_startup(m, serialization, fds); if (r < 0) log_error("Failed to fully start up daemon: %s", strerror(-r)); + /* This will close all file descriptors that were opened, but + * not claimed by any unit. */ if (fds) { - /* This will close all file descriptors that were opened, but - * not claimed by any unit. */ - fdset_free(fds); fds = NULL; } @@ -1452,7 +1552,9 @@ int main(int argc, char *argv[]) { if (serialization) { fclose(serialization); serialization = NULL; - } else { + } + + if (queue_default_job) { DBusError error; Unit *target = NULL; Job *default_unit_job; @@ -1537,6 +1639,7 @@ int main(int argc, char *argv[]) { break; case MANAGER_REEXECUTE: + if (prepare_reexecute(m, &serialization, &fds) < 0) goto finish; @@ -1544,6 +1647,20 @@ int main(int argc, char *argv[]) { log_notice("Reexecuting."); goto finish; + case MANAGER_SWITCH_ROOT: + /* Steal the switch root parameters */ + switch_root_dir = m->switch_root; + switch_root_init = m->switch_root_init; + m->switch_root = m->switch_root_init = NULL; + + if (!switch_root_init) + if (prepare_reexecute(m, &serialization, &fds) < 0) + goto finish; + + reexecute = true; + log_notice("Switching root."); + goto finish; + case MANAGER_REBOOT: case MANAGER_POWEROFF: case MANAGER_HALT: @@ -1571,74 +1688,100 @@ finish: if (m) manager_free(m); + for (j = 0; j < RLIMIT_NLIMITS; j++) + free (arg_default_rlimit[j]); + free(arg_default_unit); strv_free(arg_default_controllers); free_join_controllers(); dbus_shutdown(); - label_finish(); if (reexecute) { - const char *args[15]; - unsigned i = 0; - char sfd[16]; + const char **args; + unsigned i, args_size; - assert(serialization); - assert(fds); + /* 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); - args[i++] = SYSTEMD_BINARY_PATH; + if (switch_root_dir) { + r = switch_root(switch_root_dir); + if (r < 0) + log_error("Failed to switch root, ignoring: %s", strerror(-r)); + } - args[i++] = "--log-level"; - args[i++] = log_level_to_string(log_get_max_level()); + args_size = MAX(6, argc+1); + args = newa(const char*, args_size); - args[i++] = "--log-target"; - args[i++] = log_target_to_string(log_get_target()); + if (!switch_root_init) { + char sfd[16]; - if (arg_running_as == MANAGER_SYSTEM) - args[i++] = "--system"; - else - args[i++] = "--user"; + /* 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. */ - if (arg_dump_core) - args[i++] = "--dump-core"; + assert(serialization); + assert(fds); - if (arg_crash_shell) - args[i++] = "--crash-shell"; + snprintf(sfd, sizeof(sfd), "%i", fileno(serialization)); + char_array_0(sfd); - if (arg_confirm_spawn) - args[i++] = "--confirm-spawn"; + i = 0; + args[i++] = SYSTEMD_BINARY_PATH; + if (switch_root_dir) + args[i++] = "--switched-root"; + args[i++] = arg_running_as == MANAGER_SYSTEM ? "--system" : "--user"; + args[i++] = "--deserialize"; + args[i++] = sfd; + args[i++] = NULL; - if (arg_show_status) - args[i++] = "--show-status=1"; - else - args[i++] = "--show-status=0"; + assert(i <= args_size); + execv(args[0], (char* const*) args); + } -#ifdef HAVE_SYSV_COMPAT - if (arg_sysv_console) - args[i++] = "--sysv-console=1"; - else - args[i++] = "--sysv-console=0"; -#endif + /* 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.) */ - snprintf(sfd, sizeof(sfd), "%i", fileno(serialization)); - char_array_0(sfd); + if (serialization) { + fclose(serialization); + serialization = NULL; + } - args[i++] = "--deserialize"; - args[i++] = sfd; + if (fds) { + fdset_free(fds); + fds = NULL; + } + for (j = 1, i = 1; j < argc; j++) + args[i++] = argv[j]; args[i++] = NULL; + assert(i <= args_size); - assert(i <= ELEMENTSOF(args)); - - /* 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); + if (switch_root_init) { + args[0] = switch_root_init; + execv(args[0], (char* const*) args); + log_warning("Failed to execute configured init, trying fallback: %m"); + } + args[0] = "/sbin/init"; execv(args[0], (char* const*) args); - log_error("Failed to reexecute: %m"); + if (errno == ENOENT) { + log_warning("No /sbin/init, trying fallback"); + + args[0] = "/bin/sh"; + args[1] = NULL; + execv(args[0], (char* const*) args); + log_error("Failed to execute /bin/sh, giving up: %m"); + } else + log_warning("Failed to execute /sbin/init, giving up: %m"); } if (serialization)