diff --git a/man/org.freedesktop.systemd1.xml b/man/org.freedesktop.systemd1.xml
index 7543a617b7..13771a459a 100644
--- a/man/org.freedesktop.systemd1.xml
+++ b/man/org.freedesktop.systemd1.xml
@@ -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 {
+
+
@@ -2160,8 +2164,16 @@ node /org/freedesktop/systemd1/unit/avahi_2ddaemon_2eservice {
NeedDaemonReload is a boolean that indicates whether the configuration file
this unit is loaded from (i.e. FragmentPath or SourcePath) has
- changed since the configuration was read and hence whether a configuration reload is
- recommended.
+ changed since the configuration was read and hence whether a configuration reload is recommended.
+
+
+ Markers is an array of string flags that can be set using
+ SetUnitProperties() to indicate that the service should be reloaded or
+ restarted. Currently known values are needs-restart and
+ needs-reload. 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.
JobTimeoutUSec maps directly to the corresponding configuration setting in the
unit file.
diff --git a/src/basic/unit-def.c b/src/basic/unit-def.c
index bb4fa1ec6b..6fbb947f09 100644
--- a/src/basic/unit-def.c
+++ b/src/basic/unit-def.c
@@ -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",
diff --git a/src/basic/unit-def.h b/src/basic/unit-def.h
index ff05799c1d..7742c5c078 100644
--- a/src/basic/unit-def.h
+++ b/src/basic/unit-def.h
@@ -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_;
diff --git a/src/core/dbus-unit.c b/src/core/dbus-unit.c
index 90bf5514a3..c9837df9a2 100644
--- a/src/core/dbus-unit.c
+++ b/src/core/dbus-unit.c
@@ -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),
diff --git a/src/core/unit-serialize.c b/src/core/unit-serialize.c
index ea4a57907e..3f099248ce 100644
--- a/src/core/unit-serialize.c
+++ b/src/core/unit-serialize.c
@@ -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);
diff --git a/src/core/unit.c b/src/core/unit.c
index d1525f5553..9c7f3dcdc0 100644
--- a/src/core/unit.c
+++ b/src/core/unit.c
@@ -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);
diff --git a/src/core/unit.h b/src/core/unit.h
index 457eba44a1..7c13e50878 100644
--- a/src/core/unit.h
+++ b/src/core/unit.h
@@ -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;
diff --git a/src/udev/test-udev-builtin.c b/src/udev/test-udev-builtin.c
index 1bd1dbddf5..21a8ea3fa6 100644
--- a/src/udev/test-udev-builtin.c
+++ b/src/udev/test-udev-builtin.c
@@ -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);