chiark / gitweb /
job: avoid recursion into transaction code from job cancelation
[elogind.git] / src / core / job.c
index 301d83a9dc1b51fbc1b1a01916c0e64f18c00c0e..f08b8cbc7db3d6dfed8358926234cedb8128d05f 100644 (file)
@@ -24,6 +24,8 @@
 #include <sys/timerfd.h>
 #include <sys/epoll.h>
 
+#include "systemd/sd-id128.h"
+#include "systemd/sd-messages.h"
 #include "set.h"
 #include "unit.h"
 #include "macro.h"
@@ -178,7 +180,7 @@ Job* job_install(Job *j) {
 
         if (uj) {
                 if (j->type != JOB_NOP && job_type_is_conflicting(uj->type, j->type))
-                        job_finish_and_invalidate(uj, JOB_CANCELED, true);
+                        job_finish_and_invalidate(uj, JOB_CANCELED, false);
                 else {
                         /* not conflicting, i.e. mergeable */
 
@@ -470,11 +472,10 @@ int job_run_and_invalidate(Job *j) {
         assert(j);
         assert(j->installed);
         assert(j->type < _JOB_TYPE_MAX_IN_TRANSACTION);
+        assert(j->in_run_queue);
 
-        if (j->in_run_queue) {
-                LIST_REMOVE(Job, run_queue, j->manager->run_queue, j);
-                j->in_run_queue = false;
-        }
+        LIST_REMOVE(Job, run_queue, j->manager->run_queue, j);
+        j->in_run_queue = false;
 
         if (j->state != JOB_WAITING)
                 return 0;
@@ -550,54 +551,184 @@ int job_run_and_invalidate(Job *j) {
         return r;
 }
 
+static const char *job_get_status_message_format(Unit *u, JobType t, JobResult result) {
+        const UnitStatusMessageFormats *format_table;
+
+        assert(u);
+        assert(t >= 0);
+        assert(t < _JOB_TYPE_MAX);
+
+        format_table = &UNIT_VTABLE(u)->status_message_formats;
+        if (!format_table)
+                return NULL;
+
+        if (t == JOB_START)
+                return format_table->finished_start_job[result];
+        else if (t == JOB_STOP || t == JOB_RESTART)
+                return format_table->finished_stop_job[result];
+
+        return NULL;
+}
+
+static const char *job_get_status_message_format_try_harder(Unit *u, JobType t, JobResult result) {
+        const char *format;
+
+        assert(u);
+        assert(t >= 0);
+        assert(t < _JOB_TYPE_MAX);
+
+        format = job_get_status_message_format(u, t, result);
+        if (format)
+                return format;
+
+        /* Return generic strings */
+        if (t == JOB_START) {
+                if (result == JOB_DONE)
+                        return "Started %s.";
+                else if (result == JOB_FAILED)
+                        return "Failed to start %s.";
+                else if (result == JOB_DEPENDENCY)
+                        return "Dependency failed for %s.";
+                else if (result == JOB_TIMEOUT)
+                        return "Timed out starting %s.";
+        } else if (t == JOB_STOP || t == JOB_RESTART) {
+                if (result == JOB_DONE)
+                        return "Stopped %s.";
+                else if (result == JOB_FAILED)
+                        return "Stopped (with error) %s.";
+                else if (result == JOB_TIMEOUT)
+                        return "Timed out stoppping %s.";
+        } else if (t == JOB_RELOAD) {
+                if (result == JOB_DONE)
+                        return "Reloaded %s.";
+                else if (result == JOB_FAILED)
+                        return "Reload failed for %s.";
+                else if (result == JOB_TIMEOUT)
+                        return "Timed out reloading %s.";
+        }
+
+        return NULL;
+}
+
 static void job_print_status_message(Unit *u, JobType t, JobResult result) {
+        const char *format;
+
         assert(u);
+        assert(t >= 0);
+        assert(t < _JOB_TYPE_MAX);
 
         if (t == JOB_START) {
+                format = job_get_status_message_format(u, t, result);
+                if (!format)
+                        return;
 
                 switch (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, format, 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, format, 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_YELLOW_ON "DEPEND" ANSI_HIGHLIGHT_OFF, format, 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, format, unit_description(u));
                         break;
 
                 default:
                         ;
                 }
 
-        } else if (t == JOB_STOP) {
+        } else if (t == JOB_STOP || t == JOB_RESTART) {
+
+                format = job_get_status_message_format(u, t, result);
+                if (!format)
+                        return;
 
                 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, format, 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, format, unit_description(u));
                         break;
 
                 default:
                         ;
                 }
+
+        } else if (t == JOB_VERIFY_ACTIVE) {
+
+                /* When verify-active detects the unit is inactive, report it.
+                 * Most likely a DEPEND warning from a requisiting unit will
+                 * occur next and it's nice to see what was requisited. */
+                if (result == JOB_SKIPPED)
+                        unit_status_printf(u, ANSI_HIGHLIGHT_ON " INFO " ANSI_HIGHLIGHT_OFF, "%s is not active.", unit_description(u));
         }
 }
 
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wformat-nonliteral"
+static void job_log_status_message(Unit *u, JobType t, JobResult result) {
+        const char *format;
+        char buf[LINE_MAX];
+
+        assert(u);
+        assert(t >= 0);
+        assert(t < _JOB_TYPE_MAX);
+
+        /* Skip this if it goes to the console. since we already print
+         * to the console anyway... */
+
+        if (log_on_console())
+                return;
+
+        format = job_get_status_message_format_try_harder(u, t, result);
+        if (!format)
+                return;
+
+        snprintf(buf, sizeof(buf), format, unit_description(u));
+        char_array_0(buf);
+
+        if (t == JOB_START) {
+                sd_id128_t mid;
+
+                mid = result == JOB_DONE ? SD_MESSAGE_UNIT_STARTED : SD_MESSAGE_UNIT_FAILED;
+                log_struct(result == JOB_DONE ? LOG_INFO : LOG_ERR,
+                           MESSAGE_ID(mid),
+                           "UNIT=%s", u->id,
+                           "RESULT=%s", job_result_to_string(result),
+                           "MESSAGE=%s", buf,
+                           NULL);
+
+        } else if (t == JOB_STOP)
+                log_struct(result == JOB_DONE ? LOG_INFO : LOG_ERR,
+                           MESSAGE_ID(SD_MESSAGE_UNIT_STOPPED),
+                           "UNIT=%s", u->id,
+                           "RESULT=%s", job_result_to_string(result),
+                           "MESSAGE=%s", buf,
+                           NULL);
+
+        else if (t == JOB_RELOAD)
+                log_struct(result == JOB_DONE ? LOG_INFO : LOG_ERR,
+                           MESSAGE_ID(SD_MESSAGE_UNIT_RELOADED),
+                           "UNIT=%s", u->id,
+                           "RESULT=%s", job_result_to_string(result),
+                           "MESSAGE=%s", buf,
+                           NULL);
+}
+#pragma GCC diagnostic pop
+
 int job_finish_and_invalidate(Job *j, JobResult result, bool recursive) {
         Unit *u;
         Unit *other;
@@ -608,34 +739,35 @@ int job_finish_and_invalidate(Job *j, JobResult result, bool recursive) {
         assert(j->installed);
         assert(j->type < _JOB_TYPE_MAX_IN_TRANSACTION);
 
+        u = j->unit;
+        t = j->type;
+
+        j->result = result;
+
+        log_debug("Job %s/%s finished, result=%s", u->id, job_type_to_string(t), job_result_to_string(result));
+
+        job_print_status_message(u, t, result);
+        job_log_status_message(u, t, result);
+
         job_add_to_dbus_queue(j);
 
         /* Patch restart jobs so that they become normal start jobs */
-        if (result == JOB_DONE && j->type == JOB_RESTART) {
+        if (result == JOB_DONE && t == JOB_RESTART) {
 
                 job_change_type(j, JOB_START);
                 j->state = JOB_WAITING;
 
                 job_add_to_run_queue(j);
 
-                u = j->unit;
                 goto finish;
         }
 
-        j->result = result;
-
-        log_debug("Job %s/%s finished, result=%s", j->unit->id, job_type_to_string(j->type), job_result_to_string(result));
-
         if (result == JOB_FAILED)
                 j->manager->n_failed_jobs ++;
 
-        u = j->unit;
-        t = j->type;
         job_uninstall(j);
         job_free(j);
 
-        job_print_status_message(u, t, result);
-
         /* Fail depending jobs on failure */
         if (result != JOB_DONE && recursive) {