]> git.ipfire.org Git - thirdparty/systemd.git/blobdiff - src/core/manager.c
pid1: make use of new "prohibit_ipc" logging flag in PID 1
[thirdparty/systemd.git] / src / core / manager.c
index be44ab3778daac3c6549c41e4dabfb97c3ed5d8b..2952f217c3200099f672dfb55603f61d04809fd5 100644 (file)
@@ -48,6 +48,7 @@
 #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"
@@ -108,6 +109,7 @@ static int manager_dispatch_idle_pipe_fd(sd_event_source *source, int fd, uint32
 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);
 
@@ -139,7 +141,7 @@ static void manager_watch_jobs_in_progress(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;
@@ -262,7 +264,7 @@ static int manager_dispatch_ask_password_fd(sd_event_source *source,
 
         assert(m);
 
-        flush_fd(fd);
+        (void) flush_fd(fd);
 
         m->have_ask_password = have_ask_password();
         if (m->have_ask_password < 0)
@@ -512,23 +514,31 @@ static int manager_setup_signals(Manager *m) {
         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) {
@@ -555,8 +565,7 @@ 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;
 }
@@ -603,6 +612,52 @@ static int manager_setup_prefix(Manager *m) {
         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;
@@ -687,20 +742,10 @@ int manager_new(UnitFileScope scope, unsigned test_run_flags, Manager **_m) {
         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;
@@ -713,27 +758,33 @@ int manager_new(UnitFileScope scope, unsigned test_run_flags, Manager **_m) {
         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;
 
@@ -794,7 +845,7 @@ static int manager_setup_notify(Manager *m) {
 
                 /* 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");
 
@@ -923,7 +974,7 @@ static int manager_setup_user_lookup_fd(Manager *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");
 
@@ -1154,14 +1205,14 @@ Manager* manager_free(Manager *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);
@@ -1738,7 +1789,7 @@ int manager_get_dump_string(Manager *m, char **ret) {
         if (!f)
                 return -errno;
 
-        __fsetlocking(f, FSETLOCKING_BYCALLER);
+        (void) __fsetlocking(f, FSETLOCKING_BYCALLER);
 
         manager_dump(m, f, NULL);
 
@@ -1856,27 +1907,40 @@ static int manager_dispatch_cgroups_agent_fd(sd_event_source *source, int fd, ui
         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));
         }
 }
@@ -1904,9 +1968,11 @@ static int manager_dispatch_notify_fd(sd_event_source *source, int fd, uint32_t
 
         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);
@@ -1953,7 +2019,7 @@ static int manager_dispatch_notify_fd(sd_event_source *source, int fd, uint32_t
                 }
         }
 
-        if (!ucred || ucred->pid <= 0) {
+        if (!ucred || !pid_is_valid(ucred->pid)) {
                 log_warning("Received notify message without valid credentials. Ignoring.");
                 return 0;
         }
@@ -1973,22 +2039,41 @@ static int manager_dispatch_notify_fd(sd_event_source *source, int fd, uint32_t
         /* 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.");
@@ -1996,89 +2081,111 @@ static int manager_dispatch_notify_fd(sd_event_source *source, int fd, uint32_t
         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;
 }
@@ -2110,7 +2217,6 @@ static int manager_dispatch_signal_fd(sd_event_source *source, int fd, uint32_t
         Manager *m = userdata;
         ssize_t n;
         struct signalfd_siginfo sfsi;
-        bool sigchld = false;
         int r;
 
         assert(m);
@@ -2121,195 +2227,192 @@ static int manager_dispatch_signal_fd(sd_event_source *source, int fd, uint32_t
                 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;
 }
@@ -2384,11 +2487,10 @@ int manager_loop(Manager *m) {
 
         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;
@@ -2627,6 +2729,8 @@ int manager_serialize(Manager *m, FILE *f, FDSet *fds, bool switching_root) {
         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 */
@@ -2635,7 +2739,7 @@ int manager_serialize(Manager *m, FILE *f, FDSet *fds, bool switching_root) {
 
                 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);
                 }
@@ -2688,15 +2792,15 @@ int manager_serialize(Manager *m, FILE *f, FDSet *fds, bool switching_root) {
         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) {
@@ -2789,6 +2893,24 @@ int manager_deserialize(Manager *m, FILE *f, FDSet *fds) {
                         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)
@@ -3010,6 +3132,9 @@ int manager_reload(Manager *m) {
         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);
@@ -3046,6 +3171,27 @@ bool manager_unit_inactive_or_pending(Manager *m, const char *name) {
         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;
@@ -3098,7 +3244,7 @@ static void manager_notify_finished(Manager *m) {
                                    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;
 
@@ -3118,32 +3264,55 @@ static void manager_notify_finished(Manager *m) {
                                    "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)
@@ -3292,8 +3461,7 @@ int manager_environment_add(Manager *m, char **minus, char **plus) {
                 strv_free(b);
 
         m->environment = l;
-        manager_clean_environment(m);
-        strv_sort(m->environment);
+        manager_sanitize_environment(m);
 
         return 0;
 }
@@ -3317,29 +3485,52 @@ int manager_set_default_rlimits(Manager *m, struct rlimit **default_rlimit) {
         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) {
@@ -3851,6 +4042,58 @@ int manager_dispatch_user_lookup_fd(sd_event_source *source, int fd, uint32_t re
         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",