1 /*-*- Mode: C; c-basic-offset: 8 -*-*/
7 #include <sys/timerfd.h>
14 #include "load-fragment.h"
15 #include "load-dropin.h"
18 const NameVTable
* const name_vtable
[_NAME_TYPE_MAX
] = {
19 [NAME_SERVICE
] = &service_vtable
,
20 [NAME_TIMER
] = &timer_vtable
,
21 [NAME_SOCKET
] = &socket_vtable
,
22 [NAME_TARGET
] = &target_vtable
,
23 [NAME_DEVICE
] = &device_vtable
,
24 [NAME_MOUNT
] = &mount_vtable
,
25 [NAME_AUTOMOUNT
] = &automount_vtable
,
26 [NAME_SNAPSHOT
] = &snapshot_vtable
29 NameType
name_type_from_string(const char *n
) {
34 for (t
= 0; t
< _NAME_TYPE_MAX
; t
++)
35 if (endswith(n
, name_vtable
[t
]->suffix
))
38 return _NAME_TYPE_INVALID
;
43 "abcdefghijklmnopqrstuvwxyz" \
44 "ABCDEFGHIJKLMNOPQRSTUVWXYZ" \
47 bool name_is_valid(const char *n
) {
53 if (strlen(n
) >= NAME_MAX
)
56 t
= name_type_from_string(n
);
57 if (t
< 0 || t
>= _NAME_TYPE_MAX
)
60 if (!(e
= strrchr(n
, '.')))
63 for (i
= n
; i
< e
; i
++)
64 if (!strchr(VALID_CHARS
, *i
))
70 Name
*name_new(Manager
*m
) {
75 if (!(n
= new0(Name
, 1)))
78 if (!(n
->meta
.names
= set_new(string_hash_func
, string_compare_func
))) {
84 n
->meta
.type
= _NAME_TYPE_INVALID
;
86 /* We don't link the name here, that is left for name_link() */
91 int name_add_name(Name
*n
, const char *text
) {
99 if ((t
= name_type_from_string(text
)) == _NAME_TYPE_INVALID
)
102 if (n
->meta
.type
!= _NAME_TYPE_INVALID
&& t
!= n
->meta
.type
)
105 if (!(s
= strdup(text
)))
108 if ((r
= set_put(n
->meta
.names
, s
)) < 0) {
121 /* FIXME: Does not rollback on failure! */
122 int name_link_names(Name
*n
, bool replace
) {
132 /* Link all names that aren't linked yet. */
134 SET_FOREACH(t
, n
->meta
.names
, i
)
136 if ((r
= hashmap_replace(n
->meta
.manager
->names
, t
, n
)) < 0)
139 if ((r
= hashmap_put(n
->meta
.manager
->names
, t
, n
)) < 0)
146 int name_link(Name
*n
) {
150 assert(!set_isempty(n
->meta
.names
));
151 assert(!n
->meta
.linked
);
153 if ((r
= name_sanitize(n
)) < 0)
156 n
->meta
.linked
= true;
158 if ((r
= name_link_names(n
, false)) < 0) {
162 /* Rollback the registered names */
163 SET_FOREACH(t
, n
->meta
.names
, i
)
164 hashmap_remove_value(n
->meta
.manager
->names
, t
, n
);
166 n
->meta
.linked
= false;
170 if (n
->meta
.load_state
== NAME_STUB
) {
171 LIST_PREPEND(Meta
, load_queue
, n
->meta
.manager
->load_queue
, &n
->meta
);
172 n
->meta
.in_load_queue
= true;
178 static void bidi_set_free(Name
*name
, Set
*s
) {
184 /* Frees the set and makes sure we are dropped from the
185 * inverse pointers */
187 if (name
->meta
.linked
) {
188 SET_FOREACH(other
, s
, i
) {
191 for (d
= 0; d
< _NAME_DEPENDENCY_MAX
; d
++)
192 set_remove(other
->meta
.dependencies
[d
], name
);
199 void name_free(Name
*name
) {
205 /* Detach from next 'bigger' objects */
206 if (name
->meta
.linked
) {
210 SET_FOREACH(t
, name
->meta
.names
, i
)
211 hashmap_remove_value(name
->meta
.manager
->names
, t
, name
);
213 if (name
->meta
.in_load_queue
)
214 LIST_REMOVE(Meta
, load_queue
, name
->meta
.manager
->load_queue
, &name
->meta
);
217 if (name
->meta
.load_state
== NAME_LOADED
)
218 if (NAME_VTABLE(name
)->done
)
219 NAME_VTABLE(name
)->done(name
);
221 /* Free data and next 'smaller' objects */
223 job_free(name
->meta
.job
);
225 for (d
= 0; d
< _NAME_DEPENDENCY_MAX
; d
++)
226 bidi_set_free(name
, name
->meta
.dependencies
[d
]);
228 free(name
->meta
.description
);
230 while ((t
= set_steal_first(name
->meta
.names
)))
232 set_free(name
->meta
.names
);
237 NameActiveState
name_active_state(Name
*name
) {
240 if (name
->meta
.load_state
!= NAME_LOADED
)
241 return NAME_INACTIVE
;
243 return NAME_VTABLE(name
)->active_state(name
);
246 static int ensure_merge(Set
**s
, Set
*other
) {
252 return set_merge(*s
, other
);
254 if (!(*s
= set_copy(other
)))
260 /* FIXME: Does not rollback on failure! */
261 int name_merge(Name
*name
, Name
*other
) {
267 assert(name
->meta
.manager
== other
->meta
.manager
);
269 /* This merges 'other' into 'name'. FIXME: This does not
270 * rollback on failure. */
272 if (name
->meta
.type
!= other
->meta
.type
)
275 if (other
->meta
.load_state
!= NAME_STUB
)
279 if ((r
= ensure_merge(&name
->meta
.names
, other
->meta
.names
)) < 0)
282 /* Merge dependencies */
283 for (d
= 0; d
< _NAME_DEPENDENCY_MAX
; d
++)
284 /* fixme, the inverse mapping is missing */
285 if ((r
= ensure_merge(&name
->meta
.dependencies
[d
], other
->meta
.dependencies
[d
])) < 0)
288 /* Hookup new deps and names */
289 if (name
->meta
.linked
) {
290 if ((r
= name_sanitize(name
)) < 0)
293 if ((r
= name_link_names(name
, true)) < 0)
300 int name_sanitize(Name
*n
) {
306 for (d
= 0; d
< _NAME_DEPENDENCY_MAX
; d
++)
307 set_remove(n
->meta
.dependencies
[d
], n
);
312 const char* name_id(Name
*n
) {
318 return set_first(n
->meta
.names
);
321 const char *name_description(Name
*n
) {
324 if (n
->meta
.description
)
325 return n
->meta
.description
;
330 void name_dump(Name
*n
, FILE *f
, const char *prefix
) {
332 static const char* const load_state_table
[_NAME_LOAD_STATE_MAX
] = {
333 [NAME_STUB
] = "stub",
334 [NAME_LOADED
] = "loaded",
335 [NAME_FAILED
] = "failed"
338 static const char* const active_state_table
[_NAME_ACTIVE_STATE_MAX
] = {
339 [NAME_ACTIVE
] = "active",
340 [NAME_INACTIVE
] = "inactive",
341 [NAME_ACTIVATING
] = "activating",
342 [NAME_DEACTIVATING
] = "deactivating"
345 static const char* const dependency_table
[_NAME_DEPENDENCY_MAX
] = {
346 [NAME_REQUIRES
] = "Requires",
347 [NAME_SOFT_REQUIRES
] = "SoftRequires",
348 [NAME_WANTS
] = "Wants",
349 [NAME_REQUISITE
] = "Requisite",
350 [NAME_SOFT_REQUISITE
] = "SoftRequisite",
351 [NAME_REQUIRED_BY
] = "RequiredBy",
352 [NAME_SOFT_REQUIRED_BY
] = "SoftRequiredBy",
353 [NAME_WANTED_BY
] = "WantedBy",
354 [NAME_CONFLICTS
] = "Conflicts",
355 [NAME_BEFORE
] = "Before",
356 [NAME_AFTER
] = "After",
368 prefix2
= strappend(prefix
, "\t");
374 "%s\tDescription: %s\n"
375 "%s\tName Load State: %s\n"
376 "%s\tName Active State: %s\n",
378 prefix
, name_description(n
),
379 prefix
, load_state_table
[n
->meta
.load_state
],
380 prefix
, active_state_table
[name_active_state(n
)]);
382 SET_FOREACH(t
, n
->meta
.names
, i
)
383 fprintf(f
, "%s\tName: %s\n", prefix
, t
);
385 for (d
= 0; d
< _NAME_DEPENDENCY_MAX
; d
++) {
388 if (set_isempty(n
->meta
.dependencies
[d
]))
391 SET_FOREACH(other
, n
->meta
.dependencies
[d
], i
)
392 fprintf(f
, "%s\t%s: %s\n", prefix
, dependency_table
[d
], name_id(other
));
395 if (NAME_VTABLE(n
)->dump
)
396 NAME_VTABLE(n
)->dump(n
, f
, prefix2
);
399 job_dump(n
->meta
.job
, f
, prefix2
);
404 static int verify_type(Name
*name
) {
410 /* Checks that all aliases of this name have the same and valid type */
412 SET_FOREACH(n
, name
->meta
.names
, i
) {
415 if ((t
= name_type_from_string(n
)) == _NAME_TYPE_INVALID
)
418 if (name
->meta
.type
== _NAME_TYPE_INVALID
) {
423 if (name
->meta
.type
!= t
)
427 if (name
->meta
.type
== _NAME_TYPE_INVALID
)
433 /* Common implementation for multiple backends */
434 int name_load_fragment_and_dropin(Name
*n
) {
439 /* Load a .socket file */
440 if ((r
= name_load_fragment(n
)) < 0)
443 /* Load drop-in directory data */
444 if ((r
= name_load_dropin(n
)) < 0)
450 int name_load(Name
*name
) {
455 if (name
->meta
.in_load_queue
) {
456 LIST_REMOVE(Meta
, load_queue
, name
->meta
.manager
->load_queue
, &name
->meta
);
457 name
->meta
.in_load_queue
= false;
460 if (name
->meta
.load_state
!= NAME_STUB
)
463 if ((r
= verify_type(name
)) < 0)
466 if (NAME_VTABLE(name
)->init
)
467 if ((r
= NAME_VTABLE(name
)->init(name
)) < 0)
470 if ((r
= name_sanitize(name
)) < 0)
473 if ((r
= name_link_names(name
, false)) < 0)
476 name
->meta
.load_state
= NAME_LOADED
;
480 if (NAME_VTABLE(name
)->done
)
481 NAME_VTABLE(name
)->done(name
);
484 name
->meta
.load_state
= NAME_FAILED
;
489 * -EBADR: This name type does not support starting.
490 * -EALREADY: Name is already started.
491 * -EAGAIN: An operation is already in progress. Retry later.
493 int name_start(Name
*n
) {
494 NameActiveState state
;
498 if (!NAME_VTABLE(n
)->start
)
501 state
= name_active_state(n
);
502 if (NAME_IS_ACTIVE_OR_RELOADING(state
))
505 /* We don't suppress calls to ->start() here when we are
506 * already starting, to allow this request to be used as a
507 * "hurry up" call, for example when the name is in some "auto
508 * restart" state where it waits for a holdoff timer to elapse
509 * before it will start again. */
511 return NAME_VTABLE(n
)->start(n
);
514 bool name_type_can_start(NameType t
) {
515 assert(t
>= 0 && t
< _NAME_TYPE_MAX
);
517 return !!name_vtable
[t
]->start
;
521 * -EBADR: This name type does not support stopping.
522 * -EALREADY: Name is already stopped.
523 * -EAGAIN: An operation is already in progress. Retry later.
525 int name_stop(Name
*n
) {
526 NameActiveState state
;
530 if (!NAME_VTABLE(n
)->stop
)
533 state
= name_active_state(n
);
534 if (state
== NAME_INACTIVE
)
537 if (state
== NAME_DEACTIVATING
)
540 return NAME_VTABLE(n
)->stop(n
);
544 * -EBADR: This name type does not support reloading.
545 * -ENOEXEC: Name is not started.
546 * -EAGAIN: An operation is already in progress. Retry later.
548 int name_reload(Name
*n
) {
549 NameActiveState state
;
553 if (!name_can_reload(n
))
556 state
= name_active_state(n
);
557 if (name_active_state(n
) == NAME_ACTIVE_RELOADING
)
560 if (name_active_state(n
) != NAME_ACTIVE
)
563 return NAME_VTABLE(n
)->reload(n
);
566 bool name_type_can_reload(NameType t
) {
567 assert(t
>= 0 && t
< _NAME_TYPE_MAX
);
569 return !!name_vtable
[t
]->reload
;
572 bool name_can_reload(Name
*n
) {
575 if (!name_type_can_reload(n
->meta
.type
))
578 if (!NAME_VTABLE(n
)->can_reload
)
581 return NAME_VTABLE(n
)->can_reload(n
);
584 static void retroactively_start_dependencies(Name
*n
) {
589 assert(NAME_IS_ACTIVE_OR_ACTIVATING(name_active_state(n
)));
591 SET_FOREACH(other
, n
->meta
.dependencies
[NAME_REQUIRES
], i
)
592 if (!NAME_IS_ACTIVE_OR_ACTIVATING(name_active_state(other
)))
593 manager_add_job(n
->meta
.manager
, JOB_START
, other
, JOB_REPLACE
, true, NULL
);
595 SET_FOREACH(other
, n
->meta
.dependencies
[NAME_SOFT_REQUIRES
], i
)
596 if (!NAME_IS_ACTIVE_OR_ACTIVATING(name_active_state(other
)))
597 manager_add_job(n
->meta
.manager
, JOB_START
, other
, JOB_FAIL
, false, NULL
);
599 SET_FOREACH(other
, n
->meta
.dependencies
[NAME_REQUISITE
], i
)
600 if (!NAME_IS_ACTIVE_OR_ACTIVATING(name_active_state(other
)))
601 manager_add_job(n
->meta
.manager
, JOB_START
, other
, JOB_REPLACE
, true, NULL
);
603 SET_FOREACH(other
, n
->meta
.dependencies
[NAME_WANTS
], i
)
604 if (!NAME_IS_ACTIVE_OR_ACTIVATING(name_active_state(other
)))
605 manager_add_job(n
->meta
.manager
, JOB_START
, other
, JOB_FAIL
, false, NULL
);
607 SET_FOREACH(other
, n
->meta
.dependencies
[NAME_CONFLICTS
], i
)
608 if (!NAME_IS_ACTIVE_OR_ACTIVATING(name_active_state(other
)))
609 manager_add_job(n
->meta
.manager
, JOB_STOP
, other
, JOB_REPLACE
, true, NULL
);
612 static void retroactively_stop_dependencies(Name
*n
) {
617 assert(NAME_IS_INACTIVE_OR_DEACTIVATING(name_active_state(n
)));
619 SET_FOREACH(other
, n
->meta
.dependencies
[NAME_REQUIRED_BY
], i
)
620 if (!NAME_IS_INACTIVE_OR_DEACTIVATING(name_active_state(other
)))
621 manager_add_job(n
->meta
.manager
, JOB_STOP
, other
, JOB_REPLACE
, true, NULL
);
624 void name_notify(Name
*n
, NameActiveState os
, NameActiveState ns
) {
626 assert(os
< _NAME_ACTIVE_STATE_MAX
);
627 assert(ns
< _NAME_ACTIVE_STATE_MAX
);
628 assert(!(os
== NAME_ACTIVE
&& ns
== NAME_ACTIVATING
));
629 assert(!(os
== NAME_INACTIVE
&& ns
== NAME_DEACTIVATING
));
634 if (!NAME_IS_ACTIVE_OR_RELOADING(os
) && NAME_IS_ACTIVE_OR_RELOADING(ns
))
635 n
->meta
.active_enter_timestamp
= now(CLOCK_REALTIME
);
636 else if (NAME_IS_ACTIVE_OR_RELOADING(os
) && !NAME_IS_ACTIVE_OR_RELOADING(ns
))
637 n
->meta
.active_exit_timestamp
= now(CLOCK_REALTIME
);
641 if (n
->meta
.job
->state
== JOB_WAITING
)
643 /* So we reached a different state for this
644 * job. Let's see if we can run it now if it
645 * failed previously due to EAGAIN. */
646 job_schedule_run(n
->meta
.job
);
649 assert(n
->meta
.job
->state
== JOB_RUNNING
);
651 /* Let's check of this state change
652 * constitutes a finished job, or maybe
653 * cotradicts a running job and hence needs to
654 * invalidate jobs. */
656 switch (n
->meta
.job
->type
) {
659 case JOB_VERIFY_ACTIVE
:
661 if (NAME_IS_ACTIVE_OR_RELOADING(ns
)) {
662 job_finish_and_invalidate(n
->meta
.job
, true);
664 } else if (ns
== NAME_ACTIVATING
)
667 job_finish_and_invalidate(n
->meta
.job
, false);
672 case JOB_RELOAD_OR_START
:
674 if (ns
== NAME_ACTIVE
) {
675 job_finish_and_invalidate(n
->meta
.job
, true);
677 } else if (ns
== NAME_ACTIVATING
|| ns
== NAME_ACTIVE_RELOADING
)
680 job_finish_and_invalidate(n
->meta
.job
, false);
686 case JOB_TRY_RESTART
:
688 if (ns
== NAME_INACTIVE
) {
689 job_finish_and_invalidate(n
->meta
.job
, true);
691 } else if (ns
== NAME_DEACTIVATING
)
694 job_finish_and_invalidate(n
->meta
.job
, false);
699 assert_not_reached("Job type unknown");
704 /* If this state change happened without being requested by a
705 * job, then let's retroactively start or stop dependencies */
707 if (NAME_IS_INACTIVE_OR_DEACTIVATING(os
) && NAME_IS_ACTIVE_OR_ACTIVATING(ns
))
708 retroactively_start_dependencies(n
);
709 else if (NAME_IS_ACTIVE_OR_ACTIVATING(os
) && NAME_IS_INACTIVE_OR_DEACTIVATING(ns
))
710 retroactively_stop_dependencies(n
);
713 int name_watch_fd(Name
*n
, int fd
, uint32_t events
) {
714 struct epoll_event ev
;
722 ev
.data
.u32
= MANAGER_FD
;
725 if (epoll_ctl(n
->meta
.manager
->epoll_fd
, EPOLL_CTL_ADD
, fd
, &ev
) >= 0)
729 if (epoll_ctl(n
->meta
.manager
->epoll_fd
, EPOLL_CTL_MOD
, fd
, &ev
) >= 0)
735 void name_unwatch_fd(Name
*n
, int fd
) {
739 assert_se(epoll_ctl(n
->meta
.manager
->epoll_fd
, EPOLL_CTL_DEL
, fd
, NULL
) >= 0 || errno
== ENOENT
);
742 int name_watch_pid(Name
*n
, pid_t pid
) {
746 return hashmap_put(n
->meta
.manager
->watch_pids
, UINT32_TO_PTR(pid
), n
);
749 void name_unwatch_pid(Name
*n
, pid_t pid
) {
753 hashmap_remove(n
->meta
.manager
->watch_pids
, UINT32_TO_PTR(pid
));
756 int name_watch_timer(Name
*n
, usec_t delay
, int *id
) {
757 struct epoll_event ev
;
759 struct itimerspec its
;
766 /* This will try to reuse the old timer if there is one */
775 if ((fd
= timerfd_create(CLOCK_MONOTONIC
, TFD_NONBLOCK
|TFD_CLOEXEC
)) < 0)
782 /* Set absolute time in the past, but not 0, since we
783 * don't want to disarm the timer */
784 its
.it_value
.tv_sec
= 0;
785 its
.it_value
.tv_nsec
= 1;
787 flags
= TFD_TIMER_ABSTIME
;
789 timespec_store(&its
.it_value
, delay
);
793 /* This will also flush the elapse counter */
794 if (timerfd_settime(fd
, flags
, &its
, NULL
) < 0)
800 ev
.data
.u32
= MANAGER_TIMER
;
803 if (epoll_ctl(n
->meta
.manager
->epoll_fd
, EPOLL_CTL_ADD
, fd
, &ev
) < 0)
811 assert_se(close_nointr(fd
) == 0);
816 void name_unwatch_timer(Name
*n
, int *id
) {
821 assert_se(epoll_ctl(n
->meta
.manager
->epoll_fd
, EPOLL_CTL_DEL
, *id
, NULL
) >= 0);
822 assert_se(close_nointr(*id
) == 0);
828 char *name_change_suffix(const char *t
, const char *suffix
) {
833 assert(name_is_valid(t
));
836 assert_se(e
= strrchr(t
, '.'));
840 if (!(n
= new(char, a
+ b
+ 1)))
844 memcpy(n
+a
, suffix
, b
+1);
849 bool name_job_is_applicable(Name
*n
, JobType j
) {
851 assert(j
>= 0 && j
< _JOB_TYPE_MAX
);
854 case JOB_VERIFY_ACTIVE
:
860 case JOB_TRY_RESTART
:
861 return name_can_start(n
);
864 return name_can_reload(n
);
866 case JOB_RELOAD_OR_START
:
867 return name_can_reload(n
) && name_can_start(n
);
870 assert_not_reached("Invalid job type");
874 int name_add_dependency(Name
*n
, NameDependency d
, Name
*other
) {
876 static const NameDependency inverse_table
[_NAME_DEPENDENCY_MAX
] = {
877 [NAME_REQUIRES
] = NAME_REQUIRED_BY
,
878 [NAME_SOFT_REQUIRES
] = NAME_SOFT_REQUIRED_BY
,
879 [NAME_WANTS
] = NAME_WANTED_BY
,
880 [NAME_REQUISITE
] = NAME_REQUIRED_BY
,
881 [NAME_SOFT_REQUISITE
] = NAME_SOFT_REQUIRED_BY
,
882 [NAME_REQUIRED_BY
] = _NAME_DEPENDENCY_INVALID
,
883 [NAME_SOFT_REQUIRED_BY
] = _NAME_DEPENDENCY_INVALID
,
884 [NAME_WANTED_BY
] = _NAME_DEPENDENCY_INVALID
,
885 [NAME_CONFLICTS
] = NAME_CONFLICTS
,
886 [NAME_BEFORE
] = NAME_AFTER
,
887 [NAME_AFTER
] = NAME_BEFORE
892 assert(d
>= 0 && d
< _NAME_DEPENDENCY_MAX
);
893 assert(inverse_table
[d
] != _NAME_DEPENDENCY_INVALID
);
896 if ((r
= set_ensure_allocated(&n
->meta
.dependencies
[d
], trivial_hash_func
, trivial_compare_func
)) < 0)
899 if ((r
= set_ensure_allocated(&other
->meta
.dependencies
[inverse_table
[d
]], trivial_hash_func
, trivial_compare_func
)) < 0)
902 if ((r
= set_put(n
->meta
.dependencies
[d
], other
)) < 0)
905 if ((r
= set_put(other
->meta
.dependencies
[inverse_table
[d
]], n
)) < 0) {
906 set_remove(n
->meta
.dependencies
[d
], other
);