Merge pull request #20833 from pdmorrow/onfailure_env
service: pass exiting service state to triggered On{Failure,Success}= dependencies
This commit is contained in:
+14
-13
@@ -370,21 +370,22 @@ static inline bool exec_context_with_rootfs(const ExecContext *c) {
|
||||
}
|
||||
|
||||
typedef enum ExecFlags {
|
||||
EXEC_APPLY_SANDBOXING = 1 << 0,
|
||||
EXEC_APPLY_CHROOT = 1 << 1,
|
||||
EXEC_APPLY_TTY_STDIN = 1 << 2,
|
||||
EXEC_PASS_LOG_UNIT = 1 << 3, /* Whether to pass the unit name to the service's journal stream connection */
|
||||
EXEC_CHOWN_DIRECTORIES = 1 << 4, /* chown() the runtime/state/cache/log directories to the user we run as, under all conditions */
|
||||
EXEC_NSS_BYPASS_BUS = 1 << 5, /* Set the SYSTEMD_NSS_BYPASS_BUS environment variable, to disable nss-systemd for dbus */
|
||||
EXEC_CGROUP_DELEGATE = 1 << 6,
|
||||
EXEC_IS_CONTROL = 1 << 7,
|
||||
EXEC_CONTROL_CGROUP = 1 << 8, /* Place the process not in the indicated cgroup but in a subcgroup '/.control', but only EXEC_CGROUP_DELEGATE and EXEC_IS_CONTROL is set, too */
|
||||
EXEC_WRITE_CREDENTIALS = 1 << 9, /* Set up the credential store logic */
|
||||
EXEC_APPLY_SANDBOXING = 1 << 0,
|
||||
EXEC_APPLY_CHROOT = 1 << 1,
|
||||
EXEC_APPLY_TTY_STDIN = 1 << 2,
|
||||
EXEC_PASS_LOG_UNIT = 1 << 3, /* Whether to pass the unit name to the service's journal stream connection */
|
||||
EXEC_CHOWN_DIRECTORIES = 1 << 4, /* chown() the runtime/state/cache/log directories to the user we run as, under all conditions */
|
||||
EXEC_NSS_BYPASS_BUS = 1 << 5, /* Set the SYSTEMD_NSS_BYPASS_BUS environment variable, to disable nss-systemd for dbus */
|
||||
EXEC_CGROUP_DELEGATE = 1 << 6,
|
||||
EXEC_IS_CONTROL = 1 << 7,
|
||||
EXEC_CONTROL_CGROUP = 1 << 8, /* Place the process not in the indicated cgroup but in a subcgroup '/.control', but only EXEC_CGROUP_DELEGATE and EXEC_IS_CONTROL is set, too */
|
||||
EXEC_WRITE_CREDENTIALS = 1 << 9, /* Set up the credential store logic */
|
||||
|
||||
/* The following are not used by execute.c, but by consumers internally */
|
||||
EXEC_PASS_FDS = 1 << 10,
|
||||
EXEC_SETENV_RESULT = 1 << 11,
|
||||
EXEC_SET_WATCHDOG = 1 << 12,
|
||||
EXEC_PASS_FDS = 1 << 10,
|
||||
EXEC_SETENV_RESULT = 1 << 11,
|
||||
EXEC_SET_WATCHDOG = 1 << 12,
|
||||
EXEC_SETENV_MONITOR_RESULT = 1 << 13, /* Pass exit status to OnFailure= and OnSuccess= dependencies. */
|
||||
} ExecFlags;
|
||||
|
||||
/* Parameters for a specific invocation of a command. This structure is put together right before a command is
|
||||
|
||||
@@ -99,6 +99,13 @@ Job* job_free(Job *j) {
|
||||
assert(!j->subject_list);
|
||||
assert(!j->object_list);
|
||||
|
||||
do {
|
||||
Unit *tu = NULL;
|
||||
|
||||
LIST_FOREACH(triggered_by, tu, j->triggered_by)
|
||||
LIST_REMOVE(triggered_by, j->triggered_by, tu);
|
||||
} while (!LIST_IS_EMPTY(j->triggered_by));
|
||||
|
||||
job_unlink(j);
|
||||
|
||||
sd_bus_track_unref(j->bus_track);
|
||||
@@ -107,6 +114,13 @@ Job* job_free(Job *j) {
|
||||
return mfree(j);
|
||||
}
|
||||
|
||||
void job_add_triggering_unit(Job *j, Unit *u) {
|
||||
assert(j);
|
||||
assert(u);
|
||||
|
||||
LIST_APPEND(triggered_by, j->triggered_by, u);
|
||||
}
|
||||
|
||||
static void job_set_state(Job *j, JobState state) {
|
||||
assert(j);
|
||||
assert(state >= 0);
|
||||
@@ -187,6 +201,8 @@ static void job_merge_into_installed(Job *j, Job *other) {
|
||||
|
||||
j->irreversible = j->irreversible || other->irreversible;
|
||||
j->ignore_order = j->ignore_order || other->ignore_order;
|
||||
if (other->triggered_by)
|
||||
LIST_JOIN(triggered_by, j->triggered_by, other->triggered_by);
|
||||
}
|
||||
|
||||
Job* job_install(Job *j) {
|
||||
|
||||
@@ -124,6 +124,8 @@ struct Job {
|
||||
LIST_HEAD(JobDependency, subject_list);
|
||||
LIST_HEAD(JobDependency, object_list);
|
||||
|
||||
LIST_HEAD(Unit, triggered_by);
|
||||
|
||||
/* Used for graph algs as a "I have been here" marker */
|
||||
Job* marker;
|
||||
unsigned generation;
|
||||
@@ -244,3 +246,5 @@ JobResult job_result_from_string(const char *s) _pure_;
|
||||
const char* job_type_to_access_method(JobType t);
|
||||
|
||||
int job_compare(Job *a, Job *b, UnitDependencyAtom assume_dep);
|
||||
|
||||
void job_add_triggering_unit(Job *j, Unit *u);
|
||||
|
||||
+98
-2
@@ -1440,6 +1440,93 @@ static bool service_exec_needs_notify_socket(Service *s, ExecFlags flags) {
|
||||
return s->notify_access != NOTIFY_NONE;
|
||||
}
|
||||
|
||||
static int service_create_monitor_md_env(Job *j, char **ret) {
|
||||
_cleanup_free_ char *var = NULL;
|
||||
const char *list_delim = ";";
|
||||
bool first = true;
|
||||
Unit *tu;
|
||||
|
||||
assert(j);
|
||||
assert(ret);
|
||||
|
||||
/* Create an environment variable 'MONITOR_METADATA', if creation is successful
|
||||
* a pointer to it is returned via ret.
|
||||
*
|
||||
* This variable contains a space separated set of fields which relate to
|
||||
* the service(s) which triggered job 'j'. Job 'j' is the JOB_START job for
|
||||
* an OnFailure= or OnSuccess= dependency. Format of the MONITOR_METADATA
|
||||
* variable is as follows:
|
||||
*
|
||||
* MONITOR_METADATA="SERVICE_RESULT=<result-string0>,EXIT_CODE=<exit-code0>,EXIT_STATUS=<exit-status0>,
|
||||
* INVOCATION_ID=<id>,UNIT=<triggering-unit0.service>;
|
||||
* SERVICE_RESULT=<result-stringN>,EXIT_CODE=<exit-codeN>,EXIT_STATUS=<exit-statusN>,
|
||||
* INVOCATION_ID=<id>,UNIT=<triggering-unitN.service>"
|
||||
*
|
||||
* Multiple results may be passed as in the above example if jobs are merged, i.e.
|
||||
* some services a and b contain an OnFailure= or OnSuccess= dependency on the same
|
||||
* service.
|
||||
*
|
||||
* For example:
|
||||
*
|
||||
* MONITOR_METADATA="SERVICE_RESULT=exit-code,EXIT_CODE=exited,EXIT_STATUS=1,INVOCATION_ID=02dd868af2f344b18edaf74b618b2f90,UNIT=failer.service;
|
||||
* SERVICE_RESULT=exit-code,EXIT_CODE=exited,EXIT_STATUS=1,INVOCATION_ID=80cb228bd7344f77a090eda603a3cfe2,UNIT=failer2.service"
|
||||
*/
|
||||
|
||||
LIST_FOREACH(triggered_by, tu, j->triggered_by) {
|
||||
Service *env_source = SERVICE(tu);
|
||||
int r;
|
||||
|
||||
if (!env_source)
|
||||
continue;
|
||||
|
||||
if (first) {
|
||||
/* Add the environment variable name first. */
|
||||
r = strextendf(&var, "MONITOR_METADATA=");
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
}
|
||||
|
||||
r = strextendf(&var, "%sSERVICE_RESULT=%s",
|
||||
!first ? list_delim : "", service_result_to_string(env_source->result));
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
first = false;
|
||||
|
||||
if (env_source->main_exec_status.pid > 0 &&
|
||||
dual_timestamp_is_set(&env_source->main_exec_status.exit_timestamp)) {
|
||||
r = strextendf(&var, ",EXIT_CODE=%s",
|
||||
sigchld_code_to_string(env_source->main_exec_status.code));
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
if (env_source->main_exec_status.code == CLD_EXITED)
|
||||
r = strextendf(&var, ",EXIT_STATUS=%i",
|
||||
env_source->main_exec_status.status);
|
||||
else
|
||||
r = strextendf(&var, ",EXIT_STATUS=%s",
|
||||
signal_to_string(env_source->main_exec_status.status));
|
||||
if (r < 0)
|
||||
return r;
|
||||
}
|
||||
|
||||
if (!sd_id128_is_null(UNIT(env_source)->invocation_id)) {
|
||||
r = strextendf(&var, ",INVOCATION_ID=" SD_ID128_FORMAT_STR,
|
||||
SD_ID128_FORMAT_VAL(UNIT(env_source)->invocation_id));
|
||||
if (r < 0)
|
||||
return r;
|
||||
}
|
||||
|
||||
r = strextendf(&var, ",UNIT=%s", UNIT(env_source)->id);
|
||||
if (r < 0)
|
||||
return r;
|
||||
}
|
||||
|
||||
*ret = TAKE_PTR(var);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int service_spawn(
|
||||
Service *s,
|
||||
ExecCommand *c,
|
||||
@@ -1574,9 +1661,18 @@ static int service_spawn(
|
||||
r = asprintf(our_env + n_env++, "EXIT_STATUS=%i", s->main_exec_status.status);
|
||||
else
|
||||
r = asprintf(our_env + n_env++, "EXIT_STATUS=%s", signal_to_string(s->main_exec_status.status));
|
||||
|
||||
if (r < 0)
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
} else if (flags & EXEC_SETENV_MONITOR_RESULT) {
|
||||
Job *j = UNIT(s)->job;
|
||||
if (j) {
|
||||
r = service_create_monitor_md_env(j, our_env + n_env++);
|
||||
if (r < 0)
|
||||
return r;
|
||||
}
|
||||
}
|
||||
|
||||
r = unit_set_exec_params(UNIT(s), &exec_params);
|
||||
@@ -2164,7 +2260,7 @@ static void service_enter_start(Service *s) {
|
||||
r = service_spawn(s,
|
||||
c,
|
||||
timeout,
|
||||
EXEC_PASS_FDS|EXEC_APPLY_SANDBOXING|EXEC_APPLY_CHROOT|EXEC_APPLY_TTY_STDIN|EXEC_SET_WATCHDOG|EXEC_WRITE_CREDENTIALS,
|
||||
EXEC_PASS_FDS|EXEC_APPLY_SANDBOXING|EXEC_APPLY_CHROOT|EXEC_APPLY_TTY_STDIN|EXEC_SET_WATCHDOG|EXEC_WRITE_CREDENTIALS|EXEC_SETENV_MONITOR_RESULT,
|
||||
&pid);
|
||||
if (r < 0)
|
||||
goto fail;
|
||||
@@ -2222,7 +2318,7 @@ static void service_enter_start_pre(Service *s) {
|
||||
r = service_spawn(s,
|
||||
s->control_command,
|
||||
s->timeout_start_usec,
|
||||
EXEC_APPLY_SANDBOXING|EXEC_APPLY_CHROOT|EXEC_IS_CONTROL|EXEC_APPLY_TTY_STDIN,
|
||||
EXEC_APPLY_SANDBOXING|EXEC_APPLY_CHROOT|EXEC_IS_CONTROL|EXEC_APPLY_TTY_STDIN|EXEC_SETENV_MONITOR_RESULT,
|
||||
&s->control_pid);
|
||||
if (r < 0)
|
||||
goto fail;
|
||||
|
||||
@@ -82,11 +82,15 @@ static const UnitDependencyAtom atom_map[_UNIT_DEPENDENCY_MAX] = {
|
||||
[UNIT_PROPAGATES_STOP_TO] = UNIT_ATOM_RETROACTIVE_STOP_ON_STOP |
|
||||
UNIT_ATOM_PROPAGATE_STOP,
|
||||
|
||||
[UNIT_ON_FAILURE] = UNIT_ATOM_ON_FAILURE |
|
||||
UNIT_ATOM_BACK_REFERENCE_IMPLIED,
|
||||
|
||||
[UNIT_ON_SUCCESS] = UNIT_ATOM_ON_SUCCESS |
|
||||
UNIT_ATOM_BACK_REFERENCE_IMPLIED,
|
||||
|
||||
/* These are simple dependency types: they consist of a single atom only */
|
||||
[UNIT_BEFORE] = UNIT_ATOM_BEFORE,
|
||||
[UNIT_AFTER] = UNIT_ATOM_AFTER,
|
||||
[UNIT_ON_SUCCESS] = UNIT_ATOM_ON_SUCCESS,
|
||||
[UNIT_ON_FAILURE] = UNIT_ATOM_ON_FAILURE,
|
||||
[UNIT_TRIGGERS] = UNIT_ATOM_TRIGGERS,
|
||||
[UNIT_TRIGGERED_BY] = UNIT_ATOM_TRIGGERED_BY,
|
||||
[UNIT_PROPAGATES_RELOAD_TO] = UNIT_ATOM_PROPAGATES_RELOAD_TO,
|
||||
@@ -196,6 +200,16 @@ UnitDependency unit_dependency_from_unique_atom(UnitDependencyAtom atom) {
|
||||
case UNIT_ATOM_PROPAGATE_STOP_FAILURE:
|
||||
return UNIT_CONFLICTED_BY;
|
||||
|
||||
case UNIT_ATOM_ON_FAILURE |
|
||||
UNIT_ATOM_BACK_REFERENCE_IMPLIED:
|
||||
case UNIT_ATOM_ON_FAILURE:
|
||||
return UNIT_ON_FAILURE;
|
||||
|
||||
case UNIT_ATOM_ON_SUCCESS |
|
||||
UNIT_ATOM_BACK_REFERENCE_IMPLIED:
|
||||
case UNIT_ATOM_ON_SUCCESS:
|
||||
return UNIT_ON_SUCCESS;
|
||||
|
||||
/* And now, the simple ones */
|
||||
|
||||
case UNIT_ATOM_BEFORE:
|
||||
@@ -204,12 +218,6 @@ UnitDependency unit_dependency_from_unique_atom(UnitDependencyAtom atom) {
|
||||
case UNIT_ATOM_AFTER:
|
||||
return UNIT_AFTER;
|
||||
|
||||
case UNIT_ATOM_ON_SUCCESS:
|
||||
return UNIT_ON_SUCCESS;
|
||||
|
||||
case UNIT_ATOM_ON_FAILURE:
|
||||
return UNIT_ON_FAILURE;
|
||||
|
||||
case UNIT_ATOM_TRIGGERS:
|
||||
return UNIT_TRIGGERS;
|
||||
|
||||
|
||||
@@ -66,20 +66,27 @@ typedef enum UnitDependencyAtom {
|
||||
/* Recheck default target deps on other units (which are target units) */
|
||||
UNIT_ATOM_DEFAULT_TARGET_DEPENDENCIES = UINT64_C(1) << 21,
|
||||
|
||||
/* Dependencies which include this atom automatically get a reverse
|
||||
* REFERENCES/REFERENCED_BY dependency. */
|
||||
UNIT_ATOM_BACK_REFERENCE_IMPLIED = UINT64_C(1) << 22,
|
||||
|
||||
/* Trigger a dependency on successful service exit. */
|
||||
UNIT_ATOM_ON_SUCCESS = UINT64_C(1) << 23,
|
||||
/* Trigger a dependency on unsuccessful service exit. */
|
||||
UNIT_ATOM_ON_FAILURE = UINT64_C(1) << 24,
|
||||
|
||||
/* The remaining atoms map 1:1 to the equally named high-level deps */
|
||||
UNIT_ATOM_BEFORE = UINT64_C(1) << 22,
|
||||
UNIT_ATOM_AFTER = UINT64_C(1) << 23,
|
||||
UNIT_ATOM_ON_SUCCESS = UINT64_C(1) << 24,
|
||||
UNIT_ATOM_ON_FAILURE = UINT64_C(1) << 25,
|
||||
UNIT_ATOM_TRIGGERS = UINT64_C(1) << 26,
|
||||
UNIT_ATOM_TRIGGERED_BY = UINT64_C(1) << 27,
|
||||
UNIT_ATOM_PROPAGATES_RELOAD_TO = UINT64_C(1) << 28,
|
||||
UNIT_ATOM_JOINS_NAMESPACE_OF = UINT64_C(1) << 29,
|
||||
UNIT_ATOM_REFERENCES = UINT64_C(1) << 30,
|
||||
UNIT_ATOM_REFERENCED_BY = UINT64_C(1) << 31,
|
||||
UNIT_ATOM_IN_SLICE = UINT64_C(1) << 32,
|
||||
UNIT_ATOM_SLICE_OF = UINT64_C(1) << 33,
|
||||
_UNIT_DEPENDENCY_ATOM_MAX = (UINT64_C(1) << 34) - 1,
|
||||
UNIT_ATOM_BEFORE = UINT64_C(1) << 25,
|
||||
UNIT_ATOM_AFTER = UINT64_C(1) << 26,
|
||||
UNIT_ATOM_TRIGGERS = UINT64_C(1) << 27,
|
||||
UNIT_ATOM_TRIGGERED_BY = UINT64_C(1) << 28,
|
||||
UNIT_ATOM_PROPAGATES_RELOAD_TO = UINT64_C(1) << 29,
|
||||
UNIT_ATOM_JOINS_NAMESPACE_OF = UINT64_C(1) << 30,
|
||||
UNIT_ATOM_REFERENCES = UINT64_C(1) << 31,
|
||||
UNIT_ATOM_REFERENCED_BY = UINT64_C(1) << 32,
|
||||
UNIT_ATOM_IN_SLICE = UINT64_C(1) << 33,
|
||||
UNIT_ATOM_SLICE_OF = UINT64_C(1) << 34,
|
||||
_UNIT_DEPENDENCY_ATOM_MAX = (UINT64_C(1) << 35) - 1,
|
||||
_UNIT_DEPENDENCY_ATOM_INVALID = -EINVAL,
|
||||
} UnitDependencyAtom;
|
||||
|
||||
|
||||
+22
-1
@@ -2222,17 +2222,24 @@ void unit_start_on_failure(
|
||||
|
||||
UNIT_FOREACH_DEPENDENCY(other, u, atom) {
|
||||
_cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
|
||||
Job *job = NULL;
|
||||
|
||||
if (!logged) {
|
||||
log_unit_info(u, "Triggering %s dependencies.", dependency_name);
|
||||
logged = true;
|
||||
}
|
||||
|
||||
r = manager_add_job(u->manager, JOB_START, other, job_mode, NULL, &error, NULL);
|
||||
r = manager_add_job(u->manager, JOB_START, other, job_mode, NULL, &error, &job);
|
||||
if (r < 0)
|
||||
log_unit_warning_errno(
|
||||
u, r, "Failed to enqueue %s job, ignoring: %s",
|
||||
dependency_name, bus_error_message(&error, r));
|
||||
else if (job)
|
||||
/* u will be kept pinned since both UNIT_ON_FAILURE and UNIT_ON_SUCCESS includes
|
||||
* UNIT_ATOM_BACK_REFERENCE_IMPLIED. We save the triggering unit here since we
|
||||
* want to be able to reference it when we come to run the OnFailure= or OnSuccess=
|
||||
* dependency. */
|
||||
job_add_triggering_unit(job, u);
|
||||
}
|
||||
|
||||
if (logged)
|
||||
@@ -3114,6 +3121,20 @@ int unit_add_dependency(
|
||||
noop = false;
|
||||
}
|
||||
|
||||
if (FLAGS_SET(a, UNIT_ATOM_BACK_REFERENCE_IMPLIED)) {
|
||||
r = unit_add_dependency_hashmap(&other->dependencies, UNIT_REFERENCES, u, 0, mask);
|
||||
if (r < 0)
|
||||
return r;
|
||||
if (r)
|
||||
noop = false;
|
||||
|
||||
r = unit_add_dependency_hashmap(&u->dependencies, UNIT_REFERENCED_BY, other, 0, mask);
|
||||
if (r < 0)
|
||||
return r;
|
||||
if (r)
|
||||
noop = false;
|
||||
}
|
||||
|
||||
if (add_reference) {
|
||||
r = unit_add_dependency_hashmap(&u->dependencies, UNIT_REFERENCES, other, mask, 0);
|
||||
if (r < 0)
|
||||
|
||||
@@ -242,6 +242,9 @@ typedef struct Unit {
|
||||
/* Queue of units that have a BindTo= dependency on some other unit, and should possibly be shut down */
|
||||
LIST_FIELDS(Unit, stop_when_bound_queue);
|
||||
|
||||
/* Queue of units which have triggered an OnFailure= or OnSuccess= dependency job. */
|
||||
LIST_FIELDS(Unit, triggered_by);
|
||||
|
||||
/* PIDs we keep an eye on. Note that a unit might have many
|
||||
* more, but these are the ones we care enough about to
|
||||
* process SIGCHLD for */
|
||||
|
||||
Reference in New Issue
Block a user