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);