@org.freedesktop.DBus.Property.EmitsChangedSignal("const")
readonly as OnFailureOf = ['...', ...];
@org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+ readonly as OnSuccess = ['...', ...];
+ @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+ readonly as OnSuccessOf = ['...', ...];
+ @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
readonly as Triggers = ['...', ...];
@org.freedesktop.DBus.Property.EmitsChangedSignal("const")
readonly as TriggeredBy = ['...', ...];
@org.freedesktop.DBus.Property.EmitsChangedSignal("const")
readonly b DefaultDependencies = ...;
@org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+ readonly s OnSuccesJobMode = '...';
+ @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
readonly s OnFailureJobMode = '...';
@org.freedesktop.DBus.Property.EmitsChangedSignal("const")
readonly b IgnoreOnIsolate = ...;
<!--property OnFailureOf is not documented!-->
+ <!--property OnSuccess is not documented!-->
+
+ <!--property OnSuccessOf is not documented!-->
+
<!--property ReloadPropagatedFrom is not documented!-->
<!--property PropagatesStopTo is not documented!-->
<!--property CanFreeze is not documented!-->
+ <!--property OnSuccesJobMode is not documented!-->
+
<!--property OnFailureJobMode is not documented!-->
<!--property JobRunningTimeoutUSec is not documented!-->
<variablelist class="dbus-property" generated="True" extra-ref="OnFailureOf"/>
+ <variablelist class="dbus-property" generated="True" extra-ref="OnSuccess"/>
+
+ <variablelist class="dbus-property" generated="True" extra-ref="OnSuccessOf"/>
+
<variablelist class="dbus-property" generated="True" extra-ref="Triggers"/>
<variablelist class="dbus-property" generated="True" extra-ref="TriggeredBy"/>
<variablelist class="dbus-property" generated="True" extra-ref="DefaultDependencies"/>
+ <variablelist class="dbus-property" generated="True" extra-ref="OnSuccesJobMode"/>
+
<variablelist class="dbus-property" generated="True" extra-ref="OnFailureJobMode"/>
<variablelist class="dbus-property" generated="True" extra-ref="IgnoreOnIsolate"/>
<varlistentry>
<term><varname>OnFailure=</varname></term>
- <listitem><para>A space-separated list of one or more units
- that are activated when this unit enters the
- <literal>failed</literal> state. A service unit using
- <varname>Restart=</varname> enters the failed state only after
- the start limits are reached.</para></listitem>
+ <listitem><para>A space-separated list of one or more units that are activated when this unit enters
+ the <literal>failed</literal> state. A service unit using <varname>Restart=</varname> enters the
+ failed state only after the start limits are reached.</para></listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><varname>OnSuccess=</varname></term>
+
+ <listitem><para>A space-separated list of one or more units that are activated when this unit enters
+ the <literal>inactive</literal> state.</para></listitem>
</varlistentry>
<varlistentry>
[UNIT_CONFLICTED_BY] = "ConflictedBy",
[UNIT_BEFORE] = "Before",
[UNIT_AFTER] = "After",
+ [UNIT_ON_SUCCESS] = "OnSuccess",
+ [UNIT_ON_SUCCESS_OF] = "OnSuccessOf",
[UNIT_ON_FAILURE] = "OnFailure",
[UNIT_ON_FAILURE_OF] = "OnFailureOf",
[UNIT_TRIGGERS] = "Triggers",
UNIT_BEFORE, /* inverse of 'before' is 'after' and vice versa */
UNIT_AFTER,
- /* On Failure */
+ /* OnSuccess= + OnFailure= */
+ UNIT_ON_SUCCESS,
+ UNIT_ON_SUCCESS_OF,
UNIT_ON_FAILURE,
UNIT_ON_FAILURE_OF,
SD_BUS_PROPERTY("After", "as", property_get_dependencies, 0, SD_BUS_VTABLE_PROPERTY_CONST),
SD_BUS_PROPERTY("OnFailure", "as", property_get_dependencies, 0, SD_BUS_VTABLE_PROPERTY_CONST),
SD_BUS_PROPERTY("OnFailureOf", "as", property_get_dependencies, 0, SD_BUS_VTABLE_PROPERTY_CONST),
+ SD_BUS_PROPERTY("OnSuccess", "as", property_get_dependencies, 0, SD_BUS_VTABLE_PROPERTY_CONST),
+ SD_BUS_PROPERTY("OnSuccessOf", "as", property_get_dependencies, 0, SD_BUS_VTABLE_PROPERTY_CONST),
SD_BUS_PROPERTY("Triggers", "as", property_get_dependencies, 0, SD_BUS_VTABLE_PROPERTY_CONST),
SD_BUS_PROPERTY("TriggeredBy", "as", property_get_dependencies, 0, SD_BUS_VTABLE_PROPERTY_CONST),
SD_BUS_PROPERTY("PropagatesReloadTo", "as", property_get_dependencies, 0, SD_BUS_VTABLE_PROPERTY_CONST),
SD_BUS_PROPERTY("RefuseManualStop", "b", bus_property_get_bool, offsetof(Unit, refuse_manual_stop), SD_BUS_VTABLE_PROPERTY_CONST),
SD_BUS_PROPERTY("AllowIsolate", "b", bus_property_get_bool, offsetof(Unit, allow_isolate), SD_BUS_VTABLE_PROPERTY_CONST),
SD_BUS_PROPERTY("DefaultDependencies", "b", bus_property_get_bool, offsetof(Unit, default_dependencies), SD_BUS_VTABLE_PROPERTY_CONST),
+ SD_BUS_PROPERTY("OnSuccesJobMode", "s", property_get_job_mode, offsetof(Unit, on_success_job_mode), SD_BUS_VTABLE_PROPERTY_CONST),
SD_BUS_PROPERTY("OnFailureJobMode", "s", property_get_job_mode, offsetof(Unit, on_failure_job_mode), SD_BUS_VTABLE_PROPERTY_CONST),
SD_BUS_PROPERTY("IgnoreOnIsolate", "b", bus_property_get_bool, offsetof(Unit, ignore_on_isolate), SD_BUS_VTABLE_PROPERTY_CONST),
SD_BUS_PROPERTY("NeedDaemonReload", "b", property_get_need_daemon_reload, 0, SD_BUS_VTABLE_PROPERTY_CONST),
if (streq(name, "DefaultDependencies"))
return bus_set_transient_bool(u, name, &u->default_dependencies, message, flags, error);
+ if (streq(name, "OnSuccessJobMode"))
+ return bus_set_transient_job_mode(u, name, &u->on_success_job_mode, message, flags, error);
+
if (streq(name, "OnFailureJobMode"))
return bus_set_transient_job_mode(u, name, &u->on_failure_job_mode, message, flags, error);
UNIT_CONFLICTS,
UNIT_BEFORE,
UNIT_AFTER,
+ UNIT_ON_SUCCESS,
UNIT_ON_FAILURE,
UNIT_PROPAGATES_RELOAD_TO,
UNIT_RELOAD_PROPAGATED_FROM,
job_type_to_string(t),
job_result_to_string(result)));
- unit_start_on_failure(u);
+ unit_start_on_failure(u, "OnFailure=", UNIT_ATOM_ON_FAILURE, u->on_failure_job_mode);
}
unit_trigger_notify(u);
Unit.Conflicts, config_parse_unit_deps, UNIT_CONFLICTS, 0
Unit.Before, config_parse_unit_deps, UNIT_BEFORE, 0
Unit.After, config_parse_unit_deps, UNIT_AFTER, 0
+Unit.OnSuccess, config_parse_unit_deps, UNIT_ON_SUCCESS, 0
Unit.OnFailure, config_parse_unit_deps, UNIT_ON_FAILURE, 0
Unit.PropagatesReloadTo, config_parse_unit_deps, UNIT_PROPAGATES_RELOAD_TO, 0
Unit.PropagateReloadTo, config_parse_unit_deps, UNIT_PROPAGATES_RELOAD_TO, 0
Unit.RefuseManualStop, config_parse_bool, 0, offsetof(Unit, refuse_manual_stop)
Unit.AllowIsolate, config_parse_bool, 0, offsetof(Unit, allow_isolate)
Unit.DefaultDependencies, config_parse_bool, 0, offsetof(Unit, default_dependencies)
+Unit.OnSuccessJobMode, config_parse_job_mode, 0, offsetof(Unit, on_success_job_mode)
Unit.OnFailureJobMode, config_parse_job_mode, 0, offsetof(Unit, on_failure_job_mode)
{# The following is a legacy alias name for compatibility #}
Unit.OnFailureIsolate, config_parse_job_mode_isolate, 0, offsetof(Unit, on_failure_job_mode)
/* 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,
* things discoverable/debuggable as they are the inverse dependencies to some of the above. As they
* have no effect of their own, they all map to no atoms at all, i.e. the value 0. */
[UNIT_RELOAD_PROPAGATED_FROM] = 0,
+ [UNIT_ON_SUCCESS_OF] = 0,
[UNIT_ON_FAILURE_OF] = 0,
[UNIT_STOP_PROPAGATED_FROM] = 0,
};
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;
/* 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,
"%s\tRefuseManualStart: %s\n"
"%s\tRefuseManualStop: %s\n"
"%s\tDefaultDependencies: %s\n"
+ "%s\tOnSuccessJobMode: %s\n"
"%s\tOnFailureJobMode: %s\n"
"%s\tIgnoreOnIsolate: %s\n",
prefix, yes_no(u->stop_when_unneeded),
prefix, yes_no(u->refuse_manual_start),
prefix, yes_no(u->refuse_manual_stop),
prefix, yes_no(u->default_dependencies),
+ prefix, job_mode_to_string(u->on_success_job_mode),
prefix, job_mode_to_string(u->on_failure_job_mode),
prefix, yes_no(u->ignore_on_isolate));
u->unit_file_state = _UNIT_FILE_STATE_INVALID;
u->unit_file_preset = -1;
u->on_failure_job_mode = JOB_REPLACE;
+ u->on_success_job_mode = JOB_FAIL;
u->cgroup_control_inotify_wd = -1;
u->cgroup_memory_inotify_wd = -1;
u->job_timeout = USEC_INFINITY;
UNIT_CONFLICTED_BY,
UNIT_BEFORE,
UNIT_AFTER,
+ UNIT_ON_SUCCESS,
UNIT_ON_FAILURE,
UNIT_TRIGGERS,
UNIT_TRIGGERED_BY))
return set_ensure_put(&u->manager->startup_units, NULL, u);
}
+static int unit_validate_on_failure_job_mode(
+ Unit *u,
+ const char *job_mode_setting,
+ JobMode job_mode,
+ const char *dependency_name,
+ UnitDependencyAtom atom) {
+
+ Unit *other, *found = NULL;
+
+ if (job_mode != JOB_ISOLATE)
+ return 0;
+
+ UNIT_FOREACH_DEPENDENCY(other, u, atom) {
+ if (!found)
+ found = other;
+ else if (found != other)
+ return log_unit_error_errno(
+ u, SYNTHETIC_ERRNO(ENOEXEC),
+ "More than one %s dependencies specified but %sisolate set. Refusing.",
+ dependency_name, job_mode_setting);
+ }
+
+ return 0;
+}
+
int unit_load(Unit *u) {
int r;
if (r < 0)
goto fail;
- if (u->on_failure_job_mode == JOB_ISOLATE) {
- Unit *other, *found = NULL;
+ r = unit_validate_on_failure_job_mode(u, "OnSuccessJobMode=", u->on_success_job_mode, "OnSuccess=", UNIT_ATOM_ON_SUCCESS);
+ if (r < 0)
+ goto fail;
- UNIT_FOREACH_DEPENDENCY(other, u, UNIT_ATOM_ON_FAILURE) {
- if (!found)
- found = other;
- else if (found != other) {
- r = log_unit_error_errno(u, SYNTHETIC_ERRNO(ENOEXEC),
- "More than one OnFailure= dependencies specified but OnFailureJobMode=isolate set. Refusing.");
- goto fail;
- }
- }
- }
+ r = unit_validate_on_failure_job_mode(u, "OnFailureJobMode=", u->on_failure_job_mode, "OnFailure=", UNIT_ATOM_ON_FAILURE);
+ if (r < 0)
+ goto fail;
if (u->job_running_timeout != USEC_INFINITY && u->job_running_timeout > u->job_timeout)
log_unit_warning(u, "JobRunningTimeoutSec= is greater than JobTimeoutSec=, it has no effect.");
manager_add_job(u->manager, JOB_STOP, other, JOB_REPLACE, NULL, NULL, NULL);
}
-void unit_start_on_failure(Unit *u) {
+void unit_start_on_failure(
+ Unit *u,
+ const char *dependency_name,
+ UnitDependencyAtom atom,
+ JobMode job_mode) {
+
bool logged = false;
Unit *other;
int r;
assert(u);
+ assert(dependency_name);
+ assert(IN_SET(atom, UNIT_ATOM_ON_SUCCESS, UNIT_ATOM_ON_FAILURE));
+
+ /* Act on OnFailure= and OnSuccess= dependencies */
- UNIT_FOREACH_DEPENDENCY(other, u, UNIT_ATOM_ON_FAILURE) {
+ UNIT_FOREACH_DEPENDENCY(other, u, atom) {
_cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
if (!logged) {
- log_unit_info(u, "Triggering OnFailure= dependencies.");
+ log_unit_info(u, "Triggering %s dependencies.", dependency_name);
logged = true;
}
- r = manager_add_job(u->manager, JOB_START, other, u->on_failure_job_mode, NULL, &error, NULL);
+ r = manager_add_job(u->manager, JOB_START, other, job_mode, NULL, &error, NULL);
if (r < 0)
- log_unit_warning_errno(u, r, "Failed to enqueue OnFailure= job, ignoring: %s", bus_error_message(&error, r));
+ log_unit_warning_errno(
+ u, r, "Failed to enqueue %s job, ignoring: %s",
+ dependency_name, bus_error_message(&error, r));
}
+
+ if (logged)
+ log_unit_debug(u, "Triggering %s dependencies done.", dependency_name);
}
void unit_trigger_notify(Unit *u) {
log_unit_debug(u, "Unit entered failed state.");
if (!(flags & UNIT_NOTIFY_WILL_AUTO_RESTART))
- unit_start_on_failure(u);
+ unit_start_on_failure(u, "OnFailure=", UNIT_ATOM_ON_FAILURE, u->on_failure_job_mode);
}
if (UNIT_IS_ACTIVE_OR_RELOADING(ns) && !UNIT_IS_ACTIVE_OR_RELOADING(os)) {
unit_emit_audit_stop(u, ns);
unit_log_resources(u);
}
+
+ if (ns == UNIT_INACTIVE && !IN_SET(os, UNIT_FAILED, UNIT_INACTIVE, UNIT_MAINTENANCE) &&
+ !(flags & UNIT_NOTIFY_WILL_AUTO_RESTART))
+ unit_start_on_failure(u, "OnSuccess=", UNIT_ATOM_ON_SUCCESS, u->on_success_job_mode);
}
manager_recheck_journal(m);
[UNIT_CONFLICTED_BY] = UNIT_CONFLICTS,
[UNIT_BEFORE] = UNIT_AFTER,
[UNIT_AFTER] = UNIT_BEFORE,
+ [UNIT_ON_SUCCESS] = UNIT_ON_SUCCESS_OF,
+ [UNIT_ON_SUCCESS_OF] = UNIT_ON_SUCCESS,
[UNIT_ON_FAILURE] = UNIT_ON_FAILURE_OF,
[UNIT_ON_FAILURE_OF] = UNIT_ON_FAILURE,
[UNIT_REFERENCES] = UNIT_REFERENCED_BY,
* ones which might have appeared. */
sd_event_source *rewatch_pids_event_source;
- /* How to start OnFailure units */
+ /* How to start OnSuccess=/OnFailure= units */
+ JobMode on_success_job_mode;
JobMode on_failure_job_mode;
/* Tweaking the GC logic */
int unit_add_default_target_dependency(Unit *u, Unit *target);
-void unit_start_on_failure(Unit *u);
+void unit_start_on_failure(Unit *u, const char *dependency_name, UnitDependencyAtom atom, JobMode job_mode);
void unit_trigger_notify(Unit *u);
UnitFileState unit_get_unit_file_state(Unit *u);
OnFailure=
OnFailureIsolate=
OnFailureJobMode=
+OnSuccess=
+OnSuccessJobMode=
PartOf=
PropagateReloadFrom=
PropagateReloadTo=