chiark / gitweb /
execute: improve exec_spawn() logging
[elogind.git] / manager.c
index e9bd3669041ceec88a56f43cf9a121e6541cd53b..9e4fbbbaeddd0d70973817bc0c76433de776dcf4 100644 (file)
--- a/manager.c
+++ b/manager.c
@@ -49,8 +49,6 @@ static int manager_setup_signals(Manager *m) {
 
         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 */
@@ -194,6 +192,7 @@ fail:
 static int manager_find_paths(Manager *m) {
         const char *e;
         char *t;
+
         assert(m);
 
         /* First priority is whatever has been passed to us via env
@@ -233,10 +232,24 @@ static int manager_find_paths(Manager *m) {
                                               NULL)))
                                 return -ENOMEM;
                 }
+
+                if ((e = getenv("SYSTEMD_SYSVRCND_PATH")))
+                        if (!(m->sysvrcnd_path = split_path_and_make_absolute(e)))
+                                return -ENOMEM;
+
+                if (strv_isempty(m->sysvrcnd_path)) {
+                        strv_free(m->sysvrcnd_path);
+
+                        if (!(m->sysvrcnd_path = strv_new(
+                                              SYSTEM_SYSVRCND_PATH,     /* /etc/rcN.d/ */
+                                              NULL)))
+                                return -ENOMEM;
+                }
         }
 
         strv_uniq(m->unit_path);
         strv_uniq(m->sysvinit_path);
+        strv_uniq(m->sysvrcnd_path);
 
         assert(!strv_isempty(m->unit_path));
         if (!(t = strv_join(m->unit_path, "\n\t")))
@@ -254,18 +267,31 @@ static int manager_find_paths(Manager *m) {
         } else
                 log_debug("Ignoring SysV init scripts.");
 
+        if (!strv_isempty(m->sysvrcnd_path)) {
+
+                if (!(t = strv_join(m->sysvrcnd_path, "\n\t")))
+                        return -ENOMEM;
+
+                log_debug("Looking for SysV rcN.d links in:\n\t%s", t);
+                free(t);
+        } else
+                log_debug("Ignoring SysV rcN.d links.");
+
         return 0;
 }
 
-int manager_new(Manager **_m) {
+int manager_new(ManagerRunningAs running_as, Manager **_m) {
         Manager *m;
         int r = -ENOMEM;
 
         assert(_m);
+        assert(running_as >= 0);
+        assert(running_as < _MANAGER_RUNNING_AS_MAX);
 
         if (!(m = new0(Manager, 1)))
                 return -ENOMEM;
 
+        m->running_as = running_as;
         m->signal_watch.fd = m->mount_watch.fd = m->udev_watch.fd = m->epoll_fd = -1;
         m->current_job_id = 1; /* start as id #1, so that we can leave #0 around as "null-like" value */
 
@@ -287,35 +313,18 @@ int manager_new(Manager **_m) {
         if ((m->epoll_fd = epoll_create1(EPOLL_CLOEXEC)) < 0)
                 goto fail;
 
-        if (getpid() == 1)
-                m->running_as = MANAGER_INIT;
-        else if (getuid() == 0)
-                m->running_as = MANAGER_SYSTEM;
-        else
-                m->running_as = MANAGER_SESSION;
-
-        log_debug("systemd running in %s mode.", manager_running_as_to_string(m->running_as));
-
         if ((r = manager_find_paths(m)) < 0)
                 goto fail;
 
-        if (chdir("/") < 0)
-                log_warning("Failed to chdir to /: %s", strerror(errno));
-
-        /* Become a session leader if we aren't one yet. */
-        setsid();
-
         if ((r = manager_setup_signals(m)) < 0)
                 goto fail;
 
-        if ((r = mount_setup()) < 0)
-                goto fail;
-
         if ((r = manager_setup_cgroup(m)) < 0)
                 goto fail;
 
-        /* FIXME: this should be called only when the D-Bus bus daemon is running */
-        if ((r = bus_init(m)) < 0)
+        /* Try to connect to the busses, if possible. */
+        if ((r = bus_init_system(m)) < 0 ||
+            (r = bus_init_api(m)) < 0)
                 goto fail;
 
         *_m = m;
@@ -326,6 +335,22 @@ fail:
         return r;
 }
 
+static unsigned manager_dispatch_cleanup_queue(Manager *m) {
+        Meta *meta;
+        unsigned n = 0;
+
+        assert(m);
+
+        while ((meta = m->cleanup_queue)) {
+                assert(meta->in_cleanup_queue);
+
+                unit_free(UNIT(meta));
+                n++;
+        }
+
+        return n;
+}
+
 void manager_free(Manager *m) {
         UnitType c;
         Unit *u;
@@ -339,13 +364,16 @@ void manager_free(Manager *m) {
         while ((u = hashmap_first(m->units)))
                 unit_free(u);
 
+        manager_dispatch_cleanup_queue(m);
+
         for (c = 0; c < _UNIT_TYPE_MAX; c++)
                 if (unit_vtable[c]->shutdown)
                         unit_vtable[c]->shutdown(m);
 
         manager_shutdown_cgroup(m);
 
-        bus_done(m);
+        bus_done_api(m);
+        bus_done_system(m);
 
         hashmap_free(m->units);
         hashmap_free(m->jobs);
@@ -359,6 +387,7 @@ void manager_free(Manager *m) {
 
         strv_free(m->unit_path);
         strv_free(m->sysvinit_path);
+        strv_free(m->sysvrcnd_path);
 
         free(m->cgroup_controller);
         free(m->cgroup_hierarchy);
@@ -402,13 +431,13 @@ int manager_coldplug(Manager *m) {
         return 0;
 }
 
-static void transaction_delete_job(Manager *m, Job *j) {
+static void transaction_delete_job(Manager *m, Job *j, bool delete_dependencies) {
         assert(m);
         assert(j);
 
         /* Deletes one job from the transaction */
 
-        manager_transaction_unlink_job(m, j);
+        manager_transaction_unlink_job(m, j, delete_dependencies);
 
         if (!j->installed)
                 job_free(j);
@@ -421,7 +450,7 @@ static void transaction_delete_unit(Manager *m, Unit *u) {
          * transaction */
 
         while ((j = hashmap_get(m->transaction_jobs, u)))
-                transaction_delete_job(m, j);
+                transaction_delete_job(m, j, true);
 }
 
 static void transaction_clean_dependencies(Manager *m) {
@@ -449,7 +478,7 @@ static void transaction_abort(Manager *m) {
 
         while ((j = hashmap_first(m->transaction_jobs)))
                 if (j->installed)
-                        transaction_delete_job(m, j);
+                        transaction_delete_job(m, j, true);
                 else
                         job_free(j);
 
@@ -541,7 +570,7 @@ static void transaction_merge_and_delete_job(Manager *m, Job *j, Job *other, Job
         /* Kill the other job */
         other->subject_list = NULL;
         other->object_list = NULL;
-        transaction_delete_job(m, other);
+        transaction_delete_job(m, other, true);
 }
 
 static int delete_one_unmergeable_job(Manager *m, Job *j) {
@@ -574,8 +603,8 @@ static int delete_one_unmergeable_job(Manager *m, Job *j) {
                                 return -ENOEXEC;
 
                         /* Ok, we can drop one, so let's do so. */
-                        log_debug("Try to fix job merging by deleting job %s/%s", unit_id(d->unit), job_type_to_string(d->type));
-                        transaction_delete_job(m, d);
+                        log_debug("Trying to fix job merging by deleting job %s/%s", unit_id(d->unit), job_type_to_string(d->type));
+                        transaction_delete_job(m, d, true);
                         return 0;
                 }
 
@@ -643,6 +672,46 @@ static int transaction_merge_jobs(Manager *m) {
         return 0;
 }
 
+static void transaction_drop_redundant(Manager *m) {
+        bool again;
+
+        assert(m);
+
+        /* Goes through the transaction and removes all jobs that are
+         * a noop */
+
+        do {
+                Job *j;
+                Iterator i;
+
+                again = false;
+
+                HASHMAP_FOREACH(j, m->transaction_jobs, i) {
+                        bool changes_something = false;
+                        Job *k;
+
+                        LIST_FOREACH(transaction, k, j) {
+
+                                if (!job_is_anchor(k) &&
+                                    job_type_is_redundant(k->type, unit_active_state(k->unit)))
+                                        continue;
+
+                                changes_something = true;
+                                break;
+                        }
+
+                        if (changes_something)
+                                continue;
+
+                        log_debug("Found redundant job %s/%s, dropping.", unit_id(j->unit), job_type_to_string(j->type));
+                        transaction_delete_job(m, j, false);
+                        again = true;
+                        break;
+                }
+
+        } while (again);
+}
+
 static bool unit_matters_to_anchor(Unit *u, Job *j) {
         assert(u);
         assert(!j->transaction_prev);
@@ -680,11 +749,11 @@ 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. */
 
-                log_debug("Found cycle on %s/%s", unit_id(j->unit), job_type_to_string(j->type));
+                log_debug("Found ordering 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)) {
 
-                        log_debug("Walked on cycle path to %s/%s", unit_id(j->unit), job_type_to_string(j->type));
+                        log_debug("Walked on cycle path to %s/%s", unit_id(k->unit), job_type_to_string(k->type));
 
                         if (!k->installed &&
                             !unit_matters_to_anchor(k->unit, k)) {
@@ -772,7 +841,7 @@ static void transaction_collect_garbage(Manager *m) {
                                 continue;
 
                         log_debug("Garbage collecting job %s/%s", unit_id(j->unit), job_type_to_string(j->type));
-                        transaction_delete_job(m, j);
+                        transaction_delete_job(m, j, true);
                         again = true;
                         break;
                 }
@@ -847,7 +916,7 @@ static void transaction_minimize_impact(Manager *m) {
                                 /* 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));
 
-                                transaction_delete_job(m, j);
+                                transaction_delete_job(m, j, true);
                                 again = true;
                                 break;
                         }
@@ -932,12 +1001,15 @@ static int transaction_activate(Manager *m, JobMode mode) {
          * jobs if we don't have to. */
         transaction_minimize_impact(m);
 
+        /* Third step: Drop redundant jobs */
+        transaction_drop_redundant(m);
+
         for (;;) {
-                /* Third step: Let's remove unneeded jobs that might
+                /* Fourth step: Let's remove unneeded jobs that might
                  * be lurking. */
                 transaction_collect_garbage(m);
 
-                /* Fourth step: verify order makes sense and correct
+                /* Fifth step: verify order makes sense and correct
                  * cycles if necessary and possible */
                 if ((r = transaction_verify_order(m, &generation)) >= 0)
                         break;
@@ -952,7 +1024,7 @@ static int transaction_activate(Manager *m, JobMode mode) {
         }
 
         for (;;) {
-                /* Fifth step: let's drop unmergeable entries if
+                /* Sixth step: let's drop unmergeable entries if
                  * necessary and possible, merge entries we can
                  * merge */
                 if ((r = transaction_merge_jobs(m)) >= 0)
@@ -963,7 +1035,7 @@ static int transaction_activate(Manager *m, JobMode mode) {
                         goto rollback;
                 }
 
-                /* Sixth step: an entry got dropped, let's garbage
+                /* Seventh step: an entry got dropped, let's garbage
                  * collect its dependencies. */
                 transaction_collect_garbage(m);
 
@@ -971,14 +1043,17 @@ static int transaction_activate(Manager *m, JobMode mode) {
                  * unmergeable entries ... */
         }
 
-        /* Seventh step: check whether we can actually apply this */
+        /* Eights step: Drop redundant jobs again, if the merging now allows us to drop more. */
+        transaction_drop_redundant(m);
+
+        /* Ninth step: check whether we can actually apply this */
         if (mode == JOB_FAIL)
                 if ((r = transaction_is_destructive(m, mode)) < 0) {
                         log_debug("Requested transaction contradicts existing jobs: %s", strerror(-r));
                         goto rollback;
                 }
 
-        /* Eights step: apply changes */
+        /* Tenth step: apply changes */
         if ((r = transaction_apply(m, mode)) < 0) {
                 log_debug("Failed to apply transaction: %s", strerror(-r));
                 goto rollback;
@@ -1037,10 +1112,12 @@ static Job* transaction_add_one_job(Manager *m, JobType type, Unit *unit, bool f
         if (is_new)
                 *is_new = true;
 
+        log_debug("Added job %s/%s to transaction.", unit_id(unit), job_type_to_string(type));
+
         return j;
 }
 
-void manager_transaction_unlink_job(Manager *m, Job *j) {
+void manager_transaction_unlink_job(Manager *m, Job *j, bool delete_dependencies) {
         assert(m);
         assert(j);
 
@@ -1064,11 +1141,11 @@ void manager_transaction_unlink_job(Manager *m, Job *j) {
 
                 job_dependency_free(j->object_list);
 
-                if (other) {
+                if (other && delete_dependencies) {
                         log_debug("Deleting job %s/%s as dependency of job %s/%s",
                                   unit_id(other->unit), job_type_to_string(other->type),
                                   unit_id(j->unit), job_type_to_string(j->type));
-                        transaction_delete_job(m, other);
+                        transaction_delete_job(m, other, delete_dependencies);
                 }
         }
 }
@@ -1108,8 +1185,8 @@ static int transaction_add_job_and_dependencies(Manager *m, JobType type, Unit *
                                 if ((r = transaction_add_job_and_dependencies(m, JOB_START, dep, ret, !force, force, NULL)) < 0 && r != -EBADR)
                                         goto fail;
                         SET_FOREACH(dep, ret->unit->meta.dependencies[UNIT_WANTS], i)
-                                if ((r = transaction_add_job_and_dependencies(m, JOB_START, dep, ret, false, force, NULL)) < 0 && r != -EBADR)
-                                        goto fail;
+                                if ((r = transaction_add_job_and_dependencies(m, JOB_START, dep, ret, false, force, NULL)) < 0)
+                                        log_warning("Cannot add dependency job for unit %s, ignoring: %s", unit_id(dep), strerror(-r));
                         SET_FOREACH(dep, ret->unit->meta.dependencies[UNIT_REQUISITE], i)
                                 if ((r = transaction_add_job_and_dependencies(m, JOB_VERIFY_ACTIVE, dep, ret, true, force, NULL)) < 0 && r != -EBADR)
                                         goto fail;
@@ -1244,7 +1321,7 @@ int manager_load_unit(Manager *m, const char *path, Unit **_ret) {
 
         manager_dispatch_load_queue(m);
 
-        *_ret = ret;
+        *_ret = unit_follow_merge(ret);
         return 0;
 }
 
@@ -1317,10 +1394,9 @@ unsigned manager_dispatch_dbus_queue(Manager *m) {
         m->dispatching_dbus_queue = true;
 
         while ((meta = m->dbus_unit_queue)) {
-                Unit *u = (Unit*) meta;
-                assert(u->meta.in_dbus_queue);
+                assert(meta->in_dbus_queue);
 
-                bus_unit_send_change_signal(u);
+                bus_unit_send_change_signal(UNIT(meta));
                 n++;
         }
 
@@ -1338,8 +1414,6 @@ unsigned manager_dispatch_dbus_queue(Manager *m) {
 static int manager_dispatch_sigchld(Manager *m) {
         assert(m);
 
-        log_debug("dispatching SIGCHLD");
-
         for (;;) {
                 siginfo_t si;
                 Unit *u;
@@ -1364,6 +1438,8 @@ static int manager_dispatch_sigchld(Manager *m) {
                 if (!(u = hashmap_remove(m->watch_pids, UINT32_TO_PTR(si.si_pid))))
                         continue;
 
+                log_debug("child %llu belongs to %s", (long long unsigned) si.si_pid, unit_id(u));
+
                 UNIT_VTABLE(u)->sigchld_event(u, si.si_pid, si.si_code, si.si_status);
         }
 
@@ -1391,9 +1467,16 @@ static int manager_process_signal_fd(Manager *m, bool *quit) {
 
                 switch (sfsi.ssi_signo) {
 
-                case SIGCHLD:
+                case SIGCHLD: {
+                        char *name = NULL;
+
+                        get_process_name(sfsi.ssi_pid, &name);
+                        log_debug("Got SIGCHLD for process %llu (%s)", (unsigned long long) sfsi.ssi_pid, strna(name));
+                        free(name);
+
                         sigchld = true;
                         break;
+                }
 
                 case SIGINT:
                 case SIGTERM:
@@ -1432,6 +1515,16 @@ static int manager_process_signal_fd(Manager *m, bool *quit) {
 
                         break;
 
+                case SIGUSR1:
+
+                        printf("→ By units:\n");
+                        manager_dump_units(m, stdout, "\t");
+
+                        printf("→ By jobs:\n");
+                        manager_dump_jobs(m, stdout, "\t");
+
+                        break;
+
                 default:
                         log_info("Got unhandled signal <%s>.", strsignal(sfsi.ssi_signo));
                 }
@@ -1521,7 +1614,7 @@ int manager_loop(Manager *m) {
 
         assert(m);
 
-        for (;;) {
+        do {
                 struct epoll_event event;
                 int n;
 
@@ -1531,6 +1624,9 @@ int manager_loop(Manager *m) {
                         sleep(1);
                 }
 
+                if (manager_dispatch_cleanup_queue(m) > 0)
+                        continue;
+
                 if (manager_dispatch_load_queue(m) > 0)
                         continue;
 
@@ -1555,10 +1651,9 @@ int manager_loop(Manager *m) {
 
                 if ((r = process_event(m, &event, &quit)) < 0)
                         return r;
+        } while (!quit);
 
-                if (quit)
-                        return 0;
-        }
+        return 0;
 }
 
 int manager_get_unit_from_dbus_path(Manager *m, const char *s, Unit **_u) {