}
}
- for (;;) {
- r = manager_loop(m);
+ if (arg_system && arg_no_new_privs) {
+ if (prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0) < 0) {
+ *ret_error_message = "Failed to disable new privileges";
+ return log_emergency_errno(errno, "Failed to disable new privileges: %m");
+ }
+ }
+
+ if (arg_syscall_archs) {
+ r = enforce_syscall_archs(arg_syscall_archs);
if (r < 0) {
- log_emergency_errno(r, "Failed to run main loop: %m");
- error_message = "Failed to run main loop";
- goto finish;
+ *ret_error_message = "Failed to set syscall architectures";
+ return r;
}
+ }
- switch (m->exit_code) {
+ if (!arg_system)
+ /* Become reaper of our children */
+ if (prctl(PR_SET_CHILD_SUBREAPER, 1) < 0)
+ log_warning_errno(errno, "Failed to make us a subreaper: %m");
- case MANAGER_RELOAD:
- log_info("Reloading.");
+ /* Bump up RLIMIT_NOFILE for systemd itself */
+ (void) bump_rlimit_nofile(saved_rlimit_nofile);
+ (void) bump_rlimit_memlock(saved_rlimit_memlock);
- r = parse_config_file();
- if (r < 0)
- log_error("Failed to parse config file.");
+ return 0;
+ }
- manager_set_defaults(m);
+ static int do_queue_default_job(
+ Manager *m,
+ const char **ret_error_message) {
- r = manager_reload(m);
- if (r < 0)
- log_error_errno(r, "Failed to reload: %m");
- break;
+ _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
+ Job *default_unit_job;
+ Unit *target = NULL;
+ int r;
- case MANAGER_REEXECUTE:
+ log_debug("Activating default unit: %s", arg_default_unit);
- if (prepare_reexecute(m, &arg_serialization, &fds, false) < 0) {
- error_message = "Failed to prepare for reexecution";
- goto finish;
- }
+ r = manager_load_startable_unit_or_warn(m, arg_default_unit, NULL, &target);
+ if (r < 0) {
+ log_info("Falling back to rescue target: " SPECIAL_RESCUE_TARGET);
- reexecute = true;
- log_notice("Reexecuting.");
- goto finish;
+ r = manager_load_startable_unit_or_warn(m, SPECIAL_RESCUE_TARGET, NULL, &target);
+ if (r < 0) {
+ *ret_error_message = r == -ERFKILL ? "Rescue target masked"
+ : "Failed to load rescue target";
+ return r;
+ }
+ }
- 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;
+ assert(target->load_state == UNIT_LOADED);
- if (!switch_root_init)
- if (prepare_reexecute(m, &arg_serialization, &fds, true) < 0) {
- error_message = "Failed to prepare for reexecution";
- goto finish;
- }
+ r = manager_add_job(m, JOB_START, target, JOB_ISOLATE, NULL, &error, &default_unit_job);
+ if (r == -EPERM) {
+ log_debug_errno(r, "Default target could not be isolated, starting instead: %s", bus_error_message(&error, r));
- reexecute = true;
- log_notice("Switching root.");
- goto finish;
+ sd_bus_error_free(&error);
- case MANAGER_EXIT:
- retval = m->return_value;
+ r = manager_add_job(m, JOB_START, target, JOB_REPLACE, NULL, &error, &default_unit_job);
+ if (r < 0) {
+ *ret_error_message = "Failed to start default target";
+ return log_emergency_errno(r, "Failed to start default target: %s", bus_error_message(&error, r));
+ }
- if (MANAGER_IS_USER(m)) {
- log_debug("Exit.");
- goto finish;
- }
+ } else if (r < 0) {
+ *ret_error_message = "Failed to isolate default target";
+ return log_emergency_errno(r, "Failed to isolate default target: %s", bus_error_message(&error, r));
+ }
- /* fallthrough */
- case MANAGER_REBOOT:
- case MANAGER_POWEROFF:
- case MANAGER_HALT:
- case MANAGER_KEXEC: {
- static const char * const table[_MANAGER_EXIT_CODE_MAX] = {
- [MANAGER_EXIT] = "exit",
- [MANAGER_REBOOT] = "reboot",
- [MANAGER_POWEROFF] = "poweroff",
- [MANAGER_HALT] = "halt",
- [MANAGER_KEXEC] = "kexec"
- };
+ m->default_unit_job_id = default_unit_job->id;
- assert_se(shutdown_verb = table[m->exit_code]);
- arm_reboot_watchdog = m->exit_code == MANAGER_REBOOT;
+ return 0;
+ }
- log_notice("Shutting down.");
- goto finish;
- }
+ static void save_rlimits(struct rlimit *saved_rlimit_nofile,
+ struct rlimit *saved_rlimit_memlock) {
- default:
- assert_not_reached("Unknown exit code.");
- }
+ assert(saved_rlimit_nofile);
+ assert(saved_rlimit_memlock);
+
+ if (getrlimit(RLIMIT_NOFILE, saved_rlimit_nofile) < 0)
+ log_warning_errno(errno, "Reading RLIMIT_NOFILE failed, ignoring: %m");
+
+ if (getrlimit(RLIMIT_MEMLOCK, saved_rlimit_memlock) < 0)
+ log_warning_errno(errno, "Reading RLIMIT_MEMLOCK failed, ignoring: %m");
+ }
+
+ static void fallback_rlimit_nofile(const struct rlimit *saved_rlimit_nofile) {
+ struct rlimit *rl;
+
+ if (arg_default_rlimit[RLIMIT_NOFILE])
+ return;
+
+ /* Make sure forked processes get limits based on the original kernel setting */
+
+ rl = newdup(struct rlimit, saved_rlimit_nofile, 1);
+ if (!rl) {
+ log_oom();
+ return;
}
- finish:
- pager_close();
+ /* Bump the hard limit for system services to a substantially higher value. The default
+ * hard limit current kernels set is pretty low (4K), mostly for historical
+ * reasons. According to kernel developers, the fd handling in recent kernels has been
+ * optimized substantially enough, so that we can bump the limit now, without paying too
+ * high a price in memory or performance. Note however that we only bump the hard limit,
+ * not the soft limit. That's because select() works the way it works, and chokes on fds
+ * >= 1024. If we'd bump the soft limit globally, it might accidentally happen to
+ * unexpecting programs that they get fds higher than what they can process using
+ * select(). By only bumping the hard limit but leaving the low limit as it is we avoid
+ * this pitfall: programs that are written by folks aware of the select() problem in mind
+ * (and thus use poll()/epoll instead of select(), the way everybody should) can
+ * explicitly opt into high fds by bumping their soft limit beyond 1024, to the hard limit
+ * we pass. */
+ if (arg_system) {
+ int nr;
+
+ /* Get the underlying absolute limit the kernel enforces */
+ nr = read_nr_open();
+
+ rl->rlim_max = MIN((rlim_t) nr, MAX(rl->rlim_max, (rlim_t) HIGH_RLIMIT_NOFILE));
+ }
+
+ /* If for some reason we were invoked with a soft limit above 1024 (which should never
+ * happen!, but who knows what we get passed in from pam_limit when invoked as --user
+ * instance), then lower what we pass on to not confuse our children */
+ rl->rlim_cur = MIN(rl->rlim_cur, (rlim_t) FD_SETSIZE);
+
+ arg_default_rlimit[RLIMIT_NOFILE] = rl;
+ }
+
+ static void fallback_rlimit_memlock(const struct rlimit *saved_rlimit_memlock) {
+ struct rlimit *rl;
- if (m)
- arg_shutdown_watchdog = m->shutdown_watchdog;
+ /* Pass the original value down to invoked processes */
- m = manager_free(m);
+ if (arg_default_rlimit[RLIMIT_MEMLOCK])
+ return;
+
+ rl = newdup(struct rlimit, saved_rlimit_memlock, 1);
+ if (!rl) {
+ log_oom();
+ return;
+ }
- for (j = 0; j < ELEMENTSOF(arg_default_rlimit); j++)
- arg_default_rlimit[j] = mfree(arg_default_rlimit[j]);
+ arg_default_rlimit[RLIMIT_MEMLOCK] = rl;
+ }
+
+ static void reset_arguments(void) {
+ /* Frees/resets arg_* variables, with a few exceptions commented below. */
arg_default_unit = mfree(arg_default_unit);
- arg_join_controllers = strv_free_free(arg_join_controllers);
+
+ /* arg_system — ignore */
+
+ arg_dump_core = true;
+ arg_crash_chvt = -1;
+ arg_crash_shell = false;
+ arg_crash_reboot = false;
+ arg_confirm_spawn = mfree(arg_confirm_spawn);
+ arg_show_status = _SHOW_STATUS_INVALID;
+ arg_status_unit_format = STATUS_UNIT_FORMAT_DEFAULT;
+ arg_switched_root = false;
+ arg_pager_flags = 0;
+ arg_service_watchdogs = true;
+ arg_default_std_output = EXEC_OUTPUT_JOURNAL;
+ arg_default_std_error = EXEC_OUTPUT_INHERIT;
+ arg_default_restart_usec = DEFAULT_RESTART_USEC;
+ arg_default_timeout_start_usec = DEFAULT_TIMEOUT_USEC;
+ arg_default_timeout_stop_usec = DEFAULT_TIMEOUT_USEC;
+ arg_default_timeout_abort_usec = DEFAULT_TIMEOUT_USEC;
+ arg_default_timeout_abort_set = false;
+ arg_default_start_limit_interval = DEFAULT_START_LIMIT_INTERVAL;
+ arg_default_start_limit_burst = DEFAULT_START_LIMIT_BURST;
+ arg_runtime_watchdog = 0;
- arg_reboot_watchdog = 10 * USEC_PER_MINUTE;
++ arg_reboot_watchdog = 0; /* 10 * USEC_PER_MINUTE; */
+ arg_kexec_watchdog = 0;
+ arg_early_core_pattern = NULL;
+ arg_watchdog_device = NULL;
+
arg_default_environment = strv_free(arg_default_environment);
+ rlimit_free_all(arg_default_rlimit);
+
+ arg_capability_bounding_set = CAP_ALL;
+ arg_no_new_privs = false;
+ arg_timer_slack_nsec = NSEC_INFINITY;
+ arg_default_timer_accuracy_usec = 1 * USEC_PER_MINUTE;
+
arg_syscall_archs = set_free(arg_syscall_archs);
- mac_selinux_finish();
+ /* arg_serialization — ignore */
+
+ arg_default_cpu_accounting = -1;
+ arg_default_io_accounting = false;
+ arg_default_ip_accounting = false;
+ arg_default_blockio_accounting = false;
+ arg_default_memory_accounting = MEMORY_ACCOUNTING_DEFAULT;
+ arg_default_tasks_accounting = true;
+ arg_default_tasks_max = DEFAULT_TASKS_MAX;
+ arg_machine_id = (sd_id128_t) {};
+ arg_cad_burst_action = EMERGENCY_ACTION_REBOOT_FORCE;
+ arg_default_oom_policy = OOM_STOP;
+
+ cpu_set_reset(&arg_cpu_affinity);
+ numa_policy_reset(&arg_numa_policy);
+ }
- if (reexecute) {
- const char **args;
- unsigned i, args_size;
+ static int parse_configuration(const struct rlimit *saved_rlimit_nofile,
+ const struct rlimit *saved_rlimit_memlock) {
+ int r;
- /* 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);
+ assert(saved_rlimit_nofile);
+ assert(saved_rlimit_memlock);
- /* 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)
- (void) setrlimit(RLIMIT_NOFILE, &saved_rlimit_nofile);
-
- 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, true);
-
- /* And switch root with MS_MOVE, because we remove the old directory afterwards and detach it. */
- r = switch_root(switch_root_dir, "/mnt", true, MS_MOVE);
- if (r < 0)
- log_error_errno(r, "Failed to switch root, trying to continue: %m");
+ /* Assign configuration defaults */
+ reset_arguments();
+
+ r = parse_config_file();
+ if (r < 0)
+ log_warning_errno(r, "Failed to parse config file, ignoring: %m");
+
+ if (arg_system) {
+ r = proc_cmdline_parse(parse_proc_cmdline_item, NULL, 0);
+ if (r < 0)
+ log_warning_errno(r, "Failed to parse kernel command line, ignoring: %m");
+ }
+
+ /* Initialize some default rlimits for services if they haven't been configured */
+ fallback_rlimit_nofile(saved_rlimit_nofile);
+ fallback_rlimit_memlock(saved_rlimit_memlock);
+
+ /* Note that this also parses bits from the kernel command line, including "debug". */
+ log_parse_environment();
+
+ return 0;
+ }
+
+ static int load_configuration(
+ int argc,
+ char **argv,
+ const struct rlimit *saved_rlimit_nofile,
+ const struct rlimit *saved_rlimit_memlock,
+ const char **ret_error_message) {
+ int r;
+
+ assert(saved_rlimit_nofile);
+ assert(saved_rlimit_memlock);
+ assert(ret_error_message);
+
+ (void) parse_configuration(saved_rlimit_nofile, saved_rlimit_memlock);
+
+ r = parse_argv(argc, argv);
+ if (r < 0) {
+ *ret_error_message = "Failed to parse commandline arguments";
+ return r;
+ }
+
+ /* Initialize default unit */
+ if (!arg_default_unit) {
+ arg_default_unit = strdup(SPECIAL_DEFAULT_TARGET);
+ if (!arg_default_unit) {
+ *ret_error_message = "Failed to set default unit";
+ return log_oom();
}
+ }
- /* Reopen the console */
- (void) make_console_stdio();
+ /* Initialize the show status setting if it hasn't been set explicitly yet */
+ if (arg_show_status == _SHOW_STATUS_INVALID)
+ arg_show_status = SHOW_STATUS_YES;
- args_size = MAX(6, argc+1);
- args = newa(const char*, args_size);
+ return 0;
+ }
+
+ static int safety_checks(void) {
- if (!switch_root_init) {
- char sfd[DECIMAL_STR_MAX(int) + 1];
+ if (getpid_cached() == 1 &&
+ arg_action != ACTION_RUN)
+ return log_error_errno(SYNTHETIC_ERRNO(EPERM),
+ "Unsupported execution mode while PID 1.");
- /* 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 (getpid_cached() == 1 &&
+ !arg_system)
+ return log_error_errno(SYNTHETIC_ERRNO(EPERM),
+ "Can't run --user mode as PID 1.");
- assert(arg_serialization);
- assert(fds);
+ if (arg_action == ACTION_RUN &&
+ arg_system &&
+ getpid_cached() != 1)
+ return log_error_errno(SYNTHETIC_ERRNO(EPERM),
+ "Can't run system mode unless PID 1.");
- xsprintf(sfd, "%i", fileno(arg_serialization));
+ if (arg_action == ACTION_TEST &&
+ geteuid() == 0)
+ return log_error_errno(SYNTHETIC_ERRNO(EPERM),
+ "Don't run test mode as root.");
- i = 0;
- args[i++] = SYSTEMD_BINARY_PATH;
- if (switch_root_dir)
- args[i++] = "--switched-root";
- args[i++] = arg_system ? "--system" : "--user";
- args[i++] = "--deserialize";
- args[i++] = sfd;
- args[i++] = NULL;
+ if (!arg_system &&
+ arg_action == ACTION_RUN &&
+ sd_booted() <= 0)
+ return log_error_errno(SYNTHETIC_ERRNO(EOPNOTSUPP),
+ "Trying to run as user instance, but the system has not been booted with systemd.");
+
+ if (!arg_system &&
+ arg_action == ACTION_RUN &&
+ !getenv("XDG_RUNTIME_DIR"))
+ return log_error_errno(SYNTHETIC_ERRNO(EUNATCH),
+ "Trying to run as user instance, but $XDG_RUNTIME_DIR is not set.");
+
+ if (arg_system &&
+ arg_action == ACTION_RUN &&
+ running_in_chroot() > 0)
+ return log_error_errno(SYNTHETIC_ERRNO(EOPNOTSUPP),
+ "Cannot be run in a chroot() environment.");
+
+ return 0;
+ }
+
+ static int initialize_security(
+ bool *loaded_policy,
+ dual_timestamp *security_start_timestamp,
+ dual_timestamp *security_finish_timestamp,
+ const char **ret_error_message) {
+
+ int r;
- assert(i <= args_size);
+ assert(loaded_policy);
+ assert(security_start_timestamp);
+ assert(security_finish_timestamp);
+ assert(ret_error_message);
- /*
- * We want valgrind to print its memory usage summary before reexecution.
- * Valgrind won't do this is on its own on exec(), but it will do it on exit().
- * Hence, to ensure we get a summary here, fork() off a child, let it exit() cleanly,
- * so that it prints the summary, and wait() for it in the parent, before proceeding into the exec().
- */
- valgrind_summary_hack();
+ dual_timestamp_get(security_start_timestamp);
- (void) execv(args[0], (char* const*) args);
+ r = mac_selinux_setup(loaded_policy);
+ if (r < 0) {
+ *ret_error_message = "Failed to load SELinux policy";
+ return r;
+ }
+
+ r = mac_smack_setup(loaded_policy);
+ if (r < 0) {
+ *ret_error_message = "Failed to load SMACK policy";
+ return r;
+ }
+
+ r = ima_setup();
+ if (r < 0) {
+ *ret_error_message = "Failed to load IMA policy";
+ return r;
+ }
+
+ dual_timestamp_get(security_finish_timestamp);
+ return 0;
+ }
+
+ static void test_summary(Manager *m) {
+ assert(m);
+
+ printf("-> By units:\n");
+ manager_dump_units(m, stdout, "\t");
+
+ printf("-> By jobs:\n");
+ manager_dump_jobs(m, stdout, "\t");
+ }
+
+ static int collect_fds(FDSet **ret_fds, const char **ret_error_message) {
+ int r;
+
+ assert(ret_fds);
+ assert(ret_error_message);
+
+ r = fdset_new_fill(ret_fds);
+ if (r < 0) {
+ *ret_error_message = "Failed to allocate fd set";
+ return log_emergency_errno(r, "Failed to allocate fd set: %m");
+ }
+
+ fdset_cloexec(*ret_fds, true);
+
+ if (arg_serialization)
+ assert_se(fdset_remove(*ret_fds, fileno(arg_serialization)) >= 0);
+
+ return 0;
+ }
+
+ static void setup_console_terminal(bool skip_setup) {
+
+ if (!arg_system)
+ return;
+
+ /* Become a session leader if we aren't one yet. */
+ (void) setsid();
+
+ /* If we are init, we connect stdin/stdout/stderr to /dev/null and make sure we don't have a controlling
+ * tty. */
+ (void) release_terminal();
+
+ /* Reset the console, but only if this is really init and we are freshly booted */
+ if (getpid_cached() == 1 && !skip_setup)
+ (void) console_setup();
+ }
+
+ static bool early_skip_setup_check(int argc, char *argv[]) {
+ bool found_deserialize = false;
+ int i;
+
+ /* 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. Note that if we have switched root, do all the special setup things
+ * anyway, even if in that case we also do deserialization. */
+
+ for (i = 1; i < argc; i++) {
+ if (streq(argv[i], "--switched-root"))
+ return false; /* If we switched root, don't skip the setup. */
+ else if (streq(argv[i], "--deserialize"))
+ found_deserialize = true;
+ }
+
+ return found_deserialize; /* When we are deserializing, then we are reexecuting, hence avoid the extensive setup */
+ }
+
+ static int save_env(void) {
+ char **l;
+
+ l = strv_copy(environ);
+ if (!l)
+ return -ENOMEM;
+
+ strv_free_and_replace(saved_env, l);
+ return 0;
+ }
+
+ int main(int argc, char *argv[]) {
+
+ dual_timestamp initrd_timestamp = DUAL_TIMESTAMP_NULL, userspace_timestamp = DUAL_TIMESTAMP_NULL, kernel_timestamp = DUAL_TIMESTAMP_NULL,
+ security_start_timestamp = DUAL_TIMESTAMP_NULL, security_finish_timestamp = DUAL_TIMESTAMP_NULL;
+ struct rlimit saved_rlimit_nofile = RLIMIT_MAKE_CONST(0),
+ saved_rlimit_memlock = RLIMIT_MAKE_CONST(RLIM_INFINITY); /* The original rlimits we passed
+ * in. Note we use different values
+ * for the two that indicate whether
+ * these fields are initialized! */
+ bool skip_setup, loaded_policy = false, queue_default_job = false, first_boot = false, reexecute = false;
+ char *switch_root_dir = NULL, *switch_root_init = NULL;
+ usec_t before_startup, after_startup;
+ static char systemd[] = "systemd";
+ char timespan[FORMAT_TIMESPAN_MAX];
+ const char *shutdown_verb = NULL, *error_message = NULL;
+ int r, retval = EXIT_FAILURE;
+ Manager *m = NULL;
+ FDSet *fds = NULL;
+
+ /* SysV compatibility: redirect init → telinit */
+ redirect_telinit(argc, argv);
+
+ /* Take timestamps early on */
+ dual_timestamp_from_monotonic(&kernel_timestamp, 0);
+ dual_timestamp_get(&userspace_timestamp);
+
+ /* Figure out whether we need to do initialize the system, or if we already did that because we are
+ * reexecuting */
+ skip_setup = early_skip_setup_check(argc, argv);
+
+ /* If we get started via the /sbin/init symlink then we are called 'init'. After a subsequent reexecution we
+ * are then called 'systemd'. That is confusing, hence let's call us systemd right-away. */
+ program_invocation_short_name = systemd;
+ (void) prctl(PR_SET_NAME, systemd);
+
+ /* Save the original command line */
+ save_argc_argv(argc, argv);
+
+ /* Save the original environment as we might need to restore it if we're requested to execute another
+ * system manager later. */
+ r = save_env();
+ if (r < 0) {
+ error_message = "Failed to copy environment block";
+ goto finish;
+ }
+
+ /* Make sure that if the user says "syslog" we actually log to the journal. */
+ log_set_upgrade_syslog_to_journal(true);
+
+ if (getpid_cached() == 1) {
+ /* When we run as PID 1 force system mode */
+ arg_system = true;
+
+ /* Disable the umask logic */
+ umask(0);
+
+ /* Make sure that at least initially we do not ever log to journald/syslogd, because it might not be
+ * activated yet (even though the log socket for it exists). */
+ log_set_prohibit_ipc(true);
+
+ /* Always reopen /dev/console when running as PID 1 or one of its pre-execve() children. This is
+ * important so that we never end up logging to any foreign stderr, for example if we have to log in a
+ * child process right before execve()'ing the actual binary, at a point in time where socket
+ * activation stderr/stdout area already set up. */
+ log_set_always_reopen_console(true);
+
+ if (detect_container() <= 0) {
+
+ /* Running outside of a container as PID 1 */
+ log_set_target(LOG_TARGET_KMSG);
+ log_open();
+
+ if (in_initrd())
+ initrd_timestamp = userspace_timestamp;
+
+ if (!skip_setup) {
+ r = mount_setup_early();
+ if (r < 0) {
+ error_message = "Failed to mount early API filesystems";
+ goto finish;
+ }
+
+ /* Let's open the log backend a second time, in case the first time didn't
+ * work. Quite possibly we have mounted /dev just now, so /dev/kmsg became
+ * available, and it previously wasn't. */
+ log_open();
+
+ disable_printk_ratelimit();
+
+ r = initialize_security(
+ &loaded_policy,
+ &security_start_timestamp,
+ &security_finish_timestamp,
+ &error_message);
+ if (r < 0)
+ goto finish;
+ }
+
+ if (mac_selinux_init() < 0) {
+ error_message = "Failed to initialize SELinux policy";
+ goto finish;
+ }
+
+ if (!skip_setup)
+ initialize_clock();
+
+ /* 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 opening that before we parsed /proc/cmdline which might redirect output
+ * elsewhere. */
+ log_set_target(LOG_TARGET_JOURNAL_OR_KMSG);
+
+ } else {
+ /* Running inside a container, as PID 1 */
+ log_set_target(LOG_TARGET_CONSOLE);
+ log_open();
+
+ /* For later on, see above... */
+ log_set_target(LOG_TARGET_JOURNAL);
+
+ /* clear the kernel timestamp,
+ * because we are in a container */
+ kernel_timestamp = DUAL_TIMESTAMP_NULL;
}
- /* 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.) */
+ initialize_coredump(skip_setup);
- arg_serialization = safe_fclose(arg_serialization);
- fds = fdset_free(fds);
+ r = fixup_environment();
+ if (r < 0) {
+ log_emergency_errno(r, "Failed to fix up PID 1 environment: %m");
+ error_message = "Failed to fix up PID1 environment";
+ goto finish;
+ }
- for (j = 1, i = 1; j < (unsigned) argc; j++)
- args[i++] = argv[j];
- args[i++] = NULL;
- assert(i <= args_size);
+ } else {
+ /* Running as user instance */
+ arg_system = false;
+ log_set_target(LOG_TARGET_AUTO);
+ log_open();
+
+ /* clear the kernel timestamp,
+ * because we are not PID 1 */
+ kernel_timestamp = DUAL_TIMESTAMP_NULL;
+ }
+
+ if (arg_system) {
+ /* Try to figure out if we can use colors with the console. No need to do that for user instances since
+ * they never log into the console. */
+ log_show_color(colors_enabled());
- /* Reenable any blocked signals, especially important
- * if we switch from initial ramdisk to init=... */
- (void) reset_all_signal_handlers();
- (void) reset_signal_mask();
+ r = make_null_stdio();
+ if (r < 0)
+ log_warning_errno(r, "Failed to redirect standard streams to /dev/null, ignoring: %m");
+ }
- if (switch_root_init) {
- args[0] = switch_root_init;
- (void) execv(args[0], (char* const*) args);
- log_warning_errno(errno, "Failed to execute configured init, trying fallback: %m");
+ /* Mount /proc, /sys and friends, so that /proc/cmdline and
+ * /proc/$PID/fd is available. */
+ if (getpid_cached() == 1) {
+
+ /* Load the kernel modules early. */
+ if (!skip_setup)
+ kmod_setup();
+
+ r = mount_setup(loaded_policy);
+ if (r < 0) {
+ error_message = "Failed to mount API filesystems";
+ goto finish;
}
- args[0] = "/sbin/init";
- (void) execv(args[0], (char* const*) args);
+ /* The efivarfs is now mounted, let's read the random seed off it */
+ (void) efi_take_random_seed();
+ }
- if (errno == ENOENT) {
- log_warning("No /sbin/init, trying fallback");
+ /* Save the original RLIMIT_NOFILE/RLIMIT_MEMLOCK so that we can reset it later when
+ * transitioning from the initrd to the main systemd or suchlike. */
+ save_rlimits(&saved_rlimit_nofile, &saved_rlimit_memlock);
- args[0] = "/bin/sh";
- args[1] = NULL;
- (void) execv(args[0], (char* const*) args);
- log_error_errno(errno, "Failed to execute /bin/sh, giving up: %m");
- } else
- log_warning_errno(errno, "Failed to execute /sbin/init, giving up: %m");
+ /* Reset all signal handlers. */
+ (void) reset_all_signal_handlers();
+ (void) ignore_signals(SIGNALS_IGNORE, -1);
+
+ r = load_configuration(argc, argv, &saved_rlimit_nofile, &saved_rlimit_memlock, &error_message);
+ if (r < 0)
+ goto finish;
+
+ r = safety_checks();
+ if (r < 0)
+ goto finish;
+
+ if (IN_SET(arg_action, ACTION_TEST, ACTION_HELP, ACTION_DUMP_CONFIGURATION_ITEMS, ACTION_DUMP_BUS_PROPERTIES))
+ (void) pager_open(arg_pager_flags);
+
+ if (arg_action != ACTION_RUN)
+ skip_setup = true;
+
+ if (arg_action == ACTION_HELP) {
+ retval = help() < 0 ? EXIT_FAILURE : EXIT_SUCCESS;
+ goto finish;
+ } else if (arg_action == ACTION_VERSION) {
+ retval = version();
+ goto finish;
+ } else if (arg_action == ACTION_DUMP_CONFIGURATION_ITEMS) {
+ unit_dump_config_items(stdout);
+ retval = EXIT_SUCCESS;
+ goto finish;
+ } else if (arg_action == ACTION_DUMP_BUS_PROPERTIES) {
+ dump_bus_properties(stdout);
+ retval = EXIT_SUCCESS;
+ goto finish;
}
- arg_serialization = safe_fclose(arg_serialization);
- fds = fdset_free(fds);
+ assert_se(IN_SET(arg_action, ACTION_RUN, ACTION_TEST));
- #ifdef HAVE_VALGRIND_VALGRIND_H
- /* If we are PID 1 and running under valgrind, then let's exit
- * here explicitly. valgrind will only generate nice output on
- * exit(), not on exec(), hence let's do the former not the
- * latter here. */
- if (getpid() == 1 && RUNNING_ON_VALGRIND)
- return 0;
- #endif
+ /* Move out of the way, so that we won't block unmounts */
+ assert_se(chdir("/") == 0);
- if (shutdown_verb) {
- char log_level[DECIMAL_STR_MAX(int) + 1];
- char exit_code[DECIMAL_STR_MAX(uint8_t) + 1];
- const char* command_line[11] = {
- SYSTEMD_SHUTDOWN_BINARY_PATH,
- shutdown_verb,
- "--log-level", log_level,
- "--log-target",
- };
- unsigned pos = 5;
- _cleanup_strv_free_ char **env_block = NULL;
+ if (arg_action == ACTION_RUN) {
- assert(command_line[pos] == NULL);
- env_block = strv_copy(environ);
+ /* A core pattern might have been specified via the cmdline. */
+ initialize_core_pattern(skip_setup);
- xsprintf(log_level, "%d", log_get_max_level());
+ /* Close logging fds, in order not to confuse collecting passed fds and terminal logic below */
+ log_close();
- switch (log_get_target()) {
+ /* Remember open file descriptors for later deserialization */
+ r = collect_fds(&fds, &error_message);
+ if (r < 0)
+ goto finish;
- case LOG_TARGET_KMSG:
- case LOG_TARGET_JOURNAL_OR_KMSG:
- case LOG_TARGET_SYSLOG_OR_KMSG:
- command_line[pos++] = "kmsg";
- break;
+ /* Give up any control of the console, but make sure its initialized. */
+ setup_console_terminal(skip_setup);
- case LOG_TARGET_NULL:
- command_line[pos++] = "null";
- break;
+ /* Open the logging devices, if possible and necessary */
+ log_open();
+ }
- case LOG_TARGET_CONSOLE:
- default:
- command_line[pos++] = "console";
- break;
- };
+ log_execution_mode(&first_boot);
- if (log_get_show_color())
- command_line[pos++] = "--log-color";
+ r = initialize_runtime(skip_setup,
+ &saved_rlimit_nofile,
+ &saved_rlimit_memlock,
+ &error_message);
+ if (r < 0)
+ goto finish;
- if (log_get_show_location())
- command_line[pos++] = "--log-location";
+ r = manager_new(arg_system ? UNIT_FILE_SYSTEM : UNIT_FILE_USER,
+ arg_action == ACTION_TEST ? MANAGER_TEST_FULL : 0,
+ &m);
+ if (r < 0) {
+ log_emergency_errno(r, "Failed to allocate manager object: %m");
+ error_message = "Failed to allocate manager object";
+ goto finish;
+ }
- if (streq(shutdown_verb, "exit")) {
- command_line[pos++] = "--exit-code";
- command_line[pos++] = exit_code;
- xsprintf(exit_code, "%d", retval);
- }
+ m->timestamps[MANAGER_TIMESTAMP_KERNEL] = kernel_timestamp;
+ m->timestamps[MANAGER_TIMESTAMP_INITRD] = initrd_timestamp;
+ m->timestamps[MANAGER_TIMESTAMP_USERSPACE] = userspace_timestamp;
+ m->timestamps[manager_timestamp_initrd_mangle(MANAGER_TIMESTAMP_SECURITY_START)] = security_start_timestamp;
+ m->timestamps[manager_timestamp_initrd_mangle(MANAGER_TIMESTAMP_SECURITY_FINISH)] = security_finish_timestamp;
- assert(pos < ELEMENTSOF(command_line));
+ set_manager_defaults(m);
+ set_manager_settings(m);
+ manager_set_first_boot(m, first_boot);
- if (arm_reboot_watchdog && arg_shutdown_watchdog > 0 && arg_shutdown_watchdog != USEC_INFINITY) {
- char *e;
+ /* Remember whether we should queue the default job */
+ queue_default_job = !arg_serialization || arg_switched_root;
- /* If we reboot let's set the shutdown
- * watchdog and tell the shutdown binary to
- * repeatedly ping it */
- r = watchdog_set_timeout(&arg_shutdown_watchdog);
- watchdog_close(r < 0);
+ before_startup = now(CLOCK_MONOTONIC);
- /* Tell the binary how often to ping, ignore failure */
- if (asprintf(&e, "WATCHDOG_USEC="USEC_FMT, arg_shutdown_watchdog) > 0)
- (void) strv_push(&env_block, e);
- } else
- watchdog_close(true);
+ r = manager_startup(m, arg_serialization, fds);
+ if (r < 0) {
+ error_message = "Failed to start up manager";
+ goto finish;
+ }
- /* Avoid the creation of new processes forked by the
- * kernel; at this point, we will not listen to the
- * signals anyway */
- if (detect_container() <= 0)
- (void) cg_uninstall_release_agent(SYSTEMD_CGROUP_CONTROLLER);
+ /* This will close all file descriptors that were opened, but not claimed by any unit. */
+ fds = fdset_free(fds);
+ arg_serialization = safe_fclose(arg_serialization);
- execve(SYSTEMD_SHUTDOWN_BINARY_PATH, (char **) command_line, env_block);
- log_error_errno(errno, "Failed to execute shutdown binary, %s: %m",
- getpid() == 1 ? "freezing" : "quitting");
+ if (queue_default_job) {
+ r = do_queue_default_job(m, &error_message);
+ if (r < 0)
+ goto finish;
}
- if (getpid() == 1) {
+ after_startup = now(CLOCK_MONOTONIC);
+
+ log_full(arg_action == ACTION_TEST ? LOG_INFO : LOG_DEBUG,
+ "Loaded units and determined initial transaction in %s.",
+ format_timespan(timespan, sizeof(timespan), after_startup - before_startup, 100 * USEC_PER_MSEC));
+
+ if (arg_action == ACTION_TEST) {
+ test_summary(m);
+ retval = EXIT_SUCCESS;
+ goto finish;
+ }
+
+ (void) invoke_main_loop(m,
+ &saved_rlimit_nofile,
+ &saved_rlimit_memlock,
+ &reexecute,
+ &retval,
+ &shutdown_verb,
+ &fds,
+ &switch_root_dir,
+ &switch_root_init,
+ &error_message);
+
+ finish:
+ pager_close();
+
+ if (m) {
+ arg_reboot_watchdog = m->reboot_watchdog;
+ arg_kexec_watchdog = m->kexec_watchdog;
+ m = manager_free(m);
+ }
+
+ reset_arguments();
+ mac_selinux_finish();
+
+ if (reexecute)
+ do_reexecute(argc, argv,
+ &saved_rlimit_nofile,
+ &saved_rlimit_memlock,
+ fds,
+ switch_root_dir,
+ switch_root_init,
+ &error_message); /* This only returns if reexecution failed */
+
+ arg_serialization = safe_fclose(arg_serialization);
+ fds = fdset_free(fds);
+
+ saved_env = strv_free(saved_env);
+
+ #if HAVE_VALGRIND_VALGRIND_H
+ /* If we are PID 1 and running under valgrind, then let's exit
+ * here explicitly. valgrind will only generate nice output on
+ * exit(), not on exec(), hence let's do the former not the
+ * latter here. */
+ if (getpid_cached() == 1 && RUNNING_ON_VALGRIND) {
+ /* Cleanup watchdog_device strings for valgrind. We need them
+ * in become_shutdown() so normally we cannot free them yet. */
+ watchdog_free_device();
+ arg_watchdog_device = mfree(arg_watchdog_device);
+ return retval;
+ }
+ #endif
+
+ #if HAS_FEATURE_ADDRESS_SANITIZER
+ __lsan_do_leak_check();
+ #endif
+
+ if (shutdown_verb) {
+ r = become_shutdown(shutdown_verb, retval);
+ log_error_errno(r, "Failed to execute shutdown binary, %s: %m", getpid_cached() == 1 ? "freezing" : "quitting");
+ error_message = "Failed to execute shutdown binary";
+ }
+
+ watchdog_free_device();
+ arg_watchdog_device = mfree(arg_watchdog_device);
+
+ if (getpid_cached() == 1) {
if (error_message)
manager_status_printf(NULL, STATUS_TYPE_EMERGENCY,
ANSI_HIGHLIGHT_RED "!!!!!!" ANSI_NORMAL,