#include "bus-kernel.h"
#include "bus-util.h"
#include "clean-ipc.h"
+#include "clock-util.h"
#include "dbus-job.h"
#include "dbus-manager.h"
#include "dbus-unit.h"
static int manager_dispatch_user_lookup_fd(sd_event_source *source, int fd, uint32_t revents, void *userdata);
static int manager_dispatch_jobs_in_progress(sd_event_source *source, usec_t usec, void *userdata);
static int manager_dispatch_run_queue(sd_event_source *source, void *userdata);
+static int manager_dispatch_sigchld(sd_event_source *source, void *userdata);
static int manager_run_environment_generators(Manager *m);
static int manager_run_generators(Manager *m);
(void) sd_event_source_set_description(m->jobs_in_progress_event_source, "manager-jobs-in-progress");
}
-#define CYLON_BUFFER_EXTRA (2*(sizeof(ANSI_RED)-1) + sizeof(ANSI_HIGHLIGHT_RED)-1 + 2*(sizeof(ANSI_NORMAL)-1))
+#define CYLON_BUFFER_EXTRA (2*STRLEN(ANSI_RED) + STRLEN(ANSI_HIGHLIGHT_RED) + 2*STRLEN(ANSI_NORMAL))
static void draw_cylon(char buffer[], size_t buflen, unsigned width, unsigned pos) {
char *p = buffer;
assert(m);
- flush_fd(fd);
+ (void) flush_fd(fd);
m->have_ask_password = have_ask_password();
if (m->have_ask_password < 0)
return 0;
}
-static void manager_clean_environment(Manager *m) {
+static void manager_sanitize_environment(Manager *m) {
assert(m);
- /* Let's remove some environment variables that we
- * need ourselves to communicate with our clients */
+ /* Let's remove some environment variables that we need ourselves to communicate with our clients */
strv_env_unset_many(
m->environment,
- "NOTIFY_SOCKET",
+ "EXIT_CODE",
+ "EXIT_STATUS",
+ "INVOCATION_ID",
+ "JOURNAL_STREAM",
+ "LISTEN_FDNAMES",
+ "LISTEN_FDS",
+ "LISTEN_PID",
"MAINPID",
"MANAGERPID",
- "LISTEN_PID",
- "LISTEN_FDS",
- "LISTEN_FDNAMES",
+ "NOTIFY_SOCKET",
+ "REMOTE_ADDR",
+ "REMOTE_PORT",
+ "SERVICE_RESULT",
"WATCHDOG_PID",
"WATCHDOG_USEC",
- "INVOCATION_ID",
NULL);
+
+ /* Let's order the environment alphabetically, just to make it pretty */
+ strv_sort(m->environment);
}
static int manager_default_environment(Manager *m) {
if (!m->environment)
return -ENOMEM;
- manager_clean_environment(m);
- strv_sort(m->environment);
+ manager_sanitize_environment(m);
return 0;
}
return 0;
}
+static int manager_setup_run_queue(Manager *m) {
+ int r;
+
+ assert(m);
+ assert(!m->run_queue_event_source);
+
+ r = sd_event_add_defer(m->event, &m->run_queue_event_source, manager_dispatch_run_queue, m);
+ if (r < 0)
+ return r;
+
+ r = sd_event_source_set_priority(m->run_queue_event_source, SD_EVENT_PRIORITY_IDLE);
+ if (r < 0)
+ return r;
+
+ r = sd_event_source_set_enabled(m->run_queue_event_source, SD_EVENT_OFF);
+ if (r < 0)
+ return r;
+
+ (void) sd_event_source_set_description(m->run_queue_event_source, "manager-run-queue");
+
+ return 0;
+}
+
+static int manager_setup_sigchld_event_source(Manager *m) {
+ int r;
+
+ assert(m);
+ assert(!m->sigchld_event_source);
+
+ r = sd_event_add_defer(m->event, &m->sigchld_event_source, manager_dispatch_sigchld, m);
+ if (r < 0)
+ return r;
+
+ r = sd_event_source_set_priority(m->sigchld_event_source, SD_EVENT_PRIORITY_NORMAL-7);
+ if (r < 0)
+ return r;
+
+ r = sd_event_source_set_enabled(m->sigchld_event_source, SD_EVENT_OFF);
+ if (r < 0)
+ return r;
+
+ (void) sd_event_source_set_description(m->sigchld_event_source, "manager-sigchld");
+
+ return 0;
+}
+
int manager_new(UnitFileScope scope, unsigned test_run_flags, Manager **_m) {
Manager *m;
int r;
if (r < 0)
goto fail;
- r = sd_event_add_defer(m->event, &m->run_queue_event_source, manager_dispatch_run_queue, m);
- if (r < 0)
- goto fail;
-
- r = sd_event_source_set_priority(m->run_queue_event_source, SD_EVENT_PRIORITY_IDLE);
- if (r < 0)
- goto fail;
-
- r = sd_event_source_set_enabled(m->run_queue_event_source, SD_EVENT_OFF);
+ r = manager_setup_run_queue(m);
if (r < 0)
goto fail;
- (void) sd_event_source_set_description(m->run_queue_event_source, "manager-run-queue");
-
r = manager_setup_signals(m);
if (r < 0)
goto fail;
if (r < 0)
goto fail;
+ r = manager_setup_sigchld_event_source(m);
+ if (r < 0)
+ goto fail;
+
m->udev = udev_new();
if (!m->udev) {
r = -ENOMEM;
goto fail;
}
- if (MANAGER_IS_SYSTEM(m)) {
+ r = manager_setup_prefix(m);
+ if (r < 0)
+ goto fail;
+
+ if (MANAGER_IS_SYSTEM(m) && test_run_flags == 0) {
r = mkdir_label("/run/systemd/units", 0755);
if (r < 0 && r != -EEXIST)
goto fail;
}
+ m->taint_usr =
+ !in_initrd() &&
+ dir_is_empty("/usr") > 0;
+
/* Note that we do not set up the notify fd here. We do that after deserialization,
* since they might have gotten serialized across the reexec. */
- m->taint_usr = dir_is_empty("/usr") > 0;
-
- r = manager_setup_prefix(m);
- if (r < 0)
- goto fail;
-
*_m = m;
return 0;
/* Process notification messages a bit earlier than SIGCHLD, so that we can still identify to which
* service an exit message belongs. */
- r = sd_event_source_set_priority(m->notify_event_source, SD_EVENT_PRIORITY_NORMAL-7);
+ r = sd_event_source_set_priority(m->notify_event_source, SD_EVENT_PRIORITY_NORMAL-8);
if (r < 0)
return log_error_errno(r, "Failed to set priority of notify event source: %m");
/* Process even earlier than the notify event source, so that we always know first about valid UID/GID
* resolutions */
- r = sd_event_source_set_priority(m->user_lookup_event_source, SD_EVENT_PRIORITY_NORMAL-8);
+ r = sd_event_source_set_priority(m->user_lookup_event_source, SD_EVENT_PRIORITY_NORMAL-11);
if (r < 0)
return log_error_errno(errno, "Failed to set priority ot user lookup event source: %m");
hashmap_free(m->units);
hashmap_free(m->units_by_invocation_id);
hashmap_free(m->jobs);
- hashmap_free(m->watch_pids1);
- hashmap_free(m->watch_pids2);
+ hashmap_free(m->watch_pids);
hashmap_free(m->watch_bus);
set_free(m->startup_units);
set_free(m->failed_units);
sd_event_source_unref(m->signal_event_source);
+ sd_event_source_unref(m->sigchld_event_source);
sd_event_source_unref(m->notify_event_source);
sd_event_source_unref(m->cgroups_agent_event_source);
sd_event_source_unref(m->time_change_event_source);
if (!f)
return -errno;
- __fsetlocking(f, FSETLOCKING_BYCALLER);
+ (void) __fsetlocking(f, FSETLOCKING_BYCALLER);
manager_dump(m, f, NULL);
return 0;
}
-static void manager_invoke_notify_message(Manager *m, Unit *u, pid_t pid, const char *buf, FDSet *fds) {
- _cleanup_strv_free_ char **tags = NULL;
+static void manager_invoke_notify_message(
+ Manager *m,
+ Unit *u,
+ const struct ucred *ucred,
+ const char *buf,
+ FDSet *fds) {
assert(m);
assert(u);
+ assert(ucred);
assert(buf);
- tags = strv_split(buf, "\n\r");
- if (!tags) {
- log_oom();
+ if (u->notifygen == m->notifygen) /* Already invoked on this same unit in this same iteration? */
return;
- }
+ u->notifygen = m->notifygen;
+
+ if (UNIT_VTABLE(u)->notify_message) {
+ _cleanup_strv_free_ char **tags = NULL;
+
+ tags = strv_split(buf, NEWLINE);
+ if (!tags) {
+ log_oom();
+ return;
+ }
- if (UNIT_VTABLE(u)->notify_message)
- UNIT_VTABLE(u)->notify_message(u, pid, tags, fds);
- else if (_unlikely_(log_get_max_level() >= LOG_DEBUG)) {
+ UNIT_VTABLE(u)->notify_message(u, ucred, tags, fds);
+
+ } else if (DEBUG_LOGGING) {
_cleanup_free_ char *x = NULL, *y = NULL;
- x = cescape(buf);
+ x = ellipsize(buf, 20, 90);
if (x)
- y = ellipsize(x, 20, 90);
+ y = cescape(x);
+
log_unit_debug(u, "Got notification message \"%s\", ignoring.", strnull(y));
}
}
struct cmsghdr *cmsg;
struct ucred *ucred = NULL;
- Unit *u1, *u2, *u3;
+ _cleanup_free_ Unit **array_copy = NULL;
+ Unit *u1, *u2, **array;
int r, *fd_array = NULL;
unsigned n_fds = 0;
+ bool found = false;
ssize_t n;
assert(m);
}
}
- if (!ucred || ucred->pid <= 0) {
+ if (!ucred || !pid_is_valid(ucred->pid)) {
log_warning("Received notify message without valid credentials. Ignoring.");
return 0;
}
/* Make sure it's NUL-terminated. */
buf[n] = 0;
- /* Notify every unit that might be interested, but try
- * to avoid notifying the same one multiple times. */
+ /* Increase the generation counter used for filtering out duplicate unit invocations. */
+ m->notifygen++;
+
+ /* Notify every unit that might be interested, which might be multiple. */
u1 = manager_get_unit_by_pid_cgroup(m, ucred->pid);
- if (u1)
- manager_invoke_notify_message(m, u1, ucred->pid, buf, fds);
+ u2 = hashmap_get(m->watch_pids, PID_TO_PTR(ucred->pid));
+ array = hashmap_get(m->watch_pids, PID_TO_PTR(-ucred->pid));
+ if (array) {
+ size_t k = 0;
- u2 = hashmap_get(m->watch_pids1, PID_TO_PTR(ucred->pid));
- if (u2 && u2 != u1)
- manager_invoke_notify_message(m, u2, ucred->pid, buf, fds);
+ while (array[k])
+ k++;
- u3 = hashmap_get(m->watch_pids2, PID_TO_PTR(ucred->pid));
- if (u3 && u3 != u2 && u3 != u1)
- manager_invoke_notify_message(m, u3, ucred->pid, buf, fds);
+ array_copy = newdup(Unit*, array, k+1);
+ if (!array_copy)
+ log_oom();
+ }
+ /* And now invoke the per-unit callbacks. Note that manager_invoke_notify_message() will handle duplicate units
+ * make sure we only invoke each unit's handler once. */
+ if (u1) {
+ manager_invoke_notify_message(m, u1, ucred, buf, fds);
+ found = true;
+ }
+ if (u2) {
+ manager_invoke_notify_message(m, u2, ucred, buf, fds);
+ found = true;
+ }
+ if (array_copy)
+ for (size_t i = 0; array_copy[i]; i++) {
+ manager_invoke_notify_message(m, array_copy[i], ucred, buf, fds);
+ found = true;
+ }
- if (!u1 && !u2 && !u3)
- log_warning("Cannot find unit for notify message of PID "PID_FMT".", ucred->pid);
+ if (!found)
+ log_warning("Cannot find unit for notify message of PID "PID_FMT", ignoring.", ucred->pid);
if (fdset_size(fds) > 0)
log_warning("Got extra auxiliary fds with notification message, closing them.");
return 0;
}
-static void invoke_sigchld_event(Manager *m, Unit *u, const siginfo_t *si) {
- uint64_t iteration;
+static void manager_invoke_sigchld_event(
+ Manager *m,
+ Unit *u,
+ const siginfo_t *si) {
assert(m);
assert(u);
assert(si);
- sd_event_get_iteration(m->event, &iteration);
-
- log_unit_debug(u, "Child "PID_FMT" belongs to %s", si->si_pid, u->id);
+ /* Already invoked the handler of this unit in this iteration? Then don't process this again */
+ if (u->sigchldgen == m->sigchldgen)
+ return;
+ u->sigchldgen = m->sigchldgen;
+ log_unit_debug(u, "Child "PID_FMT" belongs to %s.", si->si_pid, u->id);
unit_unwatch_pid(u, si->si_pid);
- if (UNIT_VTABLE(u)->sigchld_event) {
- if (set_size(u->pids) <= 1 ||
- iteration != u->sigchldgen ||
- unit_main_pid(u) == si->si_pid ||
- unit_control_pid(u) == si->si_pid) {
- UNIT_VTABLE(u)->sigchld_event(u, si->si_pid, si->si_code, si->si_status);
- u->sigchldgen = iteration;
- } else
- log_debug("%s already issued a sigchld this iteration %" PRIu64 ", skipping. Pids still being watched %d", u->id, iteration, set_size(u->pids));
- }
+ if (UNIT_VTABLE(u)->sigchld_event)
+ UNIT_VTABLE(u)->sigchld_event(u, si->si_pid, si->si_code, si->si_status);
}
-static int manager_dispatch_sigchld(Manager *m) {
+static int manager_dispatch_sigchld(sd_event_source *source, void *userdata) {
+ Manager *m = userdata;
+ siginfo_t si = {};
+ int r;
+
+ assert(source);
assert(m);
- for (;;) {
- siginfo_t si = {};
+ /* First we call waitd() for a PID and do not reap the zombie. That way we can still access /proc/$PID for it
+ * while it is a zombie. */
- /* First we call waitd() for a PID and do not reap the
- * zombie. That way we can still access /proc/$PID for
- * it while it is a zombie. */
- if (waitid(P_ALL, 0, &si, WEXITED|WNOHANG|WNOWAIT) < 0) {
+ if (waitid(P_ALL, 0, &si, WEXITED|WNOHANG|WNOWAIT) < 0) {
- if (errno == ECHILD)
- break;
+ if (errno == ECHILD)
+ goto turn_off;
- if (errno == EINTR)
- continue;
+ log_error_errno(errno, "Failed to peek for child with waitid(), ignoring: %m");
+ return 0;
+ }
- return -errno;
+ if (si.si_pid <= 0)
+ goto turn_off;
+
+ if (IN_SET(si.si_code, CLD_EXITED, CLD_KILLED, CLD_DUMPED)) {
+ _cleanup_free_ Unit **array_copy = NULL;
+ _cleanup_free_ char *name = NULL;
+ Unit *u1, *u2, **array;
+
+ (void) get_process_comm(si.si_pid, &name);
+
+ log_debug("Child "PID_FMT" (%s) died (code=%s, status=%i/%s)",
+ si.si_pid, strna(name),
+ sigchld_code_to_string(si.si_code),
+ si.si_status,
+ strna(si.si_code == CLD_EXITED
+ ? exit_status_to_string(si.si_status, EXIT_STATUS_FULL)
+ : signal_to_string(si.si_status)));
+
+ /* Increase the generation counter used for filtering out duplicate unit invocations */
+ m->sigchldgen++;
+
+ /* And now figure out the unit this belongs to, it might be multiple... */
+ u1 = manager_get_unit_by_pid_cgroup(m, si.si_pid);
+ u2 = hashmap_get(m->watch_pids, PID_TO_PTR(si.si_pid));
+ array = hashmap_get(m->watch_pids, PID_TO_PTR(-si.si_pid));
+ if (array) {
+ size_t n = 0;
+
+ /* Cound how many entries the array has */
+ while (array[n])
+ n++;
+
+ /* Make a copy of the array so that we don't trip up on the array changing beneath us */
+ array_copy = newdup(Unit*, array, n+1);
+ if (!array_copy)
+ log_oom();
}
- if (si.si_pid <= 0)
- break;
+ /* Finally, execute them all. Note that u1, u2 and the array might contain duplicates, but
+ * that's fine, manager_invoke_sigchld_event() will ensure we only invoke the handlers once for
+ * each iteration. */
+ if (u1)
+ manager_invoke_sigchld_event(m, u1, &si);
+ if (u2)
+ manager_invoke_sigchld_event(m, u2, &si);
+ if (array_copy)
+ for (size_t i = 0; array_copy[i]; i++)
+ manager_invoke_sigchld_event(m, array_copy[i], &si);
+ }
- if (IN_SET(si.si_code, CLD_EXITED, CLD_KILLED, CLD_DUMPED)) {
- _cleanup_free_ char *name = NULL;
- Unit *u1, *u2, *u3;
-
- get_process_comm(si.si_pid, &name);
-
- log_debug("Child "PID_FMT" (%s) died (code=%s, status=%i/%s)",
- si.si_pid, strna(name),
- sigchld_code_to_string(si.si_code),
- si.si_status,
- strna(si.si_code == CLD_EXITED
- ? exit_status_to_string(si.si_status, EXIT_STATUS_FULL)
- : signal_to_string(si.si_status)));
-
- /* And now figure out the unit this belongs
- * to, it might be multiple... */
- u1 = manager_get_unit_by_pid_cgroup(m, si.si_pid);
- if (u1)
- invoke_sigchld_event(m, u1, &si);
- u2 = hashmap_get(m->watch_pids1, PID_TO_PTR(si.si_pid));
- if (u2 && u2 != u1)
- invoke_sigchld_event(m, u2, &si);
- u3 = hashmap_get(m->watch_pids2, PID_TO_PTR(si.si_pid));
- if (u3 && u3 != u2 && u3 != u1)
- invoke_sigchld_event(m, u3, &si);
- }
+ /* And now, we actually reap the zombie. */
+ if (waitid(P_PID, si.si_pid, &si, WEXITED) < 0) {
+ log_error_errno(errno, "Failed to dequeue child, ignoring: %m");
+ return 0;
+ }
- /* And now, we actually reap the zombie. */
- if (waitid(P_PID, si.si_pid, &si, WEXITED) < 0) {
- if (errno == EINTR)
- continue;
+ return 0;
- return -errno;
- }
- }
+turn_off:
+ /* All children processed for now, turn off event source */
+
+ r = sd_event_source_set_enabled(m->sigchld_event_source, SD_EVENT_OFF);
+ if (r < 0)
+ return log_error_errno(r, "Failed to disable SIGCHLD event source: %m");
return 0;
}
Manager *m = userdata;
ssize_t n;
struct signalfd_siginfo sfsi;
- bool sigchld = false;
int r;
assert(m);
return 0;
}
- for (;;) {
- n = read(m->signal_fd, &sfsi, sizeof(sfsi));
- if (n != sizeof(sfsi)) {
- if (n >= 0) {
- log_warning("Truncated read from signal fd (%zu bytes)!", n);
- return 0;
- }
+ n = read(m->signal_fd, &sfsi, sizeof(sfsi));
+ if (n != sizeof(sfsi)) {
+ if (n >= 0) {
+ log_warning("Truncated read from signal fd (%zu bytes), ignoring!", n);
+ return 0;
+ }
- if (IN_SET(errno, EINTR, EAGAIN))
- break;
+ if (IN_SET(errno, EINTR, EAGAIN))
+ return 0;
- /* We return an error here, which will kill this handler,
- * to avoid a busy loop on read error. */
- return log_error_errno(errno, "Reading from signal fd failed: %m");
- }
+ /* We return an error here, which will kill this handler,
+ * to avoid a busy loop on read error. */
+ return log_error_errno(errno, "Reading from signal fd failed: %m");
+ }
- log_received_signal(sfsi.ssi_signo == SIGCHLD ||
- (sfsi.ssi_signo == SIGTERM && MANAGER_IS_USER(m))
- ? LOG_DEBUG : LOG_INFO,
- &sfsi);
+ log_received_signal(sfsi.ssi_signo == SIGCHLD ||
+ (sfsi.ssi_signo == SIGTERM && MANAGER_IS_USER(m))
+ ? LOG_DEBUG : LOG_INFO,
+ &sfsi);
- switch (sfsi.ssi_signo) {
+ switch (sfsi.ssi_signo) {
- case SIGCHLD:
- sigchld = true;
- break;
+ case SIGCHLD:
+ r = sd_event_source_set_enabled(m->sigchld_event_source, SD_EVENT_ON);
+ if (r < 0)
+ log_warning_errno(r, "Failed to enable SIGCHLD even source, ignoring: %m");
- case SIGTERM:
- if (MANAGER_IS_SYSTEM(m)) {
- /* This is for compatibility with the
- * original sysvinit */
- r = verify_run_space_and_log("Refusing to reexecute");
- if (r >= 0)
- m->exit_code = MANAGER_REEXECUTE;
- break;
- }
+ break;
- _fallthrough_;
- case SIGINT:
- if (MANAGER_IS_SYSTEM(m))
- manager_handle_ctrl_alt_del(m);
- else
- manager_start_target(m, SPECIAL_EXIT_TARGET,
- JOB_REPLACE_IRREVERSIBLY);
+ case SIGTERM:
+ if (MANAGER_IS_SYSTEM(m)) {
+ /* This is for compatibility with the
+ * original sysvinit */
+ r = verify_run_space_and_log("Refusing to reexecute");
+ if (r >= 0)
+ m->exit_code = MANAGER_REEXECUTE;
break;
+ }
- case SIGWINCH:
- if (MANAGER_IS_SYSTEM(m))
- manager_start_target(m, SPECIAL_KBREQUEST_TARGET, JOB_REPLACE);
+ _fallthrough_;
+ case SIGINT:
+ if (MANAGER_IS_SYSTEM(m))
+ manager_handle_ctrl_alt_del(m);
+ else
+ manager_start_target(m, SPECIAL_EXIT_TARGET,
+ JOB_REPLACE_IRREVERSIBLY);
+ break;
- /* This is a nop on non-init */
- break;
+ case SIGWINCH:
+ if (MANAGER_IS_SYSTEM(m))
+ manager_start_target(m, SPECIAL_KBREQUEST_TARGET, JOB_REPLACE);
- case SIGPWR:
- if (MANAGER_IS_SYSTEM(m))
- manager_start_target(m, SPECIAL_SIGPWR_TARGET, JOB_REPLACE);
+ /* This is a nop on non-init */
+ break;
- /* This is a nop on non-init */
- break;
+ case SIGPWR:
+ if (MANAGER_IS_SYSTEM(m))
+ manager_start_target(m, SPECIAL_SIGPWR_TARGET, JOB_REPLACE);
+
+ /* This is a nop on non-init */
+ break;
- case SIGUSR1: {
- Unit *u;
+ case SIGUSR1: {
+ Unit *u;
- u = manager_get_unit(m, SPECIAL_DBUS_SERVICE);
+ u = manager_get_unit(m, SPECIAL_DBUS_SERVICE);
- if (!u || UNIT_IS_ACTIVE_OR_RELOADING(unit_active_state(u))) {
- log_info("Trying to reconnect to bus...");
- bus_init(m, true);
- }
+ if (!u || UNIT_IS_ACTIVE_OR_RELOADING(unit_active_state(u))) {
+ log_info("Trying to reconnect to bus...");
+ bus_init(m, true);
+ }
- if (!u || !UNIT_IS_ACTIVE_OR_ACTIVATING(unit_active_state(u))) {
- log_info("Loading D-Bus service...");
- manager_start_target(m, SPECIAL_DBUS_SERVICE, JOB_REPLACE);
- }
+ if (!u || !UNIT_IS_ACTIVE_OR_ACTIVATING(unit_active_state(u))) {
+ log_info("Loading D-Bus service...");
+ manager_start_target(m, SPECIAL_DBUS_SERVICE, JOB_REPLACE);
+ }
+ break;
+ }
+
+ case SIGUSR2: {
+ _cleanup_free_ char *dump = NULL;
+
+ r = manager_get_dump_string(m, &dump);
+ if (r < 0) {
+ log_warning_errno(errno, "Failed to acquire manager dump: %m");
break;
}
- case SIGUSR2: {
- _cleanup_free_ char *dump = NULL;
+ log_dump(LOG_INFO, dump);
+ break;
+ }
- r = manager_get_dump_string(m, &dump);
- if (r < 0) {
- log_warning_errno(errno, "Failed to acquire manager dump: %m");
- break;
- }
+ case SIGHUP:
+ r = verify_run_space_and_log("Refusing to reload");
+ if (r >= 0)
+ m->exit_code = MANAGER_RELOAD;
+ break;
+
+ default: {
+
+ /* Starting SIGRTMIN+0 */
+ static const struct {
+ const char *target;
+ JobMode mode;
+ } target_table[] = {
+ [0] = { SPECIAL_DEFAULT_TARGET, JOB_ISOLATE },
+ [1] = { SPECIAL_RESCUE_TARGET, JOB_ISOLATE },
+ [2] = { SPECIAL_EMERGENCY_TARGET, JOB_ISOLATE },
+ [3] = { SPECIAL_HALT_TARGET, JOB_REPLACE_IRREVERSIBLY },
+ [4] = { SPECIAL_POWEROFF_TARGET, JOB_REPLACE_IRREVERSIBLY },
+ [5] = { SPECIAL_REBOOT_TARGET, JOB_REPLACE_IRREVERSIBLY },
+ [6] = { SPECIAL_KEXEC_TARGET, JOB_REPLACE_IRREVERSIBLY },
+ };
- log_dump(LOG_INFO, dump);
+ /* Starting SIGRTMIN+13, so that target halt and system halt are 10 apart */
+ static const ManagerExitCode code_table[] = {
+ [0] = MANAGER_HALT,
+ [1] = MANAGER_POWEROFF,
+ [2] = MANAGER_REBOOT,
+ [3] = MANAGER_KEXEC,
+ };
+
+ if ((int) sfsi.ssi_signo >= SIGRTMIN+0 &&
+ (int) sfsi.ssi_signo < SIGRTMIN+(int) ELEMENTSOF(target_table)) {
+ int idx = (int) sfsi.ssi_signo - SIGRTMIN;
+ manager_start_target(m, target_table[idx].target,
+ target_table[idx].mode);
break;
}
- case SIGHUP:
- r = verify_run_space_and_log("Refusing to reload");
- if (r >= 0)
- m->exit_code = MANAGER_RELOAD;
+ if ((int) sfsi.ssi_signo >= SIGRTMIN+13 &&
+ (int) sfsi.ssi_signo < SIGRTMIN+13+(int) ELEMENTSOF(code_table)) {
+ m->exit_code = code_table[sfsi.ssi_signo - SIGRTMIN - 13];
+ break;
+ }
+
+ switch (sfsi.ssi_signo - SIGRTMIN) {
+
+ case 20:
+ manager_set_show_status(m, SHOW_STATUS_YES);
break;
- default: {
-
- /* Starting SIGRTMIN+0 */
- static const struct {
- const char *target;
- JobMode mode;
- } target_table[] = {
- [0] = { SPECIAL_DEFAULT_TARGET, JOB_ISOLATE },
- [1] = { SPECIAL_RESCUE_TARGET, JOB_ISOLATE },
- [2] = { SPECIAL_EMERGENCY_TARGET, JOB_ISOLATE },
- [3] = { SPECIAL_HALT_TARGET, JOB_REPLACE_IRREVERSIBLY },
- [4] = { SPECIAL_POWEROFF_TARGET, JOB_REPLACE_IRREVERSIBLY },
- [5] = { SPECIAL_REBOOT_TARGET, JOB_REPLACE_IRREVERSIBLY },
- [6] = { SPECIAL_KEXEC_TARGET, JOB_REPLACE_IRREVERSIBLY }
- };
-
- /* Starting SIGRTMIN+13, so that target halt and system halt are 10 apart */
- static const ManagerExitCode code_table[] = {
- [0] = MANAGER_HALT,
- [1] = MANAGER_POWEROFF,
- [2] = MANAGER_REBOOT,
- [3] = MANAGER_KEXEC
- };
-
- if ((int) sfsi.ssi_signo >= SIGRTMIN+0 &&
- (int) sfsi.ssi_signo < SIGRTMIN+(int) ELEMENTSOF(target_table)) {
- int idx = (int) sfsi.ssi_signo - SIGRTMIN;
- manager_start_target(m, target_table[idx].target,
- target_table[idx].mode);
- break;
- }
+ case 21:
+ manager_set_show_status(m, SHOW_STATUS_NO);
+ break;
- if ((int) sfsi.ssi_signo >= SIGRTMIN+13 &&
- (int) sfsi.ssi_signo < SIGRTMIN+13+(int) ELEMENTSOF(code_table)) {
- m->exit_code = code_table[sfsi.ssi_signo - SIGRTMIN - 13];
- break;
- }
+ case 22:
+ log_set_max_level(LOG_DEBUG);
+ log_info("Setting log level to debug.");
+ break;
+
+ case 23:
+ log_set_max_level(LOG_INFO);
+ log_info("Setting log level to info.");
+ break;
- switch (sfsi.ssi_signo - SIGRTMIN) {
-
- case 20:
- manager_set_show_status(m, SHOW_STATUS_YES);
- break;
-
- case 21:
- manager_set_show_status(m, SHOW_STATUS_NO);
- break;
-
- case 22:
- log_set_max_level(LOG_DEBUG);
- log_info("Setting log level to debug.");
- break;
-
- case 23:
- log_set_max_level(LOG_INFO);
- log_info("Setting log level to info.");
- break;
-
- case 24:
- if (MANAGER_IS_USER(m)) {
- m->exit_code = MANAGER_EXIT;
- return 0;
- }
-
- /* This is a nop on init */
- break;
-
- case 26:
- case 29: /* compatibility: used to be mapped to LOG_TARGET_SYSLOG_OR_KMSG */
- log_set_target(LOG_TARGET_JOURNAL_OR_KMSG);
- log_notice("Setting log target to journal-or-kmsg.");
- break;
-
- case 27:
- log_set_target(LOG_TARGET_CONSOLE);
- log_notice("Setting log target to console.");
- break;
-
- case 28:
- log_set_target(LOG_TARGET_KMSG);
- log_notice("Setting log target to kmsg.");
- break;
-
- default:
- log_warning("Got unhandled signal <%s>.", signal_to_string(sfsi.ssi_signo));
+ case 24:
+ if (MANAGER_IS_USER(m)) {
+ m->exit_code = MANAGER_EXIT;
+ return 0;
}
- }
- }
- }
- if (sigchld)
- manager_dispatch_sigchld(m);
+ /* This is a nop on init */
+ break;
+
+ case 26:
+ case 29: /* compatibility: used to be mapped to LOG_TARGET_SYSLOG_OR_KMSG */
+ log_set_target(LOG_TARGET_JOURNAL_OR_KMSG);
+ log_notice("Setting log target to journal-or-kmsg.");
+ break;
+
+ case 27:
+ log_set_target(LOG_TARGET_CONSOLE);
+ log_notice("Setting log target to console.");
+ break;
+
+ case 28:
+ log_set_target(LOG_TARGET_KMSG);
+ log_notice("Setting log target to kmsg.");
+ break;
+
+ default:
+ log_warning("Got unhandled signal <%s>.", signal_to_string(sfsi.ssi_signo));
+ }
+ }}
return 0;
}
manager_check_finished(m);
- /* There might still be some zombies hanging around from
- * before we were exec()'ed. Let's reap them. */
- r = manager_dispatch_sigchld(m);
+ /* There might still be some zombies hanging around from before we were exec()'ed. Let's reap them. */
+ r = sd_event_source_set_enabled(m->sigchld_event_source, SD_EVENT_ON);
if (r < 0)
- return r;
+ return log_error_errno(r, "Failed to enable SIGCHLD event source: %m");
while (m->exit_code == MANAGER_OK) {
usec_t wait_usec;
fprintf(f, "n-failed-jobs=%u\n", m->n_failed_jobs);
fprintf(f, "taint-usr=%s\n", yes_no(m->taint_usr));
fprintf(f, "ready-sent=%s\n", yes_no(m->ready_sent));
+ fprintf(f, "taint-logged=%s\n", yes_no(m->taint_logged));
+ fprintf(f, "service-watchdogs=%s\n", yes_no(m->service_watchdogs));
for (q = 0; q < _MANAGER_TIMESTAMP_MAX; q++) {
/* The userspace and finish timestamps only apply to the host system, hence only serialize them there */
t = manager_timestamp_to_string(q);
{
- char field[strlen(t) + strlen("-timestamp") + 1];
+ char field[strlen(t) + STRLEN("-timestamp") + 1];
strcpy(stpcpy(field, t), "-timestamp");
dual_timestamp_serialize(f, field, m->timestamps + q);
}
manager_serialize_uid_refs(m, f);
manager_serialize_gid_refs(m, f);
- fputc_unlocked('\n', f);
+ (void) fputc('\n', f);
HASHMAP_FOREACH_KEY(u, t, m->units, i) {
if (u->id != t)
continue;
/* Start marker */
- fputs_unlocked(u->id, f);
- fputc_unlocked('\n', f);
+ fputs(u->id, f);
+ fputc('\n', f);
r = unit_serialize(u, f, fds, !switching_root);
if (r < 0) {
else
m->ready_sent = m->ready_sent || b;
+ } else if ((val = startswith(l, "taint-logged="))) {
+ int b;
+
+ b = parse_boolean(val);
+ if (b < 0)
+ log_notice("Failed to parse taint-logged flag %s", val);
+ else
+ m->taint_logged = m->taint_logged || b;
+
+ } else if ((val = startswith(l, "service-watchdogs="))) {
+ int b;
+
+ b = parse_boolean(val);
+ if (b < 0)
+ log_notice("Failed to parse service-watchdogs flag %s", val);
+ else
+ m->service_watchdogs = b;
+
} else if (startswith(l, "env=")) {
r = deserialize_environment(&m->environment, l);
if (r == -ENOMEM)
manager_vacuum_uid_refs(m);
manager_vacuum_gid_refs(m);
+ /* It might be safe to log to the journal now. */
+ manager_recheck_journal(m);
+
/* Sync current state of bus names with our set of listening units */
if (m->api_bus)
manager_sync_bus_names(m, m->api_bus);
return unit_inactive_or_pending(u);
}
+static void log_taint_string(Manager *m) {
+ _cleanup_free_ char *taint = NULL;
+
+ assert(m);
+
+ if (MANAGER_IS_USER(m) || m->taint_logged)
+ return;
+
+ m->taint_logged = true; /* only check for taint once */
+
+ taint = manager_taint_string(m);
+ if (isempty(taint))
+ return;
+
+ log_struct(LOG_NOTICE,
+ LOG_MESSAGE("System is tainted: %s", taint),
+ "TAINT=%s", taint,
+ "MESSAGE_ID=" SD_MESSAGE_TAINTED_STR,
+ NULL);
+}
+
static void manager_notify_finished(Manager *m) {
char userspace[FORMAT_TIMESPAN_MAX], initrd[FORMAT_TIMESPAN_MAX], kernel[FORMAT_TIMESPAN_MAX], sum[FORMAT_TIMESPAN_MAX];
usec_t firmware_usec, loader_usec, kernel_usec, initrd_usec, userspace_usec, total_usec;
NULL);
}
} else {
- /* The container case */
+ /* The container and --user case */
firmware_usec = loader_usec = initrd_usec = kernel_usec = 0;
total_usec = userspace_usec = m->timestamps[MANAGER_TIMESTAMP_FINISH].monotonic - m->timestamps[MANAGER_TIMESTAMP_USERSPACE].monotonic;
"STATUS=Startup finished in %s.",
format_timespan(sum, sizeof(sum), total_usec, USEC_PER_MSEC));
m->ready_sent = true;
+
+ log_taint_string(m);
}
-void manager_check_finished(Manager *m) {
+static void manager_send_ready(Manager *m) {
assert(m);
- if (MANAGER_IS_RELOADING(m))
+ /* We send READY=1 on reaching basic.target only when running in --user mode. */
+ if (!MANAGER_IS_USER(m) || m->ready_sent)
+ return;
+
+ m->ready_sent = true;
+
+ sd_notifyf(false,
+ "READY=1\n"
+ "STATUS=Reached " SPECIAL_BASIC_TARGET ".");
+}
+
+static void manager_check_basic_target(Manager *m) {
+ Unit *u;
+
+ assert(m);
+
+ /* Small shortcut */
+ if (m->ready_sent && m->taint_logged)
return;
- /* Verify that we are actually running currently. Initially
- * the exit code is set to invalid, and during operation it is
- * then set to MANAGER_OK */
- if (m->exit_code != MANAGER_OK)
+ u = manager_get_unit(m, SPECIAL_BASIC_TARGET);
+ if (!u || !UNIT_IS_ACTIVE_OR_RELOADING(unit_active_state(u)))
return;
/* For user managers, send out READY=1 as soon as we reach basic.target */
- if (MANAGER_IS_USER(m) && !m->ready_sent) {
- Unit *u;
+ manager_send_ready(m);
- u = manager_get_unit(m, SPECIAL_BASIC_TARGET);
- if (u && !u->job) {
- sd_notifyf(false,
- "READY=1\n"
- "STATUS=Reached " SPECIAL_BASIC_TARGET ".");
- m->ready_sent = true;
- }
- }
+ /* Log the taint string as soon as we reach basic.target */
+ log_taint_string(m);
+}
+
+void manager_check_finished(Manager *m) {
+ assert(m);
+
+ if (MANAGER_IS_RELOADING(m))
+ return;
+
+ /* Verify that we have entered the event loop already, and not left it again. */
+ if (!MANAGER_IS_RUNNING(m))
+ return;
+
+ manager_check_basic_target(m);
if (hashmap_size(m->jobs) > 0) {
if (m->jobs_in_progress_event_source)
strv_free(b);
m->environment = l;
- manager_clean_environment(m);
- strv_sort(m->environment);
+ manager_sanitize_environment(m);
return 0;
}
return 0;
}
-void manager_recheck_journal(Manager *m) {
+static bool manager_journal_is_running(Manager *m) {
Unit *u;
assert(m);
+ /* If we are the user manager we can safely assume that the journal is up */
if (!MANAGER_IS_SYSTEM(m))
- return;
+ return true;
+ /* Check that the socket is not only up, but in RUNNING state */
u = manager_get_unit(m, SPECIAL_JOURNALD_SOCKET);
- if (u && SOCKET(u)->state != SOCKET_RUNNING) {
- log_close_journal();
- return;
- }
+ if (!u)
+ return false;
+ if (SOCKET(u)->state != SOCKET_RUNNING)
+ return false;
+ /* Similar, check if the daemon itself is fully up, too */
u = manager_get_unit(m, SPECIAL_JOURNALD_SERVICE);
- if (u && SERVICE(u)->state != SERVICE_RUNNING) {
- log_close_journal();
+ if (!u)
+ return false;
+ if (SERVICE(u)->state != SERVICE_RUNNING)
+ return false;
+
+ return true;
+}
+
+void manager_recheck_journal(Manager *m) {
+
+ assert(m);
+
+ /* Don't bother with this unless we are in the special situation of being PID 1 */
+ if (getpid_cached() != 1)
return;
- }
- /* Hmm, OK, so the socket is fully up and the service is up
- * too, then let's make use of the thing. */
- log_open();
+ if (manager_journal_is_running(m)) {
+
+ /* The journal is fully and entirely up? If so, let's permit logging to it, if that's configured. */
+ log_set_prohibit_ipc(false);
+ log_open();
+ } else {
+
+ /* If the journal is down, don't ever log to it, otherwise we might end up deadlocking ourselves as we
+ * might trigger an activation ourselves we can't fulfill */
+ log_set_prohibit_ipc(true);
+ log_close_journal();
+ }
}
void manager_set_show_status(Manager *m, ShowStatus mode) {
return 0;
}
+char *manager_taint_string(Manager *m) {
+ _cleanup_free_ char *destination = NULL, *overflowuid = NULL, *overflowgid = NULL;
+ char *buf, *e;
+ int r;
+
+ /* Returns a "taint string", e.g. "local-hwclock:var-run-bad".
+ * Only things that are detected at runtime should be tagged
+ * here. For stuff that is set during compilation, emit a warning
+ * in the configuration phase. */
+
+ assert(m);
+
+ buf = new(char, sizeof("split-usr:"
+ "cgroups-missing:"
+ "local-hwclock:"
+ "var-run-bad:"
+ "overflowuid-not-65534:"
+ "overflowgid-not-65534:"));
+ if (!buf)
+ return NULL;
+
+ e = buf;
+ buf[0] = 0;
+
+ if (m->taint_usr)
+ e = stpcpy(e, "split-usr:");
+
+ if (access("/proc/cgroups", F_OK) < 0)
+ e = stpcpy(e, "cgroups-missing:");
+
+ if (clock_is_localtime(NULL) > 0)
+ e = stpcpy(e, "local-hwclock:");
+
+ r = readlink_malloc("/var/run", &destination);
+ if (r < 0 || !PATH_IN_SET(destination, "../run", "/run"))
+ e = stpcpy(e, "var-run-bad:");
+
+ r = read_one_line_file("/proc/sys/kernel/overflowuid", &overflowuid);
+ if (r >= 0 && !streq(overflowuid, "65534"))
+ e = stpcpy(e, "overflowuid-not-65534:");
+
+ r = read_one_line_file("/proc/sys/kernel/overflowgid", &overflowgid);
+ if (r >= 0 && !streq(overflowgid, "65534"))
+ e = stpcpy(e, "overflowgid-not-65534:");
+
+ /* remove the last ':' */
+ if (e != buf)
+ e[-1] = 0;
+
+ return buf;
+}
+
static const char *const manager_state_table[_MANAGER_STATE_MAX] = {
[MANAGER_INITIALIZING] = "initializing",
[MANAGER_STARTING] = "starting",