#include "sd-bus.h"
#include "sd-daemon.h"
+#include "sd-messages.h"
#include "alloc-util.h"
#include "architecture.h"
static unsigned arg_default_start_limit_burst = DEFAULT_START_LIMIT_BURST;
static usec_t arg_runtime_watchdog = 0;
static usec_t arg_shutdown_watchdog = 10 * USEC_PER_MINUTE;
+static char *arg_watchdog_device = NULL;
static char **arg_default_environment = NULL;
static struct rlimit *arg_default_rlimit[_RLIMIT_MAX] = {};
static uint64_t arg_capability_bounding_set = CAP_ALL;
if (arg_default_timeout_start_usec <= 0)
arg_default_timeout_start_usec = USEC_INFINITY;
+ } else if (proc_cmdline_key_streq(key, "systemd.watchdog_device")) {
+
+ if (proc_cmdline_value_missing(key, value))
+ return 0;
+
+ parse_path_argument_and_warn(value, false, &arg_watchdog_device);
+
} else if (streq(key, "quiet") && !value) {
if (arg_show_status == _SHOW_STATUS_UNSET)
{ "Manager", "JoinControllers", config_parse_join_controllers, 0, &arg_join_controllers },
{ "Manager", "RuntimeWatchdogSec", config_parse_sec, 0, &arg_runtime_watchdog },
{ "Manager", "ShutdownWatchdogSec", config_parse_sec, 0, &arg_shutdown_watchdog },
+ { "Manager", "WatchdogDevice", config_parse_path, 0, &arg_watchdog_device },
{ "Manager", "CapabilityBoundingSet", config_parse_capability_set, 0, &arg_capability_bounding_set },
#if HAVE_SECCOMP
{ "Manager", "SystemCallArchitectures", config_parse_syscall_archs, 0, &arg_syscall_archs },
static int fixup_environment(void) {
_cleanup_free_ char *term = NULL;
+ const char *t;
int r;
/* We expect the environment to be set correctly
r = proc_cmdline_get_key("TERM", 0, &term);
if (r < 0)
return r;
- if (r == 0) {
- term = strdup(default_term_for_tty("/dev/console"));
- if (!term)
- return -ENOMEM;
- }
- if (setenv("TERM", term, 1) < 0)
+ t = term ?: default_term_for_tty("/dev/console");
+
+ if (setenv("TERM", t, 1) < 0)
return -errno;
return 0;
int r;
assert(shutdown_verb);
- assert(command_line[pos] == NULL);
+ assert(!command_line[pos]);
env_block = strv_copy(environ);
xsprintf(log_level, "%d", log_get_max_level());
/* 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);
+ (void) strv_consume(&env_block, e);
+
+ if (arg_watchdog_device &&
+ asprintf(&e, "WATCHDOG_DEVICE=%s", arg_watchdog_device) > 0)
+ (void) strv_consume(&env_block, e);
} else
watchdog_close(true);
}
}
+static void log_execution_mode(bool *ret_first_boot) {
+ assert(ret_first_boot);
+
+ if (arg_system) {
+ int v;
+
+ log_info(PACKAGE_STRING " running in %ssystem mode. (" SYSTEMD_FEATURES ")",
+ arg_action == ACTION_TEST ? "test " : "" );
+
+ v = detect_virtualization();
+ if (v > 0)
+ log_info("Detected virtualization %s.", virtualization_to_string(v));
+
+ log_info("Detected architecture %s.", architecture_to_string(uname_architecture()));
+
+ if (in_initrd()) {
+ *ret_first_boot = false;
+ log_info("Running in initial RAM disk.");
+ } else {
+ /* Let's check whether we are in first boot, i.e. whether /etc is still unpopulated. We use
+ * /etc/machine-id as flag file, for this: if it exists we assume /etc is populated, if it
+ * doesn't it's unpopulated. 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. */
+
+ *ret_first_boot = access("/etc/machine-id", F_OK) < 0;
+ if (*ret_first_boot)
+ log_info("Running with unpopulated /etc.");
+ }
+ } else {
+ _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(), strna(t));
+
+ *ret_first_boot = false;
+ }
+}
+
static int initialize_runtime(
bool skip_setup,
struct rlimit *saved_rlimit_nofile,
machine_id_setup(NULL, arg_machine_id, NULL);
loopback_setup();
bump_unix_max_dgram_qlen();
-
test_usr();
+ write_container_id();
+ }
+
+ if (arg_system && arg_watchdog_device) {
+ r = watchdog_set_device(arg_watchdog_device);
+ if (r < 0)
+ log_warning_errno(r, "Failed to set watchdog device to %s, ignoring: %m",
+ arg_watchdog_device);
}
if (arg_system && arg_runtime_watchdog > 0 && arg_runtime_watchdog != USEC_INFINITY)
arg_syscall_archs = set_free(arg_syscall_archs);
}
+static int load_configuration(int argc, char **argv, const char **ret_error_message) {
+ int r;
+
+ assert(ret_error_message);
+
+ arg_default_tasks_max = system_tasks_max_scale(DEFAULT_TASKS_MAX_PERCENTAGE, 100U);
+
+ r = parse_config_file();
+ if (r < 0) {
+ *ret_error_message = "Failed to parse config file";
+ return r;
+ }
+
+ 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");
+ }
+
+ /* Note that this also parses bits from the kernel command line, including "debug". */
+ log_parse_environment();
+
+ 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();
+ }
+ }
+
+ return 0;
+}
+
+static int safety_checks(void) {
+
+ if (arg_action == ACTION_TEST &&
+ geteuid() == 0) {
+ log_error("Don't run test mode as root.");
+ return -EPERM;
+ }
+
+ if (!arg_system &&
+ arg_action == ACTION_RUN &&
+ sd_booted() <= 0) {
+ log_error("Trying to run as user instance, but the system has not been booted with systemd.");
+ return -EOPNOTSUPP;
+ }
+
+ if (!arg_system &&
+ arg_action == ACTION_RUN &&
+ !getenv("XDG_RUNTIME_DIR")) {
+ log_error("Trying to run as user instance, but $XDG_RUNTIME_DIR is not set.");
+ return -EUNATCH;
+ }
+
+ if (arg_system &&
+ arg_action == ACTION_RUN &&
+ running_in_chroot() > 0) {
+ log_error("Cannot be run in a chroot() environment.");
+ return -EOPNOTSUPP;
+ }
+
+ return 0;
+}
+
int main(int argc, char *argv[]) {
Manager *m = NULL;
int r, retval = EXIT_FAILURE;
(void) reset_all_signal_handlers();
(void) ignore_signals(SIGNALS_IGNORE, -1);
- arg_default_tasks_max = system_tasks_max_scale(DEFAULT_TASKS_MAX_PERCENTAGE, 100U);
-
- if (parse_config_file() < 0) {
- error_message = "Failed to parse config file";
- goto finish;
- }
-
- 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");
- }
-
- /* Note that this also parses bits from the kernel command
- * line, including "debug". */
- log_parse_environment();
-
- if (parse_argv(argc, argv) < 0) {
- error_message = "Failed to parse commandline arguments";
- goto finish;
- }
-
- /* Initialize default unit */
- if (!arg_default_unit) {
- arg_default_unit = strdup(SPECIAL_DEFAULT_TARGET);
- if (!arg_default_unit) {
- r = log_oom();
- error_message = "Failed to set default unit";
- goto finish;
- }
- }
-
- if (arg_action == ACTION_TEST &&
- geteuid() == 0) {
- log_error("Don't run test mode as root.");
- goto finish;
- }
-
- if (!arg_system &&
- arg_action == ACTION_RUN &&
- sd_booted() <= 0) {
- log_error("Trying to run as user instance, but the system has not been booted with systemd.");
+ r = load_configuration(argc, argv, &error_message);
+ if (r < 0)
goto finish;
- }
- if (arg_system &&
- arg_action == ACTION_RUN &&
- running_in_chroot() > 0) {
- log_error("Cannot be run in a chroot() environment.");
+ r = safety_checks();
+ if (r < 0)
goto finish;
- }
- if (IN_SET(arg_action, ACTION_TEST, ACTION_HELP)) {
+ if (IN_SET(arg_action, ACTION_TEST, ACTION_HELP, ACTION_DUMP_CONFIGURATION_ITEMS))
pager_open(arg_no_pager, false);
+
+ if (arg_action != ACTION_RUN)
skip_setup = true;
- }
if (arg_action == ACTION_HELP) {
retval = help();
retval = version();
goto finish;
} else if (arg_action == ACTION_DUMP_CONFIGURATION_ITEMS) {
- pager_open(arg_no_pager, false);
unit_dump_config_items(stdout);
retval = EXIT_SUCCESS;
goto finish;
}
- if (!arg_system &&
- !getenv("XDG_RUNTIME_DIR")) {
- log_error("Trying to run as user instance, but $XDG_RUNTIME_DIR is not set.");
- goto finish;
- }
-
assert_se(IN_SET(arg_action, ACTION_RUN, ACTION_TEST));
/* Close logging fds, in order not to confuse fdset below */
goto finish;
}
- if (arg_system) {
- int v;
-
- log_info(PACKAGE_STRING " running in %ssystem mode. (" SYSTEMD_FEATURES ")",
- arg_action == ACTION_TEST ? "test " : "" );
-
- v = detect_virtualization();
- if (v > 0)
- log_info("Detected virtualization %s.", virtualization_to_string(v));
-
- write_container_id();
-
- log_info("Detected architecture %s.", architecture_to_string(uname_architecture()));
-
- if (in_initrd())
- log_info("Running in initial RAM disk.");
- else {
- /* Let's check whether we are in first boot, i.e. whether /etc is still unpopulated. We use
- * /etc/machine-id as flag file, for this: if it exists we assume /etc is populated, if it
- * doesn't it's unpopulated. 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. */
-
- first_boot = access("/etc/machine-id", F_OK) < 0;
- if (first_boot)
- log_info("Running with unpopulated /etc.");
- }
- } else {
- _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(), strna(t));
- }
+ log_execution_mode(&first_boot);
if (arg_action == ACTION_RUN) {
r = initialize_runtime(skip_setup,
r = manager_startup(m, arg_serialization, fds);
if (r < 0) {
log_error_errno(r, "Failed to fully start up daemon: %m");
+ error_message = "Failed to start up manager";
goto finish;
}
"Loaded units and determined initial transaction in %s.",
format_timespan(timespan, sizeof(timespan), after_startup - before_startup, 100 * USEC_PER_MSEC));
+ if (arg_system) {
+ _cleanup_free_ char *taint;
+
+ taint = manager_taint_string(m);
+ if (!isempty(taint))
+ log_struct(LOG_NOTICE,
+ LOG_MESSAGE("System is tainted: %s", taint),
+ "TAINT=%s", taint,
+ "MESSAGE_ID=" SD_MESSAGE_TAINTED_STR,
+ NULL);
+ }
+
if (arg_action == ACTION_TEST) {
printf("-> By units:\n");
manager_dump_units(m, stdout, "\t");
* 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)
+ 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 0;
+ }
#endif
if (shutdown_verb) {
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,