chiark / gitweb /
implement coldpluggin
[elogind.git] / manager.c
index 819164ca2ddb8d9c67d65d308bf9a1668059452e..9355a6a6f5f9dc79efc094f7838884acae1a5de8 100644 (file)
--- a/manager.c
+++ b/manager.c
 #include "macro.h"
 #include "strv.h"
 #include "log.h"
+#include "util.h"
 
-Manager* manager_new(void) {
-        Manager *m;
+static int manager_setup_signals(Manager *m) {
         sigset_t mask;
         struct epoll_event ev;
 
+        assert(m);
+
+        assert_se(reset_all_signal_handlers() == 0);
+
+        assert_se(sigemptyset(&mask) == 0);
+        assert_se(sigaddset(&mask, SIGCHLD) == 0);
+        assert_se(sigaddset(&mask, SIGINT) == 0);   /* Kernel sends us this on control-alt-del */
+        assert_se(sigaddset(&mask, SIGWINCH) == 0); /* Kernel sends us this on kbrequest (alt-arrowup) */
+        assert_se(sigaddset(&mask, SIGTERM) == 0);
+        assert_se(sigaddset(&mask, SIGHUP) == 0);
+        assert_se(sigaddset(&mask, SIGUSR1) == 0);
+        assert_se(sigaddset(&mask, SIGUSR2) == 0);
+        assert_se(sigaddset(&mask, SIGPIPE) == 0);
+        assert_se(sigprocmask(SIG_SETMASK, &mask, NULL) == 0);
+
+        m->signal_watch.type = WATCH_SIGNAL_FD;
+        if ((m->signal_watch.fd = signalfd(-1, &mask, SFD_NONBLOCK|SFD_CLOEXEC)) < 0)
+                return -errno;
+
+        zero(ev);
+        ev.events = EPOLLIN;
+        ev.data.ptr = &m->signal_watch;
+
+        if (epoll_ctl(m->epoll_fd, EPOLL_CTL_ADD, m->signal_watch.fd, &ev) < 0)
+                return -errno;
+
+        return 0;
+}
+
+Manager* manager_new(void) {
+        Manager *m;
+
         if (!(m = new0(Manager, 1)))
                 return NULL;
 
-        m->signal_fd = m->epoll_fd = -1;
+        m->signal_watch.fd = m->epoll_fd = -1;
 
         if (!(m->units = hashmap_new(string_hash_func, string_compare_func)))
                 goto fail;
@@ -41,18 +73,7 @@ Manager* manager_new(void) {
         if ((m->epoll_fd = epoll_create1(EPOLL_CLOEXEC)) < 0)
                 goto fail;
 
-        assert_se(sigemptyset(&mask) == 0);
-        assert_se(sigaddset(&mask, SIGCHLD) == 0);
-        assert_se(sigprocmask(SIG_SETMASK, &mask, NULL) == 0);
-
-        if ((m->signal_fd = signalfd(-1, &mask, SFD_NONBLOCK|SFD_CLOEXEC)) < 0)
-                goto fail;
-
-        zero(ev);
-        ev.events = EPOLLIN;
-        ev.data.fd = m->signal_fd;
-
-        if (epoll_ctl(m->epoll_fd, EPOLL_CTL_ADD, m->signal_fd, &ev) < 0)
+        if (manager_setup_signals(m) < 0)
                 goto fail;
 
         return m;
@@ -63,6 +84,7 @@ fail:
 }
 
 void manager_free(Manager *m) {
+        UnitType c;
         Unit *u;
         Job *j;
 
@@ -74,6 +96,10 @@ void manager_free(Manager *m) {
         while ((u = hashmap_first(m->units)))
                 unit_free(u);
 
+        for (c = 0; c < _UNIT_TYPE_MAX; c++)
+                if (unit_vtable[c]->shutdown)
+                        unit_vtable[c]->shutdown(m);
+
         hashmap_free(m->units);
         hashmap_free(m->jobs);
         hashmap_free(m->transaction_jobs);
@@ -81,12 +107,45 @@ void manager_free(Manager *m) {
 
         if (m->epoll_fd >= 0)
                 close_nointr(m->epoll_fd);
-        if (m->signal_fd >= 0)
-                close_nointr(m->signal_fd);
+        if (m->signal_watch.fd >= 0)
+                close_nointr(m->signal_watch.fd);
 
         free(m);
 }
 
+int manager_coldplug(Manager *m) {
+        int r;
+        UnitType c;
+        Iterator i;
+        Unit *u;
+        char *k;
+
+        assert(m);
+
+        /* First, let's ask every type to load all units from
+         * disk/kernel that it might know */
+        for (c = 0; c < _UNIT_TYPE_MAX; c++)
+                if (unit_vtable[c]->enumerate)
+                        if ((r = unit_vtable[c]->enumerate(m)) < 0)
+                                return r;
+
+        manager_dispatch_load_queue(m);
+
+        /* Then, let's set up their initial state. */
+        HASHMAP_FOREACH_KEY(u, k, m->units, i) {
+
+                /* ignore aliases */
+                if (unit_id(u) != k)
+                        continue;
+
+                if (UNIT_VTABLE(u)->coldplug)
+                        if ((r = UNIT_VTABLE(u)->coldplug(u)) < 0)
+                                return r;
+        }
+
+        return 0;
+}
+
 static void transaction_delete_job(Manager *m, Job *j) {
         assert(m);
         assert(j);
@@ -109,6 +168,24 @@ static void transaction_delete_unit(Manager *m, Unit *u) {
                 transaction_delete_job(m, j);
 }
 
+static void transaction_clean_dependencies(Manager *m) {
+        Iterator i;
+        Job *j;
+
+        assert(m);
+
+        /* Drops all dependencies of all installed jobs */
+
+        HASHMAP_FOREACH(j, m->jobs, i) {
+                while (j->subject_list)
+                        job_dependency_free(j->subject_list);
+                while (j->object_list)
+                        job_dependency_free(j->object_list);
+        }
+
+        assert(!m->transaction_anchor);
+}
+
 static void transaction_abort(Manager *m) {
         Job *j;
 
@@ -121,7 +198,8 @@ static void transaction_abort(Manager *m) {
                         job_free(j);
 
         assert(hashmap_isempty(m->transaction_jobs));
-        assert(!m->transaction_anchor);
+
+        transaction_clean_dependencies(m);
 }
 
 static void transaction_find_jobs_that_matter_to_anchor(Manager *m, Job *j, unsigned generation) {
@@ -536,13 +614,11 @@ static int transaction_apply(Manager *m, JobMode mode) {
                 assert(!j->transaction_next);
                 assert(!j->transaction_prev);
 
-                while (j->subject_list)
-                        job_dependency_free(j->subject_list);
-                while (j->object_list)
-                        job_dependency_free(j->object_list);
+                job_schedule_run(j);
         }
 
-        m->transaction_anchor = NULL;
+        /* As last step, kill all remaining job dependencies. */
+        transaction_clean_dependencies(m);
 
         return 0;
 
@@ -788,6 +864,8 @@ int manager_add_job(Manager *m, JobType type, Unit *unit, JobMode mode, bool for
         if ((r = transaction_activate(m, mode)) < 0)
                 return r;
 
+        log_debug("Enqueued job %s/%s", unit_id(unit), job_type_to_string(type));
+
         if (_ret)
                 *_ret = ret;
 
@@ -807,7 +885,7 @@ Unit *manager_get_unit(Manager *m, const char *name) {
         return hashmap_get(m->units, name);
 }
 
-static void dispatch_load_queue(Manager *m) {
+void manager_dispatch_load_queue(Manager *m) {
         Meta *meta;
 
         assert(m);
@@ -830,16 +908,19 @@ static void dispatch_load_queue(Manager *m) {
         m->dispatching_load_queue = false;
 }
 
-int manager_load_unit(Manager *m, const char *name, Unit **_ret) {
+int manager_load_unit(Manager *m, const char *path, Unit **_ret) {
         Unit *ret;
         int r;
+        const char *name;
 
         assert(m);
-        assert(name);
+        assert(path);
         assert(_ret);
 
         /* This will load the service information files, but not actually
-         * start any services or anything */
+         * start any services or anything. */
+
+        name = file_name_from_path(path);
 
         if ((ret = manager_get_unit(m, name))) {
                 *_ret = ret;
@@ -849,13 +930,20 @@ int manager_load_unit(Manager *m, const char *name, Unit **_ret) {
         if (!(ret = unit_new(m)))
                 return -ENOMEM;
 
+        if (is_path(path)) {
+                if (!(ret->meta.load_path = strdup(path))) {
+                        unit_free(ret);
+                        return -ENOMEM;
+                }
+        }
+
         if ((r = unit_add_name(ret, name)) < 0) {
                 unit_free(ret);
                 return r;
         }
 
         unit_add_to_load_queue(ret);
-        dispatch_load_queue(m);
+        manager_dispatch_load_queue(m);
 
         *_ret = ret;
         return 0;
@@ -917,13 +1005,20 @@ void manager_dispatch_run_queue(Manager *m) {
 static int manager_dispatch_sigchld(Manager *m) {
         assert(m);
 
+        log_debug("dispatching SIGCHLD");
+
         for (;;) {
                 siginfo_t si;
                 Unit *u;
 
                 zero(si);
-                if (waitid(P_ALL, 0, &si, WNOHANG) < 0)
+                if (waitid(P_ALL, 0, &si, WEXITED|WNOHANG) < 0) {
+
+                        if (errno == ECHILD)
+                                break;
+
                         return -errno;
+                }
 
                 if (si.si_pid == 0)
                         break;
@@ -931,6 +1026,8 @@ static int manager_dispatch_sigchld(Manager *m) {
                 if (si.si_code != CLD_EXITED && si.si_code != CLD_KILLED && si.si_code != CLD_DUMPED)
                         continue;
 
+                log_debug("child %llu died (code=%s, status=%i)", (long long unsigned) si.si_pid, sigchld_code(si.si_code), si.si_status);
+
                 if (!(u = hashmap_remove(m->watch_pids, UINT32_TO_PTR(si.si_pid))))
                         continue;
 
@@ -940,7 +1037,7 @@ static int manager_dispatch_sigchld(Manager *m) {
         return 0;
 }
 
-static int manager_process_signal_fd(Manager *m) {
+static int manager_process_signal_fd(Manager *m, bool *quit) {
         ssize_t n;
         struct signalfd_siginfo sfsi;
         bool sigchld = false;
@@ -948,19 +1045,31 @@ static int manager_process_signal_fd(Manager *m) {
         assert(m);
 
         for (;;) {
-                if ((n = read(m->signal_fd, &sfsi, sizeof(sfsi))) != sizeof(sfsi)) {
+                if ((n = read(m->signal_watch.fd, &sfsi, sizeof(sfsi))) != sizeof(sfsi)) {
 
                         if (n >= 0)
                                 return -EIO;
 
                         if (errno == EAGAIN)
-                                return 0;
+                                break;
 
                         return -errno;
                 }
 
-                if (sfsi.ssi_signo == SIGCHLD)
+                switch (sfsi.ssi_signo) {
+
+                case SIGCHLD:
                         sigchld = true;
+                        break;
+
+                case SIGINT:
+                case SIGTERM:
+                        *quit = true;
+                        return 0;
+
+                default:
+                        log_info("Got unhandled signal <%s>.", strsignal(sfsi.ssi_signo));
+                }
         }
 
         if (sigchld)
@@ -969,56 +1078,53 @@ static int manager_process_signal_fd(Manager *m) {
         return 0;
 }
 
-static int process_event(Manager *m, struct epoll_event *ev) {
+static int process_event(Manager *m, struct epoll_event *ev, bool *quit) {
         int r;
+        Watch *w;
 
         assert(m);
         assert(ev);
 
-        switch (ev->data.u32) {
+        assert(w = ev->data.ptr);
 
-                case MANAGER_SIGNAL:
-                        assert(ev->data.fd == m->signal_fd);
+        switch (w->type) {
 
-                        /* An incoming signal? */
-                        if (ev->events != POLLIN)
-                                return -EINVAL;
+        case WATCH_SIGNAL_FD:
 
-                        if ((r = manager_process_signal_fd(m)) < 0)
-                                return -r;
+                /* An incoming signal? */
+                if (ev->events != POLLIN)
+                        return -EINVAL;
 
-                        break;
+                if ((r = manager_process_signal_fd(m, quit)) < 0)
+                        return r;
 
-                case MANAGER_FD: {
-                        Unit *u;
+                break;
 
-                        /* Some fd event, to be dispatched to the units */
-                        assert_se(u = ev->data.ptr);
-                        UNIT_VTABLE(u)->fd_event(u, ev->data.fd, ev->events);
-                        break;
-                }
+        case WATCH_FD:
 
-                case MANAGER_TIMER: {
-                        Unit *u;
-                        uint64_t v;
-                        ssize_t k;
+                /* Some fd event, to be dispatched to the units */
+                UNIT_VTABLE(w->unit)->fd_event(w->unit, w->fd, ev->events, w);
+                break;
 
-                        /* Some timer event, to be dispatched to the units */
-                        if ((k = read(ev->data.fd, &v, sizeof(v))) != sizeof(v)) {
+        case WATCH_TIMER: {
+                uint64_t v;
+                ssize_t k;
 
-                                if (k < 0 && (errno == EINTR || errno == EAGAIN))
-                                        break;
+                /* Some timer event, to be dispatched to the units */
+                if ((k = read(ev->data.fd, &v, sizeof(v))) != sizeof(v)) {
 
-                                return k < 0 ? -errno : -EIO;
-                        }
+                        if (k < 0 && (errno == EINTR || errno == EAGAIN))
+                                break;
 
-                        assert_se(u = ev->data.ptr);
-                        UNIT_VTABLE(u)->timer_event(u, ev->data.fd, v);
-                        break;
+                        return k < 0 ? -errno : -EIO;
                 }
 
-                default:
-                        assert_not_reached("Unknown epoll event type.");
+                UNIT_VTABLE(w->unit)->timer_event(w->unit, v, w);
+                break;
+        }
+
+        default:
+                assert_not_reached("Unknown epoll event type.");
         }
 
         return 0;
@@ -1026,16 +1132,17 @@ static int process_event(Manager *m, struct epoll_event *ev) {
 
 int manager_loop(Manager *m) {
         int r;
+        bool quit = false;
 
         assert(m);
 
         for (;;) {
-                struct epoll_event events[32];
-                int n, i;
+                struct epoll_event event;
+                int n;
 
                 manager_dispatch_run_queue(m);
 
-                if ((n = epoll_wait(m->epoll_fd, events, ELEMENTSOF(events), -1)) < 0) {
+                if ((n = epoll_wait(m->epoll_fd, &event, 1, -1)) < 0) {
 
                         if (errno == -EINTR)
                                 continue;
@@ -1043,8 +1150,12 @@ int manager_loop(Manager *m) {
                         return -errno;
                 }
 
-                for (i = 0; i < n; i++)
-                        if ((r = process_event(m, events + i)) < 0)
-                                return r;
+                assert(n == 1);
+
+                if ((r = process_event(m, &event, &quit)) < 0)
+                        return r;
+
+                if (quit)
+                        return 0;
         }
 }