708961c701
As documented in systemd.path(5): When a service unit triggered by a path unit terminates (regardless whether it exited successfully or failed), monitored paths are checked immediately again, and the service accordingly restarted instantly. This commit implements this behaviour for PathExists=, PathExistsGlob=, and DirectoryNotEmpty=. These predicates are essentially "level-triggered": the service should be activated whenever the predicate is true. PathChanged= and PathModified=, on the other hand, are "edge-triggered": the service should only be activated when the predicate *becomes* true. The behaviour has been broken since at least as far back as commit8fca6944c2
("path: stop watching path specs once we triggered the target unit"). This commit had systemd stop monitoring inotify whenever the triggered unit was activated. Unfortunately this meant it never updated the ->inotify_triggered flag, so it never rechecked the path specs when the triggered unit deactivated. With this commit, systemd rechecks all paths specs whenever the triggered unit deactivates. If any PathExists=, PathExistsGlob= or DirectoryNotEmpty= predicate passes, the triggered unit is reactivated. If the target unit is activated by something outside of the path unit, the path unit immediately transitions to a running state. This ensures the path unit stops monitoring inotify in this situation. With this change in place, commitd7cf8c24d4
("core/path: fix spurious triggering of PathExists= on restart/reload") is no longer necessary. The path unit (and its triggered unit) is now always active whenever the PathExists= predicate passes, so there is no spurious restart when systemd is reloaded or restarted.
76 lines
1.6 KiB
C
76 lines
1.6 KiB
C
/* SPDX-License-Identifier: LGPL-2.1+ */
|
|
#pragma once
|
|
|
|
typedef struct Path Path;
|
|
typedef struct PathSpec PathSpec;
|
|
|
|
#include "unit.h"
|
|
|
|
typedef enum PathType {
|
|
PATH_EXISTS,
|
|
PATH_EXISTS_GLOB,
|
|
PATH_DIRECTORY_NOT_EMPTY,
|
|
PATH_CHANGED,
|
|
PATH_MODIFIED,
|
|
_PATH_TYPE_MAX,
|
|
_PATH_TYPE_INVALID = -1
|
|
} PathType;
|
|
|
|
typedef struct PathSpec {
|
|
Unit *unit;
|
|
|
|
char *path;
|
|
|
|
sd_event_source *event_source;
|
|
|
|
LIST_FIELDS(struct PathSpec, spec);
|
|
|
|
PathType type;
|
|
int inotify_fd;
|
|
int primary_wd;
|
|
|
|
bool previous_exists;
|
|
} PathSpec;
|
|
|
|
int path_spec_watch(PathSpec *s, sd_event_io_handler_t handler);
|
|
void path_spec_unwatch(PathSpec *s);
|
|
int path_spec_fd_event(PathSpec *s, uint32_t events);
|
|
void path_spec_done(PathSpec *s);
|
|
|
|
static inline bool path_spec_owns_inotify_fd(PathSpec *s, int fd) {
|
|
return s->inotify_fd == fd;
|
|
}
|
|
|
|
typedef enum PathResult {
|
|
PATH_SUCCESS,
|
|
PATH_FAILURE_RESOURCES,
|
|
PATH_FAILURE_START_LIMIT_HIT,
|
|
_PATH_RESULT_MAX,
|
|
_PATH_RESULT_INVALID = -1
|
|
} PathResult;
|
|
|
|
struct Path {
|
|
Unit meta;
|
|
|
|
LIST_HEAD(PathSpec, specs);
|
|
|
|
PathState state, deserialized_state;
|
|
|
|
bool make_directory;
|
|
mode_t directory_mode;
|
|
|
|
PathResult result;
|
|
};
|
|
|
|
void path_free_specs(Path *p);
|
|
|
|
extern const UnitVTable path_vtable;
|
|
|
|
const char* path_type_to_string(PathType i) _const_;
|
|
PathType path_type_from_string(const char *s) _pure_;
|
|
|
|
const char* path_result_to_string(PathResult i) _const_;
|
|
PathResult path_result_from_string(const char *s) _pure_;
|
|
|
|
DEFINE_CAST(PATH, Path);
|