1 /* SPDX-License-Identifier: LGPL-2.1-or-later */
5 #include <sys/inotify.h>
10 #include "dbus-path.h"
11 #include "dbus-unit.h"
13 #include "event-util.h"
15 #include "glob-util.h"
16 #include "inotify-util.h"
18 #include "mkdir-label.h"
20 #include "path-util.h"
21 #include "serialize.h"
23 #include "stat-util.h"
24 #include "string-table.h"
25 #include "string-util.h"
26 #include "unit-name.h"
29 static const UnitActiveState state_translation_table
[_PATH_STATE_MAX
] = {
30 [PATH_DEAD
] = UNIT_INACTIVE
,
31 [PATH_WAITING
] = UNIT_ACTIVE
,
32 [PATH_RUNNING
] = UNIT_ACTIVE
,
33 [PATH_FAILED
] = UNIT_FAILED
,
36 static int path_dispatch_io(sd_event_source
*source
, int fd
, uint32_t revents
, void *userdata
);
38 int path_spec_watch(PathSpec
*s
, sd_event_io_handler_t handler
) {
39 static const int flags_table
[_PATH_TYPE_MAX
] = {
40 [PATH_EXISTS
] = IN_DELETE_SELF
|IN_MOVE_SELF
|IN_ATTRIB
,
41 [PATH_EXISTS_GLOB
] = IN_DELETE_SELF
|IN_MOVE_SELF
|IN_ATTRIB
,
42 [PATH_CHANGED
] = IN_DELETE_SELF
|IN_MOVE_SELF
|IN_ATTRIB
|IN_CLOSE_WRITE
|IN_CREATE
|IN_DELETE
|IN_MOVED_FROM
|IN_MOVED_TO
,
43 [PATH_MODIFIED
] = IN_DELETE_SELF
|IN_MOVE_SELF
|IN_ATTRIB
|IN_CLOSE_WRITE
|IN_CREATE
|IN_DELETE
|IN_MOVED_FROM
|IN_MOVED_TO
|IN_MODIFY
,
44 [PATH_DIRECTORY_NOT_EMPTY
] = IN_DELETE_SELF
|IN_MOVE_SELF
|IN_ATTRIB
|IN_CREATE
|IN_MOVED_TO
,
48 char *slash
, *oldslash
= NULL
;
57 s
->inotify_fd
= inotify_init1(IN_NONBLOCK
|IN_CLOEXEC
);
58 if (s
->inotify_fd
< 0) {
59 r
= log_error_errno(errno
, "Failed to allocate inotify fd: %m");
63 r
= sd_event_add_io(s
->unit
->manager
->event
, &s
->event_source
, s
->inotify_fd
, EPOLLIN
, handler
, s
);
65 log_error_errno(r
, "Failed to add inotify fd to event loop: %m");
69 (void) sd_event_source_set_description(s
->event_source
, "path");
71 /* This function assumes the path was passed through path_simplify()! */
72 assert(!strstr(s
->path
, "//"));
74 for (slash
= strchr(s
->path
, '/'); ; slash
= strchr(slash
+1, '/')) {
75 bool incomplete
= false;
80 cut
= slash
+ (slash
== s
->path
);
84 flags
= IN_MOVE_SELF
| IN_DELETE_SELF
| IN_CREATE
| IN_MOVED_TO
;
87 flags
= flags_table
[s
->type
];
90 /* If this is a symlink watch both the symlink inode and where it points to. If the inode is
91 * not a symlink both calls will install the same watch, which is redundant and doesn't
93 for (int follow_symlink
= 0; follow_symlink
< 2; follow_symlink
++) {
96 SET_FLAG(f
, IN_DONT_FOLLOW
, !follow_symlink
);
98 wd
= inotify_add_watch(s
->inotify_fd
, s
->path
, f
);
100 if (IN_SET(errno
, EACCES
, ENOENT
)) {
101 incomplete
= true; /* This is an expected error, let's accept this
102 * quietly: we have an incomplete watch for
107 /* This second call to inotify_add_watch() should fail like the previous one
108 * and is done for logging the error in a comprehensive way. */
109 wd
= inotify_add_watch_and_warn(s
->inotify_fd
, s
->path
, f
);
118 /* Hmm, we succeeded in adding the watch this time... let's continue. */
131 /* Path exists, we don't need to watch parent too closely. */
133 char *cut2
= oldslash
+ (oldslash
== s
->path
);
137 (void) inotify_add_watch(s
->inotify_fd
, s
->path
, IN_MOVE_SELF
);
138 /* Error is ignored, the worst can happen is we get spurious events. */
149 /* whole path has been iterated over */
156 r
= log_error_errno(errno
, "Failed to add watch on any of the components of %s: %m", s
->path
);
157 /* either EACCESS or ENOENT */
164 path_spec_unwatch(s
);
168 void path_spec_unwatch(PathSpec
*s
) {
171 s
->event_source
= sd_event_source_disable_unref(s
->event_source
);
172 s
->inotify_fd
= safe_close(s
->inotify_fd
);
175 int path_spec_fd_event(PathSpec
*s
, uint32_t revents
) {
176 union inotify_event_buffer buffer
;
181 if (revents
!= EPOLLIN
)
182 return log_error_errno(SYNTHETIC_ERRNO(EINVAL
),
183 "Got invalid poll event on inotify.");
185 l
= read(s
->inotify_fd
, &buffer
, sizeof(buffer
));
187 if (ERRNO_IS_TRANSIENT(errno
))
190 return log_error_errno(errno
, "Failed to read inotify event: %m");
193 if (IN_SET(s
->type
, PATH_CHANGED
, PATH_MODIFIED
))
194 FOREACH_INOTIFY_EVENT_WARN(e
, buffer
, l
)
195 if (s
->primary_wd
== e
->wd
)
201 static bool path_spec_check_good(PathSpec
*s
, bool initial
, bool from_trigger_notify
, char **ret_trigger_path
) {
202 _cleanup_free_
char *trigger
= NULL
;
203 bool b
, good
= false;
206 assert(ret_trigger_path
);
211 good
= access(s
->path
, F_OK
) >= 0;
214 case PATH_EXISTS_GLOB
:
215 good
= glob_first(s
->path
, &trigger
) > 0;
218 case PATH_DIRECTORY_NOT_EMPTY
: {
221 k
= dir_is_empty(s
->path
, /* ignore_hidden_or_backup= */ true);
222 good
= !(IN_SET(k
, -ENOENT
, -ENOTDIR
) || k
> 0);
228 b
= access(s
->path
, F_OK
) >= 0;
229 good
= !initial
&& !from_trigger_notify
&& b
!= s
->previous_exists
;
230 s
->previous_exists
= b
;
239 trigger
= strdup(s
->path
);
241 (void) log_oom_debug();
243 *ret_trigger_path
= TAKE_PTR(trigger
);
249 static void path_spec_mkdir(PathSpec
*s
, mode_t mode
) {
254 if (IN_SET(s
->type
, PATH_EXISTS
, PATH_EXISTS_GLOB
))
257 r
= mkdir_p_label(s
->path
, mode
);
259 log_warning_errno(r
, "mkdir(%s) failed: %m", s
->path
);
262 static void path_spec_dump(PathSpec
*s
, FILE *f
, const char *prefix
) {
269 assert_se(type
= path_type_to_string(s
->type
));
270 fprintf(f
, "%s%s: %s\n", prefix
, type
, s
->path
);
273 void path_spec_done(PathSpec
*s
) {
275 assert(s
->inotify_fd
== -EBADF
);
280 static void path_init(Unit
*u
) {
281 Path
*p
= ASSERT_PTR(PATH(u
));
283 assert(u
->load_state
== UNIT_STUB
);
285 p
->directory_mode
= 0755;
287 p
->trigger_limit
= RATELIMIT_OFF
;
290 void path_free_specs(Path
*p
) {
295 while ((s
= LIST_POP(spec
, p
->specs
))) {
296 path_spec_unwatch(s
);
302 static void path_done(Unit
*u
) {
303 Path
*p
= ASSERT_PTR(PATH(u
));
305 p
->trigger_notify_event_source
= sd_event_source_disable_unref(p
->trigger_notify_event_source
);
309 static int path_add_mount_dependencies(Path
*p
) {
314 LIST_FOREACH(spec
, s
, p
->specs
) {
315 r
= unit_add_mounts_for(UNIT(p
), s
->path
, UNIT_DEPENDENCY_FILE
, UNIT_MOUNT_REQUIRES
);
323 static int path_verify(Path
*p
) {
325 assert(UNIT(p
)->load_state
== UNIT_LOADED
);
328 return log_unit_error_errno(UNIT(p
), SYNTHETIC_ERRNO(ENOEXEC
), "Path unit lacks path setting. Refusing.");
333 static int path_add_default_dependencies(Path
*p
) {
338 if (!UNIT(p
)->default_dependencies
)
341 r
= unit_add_dependency_by_name(UNIT(p
), UNIT_BEFORE
, SPECIAL_PATHS_TARGET
, true, UNIT_DEPENDENCY_DEFAULT
);
345 if (MANAGER_IS_SYSTEM(UNIT(p
)->manager
)) {
346 r
= unit_add_two_dependencies_by_name(UNIT(p
), UNIT_AFTER
, UNIT_REQUIRES
, SPECIAL_SYSINIT_TARGET
, true, UNIT_DEPENDENCY_DEFAULT
);
351 return unit_add_two_dependencies_by_name(UNIT(p
), UNIT_BEFORE
, UNIT_CONFLICTS
, SPECIAL_SHUTDOWN_TARGET
, true, UNIT_DEPENDENCY_DEFAULT
);
354 static int path_add_trigger_dependencies(Path
*p
) {
360 if (UNIT_TRIGGER(UNIT(p
)))
363 r
= unit_load_related_unit(UNIT(p
), ".service", &x
);
367 return unit_add_two_dependencies(UNIT(p
), UNIT_BEFORE
, UNIT_TRIGGERS
, x
, true, UNIT_DEPENDENCY_IMPLICIT
);
370 static int path_add_extras(Path
*p
) {
375 /* To avoid getting pid1 in a busy-loop state (eg: unmet condition on associated service),
376 * set a default trigger limit if the user didn't specify any. */
377 if (p
->trigger_limit
.interval
== USEC_INFINITY
)
378 p
->trigger_limit
.interval
= 2 * USEC_PER_SEC
;
380 if (p
->trigger_limit
.burst
== UINT_MAX
)
381 p
->trigger_limit
.burst
= 200;
383 r
= path_add_trigger_dependencies(p
);
387 r
= path_add_mount_dependencies(p
);
391 return path_add_default_dependencies(p
);
394 static int path_load(Unit
*u
) {
395 Path
*p
= ASSERT_PTR(PATH(u
));
398 assert(u
->load_state
== UNIT_STUB
);
400 r
= unit_load_fragment_and_dropin(u
, true);
404 if (u
->load_state
!= UNIT_LOADED
)
407 r
= path_add_extras(p
);
411 return path_verify(p
);
414 static void path_dump(Unit
*u
, FILE *f
, const char *prefix
) {
415 Path
*p
= ASSERT_PTR(PATH(u
));
421 trigger
= UNIT_TRIGGER(u
);
427 "%sMakeDirectory: %s\n"
428 "%sDirectoryMode: %04o\n"
429 "%sTriggerLimitIntervalSec: %s\n"
430 "%sTriggerLimitBurst: %u\n",
431 prefix
, path_state_to_string(p
->state
),
432 prefix
, path_result_to_string(p
->result
),
433 prefix
, trigger
? trigger
->id
: "n/a",
434 prefix
, yes_no(p
->make_directory
),
435 prefix
, p
->directory_mode
,
436 prefix
, FORMAT_TIMESPAN(p
->trigger_limit
.interval
, USEC_PER_SEC
),
437 prefix
, p
->trigger_limit
.burst
);
439 LIST_FOREACH(spec
, s
, p
->specs
)
440 path_spec_dump(s
, f
, prefix
);
443 static void path_unwatch(Path
*p
) {
446 LIST_FOREACH(spec
, s
, p
->specs
)
447 path_spec_unwatch(s
);
450 static int path_watch(Path
*p
) {
455 LIST_FOREACH(spec
, s
, p
->specs
) {
456 r
= path_spec_watch(s
, path_dispatch_io
);
464 static void path_set_state(Path
*p
, PathState state
) {
469 if (p
->state
!= state
)
470 bus_unit_send_pending_change_signal(UNIT(p
), false);
472 old_state
= p
->state
;
475 if (!IN_SET(state
, PATH_WAITING
, PATH_RUNNING
))
478 if (state
!= old_state
)
479 log_unit_debug(UNIT(p
), "Changed %s -> %s", path_state_to_string(old_state
), path_state_to_string(state
));
481 unit_notify(UNIT(p
), state_translation_table
[old_state
], state_translation_table
[state
], /* reload_success = */ true);
484 static void path_enter_waiting(Path
*p
, bool initial
, bool from_trigger_notify
);
486 static int path_coldplug(Unit
*u
) {
487 Path
*p
= ASSERT_PTR(PATH(u
));
489 assert(p
->state
== PATH_DEAD
);
491 if (p
->deserialized_state
!= p
->state
) {
493 if (IN_SET(p
->deserialized_state
, PATH_WAITING
, PATH_RUNNING
))
494 path_enter_waiting(p
, true, false);
496 path_set_state(p
, p
->deserialized_state
);
502 static void path_enter_dead(Path
*p
, PathResult f
) {
505 if (p
->result
== PATH_SUCCESS
)
508 unit_log_result(UNIT(p
), p
->result
== PATH_SUCCESS
, path_result_to_string(p
->result
));
509 path_set_state(p
, p
->result
!= PATH_SUCCESS
? PATH_FAILED
: PATH_DEAD
);
512 static void path_enter_running(Path
*p
, char *trigger_path
) {
513 _cleanup_(activation_details_unrefp
) ActivationDetails
*details
= NULL
;
514 _cleanup_(sd_bus_error_free
) sd_bus_error error
= SD_BUS_ERROR_NULL
;
521 /* Don't start job if we are supposed to go down */
522 if (unit_stop_pending(UNIT(p
)))
525 if (!ratelimit_below(&p
->trigger_limit
)) {
526 log_unit_warning(UNIT(p
), "Trigger limit hit, refusing further activation.");
527 path_enter_dead(p
, PATH_FAILURE_TRIGGER_LIMIT_HIT
);
531 trigger
= UNIT_TRIGGER(UNIT(p
));
533 log_unit_error(UNIT(p
), "Unit to trigger vanished.");
537 details
= activation_details_new(UNIT(p
));
543 r
= free_and_strdup(&(ACTIVATION_DETAILS_PATH(details
))->trigger_path_filename
, trigger_path
);
549 r
= manager_add_job(UNIT(p
)->manager
, JOB_START
, trigger
, JOB_REPLACE
, NULL
, &error
, &job
);
551 log_unit_warning(UNIT(p
), "Failed to queue unit startup job: %s", bus_error_message(&error
, r
));
555 job_set_activation_details(job
, details
);
557 path_set_state(p
, PATH_RUNNING
);
563 path_enter_dead(p
, PATH_FAILURE_RESOURCES
);
566 static bool path_check_good(Path
*p
, bool initial
, bool from_trigger_notify
, char **ret_trigger_path
) {
568 assert(ret_trigger_path
);
570 LIST_FOREACH(spec
, s
, p
->specs
)
571 if (path_spec_check_good(s
, initial
, from_trigger_notify
, ret_trigger_path
))
577 static void path_enter_waiting(Path
*p
, bool initial
, bool from_trigger_notify
) {
578 _cleanup_free_
char *trigger_path
= NULL
;
582 if (p
->trigger_notify_event_source
)
583 (void) event_source_disable(p
->trigger_notify_event_source
);
585 /* If the triggered unit is already running, so are we */
586 trigger
= UNIT_TRIGGER(UNIT(p
));
587 if (trigger
&& !UNIT_IS_INACTIVE_OR_DEACTIVATING(unit_active_state(trigger
))) {
588 path_set_state(p
, PATH_RUNNING
);
593 if (path_check_good(p
, initial
, from_trigger_notify
, &trigger_path
)) {
594 log_unit_debug(UNIT(p
), "Got triggered.");
595 path_enter_running(p
, trigger_path
);
601 log_unit_warning_errno(UNIT(p
), r
, "Failed to enter waiting state: %m");
602 path_enter_dead(p
, PATH_FAILURE_RESOURCES
);
606 /* Hmm, so now we have created inotify watches, but the file
607 * might have appeared/been removed by now, so we must
610 if (path_check_good(p
, false, from_trigger_notify
, &trigger_path
)) {
611 log_unit_debug(UNIT(p
), "Got triggered.");
612 path_enter_running(p
, trigger_path
);
616 path_set_state(p
, PATH_WAITING
);
619 static void path_mkdir(Path
*p
) {
622 if (!p
->make_directory
)
625 LIST_FOREACH(spec
, s
, p
->specs
)
626 path_spec_mkdir(s
, p
->directory_mode
);
629 static int path_start(Unit
*u
) {
630 Path
*p
= ASSERT_PTR(PATH(u
));
633 assert(IN_SET(p
->state
, PATH_DEAD
, PATH_FAILED
));
635 r
= unit_test_trigger_loaded(u
);
639 r
= unit_acquire_invocation_id(u
);
645 p
->result
= PATH_SUCCESS
;
646 path_enter_waiting(p
, true, false);
651 static int path_stop(Unit
*u
) {
652 Path
*p
= ASSERT_PTR(PATH(u
));
654 assert(IN_SET(p
->state
, PATH_WAITING
, PATH_RUNNING
));
656 path_enter_dead(p
, PATH_SUCCESS
);
660 static int path_serialize(Unit
*u
, FILE *f
, FDSet
*fds
) {
661 Path
*p
= ASSERT_PTR(PATH(u
));
666 (void) serialize_item(f
, "state", path_state_to_string(p
->state
));
667 (void) serialize_item(f
, "result", path_result_to_string(p
->result
));
669 LIST_FOREACH(spec
, s
, p
->specs
) {
671 _cleanup_free_
char *escaped
= NULL
;
673 escaped
= cescape(s
->path
);
677 assert_se(type
= path_type_to_string(s
->type
));
678 (void) serialize_item_format(f
, "path-spec", "%s %i %s",
684 (void) serialize_ratelimit(f
, "trigger-ratelimit", &p
->trigger_limit
);
689 static int path_deserialize_item(Unit
*u
, const char *key
, const char *value
, FDSet
*fds
) {
690 Path
*p
= ASSERT_PTR(PATH(u
));
696 if (streq(key
, "state")) {
699 state
= path_state_from_string(value
);
701 log_unit_debug(u
, "Failed to parse state value: %s", value
);
703 p
->deserialized_state
= state
;
705 } else if (streq(key
, "result")) {
708 f
= path_result_from_string(value
);
710 log_unit_debug(u
, "Failed to parse result value: %s", value
);
711 else if (f
!= PATH_SUCCESS
)
714 } else if (streq(key
, "path-spec")) {
715 int previous_exists
, skip
= 0;
716 _cleanup_free_
char *type_str
= NULL
;
718 if (sscanf(value
, "%ms %i %n", &type_str
, &previous_exists
, &skip
) < 2)
719 log_unit_debug(u
, "Failed to parse path-spec value: %s", value
);
721 _cleanup_free_
char *unescaped
= NULL
;
725 type
= path_type_from_string(type_str
);
727 log_unit_warning(u
, "Unknown path type \"%s\", ignoring.", type_str
);
731 l
= cunescape(value
+skip
, 0, &unescaped
);
733 log_unit_warning_errno(u
, l
, "Failed to unescape serialize path: %m");
737 LIST_FOREACH(spec
, s
, p
->specs
)
738 if (s
->type
== type
&&
739 path_equal(s
->path
, unescaped
)) {
741 s
->previous_exists
= previous_exists
;
746 } else if (streq(key
, "trigger-ratelimit"))
747 deserialize_ratelimit(&p
->trigger_limit
, key
, value
);
750 log_unit_debug(u
, "Unknown serialization key: %s", key
);
755 static UnitActiveState
path_active_state(Unit
*u
) {
756 Path
*p
= ASSERT_PTR(PATH(u
));
758 return state_translation_table
[p
->state
];
761 static const char *path_sub_state_to_string(Unit
*u
) {
762 Path
*p
= ASSERT_PTR(PATH(u
));
764 return path_state_to_string(p
->state
);
767 static int path_dispatch_io(sd_event_source
*source
, int fd
, uint32_t revents
, void *userdata
) {
768 PathSpec
*s
= ASSERT_PTR(userdata
), *found
= NULL
;
769 Path
*p
= ASSERT_PTR(PATH(s
->unit
));
774 if (!IN_SET(p
->state
, PATH_WAITING
, PATH_RUNNING
))
777 LIST_FOREACH(spec
, i
, p
->specs
)
778 if (path_spec_owns_inotify_fd(i
, fd
)) {
784 log_error("Got event on unknown fd.");
788 changed
= path_spec_fd_event(found
, revents
);
793 path_enter_running(p
, found
->path
);
795 path_enter_waiting(p
, false, false);
800 path_enter_dead(p
, PATH_FAILURE_RESOURCES
);
804 static void path_trigger_notify_impl(Unit
*u
, Unit
*other
, bool on_defer
);
806 static int path_trigger_notify_on_defer(sd_event_source
*s
, void *userdata
) {
807 Path
*p
= ASSERT_PTR(userdata
);
812 trigger
= UNIT_TRIGGER(UNIT(p
));
814 log_unit_error(UNIT(p
), "Unit to trigger vanished.");
815 path_enter_dead(p
, PATH_FAILURE_RESOURCES
);
819 path_trigger_notify_impl(UNIT(p
), trigger
, /* on_defer = */ true);
823 static void path_trigger_notify_impl(Unit
*u
, Unit
*other
, bool on_defer
) {
824 Path
*p
= ASSERT_PTR(PATH(u
));
829 /* Invoked whenever the unit we trigger changes state or gains or loses a job */
831 /* Filter out invocations with bogus state */
832 assert(UNIT_IS_LOAD_COMPLETE(other
->load_state
));
834 /* Don't propagate state changes from the triggered unit if we are already down */
835 if (!IN_SET(p
->state
, PATH_WAITING
, PATH_RUNNING
))
838 /* Propagate start limit hit state */
839 if (other
->start_limit_hit
) {
840 path_enter_dead(p
, PATH_FAILURE_UNIT_START_LIMIT_HIT
);
844 /* Don't propagate anything if there's still a job queued */
848 if (p
->state
== PATH_RUNNING
&&
849 UNIT_IS_INACTIVE_OR_DEACTIVATING(unit_active_state(other
))) {
851 log_unit_debug(u
, "Got notified about unit deactivation.");
852 } else if (p
->state
== PATH_WAITING
&&
853 !UNIT_IS_INACTIVE_OR_DEACTIVATING(unit_active_state(other
))) {
855 log_unit_debug(u
, "Got notified about unit activation.");
860 path_enter_waiting(p
, /* initial = */ false, /* from_trigger_notify = */ true);
864 /* Do not call path_enter_waiting() directly from path_trigger_notify(), as this may be called by
865 * job_install() -> job_finish_and_invalidate() -> unit_trigger_notify(), and path_enter_waiting()
866 * may install another job and will trigger assertion in job_install().
867 * https://github.com/systemd/systemd/issues/24577#issuecomment-1522628906
868 * Hence, first setup defer event source here, and call path_enter_waiting() slightly later. */
869 if (p
->trigger_notify_event_source
) {
870 r
= sd_event_source_set_enabled(p
->trigger_notify_event_source
, SD_EVENT_ONESHOT
);
872 log_unit_warning_errno(u
, r
, "Failed to enable event source for triggering notify: %m");
873 path_enter_dead(p
, PATH_FAILURE_RESOURCES
);
877 r
= sd_event_add_defer(u
->manager
->event
, &p
->trigger_notify_event_source
, path_trigger_notify_on_defer
, p
);
879 log_unit_warning_errno(u
, r
, "Failed to allocate event source for triggering notify: %m");
880 path_enter_dead(p
, PATH_FAILURE_RESOURCES
);
884 (void) sd_event_source_set_description(p
->trigger_notify_event_source
, "path-trigger-notify");
888 static void path_trigger_notify(Unit
*u
, Unit
*other
) {
889 path_trigger_notify_impl(u
, other
, /* on_defer = */ false);
892 static void path_reset_failed(Unit
*u
) {
893 Path
*p
= ASSERT_PTR(PATH(u
));
895 if (p
->state
== PATH_FAILED
)
896 path_set_state(p
, PATH_DEAD
);
898 p
->result
= PATH_SUCCESS
;
901 static int path_can_start(Unit
*u
) {
902 Path
*p
= ASSERT_PTR(PATH(u
));
905 r
= unit_test_start_limit(u
);
907 path_enter_dead(p
, PATH_FAILURE_START_LIMIT_HIT
);
914 static void activation_details_path_done(ActivationDetails
*details
) {
915 ActivationDetailsPath
*p
= ASSERT_PTR(ACTIVATION_DETAILS_PATH(details
));
917 p
->trigger_path_filename
= mfree(p
->trigger_path_filename
);
920 static void activation_details_path_serialize(ActivationDetails
*details
, FILE *f
) {
921 ActivationDetailsPath
*p
= ASSERT_PTR(ACTIVATION_DETAILS_PATH(details
));
925 if (p
->trigger_path_filename
)
926 (void) serialize_item(f
, "activation-details-path-filename", p
->trigger_path_filename
);
929 static int activation_details_path_deserialize(const char *key
, const char *value
, ActivationDetails
**details
) {
935 if (!details
|| !*details
)
938 ActivationDetailsPath
*p
= ACTIVATION_DETAILS_PATH(*details
);
942 if (!streq(key
, "activation-details-path-filename"))
945 r
= free_and_strdup(&p
->trigger_path_filename
, value
);
952 static int activation_details_path_append_env(ActivationDetails
*details
, char ***strv
) {
953 ActivationDetailsPath
*p
= ASSERT_PTR(ACTIVATION_DETAILS_PATH(details
));
959 if (isempty(p
->trigger_path_filename
))
962 s
= strjoin("TRIGGER_PATH=", p
->trigger_path_filename
);
966 r
= strv_consume(strv
, TAKE_PTR(s
));
970 return 1; /* Return the number of variables added to the env block */
973 static int activation_details_path_append_pair(ActivationDetails
*details
, char ***strv
) {
974 ActivationDetailsPath
*p
= ASSERT_PTR(ACTIVATION_DETAILS_PATH(details
));
979 if (isempty(p
->trigger_path_filename
))
982 r
= strv_extend_many(strv
, "trigger_path", p
->trigger_path_filename
);
986 return 1; /* Return the number of pairs added to the env block */
989 static const char* const path_type_table
[_PATH_TYPE_MAX
] = {
990 [PATH_EXISTS
] = "PathExists",
991 [PATH_EXISTS_GLOB
] = "PathExistsGlob",
992 [PATH_DIRECTORY_NOT_EMPTY
] = "DirectoryNotEmpty",
993 [PATH_CHANGED
] = "PathChanged",
994 [PATH_MODIFIED
] = "PathModified",
997 DEFINE_STRING_TABLE_LOOKUP(path_type
, PathType
);
999 static const char* const path_result_table
[_PATH_RESULT_MAX
] = {
1000 [PATH_SUCCESS
] = "success",
1001 [PATH_FAILURE_RESOURCES
] = "resources",
1002 [PATH_FAILURE_START_LIMIT_HIT
] = "start-limit-hit",
1003 [PATH_FAILURE_UNIT_START_LIMIT_HIT
] = "unit-start-limit-hit",
1004 [PATH_FAILURE_TRIGGER_LIMIT_HIT
] = "trigger-limit-hit",
1007 DEFINE_STRING_TABLE_LOOKUP(path_result
, PathResult
);
1009 const UnitVTable path_vtable
= {
1010 .object_size
= sizeof(Path
),
1016 .private_section
= "Path",
1018 .can_transient
= true,
1020 .can_trigger
= true,
1026 .coldplug
= path_coldplug
,
1030 .start
= path_start
,
1033 .serialize
= path_serialize
,
1034 .deserialize_item
= path_deserialize_item
,
1036 .active_state
= path_active_state
,
1037 .sub_state_to_string
= path_sub_state_to_string
,
1039 .trigger_notify
= path_trigger_notify
,
1041 .reset_failed
= path_reset_failed
,
1043 .bus_set_property
= bus_path_set_property
,
1045 .can_start
= path_can_start
,
1048 const ActivationDetailsVTable activation_details_path_vtable
= {
1049 .object_size
= sizeof(ActivationDetailsPath
),
1051 .done
= activation_details_path_done
,
1052 .serialize
= activation_details_path_serialize
,
1053 .deserialize
= activation_details_path_deserialize
,
1054 .append_env
= activation_details_path_append_env
,
1055 .append_pair
= activation_details_path_append_pair
,