]>
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> |
9152c765 | 6 | #include <sys/epoll.h> |
034c6ed7 LP |
7 | #include <sys/timerfd.h> |
8 | #include <sys/poll.h> | |
60918275 LP |
9 | |
10 | #include "set.h" | |
11 | #include "name.h" | |
12 | #include "macro.h" | |
13 | #include "strv.h" | |
7fad411c | 14 | #include "load-fragment.h" |
5cb5a6ff LP |
15 | #include "load-dropin.h" |
16 | ||
9152c765 | 17 | const NameVTable * const name_vtable[_NAME_TYPE_MAX] = { |
5cb5a6ff LP |
18 | [NAME_SERVICE] = &service_vtable, |
19 | [NAME_TIMER] = &timer_vtable, | |
20 | [NAME_SOCKET] = &socket_vtable, | |
21 | [NAME_MILESTONE] = &milestone_vtable, | |
22 | [NAME_DEVICE] = &device_vtable, | |
23 | [NAME_MOUNT] = &mount_vtable, | |
24 | [NAME_AUTOMOUNT] = &automount_vtable, | |
25 | [NAME_SNAPSHOT] = &snapshot_vtable | |
26 | }; | |
27 | ||
60918275 LP |
28 | NameType name_type_from_string(const char *n) { |
29 | NameType t; | |
60918275 LP |
30 | |
31 | assert(n); | |
32 | ||
33 | for (t = 0; t < _NAME_TYPE_MAX; t++) | |
5cb5a6ff | 34 | if (endswith(n, name_vtable[t]->suffix)) |
60918275 LP |
35 | return t; |
36 | ||
37 | return _NAME_TYPE_INVALID; | |
38 | } | |
39 | ||
07232470 LP |
40 | #define VALID_CHARS \ |
41 | "0123456789" \ | |
42 | "abcdefghijklmnopqrstuvwxyz" \ | |
43 | "ABCDEFGHIJKLMNOPQRSTUVWXYZ" \ | |
44 | "-_" | |
45 | ||
46 | bool name_is_valid(const char *n) { | |
47 | NameType t; | |
48 | const char *e, *i; | |
49 | ||
50 | assert(n); | |
51 | ||
5cb5a6ff LP |
52 | if (strlen(n) >= NAME_MAX) |
53 | return false; | |
54 | ||
07232470 LP |
55 | t = name_type_from_string(n); |
56 | if (t < 0 || t >= _NAME_TYPE_MAX) | |
57 | return false; | |
58 | ||
59 | if (!(e = strrchr(n, '.'))) | |
60 | return false; | |
61 | ||
62 | for (i = n; i < e; i++) | |
63 | if (!strchr(VALID_CHARS, *i)) | |
64 | return false; | |
65 | ||
66 | return true; | |
67 | } | |
68 | ||
60918275 LP |
69 | Name *name_new(Manager *m) { |
70 | Name *n; | |
71 | ||
72 | assert(m); | |
73 | ||
74 | if (!(n = new0(Name, 1))) | |
75 | return NULL; | |
76 | ||
87d1515d LP |
77 | if (!(n->meta.names = set_new(string_hash_func, string_compare_func))) { |
78 | free(n); | |
79 | return NULL; | |
80 | } | |
81 | ||
60918275 LP |
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 | ||
034c6ed7 LP |
90 | int name_add_name(Name *n, const char *text) { |
91 | NameType t; | |
92 | char *s; | |
93 | int r; | |
94 | ||
95 | assert(n); | |
96 | assert(text); | |
97 | ||
98 | if ((t = name_type_from_string(text)) == _NAME_TYPE_INVALID) | |
99 | return -EINVAL; | |
100 | ||
101 | if (n->meta.type != _NAME_TYPE_INVALID && t != n->meta.type) | |
102 | return -EINVAL; | |
103 | ||
104 | if (!(s = strdup(text))) | |
105 | return -ENOMEM; | |
106 | ||
107 | if ((r = set_put(n->meta.names, s)) < 0) { | |
108 | free(s); | |
109 | return r; | |
110 | } | |
111 | ||
112 | n->meta.type = t; | |
113 | ||
114 | if (!n->meta.id) | |
115 | n->meta.id = s; | |
116 | ||
117 | return 0; | |
118 | } | |
119 | ||
e5b5ae50 LP |
120 | /* FIXME: Does not rollback on failure! */ |
121 | int name_link_names(Name *n, bool replace) { | |
11dd41ce | 122 | char *t; |
034c6ed7 | 123 | Iterator i; |
11dd41ce LP |
124 | int r; |
125 | ||
126 | assert(n); | |
127 | ||
128 | if (!n->meta.linked) | |
129 | return 0; | |
130 | ||
e5b5ae50 | 131 | /* Link all names that aren't linked yet. */ |
11dd41ce | 132 | |
034c6ed7 | 133 | SET_FOREACH(t, n->meta.names, i) |
e5b5ae50 LP |
134 | if (replace) { |
135 | if ((r = hashmap_replace(n->meta.manager->names, t, n)) < 0) | |
136 | return r; | |
137 | } else { | |
138 | if ((r = hashmap_put(n->meta.manager->names, t, n)) < 0) | |
139 | return r; | |
11dd41ce LP |
140 | } |
141 | ||
142 | return 0; | |
143 | } | |
144 | ||
60918275 | 145 | int name_link(Name *n) { |
60918275 LP |
146 | int r; |
147 | ||
148 | assert(n); | |
87d1515d | 149 | assert(!set_isempty(n->meta.names)); |
60918275 LP |
150 | assert(!n->meta.linked); |
151 | ||
7fad411c LP |
152 | if ((r = name_sanitize(n)) < 0) |
153 | return r; | |
154 | ||
11dd41ce | 155 | n->meta.linked = true; |
60918275 | 156 | |
7fad411c | 157 | if ((r = name_link_names(n, false)) < 0) { |
11dd41ce | 158 | char *t; |
034c6ed7 | 159 | Iterator i; |
60918275 | 160 | |
11dd41ce | 161 | /* Rollback the registered names */ |
034c6ed7 | 162 | SET_FOREACH(t, n->meta.names, i) |
e5b5ae50 | 163 | hashmap_remove_value(n->meta.manager->names, t, n); |
60918275 | 164 | |
11dd41ce LP |
165 | n->meta.linked = false; |
166 | return r; | |
167 | } | |
60918275 | 168 | |
034c6ed7 LP |
169 | if (n->meta.load_state == NAME_STUB) { |
170 | LIST_PREPEND(Meta, load_queue, n->meta.manager->load_queue, &n->meta); | |
171 | n->meta.in_load_queue = true; | |
172 | } | |
60918275 | 173 | |
11dd41ce | 174 | return 0; |
60918275 LP |
175 | } |
176 | ||
87d1515d | 177 | static void bidi_set_free(Name *name, Set *s) { |
034c6ed7 | 178 | Iterator i; |
87d1515d LP |
179 | Name *other; |
180 | ||
181 | assert(name); | |
87d1515d LP |
182 | |
183 | /* Frees the set and makes sure we are dropped from the | |
184 | * inverse pointers */ | |
185 | ||
034c6ed7 | 186 | SET_FOREACH(other, s, i) { |
87d1515d LP |
187 | NameDependency d; |
188 | ||
189 | for (d = 0; d < _NAME_DEPENDENCY_MAX; d++) | |
190 | set_remove(other->meta.dependencies[d], name); | |
191 | } | |
192 | ||
193 | set_free(s); | |
194 | } | |
195 | ||
60918275 | 196 | void name_free(Name *name) { |
87d1515d LP |
197 | NameDependency d; |
198 | char *t; | |
60918275 LP |
199 | |
200 | assert(name); | |
201 | ||
202 | /* Detach from next 'bigger' objects */ | |
60918275 | 203 | if (name->meta.linked) { |
11dd41ce | 204 | char *t; |
034c6ed7 | 205 | Iterator i; |
60918275 | 206 | |
034c6ed7 | 207 | SET_FOREACH(t, name->meta.names, i) |
e5b5ae50 | 208 | hashmap_remove_value(name->meta.manager->names, t, name); |
60918275 | 209 | |
034c6ed7 LP |
210 | if (name->meta.in_load_queue) |
211 | LIST_REMOVE(Meta, load_queue, name->meta.manager->load_queue, &name->meta); | |
60918275 LP |
212 | } |
213 | ||
214 | /* Free data and next 'smaller' objects */ | |
60918275 LP |
215 | if (name->meta.job) |
216 | job_free(name->meta.job); | |
217 | ||
87d1515d LP |
218 | for (d = 0; d < _NAME_DEPENDENCY_MAX; d++) |
219 | bidi_set_free(name, name->meta.dependencies[d]); | |
60918275 | 220 | |
034c6ed7 LP |
221 | if (NAME_VTABLE(name)->done) |
222 | NAME_VTABLE(name)->done(name); | |
60918275 LP |
223 | |
224 | free(name->meta.description); | |
87d1515d LP |
225 | |
226 | while ((t = set_steal_first(name->meta.names))) | |
227 | free(t); | |
228 | set_free(name->meta.names); | |
60918275 LP |
229 | |
230 | free(name); | |
231 | } | |
232 | ||
5cb5a6ff | 233 | NameActiveState name_active_state(Name *name) { |
60918275 LP |
234 | assert(name); |
235 | ||
5cb5a6ff LP |
236 | if (name->meta.load_state != NAME_LOADED) |
237 | return NAME_INACTIVE; | |
60918275 | 238 | |
5cb5a6ff | 239 | return NAME_VTABLE(name)->active_state(name); |
60918275 LP |
240 | } |
241 | ||
242 | static int ensure_in_set(Set **s, void *data) { | |
243 | int r; | |
244 | ||
245 | assert(s); | |
246 | assert(data); | |
247 | ||
034c6ed7 LP |
248 | if ((r = set_ensure_allocated(s, trivial_hash_func, trivial_compare_func)) < 0) |
249 | return r; | |
60918275 | 250 | |
7fad411c | 251 | if ((r = set_put(*s, data)) < 0) |
034c6ed7 | 252 | return r; |
60918275 LP |
253 | |
254 | return 0; | |
255 | } | |
256 | ||
87d1515d LP |
257 | static int ensure_merge(Set **s, Set *other) { |
258 | ||
259 | if (!other) | |
260 | return 0; | |
261 | ||
262 | if (*s) | |
263 | return set_merge(*s, other); | |
264 | ||
265 | if (!(*s = set_copy(other))) | |
266 | return -ENOMEM; | |
267 | ||
268 | return 0; | |
269 | } | |
270 | ||
e5b5ae50 | 271 | /* FIXME: Does not rollback on failure! */ |
87d1515d LP |
272 | int name_merge(Name *name, Name *other) { |
273 | int r; | |
274 | NameDependency d; | |
275 | ||
276 | assert(name); | |
277 | assert(other); | |
87d1515d LP |
278 | assert(name->meta.manager == other->meta.manager); |
279 | ||
e5b5ae50 LP |
280 | /* This merges 'other' into 'name'. FIXME: This does not |
281 | * rollback on failure. */ | |
282 | ||
87d1515d LP |
283 | if (name->meta.type != other->meta.type) |
284 | return -EINVAL; | |
285 | ||
5cb5a6ff | 286 | if (other->meta.load_state != NAME_STUB) |
87d1515d LP |
287 | return -EINVAL; |
288 | ||
289 | /* Merge names */ | |
290 | if ((r = ensure_merge(&name->meta.names, other->meta.names)) < 0) | |
291 | return r; | |
292 | ||
293 | /* Merge dependencies */ | |
294 | for (d = 0; d < _NAME_DEPENDENCY_MAX; d++) | |
295 | if ((r = ensure_merge(&name->meta.dependencies[d], other->meta.dependencies[d])) < 0) | |
296 | return r; | |
297 | ||
e5b5ae50 LP |
298 | /* Hookup new deps and names */ |
299 | if (name->meta.linked) { | |
7fad411c | 300 | if ((r = name_sanitize(name)) < 0) |
11dd41ce LP |
301 | return r; |
302 | ||
e5b5ae50 LP |
303 | if ((r = name_link_names(name, true)) < 0) |
304 | return r; | |
305 | } | |
306 | ||
87d1515d LP |
307 | return 0; |
308 | } | |
a66d02c3 | 309 | |
7fad411c LP |
310 | /* FIXME: Does not rollback on failure! */ |
311 | static int augment(Name *n) { | |
312 | int r; | |
034c6ed7 | 313 | Iterator i; |
7fad411c LP |
314 | Name *other; |
315 | ||
316 | assert(n); | |
317 | ||
318 | /* Adds in the missing links to make all dependencies | |
319 | * bidirectional. */ | |
320 | ||
034c6ed7 | 321 | SET_FOREACH(other, n->meta.dependencies[NAME_BEFORE], i) |
7fad411c LP |
322 | if ((r = ensure_in_set(&other->meta.dependencies[NAME_AFTER], n)) < 0) |
323 | return r; | |
034c6ed7 | 324 | SET_FOREACH(other, n->meta.dependencies[NAME_AFTER], i) |
7fad411c LP |
325 | if ((r = ensure_in_set(&other->meta.dependencies[NAME_BEFORE], n)) < 0) |
326 | return r; | |
327 | ||
034c6ed7 | 328 | SET_FOREACH(other, n->meta.dependencies[NAME_CONFLICTS], i) |
7fad411c LP |
329 | if ((r = ensure_in_set(&other->meta.dependencies[NAME_CONFLICTS], n)) < 0) |
330 | return r; | |
331 | ||
034c6ed7 | 332 | SET_FOREACH(other, n->meta.dependencies[NAME_REQUIRES], i) |
7fad411c LP |
333 | if ((r = ensure_in_set(&other->meta.dependencies[NAME_REQUIRED_BY], n)) < 0) |
334 | return r; | |
034c6ed7 | 335 | SET_FOREACH(other, n->meta.dependencies[NAME_REQUISITE], i) |
7fad411c LP |
336 | if ((r = ensure_in_set(&other->meta.dependencies[NAME_REQUIRED_BY], n)) < 0) |
337 | return r; | |
338 | ||
034c6ed7 | 339 | SET_FOREACH(other, n->meta.dependencies[NAME_SOFT_REQUIRES], i) |
5cb5a6ff | 340 | if ((r = ensure_in_set(&other->meta.dependencies[NAME_SOFT_REQUIRED_BY], n)) < 0) |
7fad411c | 341 | return r; |
5cb5a6ff | 342 | |
034c6ed7 | 343 | SET_FOREACH(other, n->meta.dependencies[NAME_WANTS], i) |
7fad411c LP |
344 | if ((r = ensure_in_set(&other->meta.dependencies[NAME_WANTED_BY], n)) < 0) |
345 | return r; | |
346 | ||
347 | return 0; | |
348 | } | |
349 | ||
350 | int name_sanitize(Name *n) { | |
351 | NameDependency d; | |
352 | ||
353 | assert(n); | |
354 | ||
355 | /* Remove loops */ | |
356 | for (d = 0; d < _NAME_DEPENDENCY_MAX; d++) | |
357 | set_remove(n->meta.dependencies[d], n); | |
358 | ||
359 | return augment(n); | |
360 | } | |
361 | ||
a66d02c3 LP |
362 | const char* name_id(Name *n) { |
363 | assert(n); | |
364 | ||
034c6ed7 LP |
365 | if (n->meta.id) |
366 | return n->meta.id; | |
367 | ||
a66d02c3 LP |
368 | return set_first(n->meta.names); |
369 | } | |
370 | ||
5cb5a6ff LP |
371 | const char *name_description(Name *n) { |
372 | assert(n); | |
373 | ||
374 | if (n->meta.description) | |
375 | return n->meta.description; | |
376 | ||
377 | return name_id(n); | |
378 | } | |
379 | ||
ceed3570 | 380 | void name_dump(Name *n, FILE *f, const char *prefix) { |
a66d02c3 | 381 | |
5cb5a6ff | 382 | static const char* const load_state_table[_NAME_LOAD_STATE_MAX] = { |
42f4e3c4 LP |
383 | [NAME_STUB] = "stub", |
384 | [NAME_LOADED] = "loaded", | |
385 | [NAME_FAILED] = "failed" | |
386 | }; | |
387 | ||
5cb5a6ff LP |
388 | static const char* const active_state_table[_NAME_ACTIVE_STATE_MAX] = { |
389 | [NAME_ACTIVE] = "active", | |
390 | [NAME_INACTIVE] = "inactive", | |
391 | [NAME_ACTIVATING] = "activating", | |
392 | [NAME_DEACTIVATING] = "deactivating" | |
a66d02c3 LP |
393 | }; |
394 | ||
7fad411c LP |
395 | static const char* const dependency_table[_NAME_DEPENDENCY_MAX] = { |
396 | [NAME_REQUIRES] = "Requires", | |
397 | [NAME_SOFT_REQUIRES] = "SoftRequires", | |
398 | [NAME_WANTS] = "Wants", | |
399 | [NAME_REQUISITE] = "Requisite", | |
400 | [NAME_SOFT_REQUISITE] = "SoftRequisite", | |
401 | [NAME_REQUIRED_BY] = "RequiredBy", | |
5cb5a6ff | 402 | [NAME_SOFT_REQUIRED_BY] = "SoftRequiredBy", |
542563ba | 403 | [NAME_WANTED_BY] = "WantedBy", |
7fad411c LP |
404 | [NAME_CONFLICTS] = "Conflicts", |
405 | [NAME_BEFORE] = "Before", | |
406 | [NAME_AFTER] = "After", | |
407 | }; | |
408 | ||
11dd41ce | 409 | char *t; |
7fad411c | 410 | NameDependency d; |
034c6ed7 | 411 | Iterator i; |
11dd41ce | 412 | |
a66d02c3 LP |
413 | assert(n); |
414 | ||
ceed3570 LP |
415 | if (!prefix) |
416 | prefix = ""; | |
417 | ||
11dd41ce | 418 | fprintf(f, |
ceed3570 LP |
419 | "%sName %s:\n" |
420 | "%s\tDescription: %s\n" | |
5cb5a6ff LP |
421 | "%s\tName Load State: %s\n" |
422 | "%s\tName Active State: %s\n", | |
ceed3570 | 423 | prefix, name_id(n), |
5cb5a6ff LP |
424 | prefix, name_description(n), |
425 | prefix, load_state_table[n->meta.load_state], | |
426 | prefix, active_state_table[name_active_state(n)]); | |
ceed3570 | 427 | |
034c6ed7 | 428 | SET_FOREACH(t, n->meta.names, i) |
5cb5a6ff | 429 | fprintf(f, "%s\tName: %s\n", prefix, t); |
11dd41ce | 430 | |
7fad411c | 431 | for (d = 0; d < _NAME_DEPENDENCY_MAX; d++) { |
7fad411c LP |
432 | Name *other; |
433 | ||
434 | if (set_isempty(n->meta.dependencies[d])) | |
435 | continue; | |
436 | ||
034c6ed7 | 437 | SET_FOREACH(other, n->meta.dependencies[d], i) |
5cb5a6ff | 438 | fprintf(f, "%s\t%s: %s\n", prefix, dependency_table[d], name_id(other)); |
7fad411c LP |
439 | } |
440 | ||
5cb5a6ff LP |
441 | if (NAME_VTABLE(n)->dump) |
442 | NAME_VTABLE(n)->dump(n, f, prefix); | |
42f4e3c4 | 443 | |
a66d02c3 | 444 | if (n->meta.job) { |
ceed3570 LP |
445 | char *p; |
446 | ||
447 | if (asprintf(&p, "%s\t", prefix) >= 0) | |
448 | prefix = p; | |
449 | else | |
450 | p = NULL; | |
451 | ||
452 | job_dump(n->meta.job, f, prefix); | |
453 | free(p); | |
a66d02c3 LP |
454 | } |
455 | } | |
7fad411c LP |
456 | |
457 | static int verify_type(Name *name) { | |
458 | char *n; | |
034c6ed7 | 459 | Iterator i; |
7fad411c LP |
460 | |
461 | assert(name); | |
462 | ||
463 | /* Checks that all aliases of this name have the same and valid type */ | |
464 | ||
034c6ed7 | 465 | SET_FOREACH(n, name->meta.names, i) { |
7fad411c LP |
466 | NameType t; |
467 | ||
468 | if ((t = name_type_from_string(n)) == _NAME_TYPE_INVALID) | |
469 | return -EINVAL; | |
470 | ||
471 | if (name->meta.type == _NAME_TYPE_INVALID) { | |
472 | name->meta.type = t; | |
473 | continue; | |
474 | } | |
475 | ||
476 | if (name->meta.type != t) | |
477 | return -EINVAL; | |
478 | } | |
479 | ||
480 | if (name->meta.type == _NAME_TYPE_INVALID) | |
481 | return -EINVAL; | |
482 | ||
483 | return 0; | |
484 | } | |
485 | ||
5cb5a6ff LP |
486 | /* Common implementation for multiple backends */ |
487 | int name_load_fragment_and_dropin(Name *n) { | |
488 | int r; | |
7fad411c | 489 | |
5cb5a6ff | 490 | assert(n); |
7fad411c | 491 | |
5cb5a6ff LP |
492 | /* Load a .socket file */ |
493 | if ((r = name_load_fragment(n)) < 0) | |
494 | return r; | |
495 | ||
496 | /* Load drop-in directory data */ | |
497 | if ((r = name_load_dropin(n)) < 0) | |
498 | return r; | |
499 | ||
500 | return 0; | |
7fad411c LP |
501 | } |
502 | ||
5cb5a6ff LP |
503 | int name_load(Name *name) { |
504 | int r; | |
505 | ||
506 | assert(name); | |
507 | ||
034c6ed7 LP |
508 | if (name->meta.in_load_queue) { |
509 | LIST_REMOVE(Meta, load_queue, name->meta.manager->load_queue, &name->meta); | |
510 | name->meta.in_load_queue = false; | |
511 | } | |
512 | ||
5cb5a6ff LP |
513 | if (name->meta.load_state != NAME_STUB) |
514 | return 0; | |
7fad411c | 515 | |
5cb5a6ff LP |
516 | if ((r = verify_type(name)) < 0) |
517 | return r; | |
518 | ||
034c6ed7 LP |
519 | if (NAME_VTABLE(name)->init) |
520 | if ((r = NAME_VTABLE(name)->init(name)) < 0) | |
5cb5a6ff | 521 | goto fail; |
7fad411c | 522 | |
5cb5a6ff LP |
523 | if ((r = name_sanitize(name)) < 0) |
524 | goto fail; | |
525 | ||
526 | if ((r = name_link_names(name, false)) < 0) | |
527 | goto fail; | |
528 | ||
529 | name->meta.load_state = NAME_LOADED; | |
7fad411c | 530 | return 0; |
5cb5a6ff LP |
531 | |
532 | fail: | |
533 | name->meta.load_state = NAME_FAILED; | |
534 | return r; | |
7fad411c LP |
535 | } |
536 | ||
5cb5a6ff LP |
537 | /* Errors: |
538 | * -EBADR: This name type does not support starting. | |
539 | * -EALREADY: Name is already started. | |
540 | * -EAGAIN: An operation is already in progress. Retry later. | |
541 | */ | |
542 | int name_start(Name *n) { | |
543 | NameActiveState state; | |
544 | ||
545 | assert(n); | |
7fad411c | 546 | |
5cb5a6ff LP |
547 | if (!NAME_VTABLE(n)->start) |
548 | return -EBADR; | |
7fad411c | 549 | |
5cb5a6ff LP |
550 | state = name_active_state(n); |
551 | if (NAME_IS_ACTIVE_OR_RELOADING(state)) | |
552 | return -EALREADY; | |
553 | ||
034c6ed7 LP |
554 | /* We don't suppress calls to ->start() here when we are |
555 | * already starting, to allow this request to be used as a | |
556 | * "hurry up" call, for example when the name is in some "auto | |
557 | * restart" state where it waits for a holdoff timer to elapse | |
558 | * before it will start again. */ | |
5cb5a6ff LP |
559 | |
560 | return NAME_VTABLE(n)->start(n); | |
7fad411c LP |
561 | } |
562 | ||
5cb5a6ff LP |
563 | bool name_type_can_start(NameType t) { |
564 | assert(t >= 0 && t < _NAME_TYPE_MAX); | |
565 | ||
566 | return !!name_vtable[t]->start; | |
567 | } | |
568 | ||
569 | /* Errors: | |
570 | * -EBADR: This name type does not support stopping. | |
571 | * -EALREADY: Name is already stopped. | |
572 | * -EAGAIN: An operation is already in progress. Retry later. | |
573 | */ | |
574 | int name_stop(Name *n) { | |
575 | NameActiveState state; | |
576 | ||
7fad411c LP |
577 | assert(n); |
578 | ||
5cb5a6ff LP |
579 | if (!NAME_VTABLE(n)->stop) |
580 | return -EBADR; | |
7fad411c | 581 | |
5cb5a6ff LP |
582 | state = name_active_state(n); |
583 | if (state == NAME_INACTIVE) | |
584 | return -EALREADY; | |
585 | ||
586 | if (state == NAME_DEACTIVATING) | |
587 | return 0; | |
588 | ||
589 | return NAME_VTABLE(n)->stop(n); | |
7fad411c LP |
590 | } |
591 | ||
5cb5a6ff LP |
592 | /* Errors: |
593 | * -EBADR: This name type does not support reloading. | |
594 | * -ENOEXEC: Name is not started. | |
595 | * -EAGAIN: An operation is already in progress. Retry later. | |
596 | */ | |
597 | int name_reload(Name *n) { | |
598 | NameActiveState state; | |
7fad411c | 599 | |
5cb5a6ff LP |
600 | assert(n); |
601 | ||
034c6ed7 | 602 | if (!name_can_reload(n)) |
5cb5a6ff LP |
603 | return -EBADR; |
604 | ||
605 | state = name_active_state(n); | |
606 | if (name_active_state(n) == NAME_ACTIVE_RELOADING) | |
607 | return -EALREADY; | |
608 | ||
609 | if (name_active_state(n) != NAME_ACTIVE) | |
610 | return -ENOEXEC; | |
611 | ||
612 | return NAME_VTABLE(n)->reload(n); | |
613 | } | |
7fad411c | 614 | |
5cb5a6ff LP |
615 | bool name_type_can_reload(NameType t) { |
616 | assert(t >= 0 && t < _NAME_TYPE_MAX); | |
034c6ed7 | 617 | |
5cb5a6ff LP |
618 | return !!name_vtable[t]->reload; |
619 | } | |
620 | ||
034c6ed7 LP |
621 | bool name_can_reload(Name *n) { |
622 | assert(n); | |
623 | ||
624 | if (!name_type_can_reload(n->meta.type)) | |
625 | return false; | |
626 | ||
627 | if (!NAME_VTABLE(n)->can_reload) | |
628 | return true; | |
629 | ||
630 | return NAME_VTABLE(n)->can_reload(n); | |
631 | } | |
632 | ||
5cb5a6ff | 633 | static void retroactively_start_dependencies(Name *n) { |
034c6ed7 | 634 | Iterator i; |
5cb5a6ff LP |
635 | Name *other; |
636 | ||
637 | assert(n); | |
638 | assert(NAME_IS_ACTIVE_OR_ACTIVATING(name_active_state(n))); | |
639 | ||
034c6ed7 | 640 | SET_FOREACH(other, n->meta.dependencies[NAME_REQUIRES], i) |
5cb5a6ff LP |
641 | if (!NAME_IS_ACTIVE_OR_ACTIVATING(name_active_state(other))) |
642 | manager_add_job(n->meta.manager, JOB_START, other, JOB_REPLACE, true, NULL); | |
643 | ||
034c6ed7 | 644 | SET_FOREACH(other, n->meta.dependencies[NAME_SOFT_REQUIRES], i) |
5cb5a6ff LP |
645 | if (!NAME_IS_ACTIVE_OR_ACTIVATING(name_active_state(other))) |
646 | manager_add_job(n->meta.manager, JOB_START, other, JOB_FAIL, false, NULL); | |
647 | ||
034c6ed7 | 648 | SET_FOREACH(other, n->meta.dependencies[NAME_REQUISITE], i) |
5cb5a6ff LP |
649 | if (!NAME_IS_ACTIVE_OR_ACTIVATING(name_active_state(other))) |
650 | manager_add_job(n->meta.manager, JOB_START, other, JOB_REPLACE, true, NULL); | |
651 | ||
034c6ed7 | 652 | SET_FOREACH(other, n->meta.dependencies[NAME_WANTS], i) |
5cb5a6ff LP |
653 | if (!NAME_IS_ACTIVE_OR_ACTIVATING(name_active_state(other))) |
654 | manager_add_job(n->meta.manager, JOB_START, other, JOB_FAIL, false, NULL); | |
655 | ||
034c6ed7 | 656 | SET_FOREACH(other, n->meta.dependencies[NAME_CONFLICTS], i) |
5cb5a6ff LP |
657 | if (!NAME_IS_ACTIVE_OR_ACTIVATING(name_active_state(other))) |
658 | manager_add_job(n->meta.manager, JOB_STOP, other, JOB_REPLACE, true, NULL); | |
659 | } | |
660 | ||
661 | static void retroactively_stop_dependencies(Name *n) { | |
034c6ed7 | 662 | Iterator i; |
5cb5a6ff LP |
663 | Name *other; |
664 | ||
665 | assert(n); | |
666 | assert(NAME_IS_INACTIVE_OR_DEACTIVATING(name_active_state(n))); | |
667 | ||
034c6ed7 | 668 | SET_FOREACH(other, n->meta.dependencies[NAME_REQUIRED_BY], i) |
5cb5a6ff LP |
669 | if (!NAME_IS_INACTIVE_OR_DEACTIVATING(name_active_state(other))) |
670 | manager_add_job(n->meta.manager, JOB_STOP, other, JOB_REPLACE, true, NULL); | |
671 | } | |
672 | ||
83c60c9f | 673 | void name_notify(Name *n, NameActiveState os, NameActiveState ns) { |
5cb5a6ff LP |
674 | assert(n); |
675 | assert(os < _NAME_ACTIVE_STATE_MAX); | |
676 | assert(ns < _NAME_ACTIVE_STATE_MAX); | |
677 | assert(!(os == NAME_ACTIVE && ns == NAME_ACTIVATING)); | |
678 | assert(!(os == NAME_INACTIVE && ns == NAME_DEACTIVATING)); | |
679 | ||
680 | if (os == ns) | |
83c60c9f | 681 | return; |
7fad411c | 682 | |
034c6ed7 LP |
683 | if (!NAME_IS_ACTIVE_OR_RELOADING(os) && NAME_IS_ACTIVE_OR_RELOADING(ns)) |
684 | n->meta.active_enter_timestamp = now(CLOCK_REALTIME); | |
685 | else if (NAME_IS_ACTIVE_OR_RELOADING(os) && !NAME_IS_ACTIVE_OR_RELOADING(ns)) | |
686 | n->meta.active_exit_timestamp = now(CLOCK_REALTIME); | |
687 | ||
5cb5a6ff | 688 | if (n->meta.job) { |
7fad411c | 689 | |
5cb5a6ff | 690 | if (n->meta.job->state == JOB_WAITING) |
7fad411c | 691 | |
5cb5a6ff LP |
692 | /* So we reached a different state for this |
693 | * job. Let's see if we can run it now if it | |
694 | * failed previously due to EAGAIN. */ | |
034c6ed7 | 695 | job_schedule_run(n->meta.job); |
7fad411c | 696 | |
5cb5a6ff LP |
697 | else { |
698 | assert(n->meta.job->state == JOB_RUNNING); | |
7fad411c | 699 | |
5cb5a6ff LP |
700 | /* Let's check of this state change |
701 | * constitutes a finished job, or maybe | |
702 | * cotradicts a running job and hence needs to | |
703 | * invalidate jobs. */ | |
7fad411c | 704 | |
5cb5a6ff | 705 | switch (n->meta.job->type) { |
7fad411c | 706 | |
5cb5a6ff LP |
707 | case JOB_START: |
708 | case JOB_VERIFY_ACTIVE: | |
7fad411c | 709 | |
83c60c9f LP |
710 | if (NAME_IS_ACTIVE_OR_RELOADING(ns)) { |
711 | job_finish_and_invalidate(n->meta.job, true); | |
712 | return; | |
713 | } else if (ns == NAME_ACTIVATING) | |
714 | return; | |
5cb5a6ff LP |
715 | else |
716 | job_finish_and_invalidate(n->meta.job, false); | |
7fad411c | 717 | |
5cb5a6ff | 718 | break; |
7fad411c | 719 | |
5cb5a6ff LP |
720 | case JOB_RELOAD: |
721 | case JOB_RELOAD_OR_START: | |
7fad411c | 722 | |
83c60c9f LP |
723 | if (ns == NAME_ACTIVE) { |
724 | job_finish_and_invalidate(n->meta.job, true); | |
725 | return; | |
726 | } else if (ns == NAME_ACTIVATING || ns == NAME_ACTIVE_RELOADING) | |
727 | return; | |
5cb5a6ff LP |
728 | else |
729 | job_finish_and_invalidate(n->meta.job, false); | |
7fad411c | 730 | |
5cb5a6ff | 731 | break; |
7fad411c | 732 | |
5cb5a6ff LP |
733 | case JOB_STOP: |
734 | case JOB_RESTART: | |
735 | case JOB_TRY_RESTART: | |
736 | ||
83c60c9f LP |
737 | if (ns == NAME_INACTIVE) { |
738 | job_finish_and_invalidate(n->meta.job, true); | |
739 | return; | |
740 | } else if (ns == NAME_DEACTIVATING) | |
741 | return; | |
5cb5a6ff LP |
742 | else |
743 | job_finish_and_invalidate(n->meta.job, false); | |
744 | ||
745 | break; | |
746 | ||
747 | default: | |
748 | assert_not_reached("Job type unknown"); | |
749 | } | |
750 | } | |
751 | } | |
752 | ||
753 | /* If this state change happened without being requested by a | |
754 | * job, then let's retroactively start or stop dependencies */ | |
755 | ||
756 | if (NAME_IS_INACTIVE_OR_DEACTIVATING(os) && NAME_IS_ACTIVE_OR_ACTIVATING(ns)) | |
757 | retroactively_start_dependencies(n); | |
758 | else if (NAME_IS_ACTIVE_OR_ACTIVATING(os) && NAME_IS_INACTIVE_OR_DEACTIVATING(ns)) | |
759 | retroactively_stop_dependencies(n); | |
7fad411c | 760 | } |
9152c765 LP |
761 | |
762 | int name_watch_fd(Name *n, int fd, uint32_t events) { | |
763 | struct epoll_event ev; | |
764 | ||
765 | assert(n); | |
766 | assert(fd >= 0); | |
767 | ||
768 | zero(ev); | |
769 | ev.data.fd = fd; | |
770 | ev.data.ptr = n; | |
034c6ed7 | 771 | ev.data.u32 = MANAGER_FD; |
9152c765 LP |
772 | ev.events = events; |
773 | ||
034c6ed7 LP |
774 | if (epoll_ctl(n->meta.manager->epoll_fd, EPOLL_CTL_ADD, fd, &ev) >= 0) |
775 | return 0; | |
9152c765 | 776 | |
034c6ed7 LP |
777 | if (errno == EEXIST) |
778 | if (epoll_ctl(n->meta.manager->epoll_fd, EPOLL_CTL_MOD, fd, &ev) >= 0) | |
779 | return 0; | |
780 | ||
781 | return -errno; | |
9152c765 LP |
782 | } |
783 | ||
784 | void name_unwatch_fd(Name *n, int fd) { | |
785 | assert(n); | |
786 | assert(fd >= 0); | |
787 | ||
788 | assert_se(epoll_ctl(n->meta.manager->epoll_fd, EPOLL_CTL_DEL, fd, NULL) >= 0 || errno == ENOENT); | |
789 | } | |
790 | ||
791 | int name_watch_pid(Name *n, pid_t pid) { | |
792 | assert(n); | |
793 | assert(pid >= 1); | |
794 | ||
795 | return hashmap_put(n->meta.manager->watch_pids, UINT32_TO_PTR(pid), n); | |
796 | } | |
797 | ||
798 | void name_unwatch_pid(Name *n, pid_t pid) { | |
799 | assert(n); | |
800 | assert(pid >= 1); | |
801 | ||
802 | hashmap_remove(n->meta.manager->watch_pids, UINT32_TO_PTR(pid)); | |
803 | } | |
034c6ed7 LP |
804 | |
805 | int name_watch_timer(Name *n, usec_t delay, int *id) { | |
806 | struct epoll_event ev; | |
807 | int fd; | |
808 | struct itimerspec its; | |
809 | int flags; | |
810 | bool ours; | |
811 | ||
812 | assert(n); | |
813 | assert(id); | |
814 | ||
815 | /* This will try to reuse the old timer if there is one */ | |
816 | ||
817 | if (*id >= 0) { | |
818 | ours = false; | |
819 | fd = *id; | |
820 | ||
821 | } else { | |
822 | ours = true; | |
823 | ||
824 | if ((fd = timerfd_create(CLOCK_MONOTONIC, TFD_NONBLOCK|TFD_CLOEXEC)) < 0) | |
825 | return -errno; | |
826 | } | |
827 | ||
828 | zero(its); | |
829 | ||
830 | if (delay <= 0) { | |
831 | /* Set absolute time in the past, but not 0, since we | |
832 | * don't want to disarm the timer */ | |
833 | its.it_value.tv_sec = 0; | |
834 | its.it_value.tv_nsec = 1; | |
835 | ||
836 | flags = TFD_TIMER_ABSTIME; | |
837 | } else { | |
838 | timespec_store(&its.it_value, delay); | |
839 | flags = 0; | |
840 | } | |
841 | ||
842 | /* This will also flush the elapse counter */ | |
843 | if (timerfd_settime(fd, flags, &its, NULL) < 0) | |
844 | goto fail; | |
845 | ||
846 | zero(ev); | |
847 | ev.data.fd = fd; | |
848 | ev.data.ptr = n; | |
849 | ev.data.u32 = MANAGER_TIMER; | |
850 | ev.events = POLLIN; | |
851 | ||
852 | if (epoll_ctl(n->meta.manager->epoll_fd, EPOLL_CTL_ADD, fd, &ev) < 0) | |
853 | goto fail; | |
854 | ||
855 | *id = fd; | |
856 | return 0; | |
857 | ||
858 | fail: | |
859 | if (ours) | |
860 | assert_se(close_nointr(fd) == 0); | |
861 | ||
862 | return -errno; | |
863 | } | |
864 | ||
865 | void name_unwatch_timer(Name *n, int *id) { | |
866 | assert(n); | |
867 | assert(id); | |
868 | ||
869 | if (*id >= 0) { | |
870 | assert_se(epoll_ctl(n->meta.manager->epoll_fd, EPOLL_CTL_DEL, *id, NULL) >= 0); | |
871 | assert_se(close_nointr(*id) == 0); | |
872 | ||
873 | *id = -1; | |
874 | } | |
875 | } | |
876 | ||
877 | char *name_change_suffix(const char *t, const char *suffix) { | |
878 | char *e, *n; | |
879 | size_t a, b; | |
880 | ||
881 | assert(t); | |
882 | assert(name_is_valid(t)); | |
883 | assert(suffix); | |
884 | ||
885 | assert_se(e = strrchr(t, '.')); | |
886 | a = e - t; | |
887 | b = strlen(suffix); | |
888 | ||
889 | if (!(n = new(char, a + b + 1))) | |
890 | return NULL; | |
891 | ||
892 | memcpy(n, t, a); | |
893 | memcpy(n+a, t, b+1); | |
894 | ||
895 | return n; | |
896 | } | |
897 | ||
898 | bool name_job_is_applicable(Name *n, JobType j) { | |
899 | assert(n); | |
900 | assert(j >= 0 && j < _JOB_TYPE_MAX); | |
901 | ||
902 | switch (j) { | |
903 | case JOB_VERIFY_ACTIVE: | |
904 | case JOB_START: | |
905 | return true; | |
906 | ||
907 | case JOB_STOP: | |
908 | case JOB_RESTART: | |
909 | case JOB_TRY_RESTART: | |
910 | return name_can_start(n); | |
911 | ||
912 | case JOB_RELOAD: | |
913 | return name_can_reload(n); | |
914 | ||
915 | case JOB_RELOAD_OR_START: | |
916 | return name_can_reload(n) && name_can_start(n); | |
917 | ||
918 | default: | |
919 | assert_not_reached("Invalid job type"); | |
920 | } | |
921 | } |