+
+static int process_event(Manager *m, struct epoll_event *ev) {
+ int r;
+ Watch *w;
+
+ assert(m);
+ assert(ev);
+
+ assert(w = ev->data.ptr);
+
+ switch (w->type) {
+
+ case WATCH_SIGNAL:
+
+ /* An incoming signal? */
+ if (ev->events != EPOLLIN)
+ return -EINVAL;
+
+ if ((r = manager_process_signal_fd(m)) < 0)
+ return r;
+
+ break;
+
+ case WATCH_FD:
+
+ /* Some fd event, to be dispatched to the units */
+ UNIT_VTABLE(w->data.unit)->fd_event(w->data.unit, w->fd, ev->events, w);
+ break;
+
+ case WATCH_TIMER: {
+ uint64_t v;
+ ssize_t k;
+
+ /* Some timer event, to be dispatched to the units */
+ if ((k = read(w->fd, &v, sizeof(v))) != sizeof(v)) {
+
+ if (k < 0 && (errno == EINTR || errno == EAGAIN))
+ break;
+
+ return k < 0 ? -errno : -EIO;
+ }
+
+ UNIT_VTABLE(w->data.unit)->timer_event(w->data.unit, v, w);
+ break;
+ }
+
+ case WATCH_MOUNT:
+ /* Some mount table change, intended for the mount subsystem */
+ mount_fd_event(m, ev->events);
+ break;
+
+ case WATCH_UDEV:
+ /* Some notification from udev, intended for the device subsystem */
+ device_fd_event(m, ev->events);
+ break;
+
+ case WATCH_DBUS_WATCH:
+ bus_watch_event(m, w, ev->events);
+ break;
+
+ case WATCH_DBUS_TIMEOUT:
+ bus_timeout_event(m, w, ev->events);
+ break;
+
+ default:
+ assert_not_reached("Unknown epoll event type.");
+ }
+
+ return 0;
+}
+
+int manager_loop(Manager *m) {
+ int r;
+
+ RATELIMIT_DEFINE(rl, 1*USEC_PER_SEC, 1000);
+
+ assert(m);
+ m->exit_code = MANAGER_RUNNING;
+
+ while (m->exit_code == MANAGER_RUNNING) {
+ struct epoll_event event;
+ int n;
+
+ if (!ratelimit_test(&rl)) {
+ /* Yay, something is going seriously wrong, pause a little */
+ log_warning("Looping too fast. Throttling execution a little.");
+ sleep(1);
+ }
+
+ if (manager_dispatch_cleanup_queue(m) > 0)
+ continue;
+
+ if (manager_dispatch_gc_queue(m) > 0)
+ continue;
+
+ if (manager_dispatch_load_queue(m) > 0)
+ continue;
+
+ if (manager_dispatch_run_queue(m) > 0)
+ continue;
+
+ if (bus_dispatch(m) > 0)
+ continue;
+
+ if (manager_dispatch_dbus_queue(m) > 0)
+ continue;
+
+ if ((n = epoll_wait(m->epoll_fd, &event, 1, -1)) < 0) {
+
+ if (errno == -EINTR)
+ continue;
+
+ return -errno;
+ }
+
+ assert(n == 1);
+
+ if ((r = process_event(m, &event)) < 0)
+ return r;
+ }
+
+ return m->exit_code;
+}
+
+int manager_get_unit_from_dbus_path(Manager *m, const char *s, Unit **_u) {
+ char *n;
+ Unit *u;
+
+ assert(m);
+ assert(s);
+ assert(_u);
+
+ if (!startswith(s, "/org/freedesktop/systemd1/unit/"))
+ return -EINVAL;
+
+ if (!(n = bus_path_unescape(s+31)))
+ return -ENOMEM;
+
+ u = manager_get_unit(m, n);
+ free(n);
+
+ if (!u)
+ return -ENOENT;
+
+ *_u = u;
+
+ return 0;
+}
+
+int manager_get_job_from_dbus_path(Manager *m, const char *s, Job **_j) {
+ Job *j;
+ unsigned id;
+ int r;
+
+ assert(m);
+ assert(s);
+ assert(_j);
+
+ if (!startswith(s, "/org/freedesktop/systemd1/job/"))
+ return -EINVAL;
+
+ if ((r = safe_atou(s + 30, &id)) < 0)
+ return r;
+
+ if (!(j = manager_get_job(m, id)))
+ return -ENOENT;
+
+ *_j = j;
+
+ return 0;
+}
+
+static bool manager_utmp_good(Manager *m) {
+ int r;
+
+ assert(m);
+
+ if ((r = mount_path_is_mounted(m, _PATH_UTMPX)) <= 0) {
+
+ if (r < 0)
+ log_warning("Failed to determine whether " _PATH_UTMPX " is mounted: %s", strerror(-r));
+
+ return false;
+ }
+
+ return true;
+}
+
+void manager_write_utmp_reboot(Manager *m) {
+ int r;
+
+ assert(m);
+
+ if (m->utmp_reboot_written)
+ return;
+
+ if (m->running_as != MANAGER_INIT)
+ return;
+
+ if (!manager_utmp_good(m))
+ return;
+
+ if ((r = utmp_put_reboot(m->boot_timestamp)) < 0) {
+
+ if (r != -ENOENT && r != -EROFS)
+ log_warning("Failed to write utmp/wtmp: %s", strerror(-r));
+
+ return;
+ }
+
+ m->utmp_reboot_written = true;
+}
+
+void manager_write_utmp_runlevel(Manager *m, Unit *u) {
+ int runlevel, r;
+
+ assert(m);
+ assert(u);
+
+ if (u->meta.type != UNIT_TARGET)
+ return;
+
+ if (m->running_as != MANAGER_INIT)
+ return;
+
+ if (!manager_utmp_good(m))
+ return;
+
+ if ((runlevel = target_get_runlevel(TARGET(u))) <= 0)
+ return;
+
+ if ((r = utmp_put_runlevel(0, runlevel, 0)) < 0) {
+
+ if (r != -ENOENT && r != -EROFS)
+ log_warning("Failed to write utmp/wtmp: %s", strerror(-r));
+ }
+}
+
+void manager_dispatch_bus_name_owner_changed(
+ Manager *m,
+ const char *name,
+ const char* old_owner,
+ const char *new_owner) {
+
+ Unit *u;
+
+ assert(m);
+ assert(name);
+
+ if (!(u = hashmap_get(m->watch_bus, name)))
+ return;
+
+ UNIT_VTABLE(u)->bus_name_owner_change(u, name, old_owner, new_owner);
+}
+
+void manager_dispatch_bus_query_pid_done(
+ Manager *m,
+ const char *name,
+ pid_t pid) {
+
+ Unit *u;
+
+ assert(m);
+ assert(name);
+ assert(pid >= 1);
+
+ if (!(u = hashmap_get(m->watch_bus, name)))
+ return;
+
+ UNIT_VTABLE(u)->bus_query_pid_done(u, name, pid);
+}
+
+int manager_open_serialization(FILE **_f) {
+ char *path;
+ mode_t saved_umask;
+ int fd;
+ FILE *f;
+
+ assert(_f);
+
+ if (asprintf(&path, "/dev/shm/systemd-%u.dump-XXXXXX", (unsigned) getpid()) < 0)
+ return -ENOMEM;
+
+ saved_umask = umask(0077);
+ fd = mkostemp(path, O_RDWR|O_CLOEXEC);
+ umask(saved_umask);
+
+ if (fd < 0) {
+ free(path);
+ return -errno;
+ }
+
+ unlink(path);
+
+ log_debug("Serializing state to %s", path);
+ free(path);
+
+ if (!(f = fdopen(fd, "w+")) < 0)
+ return -errno;
+
+ *_f = f;
+
+ return 0;
+}
+
+int manager_serialize(Manager *m, FILE *f, FDSet *fds) {
+ Iterator i;
+ Unit *u;
+ const char *t;
+ int r;
+
+ assert(m);
+ assert(f);
+ assert(fds);
+
+ HASHMAP_FOREACH_KEY(u, t, m->units, i) {
+ if (u->meta.id != t)
+ continue;
+
+ if (!unit_can_serialize(u))
+ continue;
+
+ /* Start marker */
+ fputs(u->meta.id, f);
+ fputc('\n', f);
+
+ if ((r = unit_serialize(u, f, fds)) < 0)
+ return r;
+ }
+
+ if (ferror(f))
+ return -EIO;
+
+ return 0;
+}
+
+int manager_deserialize(Manager *m, FILE *f, FDSet *fds) {
+ int r = 0;
+
+ assert(m);
+ assert(f);
+
+ log_debug("Deserializing state...");
+
+ for (;;) {
+ Unit *u;
+ char name[UNIT_NAME_MAX+2];
+
+ /* Start marker */
+ if (!fgets(name, sizeof(name), f)) {
+ if (feof(f))
+ break;
+
+ return -errno;
+ }
+
+ char_array_0(name);
+
+ if ((r = manager_load_unit(m, strstrip(name), NULL, &u)) < 0)
+ return r;
+
+ if ((r = unit_deserialize(u, f, fds)) < 0)
+ return r;
+ }
+
+ if (ferror(f))
+ return -EIO;
+
+ return 0;
+}
+
+int manager_reload(Manager *m) {
+ int r, q;
+ FILE *f;
+ FDSet *fds;
+
+ assert(m);
+
+ if ((r = manager_open_serialization(&f)) < 0)
+ return r;
+
+ if (!(fds = fdset_new())) {
+ r = -ENOMEM;
+ goto finish;
+ }
+
+ if ((r = manager_serialize(m, f, fds)) < 0)
+ goto finish;
+
+ if (fseeko(f, 0, SEEK_SET) < 0) {
+ r = -errno;
+ goto finish;
+ }
+
+ /* From here on there is no way back. */
+ manager_clear_jobs_and_units(m);
+
+ /* First, enumerate what we can from all config files */
+ if ((q = manager_enumerate(m)) < 0)
+ r = q;
+
+ /* Second, deserialize our stored data */
+ if ((q = manager_deserialize(m, f, fds)) < 0)
+ r = q;
+
+ fclose(f);
+ f = NULL;
+
+ /* Third, fire things up! */
+ if ((q = manager_coldplug(m)) < 0)
+ r = q;
+
+finish:
+ if (f)
+ fclose(f);
+
+ if (fds)
+ fdset_free(fds);
+
+ return r;
+}
+
+static const char* const manager_running_as_table[_MANAGER_RUNNING_AS_MAX] = {
+ [MANAGER_INIT] = "init",
+ [MANAGER_SYSTEM] = "system",
+ [MANAGER_SESSION] = "session"
+};
+
+DEFINE_STRING_TABLE_LOOKUP(manager_running_as, ManagerRunningAs);