core: introduce SuccessAction= as unit file property
authorLennart Poettering <lennart@poettering.net>
Thu, 16 Nov 2017 14:18:01 +0000 (15:18 +0100)
committerLennart Poettering <lennart@poettering.net>
Mon, 20 Nov 2017 15:37:22 +0000 (16:37 +0100)
SuccessAction= is similar to FailureAction= but declares what to do on
success of a unit, rather than on failure. This is useful for running
commands in qemu/nspawn images, that shall power down on completion. We
frequently see "ExecStopPost=/usr/bin/systemctl poweroff" or so in unit
files like this. Offer a simple, more declarative alternative for this.

While we are at it, hook up failure action with unit_dump() and
transient units too.

man/systemd.unit.xml
src/core/dbus-unit.c
src/core/load-fragment-gperf.gperf.m4
src/core/unit.c
src/core/unit.h
src/shared/bus-unit-util.c

index 6e9cdae..9c40562 100644 (file)
 
       <varlistentry>
         <term><varname>FailureAction=</varname></term>
-        <listitem><para>Configure the action to take when the unit enters the failed state. Takes the same values as
-        the setting <varname>StartLimitAction=</varname> setting and executes the same actions (see
-        <citerefentry><refentrytitle>systemd.unit</refentrytitle><manvolnum>5</manvolnum></citerefentry>). Defaults to
-        <option>none</option>. </para></listitem>
+        <term><varname>SuccessAction=</varname></term>
+        <listitem><para>Configure the action to take when the unit stops and enters a failed state or inactive
+        state. Takes the same values as the setting <varname>StartLimitAction=</varname> setting and executes the same
+        actions (see
+        <citerefentry><refentrytitle>systemd.unit</refentrytitle><manvolnum>5</manvolnum></citerefentry>). Both options
+        default to <option>none</option>.</para></listitem>
       </varlistentry>
 
       <varlistentry>
index 5344183..d4bdf17 100644 (file)
@@ -799,6 +799,7 @@ const sd_bus_vtable bus_unit_vtable[] = {
         SD_BUS_PROPERTY("StartLimitBurst", "u", bus_property_get_unsigned, offsetof(Unit, start_limit.burst), SD_BUS_VTABLE_PROPERTY_CONST),
         SD_BUS_PROPERTY("StartLimitAction", "s", property_get_emergency_action, offsetof(Unit, start_limit_action), SD_BUS_VTABLE_PROPERTY_CONST),
         SD_BUS_PROPERTY("FailureAction", "s", property_get_emergency_action, offsetof(Unit, failure_action), SD_BUS_VTABLE_PROPERTY_CONST),
+        SD_BUS_PROPERTY("SuccessAction", "s", property_get_emergency_action, offsetof(Unit, success_action), SD_BUS_VTABLE_PROPERTY_CONST),
         SD_BUS_PROPERTY("RebootArgument", "s", NULL, offsetof(Unit, reboot_arg), SD_BUS_VTABLE_PROPERTY_CONST),
         SD_BUS_PROPERTY("InvocationID", "ay", bus_property_get_id128, offsetof(Unit, invocation_id), 0),
         SD_BUS_PROPERTY("CollectMode", "s", property_get_collect_mode, offsetof(Unit, collect_mode), 0),
@@ -1471,6 +1472,30 @@ static int bus_unit_set_transient_property(
 
                 return 1;
 
+        } else if (STR_IN_SET(name, "FailureAction", "SuccessAction")) {
+                EmergencyAction action;
+                const char *s;
+
+                r = sd_bus_message_read(message, "s", &s);
+                if (r < 0)
+                        return r;
+
+                action = emergency_action_from_string(s);
+                if (action < 0)
+                        return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid emergency action: %s", s);
+
+                if (mode != UNIT_CHECK) {
+
+                        if (streq(name, "FailureAction"))
+                                u->failure_action = action;
+                        else
+                                u->success_action = action;
+
+                        unit_write_drop_in_format(u, mode, name, "%s=%s", name, emergency_action_to_string(action));
+                }
+
+                return 1;
+
         } else if (streq(name, "AddRef")) {
 
                 int b;
index 5fc3371..716145a 100644 (file)
@@ -224,6 +224,7 @@ Unit.StartLimitInterval,         config_parse_sec,                   0,
 Unit.StartLimitBurst,            config_parse_unsigned,              0,                             offsetof(Unit, start_limit.burst)
 Unit.StartLimitAction,           config_parse_emergency_action,      0,                             offsetof(Unit, start_limit_action)
 Unit.FailureAction,              config_parse_emergency_action,      0,                             offsetof(Unit, failure_action)
+Unit.SuccessAction,              config_parse_emergency_action,      0,                             offsetof(Unit, success_action)
 Unit.RebootArgument,             config_parse_unit_string_printf,    0,                             offsetof(Unit, reboot_arg)
 Unit.ConditionPathExists,        config_parse_unit_condition_path,   CONDITION_PATH_EXISTS,         offsetof(Unit, conditions)
 Unit.ConditionPathExistsGlob,    config_parse_unit_condition_path,   CONDITION_PATH_EXISTS_GLOB,    offsetof(Unit, conditions)
index 07656c1..386238a 100644 (file)
@@ -1181,6 +1181,11 @@ void unit_dump(Unit *u, FILE *f, const char *prefix) {
         STRV_FOREACH(j, u->dropin_paths)
                 fprintf(f, "%s\tDropIn Path: %s\n", prefix, *j);
 
+        if (u->failure_action != EMERGENCY_ACTION_NONE)
+                fprintf(f, "%s\tFailure Action: %s\n", prefix, emergency_action_to_string(u->failure_action));
+        if (u->success_action != EMERGENCY_ACTION_NONE)
+                fprintf(f, "%s\tSuccess Action: %s\n", prefix, emergency_action_to_string(u->success_action));
+
         if (u->job_timeout != USEC_INFINITY)
                 fprintf(f, "%s\tJob Timeout: %s\n", prefix, format_timespan(timespan, sizeof(timespan), u->job_timeout, 0));
 
@@ -2506,6 +2511,8 @@ void unit_notify(Unit *u, UnitActiveState os, UnitActiveState ns, bool reload_su
 
                 if (os != UNIT_FAILED && ns == UNIT_FAILED)
                         (void) emergency_action(u->manager, u->failure_action, u->reboot_arg, "unit failed");
+                else if (!UNIT_IS_INACTIVE_OR_FAILED(os) && ns == UNIT_INACTIVE)
+                        (void) emergency_action(u->manager, u->success_action, u->reboot_arg, "unit succeeded");
         }
 
         unit_add_to_dbus_queue(u);
index d3403cb..ae3dcfa 100644 (file)
@@ -250,6 +250,7 @@ struct Unit {
         EmergencyAction start_limit_action;
 
         EmergencyAction failure_action;
+        EmergencyAction success_action;
         char *reboot_arg;
 
         /* Make sure we never enter endless loops with the check unneeded logic, or the BindsTo= logic */
index a607233..78b9a68 100644 (file)
@@ -408,7 +408,7 @@ int bus_append_unit_property_assignment(sd_bus_message *m, const char *assignmen
                               "RootDirectory", "SyslogIdentifier", "ProtectSystem",
                               "ProtectHome", "SELinuxContext", "Restart", "RootImage",
                               "NotifyAccess", "RuntimeDirectoryPreserve", "Personality",
-                              "KeyringMode", "CollectMode"))
+                              "KeyringMode", "CollectMode", "FailureAction", "SuccessAction"))
                 r = sd_bus_message_append(m, "v", "s", eq);
 
         else if (streq(field, "StandardInputData")) {