]> git.ipfire.org Git - thirdparty/systemd.git/commitdiff
core: add Unit.Markers property
authorZbigniew Jędrzejewski-Szmek <zbyszek@in.waw.pl>
Sat, 30 Jan 2021 15:58:19 +0000 (16:58 +0100)
committerZbigniew Jędrzejewski-Szmek <zbyszek@in.waw.pl>
Mon, 15 Feb 2021 19:49:14 +0000 (20:49 +0100)
The property is never set by systemd, only reset after a stop or restart or
reload. It may externally be set to mark the unit for a later restart/reload.

I wasn't sure whether to configure the property only for the types where this
makes sense (Service, Swap, etc). But Restart() method is defined on the unit,
and also having this always under the same property name is more convenient.

man/org.freedesktop.systemd1.xml
src/basic/unit-def.c
src/basic/unit-def.h
src/core/dbus-unit.c
src/core/unit-serialize.c
src/core/unit.c
src/core/unit.h
src/udev/test-udev-builtin.c

index 7543a617b7853c810121dd7b782ae2ad7f5f0f41..13771a459a1b8d5ccd786873b10279379bc0526b 100644 (file)
@@ -1685,6 +1685,8 @@ node /org/freedesktop/systemd1/unit/avahi_2ddaemon_2eservice {
       readonly b IgnoreOnIsolate = ...;
       @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
       readonly b NeedDaemonReload = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("false")
+      readonly as Markers = ['...', ...];
       @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
       readonly t JobTimeoutUSec = ...;
       @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
@@ -1969,6 +1971,8 @@ node /org/freedesktop/systemd1/unit/avahi_2ddaemon_2eservice {
 
     <variablelist class="dbus-property" generated="True" extra-ref="NeedDaemonReload"/>
 
+    <variablelist class="dbus-property" generated="True" extra-ref="Markers"/>
+
     <variablelist class="dbus-property" generated="True" extra-ref="JobTimeoutUSec"/>
 
     <variablelist class="dbus-property" generated="True" extra-ref="JobRunningTimeoutUSec"/>
@@ -2160,8 +2164,16 @@ node /org/freedesktop/systemd1/unit/avahi_2ddaemon_2eservice {
 
       <para><varname>NeedDaemonReload</varname> is a boolean that indicates whether the configuration file
       this unit is loaded from (i.e. <varname>FragmentPath</varname> or <varname>SourcePath</varname>) has
-      changed since the configuration was read and hence whether a configuration reload is
-      recommended.</para>
+      changed since the configuration was read and hence whether a configuration reload is recommended.
+      </para>
+
+      <para><varname>Markers</varname> is an array of string flags that can be set using
+      <function>SetUnitProperties()</function> to indicate that the service should be reloaded or
+      restarted. Currently known values are <literal>needs-restart</literal> and
+      <literal>needs-reload</literal>. Package scripts may use the first to mark units for later restart when
+      a new version of the package is installed. Configuration management scripts may use the second to mark
+      units for a later reload when the configuration is adjusted. Those flags are not set by the manager,
+      except to unset as appropriate when when the unit is stopped, restarted, or reloaded.</para>
 
       <para><varname>JobTimeoutUSec</varname> maps directly to the corresponding configuration setting in the
       unit file.</para>
index bb4fa1ec6bf31daf0368475e12579309c0f3fe82..6fbb947f096dab7146b7517cd2cb9f43c0f8529b 100644 (file)
@@ -117,6 +117,13 @@ static const char* const freezer_state_table[_FREEZER_STATE_MAX] = {
 
 DEFINE_STRING_TABLE_LOOKUP(freezer_state, FreezerState);
 
+static const char* const unit_marker_table[_UNIT_MARKER_MAX] = {
+        [UNIT_MARKER_NEEDS_RELOAD]  = "needs-reload",
+        [UNIT_MARKER_NEEDS_RESTART] = "needs-restart",
+};
+
+DEFINE_STRING_TABLE_LOOKUP(unit_marker, UnitMarker);
+
 static const char* const automount_state_table[_AUTOMOUNT_STATE_MAX] = {
         [AUTOMOUNT_DEAD] = "dead",
         [AUTOMOUNT_WAITING] = "waiting",
index ff05799c1d54bc788dc7daa722f12b88ffcd0ab0..7742c5c078593e2ce4c8c264944b8378b51e2f14 100644 (file)
@@ -58,6 +58,13 @@ typedef enum FreezerState {
         _FREEZER_STATE_INVALID = -EINVAL,
 } FreezerState;
 
+typedef enum UnitMarker {
+        UNIT_MARKER_NEEDS_RELOAD,
+        UNIT_MARKER_NEEDS_RESTART,
+        _UNIT_MARKER_MAX,
+        _UNIT_MARKER_INVALID = -1
+} UnitMarker;
+
 typedef enum AutomountState {
         AUTOMOUNT_DEAD,
         AUTOMOUNT_WAITING,
@@ -267,6 +274,9 @@ UnitActiveState unit_active_state_from_string(const char *s) _pure_;
 const char *freezer_state_to_string(FreezerState i) _const_;
 FreezerState freezer_state_from_string(const char *s) _pure_;
 
+const char *unit_marker_to_string(UnitMarker m) _const_;
+UnitMarker unit_marker_from_string(const char *s) _pure_;
+
 const char* automount_state_to_string(AutomountState i) _const_;
 AutomountState automount_state_from_string(const char *s) _pure_;
 
index 90bf5514a3eb25f5a7cbb323958da99378cc9c09..c9837df9a2eb2c30f119f9f43dc6f44c19bbad0f 100644 (file)
@@ -323,6 +323,39 @@ static int property_get_load_error(
         return sd_bus_message_append(reply, "(ss)", NULL, NULL);
 }
 
+static int property_get_markers(
+                sd_bus *bus,
+                const char *path,
+                const char *interface,
+                const char *property,
+                sd_bus_message *reply,
+                void *userdata,
+                sd_bus_error *error) {
+
+        unsigned *markers = userdata;
+        int r;
+
+        assert(bus);
+        assert(reply);
+        assert(markers);
+
+        r = sd_bus_message_open_container(reply, 'a', "s");
+        if (r < 0)
+                return r;
+
+        /* Make sure out values fit in the bitfield. */
+        assert_cc(_UNIT_MARKER_MAX <= sizeof(((Unit){}).markers) * 8);
+
+        for (UnitMarker m = 0; m < _UNIT_MARKER_MAX; m++)
+                if (FLAGS_SET(*markers, 1u << m)) {
+                        r = sd_bus_message_append(reply, "s", unit_marker_to_string(m));
+                        if (r < 0)
+                                return r;
+                }
+
+        return sd_bus_message_close_container(reply);
+}
+
 static const char *const polkit_message_for_job[_JOB_TYPE_MAX] = {
         [JOB_START]       = N_("Authentication is required to start '$(unit)'."),
         [JOB_STOP]        = N_("Authentication is required to stop '$(unit)'."),
@@ -864,6 +897,7 @@ const sd_bus_vtable bus_unit_vtable[] = {
         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),
+        SD_BUS_PROPERTY("Markers", "as", property_get_markers, offsetof(Unit, markers), 0),
         SD_BUS_PROPERTY("JobTimeoutUSec", "t", bus_property_get_usec, offsetof(Unit, job_timeout), SD_BUS_VTABLE_PROPERTY_CONST),
         SD_BUS_PROPERTY("JobRunningTimeoutUSec", "t", bus_property_get_usec, offsetof(Unit, job_running_timeout), SD_BUS_VTABLE_PROPERTY_CONST),
         SD_BUS_PROPERTY("JobTimeoutAction", "s", property_get_emergency_action, offsetof(Unit, job_timeout_action), SD_BUS_VTABLE_PROPERTY_CONST),
index ea4a57907e74b2923e4514fa8e712ef86d4aa41e..3f099248ce680e7d9d2e7251d9703bc3c8fbbe3a 100644 (file)
@@ -28,6 +28,45 @@ static int serialize_cgroup_mask(FILE *f, const char *key, CGroupMask mask) {
         return serialize_item(f, key, s);
 }
 
+/* Make sure out values fit in the bitfield. */
+assert_cc(_UNIT_MARKER_MAX <= sizeof(((Unit){}).markers) * 8);
+
+static int serialize_markers(FILE *f, unsigned markers) {
+        assert(f);
+
+        if (markers == 0)
+                return 0;
+
+        fputs("markers=", f);
+        for (UnitMarker m = 0; m < _UNIT_MARKER_MAX; m++)
+                if (FLAGS_SET(markers, 1u << m))
+                        fputs(unit_marker_to_string(m), f);
+        fputc('\n', f);
+        return 0;
+}
+
+static int deserialize_markers(Unit *u, const char *value) {
+        assert(u);
+        assert(value);
+        int r;
+
+        for (const char *p = value;;) {
+                _cleanup_free_ char *word = NULL;
+
+                r = extract_first_word(&p, &word, NULL, 0);
+                if (r <= 0)
+                        return r;
+
+                UnitMarker m = unit_marker_from_string(word);
+                if (m < 0) {
+                        log_unit_debug_errno(u, m, "Unknown unit marker \"%s\", ignoring.", word);
+                        continue;
+                }
+
+                u->markers |= 1u << m;
+        }
+}
+
 static const char *const ip_accounting_metric_field[_CGROUP_IP_ACCOUNTING_METRIC_MAX] = {
         [CGROUP_IP_INGRESS_BYTES] = "ip-accounting-ingress-bytes",
         [CGROUP_IP_INGRESS_PACKETS] = "ip-accounting-ingress-packets",
@@ -121,6 +160,7 @@ int unit_serialize(Unit *u, FILE *f, FDSet *fds, bool serialize_jobs) {
                 (void) serialize_item_format(f, "invocation-id", SD_ID128_FORMAT_STR, SD_ID128_FORMAT_VAL(u->invocation_id));
 
         (void) serialize_item_format(f, "freezer-state", "%s", freezer_state_to_string(unit_freezer_state(u)));
+        (void) serialize_markers(f, u->markers);
 
         bus_track_serialize(u->bus_track, f, "ref");
 
@@ -365,6 +405,13 @@ int unit_deserialize(Unit *u, FILE *f, FDSet *fds) {
                 } else if (MATCH_DESERIALIZE("freezer-state", l, v, freezer_state_from_string, u->freezer_state))
                         continue;
 
+                else if (streq(l, "markers")) {
+                        r = deserialize_markers(u, v);
+                        if (r < 0)
+                                log_unit_debug_errno(u, r, "Failed to deserialize \"%s=%s\", ignoring: %m", l, v);
+                        continue;
+                }
+
                 /* Check if this is an IP accounting metric serialization field */
                 m = string_table_lookup(ip_accounting_metric_field, ELEMENTSOF(ip_accounting_metric_field), l);
                 if (m >= 0) {
@@ -559,6 +606,15 @@ void unit_dump(Unit *u, FILE *f, const char *prefix) {
                 prefix, strna(u->cgroup_path),
                 prefix, yes_no(u->cgroup_realized));
 
+        if (u->markers != 0) {
+                fprintf(f, "%s\tMarkers:", prefix);
+
+                for (UnitMarker marker = 0; marker < _UNIT_MARKER_MAX; marker++)
+                        if (FLAGS_SET(u->markers, 1u << marker))
+                                fprintf(f, " %s", unit_marker_to_string(marker));
+                fputs("\n", f);
+        }
+
         if (u->cgroup_realized_mask != 0) {
                 _cleanup_free_ char *s = NULL;
                 (void) cg_mask_to_string(u->cgroup_realized_mask, &s);
index d1525f55531547a39dd8de5be2c21879e83251b5..9c7f3dcdc036aa78714267d252ededbe0a3b2c98 100644 (file)
@@ -2390,9 +2390,13 @@ void unit_notify(Unit *u, UnitActiveState os, UnitActiveState ns, UnitNotifyFlag
 
         /* Make sure the cgroup and state files are always removed when we become inactive */
         if (UNIT_IS_INACTIVE_OR_FAILED(ns)) {
+                SET_FLAG(u->markers,
+                         (1u << UNIT_MARKER_NEEDS_RELOAD)|(1u << UNIT_MARKER_NEEDS_RESTART),
+                         false);
                 unit_prune_cgroup(u);
                 unit_unlink_state_files(u);
-        }
+        } else if (ns != os && ns == UNIT_RELOADING)
+                SET_FLAG(u->markers, 1u << UNIT_MARKER_NEEDS_RELOAD, false);
 
         unit_update_on_console(u);
 
index 457eba44a13169075a6f169939f3c02ba0146a81..7c13e5087889d79a70fdbc25f2663b664621254e 100644 (file)
@@ -242,6 +242,9 @@ typedef struct Unit {
         RateLimit start_ratelimit;
         EmergencyAction start_limit_action;
 
+        /* The unit has been marked for reload, restart, etc. Stored as 1u << marker1 | 1u << marker2. */
+        unsigned markers;
+
         /* What to do on failure or success */
         EmergencyAction success_action, failure_action;
         int success_action_exit_status, failure_action_exit_status;
index 1bd1dbddf526b28a483e7ea1a54294c038191b37..21a8ea3fa6f004209932d7c696ca3922c6bb2662 100644 (file)
@@ -6,7 +6,7 @@
 static void test_udev_builtin_cmd_to_ptr(void) {
         log_info("/* %s */", __func__);
 
-        /* Those could have been static_assert()s, but ({}) is not allowed there. */
+        /* Those could have been static asserts, but ({}) is not allowed there. */
 #if HAVE_BLKID
         assert_se(UDEV_BUILTIN_CMD_TO_PTR(UDEV_BUILTIN_BLKID));
         assert_se(PTR_TO_UDEV_BUILTIN_CMD(UDEV_BUILTIN_CMD_TO_PTR(UDEV_BUILTIN_BLKID)) == UDEV_BUILTIN_BLKID);