chiark / gitweb /
greatly extend what we enforce as process properties
[elogind.git] / manager.c
index a32755cedc492c3996bda1869111f095c30f3572..fca1963af1a111782d982b20c8e50a24ce31ef5b 100644 (file)
--- a/manager.c
+++ b/manager.c
 #include "log.h"
 #include "util.h"
 
 #include "log.h"
 #include "util.h"
 
-static const char * const special_table[_SPECIAL_UNIT_MAX] = {
-        [SPECIAL_SYSLOG_SERVICE] = "syslog.service",
-        [SPECIAL_DBUS_SERVICE] = "messagebus.service",
-        [SPECIAL_LOGGER_SOCKET] = "systemd-logger.socket"
-};
-
 static int manager_setup_signals(Manager *m) {
         sigset_t mask;
         struct epoll_event ev;
 static int manager_setup_signals(Manager *m) {
         sigset_t mask;
         struct epoll_event ev;
@@ -42,7 +36,7 @@ static int manager_setup_signals(Manager *m) {
         assert_se(sigaddset(&mask, SIGPIPE) == 0);
         assert_se(sigprocmask(SIG_SETMASK, &mask, NULL) == 0);
 
         assert_se(sigaddset(&mask, SIGPIPE) == 0);
         assert_se(sigprocmask(SIG_SETMASK, &mask, NULL) == 0);
 
-        m->signal_watch.type = WATCH_SIGNAL_FD;
+        m->signal_watch.type = WATCH_SIGNAL;
         if ((m->signal_watch.fd = signalfd(-1, &mask, SFD_NONBLOCK|SFD_CLOEXEC)) < 0)
                 return -errno;
 
         if ((m->signal_watch.fd = signalfd(-1, &mask, SFD_NONBLOCK|SFD_CLOEXEC)) < 0)
                 return -errno;
 
@@ -56,28 +50,13 @@ static int manager_setup_signals(Manager *m) {
         return 0;
 }
 
         return 0;
 }
 
-static int manager_load_special_units(Manager *m) {
-        SpecialUnit c;
-        int r;
-
-        assert(m);
-
-        /* Loads all 'special' units, so that we have easy access to them later */
-
-        for (c = 0; c < _SPECIAL_UNIT_MAX; c++)
-                if ((r = manager_load_unit(m, special_table[c], m->special_units+c)) < 0)
-                        return r;
-
-        return 0;
-};
-
 Manager* manager_new(void) {
         Manager *m;
 
         if (!(m = new0(Manager, 1)))
                 return NULL;
 
 Manager* manager_new(void) {
         Manager *m;
 
         if (!(m = new0(Manager, 1)))
                 return NULL;
 
-        m->signal_watch.fd = m->epoll_fd = -1;
+        m->signal_watch.fd = m->mount_watch.fd = m->udev_watch.fd = m->epoll_fd = -1;
 
         if (!(m->units = hashmap_new(string_hash_func, string_compare_func)))
                 goto fail;
 
         if (!(m->units = hashmap_new(string_hash_func, string_compare_func)))
                 goto fail;
@@ -97,9 +76,6 @@ Manager* manager_new(void) {
         if (manager_setup_signals(m) < 0)
                 goto fail;
 
         if (manager_setup_signals(m) < 0)
                 goto fail;
 
-        if (manager_load_special_units(m) < 0)
-                goto fail;
-
         return m;
 
 fail:
         return m;
 
 fail:
@@ -108,6 +84,7 @@ fail:
 }
 
 void manager_free(Manager *m) {
 }
 
 void manager_free(Manager *m) {
+        UnitType c;
         Unit *u;
         Job *j;
 
         Unit *u;
         Job *j;
 
@@ -119,6 +96,10 @@ void manager_free(Manager *m) {
         while ((u = hashmap_first(m->units)))
                 unit_free(u);
 
         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);
         hashmap_free(m->units);
         hashmap_free(m->jobs);
         hashmap_free(m->transaction_jobs);
@@ -132,6 +113,39 @@ void manager_free(Manager *m) {
         free(m);
 }
 
         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);
 static void transaction_delete_job(Manager *m, Job *j) {
         assert(m);
         assert(j);
@@ -410,8 +424,12 @@ static int transaction_verify_order_one(Manager *m, Job *j, Job *from, unsigned
                  * since smart how we are we stored our way back in
                  * there. */
 
                  * since smart how we are we stored our way back in
                  * there. */
 
+                log_debug("Found cycle on %s/%s", unit_id(j->unit), job_type_to_string(j->type));
+
                 for (k = from; k; k = (k->generation == generation ? k->marker : NULL)) {
 
                 for (k = from; k; k = (k->generation == generation ? k->marker : NULL)) {
 
+                        log_debug("Walked on cycle path to %s/%s", unit_id(j->unit), job_type_to_string(j->type));
+
                         if (!k->installed &&
                             !unit_matters_to_anchor(k->unit, k)) {
                                 /* Ok, we can drop this one, so let's
                         if (!k->installed &&
                             !unit_matters_to_anchor(k->unit, k)) {
                                 /* Ok, we can drop this one, so let's
@@ -427,6 +445,8 @@ static int transaction_verify_order_one(Manager *m, Job *j, Job *from, unsigned
                                 break;
                 }
 
                                 break;
                 }
 
+                log_debug("Unable to break cycle");
+
                 return -ENOEXEC;
         }
 
                 return -ENOEXEC;
         }
 
@@ -453,6 +473,10 @@ static int transaction_verify_order_one(Manager *m, Job *j, Job *from, unsigned
                         return r;
         }
 
                         return r;
         }
 
+        /* Ok, let's backtrack, and remember that this entry is not on
+         * our path anymore. */
+        j->marker = NULL;
+
         return 0;
 }
 
         return 0;
 }
 
@@ -539,6 +563,7 @@ static void transaction_minimize_impact(Manager *m) {
 
                 HASHMAP_FOREACH(j, m->transaction_jobs, i) {
                         LIST_FOREACH(transaction, j, j) {
 
                 HASHMAP_FOREACH(j, m->transaction_jobs, i) {
                         LIST_FOREACH(transaction, j, j) {
+                                bool stops_running_service, changes_existing_job;
 
                                 /* If it matters, we shouldn't drop it */
                                 if (j->matters_to_anchor)
 
                                 /* If it matters, we shouldn't drop it */
                                 if (j->matters_to_anchor)
@@ -547,12 +572,25 @@ static void transaction_minimize_impact(Manager *m) {
                                 /* Would this stop a running service?
                                  * Would this change an existing job?
                                  * If so, let's drop this entry */
                                 /* Would this stop a running service?
                                  * Would this change an existing job?
                                  * If so, let's drop this entry */
-                                if ((j->type != JOB_STOP || UNIT_IS_INACTIVE_OR_DEACTIVATING(unit_active_state(j->unit))) &&
-                                    (!j->unit->meta.job  || job_type_is_conflicting(j->type, j->unit->meta.job->state)))
+
+                                stops_running_service =
+                                        j->type == JOB_STOP && UNIT_IS_ACTIVE_OR_ACTIVATING(unit_active_state(j->unit));
+
+                                changes_existing_job =
+                                        j->unit->meta.job && job_type_is_conflicting(j->type, j->unit->meta.job->state);
+
+                                if (!stops_running_service && !changes_existing_job)
                                         continue;
 
                                         continue;
 
+                                if (stops_running_service)
+                                        log_debug("%s/%s would stop a running service.", unit_id(j->unit), job_type_to_string(j->type));
+
+                                if (changes_existing_job)
+                                        log_debug("%s/%s would change existing job.", unit_id(j->unit), job_type_to_string(j->type));
+
                                 /* Ok, let's get rid of this */
                                 /* Ok, let's get rid of this */
-                                log_debug("Deleting %s/%s to minimize impact", unit_id(j->unit), job_type_to_string(j->type));
+                                log_debug("Deleting %s/%s to minimize impact.", unit_id(j->unit), job_type_to_string(j->type));
+
                                 transaction_delete_job(m, j);
                                 again = true;
                                 break;
                                 transaction_delete_job(m, j);
                                 again = true;
                                 break;
@@ -647,8 +685,10 @@ static int transaction_activate(Manager *m, JobMode mode) {
                 if ((r = transaction_verify_order(m, &generation)) >= 0)
                         break;
 
                 if ((r = transaction_verify_order(m, &generation)) >= 0)
                         break;
 
-                if (r != -EAGAIN)
+                if (r != -EAGAIN) {
+                        log_debug("Requested transaction contains an unfixable cyclic ordering dependency: %s", strerror(-r));
                         goto rollback;
                         goto rollback;
+                }
 
                 /* Let's see if the resulting transaction ordering
                  * graph is still cyclic... */
 
                 /* Let's see if the resulting transaction ordering
                  * graph is still cyclic... */
@@ -661,8 +701,10 @@ static int transaction_activate(Manager *m, JobMode mode) {
                 if ((r = transaction_merge_jobs(m)) >= 0)
                         break;
 
                 if ((r = transaction_merge_jobs(m)) >= 0)
                         break;
 
-                if (r != -EAGAIN)
+                if (r != -EAGAIN) {
+                        log_debug("Requested transaction contains unmergable jobs: %s", strerror(-r));
                         goto rollback;
                         goto rollback;
+                }
 
                 /* Sixth step: an entry got dropped, let's garbage
                  * collect its dependencies. */
 
                 /* Sixth step: an entry got dropped, let's garbage
                  * collect its dependencies. */
@@ -674,12 +716,16 @@ static int transaction_activate(Manager *m, JobMode mode) {
 
         /* Seventh step: check whether we can actually apply this */
         if (mode == JOB_FAIL)
 
         /* Seventh step: check whether we can actually apply this */
         if (mode == JOB_FAIL)
-                if ((r = transaction_is_destructive(m, mode)) < 0)
+                if ((r = transaction_is_destructive(m, mode)) < 0) {
+                        log_debug("Requested transaction contradicts existing jobs: %s", strerror(-r));
                         goto rollback;
                         goto rollback;
+                }
 
         /* Eights step: apply changes */
 
         /* Eights step: apply changes */
-        if ((r = transaction_apply(m, mode)) < 0)
+        if ((r = transaction_apply(m, mode)) < 0) {
+                log_debug("Failed to apply transaction: %s", strerror(-r));
                 goto rollback;
                 goto rollback;
+        }
 
         assert(hashmap_isempty(m->transaction_jobs));
         assert(!m->transaction_anchor);
 
         assert(hashmap_isempty(m->transaction_jobs));
         assert(!m->transaction_anchor);
@@ -842,6 +888,8 @@ int manager_add_job(Manager *m, JobType type, Unit *unit, JobMode mode, bool for
         assert(unit);
         assert(mode < _JOB_MODE_MAX);
 
         assert(unit);
         assert(mode < _JOB_MODE_MAX);
 
+        log_debug("Trying to enqueue job %s/%s", unit_id(unit), job_type_to_string(type));
+
         if ((r = transaction_add_job_and_dependencies(m, type, unit, NULL, true, force, &ret))) {
                 transaction_abort(m);
                 return r;
         if ((r = transaction_add_job_and_dependencies(m, type, unit, NULL, true, force, &ret))) {
                 transaction_abort(m);
                 return r;
@@ -850,6 +898,8 @@ int manager_add_job(Manager *m, JobType type, Unit *unit, JobMode mode, bool for
         if ((r = transaction_activate(m, mode)) < 0)
                 return r;
 
         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;
 
         if (_ret)
                 *_ret = ret;
 
@@ -869,7 +919,7 @@ Unit *manager_get_unit(Manager *m, const char *name) {
         return hashmap_get(m->units, 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);
         Meta *meta;
 
         assert(m);
@@ -927,7 +977,7 @@ int manager_load_unit(Manager *m, const char *path, Unit **_ret) {
         }
 
         unit_add_to_load_queue(ret);
         }
 
         unit_add_to_load_queue(ret);
-        dispatch_load_queue(m);
+        manager_dispatch_load_queue(m);
 
         *_ret = ret;
         return 0;
 
         *_ret = ret;
         return 0;
@@ -1010,7 +1060,7 @@ static int manager_dispatch_sigchld(Manager *m) {
                 if (si.si_code != CLD_EXITED && si.si_code != CLD_KILLED && si.si_code != CLD_DUMPED)
                         continue;
 
                 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);
+                log_debug("child %llu died (code=%s, status=%i)", (long long unsigned) si.si_pid, sigchld_code_to_string(si.si_code), si.si_status);
 
                 if (!(u = hashmap_remove(m->watch_pids, UINT32_TO_PTR(si.si_pid))))
                         continue;
 
                 if (!(u = hashmap_remove(m->watch_pids, UINT32_TO_PTR(si.si_pid))))
                         continue;
@@ -1073,10 +1123,10 @@ static int process_event(Manager *m, struct epoll_event *ev, bool *quit) {
 
         switch (w->type) {
 
 
         switch (w->type) {
 
-        case WATCH_SIGNAL_FD:
+        case WATCH_SIGNAL:
 
                 /* An incoming signal? */
 
                 /* An incoming signal? */
-                if (ev->events != POLLIN)
+                if (ev->events != EPOLLIN)
                         return -EINVAL;
 
                 if ((r = manager_process_signal_fd(m, quit)) < 0)
                         return -EINVAL;
 
                 if ((r = manager_process_signal_fd(m, quit)) < 0)
@@ -1107,6 +1157,16 @@ static int process_event(Manager *m, struct epoll_event *ev, bool *quit) {
                 break;
         }
 
                 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;
+
         default:
                 assert_not_reached("Unknown epoll event type.");
         }
         default:
                 assert_not_reached("Unknown epoll event type.");
         }