chiark / gitweb /
Merge branch 'python-systemd-reader'
[elogind.git] / src / core / job.c
index 706910b695d46df65a7f87bbaf376afad6474d18..90de550016ff22cbf34600e66f597c72467c33b2 100644 (file)
@@ -34,6 +34,9 @@
 #include "load-dropin.h"
 #include "log.h"
 #include "dbus-job.h"
+#include "special.h"
+#include "sync.h"
+#include "virt.h"
 
 JobBusClient* job_bus_client_new(DBusConnection *connection, const char *name) {
         JobBusClient *cl;
@@ -166,6 +169,8 @@ static void job_merge_into_installed(Job *j, Job *other) {
                 assert(other->type == JOB_NOP);
 
         j->override = j->override || other->override;
+        j->irreversible = j->irreversible || other->irreversible;
+        j->ignore_order = j->ignore_order || other->ignore_order;
 }
 
 Job* job_install(Job *j) {
@@ -180,15 +185,16 @@ 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 */
 
                         if (j->type == JOB_NOP || 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);
+                                log_debug_unit(uj->unit->id,
+                                               "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 */
@@ -196,9 +202,11 @@ Job* job_install(Job *j) {
                                 /* 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);
+                                log_debug_unit(uj->unit->id,
+                                               "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;
+                                uj->manager->n_running_jobs--;
                                 return uj;
                         }
                 }
@@ -208,7 +216,9 @@ Job* job_install(Job *j) {
         *pj = 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);
+        log_debug_unit(j->unit->id,
+                       "Installed new job %s/%s as %u",
+                       j->unit->id, job_type_to_string(j->type), (unsigned) j->id);
         return j;
 }
 
@@ -225,12 +235,16 @@ int job_install_deserialized(Job *j) {
         pj = (j->type == JOB_NOP) ? &j->unit->nop_job : &j->unit->job;
 
         if (*pj) {
-                log_debug("Unit %s already has a job installed. Not installing deserialized job.", j->unit->id);
+                log_debug_unit(j->unit->id,
+                               "Unit %s already has a job installed. Not installing deserialized job.",
+                               j->unit->id);
                 return -EEXIST;
         }
         *pj = j;
         j->installed = true;
-        log_debug("Reinstalled deserialized job %s/%s as %u", j->unit->id, job_type_to_string(j->type), (unsigned) j->id);
+        log_debug_unit(j->unit->id,
+                       "Reinstalled deserialized job %s/%s as %u",
+                       j->unit->id, job_type_to_string(j->type), (unsigned) j->id);
         return 0;
 }
 
@@ -282,11 +296,13 @@ void job_dump(Job *j, FILE*f, const char *prefix) {
                 "%s-> Job %u:\n"
                 "%s\tAction: %s -> %s\n"
                 "%s\tState: %s\n"
-                "%s\tForced: %s\n",
+                "%s\tForced: %s\n"
+                "%s\tIrreversible: %s\n",
                 prefix, j->id,
                 prefix, j->unit->id, job_type_to_string(j->type),
                 prefix, job_state_to_string(j->state),
-                prefix, yes_no(j->override));
+                prefix, yes_no(j->override),
+                prefix, yes_no(j->irreversible));
 }
 
 /*
@@ -457,9 +473,10 @@ bool job_is_runnable(Job *j) {
 }
 
 static void job_change_type(Job *j, JobType newtype) {
-        log_debug("Converting job %s/%s -> %s/%s",
-                  j->unit->id, job_type_to_string(j->type),
-                  j->unit->id, job_type_to_string(newtype));
+        log_debug_unit(j->unit->id,
+                       "Converting job %s/%s -> %s/%s",
+                       j->unit->id, job_type_to_string(j->type),
+                       j->unit->id, job_type_to_string(newtype));
 
         j->type = newtype;
 }
@@ -467,7 +484,7 @@ static void job_change_type(Job *j, JobType newtype) {
 int job_run_and_invalidate(Job *j) {
         int r;
         uint32_t id;
-        Manager *m;
+        Manager *m = j->manager;
 
         assert(j);
         assert(j->installed);
@@ -484,6 +501,7 @@ int job_run_and_invalidate(Job *j) {
                 return -EAGAIN;
 
         j->state = JOB_RUNNING;
+        m->n_running_jobs++;
         job_add_to_dbus_queue(j);
 
         /* While we execute this operation the job might go away (for
@@ -492,7 +510,6 @@ int job_run_and_invalidate(Job *j) {
          * store the id here, so that we can verify the job is still
          * valid. */
         id = j->id;
-        m = j->manager;
 
         switch (j->type) {
 
@@ -542,9 +559,10 @@ int job_run_and_invalidate(Job *j) {
                         r = job_finish_and_invalidate(j, JOB_DONE, true);
                 else if (r == -ENOEXEC)
                         r = job_finish_and_invalidate(j, JOB_SKIPPED, true);
-                else if (r == -EAGAIN)
+                else if (r == -EAGAIN) {
                         j->state = JOB_WAITING;
-                else if (r < 0)
+                        m->n_running_jobs--;
+                } else if (r < 0)
                         r = job_finish_and_invalidate(j, JOB_FAILED, true);
         }
 
@@ -626,20 +644,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, format, unit_description(u));
+                                unit_status_printf(u, ANSI_HIGHLIGHT_GREEN_ON "  OK  " ANSI_HIGHLIGHT_OFF, format);
                         break;
 
                 case JOB_FAILED:
-                        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);
+                        unit_status_printf(u, ANSI_HIGHLIGHT_RED_ON "FAILED" ANSI_HIGHLIGHT_OFF, format);
+                        manager_status_printf(u->manager, false, NULL, "See 'systemctl status %s' for details.", u->id);
                         break;
 
                 case JOB_DEPENDENCY:
-                        unit_status_printf(u, ANSI_HIGHLIGHT_YELLOW_ON "DEPEND" ANSI_HIGHLIGHT_OFF, format, unit_description(u));
+                        unit_status_printf(u, ANSI_HIGHLIGHT_YELLOW_ON "DEPEND" ANSI_HIGHLIGHT_OFF, format);
                         break;
 
                 case JOB_TIMEOUT:
-                        unit_status_printf(u, ANSI_HIGHLIGHT_RED_ON " TIME " ANSI_HIGHLIGHT_OFF, format, unit_description(u));
+                        unit_status_printf(u, ANSI_HIGHLIGHT_RED_ON " TIME " ANSI_HIGHLIGHT_OFF, format);
                         break;
 
                 default:
@@ -655,12 +673,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, format, unit_description(u));
+                        unit_status_printf(u, ANSI_HIGHLIGHT_RED_ON " TIME " ANSI_HIGHLIGHT_OFF, format);
                         break;
 
                 case JOB_DONE:
                 case JOB_FAILED:
-                        unit_status_printf(u, ANSI_HIGHLIGHT_GREEN_ON "  OK  " ANSI_HIGHLIGHT_OFF, format, unit_description(u));
+                        unit_status_printf(u, ANSI_HIGHLIGHT_GREEN_ON "  OK  " ANSI_HIGHLIGHT_OFF, format);
                         break;
 
                 default:
@@ -673,7 +691,7 @@ static void job_print_status_message(Unit *u, JobType t, JobResult result) {
                  * 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));
+                        unit_status_printf(u, ANSI_HIGHLIGHT_ON " INFO " ANSI_HIGHLIGHT_OFF, "%s is not active.");
         }
 }
 
@@ -704,25 +722,25 @@ static void job_log_status_message(Unit *u, JobType t, JobResult result) {
                 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,
-                           "MESSSAGE_ID=" SD_ID128_FORMAT_STR, SD_ID128_FORMAT_VAL(mid),
-                           "UNIT=%s", u->id,
+                log_struct_unit(result == JOB_DONE ? LOG_INFO : LOG_ERR,
+                           u->id,
+                           MESSAGE_ID(mid),
                            "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,
-                           "MESSSAGE_ID=" SD_ID128_FORMAT_STR, SD_ID128_FORMAT_VAL(SD_MESSAGE_UNIT_STOPPED),
-                           "UNIT=%s", u->id,
+                log_struct_unit(result == JOB_DONE ? LOG_INFO : LOG_ERR,
+                           u->id,
+                           MESSAGE_ID(SD_MESSAGE_UNIT_STOPPED),
                            "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,
-                           "MESSSAGE_ID=" SD_ID128_FORMAT_STR, SD_ID128_FORMAT_VAL(SD_MESSAGE_UNIT_RELOADED),
-                           "UNIT=%s", u->id,
+                log_struct_unit(result == JOB_DONE ? LOG_INFO : LOG_ERR,
+                           u->id,
+                           MESSAGE_ID(SD_MESSAGE_UNIT_RELOADED),
                            "RESULT=%s", job_result_to_string(result),
                            "MESSAGE=%s", buf,
                            NULL);
@@ -744,7 +762,11 @@ int job_finish_and_invalidate(Job *j, JobResult result, bool recursive) {
 
         j->result = result;
 
-        log_debug("Job %s/%s finished, result=%s", u->id, job_type_to_string(t), job_result_to_string(result));
+        if (j->state == JOB_RUNNING)
+                j->manager->n_running_jobs--;
+
+        log_debug_unit(u->id, "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);
@@ -804,14 +826,19 @@ int job_finish_and_invalidate(Job *j, JobResult result, bool recursive) {
         }
 
         /* Trigger OnFailure dependencies that are not generated by
-         * the unit itself. We don't tread JOB_CANCELED as failure in
+         * the unit itself. We don't treat JOB_CANCELED as failure in
          * this context. And JOB_FAILURE is already handled by the
          * unit itself. */
         if (result == JOB_TIMEOUT || result == JOB_DEPENDENCY) {
-                log_notice("Job %s/%s failed with result '%s'.",
+                log_struct_unit(LOG_NOTICE,
+                           u->id,
+                           "JOB_TYPE=%s", job_type_to_string(t),
+                           "JOB_RESULT=%s", job_result_to_string(result),
+                           "Job %s/%s failed with result '%s'.",
                            u->id,
                            job_type_to_string(t),
-                           job_result_to_string(result));
+                           job_result_to_string(result),
+                           NULL);
 
                 unit_trigger_on_failure(u);
         }
@@ -918,7 +945,8 @@ void job_timer_event(Job *j, uint64_t n_elapsed, Watch *w) {
         assert(j);
         assert(w == &j->timer_watch);
 
-        log_warning("Job %s/%s timed out.", j->unit->id, job_type_to_string(j->type));
+        log_warning_unit(j->unit->id, "Job %s/%s timed out.",
+                         j->unit->id, job_type_to_string(j->type));
         job_finish_and_invalidate(j, JOB_TIMEOUT, true);
 }
 
@@ -927,6 +955,7 @@ int job_serialize(Job *j, FILE *f, FDSet *fds) {
         fprintf(f, "job-type=%s\n", job_type_to_string(j->type));
         fprintf(f, "job-state=%s\n", job_state_to_string(j->state));
         fprintf(f, "job-override=%s\n", yes_no(j->override));
+        fprintf(f, "job-irreversible=%s\n", yes_no(j->irreversible));
         fprintf(f, "job-sent-dbus-new-signal=%s\n", yes_no(j->sent_dbus_new_signal));
         fprintf(f, "job-ignore-order=%s\n", yes_no(j->ignore_order));
         /* Cannot save bus clients. Just note the fact that we're losing
@@ -994,6 +1023,12 @@ int job_deserialize(Job *j, FILE *f, FDSet *fds) {
                                 log_debug("Failed to parse job override flag %s", v);
                         else
                                 j->override = j->override || b;
+                } else if (streq(l, "job-irreversible")) {
+                        int b = parse_boolean(v);
+                        if (b < 0)
+                                log_debug("Failed to parse job irreversible flag %s", v);
+                        else
+                                j->irreversible = j->irreversible || b;
                 } else if (streq(l, "job-sent-dbus-new-signal")) {
                         int b = parse_boolean(v);
                         if (b < 0)
@@ -1044,6 +1079,29 @@ int job_coldplug(Job *j) {
         return 0;
 }
 
+void job_shutdown_magic(Job *j) {
+        assert(j);
+
+        /* The shutdown target gets some special treatment here: we
+         * tell the kernel to begin with flushing its disk caches, to
+         * optimize shutdown time a bit. Ideally we wouldn't hardcode
+         * this magic into PID 1. However all other processes aren't
+         * options either since they'd exit much sooner than PID 1 and
+         * asynchronous sync() would cause their exit to be
+         * delayed. */
+
+        if (!unit_has_name(j->unit, SPECIAL_SHUTDOWN_TARGET))
+                return;
+
+        if (j->type != JOB_START)
+                return;
+
+        if (detect_container(NULL) > 0)
+                return;
+
+        asynchronous_sync();
+}
+
 static const char* const job_state_table[_JOB_STATE_MAX] = {
         [JOB_WAITING] = "waiting",
         [JOB_RUNNING] = "running"
@@ -1067,6 +1125,7 @@ DEFINE_STRING_TABLE_LOOKUP(job_type, JobType);
 static const char* const job_mode_table[_JOB_MODE_MAX] = {
         [JOB_FAIL] = "fail",
         [JOB_REPLACE] = "replace",
+        [JOB_REPLACE_IRREVERSIBLY] = "replace-irreversibly",
         [JOB_ISOLATE] = "isolate",
         [JOB_IGNORE_DEPENDENCIES] = "ignore-dependencies",
         [JOB_IGNORE_REQUIREMENTS] = "ignore-requirements"