sysupdate: add config file with metadata for sysupdate components (#42651)

This carries some metadata for components. It's supposed to grow a bit,
and include a way to enable/disable transfers, and to condition them.
This commit is contained in:
Luca Boccassi
2026-07-16 12:48:10 +01:00
committed by GitHub
15 changed files with 1113 additions and 60 deletions

10
TODO.md
View File

@@ -255,15 +255,6 @@ SPDX-License-Identifier: LGPL-2.1-or-later
- sysupdate: go through all components, and update them all, one by one.
- sysupdate: add concept for enabling/disabling specific components explicitly,
just like features.
- sysupdate: add conditions to transfer files, copying what we have for unit
files and .network files
- pid1,sysupdate,network: add support for a new "tags" condition, that checks
all of the above.
- pcrextend: we probably should measure /etc/machine-info during boot somehow
- pcrextend: we should measure something when we enter developer mode, by some
@@ -2657,7 +2648,6 @@ SPDX-License-Identifier: LGPL-2.1-or-later
- download multiple arbitrary patterns from same source
- SHA256SUMS format with bearer tokens for each resource to download
- decrypt SHA256SUMS with key from tpm
- clean up stuff on disk that disappears from SHA256SUMS
- turn http backend stuff int plugin via varlink
- for each transfer support looking at multiple sources,
pick source with newest entry. If multiple sources have the same entry, use

View File

@@ -1277,7 +1277,8 @@ manpages = [
''],
['systemd-sysupdate',
'8',
['systemd-sysupdate-reboot.service',
['systemd-sysupdate-auto-enable.service',
'systemd-sysupdate-reboot.service',
'systemd-sysupdate-reboot.timer',
'systemd-sysupdate-update.service',
'systemd-sysupdate-update.timer',
@@ -1378,6 +1379,7 @@ manpages = [
['systemd.timer', '5', [], ''],
['systemd.unit', '5', [], ''],
['systemd.v', '7', [], ''],
['sysupdate.components', '5', [], 'ENABLE_SYSUPDATE'],
['sysupdate.d', '5', [], 'ENABLE_SYSUPDATE'],
['sysupdate.features', '5', [], 'ENABLE_SYSUPDATE'],
['sysusers.d', '5', [], 'ENABLE_SYSUSERS'],

View File

@@ -22,6 +22,7 @@
<refname>systemd-sysupdate-update.timer</refname>
<refname>systemd-sysupdate-reboot.service</refname>
<refname>systemd-sysupdate-reboot.timer</refname>
<refname>systemd-sysupdate-auto-enable.service</refname>
<!-- Compatibility symlinks for old names of systemd-sysupdate-update.{service,timer} -->
<refname>systemd-sysupdate.service</refname>
@@ -87,6 +88,12 @@
appropriate time (i.e. typically at night). The two sets of service/timer units may be enabled
separately.</para>
<para>The <filename>systemd-sysupdate-auto-enable.service</filename> system service automatically enables all
suggested components and all features of the enabled components before each update. It is ordered before and
pulled in by <filename>systemd-sysupdate-update.service</filename>, and is not enabled by default. Enable it
if newly suggested components and features shall be picked up automatically as they become available, rather
than requiring manual <command>enable-component</command>/<command>enable-feature</command> invocations.</para>
<para>For details about transfer files and examples see
<citerefentry><refentrytitle>sysupdate.d</refentrytitle><manvolnum>5</manvolnum></citerefentry>.</para>
</refsect1>
@@ -121,6 +128,21 @@
<xi:include href="version-info.xml" xpointer="v257"/></listitem>
</varlistentry>
<varlistentry>
<term><option>enable-feature</option> <replaceable>FEATURE</replaceable></term>
<term><option>disable-feature</option> <replaceable>FEATURE</replaceable></term>
<listitem><para>Enables or disables the specified optional features. This writes an
<varname>Enabled=</varname> setting into a drop-in next to the feature definition, see
<citerefentry><refentrytitle>sysupdate.features</refentrytitle><manvolnum>5</manvolnum></citerefentry>
for details. The features may be specified either as arguments or selected via the
<option>--feature-all</option> or <option>--feature-suggested</option> switches. Note that this only
changes the configuration: to actually download the newly enabled transfers, or remove the disabled
ones, invoke <option>update</option> or <option>vacuum</option> afterwards.</para>
<xi:include href="version-info.xml" xpointer="v262"/></listitem>
</varlistentry>
<varlistentry>
<term><option>check-new</option></term>
@@ -209,6 +231,18 @@
<xi:include href="version-info.xml" xpointer="v251"/></listitem>
</varlistentry>
<varlistentry>
<term><option>enable-component</option> <replaceable>COMPONENT</replaceable></term>
<term><option>disable-component</option> <replaceable>COMPONENT</replaceable></term>
<listitem><para>Enables or disables the specified components. This writes an
<varname>Enabled=</varname> setting into a drop-in next to the component definition. The components
may be specified either as arguments or selected via the <option>--component=</option>,
<option>--component-all</option> or <option>--component-suggested</option> switches.</para>
<xi:include href="version-info.xml" xpointer="v262"/></listitem>
</varlistentry>
<varlistentry>
<term><option>cleanup</option></term>
@@ -280,15 +314,57 @@
<term><option>--component-all</option></term>
<term><option>-A</option></term>
<listitem><para>Instead of operating on a single component, operate on all known components (as well as
the default, component-less installation). This is currently only supported for the
<command>cleanup</command> command; all other commands will fail if this switch is used.</para>
<listitem><para>Instead of operating on a single component, operate on all known components (as well
as the default, component-less installation). This is supported for the <command>update</command>,
<command>acquire</command>, <command>cleanup</command>, <command>enable-feature</command>,
<command>disable-feature</command>, <command>enable-component</command> and
<command>disable-component</command> commands; all other commands will fail if this switch is
used.</para>
<para>This option may not be combined with <option>--component=</option>.</para>
<xi:include href="version-info.xml" xpointer="v262"/></listitem>
</varlistentry>
<varlistentry>
<term><option>--component-suggested</option></term>
<term><option>-S</option></term>
<listitem><para>Similar to <option>--component-all</option>, but operates only on the components that
are currently <emphasis>suggested</emphasis> for this system, as determined by the
<varname>Suggest=</varname> and <varname>SuggestOn…=</varname> settings in the component definition
files. This is supported for the <command>enable-component</command> and
<command>disable-component</command> commands.</para>
<para>This option may not be combined with <option>--component=</option>.</para>
<xi:include href="version-info.xml" xpointer="v262"/></listitem>
</varlistentry>
<varlistentry>
<term><option>--feature-all</option></term>
<term><option>-a</option></term>
<listitem><para>Instead of operating on features specified on the command line, operate on all
optional features known to the selected component(s). This is supported for the
<command>enable-feature</command> and <command>disable-feature</command> commands.</para>
<xi:include href="version-info.xml" xpointer="v262"/></listitem>
</varlistentry>
<varlistentry>
<term><option>--feature-suggested</option></term>
<term><option>-s</option></term>
<listitem><para>Similar to <option>--feature-all</option>, but operates only on the optional features
that are currently <emphasis>suggested</emphasis> for this system, as determined by the
<varname>Suggest=</varname> and <varname>SuggestOn…=</varname> settings in the feature definition
files. This is supported for the <command>enable-feature</command> and
<command>disable-feature</command> commands.</para>
<xi:include href="version-info.xml" xpointer="v262"/></listitem>
</varlistentry>
<varlistentry>
<term><option>--definitions=</option></term>
@@ -416,6 +492,8 @@
<para><simplelist type="inline">
<member><citerefentry><refentrytitle>systemd</refentrytitle><manvolnum>1</manvolnum></citerefentry></member>
<member><citerefentry><refentrytitle>sysupdate.d</refentrytitle><manvolnum>5</manvolnum></citerefentry></member>
<member><citerefentry><refentrytitle>sysupdate.features</refentrytitle><manvolnum>5</manvolnum></citerefentry></member>
<member><citerefentry><refentrytitle>sysupdate.components</refentrytitle><manvolnum>5</manvolnum></citerefentry></member>
<member><citerefentry><refentrytitle>systemd-sysupdated.service</refentrytitle><manvolnum>8</manvolnum></citerefentry></member>
<member><citerefentry><refentrytitle>systemd-repart</refentrytitle><manvolnum>8</manvolnum></citerefentry></member>
</simplelist></para>

View File

@@ -0,0 +1,292 @@
<?xml version='1.0'?>
<!DOCTYPE refentry PUBLIC "-//OASIS//DTD DocBook XML V4.5//EN"
"http://www.oasis-open.org/docbook/xml/4.5/docbookx.dtd">
<!-- SPDX-License-Identifier: LGPL-2.1-or-later -->
<refentry id="sysupdate.components" conditional='ENABLE_SYSUPDATE'
xmlns:xi="http://www.w3.org/2001/XInclude">
<refentryinfo>
<title>sysupdate.components</title>
<productname>systemd</productname>
</refentryinfo>
<refmeta>
<refentrytitle>sysupdate.components</refentrytitle>
<manvolnum>5</manvolnum>
</refmeta>
<refnamediv>
<refname>sysupdate.components</refname>
<refpurpose>Definition Files for Update Components</refpurpose>
</refnamediv>
<refsynopsisdiv>
<para><simplelist>
<member><filename>/etc/sysupdate.<replaceable>component</replaceable>.component</filename></member>
<member><filename>/run/sysupdate.<replaceable>component</replaceable>.component</filename></member>
<member><filename>/usr/local/lib/sysupdate.<replaceable>component</replaceable>.component</filename></member>
<member><filename>/usr/lib/sysupdate.<replaceable>component</replaceable>.component</filename></member>
</simplelist></para>
</refsynopsisdiv>
<refsect1>
<title>Description</title>
<para>A "Component" is a set of
<citerefentry><refentrytitle>sysupdate.d</refentrytitle><manvolnum>5</manvolnum></citerefentry>
transfer definitions that
<citerefentry><refentrytitle>systemd-sysupdate</refentrytitle><manvolnum>8</manvolnum></citerefentry>
updates independently from the rest of the OS.
Each component is a self-contained installation with its own version, and its own set of transfer files,
stored in a directory named after the component:
<filename>sysupdate.<replaceable>component</replaceable>.d/*.transfer</filename> (as opposed to the
default, component-less <filename>sysupdate.d/*.transfer</filename>).
Components are selected via the <option>--component=</option>, <option>--component-all</option> or
<option>--component-suggested</option> switches of
<citerefentry><refentrytitle>systemd-sysupdate</refentrytitle><manvolnum>8</manvolnum></citerefentry>, and
enumerated by its <command>components</command> command.</para>
<para>Because components are updated separately, they are the right tool for OS resources that shall be
versioned and updated independently from the base OS — for example a container image or a portable service
image that follows its own release cycle. This is the primary difference between a component and an
<citerefentry><refentrytitle>sysupdate.features</refentrytitle><manvolnum>5</manvolnum></citerefentry>
Optional Feature: the transfers belonging to a feature are updated in lock-step with the target they are
part of, while a component is a wholly separate installation. Do not use a component for resources that
shall always be updated synchronously with something else; use a feature (or simply additional transfer
files in the same directory) for those cases.</para>
<para>A component does not require a <filename>*.component</filename> file: as soon as a matching
<filename>sysupdate.<replaceable>component</replaceable>.d/</filename> directory with transfer definitions
exists, the component is available for updating. The <filename>*.component</filename> file described here
is optional, and carries metadata about the component (such as a human-readable description and a
documentation URL) as well as its enablement state.</para>
<para>When a component is disabled (via <varname>Enabled=false</varname>, see below), operations that
would download or install new versions — most importantly <command>update</command> — refuse to act on
it. Other operations, such as <command>vacuum</command> and <command>cleanup</command>, continue to
consider the component's transfers so that previously installed instances remain managed and can be
cleaned up. Components are enabled or disabled with the <command>enable-component</command> and
<command>disable-component</command> commands of
<citerefentry><refentrytitle>systemd-sysupdate</refentrytitle><manvolnum>8</manvolnum></citerefentry>,
which write an <varname>Enabled=</varname> setting into a drop-in next to the component definition.</para>
<para>Component definitions support drop-in files, which are most commonly used to override the
<varname>Enabled=</varname> setting. Drop-ins are read from
<filename>sysupdate.<replaceable>component</replaceable>.component.d/*.conf</filename> in the same set of
directories as the main file. They can also be masked to hide the component definition entirely.</para>
<para>Each <filename>*.component</filename> file contains one section: [Component].</para>
</refsect1>
<refsect1>
<title>[Component] Section Options</title>
<para>This section defines general properties of this component.</para>
<variablelist>
<varlistentry>
<term><varname>Description=</varname></term>
<listitem><para>A short human-readable description of this component.
This may be used as a label for this component, so the string should meaningfully identify the
component among the components available on the system.</para>
<xi:include href="version-info.xml" xpointer="v262"/></listitem>
</varlistentry>
<varlistentry>
<term><varname>Documentation=</varname></term>
<listitem><para>A user-presentable URL to documentation about this component.
This setting supports specifier expansion; see below for details on supported specifiers.</para>
<xi:include href="version-info.xml" xpointer="v262"/></listitem>
</varlistentry>
<varlistentry>
<term><varname>Enabled=</varname></term>
<listitem><para>Whether or not this component is enabled. If unspecified, the component is enabled
by default. When disabled, <command>systemd-sysupdate</command> refuses to download or install new
versions of this component, but continues to consider its transfers while vacuuming and in other
situations where it needs to determine ownership over previously downloaded system resources.</para>
<xi:include href="version-info.xml" xpointer="v262"/></listitem>
</varlistentry>
<varlistentry>
<term><varname>Suggest=</varname></term>
<listitem><para>Takes a boolean argument. If true, this component is suggested for enablement, if
false it is not. This does not enable the component on its own, but is a hint surfaced by the update
tools (for example via the <option>--component-suggested</option> switch of
<citerefentry><refentrytitle>systemd-sysupdate</refentrytitle><manvolnum>8</manvolnum></citerefentry>)
so that the system administrator or higher-level tooling may act on it.</para>
<para>If this setting is not specified, the <varname>SuggestOn…=</varname> conditions described below
are evaluated instead to determine whether the component is suggested. If neither this setting nor any
<varname>SuggestOn…=</varname> condition is specified, the component is not suggested.</para>
<xi:include href="version-info.xml" xpointer="v262"/></listitem>
</varlistentry>
<varlistentry>
<term><varname>SuggestOnArchitecture=</varname></term>
<term><varname>SuggestOnFirmware=</varname></term>
<term><varname>SuggestOnVirtualization=</varname></term>
<term><varname>SuggestOnHost=</varname></term>
<term><varname>SuggestOnFraction=</varname></term>
<term><varname>SuggestOnKernelCommandLine=</varname></term>
<term><varname>SuggestOnVersion=</varname></term>
<term><varname>SuggestOnCredential=</varname></term>
<term><varname>SuggestOnSecurity=</varname></term>
<term><varname>SuggestOnOSRelease=</varname></term>
<term><varname>SuggestOnMachineTag=</varname></term>
<listitem><para>Suggest this component for enablement depending on system properties. These settings
take the same arguments and implement the same semantics — including the leading <literal>!</literal>
for negation — as the identically-named
<varname>ConditionArchitecture=</varname>, <varname>ConditionFirmware=</varname>,
<varname>ConditionVirtualization=</varname>, <varname>ConditionHost=</varname>,
<varname>ConditionFraction=</varname>, <varname>ConditionKernelCommandLine=</varname>,
<varname>ConditionVersion=</varname>, <varname>ConditionCredential=</varname>,
<varname>ConditionSecurity=</varname>, <varname>ConditionOSRelease=</varname> and
<varname>ConditionMachineTag=</varname> settings for unit files, which are documented in
<citerefentry><refentrytitle>systemd.unit</refentrytitle><manvolnum>5</manvolnum></citerefentry>.
The component is suggested if all specified conditions apply. As with the <varname>Condition…=</varname>
settings, assigning an empty string to one of these resets the list.</para>
<para>These conditions are only evaluated if <varname>Suggest=</varname> is not specified.</para>
<xi:include href="version-info.xml" xpointer="v262"/></listitem>
</varlistentry>
</variablelist>
</refsect1>
<refsect1>
<title>Specifiers</title>
<para>Specifiers may be used in the <varname>Documentation=</varname> setting. The following expansions
are understood:</para>
<table class='specifiers'>
<title>Specifiers available</title>
<tgroup cols='3' align='left' colsep='1' rowsep='1'>
<colspec colname="spec" />
<colspec colname="mean" />
<colspec colname="detail" />
<thead>
<row>
<entry>Specifier</entry>
<entry>Meaning</entry>
<entry>Details</entry>
</row>
</thead>
<tbody>
<xi:include href="standard-specifiers.xml" xpointer="a"/>
<xi:include href="standard-specifiers.xml" xpointer="A"/>
<xi:include href="standard-specifiers.xml" xpointer="b"/>
<xi:include href="standard-specifiers.xml" xpointer="B"/>
<xi:include href="standard-specifiers.xml" xpointer="H"/>
<xi:include href="standard-specifiers.xml" xpointer="l"/>
<xi:include href="standard-specifiers.xml" xpointer="m"/>
<xi:include href="standard-specifiers.xml" xpointer="M"/>
<xi:include href="standard-specifiers.xml" xpointer="o"/>
<xi:include href="standard-specifiers.xml" xpointer="v"/>
<xi:include href="standard-specifiers.xml" xpointer="w"/>
<xi:include href="standard-specifiers.xml" xpointer="W"/>
<xi:include href="standard-specifiers.xml" xpointer="T"/>
<xi:include href="standard-specifiers.xml" xpointer="V"/>
<xi:include href="standard-specifiers.xml" xpointer="percent"/>
</tbody>
</tgroup>
</table>
</refsect1>
<refsect1>
<title>Examples</title>
<example>
<title>A Separately Updated Container Image</title>
<para>We'll use the hypothetical "foobarOS" described in
<citerefentry><refentrytitle>sysupdate.d</refentrytitle><manvolnum>5</manvolnum></citerefentry> as our
example base OS.
foobarOS ships a container image that many, but not all, deployments want to run. The image has its own
release cadence and version number, entirely separate from the base OS, so it is a poor fit for an
Optional Feature (which would be version-locked to the OS). Instead, let's model it as a component named
<literal>webapp</literal>.</para>
<para>First, the component definition, carrying its metadata and marking it as disabled by default:</para>
<para><programlisting># /usr/lib/sysupdate.webapp.component
[Component]
Description=WebApp Container Image
Documentation=https://docs.example.com/foobarOS/webapp
Enabled=false
</programlisting></para>
<para>Next, the transfer definition for the component, placed in the component's own directory
<filename>sysupdate.webapp.d/</filename>:</para>
<para><programlisting># /usr/lib/sysupdate.webapp.d/50-webapp.transfer
[Source]
Type=url-file
Path=https://download.example.com/
MatchPattern=webapp_@v.raw.xz
[Target]
Type=regular-file
Path=/var/lib/machines
MatchPattern=webapp_@v.raw
Mode=0444
InstancesMax=2
</programlisting></para>
<para>With these two files, updates for the <literal>webapp</literal> component can be checked and
downloaded independently of the base OS, for example with
<command>systemd-sysupdate --component=webapp update</command>. Because
<varname>Enabled=false</varname> was set, the administrator must first opt in with
<command>systemd-sysupdate enable-component webapp</command>, which writes the following drop-in:</para>
<para><programlisting># /etc/sysupdate.webapp.component.d/50-systemd-sysupdate-enabled.conf
# Generated via 'systemd-sysupdate enable-component'
[Component]
Enabled=true
</programlisting></para>
</example>
<example>
<title>A Component Suggested Only on Bare Metal</title>
<para>Suppose foobarOS ships a component with firmware and microcode updates that is only relevant when
running on physical hardware, not inside a virtual machine. We can use <varname>SuggestOn…=</varname> to
hint that the component should be enabled in that case, without enabling it automatically:</para>
<para><programlisting># /usr/lib/sysupdate.firmware.component
[Component]
Description=Firmware and Microcode Updates
Documentation=https://docs.example.com/foobarOS/firmware
Enabled=false
SuggestOnVirtualization=!vm
</programlisting></para>
<para>Higher-level tooling can now query the suggested components (via
<command>systemd-sysupdate --component-suggested …</command>) and enable them as appropriate. On bare
metal the <literal>firmware</literal> component is suggested; inside a VM it is not.</para>
</example>
</refsect1>
<refsect1>
<title>See Also</title>
<para><simplelist type="inline">
<member><citerefentry><refentrytitle>systemd</refentrytitle><manvolnum>1</manvolnum></citerefentry></member>
<member><citerefentry><refentrytitle>systemd-sysupdate</refentrytitle><manvolnum>8</manvolnum></citerefentry></member>
<member><citerefentry><refentrytitle>sysupdate.d</refentrytitle><manvolnum>5</manvolnum></citerefentry></member>
<member><citerefentry><refentrytitle>sysupdate.features</refentrytitle><manvolnum>5</manvolnum></citerefentry></member>
</simplelist></para>
</refsect1>
</refentry>

View File

@@ -1068,6 +1068,8 @@ CurrentSymlink=myContainer</programlisting></para>
<para><simplelist type="inline">
<member><citerefentry><refentrytitle>systemd</refentrytitle><manvolnum>1</manvolnum></citerefentry></member>
<member><citerefentry><refentrytitle>systemd-sysupdate</refentrytitle><manvolnum>8</manvolnum></citerefentry></member>
<member><citerefentry><refentrytitle>sysupdate.features</refentrytitle><manvolnum>5</manvolnum></citerefentry></member>
<member><citerefentry><refentrytitle>sysupdate.components</refentrytitle><manvolnum>5</manvolnum></citerefentry></member>
<member><citerefentry><refentrytitle>systemd-repart</refentrytitle><manvolnum>8</manvolnum></citerefentry></member>
</simplelist></para>
</refsect1>

View File

@@ -381,6 +381,7 @@ InstancesMax=2
<member><citerefentry><refentrytitle>systemd</refentrytitle><manvolnum>1</manvolnum></citerefentry></member>
<member><citerefentry><refentrytitle>systemd-sysupdate</refentrytitle><manvolnum>8</manvolnum></citerefentry></member>
<member><citerefentry><refentrytitle>sysupdate.d</refentrytitle><manvolnum>5</manvolnum></citerefentry></member>
<member><citerefentry><refentrytitle>sysupdate.components</refentrytitle><manvolnum>5</manvolnum></citerefentry></member>
</simplelist></para>
</refsect1>

View File

@@ -55,6 +55,7 @@ foreach item : [
['systemd-run', ''],
['systemd-sysext', 'ENABLE_SYSEXT'],
['systemd-sysinstall', 'ENABLE_SYSINSTALL'],
['systemd-sysupdate', 'ENABLE_SYSUPDATE'],
['systemd-vmspawn', 'ENABLE_VMSPAWN'],
['systemd-vpick', ''],
['timedatectl', 'ENABLE_TIMEDATED'],

View File

@@ -0,0 +1,138 @@
# shellcheck shell=bash
# systemd-sysupdate(8) completion -*- shell-script -*-
# SPDX-License-Identifier: LGPL-2.1-or-later
#
# This file is part of systemd.
#
# systemd is free software; you can redistribute it and/or modify it
# under the terms of the GNU Lesser General Public License as published by
# the Free Software Foundation; either version 2.1 of the License, or
# (at your option) any later version.
#
# systemd is distributed in the hope that it will be useful, but
# WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
# General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public License
# along with systemd; If not, see <https://www.gnu.org/licenses/>.
__contains_word() {
local w word=$1; shift
for w in "$@"; do
[[ $w = "$word" ]] && return
done
}
# List the components or features known to the installation. The argument is both
# the verb to invoke ('components' or 'features') and the JSON array key to extract.
__systemd_sysupdate_list() {
systemd-sysupdate --no-pager --json=short "$1" 2>/dev/null |
sed -n "s/.*\"$1\":\[\([^]]*\)\].*/\1/p" | tr ',' '\n' | tr -d '"'
}
_systemd-sysupdate() {
local i verb comps
local cur=${COMP_WORDS[COMP_CWORD]} prev=${COMP_WORDS[COMP_CWORD-1]} words cword
local -A OPTS=(
[STANDALONE]='-h --help --version
--no-pager
--no-legend
-A --component-all
-S --component-suggested
-a --feature-all
-s --feature-suggested
--reboot
--offline'
[ARG]='-C --component
--definitions
--root
--image
--image-policy
--transfer-source
-m --instances-max
--sync
--verify
--cleanup
--json'
)
local -A VERBS=(
[STANDALONE]='check-new
vacuum
cleanup
pending
reboot
components'
[VERSION]='list
update
acquire'
[FEATURE]='features
enable-feature
disable-feature'
[COMPONENT]='enable-component
disable-component'
)
_init_completion || return
if __contains_word "$prev" ${OPTS[ARG]}; then
case $prev in
-C|--component)
comps=$( __systemd_sysupdate_list components )
;;
--definitions|--root|--transfer-source)
comps=$(compgen -A directory -- "$cur" )
compopt -o dirnames
;;
--image)
comps=$(compgen -A file -- "$cur" )
compopt -o filenames
;;
--image-policy)
comps=''
;;
--instances-max|-m)
comps=''
;;
--sync|--verify|--cleanup)
comps='no yes'
;;
--json)
comps='pretty short off'
;;
esac
COMPREPLY=( $(compgen -W '$comps' -- "$cur") )
return 0
fi
if [[ "$cur" = -* ]]; then
COMPREPLY=( $(compgen -W '${OPTS[*]}' -- "$cur") )
return 0
fi
for ((i=0; i < COMP_CWORD; i++)); do
if __contains_word "${COMP_WORDS[i]}" ${VERBS[*]} &&
! __contains_word "${COMP_WORDS[i-1]}" ${OPTS[ARG]}; then
verb=${COMP_WORDS[i]}
break
fi
done
if [[ -z ${verb-} ]]; then
comps=${VERBS[*]}
elif __contains_word "$verb" ${VERBS[FEATURE]}; then
comps=$( __systemd_sysupdate_list features )
elif __contains_word "$verb" ${VERBS[COMPONENT]}; then
comps=$( __systemd_sysupdate_list components )
else
# STANDALONE verbs take no argument, VERSION verbs take a remote version
# that cannot be enumerated locally.
comps=''
fi
COMPREPLY=( $(compgen -W '$comps' -- "$cur") )
return 0
}
complete -F _systemd-sysupdate systemd-sysupdate

View File

@@ -0,0 +1,92 @@
#compdef systemd-sysupdate
# SPDX-License-Identifier: LGPL-2.1-or-later
local context state state_descr line
typeset -A opt_args
local ret=1
# Pick the component/feature names out of the --json=short output of the
# 'components'/'features' verbs. (The tabular output is unsuitable here, since
# it prefixes each name with enabled/suggested checkmark columns.)
_systemd-sysupdate_components() {
local expl
local -a components=( ${(@)${(@)${(s:,:)${${"$(_call_program -l components systemd-sysupdate --no-pager --json=short components 2>/dev/null)"#*\"components\":\[}%%\]*}}#\"}%\"} )
_wanted components expl "component" compadd "$@" -a - components
}
_systemd-sysupdate_features() {
local expl
local -a features=( ${(@)${(@)${(s:,:)${${"$(_call_program -l features systemd-sysupdate --no-pager --json=short features 2>/dev/null)"#*\"features\":\[}%%\]*}}#\"}%\"} )
_wanted features expl "feature" compadd "$@" -a - features
}
local -a opts=(
{-h,--help}'[Show help message and exit]'
'--version[Show package version and exit]'
'--no-pager[Do not pipe output into a pager]'
'--no-legend[Do not show the headers and footers]'
'--json=[Show output as JSON]:mode:(pretty short off)'
'(-A --component-all -S --component-suggested)'{-C+,--component=}'[Select component to update]:component:_systemd-sysupdate_components'
'(-C --component -S --component-suggested)'{-A,--component-all}'[Select all components]'
'(-C --component -A --component-all)'{-S,--component-suggested}'[Select all suggested components]'
'(-s --feature-suggested)'{-a,--feature-all}'[Select all features]'
'(-a --feature-all)'{-s,--feature-suggested}'[Select all suggested features]'
'--definitions=[Find transfer definitions in specified directory]:directory:_directories'
'--root=[Operate on an alternate filesystem root]:directory:_directories'
'--image=[Operate on disk image as filesystem root]:image file:_files'
'--image-policy=[Specify disk image dissection policy]:policy'
'--transfer-source=[Specify the directory to transfer sources from]:directory:_directories'
{-m+,--instances-max=}'[How many instances to maintain]:number'
'--sync=[Control whether to sync data to disk]:bool:(yes no)'
'--verify=[Force signature verification on or off]:bool:(yes no)'
'--reboot[Reboot after updating to newer version]'
'--offline[Do not fetch metadata from the network]'
'--cleanup=[Clean up orphaned files after completing update]:bool:(yes no)'
)
local -a commands=(
'list:List available and installed versions'
'features:List optional features'
'enable-feature:Enable optional features'
'disable-feature:Disable optional features'
'check-new:Check if a newer version is available'
'update:Install newest version'
'acquire:Download newest version without installing'
'vacuum:Make room by deleting old versions'
'cleanup:Clean up orphaned files'
'pending:Report whether a newer version is installed than booted'
'reboot:Reboot if a newer version is installed than booted'
'components:Show list of components'
'enable-component:Enable components'
'disable-component:Disable components'
)
_arguments -s -A '-*' \
"$opts[@]" \
':command:->command' \
'*:: :->argument' && ret=0
case $state in
command)
_describe -t commands 'systemd-sysupdate command' commands && ret=0
;;
argument)
local curcontext=${curcontext%:*:*}:systemd-sysupdate-$words[1]:
case $words[1] in
features)
_arguments -s "$opts[@]" ':feature:_systemd-sysupdate_features' && ret=0
;;
enable-feature|disable-feature)
_arguments -s "$opts[@]" '*:feature:_systemd-sysupdate_features' && ret=0
;;
enable-component|disable-component)
_arguments -s "$opts[@]" '*:component:_systemd-sysupdate_components' && ret=0
;;
*)
_arguments -s "$opts[@]" && ret=0
;;
esac
;;
esac
return ret

View File

@@ -45,6 +45,7 @@ foreach item : [
['_systemd-path', ''],
['_systemd-run', ''],
['_systemd-sysinstall', 'ENABLE_SYSINSTALL'],
['_systemd-sysupdate', 'ENABLE_SYSUPDATE'],
['_systemd-tmpfiles', 'ENABLE_TMPFILES'],
['_timedatectl', 'ENABLE_TIMEDATED'],
['_udevadm', ''],

View File

@@ -2591,44 +2591,20 @@ typedef enum {
UPDATE_ACTION_INSTALL = 1 << 1,
} UpdateActionFlags;
static int verb_update_impl(int argc, char **argv, UpdateActionFlags action_flags) {
_cleanup_free_ char *booted_version = NULL;
static int context_update(
Context *c,
const char *version,
const char *booted_version,
UpdateActionFlags action_flags) {
UpdateSet *applied = NULL;
const char *version;
int r;
assert(argc <= 2);
version = argc >= 2 ? argv[1] : NULL;
_cleanup_(context_done) Context context = CONTEXT_NULL;
r = context_from_cmdline(&context);
if (r < 0)
return r;
if (context.feature_select != SELECT_EXPLICIT)
return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "--feature-all/--feature-suggested is not supported for '%s'.", argv[0]);
if (context.component_select != SELECT_EXPLICIT)
return log_error_errno(SYNTHETIC_ERRNO(EOPNOTSUPP), "--component-all/--component-suggested currently not supported for '%s'.", argv[0]);
if (context.instances_max < 2)
return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
"The --instances-max= argument must be >= 2 while updating");
if (context.reboot) {
/* If automatic reboot on completion is requested, let's first determine the currently booted image */
r = parse_os_release(context.root, "IMAGE_VERSION", &booted_version);
if (r < 0)
return log_error_errno(r, "Failed to parse /etc/os-release: %m");
if (!booted_version)
return log_error_errno(SYNTHETIC_ERRNO(ENODATA), "/etc/os-release lacks IMAGE_VERSION field.");
}
bool installed = false;
int ret = 0;
int r, ret = 0;
assert(c);
r = context_load_online(
&context,
c,
/* process_image_flags= */ 0,
READ_DEFINITIONS_REQUIRES_ENABLED_TRANSFERS|
READ_DEFINITIONS_REQUIRES_ANY_TRANSFERS|
@@ -2640,15 +2616,15 @@ static int verb_update_impl(int argc, char **argv, UpdateActionFlags action_flag
/* No transfer files found. In that case, still do the installdb cleanup below */
RET_GATHER(ret, r);
} else {
if (action_flags & UPDATE_ACTION_ACQUIRE)
r = context_acquire(&context, version);
if (FLAGS_SET(action_flags, UPDATE_ACTION_ACQUIRE))
r = context_acquire(c, version);
else
r = context_process_partial_and_pending(&context, version);
r = context_process_partial_and_pending(c, version);
if (r < 0)
return r;
if (FLAGS_SET(action_flags, UPDATE_ACTION_INSTALL) && r > 0) { /* installation of update indicated */
r = context_install(&context, version, &applied);
r = context_install(c, version, &applied);
if (r < 0)
return r;
@@ -2658,22 +2634,22 @@ static int verb_update_impl(int argc, char **argv, UpdateActionFlags action_flag
/* context_install() returns > 0 (and emits a notification) only if it actually applied an update. If
* nothing was applied but SYSTEMD_SYSUPDATE_FORCE_NOTIFY=1 is set, still notify subscribers (without a
* resource list), so e.g. a kernel/policy refresh can be triggered unconditionally. */
if ((action_flags & UPDATE_ACTION_INSTALL) && !installed) {
if (FLAGS_SET(action_flags, UPDATE_ACTION_INSTALL) && !installed) {
int f = secure_getenv_bool("SYSTEMD_SYSUPDATE_FORCE_NOTIFY");
if (f < 0 && f != -ENXIO)
log_debug_errno(f, "Failed to parse $SYSTEMD_SYSUPDATE_FORCE_NOTIFY, ignoring: %m");
if (f > 0)
(void) context_notify_subscribers(&context, /* us= */ NULL);
(void) context_notify_subscribers(c, /* us= */ NULL);
}
}
if (context.cleanup > 0)
RET_GATHER(ret, installdb_cleanup_component(&context));
if (c->cleanup > 0)
RET_GATHER(ret, installdb_cleanup_component(c));
if (installed) {
/* We installed something, yay */
if (context.reboot) {
if (c->reboot) {
assert(applied);
assert(booted_version);
@@ -2692,6 +2668,89 @@ static int verb_update_impl(int argc, char **argv, UpdateActionFlags action_flag
return ret;
}
static int verb_update_impl(int argc, char **argv, UpdateActionFlags action_flags) {
const char *version;
int r;
assert(argc <= 2);
version = argc >= 2 ? argv[1] : NULL;
_cleanup_(context_done) Context context = CONTEXT_NULL;
r = context_from_cmdline(&context);
if (r < 0)
return r;
if (context.feature_select != SELECT_EXPLICIT)
return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "--feature-all/--feature-suggested is not supported for '%s'.", argv[0]);
if (!IN_SET(context.component_select, SELECT_EXPLICIT, SELECT_ALL))
return log_error_errno(SYNTHETIC_ERRNO(EOPNOTSUPP), "--component-suggested currently not supported for '%s'.", argv[0]);
if (context.instances_max < 2)
return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
"The --instances-max= argument must be >= 2 while updating");
_cleanup_free_ char *booted_version = NULL;
if (context.reboot) {
/* We already checked in parse_argv() that --component=/--component-all/--component-suggested
* are not combined with --reboot */
assert(!context.component);
assert(context.component_select == SELECT_EXPLICIT);
/* If automatic reboot on completion is requested, let's first determine the currently booted image */
r = parse_os_release(context.root, "IMAGE_VERSION", &booted_version);
if (r < 0)
return log_error_errno(r, "Failed to parse /etc/os-release: %m");
if (!booted_version)
return log_error_errno(SYNTHETIC_ERRNO(ENODATA), "/etc/os-release lacks IMAGE_VERSION field.");
}
switch (context.component_select) {
case SELECT_EXPLICIT:
return context_update(&context, version, booted_version, action_flags);
case SELECT_ALL: {
int ret = 0;
/* Update the default, component-less installation first (if any). Running it before the
* enumeration below also ensures the image (if any) is mounted and context.root is set, so
* that we enumerate the components inside the image rather than on the host. A missing
* default installation (ENOENT) is not an error in this mode. */
r = context_update(&context, version, booted_version, action_flags);
if (r != -ENOENT)
RET_GATHER(ret, r);
_cleanup_strv_free_ char **component_names = NULL;
r = context_list_components(&context, &component_names, /* ret_has_default_component= */ NULL);
if (r < 0) {
RET_GATHER(ret, r);
return ret;
}
STRV_FOREACH(name, component_names) {
_cleanup_(context_done) Context cc = CONTEXT_NULL;
r = context_from_base_with_component(&context, *name, &cc);
if (r < 0) {
RET_GATHER(ret, r);
continue;
}
r = context_update(&cc, version, booted_version, action_flags);
if (r == -EHOSTDOWN) /* Component disabled → skip it in the "all" case. */
continue;
RET_GATHER(ret, r);
}
return ret;
}
default:
assert_not_reached();
}
}
VERB(verb_update, "update", "[VERSION]", VERB_ANY, 2, 0,
"Install new version now");
static int verb_update(int argc, char *argv[], uintptr_t _data, void *userdata) {

View File

@@ -918,8 +918,9 @@ test -f "$COMPALL/target-b/comp-b-v1.bin"
test -d /var/lib/systemd/sysupdate/installdb.comp-a
test -d /var/lib/systemd/sysupdate/installdb.comp-b
# --component-all is only supported for the "cleanup" verb, refuse it elsewhere.
(! "$SYSUPDATE" --component-all --verify=no update)
# --component-all is not supported for every verb, so it must be refused where it isn't (e.g. "vacuum").
# (It *is* supported for update/acquire/cleanup/enable-*/disable-*, which is exercised further down.)
(! "$SYSUPDATE" --component-all --verify=no vacuum)
# With the transfer files still in place "cleanup --component-all" is a no-op:
# nothing is orphaned.
@@ -1449,4 +1450,375 @@ rm -rf "$CONFIGDIR/01-manifest-yes-and-retry.transfer" \
"$SYSUPDATE" components |& grep "No components defined." >/dev/null
[[ $(varlinkctl call "$VARLINK_SOCKET" io.systemd.SysUpdate.ListTargets | jq -r '.targets') == "[]" ]]
# ============================================================================
# Components & features: enable/disable verbs, the --component-{all,suggested}
# and --feature-{all,suggested} switches, plus the Suggest=/SuggestOn…=
# settings. Everything below operates on freshly minted throw-away components
# and features, and cleans up after itself.
#
# Layout used throughout:
# * a component <c> is defined by a transfer directory /run/sysupdate.<c>.d/
# (which is what makes it show up in the 'components' list) plus an optional
# metadata file /run/sysupdate.<c>.component carrying Description=/Suggest=/…
# * the enable-component/disable-component verbs write their Enabled= override
# into a drop-in below /etc/sysupdate.<c>.component.d/
# * the enable-feature/disable-feature verbs write their Enabled= override into
# a drop-in below /etc/sysupdate.d/<f>.feature.d/ (default component) or
# /etc/sysupdate.<c>.d/<f>.feature.d/ (named component)
# ============================================================================
CF="$WORKDIR/compfeat"
# The Suggest…MachineTag= tests below drive the machine tags via /etc/machine-info
# (which the condition logic reads directly); back up any pre-existing file so we
# can restore it afterwards, mirroring TEST-74-AUX-UTILS.machine-tags.sh.
MI_BAK="$WORKDIR/machine-info.orig"
rm -f "$MI_BAK"
[[ -e /etc/machine-info ]] && cp -a /etc/machine-info "$MI_BAK"
set_machine_tags() {
if [[ -n "${1:-}" ]]; then
echo "TAGS=$1" >/etc/machine-info
else
rm -f /etc/machine-info
fi
}
restore_machine_info() {
if [[ -e "$MI_BAK" ]]; then
cp -a "$MI_BAK" /etc/machine-info
else
rm -f /etc/machine-info
fi
}
compfeat_cleanup() {
rm -rf /run/sysupdate.d \
/run/sysupdate.compx.d /run/sysupdate.compx.component /run/sysupdate.compx.component.d \
/run/sysupdate.compy.d /run/sysupdate.compy.component /run/sysupdate.compy.component.d \
/run/sysupdate.compz.d /run/sysupdate.compz.component /run/sysupdate.compz.component.d \
/etc/sysupdate.d \
/etc/sysupdate.compx.d /etc/sysupdate.compx.component.d \
/etc/sysupdate.compy.d /etc/sysupdate.compy.component.d \
/etc/sysupdate.compz.d /etc/sysupdate.compz.component.d
rm -rf "$CF"
}
compfeat_reset() {
compfeat_cleanup
mkdir -p "$CF/source" \
"$CF/target-default" "$CF/target-compx" "$CF/target-compy" "$CF/target-compz"
}
# (Re)generate the source payloads + SHA256SUMS for a given version. We create a
# payload for every component/feature so a single SHA256SUMS covers them all;
# individual transfers only ever match their own pattern.
compfeat_source() {
local v="${1:?}"
local n
for n in base compx compy compz feata featb featc; do
echo "$n-$v-$RANDOM" >"$CF/source/$n-$v.bin"
done
(cd "$CF/source" && sha256sum -- *.bin >SHA256SUMS)
}
# Write a regular-file transfer; optional 4th argument gates it behind a feature.
compfeat_transfer() {
local file="${1:?}" pat="${2:?}" tgt="${3:?}" feature="${4:-}"
{
if [[ -n "$feature" ]]; then
printf '[Transfer]\nFeatures=%s\n\n' "$feature"
fi
printf '[Source]\nType=regular-file\nPath=%s\nMatchPattern=%s-@v.bin\n\n' "$CF/source" "$pat"
printf '[Target]\nType=regular-file\nPath=%s\nMatchPattern=%s-@v.bin\nInstancesMax=2\n' "$tgt" "$pat"
} >"$file"
}
comp_enable_dropin() { echo "/etc/sysupdate.$1.component.d/50-systemd-sysupdate-enabled.conf"; }
feat_enable_dropin_default() { echo "/etc/sysupdate.d/$1.feature.d/50-systemd-sysupdate-enabled.conf"; }
feat_enable_dropin_comp() { echo "/etc/sysupdate.$1.d/$2.feature.d/50-systemd-sysupdate-enabled.conf"; }
# Assert a generated Enabled= drop-in exists and carries the expected value.
assert_dropin() {
local file="${1:?}" val="${2:?}"
test -f "$file"
grep "^Enabled=$val$" "$file" >/dev/null
}
# ---------------------------------------------------------------------------
# enable-component / disable-component: explicit selection + observable effect
# ---------------------------------------------------------------------------
compfeat_reset
compfeat_source v1
mkdir -p /run/sysupdate.compx.d
compfeat_transfer /run/sysupdate.compx.d/01-compx.transfer compx "$CF/target-compx"
# A metadata file carrying a human-readable description (exercises loading of the
# per-component *.component file from the search dirs).
cat >/run/sysupdate.compx.component <<EOF
[Component]
Description=CompXDescription
EOF
# The description from the *.component file must surface in the 'components' listing.
"$SYSUPDATE" --no-legend components | grep -F "CompXDescription" >/dev/null
# A brand new component (no Enabled= override anywhere) is enabled by default and
# can be updated.
"$SYSUPDATE" --component=compx --verify=no update
test -f "$CF/target-compx/compx-v1.bin"
# Disable it: this must drop an Enabled=no override next to the definition, and
# subsequent updates for that component must be refused.
"$SYSUPDATE" disable-component compx
assert_dropin "$(comp_enable_dropin compx)" no
(! "$SYSUPDATE" --component=compx --verify=no update) |& grep -F "Component is disabled" >/dev/null
# Re-enable via the "--component= + no positional argument" form and verify the
# update works again.
"$SYSUPDATE" --component=compx enable-component
assert_dropin "$(comp_enable_dropin compx)" yes
rm -f "$CF/target-compx/compx-v1.bin"
"$SYSUPDATE" --component=compx --verify=no update
test -f "$CF/target-compx/compx-v1.bin"
# Enabling a component must not conjure a bogus "<c>.component" pseudo-component
# out of the freshly created sysupdate.compx.component.d/ drop-in directory.
(! "$SYSUPDATE" --json=short components | grep -F '"compx.component"' >/dev/null)
"$SYSUPDATE" --json=short components | grep -F '"compx"' >/dev/null
# ---------------------------------------------------------------------------
# enable-component / disable-component: argument validation & error handling
# ---------------------------------------------------------------------------
# Positional argument and --component= are mutually exclusive.
(! "$SYSUPDATE" --component=compx enable-component compx) |& grep -F "not both" >/dev/null
# Syntactically invalid component name.
(! "$SYSUPDATE" enable-component ../nope) |& grep -F "Component name invalid" >/dev/null
# Unknown component.
(! "$SYSUPDATE" enable-component doesnotexist) |& grep -F "Component not found" >/dev/null
# --definitions= is incompatible with component enablement.
(! "$SYSUPDATE" --definitions="$CF" enable-component compx) |& grep -F "may not be combined" >/dev/null
# The feature-selection switches make no sense here.
(! "$SYSUPDATE" --feature-all enable-component compx) |& grep -F "not supported" >/dev/null
# ---------------------------------------------------------------------------
# --component-all for enable-component / disable-component
# ---------------------------------------------------------------------------
compfeat_reset
compfeat_source v1
mkdir -p /run/sysupdate.compx.d /run/sysupdate.compy.d
compfeat_transfer /run/sysupdate.compx.d/01-compx.transfer compx "$CF/target-compx"
compfeat_transfer /run/sysupdate.compy.d/01-compy.transfer compy "$CF/target-compy"
# Disable *all* components in one go, then re-enable them all.
"$SYSUPDATE" --component-all disable-component
assert_dropin "$(comp_enable_dropin compx)" no
assert_dropin "$(comp_enable_dropin compy)" no
(! "$SYSUPDATE" --component=compx --verify=no update) |& grep -F "Component is disabled" >/dev/null
(! "$SYSUPDATE" --component=compy --verify=no update) |& grep -F "Component is disabled" >/dev/null
"$SYSUPDATE" --component-all enable-component
assert_dropin "$(comp_enable_dropin compx)" yes
assert_dropin "$(comp_enable_dropin compy)" yes
"$SYSUPDATE" --component=compx --verify=no update
"$SYSUPDATE" --component=compy --verify=no update
test -f "$CF/target-compx/compx-v1.bin"
test -f "$CF/target-compy/compy-v1.bin"
# ---------------------------------------------------------------------------
# --component-suggested driven by Suggest= (compx suggested, compy not)
# ---------------------------------------------------------------------------
compfeat_reset
compfeat_source v1
mkdir -p /run/sysupdate.compx.d /run/sysupdate.compy.d
compfeat_transfer /run/sysupdate.compx.d/01-compx.transfer compx "$CF/target-compx"
compfeat_transfer /run/sysupdate.compy.d/01-compy.transfer compy "$CF/target-compy"
cat >/run/sysupdate.compx.component <<EOF
[Component]
Suggest=yes
EOF
cat >/run/sysupdate.compy.component <<EOF
[Component]
Suggest=no
EOF
# 'enable-component --component-suggested' acts on the suggested components only.
"$SYSUPDATE" --component-suggested enable-component
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).
"$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)" yes
# --component-suggested is not supported for the update verb.
(! "$SYSUPDATE" --component-suggested --verify=no update) |& grep -F "not supported" >/dev/null
# ---------------------------------------------------------------------------
# SuggestOnMachineTag= for a component
# ---------------------------------------------------------------------------
compfeat_reset
compfeat_source v1
mkdir -p /run/sysupdate.compz.d
compfeat_transfer /run/sysupdate.compz.d/01-compz.transfer compz "$CF/target-compz"
cat >/run/sysupdate.compz.component <<EOF
[Component]
SuggestOnMachineTag=sysupdate-test-tag
EOF
# Without the matching machine tag the component is not suggested, so
# --component-suggested selects nothing.
set_machine_tags some-other-tag
"$SYSUPDATE" --component-suggested enable-component
test ! -e "$(comp_enable_dropin compz)"
# With the matching tag present it becomes suggested and gets enabled.
set_machine_tags sysupdate-test-tag:another
"$SYSUPDATE" --component-suggested enable-component
assert_dropin "$(comp_enable_dropin compz)" yes
set_machine_tags ""
# ---------------------------------------------------------------------------
# enable-feature / disable-feature on the default component + observable effect
# ---------------------------------------------------------------------------
compfeat_reset
compfeat_source v1
mkdir -p /run/sysupdate.d
compfeat_transfer /run/sysupdate.d/01-base.transfer base "$CF/target-default"
compfeat_transfer /run/sysupdate.d/50-feata.transfer feata "$CF/target-default" feata
cat >/run/sysupdate.d/feata.feature <<EOF
[Feature]
Description=Feature A
EOF
# The feature is listed and disabled by default, so its transfer is not installed.
"$SYSUPDATE" features | grep -F "feata" >/dev/null
"$SYSUPDATE" --verify=no update
test -f "$CF/target-default/base-v1.bin"
test ! -e "$CF/target-default/feata-v1.bin"
# Enabling the feature must drop an Enabled=yes override and cause the gated
# transfer to be installed on the next update.
"$SYSUPDATE" enable-feature feata
assert_dropin "$(feat_enable_dropin_default feata)" yes
"$SYSUPDATE" --verify=no update
test -f "$CF/target-default/feata-v1.bin"
# Disabling it again + vacuum must remove the now-orphaned feature resource.
"$SYSUPDATE" disable-feature feata
assert_dropin "$(feat_enable_dropin_default feata)" no
"$SYSUPDATE" --verify=no vacuum
test ! -e "$CF/target-default/feata-v1.bin"
# Argument validation for the feature verbs.
(! "$SYSUPDATE" enable-feature --feature-all feata) |& grep -F "not both" >/dev/null
(! "$SYSUPDATE" enable-feature 'bad/name') |& grep -F "Feature name invalid" >/dev/null
(! "$SYSUPDATE" --component-suggested enable-feature feata) |& grep -F "not supported" >/dev/null
# ---------------------------------------------------------------------------
# --feature-all / --feature-suggested (default component)
# ---------------------------------------------------------------------------
compfeat_reset
compfeat_source v1
mkdir -p /run/sysupdate.d
compfeat_transfer /run/sysupdate.d/01-base.transfer base "$CF/target-default"
# feata: suggested (Suggest=yes); featb: not suggested (Suggest=no);
# featc: suggested on a machine tag we will set below.
cat >/run/sysupdate.d/feata.feature <<EOF
[Feature]
Suggest=yes
EOF
cat >/run/sysupdate.d/featb.feature <<EOF
[Feature]
Suggest=no
EOF
cat >/run/sysupdate.d/featc.feature <<EOF
[Feature]
SuggestOnMachineTag=sysupdate-test-tag
EOF
# --feature-all operates on every known feature.
"$SYSUPDATE" enable-feature --feature-all
assert_dropin "$(feat_enable_dropin_default feata)" yes
assert_dropin "$(feat_enable_dropin_default featb)" yes
assert_dropin "$(feat_enable_dropin_default featc)" yes
# --feature-suggested (no machine tag): only the Suggest=yes feature is picked.
rm -rf /etc/sysupdate.d
set_machine_tags unrelated
"$SYSUPDATE" enable-feature --feature-suggested
assert_dropin "$(feat_enable_dropin_default feata)" yes
test ! -e "$(feat_enable_dropin_default featb)"
test ! -e "$(feat_enable_dropin_default featc)"
# --feature-suggested with the machine tag set: feata + featc are picked.
rm -rf /etc/sysupdate.d
set_machine_tags sysupdate-test-tag
"$SYSUPDATE" enable-feature --feature-suggested
assert_dropin "$(feat_enable_dropin_default feata)" yes
assert_dropin "$(feat_enable_dropin_default featc)" yes
test ! -e "$(feat_enable_dropin_default featb)"
set_machine_tags ""
# ---------------------------------------------------------------------------
# Features scoped to a named component, and across all components at once
# ---------------------------------------------------------------------------
compfeat_reset
compfeat_source v1
mkdir -p /run/sysupdate.d /run/sysupdate.compx.d
compfeat_transfer /run/sysupdate.d/01-base.transfer base "$CF/target-default"
compfeat_transfer /run/sysupdate.compx.d/01-compx.transfer compx "$CF/target-compx"
cat >/run/sysupdate.d/feata.feature <<EOF
[Feature]
Description=Default feature A
EOF
cat >/run/sysupdate.compx.d/featx.feature <<EOF
[Feature]
Description=Component X feature
EOF
# --component= scopes feature operations to that component: the drop-in must land
# next to the component's definitions, and the default component is untouched.
"$SYSUPDATE" --component=compx enable-feature --feature-all
assert_dropin "$(feat_enable_dropin_comp compx featx)" yes
test ! -e "$(feat_enable_dropin_default feata)"
# --component-all --feature-all fans out over the default component *and* every
# named component.
rm -rf /etc/sysupdate.d /etc/sysupdate.compx.d
"$SYSUPDATE" --component-all enable-feature --feature-all
assert_dropin "$(feat_enable_dropin_default feata)" yes
assert_dropin "$(feat_enable_dropin_comp compx featx)" yes
# ---------------------------------------------------------------------------
# update --component-all installs every component's update in one invocation
# ---------------------------------------------------------------------------
compfeat_reset
compfeat_source v1
mkdir -p /run/sysupdate.compx.d /run/sysupdate.compy.d
compfeat_transfer /run/sysupdate.compx.d/01-compx.transfer compx "$CF/target-compx"
compfeat_transfer /run/sysupdate.compy.d/01-compy.transfer compy "$CF/target-compy"
"$SYSUPDATE" --component-all --verify=no update
test -f "$CF/target-compx/compx-v1.bin"
test -f "$CF/target-compy/compy-v1.bin"
# A second version must likewise be rolled out to every component at once.
compfeat_source v2
"$SYSUPDATE" --component-all --verify=no update
test -f "$CF/target-compx/compx-v2.bin"
test -f "$CF/target-compy/compy-v2.bin"
# A disabled component must not turn "update --component-all" into a failure:
# it is skipped, while the remaining enabled components are still updated.
"$SYSUPDATE" disable-component compy
compfeat_source v3
"$SYSUPDATE" --component-all --verify=no update
test -f "$CF/target-compx/compx-v3.bin"
test ! -e "$CF/target-compy/compy-v3.bin"
compfeat_cleanup
restore_machine_info
touch /testok

View File

@@ -872,6 +872,10 @@ units = [
'file' : 'systemd-sysinstall@.service',
'conditions' : ['ENABLE_SYSINSTALL'],
},
{
'file' : 'systemd-sysupdate-auto-enable.service',
'conditions' : ['ENABLE_SYSUPDATE'],
},
{
'file' : 'systemd-sysupdate-reboot.service',
'conditions' : ['ENABLE_SYSUPDATE'],

View File

@@ -0,0 +1,21 @@
# SPDX-License-Identifier: LGPL-2.1-or-later
#
# This file is part of systemd.
#
# systemd is free software; you can redistribute it and/or modify it
# under the terms of the GNU Lesser General Public License as published by
# the Free Software Foundation; either version 2.1 of the License, or
# (at your option) any later version.
[Unit]
Description=Automatically Enable All Suggested Components & Features for System Updates
Documentation=man:systemd-sysupdate-auto-enable.service(8)
Before=systemd-sysupdate-update.service
[Service]
Type=oneshot
ExecStart=systemd-sysupdate enable-component --component-suggested
ExecStart=systemd-sysupdate enable-feature --component-all --feature-all
[Install]
WantedBy=systemd-sysupdate-update.service

View File

@@ -17,7 +17,7 @@ ConditionVirtualization=!container
[Service]
Type=simple
NotifyAccess=main
ExecStart=systemd-sysupdate update --cleanup=yes
ExecStart=systemd-sysupdate update --component-all --cleanup=yes
# Keep this sandboxing synchronised with systemd-sysupdate@.service
CapabilityBoundingSet=CAP_CHOWN CAP_FOWNER CAP_FSETID CAP_MKNOD CAP_SETFCAP CAP_SYS_ADMIN CAP_SETPCAP CAP_DAC_OVERRIDE CAP_LINUX_IMMUTABLE