From: Luca Boccassi Date: Sat, 27 Dec 2025 11:02:30 +0000 (+0000) Subject: manager: add 'needs-stop/needs-start' markers X-Git-Tag: v260-rc1~35^2~1 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=f3a34fcc3a02b4de0fa8dbc8c0e4a8cc1305500b;p=thirdparty%2Fsystemd.git manager: add 'needs-stop/needs-start' markers Useful for packaging scripts, when units are removed. When multiple markers are assigned without +/-, the last one wins. When using +/-, the job merging logic is followed to the extent possible. --- diff --git a/man/org.freedesktop.systemd1.xml b/man/org.freedesktop.systemd1.xml index f0a7ccc6d6e..f4a06901b03 100644 --- a/man/org.freedesktop.systemd1.xml +++ b/man/org.freedesktop.systemd1.xml @@ -2644,12 +2644,33 @@ node /org/freedesktop/systemd1/unit/avahi_2ddaemon_2eservice { 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 the unit is stopped, restarted, or reloaded. + SetUnitProperties() to indicate that the service should be reloaded or restarted. + Currently known values are needs-restart, needs-stop, + needs-start and needs-reload. Package scripts may use the first + three to mark units for later restart or start or stop when a new version of the package is installed + or removed. Configuration management scripts may use the fourth 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 the unit is stopped, restarted, or reloaded. When markers are set, they are normalized according + to the following precedence rules, modeled after the job type merging logic. When new markers are + applied incrementally (using the + prefix), conflicting existing markers are + cleared before the new markers are merged in: + + needs-reload loses against all other markers. If any of + needs-restart, needs-start, or needs-stop + is set, needs-reload is cleared. + needs-stop wins against needs-restart and + needs-reload, clearing both. + needs-start wins against needs-stop, clearing + it. + needs-restart wins against needs-start. If + both are set, needs-start is cleared. + + For example, if a unit currently has needs-stop set and a new + +needs-start marker is applied, the existing needs-stop is + cleared and only needs-start remains. Conversely, applying + +needs-stop to any existing marker will clear all other markers, as + needs-stop takes precedence over needs-restart and + needs-reload, and the new marker clears conflicting existing ones. 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 6da61c510d8..ea4eebbf5d3 100644 --- a/src/basic/unit-def.c +++ b/src/basic/unit-def.c @@ -155,6 +155,8 @@ FreezerState freezer_state_objective(FreezerState state) { static const char* const unit_marker_table[_UNIT_MARKER_MAX] = { [UNIT_MARKER_NEEDS_RELOAD] = "needs-reload", [UNIT_MARKER_NEEDS_RESTART] = "needs-restart", + [UNIT_MARKER_NEEDS_STOP] = "needs-stop", + [UNIT_MARKER_NEEDS_START] = "needs-start", }; DEFINE_STRING_TABLE_LOOKUP(unit_marker, UnitMarker); diff --git a/src/basic/unit-def.h b/src/basic/unit-def.h index 6ed4c0cbb7a..5fecd3ecec1 100644 --- a/src/basic/unit-def.h +++ b/src/basic/unit-def.h @@ -62,6 +62,8 @@ typedef enum FreezerState { typedef enum UnitMarker { UNIT_MARKER_NEEDS_RELOAD, UNIT_MARKER_NEEDS_RESTART, + UNIT_MARKER_NEEDS_STOP, + UNIT_MARKER_NEEDS_START, _UNIT_MARKER_MAX, _UNIT_MARKER_INVALID = -EINVAL, } UnitMarker; diff --git a/src/core/dbus-manager.c b/src/core/dbus-manager.c index 232389e06ca..ad79c6e96ab 100644 --- a/src/core/dbus-manager.c +++ b/src/core/dbus-manager.c @@ -2132,17 +2132,26 @@ static int method_enqueue_marked_jobs(sd_bus_message *message, void *userdata, s continue; BusUnitQueueFlags flags; - if (BIT_SET(u->markers, UNIT_MARKER_NEEDS_RESTART)) + JobType job; + if (BIT_SET(u->markers, UNIT_MARKER_NEEDS_RESTART)) { flags = 0; - else if (BIT_SET(u->markers, UNIT_MARKER_NEEDS_RELOAD)) + job = JOB_TRY_RESTART; + } else if (BIT_SET(u->markers, UNIT_MARKER_NEEDS_RELOAD)) { flags = BUS_UNIT_QUEUE_RELOAD_IF_POSSIBLE; - else + job = JOB_TRY_RESTART; + } else if (BIT_SET(u->markers, UNIT_MARKER_NEEDS_STOP)) { + flags = 0; + job = JOB_STOP; + } else if (BIT_SET(u->markers, UNIT_MARKER_NEEDS_START)) { + flags = 0; + job = JOB_START; + } else continue; - r = mac_selinux_unit_access_check(u, message, "start", &error); + r = mac_selinux_unit_access_check(u, message, job_type_to_access_method(job), &error); if (r >= 0) r = bus_unit_queue_job_one(message, u, - JOB_TRY_RESTART, JOB_FAIL, flags, + job, JOB_FAIL, flags, reply, &error); if (ERRNO_IS_NEG_RESOURCE(r)) return r; diff --git a/src/core/dbus-unit.c b/src/core/dbus-unit.c index eff73591fde..525ec76da26 100644 --- a/src/core/dbus-unit.c +++ b/src/core/dbus-unit.c @@ -2151,9 +2151,8 @@ static int bus_unit_set_live_property( if (!UNIT_WRITE_FLAGS_NOOP(flags)) { if (some_absolute) - u->markers = settings; - else - u->markers = settings | (u->markers & ~mask); + mask = UINT_MAX; + u->markers = unit_normalize_markers((u->markers & ~mask), settings); } return 1; diff --git a/src/core/unit.c b/src/core/unit.c index 78c67c9833b..1995bb7f8eb 100644 --- a/src/core/unit.c +++ b/src/core/unit.c @@ -11,6 +11,7 @@ #include "all-units.h" #include "alloc-util.h" #include "ansi-color.h" +#include "bitfield.h" #include "bpf-restrict-fs.h" #include "bus-common-errors.h" #include "bus-internal.h" @@ -479,6 +480,9 @@ bool unit_may_gc(Unit *u) { if (r <= 0 && !IN_SET(r, -ENXIO, -EOWNERDEAD)) return false; /* ENXIO/EOWNERDEAD means: currently not realized */ + if (unit_can_start(u) && BIT_SET(u->markers, UNIT_MARKER_NEEDS_START)) + return false; + if (!UNIT_VTABLE(u)->may_gc) return true; @@ -2758,11 +2762,13 @@ void unit_notify(Unit *u, UnitActiveState os, UnitActiveState ns, bool reload_su /* 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), + (1u << UNIT_MARKER_NEEDS_RELOAD)|(1u << UNIT_MARKER_NEEDS_RESTART)|(1u << UNIT_MARKER_NEEDS_STOP), false); unit_prune_cgroup(u); unit_unlink_state_files(u); - } else if (ns != os && ns == UNIT_RELOADING) + } else if (UNIT_IS_ACTIVE_OR_ACTIVATING(ns)) + SET_FLAG(u->markers, 1u << UNIT_MARKER_NEEDS_START, false); + else if (ns != os && ns == UNIT_RELOADING) SET_FLAG(u->markers, 1u << UNIT_MARKER_NEEDS_RELOAD, false); unit_update_on_console(u); @@ -7086,8 +7092,45 @@ int parse_unit_marker(const char *marker, unsigned *settings, unsigned *mask) { if (m < 0) return -EINVAL; + /* When +- are not used, last one wins, so reset the bitmask before storing the new result */ + if (!some_plus_minus) + *settings = 0; + SET_FLAG(*settings, 1u << m, b); SET_FLAG(*mask, 1u << m, true); return some_plus_minus; } + +unsigned unit_normalize_markers(unsigned existing_markers, unsigned new_markers) { + /* Follow the job merging logic: when new markers conflict with existing ones, the new marker + * takes precedence and clears out conflicting existing markers. Then standard normalization + * resolves any remaining conflicts. */ + + /* New stop wins against all existing markers */ + if (BIT_SET(new_markers, UNIT_MARKER_NEEDS_STOP)) + CLEAR_BITS(existing_markers, UNIT_MARKER_NEEDS_RESTART, UNIT_MARKER_NEEDS_START, UNIT_MARKER_NEEDS_RELOAD); + /* New start wins against existing stop */ + if (BIT_SET(new_markers, UNIT_MARKER_NEEDS_START)) + CLEAR_BIT(existing_markers, UNIT_MARKER_NEEDS_STOP); + /* New restart wins against existing start and reload */ + if (BIT_SET(new_markers, UNIT_MARKER_NEEDS_RESTART)) + CLEAR_BITS(existing_markers, UNIT_MARKER_NEEDS_START, UNIT_MARKER_NEEDS_RELOAD); + + unsigned markers = existing_markers | new_markers; + + /* Standard normalization: reload loses against everything */ + if (BIT_SET(markers, UNIT_MARKER_NEEDS_RESTART) || BIT_SET(markers, UNIT_MARKER_NEEDS_START) || BIT_SET(markers, UNIT_MARKER_NEEDS_STOP)) + CLEAR_BIT(markers, UNIT_MARKER_NEEDS_RELOAD); + /* Stop wins against restart and reload */ + if (BIT_SET(markers, UNIT_MARKER_NEEDS_STOP)) + CLEAR_BITS(markers, UNIT_MARKER_NEEDS_RESTART, UNIT_MARKER_NEEDS_RELOAD); + /* Start wins against stop */ + if (BIT_SET(markers, UNIT_MARKER_NEEDS_START)) + CLEAR_BIT(markers, UNIT_MARKER_NEEDS_STOP); + /* Restart wins against start */ + if (BITS_SET(markers, UNIT_MARKER_NEEDS_RESTART, UNIT_MARKER_NEEDS_START)) + CLEAR_BIT(markers, UNIT_MARKER_NEEDS_START); + + return markers; +} diff --git a/src/core/unit.h b/src/core/unit.h index 95959cfcea4..9c94113239e 100644 --- a/src/core/unit.h +++ b/src/core/unit.h @@ -1095,6 +1095,7 @@ DECLARE_STRING_TABLE_LOOKUP(oom_policy, OOMPolicy); int unit_queue_job_check_and_mangle_type(Unit *u, JobType *type, bool reload_if_possible, sd_bus_error *reterr_error); int parse_unit_marker(const char *marker, unsigned *settings, unsigned *mask); +unsigned unit_normalize_markers(unsigned existing_markers, unsigned new_markers); /* Macros which append UNIT= or USER_UNIT= to the message */ diff --git a/src/core/varlink-manager.c b/src/core/varlink-manager.c index dbe97f20c9b..d00f7e5a248 100644 --- a/src/core/varlink-manager.c +++ b/src/core/varlink-manager.c @@ -343,6 +343,7 @@ int vl_method_enqueue_marked_jobs_manager(sd_varlink *link, sd_json_variant *par _cleanup_(sd_bus_error_free) sd_bus_error bus_error = SD_BUS_ERROR_NULL; const char *error_id = NULL; uint32_t job_id = 0; /* silence 'maybe-uninitialized' compiler warning */ + JobType job; /* ignore aliases */ if (u->id != k) @@ -350,14 +351,20 @@ int vl_method_enqueue_marked_jobs_manager(sd_varlink *link, sd_json_variant *par if (u->markers == 0) continue; + if (BIT_SET(u->markers, UNIT_MARKER_NEEDS_STOP)) + job = JOB_STOP; + else if (BIT_SET(u->markers, UNIT_MARKER_NEEDS_START)) + job = JOB_START; + else + job = JOB_TRY_RESTART; - r = mac_selinux_unit_access_check_varlink(u, link, job_type_to_access_method(JOB_TRY_RESTART)); + r = mac_selinux_unit_access_check_varlink(u, link, job_type_to_access_method(job)); if (r < 0) error_id = SD_VARLINK_ERROR_PERMISSION_DENIED; else r = varlink_unit_queue_job_one( u, - JOB_TRY_RESTART, + job, JOB_FAIL, /* reload_if_possible= */ !BIT_SET(u->markers, UNIT_MARKER_NEEDS_RESTART), &job_id, diff --git a/src/core/varlink-unit.c b/src/core/varlink-unit.c index 790d0df2dfb..b3375aebfe8 100644 --- a/src/core/varlink-unit.c +++ b/src/core/varlink-unit.c @@ -668,7 +668,7 @@ int vl_method_set_unit_properties(sd_varlink *link, sd_json_variant *parameters, return r; if (p.markers_found) - unit->markers = p.markers | (unit->markers & ~p.markers_mask); + unit->markers = unit_normalize_markers((unit->markers & ~p.markers_mask), p.markers); return sd_varlink_reply(link, NULL); } diff --git a/test/units/TEST-26-SYSTEMCTL.sh b/test/units/TEST-26-SYSTEMCTL.sh index 2b8c6fa2308..75feedd3b09 100755 --- a/test/units/TEST-26-SYSTEMCTL.sh +++ b/test/units/TEST-26-SYSTEMCTL.sh @@ -371,18 +371,153 @@ systemctl status 1 # --marked systemctl restart "$UNIT_NAME" -systemctl set-property "$UNIT_NAME" Markers=needs-restart +systemctl set-property "$UNIT_NAME" "Markers=needs-reload needs-restart" systemctl show -P Markers "$UNIT_NAME" | grep needs-restart +systemctl show -P Markers "$UNIT_NAME" | grep -v needs-reload systemctl reload-or-restart --marked (! systemctl show -P Markers "$UNIT_NAME" | grep needs-restart) +systemctl is-active "$UNIT_NAME" +systemctl set-property "$UNIT_NAME" "Markers=needs-reload needs-stop" +systemctl show -P Markers "$UNIT_NAME" | grep needs-stop +systemctl show -P Markers "$UNIT_NAME" | grep -v needs-reload +systemctl reload-or-restart --marked +(! systemctl show -P Markers "$UNIT_NAME" | grep needs-stop) +(! systemctl is-active "$UNIT_NAME") +systemctl set-property "$UNIT_NAME" "Markers=needs-start" +systemctl show -P Markers "$UNIT_NAME" | grep needs-start +systemctl show -P Markers "$UNIT_NAME" | grep -v needs-stop +systemctl reload-or-restart --marked +(! systemctl show -P Markers "$UNIT_NAME" | grep needs-start) +systemctl is-active "$UNIT_NAME" +systemctl set-property "$UNIT_NAME" "Markers=needs-start needs-stop" +systemctl show -P Markers "$UNIT_NAME" | grep needs-stop +systemctl show -P Markers "$UNIT_NAME" | grep -v needs-start +systemctl reload-or-restart --marked +(! systemctl show -P Markers "$UNIT_NAME" | grep needs-stop) +(! systemctl is-active "$UNIT_NAME") + +# Test marker normalization with incremental (+) syntax + +# needs-start + +needs-restart → needs-restart (restart wins against start) +systemctl set-property "$UNIT_NAME" "Markers=needs-start" +systemctl set-property "$UNIT_NAME" "Markers=+needs-restart" +systemctl show -P Markers "$UNIT_NAME" | grep needs-restart +(! systemctl show -P Markers "$UNIT_NAME" | grep needs-start) +systemctl set-property "$UNIT_NAME" "Markers=" + +# needs-restart + +needs-start → needs-restart (restart wins against start) +systemctl set-property "$UNIT_NAME" "Markers=needs-restart" +systemctl set-property "$UNIT_NAME" "Markers=+needs-start" +systemctl show -P Markers "$UNIT_NAME" | grep needs-restart +(! systemctl show -P Markers "$UNIT_NAME" | grep needs-start) +systemctl set-property "$UNIT_NAME" "Markers=" + +# needs-restart + +needs-reload → needs-restart (reload loses against restart) +systemctl set-property "$UNIT_NAME" "Markers=needs-restart" +systemctl set-property "$UNIT_NAME" "Markers=+needs-reload" +systemctl show -P Markers "$UNIT_NAME" | grep needs-restart +(! systemctl show -P Markers "$UNIT_NAME" | grep needs-reload) +systemctl set-property "$UNIT_NAME" "Markers=" + +# needs-stop + +needs-start → needs-start (start overrides stop) +systemctl set-property "$UNIT_NAME" "Markers=needs-stop" +systemctl set-property "$UNIT_NAME" "Markers=+needs-start" +systemctl show -P Markers "$UNIT_NAME" | grep needs-start +(! systemctl show -P Markers "$UNIT_NAME" | grep needs-stop) +systemctl set-property "$UNIT_NAME" "Markers=" + +# anything + +needs-stop → needs-stop (stop wins against everything) +for marker in needs-start needs-restart needs-reload; do + systemctl set-property "$UNIT_NAME" "Markers=$marker" + systemctl set-property "$UNIT_NAME" "Markers=+needs-stop" + systemctl show -P Markers "$UNIT_NAME" | grep needs-stop + (! systemctl show -P Markers "$UNIT_NAME" | grep "$marker") + systemctl set-property "$UNIT_NAME" "Markers=" +done + +# needs-stop + +needs-reload → needs-stop (stop wins against reload) +systemctl set-property "$UNIT_NAME" "Markers=needs-stop" +systemctl set-property "$UNIT_NAME" "Markers=+needs-reload" +systemctl show -P Markers "$UNIT_NAME" | grep needs-stop +(! systemctl show -P Markers "$UNIT_NAME" | grep needs-reload) +systemctl set-property "$UNIT_NAME" "Markers=" # again, but with varlinkctl instead systemctl restart "$UNIT_NAME" -varlinkctl call /run/systemd/io.systemd.Manager io.systemd.Unit.SetProperties "{\"runtime\": true, \"name\": \"$UNIT_NAME\", \"properties\": {\"Markers\": [\"needs-restart\"]}}" +varlinkctl call /run/systemd/io.systemd.Manager io.systemd.Unit.SetProperties "{\"runtime\": true, \"name\": \"$UNIT_NAME\", \"properties\": {\"Markers\": [\"needs-reload\", \"needs-restart\"]}}" systemctl show -P Markers "$UNIT_NAME" | grep needs-restart +systemctl show -P Markers "$UNIT_NAME" | grep -v needs-reload varlinkctl call /run/systemd/io.systemd.Manager io.systemd.Manager.EnqueueMarkedJobs '{}' timeout 30 bash -c "until systemctl list-jobs $UNIT_NAME | grep \"No jobs\" 2>/dev/null; do sleep 1; done" (! systemctl show -P Markers "$UNIT_NAME" | grep needs-restart) +systemctl is-active "$UNIT_NAME" +varlinkctl call /run/systemd/io.systemd.Manager io.systemd.Unit.SetProperties "{\"runtime\": true, \"name\": \"$UNIT_NAME\", \"properties\": {\"Markers\": [\"needs-reload\", \"needs-stop\"]}}" +systemctl show -P Markers "$UNIT_NAME" | grep needs-stop +systemctl show -P Markers "$UNIT_NAME" | grep -v needs-reload +varlinkctl call /run/systemd/io.systemd.Manager io.systemd.Manager.EnqueueMarkedJobs '{}' +timeout 30 bash -c "until systemctl list-jobs $UNIT_NAME | grep \"No jobs\" 2>/dev/null; do sleep 1; done" +(! systemctl show -P Markers "$UNIT_NAME" | grep needs-stop) +(! systemctl is-active "$UNIT_NAME") +varlinkctl call /run/systemd/io.systemd.Manager io.systemd.Unit.SetProperties "{\"runtime\": true, \"name\": \"$UNIT_NAME\", \"properties\": {\"Markers\": [\"needs-start\"]}}" +systemctl show -P Markers "$UNIT_NAME" | grep needs-start +systemctl show -P Markers "$UNIT_NAME" | grep -v needs-stop +varlinkctl call /run/systemd/io.systemd.Manager io.systemd.Manager.EnqueueMarkedJobs '{}' +timeout 30 bash -c "until systemctl list-jobs $UNIT_NAME | grep \"No jobs\" 2>/dev/null; do sleep 1; done" +(! systemctl show -P Markers "$UNIT_NAME" | grep needs-start) +systemctl is-active "$UNIT_NAME" +varlinkctl call /run/systemd/io.systemd.Manager io.systemd.Unit.SetProperties "{\"runtime\": true, \"name\": \"$UNIT_NAME\", \"properties\": {\"Markers\": [\"needs-start\", \"needs-stop\"]}}" +systemctl show -P Markers "$UNIT_NAME" | grep needs-stop +systemctl show -P Markers "$UNIT_NAME" | grep -v needs-start +varlinkctl call /run/systemd/io.systemd.Manager io.systemd.Manager.EnqueueMarkedJobs '{}' +timeout 30 bash -c "until systemctl list-jobs $UNIT_NAME | grep \"No jobs\" 2>/dev/null; do sleep 1; done" +(! systemctl show -P Markers "$UNIT_NAME" | grep needs-stop) +(! systemctl is-active "$UNIT_NAME") + +# Test marker normalization with incremental (+) syntax via varlinkctl + +# needs-start + +needs-restart → needs-restart (restart wins against start) +varlinkctl call /run/systemd/io.systemd.Manager io.systemd.Unit.SetProperties "{\"runtime\": true, \"name\": \"$UNIT_NAME\", \"properties\": {\"Markers\": [\"needs-start\"]}}" +varlinkctl call /run/systemd/io.systemd.Manager io.systemd.Unit.SetProperties "{\"runtime\": true, \"name\": \"$UNIT_NAME\", \"properties\": {\"Markers\": [\"+needs-restart\"]}}" +systemctl show -P Markers "$UNIT_NAME" | grep needs-restart +(! systemctl show -P Markers "$UNIT_NAME" | grep needs-start) +varlinkctl call /run/systemd/io.systemd.Manager io.systemd.Unit.SetProperties "{\"runtime\": true, \"name\": \"$UNIT_NAME\", \"properties\": {\"Markers\": []}}" + +# needs-restart + +needs-start → needs-restart (restart wins against start) +varlinkctl call /run/systemd/io.systemd.Manager io.systemd.Unit.SetProperties "{\"runtime\": true, \"name\": \"$UNIT_NAME\", \"properties\": {\"Markers\": [\"needs-restart\"]}}" +varlinkctl call /run/systemd/io.systemd.Manager io.systemd.Unit.SetProperties "{\"runtime\": true, \"name\": \"$UNIT_NAME\", \"properties\": {\"Markers\": [\"+needs-start\"]}}" +systemctl show -P Markers "$UNIT_NAME" | grep needs-restart +(! systemctl show -P Markers "$UNIT_NAME" | grep needs-start) +varlinkctl call /run/systemd/io.systemd.Manager io.systemd.Unit.SetProperties "{\"runtime\": true, \"name\": \"$UNIT_NAME\", \"properties\": {\"Markers\": []}}" + +# needs-restart + +needs-reload → needs-restart (reload loses against restart) +varlinkctl call /run/systemd/io.systemd.Manager io.systemd.Unit.SetProperties "{\"runtime\": true, \"name\": \"$UNIT_NAME\", \"properties\": {\"Markers\": [\"needs-restart\"]}}" +varlinkctl call /run/systemd/io.systemd.Manager io.systemd.Unit.SetProperties "{\"runtime\": true, \"name\": \"$UNIT_NAME\", \"properties\": {\"Markers\": [\"+needs-reload\"]}}" +systemctl show -P Markers "$UNIT_NAME" | grep needs-restart +(! systemctl show -P Markers "$UNIT_NAME" | grep needs-reload) +varlinkctl call /run/systemd/io.systemd.Manager io.systemd.Unit.SetProperties "{\"runtime\": true, \"name\": \"$UNIT_NAME\", \"properties\": {\"Markers\": []}}" + +# needs-stop + +needs-start → needs-start (start overrides stop) +varlinkctl call /run/systemd/io.systemd.Manager io.systemd.Unit.SetProperties "{\"runtime\": true, \"name\": \"$UNIT_NAME\", \"properties\": {\"Markers\": [\"needs-stop\"]}}" +varlinkctl call /run/systemd/io.systemd.Manager io.systemd.Unit.SetProperties "{\"runtime\": true, \"name\": \"$UNIT_NAME\", \"properties\": {\"Markers\": [\"+needs-start\"]}}" +systemctl show -P Markers "$UNIT_NAME" | grep needs-start +(! systemctl show -P Markers "$UNIT_NAME" | grep needs-stop) +varlinkctl call /run/systemd/io.systemd.Manager io.systemd.Unit.SetProperties "{\"runtime\": true, \"name\": \"$UNIT_NAME\", \"properties\": {\"Markers\": []}}" + +# anything + +needs-stop → needs-stop (stop wins against everything) +for marker in needs-start needs-restart needs-reload; do + varlinkctl call /run/systemd/io.systemd.Manager io.systemd.Unit.SetProperties "{\"runtime\": true, \"name\": \"$UNIT_NAME\", \"properties\": {\"Markers\": [\"$marker\"]}}" + varlinkctl call /run/systemd/io.systemd.Manager io.systemd.Unit.SetProperties "{\"runtime\": true, \"name\": \"$UNIT_NAME\", \"properties\": {\"Markers\": [\"+needs-stop\"]}}" + systemctl show -P Markers "$UNIT_NAME" | grep needs-stop + (! systemctl show -P Markers "$UNIT_NAME" | grep "$marker") + varlinkctl call /run/systemd/io.systemd.Manager io.systemd.Unit.SetProperties "{\"runtime\": true, \"name\": \"$UNIT_NAME\", \"properties\": {\"Markers\": []}}" +done + +# needs-stop + +needs-reload → needs-stop (stop wins against reload) +varlinkctl call /run/systemd/io.systemd.Manager io.systemd.Unit.SetProperties "{\"runtime\": true, \"name\": \"$UNIT_NAME\", \"properties\": {\"Markers\": [\"needs-stop\"]}}" +varlinkctl call /run/systemd/io.systemd.Manager io.systemd.Unit.SetProperties "{\"runtime\": true, \"name\": \"$UNIT_NAME\", \"properties\": {\"Markers\": [\"+needs-reload\"]}}" +systemctl show -P Markers "$UNIT_NAME" | grep needs-stop +(! systemctl show -P Markers "$UNIT_NAME" | grep needs-reload) +varlinkctl call /run/systemd/io.systemd.Manager io.systemd.Unit.SetProperties "{\"runtime\": true, \"name\": \"$UNIT_NAME\", \"properties\": {\"Markers\": []}}" # --dry-run with destructive verbs # kexec is skipped intentionally, as it requires a bit more involved setup