service: set TRIGGER_UNIT= and TRIGGER_PATH= on activation by path unit

When a service is triggered by a path unit, pass the
path unit name and the path that triggered it via env vars
to the spawned processes.
Note that this is best-effort, as there might be many triggers
at the same time, but we only get woken up by one.
This commit is contained in:
Luca Boccassi
2022-08-23 20:38:08 +01:00
parent 48b92b37ac
commit 4c42032854
9 changed files with 184 additions and 11 deletions
+133 -11
View File
@@ -197,9 +197,13 @@ int path_spec_fd_event(PathSpec *s, uint32_t revents) {
return 0;
}
static bool path_spec_check_good(PathSpec *s, bool initial, bool from_trigger_notify) {
static bool path_spec_check_good(PathSpec *s, bool initial, bool from_trigger_notify, char **ret_trigger_path) {
_cleanup_free_ char *trigger = NULL;
bool b, good = false;
assert(s);
assert(ret_trigger_path);
switch (s->type) {
case PATH_EXISTS:
@@ -207,7 +211,7 @@ static bool path_spec_check_good(PathSpec *s, bool initial, bool from_trigger_no
break;
case PATH_EXISTS_GLOB:
good = glob_exists(s->path) > 0;
good = glob_first(s->path, &trigger) > 0;
break;
case PATH_DIRECTORY_NOT_EMPTY: {
@@ -229,6 +233,15 @@ static bool path_spec_check_good(PathSpec *s, bool initial, bool from_trigger_no
;
}
if (good) {
if (!trigger) {
trigger = strdup(s->path);
if (!trigger)
(void) log_oom_debug();
}
*ret_trigger_path = TAKE_PTR(trigger);
}
return good;
}
@@ -494,9 +507,11 @@ static void path_enter_dead(Path *p, PathResult f) {
path_set_state(p, p->result != PATH_SUCCESS ? PATH_FAILED : PATH_DEAD);
}
static void path_enter_running(Path *p) {
static void path_enter_running(Path *p, char *trigger_path) {
_cleanup_(activation_details_unrefp) ActivationDetails *details = NULL;
_cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
Unit *trigger;
Job *job;
int r;
assert(p);
@@ -518,10 +533,22 @@ static void path_enter_running(Path *p) {
return;
}
r = manager_add_job(UNIT(p)->manager, JOB_START, trigger, JOB_REPLACE, NULL, &error, NULL);
details = activation_details_new(UNIT(p));
if (!details) {
r = -ENOMEM;
goto fail;
}
r = free_and_strdup(&(ACTIVATION_DETAILS_PATH(details))->trigger_path_filename, trigger_path);
if (r < 0)
goto fail;
r = manager_add_job(UNIT(p)->manager, JOB_START, trigger, JOB_REPLACE, NULL, &error, &job);
if (r < 0)
goto fail;
job_set_activation_details(job, details);
path_set_state(p, PATH_RUNNING);
path_unwatch(p);
@@ -532,17 +559,19 @@ fail:
path_enter_dead(p, PATH_FAILURE_RESOURCES);
}
static bool path_check_good(Path *p, bool initial, bool from_trigger_notify) {
static bool path_check_good(Path *p, bool initial, bool from_trigger_notify, char **ret_trigger_path) {
assert(p);
assert(ret_trigger_path);
LIST_FOREACH(spec, s, p->specs)
if (path_spec_check_good(s, initial, from_trigger_notify))
if (path_spec_check_good(s, initial, from_trigger_notify, ret_trigger_path))
return true;
return false;
}
static void path_enter_waiting(Path *p, bool initial, bool from_trigger_notify) {
_cleanup_free_ char *trigger_path = NULL;
Unit *trigger;
int r;
@@ -554,9 +583,9 @@ static void path_enter_waiting(Path *p, bool initial, bool from_trigger_notify)
return;
}
if (path_check_good(p, initial, from_trigger_notify)) {
if (path_check_good(p, initial, from_trigger_notify, &trigger_path)) {
log_unit_debug(UNIT(p), "Got triggered.");
path_enter_running(p);
path_enter_running(p, trigger_path);
return;
}
@@ -568,9 +597,9 @@ static void path_enter_waiting(Path *p, bool initial, bool from_trigger_notify)
* might have appeared/been removed by now, so we must
* recheck */
if (path_check_good(p, false, from_trigger_notify)) {
if (path_check_good(p, false, from_trigger_notify, &trigger_path)) {
log_unit_debug(UNIT(p), "Got triggered.");
path_enter_running(p);
path_enter_running(p, trigger_path);
return;
}
@@ -759,7 +788,7 @@ static int path_dispatch_io(sd_event_source *source, int fd, uint32_t revents, v
goto fail;
if (changed)
path_enter_running(p);
path_enter_running(p, found->path);
else
path_enter_waiting(p, false, false);
@@ -832,6 +861,89 @@ static int path_can_start(Unit *u) {
return 1;
}
static void activation_details_path_done(ActivationDetails *details) {
ActivationDetailsPath *p = ASSERT_PTR(ACTIVATION_DETAILS_PATH(details));
p->trigger_path_filename = mfree(p->trigger_path_filename);
}
static void activation_details_path_serialize(ActivationDetails *details, FILE *f) {
ActivationDetailsPath *p = ASSERT_PTR(ACTIVATION_DETAILS_PATH(details));
assert(f);
if (p->trigger_path_filename)
(void) serialize_item(f, "activation-details-path-filename", p->trigger_path_filename);
}
static int activation_details_path_deserialize(const char *key, const char *value, ActivationDetails **details) {
int r;
assert(key);
assert(value);
if (!details || !*details)
return -EINVAL;
ActivationDetailsPath *p = ACTIVATION_DETAILS_PATH(*details);
if (!p)
return -EINVAL;
if (!streq(key, "activation-details-path-filename"))
return -EINVAL;
r = free_and_strdup(&p->trigger_path_filename, value);
if (r < 0)
return r;
return 0;
}
static int activation_details_path_append_env(ActivationDetails *details, char ***strv) {
ActivationDetailsPath *p = ACTIVATION_DETAILS_PATH(details);
char *s;
int r;
assert(details);
assert(strv);
assert(p);
if (isempty(p->trigger_path_filename))
return 0;
s = strjoin("TRIGGER_PATH=", p->trigger_path_filename);
if (!s)
return -ENOMEM;
r = strv_consume(strv, TAKE_PTR(s));
if (r < 0)
return r;
return 1; /* Return the number of variables added to the env block */
}
static int activation_details_path_append_pair(ActivationDetails *details, char ***strv) {
ActivationDetailsPath *p = ACTIVATION_DETAILS_PATH(details);
int r;
assert(details);
assert(strv);
assert(p);
if (isempty(p->trigger_path_filename))
return 0;
r = strv_extend(strv, "trigger_path");
if (r < 0)
return r;
r = strv_extend(strv, p->trigger_path_filename);
if (r < 0)
return r;
return 1; /* Return the number of pairs added to the env block */
}
static const char* const path_type_table[_PATH_TYPE_MAX] = {
[PATH_EXISTS] = "PathExists",
[PATH_EXISTS_GLOB] = "PathExistsGlob",
@@ -890,3 +1002,13 @@ const UnitVTable path_vtable = {
.can_start = path_can_start,
};
const ActivationDetailsVTable activation_details_path_vtable = {
.object_size = sizeof(ActivationDetailsPath),
.done = activation_details_path_done,
.serialize = activation_details_path_serialize,
.deserialize = activation_details_path_deserialize,
.append_env = activation_details_path_append_env,
.append_pair = activation_details_path_append_pair,
};
+8
View File
@@ -3,6 +3,7 @@
typedef struct Path Path;
typedef struct PathSpec PathSpec;
typedef struct ActivationDetailsPath ActivationDetailsPath;
#include "unit.h"
@@ -66,9 +67,15 @@ struct Path {
RateLimit trigger_limit;
};
struct ActivationDetailsPath {
ActivationDetails meta;
char *trigger_path_filename;
};
void path_free_specs(Path *p);
extern const UnitVTable path_vtable;
extern const ActivationDetailsVTable activation_details_path_vtable;
const char* path_type_to_string(PathType i) _const_;
PathType path_type_from_string(const char *s) _pure_;
@@ -77,3 +84,4 @@ const char* path_result_to_string(PathResult i) _const_;
PathResult path_result_from_string(const char *s) _pure_;
DEFINE_CAST(PATH, Path);
DEFINE_ACTIVATION_DETAILS_CAST(ACTIVATION_DETAILS_PATH, ActivationDetailsPath, PATH);
+1
View File
@@ -5930,6 +5930,7 @@ int unit_get_dependency_array(const Unit *u, UnitDependencyAtom atom, Unit ***re
}
const ActivationDetailsVTable * const activation_details_vtable[_UNIT_TYPE_MAX] = {
[UNIT_PATH] = &activation_details_path_vtable,
};
ActivationDetails *activation_details_new(Unit *trigger_unit) {