chiark / gitweb /
manager: introduce watch_init() initializer for watches
[elogind.git] / src / core / manager.c
index a6013668b8a8221997cf92cda3f3430e506f4758..72ce2f25956fef35c1b1a482142d3c5355f1bc72 100644 (file)
@@ -41,7 +41,9 @@
 #include <libaudit.h>
 #endif
 
-#include <systemd/sd-daemon.h>
+#include "systemd/sd-daemon.h"
+#include "systemd/sd-id128.h"
+#include "systemd/sd-messages.h"
 
 #include "manager.h"
 #include "transaction.h"
@@ -65,6 +67,8 @@
 #include "virt.h"
 #include "watchdog.h"
 #include "cgroup-util.h"
+#include "path-util.h"
+#include "audit-fd.h"
 
 /* As soon as 16 units are in our GC queue, make sure to run a gc sweep */
 #define GC_QUEUE_ENTRIES_MAX 16
@@ -73,8 +77,7 @@
 #define GC_QUEUE_USEC_MAX (10*USEC_PER_SEC)
 
 /* Where clients shall send notification messages to */
-#define NOTIFY_SOCKET_SYSTEM "/run/systemd/notify"
-#define NOTIFY_SOCKET_USER "@/org/freedesktop/systemd1/notify"
+#define NOTIFY_SOCKET "@/org/freedesktop/systemd1/notify"
 
 static int manager_setup_notify(Manager *m) {
         union {
@@ -82,13 +85,13 @@ static int manager_setup_notify(Manager *m) {
                 struct sockaddr_un un;
         } sa;
         struct epoll_event ev;
-        int one = 1, r;
-        mode_t u;
+        int one = 1;
 
         assert(m);
 
         m->notify_watch.type = WATCH_NOTIFY;
-        if ((m->notify_watch.fd = socket(AF_UNIX, SOCK_DGRAM|SOCK_CLOEXEC|SOCK_NONBLOCK, 0)) < 0) {
+        m->notify_watch.fd = socket(AF_UNIX, SOCK_DGRAM|SOCK_CLOEXEC|SOCK_NONBLOCK, 0);
+        if (m->notify_watch.fd < 0) {
                 log_error("Failed to allocate notification socket: %m");
                 return -errno;
         }
@@ -96,21 +99,14 @@ static int manager_setup_notify(Manager *m) {
         zero(sa);
         sa.sa.sa_family = AF_UNIX;
 
-        if (getpid() != 1)
-                snprintf(sa.un.sun_path, sizeof(sa.un.sun_path), NOTIFY_SOCKET_USER "/%llu", random_ull());
-        else {
-                unlink(NOTIFY_SOCKET_SYSTEM);
-                strncpy(sa.un.sun_path, NOTIFY_SOCKET_SYSTEM, sizeof(sa.un.sun_path));
-        }
-
-        if (sa.un.sun_path[0] == '@')
-                sa.un.sun_path[0] = 0;
+        if (getpid() != 1 || detect_container(NULL) > 0)
+                snprintf(sa.un.sun_path, sizeof(sa.un.sun_path), NOTIFY_SOCKET "/%llu", random_ull());
+        else
+                strncpy(sa.un.sun_path, NOTIFY_SOCKET, sizeof(sa.un.sun_path));
 
-        u = umask(0111);
-        r = bind(m->notify_watch.fd, &sa.sa, offsetof(struct sockaddr_un, sun_path) + 1 + strlen(sa.un.sun_path+1));
-        umask(u);
+        sa.un.sun_path[0] = 0;
 
-        if (r < 0) {
+        if (bind(m->notify_watch.fd, &sa.sa, offsetof(struct sockaddr_un, sun_path) + 1 + strlen(sa.un.sun_path+1)) < 0) {
                 log_error("bind() failed: %m");
                 return -errno;
         }
@@ -127,10 +123,9 @@ static int manager_setup_notify(Manager *m) {
         if (epoll_ctl(m->epoll_fd, EPOLL_CTL_ADD, m->notify_watch.fd, &ev) < 0)
                 return -errno;
 
-        if (sa.un.sun_path[0] == 0)
-                sa.un.sun_path[0] = '@';
-
-        if (!(m->notify_socket = strdup(sa.un.sun_path)))
+        sa.un.sun_path[0] = '@';
+        m->notify_socket = strdup(sa.un.sun_path);
+        if (!m->notify_socket)
                 return -ENOMEM;
 
         log_debug("Using notification socket %s", m->notify_socket);
@@ -144,8 +139,9 @@ static int enable_special_signals(Manager *m) {
         assert(m);
 
         /* Enable that we get SIGINT on control-alt-del. In containers
-         * this will fail with EPERM, so ignore that. */
-        if (reboot(RB_DISABLE_CAD) < 0 && errno != EPERM)
+         * this will fail with EPERM (older) or EINVAL (newer), so
+         * ignore that. */
+        if (reboot(RB_DISABLE_CAD) < 0 && errno != EPERM && errno != EINVAL)
                 log_warning("Failed to enable ctrl-alt-del handling: %m");
 
         fd = open_terminal("/dev/tty0", O_RDWR|O_NOCTTY|O_CLOEXEC);
@@ -203,6 +199,7 @@ static int manager_setup_signals(Manager *m) {
                         SIGRTMIN+21, /* systemd: disable status messages */
                         SIGRTMIN+22, /* systemd: set log level to LOG_DEBUG */
                         SIGRTMIN+23, /* systemd: set log level to LOG_INFO */
+                        SIGRTMIN+24, /* systemd: Immediate exit (--user only) */
                         SIGRTMIN+26, /* systemd: set log target to journal-or-kmsg */
                         SIGRTMIN+27, /* systemd: set log target to console */
                         SIGRTMIN+28, /* systemd: set log target to kmsg */
@@ -221,7 +218,7 @@ static int manager_setup_signals(Manager *m) {
         if (epoll_ctl(m->epoll_fd, EPOLL_CTL_ADD, m->signal_watch.fd, &ev) < 0)
                 return -errno;
 
-        if (m->running_as == MANAGER_SYSTEM)
+        if (m->running_as == SYSTEMD_SYSTEM)
                 return enable_special_signals(m);
 
         return 0;
@@ -242,18 +239,19 @@ static void manager_strip_environment(Manager *m) {
         strv_remove_prefix(m->environment, "RD_");
 }
 
-int manager_new(ManagerRunningAs running_as, Manager **_m) {
+int manager_new(SystemdRunningAs running_as, Manager **_m) {
         Manager *m;
         int r = -ENOMEM;
 
         assert(_m);
         assert(running_as >= 0);
-        assert(running_as < _MANAGER_RUNNING_AS_MAX);
+        assert(running_as < _SYSTEMD_RUNNING_AS_MAX);
 
-        if (!(m = new0(Manager, 1)))
+        m = new0(Manager, 1);
+        if (!m)
                 return -ENOMEM;
 
-        dual_timestamp_get(&m->startup_timestamp);
+        dual_timestamp_get(&m->userspace_timestamp);
 
         m->running_as = running_as;
         m->name_data_slot = m->conn_data_slot = m->subscribed_data_slot = -1;
@@ -261,10 +259,6 @@ int manager_new(ManagerRunningAs running_as, Manager **_m) {
         m->pin_cgroupfs_fd = -1;
         m->idle_pipe[0] = m->idle_pipe[1] = -1;
 
-#ifdef HAVE_AUDIT
-        m->audit_fd = -1;
-#endif
-
         m->signal_watch.fd = m->mount_watch.fd = m->udev_watch.fd = m->epoll_fd = m->dev_autofs_fd = m->swap_watch.fd = -1;
         m->current_job_id = 1; /* start as id #1, so that we can leave #0 around as "null-like" value */
 
@@ -274,7 +268,7 @@ int manager_new(ManagerRunningAs running_as, Manager **_m) {
 
         manager_strip_environment(m);
 
-        if (running_as == MANAGER_SYSTEM) {
+        if (running_as == SYSTEMD_SYSTEM) {
                 m->default_controllers = strv_new("cpu", NULL);
                 if (!m->default_controllers)
                         goto fail;
@@ -298,9 +292,6 @@ int manager_new(ManagerRunningAs running_as, Manager **_m) {
         if ((m->epoll_fd = epoll_create1(EPOLL_CLOEXEC)) < 0)
                 goto fail;
 
-        if ((r = lookup_paths_init(&m->lookup_paths, m->running_as, true)) < 0)
-                goto fail;
-
         if ((r = manager_setup_signals(m)) < 0)
                 goto fail;
 
@@ -311,17 +302,9 @@ int manager_new(ManagerRunningAs running_as, Manager **_m) {
                 goto fail;
 
         /* Try to connect to the busses, if possible. */
-        if ((r = bus_init(m, running_as != MANAGER_SYSTEM)) < 0)
+        if ((r = bus_init(m, running_as != SYSTEMD_SYSTEM)) < 0)
                 goto fail;
 
-#ifdef HAVE_AUDIT
-        if ((m->audit_fd = audit_open()) < 0 &&
-            /* If the kernel lacks netlink or audit support,
-             * don't worry about it. */
-            errno != EAFNOSUPPORT && errno != EPROTONOSUPPORT)
-                log_error("Failed to connect to audit log: %m");
-#endif
-
         m->taint_usr = dir_is_empty("/usr") > 0;
 
         *_m = m;
@@ -475,6 +458,7 @@ static void manager_clear_jobs_and_units(Manager *m) {
 
 void manager_free(Manager *m) {
         UnitType c;
+        int i;
 
         assert(m);
 
@@ -504,11 +488,6 @@ void manager_free(Manager *m) {
         if (m->notify_watch.fd >= 0)
                 close_nointr_nofail(m->notify_watch.fd);
 
-#ifdef HAVE_AUDIT
-        if (m->audit_fd >= 0)
-                audit_close(m->audit_fd);
-#endif
-
         free(m->notify_socket);
 
         lookup_paths_free(&m->lookup_paths);
@@ -521,6 +500,12 @@ void manager_free(Manager *m) {
 
         close_pipe(m->idle_pipe);
 
+        free(m->switch_root);
+        free(m->switch_root_init);
+
+        for (i = 0; i < RLIMIT_NLIMITS; i++)
+                free(m->rlimit[i]);
+
         free(m);
 }
 
@@ -594,7 +579,7 @@ static void manager_build_unit_path_cache(Manager *m) {
                         if (ignore_file(de->d_name))
                                 continue;
 
-                        p = join(streq(*i, "/") ? "" : *i, "/", de->d_name, NULL);
+                        p = strjoin(streq(*i, "/") ? "" : *i, "/", de->d_name, NULL);
                         if (!p) {
                                 r = -ENOMEM;
                                 goto fail;
@@ -629,6 +614,14 @@ int manager_startup(Manager *m, FILE *serialization, FDSet *fds) {
 
         manager_run_generators(m);
 
+        r = lookup_paths_init(
+                        &m->lookup_paths, m->running_as, true,
+                        m->generator_unit_path,
+                        m->generator_unit_path_early,
+                        m->generator_unit_path_late);
+        if (r < 0)
+                return r;
+
         manager_build_unit_path_cache(m);
 
         /* If we will deserialize make sure that during enumeration
@@ -641,12 +634,15 @@ int manager_startup(Manager *m, FILE *serialization, FDSet *fds) {
         r = manager_enumerate(m);
 
         /* Second, deserialize if there is something to deserialize */
-        if (serialization)
-                if ((q = manager_deserialize(m, serialization, fds)) < 0)
+        if (serialization) {
+                q = manager_deserialize(m, serialization, fds);
+                if (q < 0)
                         r = q;
+        }
 
         /* Third, fire things up! */
-        if ((q = manager_coldplug(m)) < 0)
+        q = manager_coldplug(m);
+        if (q < 0)
                 r = q;
 
         if (serialization) {
@@ -678,6 +674,8 @@ int manager_add_job(Manager *m, JobType type, Unit *unit, JobMode mode, bool ove
 
         log_debug("Trying to enqueue job %s/%s/%s", unit->id, job_type_to_string(type), job_mode_to_string(mode));
 
+        job_type_collapse(&type, unit);
+
         tr = transaction_new();
         if (!tr)
                 return -ENOMEM;
@@ -721,7 +719,8 @@ int manager_add_job_by_name(Manager *m, JobType type, const char *name, JobMode
         assert(name);
         assert(mode < _JOB_MODE_MAX);
 
-        if ((r = manager_load_unit(m, name, NULL, NULL, &unit)) < 0)
+        r = manager_load_unit(m, name, NULL, NULL, &unit);
+        if (r < 0)
                 return r;
 
         return manager_add_job(m, type, unit, mode, override, e, _ret);
@@ -783,11 +782,11 @@ int manager_load_unit_prepare(Manager *m, const char *name, const char *path, DB
         }
 
         if (!name)
-                name = file_name_from_path(path);
+                name = path_get_file_name(path);
 
         t = unit_name_to_type(name);
 
-        if (t == _UNIT_TYPE_INVALID || !unit_name_is_valid_no_type(name, false)) {
+        if (t == _UNIT_TYPE_INVALID || !unit_name_is_valid(name, false)) {
                 dbus_set_error(e, BUS_ERROR_INVALID_NAME, "Unit name %s is not valid.", name);
                 return -EINVAL;
         }
@@ -833,7 +832,8 @@ int manager_load_unit(Manager *m, const char *name, const char *path, DBusError
         /* This will load the service information files, but not actually
          * start any services or anything. */
 
-        if ((r = manager_load_unit_prepare(m, name, path, e, _ret)) != 0)
+        r = manager_load_unit_prepare(m, name, path, e, _ret);
+        if (r != 0)
                 return r;
 
         manager_dispatch_load_queue(m);
@@ -1130,7 +1130,7 @@ static int manager_process_signal_fd(Manager *m) {
                         break;
 
                 case SIGTERM:
-                        if (m->running_as == MANAGER_SYSTEM) {
+                        if (m->running_as == SYSTEMD_SYSTEM) {
                                 /* This is for compatibility with the
                                  * original sysvinit */
                                 m->exit_code = MANAGER_REEXECUTE;
@@ -1140,7 +1140,7 @@ static int manager_process_signal_fd(Manager *m) {
                         /* Fall through */
 
                 case SIGINT:
-                        if (m->running_as == MANAGER_SYSTEM) {
+                        if (m->running_as == SYSTEMD_SYSTEM) {
                                 manager_start_target(m, SPECIAL_CTRL_ALT_DEL_TARGET, JOB_REPLACE);
                                 break;
                         }
@@ -1154,14 +1154,14 @@ static int manager_process_signal_fd(Manager *m) {
                         break;
 
                 case SIGWINCH:
-                        if (m->running_as == MANAGER_SYSTEM)
+                        if (m->running_as == SYSTEMD_SYSTEM)
                                 manager_start_target(m, SPECIAL_KBREQUEST_TARGET, JOB_REPLACE);
 
                         /* This is a nop on non-init */
                         break;
 
                 case SIGPWR:
-                        if (m->running_as == MANAGER_SYSTEM)
+                        if (m->running_as == SYSTEMD_SYSTEM)
                                 manager_start_target(m, SPECIAL_SIGPWR_TARGET, JOB_REPLACE);
 
                         /* This is a nop on non-init */
@@ -1273,6 +1273,15 @@ static int manager_process_signal_fd(Manager *m) {
                                 log_notice("Setting log level to info.");
                                 break;
 
+                        case 24:
+                                if (m->running_as == SYSTEMD_USER) {
+                                        m->exit_code = MANAGER_EXIT;
+                                        return 0;
+                                }
+
+                                /* This is a nop on init */
+                                break;
+
                         case 26:
                                 log_set_target(LOG_TARGET_JOURNAL_OR_KMSG);
                                 log_notice("Setting log target to journal-or-kmsg.");
@@ -1354,7 +1363,8 @@ static int process_event(Manager *m, struct epoll_event *ev) {
                 ssize_t k;
 
                 /* Some timer event, to be dispatched to the units */
-                if ((k = read(w->fd, &v, sizeof(v))) != sizeof(v)) {
+                k = read(w->fd, &v, sizeof(v));
+                if (k != sizeof(v)) {
 
                         if (k < 0 && (errno == EINTR || errno == EAGAIN))
                                 break;
@@ -1425,7 +1435,7 @@ int manager_loop(Manager *m) {
                 int n;
                 int wait_msec = -1;
 
-                if (m->runtime_watchdog > 0 && m->running_as == MANAGER_SYSTEM)
+                if (m->runtime_watchdog > 0 && m->running_as == SYSTEMD_SYSTEM)
                         watchdog_ping();
 
                 if (!ratelimit_test(&rl)) {
@@ -1457,7 +1467,7 @@ int manager_loop(Manager *m) {
                         continue;
 
                 /* Sleep for half the watchdog time */
-                if (m->runtime_watchdog > 0 && m->running_as == MANAGER_SYSTEM) {
+                if (m->runtime_watchdog > 0 && m->running_as == SYSTEMD_SYSTEM) {
                         wait_msec = (int) (m->runtime_watchdog / 2 / USEC_PER_MSEC);
                         if (wait_msec <= 0)
                                 wait_msec = 1;
@@ -1484,9 +1494,10 @@ int manager_loop(Manager *m) {
         return m->exit_code;
 }
 
-int manager_get_unit_from_dbus_path(Manager *m, const char *s, Unit **_u) {
+int manager_load_unit_from_dbus_path(Manager *m, const char *s, DBusError *e, Unit **_u) {
         char *n;
         Unit *u;
+        int r;
 
         assert(m);
         assert(s);
@@ -1495,14 +1506,15 @@ int manager_get_unit_from_dbus_path(Manager *m, const char *s, Unit **_u) {
         if (!startswith(s, "/org/freedesktop/systemd1/unit/"))
                 return -EINVAL;
 
-        if (!(n = bus_path_unescape(s+31)))
+        n = bus_path_unescape(s+31);
+        if (!n)
                 return -ENOMEM;
 
-        u = manager_get_unit(m, n);
+        r = manager_load_unit(m, n, NULL, e, &u);
         free(n);
 
-        if (!u)
-                return -ENOENT;
+        if (r < 0)
+                return r;
 
         *_u = u;
 
@@ -1536,8 +1548,10 @@ void manager_send_unit_audit(Manager *m, Unit *u, int type, bool success) {
 
 #ifdef HAVE_AUDIT
         char *p;
+        int audit_fd;
 
-        if (m->audit_fd < 0)
+        audit_fd = get_audit_fd();
+        if (audit_fd < 0)
                 return;
 
         /* Don't generate audit events if the service was already
@@ -1545,7 +1559,7 @@ void manager_send_unit_audit(Manager *m, Unit *u, int type, bool success) {
         if (m->n_reloading > 0)
                 return;
 
-        if (m->running_as != MANAGER_SYSTEM)
+        if (m->running_as != SYSTEMD_SYSTEM)
                 return;
 
         if (u->type != UNIT_SERVICE)
@@ -1556,12 +1570,11 @@ void manager_send_unit_audit(Manager *m, Unit *u, int type, bool success) {
                 return;
         }
 
-        if (audit_log_user_comm_message(m->audit_fd, type, "", p, NULL, NULL, NULL, success) < 0) {
+        if (audit_log_user_comm_message(audit_fd, type, "", p, NULL, NULL, NULL, success) < 0) {
                 if (errno == EPERM) {
                         /* We aren't allowed to send audit messages?
                          * Then let's not retry again. */
-                        audit_close(m->audit_fd);
-                        m->audit_fd = -1;
+                        close_audit_fd();
                 } else
                         log_warning("Failed to send audit message: %m");
         }
@@ -1582,7 +1595,7 @@ void manager_send_unit_plymouth(Manager *m, Unit *u) {
         if (m->n_reloading > 0)
                 return;
 
-        if (m->running_as != MANAGER_SYSTEM)
+        if (m->running_as != SYSTEMD_SYSTEM)
                 return;
 
         if (u->type != UNIT_SERVICE &&
@@ -1614,7 +1627,7 @@ void manager_send_unit_plymouth(Manager *m, Unit *u) {
         }
 
         if (asprintf(&message, "U\002%c%s%n", (int) (strlen(u->id) + 1), u->id, &n) < 0) {
-                log_error("Out of memory");
+                log_oom();
                 goto finish;
         }
 
@@ -1681,7 +1694,7 @@ int manager_open_serialization(Manager *m, FILE **_f) {
 
         assert(_f);
 
-        if (m->running_as == MANAGER_SYSTEM)
+        if (m->running_as == SYSTEMD_SYSTEM)
                 asprintf(&path, "/run/systemd/dump-%lu-XXXXXX", (unsigned long) getpid());
         else
                 asprintf(&path, "/tmp/systemd-dump-%lu-XXXXXX", (unsigned long) getpid());
@@ -1711,7 +1724,7 @@ int manager_open_serialization(Manager *m, FILE **_f) {
         return 0;
 }
 
-int manager_serialize(Manager *m, FILE *f, FDSet *fds) {
+int manager_serialize(Manager *m, FILE *f, FDSet *fds, bool serialize_jobs) {
         Iterator i;
         Unit *u;
         const char *t;
@@ -1725,10 +1738,18 @@ int manager_serialize(Manager *m, FILE *f, FDSet *fds) {
 
         fprintf(f, "current-job-id=%i\n", m->current_job_id);
         fprintf(f, "taint-usr=%s\n", yes_no(m->taint_usr));
+        fprintf(f, "n-installed-jobs=%u\n", m->n_installed_jobs);
+        fprintf(f, "n-failed-jobs=%u\n", m->n_failed_jobs);
 
+        dual_timestamp_serialize(f, "firmware-timestamp", &m->firmware_timestamp);
+        dual_timestamp_serialize(f, "kernel-timestamp", &m->kernel_timestamp);
+        dual_timestamp_serialize(f, "loader-timestamp", &m->loader_timestamp);
         dual_timestamp_serialize(f, "initrd-timestamp", &m->initrd_timestamp);
-        dual_timestamp_serialize(f, "startup-timestamp", &m->startup_timestamp);
-        dual_timestamp_serialize(f, "finish-timestamp", &m->finish_timestamp);
+
+        if (!in_initrd()) {
+                dual_timestamp_serialize(f, "userspace-timestamp", &m->userspace_timestamp);
+                dual_timestamp_serialize(f, "finish-timestamp", &m->finish_timestamp);
+        }
 
         fputc('\n', f);
 
@@ -1743,7 +1764,7 @@ int manager_serialize(Manager *m, FILE *f, FDSet *fds) {
                 fputs(u->id, f);
                 fputc('\n', f);
 
-                if ((r = unit_serialize(u, f, fds)) < 0) {
+                if ((r = unit_serialize(u, f, fds, serialize_jobs)) < 0) {
                         m->n_reloading --;
                         return r;
                 }
@@ -1797,6 +1818,20 @@ int manager_deserialize(Manager *m, FILE *f, FDSet *fds) {
                                 log_debug("Failed to parse current job id value %s", l+15);
                         else
                                 m->current_job_id = MAX(m->current_job_id, id);
+                } else if (startswith(l, "n-installed-jobs=")) {
+                        uint32_t n;
+
+                        if (safe_atou32(l+17, &n) < 0)
+                                log_debug("Failed to parse installed jobs counter %s", l+17);
+                        else
+                                m->n_installed_jobs += n;
+                } else if (startswith(l, "n-failed-jobs=")) {
+                        uint32_t n;
+
+                        if (safe_atou32(l+14, &n) < 0)
+                                log_debug("Failed to parse failed jobs counter %s", l+14);
+                        else
+                                m->n_failed_jobs += n;
                 } else if (startswith(l, "taint-usr=")) {
                         int b;
 
@@ -1804,10 +1839,16 @@ int manager_deserialize(Manager *m, FILE *f, FDSet *fds) {
                                 log_debug("Failed to parse taint /usr flag %s", l+10);
                         else
                                 m->taint_usr = m->taint_usr || b;
-                } else if (startswith(l, "initrd-timestamp="))
+                } else if (startswith(l, "firmware-timestamp="))
+                        dual_timestamp_deserialize(l+19, &m->firmware_timestamp);
+                else if (startswith(l, "loader-timestamp="))
+                        dual_timestamp_deserialize(l+17, &m->loader_timestamp);
+                else if (startswith(l, "kernel-timestamp="))
+                        dual_timestamp_deserialize(l+17, &m->kernel_timestamp);
+                else if (startswith(l, "initrd-timestamp="))
                         dual_timestamp_deserialize(l+17, &m->initrd_timestamp);
-                else if (startswith(l, "startup-timestamp="))
-                        dual_timestamp_deserialize(l+18, &m->startup_timestamp);
+                else if (startswith(l, "userspace-timestamp="))
+                        dual_timestamp_deserialize(l+20, &m->userspace_timestamp);
                 else if (startswith(l, "finish-timestamp="))
                         dual_timestamp_deserialize(l+17, &m->finish_timestamp);
                 else
@@ -1856,18 +1897,21 @@ int manager_reload(Manager *m) {
 
         assert(m);
 
-        if ((r = manager_open_serialization(m, &f)) < 0)
+        r = manager_open_serialization(m, &f);
+        if (r < 0)
                 return r;
 
         m->n_reloading ++;
 
-        if (!(fds = fdset_new())) {
+        fds = fdset_new();
+        if (!fds) {
                 m->n_reloading --;
                 r = -ENOMEM;
                 goto finish;
         }
 
-        if ((r = manager_serialize(m, f, fds)) < 0) {
+        r = manager_serialize(m, f, fds, true);
+        if (r < 0) {
                 m->n_reloading --;
                 goto finish;
         }
@@ -1881,29 +1925,37 @@ int manager_reload(Manager *m) {
         /* From here on there is no way back. */
         manager_clear_jobs_and_units(m);
         manager_undo_generators(m);
-
-        /* Find new unit paths */
         lookup_paths_free(&m->lookup_paths);
-        if ((q = lookup_paths_init(&m->lookup_paths, m->running_as, true)) < 0)
-                r = q;
 
+        /* Find new unit paths */
         manager_run_generators(m);
 
+        q = lookup_paths_init(
+                        &m->lookup_paths, m->running_as, true,
+                        m->generator_unit_path,
+                        m->generator_unit_path_early,
+                        m->generator_unit_path_late);
+        if (q < 0)
+                r = q;
+
         manager_build_unit_path_cache(m);
 
         /* First, enumerate what we can from all config files */
-        if ((q = manager_enumerate(m)) < 0)
+        q = manager_enumerate(m);
+        if (q < 0)
                 r = q;
 
         /* Second, deserialize our stored data */
-        if ((q = manager_deserialize(m, f, fds)) < 0)
+        q = manager_deserialize(m, f, fds);
+        if (q < 0)
                 r = q;
 
         fclose(f);
         f = NULL;
 
         /* Third, fire things up! */
-        if ((q = manager_coldplug(m)) < 0)
+        q = manager_coldplug(m);
+        if (q < 0)
                 r = q;
 
         assert(m->n_reloading > 0);
@@ -1961,7 +2013,7 @@ bool manager_unit_pending_inactive(Manager *m, const char *name) {
 
 void manager_check_finished(Manager *m) {
         char userspace[FORMAT_TIMESPAN_MAX], initrd[FORMAT_TIMESPAN_MAX], kernel[FORMAT_TIMESPAN_MAX], sum[FORMAT_TIMESPAN_MAX];
-        usec_t kernel_usec, initrd_usec, userspace_usec, total_usec;
+        usec_t firmware_usec, loader_usec, kernel_usec, initrd_usec, userspace_usec, total_usec;
 
         assert(m);
 
@@ -1971,61 +2023,144 @@ void manager_check_finished(Manager *m) {
         /* Notify Type=idle units that we are done now */
         close_pipe(m->idle_pipe);
 
+        /* Turn off confirm spawn now */
+        m->confirm_spawn = false;
+
         if (dual_timestamp_is_set(&m->finish_timestamp))
                 return;
 
         dual_timestamp_get(&m->finish_timestamp);
 
-        if (m->running_as == MANAGER_SYSTEM && detect_container(NULL) <= 0) {
+        if (m->running_as == SYSTEMD_SYSTEM && detect_container(NULL) <= 0) {
 
-                userspace_usec = m->finish_timestamp.monotonic - m->startup_timestamp.monotonic;
-                total_usec = m->finish_timestamp.monotonic;
+                /* Note that m->kernel_usec.monotonic is always at 0,
+                 * and m->firmware_usec.monotonic and
+                 * m->loader_usec.monotonic should be considered
+                 * negative values. */
 
-                if (dual_timestamp_is_set(&m->initrd_timestamp)) {
+                firmware_usec = m->firmware_timestamp.monotonic - m->loader_timestamp.monotonic;
+                loader_usec = m->loader_timestamp.monotonic - m->kernel_timestamp.monotonic;
+                userspace_usec = m->finish_timestamp.monotonic - m->userspace_timestamp.monotonic;
+                total_usec = m->firmware_timestamp.monotonic + m->finish_timestamp.monotonic;
 
-                        kernel_usec = m->initrd_timestamp.monotonic;
-                        initrd_usec = m->startup_timestamp.monotonic - m->initrd_timestamp.monotonic;
+                if (dual_timestamp_is_set(&m->initrd_timestamp)) {
 
-                        log_info("Startup finished in %s (kernel) + %s (initrd) + %s (userspace) = %s.",
-                                 format_timespan(kernel, sizeof(kernel), kernel_usec),
-                                 format_timespan(initrd, sizeof(initrd), initrd_usec),
-                                 format_timespan(userspace, sizeof(userspace), userspace_usec),
-                                 format_timespan(sum, sizeof(sum), total_usec));
+                        kernel_usec = m->initrd_timestamp.monotonic - m->kernel_timestamp.monotonic;
+                        initrd_usec = m->userspace_timestamp.monotonic - m->initrd_timestamp.monotonic;
+
+                        if (!log_on_console())
+                                log_struct(LOG_INFO,
+                                           MESSAGE_ID(SD_MESSAGE_STARTUP_FINISHED),
+                                           "KERNEL_USEC=%llu", (unsigned long long) kernel_usec,
+                                           "INITRD_USEC=%llu", (unsigned long long) initrd_usec,
+                                           "USERSPACE_USEC=%llu", (unsigned long long) userspace_usec,
+                                           "MESSAGE=Startup finished in %s (kernel) + %s (initrd) + %s (userspace) = %s.",
+                                           format_timespan(kernel, sizeof(kernel), kernel_usec),
+                                           format_timespan(initrd, sizeof(initrd), initrd_usec),
+                                           format_timespan(userspace, sizeof(userspace), userspace_usec),
+                                           format_timespan(sum, sizeof(sum), total_usec),
+                                           NULL);
                 } else {
-                        kernel_usec = m->startup_timestamp.monotonic;
+                        kernel_usec = m->userspace_timestamp.monotonic - m->kernel_timestamp.monotonic;
                         initrd_usec = 0;
 
-                        log_info("Startup finished in %s (kernel) + %s (userspace) = %s.",
-                                 format_timespan(kernel, sizeof(kernel), kernel_usec),
-                                 format_timespan(userspace, sizeof(userspace), userspace_usec),
-                                 format_timespan(sum, sizeof(sum), total_usec));
+                        if (!log_on_console())
+                                log_struct(LOG_INFO,
+                                           MESSAGE_ID(SD_MESSAGE_STARTUP_FINISHED),
+                                           "KERNEL_USEC=%llu", (unsigned long long) kernel_usec,
+                                           "USERSPACE_USEC=%llu", (unsigned long long) userspace_usec,
+                                           "MESSAGE=Startup finished in %s (kernel) + %s (userspace) = %s.",
+                                           format_timespan(kernel, sizeof(kernel), kernel_usec),
+                                           format_timespan(userspace, sizeof(userspace), userspace_usec),
+                                           format_timespan(sum, sizeof(sum), total_usec),
+                                           NULL);
                 }
         } else {
-                userspace_usec = initrd_usec = kernel_usec = 0;
-                total_usec = m->finish_timestamp.monotonic - m->startup_timestamp.monotonic;
-
-                log_debug("Startup finished in %s.",
-                          format_timespan(sum, sizeof(sum), total_usec));
+                firmware_usec = loader_usec = initrd_usec = kernel_usec = 0;
+                total_usec = userspace_usec = m->finish_timestamp.monotonic - m->userspace_timestamp.monotonic;
+
+                if (!log_on_console())
+                        log_struct(LOG_INFO,
+                                   MESSAGE_ID(SD_MESSAGE_STARTUP_FINISHED),
+                                   "USERSPACE_USEC=%llu", (unsigned long long) userspace_usec,
+                                   "MESSAGE=Startup finished in %s.",
+                                   format_timespan(sum, sizeof(sum), total_usec),
+                                   NULL);
         }
 
-        bus_broadcast_finished(m, kernel_usec, initrd_usec, userspace_usec, total_usec);
+        bus_broadcast_finished(m, firmware_usec, loader_usec, kernel_usec, initrd_usec, userspace_usec, total_usec);
 
         sd_notifyf(false,
                    "READY=1\nSTATUS=Startup finished in %s.",
                    format_timespan(sum, sizeof(sum), total_usec));
 }
 
+static int create_generator_dir(Manager *m, char **generator, const char *name) {
+        char *p;
+        int r;
+
+        assert(m);
+        assert(generator);
+        assert(name);
+
+        if (*generator)
+                return 0;
+
+        if (m->running_as == SYSTEMD_SYSTEM && getpid() == 1) {
+
+                p = strappend("/run/systemd/", name);
+                if (!p)
+                        return log_oom();
+
+                r = mkdir_p_label(p, 0755);
+                if (r < 0) {
+                        log_error("Failed to create generator directory: %s", strerror(-r));
+                        free(p);
+                        return r;
+                }
+        } else {
+                p = strjoin("/tmp/systemd-", name, ".XXXXXX", NULL);
+                if (!p)
+                        return log_oom();
+
+                if (!mkdtemp(p)) {
+                        free(p);
+                        log_error("Failed to create generator directory: %m");
+                        return -errno;
+                }
+        }
+
+        *generator = p;
+        return 0;
+}
+
+static void trim_generator_dir(Manager *m, char **generator) {
+        assert(m);
+        assert(generator);
+
+        if (!*generator)
+                return;
+
+        if (rmdir(*generator) >= 0) {
+                free(*generator);
+                *generator = NULL;
+        }
+
+        return;
+}
+
 void manager_run_generators(Manager *m) {
         DIR *d = NULL;
         const char *generator_path;
-        const char *argv[3];
+        const char *argv[5];
         mode_t u;
+        int r;
 
         assert(m);
 
-        generator_path = m->running_as == MANAGER_SYSTEM ? SYSTEM_GENERATOR_PATH : USER_GENERATOR_PATH;
-        if (!(d = opendir(generator_path))) {
-
+        generator_path = m->running_as == SYSTEMD_SYSTEM ? SYSTEM_GENERATOR_PATH : USER_GENERATOR_PATH;
+        d = opendir(generator_path);
+        if (!d) {
                 if (errno == ENOENT)
                         return;
 
@@ -2033,79 +2168,57 @@ void manager_run_generators(Manager *m) {
                 return;
         }
 
-        if (!m->generator_unit_path) {
-                const char *p;
-                char user_path[] = "/tmp/systemd-generator-XXXXXX";
-
-                if (m->running_as == MANAGER_SYSTEM && getpid() == 1) {
-                        p = "/run/systemd/generator";
-
-                        if (mkdir_p(p, 0755) < 0) {
-                                log_error("Failed to create generator directory: %m");
-                                goto finish;
-                        }
+        r = create_generator_dir(m, &m->generator_unit_path, "generator");
+        if (r < 0)
+                goto finish;
 
-                } else {
-                        if (!(p = mkdtemp(user_path))) {
-                                log_error("Failed to create generator directory: %m");
-                                goto finish;
-                        }
-                }
+        r = create_generator_dir(m, &m->generator_unit_path_early, "generator.early");
+        if (r < 0)
+                goto finish;
 
-                if (!(m->generator_unit_path = strdup(p))) {
-                        log_error("Failed to allocate generator unit path.");
-                        goto finish;
-                }
-        }
+        r = create_generator_dir(m, &m->generator_unit_path_late, "generator.late");
+        if (r < 0)
+                goto finish;
 
         argv[0] = NULL; /* Leave this empty, execute_directory() will fill something in */
         argv[1] = m->generator_unit_path;
-        argv[2] = NULL;
+        argv[2] = m->generator_unit_path_early;
+        argv[3] = m->generator_unit_path_late;
+        argv[4] = NULL;
 
         u = umask(0022);
         execute_directory(generator_path, d, (char**) argv);
         umask(u);
 
-        if (rmdir(m->generator_unit_path) >= 0) {
-                /* Uh? we were able to remove this dir? I guess that
-                 * means the directory was empty, hence let's shortcut
-                 * this */
-
-                free(m->generator_unit_path);
-                m->generator_unit_path = NULL;
-                goto finish;
-        }
-
-        if (!strv_find(m->lookup_paths.unit_path, m->generator_unit_path)) {
-                char **l;
-
-                if (!(l = strv_append(m->lookup_paths.unit_path, m->generator_unit_path))) {
-                        log_error("Failed to add generator directory to unit search path: %m");
-                        goto finish;
-                }
-
-                strv_free(m->lookup_paths.unit_path);
-                m->lookup_paths.unit_path = l;
-
-                log_debug("Added generator unit path %s to search path.", m->generator_unit_path);
-        }
+        trim_generator_dir(m, &m->generator_unit_path);
+        trim_generator_dir(m, &m->generator_unit_path_early);
+        trim_generator_dir(m, &m->generator_unit_path_late);
 
 finish:
         if (d)
                 closedir(d);
 }
 
-void manager_undo_generators(Manager *m) {
+static void remove_generator_dir(Manager *m, char **generator) {
         assert(m);
+        assert(generator);
 
-        if (!m->generator_unit_path)
+        if (!*generator)
                 return;
 
-        strv_remove(m->lookup_paths.unit_path, m->generator_unit_path);
-        rm_rf(m->generator_unit_path, false, true, false);
+        strv_remove(m->lookup_paths.unit_path, *generator);
+        rm_rf(*generator, false, true, false);
+
+        free(*generator);
+        *generator = NULL;
+}
+
+void manager_undo_generators(Manager *m) {
+        assert(m);
 
-        free(m->generator_unit_path);
-        m->generator_unit_path = NULL;
+        remove_generator_dir(m, &m->generator_unit_path);
+        remove_generator_dir(m, &m->generator_unit_path_early);
+        remove_generator_dir(m, &m->generator_unit_path_late);
 }
 
 int manager_set_default_controllers(Manager *m, char **controllers) {
@@ -2125,12 +2238,29 @@ int manager_set_default_controllers(Manager *m, char **controllers) {
         return 0;
 }
 
+int manager_set_default_rlimits(Manager *m, struct rlimit **default_rlimit) {
+        int i;
+
+        assert(m);
+
+        for (i = 0; i < RLIMIT_NLIMITS; i++) {
+                if (!default_rlimit[i])
+                        continue;
+
+                m->rlimit[i] = newdup(struct rlimit, default_rlimit[i], 1);
+                if (!m->rlimit[i])
+                        return -ENOMEM;
+        }
+
+        return 0;
+}
+
 void manager_recheck_journal(Manager *m) {
         Unit *u;
 
         assert(m);
 
-        if (m->running_as != MANAGER_SYSTEM)
+        if (m->running_as != SYSTEMD_SYSTEM)
                 return;
 
         u = manager_get_unit(m, SPECIAL_JOURNALD_SOCKET);
@@ -2153,7 +2283,7 @@ void manager_recheck_journal(Manager *m) {
 void manager_set_show_status(Manager *m, bool b) {
         assert(m);
 
-        if (m->running_as != MANAGER_SYSTEM)
+        if (m->running_as != SYSTEMD_SYSTEM)
                 return;
 
         m->show_status = b;
@@ -2167,7 +2297,7 @@ void manager_set_show_status(Manager *m, bool b) {
 bool manager_get_show_status(Manager *m) {
         assert(m);
 
-        if (m->running_as != MANAGER_SYSTEM)
+        if (m->running_as != SYSTEMD_SYSTEM)
                 return false;
 
         if (m->show_status)
@@ -2179,9 +2309,9 @@ bool manager_get_show_status(Manager *m) {
         return plymouth_running();
 }
 
-static const char* const manager_running_as_table[_MANAGER_RUNNING_AS_MAX] = {
-        [MANAGER_SYSTEM] = "system",
-        [MANAGER_USER] = "user"
-};
+void watch_init(Watch *w) {
+        assert(w);
 
-DEFINE_STRING_TABLE_LOOKUP(manager_running_as, ManagerRunningAs);
+        w->type = WATCH_INVALID;
+        w->fd = -1;
+}