#include "path-util.h"
#include "switch-root.h"
#include "capability.h"
+#include "killall.h"
+#include "env-util.h"
+#include "hwclock.h"
+#include "sd-daemon.h"
#include "mount-setup.h"
#include "loopback-setup.h"
+#ifdef HAVE_KMOD
#include "kmod-setup.h"
+#endif
#include "hostname-setup.h"
#include "machine-id-setup.h"
#include "locale-setup.h"
-#include "hwclock.h"
#include "selinux-setup.h"
#include "ima-setup.h"
+#include "fileio.h"
static enum {
ACTION_RUN,
ACTION_HELP,
+ ACTION_VERSION,
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 SystemdRunningAs arg_running_as = _SYSTEMD_RUNNING_AS_INVALID;
static bool arg_dump_core = true;
static bool arg_crash_shell = false;
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));
+ pid = fork();
+ if (pid < 0)
+ log_error("Failed to fork off crash shell: %m");
else if (pid == 0) {
- int fd, r;
-
- if ((fd = acquire_terminal("/dev/console", false, true, true, (usec_t) -1)) < 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));
-
+ make_console_stdio();
execl("/bin/sh", "/bin/sh", NULL);
- log_error("execl() failed: %s", strerror(errno));
+ log_error("execl() failed: %m");
_exit(1);
}
else
arg_default_std_error = r;
} else if (startswith(word, "systemd.setenv=")) {
- char *cenv, *eq;
+ _cleanup_free_ char *cenv = NULL;
+ char *eq;
int r;
cenv = strdup(word + 15);
eq = strchr(cenv, '=');
if (!eq) {
- r = unsetenv(cenv);
- if (r < 0)
- log_warning("unsetenv failed %s. Ignoring.", strerror(errno));
+ if (!env_name_is_valid(cenv))
+ log_warning("Environment variable name '%s' is not valid. Ignoring.", cenv);
+ else {
+ r = unsetenv(cenv);
+ if (r < 0)
+ log_warning("Unsetting environment variable '%s' failed, ignoring: %m", cenv);
+ }
} else {
- *eq = 0;
- r = setenv(cenv, eq + 1, 1);
- if (r < 0)
- log_warning("setenv failed %s. Ignoring.", strerror(errno));
+ if (!env_assignment_is_valid(cenv))
+ log_warning("Environment variable assignment '%s' is not valid. Ignoring.", cenv);
+ else {
+ *eq = 0;
+ r = setenv(cenv, eq + 1, 1);
+ if (r < 0)
+ log_warning("Setting environment variable '%s=%s' failed, ignoring: %m", cenv, eq + 1);
+ }
}
- free(cenv);
} else if (startswith(word, "systemd.") ||
(in_initrd() && startswith(word, "rd.systemd."))) {
unsigned cpu;
if (!(t = strndup(w, l)))
- return -ENOMEM;
+ return log_oom();
r = safe_atou(t, &cpu);
free(t);
if (!c)
if (!(c = cpu_set_malloc(&ncpus)))
- return -ENOMEM;
+ return log_oom();
if (r < 0 || cpu >= ncpus) {
log_error("[%s:%u] Failed to parse CPU affinity: %s", filename, line, rvalue);
s = strndup(w, length);
if (!s)
- return -ENOMEM;
+ return log_oom();
l = strv_split(s, ",");
free(s);
arg_join_controllers = new(char**, 2);
if (!arg_join_controllers) {
strv_free(l);
- return -ENOMEM;
+ return log_oom();
}
arg_join_controllers[0] = l;
t = new0(char**, n+2);
if (!t) {
strv_free(l);
- return -ENOMEM;
+ return log_oom();
}
n = 0;
if (!c) {
strv_free(l);
strv_free_free(t);
- return -ENOMEM;
+ return log_oom();
}
strv_free(l);
if (!c) {
strv_free(l);
strv_free_free(t);
- return -ENOMEM;
+ return log_oom();
}
t[n++] = c;
const char *fn;
int r;
- fn = arg_running_as == MANAGER_SYSTEM ? SYSTEM_CONFIG_FILE : USER_CONFIG_FILE;
+ fn = arg_running_as == SYSTEMD_SYSTEM ? SYSTEM_CONFIG_FILE : USER_CONFIG_FILE;
f = fopen(fn, "re");
if (!f) {
if (errno == ENOENT)
}
r = parse_proc_cmdline_word(word);
- free(word);
-
- if (r < 0)
+ if (r < 0) {
+ log_error("Failed on cmdline argument %s: %s", word, strerror(-r));
+ free(word);
goto finish;
+ }
+
+ free(word);
}
r = 0;
ARG_SYSTEM,
ARG_USER,
ARG_TEST,
+ ARG_VERSION,
ARG_DUMP_CONFIGURATION_ITEMS,
ARG_DUMP_CORE,
ARG_CRASH_SHELL,
{ "user", no_argument, NULL, ARG_USER },
{ "test", no_argument, NULL, ARG_TEST },
{ "help", no_argument, NULL, 'h' },
+ { "version", no_argument, NULL, ARG_VERSION },
{ "dump-configuration-items", no_argument, NULL, ARG_DUMP_CONFIGURATION_ITEMS },
{ "dump-core", optional_argument, NULL, ARG_DUMP_CORE },
{ "crash-shell", optional_argument, NULL, ARG_CRASH_SHELL },
break;
case ARG_SYSTEM:
- arg_running_as = MANAGER_SYSTEM;
+ arg_running_as = SYSTEMD_SYSTEM;
break;
case ARG_USER:
- arg_running_as = MANAGER_USER;
+ arg_running_as = SYSTEMD_USER;
break;
case ARG_TEST:
arg_action = ACTION_TEST;
break;
+ case ARG_VERSION:
+ arg_action = ACTION_VERSION;
+ break;
+
case ARG_DUMP_CONFIGURATION_ITEMS:
arg_action = ACTION_DUMP_CONFIGURATION_ITEMS;
break;
int fd;
FILE *f;
- if ((r = safe_atoi(optarg, &fd)) < 0 || fd < 0) {
+ r = safe_atoi(optarg, &fd);
+ if (r < 0 || fd < 0) {
log_error("Failed to parse deserialize option %s.", optarg);
- return r;
+ return r < 0 ? r : -EINVAL;
}
- if (!(f = fdopen(fd, "r"))) {
+ fd_cloexec(fd, true);
+
+ f = fdopen(fd, "r");
+ if (!f) {
log_error("Failed to open serialization fd: %m");
- return r;
+ return -errno;
}
if (serialization)
* instead. */
for (a = argv; a < argv + argc; a++)
- if ((r = parse_proc_cmdline_word(*a)) < 0)
+ if ((r = parse_proc_cmdline_word(*a)) < 0) {
+ log_error("Failed on cmdline argument %s: %s", *a, strerror(-r));
return r;
+ }
}
return 0;
return 0;
}
-static int prepare_reexecute(Manager *m, FILE **_f, FDSet **_fds) {
+static int version(void) {
+ puts(PACKAGE_STRING);
+ puts(SYSTEMD_FEATURES);
+
+ return 0;
+}
+
+static int prepare_reexecute(Manager *m, FILE **_f, FDSet **_fds, bool serialize_jobs) {
FILE *f = NULL;
FDSet *fds = NULL;
int r;
/* Make sure nothing is really destructed when we shut down */
m->n_reloading ++;
- if ((r = manager_open_serialization(m, &f)) < 0) {
+ r = manager_open_serialization(m, &f);
+ if (r < 0) {
log_error("Failed to create serialization file: %s", strerror(-r));
goto fail;
}
- if (!(fds = fdset_new())) {
+ fds = fdset_new();
+ if (!fds) {
r = -ENOMEM;
log_error("Failed to allocate fd set: %s", strerror(-r));
goto fail;
}
- if ((r = manager_serialize(m, f, fds)) < 0) {
+ r = manager_serialize(m, f, fds, serialize_jobs);
+ if (r < 0) {
log_error("Failed to serialize state: %s", strerror(-r));
goto fail;
}
goto fail;
}
- if ((r = fd_cloexec(fileno(f), false)) < 0) {
+ r = fd_cloexec(fileno(f), false);
+ if (r < 0) {
log_error("Failed to disable O_CLOEXEC for serialization: %s", strerror(-r));
goto fail;
}
- if ((r = fdset_cloexec(fds, false)) < 0) {
+ r = fdset_cloexec(fds, false);
+ if (r < 0) {
log_error("Failed to disable O_CLOEXEC for serialization fds: %s", strerror(-r));
goto fail;
}
return r;
}
+static int bump_rlimit_nofile(struct rlimit *saved_rlimit) {
+ struct rlimit nl;
+ int r;
+
+ assert(saved_rlimit);
+
+ /* Save the original RLIMIT_NOFILE so that we can reset it
+ * later when transitioning from the initrd to the main
+ * systemd or suchlike. */
+ if (getrlimit(RLIMIT_NOFILE, saved_rlimit) < 0) {
+ log_error("Reading RLIMIT_NOFILE failed: %m");
+ return -errno;
+ }
+
+ /* Make sure forked processes get the default kernel setting */
+ if (!arg_default_rlimit[RLIMIT_NOFILE]) {
+ struct rlimit *rl;
+
+ rl = newdup(struct rlimit, saved_rlimit, 1);
+ if (!rl)
+ return log_oom();
+
+ arg_default_rlimit[RLIMIT_NOFILE] = rl;
+ }
+
+ /* Bump up the resource limit for ourselves substantially */
+ nl.rlim_cur = nl.rlim_max = 64*1024;
+ r = setrlimit_closest(RLIMIT_NOFILE, &nl);
+ if (r < 0) {
+ log_error("Setting RLIMIT_NOFILE failed: %s", strerror(-r));
+ return r;
+ }
+
+ return 0;
+}
+
static struct dual_timestamp* parse_initrd_timestamp(struct dual_timestamp *t) {
const char *e;
unsigned long long a, b;
sleep(10);
}
+static int initialize_join_controllers(void) {
+ /* By default, mount "cpu" + "cpuacct" together, and "net_cls"
+ * + "net_prio". We'd like to add "cpuset" to the mix, but
+ * "cpuset" does't really work for groups with no initialized
+ * attributes. */
+
+ arg_join_controllers = new(char**, 3);
+ if (!arg_join_controllers)
+ return -ENOMEM;
+
+ arg_join_controllers[0] = strv_new("cpu", "cpuacct", NULL);
+ if (!arg_join_controllers[0])
+ return -ENOMEM;
+
+ arg_join_controllers[1] = strv_new("net_cls", "net_prio", NULL);
+ if (!arg_join_controllers[1])
+ return -ENOMEM;
+
+ arg_join_controllers[2] = NULL;
+ return 0;
+}
+
int main(int argc, char *argv[]) {
Manager *m = NULL;
int r, retval = EXIT_FAILURE;
bool arm_reboot_watchdog = false;
bool queue_default_job = false;
char *switch_root_dir = NULL, *switch_root_init = NULL;
+ static struct rlimit saved_rlimit_nofile = { 0, 0 };
#ifdef HAVE_SYSV_COMPAT
if (getpid() != 1 && strstr(program_invocation_short_name, "init")) {
saved_argc = argc;
log_show_color(isatty(STDERR_FILENO) > 0);
- log_show_location(false);
- log_set_max_level(LOG_INFO);
- if (getpid() == 1) {
+ if (getpid() == 1 && detect_container(NULL) <= 0) {
+
+ /* Running outside of a container as PID 1 */
+ arg_running_as = SYSTEMD_SYSTEM;
+ make_null_stdio();
+ log_set_target(LOG_TARGET_KMSG);
+ log_open();
+
if (in_initrd()) {
char *rd_timestamp = NULL;
}
}
- arg_running_as = MANAGER_SYSTEM;
- log_set_target(detect_container(NULL) > 0 ? LOG_TARGET_JOURNAL : LOG_TARGET_JOURNAL_OR_KMSG);
-
if (!skip_setup) {
if (selinux_setup(&loaded_policy) < 0)
goto finish;
goto finish;
}
- log_open();
-
if (label_init(NULL) < 0)
goto finish;
- if (!skip_setup)
+ if (!skip_setup) {
if (hwclock_is_localtime() > 0) {
int min;
- r = hwclock_apply_localtime_delta(&min);
+ /* The first-time call to settimeofday() does a time warp in the kernel */
+ r = hwclock_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 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.
+ */
+ hwclock_reset_timezone();
+
+ /* Tell the kernel our time zone */
+ r = hwclock_set_timezone(NULL);
+ if (r < 0)
+ log_error("Failed to set the kernel's time zone, ignoring: %s", strerror(-r));
}
+ }
+
+ /* 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 if (getpid() == 1) {
+
+ /* Running inside a container, as PID 1 */
+ arg_running_as = SYSTEMD_SYSTEM;
+ log_set_target(LOG_TARGET_CONSOLE);
+ log_open();
+
+ /* For the later on, see above... */
+ log_set_target(LOG_TARGET_JOURNAL);
} else {
- arg_running_as = MANAGER_USER;
+
+ /* Running as user instance */
+ arg_running_as = SYSTEMD_USER;
log_set_target(LOG_TARGET_AUTO);
log_open();
}
/* Initialize default unit */
- if (set_default_unit(SPECIAL_DEFAULT_TARGET) < 0)
- goto finish;
-
- /* By default, mount "cpu" and "cpuacct" together */
- arg_join_controllers = new(char**, 2);
- if (!arg_join_controllers)
+ r = set_default_unit(SPECIAL_DEFAULT_TARGET);
+ if (r < 0) {
+ log_error("Failed to set default unit %s: %s", SPECIAL_DEFAULT_TARGET, strerror(-r));
goto finish;
+ }
- arg_join_controllers[0] = strv_new("cpu", "cpuacct", NULL);
- arg_join_controllers[1] = NULL;
-
- if (!arg_join_controllers[0])
+ r = initialize_join_controllers();
+ if (r < 0)
goto finish;
/* Mount /proc, /sys and friends, so that /proc/cmdline and
if (parse_config_file() < 0)
goto finish;
- if (arg_running_as == MANAGER_SYSTEM)
+ if (arg_running_as == SYSTEMD_SYSTEM)
if (parse_proc_cmdline() < 0)
goto finish;
if (parse_argv(argc, argv) < 0)
goto finish;
- if (arg_action == ACTION_TEST && geteuid() == 0) {
+ if (arg_action == ACTION_TEST &&
+ geteuid() == 0) {
log_error("Don't run test mode as root.");
goto finish;
}
- if (arg_running_as == MANAGER_SYSTEM &&
+ if (arg_running_as == SYSTEMD_USER &&
+ arg_action == ACTION_RUN &&
+ sd_booted() <= 0) {
+ log_error("Trying to run as user instance, but the system has not been booted with systemd.");
+ goto finish;
+ }
+
+ if (arg_running_as == SYSTEMD_SYSTEM &&
arg_action == ACTION_RUN &&
running_in_chroot() > 0) {
log_error("Cannot be run in a chroot() environment.");
if (arg_action == ACTION_HELP) {
retval = help();
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;
log_close();
/* Remember open file descriptors for later deserialization */
- if (serialization) {
- r = fdset_new_fill(&fds);
- if (r < 0) {
- log_error("Failed to allocate fd set: %s", strerror(-r));
- goto finish;
- }
+ r = fdset_new_fill(&fds);
+ if (r < 0) {
+ log_error("Failed to allocate fd set: %s", strerror(-r));
+ goto finish;
+ } else
+ fdset_cloexec(fds, true);
+ if (serialization)
assert_se(fdset_remove(fds, fileno(serialization)) >= 0);
- } else
- close_all_fds(NULL, 0);
/* Set up PATH unless it is already set */
setenv("PATH",
#else
"/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin",
#endif
- arg_running_as == MANAGER_SYSTEM);
+ arg_running_as == SYSTEMD_SYSTEM);
- if (arg_running_as == MANAGER_SYSTEM) {
+ if (arg_running_as == SYSTEMD_SYSTEM) {
/* Parse the data passed to us. We leave this
* variables set, but the manager later on will not
* pass them on to our children. */
unsetenv("USER");
unsetenv("LOGNAME");
+ /* We suppress the socket activation env vars, as
+ * we'll try to match *any* open fd to units if
+ * possible. */
+ unsetenv("LISTEN_FDS");
+ unsetenv("LISTEN_PID");
+
/* All other variables are left as is, so that clients
* can still read them via /proc/1/environ */
}
/* Move out of the way, so that we won't block unmounts */
assert_se(chdir("/") == 0);
- if (arg_running_as == MANAGER_SYSTEM) {
+ if (arg_running_as == SYSTEMD_SYSTEM) {
/* Become a session leader if we aren't one yet. */
setsid();
/* 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) {
+ if (arg_running_as == SYSTEMD_SYSTEM && arg_action == ACTION_RUN)
console_setup(getpid() == 1 && !skip_setup);
- make_null_stdio();
- }
/* Open the logging devices, if possible and necessary */
log_open();
goto finish;
}
- if (arg_running_as == MANAGER_SYSTEM) {
+ if (arg_running_as == SYSTEMD_SYSTEM) {
const char *virtualization = NULL;
- log_info(PACKAGE_STRING " running in system mode. (" SYSTEMD_FEATURES "; " DISTRIBUTION ")");
+ log_info(PACKAGE_STRING " running in system mode. (" SYSTEMD_FEATURES ")");
detect_virtualization(&virtualization);
if (virtualization)
log_info("Detected virtualization '%s'.", virtualization);
+ if (in_initrd())
+ log_info("Running in initial RAM disk.");
+
} else
- log_debug(PACKAGE_STRING " running in user mode. (" SYSTEMD_FEATURES "; " DISTRIBUTION ")");
+ log_debug(PACKAGE_STRING " running in user mode. (" SYSTEMD_FEATURES ")");
- if (arg_running_as == MANAGER_SYSTEM && !skip_setup) {
+ if (arg_running_as == SYSTEMD_SYSTEM && !skip_setup) {
locale_setup();
if (arg_show_status || plymouth_running())
status_welcome();
+#ifdef HAVE_KMOD
kmod_setup();
+#endif
hostname_setup();
machine_id_setup();
loopback_setup();
test_cgroups();
}
- if (arg_running_as == MANAGER_SYSTEM && arg_runtime_watchdog > 0)
+ 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_running_as == SYSTEMD_USER) {
+ /* Become reaper of our children */
+ if (prctl(PR_SET_CHILD_SUBREAPER, 1) < 0) {
+ log_warning("Failed to make us a subreaper: %m");
+ if (errno == EINVAL)
+ log_info("Perhaps the kernel version is too old (< 3.4?)");
+ }
+ }
+
+ if (arg_running_as == SYSTEMD_SYSTEM)
+ bump_rlimit_nofile(&saved_rlimit_nofile);
+
r = manager_new(arg_running_as, &m);
if (r < 0) {
log_error("Failed to allocate manager object: %s", strerror(-r));
/* This will close all file descriptors that were opened, but
* not claimed by any unit. */
- if (fds) {
- fdset_free(fds);
- fds = NULL;
- }
+ fdset_free(fds);
if (serialization) {
fclose(serialization);
case MANAGER_REEXECUTE:
- if (prepare_reexecute(m, &serialization, &fds) < 0)
+ if (prepare_reexecute(m, &serialization, &fds, true) < 0)
goto finish;
reexecute = true;
m->switch_root = m->switch_root_init = NULL;
if (!switch_root_init)
- if (prepare_reexecute(m, &serialization, &fds) < 0)
+ if (prepare_reexecute(m, &serialization, &fds, false) < 0)
goto finish;
reexecute = true;
manager_free(m);
for (j = 0; j < RLIMIT_NLIMITS; j++)
- free (arg_default_rlimit[j]);
+ free(arg_default_rlimit[j]);
free(arg_default_unit);
strv_free(arg_default_controllers);
* rebooted while we do that */
watchdog_close(true);
+ /* 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)
+ 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);
+
+ /* And switch root */
r = switch_root(switch_root_dir);
if (r < 0)
log_error("Failed to switch root, ignoring: %s", strerror(-r));
args[i++] = SYSTEMD_BINARY_PATH;
if (switch_root_dir)
args[i++] = "--switched-root";
- args[i++] = arg_running_as == MANAGER_SYSTEM ? "--system" : "--user";
+ args[i++] = arg_running_as == SYSTEMD_SYSTEM ? "--system" : "--user";
args[i++] = "--deserialize";
args[i++] = sfd;
args[i++] = NULL;
fds = NULL;
}
+ /* Reopen the console */
+ make_console_stdio();
+
for (j = 1, i = 1; j < argc; j++)
args[i++] = argv[j];
args[i++] = NULL;