Path units having either PathExists=, PathExistsGlob=, or
DirectoryNotEmpty= want the service they trigger when the condition is
met. This way it becomes meaningful to include StopWhenUnneeded=true in
the triggered service.
---
This fixes #87287.
man/systemd.path.xml | 10 ++
src/core/path.c | 42 +++--
src/core/path.h | 1 +
src/core/unit.c | 86 +++-
src/core/unit.h | 2 ++
5 files changed, 98 insertions(+), 43 deletions(-)
diff --git a/man/systemd.path.xml b/man/systemd.path.xml
index c6d61cf..9b78d45 100644
--- a/man/systemd.path.xml
+++ b/man/systemd.path.xml
@@ -170,6 +170,16 @@
to varnamePathChanged=/varname and
varnamePathModified=/varname./para
+paraIf the triggered unit has
+varnameStopWhenUnneeded=true/varname
+then it is not stopped as long as a path
+exists (in case of
+varnamePathExists=/varname and
+varnamePathExistsGlob=/varname) or
+a directory is not empty (in case of
+varnameDirectoryNotEmpty=/varname).
+/para
+
paraIf the path itself or any of the
containing directories are not
accessible, commandsystemd/command
diff --git a/src/core/path.c b/src/core/path.c
index 0fdf483..5b1bccd 100644
--- a/src/core/path.c
+++ b/src/core/path.c
@@ -467,6 +467,22 @@ static void path_enter_dead(Path *p, PathResult f) {
path_set_state(p, p-result != PATH_SUCCESS ? PATH_FAILED : PATH_DEAD);
}
+static bool path_check_good(Path *p, bool initial) {
+PathSpec *s;
+bool good = false;
+
+assert(p);
+
+LIST_FOREACH(spec, s, p-specs) {
+good = path_spec_check_good(s, initial);
+
+if (good)
+break;
+}
+
+return good;
+}
+
static void path_enter_running(Path *p) {
_cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
int r;
@@ -477,6 +493,11 @@ static void path_enter_running(Path *p) {
if (unit_stop_pending(UNIT(p)))
return;
+if (!p-has_dependency path_check_good(p, true)) {
+if (unit_add_dependency(UNIT(p), UNIT_WANTS,
UNIT_TRIGGER(UNIT(p)), false) = 0)
+p-has_dependency = true;
+}
+
r = manager_add_job(UNIT(p)-manager, JOB_START, UNIT_TRIGGER(UNIT(p)),
JOB_REPLACE, true, error, NULL);
if (r 0)
@@ -497,22 +518,6 @@ fail:
path_enter_dead(p, PATH_FAILURE_RESOURCES);
}
-static bool path_check_good(Path *p, bool initial) {
-PathSpec *s;
-bool good = false;
-
-assert(p);
-
-LIST_FOREACH(spec, s, p-specs) {
-good = path_spec_check_good(s, initial);
-
-if (good)
-break;
-}
-
-return good;
-}
-
static void path_enter_waiting(Path *p, bool initial, bool recheck) {
int r;
@@ -538,6 +543,11 @@ static void path_enter_waiting(Path *p, bool initial, bool
recheck) {
return;
}
+if (p-has_dependency) {
+unit_delete_dependency(UNIT(p), UNIT_WANTS,
UNIT_TRIGGER(UNIT(p)), false);
+p-has_dependency = false;
+}
+
path_set_state(p, PATH_WAITING);
return;
diff --git a/src/core/path.h b/src/core/path.h
index d2e91d7..0a9b1cf 100644
--- a/src/core/path.h
+++ b/src/core/path.h
@@ -85,6 +85,7 @@ struct Path {
PathState state, deserialized_state;
bool inotify_triggered;
+bool has_dependency;
bool make_directory;
mode_t directory_mode;
diff --git a/src/core/unit.c b/src/core/unit.c
index a2f3728..e06b41c 100644
--- a/src/core/unit.c
+++ b/src/core/unit.c
@@ -69,6 +69,33 @@ const UnitVTable * const unit_vtable[_UNIT_TYPE_MAX] = {
[UNIT_SCOPE] = scope_vtable
};
+const UnitDependency inverse_table[_UNIT_DEPENDENCY_MAX] = {
+[UNIT_REQUIRES] = UNIT_REQUIRED_BY,
+[UNIT_REQUIRES_OVERRIDABLE] = UNIT_REQUIRED_BY_OVERRIDABLE,
+[UNIT_WANTS] = UNIT_WANTED_BY,
+[UNIT_REQUISITE] = UNIT_REQUIRED_BY,
+[UNIT_REQUISITE_OVERRIDABLE] = UNIT_REQUIRED_BY_OVERRIDABLE,
+[UNIT_BINDS_TO] = UNIT_BOUND_BY,
+[UNIT_PART_OF] = UNIT_CONSISTS_OF,
+[UNIT_REQUIRED_BY] = _UNIT_DEPENDENCY_INVALID,
+[UNIT_REQUIRED_BY_OVERRIDABLE] = _UNIT_DEPENDENCY_INVALID,
+[UNIT_WANTED_BY] = _UNIT_DEPENDENCY_INVALID,
+[UNIT_BOUND_BY] = UNIT_BINDS_TO,
+[UNIT_CONSISTS_OF] = UNIT_PART_OF,