chiark / gitweb /
job: the status messages are proper sentences, hence end them with a full stop
[elogind.git] / src / core / job.c
index 5ea717eae1e58b421f07fc8e905865c6d065f110..07d4fc3cc0fc236d136d047c2513db24c9de34d7 100644 (file)
 #include "log.h"
 #include "dbus-job.h"
 
-Job* job_new(Manager *m, JobType type, Unit *unit) {
+JobBusClient* job_bus_client_new(DBusConnection *connection, const char *name) {
+        JobBusClient *cl;
+        size_t name_len;
+
+        name_len = strlen(name);
+        cl = malloc0(sizeof(JobBusClient) + name_len + 1);
+        if (!cl)
+                return NULL;
+
+        cl->bus = connection;
+        memcpy(cl->name, name, name_len + 1);
+        return cl;
+}
+
+Job* job_new(Unit *unit, JobType type) {
         Job *j;
 
-        assert(m);
         assert(type < _JOB_TYPE_MAX);
         assert(unit);
 
         if (!(j = new0(Job, 1)))
                 return NULL;
 
-        j->manager = m;
-        j->id = m->current_job_id++;
+        j->manager = unit->manager;
+        j->id = j->manager->current_job_id++;
         j->type = type;
         j->unit = unit;
 
@@ -56,21 +69,10 @@ Job* job_new(Manager *m, JobType type, Unit *unit) {
 }
 
 void job_free(Job *j) {
-        assert(j);
-
-        /* Detach from next 'bigger' objects */
-        if (j->installed) {
-                bus_job_send_removed_signal(j);
-
-                if (j->unit->job == j) {
-                        j->unit->job = NULL;
-                        unit_add_to_gc_queue(j->unit);
-                }
-
-                hashmap_remove(j->manager->jobs, UINT32_TO_PTR(j->id));
-                j->installed = false;
-        }
+        JobBusClient *cl;
 
+        assert(j);
+        assert(!j->installed);
         assert(!j->transaction_prev);
         assert(!j->transaction_next);
         assert(!j->subject_list);
@@ -91,10 +93,93 @@ void job_free(Job *j) {
                 close_nointr_nofail(j->timer_watch.fd);
         }
 
-        free(j->bus_client);
+        while ((cl = j->bus_client_list)) {
+                LIST_REMOVE(JobBusClient, client, j->bus_client_list, cl);
+                free(cl);
+        }
         free(j);
 }
 
+void job_uninstall(Job *j) {
+        assert(j->installed);
+        assert(j->unit->job == j);
+        /* Detach from next 'bigger' objects */
+
+        bus_job_send_removed_signal(j);
+
+        j->unit->job = NULL;
+        unit_add_to_gc_queue(j->unit);
+
+        hashmap_remove(j->manager->jobs, UINT32_TO_PTR(j->id));
+        j->installed = false;
+}
+
+static bool job_type_allows_late_merge(JobType t) {
+        /* Tells whether it is OK to merge a job of type 't' with an already
+         * running job.
+         * Reloads cannot be merged this way. Think of the sequence:
+         * 1. Reload of a daemon is in progress; the daemon has already loaded
+         *    its config file, but hasn't completed the reload operation yet.
+         * 2. Edit foo's config file.
+         * 3. Trigger another reload to have the daemon use the new config.
+         * Should the second reload job be merged into the first one, the daemon
+         * would not know about the new config.
+         * JOB_RESTART jobs on the other hand can be merged, because they get
+         * patched into JOB_START after stopping the unit. So if we see a
+         * JOB_RESTART running, it means the unit hasn't stopped yet and at
+         * this time the merge is still allowed. */
+        return !(t == JOB_RELOAD || t == JOB_RELOAD_OR_START);
+}
+
+static void job_merge_into_installed(Job *j, Job *other) {
+        assert(j->installed);
+        assert(j->unit == other->unit);
+
+        j->type = job_type_lookup_merge(j->type, other->type);
+        assert(j->type >= 0);
+
+        j->override = j->override || other->override;
+}
+
+Job* job_install(Job *j) {
+        Job *uj = j->unit->job;
+
+        assert(!j->installed);
+
+        if (uj) {
+                if (job_type_is_conflicting(uj->type, j->type))
+                        job_finish_and_invalidate(uj, JOB_CANCELED);
+                else {
+                        /* not conflicting, i.e. mergeable */
+
+                        if (uj->state == JOB_WAITING ||
+                            (job_type_allows_late_merge(j->type) && job_type_is_superset(uj->type, j->type))) {
+                                job_merge_into_installed(uj, j);
+                                log_debug("Merged into installed job %s/%s as %u",
+                                          uj->unit->id, job_type_to_string(uj->type), (unsigned) uj->id);
+                                return uj;
+                        } else {
+                                /* already running and not safe to merge into */
+                                /* Patch uj to become a merged job and re-run it. */
+                                /* XXX It should be safer to queue j to run after uj finishes, but it is
+                                 * not currently possible to have more than one installed job per unit. */
+                                job_merge_into_installed(uj, j);
+                                log_debug("Merged into running job, re-running: %s/%s as %u",
+                                          uj->unit->id, job_type_to_string(uj->type), (unsigned) uj->id);
+                                uj->state = JOB_WAITING;
+                                return uj;
+                        }
+                }
+        }
+
+        /* Install the job */
+        j->unit->job = j;
+        j->installed = true;
+        j->manager->n_installed_jobs ++;
+        log_debug("Installed new job %s/%s as %u", j->unit->id, job_type_to_string(j->type), (unsigned) j->id);
+        return j;
+}
+
 JobDependency* job_dependency_new(Job *subject, Job *object, bool matters, bool conflicts) {
         JobDependency *l;
 
@@ -115,8 +200,6 @@ JobDependency* job_dependency_new(Job *subject, Job *object, bool matters, bool
 
         if (subject)
                 LIST_PREPEND(JobDependency, subject, subject->subject_list, l);
-        else
-                LIST_PREPEND(JobDependency, subject, object->manager->transaction_anchor, l);
 
         LIST_PREPEND(JobDependency, object, object->object_list, l);
 
@@ -128,8 +211,6 @@ void job_dependency_free(JobDependency *l) {
 
         if (l->subject)
                 LIST_REMOVE(JobDependency, subject, l->subject->subject_list, l);
-        else
-                LIST_REMOVE(JobDependency, subject, l->object->manager->transaction_anchor, l);
 
         LIST_REMOVE(JobDependency, object, l->object->object_list, l);
 
@@ -154,18 +235,6 @@ void job_dump(Job *j, FILE*f, const char *prefix) {
                 prefix, yes_no(j->override));
 }
 
-bool job_is_anchor(Job *j) {
-        JobDependency *l;
-
-        assert(j);
-
-        LIST_FOREACH(object, l, j->object_list)
-                if (!l->subject)
-                        return true;
-
-        return false;
-}
-
 /*
  * Merging is commutative, so imagine the matrix as symmetric. We store only
  * its lower triangle to avoid duplication. We don't store the main diagonal,
@@ -420,20 +489,20 @@ static void job_print_status_message(Unit *u, JobType t, JobResult result) {
 
                 case JOB_DONE:
                         if (u->condition_result)
-                                unit_status_printf(u, ANSI_HIGHLIGHT_GREEN_ON "  OK  " ANSI_HIGHLIGHT_OFF, "Started %s", unit_description(u));
+                                unit_status_printf(u, ANSI_HIGHLIGHT_GREEN_ON "  OK  " ANSI_HIGHLIGHT_OFF, "Started %s.", unit_description(u));
                         break;
 
                 case JOB_FAILED:
-                        unit_status_printf(u, ANSI_HIGHLIGHT_RED_ON "FAILED" ANSI_HIGHLIGHT_OFF, "Failed to start %s", unit_description(u));
+                        unit_status_printf(u, ANSI_HIGHLIGHT_RED_ON "FAILED" ANSI_HIGHLIGHT_OFF, "Failed to start %s.", unit_description(u));
                         unit_status_printf(u, NULL, "See 'systemctl status %s' for details.", u->id);
                         break;
 
                 case JOB_DEPENDENCY:
-                        unit_status_printf(u, ANSI_HIGHLIGHT_RED_ON " ABORT" ANSI_HIGHLIGHT_OFF, "Dependency failed. Aborted start of %s", unit_description(u));
+                        unit_status_printf(u, ANSI_HIGHLIGHT_RED_ON " ABORT" ANSI_HIGHLIGHT_OFF, "Dependency failed. Aborted start of %s.", unit_description(u));
                         break;
 
                 case JOB_TIMEOUT:
-                        unit_status_printf(u, ANSI_HIGHLIGHT_RED_ON " TIME " ANSI_HIGHLIGHT_OFF, "Timed out starting %s", unit_description(u));
+                        unit_status_printf(u, ANSI_HIGHLIGHT_RED_ON " TIME " ANSI_HIGHLIGHT_OFF, "Timed out starting %s.", unit_description(u));
                         break;
 
                 default:
@@ -445,12 +514,12 @@ static void job_print_status_message(Unit *u, JobType t, JobResult result) {
                 switch (result) {
 
                 case JOB_TIMEOUT:
-                        unit_status_printf(u, ANSI_HIGHLIGHT_RED_ON " TIME " ANSI_HIGHLIGHT_OFF, "Timed out stopping %s", unit_description(u));
+                        unit_status_printf(u, ANSI_HIGHLIGHT_RED_ON " TIME " ANSI_HIGHLIGHT_OFF, "Timed out stopping %s.", unit_description(u));
                         break;
 
                 case JOB_DONE:
                 case JOB_FAILED:
-                        unit_status_printf(u, ANSI_HIGHLIGHT_GREEN_ON "  OK  " ANSI_HIGHLIGHT_OFF, "Stopped %s", unit_description(u));
+                        unit_status_printf(u, ANSI_HIGHLIGHT_GREEN_ON "  OK  " ANSI_HIGHLIGHT_OFF, "Stopped %s.", unit_description(u));
                         break;
 
                 default:
@@ -492,6 +561,7 @@ int job_finish_and_invalidate(Job *j, JobResult result) {
 
         u = j->unit;
         t = j->type;
+        job_uninstall(j);
         job_free(j);
 
         job_print_status_message(u, t, result);