LCOV - code coverage report
Current view: top level - core - job.c (source / functions) Hit Total Coverage
Test: main_coverage.info Lines: 369 783 47.1 %
Date: 2019-08-22 15:41:25 Functions: 42 57 73.7 %

          Line data    Source code
       1             : /* SPDX-License-Identifier: LGPL-2.1+ */
       2             : 
       3             : #include <errno.h>
       4             : 
       5             : #include "sd-id128.h"
       6             : #include "sd-messages.h"
       7             : 
       8             : #include "alloc-util.h"
       9             : #include "async.h"
      10             : #include "cgroup.h"
      11             : #include "dbus-job.h"
      12             : #include "dbus.h"
      13             : #include "escape.h"
      14             : #include "fileio.h"
      15             : #include "job.h"
      16             : #include "log.h"
      17             : #include "macro.h"
      18             : #include "parse-util.h"
      19             : #include "serialize.h"
      20             : #include "set.h"
      21             : #include "sort-util.h"
      22             : #include "special.h"
      23             : #include "stdio-util.h"
      24             : #include "string-table.h"
      25             : #include "string-util.h"
      26             : #include "strv.h"
      27             : #include "terminal-util.h"
      28             : #include "unit.h"
      29             : #include "virt.h"
      30             : 
      31         182 : Job* job_new_raw(Unit *unit) {
      32             :         Job *j;
      33             : 
      34             :         /* used for deserialization */
      35             : 
      36         182 :         assert(unit);
      37             : 
      38         182 :         j = new(Job, 1);
      39         182 :         if (!j)
      40           0 :                 return NULL;
      41             : 
      42         182 :         *j = (Job) {
      43         182 :                 .manager = unit->manager,
      44             :                 .unit = unit,
      45             :                 .type = _JOB_TYPE_INVALID,
      46             :         };
      47             : 
      48         182 :         return j;
      49             : }
      50             : 
      51         182 : Job* job_new(Unit *unit, JobType type) {
      52             :         Job *j;
      53             : 
      54         182 :         assert(type < _JOB_TYPE_MAX);
      55             : 
      56         182 :         j = job_new_raw(unit);
      57         182 :         if (!j)
      58           0 :                 return NULL;
      59             : 
      60         182 :         j->id = j->manager->current_job_id++;
      61         182 :         j->type = type;
      62             : 
      63             :         /* We don't link it here, that's what job_dependency() is for */
      64             : 
      65         182 :         return j;
      66             : }
      67             : 
      68         182 : void job_unlink(Job *j) {
      69         182 :         assert(j);
      70         182 :         assert(!j->installed);
      71         182 :         assert(!j->transaction_prev);
      72         182 :         assert(!j->transaction_next);
      73         182 :         assert(!j->subject_list);
      74         182 :         assert(!j->object_list);
      75             : 
      76         182 :         if (j->in_run_queue) {
      77          26 :                 prioq_remove(j->manager->run_queue, j, &j->run_queue_idx);
      78          26 :                 j->in_run_queue = false;
      79             :         }
      80             : 
      81         182 :         if (j->in_dbus_queue) {
      82           0 :                 LIST_REMOVE(dbus_queue, j->manager->dbus_job_queue, j);
      83           0 :                 j->in_dbus_queue = false;
      84             :         }
      85             : 
      86         182 :         if (j->in_gc_queue) {
      87           0 :                 LIST_REMOVE(gc_queue, j->manager->gc_job_queue, j);
      88           0 :                 j->in_gc_queue = false;
      89             :         }
      90             : 
      91         182 :         j->timer_event_source = sd_event_source_unref(j->timer_event_source);
      92         182 : }
      93             : 
      94         182 : Job* job_free(Job *j) {
      95         182 :         assert(j);
      96         182 :         assert(!j->installed);
      97         182 :         assert(!j->transaction_prev);
      98         182 :         assert(!j->transaction_next);
      99         182 :         assert(!j->subject_list);
     100         182 :         assert(!j->object_list);
     101             : 
     102         182 :         job_unlink(j);
     103             : 
     104         182 :         sd_bus_track_unref(j->bus_track);
     105         182 :         strv_free(j->deserialized_clients);
     106             : 
     107         182 :         return mfree(j);
     108             : }
     109             : 
     110          74 : static void job_set_state(Job *j, JobState state) {
     111          74 :         assert(j);
     112          74 :         assert(state >= 0);
     113          74 :         assert(state < _JOB_STATE_MAX);
     114             : 
     115          74 :         if (j->state == state)
     116          26 :                 return;
     117             : 
     118          48 :         j->state = state;
     119             : 
     120          48 :         if (!j->installed)
     121           0 :                 return;
     122             : 
     123          48 :         if (j->state == JOB_RUNNING)
     124          24 :                 j->unit->manager->n_running_jobs++;
     125             :         else {
     126          24 :                 assert(j->state == JOB_WAITING);
     127          24 :                 assert(j->unit->manager->n_running_jobs > 0);
     128             : 
     129          24 :                 j->unit->manager->n_running_jobs--;
     130             : 
     131          24 :                 if (j->unit->manager->n_running_jobs <= 0)
     132          24 :                         j->unit->manager->jobs_in_progress_event_source = sd_event_source_unref(j->unit->manager->jobs_in_progress_event_source);
     133             :         }
     134             : }
     135             : 
     136          50 : void job_uninstall(Job *j) {
     137             :         Job **pj;
     138             : 
     139          50 :         assert(j->installed);
     140             : 
     141          50 :         job_set_state(j, JOB_WAITING);
     142             : 
     143          50 :         pj = (j->type == JOB_NOP) ? &j->unit->nop_job : &j->unit->job;
     144          50 :         assert(*pj == j);
     145             : 
     146             :         /* Detach from next 'bigger' objects */
     147             : 
     148             :         /* daemon-reload should be transparent to job observers */
     149          50 :         if (!MANAGER_IS_RELOADING(j->manager))
     150          50 :                 bus_job_send_removed_signal(j);
     151             : 
     152          50 :         *pj = NULL;
     153             : 
     154          50 :         unit_add_to_gc_queue(j->unit);
     155             : 
     156          50 :         unit_add_to_dbus_queue(j->unit); /* The Job property of the unit has changed now */
     157             : 
     158          50 :         hashmap_remove_value(j->manager->jobs, UINT32_TO_PTR(j->id), j);
     159          50 :         j->installed = false;
     160          50 : }
     161             : 
     162           0 : static bool job_type_allows_late_merge(JobType t) {
     163             :         /* Tells whether it is OK to merge a job of type 't' with an already
     164             :          * running job.
     165             :          * Reloads cannot be merged this way. Think of the sequence:
     166             :          * 1. Reload of a daemon is in progress; the daemon has already loaded
     167             :          *    its config file, but hasn't completed the reload operation yet.
     168             :          * 2. Edit foo's config file.
     169             :          * 3. Trigger another reload to have the daemon use the new config.
     170             :          * Should the second reload job be merged into the first one, the daemon
     171             :          * would not know about the new config.
     172             :          * JOB_RESTART jobs on the other hand can be merged, because they get
     173             :          * patched into JOB_START after stopping the unit. So if we see a
     174             :          * JOB_RESTART running, it means the unit hasn't stopped yet and at
     175             :          * this time the merge is still allowed. */
     176           0 :         return t != JOB_RELOAD;
     177             : }
     178             : 
     179          18 : static void job_merge_into_installed(Job *j, Job *other) {
     180          18 :         assert(j->installed);
     181          18 :         assert(j->unit == other->unit);
     182             : 
     183          18 :         if (j->type != JOB_NOP)
     184          18 :                 assert_se(job_type_merge_and_collapse(&j->type, other->type, j->unit) == 0);
     185             :         else
     186           0 :                 assert(other->type == JOB_NOP);
     187             : 
     188          18 :         j->irreversible = j->irreversible || other->irreversible;
     189          18 :         j->ignore_order = j->ignore_order || other->ignore_order;
     190          18 : }
     191             : 
     192          68 : Job* job_install(Job *j) {
     193             :         Job **pj;
     194             :         Job *uj;
     195             : 
     196          68 :         assert(!j->installed);
     197          68 :         assert(j->type < _JOB_TYPE_MAX_IN_TRANSACTION);
     198          68 :         assert(j->state == JOB_WAITING);
     199             : 
     200          68 :         pj = (j->type == JOB_NOP) ? &j->unit->nop_job : &j->unit->job;
     201          68 :         uj = *pj;
     202             : 
     203          68 :         if (uj) {
     204          20 :                 if (job_type_is_conflicting(uj->type, j->type))
     205           2 :                         job_finish_and_invalidate(uj, JOB_CANCELED, false, false);
     206             :                 else {
     207             :                         /* not conflicting, i.e. mergeable */
     208             : 
     209          18 :                         if (uj->state == JOB_WAITING ||
     210           0 :                             (job_type_allows_late_merge(j->type) && job_type_is_superset(uj->type, j->type))) {
     211          18 :                                 job_merge_into_installed(uj, j);
     212          18 :                                 log_unit_debug(uj->unit,
     213             :                                                "Merged %s/%s into installed job %s/%s as %"PRIu32,
     214             :                                                j->unit->id, job_type_to_string(j->type), uj->unit->id,
     215             :                                                job_type_to_string(uj->type), uj->id);
     216          18 :                                 return uj;
     217             :                         } else {
     218             :                                 /* already running and not safe to merge into */
     219             :                                 /* Patch uj to become a merged job and re-run it. */
     220             :                                 /* XXX It should be safer to queue j to run after uj finishes, but it is
     221             :                                  * not currently possible to have more than one installed job per unit. */
     222           0 :                                 job_merge_into_installed(uj, j);
     223           0 :                                 log_unit_debug(uj->unit,
     224             :                                                "Merged into running job, re-running: %s/%s as %"PRIu32,
     225             :                                                uj->unit->id, job_type_to_string(uj->type), uj->id);
     226             : 
     227           0 :                                 job_set_state(uj, JOB_WAITING);
     228           0 :                                 return uj;
     229             :                         }
     230             :                 }
     231             :         }
     232             : 
     233             :         /* Install the job */
     234          50 :         *pj = j;
     235          50 :         j->installed = true;
     236             : 
     237          50 :         j->manager->n_installed_jobs++;
     238          50 :         log_unit_debug(j->unit,
     239             :                        "Installed new job %s/%s as %u",
     240             :                        j->unit->id, job_type_to_string(j->type), (unsigned) j->id);
     241             : 
     242          50 :         job_add_to_gc_queue(j);
     243             : 
     244          50 :         job_add_to_dbus_queue(j); /* announce this job to clients */
     245          50 :         unit_add_to_dbus_queue(j->unit); /* The Job property of the unit has changed now */
     246             : 
     247          50 :         return j;
     248             : }
     249             : 
     250           0 : int job_install_deserialized(Job *j) {
     251             :         Job **pj;
     252             :         int r;
     253             : 
     254           0 :         assert(!j->installed);
     255             : 
     256           0 :         if (j->type < 0 || j->type >= _JOB_TYPE_MAX_IN_TRANSACTION)
     257           0 :                 return log_unit_debug_errno(j->unit, SYNTHETIC_ERRNO(EINVAL),
     258             :                                        "Invalid job type %s in deserialization.",
     259             :                                        strna(job_type_to_string(j->type)));
     260             : 
     261           0 :         pj = (j->type == JOB_NOP) ? &j->unit->nop_job : &j->unit->job;
     262           0 :         if (*pj)
     263           0 :                 return log_unit_debug_errno(j->unit, SYNTHETIC_ERRNO(EEXIST),
     264             :                                             "Unit already has a job installed. Not installing deserialized job.");
     265             : 
     266           0 :         r = hashmap_put(j->manager->jobs, UINT32_TO_PTR(j->id), j);
     267           0 :         if (r == -EEXIST)
     268           0 :                 return log_unit_debug_errno(j->unit, r, "Job ID %" PRIu32 " already used, cannot deserialize job.", j->id);
     269           0 :         if (r < 0)
     270           0 :                 return log_unit_debug_errno(j->unit, r, "Failed to insert job into jobs hash table: %m");
     271             : 
     272           0 :         *pj = j;
     273           0 :         j->installed = true;
     274             : 
     275           0 :         if (j->state == JOB_RUNNING)
     276           0 :                 j->unit->manager->n_running_jobs++;
     277             : 
     278           0 :         log_unit_debug(j->unit,
     279             :                        "Reinstalled deserialized job %s/%s as %u",
     280             :                        j->unit->id, job_type_to_string(j->type), (unsigned) j->id);
     281           0 :         return 0;
     282             : }
     283             : 
     284         293 : JobDependency* job_dependency_new(Job *subject, Job *object, bool matters, bool conflicts) {
     285             :         JobDependency *l;
     286             : 
     287         293 :         assert(object);
     288             : 
     289             :         /* Adds a new job link, which encodes that the 'subject' job
     290             :          * needs the 'object' job in some way. If 'subject' is NULL
     291             :          * this means the 'anchor' job (i.e. the one the user
     292             :          * explicitly asked for) is the requester. */
     293             : 
     294         293 :         l = new0(JobDependency, 1);
     295         293 :         if (!l)
     296           0 :                 return NULL;
     297             : 
     298         293 :         l->subject = subject;
     299         293 :         l->object = object;
     300         293 :         l->matters = matters;
     301         293 :         l->conflicts = conflicts;
     302             : 
     303         293 :         if (subject)
     304         293 :                 LIST_PREPEND(subject, subject->subject_list, l);
     305             : 
     306         293 :         LIST_PREPEND(object, object->object_list, l);
     307             : 
     308         293 :         return l;
     309             : }
     310             : 
     311         293 : void job_dependency_free(JobDependency *l) {
     312         293 :         assert(l);
     313             : 
     314         293 :         if (l->subject)
     315         293 :                 LIST_REMOVE(subject, l->subject->subject_list, l);
     316             : 
     317         293 :         LIST_REMOVE(object, l->object->object_list, l);
     318             : 
     319         293 :         free(l);
     320         293 : }
     321             : 
     322          63 : void job_dump(Job *j, FILE *f, const char *prefix) {
     323          63 :         assert(j);
     324          63 :         assert(f);
     325             : 
     326          63 :         prefix = strempty(prefix);
     327             : 
     328          63 :         fprintf(f,
     329             :                 "%s-> Job %u:\n"
     330             :                 "%s\tAction: %s -> %s\n"
     331             :                 "%s\tState: %s\n"
     332             :                 "%s\tIrreversible: %s\n"
     333             :                 "%s\tMay GC: %s\n",
     334             :                 prefix, j->id,
     335          63 :                 prefix, j->unit->id, job_type_to_string(j->type),
     336             :                 prefix, job_state_to_string(j->state),
     337          63 :                 prefix, yes_no(j->irreversible),
     338          63 :                 prefix, yes_no(job_may_gc(j)));
     339          63 : }
     340             : 
     341             : /*
     342             :  * Merging is commutative, so imagine the matrix as symmetric. We store only
     343             :  * its lower triangle to avoid duplication. We don't store the main diagonal,
     344             :  * because A merged with A is simply A.
     345             :  *
     346             :  * If the resulting type is collapsed immediately afterwards (to get rid of
     347             :  * the JOB_RELOAD_OR_START, which lies outside the lookup function's domain),
     348             :  * the following properties hold:
     349             :  *
     350             :  * Merging is associative! A merged with B, and then merged with C is the same
     351             :  * as A merged with the result of B merged with C.
     352             :  *
     353             :  * Mergeability is transitive! If A can be merged with B and B with C then
     354             :  * A also with C.
     355             :  *
     356             :  * Also, if A merged with B cannot be merged with C, then either A or B cannot
     357             :  * be merged with C either.
     358             :  */
     359             : static const JobType job_merging_table[] = {
     360             : /* What \ With       *  JOB_START         JOB_VERIFY_ACTIVE  JOB_STOP JOB_RELOAD */
     361             : /*********************************************************************************/
     362             : /*JOB_START          */
     363             : /*JOB_VERIFY_ACTIVE  */ JOB_START,
     364             : /*JOB_STOP           */ -1,                  -1,
     365             : /*JOB_RELOAD         */ JOB_RELOAD_OR_START, JOB_RELOAD,          -1,
     366             : /*JOB_RESTART        */ JOB_RESTART,         JOB_RESTART,         -1, JOB_RESTART,
     367             : };
     368             : 
     369        2020 : JobType job_type_lookup_merge(JobType a, JobType b) {
     370             :         assert_cc(ELEMENTSOF(job_merging_table) == _JOB_TYPE_MAX_MERGING * (_JOB_TYPE_MAX_MERGING - 1) / 2);
     371        2020 :         assert(a >= 0 && a < _JOB_TYPE_MAX_MERGING);
     372        2020 :         assert(b >= 0 && b < _JOB_TYPE_MAX_MERGING);
     373             : 
     374        2020 :         if (a == b)
     375         508 :                 return a;
     376             : 
     377        1512 :         if (a < b) {
     378         701 :                 JobType tmp = a;
     379         701 :                 a = b;
     380         701 :                 b = tmp;
     381             :         }
     382             : 
     383        1512 :         return job_merging_table[(a - 1) * a / 2 + b];
     384             : }
     385             : 
     386         403 : bool job_type_is_redundant(JobType a, UnitActiveState b) {
     387         403 :         switch (a) {
     388             : 
     389         337 :         case JOB_START:
     390         337 :                 return IN_SET(b, UNIT_ACTIVE, UNIT_RELOADING);
     391             : 
     392          66 :         case JOB_STOP:
     393          66 :                 return IN_SET(b, UNIT_INACTIVE, UNIT_FAILED);
     394             : 
     395           0 :         case JOB_VERIFY_ACTIVE:
     396           0 :                 return IN_SET(b, UNIT_ACTIVE, UNIT_RELOADING);
     397             : 
     398           0 :         case JOB_RELOAD:
     399             :                 return
     400           0 :                         b == UNIT_RELOADING;
     401             : 
     402           0 :         case JOB_RESTART:
     403             :                 return
     404           0 :                         b == UNIT_ACTIVATING;
     405             : 
     406           0 :         case JOB_NOP:
     407           0 :                 return true;
     408             : 
     409           0 :         default:
     410           0 :                 assert_not_reached("Invalid job type");
     411             :         }
     412             : }
     413             : 
     414         592 : JobType job_type_collapse(JobType t, Unit *u) {
     415             :         UnitActiveState s;
     416             : 
     417         592 :         switch (t) {
     418             : 
     419           0 :         case JOB_TRY_RESTART:
     420           0 :                 s = unit_active_state(u);
     421           0 :                 if (UNIT_IS_INACTIVE_OR_DEACTIVATING(s))
     422           0 :                         return JOB_NOP;
     423             : 
     424           0 :                 return JOB_RESTART;
     425             : 
     426           0 :         case JOB_TRY_RELOAD:
     427           0 :                 s = unit_active_state(u);
     428           0 :                 if (UNIT_IS_INACTIVE_OR_DEACTIVATING(s))
     429           0 :                         return JOB_NOP;
     430             : 
     431           0 :                 return JOB_RELOAD;
     432             : 
     433          68 :         case JOB_RELOAD_OR_START:
     434          68 :                 s = unit_active_state(u);
     435          68 :                 if (UNIT_IS_INACTIVE_OR_DEACTIVATING(s))
     436          34 :                         return JOB_START;
     437             : 
     438          34 :                 return JOB_RELOAD;
     439             : 
     440         524 :         default:
     441         524 :                 return t;
     442             :         }
     443             : }
     444             : 
     445         628 : int job_type_merge_and_collapse(JobType *a, JobType b, Unit *u) {
     446             :         JobType t;
     447             : 
     448         628 :         t = job_type_lookup_merge(*a, b);
     449         628 :         if (t < 0)
     450          56 :                 return -EEXIST;
     451             : 
     452         572 :         *a = job_type_collapse(t, u);
     453         572 :         return 0;
     454             : }
     455             : 
     456          36 : static bool job_is_runnable(Job *j) {
     457             :         Iterator i;
     458             :         Unit *other;
     459             :         void *v;
     460             : 
     461          36 :         assert(j);
     462          36 :         assert(j->installed);
     463             : 
     464             :         /* Checks whether there is any job running for the units this
     465             :          * job needs to be running after (in the case of a 'positive'
     466             :          * job type) or before (in the case of a 'negative' job
     467             :          * type. */
     468             : 
     469             :         /* Note that unit types have a say in what is runnable,
     470             :          * too. For example, if they return -EAGAIN from
     471             :          * unit_start() they can indicate they are not
     472             :          * runnable yet. */
     473             : 
     474             :         /* First check if there is an override */
     475          36 :         if (j->ignore_order)
     476           0 :                 return true;
     477             : 
     478          36 :         if (j->type == JOB_NOP)
     479           0 :                 return true;
     480             : 
     481         136 :         HASHMAP_FOREACH_KEY(v, other, j->unit->dependencies[UNIT_AFTER], i)
     482         112 :                 if (other->job && job_compare(j, other->job, UNIT_AFTER) > 0)
     483          12 :                         return false;
     484             : 
     485          66 :         HASHMAP_FOREACH_KEY(v, other, j->unit->dependencies[UNIT_BEFORE], i)
     486          42 :                 if (other->job && job_compare(j, other->job, UNIT_BEFORE) > 0)
     487           0 :                         return false;
     488             : 
     489          24 :         return true;
     490             : }
     491             : 
     492           0 : static void job_change_type(Job *j, JobType newtype) {
     493           0 :         assert(j);
     494             : 
     495           0 :         log_unit_debug(j->unit,
     496             :                        "Converting job %s/%s -> %s/%s",
     497             :                        j->unit->id, job_type_to_string(j->type),
     498             :                        j->unit->id, job_type_to_string(newtype));
     499             : 
     500           0 :         j->type = newtype;
     501           0 : }
     502             : 
     503           6 : _pure_ static const char* job_get_begin_status_message_format(Unit *u, JobType t) {
     504             :         const char *format;
     505             : 
     506           6 :         assert(u);
     507             : 
     508           6 :         if (t == JOB_RELOAD)
     509           0 :                 return "Reloading %s.";
     510             : 
     511           6 :         assert(IN_SET(t, JOB_START, JOB_STOP));
     512             : 
     513           6 :         format = UNIT_VTABLE(u)->status_message_formats.starting_stopping[t == JOB_STOP];
     514           6 :         if (format)
     515           6 :                 return format;
     516             : 
     517             :         /* Return generic strings */
     518           0 :         if (t == JOB_START)
     519           0 :                 return "Starting %s.";
     520             :         else {
     521           0 :                 assert(t == JOB_STOP);
     522           0 :                 return "Stopping %s.";
     523             :         }
     524             : }
     525             : 
     526           6 : static void job_print_begin_status_message(Unit *u, JobType t) {
     527             :         const char *format;
     528             : 
     529           6 :         assert(u);
     530             : 
     531             :         /* Reload status messages have traditionally not been printed to console. */
     532           6 :         if (!IN_SET(t, JOB_START, JOB_STOP))
     533           0 :                 return;
     534             : 
     535           6 :         format = job_get_begin_status_message_format(u, t);
     536             : 
     537             :         DISABLE_WARNING_FORMAT_NONLITERAL;
     538           6 :         unit_status_printf(u, "", format);
     539             :         REENABLE_WARNING;
     540             : }
     541             : 
     542           6 : static void job_log_begin_status_message(Unit *u, uint32_t job_id, JobType t) {
     543             :         const char *format, *mid;
     544             :         char buf[LINE_MAX];
     545             : 
     546           6 :         assert(u);
     547           6 :         assert(t >= 0);
     548           6 :         assert(t < _JOB_TYPE_MAX);
     549             : 
     550           6 :         if (!IN_SET(t, JOB_START, JOB_STOP, JOB_RELOAD))
     551           6 :                 return;
     552             : 
     553           6 :         if (log_on_console()) /* Skip this if it would only go on the console anyway */
     554           6 :                 return;
     555             : 
     556             :         /* We log status messages for all units and all operations. */
     557             : 
     558           0 :         format = job_get_begin_status_message_format(u, t);
     559             : 
     560             :         DISABLE_WARNING_FORMAT_NONLITERAL;
     561           0 :         (void) snprintf(buf, sizeof buf, format, unit_status_string(u));
     562             :         REENABLE_WARNING;
     563             : 
     564           0 :         mid = t == JOB_START ? "MESSAGE_ID=" SD_MESSAGE_UNIT_STARTING_STR :
     565           0 :               t == JOB_STOP  ? "MESSAGE_ID=" SD_MESSAGE_UNIT_STOPPING_STR :
     566             :                                "MESSAGE_ID=" SD_MESSAGE_UNIT_RELOADING_STR;
     567             : 
     568             :         /* Note that we deliberately use LOG_MESSAGE() instead of
     569             :          * LOG_UNIT_MESSAGE() here, since this is supposed to mimic
     570             :          * closely what is written to screen using the status output,
     571             :          * which is supposed the highest level, friendliest output
     572             :          * possible, which means we should avoid the low-level unit
     573             :          * name. */
     574           0 :         log_struct(LOG_INFO,
     575             :                    LOG_MESSAGE("%s", buf),
     576             :                    "JOB_ID=%" PRIu32, job_id,
     577             :                    "JOB_TYPE=%s", job_type_to_string(t),
     578             :                    LOG_UNIT_ID(u),
     579             :                    LOG_UNIT_INVOCATION_ID(u),
     580             :                    mid);
     581             : }
     582             : 
     583           6 : static void job_emit_begin_status_message(Unit *u, uint32_t job_id, JobType t) {
     584           6 :         assert(u);
     585           6 :         assert(t >= 0);
     586           6 :         assert(t < _JOB_TYPE_MAX);
     587             : 
     588           6 :         job_log_begin_status_message(u, job_id, t);
     589           6 :         job_print_begin_status_message(u, t);
     590           6 : }
     591             : 
     592          24 : static int job_perform_on_unit(Job **j) {
     593             :         uint32_t id;
     594             :         Manager *m;
     595             :         JobType t;
     596             :         Unit *u;
     597             :         int r;
     598             : 
     599             :         /* While we execute this operation the job might go away (for
     600             :          * example: because it finishes immediately or is replaced by
     601             :          * a new, conflicting job.) To make sure we don't access a
     602             :          * freed job later on we store the id here, so that we can
     603             :          * verify the job is still valid. */
     604             : 
     605          24 :         assert(j);
     606          24 :         assert(*j);
     607             : 
     608          24 :         m = (*j)->manager;
     609          24 :         u = (*j)->unit;
     610          24 :         t = (*j)->type;
     611          24 :         id = (*j)->id;
     612             : 
     613          24 :         switch (t) {
     614          24 :                 case JOB_START:
     615          24 :                         r = unit_start(u);
     616          24 :                         break;
     617             : 
     618           0 :                 case JOB_RESTART:
     619           0 :                         t = JOB_STOP;
     620             :                         _fallthrough_;
     621           0 :                 case JOB_STOP:
     622           0 :                         r = unit_stop(u);
     623           0 :                         break;
     624             : 
     625           0 :                 case JOB_RELOAD:
     626           0 :                         r = unit_reload(u);
     627           0 :                         break;
     628             : 
     629           0 :                 default:
     630           0 :                         assert_not_reached("Invalid job type");
     631             :         }
     632             : 
     633             :         /* Log if the job still exists and the start/stop/reload function actually did something. Note that this means
     634             :          * for units for which there's no 'activating' phase (i.e. because we transition directly from 'inactive' to
     635             :          * 'active') we'll possibly skip the "Starting..." message. */
     636          24 :         *j = manager_get_job(m, id);
     637          24 :         if (*j && r > 0)
     638           6 :                 job_emit_begin_status_message(u, id, t);
     639             : 
     640          24 :         return r;
     641             : }
     642             : 
     643          36 : int job_run_and_invalidate(Job *j) {
     644             :         int r;
     645             : 
     646          36 :         assert(j);
     647          36 :         assert(j->installed);
     648          36 :         assert(j->type < _JOB_TYPE_MAX_IN_TRANSACTION);
     649          36 :         assert(j->in_run_queue);
     650             : 
     651          36 :         prioq_remove(j->manager->run_queue, j, &j->run_queue_idx);
     652          36 :         j->in_run_queue = false;
     653             : 
     654          36 :         if (j->state != JOB_WAITING)
     655           0 :                 return 0;
     656             : 
     657          36 :         if (!job_is_runnable(j))
     658          12 :                 return -EAGAIN;
     659             : 
     660          24 :         job_start_timer(j, true);
     661          24 :         job_set_state(j, JOB_RUNNING);
     662          24 :         job_add_to_dbus_queue(j);
     663             : 
     664          24 :         switch (j->type) {
     665             : 
     666           0 :                 case JOB_VERIFY_ACTIVE: {
     667             :                         UnitActiveState t;
     668             : 
     669           0 :                         t = unit_active_state(j->unit);
     670           0 :                         if (UNIT_IS_ACTIVE_OR_RELOADING(t))
     671           0 :                                 r = -EALREADY;
     672           0 :                         else if (t == UNIT_ACTIVATING)
     673           0 :                                 r = -EAGAIN;
     674             :                         else
     675           0 :                                 r = -EBADR;
     676           0 :                         break;
     677             :                 }
     678             : 
     679          24 :                 case JOB_START:
     680             :                 case JOB_STOP:
     681             :                 case JOB_RESTART:
     682          24 :                         r = job_perform_on_unit(&j);
     683             : 
     684             :                         /* If the unit type does not support starting/stopping, then simply wait. */
     685          24 :                         if (r == -EBADR)
     686           0 :                                 r = 0;
     687          24 :                         break;
     688             : 
     689           0 :                 case JOB_RELOAD:
     690           0 :                         r = job_perform_on_unit(&j);
     691           0 :                         break;
     692             : 
     693           0 :                 case JOB_NOP:
     694           0 :                         r = -EALREADY;
     695           0 :                         break;
     696             : 
     697           0 :                 default:
     698           0 :                         assert_not_reached("Unknown job type");
     699             :         }
     700             : 
     701          24 :         if (j) {
     702           6 :                 if (r == -EAGAIN)
     703           0 :                         job_set_state(j, JOB_WAITING); /* Hmm, not ready after all, let's return to JOB_WAITING state */
     704           6 :                 else if (r == -EALREADY) /* already being executed */
     705           0 :                         r = job_finish_and_invalidate(j, JOB_DONE, true, true);
     706           6 :                 else if (r == -ECOMM)    /* condition failed, but all is good */
     707           0 :                         r = job_finish_and_invalidate(j, JOB_DONE, true, false);
     708           6 :                 else if (r == -EBADR)
     709           0 :                         r = job_finish_and_invalidate(j, JOB_SKIPPED, true, false);
     710           6 :                 else if (r == -ENOEXEC)
     711           0 :                         r = job_finish_and_invalidate(j, JOB_INVALID, true, false);
     712           6 :                 else if (r == -EPROTO)
     713           0 :                         r = job_finish_and_invalidate(j, JOB_ASSERT, true, false);
     714           6 :                 else if (r == -EOPNOTSUPP)
     715           0 :                         r = job_finish_and_invalidate(j, JOB_UNSUPPORTED, true, false);
     716           6 :                 else if (r == -ENOLINK)
     717           0 :                         r = job_finish_and_invalidate(j, JOB_DEPENDENCY, true, false);
     718           6 :                 else if (r == -ESTALE)
     719           0 :                         r = job_finish_and_invalidate(j, JOB_ONCE, true, false);
     720           6 :                 else if (r < 0)
     721           0 :                         r = job_finish_and_invalidate(j, JOB_FAILED, true, false);
     722             :         }
     723             : 
     724          24 :         return r;
     725             : }
     726             : 
     727          44 : _pure_ static const char *job_get_done_status_message_format(Unit *u, JobType t, JobResult result) {
     728             : 
     729             :         static const char *const generic_finished_start_job[_JOB_RESULT_MAX] = {
     730             :                 [JOB_DONE]        = "Started %s.",
     731             :                 [JOB_TIMEOUT]     = "Timed out starting %s.",
     732             :                 [JOB_FAILED]      = "Failed to start %s.",
     733             :                 [JOB_DEPENDENCY]  = "Dependency failed for %s.",
     734             :                 [JOB_ASSERT]      = "Assertion failed for %s.",
     735             :                 [JOB_UNSUPPORTED] = "Starting of %s not supported.",
     736             :                 [JOB_COLLECTED]   = "Unnecessary job for %s was removed.",
     737             :                 [JOB_ONCE]        = "Unit %s has been started before and cannot be started again."
     738             :         };
     739             :         static const char *const generic_finished_stop_job[_JOB_RESULT_MAX] = {
     740             :                 [JOB_DONE]        = "Stopped %s.",
     741             :                 [JOB_FAILED]      = "Stopped (with error) %s.",
     742             :                 [JOB_TIMEOUT]     = "Timed out stopping %s.",
     743             :         };
     744             :         static const char *const generic_finished_reload_job[_JOB_RESULT_MAX] = {
     745             :                 [JOB_DONE]        = "Reloaded %s.",
     746             :                 [JOB_FAILED]      = "Reload failed for %s.",
     747             :                 [JOB_TIMEOUT]     = "Timed out reloading %s.",
     748             :         };
     749             :         /* When verify-active detects the unit is inactive, report it.
     750             :          * Most likely a DEPEND warning from a requisiting unit will
     751             :          * occur next and it's nice to see what was requisited. */
     752             :         static const char *const generic_finished_verify_active_job[_JOB_RESULT_MAX] = {
     753             :                 [JOB_SKIPPED]     = "%s is not active.",
     754             :         };
     755             : 
     756             :         const char *format;
     757             : 
     758          44 :         assert(u);
     759          44 :         assert(t >= 0);
     760          44 :         assert(t < _JOB_TYPE_MAX);
     761             : 
     762          44 :         if (IN_SET(t, JOB_START, JOB_STOP, JOB_RESTART)) {
     763          44 :                 format = t == JOB_START ?
     764          49 :                         UNIT_VTABLE(u)->status_message_formats.finished_start_job[result] :
     765           5 :                         UNIT_VTABLE(u)->status_message_formats.finished_stop_job[result];
     766          44 :                 if (format)
     767          18 :                         return format;
     768             :         }
     769             : 
     770             :         /* Return generic strings */
     771          26 :         if (t == JOB_START)
     772          21 :                 return generic_finished_start_job[result];
     773           5 :         else if (IN_SET(t, JOB_STOP, JOB_RESTART))
     774           5 :                 return generic_finished_stop_job[result];
     775           0 :         else if (t == JOB_RELOAD)
     776           0 :                 return generic_finished_reload_job[result];
     777           0 :         else if (t == JOB_VERIFY_ACTIVE)
     778           0 :                 return generic_finished_verify_active_job[result];
     779             : 
     780           0 :         return NULL;
     781             : }
     782             : 
     783             : static const struct {
     784             :         const char *color, *word;
     785             : } job_print_done_status_messages[_JOB_RESULT_MAX] = {
     786             :         [JOB_DONE]        = { ANSI_OK_COLOR,         "  OK  " },
     787             :         [JOB_TIMEOUT]     = { ANSI_HIGHLIGHT_RED,    " TIME " },
     788             :         [JOB_FAILED]      = { ANSI_HIGHLIGHT_RED,    "FAILED" },
     789             :         [JOB_DEPENDENCY]  = { ANSI_HIGHLIGHT_YELLOW, "DEPEND" },
     790             :         [JOB_SKIPPED]     = { ANSI_HIGHLIGHT,        " INFO " },
     791             :         [JOB_ASSERT]      = { ANSI_HIGHLIGHT_YELLOW, "ASSERT" },
     792             :         [JOB_UNSUPPORTED] = { ANSI_HIGHLIGHT_YELLOW, "UNSUPP" },
     793             :         /* JOB_COLLECTED */
     794             :         [JOB_ONCE]        = { ANSI_HIGHLIGHT_RED,    " ONCE " },
     795             : };
     796             : 
     797          44 : static void job_print_done_status_message(Unit *u, JobType t, JobResult result) {
     798             :         const char *format;
     799             :         const char *status;
     800             : 
     801          44 :         assert(u);
     802          44 :         assert(t >= 0);
     803          44 :         assert(t < _JOB_TYPE_MAX);
     804             : 
     805             :         /* Reload status messages have traditionally not been printed to console. */
     806          44 :         if (t == JOB_RELOAD)
     807           0 :                 return;
     808             : 
     809             :         /* No message if the job did not actually do anything due to failed condition. */
     810          44 :         if (t == JOB_START && result == JOB_DONE && !u->condition_result)
     811           0 :                 return;
     812             : 
     813          44 :         if (!job_print_done_status_messages[result].word)
     814          26 :                 return;
     815             : 
     816          18 :         format = job_get_done_status_message_format(u, t, result);
     817          18 :         if (!format)
     818           0 :                 return;
     819             : 
     820          18 :         if (log_get_show_color())
     821           0 :                 status = strjoina(job_print_done_status_messages[result].color,
     822             :                                   job_print_done_status_messages[result].word,
     823             :                                   ANSI_NORMAL);
     824             :         else
     825          18 :                 status = job_print_done_status_messages[result].word;
     826             : 
     827          18 :         if (result != JOB_DONE)
     828           0 :                 manager_flip_auto_status(u->manager, true);
     829             : 
     830             :         DISABLE_WARNING_FORMAT_NONLITERAL;
     831          18 :         unit_status_printf(u, status, format);
     832             :         REENABLE_WARNING;
     833             : 
     834          18 :         if (t == JOB_START && result == JOB_FAILED) {
     835           0 :                 _cleanup_free_ char *quoted;
     836             : 
     837           0 :                 quoted = shell_maybe_quote(u->id, ESCAPE_BACKSLASH);
     838           0 :                 manager_status_printf(u->manager, STATUS_TYPE_NORMAL, NULL, "See 'systemctl status %s' for details.", strna(quoted));
     839             :         }
     840             : }
     841             : 
     842          44 : static void job_log_done_status_message(Unit *u, uint32_t job_id, JobType t, JobResult result) {
     843             :         const char *format, *mid;
     844             :         char buf[LINE_MAX];
     845             :         static const int job_result_log_level[_JOB_RESULT_MAX] = {
     846             :                 [JOB_DONE]        = LOG_INFO,
     847             :                 [JOB_CANCELED]    = LOG_INFO,
     848             :                 [JOB_TIMEOUT]     = LOG_ERR,
     849             :                 [JOB_FAILED]      = LOG_ERR,
     850             :                 [JOB_DEPENDENCY]  = LOG_WARNING,
     851             :                 [JOB_SKIPPED]     = LOG_NOTICE,
     852             :                 [JOB_INVALID]     = LOG_INFO,
     853             :                 [JOB_ASSERT]      = LOG_WARNING,
     854             :                 [JOB_UNSUPPORTED] = LOG_WARNING,
     855             :                 [JOB_COLLECTED]   = LOG_INFO,
     856             :                 [JOB_ONCE]        = LOG_ERR,
     857             :         };
     858             : 
     859          44 :         assert(u);
     860          44 :         assert(t >= 0);
     861          44 :         assert(t < _JOB_TYPE_MAX);
     862             : 
     863             :         /* Skip printing if output goes to the console, and job_print_status_message()
     864             :            will actually print something to the console. */
     865          44 :         if (log_on_console() && job_print_done_status_messages[result].word)
     866          44 :                 return;
     867             : 
     868             :         /* Show condition check message if the job did not actually do anything due to failed condition. */
     869          26 :         if ((t == JOB_START && result == JOB_DONE && !u->condition_result) ||
     870          21 :             (t == JOB_START && result == JOB_SKIPPED)) {
     871           0 :                 log_struct(LOG_INFO,
     872             :                            "MESSAGE=Condition check resulted in %s being skipped.", unit_status_string(u),
     873             :                            "JOB_ID=%" PRIu32, job_id,
     874             :                            "JOB_TYPE=%s", job_type_to_string(t),
     875             :                            "JOB_RESULT=%s", job_result_to_string(result),
     876             :                            LOG_UNIT_ID(u),
     877             :                            LOG_UNIT_INVOCATION_ID(u),
     878             :                            "MESSAGE_ID=" SD_MESSAGE_UNIT_STARTED_STR);
     879             : 
     880           0 :                 return;
     881             :         }
     882             : 
     883          26 :         format = job_get_done_status_message_format(u, t, result);
     884          26 :         if (!format)
     885          26 :                 return;
     886             : 
     887             :         /* The description might be longer than the buffer, but that's OK,
     888             :          * we'll just truncate it here. Note that we use snprintf() rather than
     889             :          * xsprintf() on purpose here: we are fine with truncation and don't
     890             :          * consider that an error. */
     891             :         DISABLE_WARNING_FORMAT_NONLITERAL;
     892           0 :         (void) snprintf(buf, sizeof(buf), format, unit_status_string(u));
     893             :         REENABLE_WARNING;
     894             : 
     895           0 :         switch (t) {
     896             : 
     897           0 :         case JOB_START:
     898           0 :                 if (result == JOB_DONE)
     899           0 :                         mid = "MESSAGE_ID=" SD_MESSAGE_UNIT_STARTED_STR;
     900             :                 else
     901           0 :                         mid = "MESSAGE_ID=" SD_MESSAGE_UNIT_FAILED_STR;
     902           0 :                 break;
     903             : 
     904           0 :         case JOB_RELOAD:
     905           0 :                 mid = "MESSAGE_ID=" SD_MESSAGE_UNIT_RELOADED_STR;
     906           0 :                 break;
     907             : 
     908           0 :         case JOB_STOP:
     909             :         case JOB_RESTART:
     910           0 :                 mid = "MESSAGE_ID=" SD_MESSAGE_UNIT_STOPPED_STR;
     911           0 :                 break;
     912             : 
     913           0 :         default:
     914           0 :                 log_struct(job_result_log_level[result],
     915             :                            LOG_MESSAGE("%s", buf),
     916             :                            "JOB_ID=%" PRIu32, job_id,
     917             :                            "JOB_TYPE=%s", job_type_to_string(t),
     918             :                            "JOB_RESULT=%s", job_result_to_string(result),
     919             :                            LOG_UNIT_ID(u),
     920             :                            LOG_UNIT_INVOCATION_ID(u));
     921           0 :                 return;
     922             :         }
     923             : 
     924           0 :         log_struct(job_result_log_level[result],
     925             :                    LOG_MESSAGE("%s", buf),
     926             :                    "JOB_ID=%" PRIu32, job_id,
     927             :                    "JOB_TYPE=%s", job_type_to_string(t),
     928             :                    "JOB_RESULT=%s", job_result_to_string(result),
     929             :                    LOG_UNIT_ID(u),
     930             :                    LOG_UNIT_INVOCATION_ID(u),
     931             :                    mid);
     932             : }
     933             : 
     934          44 : static void job_emit_done_status_message(Unit *u, uint32_t job_id, JobType t, JobResult result) {
     935          44 :         assert(u);
     936             : 
     937          44 :         job_log_done_status_message(u, job_id, t, result);
     938          44 :         job_print_done_status_message(u, t, result);
     939          44 : }
     940             : 
     941           0 : static void job_fail_dependencies(Unit *u, UnitDependency d) {
     942             :         Unit *other;
     943             :         Iterator i;
     944             :         void *v;
     945             : 
     946           0 :         assert(u);
     947             : 
     948           0 :         HASHMAP_FOREACH_KEY(v, other, u->dependencies[d], i) {
     949           0 :                 Job *j = other->job;
     950             : 
     951           0 :                 if (!j)
     952           0 :                         continue;
     953           0 :                 if (!IN_SET(j->type, JOB_START, JOB_VERIFY_ACTIVE))
     954           0 :                         continue;
     955             : 
     956           0 :                 job_finish_and_invalidate(j, JOB_DEPENDENCY, true, false);
     957             :         }
     958           0 : }
     959             : 
     960          44 : int job_finish_and_invalidate(Job *j, JobResult result, bool recursive, bool already) {
     961             :         Unit *u;
     962             :         Unit *other;
     963             :         JobType t;
     964             :         Iterator i;
     965             :         void *v;
     966             : 
     967          44 :         assert(j);
     968          44 :         assert(j->installed);
     969          44 :         assert(j->type < _JOB_TYPE_MAX_IN_TRANSACTION);
     970             : 
     971          44 :         u = j->unit;
     972          44 :         t = j->type;
     973             : 
     974          44 :         j->result = result;
     975             : 
     976          44 :         log_unit_debug(u, "Job %" PRIu32 " %s/%s finished, result=%s", j->id, u->id, job_type_to_string(t), job_result_to_string(result));
     977             : 
     978             :         /* If this job did nothing to respective unit we don't log the status message */
     979          44 :         if (!already)
     980          44 :                 job_emit_done_status_message(u, j->id, t, result);
     981             : 
     982             :         /* Patch restart jobs so that they become normal start jobs */
     983          44 :         if (result == JOB_DONE && t == JOB_RESTART) {
     984             : 
     985           0 :                 job_change_type(j, JOB_START);
     986           0 :                 job_set_state(j, JOB_WAITING);
     987             : 
     988           0 :                 job_add_to_dbus_queue(j);
     989           0 :                 job_add_to_run_queue(j);
     990           0 :                 job_add_to_gc_queue(j);
     991             : 
     992           0 :                 goto finish;
     993             :         }
     994             : 
     995          44 :         if (IN_SET(result, JOB_FAILED, JOB_INVALID))
     996           0 :                 j->manager->n_failed_jobs++;
     997             : 
     998          44 :         job_uninstall(j);
     999          44 :         job_free(j);
    1000             : 
    1001             :         /* Fail depending jobs on failure */
    1002          44 :         if (result != JOB_DONE && recursive) {
    1003           0 :                 if (IN_SET(t, JOB_START, JOB_VERIFY_ACTIVE)) {
    1004           0 :                         job_fail_dependencies(u, UNIT_REQUIRED_BY);
    1005           0 :                         job_fail_dependencies(u, UNIT_REQUISITE_OF);
    1006           0 :                         job_fail_dependencies(u, UNIT_BOUND_BY);
    1007           0 :                 } else if (t == JOB_STOP)
    1008           0 :                         job_fail_dependencies(u, UNIT_CONFLICTED_BY);
    1009             :         }
    1010             : 
    1011             :         /* A special check to make sure we take down anything RequisiteOf if we
    1012             :          * aren't active. This is when the verify-active job merges with a
    1013             :          * satisfying job type, and then loses it's invalidation effect, as the
    1014             :          * result there is JOB_DONE for the start job we merged into, while we
    1015             :          * should be failing the depending job if the said unit isn't infact
    1016             :          * active. Oneshots are an example of this, where going directly from
    1017             :          * activating to inactive is success.
    1018             :          *
    1019             :          * This happens when you use ConditionXYZ= in a unit too, since in that
    1020             :          * case the job completes with the JOB_DONE result, but the unit never
    1021             :          * really becomes active. Note that such a case still involves merging:
    1022             :          *
    1023             :          * A start job waits for something else, and a verify-active comes in
    1024             :          * and merges in the installed job. Then, later, when it becomes
    1025             :          * runnable, it finishes with JOB_DONE result as execution on conditions
    1026             :          * not being met is skipped, breaking our dependency semantics.
    1027             :          *
    1028             :          * Also, depending on if start job waits or not, the merging may or may
    1029             :          * not happen (the verify-active job may trigger after it finishes), so
    1030             :          * you get undeterministic results without this check.
    1031             :          */
    1032          44 :         if (result == JOB_DONE && recursive && !UNIT_IS_ACTIVE_OR_RELOADING(unit_active_state(u))) {
    1033           0 :                 if (IN_SET(t, JOB_START, JOB_RELOAD))
    1034           0 :                         job_fail_dependencies(u, UNIT_REQUISITE_OF);
    1035             :         }
    1036             :         /* Trigger OnFailure dependencies that are not generated by
    1037             :          * the unit itself. We don't treat JOB_CANCELED as failure in
    1038             :          * this context. And JOB_FAILURE is already handled by the
    1039             :          * unit itself. */
    1040          44 :         if (IN_SET(result, JOB_TIMEOUT, JOB_DEPENDENCY)) {
    1041           0 :                 log_struct(LOG_NOTICE,
    1042             :                            "JOB_TYPE=%s", job_type_to_string(t),
    1043             :                            "JOB_RESULT=%s", job_result_to_string(result),
    1044             :                            LOG_UNIT_ID(u),
    1045             :                            LOG_UNIT_MESSAGE(u, "Job %s/%s failed with result '%s'.",
    1046             :                                             u->id,
    1047             :                                             job_type_to_string(t),
    1048             :                                             job_result_to_string(result)));
    1049             : 
    1050           0 :                 unit_start_on_failure(u);
    1051             :         }
    1052             : 
    1053          44 :         unit_trigger_notify(u);
    1054             : 
    1055          44 : finish:
    1056             :         /* Try to start the next jobs that can be started */
    1057         176 :         HASHMAP_FOREACH_KEY(v, other, u->dependencies[UNIT_AFTER], i)
    1058         132 :                 if (other->job) {
    1059          13 :                         job_add_to_run_queue(other->job);
    1060          13 :                         job_add_to_gc_queue(other->job);
    1061             :                 }
    1062         138 :         HASHMAP_FOREACH_KEY(v, other, u->dependencies[UNIT_BEFORE], i)
    1063          94 :                 if (other->job) {
    1064          30 :                         job_add_to_run_queue(other->job);
    1065          30 :                         job_add_to_gc_queue(other->job);
    1066             :                 }
    1067             : 
    1068          44 :         manager_check_finished(u->manager);
    1069             : 
    1070          44 :         return 0;
    1071             : }
    1072             : 
    1073           0 : static int job_dispatch_timer(sd_event_source *s, uint64_t monotonic, void *userdata) {
    1074           0 :         Job *j = userdata;
    1075             :         Unit *u;
    1076             : 
    1077           0 :         assert(j);
    1078           0 :         assert(s == j->timer_event_source);
    1079             : 
    1080           0 :         log_unit_warning(j->unit, "Job %s/%s timed out.", j->unit->id, job_type_to_string(j->type));
    1081             : 
    1082           0 :         u = j->unit;
    1083           0 :         job_finish_and_invalidate(j, JOB_TIMEOUT, true, false);
    1084             : 
    1085           0 :         emergency_action(u->manager, u->job_timeout_action,
    1086             :                          EMERGENCY_ACTION_IS_WATCHDOG|EMERGENCY_ACTION_WARN,
    1087           0 :                          u->job_timeout_reboot_arg, -1, "job timed out");
    1088             : 
    1089           0 :         return 0;
    1090             : }
    1091             : 
    1092          92 : int job_start_timer(Job *j, bool job_running) {
    1093             :         int r;
    1094             :         usec_t timeout_time, old_timeout_time;
    1095             : 
    1096          92 :         if (job_running) {
    1097          24 :                 j->begin_running_usec = now(CLOCK_MONOTONIC);
    1098             : 
    1099          24 :                 if (j->unit->job_running_timeout == USEC_INFINITY)
    1100          24 :                         return 0;
    1101             : 
    1102           0 :                 timeout_time = usec_add(j->begin_running_usec, j->unit->job_running_timeout);
    1103             : 
    1104           0 :                 if (j->timer_event_source) {
    1105             :                         /* Update only if JobRunningTimeoutSec= results in earlier timeout */
    1106           0 :                         r = sd_event_source_get_time(j->timer_event_source, &old_timeout_time);
    1107           0 :                         if (r < 0)
    1108           0 :                                 return r;
    1109             : 
    1110           0 :                         if (old_timeout_time <= timeout_time)
    1111           0 :                                 return 0;
    1112             : 
    1113           0 :                         return sd_event_source_set_time(j->timer_event_source, timeout_time);
    1114             :                 }
    1115             :         } else {
    1116          68 :                 if (j->timer_event_source)
    1117           0 :                         return 0;
    1118             : 
    1119          68 :                 j->begin_usec = now(CLOCK_MONOTONIC);
    1120             : 
    1121          68 :                 if (j->unit->job_timeout == USEC_INFINITY)
    1122          68 :                         return 0;
    1123             : 
    1124           0 :                 timeout_time = usec_add(j->begin_usec, j->unit->job_timeout);
    1125             :         }
    1126             : 
    1127           0 :         r = sd_event_add_time(
    1128           0 :                         j->manager->event,
    1129             :                         &j->timer_event_source,
    1130             :                         CLOCK_MONOTONIC,
    1131             :                         timeout_time, 0,
    1132             :                         job_dispatch_timer, j);
    1133           0 :         if (r < 0)
    1134           0 :                 return r;
    1135             : 
    1136           0 :         (void) sd_event_source_set_description(j->timer_event_source, "job-start");
    1137             : 
    1138           0 :         return 0;
    1139             : }
    1140             : 
    1141         111 : void job_add_to_run_queue(Job *j) {
    1142             :         int r;
    1143             : 
    1144         111 :         assert(j);
    1145         111 :         assert(j->installed);
    1146             : 
    1147         111 :         if (j->in_run_queue)
    1148          49 :                 return;
    1149             : 
    1150          62 :         if (prioq_isempty(j->manager->run_queue)) {
    1151           9 :                 r = sd_event_source_set_enabled(j->manager->run_queue_event_source, SD_EVENT_ONESHOT);
    1152           9 :                 if (r < 0)
    1153           0 :                         log_warning_errno(r, "Failed to enable job run queue event source, ignoring: %m");
    1154             :         }
    1155             : 
    1156          62 :         r = prioq_put(j->manager->run_queue, j, &j->run_queue_idx);
    1157          62 :         if (r < 0)
    1158           0 :                 log_warning_errno(r, "Failed put job in run queue, ignoring: %m");
    1159             :         else
    1160          62 :                 j->in_run_queue = true;
    1161             : }
    1162             : 
    1163         142 : void job_add_to_dbus_queue(Job *j) {
    1164         142 :         assert(j);
    1165         142 :         assert(j->installed);
    1166             : 
    1167         142 :         if (j->in_dbus_queue)
    1168          92 :                 return;
    1169             : 
    1170             :         /* We don't check if anybody is subscribed here, since this
    1171             :          * job might just have been created and not yet assigned to a
    1172             :          * connection/client. */
    1173             : 
    1174          50 :         LIST_PREPEND(dbus_queue, j->manager->dbus_job_queue, j);
    1175          50 :         j->in_dbus_queue = true;
    1176             : }
    1177             : 
    1178           0 : char *job_dbus_path(Job *j) {
    1179             :         char *p;
    1180             : 
    1181           0 :         assert(j);
    1182             : 
    1183           0 :         if (asprintf(&p, "/org/freedesktop/systemd1/job/%"PRIu32, j->id) < 0)
    1184           0 :                 return NULL;
    1185             : 
    1186           0 :         return p;
    1187             : }
    1188             : 
    1189           0 : int job_serialize(Job *j, FILE *f) {
    1190           0 :         assert(j);
    1191           0 :         assert(f);
    1192             : 
    1193           0 :         (void) serialize_item_format(f, "job-id", "%u", j->id);
    1194           0 :         (void) serialize_item(f, "job-type", job_type_to_string(j->type));
    1195           0 :         (void) serialize_item(f, "job-state", job_state_to_string(j->state));
    1196           0 :         (void) serialize_bool(f, "job-irreversible", j->irreversible);
    1197           0 :         (void) serialize_bool(f, "job-sent-dbus-new-signal", j->sent_dbus_new_signal);
    1198           0 :         (void) serialize_bool(f, "job-ignore-order", j->ignore_order);
    1199             : 
    1200           0 :         if (j->begin_usec > 0)
    1201           0 :                 (void) serialize_usec(f, "job-begin", j->begin_usec);
    1202           0 :         if (j->begin_running_usec > 0)
    1203           0 :                 (void) serialize_usec(f, "job-begin-running", j->begin_running_usec);
    1204             : 
    1205           0 :         bus_track_serialize(j->bus_track, f, "subscribed");
    1206             : 
    1207             :         /* End marker */
    1208           0 :         fputc('\n', f);
    1209           0 :         return 0;
    1210             : }
    1211             : 
    1212           0 : int job_deserialize(Job *j, FILE *f) {
    1213             :         int r;
    1214             : 
    1215           0 :         assert(j);
    1216           0 :         assert(f);
    1217             : 
    1218           0 :         for (;;) {
    1219           0 :                 _cleanup_free_ char *line = NULL;
    1220             :                 char *l, *v;
    1221             :                 size_t k;
    1222             : 
    1223           0 :                 r = read_line(f, LONG_LINE_MAX, &line);
    1224           0 :                 if (r < 0)
    1225           0 :                         return log_error_errno(r, "Failed to read serialization line: %m");
    1226           0 :                 if (r == 0)
    1227           0 :                         return 0;
    1228             : 
    1229           0 :                 l = strstrip(line);
    1230             : 
    1231             :                 /* End marker */
    1232           0 :                 if (isempty(l))
    1233           0 :                         return 0;
    1234             : 
    1235           0 :                 k = strcspn(l, "=");
    1236             : 
    1237           0 :                 if (l[k] == '=') {
    1238           0 :                         l[k] = 0;
    1239           0 :                         v = l+k+1;
    1240             :                 } else
    1241           0 :                         v = l+k;
    1242             : 
    1243           0 :                 if (streq(l, "job-id")) {
    1244             : 
    1245           0 :                         if (safe_atou32(v, &j->id) < 0)
    1246           0 :                                 log_debug("Failed to parse job id value: %s", v);
    1247             : 
    1248           0 :                 } else if (streq(l, "job-type")) {
    1249             :                         JobType t;
    1250             : 
    1251           0 :                         t = job_type_from_string(v);
    1252           0 :                         if (t < 0)
    1253           0 :                                 log_debug("Failed to parse job type: %s", v);
    1254           0 :                         else if (t >= _JOB_TYPE_MAX_IN_TRANSACTION)
    1255           0 :                                 log_debug("Cannot deserialize job of type: %s", v);
    1256             :                         else
    1257           0 :                                 j->type = t;
    1258             : 
    1259           0 :                 } else if (streq(l, "job-state")) {
    1260             :                         JobState s;
    1261             : 
    1262           0 :                         s = job_state_from_string(v);
    1263           0 :                         if (s < 0)
    1264           0 :                                 log_debug("Failed to parse job state: %s", v);
    1265             :                         else
    1266           0 :                                 job_set_state(j, s);
    1267             : 
    1268           0 :                 } else if (streq(l, "job-irreversible")) {
    1269             :                         int b;
    1270             : 
    1271           0 :                         b = parse_boolean(v);
    1272           0 :                         if (b < 0)
    1273           0 :                                 log_debug("Failed to parse job irreversible flag: %s", v);
    1274             :                         else
    1275           0 :                                 j->irreversible = j->irreversible || b;
    1276             : 
    1277           0 :                 } else if (streq(l, "job-sent-dbus-new-signal")) {
    1278             :                         int b;
    1279             : 
    1280           0 :                         b = parse_boolean(v);
    1281           0 :                         if (b < 0)
    1282           0 :                                 log_debug("Failed to parse job sent_dbus_new_signal flag: %s", v);
    1283             :                         else
    1284           0 :                                 j->sent_dbus_new_signal = j->sent_dbus_new_signal || b;
    1285             : 
    1286           0 :                 } else if (streq(l, "job-ignore-order")) {
    1287             :                         int b;
    1288             : 
    1289           0 :                         b = parse_boolean(v);
    1290           0 :                         if (b < 0)
    1291           0 :                                 log_debug("Failed to parse job ignore_order flag: %s", v);
    1292             :                         else
    1293           0 :                                 j->ignore_order = j->ignore_order || b;
    1294             : 
    1295           0 :                 } else if (streq(l, "job-begin"))
    1296           0 :                         (void) deserialize_usec(v, &j->begin_usec);
    1297             : 
    1298           0 :                 else if (streq(l, "job-begin-running"))
    1299           0 :                         (void) deserialize_usec(v, &j->begin_running_usec);
    1300             : 
    1301           0 :                 else if (streq(l, "subscribed")) {
    1302           0 :                         if (strv_extend(&j->deserialized_clients, v) < 0)
    1303           0 :                                 return log_oom();
    1304             :                 } else
    1305           0 :                         log_debug("Unknown job serialization key: %s", l);
    1306             :         }
    1307             : }
    1308             : 
    1309           0 : int job_coldplug(Job *j) {
    1310             :         int r;
    1311           0 :         usec_t timeout_time = USEC_INFINITY;
    1312             : 
    1313           0 :         assert(j);
    1314             : 
    1315             :         /* After deserialization is complete and the bus connection
    1316             :          * set up again, let's start watching our subscribers again */
    1317           0 :         (void) bus_job_coldplug_bus_track(j);
    1318             : 
    1319           0 :         if (j->state == JOB_WAITING)
    1320           0 :                 job_add_to_run_queue(j);
    1321             : 
    1322             :         /* Maybe due to new dependencies we don't actually need this job anymore? */
    1323           0 :         job_add_to_gc_queue(j);
    1324             : 
    1325             :         /* Create timer only when job began or began running and the respective timeout is finite.
    1326             :          * Follow logic of job_start_timer() if both timeouts are finite */
    1327           0 :         if (j->begin_usec == 0)
    1328           0 :                 return 0;
    1329             : 
    1330           0 :         if (j->unit->job_timeout != USEC_INFINITY)
    1331           0 :                 timeout_time = usec_add(j->begin_usec, j->unit->job_timeout);
    1332             : 
    1333           0 :         if (timestamp_is_set(j->begin_running_usec))
    1334           0 :                 timeout_time = MIN(timeout_time, usec_add(j->begin_running_usec, j->unit->job_running_timeout));
    1335             : 
    1336           0 :         if (timeout_time == USEC_INFINITY)
    1337           0 :                 return 0;
    1338             : 
    1339           0 :         j->timer_event_source = sd_event_source_unref(j->timer_event_source);
    1340             : 
    1341           0 :         r = sd_event_add_time(
    1342           0 :                         j->manager->event,
    1343             :                         &j->timer_event_source,
    1344             :                         CLOCK_MONOTONIC,
    1345             :                         timeout_time, 0,
    1346             :                         job_dispatch_timer, j);
    1347           0 :         if (r < 0)
    1348           0 :                 log_debug_errno(r, "Failed to restart timeout for job: %m");
    1349             : 
    1350           0 :         (void) sd_event_source_set_description(j->timer_event_source, "job-timeout");
    1351             : 
    1352           0 :         return r;
    1353             : }
    1354             : 
    1355          68 : void job_shutdown_magic(Job *j) {
    1356          68 :         assert(j);
    1357             : 
    1358             :         /* The shutdown target gets some special treatment here: we
    1359             :          * tell the kernel to begin with flushing its disk caches, to
    1360             :          * optimize shutdown time a bit. Ideally we wouldn't hardcode
    1361             :          * this magic into PID 1. However all other processes aren't
    1362             :          * options either since they'd exit much sooner than PID 1 and
    1363             :          * asynchronous sync() would cause their exit to be
    1364             :          * delayed. */
    1365             : 
    1366          68 :         if (j->type != JOB_START)
    1367           5 :                 return;
    1368             : 
    1369          63 :         if (!MANAGER_IS_SYSTEM(j->unit->manager))
    1370          63 :                 return;
    1371             : 
    1372           0 :         if (!unit_has_name(j->unit, SPECIAL_SHUTDOWN_TARGET))
    1373           0 :                 return;
    1374             : 
    1375             :         /* In case messages on console has been disabled on boot */
    1376           0 :         j->unit->manager->no_console_output = false;
    1377             : 
    1378           0 :         if (detect_container() > 0)
    1379           0 :                 return;
    1380             : 
    1381           0 :         (void) asynchronous_sync(NULL);
    1382             : }
    1383             : 
    1384           0 : int job_get_timeout(Job *j, usec_t *timeout) {
    1385           0 :         usec_t x = USEC_INFINITY, y = USEC_INFINITY;
    1386           0 :         Unit *u = j->unit;
    1387             :         int r;
    1388             : 
    1389           0 :         assert(u);
    1390             : 
    1391           0 :         if (j->timer_event_source) {
    1392           0 :                 r = sd_event_source_get_time(j->timer_event_source, &x);
    1393           0 :                 if (r < 0)
    1394           0 :                         return r;
    1395             :         }
    1396             : 
    1397           0 :         if (UNIT_VTABLE(u)->get_timeout) {
    1398           0 :                 r = UNIT_VTABLE(u)->get_timeout(u, &y);
    1399           0 :                 if (r < 0)
    1400           0 :                         return r;
    1401             :         }
    1402             : 
    1403           0 :         if (x == USEC_INFINITY && y == USEC_INFINITY)
    1404           0 :                 return 0;
    1405             : 
    1406           0 :         *timeout = MIN(x, y);
    1407           0 :         return 1;
    1408             : }
    1409             : 
    1410         156 : bool job_may_gc(Job *j) {
    1411             :         Unit *other;
    1412             :         Iterator i;
    1413             :         void *v;
    1414             : 
    1415         156 :         assert(j);
    1416             : 
    1417             :         /* Checks whether this job should be GC'ed away. We only do this for jobs of units that have no effect on their
    1418             :          * own and just track external state. For now the only unit type that qualifies for this are .device units.
    1419             :          * Returns true if the job can be collected. */
    1420             : 
    1421         156 :         if (!UNIT_VTABLE(j->unit)->gc_jobs)
    1422         156 :                 return false;
    1423             : 
    1424           0 :         if (sd_bus_track_count(j->bus_track) > 0)
    1425           0 :                 return false;
    1426             : 
    1427             :         /* FIXME: So this is a bit ugly: for now we don't properly track references made via private bus connections
    1428             :          * (because it's nasty, as sd_bus_track doesn't apply to it). We simply remember that the job was once
    1429             :          * referenced by one, and reset this whenever we notice that no private bus connections are around. This means
    1430             :          * the GC is a bit too conservative when it comes to jobs created by private bus connections. */
    1431           0 :         if (j->ref_by_private_bus) {
    1432           0 :                 if (set_isempty(j->unit->manager->private_buses))
    1433           0 :                         j->ref_by_private_bus = false;
    1434             :                 else
    1435           0 :                         return false;
    1436             :         }
    1437             : 
    1438           0 :         if (j->type == JOB_NOP)
    1439           0 :                 return false;
    1440             : 
    1441             :         /* The logic is inverse to job_is_runnable, we cannot GC as long as we block any job. */
    1442           0 :         HASHMAP_FOREACH_KEY(v, other, j->unit->dependencies[UNIT_BEFORE], i)
    1443           0 :                 if (other->job && job_compare(j, other->job, UNIT_BEFORE) < 0)
    1444           0 :                         return false;
    1445             : 
    1446           0 :         HASHMAP_FOREACH_KEY(v, other, j->unit->dependencies[UNIT_AFTER], i)
    1447           0 :                 if (other->job && job_compare(j, other->job, UNIT_AFTER) < 0)
    1448           0 :                         return false;
    1449             : 
    1450           0 :         return true;
    1451             : }
    1452             : 
    1453          93 : void job_add_to_gc_queue(Job *j) {
    1454          93 :         assert(j);
    1455             : 
    1456          93 :         if (j->in_gc_queue)
    1457           0 :                 return;
    1458             : 
    1459          93 :         if (!job_may_gc(j))
    1460          93 :                 return;
    1461             : 
    1462           0 :         LIST_PREPEND(gc_queue, j->unit->manager->gc_job_queue, j);
    1463           0 :         j->in_gc_queue = true;
    1464             : }
    1465             : 
    1466           0 : static int job_compare_id(Job * const *a, Job * const *b) {
    1467           0 :         return CMP((*a)->id, (*b)->id);
    1468             : }
    1469             : 
    1470           0 : static size_t sort_job_list(Job **list, size_t n) {
    1471           0 :         Job *previous = NULL;
    1472             :         size_t a, b;
    1473             : 
    1474             :         /* Order by numeric IDs */
    1475           0 :         typesafe_qsort(list, n, job_compare_id);
    1476             : 
    1477             :         /* Filter out duplicates */
    1478           0 :         for (a = 0, b = 0; a < n; a++) {
    1479             : 
    1480           0 :                 if (previous == list[a])
    1481           0 :                         continue;
    1482             : 
    1483           0 :                 previous = list[b++] = list[a];
    1484             :         }
    1485             : 
    1486           0 :         return b;
    1487             : }
    1488             : 
    1489           0 : int job_get_before(Job *j, Job*** ret) {
    1490           0 :         _cleanup_free_ Job** list = NULL;
    1491           0 :         size_t n = 0, n_allocated = 0;
    1492           0 :         Unit *other = NULL;
    1493             :         Iterator i;
    1494             :         void *v;
    1495             : 
    1496             :         /* Returns a list of all pending jobs that need to finish before this job may be started. */
    1497             : 
    1498           0 :         assert(j);
    1499           0 :         assert(ret);
    1500             : 
    1501           0 :         if (j->ignore_order) {
    1502           0 :                 *ret = NULL;
    1503           0 :                 return 0;
    1504             :         }
    1505             : 
    1506           0 :         HASHMAP_FOREACH_KEY(v, other, j->unit->dependencies[UNIT_AFTER], i) {
    1507           0 :                 if (!other->job)
    1508           0 :                         continue;
    1509           0 :                 if (job_compare(j, other->job, UNIT_AFTER) <= 0)
    1510           0 :                         continue;
    1511             : 
    1512           0 :                 if (!GREEDY_REALLOC(list, n_allocated, n+1))
    1513           0 :                         return -ENOMEM;
    1514           0 :                 list[n++] = other->job;
    1515             :         }
    1516             : 
    1517           0 :         HASHMAP_FOREACH_KEY(v, other, j->unit->dependencies[UNIT_BEFORE], i) {
    1518           0 :                 if (!other->job)
    1519           0 :                         continue;
    1520           0 :                 if (job_compare(j, other->job, UNIT_BEFORE) <= 0)
    1521           0 :                         continue;
    1522             : 
    1523           0 :                 if (!GREEDY_REALLOC(list, n_allocated, n+1))
    1524           0 :                         return -ENOMEM;
    1525           0 :                 list[n++] = other->job;
    1526             :         }
    1527             : 
    1528           0 :         n = sort_job_list(list, n);
    1529             : 
    1530           0 :         *ret = TAKE_PTR(list);
    1531             : 
    1532           0 :         return (int) n;
    1533             : }
    1534             : 
    1535           0 : int job_get_after(Job *j, Job*** ret) {
    1536           0 :         _cleanup_free_ Job** list = NULL;
    1537           0 :         size_t n = 0, n_allocated = 0;
    1538           0 :         Unit *other = NULL;
    1539             :         void *v;
    1540             :         Iterator i;
    1541             : 
    1542           0 :         assert(j);
    1543           0 :         assert(ret);
    1544             : 
    1545             :         /* Returns a list of all pending jobs that are waiting for this job to finish. */
    1546             : 
    1547           0 :         HASHMAP_FOREACH_KEY(v, other, j->unit->dependencies[UNIT_BEFORE], i) {
    1548           0 :                 if (!other->job)
    1549           0 :                         continue;
    1550             : 
    1551           0 :                 if (other->job->ignore_order)
    1552           0 :                         continue;
    1553             : 
    1554           0 :                 if (job_compare(j, other->job, UNIT_BEFORE) >= 0)
    1555           0 :                         continue;
    1556             : 
    1557           0 :                 if (!GREEDY_REALLOC(list, n_allocated, n+1))
    1558           0 :                         return -ENOMEM;
    1559           0 :                 list[n++] = other->job;
    1560             :         }
    1561             : 
    1562           0 :         HASHMAP_FOREACH_KEY(v, other, j->unit->dependencies[UNIT_AFTER], i) {
    1563           0 :                 if (!other->job)
    1564           0 :                         continue;
    1565             : 
    1566           0 :                 if (other->job->ignore_order)
    1567           0 :                         continue;
    1568             : 
    1569           0 :                 if (job_compare(j, other->job, UNIT_AFTER) >= 0)
    1570           0 :                         continue;
    1571             : 
    1572           0 :                 if (!GREEDY_REALLOC(list, n_allocated, n+1))
    1573           0 :                         return -ENOMEM;
    1574           0 :                 list[n++] = other->job;
    1575             :         }
    1576             : 
    1577           0 :         n = sort_job_list(list, n);
    1578             : 
    1579           0 :         *ret = TAKE_PTR(list);
    1580             : 
    1581           0 :         return (int) n;
    1582             : }
    1583             : 
    1584             : static const char* const job_state_table[_JOB_STATE_MAX] = {
    1585             :         [JOB_WAITING] = "waiting",
    1586             :         [JOB_RUNNING] = "running",
    1587             : };
    1588             : 
    1589          71 : DEFINE_STRING_TABLE_LOOKUP(job_state, JobState);
    1590             : 
    1591             : static const char* const job_type_table[_JOB_TYPE_MAX] = {
    1592             :         [JOB_START] = "start",
    1593             :         [JOB_VERIFY_ACTIVE] = "verify-active",
    1594             :         [JOB_STOP] = "stop",
    1595             :         [JOB_RELOAD] = "reload",
    1596             :         [JOB_RELOAD_OR_START] = "reload-or-start",
    1597             :         [JOB_RESTART] = "restart",
    1598             :         [JOB_TRY_RESTART] = "try-restart",
    1599             :         [JOB_TRY_RELOAD] = "try-reload",
    1600             :         [JOB_NOP] = "nop",
    1601             : };
    1602             : 
    1603        1774 : DEFINE_STRING_TABLE_LOOKUP(job_type, JobType);
    1604             : 
    1605             : static const char* const job_mode_table[_JOB_MODE_MAX] = {
    1606             :         [JOB_FAIL] = "fail",
    1607             :         [JOB_REPLACE] = "replace",
    1608             :         [JOB_REPLACE_IRREVERSIBLY] = "replace-irreversibly",
    1609             :         [JOB_ISOLATE] = "isolate",
    1610             :         [JOB_FLUSH] = "flush",
    1611             :         [JOB_IGNORE_DEPENDENCIES] = "ignore-dependencies",
    1612             :         [JOB_IGNORE_REQUIREMENTS] = "ignore-requirements",
    1613             : };
    1614             : 
    1615        1040 : DEFINE_STRING_TABLE_LOOKUP(job_mode, JobMode);
    1616             : 
    1617             : static const char* const job_result_table[_JOB_RESULT_MAX] = {
    1618             :         [JOB_DONE] = "done",
    1619             :         [JOB_CANCELED] = "canceled",
    1620             :         [JOB_TIMEOUT] = "timeout",
    1621             :         [JOB_FAILED] = "failed",
    1622             :         [JOB_DEPENDENCY] = "dependency",
    1623             :         [JOB_SKIPPED] = "skipped",
    1624             :         [JOB_INVALID] = "invalid",
    1625             :         [JOB_ASSERT] = "assert",
    1626             :         [JOB_UNSUPPORTED] = "unsupported",
    1627             :         [JOB_COLLECTED] = "collected",
    1628             :         [JOB_ONCE] = "once",
    1629             : };
    1630             : 
    1631          70 : DEFINE_STRING_TABLE_LOOKUP(job_result, JobResult);
    1632             : 
    1633           0 : const char* job_type_to_access_method(JobType t) {
    1634           0 :         assert(t >= 0);
    1635           0 :         assert(t < _JOB_TYPE_MAX);
    1636             : 
    1637           0 :         if (IN_SET(t, JOB_START, JOB_RESTART, JOB_TRY_RESTART))
    1638           0 :                 return "start";
    1639           0 :         else if (t == JOB_STOP)
    1640           0 :                 return "stop";
    1641             :         else
    1642           0 :                 return "reload";
    1643             : }
    1644             : 
    1645             : /*
    1646             :  * assume_dep   assumed dependency between units (a is before/after b)
    1647             :  *
    1648             :  * Returns
    1649             :  *    0         jobs are independent,
    1650             :  *   >0         a should run after b,
    1651             :  *   <0         a should run before b,
    1652             :  *
    1653             :  * The logic means that for a service a and a service b where b.After=a:
    1654             :  *
    1655             :  *  start a + start b → 1st step start a, 2nd step start b
    1656             :  *  start a + stop b  → 1st step stop b,  2nd step start a
    1657             :  *  stop a  + start b → 1st step stop a,  2nd step start b
    1658             :  *  stop a  + stop b  → 1st step stop b,  2nd step stop a
    1659             :  *
    1660             :  *  This has the side effect that restarts are properly
    1661             :  *  synchronized too.
    1662             :  */
    1663         247 : int job_compare(Job *a, Job *b, UnitDependency assume_dep) {
    1664         247 :         assert(a->type < _JOB_TYPE_MAX_IN_TRANSACTION);
    1665         247 :         assert(b->type < _JOB_TYPE_MAX_IN_TRANSACTION);
    1666         247 :         assert(IN_SET(assume_dep, UNIT_AFTER, UNIT_BEFORE));
    1667             : 
    1668             :         /* Trivial cases first */
    1669         247 :         if (a->type == JOB_NOP || b->type == JOB_NOP)
    1670           0 :                 return 0;
    1671             : 
    1672         247 :         if (a->ignore_order || b->ignore_order)
    1673           0 :                 return 0;
    1674             : 
    1675         247 :         if (assume_dep == UNIT_AFTER)
    1676          76 :                 return -job_compare(b, a, UNIT_BEFORE);
    1677             : 
    1678             :         /* Let's make it simple, JOB_STOP goes always first (in case both ua and ub stop,
    1679             :          * then ub's stop goes first anyway).
    1680             :          * JOB_RESTART is JOB_STOP in disguise (before it is patched to JOB_START). */
    1681         171 :         if (IN_SET(b->type, JOB_STOP, JOB_RESTART))
    1682          23 :                 return 1;
    1683             :         else
    1684         148 :                 return -1;
    1685             : }

Generated by: LCOV version 1.14