#include "conf-parser.h"
#include "missing.h"
#include "label.h"
+#include "pager.h"
#include "build.h"
#include "strv.h"
#include "def.h"
#include "capability.h"
#include "killall.h"
#include "env-util.h"
-#include "hwclock.h"
+#include "clock-util.h"
#include "fileio.h"
#include "dbus-manager.h"
#include "bus-error.h"
static bool arg_confirm_spawn = false;
static ShowStatus arg_show_status = _SHOW_STATUS_UNSET;
static bool arg_switched_root = false;
+static int arg_no_pager = -1;
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 char **arg_default_environment = NULL;
static struct rlimit *arg_default_rlimit[_RLIMIT_MAX] = {};
static uint64_t arg_capability_bounding_set_drop = 0;
-static nsec_t arg_timer_slack_nsec = (nsec_t) -1;
+static nsec_t arg_timer_slack_nsec = NSEC_INFINITY;
static usec_t arg_default_timer_accuracy_usec = 1 * USEC_PER_MINUTE;
static Set* arg_syscall_archs = NULL;
static FILE* arg_serialization = NULL;
static bool arg_default_cpu_accounting = false;
static bool arg_default_blockio_accounting = false;
static bool arg_default_memory_accounting = false;
+static usec_t arg_start_timeout_usec = DEFAULT_MANAGER_START_TIMEOUT_USEC;
+static FailureAction arg_start_timeout_action = FAILURE_ACTION_POWEROFF_FORCE;
+static char *arg_start_timeout_reboot_arg = NULL;
static void nop_handler(int sig) {}
+static void pager_open_if_enabled(void) {
+
+ if (arg_no_pager <= 0)
+ return;
+
+ pager_open(false);
+}
+
noreturn static void crash(int sig) {
if (getpid() != 1)
sigaction_many(&sa, SIGNALS_CRASH_HANDLER, -1);
}
-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;
+static int console_setup(void) {
+ _cleanup_close_ int tty_fd = -1;
+ int r;
tty_fd = open_terminal("/dev/console", O_WRONLY|O_NOCTTY|O_CLOEXEC);
if (tty_fd < 0) {
log_error("Failed to open /dev/console: %s", strerror(-tty_fd));
- return -tty_fd;
+ return tty_fd;
}
- /* We don't want to force text mode.
- * plymouth may be showing pictures already from initrd. */
+ /* 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)
+ if (r < 0) {
log_error("Failed to reset /dev/console: %s", strerror(-r));
+ return r;
+ }
- safe_close(tty_fd);
- return r;
+ return 0;
}
static int set_default_unit(const char *u) {
} else if (streq(key, "rd.systemd.unit") && value) {
- return set_default_unit(value);
-
- } else if (streq(key, "systemd.log_target") && value) {
-
- if (log_set_target_from_string(value) < 0)
- log_warning("Failed to parse log target %s. Ignoring.", value);
-
- } else if (streq(key, "systemd.log_level") && value) {
-
- if (log_set_max_level_from_string(value) < 0)
- log_warning("Failed to parse log level %s. Ignoring.", value);
-
- } else if (streq(key, "systemd.log_color") && value) {
-
- if (log_show_color_from_string(value) < 0)
- log_warning("Failed to parse log color setting %s. Ignoring.", value);
-
- } else if (streq(key, "systemd.log_location") && value) {
-
- if (log_show_location_from_string(value) < 0)
- log_warning("Failed to parse log location setting %s. Ignoring.", value);
+ if (in_initrd())
+ return set_default_unit(value);
} else if (streq(key, "systemd.dump_core") && value) {
} else
log_warning("Environment variable name '%s' is not valid. Ignoring.", value);
- } else if (!streq(key, "systemd.restore_state") &&
- !streq(key, "systemd.gpt_auto") &&
- (startswith(key, "systemd.") || startswith(key, "rd.systemd."))) {
-
- const char *c;
-
- /* Ignore systemd.journald.xyz and friends */
- c = key;
- if (startswith(c, "rd."))
- c += 3;
- if (startswith(c, "systemd."))
- c += 8;
- if (c[strcspn(c, ".=")] != '.') {
-
- log_warning("Unknown kernel switch %s. Ignoring.", key);
-
- 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|auto 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"
- "systemd.restore_state=0|1 Restore backlight/rfkill state at boot\n");
- }
-
} else if (streq(key, "quiet") && !value) {
+
+ log_set_max_level(LOG_NOTICE);
+
if (arg_show_status == _SHOW_STATUS_UNSET)
arg_show_status = SHOW_STATUS_AUTO;
} else if (streq(key, "debug") && !value) {
- log_set_max_level(LOG_DEBUG);
+
+ /* Note that log_parse_environment() handles 'debug'
+ * too, and sets the log level to LOG_DEBUG. */
+
if (detect_container(NULL) > 0)
log_set_target(LOG_TARGET_CONSOLE);
void *data,
void *userdata) {
- char *w;
+ const char *word, *state;
size_t l;
- char *state;
cpu_set_t *c = NULL;
unsigned ncpus = 0;
assert(lvalue);
assert(rvalue);
- FOREACH_WORD_QUOTED(w, l, rvalue, state) {
+ FOREACH_WORD_QUOTED(word, l, rvalue, state) {
char *t;
int r;
unsigned cpu;
- if (!(t = strndup(w, l)))
+ if (!(t = strndup(word, l)))
return log_oom();
r = safe_atou(t, &cpu);
CPU_SET_S(cpu, CPU_ALLOC_SIZE(ncpus), c);
}
+ if (!isempty(state))
+ log_syntax(unit, LOG_ERR, filename, line, EINVAL,
+ "Trailing garbage, ignoring.");
if (c) {
if (sched_setaffinity(0, CPU_ALLOC_SIZE(ncpus), c) < 0)
void *userdata) {
unsigned n = 0;
- char *state, *w;
+ const char *word, *state;
size_t length;
assert(filename);
free_join_controllers();
- FOREACH_WORD_QUOTED(w, length, rvalue, state) {
+ FOREACH_WORD_QUOTED(word, length, rvalue, state) {
char *s, **l;
- s = strndup(w, length);
+ s = strndup(word, length);
if (!s)
return log_oom();
arg_join_controllers = t;
}
}
+ if (!isempty(state))
+ log_syntax(unit, LOG_ERR, filename, line, EINVAL,
+ "Trailing garbage, ignoring.");
return 0;
}
{ "Manager", "DefaultCPUAccounting", config_parse_bool, 0, &arg_default_cpu_accounting },
{ "Manager", "DefaultBlockIOAccounting", config_parse_bool, 0, &arg_default_blockio_accounting },
{ "Manager", "DefaultMemoryAccounting", config_parse_bool, 0, &arg_default_memory_accounting },
+ { "Manager", "StartTimeoutSec", config_parse_sec, 0, &arg_start_timeout_usec },
+ { "Manager", "StartTimeoutAction", config_parse_failure_action, 0, &arg_start_timeout_action },
+ { "Manager", "StartTimeoutRebootArgument",config_parse_string, 0, &arg_start_timeout_reboot_arg },
{}
};
- _cleanup_fclose_ FILE *f;
const char *fn;
- int r;
fn = arg_running_as == SYSTEMD_SYSTEM ? PKGSYSCONFDIR "/system.conf" : PKGSYSCONFDIR "/user.conf";
- f = fopen(fn, "re");
- if (!f) {
- if (errno == ENOENT)
- return 0;
-
- log_warning("Failed to open configuration file '%s': %m", fn);
- return 0;
- }
-
- r = config_parse(NULL, fn, f, "Manager\0", config_item_table_lookup, (void*) items, false, false, NULL);
- if (r < 0)
- log_warning("Failed to parse configuration file: %s", strerror(-r));
+ config_parse(NULL, fn, NULL,
+ "Manager\0",
+ config_item_table_lookup, items,
+ false, false, true, NULL);
return 0;
}
ARG_SYSTEM,
ARG_USER,
ARG_TEST,
+ ARG_NO_PAGER,
ARG_VERSION,
ARG_DUMP_CONFIGURATION_ITEMS,
ARG_DUMP_CORE,
{ "system", no_argument, NULL, ARG_SYSTEM },
{ "user", no_argument, NULL, ARG_USER },
{ "test", no_argument, NULL, ARG_TEST },
+ { "no-pager", no_argument, NULL, ARG_NO_PAGER },
{ "help", no_argument, NULL, 'h' },
{ "version", no_argument, NULL, ARG_VERSION },
{ "dump-configuration-items", no_argument, NULL, ARG_DUMP_CONFIGURATION_ITEMS },
case ARG_TEST:
arg_action = ACTION_TEST;
+ if (arg_no_pager < 0)
+ arg_no_pager = true;
+ break;
+
+ case ARG_NO_PAGER:
+ arg_no_pager = true;
break;
case ARG_VERSION:
case 'h':
arg_action = ACTION_HELP;
+ if (arg_no_pager < 0)
+ arg_no_pager = true;
break;
case 'D':
* parse_proc_cmdline_word() or ignore. */
case '?':
- default:
- if (getpid() != 1) {
- log_error("Unknown option code %c", c);
+ if (getpid() != 1)
return -EINVAL;
- }
+ else
+ return 0;
- break;
+ default:
+ assert_not_reached("Unhandled option code.");
}
if (optind < argc && getpid() != 1) {
return -EINVAL;
}
- if (detect_container(NULL) > 0) {
- char **a;
-
- /* All /proc/cmdline arguments the kernel didn't
- * understand it passed to us. We're not really
- * interested in that usually since /proc/cmdline is
- * more interesting and complete. With one exception:
- * if we are run in a container /proc/cmdline is not
- * relevant for the container, hence we rely on argv[]
- * instead. */
-
- for (a = argv; a < argv + argc; a++) {
- _cleanup_free_ char *w;
- char *value;
-
- w = strdup(*a);
- if (!w)
- return log_oom();
-
- value = strchr(w, '=');
- if (value)
- *(value++) = 0;
-
- r = parse_proc_cmdline_item(w, value);
- if (r < 0) {
- log_error("Failed on cmdline argument %s: %s", *a, strerror(-r));
- return r;
- }
- }
- }
-
return 0;
}
"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"
+ " --no-pager Do not pipe output into a pager\n"
" --dump-configuration-items Dump understood unit configuration items\n"
" --unit=UNIT Set default unit\n"
" --system Run a system instance, even if PID != 1\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-target=TARGET Set log target (console, journal, kmsg, journal-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"
}
static void test_mtab(void) {
- char *p;
- /* Check that /etc/mtab is a symlink */
+ static const char ok[] =
+ "/proc/self/mounts\0"
+ "/proc/mounts\0"
+ "../proc/self/mounts\0"
+ "../proc/mounts\0";
- if (readlink_malloc("/etc/mtab", &p) >= 0) {
- bool b;
+ _cleanup_free_ char *p = NULL;
+ int r;
- b = streq(p, "/proc/self/mounts") || streq(p, "/proc/mounts");
- free(p);
+ /* Check that /etc/mtab is a symlink to the right place or
+ * non-existing. But certainly not a file, or a symlink to
+ * some weird place... */
- if (b)
- return;
- }
+ r = readlink_malloc("/etc/mtab", &p);
+ if (r == -ENOENT)
+ return;
+ if (r >= 0 && nulstr_contains(ok, p))
+ return;
log_warning("/etc/mtab is not a symlink or not pointing to /proc/self/mounts. "
"This is not supported anymore. "
"PRETTY_NAME", &pretty_name,
"ANSI_COLOR", &ansi_color,
NULL);
+ if (r == -ENOENT) {
+ r = parse_env_file("/usr/lib/os-release", NEWLINE,
+ "PRETTY_NAME", &pretty_name,
+ "ANSI_COLOR", &ansi_color,
+ NULL);
+ }
if (r < 0 && r != -ENOENT)
- log_warning("Failed to read /etc/os-release: %s", strerror(-r));
+ log_warning("Failed to read os-release file: %s", strerror(-r));
return status_printf(NULL, false, false,
"\nWelcome to \x1B[%sm%s\x1B[0m!\n",
isempty(pretty_name) ? "Linux" : pretty_name);
}
+static int write_container_id(void) {
+ const char *c;
+
+ c = getenv("container");
+ if (isempty(c))
+ return 0;
+
+ return write_string_file("/run/systemd/container", c);
+}
+
int main(int argc, char *argv[]) {
Manager *m = NULL;
int r, retval = EXIT_FAILURE;
bool loaded_policy = false;
bool arm_reboot_watchdog = false;
bool queue_default_job = false;
+ bool empty_etc = false;
char *switch_root_dir = NULL, *switch_root_init = NULL;
static struct rlimit saved_rlimit_nofile = { 0, 0 };
saved_argc = argc;
log_show_color(isatty(STDERR_FILENO) > 0);
+ log_set_upgrade_syslog_to_journal(true);
/* Disable the umask logic */
if (getpid() == 1)
goto finish;
if (!skip_setup) {
- if (hwclock_is_localtime() > 0) {
+ if (clock_is_localtime() > 0) {
int min;
- /* The first-time call to settimeofday() does a time warp in the kernel */
- r = hwclock_set_timezone(&min);
+ /*
+ * The very first call of settimeofday() also does a time warp in the kernel.
+ *
+ * In the rtc-in-local time mode, we set the kernel's timezone, and rely on
+ * external tools to take care of maintaining the RTC and do all adjustments.
+ * This matches the behavior of Windows, which leaves the RTC alone if the
+ * registry tells that the RTC runs in UTC.
+ */
+ r = clock_set_timezone(&min);
if (r < 0)
log_error("Failed to apply local time delta, ignoring: %s", strerror(-r));
else
log_info("RTC configured in localtime, applying delta of %i minutes to system time.", min);
} else if (!in_initrd()) {
/*
- * Do dummy first-time call to seal the kernel's time warp magic
+ * Do a dummy very first call to seal the kernel's time warp magic.
*
* Do not call this this from inside the initrd. The initrd might not
* carry /etc/adjtime with LOCAL, but the real system could be set up
* that way. In such case, we need to delay the time-warp or the sealing
* until we reach the real system.
+ *
+ * Do no set the kernel's timezone. The concept of local time cannot
+ * be supported reliably, the time will jump or be incorrect at every daylight
+ * saving time change. All kernel local time concepts will be treated
+ * as UTC that way.
*/
- hwclock_reset_timezone();
-
- /* Tell the kernel our timezone */
- r = hwclock_set_timezone(NULL);
- if (r < 0)
- log_error("Failed to set the kernel's timezone, ignoring: %s", strerror(-r));
+ clock_reset_timewarp();
}
}
if (parse_proc_cmdline(parse_proc_cmdline_item) < 0)
goto finish;
+ /* Note that this also parses bits from the kernel command
+ * line, including "debug". */
log_parse_environment();
if (parse_argv(argc, argv) < 0)
goto finish;
}
+ if (arg_action == ACTION_TEST)
+ skip_setup = true;
+
+ pager_open_if_enabled();
+
if (arg_action == ACTION_HELP) {
retval = help();
goto finish;
/* Reset the console, but only if this is really init and we
* are freshly booted */
- if (arg_running_as == SYSTEMD_SYSTEM && arg_action == ACTION_RUN)
- console_setup(getpid() == 1 && !skip_setup);
+ if (arg_running_as == SYSTEMD_SYSTEM && arg_action == ACTION_RUN) {
+
+ /* 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 (getpid() == 1 && !skip_setup)
+ console_setup();
+ }
/* Open the logging devices, if possible and necessary */
log_open();
if (arg_running_as == SYSTEMD_SYSTEM) {
const char *virtualization = NULL;
- log_info(PACKAGE_STRING " running in system mode. (" SYSTEMD_FEATURES ")");
+ log_info(PACKAGE_STRING " running in %ssystem mode. (" SYSTEMD_FEATURES ")",
+ arg_action == ACTION_TEST ? "test " : "" );
detect_virtualization(&virtualization);
if (virtualization)
log_info("Detected virtualization '%s'.", virtualization);
+ write_container_id();
+
log_info("Detected architecture '%s'.", architecture_to_string(uname_architecture()));
if (in_initrd())
log_info("Running in initial RAM disk.");
+ /* Let's check whether /etc is already populated. We
+ * don't actually really check for that, but use
+ * /etc/machine-id as flag file. This allows container
+ * managers and installers to provision a couple of
+ * files already. If the container manager wants to
+ * provision the machine ID itself it should pass
+ * $container_uuid to PID 1.*/
+
+ empty_etc = access("/etc/machine-id", F_OK) < 0;
+ if (empty_etc)
+ log_info("Running with unpopulated /etc.");
} else {
- _cleanup_free_ char *t = uid_to_name(getuid());
- log_debug(PACKAGE_STRING " running in user mode for user "PID_FMT"/%s. (" SYSTEMD_FEATURES ")",
- getuid(), t);
+ _cleanup_free_ char *t;
+
+ t = uid_to_name(getuid());
+ log_debug(PACKAGE_STRING " running in %suser mode for user "UID_FMT"/%s. (" SYSTEMD_FEATURES ")",
+ arg_action == ACTION_TEST ? " test" : "", getuid(), t);
}
if (arg_running_as == SYSTEMD_SYSTEM && !skip_setup) {
status_welcome();
#ifdef HAVE_KMOD
- if (detect_container(NULL) <= 0)
- kmod_setup();
+ kmod_setup();
#endif
hostname_setup();
- machine_id_setup("");
+ machine_id_setup(NULL);
loopback_setup();
test_mtab();
if (arg_running_as == SYSTEMD_SYSTEM && arg_runtime_watchdog > 0)
watchdog_set_timeout(&arg_runtime_watchdog);
- if (arg_timer_slack_nsec != (nsec_t) -1)
+ if (arg_timer_slack_nsec != NSEC_INFINITY)
if (prctl(PR_SET_TIMERSLACK, arg_timer_slack_nsec) < 0)
log_error("Failed to adjust timer slack: %m");
}
}
- if (arg_running_as == SYSTEMD_SYSTEM)
+ if (arg_running_as == SYSTEMD_SYSTEM) {
bump_rlimit_nofile(&saved_rlimit_nofile);
- r = manager_new(arg_running_as, &m);
+ if (empty_etc) {
+ r = unit_file_preset_all(UNIT_FILE_SYSTEM, false, NULL, UNIT_FILE_PRESET_FULL, false, NULL, 0);
+ if (r < 0)
+ log_warning("Failed to populate /etc with preset unit settings, ignoring: %s", strerror(-r));
+ else
+ log_info("Populated /etc with preset unit settings.");
+ }
+ }
+
+ r = manager_new(arg_running_as, arg_action == ACTION_TEST, &m);
if (r < 0) {
log_error("Failed to allocate manager object: %s", strerror(-r));
goto finish;
m->default_memory_accounting = arg_default_memory_accounting;
m->runtime_watchdog = arg_runtime_watchdog;
m->shutdown_watchdog = arg_shutdown_watchdog;
+ m->start_timeout_usec = arg_start_timeout_usec;
+ m->start_timeout_action = arg_start_timeout_action;
+ free_and_strdup(&m->start_timeout_reboot_arg, arg_start_timeout_reboot_arg);
+
m->userspace_timestamp = userspace_timestamp;
m->kernel_timestamp = kernel_timestamp;
m->initrd_timestamp = initrd_timestamp;
manager_set_default_rlimits(m, arg_default_rlimit);
manager_environment_add(m, NULL, arg_default_environment);
manager_set_show_status(m, arg_show_status);
+ manager_set_first_boot(m, empty_etc);
/* Remember whether we should queue the default job */
queue_default_job = !arg_serialization || arg_switched_root;
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, 0));
+ format_timespan(timespan, sizeof(timespan), after_startup - before_startup, 100 * USEC_PER_MSEC));
if (arg_action == ACTION_TEST) {
printf("-> By jobs:\n");
}
finish:
+ pager_close();
+
if (m) {
manager_free(m);
m = NULL;
set_free(arg_syscall_archs);
arg_syscall_archs = NULL;
+ free(arg_start_timeout_reboot_arg);
+ arg_start_timeout_reboot_arg = NULL;
+
label_finish();
if (reexecute) {
* deserializing. */
broadcast_signal(SIGTERM, false, true);
- /* And switch root */
- r = switch_root(switch_root_dir);
+ /* 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("Failed to switch root, ignoring: %s", strerror(-r));
}
args[i++] = NULL;
assert(i <= args_size);
+ /* Reenable any blocked signals, especially important
+ * if we switch from initial ramdisk to init=... */
+ reset_all_signal_handlers();
+ reset_signal_mask();
+
if (switch_root_init) {
args[0] = switch_root_init;
execv(args[0], (char* const*) args);