chiark / gitweb /
systemctl: introduce reset-maintenance command
[elogind.git] / src / manager.c
index 638ef2f4500af4997ea01f946ee2a039706c5a95..8a9b9dd44edf9f98e070d4adca09af1a334499b2 100644 (file)
@@ -36,6 +36,7 @@
 #include <fcntl.h>
 #include <sys/types.h>
 #include <sys/stat.h>
+#include <dirent.h>
 
 #include "manager.h"
 #include "hashmap.h"
@@ -88,7 +89,7 @@ static int manager_setup_notify(Manager *m) {
         else
                 strncpy(sa.un.sun_path+1, NOTIFY_SOCKET, sizeof(sa.un.sun_path)-1);
 
-        if (bind(m->notify_watch.fd, &sa.sa, sizeof(sa)) < 0) {
+        if (bind(m->notify_watch.fd, &sa.sa, sizeof(sa_family_t) + 1 + strlen(sa.un.sun_path+1)) < 0) {
                 log_error("bind() failed: %m");
                 return -errno;
         }
@@ -186,7 +187,6 @@ static int manager_setup_signals(Manager *m) {
 int manager_new(ManagerRunningAs running_as, Manager **_m) {
         Manager *m;
         int r = -ENOMEM;
-        char *p;
 
         assert(_m);
         assert(running_as >= 0);
@@ -245,14 +245,6 @@ int manager_new(ManagerRunningAs running_as, Manager **_m) {
         if ((r = bus_init(m)) < 0)
                 goto fail;
 
-        if (asprintf(&p, "%s/%s", m->cgroup_mount_point, m->cgroup_hierarchy) < 0) {
-                r = -ENOMEM;
-                goto fail;
-        }
-
-        m->pin_cgroupfs_fd = open(p, O_RDONLY|O_CLOEXEC|O_DIRECTORY|O_NOCTTY|O_NONBLOCK);
-        free(p);
-
         *_m = m;
         return 0;
 
@@ -420,8 +412,7 @@ void manager_free(Manager *m) {
 
         /* If we reexecute ourselves, we keep the root cgroup
          * around */
-        if (m->exit_code != MANAGER_REEXECUTE)
-                manager_shutdown_cgroup(m);
+        manager_shutdown_cgroup(m, m->exit_code != MANAGER_REEXECUTE);
 
         bus_done(m);
 
@@ -443,14 +434,8 @@ void manager_free(Manager *m) {
         lookup_paths_free(&m->lookup_paths);
         strv_free(m->environment);
 
-        free(m->cgroup_controller);
-        free(m->cgroup_hierarchy);
-        free(m->cgroup_mount_point);
-
         hashmap_free(m->cgroup_bondings);
-
-        if (m->pin_cgroupfs_fd >= 0)
-                close_nointr_nofail(m->pin_cgroupfs_fd);
+        set_free_free(m->unit_path_cache);
 
         free(m);
 }
@@ -494,11 +479,77 @@ int manager_coldplug(Manager *m) {
         return r;
 }
 
+static void manager_build_unit_path_cache(Manager *m) {
+        char **i;
+        DIR *d = NULL;
+        int r;
+
+        assert(m);
+
+        set_free_free(m->unit_path_cache);
+
+        if (!(m->unit_path_cache = set_new(string_hash_func, string_compare_func))) {
+                log_error("Failed to allocate unit path cache.");
+                return;
+        }
+
+        /* This simply builds a list of files we know exist, so that
+         * we don't always have to go to disk */
+
+        STRV_FOREACH(i, m->lookup_paths.unit_path) {
+                struct dirent *de;
+
+                if (!(d = opendir(*i))) {
+                        log_error("Failed to open directory: %m");
+                        continue;
+                }
+
+                while ((de = readdir(d))) {
+                        char *p;
+
+                        if (ignore_file(de->d_name))
+                                continue;
+
+                        if (asprintf(&p, "%s/%s", streq(*i, "/") ? "" : *i, de->d_name) < 0) {
+                                r = -ENOMEM;
+                                goto fail;
+                        }
+
+                        if ((r = set_put(m->unit_path_cache, p)) < 0) {
+                                free(p);
+                                goto fail;
+                        }
+                }
+
+                closedir(d);
+                d = NULL;
+        }
+
+        return;
+
+fail:
+        log_error("Failed to build unit path cache: %s", strerror(-r));
+
+        set_free_free(m->unit_path_cache);
+        m->unit_path_cache = NULL;
+
+        if (d)
+                closedir(d);
+}
+
 int manager_startup(Manager *m, FILE *serialization, FDSet *fds) {
         int r, q;
 
         assert(m);
 
+        manager_build_unit_path_cache(m);
+
+        /* If we will deserialize make sure that during enumeration
+         * this is already known, so we increase the counter here
+         * already */
+        if (serialization)
+                m->n_deserializing ++;
+
         /* First, enumerate what we can from all config files */
         r = manager_enumerate(m);
 
@@ -511,6 +562,11 @@ int manager_startup(Manager *m, FILE *serialization, FDSet *fds) {
         if ((q = manager_coldplug(m)) < 0)
                 r = q;
 
+        if (serialization) {
+                assert(m->n_deserializing > 0);
+                m->n_deserializing --;
+        }
+
         /* Now that the initial devices are available, let's see if we
          * can write the utmp file */
         manager_write_utmp_reboot(m);
@@ -1068,6 +1124,7 @@ static int transaction_apply(Manager *m) {
 
                 job_add_to_run_queue(j);
                 job_add_to_dbus_queue(j);
+                job_start_timer(j);
         }
 
         /* As last step, kill all remaining job dependencies. */
@@ -1272,7 +1329,8 @@ static int transaction_add_job_and_dependencies(
         assert(type < _JOB_TYPE_MAX);
         assert(unit);
 
-        if (unit->meta.load_state != UNIT_LOADED) {
+        if (type != JOB_STOP &&
+            unit->meta.load_state != UNIT_LOADED) {
                 dbus_set_error(e, BUS_ERROR_LOAD_FAILED, "Unit %s failed to load. See logs for details.", unit->meta.id);
                 return -EINVAL;
         }
@@ -1298,20 +1356,26 @@ static int transaction_add_job_and_dependencies(
                                         goto fail;
 
                         SET_FOREACH(dep, ret->unit->meta.dependencies[UNIT_REQUIRES_OVERRIDABLE], i)
-                                if ((r = transaction_add_job_and_dependencies(m, JOB_START, dep, ret, !override, override, e, NULL)) < 0 && r != -EBADR)
-                                        log_warning("Cannot add dependency job for unit %s, ignoring: %s", dep->meta.id, strerror(-r));
+                                if ((r = transaction_add_job_and_dependencies(m, JOB_START, dep, ret, !override, override, e, NULL)) < 0 && r != -EBADR) {
+                                        log_warning("Cannot add dependency job for unit %s, ignoring: %s", dep->meta.id, bus_error(e, r));
+                                        dbus_error_free(e);
+                                }
 
                         SET_FOREACH(dep, ret->unit->meta.dependencies[UNIT_WANTS], i)
-                                if ((r = transaction_add_job_and_dependencies(m, JOB_START, dep, ret, false, false, e, NULL)) < 0)
-                                        log_warning("Cannot add dependency job for unit %s, ignoring: %s", dep->meta.id, strerror(-r));
+                                if ((r = transaction_add_job_and_dependencies(m, JOB_START, dep, ret, false, false, e, NULL)) < 0) {
+                                        log_warning("Cannot add dependency job for unit %s, ignoring: %s", dep->meta.id, bus_error(e, r));
+                                        dbus_error_free(e);
+                                }
 
                         SET_FOREACH(dep, ret->unit->meta.dependencies[UNIT_REQUISITE], i)
                                 if ((r = transaction_add_job_and_dependencies(m, JOB_VERIFY_ACTIVE, dep, ret, true, override, e, NULL)) < 0 && r != -EBADR)
                                         goto fail;
 
                         SET_FOREACH(dep, ret->unit->meta.dependencies[UNIT_REQUISITE_OVERRIDABLE], i)
-                                if ((r = transaction_add_job_and_dependencies(m, JOB_VERIFY_ACTIVE, dep, ret, !override, override, e, NULL)) < 0 && r != -EBADR)
-                                        log_warning("Cannot add dependency job for unit %s, ignoring: %s", dep->meta.id, strerror(-r));
+                                if ((r = transaction_add_job_and_dependencies(m, JOB_VERIFY_ACTIVE, dep, ret, !override, override, e, NULL)) < 0 && r != -EBADR) {
+                                        log_warning("Cannot add dependency job for unit %s, ignoring: %s", dep->meta.id, bus_error(e, r));
+                                        dbus_error_free(e);
+                                }
 
                         SET_FOREACH(dep, ret->unit->meta.dependencies[UNIT_CONFLICTS], i)
                                 if ((r = transaction_add_job_and_dependencies(m, JOB_STOP, dep, ret, true, override, e, NULL)) < 0 && r != -EBADR)
@@ -1665,7 +1729,7 @@ static int manager_process_notify_fd(Manager *m) {
 
                 ucred = (struct ucred*) CMSG_DATA(&control.cmsghdr);
 
-                if (!(u = hashmap_get(m->watch_pids, UINT32_TO_PTR(ucred->pid))))
+                if (!(u = hashmap_get(m->watch_pids, LONG_TO_PTR(ucred->pid))))
                         if (!(u = cgroup_unit_by_pid(m, ucred->pid))) {
                                 log_warning("Cannot find unit for notify message of PID %lu.", (unsigned long) ucred->pid);
                                 continue;
@@ -1730,7 +1794,7 @@ static int manager_dispatch_sigchld(Manager *m) {
                         return r;
 
                 /* And now figure out the unit this belongs to */
-                if (!(u = hashmap_get(m->watch_pids, UINT32_TO_PTR(si.si_pid))))
+                if (!(u = hashmap_get(m->watch_pids, LONG_TO_PTR(si.si_pid))))
                         u = cgroup_unit_by_pid(m, si.si_pid);
 
                 /* And now, we actually reap the zombie. */
@@ -1748,14 +1812,14 @@ static int manager_dispatch_sigchld(Manager *m) {
                           (long unsigned) si.si_pid,
                           sigchld_code_to_string(si.si_code),
                           si.si_status,
-                          strna(si.si_code == CLD_EXITED ? exit_status_to_string(si.si_status) : strsignal(si.si_status)));
+                          strna(si.si_code == CLD_EXITED ? exit_status_to_string(si.si_status) : signal_to_string(si.si_status)));
 
                 if (!u)
                         continue;
 
-                log_debug("Child %llu belongs to %s", (long long unsigned) si.si_pid, u->meta.id);
+                log_debug("Child %lu belongs to %s", (long unsigned) si.si_pid, u->meta.id);
 
-                hashmap_remove(m->watch_pids, UINT32_TO_PTR(si.si_pid));
+                hashmap_remove(m->watch_pids, LONG_TO_PTR(si.si_pid));
                 UNIT_VTABLE(u)->sigchld_event(u, si.si_pid, si.si_code, si.si_status);
         }
 
@@ -1768,6 +1832,8 @@ static int manager_start_target(Manager *m, const char *name, JobMode mode) {
 
         dbus_error_init(&error);
 
+        log_info("Activating special unit %s", name);
+
         if ((r = manager_add_job_by_name(m, JOB_START, name, mode, true, &error, NULL)) < 0)
                 log_error("Failed to enqueue %s job: %s", name, bus_error(&error, r));
 
@@ -1795,6 +1861,8 @@ static int manager_process_signal_fd(Manager *m) {
                         return -errno;
                 }
 
+                log_debug("Received SIG%s", strna(signal_to_string(sfsi.ssi_signo)));
+
                 switch (sfsi.ssi_signo) {
 
                 case SIGCHLD:
@@ -1892,7 +1960,7 @@ static int manager_process_signal_fd(Manager *m) {
                         static const char * const table[] = {
                                 [0] = SPECIAL_DEFAULT_TARGET,
                                 [1] = SPECIAL_RESCUE_TARGET,
-                                [2] = SPECIAL_EMERGENCY_SERVICE,
+                                [2] = SPECIAL_EMERGENCY_TARGET,
                                 [3] = SPECIAL_HALT_TARGET,
                                 [4] = SPECIAL_POWEROFF_TARGET,
                                 [5] = SPECIAL_REBOOT_TARGET
@@ -1905,7 +1973,7 @@ static int manager_process_signal_fd(Manager *m) {
                                 break;
                         }
 
-                        log_info("Got unhandled signal <%s>.", strsignal(sfsi.ssi_signo));
+                        log_warning("Got unhandled signal <%s>.", strna(signal_to_string(sfsi.ssi_signo)));
                 }
                 }
         }
@@ -1955,7 +2023,8 @@ static int process_event(Manager *m, struct epoll_event *ev) {
                 UNIT_VTABLE(w->data.unit)->fd_event(w->data.unit, w->fd, ev->events, w);
                 break;
 
-        case WATCH_TIMER: {
+        case WATCH_UNIT_TIMER:
+        case WATCH_JOB_TIMER: {
                 uint64_t v;
                 ssize_t k;
 
@@ -1968,7 +2037,10 @@ static int process_event(Manager *m, struct epoll_event *ev) {
                         return k < 0 ? -errno : -EIO;
                 }
 
-                UNIT_VTABLE(w->data.unit)->timer_event(w->data.unit, v, w);
+                if (w->type == WATCH_UNIT_TIMER)
+                        UNIT_VTABLE(w->data.unit)->timer_event(w->data.unit, v, w);
+                else
+                        job_timer_event(w->data.job, v, w);
                 break;
         }
 
@@ -2005,6 +2077,10 @@ int manager_loop(Manager *m) {
         assert(m);
         m->exit_code = MANAGER_RUNNING;
 
+        /* Release the path cache */
+        set_free_free(m->unit_path_cache);
+        m->unit_path_cache = NULL;
+
         /* There might still be some zombies hanging around from
          * before we were exec()'ed. Leat's reap them */
         if ((r = manager_dispatch_sigchld(m)) < 0)
@@ -2275,6 +2351,8 @@ int manager_deserialize(Manager *m, FILE *f, FDSet *fds) {
 
         log_debug("Deserializing state...");
 
+        m->n_deserializing ++;
+
         for (;;) {
                 Unit *u;
                 char name[UNIT_NAME_MAX+2];
@@ -2284,22 +2362,31 @@ int manager_deserialize(Manager *m, FILE *f, FDSet *fds) {
                         if (feof(f))
                                 break;
 
-                        return -errno;
+                        r = -errno;
+                        goto finish;
                 }
 
                 char_array_0(name);
 
                 if ((r = manager_load_unit(m, strstrip(name), NULL, NULL, &u)) < 0)
-                        return r;
+                        goto finish;
 
                 if ((r = unit_deserialize(u, f, fds)) < 0)
-                        return r;
+                        goto finish;
         }
 
-        if (ferror(f))
-                return -EIO;
+        if (ferror(f)) {
+                r = -EIO;
+                goto finish;
+        }
 
-        return 0;
+        r = 0;
+
+finish:
+        assert(m->n_deserializing > 0);
+        m->n_deserializing --;
+
+        return r;
 }
 
 int manager_reload(Manager *m) {
@@ -2333,6 +2420,8 @@ int manager_reload(Manager *m) {
         if ((q = lookup_paths_init(&m->lookup_paths, m->running_as)) < 0)
                 r = q;
 
+        m->n_deserializing ++;
+
         /* First, enumerate what we can from all config files */
         if ((q = manager_enumerate(m)) < 0)
                 r = q;
@@ -2348,6 +2437,9 @@ int manager_reload(Manager *m) {
         if ((q = manager_coldplug(m)) < 0)
                 r = q;
 
+        assert(m->n_deserializing > 0);
+        m->n_deserializing ++;
+
 finish:
         if (f)
                 fclose(f);
@@ -2374,6 +2466,16 @@ bool manager_is_booting_or_shutting_down(Manager *m) {
         return false;
 }
 
+void manager_reset_maintenance(Manager *m) {
+        Unit *u;
+        Iterator i;
+
+        assert(m);
+
+        HASHMAP_FOREACH(u, m->units, i)
+                unit_reset_maintenance(u);
+}
+
 static const char* const manager_running_as_table[_MANAGER_RUNNING_AS_MAX] = {
         [MANAGER_SYSTEM] = "system",
         [MANAGER_SESSION] = "session"