1 /* SPDX-License-Identifier: LGPL-2.1-or-later */
5 #include <sys/inotify.h>
10 #include "dbus-path.h"
11 #include "dbus-unit.h"
15 #include "glob-util.h"
19 #include "path-util.h"
20 #include "serialize.h"
22 #include "stat-util.h"
23 #include "string-table.h"
24 #include "string-util.h"
25 #include "unit-name.h"
28 static const UnitActiveState state_translation_table
[_PATH_STATE_MAX
] = {
29 [PATH_DEAD
] = UNIT_INACTIVE
,
30 [PATH_WAITING
] = UNIT_ACTIVE
,
31 [PATH_RUNNING
] = UNIT_ACTIVE
,
32 [PATH_FAILED
] = UNIT_FAILED
,
35 static int path_dispatch_io(sd_event_source
*source
, int fd
, uint32_t revents
, void *userdata
);
37 int path_spec_watch(PathSpec
*s
, sd_event_io_handler_t handler
) {
38 static const int flags_table
[_PATH_TYPE_MAX
] = {
39 [PATH_EXISTS
] = IN_DELETE_SELF
|IN_MOVE_SELF
|IN_ATTRIB
,
40 [PATH_EXISTS_GLOB
] = IN_DELETE_SELF
|IN_MOVE_SELF
|IN_ATTRIB
,
41 [PATH_CHANGED
] = IN_DELETE_SELF
|IN_MOVE_SELF
|IN_ATTRIB
|IN_CLOSE_WRITE
|IN_CREATE
|IN_DELETE
|IN_MOVED_FROM
|IN_MOVED_TO
,
42 [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
,
43 [PATH_DIRECTORY_NOT_EMPTY
] = IN_DELETE_SELF
|IN_MOVE_SELF
|IN_ATTRIB
|IN_CREATE
|IN_MOVED_TO
,
47 char *slash
, *oldslash
= NULL
;
56 s
->inotify_fd
= inotify_init1(IN_NONBLOCK
|IN_CLOEXEC
);
57 if (s
->inotify_fd
< 0) {
62 r
= sd_event_add_io(s
->unit
->manager
->event
, &s
->event_source
, s
->inotify_fd
, EPOLLIN
, handler
, s
);
66 (void) sd_event_source_set_description(s
->event_source
, "path");
68 /* This function assumes the path was passed through path_simplify()! */
69 assert(!strstr(s
->path
, "//"));
71 for (slash
= strchr(s
->path
, '/'); ; slash
= strchr(slash
+1, '/')) {
77 cut
= slash
+ (slash
== s
->path
);
81 flags
= IN_MOVE_SELF
| IN_DELETE_SELF
| IN_ATTRIB
| IN_CREATE
| IN_MOVED_TO
;
83 flags
= flags_table
[s
->type
];
85 r
= inotify_add_watch(s
->inotify_fd
, s
->path
, flags
);
87 if (IN_SET(errno
, EACCES
, ENOENT
)) {
93 /* This second call to inotify_add_watch() should fail like the previous
94 * one and is done for logging the error in a comprehensive way. */
95 r
= inotify_add_watch_and_warn(s
->inotify_fd
, s
->path
, flags
);
102 /* Hmm, we succeeded in adding the watch this time... let's continue. */
106 /* Path exists, we don't need to watch parent too closely. */
108 char *cut2
= oldslash
+ (oldslash
== s
->path
);
112 (void) inotify_add_watch(s
->inotify_fd
, s
->path
, IN_MOVE_SELF
);
113 /* Error is ignored, the worst can happen is we get spurious events. */
124 /* whole path has been iterated over */
131 r
= log_error_errno(errno
, "Failed to add watch on any of the components of %s: %m", s
->path
);
132 /* either EACCESS or ENOENT */
139 path_spec_unwatch(s
);
143 void path_spec_unwatch(PathSpec
*s
) {
146 s
->event_source
= sd_event_source_unref(s
->event_source
);
147 s
->inotify_fd
= safe_close(s
->inotify_fd
);
150 int path_spec_fd_event(PathSpec
*s
, uint32_t revents
) {
151 union inotify_event_buffer buffer
;
152 struct inotify_event
*e
;
156 if (revents
!= EPOLLIN
)
157 return log_error_errno(SYNTHETIC_ERRNO(EINVAL
),
158 "Got invalid poll event on inotify.");
160 l
= read(s
->inotify_fd
, &buffer
, sizeof(buffer
));
162 if (IN_SET(errno
, EAGAIN
, EINTR
))
165 return log_error_errno(errno
, "Failed to read inotify event: %m");
168 FOREACH_INOTIFY_EVENT(e
, buffer
, l
) {
169 if (IN_SET(s
->type
, PATH_CHANGED
, PATH_MODIFIED
) &&
170 s
->primary_wd
== e
->wd
)
177 static bool path_spec_check_good(PathSpec
*s
, bool initial
, bool from_trigger_notify
) {
178 bool b
, good
= false;
183 good
= access(s
->path
, F_OK
) >= 0;
186 case PATH_EXISTS_GLOB
:
187 good
= glob_exists(s
->path
) > 0;
190 case PATH_DIRECTORY_NOT_EMPTY
: {
193 k
= dir_is_empty(s
->path
);
194 good
= !(k
== -ENOENT
|| k
> 0);
200 b
= access(s
->path
, F_OK
) >= 0;
201 good
= !initial
&& !from_trigger_notify
&& b
!= s
->previous_exists
;
202 s
->previous_exists
= b
;
212 static void path_spec_mkdir(PathSpec
*s
, mode_t mode
) {
215 if (IN_SET(s
->type
, PATH_EXISTS
, PATH_EXISTS_GLOB
))
218 r
= mkdir_p_label(s
->path
, mode
);
220 log_warning_errno(r
, "mkdir(%s) failed: %m", s
->path
);
223 static void path_spec_dump(PathSpec
*s
, FILE *f
, const char *prefix
) {
226 assert_se(type
= path_type_to_string(s
->type
));
227 fprintf(f
, "%s%s: %s\n", prefix
, type
, s
->path
);
230 void path_spec_done(PathSpec
*s
) {
232 assert(s
->inotify_fd
== -1);
237 static void path_init(Unit
*u
) {
241 assert(u
->load_state
== UNIT_STUB
);
243 p
->directory_mode
= 0755;
246 void path_free_specs(Path
*p
) {
251 while ((s
= p
->specs
)) {
252 path_spec_unwatch(s
);
253 LIST_REMOVE(spec
, p
->specs
, s
);
259 static void path_done(Unit
*u
) {
267 static int path_add_mount_dependencies(Path
*p
) {
273 LIST_FOREACH(spec
, s
, p
->specs
) {
274 r
= unit_require_mounts_for(UNIT(p
), s
->path
, UNIT_DEPENDENCY_FILE
);
282 static int path_verify(Path
*p
) {
284 assert(UNIT(p
)->load_state
== UNIT_LOADED
);
287 return log_unit_error_errno(UNIT(p
), SYNTHETIC_ERRNO(ENOEXEC
), "Path unit lacks path setting. Refusing.");
292 static int path_add_default_dependencies(Path
*p
) {
297 if (!UNIT(p
)->default_dependencies
)
300 r
= unit_add_dependency_by_name(UNIT(p
), UNIT_BEFORE
, SPECIAL_PATHS_TARGET
, true, UNIT_DEPENDENCY_DEFAULT
);
304 if (MANAGER_IS_SYSTEM(UNIT(p
)->manager
)) {
305 r
= unit_add_two_dependencies_by_name(UNIT(p
), UNIT_AFTER
, UNIT_REQUIRES
, SPECIAL_SYSINIT_TARGET
, true, UNIT_DEPENDENCY_DEFAULT
);
310 return unit_add_two_dependencies_by_name(UNIT(p
), UNIT_BEFORE
, UNIT_CONFLICTS
, SPECIAL_SHUTDOWN_TARGET
, true, UNIT_DEPENDENCY_DEFAULT
);
313 static int path_add_trigger_dependencies(Path
*p
) {
319 if (!hashmap_isempty(UNIT(p
)->dependencies
[UNIT_TRIGGERS
]))
322 r
= unit_load_related_unit(UNIT(p
), ".service", &x
);
326 return unit_add_two_dependencies(UNIT(p
), UNIT_BEFORE
, UNIT_TRIGGERS
, x
, true, UNIT_DEPENDENCY_IMPLICIT
);
329 static int path_add_extras(Path
*p
) {
332 r
= path_add_trigger_dependencies(p
);
336 r
= path_add_mount_dependencies(p
);
340 return path_add_default_dependencies(p
);
343 static int path_load(Unit
*u
) {
348 assert(u
->load_state
== UNIT_STUB
);
350 r
= unit_load_fragment_and_dropin(u
, true);
354 if (u
->load_state
!= UNIT_LOADED
)
357 r
= path_add_extras(p
);
361 return path_verify(p
);
364 static void path_dump(Unit
*u
, FILE *f
, const char *prefix
) {
372 trigger
= UNIT_TRIGGER(u
);
378 "%sMakeDirectory: %s\n"
379 "%sDirectoryMode: %04o\n",
380 prefix
, path_state_to_string(p
->state
),
381 prefix
, path_result_to_string(p
->result
),
382 prefix
, trigger
? trigger
->id
: "n/a",
383 prefix
, yes_no(p
->make_directory
),
384 prefix
, p
->directory_mode
);
386 LIST_FOREACH(spec
, s
, p
->specs
)
387 path_spec_dump(s
, f
, prefix
);
390 static void path_unwatch(Path
*p
) {
395 LIST_FOREACH(spec
, s
, p
->specs
)
396 path_spec_unwatch(s
);
399 static int path_watch(Path
*p
) {
405 LIST_FOREACH(spec
, s
, p
->specs
) {
406 r
= path_spec_watch(s
, path_dispatch_io
);
414 static void path_set_state(Path
*p
, PathState state
) {
418 if (p
->state
!= state
)
419 bus_unit_send_pending_change_signal(UNIT(p
), false);
421 old_state
= p
->state
;
424 if (!IN_SET(state
, PATH_WAITING
, PATH_RUNNING
))
427 if (state
!= old_state
)
428 log_unit_debug(UNIT(p
), "Changed %s -> %s", path_state_to_string(old_state
), path_state_to_string(state
));
430 unit_notify(UNIT(p
), state_translation_table
[old_state
], state_translation_table
[state
], 0);
433 static void path_enter_waiting(Path
*p
, bool initial
, bool from_trigger_notify
);
435 static int path_coldplug(Unit
*u
) {
439 assert(p
->state
== PATH_DEAD
);
441 if (p
->deserialized_state
!= p
->state
) {
443 if (IN_SET(p
->deserialized_state
, PATH_WAITING
, PATH_RUNNING
))
444 path_enter_waiting(p
, true, false);
446 path_set_state(p
, p
->deserialized_state
);
452 static void path_enter_dead(Path
*p
, PathResult f
) {
455 if (p
->result
== PATH_SUCCESS
)
458 unit_log_result(UNIT(p
), p
->result
== PATH_SUCCESS
, path_result_to_string(p
->result
));
459 path_set_state(p
, p
->result
!= PATH_SUCCESS
? PATH_FAILED
: PATH_DEAD
);
462 static void path_enter_running(Path
*p
) {
463 _cleanup_(sd_bus_error_free
) sd_bus_error error
= SD_BUS_ERROR_NULL
;
469 /* Don't start job if we are supposed to go down */
470 if (unit_stop_pending(UNIT(p
)))
473 trigger
= UNIT_TRIGGER(UNIT(p
));
475 log_unit_error(UNIT(p
), "Unit to trigger vanished.");
476 path_enter_dead(p
, PATH_FAILURE_RESOURCES
);
480 r
= manager_add_job(UNIT(p
)->manager
, JOB_START
, trigger
, JOB_REPLACE
, NULL
, &error
, NULL
);
484 path_set_state(p
, PATH_RUNNING
);
490 log_unit_warning(UNIT(p
), "Failed to queue unit startup job: %s", bus_error_message(&error
, r
));
491 path_enter_dead(p
, PATH_FAILURE_RESOURCES
);
494 static bool path_check_good(Path
*p
, bool initial
, bool from_trigger_notify
) {
499 LIST_FOREACH(spec
, s
, p
->specs
)
500 if (path_spec_check_good(s
, initial
, from_trigger_notify
))
506 static void path_enter_waiting(Path
*p
, bool initial
, bool from_trigger_notify
) {
510 /* If the triggered unit is already running, so are we */
511 trigger
= UNIT_TRIGGER(UNIT(p
));
512 if (trigger
&& !UNIT_IS_INACTIVE_OR_FAILED(unit_active_state(trigger
))) {
513 path_set_state(p
, PATH_RUNNING
);
518 if (path_check_good(p
, initial
, from_trigger_notify
)) {
519 log_unit_debug(UNIT(p
), "Got triggered.");
520 path_enter_running(p
);
528 /* Hmm, so now we have created inotify watches, but the file
529 * might have appeared/been removed by now, so we must
532 if (path_check_good(p
, false, from_trigger_notify
)) {
533 log_unit_debug(UNIT(p
), "Got triggered.");
534 path_enter_running(p
);
538 path_set_state(p
, PATH_WAITING
);
542 log_unit_warning_errno(UNIT(p
), r
, "Failed to enter waiting state: %m");
543 path_enter_dead(p
, PATH_FAILURE_RESOURCES
);
546 static void path_mkdir(Path
*p
) {
551 if (!p
->make_directory
)
554 LIST_FOREACH(spec
, s
, p
->specs
)
555 path_spec_mkdir(s
, p
->directory_mode
);
558 static int path_start(Unit
*u
) {
563 assert(IN_SET(p
->state
, PATH_DEAD
, PATH_FAILED
));
565 r
= unit_test_trigger_loaded(u
);
569 r
= unit_test_start_limit(u
);
571 path_enter_dead(p
, PATH_FAILURE_START_LIMIT_HIT
);
575 r
= unit_acquire_invocation_id(u
);
581 p
->result
= PATH_SUCCESS
;
582 path_enter_waiting(p
, true, false);
587 static int path_stop(Unit
*u
) {
591 assert(IN_SET(p
->state
, PATH_WAITING
, PATH_RUNNING
));
593 path_enter_dead(p
, PATH_SUCCESS
);
597 static int path_serialize(Unit
*u
, FILE *f
, FDSet
*fds
) {
605 (void) serialize_item(f
, "state", path_state_to_string(p
->state
));
606 (void) serialize_item(f
, "result", path_result_to_string(p
->result
));
608 LIST_FOREACH(spec
, s
, p
->specs
) {
610 _cleanup_free_
char *escaped
= NULL
;
612 escaped
= cescape(s
->path
);
616 assert_se(type
= path_type_to_string(s
->type
));
617 (void) serialize_item_format(f
, "path-spec", "%s %i %s",
626 static int path_deserialize_item(Unit
*u
, const char *key
, const char *value
, FDSet
*fds
) {
634 if (streq(key
, "state")) {
637 state
= path_state_from_string(value
);
639 log_unit_debug(u
, "Failed to parse state value: %s", value
);
641 p
->deserialized_state
= state
;
643 } else if (streq(key
, "result")) {
646 f
= path_result_from_string(value
);
648 log_unit_debug(u
, "Failed to parse result value: %s", value
);
649 else if (f
!= PATH_SUCCESS
)
652 } else if (streq(key
, "path-spec")) {
653 int previous_exists
, skip
= 0, r
;
654 _cleanup_free_
char *type_str
= NULL
;
656 if (sscanf(value
, "%ms %i %n", &type_str
, &previous_exists
, &skip
) < 2)
657 log_unit_debug(u
, "Failed to parse path-spec value: %s", value
);
659 _cleanup_free_
char *unescaped
= NULL
;
663 type
= path_type_from_string(type_str
);
665 log_unit_warning(u
, "Unknown path type \"%s\", ignoring.", type_str
);
669 r
= cunescape(value
+skip
, 0, &unescaped
);
671 log_unit_warning_errno(u
, r
, "Failed to unescape serialize path: %m");
675 LIST_FOREACH(spec
, s
, p
->specs
)
676 if (s
->type
== type
&&
677 path_equal(s
->path
, unescaped
)) {
679 s
->previous_exists
= previous_exists
;
685 log_unit_debug(u
, "Unknown serialization key: %s", key
);
690 _pure_
static UnitActiveState
path_active_state(Unit
*u
) {
693 return state_translation_table
[PATH(u
)->state
];
696 _pure_
static const char *path_sub_state_to_string(Unit
*u
) {
699 return path_state_to_string(PATH(u
)->state
);
702 static int path_dispatch_io(sd_event_source
*source
, int fd
, uint32_t revents
, void *userdata
) {
703 PathSpec
*s
= userdata
;
713 if (!IN_SET(p
->state
, PATH_WAITING
, PATH_RUNNING
))
716 /* log_debug("inotify wakeup on %s.", UNIT(p)->id); */
718 LIST_FOREACH(spec
, s
, p
->specs
)
719 if (path_spec_owns_inotify_fd(s
, fd
))
723 log_error("Got event on unknown fd.");
727 changed
= path_spec_fd_event(s
, revents
);
732 path_enter_running(p
);
734 path_enter_waiting(p
, false, false);
739 path_enter_dead(p
, PATH_FAILURE_RESOURCES
);
743 static void path_trigger_notify(Unit
*u
, Unit
*other
) {
749 /* Invoked whenever the unit we trigger changes state or gains or loses a job */
751 /* Filter out invocations with bogus state */
752 assert(UNIT_IS_LOAD_COMPLETE(other
->load_state
));
754 /* Don't propagate state changes from the triggered unit if we are already down */
755 if (!IN_SET(p
->state
, PATH_WAITING
, PATH_RUNNING
))
758 /* Propagate start limit hit state */
759 if (other
->start_limit_hit
) {
760 path_enter_dead(p
, PATH_FAILURE_UNIT_START_LIMIT_HIT
);
764 /* Don't propagate anything if there's still a job queued */
768 if (p
->state
== PATH_RUNNING
&&
769 UNIT_IS_INACTIVE_OR_FAILED(unit_active_state(other
))) {
770 log_unit_debug(UNIT(p
), "Got notified about unit deactivation.");
771 path_enter_waiting(p
, false, true);
772 } else if (p
->state
== PATH_WAITING
&&
773 !UNIT_IS_INACTIVE_OR_FAILED(unit_active_state(other
))) {
774 log_unit_debug(UNIT(p
), "Got notified about unit activation.");
775 path_enter_waiting(p
, false, true);
779 static void path_reset_failed(Unit
*u
) {
784 if (p
->state
== PATH_FAILED
)
785 path_set_state(p
, PATH_DEAD
);
787 p
->result
= PATH_SUCCESS
;
790 static const char* const path_type_table
[_PATH_TYPE_MAX
] = {
791 [PATH_EXISTS
] = "PathExists",
792 [PATH_EXISTS_GLOB
] = "PathExistsGlob",
793 [PATH_DIRECTORY_NOT_EMPTY
] = "DirectoryNotEmpty",
794 [PATH_CHANGED
] = "PathChanged",
795 [PATH_MODIFIED
] = "PathModified",
798 DEFINE_STRING_TABLE_LOOKUP(path_type
, PathType
);
800 static const char* const path_result_table
[_PATH_RESULT_MAX
] = {
801 [PATH_SUCCESS
] = "success",
802 [PATH_FAILURE_RESOURCES
] = "resources",
803 [PATH_FAILURE_START_LIMIT_HIT
] = "start-limit-hit",
804 [PATH_FAILURE_UNIT_START_LIMIT_HIT
] = "unit-start-limit-hit",
807 DEFINE_STRING_TABLE_LOOKUP(path_result
, PathResult
);
809 const UnitVTable path_vtable
= {
810 .object_size
= sizeof(Path
),
816 .private_section
= "Path",
818 .can_transient
= true,
826 .coldplug
= path_coldplug
,
833 .serialize
= path_serialize
,
834 .deserialize_item
= path_deserialize_item
,
836 .active_state
= path_active_state
,
837 .sub_state_to_string
= path_sub_state_to_string
,
839 .trigger_notify
= path_trigger_notify
,
841 .reset_failed
= path_reset_failed
,
843 .bus_set_property
= bus_path_set_property
,