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