diff --git a/man/systemd-sysupdate.xml b/man/systemd-sysupdate.xml
index dcfcf94559..49080f1e36 100644
--- a/man/systemd-sysupdate.xml
+++ b/man/systemd-sysupdate.xml
@@ -334,10 +334,19 @@
are currently suggested for this system, as determined by the
Suggest= and SuggestOn…= settings in the component definition
files. This is supported for the enable-component and
- disable-component commands.
+ disable-component commands. Both commands operate on the same set of components,
+ thus disable-component undoes what enable-component did.
This option may not be combined with .
+ In order to reconcile the system according to suggestions, i.e., to also turn off the components
+ that are not (or no longer) suggested, first disable all components with ,
+ then enable the suggested ones with this switch. Note that this has wider consequences than the equivalent
+ reconciliation of optional features because components are enabled by default, and a component counts as not
+ suggested unless Suggest= or one of the SuggestOn…= settings says otherwise.
+ Reconciling hence explicitly disables every component that is not suggested, including components that ship no
+ *.component file at all. The default component is not affected.
+
@@ -360,7 +369,12 @@
that are currently suggested for this system, as determined by the
Suggest= and SuggestOn…= settings in the feature definition
files. This is supported for the enable-feature and
- disable-feature commands.
+ disable-feature commands. Both commands operate on the same set of features, thus
+ disable-feature undoes what enable-feature did.
+
+ In order to reconcile the system according to suggestions, i.e., to also turn off the features
+ that are not (or no longer) suggested, first disable all features with ,
+ then enable the suggested ones with this switch.
diff --git a/src/sysupdate/sysupdate.c b/src/sysupdate/sysupdate.c
index 9ae80a2a8b..0e1338140f 100644
--- a/src/sysupdate/sysupdate.c
+++ b/src/sysupdate/sysupdate.c
@@ -2244,9 +2244,9 @@ static int context_enable_feature(
HASHMAP_FOREACH(f, c->features) {
r = feature_is_suggested(f);
if (r < 0)
- return log_error_errno(r, "Failed to determine if feature '%s' of component '%s' shall be enabled: %m", f->id, context_component_display(c));
- if (!!r != !!enable) {
- log_debug("Skipping feature '%s' of component '%s'.", f->id, context_component_display(c));
+ return log_error_errno(r, "Failed to determine if feature '%s' of component '%s' is suggested: %m", f->id, context_component_display(c));
+ if (r == 0) {
+ log_debug("Feature '%s' of component '%s' is not suggested, skipping.", f->id, context_component_display(c));
continue;
}
@@ -3245,15 +3245,11 @@ static int verb_enable_component(int argc, char *argv[], uintptr_t _data, void *
r = context_component_is_suggested(&cc);
if (r < 0) {
- log_warning_errno(r, "Failed to determine whether '%s' shall be enabled, skipping: %m", *name);
+ log_warning_errno(r, "Failed to determine whether '%s' is suggested, skipping: %m", *name);
continue;
}
-
- /* This reconciles the system with the suggestions: on 'enable-component' we act on
- * the components that are suggested, on 'disable-component' we act on the ones that
- * are not. Hence pick the components whose suggestion state matches the operation. */
- if (!!r != !!enable) {
- log_debug("Skipping '%s'.", *name);
+ if (r == 0) {
+ log_debug("Component '%s' is not suggested, skipping.", *name);
continue;
}
diff --git a/test/units/TEST-72-SYSUPDATE.sh b/test/units/TEST-72-SYSUPDATE.sh
index 1ddb2ac9b9..f35235ce84 100755
--- a/test/units/TEST-72-SYSUPDATE.sh
+++ b/test/units/TEST-72-SYSUPDATE.sh
@@ -1717,12 +1717,20 @@ EOF
assert_dropin "$(comp_enable_dropin compx)" yes
test ! -e "$(comp_enable_dropin compy)"
-# 'disable-component --component-suggested' reconciles the other way around: it
-# acts on the components that are *not* suggested (i.e. compy).
+# 'disable-component --component-suggested' acts on the very same set, i.e. it
+# undoes what the enable above did. The non-suggested compy is left untouched.
"$SYSUPDATE" --component-suggested disable-component
-assert_dropin "$(comp_enable_dropin compy)" no
-# compx must be left as it was (still enabled from above).
+assert_dropin "$(comp_enable_dropin compx)" no
+test ! -e "$(comp_enable_dropin compy)"
+
+# Reconciling the system with the current suggestions is a two step operation:
+# disable all components, then enable the suggested ones. Unlike the single
+# 'disable-component --component-suggested' above this makes the non-suggested
+# compy explicitly disabled.
+"$SYSUPDATE" --component-all disable-component
+"$SYSUPDATE" --component-suggested enable-component
assert_dropin "$(comp_enable_dropin compx)" yes
+assert_dropin "$(comp_enable_dropin compy)" no
# --component-suggested is not supported for the update verb.
(! "$SYSUPDATE" --component-suggested --verify=no update) |& grep -F "not supported" >/dev/null
@@ -1816,6 +1824,12 @@ assert_dropin "$(feat_enable_dropin_default feata)" yes
assert_dropin "$(feat_enable_dropin_default featb)" yes
assert_dropin "$(feat_enable_dropin_default featc)" yes
+# ... and so does 'disable-feature --feature-all', in the other direction.
+"$SYSUPDATE" disable-feature --feature-all
+assert_dropin "$(feat_enable_dropin_default feata)" no
+assert_dropin "$(feat_enable_dropin_default featb)" no
+assert_dropin "$(feat_enable_dropin_default featc)" no
+
# --feature-suggested (no machine tag): only the Suggest=yes feature is picked.
rm -rf /etc/sysupdate.d
set_machine_tags unrelated
@@ -1831,6 +1845,21 @@ set_machine_tags sysupdate-test-tag
assert_dropin "$(feat_enable_dropin_default feata)" yes
assert_dropin "$(feat_enable_dropin_default featc)" yes
test ! -e "$(feat_enable_dropin_default featb)"
+
+# 'disable-feature --feature-suggested' selects the same features as the enable
+# above, i.e. it undoes it and leaves the non-suggested featb untouched.
+"$SYSUPDATE" disable-feature --feature-suggested
+assert_dropin "$(feat_enable_dropin_default feata)" no
+assert_dropin "$(feat_enable_dropin_default featc)" no
+test ! -e "$(feat_enable_dropin_default featb)"
+
+# The same two step reconciliation for features: disable all of them, then
+# enable the suggested ones, leaving the non-suggested featb explicitly off.
+"$SYSUPDATE" disable-feature --feature-all
+"$SYSUPDATE" enable-feature --feature-suggested
+assert_dropin "$(feat_enable_dropin_default feata)" yes
+assert_dropin "$(feat_enable_dropin_default featc)" yes
+assert_dropin "$(feat_enable_dropin_default featb)" no
set_machine_tags ""
# --component-all must still include the default component when all of its