1
0
mirror of https://github.com/systemd/systemd synced 2026-02-25 08:44:45 +01:00

Compare commits

...

4 Commits

Author SHA1 Message Date
Zbigniew Jędrzejewski-Szmek
04d2a86e66
manager: add needs-stop/needs-start (#40709)
Intend to use these in the deb packaging scriptlets

Fixes: #19755
2026-02-24 00:36:06 +01:00
Luca Boccassi
f895dae683 mkosi: update debian commit reference to 89a825b80ee85e58b530cd95438988a6fb3531a3
* 89a825b80e Install new files for upstream build
* 38b1a75eaa systemd.postinst: update journal catalog after reexecing managers
* 9420ffa0dd Install new files for upstream build
* cb5fdf4df0 Install sd-report from new path
* 2501efe312 homed: drop dependency satisfied since bookworm/noble
* 424262b674 d/control: add TODO
* 9aaa01c99d d/libsystemd-shared.preinst: refuse to upgrade without unified cgroupv2 hierarchy
* 7d0ac8a4b8 Install new files for upstream build
* 7e55d41841 Do not install legacy.conf in upstream builds
* 190387958e d/e/checkout-upstream: try to install build deps with sudo in case it fails
* 2a4aa9f426 Disable installing runlevel targets for upstream build
* e02600f2d9 Install new files for upstream build
* 0f1a56cf10 Update changelog for 259.1-1 release
* e463300b1a d/e/checkout-upstream: try to install build deps again after switching to CI branch
* b3f72821e5 Install ask-password polkit policy file
* 8219d561ae Restrict the tpm2-generator manpage to arches where it is built
* 30a0f27283 systemd.postinst: reexec system manager before user instances
* 1fc9ea2790 systemd.postinst: reexec before restarting any service
* d9991aeb22 d/control: use libarchive13t64 | libarchive13 for bookworm/jammy
* b750346711 d/control: make systemd-container Depends: libarchive13t64

Fixes https://github.com/systemd/systemd/issues/40777
2026-02-23 23:14:37 +00:00
Luca Boccassi
351b4dd123 systemctl: allow --marker with start/stop too 2026-02-23 13:05:23 +00:00
Luca Boccassi
f3a34fcc3a 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.
2026-02-23 13:05:23 +00:00
14 changed files with 252 additions and 34 deletions

View File

@ -2644,12 +2644,33 @@ node /org/freedesktop/systemd1/unit/avahi_2ddaemon_2eservice {
</para> </para>
<para><varname>Markers</varname> is an array of string flags that can be set using <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 <function>SetUnitProperties()</function> to indicate that the service should be reloaded or restarted.
restarted. Currently known values are <literal>needs-restart</literal> and Currently known values are <literal>needs-restart</literal>, <literal>needs-stop</literal>,
<literal>needs-reload</literal>. Package scripts may use the first to mark units for later restart when <literal>needs-start</literal> and <literal>needs-reload</literal>. Package scripts may use the first
a new version of the package is installed. Configuration management scripts may use the second to mark three to mark units for later restart or start or stop when a new version of the package is installed
units for a later reload when the configuration is adjusted. Those flags are not set by the manager, or removed. Configuration management scripts may use the fourth to mark units for a later reload when
except to unset as appropriate when the unit is stopped, restarted, or reloaded.</para> 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 <literal>+</literal> prefix), conflicting existing markers are
cleared before the new markers are merged in:
<itemizedlist>
<listitem><para><literal>needs-reload</literal> loses against all other markers. If any of
<literal>needs-restart</literal>, <literal>needs-start</literal>, or <literal>needs-stop</literal>
is set, <literal>needs-reload</literal> is cleared.</para></listitem>
<listitem><para><literal>needs-stop</literal> wins against <literal>needs-restart</literal> and
<literal>needs-reload</literal>, clearing both.</para></listitem>
<listitem><para><literal>needs-start</literal> wins against <literal>needs-stop</literal>, clearing
it.</para></listitem>
<listitem><para><literal>needs-restart</literal> wins against <literal>needs-start</literal>. If
both are set, <literal>needs-start</literal> is cleared.</para></listitem>
</itemizedlist>
For example, if a unit currently has <literal>needs-stop</literal> set and a new
<literal>+needs-start</literal> marker is applied, the existing <literal>needs-stop</literal> is
cleared and only <literal>needs-start</literal> remains. Conversely, applying
<literal>+needs-stop</literal> to any existing marker will clear all other markers, as
<literal>needs-stop</literal> takes precedence over <literal>needs-restart</literal> and
<literal>needs-reload</literal>, and the new marker clears conflicting existing ones.</para>
<para><varname>JobTimeoutUSec</varname> maps directly to the corresponding configuration setting in the <para><varname>JobTimeoutUSec</varname> maps directly to the corresponding configuration setting in the
unit file.</para> unit file.</para>

View File

@ -2855,10 +2855,10 @@ Jan 12 10:46:45 example.com bluetoothd[8900]: gatt-time-server: Input/output err
<varlistentry> <varlistentry>
<term><option>--marked</option></term> <term><option>--marked</option></term>
<listitem><para>Only allowed with <command>reload-or-restart</command>. Enqueues restart jobs for all <listitem><para>Only allowed with <command>reload-or-restart</command>, <command>start</command>, or
units that have the <literal>needs-restart</literal> mark, and reload jobs for units that have the <command>stop</command>. Enqueues jobs for all units that are marked. When a unit marked for reload
<literal>needs-reload</literal> mark. When a unit marked for reload does not support reload, restart does not support reload, restart will be queued. Those properties can be set using
will be queued. Those properties can be set using <command>set-property Markers=…</command>.</para> <command>set-property Markers=…</command>.</para>
<para>Unless <option>--no-block</option> is used, <command>systemctl</command> will wait for the <para>Unless <option>--no-block</option> is used, <command>systemctl</command> will wait for the
queued jobs to finish.</para> queued jobs to finish.</para>

View File

@ -9,5 +9,5 @@ Environment=
GIT_URL=https://salsa.debian.org/systemd-team/systemd.git GIT_URL=https://salsa.debian.org/systemd-team/systemd.git
GIT_SUBDIR=debian GIT_SUBDIR=debian
GIT_BRANCH=debian/master GIT_BRANCH=debian/master
GIT_COMMIT=ecec6127927ca59726e3d3535a2b2344f585cf74 GIT_COMMIT=89a825b80ee85e58b530cd95438988a6fb3531a3
PKG_SUBDIR=debian PKG_SUBDIR=debian

View File

@ -155,6 +155,8 @@ FreezerState freezer_state_objective(FreezerState state) {
static const char* const unit_marker_table[_UNIT_MARKER_MAX] = { static const char* const unit_marker_table[_UNIT_MARKER_MAX] = {
[UNIT_MARKER_NEEDS_RELOAD] = "needs-reload", [UNIT_MARKER_NEEDS_RELOAD] = "needs-reload",
[UNIT_MARKER_NEEDS_RESTART] = "needs-restart", [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); DEFINE_STRING_TABLE_LOOKUP(unit_marker, UnitMarker);

View File

@ -62,6 +62,8 @@ typedef enum FreezerState {
typedef enum UnitMarker { typedef enum UnitMarker {
UNIT_MARKER_NEEDS_RELOAD, UNIT_MARKER_NEEDS_RELOAD,
UNIT_MARKER_NEEDS_RESTART, UNIT_MARKER_NEEDS_RESTART,
UNIT_MARKER_NEEDS_STOP,
UNIT_MARKER_NEEDS_START,
_UNIT_MARKER_MAX, _UNIT_MARKER_MAX,
_UNIT_MARKER_INVALID = -EINVAL, _UNIT_MARKER_INVALID = -EINVAL,
} UnitMarker; } UnitMarker;

View File

@ -2132,17 +2132,26 @@ static int method_enqueue_marked_jobs(sd_bus_message *message, void *userdata, s
continue; continue;
BusUnitQueueFlags flags; BusUnitQueueFlags flags;
if (BIT_SET(u->markers, UNIT_MARKER_NEEDS_RESTART)) JobType job;
if (BIT_SET(u->markers, UNIT_MARKER_NEEDS_RESTART)) {
flags = 0; 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; 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; 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) if (r >= 0)
r = bus_unit_queue_job_one(message, u, r = bus_unit_queue_job_one(message, u,
JOB_TRY_RESTART, JOB_FAIL, flags, job, JOB_FAIL, flags,
reply, &error); reply, &error);
if (ERRNO_IS_NEG_RESOURCE(r)) if (ERRNO_IS_NEG_RESOURCE(r))
return r; return r;

View File

@ -2151,9 +2151,8 @@ static int bus_unit_set_live_property(
if (!UNIT_WRITE_FLAGS_NOOP(flags)) { if (!UNIT_WRITE_FLAGS_NOOP(flags)) {
if (some_absolute) if (some_absolute)
u->markers = settings; mask = UINT_MAX;
else u->markers = unit_normalize_markers((u->markers & ~mask), settings);
u->markers = settings | (u->markers & ~mask);
} }
return 1; return 1;

View File

@ -11,6 +11,7 @@
#include "all-units.h" #include "all-units.h"
#include "alloc-util.h" #include "alloc-util.h"
#include "ansi-color.h" #include "ansi-color.h"
#include "bitfield.h"
#include "bpf-restrict-fs.h" #include "bpf-restrict-fs.h"
#include "bus-common-errors.h" #include "bus-common-errors.h"
#include "bus-internal.h" #include "bus-internal.h"
@ -479,6 +480,9 @@ bool unit_may_gc(Unit *u) {
if (r <= 0 && !IN_SET(r, -ENXIO, -EOWNERDEAD)) if (r <= 0 && !IN_SET(r, -ENXIO, -EOWNERDEAD))
return false; /* ENXIO/EOWNERDEAD means: currently not realized */ 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) if (!UNIT_VTABLE(u)->may_gc)
return true; 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 */ /* Make sure the cgroup and state files are always removed when we become inactive */
if (UNIT_IS_INACTIVE_OR_FAILED(ns)) { if (UNIT_IS_INACTIVE_OR_FAILED(ns)) {
SET_FLAG(u->markers, 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); false);
unit_prune_cgroup(u); unit_prune_cgroup(u);
unit_unlink_state_files(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); SET_FLAG(u->markers, 1u << UNIT_MARKER_NEEDS_RELOAD, false);
unit_update_on_console(u); unit_update_on_console(u);
@ -7086,8 +7092,45 @@ int parse_unit_marker(const char *marker, unsigned *settings, unsigned *mask) {
if (m < 0) if (m < 0)
return -EINVAL; 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(*settings, 1u << m, b);
SET_FLAG(*mask, 1u << m, true); SET_FLAG(*mask, 1u << m, true);
return some_plus_minus; 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;
}

View File

@ -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 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); 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 */ /* Macros which append UNIT= or USER_UNIT= to the message */

View File

@ -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; _cleanup_(sd_bus_error_free) sd_bus_error bus_error = SD_BUS_ERROR_NULL;
const char *error_id = NULL; const char *error_id = NULL;
uint32_t job_id = 0; /* silence 'maybe-uninitialized' compiler warning */ uint32_t job_id = 0; /* silence 'maybe-uninitialized' compiler warning */
JobType job;
/* ignore aliases */ /* ignore aliases */
if (u->id != k) 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) if (u->markers == 0)
continue; 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) if (r < 0)
error_id = SD_VARLINK_ERROR_PERMISSION_DENIED; error_id = SD_VARLINK_ERROR_PERMISSION_DENIED;
else else
r = varlink_unit_queue_job_one( r = varlink_unit_queue_job_one(
u, u,
JOB_TRY_RESTART, job,
JOB_FAIL, JOB_FAIL,
/* reload_if_possible= */ !BIT_SET(u->markers, UNIT_MARKER_NEEDS_RESTART), /* reload_if_possible= */ !BIT_SET(u->markers, UNIT_MARKER_NEEDS_RESTART),
&job_id, &job_id,

View File

@ -668,7 +668,7 @@ int vl_method_set_unit_properties(sd_varlink *link, sd_json_variant *parameters,
return r; return r;
if (p.markers_found) 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); return sd_varlink_reply(link, NULL);
} }

View File

@ -59,8 +59,8 @@ static int systemctl_main(int argc, char *argv[]) {
{ "list-machines", VERB_ANY, VERB_ANY, VERB_ONLINE_ONLY, verb_list_machines }, { "list-machines", VERB_ANY, VERB_ANY, VERB_ONLINE_ONLY, verb_list_machines },
{ "clear-jobs", VERB_ANY, 1, VERB_ONLINE_ONLY, verb_trivial_method }, { "clear-jobs", VERB_ANY, 1, VERB_ONLINE_ONLY, verb_trivial_method },
{ "cancel", VERB_ANY, VERB_ANY, VERB_ONLINE_ONLY, verb_cancel }, { "cancel", VERB_ANY, VERB_ANY, VERB_ONLINE_ONLY, verb_cancel },
{ "start", 2, VERB_ANY, VERB_ONLINE_ONLY, verb_start }, { "start", VERB_ANY, VERB_ANY, VERB_ONLINE_ONLY, verb_start },
{ "stop", 2, VERB_ANY, VERB_ONLINE_ONLY, verb_start }, { "stop", VERB_ANY, VERB_ANY, VERB_ONLINE_ONLY, verb_start },
{ "condstop", 2, VERB_ANY, VERB_ONLINE_ONLY, verb_start }, /* For compatibility with ALTLinux */ { "condstop", 2, VERB_ANY, VERB_ONLINE_ONLY, verb_start }, /* For compatibility with ALTLinux */
{ "reload", 2, VERB_ANY, VERB_ONLINE_ONLY, verb_start }, { "reload", 2, VERB_ANY, VERB_ONLINE_ONLY, verb_start },
{ "restart", 2, VERB_ANY, VERB_ONLINE_ONLY, verb_start }, { "restart", 2, VERB_ANY, VERB_ONLINE_ONLY, verb_start },

View File

@ -1069,14 +1069,13 @@ static int systemctl_parse_argv(int argc, char *argv[]) {
return log_error_errno(SYNTHETIC_ERRNO(EINVAL), return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
"--wait may not be combined with --no-block."); "--wait may not be combined with --no-block.");
bool do_reload_or_restart = streq_ptr(argv[optind], "reload-or-restart");
if (arg_marked) { if (arg_marked) {
if (!do_reload_or_restart) if (!STRPTR_IN_SET(argv[optind], "reload-or-restart", "start", "stop"))
return log_error_errno(SYNTHETIC_ERRNO(EINVAL), return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
"--marked may only be used with 'reload-or-restart'."); "--marked may only be used with 'reload-or-restart', 'start', or 'stop'.");
if (optind + 1 < argc) if (optind + 1 < argc)
return log_error_errno(SYNTHETIC_ERRNO(EINVAL), return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
"No additional arguments allowed with 'reload-or-restart --marked'."); "No additional arguments allowed with '%s --marked'.", strna(argv[optind]));
if (arg_wait) if (arg_wait)
return log_error_errno(SYNTHETIC_ERRNO(EINVAL), return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
"--marked --wait is not supported."); "--marked --wait is not supported.");
@ -1084,10 +1083,10 @@ static int systemctl_parse_argv(int argc, char *argv[]) {
return log_error_errno(SYNTHETIC_ERRNO(EINVAL), return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
"--marked --show-transaction is not supported."); "--marked --show-transaction is not supported.");
} else if (do_reload_or_restart) { } else if (STRPTR_IN_SET(argv[optind], "reload-or-restart", "start", "stop")) {
if (optind + 1 >= argc) if (optind + 1 >= argc)
return log_error_errno(SYNTHETIC_ERRNO(EINVAL), return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
"List of units to restart/reload is required."); "List of units to %s is required.", strna(argv[optind]));
} }
if (arg_image && arg_root) if (arg_image && arg_root)

View File

@ -371,18 +371,153 @@ systemctl status 1
# --marked # --marked
systemctl restart "$UNIT_NAME" 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 needs-restart
systemctl show -P Markers "$UNIT_NAME" | grep -v needs-reload
systemctl reload-or-restart --marked systemctl reload-or-restart --marked
(! systemctl show -P Markers "$UNIT_NAME" | grep needs-restart) (! 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 # again, but with varlinkctl instead
systemctl restart "$UNIT_NAME" 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 needs-restart
systemctl show -P Markers "$UNIT_NAME" | grep -v needs-reload
varlinkctl call /run/systemd/io.systemd.Manager io.systemd.Manager.EnqueueMarkedJobs '{}' 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" 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 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 # --dry-run with destructive verbs
# kexec is skipped intentionally, as it requires a bit more involved setup # kexec is skipped intentionally, as it requires a bit more involved setup