]>
Commit | Line | Data |
---|---|---|
60918275 LP |
1 | /*-*- Mode: C; c-basic-offset: 8 -*-*/ |
2 | ||
3 | #include <assert.h> | |
4 | #include <errno.h> | |
07232470 | 5 | #include <string.h> |
60918275 LP |
6 | |
7 | #include "set.h" | |
8 | #include "name.h" | |
9 | #include "macro.h" | |
10 | #include "strv.h" | |
7fad411c | 11 | #include "load-fragment.h" |
5cb5a6ff LP |
12 | #include "load-dropin.h" |
13 | ||
14 | static const NameVTable * const name_vtable[_NAME_TYPE_MAX] = { | |
15 | [NAME_SERVICE] = &service_vtable, | |
16 | [NAME_TIMER] = &timer_vtable, | |
17 | [NAME_SOCKET] = &socket_vtable, | |
18 | [NAME_MILESTONE] = &milestone_vtable, | |
19 | [NAME_DEVICE] = &device_vtable, | |
20 | [NAME_MOUNT] = &mount_vtable, | |
21 | [NAME_AUTOMOUNT] = &automount_vtable, | |
22 | [NAME_SNAPSHOT] = &snapshot_vtable | |
23 | }; | |
24 | ||
25 | #define NAME_VTABLE(n) name_vtable[(n)->meta.type] | |
60918275 LP |
26 | |
27 | NameType name_type_from_string(const char *n) { | |
28 | NameType t; | |
60918275 LP |
29 | |
30 | assert(n); | |
31 | ||
32 | for (t = 0; t < _NAME_TYPE_MAX; t++) | |
5cb5a6ff | 33 | if (endswith(n, name_vtable[t]->suffix)) |
60918275 LP |
34 | return t; |
35 | ||
36 | return _NAME_TYPE_INVALID; | |
37 | } | |
38 | ||
07232470 LP |
39 | #define VALID_CHARS \ |
40 | "0123456789" \ | |
41 | "abcdefghijklmnopqrstuvwxyz" \ | |
42 | "ABCDEFGHIJKLMNOPQRSTUVWXYZ" \ | |
43 | "-_" | |
44 | ||
45 | bool name_is_valid(const char *n) { | |
46 | NameType t; | |
47 | const char *e, *i; | |
48 | ||
49 | assert(n); | |
50 | ||
5cb5a6ff LP |
51 | if (strlen(n) >= NAME_MAX) |
52 | return false; | |
53 | ||
07232470 LP |
54 | t = name_type_from_string(n); |
55 | if (t < 0 || t >= _NAME_TYPE_MAX) | |
56 | return false; | |
57 | ||
58 | if (!(e = strrchr(n, '.'))) | |
59 | return false; | |
60 | ||
61 | for (i = n; i < e; i++) | |
62 | if (!strchr(VALID_CHARS, *i)) | |
63 | return false; | |
64 | ||
65 | return true; | |
66 | } | |
67 | ||
60918275 LP |
68 | Name *name_new(Manager *m) { |
69 | Name *n; | |
70 | ||
71 | assert(m); | |
72 | ||
73 | if (!(n = new0(Name, 1))) | |
74 | return NULL; | |
75 | ||
87d1515d LP |
76 | if (!(n->meta.names = set_new(string_hash_func, string_compare_func))) { |
77 | free(n); | |
78 | return NULL; | |
79 | } | |
80 | ||
60918275 LP |
81 | /* Not much initialization happening here at this time */ |
82 | n->meta.manager = m; | |
83 | n->meta.type = _NAME_TYPE_INVALID; | |
60918275 LP |
84 | |
85 | /* We don't link the name here, that is left for name_link() */ | |
86 | ||
87 | return n; | |
88 | } | |
89 | ||
e5b5ae50 LP |
90 | /* FIXME: Does not rollback on failure! */ |
91 | int name_link_names(Name *n, bool replace) { | |
11dd41ce LP |
92 | char *t; |
93 | void *state; | |
94 | int r; | |
95 | ||
96 | assert(n); | |
97 | ||
98 | if (!n->meta.linked) | |
99 | return 0; | |
100 | ||
e5b5ae50 | 101 | /* Link all names that aren't linked yet. */ |
11dd41ce LP |
102 | |
103 | SET_FOREACH(t, n->meta.names, state) | |
e5b5ae50 LP |
104 | if (replace) { |
105 | if ((r = hashmap_replace(n->meta.manager->names, t, n)) < 0) | |
106 | return r; | |
107 | } else { | |
108 | if ((r = hashmap_put(n->meta.manager->names, t, n)) < 0) | |
109 | return r; | |
11dd41ce LP |
110 | } |
111 | ||
112 | return 0; | |
113 | } | |
114 | ||
60918275 | 115 | int name_link(Name *n) { |
60918275 LP |
116 | int r; |
117 | ||
118 | assert(n); | |
87d1515d | 119 | assert(!set_isempty(n->meta.names)); |
60918275 LP |
120 | assert(!n->meta.linked); |
121 | ||
7fad411c LP |
122 | if ((r = name_sanitize(n)) < 0) |
123 | return r; | |
124 | ||
11dd41ce | 125 | n->meta.linked = true; |
60918275 | 126 | |
7fad411c | 127 | if ((r = name_link_names(n, false)) < 0) { |
11dd41ce LP |
128 | char *t; |
129 | void *state; | |
60918275 | 130 | |
11dd41ce LP |
131 | /* Rollback the registered names */ |
132 | SET_FOREACH(t, n->meta.names, state) | |
e5b5ae50 | 133 | hashmap_remove_value(n->meta.manager->names, t, n); |
60918275 | 134 | |
11dd41ce LP |
135 | n->meta.linked = false; |
136 | return r; | |
137 | } | |
60918275 | 138 | |
5cb5a6ff | 139 | if (n->meta.load_state == NAME_STUB) |
11dd41ce | 140 | LIST_PREPEND(Meta, n->meta.manager->load_queue, &n->meta); |
60918275 | 141 | |
11dd41ce | 142 | return 0; |
60918275 LP |
143 | } |
144 | ||
87d1515d LP |
145 | static void bidi_set_free(Name *name, Set *s) { |
146 | void *state; | |
147 | Name *other; | |
148 | ||
149 | assert(name); | |
87d1515d LP |
150 | |
151 | /* Frees the set and makes sure we are dropped from the | |
152 | * inverse pointers */ | |
153 | ||
154 | SET_FOREACH(other, s, state) { | |
155 | NameDependency d; | |
156 | ||
157 | for (d = 0; d < _NAME_DEPENDENCY_MAX; d++) | |
158 | set_remove(other->meta.dependencies[d], name); | |
159 | } | |
160 | ||
161 | set_free(s); | |
162 | } | |
163 | ||
60918275 | 164 | void name_free(Name *name) { |
87d1515d LP |
165 | NameDependency d; |
166 | char *t; | |
60918275 LP |
167 | |
168 | assert(name); | |
169 | ||
170 | /* Detach from next 'bigger' objects */ | |
60918275 | 171 | if (name->meta.linked) { |
11dd41ce | 172 | char *t; |
87d1515d | 173 | void *state; |
60918275 | 174 | |
87d1515d | 175 | SET_FOREACH(t, name->meta.names, state) |
e5b5ae50 | 176 | hashmap_remove_value(name->meta.manager->names, t, name); |
60918275 | 177 | |
5cb5a6ff | 178 | if (name->meta.load_state == NAME_STUB) |
87d1515d | 179 | LIST_REMOVE(Meta, name->meta.manager->load_queue, &name->meta); |
60918275 LP |
180 | } |
181 | ||
182 | /* Free data and next 'smaller' objects */ | |
60918275 LP |
183 | if (name->meta.job) |
184 | job_free(name->meta.job); | |
185 | ||
87d1515d LP |
186 | for (d = 0; d < _NAME_DEPENDENCY_MAX; d++) |
187 | bidi_set_free(name, name->meta.dependencies[d]); | |
60918275 | 188 | |
5cb5a6ff LP |
189 | if (NAME_VTABLE(name)->free_hook) |
190 | NAME_VTABLE(name)->free_hook(name); | |
60918275 LP |
191 | |
192 | free(name->meta.description); | |
87d1515d LP |
193 | |
194 | while ((t = set_steal_first(name->meta.names))) | |
195 | free(t); | |
196 | set_free(name->meta.names); | |
60918275 LP |
197 | |
198 | free(name); | |
199 | } | |
200 | ||
5cb5a6ff | 201 | NameActiveState name_active_state(Name *name) { |
60918275 LP |
202 | assert(name); |
203 | ||
5cb5a6ff LP |
204 | if (name->meta.load_state != NAME_LOADED) |
205 | return NAME_INACTIVE; | |
60918275 | 206 | |
5cb5a6ff | 207 | return NAME_VTABLE(name)->active_state(name); |
60918275 LP |
208 | } |
209 | ||
210 | static int ensure_in_set(Set **s, void *data) { | |
211 | int r; | |
212 | ||
213 | assert(s); | |
214 | assert(data); | |
215 | ||
216 | if (!*s) | |
217 | if (!(*s = set_new(trivial_hash_func, trivial_compare_func))) | |
218 | return -ENOMEM; | |
219 | ||
7fad411c | 220 | if ((r = set_put(*s, data)) < 0) |
60918275 LP |
221 | if (r != -EEXIST) |
222 | return r; | |
223 | ||
224 | return 0; | |
225 | } | |
226 | ||
87d1515d LP |
227 | static int ensure_merge(Set **s, Set *other) { |
228 | ||
229 | if (!other) | |
230 | return 0; | |
231 | ||
232 | if (*s) | |
233 | return set_merge(*s, other); | |
234 | ||
235 | if (!(*s = set_copy(other))) | |
236 | return -ENOMEM; | |
237 | ||
238 | return 0; | |
239 | } | |
240 | ||
e5b5ae50 | 241 | /* FIXME: Does not rollback on failure! */ |
87d1515d LP |
242 | int name_merge(Name *name, Name *other) { |
243 | int r; | |
244 | NameDependency d; | |
245 | ||
246 | assert(name); | |
247 | assert(other); | |
87d1515d LP |
248 | assert(name->meta.manager == other->meta.manager); |
249 | ||
e5b5ae50 LP |
250 | /* This merges 'other' into 'name'. FIXME: This does not |
251 | * rollback on failure. */ | |
252 | ||
87d1515d LP |
253 | if (name->meta.type != other->meta.type) |
254 | return -EINVAL; | |
255 | ||
5cb5a6ff | 256 | if (other->meta.load_state != NAME_STUB) |
87d1515d LP |
257 | return -EINVAL; |
258 | ||
259 | /* Merge names */ | |
260 | if ((r = ensure_merge(&name->meta.names, other->meta.names)) < 0) | |
261 | return r; | |
262 | ||
263 | /* Merge dependencies */ | |
264 | for (d = 0; d < _NAME_DEPENDENCY_MAX; d++) | |
265 | if ((r = ensure_merge(&name->meta.dependencies[d], other->meta.dependencies[d])) < 0) | |
266 | return r; | |
267 | ||
e5b5ae50 LP |
268 | /* Hookup new deps and names */ |
269 | if (name->meta.linked) { | |
7fad411c | 270 | if ((r = name_sanitize(name)) < 0) |
11dd41ce LP |
271 | return r; |
272 | ||
e5b5ae50 LP |
273 | if ((r = name_link_names(name, true)) < 0) |
274 | return r; | |
275 | } | |
276 | ||
87d1515d LP |
277 | return 0; |
278 | } | |
a66d02c3 | 279 | |
7fad411c LP |
280 | /* FIXME: Does not rollback on failure! */ |
281 | static int augment(Name *n) { | |
282 | int r; | |
283 | void* state; | |
284 | Name *other; | |
285 | ||
286 | assert(n); | |
287 | ||
288 | /* Adds in the missing links to make all dependencies | |
289 | * bidirectional. */ | |
290 | ||
291 | SET_FOREACH(other, n->meta.dependencies[NAME_BEFORE], state) | |
292 | if ((r = ensure_in_set(&other->meta.dependencies[NAME_AFTER], n)) < 0) | |
293 | return r; | |
294 | SET_FOREACH(other, n->meta.dependencies[NAME_AFTER], state) | |
295 | if ((r = ensure_in_set(&other->meta.dependencies[NAME_BEFORE], n)) < 0) | |
296 | return r; | |
297 | ||
298 | SET_FOREACH(other, n->meta.dependencies[NAME_CONFLICTS], state) | |
299 | if ((r = ensure_in_set(&other->meta.dependencies[NAME_CONFLICTS], n)) < 0) | |
300 | return r; | |
301 | ||
302 | SET_FOREACH(other, n->meta.dependencies[NAME_REQUIRES], state) | |
303 | if ((r = ensure_in_set(&other->meta.dependencies[NAME_REQUIRED_BY], n)) < 0) | |
304 | return r; | |
305 | SET_FOREACH(other, n->meta.dependencies[NAME_REQUISITE], state) | |
306 | if ((r = ensure_in_set(&other->meta.dependencies[NAME_REQUIRED_BY], n)) < 0) | |
307 | return r; | |
308 | ||
7fad411c | 309 | SET_FOREACH(other, n->meta.dependencies[NAME_SOFT_REQUIRES], state) |
5cb5a6ff | 310 | if ((r = ensure_in_set(&other->meta.dependencies[NAME_SOFT_REQUIRED_BY], n)) < 0) |
7fad411c | 311 | return r; |
5cb5a6ff LP |
312 | |
313 | SET_FOREACH(other, n->meta.dependencies[NAME_WANTS], state) | |
7fad411c LP |
314 | if ((r = ensure_in_set(&other->meta.dependencies[NAME_WANTED_BY], n)) < 0) |
315 | return r; | |
316 | ||
317 | return 0; | |
318 | } | |
319 | ||
320 | int name_sanitize(Name *n) { | |
321 | NameDependency d; | |
322 | ||
323 | assert(n); | |
324 | ||
325 | /* Remove loops */ | |
326 | for (d = 0; d < _NAME_DEPENDENCY_MAX; d++) | |
327 | set_remove(n->meta.dependencies[d], n); | |
328 | ||
329 | return augment(n); | |
330 | } | |
331 | ||
a66d02c3 LP |
332 | const char* name_id(Name *n) { |
333 | assert(n); | |
334 | ||
335 | return set_first(n->meta.names); | |
336 | } | |
337 | ||
5cb5a6ff LP |
338 | const char *name_description(Name *n) { |
339 | assert(n); | |
340 | ||
341 | if (n->meta.description) | |
342 | return n->meta.description; | |
343 | ||
344 | return name_id(n); | |
345 | } | |
346 | ||
ceed3570 | 347 | void name_dump(Name *n, FILE *f, const char *prefix) { |
a66d02c3 | 348 | |
5cb5a6ff | 349 | static const char* const load_state_table[_NAME_LOAD_STATE_MAX] = { |
42f4e3c4 LP |
350 | [NAME_STUB] = "stub", |
351 | [NAME_LOADED] = "loaded", | |
352 | [NAME_FAILED] = "failed" | |
353 | }; | |
354 | ||
5cb5a6ff LP |
355 | static const char* const active_state_table[_NAME_ACTIVE_STATE_MAX] = { |
356 | [NAME_ACTIVE] = "active", | |
357 | [NAME_INACTIVE] = "inactive", | |
358 | [NAME_ACTIVATING] = "activating", | |
359 | [NAME_DEACTIVATING] = "deactivating" | |
a66d02c3 LP |
360 | }; |
361 | ||
7fad411c LP |
362 | static const char* const dependency_table[_NAME_DEPENDENCY_MAX] = { |
363 | [NAME_REQUIRES] = "Requires", | |
364 | [NAME_SOFT_REQUIRES] = "SoftRequires", | |
365 | [NAME_WANTS] = "Wants", | |
366 | [NAME_REQUISITE] = "Requisite", | |
367 | [NAME_SOFT_REQUISITE] = "SoftRequisite", | |
368 | [NAME_REQUIRED_BY] = "RequiredBy", | |
5cb5a6ff | 369 | [NAME_SOFT_REQUIRED_BY] = "SoftRequiredBy", |
542563ba | 370 | [NAME_WANTED_BY] = "WantedBy", |
7fad411c LP |
371 | [NAME_CONFLICTS] = "Conflicts", |
372 | [NAME_BEFORE] = "Before", | |
373 | [NAME_AFTER] = "After", | |
374 | }; | |
375 | ||
11dd41ce LP |
376 | void *state; |
377 | char *t; | |
7fad411c | 378 | NameDependency d; |
11dd41ce | 379 | |
a66d02c3 LP |
380 | assert(n); |
381 | ||
ceed3570 LP |
382 | if (!prefix) |
383 | prefix = ""; | |
384 | ||
11dd41ce | 385 | fprintf(f, |
ceed3570 LP |
386 | "%sName %s:\n" |
387 | "%s\tDescription: %s\n" | |
5cb5a6ff LP |
388 | "%s\tName Load State: %s\n" |
389 | "%s\tName Active State: %s\n", | |
ceed3570 | 390 | prefix, name_id(n), |
5cb5a6ff LP |
391 | prefix, name_description(n), |
392 | prefix, load_state_table[n->meta.load_state], | |
393 | prefix, active_state_table[name_active_state(n)]); | |
ceed3570 | 394 | |
11dd41ce | 395 | SET_FOREACH(t, n->meta.names, state) |
5cb5a6ff | 396 | fprintf(f, "%s\tName: %s\n", prefix, t); |
11dd41ce | 397 | |
7fad411c LP |
398 | for (d = 0; d < _NAME_DEPENDENCY_MAX; d++) { |
399 | void *state; | |
400 | Name *other; | |
401 | ||
402 | if (set_isempty(n->meta.dependencies[d])) | |
403 | continue; | |
404 | ||
7fad411c | 405 | SET_FOREACH(other, n->meta.dependencies[d], state) |
5cb5a6ff | 406 | fprintf(f, "%s\t%s: %s\n", prefix, dependency_table[d], name_id(other)); |
7fad411c LP |
407 | } |
408 | ||
5cb5a6ff LP |
409 | if (NAME_VTABLE(n)->dump) |
410 | NAME_VTABLE(n)->dump(n, f, prefix); | |
42f4e3c4 | 411 | |
a66d02c3 | 412 | if (n->meta.job) { |
ceed3570 LP |
413 | char *p; |
414 | ||
415 | if (asprintf(&p, "%s\t", prefix) >= 0) | |
416 | prefix = p; | |
417 | else | |
418 | p = NULL; | |
419 | ||
420 | job_dump(n->meta.job, f, prefix); | |
421 | free(p); | |
a66d02c3 LP |
422 | } |
423 | } | |
7fad411c LP |
424 | |
425 | static int verify_type(Name *name) { | |
426 | char *n; | |
427 | void *state; | |
428 | ||
429 | assert(name); | |
430 | ||
431 | /* Checks that all aliases of this name have the same and valid type */ | |
432 | ||
433 | SET_FOREACH(n, name->meta.names, state) { | |
434 | NameType t; | |
435 | ||
436 | if ((t = name_type_from_string(n)) == _NAME_TYPE_INVALID) | |
437 | return -EINVAL; | |
438 | ||
439 | if (name->meta.type == _NAME_TYPE_INVALID) { | |
440 | name->meta.type = t; | |
441 | continue; | |
442 | } | |
443 | ||
444 | if (name->meta.type != t) | |
445 | return -EINVAL; | |
446 | } | |
447 | ||
448 | if (name->meta.type == _NAME_TYPE_INVALID) | |
449 | return -EINVAL; | |
450 | ||
451 | return 0; | |
452 | } | |
453 | ||
5cb5a6ff LP |
454 | /* Common implementation for multiple backends */ |
455 | int name_load_fragment_and_dropin(Name *n) { | |
456 | int r; | |
7fad411c | 457 | |
5cb5a6ff | 458 | assert(n); |
7fad411c | 459 | |
5cb5a6ff LP |
460 | /* Load a .socket file */ |
461 | if ((r = name_load_fragment(n)) < 0) | |
462 | return r; | |
463 | ||
464 | /* Load drop-in directory data */ | |
465 | if ((r = name_load_dropin(n)) < 0) | |
466 | return r; | |
467 | ||
468 | return 0; | |
7fad411c LP |
469 | } |
470 | ||
5cb5a6ff LP |
471 | int name_load(Name *name) { |
472 | int r; | |
473 | ||
474 | assert(name); | |
475 | ||
476 | if (name->meta.load_state != NAME_STUB) | |
477 | return 0; | |
7fad411c | 478 | |
5cb5a6ff LP |
479 | if ((r = verify_type(name)) < 0) |
480 | return r; | |
481 | ||
482 | if (NAME_VTABLE(name)->load) | |
483 | if ((r = NAME_VTABLE(name)->load(name)) < 0) | |
484 | goto fail; | |
7fad411c | 485 | |
5cb5a6ff LP |
486 | if ((r = name_sanitize(name)) < 0) |
487 | goto fail; | |
488 | ||
489 | if ((r = name_link_names(name, false)) < 0) | |
490 | goto fail; | |
491 | ||
492 | name->meta.load_state = NAME_LOADED; | |
7fad411c | 493 | return 0; |
5cb5a6ff LP |
494 | |
495 | fail: | |
496 | name->meta.load_state = NAME_FAILED; | |
497 | return r; | |
7fad411c LP |
498 | } |
499 | ||
5cb5a6ff LP |
500 | /* Errors: |
501 | * -EBADR: This name type does not support starting. | |
502 | * -EALREADY: Name is already started. | |
503 | * -EAGAIN: An operation is already in progress. Retry later. | |
504 | */ | |
505 | int name_start(Name *n) { | |
506 | NameActiveState state; | |
507 | ||
508 | assert(n); | |
7fad411c | 509 | |
5cb5a6ff LP |
510 | if (!NAME_VTABLE(n)->start) |
511 | return -EBADR; | |
7fad411c | 512 | |
5cb5a6ff LP |
513 | state = name_active_state(n); |
514 | if (NAME_IS_ACTIVE_OR_RELOADING(state)) | |
515 | return -EALREADY; | |
516 | ||
517 | if (state == NAME_ACTIVATING) | |
518 | return 0; | |
519 | ||
520 | return NAME_VTABLE(n)->start(n); | |
7fad411c LP |
521 | } |
522 | ||
5cb5a6ff LP |
523 | bool name_type_can_start(NameType t) { |
524 | assert(t >= 0 && t < _NAME_TYPE_MAX); | |
525 | ||
526 | return !!name_vtable[t]->start; | |
527 | } | |
528 | ||
529 | /* Errors: | |
530 | * -EBADR: This name type does not support stopping. | |
531 | * -EALREADY: Name is already stopped. | |
532 | * -EAGAIN: An operation is already in progress. Retry later. | |
533 | */ | |
534 | int name_stop(Name *n) { | |
535 | NameActiveState state; | |
536 | ||
7fad411c LP |
537 | assert(n); |
538 | ||
5cb5a6ff LP |
539 | if (!NAME_VTABLE(n)->stop) |
540 | return -EBADR; | |
7fad411c | 541 | |
5cb5a6ff LP |
542 | state = name_active_state(n); |
543 | if (state == NAME_INACTIVE) | |
544 | return -EALREADY; | |
545 | ||
546 | if (state == NAME_DEACTIVATING) | |
547 | return 0; | |
548 | ||
549 | return NAME_VTABLE(n)->stop(n); | |
7fad411c LP |
550 | } |
551 | ||
5cb5a6ff LP |
552 | /* Errors: |
553 | * -EBADR: This name type does not support reloading. | |
554 | * -ENOEXEC: Name is not started. | |
555 | * -EAGAIN: An operation is already in progress. Retry later. | |
556 | */ | |
557 | int name_reload(Name *n) { | |
558 | NameActiveState state; | |
7fad411c | 559 | |
5cb5a6ff LP |
560 | assert(n); |
561 | ||
562 | if (!NAME_VTABLE(n)->reload) | |
563 | return -EBADR; | |
564 | ||
565 | state = name_active_state(n); | |
566 | if (name_active_state(n) == NAME_ACTIVE_RELOADING) | |
567 | return -EALREADY; | |
568 | ||
569 | if (name_active_state(n) != NAME_ACTIVE) | |
570 | return -ENOEXEC; | |
571 | ||
572 | return NAME_VTABLE(n)->reload(n); | |
573 | } | |
7fad411c | 574 | |
5cb5a6ff LP |
575 | bool name_type_can_reload(NameType t) { |
576 | assert(t >= 0 && t < _NAME_TYPE_MAX); | |
577 | return !!name_vtable[t]->reload; | |
578 | } | |
579 | ||
580 | static void retroactively_start_dependencies(Name *n) { | |
581 | void *state; | |
582 | Name *other; | |
583 | ||
584 | assert(n); | |
585 | assert(NAME_IS_ACTIVE_OR_ACTIVATING(name_active_state(n))); | |
586 | ||
587 | SET_FOREACH(other, n->meta.dependencies[NAME_REQUIRES], state) | |
588 | if (!NAME_IS_ACTIVE_OR_ACTIVATING(name_active_state(other))) | |
589 | manager_add_job(n->meta.manager, JOB_START, other, JOB_REPLACE, true, NULL); | |
590 | ||
591 | SET_FOREACH(other, n->meta.dependencies[NAME_SOFT_REQUIRES], state) | |
592 | if (!NAME_IS_ACTIVE_OR_ACTIVATING(name_active_state(other))) | |
593 | manager_add_job(n->meta.manager, JOB_START, other, JOB_FAIL, false, NULL); | |
594 | ||
595 | SET_FOREACH(other, n->meta.dependencies[NAME_REQUISITE], state) | |
596 | if (!NAME_IS_ACTIVE_OR_ACTIVATING(name_active_state(other))) | |
597 | manager_add_job(n->meta.manager, JOB_START, other, JOB_REPLACE, true, NULL); | |
598 | ||
599 | SET_FOREACH(other, n->meta.dependencies[NAME_WANTS], state) | |
600 | if (!NAME_IS_ACTIVE_OR_ACTIVATING(name_active_state(other))) | |
601 | manager_add_job(n->meta.manager, JOB_START, other, JOB_FAIL, false, NULL); | |
602 | ||
603 | SET_FOREACH(other, n->meta.dependencies[NAME_CONFLICTS], state) | |
604 | if (!NAME_IS_ACTIVE_OR_ACTIVATING(name_active_state(other))) | |
605 | manager_add_job(n->meta.manager, JOB_STOP, other, JOB_REPLACE, true, NULL); | |
606 | } | |
607 | ||
608 | static void retroactively_stop_dependencies(Name *n) { | |
609 | void *state; | |
610 | Name *other; | |
611 | ||
612 | assert(n); | |
613 | assert(NAME_IS_INACTIVE_OR_DEACTIVATING(name_active_state(n))); | |
614 | ||
615 | SET_FOREACH(other, n->meta.dependencies[NAME_REQUIRED_BY], state) | |
616 | if (!NAME_IS_INACTIVE_OR_DEACTIVATING(name_active_state(other))) | |
617 | manager_add_job(n->meta.manager, JOB_STOP, other, JOB_REPLACE, true, NULL); | |
618 | } | |
619 | ||
620 | int name_notify(Name *n, NameActiveState os, NameActiveState ns) { | |
621 | assert(n); | |
622 | assert(os < _NAME_ACTIVE_STATE_MAX); | |
623 | assert(ns < _NAME_ACTIVE_STATE_MAX); | |
624 | assert(!(os == NAME_ACTIVE && ns == NAME_ACTIVATING)); | |
625 | assert(!(os == NAME_INACTIVE && ns == NAME_DEACTIVATING)); | |
626 | ||
627 | if (os == ns) | |
7fad411c LP |
628 | return 0; |
629 | ||
5cb5a6ff | 630 | if (n->meta.job) { |
7fad411c | 631 | |
5cb5a6ff | 632 | if (n->meta.job->state == JOB_WAITING) |
7fad411c | 633 | |
5cb5a6ff LP |
634 | /* So we reached a different state for this |
635 | * job. Let's see if we can run it now if it | |
636 | * failed previously due to EAGAIN. */ | |
637 | job_run_and_invalidate(n->meta.job); | |
7fad411c | 638 | |
5cb5a6ff LP |
639 | else { |
640 | assert(n->meta.job->state == JOB_RUNNING); | |
7fad411c | 641 | |
5cb5a6ff LP |
642 | /* Let's check of this state change |
643 | * constitutes a finished job, or maybe | |
644 | * cotradicts a running job and hence needs to | |
645 | * invalidate jobs. */ | |
7fad411c | 646 | |
5cb5a6ff | 647 | switch (n->meta.job->type) { |
7fad411c | 648 | |
5cb5a6ff LP |
649 | case JOB_START: |
650 | case JOB_VERIFY_ACTIVE: | |
7fad411c | 651 | |
5cb5a6ff LP |
652 | if (NAME_IS_ACTIVE_OR_RELOADING(ns)) |
653 | return job_finish_and_invalidate(n->meta.job, true); | |
654 | else if (ns == NAME_ACTIVATING) | |
655 | return 0; | |
656 | else | |
657 | job_finish_and_invalidate(n->meta.job, false); | |
7fad411c | 658 | |
5cb5a6ff | 659 | break; |
7fad411c | 660 | |
5cb5a6ff LP |
661 | case JOB_RELOAD: |
662 | case JOB_RELOAD_OR_START: | |
7fad411c | 663 | |
5cb5a6ff LP |
664 | if (ns == NAME_ACTIVE) |
665 | return job_finish_and_invalidate(n->meta.job, true); | |
666 | else if (ns == NAME_ACTIVATING || ns == NAME_ACTIVE_RELOADING) | |
667 | return 0; | |
668 | else | |
669 | job_finish_and_invalidate(n->meta.job, false); | |
7fad411c | 670 | |
5cb5a6ff | 671 | break; |
7fad411c | 672 | |
5cb5a6ff LP |
673 | case JOB_STOP: |
674 | case JOB_RESTART: | |
675 | case JOB_TRY_RESTART: | |
676 | ||
677 | if (ns == NAME_INACTIVE) | |
678 | return job_finish_and_invalidate(n->meta.job, true); | |
679 | else if (ns == NAME_DEACTIVATING) | |
680 | return 0; | |
681 | else | |
682 | job_finish_and_invalidate(n->meta.job, false); | |
683 | ||
684 | break; | |
685 | ||
686 | default: | |
687 | assert_not_reached("Job type unknown"); | |
688 | } | |
689 | } | |
690 | } | |
691 | ||
692 | /* If this state change happened without being requested by a | |
693 | * job, then let's retroactively start or stop dependencies */ | |
694 | ||
695 | if (NAME_IS_INACTIVE_OR_DEACTIVATING(os) && NAME_IS_ACTIVE_OR_ACTIVATING(ns)) | |
696 | retroactively_start_dependencies(n); | |
697 | else if (NAME_IS_ACTIVE_OR_ACTIVATING(os) && NAME_IS_INACTIVE_OR_DEACTIVATING(ns)) | |
698 | retroactively_stop_dependencies(n); | |
7fad411c | 699 | |
7fad411c LP |
700 | return 0; |
701 | } |