chiark / gitweb /
d02551aff443e5aa2135cd4cdbc1145f2b035b66
[elogind.git] / job.c
1 /*-*- Mode: C; c-basic-offset: 8 -*-*/
2
3 #include <assert.h>
4 #include <errno.h>
5
6 #include "set.h"
7 #include "unit.h"
8 #include "macro.h"
9 #include "strv.h"
10 #include "load-fragment.h"
11 #include "load-dropin.h"
12 #include "log.h"
13
14 Job* job_new(Manager *m, JobType type, Unit *unit) {
15         Job *j;
16
17         assert(m);
18         assert(type < _JOB_TYPE_MAX);
19         assert(unit);
20
21         if (!(j = new0(Job, 1)))
22                 return NULL;
23
24         j->manager = m;
25         j->id = m->current_job_id++;
26         j->type = type;
27         j->unit = unit;
28
29         /* We don't link it here, that's what job_dependency() is for */
30
31         return j;
32 }
33
34 void job_free(Job *j) {
35         assert(j);
36
37         /* Detach from next 'bigger' objects */
38         if (j->installed) {
39                 if (j->unit->meta.job == j)
40                         j->unit->meta.job = NULL;
41
42                 hashmap_remove(j->manager->jobs, UINT32_TO_PTR(j->id));
43                 j->installed = false;
44         }
45
46         /* Detach from next 'smaller' objects */
47         manager_transaction_unlink_job(j->manager, j);
48
49         free(j);
50 }
51
52 JobDependency* job_dependency_new(Job *subject, Job *object, bool matters) {
53         JobDependency *l;
54
55         assert(object);
56
57         /* Adds a new job link, which encodes that the 'subject' job
58          * needs the 'object' job in some way. If 'subject' is NULL
59          * this means the 'anchor' job (i.e. the one the user
60          * explcitily asked for) is the requester. */
61
62         if (!(l = new0(JobDependency, 1)))
63                 return NULL;
64
65         l->subject = subject;
66         l->object = object;
67         l->matters = matters;
68
69         if (subject)
70                 LIST_PREPEND(JobDependency, subject, subject->subject_list, l);
71         else
72                 LIST_PREPEND(JobDependency, subject, object->manager->transaction_anchor, l);
73
74         LIST_PREPEND(JobDependency, object, object->object_list, l);
75
76         return l;
77 }
78
79 void job_dependency_free(JobDependency *l) {
80         assert(l);
81
82         if (l->subject)
83                 LIST_REMOVE(JobDependency, subject, l->subject->subject_list, l);
84         else
85                 LIST_REMOVE(JobDependency, subject, l->object->manager->transaction_anchor, l);
86
87         LIST_REMOVE(JobDependency, object, l->object->object_list, l);
88
89         free(l);
90 }
91
92 void job_dependency_delete(Job *subject, Job *object, bool *matters) {
93         JobDependency *l;
94
95         assert(object);
96
97         LIST_FOREACH(object, l, object->object_list) {
98                 assert(l->object == object);
99
100                 if (l->subject == subject)
101                         break;
102         }
103
104         if (!l) {
105                 if (matters)
106                         *matters = false;
107                 return;
108         }
109
110         if (matters)
111                 *matters = l->matters;
112
113         job_dependency_free(l);
114 }
115
116 void job_dump(Job *j, FILE*f, const char *prefix) {
117
118
119         assert(j);
120         assert(f);
121
122         fprintf(f,
123                 "%sā†’ Job %u:\n"
124                 "%s\tAction: %s ā†’ %s\n"
125                 "%s\tState: %s\n"
126                 "%s\tForced: %s\n",
127                 prefix, j->id,
128                 prefix, unit_id(j->unit), job_type_to_string(j->type),
129                 prefix, job_state_to_string(j->state),
130                 prefix, yes_no(j->forced));
131 }
132
133 bool job_is_anchor(Job *j) {
134         JobDependency *l;
135
136         assert(j);
137
138         LIST_FOREACH(object, l, j->object_list)
139                 if (!l->subject)
140                         return true;
141
142         return false;
143 }
144
145 static bool types_match(JobType a, JobType b, JobType c, JobType d) {
146         return
147                 (a == c && b == d) ||
148                 (a == d && b == c);
149 }
150
151 int job_type_merge(JobType *a, JobType b) {
152         if (*a == b)
153                 return 0;
154
155         /* Merging is associative! a merged with b merged with c is
156          * the same as a merged with c merged with b. */
157
158         /* Mergeability is transitive! if a can be merged with b and b
159          * with c then a also with c */
160
161         /* Also, if a merged with b cannot be merged with c, then
162          * either a or b cannot be merged with c either */
163
164         if (types_match(*a, b, JOB_START, JOB_VERIFY_ACTIVE))
165                 *a = JOB_START;
166         else if (types_match(*a, b, JOB_START, JOB_RELOAD) ||
167                  types_match(*a, b, JOB_START, JOB_RELOAD_OR_START) ||
168                  types_match(*a, b, JOB_VERIFY_ACTIVE, JOB_RELOAD_OR_START) ||
169                  types_match(*a, b, JOB_RELOAD, JOB_RELOAD_OR_START))
170                 *a = JOB_RELOAD_OR_START;
171         else if (types_match(*a, b, JOB_START, JOB_RESTART) ||
172                  types_match(*a, b, JOB_START, JOB_TRY_RESTART) ||
173                  types_match(*a, b, JOB_VERIFY_ACTIVE, JOB_RESTART) ||
174                  types_match(*a, b, JOB_RELOAD, JOB_RESTART) ||
175                  types_match(*a, b, JOB_RELOAD_OR_START, JOB_RESTART) ||
176                  types_match(*a, b, JOB_RELOAD_OR_START, JOB_TRY_RESTART) ||
177                  types_match(*a, b, JOB_RESTART, JOB_TRY_RESTART))
178                 *a = JOB_RESTART;
179         else if (types_match(*a, b, JOB_VERIFY_ACTIVE, JOB_RELOAD))
180                 *a = JOB_RELOAD;
181         else if (types_match(*a, b, JOB_VERIFY_ACTIVE, JOB_TRY_RESTART) ||
182                  types_match(*a, b, JOB_RELOAD, JOB_TRY_RESTART))
183                 *a = JOB_TRY_RESTART;
184         else
185                 return -EEXIST;
186
187         return 0;
188 }
189
190 bool job_type_is_mergeable(JobType a, JobType b) {
191         return job_type_merge(&a, b) >= 0;
192 }
193
194 bool job_type_is_superset(JobType a, JobType b) {
195
196         /* Checks whether operation a is a "superset" of b in its
197          * actions */
198
199         if (a == b)
200                 return true;
201
202         switch (a) {
203                 case JOB_START:
204                         return b == JOB_VERIFY_ACTIVE;
205
206                 case JOB_RELOAD:
207                         return
208                                 b == JOB_VERIFY_ACTIVE;
209
210                 case JOB_RELOAD_OR_START:
211                         return
212                                 b == JOB_RELOAD ||
213                                 b == JOB_START ||
214                                 b == JOB_VERIFY_ACTIVE;
215
216                 case JOB_RESTART:
217                         return
218                                 b == JOB_START ||
219                                 b == JOB_VERIFY_ACTIVE ||
220                                 b == JOB_RELOAD ||
221                                 b == JOB_RELOAD_OR_START ||
222                                 b == JOB_TRY_RESTART;
223
224                 case JOB_TRY_RESTART:
225                         return
226                                 b == JOB_VERIFY_ACTIVE ||
227                                 b == JOB_RELOAD;
228                 default:
229                         return false;
230
231         }
232 }
233
234 bool job_type_is_conflicting(JobType a, JobType b) {
235         assert(a >= 0 && a < _JOB_TYPE_MAX);
236         assert(b >= 0 && b < _JOB_TYPE_MAX);
237
238         return (a == JOB_STOP) != (b == JOB_STOP);
239 }
240
241 bool job_is_runnable(Job *j) {
242         Iterator i;
243         Unit *other;
244
245         assert(j);
246         assert(j->installed);
247
248         /* Checks whether there is any job running for the units this
249          * job needs to be running after (in the case of a 'positive'
250          * job type) or before (in the case of a 'negative' job type
251          * . */
252
253         if (j->type == JOB_START ||
254             j->type == JOB_VERIFY_ACTIVE ||
255             j->type == JOB_RELOAD ||
256             j->type == JOB_RELOAD_OR_START) {
257
258                 /* Immediate result is that the job is or might be
259                  * started. In this case lets wait for the
260                  * dependencies, regardless whether they are
261                  * starting or stopping something. */
262
263                 SET_FOREACH(other, j->unit->meta.dependencies[UNIT_AFTER], i)
264                         if (other->meta.job)
265                                 return false;
266         }
267
268         /* Also, if something else is being stopped and we should
269          * change state after it, then lets wait. */
270
271         SET_FOREACH(other, j->unit->meta.dependencies[UNIT_BEFORE], i)
272                 if (other->meta.job &&
273                     (other->meta.job->type == JOB_STOP ||
274                      other->meta.job->type == JOB_RESTART ||
275                      other->meta.job->type == JOB_TRY_RESTART))
276                         return false;
277
278         /* This means that for a service a and a service b where b
279          * shall be started after a:
280          *
281          *  start a + start b ā†’ 1st step start a, 2nd step start b
282          *  start a + stop b  ā†’ 1st step stop b,  2nd step start a
283          *  stop a  + start b ā†’ 1st step stop a,  2nd step start b
284          *  stop a  + stop b  ā†’ 1st step stop b,  2nd step stop a
285          *
286          *  This has the side effect that restarts are properly
287          *  synchronized too. */
288
289         return true;
290 }
291
292 int job_run_and_invalidate(Job *j) {
293         int r;
294
295         assert(j);
296         assert(j->installed);
297
298         if (j->in_run_queue) {
299                 LIST_REMOVE(Job, run_queue, j->manager->run_queue, j);
300                 j->in_run_queue = false;
301         }
302
303         if (j->state != JOB_WAITING)
304                 return 0;
305
306         if (!job_is_runnable(j))
307                 return -EAGAIN;
308
309         j->state = JOB_RUNNING;
310
311         switch (j->type) {
312
313                 case JOB_START:
314                         r = unit_start(j->unit);
315                         if (r == -EBADR)
316                                 r = 0;
317                         break;
318
319                 case JOB_VERIFY_ACTIVE: {
320                         UnitActiveState t = unit_active_state(j->unit);
321                         if (UNIT_IS_ACTIVE_OR_RELOADING(t))
322                                 r = -EALREADY;
323                         else if (t == UNIT_ACTIVATING)
324                                 r = -EAGAIN;
325                         else
326                                 r = -ENOEXEC;
327                         break;
328                 }
329
330                 case JOB_STOP:
331                         r = unit_stop(j->unit);
332                         break;
333
334                 case JOB_RELOAD:
335                         r = unit_reload(j->unit);
336                         break;
337
338                 case JOB_RELOAD_OR_START:
339                         if (unit_active_state(j->unit) == UNIT_ACTIVE)
340                                 r = unit_reload(j->unit);
341                         else
342                                 r = unit_start(j->unit);
343                         break;
344
345                 case JOB_RESTART: {
346                         UnitActiveState t = unit_active_state(j->unit);
347                         if (t == UNIT_INACTIVE || t == UNIT_ACTIVATING) {
348                                 j->type = JOB_START;
349                                 r = unit_start(j->unit);
350                         } else
351                                 r = unit_stop(j->unit);
352                         break;
353                 }
354
355                 case JOB_TRY_RESTART: {
356                         UnitActiveState t = unit_active_state(j->unit);
357                         if (t == UNIT_INACTIVE || t == UNIT_DEACTIVATING)
358                                 r = -ENOEXEC;
359                         else if (t == UNIT_ACTIVATING) {
360                                 j->type = JOB_START;
361                                 r = unit_start(j->unit);
362                         } else
363                                 r = unit_stop(j->unit);
364                         break;
365                 }
366
367                 default:
368                         assert_not_reached("Unknown job type");
369         }
370
371         if (r == -EALREADY)
372                 r = job_finish_and_invalidate(j, true);
373         else if (r == -EAGAIN) {
374                 j->state = JOB_WAITING;
375                 return -EAGAIN;
376         } else if (r < 0)
377                 r = job_finish_and_invalidate(j, false);
378
379         return r;
380 }
381
382 int job_finish_and_invalidate(Job *j, bool success) {
383         Unit *u;
384         Unit *other;
385         UnitType t;
386         Iterator i;
387
388         assert(j);
389         assert(j->installed);
390
391         log_debug("Job %s/%s finished, success=%s", unit_id(j->unit), job_type_to_string(j->type), yes_no(success));
392
393         /* Patch restart jobs so that they become normal start jobs */
394         if (success && (j->type == JOB_RESTART || j->type == JOB_TRY_RESTART)) {
395
396                 log_debug("Converting job %s/%s ā†’ %s/%s",
397                           unit_id(j->unit), job_type_to_string(j->type),
398                           unit_id(j->unit), job_type_to_string(JOB_START));
399
400                 j->state = JOB_RUNNING;
401                 j->type = JOB_START;
402
403                 job_schedule_run(j);
404                 return 0;
405         }
406
407         u = j->unit;
408         t = j->type;
409         job_free(j);
410
411         /* Fail depending jobs on failure */
412         if (!success) {
413
414                 if (t == JOB_START ||
415                     t == JOB_VERIFY_ACTIVE ||
416                     t == JOB_RELOAD_OR_START) {
417
418                         SET_FOREACH(other, u->meta.dependencies[UNIT_REQUIRED_BY], i)
419                                 if (other->meta.job &&
420                                     (other->meta.type == JOB_START ||
421                                      other->meta.type == JOB_VERIFY_ACTIVE ||
422                                      other->meta.type == JOB_RELOAD_OR_START))
423                                         job_finish_and_invalidate(other->meta.job, false);
424
425                         SET_FOREACH(other, u->meta.dependencies[UNIT_SOFT_REQUIRED_BY], i)
426                                 if (other->meta.job &&
427                                     !other->meta.job->forced &&
428                                     (other->meta.type == JOB_START ||
429                                      other->meta.type == JOB_VERIFY_ACTIVE ||
430                                      other->meta.type == JOB_RELOAD_OR_START))
431                                         job_finish_and_invalidate(other->meta.job, false);
432
433                 } else if (t == JOB_STOP) {
434
435                         SET_FOREACH(other, u->meta.dependencies[UNIT_CONFLICTS], i)
436                                 if (other->meta.job &&
437                                     (t == JOB_START ||
438                                      t == JOB_VERIFY_ACTIVE ||
439                                      t == JOB_RELOAD_OR_START))
440                                         job_finish_and_invalidate(other->meta.job, false);
441                 }
442         }
443
444         /* Try to start the next jobs that can be started */
445         SET_FOREACH(other, u->meta.dependencies[UNIT_AFTER], i)
446                 if (other->meta.job)
447                         job_schedule_run(other->meta.job);
448         SET_FOREACH(other, u->meta.dependencies[UNIT_BEFORE], i)
449                 if (other->meta.job)
450                         job_schedule_run(other->meta.job);
451
452         return 0;
453 }
454
455 void job_schedule_run(Job *j) {
456         assert(j);
457         assert(j->installed);
458
459         if (j->in_run_queue)
460                 return;
461
462         LIST_PREPEND(Job, run_queue, j->manager->run_queue, j);
463         j->in_run_queue = true;
464 }
465
466 char *job_dbus_path(Job *j) {
467         char *p;
468
469         assert(j);
470
471         if (asprintf(&p, "/org/freedesktop/systemd1/job/%lu", (unsigned long) j->id) < 0)
472                 return NULL;
473
474         return p;
475 }
476
477 static const char* const job_state_table[_JOB_STATE_MAX] = {
478         [JOB_WAITING] = "waiting",
479         [JOB_RUNNING] = "running"
480 };
481
482 DEFINE_STRING_TABLE_LOOKUP(job_state, JobState);
483
484 static const char* const job_type_table[_JOB_TYPE_MAX] = {
485         [JOB_START] = "start",
486         [JOB_VERIFY_ACTIVE] = "verify-active",
487         [JOB_STOP] = "stop",
488         [JOB_RELOAD] = "reload",
489         [JOB_RELOAD_OR_START] = "reload-or-start",
490         [JOB_RESTART] = "restart",
491         [JOB_TRY_RESTART] = "try-restart",
492 };
493
494 DEFINE_STRING_TABLE_LOOKUP(job_type, JobType);