mirror of
https://github.com/systemd/systemd.git
synced 2025-03-19 22:50:17 +03:00
Merge pull request #18181 from poettering/sysext
systemd-sysext as a method of merging simple OS extensions into /usr and /opt
This commit is contained in:
commit
656e5aa452
7
TODO
7
TODO
@ -45,6 +45,13 @@ Features:
|
||||
in a graceful way, so that updated /usr trees automatically propagate into
|
||||
updated boot loaders on reboot.
|
||||
|
||||
* sysext: optionally, if the merged trees allow it use bind mounts instead of
|
||||
overlayfs
|
||||
|
||||
* nspawn: add support for sysext extensions, too. i.e. a new --extension=
|
||||
switch that takes one or more arguments, and applies the extensions already
|
||||
during startup.
|
||||
|
||||
* add "systemd-analyze debug" + AttachDebugger= in unit files: The former
|
||||
specifies a command to execute; the latter specifies that an already running
|
||||
"systemd-analyze debug" instance shall be contacted and execution paused
|
||||
|
@ -267,3 +267,15 @@ systemd-firstboot and localectl:
|
||||
* `SYSTEMD_LIST_NON_UTF8_LOCALES=1` – if set non-UTF-8 locales are listed among
|
||||
the installed ones. By default non-UTF-8 locales are suppressed from the
|
||||
selection, since we are living in the 21st century.
|
||||
|
||||
systemd-sysext:
|
||||
|
||||
* `SYSTEMD_SYSEXT_HIERARCHIES` – if set to a colon-separated list of absolute
|
||||
paths this variable may be used to override which hierarchies to manage with
|
||||
`systemd-sysext`. By default only `/usr/` and `/opt/` are managed. With this
|
||||
environment variable this list may be changed, in order to add or remove
|
||||
directories from this list. This should only reference "real" file systems
|
||||
and directories that only contain "real" file systems as submounts — do not
|
||||
specify API file systems such as `/proc/` or `/sys/` here, or hierarchies
|
||||
that have them as submounts. In particular, do not specify the root directory
|
||||
`/` here.
|
||||
|
@ -317,6 +317,17 @@
|
||||
</para></listitem>
|
||||
</varlistentry>
|
||||
|
||||
<varlistentry>
|
||||
<term><varname>SYSEXT_LEVEL=</varname></term>
|
||||
|
||||
<listitem><para>A lower-case string (mostly numeric, no spaces or other characters outside of 0–9,
|
||||
a–z, ".", "_" and "-") identifying the operating system extensions support level, to indicate which
|
||||
extension images are supported (See:
|
||||
<citerefentry><refentrytitle>systemd-sysext</refentrytitle><manvolnum>8</manvolnum></citerefentry>).
|
||||
Example: <literal>SYSEXT_LEVEL=2</literal> or
|
||||
<literal>SYSEXT_LEVEL=15.14</literal>.</para></listitem>
|
||||
</varlistentry>
|
||||
|
||||
</variablelist>
|
||||
|
||||
<para>If you are reading this file from C code or a shell script
|
||||
|
@ -954,6 +954,7 @@ manpages = [
|
||||
'systemd-suspend-then-hibernate.service'],
|
||||
''],
|
||||
['systemd-sysctl.service', '8', ['systemd-sysctl'], ''],
|
||||
['systemd-sysext', '8', ['systemd-sysext.service'], ''],
|
||||
['systemd-system-update-generator', '8', [], ''],
|
||||
['systemd-system.conf',
|
||||
'5',
|
||||
|
239
man/systemd-sysext.xml
Normal file
239
man/systemd-sysext.xml
Normal file
@ -0,0 +1,239 @@
|
||||
<?xml version='1.0'?> <!--*-nxml-*-->
|
||||
<!DOCTYPE refentry PUBLIC "-//OASIS//DTD DocBook XML V4.5//EN"
|
||||
"http://www.oasis-open.org/docbook/xml/4.2/docbookx.dtd">
|
||||
<!-- SPDX-License-Identifier: LGPL-2.1-or-later -->
|
||||
|
||||
<refentry id="systemd-sysext"
|
||||
xmlns:xi="http://www.w3.org/2001/XInclude">
|
||||
|
||||
<refentryinfo>
|
||||
<title>systemd-sysext</title>
|
||||
<productname>systemd</productname>
|
||||
</refentryinfo>
|
||||
|
||||
<refmeta>
|
||||
<refentrytitle>systemd-sysext</refentrytitle>
|
||||
<manvolnum>8</manvolnum>
|
||||
</refmeta>
|
||||
|
||||
<refnamediv>
|
||||
<refname>systemd-sysext</refname>
|
||||
<refname>systemd-sysext.service</refname>
|
||||
<refpurpose>Activates System Extension Images</refpurpose>
|
||||
</refnamediv>
|
||||
|
||||
<refsynopsisdiv>
|
||||
<cmdsynopsis>
|
||||
<command>systemd-sysext</command>
|
||||
<arg choice="opt" rep="repeat">OPTIONS</arg>
|
||||
</cmdsynopsis>
|
||||
|
||||
<para><literallayout><filename>systemd-sysext.service</filename></literallayout></para>
|
||||
|
||||
</refsynopsisdiv>
|
||||
|
||||
<refsect1>
|
||||
<title>Description</title>
|
||||
|
||||
<para><command>systemd-sysext</command> activates/deactivates system extension images. System extension
|
||||
images may – dynamically at runtime — extend the <filename>/usr/</filename> and
|
||||
<filename>/opt/</filename> directory hierarchies with additional files. This is particularly useful on
|
||||
immutable system images where a <filename>/usr/</filename> and/or <filename>/opt/</filename> hierarchy
|
||||
residing on a read-only file system shall be extended temporarily at runtime without making any
|
||||
persistent modifications.</para>
|
||||
|
||||
<para>System extension images should contain files and directories similar in fashion to regular
|
||||
operating system tree. When one or more system extension images are activated, their
|
||||
<filename>/usr/</filename> and <filename>/opt/</filename> hierarchies are combined via
|
||||
<literal>overlayfs</literal> with the same hierarchies of the host OS, and the host
|
||||
<filename>/usr/</filename> and <filename>/opt</filename> overmounted with it ("merging"). When they are
|
||||
deactivated, the mount point is disassembled — again revealing the unmodified original host version of
|
||||
the hierarchy ("unmerging"). Merging thus makes the extension's resources suddenly appear below the
|
||||
<filename>/usr/</filename> and <filename>/opt/</filename> hierarchies as if they were included in the
|
||||
base OS image itself. Unmerging makes them disappear again, leaving in place only the files that were
|
||||
shipped with the base OS image itself.</para>
|
||||
|
||||
<para>Files and directories contained in the extension images outside of the <filename>/usr/</filename>
|
||||
and <filename>/opt/</filename> hierarchies are <emphasis>not</emphasis> merged, and hence have no effect
|
||||
when included in a system extension image. In particular, files in the <filename>/etc/</filename> and
|
||||
<filename>/var/</filename> included in a system extension image will <emphasis>not</emphasis> appear in
|
||||
the respective hierarchies after activation.</para>
|
||||
|
||||
<para>System extension images are strictly read-only, and the host <filename>/usr/</filename> and
|
||||
<filename>/opt/</filename> hierarchies become read-only too while they are activated.</para>
|
||||
|
||||
<para>System extensions are supposed to be purely additive, i.e. they are supposed to include only files
|
||||
that do not exist in the underlying basic OS image. However, the underlying mechanism (overlayfs) also
|
||||
allows removing files, but it is recommended not to make use of this.</para>
|
||||
|
||||
<para>System extension images may be provided in the following formats:</para>
|
||||
|
||||
<orderedlist>
|
||||
<listitem><para>Plain directories or btrfs subvolumes containing the OS tree</para></listitem>
|
||||
<listitem><para>Disk images with a GPT disk label, following the <ulink
|
||||
url="https://systemd.io/DISCOVERABLE_PARTITIONS">Discoverable Partition Specification</ulink></para></listitem>
|
||||
<listitem><para>Disk images lacking a partition table, with a naked Linux file system (e.g. squashfs or ext4)</para></listitem>
|
||||
</orderedlist>
|
||||
|
||||
<para>These image formats are the same ones that
|
||||
<citerefentry><refentrytitle>systemd-nspawn</refentrytitle><manvolnum>1</manvolnum></citerefentry>
|
||||
supports via it's <option>--directory=</option>/<option>--image=</option> switches and those that the
|
||||
service manager supports via <option>RootDirectory=</option>/<option>RootImage=</option>. Similar to
|
||||
them they may optionally carry Verity authentication information.</para>
|
||||
|
||||
<para>System extensions are automatically looked for in the directories
|
||||
<filename>/etc/extensions/</filename>, <filename>/run/extensions/</filename>,
|
||||
<filename>/var/lib/extensions/</filename>, <filename>/usr/lib/extensions/</filename> and
|
||||
<filename>/usr/local/lib/extensions/</filename>. The first two listed directories are not suitable for
|
||||
carrying large binary images, however are still useful for carrying symlinks to them. The primary place
|
||||
for installing system extensions is <filename>/var/lib/extensions/</filename>. Any directories found in
|
||||
these search directories are considered directory based extension images, any files with the
|
||||
<filename>.raw</filename> suffix are considered disk image based extension images.</para>
|
||||
|
||||
<para>During boot OS extension images are activated automatically, if the
|
||||
<filename>systemd-sysext.service</filename> is enabled. Note that this service runs only after the
|
||||
underlying file systems where system extensions are searched are mounted. This means they are not
|
||||
suitable for shipping resources that are processed by subsystems running in earliest boot. Specifically,
|
||||
OS extension images are not suitable for shipping system services or
|
||||
<citerefentry><refentrytitle>systemd-sysusers</refentrytitle><manvolnum>8</manvolnum></citerefentry>
|
||||
definitions. See <ulink url="https://systemd.io/PORTABLE_SERVICES">Portable Services</ulink> for a simple
|
||||
mechanism for shipping system services in disk images, in a similar fashion to OS extensions. Note the
|
||||
different isolation on these two mechanisms: while system extension directly extend the underlying OS
|
||||
image with additional files that appear in a way very similar to as if they were shipped in the OS image
|
||||
itself and thus imply no security isolation, portable services imply service level sandboxing in one way
|
||||
or another. The <filename>systemd-sysext.service</filename> service is guaranteed to finish start-up
|
||||
before <filename>basic.target</filename> is reached; i.e. at the time regular services initialize (those
|
||||
which do not use <varname>DefaultDependencies=no</varname>), the files and directories system extensions
|
||||
provide are available in <filename>/usr/</filename> and <filename>/opt/</filename> and may be
|
||||
accessed.</para>
|
||||
|
||||
<para>Note that there is no concept of enabling/disabling installed system extension images: all
|
||||
installed extension images are automatically activated at boot.</para>
|
||||
|
||||
<para>A simple mechanism for version compatibility is enforced: a system extension image must carry a
|
||||
<filename>/usr/lib/extension-release.d/extension-release.<replaceable>$name</replaceable></filename>
|
||||
file, which must match its image name, that is compared with the host <filename>os-release</filename>
|
||||
file: the contained <varname>ID=</varname> fields have to match, as well as the
|
||||
<varname>SYSEXT_LEVEL=</varname> field (if defined). If the latter is not defined, the
|
||||
<varname>VERSION_ID=</varname> field has to match instead. System extensions should not ship a
|
||||
<filename>/usr/lib/os-release</filename> file (as that would be merged into the host
|
||||
<filename>/usr/</filename> tree, overriding the host OS version data, which is not desirable). The
|
||||
<filename>extension-release</filename> file follows the same format and semantics, and carries the same
|
||||
content, as the <filename>os-release</filename> file of the OS, but it describes the resources carried
|
||||
in the extension image.</para>
|
||||
</refsect1>
|
||||
|
||||
<refsect1>
|
||||
<title>Uses</title>
|
||||
|
||||
<para>The primary use case for system images are immutable environments where debugging and development
|
||||
tools shall optionally be made available, but not included in the immutable base OS image itself
|
||||
(e.g. <filename>strace</filename> and <filename>gdb</filename> shall be an optionally installable
|
||||
addition in order to make debugging/development easier). System extension images should not be
|
||||
misunderstood as a generic software packaging framework, as no dependency scheme is available: system
|
||||
extensions should carry all files they need themselves, except for those already shipped in the
|
||||
underlying host system image. Typically, system extension images are built at the same time as the base
|
||||
OS image — within the same build system.</para>
|
||||
|
||||
<para>Another use case for the system extension concept is temporarily overriding OS supplied resources
|
||||
with newer ones, for example to install a locally compiled development version of some low-level
|
||||
component over the immutable OS image without doing a full OS rebuild or modifying the nominally
|
||||
immutable image. (e.g. "install" a locally built package with <command>DESTDIR=/var/lib/extensions/mytest
|
||||
make install && systemd-sysext --refresh</command>, making it available in
|
||||
<filename>/usr/</filename> as if it was installed in the OS image itself.) This case works regardless if
|
||||
the underlying host <filename>/usr/</filename> is managed as immutable disk image or is a traditional
|
||||
package manager controlled (i.e. writable) tree.</para>
|
||||
</refsect1>
|
||||
|
||||
<refsect1>
|
||||
<title>Commands</title>
|
||||
|
||||
<para>The following command switches are understood:</para>
|
||||
|
||||
<variablelist>
|
||||
<varlistentry>
|
||||
<term><option>--merge</option></term>
|
||||
<term><option>-m</option></term>
|
||||
<listitem><para>Merges all currently installed system extension images into
|
||||
<filename>/usr/</filename> and <filename>/opt/</filename>, by overmounting these hierarchies with an
|
||||
<literal>overlayfs</literal> file system combining the underlying hierarchies with those included in
|
||||
the extension images. This command will fail if the hierarchies are already merged.</para></listitem>
|
||||
</varlistentry>
|
||||
|
||||
<varlistentry>
|
||||
<term><option>--unmerge</option></term>
|
||||
<term><option>-u</option></term>
|
||||
<listitem><para>Unmerges all currently installed system extension images from
|
||||
<filename>/usr/</filename> and <filename>/opt/</filename>, by unmounting the
|
||||
<literal>overlayfs</literal> file systems created by <option>--merge</option>
|
||||
prior.</para></listitem>
|
||||
</varlistentry>
|
||||
|
||||
<varlistentry>
|
||||
<term><option>--refresh</option></term>
|
||||
<term><option>-R</option></term>
|
||||
<listitem><para>A combination of <option>--unmerge</option> and <option>--merge</option>: if already
|
||||
mounted the existing <literal>overlayfs</literal> instance is unmounted temporarily, and then
|
||||
replaced by a new version. This command is useful after installing/removing system extension images,
|
||||
in order to update the <literal>overlayfs</literal> file system accordingly. If no system extensions
|
||||
are installed when this command is executed, the equivalent of <option>--unmerge</option> is
|
||||
executed, without establishing any new <literal>overlayfs</literal> instance. Note that currently
|
||||
there's a brief moment where neither the old nor the new <literal>overlayfs</literal> file system is
|
||||
mounted. This implies that all resources supplied by a system extension will briefly disappear — even
|
||||
if it exists continuously during the refresh operation.</para></listitem>
|
||||
</varlistentry>
|
||||
|
||||
<varlistentry>
|
||||
<term><option>--list</option></term>
|
||||
<term><option>-l</option></term>
|
||||
|
||||
<listitem><para>A brief list of installed extension images is shown.</para></listitem>
|
||||
</varlistentry>
|
||||
|
||||
<xi:include href="standard-options.xml" xpointer="help" />
|
||||
<xi:include href="standard-options.xml" xpointer="version" />
|
||||
</variablelist>
|
||||
|
||||
<para>When invoked without any command switches, the current merge status is shown, separately for both
|
||||
<filename>/usr/</filename> and <filename>/opt/</filename>.</para>
|
||||
</refsect1>
|
||||
|
||||
<refsect1>
|
||||
<title>Options</title>
|
||||
|
||||
<variablelist>
|
||||
<varlistentry>
|
||||
<term><option>--root=</option></term>
|
||||
|
||||
<listitem><para>Operate relative to the specified root directory, i.e. establish the
|
||||
<literal>overlayfs</literal> mount not on the top-level host <filename>/usr/</filename> and
|
||||
<filename>/opt/</filename> hierarchies, but below some specified root directory.</para></listitem>
|
||||
</varlistentry>
|
||||
|
||||
<varlistentry>
|
||||
<term><option>--json=</option></term>
|
||||
|
||||
<listitem><para>Generate JSON output, instead of human readable tabular output. Takes one of
|
||||
<literal>short</literal>, <literal>pretty</literal> or <literal>off</literal> in order to control the
|
||||
output style, or explicitly disabling JSON output.</para></listitem>
|
||||
</varlistentry>
|
||||
|
||||
<xi:include href="standard-options.xml" xpointer="no-pager" />
|
||||
</variablelist>
|
||||
</refsect1>
|
||||
|
||||
<refsect1>
|
||||
<title>Exit status</title>
|
||||
|
||||
<para>On success, 0 is returned.</para>
|
||||
</refsect1>
|
||||
|
||||
<refsect1>
|
||||
<title>See Also</title>
|
||||
<para>
|
||||
<citerefentry><refentrytitle>systemd</refentrytitle><manvolnum>1</manvolnum></citerefentry>,
|
||||
<citerefentry><refentrytitle>systemd-nspawn</refentrytitle><manvolnum>1</manvolnum></citerefentry>
|
||||
</para>
|
||||
</refsect1>
|
||||
|
||||
</refentry>
|
17
meson.build
17
meson.build
@ -1502,6 +1502,7 @@ foreach term : ['analyze',
|
||||
'nss-myhostname',
|
||||
'nss-systemd',
|
||||
'portabled',
|
||||
'sysext',
|
||||
'pstore',
|
||||
'quotacheck',
|
||||
'randomseed',
|
||||
@ -1745,6 +1746,7 @@ subdir('src/portable')
|
||||
subdir('src/pstore')
|
||||
subdir('src/resolve')
|
||||
subdir('src/shutdown')
|
||||
subdir('src/sysext')
|
||||
subdir('src/systemctl')
|
||||
subdir('src/timedate')
|
||||
subdir('src/timesync')
|
||||
@ -2202,6 +2204,17 @@ if conf.get('ENABLE_PORTABLED') == 1
|
||||
install_dir : rootbindir)
|
||||
endif
|
||||
|
||||
if conf.get('ENABLE_SYSEXT') == 1
|
||||
public_programs += executable(
|
||||
'systemd-sysext',
|
||||
systemd_sysext_sources,
|
||||
include_directories : includes,
|
||||
link_with : [libshared],
|
||||
install_rpath : rootlibexecdir,
|
||||
install : true,
|
||||
install_dir : rootlibexecdir)
|
||||
endif
|
||||
|
||||
if conf.get('ENABLE_USERDB') == 1
|
||||
executable(
|
||||
'systemd-userwork',
|
||||
@ -2390,8 +2403,7 @@ if conf.get('HAVE_LIBCRYPTSETUP') == 1
|
||||
libopenssl,
|
||||
libp11kit],
|
||||
install_rpath : rootlibexecdir,
|
||||
install : true,
|
||||
install_dir : bindir)
|
||||
install : true)
|
||||
endif
|
||||
|
||||
if conf.get('HAVE_SYSV_COMPAT') == 1
|
||||
@ -3735,6 +3747,7 @@ foreach tuple : [
|
||||
['logind'],
|
||||
['machined'],
|
||||
['portabled'],
|
||||
['sysext'],
|
||||
['userdb'],
|
||||
['homed'],
|
||||
['importd'],
|
||||
|
@ -111,6 +111,8 @@ option('machined', type : 'boolean',
|
||||
description : 'install the systemd-machined stack')
|
||||
option('portabled', type : 'boolean',
|
||||
description : 'install the systemd-portabled stack')
|
||||
option('sysext', type : 'boolean',
|
||||
description : 'install the systemd-sysext stack')
|
||||
option('userdb', type : 'boolean',
|
||||
description : 'install the systemd-userdbd stack')
|
||||
option('homed', type : 'combo', choices : ['auto', 'true', 'false'],
|
||||
|
@ -66,7 +66,7 @@ static int export_tar(int argc, char *argv[], void *userdata) {
|
||||
int r, fd;
|
||||
|
||||
if (hostname_is_valid(argv[1], 0)) {
|
||||
r = image_find(IMAGE_MACHINE, argv[1], &image);
|
||||
r = image_find(IMAGE_MACHINE, argv[1], NULL, &image);
|
||||
if (r == -ENOENT)
|
||||
return log_error_errno(r, "Machine image %s not found.", argv[1]);
|
||||
if (r < 0)
|
||||
@ -142,7 +142,7 @@ static int export_raw(int argc, char *argv[], void *userdata) {
|
||||
int r, fd;
|
||||
|
||||
if (hostname_is_valid(argv[1], 0)) {
|
||||
r = image_find(IMAGE_MACHINE, argv[1], &image);
|
||||
r = image_find(IMAGE_MACHINE, argv[1], NULL, &image);
|
||||
if (r == -ENOENT)
|
||||
return log_error_errno(r, "Machine image %s not found.", argv[1]);
|
||||
if (r < 0)
|
||||
|
@ -132,7 +132,7 @@ static int import_fs(int argc, char *argv[], void *userdata) {
|
||||
local);
|
||||
|
||||
if (!arg_force) {
|
||||
r = image_find(IMAGE_MACHINE, local, NULL);
|
||||
r = image_find(IMAGE_MACHINE, local, NULL, NULL);
|
||||
if (r < 0) {
|
||||
if (r != -ENOENT)
|
||||
return log_error_errno(r, "Failed to check whether image '%s' exists: %m", local);
|
||||
|
@ -70,7 +70,7 @@ static int import_tar(int argc, char *argv[], void *userdata) {
|
||||
local);
|
||||
|
||||
if (!arg_force) {
|
||||
r = image_find(IMAGE_MACHINE, local, NULL);
|
||||
r = image_find(IMAGE_MACHINE, local, NULL, NULL);
|
||||
if (r < 0) {
|
||||
if (r != -ENOENT)
|
||||
return log_error_errno(r, "Failed to check whether image '%s' exists: %m", local);
|
||||
@ -165,7 +165,7 @@ static int import_raw(int argc, char *argv[], void *userdata) {
|
||||
local);
|
||||
|
||||
if (!arg_force) {
|
||||
r = image_find(IMAGE_MACHINE, local, NULL);
|
||||
r = image_find(IMAGE_MACHINE, local, NULL, NULL);
|
||||
if (r < 0) {
|
||||
if (r != -ENOENT)
|
||||
return log_error_errno(r, "Failed to check whether image '%s' exists: %m", local);
|
||||
|
@ -78,7 +78,7 @@ static int pull_tar(int argc, char *argv[], void *userdata) {
|
||||
local);
|
||||
|
||||
if (!arg_force) {
|
||||
r = image_find(IMAGE_MACHINE, local, NULL);
|
||||
r = image_find(IMAGE_MACHINE, local, NULL, NULL);
|
||||
if (r < 0) {
|
||||
if (r != -ENOENT)
|
||||
return log_error_errno(r, "Failed to check whether image '%s' exists: %m", local);
|
||||
@ -164,7 +164,7 @@ static int pull_raw(int argc, char *argv[], void *userdata) {
|
||||
local);
|
||||
|
||||
if (!arg_force) {
|
||||
r = image_find(IMAGE_MACHINE, local, NULL);
|
||||
r = image_find(IMAGE_MACHINE, local, NULL, NULL);
|
||||
if (r < 0) {
|
||||
if (r != -ENOENT)
|
||||
return log_error_errno(r, "Failed to check whether image '%s' exists: %m", local);
|
||||
|
@ -408,7 +408,7 @@ static int image_object_find(sd_bus *bus, const char *path, const char *interfac
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
r = image_find(IMAGE_MACHINE, e, &image);
|
||||
r = image_find(IMAGE_MACHINE, e, NULL, &image);
|
||||
if (r == -ENOENT)
|
||||
return 0;
|
||||
if (r < 0)
|
||||
@ -452,7 +452,7 @@ static int image_node_enumerator(sd_bus *bus, const char *path, void *userdata,
|
||||
if (!images)
|
||||
return -ENOMEM;
|
||||
|
||||
r = image_discover(IMAGE_MACHINE, images);
|
||||
r = image_discover(IMAGE_MACHINE, NULL, images);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
|
@ -124,7 +124,7 @@ static int method_get_image(sd_bus_message *message, void *userdata, sd_bus_erro
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
r = image_find(IMAGE_MACHINE, name, NULL);
|
||||
r = image_find(IMAGE_MACHINE, name, NULL, NULL);
|
||||
if (r == -ENOENT)
|
||||
return sd_bus_error_setf(error, BUS_ERROR_NO_SUCH_IMAGE, "No image '%s' known", name);
|
||||
if (r < 0)
|
||||
@ -480,7 +480,7 @@ static int method_list_images(sd_bus_message *message, void *userdata, sd_bus_er
|
||||
if (!images)
|
||||
return -ENOMEM;
|
||||
|
||||
r = image_discover(IMAGE_MACHINE, images);
|
||||
r = image_discover(IMAGE_MACHINE, NULL, images);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
@ -562,7 +562,7 @@ static int redirect_method_to_image(sd_bus_message *message, Manager *m, sd_bus_
|
||||
if (!image_name_is_valid(name))
|
||||
return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Image name '%s' is invalid.", name);
|
||||
|
||||
r = image_find(IMAGE_MACHINE, name, &i);
|
||||
r = image_find(IMAGE_MACHINE, name, NULL, &i);
|
||||
if (r == -ENOENT)
|
||||
return sd_bus_error_setf(error, BUS_ERROR_NO_SUCH_IMAGE, "No image '%s' known", name);
|
||||
if (r < 0)
|
||||
@ -755,7 +755,7 @@ static int method_clean_pool(sd_bus_message *message, void *userdata, sd_bus_err
|
||||
goto child_fail;
|
||||
}
|
||||
|
||||
r = image_discover(IMAGE_MACHINE, images);
|
||||
r = image_discover(IMAGE_MACHINE, NULL, images);
|
||||
if (r < 0)
|
||||
goto child_fail;
|
||||
|
||||
|
@ -2940,7 +2940,7 @@ static int determine_names(void) {
|
||||
if (arg_machine) {
|
||||
_cleanup_(image_unrefp) Image *i = NULL;
|
||||
|
||||
r = image_find(IMAGE_MACHINE, arg_machine, &i);
|
||||
r = image_find(IMAGE_MACHINE, arg_machine, NULL, &i);
|
||||
if (r == -ENOENT)
|
||||
return log_error_errno(r, "No image for machine '%s'.", arg_machine);
|
||||
if (r < 0)
|
||||
|
@ -495,7 +495,7 @@ int portable_extract(
|
||||
|
||||
assert(name_or_path);
|
||||
|
||||
r = image_find_harder(IMAGE_PORTABLE, name_or_path, &image);
|
||||
r = image_find_harder(IMAGE_PORTABLE, name_or_path, NULL, &image);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
@ -953,7 +953,7 @@ static int install_image_symlink(
|
||||
/* If the image is outside of the image search also link it into it, so that it can be found with short image
|
||||
* names and is listed among the images. */
|
||||
|
||||
if (image_in_search_path(IMAGE_PORTABLE, image_path))
|
||||
if (image_in_search_path(IMAGE_PORTABLE, NULL, image_path))
|
||||
return 0;
|
||||
|
||||
r = image_symlink(image_path, flags, &sl);
|
||||
@ -987,7 +987,7 @@ int portable_attach(
|
||||
|
||||
assert(name_or_path);
|
||||
|
||||
r = image_find_harder(IMAGE_PORTABLE, name_or_path, &image);
|
||||
r = image_find_harder(IMAGE_PORTABLE, name_or_path, NULL, &image);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
@ -1193,7 +1193,7 @@ int portable_detach(
|
||||
return log_debug_errno(r, "Failed to add unit name '%s' to set: %m", de->d_name);
|
||||
|
||||
if (path_is_absolute(marker) &&
|
||||
!image_in_search_path(IMAGE_PORTABLE, marker)) {
|
||||
!image_in_search_path(IMAGE_PORTABLE, NULL, marker)) {
|
||||
|
||||
r = set_ensure_consume(&markers, &path_hash_ops_free, TAKE_PTR(marker));
|
||||
if (r < 0)
|
||||
|
@ -606,7 +606,7 @@ int bus_image_acquire(
|
||||
if (image_name_is_valid(name_or_path)) {
|
||||
|
||||
/* If it's a short name, let's search for it */
|
||||
r = image_find(IMAGE_PORTABLE, name_or_path, &loaded);
|
||||
r = image_find(IMAGE_PORTABLE, name_or_path, NULL, &loaded);
|
||||
if (r == -ENOENT)
|
||||
return sd_bus_error_setf(error, BUS_ERROR_NO_SUCH_PORTABLE_IMAGE, "No image '%s' found.", name_or_path);
|
||||
|
||||
|
@ -92,7 +92,7 @@ int manager_image_cache_discover(Manager *m, Hashmap *images, sd_bus_error *erro
|
||||
/* A wrapper around image_discover() (for finding images in search path) and portable_discover_attached() (for
|
||||
* finding attached images). */
|
||||
|
||||
r = image_discover(IMAGE_PORTABLE, images);
|
||||
r = image_discover(IMAGE_PORTABLE, NULL, images);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
|
@ -42,18 +42,24 @@
|
||||
#include "xattr-util.h"
|
||||
|
||||
static const char* const image_search_path[_IMAGE_CLASS_MAX] = {
|
||||
[IMAGE_MACHINE] = "/etc/machines\0" /* only place symlinks here */
|
||||
"/run/machines\0" /* and here too */
|
||||
"/var/lib/machines\0" /* the main place for images */
|
||||
"/var/lib/container\0" /* legacy */
|
||||
"/usr/local/lib/machines\0"
|
||||
"/usr/lib/machines\0",
|
||||
[IMAGE_MACHINE] = "/etc/machines\0" /* only place symlinks here */
|
||||
"/run/machines\0" /* and here too */
|
||||
"/var/lib/machines\0" /* the main place for images */
|
||||
"/var/lib/container\0" /* legacy */
|
||||
"/usr/local/lib/machines\0"
|
||||
"/usr/lib/machines\0",
|
||||
|
||||
[IMAGE_PORTABLE] = "/etc/portables\0" /* only place symlinks here */
|
||||
"/run/portables\0" /* and here too */
|
||||
"/var/lib/portables\0" /* the main place for images */
|
||||
"/usr/local/lib/portables\0"
|
||||
"/usr/lib/portables\0",
|
||||
[IMAGE_PORTABLE] = "/etc/portables\0" /* only place symlinks here */
|
||||
"/run/portables\0" /* and here too */
|
||||
"/var/lib/portables\0" /* the main place for images */
|
||||
"/usr/local/lib/portables\0"
|
||||
"/usr/lib/portables\0",
|
||||
|
||||
[IMAGE_EXTENSION] = "/etc/extensions\0" /* only place symlinks here */
|
||||
"/run/extensions\0" /* and here too */
|
||||
"/var/lib/extensions\0" /* the main place for images */
|
||||
"/usr/local/lib/extensions\0"
|
||||
"/usr/lib/extensions\0",
|
||||
};
|
||||
|
||||
static Image *image_free(Image *i) {
|
||||
@ -415,7 +421,11 @@ static int image_make(
|
||||
return -EMEDIUMTYPE;
|
||||
}
|
||||
|
||||
int image_find(ImageClass class, const char *name, Image **ret) {
|
||||
int image_find(ImageClass class,
|
||||
const char *name,
|
||||
const char *root,
|
||||
Image **ret) {
|
||||
|
||||
const char *path;
|
||||
int r;
|
||||
|
||||
@ -428,20 +438,22 @@ int image_find(ImageClass class, const char *name, Image **ret) {
|
||||
return -ENOENT;
|
||||
|
||||
NULSTR_FOREACH(path, image_search_path[class]) {
|
||||
_cleanup_free_ char *resolved = NULL;
|
||||
_cleanup_closedir_ DIR *d = NULL;
|
||||
struct stat st;
|
||||
int flags;
|
||||
|
||||
d = opendir(path);
|
||||
if (!d) {
|
||||
if (errno == ENOENT)
|
||||
continue;
|
||||
r = chase_symlinks_and_opendir(path, root, CHASE_PREFIX_ROOT, &resolved, &d);
|
||||
if (r == -ENOENT)
|
||||
continue;
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
return -errno;
|
||||
}
|
||||
|
||||
/* As mentioned above, we follow symlinks on this fstatat(), because we want to permit people to
|
||||
* symlink block devices into the search path */
|
||||
if (fstatat(dirfd(d), name, &st, 0) < 0) {
|
||||
/* As mentioned above, we follow symlinks on this fstatat(), because we want to permit people
|
||||
* to symlink block devices into the search path. (For now, we disable that when operating
|
||||
* relative to some root directory.) */
|
||||
flags = root ? AT_SYMLINK_NOFOLLOW : 0;
|
||||
if (fstatat(dirfd(d), name, &st, flags) < 0) {
|
||||
_cleanup_free_ char *raw = NULL;
|
||||
|
||||
if (errno != ENOENT)
|
||||
@ -451,8 +463,7 @@ int image_find(ImageClass class, const char *name, Image **ret) {
|
||||
if (!raw)
|
||||
return -ENOMEM;
|
||||
|
||||
if (fstatat(dirfd(d), raw, &st, 0) < 0) {
|
||||
|
||||
if (fstatat(dirfd(d), raw, &st, flags) < 0) {
|
||||
if (errno == ENOENT)
|
||||
continue;
|
||||
|
||||
@ -462,13 +473,13 @@ int image_find(ImageClass class, const char *name, Image **ret) {
|
||||
if (!S_ISREG(st.st_mode))
|
||||
continue;
|
||||
|
||||
r = image_make(name, dirfd(d), path, raw, &st, ret);
|
||||
r = image_make(name, dirfd(d), resolved, raw, &st, ret);
|
||||
|
||||
} else {
|
||||
if (!S_ISDIR(st.st_mode) && !S_ISBLK(st.st_mode))
|
||||
continue;
|
||||
|
||||
r = image_make(name, dirfd(d), path, name, &st, ret);
|
||||
r = image_make(name, dirfd(d), resolved, name, &st, ret);
|
||||
}
|
||||
if (IN_SET(r, -ENOENT, -EMEDIUMTYPE))
|
||||
continue;
|
||||
@ -482,7 +493,7 @@ int image_find(ImageClass class, const char *name, Image **ret) {
|
||||
}
|
||||
|
||||
if (class == IMAGE_MACHINE && streq(name, ".host")) {
|
||||
r = image_make(".host", AT_FDCWD, NULL, "/", NULL, ret);
|
||||
r = image_make(".host", AT_FDCWD, NULL, empty_to_root(root), NULL, ret);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
@ -507,14 +518,18 @@ int image_from_path(const char *path, Image **ret) {
|
||||
return image_make(NULL, AT_FDCWD, NULL, path, NULL, ret);
|
||||
}
|
||||
|
||||
int image_find_harder(ImageClass class, const char *name_or_path, Image **ret) {
|
||||
int image_find_harder(ImageClass class, const char *name_or_path, const char *root, Image **ret) {
|
||||
if (image_name_is_valid(name_or_path))
|
||||
return image_find(class, name_or_path, ret);
|
||||
return image_find(class, name_or_path, root, ret);
|
||||
|
||||
return image_from_path(name_or_path, ret);
|
||||
}
|
||||
|
||||
int image_discover(ImageClass class, Hashmap *h) {
|
||||
int image_discover(
|
||||
ImageClass class,
|
||||
const char *root,
|
||||
Hashmap *h) {
|
||||
|
||||
const char *path;
|
||||
int r;
|
||||
|
||||
@ -523,29 +538,30 @@ int image_discover(ImageClass class, Hashmap *h) {
|
||||
assert(h);
|
||||
|
||||
NULSTR_FOREACH(path, image_search_path[class]) {
|
||||
_cleanup_free_ char *resolved = NULL;
|
||||
_cleanup_closedir_ DIR *d = NULL;
|
||||
struct dirent *de;
|
||||
|
||||
d = opendir(path);
|
||||
if (!d) {
|
||||
if (errno == ENOENT)
|
||||
continue;
|
||||
|
||||
return -errno;
|
||||
}
|
||||
r = chase_symlinks_and_opendir(path, root, CHASE_PREFIX_ROOT, &resolved, &d);
|
||||
if (r == -ENOENT)
|
||||
continue;
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
FOREACH_DIRENT_ALL(de, d, return -errno) {
|
||||
_cleanup_(image_unrefp) Image *image = NULL;
|
||||
_cleanup_free_ char *truncated = NULL;
|
||||
const char *pretty;
|
||||
struct stat st;
|
||||
int flags;
|
||||
|
||||
if (dot_or_dot_dot(de->d_name))
|
||||
continue;
|
||||
|
||||
/* As mentioned above, we follow symlinks on this fstatat(), because we want to permit people
|
||||
* to symlink block devices into the search path */
|
||||
if (fstatat(dirfd(d), de->d_name, &st, 0) < 0) {
|
||||
/* As mentioned above, we follow symlinks on this fstatat(), because we want to
|
||||
* permit people to symlink block devices into the search path. */
|
||||
flags = root ? AT_SYMLINK_NOFOLLOW : 0;
|
||||
if (fstatat(dirfd(d), de->d_name, &st, flags) < 0) {
|
||||
if (errno == ENOENT)
|
||||
continue;
|
||||
|
||||
@ -575,7 +591,7 @@ int image_discover(ImageClass class, Hashmap *h) {
|
||||
if (hashmap_contains(h, pretty))
|
||||
continue;
|
||||
|
||||
r = image_make(pretty, dirfd(d), path, de->d_name, &st, &image);
|
||||
r = image_make(pretty, dirfd(d), resolved, de->d_name, &st, &image);
|
||||
if (IN_SET(r, -ENOENT, -EMEDIUMTYPE))
|
||||
continue;
|
||||
if (r < 0)
|
||||
@ -594,7 +610,7 @@ int image_discover(ImageClass class, Hashmap *h) {
|
||||
if (class == IMAGE_MACHINE && !hashmap_contains(h, ".host")) {
|
||||
_cleanup_(image_unrefp) Image *image = NULL;
|
||||
|
||||
r = image_make(".host", AT_FDCWD, NULL, "/", NULL, &image);
|
||||
r = image_make(".host", AT_FDCWD, NULL, empty_to_root("/"), NULL, &image);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
@ -737,7 +753,7 @@ int image_rename(Image *i, const char *new_name) {
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
r = image_find(IMAGE_MACHINE, new_name, NULL);
|
||||
r = image_find(IMAGE_MACHINE, new_name, NULL, NULL);
|
||||
if (r >= 0)
|
||||
return -EEXIST;
|
||||
if (r != -ENOENT)
|
||||
@ -850,7 +866,7 @@ int image_clone(Image *i, const char *new_name, bool read_only) {
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
r = image_find(IMAGE_MACHINE, new_name, NULL);
|
||||
r = image_find(IMAGE_MACHINE, new_name, NULL, NULL);
|
||||
if (r >= 0)
|
||||
return -EEXIST;
|
||||
if (r != -ENOENT)
|
||||
@ -1242,16 +1258,27 @@ bool image_name_is_valid(const char *s) {
|
||||
return true;
|
||||
}
|
||||
|
||||
bool image_in_search_path(ImageClass class, const char *image) {
|
||||
bool image_in_search_path(
|
||||
ImageClass class,
|
||||
const char *root,
|
||||
const char *image) {
|
||||
|
||||
const char *path;
|
||||
|
||||
assert(image);
|
||||
|
||||
NULSTR_FOREACH(path, image_search_path[class]) {
|
||||
const char *p;
|
||||
const char *p, *q;
|
||||
size_t k;
|
||||
|
||||
p = path_startswith(image, path);
|
||||
if (!empty_or_root(root)) {
|
||||
q = path_startswith(path, root);
|
||||
if (!q)
|
||||
continue;
|
||||
} else
|
||||
q = path;
|
||||
|
||||
p = path_startswith(q, path);
|
||||
if (!p)
|
||||
continue;
|
||||
|
||||
|
@ -16,6 +16,7 @@
|
||||
typedef enum ImageClass {
|
||||
IMAGE_MACHINE,
|
||||
IMAGE_PORTABLE,
|
||||
IMAGE_EXTENSION,
|
||||
_IMAGE_CLASS_MAX,
|
||||
_IMAGE_CLASS_INVALID = -1
|
||||
} ImageClass;
|
||||
@ -61,10 +62,10 @@ Image *image_ref(Image *i);
|
||||
|
||||
DEFINE_TRIVIAL_CLEANUP_FUNC(Image*, image_unref);
|
||||
|
||||
int image_find(ImageClass class, const char *name, Image **ret);
|
||||
int image_find(ImageClass class, const char *root, const char *name, Image **ret);
|
||||
int image_from_path(const char *path, Image **ret);
|
||||
int image_find_harder(ImageClass class, const char *name_or_path, Image **ret);
|
||||
int image_discover(ImageClass class, Hashmap *map);
|
||||
int image_find_harder(ImageClass class, const char *root, const char *name_or_path, Image **ret);
|
||||
int image_discover(ImageClass class, const char *root, Hashmap *map);
|
||||
|
||||
int image_remove(Image *i);
|
||||
int image_rename(Image *i, const char *new_name);
|
||||
@ -83,7 +84,7 @@ int image_set_limit(Image *i, uint64_t referenced_max);
|
||||
|
||||
int image_read_metadata(Image *i);
|
||||
|
||||
bool image_in_search_path(ImageClass class, const char *image);
|
||||
bool image_in_search_path(ImageClass class, const char *root, const char *image);
|
||||
|
||||
static inline bool IMAGE_IS_HIDDEN(const struct Image *i) {
|
||||
assert(i);
|
||||
|
@ -6,6 +6,7 @@
|
||||
#include "fileio.h"
|
||||
#include "fs-util.h"
|
||||
#include "macro.h"
|
||||
#include "machine-image.h"
|
||||
#include "os-util.h"
|
||||
#include "string-util.h"
|
||||
#include "strv.h"
|
||||
@ -31,17 +32,31 @@ int path_is_os_tree(const char *path) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
int open_os_release(const char *root, char **ret_path, int *ret_fd) {
|
||||
int open_extension_release(const char *root, const char *extension, char **ret_path, int *ret_fd) {
|
||||
_cleanup_free_ char *q = NULL;
|
||||
const char *p;
|
||||
int r, fd;
|
||||
|
||||
FOREACH_STRING(p, "/etc/os-release", "/usr/lib/os-release") {
|
||||
r = chase_symlinks(p, root, CHASE_PREFIX_ROOT,
|
||||
ret_path ? &q : NULL,
|
||||
ret_fd ? &fd : NULL);
|
||||
if (r != -ENOENT)
|
||||
break;
|
||||
if (extension) {
|
||||
const char *extension_full_path;
|
||||
|
||||
if (!image_name_is_valid(extension))
|
||||
return log_debug_errno(SYNTHETIC_ERRNO(EINVAL),
|
||||
"The extension name %s is invalid.", extension);
|
||||
|
||||
extension_full_path = strjoina("/usr/lib/extension-release.d/extension-release.", extension);
|
||||
r = chase_symlinks(extension_full_path, root, CHASE_PREFIX_ROOT,
|
||||
ret_path ? &q : NULL,
|
||||
ret_fd ? &fd : NULL);
|
||||
} else {
|
||||
const char *p;
|
||||
|
||||
FOREACH_STRING(p, "/etc/os-release", "/usr/lib/os-release") {
|
||||
r = chase_symlinks(p, root, CHASE_PREFIX_ROOT,
|
||||
ret_path ? &q : NULL,
|
||||
ret_fd ? &fd : NULL);
|
||||
if (r != -ENOENT)
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (r < 0)
|
||||
return r;
|
||||
@ -64,16 +79,16 @@ int open_os_release(const char *root, char **ret_path, int *ret_fd) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
int fopen_os_release(const char *root, char **ret_path, FILE **ret_file) {
|
||||
int fopen_extension_release(const char *root, const char *extension, char **ret_path, FILE **ret_file) {
|
||||
_cleanup_free_ char *p = NULL;
|
||||
_cleanup_close_ int fd = -1;
|
||||
FILE *f;
|
||||
int r;
|
||||
|
||||
if (!ret_file)
|
||||
return open_os_release(root, ret_path, NULL);
|
||||
return open_extension_release(root, extension, ret_path, NULL);
|
||||
|
||||
r = open_os_release(root, ret_path ? &p : NULL, &fd);
|
||||
r = open_extension_release(root, extension, ret_path ? &p : NULL, &fd);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
@ -89,18 +104,35 @@ int fopen_os_release(const char *root, char **ret_path, FILE **ret_file) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
int parse_os_release(const char *root, ...) {
|
||||
static int parse_release_internal(const char *root, const char *extension, va_list ap) {
|
||||
_cleanup_fclose_ FILE *f = NULL;
|
||||
_cleanup_free_ char *p = NULL;
|
||||
va_list ap;
|
||||
int r;
|
||||
|
||||
r = fopen_os_release(root, &p, &f);
|
||||
r = fopen_extension_release(root, extension, &p, &f);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
return parse_env_filev(f, p, ap);
|
||||
}
|
||||
|
||||
int parse_extension_release(const char *root, const char *extension, ...) {
|
||||
va_list ap;
|
||||
int r;
|
||||
|
||||
va_start(ap, extension);
|
||||
r = parse_release_internal(root, extension, ap);
|
||||
va_end(ap);
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
int parse_os_release(const char *root, ...) {
|
||||
va_list ap;
|
||||
int r;
|
||||
|
||||
va_start(ap, root);
|
||||
r = parse_env_filev(f, p, ap);
|
||||
r = parse_release_internal(root, NULL, ap);
|
||||
va_end(ap);
|
||||
|
||||
return r;
|
||||
|
@ -5,9 +5,19 @@
|
||||
|
||||
int path_is_os_tree(const char *path);
|
||||
|
||||
int open_os_release(const char *root, char **ret_path, int *ret_fd);
|
||||
int fopen_os_release(const char *root, char **ret_path, FILE **ret_file);
|
||||
/* The *_extension_release flavours will look for /usr/lib/extension-release/extension-release.NAME
|
||||
* in accordance with the OS extension specification, rather than for /usr/lib/ or /etc/os-release. */
|
||||
int open_extension_release(const char *root, const char *extension, char **ret_path, int *ret_fd);
|
||||
static inline int open_os_release(const char *root, char **ret_path, int *ret_fd) {
|
||||
return open_extension_release(root, NULL, ret_path, ret_fd);
|
||||
}
|
||||
|
||||
int fopen_extension_release(const char *root, const char *extension, char **ret_path, FILE **ret_file);
|
||||
static inline int fopen_os_release(const char *root, char **ret_path, FILE **ret_file) {
|
||||
return fopen_extension_release(root, NULL, ret_path, ret_file);
|
||||
}
|
||||
|
||||
int parse_extension_release(const char *root, const char *extension, ...) _sentinel_;
|
||||
int parse_os_release(const char *root, ...) _sentinel_;
|
||||
int load_os_release_pairs(const char *root, char ***ret);
|
||||
int load_os_release_pairs_with_prefix(const char *root, const char *prefix, char ***ret);
|
||||
|
5
src/sysext/meson.build
Normal file
5
src/sysext/meson.build
Normal file
@ -0,0 +1,5 @@
|
||||
# SPDX-License-Identifier: LGPL-2.1-or-later
|
||||
|
||||
systemd_sysext_sources = files('''
|
||||
sysext.c
|
||||
'''.split())
|
1018
src/sysext/sysext.c
Normal file
1018
src/sysext/sysext.c
Normal file
File diff suppressed because it is too large
Load Diff
@ -933,7 +933,7 @@ install_execs() {
|
||||
# some {rc,halt}.local scripts and programs are okay to not exist, the rest should
|
||||
# also, plymouth is pulled in by rescue.service, but even there the exit code
|
||||
# is ignored; as it's not present on some distros, don't fail if it doesn't exist
|
||||
dinfo "Attempting to install $i"
|
||||
dinfo "Attempting to install $i (based on unit file reference)"
|
||||
inst $i || [ "${i%.local}" != "$i" ] || [ "${i%systemd-update-done}" != "$i" ] || [ "${i##*/}" == "plymouth" ]
|
||||
done
|
||||
)
|
||||
|
@ -211,6 +211,7 @@ in_units = [
|
||||
['systemd-oomd.service', 'ENABLE_OOMD'],
|
||||
['systemd-portabled.service', 'ENABLE_PORTABLED',
|
||||
'dbus-org.freedesktop.portable1.service'],
|
||||
['systemd-sysext.service', 'ENABLE_SYSEXT'],
|
||||
['systemd-userdbd.service', 'ENABLE_USERDB'],
|
||||
['systemd-homed.service', 'ENABLE_HOMED'],
|
||||
['systemd-quotacheck.service', 'ENABLE_QUOTACHECK'],
|
||||
|
31
units/systemd-sysext.service.in
Normal file
31
units/systemd-sysext.service.in
Normal file
@ -0,0 +1,31 @@
|
||||
# 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=Merge System Extension Images into /usr/ and /opt/
|
||||
Documentation=man:systemd-sysext.service(8)
|
||||
DefaultDependencies=no
|
||||
Conflicts=shutdown.target
|
||||
After=local-fs.target
|
||||
Before=sysinit.target shutdown.target systemd-tmpfiles.service
|
||||
ConditionCapability=CAP_SYS_ADMIN
|
||||
ConditionDirectoryNotEmpty=|/etc/extensions
|
||||
ConditionDirectoryNotEmpty=|/run/extensions
|
||||
ConditionDirectoryNotEmpty=|/var/lib/extensions
|
||||
ConditionDirectoryNotEmpty=|/usr/local/lib/extensions
|
||||
ConditionDirectoryNotEmpty=|/usr/lib/extensions
|
||||
|
||||
[Service]
|
||||
Type=oneshot
|
||||
RemainAfterExit=yes
|
||||
ExecStart=@rootlibexecdir@/systemd-sysext --merge
|
||||
ExecStop=@rootlibexecdir@/systemd-sysext --unmerge
|
||||
|
||||
[Install]
|
||||
WantedBy=sysinit.target
|
Loading…
x
Reference in New Issue
Block a user