steps:
- uses: actions/checkout@8e5e7e5ab8b370d6c329ec480221332ada57f0ab
- - uses: systemd/mkosi@e59f763872e4d5f96acb11b4a77995b986bb31cc
+ - uses: systemd/mkosi@87a6b70ea9ab529b95fc91306fc8151175999dca
- name: Configure
run: |
run: mkosi --debug
- name: Boot ${{ matrix.distro }} systemd-nspawn
- run: sudo mkosi --debug boot
+ run: test "$(sudo mkosi --debug boot 1>&2; echo $?)" -eq 123
- name: Boot ${{ matrix.distro }} QEMU
- run: timeout -k 30 10m mkosi --debug qemu
-
- # vsock in Github Actions with qemu is broken so for now we check for failures manually.
- - name: Check ${{ matrix.distro }} QEMU
- run: sudo mkosi shell bash -c "[[ -e /testok ]] || { cat /failed-services; exit 1; }"
+ run: timeout -k 30 10m test "$(mkosi --debug qemu 1>&2; echo $?)" -eq 123
steps:
- name: Checkout code
- uses: actions/checkout@8e5e7e5ab8b370d6c329ec480221332ada57f0ab # tag=v3.0.0
+ uses: actions/checkout@8e5e7e5ab8b370d6c329ec480221332ada57f0ab # v3.5.2
with:
persist-credentials: false
- name: Run analysis
- uses: ossf/scorecard-action@80e868c13c90f172d68d1f4501dee99e2479f7af # tag=v2.1.3
+ uses: ossf/scorecard-action@80e868c13c90f172d68d1f4501dee99e2479f7af # v2.1.3
with:
results_file: results.sarif
results_format: sarif
# Upload the results as artifacts (optional). Commenting out will disable uploads of run results in SARIF
# format to the repository Actions tab.
- name: Upload artifact
- uses: actions/upload-artifact@0b7f8abb1508181956e8e162db84b466c27e18ce # tag=v3.0.0
+ uses: actions/upload-artifact@0b7f8abb1508181956e8e162db84b466c27e18ce # v3.1.2
with:
name: SARIF file
path: results.sarif
# Upload the results to GitHub's code scanning dashboard.
- name: Upload to code-scanning
if: github.event_name != 'pull_request'
- uses: github/codeql-action/upload-sarif@0225834cc549ee0ca93cb085b92954821a145866 # tag=v1.0.26
+ uses: github/codeql-action/upload-sarif@0225834cc549ee0ca93cb085b92954821a145866 # v2.20.0
with:
sarif_file: results.sarif
Features:
+* in sd-stub: optionally add support for a new PE section .keyring or so that
+ contains additional certificates to include in the Mok keyring, extending
+ what shim might have placed there. why? let's say I use "ukify" to build +
+ sign my own fedora-based UKIs, and only enroll my personal lennart key via
+ shim. Then, I want to include the fedora keyring in it, so that kmods work.
+ But I might not want to enroll the fedora key in shim, because this would
+ also mean that the key would be in effect whenever I boot an archlinux UKI
+ built the same way, signed with the same lennart key.
+
+* resolved: take possession of some IPv6 ULA address (let's say
+ fd00:5353:5353:5353:5353:5353:5353:5353), and listen on port 53 on it for the
+ local stubs, so that we can make the stub available via ipv6 too.
+
* introduce a .microcode PE section for sd-stub which we'll pass as first initrd
to the kernel which will then upload it to the CPU. This should be distinct
from .initrd to guarantee right ordering. also, and maybe more importantly
the OS.
@TRIMMED_BYTES@ of memory were returned to the OS, which took @TRIMMED_USEC@
-micro-seconds (µs).
+micro-seconds (μs).
`$container_host_version_id=10`
5. systemd supports passing immutable binary data blobs with limited size and
- restricted access to services via the `LoadCredential=` and `SetCredential=`
- settings. The same protocol may be used to pass credentials from the
- container manager to systemd itself. The credential data should be placed in
- some location (ideally a read-only and non-swappable file system, like
- 'ramfs'), and the absolute path to this directory exported in the
+ restricted access to services via the `ImportCredential=`, `LoadCredential=`
+ and `SetCredential=` settings. The same protocol may be used to pass credentials
+ from the container manager to systemd itself. The credential data should be
+ placed in some location (ideally a read-only and non-swappable file system,
+ like 'ramfs'), and the absolute path to this directory exported in the
`$CREDENTIALS_DIRECTORY` environment variable. If the container managers
does this, the credentials passed to the service manager can be propagated
- to services via `LoadCredential=` (see ...). The container manager can
- choose any path, but `/run/host/credentials` is recommended.
+ to services via `LoadCredential=` or `ImportCredential=` (see ...). The
+ container manager can choose any path, but `/run/host/credentials` is
+ recommended.
## Advanced Integration
1. `LoadCredential=` may be used to load a credential from disk, from an
`AF_UNIX` socket, or propagate them from a system credential.
+2. `ImportCredential=` may be used to load one or more (encrypted) credentials
+ from disk or from the credential stores.
+
2. `SetCredential=` may be used to set a credential to a literal string encoded
in the unit file. Because unit files are world-readable (both on disk and
via D-Bus), this should only be used for credentials that aren't sensitive,
Or propagated to services further down:
```
-systemd-run -p LoadCredential=mycred -P --wait systemd-creds cat mycred
+systemd-run -p ImportCredential=mycred -P --wait systemd-creds cat mycred
```
## Well-Known Credentials
credentials that must be decrypted/validated before use, such as those from
`systemd-stub`).
-The `LoadCredential=` and `LoadCredentialEncrypted=` settings when configured
-with a relative source path will search for the source file to read the
-credential from automatically. Primarily, these credentials are searched among
-the credentials passed into the system. If not found there, they are searched
-in `/etc/credstore/`, `/run/credstore/`,
+The `ImportCredential=` setting (and the `LoadCredential=` and
+`LoadCredentialEncrypted=` settings when configured with a relative source path)
+will search for the source file to read the credential from automatically. Primarily,
+these credentials are searched among the credentials passed into the system. If
+not found there, they are searched in `/etc/credstore/`, `/run/credstore/`,
`/usr/lib/credstore/`. `LoadCredentialEncrypted=` will also search
-`/etc/credstore.encrypted/` and similar directories. These directories are
+`/etc/credstore.encrypted/` and similar directories. `ImportCredential` will search
+both the non-encrypted and encrypted directories. These directories are
hence a great place to store credentials to load on the system.
## Conditionalizing Services
evdev:name:Intel HID events:dmi:bvn*:bvr*:bd*:svnHP*:pnHPEliteDragonflyG2*:pvr*
KEYBOARD_KEY_08=unknown # rfkill is also reported by HP Wireless hotkeys
+# HP Elite Dragonfly G3
+evdev:atkbd:dmi:bvn*:bvr*:bd*:svnHP:pnHPEliteDragonfly13.5inchG3NotebookPC:pvr*
+ KEYBOARD_KEY_c9=up
+ KEYBOARD_KEY_d1=down
+ KEYBOARD_KEY_c8=pageup
+ KEYBOARD_KEY_d0=pagedown
+
##########################################################
# Huawei
##########################################################
<varlistentry>
<term><option>random-seed</option></term>
- <listitem><para>Generates a random seed and stores it in the EFI System Partition, for use by the
- <command>systemd-boot</command> boot loader. Also, generates a random 'system token' and stores it
- persistently as an EFI variable, if one has not been set before. If the boot loader finds the random
- seed in the ESP and the system token in the EFI variable it will derive a random seed to pass to the
- OS and a new seed to store in the ESP from the combination of both. The random seed passed to the OS
- is credited to the kernel's entropy pool by the system manager during early boot, and permits
- userspace to boot up with an entropy pool fully initialized very early on. Also see
+ <listitem><para>Generates a random seed and stores it in the EFI System Partition (ESP), for use by
+ the <command>systemd-boot</command> boot loader. If a random seed already exists in the ESP it is
+ refreshed. Also generates a random 'system token' and stores it persistently as an EFI variable, if
+ one has not been set before. If the boot loader finds the random seed in the ESP and the system token
+ in the EFI variable it will derive a random seed to pass to the OS and a new seed to store in the ESP
+ from the combination of both. The random seed passed to the OS is credited to the kernel's entropy
+ pool by the system manager during early boot, and permits userspace to boot up with an entropy pool
+ fully initialized very early on. Also see
<citerefentry><refentrytitle>systemd-boot-random-seed.service</refentrytitle><manvolnum>8</manvolnum></citerefentry>.</para>
<para>See <ulink url="https://systemd.io/RANDOM_SEEDS">Random Seeds</ulink> for further
<para>The following options are understood:</para>
<variablelist>
- <varlistentry>
- <term><option>--esp-path=</option></term>
- <listitem><para>Path to the EFI System Partition (ESP). If not specified, <filename>/efi/</filename>,
- <filename>/boot/</filename>, and <filename>/boot/efi/</filename> are checked in turn. It is
- recommended to mount the ESP to <filename>/efi/</filename>, if possible.</para></listitem>
- </varlistentry>
-
- <varlistentry>
- <term><option>--boot-path=</option></term>
- <listitem><para>Path to the Extended Boot Loader partition, as defined in the <ulink
- url="https://uapi-group.org/specifications/specs/boot_loader_specification">Boot Loader Specification</ulink>. If not
- specified, <filename>/boot/</filename> is checked. It is recommended to mount the Extended Boot
- Loader partition to <filename>/boot/</filename>, if possible.</para></listitem>
- </varlistentry>
+ <xi:include href="standard-options.xml" xpointer="esp-path"/>
+ <xi:include href="standard-options.xml" xpointer="boot-path"/>
<varlistentry>
<term><option>--root=<replaceable>root</replaceable></option></term>
</varlistentry>
<varlistentry>
- <term><option>no-journal</option></term>
+ <term><option>mode=(journal|bitmap|direct)</option></term>
<listitem><para>
- Disable the journal. Corresponds to the "direct writes" mode documented in
+ Enable journaled, bitmapped or direct (passthrough) mode. Journaled mode is the default when this option is not specified.
+ It provides safety against crashes, but can be slow because all data has to be written twice.
+ Bitmap mode is more efficient since it requires only a single write, but it is less reliable because if data corruption happens when the machine crashes, it may not be detected.
+ Direct mode disables the journal and the bitmap. Corresponds to the "direct writes" mode documented in
<ulink url="https://docs.kernel.org/admin-guide/device-mapper/dm-integrity.html">the dm-integrity documentation</ulink>.
Note that without a journal, if there is a crash, it is possible that the integrity tags and data will not match. If used, the journal-*
options below will have no effect if passed.
<option>--user</option>). If neither is specified, show all messages that the user can see.
</para>
- <para>The <option>--user</option> option affects how <option>--unit</option> arguments are
- treated. See <option>--unit</option>.</para></listitem>
+ <para>The <option>--user</option> option affects how <option>--unit=</option> arguments are
+ treated. See <option>--unit=</option>.</para>
+
+ <para>Note that <option>--user</option> only works if persistent logging is enabled, via the
+ <varname>Storage=</varname> setting in
+ <citerefentry><refentrytitle>journald.conf</refentrytitle><manvolnum>5</manvolnum></citerefentry>.</para></listitem>
</varlistentry>
<varlistentry>
<citerefentry><refentrytitle>systemd.slice</refentrytitle><manvolnum>5</manvolnum></citerefentry>
unit, all logs of children of the slice will be shown.</para>
- <para>With <option>--user</option>, all <option>--unit</option> arguments will be converted to match
- user messages as if specified with <option>--user-unit</option>.</para>
+ <para>With <option>--user</option>, all <option>--unit=</option> arguments will be converted to match
+ user messages as if specified with <option>--user-unit=</option>.</para>
<para>This parameter can be specified multiple times.</para></listitem>
</varlistentry>
<term><option>--vacuum-time=</option></term>
<term><option>--vacuum-files=</option></term>
- <listitem><para>Removes the oldest archived journal files until the disk space they use falls below
- the specified size (specified with the usual <literal>K</literal>, <literal>M</literal>,
- <literal>G</literal> and <literal>T</literal> suffixes), or all archived journal files contain no
- data older than the specified timespan (specified with the usual <literal>s</literal>,
- <literal>m</literal>, <literal>h</literal>, <literal>days</literal>, <literal>months</literal>,
- <literal>weeks</literal> and <literal>years</literal> suffixes), or no more than the specified
- number of separate journal files remain. Note that running <option>--vacuum-size=</option> has only
- an indirect effect on the output shown by <option>--disk-usage</option>, as the latter includes
- active journal files, while the vacuuming operation only operates on archived journal
- files. Similarly, <option>--vacuum-files=</option> might not actually reduce the number of journal
- files to below the specified number, as it will not remove active journal files.</para>
+ <listitem><para><option>--vacuum-size=</option> removes the oldest archived journal files until the
+ disk space they use falls below the specified size. Accepts the usual <literal>K</literal>,
+ <literal>M</literal>, <literal>G</literal> and <literal>T</literal> suffixes (to the base of
+ 1024).</para>
+
+ <para><option>--vacuum-time=</option> removes archived journal files older than the specified
+ timespan. Accepts the usual <literal>s</literal> (default), <literal>m</literal>,
+ <literal>h</literal>, <literal>days</literal>, <literal>months</literal>, <literal>weeks</literal>
+ and <literal>years</literal> suffixes, see
+ <citerefentry><refentrytitle>systemd.time</refentrytitle><manvolnum>7</manvolnum></citerefentry> for
+ details.</para>
+
+ <para><option>--vacuum-files=</option> leaves only the specified number of separate journal
+ files.</para>
+
+ <para>Note that running <option>--vacuum-size=</option> has only an indirect effect on the output
+ shown by <option>--disk-usage</option>, as the latter includes active journal files, while the
+ vacuuming operation only operates on archived journal files. Similarly,
+ <option>--vacuum-files=</option> might not actually reduce the number of journal files to below the
+ specified number, as it will not remove active journal files.</para>
<para><option>--vacuum-size=</option>, <option>--vacuum-time=</option> and
- <option>--vacuum-files=</option> may be combined in a single invocation to enforce any combination
- of a size, a time and a number of files limit on the archived journal files. Specifying any of
- these three parameters as zero is equivalent to not enforcing the specific limit, and is thus
+ <option>--vacuum-files=</option> may be combined in a single invocation to enforce any combination of
+ a size, a time and a number of files limit on the archived journal files. Specifying any of these
+ three parameters as zero is equivalent to not enforcing the specific limit, and is thus
redundant.</para>
<para>These three switches may also be combined with <option>--rotate</option> into one command. If
<filename>/var/log/journal/</filename>, as the <filename>systemd-journald@.service</filename> service
file by default carries <varname>LogsDirectory=</varname>. To turn that off, add a unit file drop-in
file that sets <varname>LogsDirectory=</varname> to an empty string.</para>
+
+ <para>Note that per-user journal files are not supported unless persistent storage is enabled, thus
+ making <command>journalctl --user</command> unavailable.</para>
</listitem>
</varlistentry>
<varlistentry>
<term><varname>systemd.clock-usec=</varname></term>
- <listitem><para>Takes a decimal, numeric timestamp in µs since January 1st 1970, 00:00am, to set the
+ <listitem><para>Takes a decimal, numeric timestamp in μs since January 1st 1970, 00:00am, to set the
system clock to. The system time is set to the specified timestamp early during boot. It is not
propagated to the hardware clock (RTC).</para></listitem>
</varlistentry>
<command>kernel-install</command>
<arg choice="opt" rep="repeat">OPTIONS</arg>
<arg choice="plain">inspect</arg>
+ <arg choice="opt"><replaceable>KERNEL-IMAGE</replaceable></arg>
</cmdsynopsis>
</refsynopsisdiv>
</listitem>
</varlistentry>
<varlistentry>
- <term><command>inspect</command></term>
+ <term><command>inspect [<replaceable>KERNEL-IMAGE</replaceable>]</command></term>
<listitem>
<para>Shows the various paths and parameters configured or auto-detected. In particular shows the
values of the various <varname>$KERNEL_INSTALL_*</varname> environment variables listed
<para>The following options are understood:</para>
<variablelist>
+ <xi:include href="standard-options.xml" xpointer="esp-path"/>
+ <xi:include href="standard-options.xml" xpointer="boot-path"/>
+
+ <varlistentry>
+ <term><option>--make-entry-directory=yes|no|auto</option></term>
+ <listitem>
+ <para>Controls creation and deletion of the
+ <ulink url="https://uapi-group.org/specifications/specs/boot_loader_specification">Boot Loader Specification</ulink>
+ Type #1 entry directory on the file system containing resources such as kernel and initrd images
+ during <option>add</option> and <option>remove</option>, respectively. The directory is named after
+ the entry token, and is placed immediately below the boot root directory. When
+ <literal>auto</literal>, the directory is created or removed only when the install layout is
+ <literal>bls</literal>. Defaults to <literal>auto</literal>.</para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><option>--entry-token=</option></term>
+
+ <listitem>
+ <para>Controls how to name and identify boot loader entries for this kernel installation or
+ deletion. Takes one of <literal>auto</literal>, <literal>machine-id</literal>,
+ <literal>os-id</literal>, <literal>os-image-id</literal>, or an arbitrary string prefixed by
+ <literal>literal:</literal> as argument.</para>
+
+ <para>If set to <option>machine-id</option> the entries are named after the machine ID of the
+ running system (e.g. <literal>b0e793a9baf14b5fa13ecbe84ff637ac</literal>). See
+ <citerefentry><refentrytitle>machine-id</refentrytitle><manvolnum>5</manvolnum></citerefentry> for
+ details about the machine ID concept and file.</para>
+
+ <para>If set to <option>os-id</option> the entries are named after the OS ID of the running system,
+ i.e. the <varname>ID=</varname> field of
+ <citerefentry><refentrytitle>os-release</refentrytitle><manvolnum>5</manvolnum></citerefentry>
+ (e.g. <literal>fedora</literal>). Similarly, if set to <option>os-image-id</option> the entries are
+ named after the OS image ID of the running system, i.e. the <varname>IMAGE_ID=</varname> field of
+ <filename>os-release</filename> (e.g. <literal>vendorx-cashier-system</literal>).</para>
+
+ <para>If set to <option>auto</option> (the default), the
+ <filename>/etc/kernel/entry-token</filename> (or
+ <filename>$KERNEL_INSTALL_CONF_ROOT/entry-token</filename>) file will be read if it exists, and the
+ stored value used. Otherwise if the local machine ID is initialized it is used. Otherwise
+ <varname>IMAGE_ID=</varname> from <filename>os-release</filename> will be used, if set. Otherwise,
+ <varname>ID=</varname> from <filename>os-release</filename> will be used, if set. Otherwise a
+ randomly generated machine ID is used.</para>
+
+ <para>Using the machine ID for naming the entries is generally preferable, however there are cases
+ where using the other identifiers is a good option. Specifically: if the identification data that
+ the machine ID entails shall not be stored on the (unencrypted) <varname>$BOOT_ROOT</varname>
+ partition, or if the ID shall be generated on first boot and is not known when the entries are
+ prepared. Note that using the machine ID has the benefit that multiple parallel installations of
+ the same OS can coexist on the same medium, and they can update their boot loader entries
+ independently. When using another identifier (such as the OS ID or the OS image ID), parallel
+ installations of the same OS would try to use the same entry name. To support parallel
+ installations, the installer must use a different entry token when adding a second installation.
+ </para>
+ </listitem>
+ </varlistentry>
+
<varlistentry>
<term><option>-v</option></term>
<term><option>--verbose</option></term>
'--stringparam', 'man.copyright.section.enabled', '0',
'--stringparam', 'systemd.version', '@0@'.format(meson.project_version()),
'--path',
- '@0@:@1@'.format(meson.current_build_dir(), meson.current_source_dir())]
+ '@0@:@1@:@2@'.format(meson.current_build_dir(),
+ meson.current_source_dir(),
+ libshared_build_dir)]
custom_man_xsl = files('custom-man.xsl')
custom_html_xsl = files('custom-html.xsl')
output : 'custom-entities.ent',
command : [jinja2_cmdline, '@INPUT@', '@OUTPUT@'])
+man_page_depends += custom_entities_ent
+
man_pages = []
html_pages = []
source_xml_files = []
input : xml,
output : [man] + manaliases,
command : xslt_cmd + [custom_man_xsl, '@INPUT@'],
- depends : custom_entities_ent,
+ depends : man_page_depends,
install : want_man,
install_dir : mandirn)
man_pages += p1
input : xml,
output : html,
command : xslt_cmd + [custom_html_xsl, '@INPUT@'],
- depends : [custom_entities_ent, p2],
+ depends : [man_page_depends, p2],
install : want_html,
install_dir : docdir / 'html')
html_pages += p3
'systemd.directives.xml',
input : ['directives-template.xml', source_xml_files],
output : 'systemd.directives.xml',
- depends : custom_entities_ent,
+ depends : man_page_depends,
command : [make_directive_index_py, '@OUTPUT@', '@INPUT@'])
nonindex_xml_files = source_xml_files + [systemd_directives_xml]
input : xml,
output : html,
command : xslt_cmd + [custom_html_xsl, '@INPUT@'],
- depends : [custom_entities_ent, p2],
+ depends : [man_page_depends, p2],
install : want_html and have_lxml,
install_dir : docdir / 'html')
html_pages += p3
command : [update_man_rules_py,
'@0@/man/*.xml'.format(project_source_root),
'@0@/rules/meson.build'.format(meson.current_source_dir())],
- depends : custom_entities_ent)
+ depends : man_page_depends)
see the <varname>MountImages=</varname> entry on
<citerefentry><refentrytitle>systemd.exec</refentrytitle><manvolnum>5</manvolnum></citerefentry>
and <citerefentry><refentrytitle>systemd-sysext</refentrytitle><manvolnum>8</manvolnum></citerefentry>.
- The <varname>flag</varname> parameter is currently unused and reserved for future purposes.</para>
+ </para>
<para><function>DetachImage()</function> detaches a portable image from the system.
This method takes an image path or name, and a boolean indicating whether the image to
<para>The four methods above accept and return a 64-bit flags value. In most cases passing 0 is sufficient
and recommended. However, the following flags are defined to alter the look-up:</para>
- <programlisting>
+ <programlisting>/* Input+Output: Protocol/scope */
#define SD_RESOLVED_DNS (UINT64_C(1) << 0)
#define SD_RESOLVED_LLMNR_IPV4 (UINT64_C(1) << 1)
#define SD_RESOLVED_LLMNR_IPV6 (UINT64_C(1) << 2)
#define SD_RESOLVED_MDNS_IPV4 (UINT64_C(1) << 3)
#define SD_RESOLVED_MDNS_IPV6 (UINT64_C(1) << 4)
+
+/* Input: Restrictions */
#define SD_RESOLVED_NO_CNAME (UINT64_C(1) << 5)
#define SD_RESOLVED_NO_TXT (UINT64_C(1) << 6)
#define SD_RESOLVED_NO_ADDRESS (UINT64_C(1) << 7)
#define SD_RESOLVED_NO_SEARCH (UINT64_C(1) << 8)
-#define SD_RESOLVED_AUTHENTICATED (UINT64_C(1) << 9)
#define SD_RESOLVED_NO_VALIDATE (UINT64_C(1) << 10)
#define SD_RESOLVED_NO_SYNTHESIZE (UINT64_C(1) << 11)
#define SD_RESOLVED_NO_CACHE (UINT64_C(1) << 12)
#define SD_RESOLVED_NO_ZONE (UINT64_C(1) << 13)
#define SD_RESOLVED_NO_TRUST_ANCHOR (UINT64_C(1) << 14)
#define SD_RESOLVED_NO_NETWORK (UINT64_C(1) << 15)
-#define SD_RESOLVED_REQUIRE_PRIMARY (UINT64_C(1) << 16)
-#define SD_RESOLVED_CLAMP_TTL (UINT64_C(1) << 17)
+
+/* Output: Security */
+#define SD_RESOLVED_AUTHENTICATED (UINT64_C(1) << 9)
#define SD_RESOLVED_CONFIDENTIAL (UINT64_C(1) << 18)
+
+/* Output: Origin */
#define SD_RESOLVED_SYNTHETIC (UINT64_C(1) << 19)
#define SD_RESOLVED_FROM_CACHE (UINT64_C(1) << 20)
#define SD_RESOLVED_FROM_ZONE (UINT64_C(1) << 21)
#define SD_RESOLVED_FROM_TRUST_ANCHOR (UINT64_C(1) << 22)
#define SD_RESOLVED_FROM_NETWORK (UINT64_C(1) << 23)
- </programlisting>
+</programlisting>
<para>On input, the first five flags control the protocols to use for the look-up. They refer to
classic unicast DNS, LLMNR via IPv4/UDP and IPv6/UDP respectively, as well as MulticastDNS via
address lookup domains). It will route neither of these to Unicast DNS servers. Also, it will do
LLMNR and Multicast DNS only on interfaces suitable for multicast.</para>
- <para>On output, these five flags indicate which protocol was used to execute the operation, and hence
- where the data was found.</para>
+ <para>On output, these five flags indicate which protocol was used to execute the operation, and
+ hence where the data was found.</para>
<para>The primary use cases for these five flags are follow-up look-ups based on DNS data retrieved
earlier. In this case it is often a good idea to limit the follow-up look-up to the protocol that was
<para>The NO_SEARCH flag turns off the search domain logic. It is only defined for input in
<function>ResolveHostname()</function>. When specified, single-label hostnames are not qualified
using defined search domains, if any are configured. Note that <function>ResolveRecord()</function>
- will never qualify single-label domain names using search domains. Also note that
- multi-label hostnames are never subject to search list expansion.</para>
+ will never qualify single-label domain names using search domains. Also note that multi-label
+ hostnames are never subject to search list expansion.</para>
+
+ <para>NO_VALIDATE can be set to disable validation via DNSSEC even if it would normally be
+ used.</para>
+
+ <para>The next four flags allow disabling certain sources during resolution. NO_SYNTHESIZE disables
+ synthetic records, e.g. the local host name, see section SYNTHETIC RECORDS in
+ <citerefentry><refentrytitle>systemd-resolved.service</refentrytitle><manvolnum>8</manvolnum></citerefentry>
+ for more information. NO_CACHE disables the use of the cache of previously resolved records. NO_ZONE
+ disables answers using locally registered public LLMNR/mDNS resource records. NO_TRUST_ANCHOR
+ disables answers using locally configured trust anchors. NO_NETWORK requires all answers to be
+ provided without using the network, i.e. either from local sources or the cache.</para>
<para>The AUTHENTICATED bit is defined only in the output flags of the four functions. If set, the
returned data has been fully authenticated. Specifically, this bit is set for all DNSSEC-protected
trusted, or where there is proof that the data is "rightfully" unauthenticated (which includes cases
where the underlying protocol or server does not support authenticating data).</para>
- <para>NO_VALIDATE can be set to disable validation via DNSSEC even if it would normally be used.
- </para>
-
- <para>The next four flags allow disabling certain sources during resolution. NO_SYNTHESIZE disables
- synthetic records, e.g. the local host name, see section SYNTHETIC RECORDS in
- <citerefentry><refentrytitle>systemd-resolved.service</refentrytitle><manvolnum>8</manvolnum></citerefentry>
- for more information. NO_CACHE disables the use of the cache of previously resolved records. NO_ZONE
- disables answers using locally registered public LLMNR/mDNS resource records. NO_TRUST_ANCHOR
- disables answers using locally configured trust anchors. NO_NETWORK requires all answers to be
- provided without using the network, i.e. either from local sources or the cache.</para>
-
- <para>With REQUIRE_PRIMARY the request must be answered from a "primary" answer, i.e. not from
- resource records acquired as a side-effect of a previous transaction.</para>
-
- <para>With CLAMP_TTL, if reply is answered from cache, the TTLs will be adjusted by age of cache
- entry.</para>
+ <para>CONFIDENTIAL means the query was resolved via encrypted channels or never left this
+ system.</para>
- <para>The next six bits flags are used in output and provide information about the source of the answer.
- CONFIDENTIAL means the query was resolved via encrypted channels or never left this system.
- FROM_SYNTHETIC means the query was (at least partially) synthesized.
- FROM_CACHE means the query was answered (at least partially) using the cache.
- FROM_ZONE means the query was answered (at least partially) using LLMNR/mDNS.
- FROM_TRUST_ANCHOR means the query was answered (at least partially) using local trust anchors.
- FROM_NETWORK means the query was answered (at least partially) using the network.
- </para>
+ <para>The next five bits flags are used in output and provide information about the origin of the
+ answer. FROM_SYNTHETIC means the query was (at least partially) synthesized locally. FROM_CACHE means
+ the query was answered (at least partially) using the cache. FROM_ZONE means the query was answered
+ (at least partially) based on public, locally registered records. FROM_TRUST_ANCHOR means the query
+ was answered (at least partially) using local trust anchors. FROM_NETWORK means the query was
+ answered (at least partially) using the network.</para>
</refsect3>
</refsect2>
unit and job objects or directly convert a unit name or job identifier to a bus path of the corresponding
D-Bus object.</para>
- <para>Properties exposing time values are usually encoded in microseconds (µs) on the bus, even if
+ <para>Properties exposing time values are usually encoded in microseconds (μs) on the bus, even if
their corresponding settings in the unit files are in seconds.</para>
<para>PID 1 uses <ulink url="https://www.freedesktop.org/software/polkit/docs/latest/">polkit</ulink> to
@org.freedesktop.DBus.Property.EmitsChangedSignal("const")
readonly a(ss) LoadCredentialEncrypted = [...];
@org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+ readonly as ImportCredential = ['...', ...];
+ @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
readonly as SupplementaryGroups = ['...', ...];
@org.freedesktop.DBus.Property.EmitsChangedSignal("const")
readonly s PAMName = '...';
<!--property LoadCredentialEncrypted is not documented!-->
+ <!--property ImportCredential is not documented!-->
+
<!--property SupplementaryGroups is not documented!-->
<!--property PAMName is not documented!-->
<variablelist class="dbus-property" generated="True" extra-ref="LoadCredentialEncrypted"/>
+ <variablelist class="dbus-property" generated="True" extra-ref="ImportCredential"/>
+
<variablelist class="dbus-property" generated="True" extra-ref="SupplementaryGroups"/>
<variablelist class="dbus-property" generated="True" extra-ref="PAMName"/>
@org.freedesktop.DBus.Property.EmitsChangedSignal("const")
readonly a(ss) LoadCredentialEncrypted = [...];
@org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+ readonly as ImportCredential = ['...', ...];
+ @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
readonly as SupplementaryGroups = ['...', ...];
@org.freedesktop.DBus.Property.EmitsChangedSignal("const")
readonly s PAMName = '...';
<!--property LoadCredentialEncrypted is not documented!-->
+ <!--property ImportCredential is not documented!-->
+
<!--property SupplementaryGroups is not documented!-->
<!--property PAMName is not documented!-->
<variablelist class="dbus-property" generated="True" extra-ref="LoadCredentialEncrypted"/>
+ <variablelist class="dbus-property" generated="True" extra-ref="ImportCredential"/>
+
<variablelist class="dbus-property" generated="True" extra-ref="SupplementaryGroups"/>
<variablelist class="dbus-property" generated="True" extra-ref="PAMName"/>
@org.freedesktop.DBus.Property.EmitsChangedSignal("const")
readonly a(ss) LoadCredentialEncrypted = [...];
@org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+ readonly as ImportCredential = ['...', ...];
+ @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
readonly as SupplementaryGroups = ['...', ...];
@org.freedesktop.DBus.Property.EmitsChangedSignal("const")
readonly s PAMName = '...';
<!--property LoadCredentialEncrypted is not documented!-->
+ <!--property ImportCredential is not documented!-->
+
<!--property SupplementaryGroups is not documented!-->
<!--property PAMName is not documented!-->
<variablelist class="dbus-property" generated="True" extra-ref="LoadCredentialEncrypted"/>
+ <variablelist class="dbus-property" generated="True" extra-ref="ImportCredential"/>
+
<variablelist class="dbus-property" generated="True" extra-ref="SupplementaryGroups"/>
<variablelist class="dbus-property" generated="True" extra-ref="PAMName"/>
@org.freedesktop.DBus.Property.EmitsChangedSignal("const")
readonly a(ss) LoadCredentialEncrypted = [...];
@org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+ readonly as ImportCredential = ['...', ...];
+ @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
readonly as SupplementaryGroups = ['...', ...];
@org.freedesktop.DBus.Property.EmitsChangedSignal("const")
readonly s PAMName = '...';
<!--property LoadCredentialEncrypted is not documented!-->
+ <!--property ImportCredential is not documented!-->
+
<!--property SupplementaryGroups is not documented!-->
<!--property PAMName is not documented!-->
<variablelist class="dbus-property" generated="True" extra-ref="LoadCredentialEncrypted"/>
+ <variablelist class="dbus-property" generated="True" extra-ref="ImportCredential"/>
+
<variablelist class="dbus-property" generated="True" extra-ref="SupplementaryGroups"/>
<variablelist class="dbus-property" generated="True" extra-ref="PAMName"/>
for light blue, or <literal>ANSI_COLOR="0;38;2;60;110;180"</literal> for Fedora blue.
</para></listitem>
</varlistentry>
+
+ <varlistentry>
+ <term><varname>VENDOR_NAME=</varname></term>
+
+ <listitem><para>The name of the OS vendor. This is the name of the organization or company which
+ produces the OS. This field is optional.</para>
+
+ <para>This name is intended to be exposed in "About this system" UIs or software update UIs when
+ needed to distinguish the OS vendor from the OS itself. It is intended to be human readable.</para>
+
+ <para>Examples: <literal>VENDOR_NAME="Fedora Project"</literal> for Fedora Linux,
+ <literal>VENDOR_NAME="Canonical"</literal> for Ubuntu.</para></listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><varname>VENDOR_URL=</varname></term>
+
+ <listitem><para>The homepage of the OS vendor. This field is optional. The
+ <varname>VENDOR_NAME=</varname> field should be set if this one is, although clients must be
+ robust against either field not being set.</para>
+
+ <para>The value should be in <ulink
+ url="https://tools.ietf.org/html/rfc3986">RFC3986 format</ulink>, and should be
+ <literal>http:</literal> or <literal>https:</literal> URLs. Only one URL shall be listed in the
+ setting.</para>
+
+ <para>Examples: <literal>VENDOR_URL="https://fedoraproject.org/"</literal>,
+ <literal>VENDOR_URL="https://canonical.com/"</literal>.</para></listitem>
+ </varlistentry>
</variablelist>
</refsect2>
output.</para></listitem>
</varlistentry>
+ <varlistentry>
+ <term><command>show-cache</command></term>
+
+ <listitem><para>Show current cache content, per scope. Use <option>--json=</option> to enable JSON
+ output.</para></listitem>
+ </varlistentry>
+
<xi:include href="systemctl.xml" xpointer="log-level" />
</variablelist>
</refsect1>
project='man-pages'><refentrytitle>timerfd_create</refentrytitle><manvolnum>2</manvolnum></citerefentry>,
supporting the <constant>CLOCK_MONOTONIC</constant>,
<constant>CLOCK_REALTIME</constant>,
- <constant>CLOCK_BOOTIME</constant> clocks, as well as the
+ <constant>CLOCK_BOOTTIME</constant> clocks, as well as the
<constant>CLOCK_REALTIME_ALARM</constant> and
<constant>CLOCK_BOOTTIME_ALARM</constant> clocks that can resume
the system from suspend. When creating timer events a required
<constant>POLLIN</constant>, <constant>POLLOUT</constant>, … events, or negative on error.
</para>
- <para><function>sd_bus_get_timeout()</function> returns the <emphasis>absolute</emphasis> time-out in µs,
+ <para><function>sd_bus_get_timeout()</function> returns the <emphasis>absolute</emphasis> time-out in μs,
from which the relative time-out to pass to <function>poll()</function> (or a similar call) can be
derived, when waiting for events on the specified bus connection. The returned timeout may be zero, in
which case a subsequent I/O polling call should be invoked in non-blocking mode. The returned timeout may
indicating that no work is pending on the connection. Internally, this call invokes <citerefentry
project='man-pages'><refentrytitle>ppoll</refentrytitle><manvolnum>2</manvolnum></citerefentry>, to wait for I/O on
the bus connection. If the <parameter>timeout_usec</parameter> parameter is specified, the call will block at most
- for the specified amount of time in µs. Pass <constant>UINT64_MAX</constant> to permit it to sleep
+ for the specified amount of time in μs. Pass <constant>UINT64_MAX</constant> to permit it to sleep
indefinitely.</para>
<para>After each invocation of <function>sd_bus_wait()</function> the <function>sd_bus_process()</function> call
<constant>CLOCK_REALTIME_ALARM</constant>, or <constant>CLOCK_BOOTTIME_ALARM</constant>. See
<citerefentry><refentrytitle>timerfd_create</refentrytitle><manvolnum>2</manvolnum></citerefentry> for details
regarding the various types of clocks. The <parameter>usec</parameter> parameter specifies the earliest time, in
- microseconds (µs), relative to the clock's epoch, when the timer shall be triggered. If a time already in the past
+ microseconds (μs), relative to the clock's epoch, when the timer shall be triggered. If a time already in the past
is specified (including <constant>0</constant>), this timer source "fires" immediately and is ready to be
dispatched. If the parameter is specified as <constant>UINT64_MAX</constant> the timer event will never elapse,
which may be used as an alternative to explicitly disabling a timer event source with
<citerefentry><refentrytitle>sd_event_source_set_enabled</refentrytitle><manvolnum>3</manvolnum></citerefentry>. The
- <parameter>accuracy</parameter> parameter specifies an additional accuracy value in µs specifying how much the
- timer event may be delayed. Use <constant>0</constant> to select the default accuracy (250ms). Use 1µs for maximum
- accuracy. Consider specifying 60000000µs (1min) or larger for long-running events that may be delayed
+ <parameter>accuracy</parameter> parameter specifies an additional accuracy value in μs specifying how much the
+ timer event may be delayed. Use <constant>0</constant> to select the default accuracy (250ms). Use 1μs for maximum
+ accuracy. Consider specifying 60000000μs (1min) or larger for long-running events that may be delayed
substantially. Picking higher accuracy values allows the system to coalesce timer events more aggressively,
improving power efficiency.</para>
<para><function>sd_event_source_get_time()</function> retrieves the configured time value of an event
source created previously with <function>sd_event_add_time()</function> or
<function>sd_event_add_time_relative()</function>. It takes the event source object and a pointer to a
- variable to store the time in, relative to the selected clock's epoch, in µs. The returned value is
+ variable to store the time in, relative to the selected clock's epoch, in μs. The returned value is
relative to the epoch, even if the event source was created with a relative time via
<function>sd_event_add_time_relative()</function>.</para>
<para><function>sd_event_source_set_time()</function> changes the time of an event source created
previously with <function>sd_event_add_time()</function> or
<function>sd_event_add_time_relative()</function>. It takes the event source object and a time relative
- to the selected clock's epoch, in µs.</para>
+ to the selected clock's epoch, in μs.</para>
<para><function>sd_event_source_set_time_relative()</function> is similar to
<function>sd_event_source_set_time()</function>, but takes a time relative to the current time of the
retrieves the configured accuracy value of an event source
created previously with <function>sd_event_add_time()</function>. It
takes the event source object and a pointer to a variable to store
- the accuracy in. The accuracy is specified in µs.</para>
+ the accuracy in. The accuracy is specified in μs.</para>
<para><function>sd_event_source_set_time_accuracy()</function>
changes the configured accuracy of a timer event source created
previously with <function>sd_event_add_time()</function>. It takes
- the event source object and accuracy, in µs.</para>
+ the event source object and accuracy, in μs.</para>
<para><function>sd_event_source_get_time_clock()</function>
retrieves the configured clock of an event source created
<citerefentry project='man-pages'><refentrytitle>clock_gettime</refentrytitle><manvolnum>2</manvolnum></citerefentry>
for more information on the various clocks. The retrieved
timestamp is stored in the <parameter>usec</parameter> parameter,
- in µs since the clock's epoch. If this function is invoked before
+ in μs since the clock's epoch. If this function is invoked before
the first event loop iteration, the current time is returned, as
reported by <function>clock_gettime()</function>. To distinguish
this case from a regular invocation the return value will be
is no timeout to wait for this will fill in <constant>(uint64_t)
-1</constant> instead. Note that <function>poll()</function> takes
a relative timeout in milliseconds rather than an absolute timeout
- in microseconds. To convert the absolute 'µs' timeout into
+ in microseconds. To convert the absolute 'μs' timeout into
relative 'ms', use code like the following:</para>
<programlisting>uint64_t t;
<term>MONOTONIC_USEC=…</term>
<listitem><para>A field carrying the monotonic timestamp (as per
- <constant>CLOCK_MONOTONIC</constant>) formatted in decimal in µs, when the notification message was
+ <constant>CLOCK_MONOTONIC</constant>) formatted in decimal in μs, when the notification message was
generated by the client. This is typically used in combination with <literal>RELOADING=1</literal>,
to allow the service manager to properly synchronize reload cycles. See
<citerefentry><refentrytitle>systemd.service</refentrytitle><manvolnum>5</manvolnum></citerefentry>
<refname>sd_session_get_vt</refname>
<refname>sd_session_get_remote_host</refname>
<refname>sd_session_get_remote_user</refname>
+ <refname>sd_session_get_leader</refname>
<refpurpose>Determine state of a specific session</refpurpose>
</refnamediv>
<paramdef>char **<parameter>display</parameter></paramdef>
</funcprototype>
+ <funcprototype>
+ <funcdef>int <function>sd_session_get_leader</function></funcdef>
+ <paramdef>const char *<parameter>session</parameter></paramdef>
+ <paramdef>pid_t *<parameter>leader</parameter></paramdef>
+ </funcprototype>
+
<funcprototype>
<funcdef>int <function>sd_session_get_remote_host</function></funcdef>
<paramdef>const char *<parameter>session</parameter></paramdef>
<citerefentry project='man-pages'><refentrytitle>free</refentrytitle><manvolnum>3</manvolnum></citerefentry>
call after use.</para>
+ <para><function>sd_session_get_leader()</function> may be used to
+ determine the PID of the leader of the session identified by the
+ specified session identifier.</para>
+
<para><function>sd_session_get_remote_host()</function> may be
used to determine the remote hostname of the session identified by
the specified session identifier. The returned string needs to be
<function>sd_session_get_type()</function>,
<function>sd_session_get_class()</function>,
<function>sd_session_get_display()</function>,
+ <function>sd_session_get_leader()</function>,
<function>sd_session_get_remote_user()</function>,
<function>sd_session_get_remote_host()</function> and
<function>sd_session_get_tty()</function> return 0 or
<para>If the <parameter>usec</parameter> parameter is non-<constant>NULL</constant>,
<function>sd_watchdog_enabled()</function> will write the timeout
- in µs for the watchdog logic to it.</para>
+ in μs for the watchdog logic to it.</para>
<para>To enable service supervision with the watchdog logic, use
<varname>WatchdogSec=</varname> in service files. See
<listitem><para>Set by the system manager for supervised
process for which watchdog support is enabled, and contains
- the watchdog timeout in µs. See above for
+ the watchdog timeout in μs. See above for
details.</para></listitem>
</varlistentry>
</variablelist>
in the image are used.</para></listitem>
</varlistentry>
+ <varlistentry id='esp-path'>
+ <term><option>--esp-path=</option></term>
+
+ <listitem>
+ <para>Path to the EFI System Partition (ESP). If not specified, <filename>/efi/</filename>,
+ <filename>/boot/</filename>, and <filename>/boot/efi/</filename> are checked in turn. It is
+ recommended to mount the ESP to <filename>/efi/</filename>, if possible.</para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry id='boot-path'>
+ <term><option>--boot-path=</option></term>
+
+ <listitem>
+ <para>Path to the Extended Boot Loader partition, as defined in the
+ <ulink url="https://uapi-group.org/specifications/specs/boot_loader_specification">Boot Loader Specification</ulink>.
+ If not specified, <filename>/boot/</filename> is checked. It is recommended to mount the Extended Boot
+ Loader partition to <filename>/boot/</filename>, if possible.</para>
+ </listitem>
+ </varlistentry>
+
</variablelist>
<term><command>mask <replaceable>UNIT</replaceable>…</command></term>
<listitem>
- <para>Mask one or more units, as specified on the command line. This will link these unit files to
- <filename>/dev/null</filename>, making it impossible to start them. This is a stronger version of
- <command>disable</command>, since it prohibits all kinds of activation of the unit, including enablement
- and manual activation. Use this option with care. This honors the <option>--runtime</option> option to only
- mask temporarily until the next reboot of the system. The <option>--now</option> option may be used to
- ensure that the units are also stopped. This command expects valid unit names only, it does not accept unit
- file paths.</para>
+ <para>Mask one or more units, as specified on the command line. This will link these unit files
+ to <filename>/dev/null</filename>, making it impossible to start them. This is a stronger version
+ of <command>disable</command>, since it prohibits all kinds of activation of the unit, including
+ enablement and manual activation. Use this option with care. This honors the
+ <option>--runtime</option> option to only mask temporarily until the next reboot of the
+ system. The <option>--now</option> option may be used to ensure that the units are also
+ stopped. This command expects valid unit names only, it does not accept unit file paths.</para>
+
+ <para>Note that this will create a symlink under the unit's name in
+ <filename>/etc/systemd/system/</filename> (in case <option>--runtime</option> is not specified)
+ or <filename>/run/systemd/system/</filename> (in case <option>--runtime</option> is
+ specified). If a matching unit file already exists under these directories this operation will
+ hence fail. This means that the operation is primarily useful to mask units shipped by the vendor
+ (as those are shipped in <filename>/usr/lib/systemd/system/</filename> and not the aforementioned
+ two directories), but typically doesn't work for units created locally (as those are typically
+ placed precisely in the two aforementioned directories). Similar restrictions apply for
+ <option>--user</option> mode, in which case the directories are below the user's home directory
+ however.</para>
</listitem>
</varlistentry>
<variablelist>
<varlistentry>
<term><option>us</option></term>
- <term><option>µs</option></term>
+ <term><option>μs</option></term>
<listitem><para><literal>Day YYYY-MM-DD HH:MM:SS.UUUUUU TZ</literal></para></listitem>
</varlistentry>
</variablelist>
<variablelist>
<varlistentry>
<term><option>us+utc</option></term>
- <term><option>µs+utc</option></term>
+ <term><option>μs+utc</option></term>
<listitem><para><literal>Day YYYY-MM-DD HH:MM:SS.UUUUUU UTC</literal></para></listitem>
</varlistentry>
</variablelist>
<varlistentry>
<term><option>--credential=</option></term>
<listitem><para>Configure a credential to read the password from – if it exists. This may be used in
- conjunction with the <varname>LoadCredential=</varname> and <varname>SetCredential=</varname>
- settings in unit files. See
+ conjunction with the <varname>ImportCredential=</varname>, <varname>LoadCredential=</varname> and
+ <varname>SetCredential=</varname> settings in unit files. See
<citerefentry><refentrytitle>systemd.exec</refentrytitle><manvolnum>5</manvolnum></citerefentry> for
details. If not specified, defaults to <literal>password</literal>. This option has no effect if no
credentials directory is passed to the program (i.e. <varname>$CREDENTIALS_DIRECTORY</varname> is not
<varlistentry>
<term><varname>COREDUMP_TIMESTAMP=</varname></term>
- <listitem><para>The time of the crash as reported by the kernel (in µs since the epoch).</para>
+ <listitem><para>The time of the crash as reported by the kernel (in μs since the epoch).</para>
</listitem>
</varlistentry>
processes. They are primarily used for passing cryptographic keys (both public and private) or
certificates, user account information or identity information from the host to services.</para>
- <para>Credentials are configured in unit files via the <varname>LoadCredential=</varname>,
- <varname>SetCredential=</varname>, <varname>LoadCredentialEncrypted=</varname> and
- <varname>SetCredentialEncrypted=</varname> settings, see
+ <para>Credentials are configured in unit files via the <varname>ImportCredential></varname>,
+ <varname>LoadCredential=</varname>, <varname>SetCredential=</varname>,
+ <varname>LoadCredentialEncrypted=</varname> and <varname>SetCredentialEncrypted=</varname> settings, see
<citerefentry><refentrytitle>systemd.exec</refentrytitle><manvolnum>5</manvolnum></citerefentry> for
details.</para>
<title>Credentials</title>
<para><command>systemd-firstboot</command> supports the service credentials logic as implemented by
- <varname>LoadCredential=</varname>/<varname>SetCredential=</varname> (see
- <citerefentry><refentrytitle>systemd.exec</refentrytitle><manvolnum>1</manvolnum></citerefentry> for
+ <varname>ImportCredential=</varname>/<varname>LoadCredential=</varname>/<varname>SetCredential=</varname>
+ (see <citerefentry><refentrytitle>systemd.exec</refentrytitle><manvolnum>1</manvolnum></citerefentry> for
details). The following credentials are used when passed in:</para>
<variablelist>
<literal>x86-64</literal>.</para></listitem>
</varlistentry>
+ <varlistentry>
+ <term><option>--offline=</option><arg>BOOL</arg></term>
+
+ <listitem><para>Instructs <command>systemd-repart</command> to build the image offline. Takes a
+ boolean or <literal>auto</literal>. Defaults to <literal>auto</literal>. If enabled, the image is
+ built without using loop devices. This is useful to build images unprivileged or when loop devices
+ are not available. If disabled, the image is always built using loop devices. If
+ <literal>auto</literal>, <command>systemd-repart</command> will build the image online if possible
+ and fall back to building the image offline if loop devices are not available or cannot be accessed
+ due to missing permissions.</para></listitem>
+ </varlistentry>
+
<xi:include href="standard-options.xml" xpointer="help" />
<xi:include href="standard-options.xml" xpointer="version" />
<xi:include href="standard-options.xml" xpointer="no-pager" />
<title>Credentials</title>
<para><command>systemd-resolved</command> supports the service credentials logic as implemented by
- <varname>LoadCredential=</varname>/<varname>SetCredential=</varname> (see
- <citerefentry><refentrytitle>systemd.exec</refentrytitle><manvolnum>1</manvolnum></citerefentry> for
+ <varname>ImportCredential=</varname>/<varname>LoadCredential=</varname>/<varname>SetCredential=</varname>
+ (see <citerefentry><refentrytitle>systemd.exec</refentrytitle><manvolnum>1</manvolnum></citerefentry> for
details). The following credentials are used when passed in:</para>
<variablelist>
</variablelist>
</refsect1>
+ <refsect1>
+ <title>IP Ports</title>
+
+ <para>The <command>systemd-resolved</command> service listens on the following IP ports:</para>
+
+ <itemizedlist>
+ <listitem><para>Port 53 on IPv4 addresses 127.0.0.53 and 127.0.0.54 (both are on the local loopback
+ interface <literal>lo</literal>). This is the local DNS stub, as discussed above. Both UDP and TCP are
+ covered.</para></listitem>
+
+ <listitem><para>Port 5353 on all local addresses, both IPv4 and IPv6 (0.0.0.0 and ::0), for
+ MulticastDNS on UDP. Note that even though the socket is bound to all local interfaces via the selected
+ "wildcard" IP addresses, the incoming datagrams are filtered by the network interface they are coming
+ in on, and separate MulticastDNS link-local scopes are maintained for each, taking into consideration
+ whether MulticastDNS is enabled for the interface or not.</para></listitem>
+
+ <listitem><para>Port 5355 on all local addresses, both IPv4 and IP6 (0.0.0.0 and ::0), for LLMNR, on
+ both TCP and UDP. As with MulticastDNS filtering by incoming network interface is applied.</para></listitem>
+ </itemizedlist>
+ </refsect1>
+
<refsect1>
<title>See Also</title>
<para>
<title>Credentials</title>
<para><command>systemd-sysctl</command> supports the service credentials logic as implemented by
- <varname>LoadCredential=</varname>/<varname>SetCredential=</varname> (see
- <citerefentry><refentrytitle>systemd.exec</refentrytitle><manvolnum>1</manvolnum></citerefentry> for
+ <varname>ImportCredential=</varname>/<varname>LoadCredential=</varname>/<varname>SetCredential=</varname>
+ (see <citerefentry><refentrytitle>systemd.exec</refentrytitle><manvolnum>1</manvolnum></citerefentry> for
details). The following credentials are used when passed in:</para>
<variablelist>
<title>Credentials</title>
<para><command>systemd-sysusers</command> supports the service credentials logic as implemented by
- <varname>LoadCredential=</varname>/<varname>SetCredential=</varname> (see
- <citerefentry><refentrytitle>systemd.exec</refentrytitle><manvolnum>1</manvolnum></citerefentry> for
+ <varname>ImportCredential=</varname><varname>LoadCredential=</varname>/<varname>SetCredential=</varname>
+ (see <citerefentry><refentrytitle>systemd.exec</refentrytitle><manvolnum>1</manvolnum></citerefentry> for
details). The following credentials are used when passed in:</para>
<variablelist>
<term><filename>/var/lib/systemd/timesync/clock</filename></term>
<listitem>
- <para>The modification time ("mtime") of this file is updated on each successful NTP synchronization
- or after each <varname>SaveIntervalSec=</varname> time interval, as specified in
- <citerefentry><refentrytitle>timesyncd.conf</refentrytitle><manvolnum>5</manvolnum></citerefentry>.
- At the minimum, it will be set to the systemd build date. It is used to ensure that the system clock
- remains roughly monotonic across reboots, in case no local RTC is available.</para>
+ <para>The modification time ("mtime") of this file is updated on each successful NTP
+ synchronization or after each <varname>SaveIntervalSec=</varname> time interval, as specified in
+ <citerefentry><refentrytitle>timesyncd.conf</refentrytitle><manvolnum>5</manvolnum></citerefentry>.</para>
+
+ <para>When initializing, the local clock is advanced to the modification time of this file (if the
+ file timestamp is in the past this adjustment is not made). If the file does not exist yet, the
+ clock is instead advanced to the modification time of <filename>/usr/lib/clock-epoch</filename> –
+ if it exists – or to a time derived from the source tree at build time. This mechanism is used to
+ ensure that the system clock remains somewhat reasonably initialized and roughly monotonic across
+ reboots, in case no battery-buffered local RTC is available.</para>
</listitem>
</varlistentry>
+ <varlistentry>
+ <term><filename>/usr/lib/clock-epoch</filename></term>
+
+ <listitem><para>The modification time ("mtime") of this file is used for advancing the system clock
+ in case <filename>/var/lib/systemd/timesync/clock</filename> does not exist yet, see
+ above.</para></listitem>
+ </varlistentry>
+
<varlistentry>
<term><filename>/run/systemd/timesync/synchronized</filename></term>
<title>Credentials</title>
<para><command>systemd-tmpfiles</command> supports the service credentials logic as implemented by
- <varname>LoadCredential=</varname>/<varname>SetCredential=</varname> (see
- <citerefentry><refentrytitle>systemd.exec</refentrytitle><manvolnum>1</manvolnum></citerefentry> for
+ <varname>ImportCredential=</varname>/<varname>LoadCredential=</varname>/<varname>SetCredential=</varname>
+ (see <citerefentry><refentrytitle>systemd.exec</refentrytitle><manvolnum>1</manvolnum></citerefentry> for
details). The following credentials are used when passed in:</para>
<variablelist>
<title>Credentials</title>
<para><command>systemd-vconsole-setup</command> supports the service credentials logic as implemented by
- <varname>LoadCredential=</varname>/<varname>SetCredential=</varname> (see
- <citerefentry><refentrytitle>systemd.exec</refentrytitle><manvolnum>1</manvolnum></citerefentry> for
+ <varname>ImportCredential=</varname><varname>LoadCredential=</varname>/<varname>SetCredential=</varname>
+ (see <citerefentry><refentrytitle>systemd.exec</refentrytitle><manvolnum>1</manvolnum></citerefentry> for
details). The following credentials are used when passed in:</para>
<variablelist>
Credentials</ulink> documentation.</para></listitem>
</varlistentry>
+ <varlistentry>
+ <term><varname>ImportCredential=</varname><replaceable>GLOB</replaceable></term>
+
+ <listitem><para>Pass one or more credentials to the unit. Takes a credential name for which we'll
+ attempt to find a credential that the service manager itself received under the specified name —
+ which may be used to propagate credentials from an invoking environment (e.g. a container manager
+ that invoked the service manager) into a service. If the credential name is a glob, all credentials
+ matching the glob are passed to the unit. Matching credentials are searched for in the system
+ credentials, the encrypted system credentials, and under <filename>/etc/credstore/</filename>,
+ <filename>/run/credstore/</filename>, <filename>/usr/lib/credstore/</filename>,
+ <filename>/run/credstore.encrypted/</filename>, <filename>/etc/credstore.encrypted/</filename>, and
+ <filename>/usr/lib/credstore.encrypted/</filename> in that order. When multiple credentials of the
+ same name are found, the first one found is used.</para>
+
+ <para>When multiple credentials of the same name are found, credentials found by
+ <varname>LoadCredential=</varname> and <varname>LoadCredentialEncrypted=</varname> take priority over
+ credentials found by <varname>ImportCredential=</varname></para></listitem>.
+ </varlistentry>
+
<varlistentry>
<term><varname>SetCredential=</varname><replaceable>ID</replaceable>:<replaceable>VALUE</replaceable></term>
<term><varname>SetCredentialEncrypted=</varname><replaceable>ID</replaceable>:<replaceable>VALUE</replaceable></term>
directly from plaintext credentials. For further details see
<varname>LoadCredentialEncrypted=</varname> above.</para>
- <para>If a credential of the same ID is listed in both <varname>LoadCredential=</varname> and
- <varname>SetCredential=</varname>, the latter will act as default if the former cannot be
- retrieved. In this case not being able to retrieve the credential from the path specified in
- <varname>LoadCredential=</varname> is not considered fatal.</para></listitem>
+ <para>When multiple credentials of the same name are found, credentials found by
+ <varname>LoadCredential=</varname>, <varname>LoadCredentialEncrypted=</varname> and
+ <varname>ImportCredential=</varname> take priority over credentials found by
+ <varname>SetCredential=</varname>. As such, <varname>SetCredential=</varname> will act as default if
+ no credentials are found by any of the former. In this case not being able to retrieve the credential
+ from the path specified in <varname>LoadCredential=</varname> or
+ <varname>LoadCredentialEncrypted=</varname> is not considered fatal.</para></listitem>
</varlistentry>
</variablelist>
</refsect1>
<term><varname>$CREDENTIALS_DIRECTORY</varname></term>
<listitem><para>An absolute path to the per-unit directory with credentials configured via
- <varname>LoadCredential=</varname>/<varname>SetCredential=</varname>. The directory is marked
- read-only and is placed in unswappable memory (if supported and permitted), and is only accessible to
- the UID associated with the unit via <varname>User=</varname> or <varname>DynamicUser=</varname> (and
- the superuser).</para></listitem>
+ <varname>ImportCredential=</varname>/<varname>LoadCredential=</varname>/<varname>SetCredential=</varname>.
+ The directory is marked read-only and is placed in unswappable memory (if supported and permitted),
+ and is only accessible to the UID associated with the unit via <varname>User=</varname> or
+ <varname>DynamicUser=</varname> (and the superuser).</para></listitem>
</varlistentry>
<varlistentry>
<row>
<entry>243</entry>
<entry><constant>EXIT_CREDENTIALS</constant></entry>
- <entry>Failed to set up unit's credentials. See <varname>LoadCredential=</varname> and <varname>SetCredential=</varname> above.</entry>
+ <entry>Failed to set up unit's credentials. See <varname>ImportCredential=</varname>, <varname>LoadCredential=</varname> and <varname>SetCredential=</varname> above.</entry>
</row>
<row>
<entry>245</entry>
"http://www.oasis-open.org/docbook/xml/4.2/docbookx.dtd">
<!-- SPDX-License-Identifier: LGPL-2.1-or-later -->
-<refentry id="systemd.link">
+<refentry id="systemd.link"
+ xmlns:xi="http://www.w3.org/2001/XInclude">
<refentryinfo>
<title>systemd.link</title>
<productname>systemd</productname>
credential <literal><replaceable>LINK</replaceable>.link.wol.password</literal> (e.g.,
<literal>60-foo.link.wol.password</literal>), and if the credential not found, then
read from <literal>wol.password</literal>. See
- <varname>LoadCredential=</varname>/<varname>SetCredential=</varname> in
+ <varname>ImportCredential=</varname>/<varname>LoadCredential=</varname>/<varname>SetCredential=</varname> in
<citerefentry><refentrytitle>systemd.exec</refentrytitle><manvolnum>1</manvolnum></citerefentry>
for details. The password in the credential, must be 6 bytes in hex format with each
byte separated by a colon (<literal>:</literal>) like an Ethernet MAC address, e.g.,
<entry>Speed (Mbps)</entry>
<entry>Duplex Mode</entry>
</row></thead>
- <tbody>
- <row><entry><option>10baset-half</option></entry>
- <entry>10</entry><entry>half</entry></row>
-
- <row><entry><option>10baset-full</option></entry>
- <entry>10</entry><entry>full</entry></row>
-
- <row><entry><option>100baset-half</option></entry>
- <entry>100</entry><entry>half</entry></row>
-
- <row><entry><option>100baset-full</option></entry>
- <entry>100</entry><entry>full</entry></row>
-
- <row><entry><option>1000baset-half</option></entry>
- <entry>1000</entry><entry>half</entry></row>
-
- <row><entry><option>1000baset-full</option></entry>
- <entry>1000</entry><entry>full</entry></row>
-
- <row><entry><option>10000baset-full</option></entry>
- <entry>10000</entry><entry>full</entry></row>
-
- <row><entry><option>2500basex-full</option></entry>
- <entry>2500</entry><entry>full</entry></row>
-
- <row><entry><option>1000basekx-full</option></entry>
- <entry>1000</entry><entry>full</entry></row>
-
- <row><entry><option>10000basekx4-full</option></entry>
- <entry>10000</entry><entry>full</entry></row>
-
- <row><entry><option>10000basekr-full</option></entry>
- <entry>10000</entry><entry>full</entry></row>
-
- <row><entry><option>10000baser-fec</option></entry>
- <entry>10000</entry><entry>full</entry></row>
-
- <row><entry><option>20000basemld2-full</option></entry>
- <entry>20000</entry><entry>full</entry></row>
-
- <row><entry><option>20000basekr2-full</option></entry>
- <entry>20000</entry><entry>full</entry></row>
- </tbody>
+ <xi:include href="ethtool-link-mode.xml" />
</tgroup>
</table>
<varname>DefaultMemoryPressureThresholdSec=</varname> setting in
<citerefentry><refentrytitle>systemd-system.conf</refentrytitle><manvolnum>5</manvolnum></citerefentry>
(which in turn defaults to 200ms). The specified value expects a time unit such as
- <literal>ms</literal> or <literal>µs</literal>, see
+ <literal>ms</literal> or <literal>μs</literal>, see
<citerefentry><refentrytitle>systemd.time</refentrytitle><manvolnum>7</manvolnum></citerefentry> for
details on the permitted syntax.</para></listitem>
</varlistentry>
<literal>MONOTONIC_USEC=</literal> set to the current monotonic time
(i.e. <constant>CLOCK_MONOTONIC</constant> in
<citerefentry><refentrytitle>clock_gettime</refentrytitle><manvolnum>2</manvolnum></citerefentry>)
- in µs, formatted as decimal string. Once reloading is complete another notification message must
+ in μs, formatted as decimal string. Once reloading is complete another notification message must
be sent, containing <literal>READY=1</literal>. Using this service type and implementing this
reload protocol is an efficient alternative to providing an <varname>ExecReload=</varname>
command for reloading of the service's configuration.</para></listitem>
<varlistentry>
<term><varname>Backlog=</varname></term>
- <listitem><para>Takes an unsigned integer argument. Specifies
- the number of connections to queue that have not been accepted
- yet. This setting matters only for stream and sequential
- packet sockets. See
- <citerefentry><refentrytitle>listen</refentrytitle><manvolnum>2</manvolnum></citerefentry>
- for details. Defaults to SOMAXCONN (128).</para></listitem>
+ <listitem><para>Takes an unsigned 32bit integer argument. Specifies the number of connections to
+ queue that have not been accepted yet. This setting matters only for stream and sequential packet
+ sockets. See
+ <citerefentry><refentrytitle>listen</refentrytitle><manvolnum>2</manvolnum></citerefentry> for
+ details. Note that this value is silently capped by the <literal>net.core.somaxconn</literal> sysctl,
+ which typically defaults to 4096. By default this is set to 4294967295, so that the sysctl takes full
+ effect.</para></listitem>
</varlistentry>
<varlistentry>
project='freebsd'><refentrytitle>inetd</refentrytitle><manvolnum>8</manvolnum></citerefentry> to work
unmodified with systemd socket activation.</para>
+ <para>Note that depending on this setting the services activated by units of this type are either
+ regular services (in case of <varname>Accept=</varname><option>no</option>) or instances of templated
+ services (in case of <varname>Accept=</varname><option>yes</option>). See the Description section
+ above for a more detailed discussion of the naming rules of triggered services.</para>
+
<para>For IPv4 and IPv6 connections, the <varname>REMOTE_ADDR</varname> environment variable will
contain the remote IP address, and <varname>REMOTE_PORT</varname> will contain the remote port. This
is the same as the format used by CGI. For <constant>SOCK_RAW</constant>, the port is the IP
<varlistentry>
<term><varname>Timestamping=</varname></term>
<listitem><para>Takes one of <literal>off</literal>, <literal>us</literal> (alias:
- <literal>usec</literal>, <literal>µs</literal>) or <literal>ns</literal> (alias:
+ <literal>usec</literal>, <literal>μs</literal>) or <literal>ns</literal> (alias:
<literal>nsec</literal>). This controls the <constant>SO_TIMESTAMP</constant> or
<constant>SO_TIMESTAMPNS</constant> socket options, and enables whether ingress network traffic shall
carry timestamping metadata. Defaults to <option>off</option>.</para></listitem>
understood:</para>
<itemizedlist>
- <listitem><para>usec, us, µs</para></listitem>
+ <listitem><para>usec, us, μs</para></listitem>
<listitem><para>msec, ms</para></listitem>
<listitem><para>seconds, second, sec, s</para></listitem>
<listitem><para>minutes, minute, min, m</para></listitem>
<varlistentry>
<term><varname>OnCalendar=</varname></term>
- <listitem><para>Defines realtime (i.e. wallclock) timers with
- calendar event expressions. See
- <citerefentry><refentrytitle>systemd.time</refentrytitle><manvolnum>7</manvolnum></citerefentry>
- for more information on the syntax of calendar event
- expressions. Otherwise, the semantics are similar to
+ <listitem><para>Defines realtime (i.e. wallclock) timers with calendar event expressions. See
+ <citerefentry><refentrytitle>systemd.time</refentrytitle><manvolnum>7</manvolnum></citerefentry> for
+ more information on the syntax of calendar event expressions. Otherwise, the semantics are similar to
<varname>OnActiveSec=</varname> and related settings.</para>
- <para>Note that timers do not necessarily expire at the
- precise time configured with this setting, as it is subject to
- the <varname>AccuracySec=</varname> setting
- below.</para>
+ <para>Note that timers do not necessarily expire at the precise time configured with this setting, as
+ it is subject to the <varname>AccuracySec=</varname> setting below.</para>
<para>May be specified more than once, in which case the timer unit will trigger whenever any of the
specified expressions elapse. Moreover calendar timers and monotonic timers (see above) may be
<para>If the empty string is assigned to any of these options, the list of timers is reset (both
<varname>OnCalendar=</varname> timers and monotonic timers, see above), and all prior assignments
- will have no effect.</para></listitem>
+ will have no effect.</para>
+
+ <para>Note that calendar timers might be triggered at unexpected times if the system's realtime clock
+ is not set correctly. Specifically, on systems that lack a battery-buffered Realtime Clock (RTC) it
+ might be wise to enable <filename>systemd-time-wait-sync.service</filename> to ensure the clock is
+ adjusted to a network time source <emphasis>before</emphasis> the timer event is set up. Timer units
+ with at least one <varname>OnCalendar=</varname> expression are automatically ordered after
+ <filename>time-sync.target</filename>, which <filename>systemd-time-wait-sync.service</filename> is
+ ordered before.</para>
+
+ <para>When a system is temporarily put to sleep (i.e. system suspend or hibernation) the realtime
+ clock does not pause. When a calendar timer elapses while the system is sleeping it will not be acted
+ on immediately, but once the system is later resumed it will catch up and process all timers that
+ triggered while the system was sleeping. Note that if a calendar timer elapsed more than once while
+ the system was continously sleeping the timer will only result in a single service activation. If
+ <varname>WakeSystem=</varname> (see below) is enabled a calendar time event elapsing while the system
+ is suspended will cause the system to wake up (under the condition the system's hardware supports
+ time-triggered wake-up functionality).</para></listitem>
</varlistentry>
<varlistentry>
</varlistentry>
<varlistentry>
+ <term><varname>OnSuccessJobMode=</varname></term>
<term><varname>OnFailureJobMode=</varname></term>
<listitem><para>Takes a value of
<literal>ignore-dependencies</literal> or
<literal>ignore-requirements</literal>. Defaults to
<literal>replace</literal>. Specifies how the units listed in
- <varname>OnFailure=</varname> will be enqueued. See
+ <varname>OnSuccess=</varname>/<varname>OnFailure=</varname> will be enqueued. See
<citerefentry><refentrytitle>systemctl</refentrytitle><manvolnum>1</manvolnum></citerefentry>'s
<option>--job-mode=</option> option for details on the
possible values. If this is set to <literal>isolate</literal>,
only a single unit may be listed in
- <varname>OnFailure=</varname>.</para></listitem>
+ <varname>OnSuccess=</varname>/<varname>OnFailure=</varname>.</para></listitem>
</varlistentry>
<varlistentry>
<term><varname>systemd.set_credential=</varname></term>
<listitem><para>Sets a system credential, which can then be propagated to system services using the
- <varname>LoadCredential=</varname> setting, see
+ <varname>ImportCredential=</varname> or <varname>LoadCredential=</varname> setting, see
<citerefentry><refentrytitle>systemd.exec</refentrytitle><manvolnum>5</manvolnum></citerefentry> for
details. Takes a pair of credential name and value, separated by a colon. Note that the kernel
command line is typically accessible by unprivileged programs in
<row>
<entry><literal>@t</literal></entry>
<entry>File modification time</entry>
- <entry>Formatted decimal integer, µs since UNIX epoch Jan 1st 1970</entry>
+ <entry>Formatted decimal integer, μs since UNIX epoch Jan 1st 1970</entry>
<entry>Only relevant if target resource type chosen as <constant>regular-file</constant></entry>
</row>
'localstatedir=/var',
'warning_level=2',
],
- meson_version : '>= 0.53.2',
+ meson_version : '>= 0.56.0',
)
libsystemd_version = '0.36.0'
fuzz_tests = want_tests != 'false' and get_option('fuzz-tests')
install_tests = get_option('install-tests')
-if add_languages('cpp', required : fuzzer_build)
+if add_languages('cpp', native : false, required : fuzzer_build)
# Used only for tests
cxx = meson.get_compiler('cpp')
cxx_cmd = ' '.join(cxx.cmd_array())
#####################################################################
# compilation result tests
-conf.set('_GNU_SOURCE', true)
+conf.set('_GNU_SOURCE', 1)
conf.set('__SANE_USERSPACE_TYPES__', true)
conf.set10('HAVE_WSTRINGOP_TRUNCATION', has_wstringop_truncation)
'/usr/sbin/' + prog[0],
'/sbin/' + prog[0],
required: false)
- path = exe.found() ? exe.path() : prog[1]
+ path = exe.found() ? exe.full_path() : prog[1]
endif
name = prog.length() > 2 ? prog[2] : prog[0].to_upper()
conf.set_quoted(name, path)
r = find_program('clang', required : bpf_framework_required, version : '>= 10.0.0')
clang_found = r.found()
if clang_found
- clang = r.path()
+ clang = r.full_path()
endif
else
clang_found = true
link_args : userspace_c_ld_args,
)
+man_page_depends = []
+
############################################################
# binaries that have --help and are intended for use by humans,
subdir('src/libudev')
subdir('src/cryptsetup/cryptsetup-tokens')
-alias_target('devel', libsystemd_pc, libudev_pc)
-
libsystemd = shared_library(
'systemd',
version : libsystemd_version,
subdir('test')
subdir('src/ukify/test') # needs to be last for test_env variable
+alias_target('devel', libsystemd_pc, libudev_pc, systemd_pc, udev_pc)
+
############################################################
# only static linking apart from libdl, to make sure that the
install : true,
install_dir : rootlibexecdir)
-kernel_install = custom_target(
+kernel_install = executable(
'kernel-install',
- input : kernel_install_in,
- output : 'kernel-install',
- command : [jinja2_cmdline, '@INPUT@', '@OUTPUT@'],
+ 'src/kernel-install/kernel-install.c',
+ include_directories : includes,
+ link_with : [libshared],
+ dependencies : [userspace,
+ versiondep],
+ install_rpath : rootpkglibdir,
install : want_kernel_install,
- install_mode : 'rwxr-xr-x',
install_dir : bindir)
-if want_kernel_install
- public_programs += exe
-endif
+public_programs += kernel_install
ukify = custom_target(
'ukify',
install_mode : 'rwxr-xr-x',
install_dir : rootlibexecdir)
if want_ukify
- public_programs += ukify
+ public_programs += ukify
endif
if want_tests != 'false' and want_kernel_install
- args = [kernel_install.full_path(), loaderentry_install, uki_copy_install]
+ args = [kernel_install.full_path(), loaderentry_install.full_path(), uki_copy_install]
+ deps = [kernel_install, loaderentry_install]
if want_ukify and boot_stubs.length() > 0
- args += [ukify.full_path(), ukify_install, boot_stubs[0]]
+ args += [ukify.full_path(), ukify_install.full_path(), boot_stubs[0]]
+ deps += [ukify, ukify_install, boot_stubs[0]]
endif
test('test-kernel-install',
test_kernel_install_sh,
env : test_env,
- args : args)
+ args : args,
+ depends: deps)
endif
############################################################
versiondep,
]
- # FIXME: Use fs.stem() with meson >= 0.54.0
- name = '@0@'.format(sources[0]).split('/')[-1]
+ # FIXME: Drop .format with meson >= 0.59.0
+ name = fs.stem('@0@'.format(sources[0]))
if not name.endswith('.cc')
deps += [userspace]
endif
endif
sources += fuzz_generated_directives
- # FIXME: Use fs.stem() with meson >= 0.54.0
- name = '@0@'.format(sources[0]).split('/')[-1].split('.')[0]
+ # FIXME: Drop .format with meson >= 0.59.0
+ name = fs.stem('@0@'.format(sources[0]))
exe = executable(
name,
# them without OOMing.
[Match]
-Distribution=arch centos
-Release=rolling 8
+Distribution=|arch
+Distribution=|centos
[Host]
QemuMem=3G
[Match]
Distribution=centos
Release=8
+
+[Distribution]
+PackageManagerTrees=mkosi.reposdir:/etc/yum.repos.d
CONFIG_EFI_STUB=y
CONFIG_EFI_ZBOOT=y
CONFIG_EFI=y
+CONFIG_EROFS_FS=y
CONFIG_EXPERT=y
CONFIG_EXT4_FS_POSIX_ACL=y
CONFIG_EXT4_FS_SECURITY=y
CONFIG_ISO9660_FS=y
CONFIG_KEXEC=y
CONFIG_KPROBES=y
+CONFIG_KSM=y
CONFIG_LOAD_UEFI_KEYS=y
CONFIG_MAC80211=y
CONFIG_MAGIC_SYSRQ=y
CONFIG_XFRM_USER=y
CONFIG_XFS_FS=y
CONFIG_XFS_POSIX_ACL=y
-CONFIG_KSM=y
# CONFIG_WIRELESS is not set
# CONFIG_WLAN is not set
# SPDX-License-Identifier: LGPL-2.1-or-later
[Match]
-Distribution=centos fedora
+Distribution=|centos
+Distribution=|fedora
[Content]
Packages=
# SPDX-License-Identifier: LGPL-2.1-or-later
[Match]
-Distribution=debian ubuntu
+Distribution=|debian
+Distribution=|ubuntu
[Content]
Packages=
# SPDX-License-Identifier: LGPL-2.1-or-later
[Match]
-Bootable=auto,yes
+Bootable=|auto
+Bootable=|yes
[Output]
Format=cpio
# SPDX-License-Identifier: LGPL-2.1-or-later
[Match]
-Distribution=arch debian fedora opensuse ubuntu
+Distribution=!centos
[Output]
CompressOutput=zst
openssl
qrencode
sed
+ socat
strace
tmux
tree
[Content]
Packages=
+ bpf
btrfs-progs
compsize
dhcp
# SPDX-License-Identifier: LGPL-2.1-or-later
[Match]
-Distribution=centos fedora
+Distribution=|centos
+Distribution=|fedora
[Content]
Packages=
+ bpftool
cryptsetup
dhcp-server
dnf
# SPDX-License-Identifier: LGPL-2.1-or-later
[Match]
-Distribution=debian ubuntu
+Distribution=|debian
+Distribution=|ubuntu
[Content]
Packages=
[Content]
Packages=
+ bpftool
linux-image-cloud-amd64
[Content]
Packages=
+ bpftool
btrfs-progs
cryptsetup
dbus-broker
# We would like to use linux-image-kvm but it does not have support for dm-verity
# See https://bugs.launchpad.net/ubuntu/+source/linux-meta-kvm/+bug/2019040.
linux-image-generic
+ linux-tools-common
+ linux-tools-generic
[Match]
PathExists=../../mkosi.kernel/
-Distribution=centos fedora
+Distribution=|centos
+Distribution=|fedora
[Content]
Packages=
[Match]
PathExists=../../mkosi.kernel/
-Distribution=debian ubuntu
+Distribution=|debian
+Distribution=|ubuntu
[Content]
Packages=
#!/bin/bash -eux
# SPDX-License-Identifier: LGPL-2.1-or-later
-rm -f /testok
-
# TODO: Figure out why this is failing
systemctl reset-failed systemd-vconsole-setup.service
# Exit with non-zero EC if the /failed-services file is not empty (we have -e set)
[[ ! -s /failed-services ]]
-touch /testok
+# On success, exit with 123 so that we can check that we receive the actual exit code from the script on the
+# host.
+exit 123
#
# Italian translation for systemd package
# Traduzione in italiano per il pacchetto systemd
-# Daniele Medri <dmedri@gmail.com>, 2013-2022.
+# Daniele Medri <dmedri@gmail.com>, 2013-2023.
msgid ""
msgstr ""
"Project-Id-Version: \n"
"Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2022-10-20 10:35+0200\n"
-"PO-Revision-Date: 2022-04-26 15:57+0200\n"
+"POT-Creation-Date: 2023-06-05 11:40+0200\n"
+"PO-Revision-Date: 2023-06-05 11:44+0200\n"
"Last-Translator: Daniele Medri <dmedri@gmail.com>\n"
"Language-Team: italian\n"
"Language: it\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=2; plural=n != 1;\n"
-"X-Generator: Poedit 2.4.2\n"
+"X-Generator: Poedit 3.2.2\n"
#: src/core/org.freedesktop.systemd1.policy.in:22
msgid "Send passphrase back to system"
msgid "Authentication is required to reload the systemd state."
msgstr "Autenticazione richiesta per ricaricare lo stato di systemd."
+#: src/core/org.freedesktop.systemd1.policy.in:74
+msgid "Dump the systemd state without rate limits"
+msgstr "Eseguire il dump dello stato di systemd senza limiti di frequenza"
+
+#: src/core/org.freedesktop.systemd1.policy.in:75
+msgid ""
+"Authentication is required to dump the systemd state without rate limits."
+msgstr ""
+"L'autenticazione è richiesta per il dump dello stato di systemd senza limiti "
+"di frequenza."
+
#: src/home/org.freedesktop.home1.policy:13
msgid "Create a home area"
msgstr "Crea un'area home"
"Autenticazione richiesta per verificare se la sincronizzazione dell'orario "
"in rete deve essere attivata."
-#: src/core/dbus-unit.c:359
+#: src/core/dbus-unit.c:364
msgid "Authentication is required to start '$(unit)'."
msgstr "Autenticazione richiesta per avviare '$(unit)'."
-#: src/core/dbus-unit.c:360
+#: src/core/dbus-unit.c:365
msgid "Authentication is required to stop '$(unit)'."
msgstr "Autenticazione richiesta per fermare '$(unit)'."
-#: src/core/dbus-unit.c:361
+#: src/core/dbus-unit.c:366
msgid "Authentication is required to reload '$(unit)'."
msgstr "Autenticazione richiesta per ricaricare '$(unit)'."
-#: src/core/dbus-unit.c:362 src/core/dbus-unit.c:363
+#: src/core/dbus-unit.c:367 src/core/dbus-unit.c:368
msgid "Authentication is required to restart '$(unit)'."
msgstr "Autenticazione richiesta per riavviare '$(unit)'."
-#: src/core/dbus-unit.c:546
+#: src/core/dbus-unit.c:565
msgid ""
"Authentication is required to send a UNIX signal to the processes of "
"'$(unit)'."
"Autenticazione richiesta per inviare un segnale UNIX ai processi di "
"'$(unit)'."
-#: src/core/dbus-unit.c:576
+#: src/core/dbus-unit.c:595
msgid "Authentication is required to reset the \"failed\" state of '$(unit)'."
msgstr ""
"Autenticazione richiesta per riconfigurare lo stato \"fallito\" di '$(unit)'."
-#: src/core/dbus-unit.c:608
+#: src/core/dbus-unit.c:627
msgid "Authentication is required to set properties on '$(unit)'."
msgstr "Autenticazione richiesta per configurare le proprietà di '$(unit)'."
-#: src/core/dbus-unit.c:714
+#: src/core/dbus-unit.c:728
msgid ""
"Authentication is required to delete files and directories associated with "
"'$(unit)'."
"Autenticazione richiesta per eliminare i file e le directory associate a "
"'$(unit)'."
-#: src/core/dbus-unit.c:762
+#: src/core/dbus-unit.c:776
msgid ""
"Authentication is required to freeze or thaw the processes of '$(unit)' unit."
msgstr ""
msgstr ""
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2022-10-20 10:35+0200\n"
-"PO-Revision-Date: 2023-06-03 15:48+0000\n"
+"PO-Revision-Date: 2023-06-13 20:18+0000\n"
"Last-Translator: 김인수 <simmon@nplob.com>\n"
"Language-Team: Korean <https://translate.fedoraproject.org/projects/systemd/"
"master/ko/>\n"
#: src/home/org.freedesktop.home1.policy:63
msgid "Change password of a home area"
-msgstr "홈 영역 암호 변경"
+msgstr "홈 영역 비밀번호 변경"
#: src/home/org.freedesktop.home1.policy:64
msgid ""
enable getty@.service
enable systemd-timesyncd.service
enable systemd-networkd.service
+enable systemd-networkd-wait-online.service
enable systemd-network-generator.service
enable systemd-resolved.service
enable systemd-homed.service
disable rescue.target
disable exit.target
-disable systemd-networkd-wait-online.service
disable systemd-time-wait-sync.service
disable systemd-boot-check-no-failures.service
disable proc-sys-fs-binfmt_misc.mount
# Apply sysctl variables to network devices (and only to those) as they appear.
ACTION=="add", SUBSYSTEM=="net", KERNEL!="lo", RUN+="{{ROOTLIBEXECDIR}}/systemd-sysctl --prefix=/net/ipv4/conf/$name --prefix=/net/ipv4/neigh/$name --prefix=/net/ipv6/conf/$name --prefix=/net/ipv6/neigh/$name"
+{% if ENABLE_BACKLIGHT %}
# Pull in backlight save/restore for all backlight devices and
# keyboard backlights
SUBSYSTEM=="backlight", TAG+="systemd", IMPORT{builtin}="path_id", ENV{SYSTEMD_WANTS}+="systemd-backlight@backlight:$name.service"
SUBSYSTEM=="leds", KERNEL=="*kbd_backlight", TAG+="systemd", IMPORT{builtin}="path_id", ENV{SYSTEMD_WANTS}+="systemd-backlight@leds:$name.service"
+{% endif %}
# Pull in rfkill save/restore for all rfkill devices
SUBSYSTEM=="rfkill", ENV{SYSTEMD_RFKILL}="1"
if bashcompletiondir == ''
bash_completion = dependency('bash-completion', required : false)
if bash_completion.found()
- bashcompletiondir = bash_completion.get_pkgconfig_variable('completionsdir')
+ bashcompletiondir = bash_completion.get_variable(pkgconfig : 'completionsdir')
else
bashcompletiondir = datadir / 'bash-completion/completions'
endif
local expl suf
_locales=( ${(f)"$(_call_program locales "$service" list-locales)"} )
- compset -P1 '*='
if [[ -prefix 1 *\= ]]; then
+ compset -P1 '*='
local conf=${PREFIX%%\=*}
_wanted locales expl "locales configs" \
_combination localeconfs confs=$conf locales "$@" -
" -h --help Show this help\n"
" --version Show package version\n"
" -v --verbose Show state as text\n"
- " --low Checks if battery is discharing and low\n",
+ " --low Check if battery is discharging and low\n",
program_invocation_short_name);
}
" --id=ID Query identifier (e.g. \"cryptsetup:/dev/sda5\")\n"
" --keyname=NAME Kernel key name for caching passwords (e.g. \"cryptsetup\")\n"
" --credential=NAME\n"
- " Credential name for LoadCredential=/SetCredential=\n"
- " credentials\n"
+ " Credential name for ImportCredential=, LoadCredential= or\n"
+ " SetCredential= credentials\n"
" --timeout=SEC Timeout in seconds\n"
" --echo=yes|no|masked\n"
" Control whether to show password while typing (echo)\n"
const char *todo;
int r;
- assert(path);
assert(!FLAGS_SET(flags, CHASE_PREFIX_ROOT));
assert(!FLAGS_SET(flags, CHASE_STEP|CHASE_EXTRACT_FILENAME));
assert(!FLAGS_SET(flags, CHASE_TRAIL_SLASH|CHASE_EXTRACT_FILENAME));
return 0;
}
+int chase_extract_filename(const char *path, const char *root, char **ret) {
+ int r;
+
+ /* This is similar to path_extract_filename(), but takes root directory.
+ * The result should be consistent with chase() with CHASE_EXTRACT_FILENAME. */
+
+ assert(path);
+ assert(ret);
+
+ if (isempty(path))
+ return -EINVAL;
+
+ if (!path_is_absolute(path))
+ return -EINVAL;
+
+ if (!empty_or_root(root)) {
+ _cleanup_free_ char *root_abs = NULL;
+
+ r = path_make_absolute_cwd(root, &root_abs);
+ if (r < 0)
+ return r;
+
+ path = path_startswith(path, root_abs);
+ if (!path)
+ return -EINVAL;
+ }
+
+ if (!isempty(path)) {
+ r = path_extract_filename(path, ret);
+ if (r != -EADDRNOTAVAIL)
+ return r;
+ }
+
+ char *fname = strdup(".");
+ if (!fname)
+ return -ENOMEM;
+
+ *ret = fname;
+ return 0;
+}
+
int chase_and_open(const char *path, const char *root, ChaseFlags chase_flags, int open_flags, char **ret_path) {
_cleanup_close_ int path_fd = -EBADF;
_cleanup_free_ char *p = NULL, *fname = NULL;
mode_t mode = open_flags & O_DIRECTORY ? 0755 : 0644;
- const char *q;
int r;
assert(!(chase_flags & (CHASE_NONEXISTENT|CHASE_STEP)));
return r;
assert(path_fd >= 0);
- assert_se(q = path_startswith(p, empty_to_root(root)));
- if (isempty(q))
- q = ".";
-
- if (!FLAGS_SET(chase_flags, CHASE_PARENT)) {
- r = path_extract_filename(q, &fname);
- if (r < 0 && r != -EADDRNOTAVAIL)
+ if (!FLAGS_SET(chase_flags, CHASE_PARENT) &&
+ !FLAGS_SET(chase_flags, CHASE_EXTRACT_FILENAME)) {
+ r = chase_extract_filename(p, root, &fname);
+ if (r < 0)
return r;
}
int chase(const char *path_with_prefix, const char *root, ChaseFlags chase_flags, char **ret_path, int *ret_fd);
int chaseat_prefix_root(const char *path, const char *root, char **ret);
+int chase_extract_filename(const char *path, const char *root, char **ret);
int chase_and_open(const char *path, const char *root, ChaseFlags chase_flags, int open_flags, char **ret_path);
int chase_and_opendir(const char *path, const char *root, ChaseFlags chase_flags, char **ret_path, DIR **ret_dir);
#include "chattr-util.h"
#include "errno-util.h"
#include "fd-util.h"
+#include "fs-util.h"
#include "macro.h"
#include "string-util.h"
-int chattr_full(const char *path,
- int fd,
- unsigned value,
- unsigned mask,
- unsigned *ret_previous,
- unsigned *ret_final,
- ChattrApplyFlags flags) {
+int chattr_full(
+ int dir_fd,
+ const char *path,
+ unsigned value,
+ unsigned mask,
+ unsigned *ret_previous,
+ unsigned *ret_final,
+ ChattrApplyFlags flags) {
- _cleanup_close_ int fd_will_close = -EBADF;
+ _cleanup_close_ int fd = -EBADF;
unsigned old_attr, new_attr;
int set_flags_errno = 0;
struct stat st;
- assert(path || fd >= 0);
+ assert(dir_fd >= 0 || dir_fd == AT_FDCWD);
- if (fd < 0) {
- fd = fd_will_close = open(path, O_RDONLY|O_CLOEXEC|O_NOCTTY|O_NOFOLLOW);
- if (fd < 0)
- return -errno;
- }
+ fd = xopenat(dir_fd, path, O_RDONLY|O_CLOEXEC|O_NOCTTY|O_NOFOLLOW, /* xopen_flags = */ 0, /* mode = */ 0);
+ if (fd < 0)
+ return -errno;
if (fstat(fd, &st) < 0)
return -errno;
/* SPDX-License-Identifier: LGPL-2.1-or-later */
#pragma once
+#include <fcntl.h>
#include <linux/fs.h>
#include <stdbool.h>
#include <stddef.h>
CHATTR_WARN_UNSUPPORTED_FLAGS = 1 << 1,
} ChattrApplyFlags;
-int chattr_full(const char *path, int fd, unsigned value, unsigned mask, unsigned *ret_previous, unsigned *ret_final, ChattrApplyFlags flags);
-
+int chattr_full(int dir_fd, const char *path, unsigned value, unsigned mask, unsigned *ret_previous, unsigned *ret_final, ChattrApplyFlags flags);
+static inline int chattr_at(int dir_fd, const char *path, unsigned value, unsigned mask, unsigned *previous) {
+ return chattr_full(dir_fd, path, value, mask, previous, NULL, 0);
+}
static inline int chattr_fd(int fd, unsigned value, unsigned mask, unsigned *previous) {
- return chattr_full(NULL, fd, value, mask, previous, NULL, 0);
+ return chattr_full(fd, NULL, value, mask, previous, NULL, 0);
}
static inline int chattr_path(const char *path, unsigned value, unsigned mask, unsigned *previous) {
- return chattr_full(path, -1, value, mask, previous, NULL, 0);
+ return chattr_full(AT_FDCWD, path, value, mask, previous, NULL, 0);
}
int read_attr_fd(int fd, unsigned *ret);
#define CHATTR_SECRET_FLAGS (FS_SECRM_FL|FS_NODUMP_FL|FS_SYNC_FL|FS_NOCOW_FL)
static inline int chattr_secret(int fd, ChattrApplyFlags flags) {
- return chattr_full(NULL, fd, CHATTR_SECRET_FLAGS, CHATTR_SECRET_FLAGS, NULL, NULL, flags|CHATTR_FALLBACK_BITWISE);
+ return chattr_full(fd, NULL, CHATTR_SECRET_FLAGS, CHATTR_SECRET_FLAGS, NULL, NULL, flags|CHATTR_FALLBACK_BITWISE);
}
/* SPDX-License-Identifier: LGPL-2.1-or-later */
#pragma once
-extern void __gcov_dump(void);
-extern void __gcov_reset(void);
+/* Use the coverage-related tweaks below only for C stuff as they're not really
+ * C++ compatible, and the only thing that is built with a C++ compiler is
+ * the lone test-bus-vtable-cc unit test.
+ */
+#ifndef __cplusplus
+
+void __gcov_dump(void);
+void __gcov_reset(void);
/* When built with --coverage (gcov) we need to explicitly call __gcov_dump()
* in places where we use _exit(), since _exit() skips at-exit hooks resulting
* explicitly on the compiler command line via the -include directive (only
* when built with -Db_coverage=true)
*/
-extern void _exit(int);
+void _exit(int);
static inline _Noreturn void _coverage__exit(int status) {
__gcov_dump();
* [0] https://gcc.gnu.org/git/?p=gcc.git;a=blob;f=libgcc/libgcov-interface.c;h=b2ee930864183b78c8826255183ca86e15e21ded;hb=HEAD
*/
-extern int execveat(int, const char *, char * const [], char * const [], int);
-extern int execvpe(const char *, char * const [], char * const []);
+int execveat(int, const char *, char * const [], char * const [], int);
+int execvpe(const char *, char * const [], char * const []);
static inline int _coverage_execveat(
int dirfd,
return r;
}
#define execvpe(f,a,e) _coverage_execvpe(f, a, e)
+
+#endif
return 0;
}
-int dir_fd_is_root(int dir_fd) {
+int path_is_root_at(int dir_fd, const char *path) {
STRUCT_NEW_STATX_DEFINE(st);
STRUCT_NEW_STATX_DEFINE(pst);
+ _cleanup_close_ int fd = -EBADF;
int r;
- assert(dir_fd >= 0);
+ assert(dir_fd >= 0 || dir_fd == AT_FDCWD);
+
+ if (!isempty(path)) {
+ fd = openat(dir_fd, path, O_PATH|O_DIRECTORY|O_CLOEXEC);
+ if (fd < 0)
+ return errno == ENOTDIR ? false : -errno;
+
+ dir_fd = fd;
+ }
r = statx_fallback(dir_fd, ".", 0, STATX_TYPE|STATX_INO|STATX_MNT_ID, &st.sx);
if (r == -ENOTDIR)
int read_nr_open(void);
int fd_get_diskseq(int fd, uint64_t *ret);
-int dir_fd_is_root(int dir_fd);
+int path_is_root_at(int dir_fd, const char *path);
+static inline int dir_fd_is_root(int dir_fd) {
+ return path_is_root_at(dir_fd, NULL);
+}
static inline int dir_fd_is_root_or_cwd(int dir_fd) {
- return dir_fd == AT_FDCWD ? true : dir_fd_is_root(dir_fd);
+ return dir_fd == AT_FDCWD ? true : path_is_root_at(dir_fd, NULL);
}
/* The maximum length a buffer for a /proc/self/fd/<fd> path needs */
int r;
assert(dir_fd >= 0 || dir_fd == AT_FDCWD);
- assert(path);
if (isempty(path)) {
assert(!FLAGS_SET(open_flags, O_CREAT|O_EXCL));
'terminal-util.c',
'time-util.c',
'tmpfile-util.c',
+ 'uid-alloc-range.c',
'uid-range.c',
'unit-def.c',
'unit-file.c',
if (detect_container() > 0)
return get_process_cmdline(1, SIZE_MAX, 0, ret);
else
- return read_one_line_file("/proc/cmdline", ret);
+ return read_full_file("/proc/cmdline", ret, NULL);
}
static int proc_cmdline_strv_internal(char ***ret, bool filter_pid1_args) {
} else {
_cleanup_free_ char *s = NULL;
- r = read_one_line_file("/proc/cmdline", &s);
+ r = read_full_file("/proc/cmdline", &s, NULL);
if (r < 0)
return r;
(uint64_t) irq_ticks + (uint64_t) softirq_ticks +
(uint64_t) guest_ticks + (uint64_t) guest_nice_ticks;
- /* Let's reduce this fraction before we apply it to avoid overflows when converting this to µsec */
+ /* Let's reduce this fraction before we apply it to avoid overflows when converting this to μsec */
gcd = calc_gcd64(NSEC_PER_SEC, ticks_per_second);
a = (uint64_t) NSEC_PER_SEC / gcd;
_cleanup_free_ char *n = NULL;
char *e, *cid_start;
unsigned port, cid;
- int r;
+ int type, r;
assert(ret_address);
assert(s);
- cid_start = startswith(s, "vsock:");
- if (!cid_start)
+ if ((cid_start = startswith(s, "vsock:")))
+ type = 0;
+ else if ((cid_start = startswith(s, "vsock-dgram:")))
+ type = SOCK_DGRAM;
+ else if ((cid_start = startswith(s, "vsock-seqpacket:")))
+ type = SOCK_SEQPACKET;
+ else if ((cid_start = startswith(s, "vsock-stream:")))
+ type = SOCK_STREAM;
+ else
return -EPROTO;
e = strchr(cid_start, ':');
.svm_family = AF_VSOCK,
.svm_port = port,
},
+ .type = type,
.size = sizeof(struct sockaddr_vm),
};
* protocol mismatch. */
int socket_address_parse_unix(SocketAddress *ret_address, const char *s);
int socket_address_parse_vsock(SocketAddress *ret_address, const char *s);
+
+/* libc's SOMAXCONN is defined to 128 or 4096 (at least on glibc). But actually, the value can be much
+ * larger. In our codebase we want to set it to the max usually, since noawadays socket memory is properly
+ * tracked by memcg, and hence we don't need to enforce extra limits here. Moreover, the kernel caps it to
+ * /proc/sys/net/core/somaxconn anyway, thus by setting this to unbounded we just make that sysctl file
+ * authoritative. */
+#define SOMAXCONN_DELUXE INT_MAX
return F_TYPE_EQUAL(s->f_type, magic_value);
}
-int fd_is_fs_type(int fd, statfs_f_type_t magic_value) {
- struct statfs s;
-
- if (fstatfs(fd, &s) < 0)
- return -errno;
-
- return is_fs_type(&s, magic_value);
-}
-
-int path_is_fs_type(const char *path, statfs_f_type_t magic_value) {
+int is_fs_type_at(int dir_fd, const char *path, statfs_f_type_t magic_value) {
struct statfs s;
+ int r;
- if (statfs(path, &s) < 0)
- return -errno;
+ r = xstatfsat(dir_fd, path, &s);
+ if (r < 0)
+ return r;
return is_fs_type(&s, magic_value);
}
_cleanup_close_ int fd = -EBADF;
assert(dir_fd >= 0 || dir_fd == AT_FDCWD);
- assert(path);
assert(ret);
fd = xopenat(dir_fd, path, O_PATH|O_CLOEXEC|O_NOCTTY, /* xopen_flags = */ 0, /* mode = */ 0);
typedef typeof(((struct statfs*)NULL)->f_type) statfs_f_type_t;
bool is_fs_type(const struct statfs *s, statfs_f_type_t magic_value) _pure_;
-int fd_is_fs_type(int fd, statfs_f_type_t magic_value);
-int path_is_fs_type(const char *path, statfs_f_type_t magic_value);
+int is_fs_type_at(int dir_fd, const char *path, statfs_f_type_t magic_value);
+static inline int fd_is_fs_type(int fd, statfs_f_type_t magic_value) {
+ return is_fs_type_at(fd, NULL, magic_value);
+}
+static inline int path_is_fs_type(const char *path, statfs_f_type_t magic_value) {
+ return is_fs_type_at(AT_FDCWD, path, magic_value);
+}
bool is_temporary_fs(const struct statfs *s) _pure_;
bool is_network_fs(const struct statfs *s) _pure_;
if (l < (size_t) (1 + 1 + 1))
return NULL; /* not enough space for even the shortest of forms */
- return snprintf_ok(buf, l, "@" USEC_FMT, t / USEC_PER_SEC); /* round down µs → s */
+ return snprintf_ok(buf, l, "@" USEC_FMT, t / USEC_PER_SEC); /* round down μs → s */
}
utc = IN_SET(style, TIMESTAMP_UTC, TIMESTAMP_US_UTC, TIMESTAMP_DATE);
{ "y", USEC_PER_YEAR },
{ "usec", 1ULL },
{ "us", 1ULL },
- { "µs", 1ULL },
+ { "μs", 1ULL }, /* U+03bc (aka GREEK SMALL LETTER MU) */
+ { "µs", 1ULL }, /* U+b5 (aka MICRO SIGN) */
};
assert(p);
{ "y", NSEC_PER_YEAR },
{ "usec", NSEC_PER_USEC },
{ "us", NSEC_PER_USEC },
- { "µs", NSEC_PER_USEC },
+ { "μs", NSEC_PER_USEC }, /* U+03bc (aka GREEK LETTER MU) */
+ { "µs", NSEC_PER_USEC }, /* U+b5 (aka MICRO SIGN) */
{ "nsec", 1ULL },
{ "ns", 1ULL },
{ "", 1ULL }, /* default is nsec */
t = (TimestampStyle) string_table_lookup(timestamp_style_table, ELEMENTSOF(timestamp_style_table), s);
if (t >= 0)
return t;
- if (streq_ptr(s, "µs"))
+ if (STRPTR_IN_SET(s, "µs", "μs")) /* acccept both µ symbols in unicode, i.e. micro symbol + greek small letter mu. */
return TIMESTAMP_US;
- if (streq_ptr(s, "µs+utc"))
+ if (STRPTR_IN_SET(s, "µs+utc", "μs+utc"))
return TIMESTAMP_US_UTC;
return t;
}
#include "stat-util.h"
#include "stdio-util.h"
#include "string-util.h"
+#include "sync-util.h"
#include "tmpfile-util.h"
#include "umask-util.h"
return RET_NERRNO(linkat(fd, "", newdirfd, newpath, AT_EMPTY_PATH));
}
-int link_tmpfile_at(int fd, int dir_fd, const char *path, const char *target, bool replace) {
+int link_tmpfile_at(int fd, int dir_fd, const char *path, const char *target, LinkTmpfileFlags flags) {
_cleanup_free_ char *tmp = NULL;
int r;
* an fd created with O_TMPFILE is assumed, and linkat() is used. Otherwise it is assumed O_TMPFILE
* is not supported on the directory, and renameat2() is used instead. */
+ if (FLAGS_SET(flags, LINK_TMPFILE_SYNC) && fsync(fd) < 0)
+ return -errno;
+
if (path) {
- if (replace)
- return RET_NERRNO(renameat(dir_fd, path, dir_fd, target));
+ if (FLAGS_SET(flags, LINK_TMPFILE_REPLACE))
+ r = RET_NERRNO(renameat(dir_fd, path, dir_fd, target));
+ else
+ r = rename_noreplace(dir_fd, path, dir_fd, target);
+ if (r < 0)
+ return r;
+ } else {
- return rename_noreplace(dir_fd, path, dir_fd, target);
- }
+ r = link_fd(fd, dir_fd, target);
+ if (r != -EEXIST || !FLAGS_SET(flags, LINK_TMPFILE_REPLACE))
+ return r;
- r = link_fd(fd, dir_fd, target);
- if (r != -EEXIST || !replace)
- return r;
+ /* So the target already exists and we were asked to replace it. That sucks a bit, since the kernel's
+ * linkat() logic does not allow that. We work-around this by linking the file to a random name
+ * first, and then renaming that to the final name. This reintroduces the race O_TMPFILE kinda is
+ * trying to fix, but at least the vulnerability window (i.e. where the file is linked into the file
+ * system under a temporary name) is very short. */
- /* So the target already exists and we were asked to replace it. That sucks a bit, since the kernel's
- * linkat() logic does not allow that. We work-around this by linking the file to a random name
- * first, and then renaming that to the final name. This reintroduces the race O_TMPFILE kinda is
- * trying to fix, but at least the vulnerability window (i.e. where the file is linked into the file
- * system under a temporary name) is very short. */
+ r = tempfn_random(target, NULL, &tmp);
+ if (r < 0)
+ return r;
- r = tempfn_random(target, NULL, &tmp);
- if (r < 0)
- return r;
+ if (link_fd(fd, dir_fd, tmp) < 0)
+ return -EEXIST; /* propagate original error */
- if (link_fd(fd, dir_fd, tmp) < 0)
- return -EEXIST; /* propagate original error */
+ r = RET_NERRNO(renameat(dir_fd, tmp, dir_fd, target));
+ if (r < 0) {
+ (void) unlinkat(dir_fd, tmp, 0);
+ return r;
+ }
+ }
- r = RET_NERRNO(renameat(dir_fd, tmp, dir_fd, target));
- if (r < 0) {
- (void) unlinkat(dir_fd, tmp, 0);
- return r;
+ if (FLAGS_SET(flags, LINK_TMPFILE_SYNC)) {
+ r = fsync_full(fd);
+ if (r < 0)
+ return r;
}
return 0;
}
-int flink_tmpfile(FILE *f, const char *path, const char *target, bool replace) {
+int flink_tmpfile(FILE *f, const char *path, const char *target, LinkTmpfileFlags flags) {
int fd, r;
assert(f);
if (fd < 0) /* Not all FILE* objects encapsulate fds */
return -EBADF;
- r = fflush_sync_and_check(f);
+ r = fflush_and_check(f);
if (r < 0)
return r;
- return link_tmpfile(fd, path, target, replace);
+ return link_tmpfile(fd, path, target, flags);
}
int mkdtemp_malloc(const char *template, char **ret) {
}
int fopen_tmpfile_linkable(const char *target, int flags, char **ret_path, FILE **ret_file);
-int link_tmpfile_at(int fd, int dir_fd, const char *path, const char *target, bool replace);
-static inline int link_tmpfile(int fd, const char *path, const char *target, bool replace) {
- return link_tmpfile_at(fd, AT_FDCWD, path, target, replace);
+
+typedef enum LinkTmpfileFlags {
+ LINK_TMPFILE_REPLACE = 1 << 0,
+ LINK_TMPFILE_SYNC = 1 << 1,
+} LinkTmpfileFlags;
+
+int link_tmpfile_at(int fd, int dir_fd, const char *path, const char *target, LinkTmpfileFlags flags);
+static inline int link_tmpfile(int fd, const char *path, const char *target, LinkTmpfileFlags flags) {
+ return link_tmpfile_at(fd, AT_FDCWD, path, target, flags);
}
-int flink_tmpfile(FILE *f, const char *path, const char *target, bool replace);
+int flink_tmpfile(FILE *f, const char *path, const char *target, LinkTmpfileFlags flags);
int mkdtemp_malloc(const char *template, char **ret);
int mkdtemp_open(const char *template, int flags, char **ret);
return gid <= defs->system_gid_max;
}
+
+bool uid_for_system_journal(uid_t uid) {
+
+ /* Returns true if the specified UID shall get its data stored in the system journal. */
+
+ return uid_is_system(uid) || uid_is_dynamic(uid) || uid == UID_NOBODY || uid_is_container(uid);
+}
int read_login_defs(UGIDAllocationRange *ret_defs, const char *path, const char *root);
const UGIDAllocationRange *acquire_ugid_allocation_range(void);
+
+bool uid_for_system_journal(uid_t uid);
fprintf(f, "default %s-*\n", arg_entry_token);
}
- r = flink_tmpfile(f, t, p, /* replace= */ false);
+ r = flink_tmpfile(f, t, p, LINK_TMPFILE_SYNC);
if (r == -EEXIST)
return 0; /* Silently skip creation if the file exists now (recheck) */
if (r < 0)
fprintf(f, "type1\n");
- r = flink_tmpfile(f, t, p, /* replace= */ false);
+ r = flink_tmpfile(f, t, p, LINK_TMPFILE_SYNC);
if (r == -EEXIST)
return 0; /* Silently skip creation if the file exists now (recheck) */
if (r < 0)
" update Update systemd-boot in the ESP and EFI variables\n"
" remove Remove systemd-boot from the ESP and EFI variables\n"
" is-installed Test whether systemd-boot is installed in the ESP\n"
- " random-seed Initialize random seed in ESP and EFI variables\n"
+ " random-seed Initialize or refresh random seed in ESP and EFI\n"
+ " variables\n"
"\n%3$sKernel Image Commands:%4$s\n"
" kernel-identify Identify kernel image type\n"
" kernel-inspect Prints details about the kernel image\n"
config_entry_free(config->entries[i]);
free(config->entries);
free(config->entry_default_config);
+ free(config->entry_default_efivar);
free(config->entry_oneshot);
+ free(config->entry_saved);
}
static void config_write_entries_to_variable(Config *config) {
* By default, Shim uninstalls its protocol when calling StartImage(). */
shim_retain_protocol();
- err = BS->OpenProtocol(
- image,
- MAKE_GUID_PTR(EFI_LOADED_IMAGE_PROTOCOL),
- (void **) &loaded_image,
- image,
- NULL,
- EFI_OPEN_PROTOCOL_GET_PROTOCOL);
+ err = BS->HandleProtocol(image, MAKE_GUID_PTR(EFI_LOADED_IMAGE_PROTOCOL), (void **) &loaded_image);
if (err != EFI_SUCCESS)
return log_error_status(err, "Error getting a LoadedImageProtocol handle: %m");
config_load_all_entries(&config, loaded_image, loaded_image_path, root_dir);
- if (config.entry_count == 0) {
- log_error("No loader found. Configuration files in \\loader\\entries\\*.conf are needed.");
- goto out;
- }
+ if (config.entry_count == 0)
+ return log_error_status(
+ EFI_NOT_FOUND,
+ "No loader found. Configuration files in \\loader\\entries\\*.conf are needed.");
/* select entry or show menu when key is pressed or timeout is set */
if (config.force_menu || config.timeout_sec > 0)
if (menu) {
efivar_set_time_usec(MAKE_GUID_PTR(LOADER), u"LoaderTimeMenuUSec", 0);
if (!menu_run(&config, &entry, loaded_image_path))
- break;
+ return EFI_SUCCESS;
}
/* if auto enrollment is activated, we try to load keys for the given entry. */
err = image_start(image, entry);
if (err != EFI_SUCCESS)
- goto out;
+ return err;
menu = true;
config.timeout_sec = 0;
}
- err = EFI_SUCCESS;
-out:
- BS->CloseProtocol(image, MAKE_GUID_PTR(EFI_LOADED_IMAGE_PROTOCOL), image, NULL);
- return err;
}
DEFINE_EFI_MAIN_FUNCTION(run, "systemd-boot", /*wait_for_debugger=*/false);
return EFI_SUCCESS;
/* get the LoadFile2 protocol that we allocated earlier */
- err = BS->OpenProtocol(
- initrd_handle,
- MAKE_GUID_PTR(EFI_LOAD_FILE2_PROTOCOL),
- (void **) &loader,
- NULL,
- NULL,
- EFI_OPEN_PROTOCOL_GET_PROTOCOL);
+ err = BS->HandleProtocol(initrd_handle, MAKE_GUID_PTR(EFI_LOAD_FILE2_PROTOCOL), (void **) &loader);
if (err != EFI_SUCCESS)
return err;
- /* close the handle */
- (void) BS->CloseProtocol(initrd_handle, MAKE_GUID_PTR(EFI_LOAD_FILE2_PROTOCOL), NULL, NULL);
-
/* uninstall all protocols thus destroying the handle */
err = BS->UninstallMultipleProtocolInterfaces(
initrd_handle, MAKE_GUID_PTR(EFI_DEVICE_PATH_PROTOCOL),
uint64_t loader_features = 0;
EFI_STATUS err;
- err = BS->OpenProtocol(
- image,
- MAKE_GUID_PTR(EFI_LOADED_IMAGE_PROTOCOL),
- (void **) &loaded_image,
- image,
- NULL,
- EFI_OPEN_PROTOCOL_GET_PROTOCOL);
+ err = BS->HandleProtocol(image, MAKE_GUID_PTR(EFI_LOADED_IMAGE_PROTOCOL), (void **) &loaded_image);
if (err != EFI_SUCCESS)
return log_error_status(err, "Error getting a LoadedImageProtocol handle: %m");
return EXIT_SUCCESS;
}
- _cleanup_tpm2_context_ Tpm2Context *c = NULL;
+ _cleanup_(tpm2_context_unrefp) Tpm2Context *c = NULL;
r = tpm2_context_new(arg_tpm2_device, &c);
if (r < 0)
return r;
if (cg_all_unified() == 0 && controller && !streq(controller, SYSTEMD_CGROUP_CONTROLLER))
printf("Controller %s; ", controller);
- printf("Control group %s:\n", empty_to_root(path));
+ printf("CGroup %s:\n", empty_to_root(path));
fflush(stdout);
}
printf("%s%s%-*s%s %s%7s%s %s%*s%s %s%8s%s %s%8s%s %s%8s%s%s\n",
ansi_underline(),
- arg_order == ORDER_PATH ? on : "", path_columns, "Control Group",
+ arg_order == ORDER_PATH ? on : "", path_columns, "CGroup",
arg_order == ORDER_PATH ? off : "",
arg_order == ORDER_TASKS ? on : "",
arg_count == COUNT_PIDS ? "Tasks" : arg_count == COUNT_USERSPACE_PROCESSES ? "Procs" : "Proc+",
}
(void) unit_check_oom(u);
+ unit_add_to_gc_queue(u);
+
return 0;
}
#include "bpf-firewall.h"
#include "bpf-foreign.h"
#include "bus-get-properties.h"
+#include "bus-util.h"
#include "cgroup-util.h"
#include "cgroup.h"
#include "core-varlink.h"
sd_bus_message *reply,
void *userdata,
sd_bus_error *error) {
- int r;
+
CGroupContext *c = ASSERT_PTR(userdata);
- char *iface;
+ int r;
assert(bus);
assert(reply);
if (r < 0)
return r;
- r = sd_bus_message_open_container(reply, 'a', "s");
- if (r < 0)
- return r;
-
- SET_FOREACH(iface, c->restrict_network_interfaces) {
- r = sd_bus_message_append(reply, "s", iface);
- if (r < 0)
- return r;
- }
-
- r = sd_bus_message_close_container(reply);
+ r = bus_message_append_string_set(reply, c->restrict_network_interfaces);
if (r < 0)
return r;
#include "af-list.h"
#include "alloc-util.h"
#include "bus-get-properties.h"
+#include "bus-util.h"
#include "cap-list.h"
#include "capability-util.h"
#include "cpu-set-util.h"
#include "io-util.h"
#include "ioprio-util.h"
#include "journal-file.h"
+#include "load-fragment.h"
#include "memstream-util.h"
#include "missing_ioprio.h"
#include "mountpoint-util.h"
SD_BUS_PROPERTY("SetCredentialEncrypted", "a(say)", property_get_set_credential, 0, SD_BUS_VTABLE_PROPERTY_CONST),
SD_BUS_PROPERTY("LoadCredential", "a(ss)", property_get_load_credential, 0, SD_BUS_VTABLE_PROPERTY_CONST),
SD_BUS_PROPERTY("LoadCredentialEncrypted", "a(ss)", property_get_load_credential, 0, SD_BUS_VTABLE_PROPERTY_CONST),
+ SD_BUS_PROPERTY("ImportCredential", "as", bus_property_get_string_set, offsetof(ExecContext, import_credentials), SD_BUS_VTABLE_PROPERTY_CONST),
SD_BUS_PROPERTY("SupplementaryGroups", "as", NULL, offsetof(ExecContext, supplementary_groups), SD_BUS_VTABLE_PROPERTY_CONST),
SD_BUS_PROPERTY("PAMName", "s", NULL, offsetof(ExecContext, pam_name), SD_BUS_VTABLE_PROPERTY_CONST),
SD_BUS_PROPERTY("ReadWritePaths", "as", NULL, offsetof(ExecContext, read_write_paths), SD_BUS_VTABLE_PROPERTY_CONST),
isempty = false;
if (!UNIT_WRITE_FLAGS_NOOP(flags)) {
- _cleanup_free_ char *copy = NULL;
- ExecLoadCredential *old;
+ bool encrypted = streq(name, "LoadCredentialEncrypted");
- copy = strdup(source);
- if (!copy)
- return -ENOMEM;
+ r = hashmap_put_credential(&c->load_credentials, id, source, encrypted);
+ if (r < 0)
+ return r;
- old = hashmap_get(c->load_credentials, id);
- if (old) {
- free_and_replace(old->path, copy);
- old->encrypted = streq(name, "LoadCredentialEncrypted");
- } else {
- _cleanup_(exec_load_credential_freep) ExecLoadCredential *lc = NULL;
+ (void) unit_write_settingf(u, flags|UNIT_ESCAPE_SPECIFIERS, name, "%s=%s:%s", name, id, source);
+ }
+ }
- lc = new(ExecLoadCredential, 1);
- if (!lc)
- return -ENOMEM;
+ r = sd_bus_message_exit_container(message);
+ if (r < 0)
+ return r;
- *lc = (ExecLoadCredential) {
- .id = strdup(id),
- .path = TAKE_PTR(copy),
- .encrypted = streq(name, "LoadCredentialEncrypted"),
- };
+ if (!UNIT_WRITE_FLAGS_NOOP(flags) && isempty) {
+ c->load_credentials = hashmap_free(c->load_credentials);
+ (void) unit_write_settingf(u, flags, name, "%s=", name);
+ }
- if (!lc->id)
- return -ENOMEM;
+ return 1;
- r = hashmap_ensure_put(&c->load_credentials, &exec_load_credential_hash_ops, lc->id, lc);
- if (r < 0)
- return r;
+ } else if (streq(name, "ImportCredential")) {
+ bool isempty = true;
- TAKE_PTR(lc);
- }
+ r = sd_bus_message_enter_container(message, 'a', "s");
+ if (r < 0)
+ return r;
- (void) unit_write_settingf(u, flags|UNIT_ESCAPE_SPECIFIERS, name, "%s=%s:%s", name, id, source);
+ for (;;) {
+ const char *s;
+
+ r = sd_bus_message_read(message, "s", &s);
+ if (r < 0)
+ return r;
+ if (r == 0)
+ break;
+
+ if (!filename_is_valid(s))
+ return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Credential name is invalid: %s", s);
+
+ isempty = false;
+
+ if (!UNIT_WRITE_FLAGS_NOOP(flags)) {
+ r = set_put_strdup(&c->import_credentials, s);
+ if (r < 0)
+ return r;
+
+ (void) unit_write_settingf(u, flags|UNIT_ESCAPE_SPECIFIERS, name, "%s=%s", name, s);
}
}
return r;
if (!UNIT_WRITE_FLAGS_NOOP(flags) && isempty) {
- c->load_credentials = hashmap_free(c->load_credentials);
+ c->import_credentials = set_free(c->import_credentials);
(void) unit_write_settingf(u, flags, name, "%s=", name);
}
if (j->in_dbus_queue) {
LIST_REMOVE(dbus_queue, j->manager->dbus_job_queue, j);
j->in_dbus_queue = false;
+
+ /* The job might be good to be GC once its pending signals have been sent */
+ job_add_to_gc_queue(j);
}
r = bus_foreach_bus(j->manager, j->bus_track, j->sent_dbus_new_signal ? send_changed_signal : send_new_signal, j);
if (u->in_dbus_queue) {
LIST_REMOVE(dbus_queue, u->manager->dbus_unit_queue, u);
u->in_dbus_queue = false;
+
+ /* The unit might be good to be GC once its pending signals have been sent */
+ unit_add_to_gc_queue(u);
}
if (!u->id)
if (r < 0)
return log_error_errno(errno, "Failed to bind private socket: %m");
- r = listen(fd, SOMAXCONN);
+ r = listen(fd, SOMAXCONN_DELUXE);
if (r < 0)
return log_error_errno(errno, "Failed to make private socket listening: %m");
assert(context);
return !hashmap_isempty(context->set_credentials) ||
- !hashmap_isempty(context->load_credentials);
+ !hashmap_isempty(context->load_credentials) ||
+ !set_isempty(context->import_credentials);
}
#if HAVE_SECCOMP
return 0;
}
-static char **credential_search_path(
- const ExecParameters *params,
- bool encrypted) {
+typedef enum CredentialSearchPath {
+ CREDENTIAL_SEARCH_PATH_TRUSTED,
+ CREDENTIAL_SEARCH_PATH_ENCRYPTED,
+ CREDENTIAL_SEARCH_PATH_ALL,
+ _CREDENTIAL_SEARCH_PATH_MAX,
+ _CREDENTIAL_SEARCH_PATH_INVALID = -EINVAL,
+} CredentialSearchPath;
+
+static char **credential_search_path(const ExecParameters *params, CredentialSearchPath path) {
_cleanup_strv_free_ char **l = NULL;
assert(params);
+ assert(path >= 0 && path < _CREDENTIAL_SEARCH_PATH_MAX);
- /* Assemble a search path to find credentials in. We'll look in /etc/credstore/ (and similar
- * directories in /usr/lib/ + /run/) for all types of credentials. If we are looking for encrypted
- * credentials, also look in /etc/credstore.encrypted/ (and similar dirs). */
+ /* Assemble a search path to find credentials in. For non-encrypted credentials, We'll look in
+ * /etc/credstore/ (and similar directories in /usr/lib/ + /run/). If we're looking for encrypted
+ * credentials, we'll look in /etc/credstore.encrypted/ (and similar dirs). */
- if (encrypted) {
+ if (IN_SET(path, CREDENTIAL_SEARCH_PATH_ENCRYPTED, CREDENTIAL_SEARCH_PATH_ALL)) {
if (strv_extend(&l, params->received_encrypted_credentials_directory) < 0)
return NULL;
return NULL;
}
- if (params->received_credentials_directory)
- if (strv_extend(&l, params->received_credentials_directory) < 0)
- return NULL;
+ if (IN_SET(path, CREDENTIAL_SEARCH_PATH_TRUSTED, CREDENTIAL_SEARCH_PATH_ALL)) {
+ if (params->received_credentials_directory)
+ if (strv_extend(&l, params->received_credentials_directory) < 0)
+ return NULL;
- if (strv_extend_strv(&l, CONF_PATHS_STRV("credstore"), /* filter_duplicates= */ true) < 0)
- return NULL;
+ if (strv_extend_strv(&l, CONF_PATHS_STRV("credstore"), /* filter_duplicates= */ true) < 0)
+ return NULL;
+ }
if (DEBUG_LOGGING) {
_cleanup_free_ char *t = strv_join(l, ":");
return TAKE_PTR(l);
}
+static int maybe_decrypt_and_write_credential(
+ int dir_fd,
+ const char *id,
+ bool encrypted,
+ uid_t uid,
+ bool ownership_ok,
+ const char *data,
+ size_t size,
+ uint64_t *left) {
+
+ _cleanup_free_ void *plaintext = NULL;
+ size_t add;
+ int r;
+
+ if (encrypted) {
+ size_t plaintext_size = 0;
+
+ r = decrypt_credential_and_warn(id, now(CLOCK_REALTIME), NULL, NULL, data, size,
+ &plaintext, &plaintext_size);
+ if (r < 0)
+ return r;
+
+ data = plaintext;
+ size = plaintext_size;
+ }
+
+ add = strlen(id) + size;
+ if (add > *left)
+ return -E2BIG;
+
+ r = write_credential(dir_fd, id, data, size, uid, ownership_ok);
+ if (r < 0)
+ return log_debug_errno(r, "Failed to write credential '%s': %m", id);
+
+ *left -= add;
+ return 0;
+}
+
+static int load_credential_glob(
+ const char *path,
+ bool encrypted,
+ char **search_path,
+ ReadFullFileFlags flags,
+ int write_dfd,
+ uid_t uid,
+ bool ownership_ok,
+ uint64_t *left) {
+
+ int r;
+
+ STRV_FOREACH(d, search_path) {
+ _cleanup_globfree_ glob_t pglob = {};
+ _cleanup_free_ char *j = NULL;
+
+ j = path_join(*d, path);
+ if (!j)
+ return -ENOMEM;
+
+ r = safe_glob(j, 0, &pglob);
+ if (r == -ENOENT)
+ continue;
+ if (r < 0)
+ return r;
+
+ for (size_t n = 0; n < pglob.gl_pathc; n++) {
+ _cleanup_free_ char *fn = NULL;
+ _cleanup_(erase_and_freep) char *data = NULL;
+ size_t size;
+
+ /* path is absolute, hence pass AT_FDCWD as nop dir fd here */
+ r = read_full_file_full(
+ AT_FDCWD,
+ pglob.gl_pathv[n],
+ UINT64_MAX,
+ encrypted ? CREDENTIAL_ENCRYPTED_SIZE_MAX : CREDENTIAL_SIZE_MAX,
+ flags,
+ NULL,
+ &data, &size);
+ if (r < 0)
+ return log_debug_errno(r, "Failed to read credential '%s': %m",
+ pglob.gl_pathv[n]);
+
+ r = path_extract_filename(pglob.gl_pathv[n], &fn);
+ if (r < 0)
+ return log_debug_errno(r, "Failed to extract filename from '%s': %m",
+ pglob.gl_pathv[n]);
+
+ r = maybe_decrypt_and_write_credential(
+ write_dfd,
+ fn,
+ encrypted,
+ uid,
+ ownership_ok,
+ data, size,
+ left);
+ if (r == -EEXIST)
+ continue;
+ if (r < 0)
+ return r;
+ }
+ }
+
+ return 0;
+}
+
static int load_credential(
const ExecContext *context,
const ExecParameters *params,
_cleanup_free_ char *bindname = NULL;
const char *source = NULL;
bool missing_ok = true;
- size_t size, add, maxsz;
+ size_t size, maxsz;
int r;
assert(context);
* directory we received ourselves. We don't support the AF_UNIX stuff in this mode, since we
* are operating on a credential store, i.e. this is guaranteed to be regular files. */
- search_path = credential_search_path(params, encrypted);
+ search_path = credential_search_path(params, CREDENTIAL_SEARCH_PATH_ALL);
if (!search_path)
return -ENOMEM;
if (r < 0)
return log_debug_errno(r, "Failed to read credential '%s': %m", path);
- if (encrypted) {
- _cleanup_free_ void *plaintext = NULL;
- size_t plaintext_size = 0;
-
- r = decrypt_credential_and_warn(id, now(CLOCK_REALTIME), NULL, NULL, data, size, &plaintext, &plaintext_size);
- if (r < 0)
- return r;
-
- free_and_replace(data, plaintext);
- size = plaintext_size;
- }
-
- add = strlen(id) + size;
- if (add > *left)
- return -E2BIG;
-
- r = write_credential(write_dfd, id, data, size, uid, ownership_ok);
- if (r < 0)
- return log_debug_errno(r, "Failed to write credential '%s': %m", id);
-
- *left -= add;
- return 0;
+ return maybe_decrypt_and_write_credential(write_dfd, id, encrypted, uid, ownership_ok, data, size, left);
}
struct load_cred_args {
uint64_t left = CREDENTIALS_TOTAL_SIZE_MAX;
_cleanup_close_ int dfd = -EBADF;
+ const char *ic;
ExecLoadCredential *lc;
ExecSetCredential *sc;
int r;
return r;
}
- /* Second, we add in literally specified credentials. If the credentials already exist, we'll not add
- * them, so that they can act as a "default" if the same credential is specified multiple times. */
+ /* Next, look for system credentials and credentials in the credentials store. Note that these do not
+ * override any credentials found earlier. */
+ SET_FOREACH(ic, context->import_credentials) {
+ _cleanup_free_ char **search_path = NULL;
+
+ search_path = credential_search_path(params, CREDENTIAL_SEARCH_PATH_TRUSTED);
+ if (!search_path)
+ return -ENOMEM;
+
+ r = load_credential_glob(
+ ic,
+ /* encrypted = */ false,
+ search_path,
+ READ_FULL_FILE_SECURE|READ_FULL_FILE_FAIL_WHEN_LARGER,
+ dfd,
+ uid,
+ ownership_ok,
+ &left);
+ if (r < 0)
+ return r;
+
+ search_path = strv_free(search_path);
+ search_path = credential_search_path(params, CREDENTIAL_SEARCH_PATH_ENCRYPTED);
+ if (!search_path)
+ return -ENOMEM;
+
+ r = load_credential_glob(
+ ic,
+ /* encrypted = */ true,
+ search_path,
+ READ_FULL_FILE_SECURE|READ_FULL_FILE_FAIL_WHEN_LARGER|READ_FULL_FILE_UNBASE64,
+ dfd,
+ uid,
+ ownership_ok,
+ &left);
+ if (r < 0)
+ return r;
+ }
+
+ /* Finally, we add in literally specified credentials. If the credentials already exist, we'll not
+ * add them, so that they can act as a "default" if the same credential is specified multiple times. */
HASHMAP_FOREACH(sc, context->set_credentials) {
_cleanup_(erase_and_freep) void *plaintext = NULL;
const char *data;
c->load_credentials = hashmap_free(c->load_credentials);
c->set_credentials = hashmap_free(c->set_credentials);
+ c->import_credentials = set_free(c->import_credentials);
c->root_image_policy = image_policy_free(c->root_image_policy);
c->mount_image_policy = image_policy_free(c->mount_image_policy);
/* When we don't match anything, -ENOENT should be returned */
assert(pglob.gl_pathc > 0);
- for (unsigned n = 0; n < pglob.gl_pathc; n++) {
+ for (size_t n = 0; n < pglob.gl_pathc; n++) {
_cleanup_strv_free_ char **p = NULL;
r = load_env_file(NULL, pglob.gl_pathv[n], &p);
Hashmap *set_credentials; /* output id → ExecSetCredential */
Hashmap *load_credentials; /* output id → ExecLoadCredential */
+ Set *import_credentials;
ImagePolicy *root_image_policy, *mount_image_policy, *extension_image_policy;
};
if (!UNIT_VTABLE(j->unit)->gc_jobs)
return false;
+ /* Make sure to send out pending D-Bus events before we unload the unit */
+ if (j->in_dbus_queue)
+ return false;
+
if (sd_bus_track_count(j->bus_track) > 0)
return false;
{{type}}.SetCredentialEncrypted, config_parse_set_credential, 1, offsetof({{type}}, exec_context)
{{type}}.LoadCredential, config_parse_load_credential, 0, offsetof({{type}}, exec_context)
{{type}}.LoadCredentialEncrypted, config_parse_load_credential, 1, offsetof({{type}}, exec_context)
+{{type}}.ImportCredential, config_parse_import_credential, 0, offsetof({{type}}, exec_context.import_credentials)
{{type}}.TimeoutCleanSec, config_parse_sec, 0, offsetof({{type}}, exec_context.timeout_clean_usec)
{% if HAVE_PAM %}
{{type}}.PAMName, config_parse_unit_string_printf, 0, offsetof({{type}}, exec_context.pam_name)
return 0;
}
+int hashmap_put_credential(Hashmap **h, const char *id, const char *path, bool encrypted) {
+ ExecLoadCredential *old;
+ int r;
+
+ assert(h);
+ assert(id);
+ assert(path);
+
+ old = hashmap_get(*h, id);
+ if (old) {
+ r = free_and_strdup(&old->path, path);
+ if (r < 0)
+ return r;
+
+ old->encrypted = encrypted;
+ } else {
+ _cleanup_(exec_load_credential_freep) ExecLoadCredential *lc = NULL;
+
+ lc = new(ExecLoadCredential, 1);
+ if (!lc)
+ return log_oom();
+
+ *lc = (ExecLoadCredential) {
+ .id = strdup(id),
+ .path = strdup(path),
+ .encrypted = encrypted,
+ };
+ if (!lc->id || !lc->path)
+ return -ENOMEM;
+
+ r = hashmap_ensure_put(h, &exec_load_credential_hash_ops, lc->id, lc);
+ if (r < 0)
+ return r;
+
+ TAKE_PTR(lc);
+ }
+
+ return 0;
+}
+
int config_parse_load_credential(
const char *unit,
const char *filename,
_cleanup_free_ char *word = NULL, *k = NULL, *q = NULL;
ExecContext *context = ASSERT_PTR(data);
- ExecLoadCredential *old;
bool encrypted = ltype;
Unit *u = userdata;
const char *p;
}
}
- old = hashmap_get(context->load_credentials, k);
- if (old) {
- free_and_replace(old->path, q);
- old->encrypted = encrypted;
- } else {
- _cleanup_(exec_load_credential_freep) ExecLoadCredential *lc = NULL;
+ r = hashmap_put_credential(&context->load_credentials, k, q, encrypted);
+ if (r < 0)
+ return log_error_errno(r, "Failed to store load credential '%s': %m", rvalue);
- lc = new(ExecLoadCredential, 1);
- if (!lc)
- return log_oom();
+ return 0;
+}
- *lc = (ExecLoadCredential) {
- .id = TAKE_PTR(k),
- .path = TAKE_PTR(q),
- .encrypted = encrypted,
- };
+int config_parse_import_credential(
+ const char *unit,
+ const char *filename,
+ unsigned line,
+ const char *section,
+ unsigned section_line,
+ const char *lvalue,
+ int ltype,
+ const char *rvalue,
+ void *data,
+ void *userdata) {
- r = hashmap_ensure_put(&context->load_credentials, &exec_load_credential_hash_ops, lc->id, lc);
- if (r == -ENOMEM)
- return log_oom();
- if (r < 0) {
- log_syntax(unit, LOG_WARNING, filename, line, r,
- "Duplicated credential value '%s', ignoring assignment: %s", lc->id, rvalue);
- return 0;
- }
+ _cleanup_free_ char *s = NULL;
+ Set** import_credentials = ASSERT_PTR(data);
+ Unit *u = userdata;
+ int r;
- TAKE_PTR(lc);
+ assert(filename);
+ assert(lvalue);
+ assert(rvalue);
+
+ if (isempty(rvalue)) {
+ /* Empty assignment resets the list */
+ *import_credentials = set_free(*import_credentials);
+ return 0;
+ }
+
+ r = unit_cred_printf(u, rvalue, &s);
+ if (r < 0) {
+ log_syntax(unit, LOG_WARNING, filename, line, r, "Failed to resolve unit specifiers in \"%s\", ignoring: %m", s);
+ return 0;
+ }
+ if (!filename_is_valid(s)) {
+ log_syntax(unit, LOG_WARNING, filename, line, 0, "Credential name \"%s\" not valid, ignoring.", s);
+ return 0;
}
+ r = set_put_strdup(import_credentials, s);
+ if (r < 0)
+ return log_error_errno(r, "Failed to store credential name '%s': %m", rvalue);
+
return 0;
}
int parse_crash_chvt(const char *value, int *data);
int parse_confirm_spawn(const char *value, char **console);
+int hashmap_put_credential(Hashmap **h, const char *id, const char *path, bool encrypted);
+
/* Read service data from .desktop file style configuration fragments */
int unit_load_fragment(Unit *u);
CONFIG_PARSER_PROTOTYPE(config_parse_exec_directories);
CONFIG_PARSER_PROTOTYPE(config_parse_set_credential);
CONFIG_PARSER_PROTOTYPE(config_parse_load_credential);
+CONFIG_PARSER_PROTOTYPE(config_parse_import_credential);
CONFIG_PARSER_PROTOTYPE(config_parse_set_status);
CONFIG_PARSER_PROTOTYPE(config_parse_namespace_path_strv);
CONFIG_PARSER_PROTOTYPE(config_parse_temporary_filesystems);
in_files = [['system.conf', pkgsysconfdir],
['user.conf', pkgsysconfdir],
- ['systemd.pc', pkgconfigdatadir],
['org.freedesktop.systemd1.policy', polkitpolicydir]]
foreach item : in_files
install_dir : dir)
endforeach
+systemd_pc = custom_target(
+ 'systemd.pc',
+ input : 'systemd.pc.in',
+ output : 'systemd.pc',
+ command : [jinja2_cmdline, '@INPUT@', '@OUTPUT@'],
+ install : pkgconfigdatadir != 'no',
+ install_tag : 'devel',
+ install_dir : pkgconfigdatadir)
+
install_data('org.freedesktop.systemd1.conf',
install_dir : dbuspolicydir)
install_data('org.freedesktop.systemd1.service',
assert(u);
assert(u->load_state == UNIT_STUB);
- s->backlog = SOMAXCONN;
+ s->backlog = SOMAXCONN_DELUXE;
s->timeout_usec = u->manager->default_timeout_start_usec;
s->directory_mode = 0755;
s->socket_mode = 0666;
* too. */
if (streq(p, "nsec"))
return SOCKET_TIMESTAMPING_NS;
- if (STR_IN_SET(p, "usec", "µs"))
+ if (STR_IN_SET(p, "usec", "µs", "μs")) /* Accept both small greek letter mu + micro sign unicode codepoints */
return SOCKET_TIMESTAMPING_US;
r = parse_boolean(p);
if (u->perpetual)
return false;
- if (u->in_cgroup_empty_queue)
+ /* if we saw a cgroup empty event for this unit, stay around until we processed it so that we remove
+ * the empty cgroup if possible. Similar, process any pending OOM events if they are already queued
+ * before we release the unit. */
+ if (u->in_cgroup_empty_queue || u->in_cgroup_oom_queue)
+ return false;
+
+ /* Make sure to send out D-Bus events before we unload the unit */
+ if (u->in_dbus_queue)
return false;
if (sd_bus_track_count(u->bus_track) > 0)
META_ARGV_UID, /* %u: as seen in the initial user namespace */
META_ARGV_GID, /* %g: as seen in the initial user namespace */
META_ARGV_SIGNAL, /* %s: number of signal causing dump */
- META_ARGV_TIMESTAMP, /* %t: time of dump, expressed as seconds since the Epoch (we expand this to µs granularity) */
+ META_ARGV_TIMESTAMP, /* %t: time of dump, expressed as seconds since the Epoch (we expand this to μs granularity) */
META_ARGV_RLIMIT, /* %c: core file size soft resource limit */
META_ARGV_HOSTNAME, /* %h: hostname */
_META_ARGV_MAX,
(void) fix_acl(fd, uid, allow_user);
(void) fix_xattr(fd, context);
- r = fsync_full(fd);
- if (r < 0)
- return log_error_errno(r, "Failed to sync coredump %s: %m", coredump_tmpfile_name(filename));
-
- r = link_tmpfile(fd, filename, target, /* replace= */ false);
+ r = link_tmpfile(fd, filename, target, LINK_TMPFILE_SYNC);
if (r < 0)
return log_error_errno(r, "Failed to move coredump %s into place: %m", target);
case META_ARGV_TIMESTAMP:
/* The journal fields contain the timestamp padded with six
* zeroes, so that the kernel-supplied 1s granularity timestamps
- * becomes 1µs granularity, i.e. the granularity systemd usually
+ * becomes 1μs granularity, i.e. the granularity systemd usually
* operates in. */
t = free_timestamp = strjoin(argv[i], "000000");
if (!t)
if (!noauto && !nofail) {
r = write_drop_in(arg_dest, dmname, 40, "device-timeout",
"# Automatically generated by systemd-cryptsetup-generator\n\n"
- "[Unit]\nJobTimeoutSec=0");
+ "[Unit]\n"
+ "JobTimeoutSec=infinity\n");
if (r < 0)
log_warning_errno(r, "Failed to write device timeout drop-in: %m");
}
return log_oom();
} else if ((val = startswith(option, "header="))) {
- if (!STR_IN_SET(arg_type, ANY_LUKS, CRYPT_LUKS1, CRYPT_LUKS2, CRYPT_TCRYPT))
+ if (!arg_type || !STR_IN_SET(arg_type, ANY_LUKS, CRYPT_LUKS1, CRYPT_LUKS2, CRYPT_TCRYPT))
arg_type = ANY_LUKS;
if (!path_is_absolute(val))
}
#if HAVE_TPM2
- _cleanup_tpm2_context_ Tpm2Context *c = NULL;
+ _cleanup_(tpm2_context_unrefp) Tpm2Context *c = NULL;
r = tpm2_context_new(arg_tpm2_device, &c);
if (r < 0)
return r;
}
/* Try to copy as directory? */
- r = copy_directory_fd(source_fd, arg_target, COPY_REFLINK|COPY_MERGE_EMPTY|COPY_SIGINT|COPY_HARDLINKS);
+ r = copy_directory_at(source_fd, NULL, AT_FDCWD, arg_target, COPY_REFLINK|COPY_MERGE_EMPTY|COPY_SIGINT|COPY_HARDLINKS);
if (r >= 0)
return 0;
if (r != -ENOTDIR)
r = write_drop_in_format(arg_dest, dmname, 50, "job-timeout",
"# Automatically generated by systemd-gpt-auto-generator\n\n"
"[Unit]\n"
- "JobTimeoutSec=0"); /* the binary handles timeouts anyway */
+ "JobTimeoutSec=infinity"); /* the binary handles timeouts anyway */
if (r < 0)
log_warning_errno(r, "Failed to write device timeout drop-in, ignoring: %m");
r = write_drop_in(arg_dest, device_unit, 40, "device-timeout",
"# Automatically generated by systemd-hibernate-resume-generator\n\n"
- "[Unit]\nJobTimeoutSec=0");
+ "[Unit]\n"
+ "JobTimeoutSec=infinity\n");
if (r < 0)
log_warning_errno(r, "Failed to write device timeout drop-in: %m");
setup->temporary_image_path = TAKE_PTR(t);
- r = chattr_full(t, setup->image_fd, FS_NOCOW_FL|FS_NOCOMP_FL, FS_NOCOW_FL|FS_NOCOMP_FL, NULL, NULL, CHATTR_FALLBACK_BITWISE);
+ r = chattr_full(setup->image_fd, NULL, FS_NOCOW_FL|FS_NOCOMP_FL, FS_NOCOW_FL|FS_NOCOMP_FL, NULL, NULL, CHATTR_FALLBACK_BITWISE);
if (r < 0 && r != -ENOANO) /* ENOANO → some bits didn't work; which we skip logging about because chattr_full() already debug logs about those flags */
log_full_errno(ERRNO_IS_NOT_SUPPORTED(r) ? LOG_DEBUG : LOG_WARNING, r,
"Failed to set file attributes on %s, ignoring: %m", setup->temporary_image_path);
static int acquire_user_record(
pam_handle_t *handle,
const char *username,
+ bool debug,
UserRecord **ret_record,
PamBusData **bus_data) {
r = bus_call_method(bus, bus_home_mgr, "GetUserRecordByName", &error, &reply, "s", username);
if (r < 0) {
if (bus_error_is_unknown_service(&error)) {
- pam_syslog(handle, LOG_DEBUG, "systemd-homed is not available: %s", bus_error_message(&error, r));
+ if (debug)
+ pam_syslog(handle, LOG_DEBUG, "systemd-homed is not available: %s", bus_error_message(&error, r));
goto user_unknown;
}
if (sd_bus_error_has_name(&error, BUS_ERROR_NO_SUCH_HOME)) {
- pam_syslog(handle, LOG_DEBUG, "Not a user managed by systemd-homed: %s", bus_error_message(&error, r));
+ if (debug)
+ pam_syslog(handle, LOG_DEBUG, "Not a user managed by systemd-homed: %s", bus_error_message(&error, r));
goto user_unknown;
}
const char *user_name,
UserRecord *secret,
int ret,
- const sd_bus_error *error) {
+ const sd_bus_error *error,
+ bool debug) {
assert(user_name);
assert(error);
if (r != PAM_SUCCESS)
return PAM_CONV_ERR; /* no logging here */
- if (isempty(newp))
- return pam_syslog_pam_error(handle, LOG_DEBUG, PAM_AUTHTOK_ERR,
- "Password request aborted.");
+ if (isempty(newp)) {
+ if (debug)
+ pam_syslog(handle, LOG_DEBUG, "Password request aborted.");
+ return PAM_AUTHTOK_ERR;
+ }
r = user_record_set_password(secret, STRV_MAKE(newp), true);
if (r < 0)
if (r != PAM_SUCCESS)
return PAM_CONV_ERR; /* no logging here */
- if (isempty(newp))
- return pam_syslog_pam_error(handle, LOG_DEBUG, PAM_AUTHTOK_ERR,
- "Recovery key request aborted.");
+ if (isempty(newp)) {
+ if (debug)
+ pam_syslog(handle, LOG_DEBUG, "Recovery key request aborted.");
+ return PAM_AUTHTOK_ERR;
+ }
r = user_record_set_password(secret, STRV_MAKE(newp), true);
if (r < 0)
if (r != PAM_SUCCESS)
return PAM_CONV_ERR; /* no logging here */
- if (isempty(newp))
- return pam_syslog_pam_error(handle, LOG_DEBUG, PAM_AUTHTOK_ERR,
- "Password request aborted.");
+ if (isempty(newp)) {
+ if (debug)
+ pam_syslog(handle, LOG_DEBUG, "Password request aborted.");
+ return PAM_AUTHTOK_ERR;
+ }
r = user_record_set_password(secret, STRV_MAKE(newp), true);
if (r != PAM_SUCCESS)
return PAM_CONV_ERR; /* no logging here */
- if (isempty(newp))
- return pam_syslog_pam_error(handle, LOG_DEBUG, PAM_AUTHTOK_ERR, "PIN request aborted.");
+ if (isempty(newp)) {
+ if (debug)
+ pam_syslog(handle, LOG_DEBUG, "PIN request aborted.");
+ return PAM_AUTHTOK_ERR;
+ }
r = user_record_set_token_pin(secret, STRV_MAKE(newp), false);
if (r < 0)
if (r != PAM_SUCCESS)
return PAM_CONV_ERR; /* no logging here */
- if (isempty(newp))
- return pam_syslog_pam_error(handle, LOG_DEBUG, PAM_AUTHTOK_ERR, "PIN request aborted.");
+ if (isempty(newp)) {
+ if (debug)
+ pam_syslog(handle, LOG_DEBUG, "PIN request aborted.");
+ return PAM_AUTHTOK_ERR;
+ }
r = user_record_set_token_pin(secret, STRV_MAKE(newp), false);
if (r < 0)
if (r != PAM_SUCCESS)
return PAM_CONV_ERR; /* no logging here */
- if (isempty(newp))
- return pam_syslog_pam_error(handle, LOG_DEBUG, PAM_AUTHTOK_ERR, "PIN request aborted.");
+ if (isempty(newp)) {
+ if (debug)
+ pam_syslog(handle, LOG_DEBUG, "PIN request aborted.");
+ return PAM_AUTHTOK_ERR;
+ }
r = user_record_set_token_pin(secret, STRV_MAKE(newp), false);
if (r < 0)
if (r != PAM_SUCCESS)
return PAM_CONV_ERR; /* no logging here */
- if (isempty(newp))
- return pam_syslog_pam_error(handle, LOG_DEBUG, PAM_AUTHTOK_ERR, "PIN request aborted.");
+ if (isempty(newp)) {
+ if (debug)
+ pam_syslog(handle, LOG_DEBUG, "PIN request aborted.");
+ return PAM_AUTHTOK_ERR;
+ }
r = user_record_set_token_pin(secret, STRV_MAKE(newp), false);
if (r < 0)
if (r != PAM_SUCCESS)
return r;
- r = acquire_user_record(handle, username, &ur, bus_data);
+ r = acquire_user_record(handle, username, debug, &ur, bus_data);
if (r != PAM_SUCCESS)
return r;
else if (sd_bus_error_has_name(&error, BUS_ERROR_HOME_LOCKED))
home_locked = true; /* Similar */
else {
- r = handle_generic_user_record_error(handle, ur->user_name, secret, r, &error);
+ r = handle_generic_user_record_error(handle, ur->user_name, secret, r, &error, debug);
if (r == PAM_CONV_ERR) {
/* Password/PIN prompts will fail in certain environments, for example when
* we are called from OpenSSH's account or session hooks, or in systemd's
if (home_locked)
(void) pam_prompt(handle, PAM_ERROR_MSG, NULL, "Home of user %s is currently locked, please unlock locally first.", ur->user_name);
- pam_syslog(handle, please_authenticate ? LOG_ERR : LOG_DEBUG, "Failed to prompt for password/prompt.");
+ if (please_authenticate || debug)
+ pam_syslog(handle, please_authenticate ? LOG_ERR : LOG_DEBUG, "Failed to prompt for password/prompt.");
return home_not_active || home_locked ? PAM_PERM_DENIED : PAM_CONV_ERR;
}
if (r != PAM_SUCCESS)
return r;
- r = acquire_user_record(handle, NULL, &ur, NULL);
+ r = acquire_user_record(handle, NULL, debug, &ur, NULL);
if (r != PAM_SUCCESS)
return r;
if (r != PAM_SUCCESS)
return r;
- r = acquire_user_record(handle, NULL, &ur, NULL);
+ r = acquire_user_record(handle, NULL, debug, &ur, NULL);
if (r != PAM_SUCCESS)
return r;
if (r != PAM_SUCCESS)
return pam_syslog_pam_error(handle, LOG_ERR, r, "Failed to get new password: @PAMERR@");
- if (isempty(new_password))
- return pam_syslog_pam_error(handle, LOG_DEBUG, PAM_AUTHTOK_ERR, "Password request aborted.");
+ if (isempty(new_password)) {
+ if (debug)
+ pam_syslog(handle, LOG_DEBUG, "Password request aborted.");
+ return PAM_AUTHTOK_ERR;
+ }
r = pam_get_authtok_verify(handle, &new_password, "new password: "); /* Lower case, since PAM prefixes 'Repeat' */
if (r != PAM_SUCCESS)
r = sd_bus_call(bus, m, HOME_SLOW_BUS_CALL_TIMEOUT_USEC, &error, NULL);
if (r < 0) {
- r = handle_generic_user_record_error(handle, ur->user_name, old_secret, r, &error);
+ r = handle_generic_user_record_error(handle, ur->user_name, old_secret, r, &error, debug);
if (r == PAM_CONV_ERR)
return pam_syslog_pam_error(handle, LOG_ERR, r,
"Failed to prompt for password/prompt.");
return r;
/* Let's try to make a snapshot, if we can, so that the export is atomic */
- r = btrfs_subvol_snapshot_fd(sfd, e->temp_path, BTRFS_SNAPSHOT_READ_ONLY|BTRFS_SNAPSHOT_RECURSIVE);
+ r = btrfs_subvol_snapshot_at(sfd, NULL, AT_FDCWD, e->temp_path, BTRFS_SNAPSHOT_READ_ONLY|BTRFS_SNAPSHOT_RECURSIVE);
if (r < 0) {
log_debug_errno(r, "Couldn't create snapshot %s of %s, not exporting atomically: %m", e->temp_path, path);
e->temp_path = mfree(e->temp_path);
BLOCK_SIGNALS(SIGINT, SIGTERM);
if (arg_btrfs_subvol)
- r = btrfs_subvol_snapshot_fd_full(
- fd,
- dest,
+ r = btrfs_subvol_snapshot_at_full(
+ fd, NULL,
+ AT_FDCWD, dest,
BTRFS_SNAPSHOT_FALLBACK_COPY|
BTRFS_SNAPSHOT_FALLBACK_DIRECTORY|
BTRFS_SNAPSHOT_RECURSIVE|
progress_bytes,
&progress);
else
- r = copy_directory_fd_full(
- fd,
- dest,
+ r = copy_directory_at_full(
+ fd, NULL,
+ AT_FDCWD, dest,
COPY_REFLINK|
COPY_SAME_MOUNT|
COPY_HARDLINKS|
return log_error_errno(r, "Failed to generate temporary filename for %s: %m", p);
if (i->flags & PULL_BTRFS_SUBVOL)
- r = btrfs_subvol_snapshot(
- i->final_path,
- t,
+ r = btrfs_subvol_snapshot_at(
+ AT_FDCWD, i->final_path,
+ AT_FDCWD, t,
(i->flags & PULL_BTRFS_QUOTA ? BTRFS_SNAPSHOT_QUOTA : 0)|
BTRFS_SNAPSHOT_FALLBACK_COPY|
BTRFS_SNAPSHOT_FALLBACK_DIRECTORY|
else if (streq(word, "allow-discards")) {
if (ret_activate_flags)
*ret_activate_flags |= CRYPT_ACTIVATE_ALLOW_DISCARDS;
- } else if (streq(word, "no-journal")) {
- if (ret_activate_flags)
- *ret_activate_flags |= CRYPT_ACTIVATE_NO_JOURNAL;
+ } else if ((val = startswith(word, "mode="))) {
+ if (streq(val, "journal")) {
+ if (ret_activate_flags)
+ *ret_activate_flags &= ~(CRYPT_ACTIVATE_NO_JOURNAL | CRYPT_ACTIVATE_NO_JOURNAL_BITMAP);
+ } else if (streq(val, "bitmap")) {
+ if (ret_activate_flags) {
+ *ret_activate_flags &= ~CRYPT_ACTIVATE_NO_JOURNAL;
+ *ret_activate_flags |= CRYPT_ACTIVATE_NO_JOURNAL_BITMAP;
+ }
+ } else if (streq(val, "direct")) {
+ if (ret_activate_flags) {
+ *ret_activate_flags |= CRYPT_ACTIVATE_NO_JOURNAL;
+ *ret_activate_flags &= ~CRYPT_ACTIVATE_NO_JOURNAL_BITMAP;
+ }
+ } else
+ log_warning("Encountered unknown mode option '%s', ignoring.", val);
} else if ((val = startswith(word, "journal-watermark="))) {
r = parse_percent(val);
if (r < 0)
"[Service]\n"
"Type=oneshot\n"
"RemainAfterExit=yes\n"
- "TimeoutSec=0\n"
+ "TimeoutSec=infinity\n"
"ExecStart=" ROOTLIBEXECDIR "/systemd-integritysetup attach '%s' '%s' '%s' '%s'\n"
"ExecStop=" ROOTLIBEXECDIR "/systemd-integritysetup detach '%s'\n",
name_escaped, device, empty_to_dash(key_file_escaped), empty_to_dash(options),
MHD_OPTION_ARRAY, opts,
MHD_OPTION_END);
if (!d->daemon) {
- log_error("Failed to start µhttp daemon");
+ log_error("Failed to start μhttp daemon");
r = -EINVAL;
goto error;
}
info = MHD_get_daemon_info(d->daemon, MHD_DAEMON_INFO_EPOLL_FD_LINUX_ONLY);
if (!info) {
- log_error("µhttp returned NULL daemon info");
+ log_error("μhttp returned NULL daemon info");
r = -EOPNOTSUPP;
goto error;
}
epoll_fd = info->listen_fd;
if (epoll_fd < 0) {
- log_error("µhttp epoll fd is invalid");
+ log_error("μhttp epoll fd is invalid");
r = -EUCLEAN;
goto error;
}
**********************************************************************
**********************************************************************/
-/* This should go away as soon as µhttpd allows state to be passed around. */
+/* This should go away as soon as μhttpd allows state to be passed around. */
RemoteServer *journal_remote_server_global;
static int dispatch_raw_source_event(sd_event_source *event,
* enum elements with the same name. Hence let's check for the *old* name,
* and define the new name by the value of the old name. */
-/* Renamed in µhttpd 0.9.51 */
+/* Renamed in μhttpd 0.9.51 */
#ifndef MHD_USE_PIPE_FOR_SHUTDOWN
# define MHD_USE_ITC MHD_USE_PIPE_FOR_SHUTDOWN
#endif
-/* Renamed in µhttpd 0.9.52 */
+/* Renamed in μhttpd 0.9.52 */
#ifndef MHD_USE_EPOLL_LINUX_ONLY
# define MHD_USE_EPOLL MHD_USE_EPOLL_LINUX_ONLY
#endif
-/* Renamed in µhttpd 0.9.52 */
+/* Renamed in μhttpd 0.9.52 */
#ifndef MHD_USE_SSL
# define MHD_USE_TLS MHD_USE_SSL
#endif
-/* Renamed in µhttpd 0.9.53 */
+/* Renamed in μhttpd 0.9.53 */
#ifndef MHD_USE_POLL_INTERNALLY
# define MHD_USE_POLL_INTERNAL_THREAD MHD_USE_POLL_INTERNALLY
#endif
# define MHD_HTTP_NOT_ACCEPTABLE MHD_HTTP_METHOD_NOT_ACCEPTABLE
#endif
-/* Renamed in µhttpd 0.9.74 (8c644fc1f4d498ea489add8d40a68f5d3e5899fa) */
+/* Renamed in μhttpd 0.9.74 (8c644fc1f4d498ea489add8d40a68f5d3e5899fa) */
#ifndef MHD_HTTP_CONTENT_TOO_LARGE
# ifdef MHD_HTTP_PAYLOAD_TOO_LARGE
# define MHD_HTTP_CONTENT_TOO_LARGE MHD_HTTP_PAYLOAD_TOO_LARGE /* 0.9.53 or newer */
NULL);
}
-static bool uid_for_system_journal(uid_t uid) {
-
- /* Returns true if the specified UID shall get its data stored in the system journal. */
-
- return uid_is_system(uid) || uid_is_dynamic(uid) || uid == UID_NOBODY;
-}
-
static void server_add_acls(ManagedJournalFile *f, uid_t uid) {
assert(f);
(void) chmod(sa.un.sun_path, 0666);
- if (listen(s->stdout_fd, SOMAXCONN) < 0)
+ if (listen(s->stdout_fd, SOMAXCONN_DELUXE) < 0)
return log_error_errno(errno, "listen(%s) failed: %m", sa.un.sun_path);
} else
(void) fd_nonblock(s->stdout_fd, true);
--- /dev/null
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+
+#include <getopt.h>
+#include <stdbool.h>
+
+#include "build.h"
+#include "boot-entry.h"
+#include "chase.h"
+#include "conf-files.h"
+#include "env-file.h"
+#include "env-util.h"
+#include "exec-util.h"
+#include "fd-util.h"
+#include "fileio.h"
+#include "find-esp.h"
+#include "id128-util.h"
+#include "kernel-image.h"
+#include "main-func.h"
+#include "mkdir.h"
+#include "parse-argument.h"
+#include "path-util.h"
+#include "pretty-print.h"
+#include "rm-rf.h"
+#include "stat-util.h"
+#include "string-table.h"
+#include "string-util.h"
+#include "strv.h"
+#include "tmpfile-util.h"
+#include "verbs.h"
+
+static bool arg_verbose = false;
+static char *arg_esp_path = NULL;
+static char *arg_xbootldr_path = NULL;
+static int arg_make_entry_directory = -1; /* tristate */
+
+STATIC_DESTRUCTOR_REGISTER(arg_esp_path, freep);
+STATIC_DESTRUCTOR_REGISTER(arg_xbootldr_path, freep);
+
+typedef enum Action {
+ ACTION_ADD,
+ ACTION_REMOVE,
+ ACTION_INSPECT,
+ _ACTION_MAX,
+ _ACTION_INVALID = -EINVAL,
+} Action;
+
+typedef enum Layout {
+ LAYOUT_AUTO,
+ LAYOUT_UKI,
+ LAYOUT_BLS,
+ LAYOUT_OTHER,
+ _LAYOUT_MAX,
+ _LAYOUT_INVALID = -EINVAL,
+} Layout;
+
+static const char * const layout_table[_LAYOUT_MAX] = {
+ [LAYOUT_AUTO] = "auto",
+ [LAYOUT_UKI] = "uki",
+ [LAYOUT_BLS] = "bls",
+ [LAYOUT_OTHER] = "other",
+};
+
+DEFINE_PRIVATE_STRING_TABLE_LOOKUP(layout, Layout);
+
+typedef struct Context {
+ int rfd;
+ Action action;
+ sd_id128_t machine_id;
+ bool machine_id_is_random;
+ KernelImageType kernel_image_type;
+ Layout layout;
+ char *layout_other;
+ char *conf_root;
+ char *boot_root;
+ BootEntryTokenType entry_token_type;
+ char *entry_token;
+ char *entry_dir;
+ char *version;
+ char *kernel;
+ char **initrds;
+ char *initrd_generator;
+ char *uki_generator;
+ char *staging_area;
+ char **plugins;
+ char **argv;
+ char **envp;
+} Context;
+
+static void context_done(Context *c) {
+ assert(c);
+
+ free(c->layout_other);
+ free(c->conf_root);
+ free(c->boot_root);
+ free(c->entry_token);
+ free(c->entry_dir);
+ free(c->version);
+ free(c->kernel);
+ strv_free(c->initrds);
+ free(c->initrd_generator);
+ free(c->uki_generator);
+ if (c->action == ACTION_INSPECT)
+ free(c->staging_area);
+ else
+ rm_rf_physical_and_free(c->staging_area);
+ strv_free(c->plugins);
+ strv_free(c->argv);
+ strv_free(c->envp);
+
+ safe_close(c->rfd);
+}
+
+static int context_open_root(Context *c) {
+ assert(c);
+ assert(c->rfd < 0);
+
+ c->rfd = open("/", O_CLOEXEC | O_DIRECTORY | O_PATH);
+ if (c->rfd < 0)
+ return log_error_errno(errno, "Failed to open root directory: %m");
+
+ return 0;
+}
+
+static const char* context_get_layout(const Context *c) {
+ assert(c);
+ assert(c->layout >= 0);
+
+ return c->layout_other ?: layout_to_string(c->layout);
+}
+
+static int context_set_layout(Context *c, const char *s, const char *source) {
+ Layout t;
+
+ assert(c);
+ assert(source);
+
+ if (c->layout >= 0 || !s)
+ return 0;
+
+ assert(!c->layout_other);
+
+ t = layout_from_string(s);
+ if (t >= 0)
+ c->layout = t;
+ else if (isempty(s))
+ c->layout = LAYOUT_AUTO;
+ else {
+ c->layout_other = strdup(s);
+ if (!c->layout_other)
+ return log_oom();
+
+ c->layout = LAYOUT_OTHER;
+ }
+
+ log_debug("layout=%s set via %s", context_get_layout(c), source);
+ return 1;
+}
+
+static int context_set_machine_id(Context *c, const char *s, const char *source) {
+ int r;
+
+ assert(c);
+ assert(source);
+
+ if (!sd_id128_is_null(c->machine_id) || !s)
+ return 0;
+
+ r = sd_id128_from_string(s, &c->machine_id);
+ if (r < 0)
+ return log_warning_errno(r, "Failed to parse machine ID specified via %s, ignoring.", source);
+
+ if (sd_id128_is_null(c->machine_id))
+ return 0;
+
+ log_debug("MACHINE_ID=%s set via %s.", SD_ID128_TO_STRING(c->machine_id), source);
+ return 1;
+}
+
+static int context_set_string(const char *s, const char *source, const char *name, char **dest) {
+ char *p;
+
+ assert(source);
+ assert(name);
+ assert(dest);
+
+ if (*dest || !s)
+ return 0;
+
+ p = strdup(s);
+ if (!p)
+ return log_oom();
+
+ log_debug("%s (%s) set via %s.", name, p, source);
+
+ *dest = p;
+ return 1;
+}
+
+static int context_set_initrd_generator(Context *c, const char *s, const char *source) {
+ assert(c);
+ return context_set_string(s, source, "INITRD_GENERATOR", &c->initrd_generator);
+}
+
+static int context_set_uki_generator(Context *c, const char *s, const char *source) {
+ assert(c);
+ return context_set_string(s, source, "UKI_GENERATOR", &c->uki_generator);
+}
+
+static int context_set_version(Context *c, const char *s) {
+ assert(c);
+
+ if (!filename_is_valid(s))
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Invalid version specified: %s", s);
+
+ return context_set_string(s, "command line", "kernel version", &c->version);
+}
+
+static int context_set_path(Context *c, int rfd, const char *s, const char *source, const char *name, char **dest) {
+ char *p;
+ int r;
+
+ assert(c);
+ assert(source);
+ assert(name);
+ assert(dest);
+
+ if (*dest || !s)
+ return 0;
+
+ if (rfd >= 0)
+ r = chaseat(rfd, s, CHASE_AT_RESOLVE_IN_ROOT, &p, /* ret_fd = */ NULL);
+ else
+ r = chase(s, /* root = */ NULL, 0, &p, /* ret_fd = */ NULL);
+ if (r < 0)
+ return log_warning_errno(r, "Failed to chase path %s for %s specified via %s, ignoring: %m",
+ s, name, source);
+
+ log_debug("%s (%s) set via %s.", name, p, source);
+
+ *dest = p;
+ return 1;
+}
+
+static int context_set_boot_root(Context *c, const char *s, const char *source) {
+ assert(c);
+ return context_set_path(c, c->rfd, s, source, "BOOT_ROOT", &c->boot_root);
+}
+
+static int context_set_conf_root(Context *c, const char *s, const char *source) {
+ assert(c);
+ return context_set_path(c, c->rfd, s, source, "CONF_ROOT", &c->conf_root);
+}
+
+static int context_set_kernel(Context *c, const char *s) {
+ assert(c);
+ /* The path specified via command line should be relative to CWD. */
+ return context_set_path(c, AT_FDCWD, s, "command line", "kernel image file", &c->kernel);
+}
+
+static int context_set_path_strv(Context *c, int rfd, char* const* strv, const char *source, const char *name, char ***dest) {
+ _cleanup_strv_free_ char **w = NULL;
+ int r;
+
+ assert(c);
+ assert(source);
+ assert(name);
+ assert(dest);
+
+ if (*dest)
+ return 0;
+
+ STRV_FOREACH(s, strv) {
+ char *p;
+
+ if (rfd >= 0)
+ r = chaseat(rfd, *s, CHASE_AT_RESOLVE_IN_ROOT, &p, /* ret_fd = */ NULL);
+ else
+ r = chase(*s, /* root = */ NULL, 0, &p, /* ret_fd = */ NULL);
+ if (r < 0)
+ return log_warning_errno(r, "Failed to chase path %s for %s specified via %s: %m",
+ *s, name, source);
+
+ r = strv_consume(&w, p);
+ if (r < 0)
+ return log_oom();
+ }
+
+ if (strv_isempty(w))
+ return 0;
+
+ log_debug("%s set via %s", name, source);
+
+ *dest = TAKE_PTR(w);
+ return 1;
+}
+
+static int context_set_plugins(Context *c, const char *s, const char *source) {
+ _cleanup_strv_free_ char **v = NULL;
+
+ assert(c);
+
+ if (c->plugins || !s)
+ return 0;
+
+ v = strv_split(s, NULL);
+ if (!v)
+ return log_oom();
+
+ return context_set_path_strv(c, c->rfd, v, source, "plugins", &c->plugins);
+}
+
+static int context_set_initrds(Context *c, char* const* strv) {
+ assert(c);
+ return context_set_path_strv(c, AT_FDCWD, strv, "command line", "initrds", &c->initrds);
+}
+
+static int context_load_environment(Context *c) {
+ assert(c);
+
+ (void) context_set_machine_id(c, getenv("MACHINE_ID"), "environment");
+ (void) context_set_boot_root(c, getenv("BOOT_ROOT"), "environment");
+ (void) context_set_conf_root(c, getenv("KERNEL_INSTALL_CONF_ROOT"), "environment");
+ (void) context_set_plugins(c, getenv("KERNEL_INSTALL_PLUGINS"), "environment");
+ return 0;
+}
+
+static int context_ensure_conf_root(Context *c) {
+ int r;
+
+ assert(c);
+
+ if (c->conf_root)
+ return 0;
+
+ r = chaseat(c->rfd, "/etc/kernel", CHASE_AT_RESOLVE_IN_ROOT, &c->conf_root, /* ret_fd = */ NULL);
+ if (r < 0)
+ log_debug_errno(r, "Failed to chase /etc/kernel, ignoring: %m");
+
+ return 0;
+}
+
+static int context_load_install_conf_one(Context *c, const char *path) {
+ _cleanup_close_ int fd = -EBADF;
+ _cleanup_free_ char
+ *conf = NULL, *machine_id = NULL, *boot_root = NULL, *layout = NULL,
+ *initrd_generator = NULL, *uki_generator = NULL;
+ int r;
+
+ assert(c);
+ assert(path);
+
+ conf = path_join(path, "install.conf");
+ if (!conf)
+ return log_oom();
+
+ r = chaseat(c->rfd, conf, CHASE_AT_RESOLVE_IN_ROOT, NULL, &fd);
+ if (r == -ENOENT)
+ return 0;
+ if (r < 0)
+ return log_error_errno(r, "Failed to chase %s: %m", conf);
+
+ log_debug("Loading %s…", conf);
+
+ r = parse_env_file_fd(fd, conf,
+ "MACHINE_ID", &machine_id,
+ "BOOT_ROOT", &boot_root,
+ "layout", &layout,
+ "initrd_generator", &initrd_generator,
+ "uki_generator", &uki_generator);
+ if (r < 0)
+ return log_error_errno(r, "Failed to parse '%s': %m", conf);
+
+ (void) context_set_machine_id(c, machine_id, conf);
+ (void) context_set_boot_root(c, boot_root, conf);
+ (void) context_set_layout(c, layout, conf);
+ (void) context_set_initrd_generator(c, initrd_generator, conf);
+ (void) context_set_uki_generator(c, uki_generator, conf);
+
+ log_debug("Loaded %s.", conf);
+ return 1;
+}
+
+static int context_load_install_conf(Context *c) {
+ int r;
+
+ assert(c);
+
+ if (c->conf_root) {
+ r = context_load_install_conf_one(c, c->conf_root);
+ if (r != 0)
+ return r;
+ }
+
+ STRV_FOREACH(p, STRV_MAKE("/etc/kernel", "/usr/lib/kernel")) {
+ r = context_load_install_conf_one(c, *p);
+ if (r != 0)
+ return r;
+ }
+
+ return 0;
+}
+
+static int context_load_machine_info(Context *c) {
+ _cleanup_close_ int fd = -EBADF;
+ _cleanup_free_ char *machine_id = NULL, *layout = NULL;
+ static const char *path = "/etc/machine-info";
+ int r;
+
+ assert(c);
+
+ /* If the user configured an explicit machine ID in /etc/machine-info to use for our purpose, we'll
+ * use that instead (for compatibility). */
+
+ if (!sd_id128_is_null(c->machine_id) && c->layout >= 0)
+ return 0;
+
+ r = chaseat(c->rfd, path, CHASE_AT_RESOLVE_IN_ROOT, NULL, &fd);
+ if (r == -ENOENT)
+ return 0;
+ if (r < 0)
+ return log_error_errno(r, "Failed to chase %s: %m", path);
+
+ log_debug("Loading %s…", path);
+
+ r = parse_env_file_fd(fd, path,
+ "KERNEL_INSTALL_MACHINE_ID", &machine_id,
+ "KERNEL_INSTALL_LAYOUT", &layout);
+ if (r < 0)
+ return log_error_errno(r, "Failed to parse '%s': %m", path);
+
+ (void) context_set_machine_id(c, machine_id, path);
+ (void) context_set_layout(c, layout, path);
+ return 0;
+}
+
+static int context_load_machine_id(Context *c) {
+ int r;
+
+ assert(c);
+
+ r = id128_get_machine_at(c->rfd, &c->machine_id);
+ if (r < 0) {
+ if (ERRNO_IS_MACHINE_ID_UNSET(r))
+ return 0;
+ return log_error_errno(r, "Failed to load machine ID from /etc/machine-id: %m");
+ }
+
+ log_debug("MACHINE_ID=%s set via /etc/machine-id.", SD_ID128_TO_STRING(c->machine_id));
+ return 1; /* loaded */
+}
+
+static int context_ensure_machine_id(Context *c) {
+ int r;
+
+ assert(c);
+
+ if (!sd_id128_is_null(c->machine_id))
+ return 0;
+
+ /* If /etc/machine-id is initialized we'll use it. */
+ r = context_load_machine_id(c);
+ if (r != 0)
+ return r;
+
+ /* Otherwise we'll use a freshly generated one. */
+ r = sd_id128_randomize(&c->machine_id);
+ if (r < 0)
+ return log_error_errno(r, "Failed to generate random ID: %m");
+
+ c->machine_id_is_random = true;
+ log_debug("New machine ID '%s' generated.", SD_ID128_TO_STRING(c->machine_id));
+ return 0;
+}
+
+static int context_acquire_xbootldr(Context *c) {
+ int r;
+
+ assert(c);
+ assert(!c->boot_root);
+
+ r = find_xbootldr_and_warn_at(
+ /* rfd = */ c->rfd,
+ /* path = */ arg_xbootldr_path,
+ /* unprivileged_mode= */ -1,
+ /* ret_path = */ &c->boot_root,
+ /* ret_uuid = */ NULL,
+ /* ret_devid = */ NULL);
+ if (r == -ENOKEY) {
+ log_debug_errno(r, "Couldn't find an XBOOTLDR partition.");
+ return 0;
+ }
+ if (r == -EACCES && geteuid() != 0)
+ return log_error_errno(r, "Failed to determine XBOOTLDR partition: %m");
+ if (r < 0)
+ return r;
+
+ log_debug("Using XBOOTLDR partition at %s as $BOOT_ROOT.", c->boot_root);
+ return 1; /* found */
+}
+
+static int context_acquire_esp(Context *c) {
+ int r;
+
+ assert(c);
+ assert(!c->boot_root);
+
+ r = find_esp_and_warn_at(
+ /* rfd = */ c->rfd,
+ /* path = */ arg_esp_path,
+ /* unprivileged_mode= */ -1,
+ /* ret_path = */ &c->boot_root,
+ /* ret_part = */ NULL,
+ /* ret_pstart = */ NULL,
+ /* ret_psize = */ NULL,
+ /* ret_uuid = */ NULL,
+ /* ret_devid = */ NULL);
+ if (r == -ENOKEY) {
+ log_debug_errno(r, "Couldn't find EFI system partition, ignoring.");
+ return 0;
+ }
+ if (r == -EACCES && geteuid() != 0)
+ return log_error_errno(r, "Failed to determine EFI system partition: %m");
+ if (r < 0)
+ return r;
+
+ log_debug("Using EFI System Partition at %s as $BOOT_ROOT.", c->boot_root);
+ return 1; /* found */
+}
+
+static int context_ensure_boot_root(Context *c) {
+ int r;
+
+ assert(c);
+
+ /* If BOOT_ROOT is specified via environment or install.conf, then use it. */
+ if (c->boot_root)
+ return 0;
+
+ /* Otherwise, use XBOOTLDR partition, if mounted. */
+ r = context_acquire_xbootldr(c);
+ if (r != 0)
+ return r;
+
+ /* Otherwise, use EFI system partition, if mounted. */
+ r = context_acquire_esp(c);
+ if (r != 0)
+ return r;
+
+ /* If all else fails, use /boot. */
+ r = chaseat(c->rfd, "/boot", CHASE_AT_RESOLVE_IN_ROOT, &c->boot_root, /* ret_fd = */ NULL);
+ if (r < 0)
+ return log_error_errno(r, "Failed to chase '/boot': %m");
+
+ log_debug("KERNEL_INSTALL_BOOT_ROOT autodetection yielded no candidates, using \"%s\".", c->boot_root);
+ return 0;
+}
+
+static int context_ensure_entry_token(Context *c) {
+ int r;
+
+ assert(c);
+
+ /* Now that we determined the machine ID to use, let's determine the "token" for the boot loader
+ * entry to generate. We use that for naming the directory below $BOOT where we want to place the
+ * kernel/initrd and related resources, as well for naming the .conf boot loader spec entry.
+ * Typically this is just the machine ID, but it can be anything else, too, if we are told so. */
+
+ r = boot_entry_token_ensure_at(
+ c->rfd,
+ c->conf_root,
+ c->machine_id,
+ c->machine_id_is_random,
+ &c->entry_token_type,
+ &c->entry_token);
+ if (r < 0)
+ return r;
+
+ log_debug("Using entry token: %s", c->entry_token);
+ return 0;
+}
+
+static int context_load_plugins(Context *c) {
+ int r;
+
+ assert(c);
+
+ if (c->plugins)
+ return 0;
+
+ r = conf_files_list_strv_at(
+ &c->plugins,
+ ".install",
+ c->rfd,
+ CONF_FILES_EXECUTABLE | CONF_FILES_REGULAR | CONF_FILES_FILTER_MASKED,
+ STRV_MAKE_CONST("/etc/kernel/install.d", "/usr/lib/kernel/install.d"));
+ if (r < 0)
+ return log_error_errno(r, "Failed to find plugins: %m");
+
+ return 0;
+}
+
+static int context_init(Context *c) {
+ int r;
+
+ assert(c);
+
+ r = context_open_root(c);
+ if (r < 0)
+ return r;
+
+ r = context_load_environment(c);
+ if (r < 0)
+ return r;
+
+ r = context_ensure_conf_root(c);
+ if (r < 0)
+ return r;
+
+ r = context_load_install_conf(c);
+ if (r < 0)
+ return r;
+
+ r = context_load_machine_info(c);
+ if (r < 0)
+ return r;
+
+ r = context_ensure_machine_id(c);
+ if (r < 0)
+ return r;
+
+ r = context_ensure_boot_root(c);
+ if (r < 0)
+ return r;
+
+ r = context_ensure_entry_token(c);
+ if (r < 0)
+ return r;
+
+ r = context_load_plugins(c);
+ if (r < 0)
+ return r;
+
+ return 0;
+}
+
+static int context_inspect_kernel(Context *c) {
+ assert(c);
+
+ if (!c->kernel)
+ return 0;
+
+ return inspect_kernel(c->rfd, c->kernel, &c->kernel_image_type, NULL, NULL, NULL);
+}
+
+static int context_ensure_layout(Context *c) {
+ int r;
+
+ assert(c);
+ assert(c->boot_root);
+ assert(c->entry_token);
+
+ if (c->layout >= 0 && c->layout != LAYOUT_AUTO)
+ return 0;
+
+ /* No layout configured by the administrator. Let's try to figure it out automatically from metadata
+ * already contained in $BOOT_ROOT. */
+
+ if (c->kernel_image_type == KERNEL_IMAGE_TYPE_UKI) {
+ c->layout = LAYOUT_UKI;
+ log_debug("Kernel image type is %s, using layout=%s.",
+ kernel_image_type_to_string(c->kernel_image_type), layout_to_string(c->layout));
+ return 0;
+ }
+
+ _cleanup_free_ char *srel_path = path_join(c->boot_root, "loader/entries.srel");
+ if (!srel_path)
+ return log_oom();
+
+ _cleanup_free_ char *srel = NULL;
+ r = read_one_line_file_at(c->rfd, srel_path, &srel);
+ if (r >= 0) {
+ if (streq(srel, "type1"))
+ /* The loader/entries.srel file clearly indicates that the installed boot loader
+ * implements the proper standard upstream boot loader spec for Type #1 entries.
+ * Let's default to that, then. */
+ c->layout = LAYOUT_BLS;
+ else
+ /* The loader/entries.srel file indicates some other spec is implemented and owns the
+ * /loader/entries/ directory. Since we have no idea what that means, let's stay away
+ * from it by default. */
+ c->layout = LAYOUT_OTHER;
+
+ log_debug("%s with '%s' found, using layout=%s.", srel_path, srel, layout_to_string(c->layout));
+ return 0;
+ } else if (r != -ENOENT)
+ return log_error_errno(r, "Failed to read %s: %m", srel_path);
+
+ _cleanup_free_ char *entry_token_path = path_join(c->boot_root, c->entry_token);
+ if (!entry_token_path)
+ return log_oom();
+
+ r = is_dir_full(c->rfd, entry_token_path, /* follow = */ false);
+ if (r < 0 && r != -ENOENT)
+ return log_error_errno(r, "Failed to check if '%s' is a directory: %m", entry_token_path);
+ if (r > 0) {
+ /* If the metadata in $BOOT_ROOT doesn't tell us anything, then check if the entry token
+ * directory already exists. If so, let's assume it's the standard boot loader spec, too. */
+ c->layout = LAYOUT_BLS;
+ log_debug("%s exists, using layout=%s.", entry_token_path, layout_to_string(c->layout));
+ return 0;
+ }
+
+ /* There's no metadata in $BOOT_ROOT, and apparently no entry token directory installed? Then we
+ * really don't know anything. */
+ c->layout = LAYOUT_OTHER;
+ log_debug("Entry-token directory not found, using layout=%s.", layout_to_string(c->layout));
+ return 0;
+}
+
+static int context_set_up_staging_area(Context *c) {
+ static const char *template = "/tmp/kernel-install.staging.XXXXXX";
+ int r;
+
+ assert(c);
+
+ if (c->staging_area)
+ return 0;
+
+ if (c->action == ACTION_INSPECT) {
+ /* This is only used for display. The directory will not be created. */
+ c->staging_area = strdup(template);
+ if (!c->staging_area)
+ return log_oom();
+ } else {
+ r = mkdtemp_malloc(template, &c->staging_area);
+ if (r < 0)
+ return log_error_errno(r, "Failed to create staging area: %m");
+ }
+
+ return 0;
+}
+
+static int context_build_entry_dir(Context *c) {
+ assert(c);
+ assert(c->boot_root);
+ assert(c->entry_token);
+ assert(c->version || c->action == ACTION_INSPECT);
+
+ if (c->entry_dir)
+ return 0;
+
+ c->entry_dir = path_join(c->boot_root, c->entry_token, c->version ?: "KERNEL_VERSION");
+ if (!c->entry_dir)
+ return log_oom();
+
+ log_debug("Using ENTRY_DIR=%s", c->entry_dir);
+ return 0;
+}
+
+static bool context_should_make_entry_dir(Context *c) {
+ assert(c);
+
+ /* Compatibility with earlier versions that used the presence of $BOOT_ROOT/$ENTRY_TOKEN to signal to
+ * 00-entry-directory to create $ENTRY_DIR to serve as the indication to use or to not use the BLS */
+
+ if (arg_make_entry_directory < 0)
+ return c->layout == LAYOUT_BLS;
+
+ return arg_make_entry_directory;
+}
+
+static int context_make_entry_dir(Context *c) {
+ _cleanup_close_ int fd = -EBADF;
+
+ assert(c);
+ assert(c->entry_dir);
+
+ if (c->action != ACTION_ADD)
+ return 0;
+
+ if (!context_should_make_entry_dir(c))
+ return 0;
+
+ log_debug("mkdir -p %s", c->entry_dir);
+ fd = chase_and_openat(c->rfd, c->entry_dir, CHASE_AT_RESOLVE_IN_ROOT | CHASE_MKDIR_0755,
+ O_CLOEXEC | O_CREAT | O_DIRECTORY | O_PATH, NULL);
+ if (fd < 0)
+ return log_error_errno(fd, "Failed to make directory '%s': %m", c->entry_dir);
+
+ return 0;
+}
+
+static int context_remove_entry_dir(Context *c) {
+ _cleanup_free_ char *p = NULL;
+ _cleanup_close_ int fd = -EBADF;
+ struct stat st;
+ int r;
+
+ assert(c);
+ assert(c->entry_dir);
+
+ if (c->action != ACTION_REMOVE)
+ return 0;
+
+ if (!context_should_make_entry_dir(c))
+ return 0;
+
+ log_debug("rm -rf %s", c->entry_dir);
+ fd = chase_and_openat(c->rfd, c->entry_dir, CHASE_AT_RESOLVE_IN_ROOT, O_CLOEXEC | O_DIRECTORY, &p);
+ if (fd < 0) {
+ if (IN_SET(fd, -ENOTDIR, -ENOENT))
+ return 0;
+ return log_debug_errno(fd, "Failed to chase and open %s, ignoring: %m", c->entry_dir);
+ }
+
+ if (fstat(fd, &st) < 0)
+ return log_debug_errno(errno, "Failed to stat %s: %m", p);
+
+ r = rm_rf_children(TAKE_FD(fd), REMOVE_PHYSICAL|REMOVE_MISSING_OK|REMOVE_CHMOD, &st);
+ if (r < 0)
+ log_debug_errno(r, "Failed to remove children of %s, ignoring: %m", p);
+
+ if (unlinkat(c->rfd, p, AT_REMOVEDIR) < 0)
+ log_debug_errno(errno, "Failed to remove %s, ignoring: %m", p);
+
+ return 0;
+}
+
+static int context_build_arguments(Context *c) {
+ _cleanup_strv_free_ char **a = NULL;
+ const char *verb;
+ int r;
+
+ assert(c);
+ assert(c->entry_dir);
+
+ if (c->argv)
+ return 0;
+
+ switch (c->action) {
+ case ACTION_ADD:
+ assert(c->version);
+ assert(c->kernel);
+ verb = "add";
+ break;
+
+ case ACTION_REMOVE:
+ assert(c->version);
+ assert(!c->kernel);
+ assert(!c->initrds);
+ verb = "remove";
+ break;
+
+ case ACTION_INSPECT:
+ assert(!c->version);
+ assert(!c->initrds);
+ verb = "add|remove";
+ break;
+
+ default:
+ assert_not_reached();
+ }
+
+ a = strv_new("dummy-arg", /* to make strv_free() works for this variable. */
+ verb,
+ c->version ?: "KERNEL_VERSION",
+ c->entry_dir);
+ if (!a)
+ return log_oom();
+
+ if (c->action == ACTION_ADD) {
+ r = strv_extend(&a, c->kernel);
+ if (r < 0)
+ return log_oom();
+
+ r = strv_extend_strv(&a, c->initrds, /* filter_duplicates = */ false);
+ if (r < 0)
+ return log_oom();
+
+ } else if (c->action == ACTION_INSPECT) {
+ r = strv_extend(&a, c->kernel ?: "[KERNEL_IMAGE]");
+ if (r < 0)
+ return log_oom();
+
+ r = strv_extend(&a, "[INITRD...]");
+ if (r < 0)
+ return log_oom();
+ }
+
+ c->argv = TAKE_PTR(a);
+ return 0;
+}
+
+static int context_build_environment(Context *c) {
+ _cleanup_strv_free_ char **e = NULL;
+ int r;
+
+ assert(c);
+
+ if (c->envp)
+ return 0;
+
+ r = strv_env_assign_many(&e,
+ "LC_COLLATE", SYSTEMD_DEFAULT_LOCALE,
+ "KERNEL_INSTALL_VERBOSE", one_zero(arg_verbose),
+ "KERNEL_INSTALL_IMAGE_TYPE", kernel_image_type_to_string(c->kernel_image_type),
+ "KERNEL_INSTALL_MACHINE_ID", SD_ID128_TO_STRING(c->machine_id),
+ "KERNEL_INSTALL_ENTRY_TOKEN", c->entry_token,
+ "KERNEL_INSTALL_BOOT_ROOT", c->boot_root,
+ "KERNEL_INSTALL_LAYOUT", context_get_layout(c),
+ "KERNEL_INSTALL_INITRD_GENERATOR", strempty(c->initrd_generator),
+ "KERNEL_INSTALL_UKI_GENERATOR", strempty(c->uki_generator),
+ "KERNEL_INSTALL_STAGING_AREA", c->staging_area);
+ if (r < 0)
+ return log_error_errno(r, "Failed to build environment variables for plugins: %m");
+
+ c->envp = TAKE_PTR(e);
+ return 0;
+}
+
+static int context_prepare_execution(Context *c) {
+ int r;
+
+ assert(c);
+
+ r = context_inspect_kernel(c);
+ if (r < 0)
+ return r;
+
+ r = context_ensure_layout(c);
+ if (r < 0)
+ return r;
+
+ r = context_set_up_staging_area(c);
+ if (r < 0)
+ return r;
+
+ r = context_build_entry_dir(c);
+ if (r < 0)
+ return r;
+
+ r = context_build_arguments(c);
+ if (r < 0)
+ return r;
+
+ r = context_build_environment(c);
+ if (r < 0)
+ return r;
+
+ return 0;
+}
+
+static int context_execute(Context *c) {
+ int r;
+
+ assert(c);
+
+ r = context_make_entry_dir(c);
+ if (r < 0)
+ return r;
+
+ if (DEBUG_LOGGING) {
+ _cleanup_free_ char *x = strv_join_full(c->plugins, "", "\n ", /* escape_separator = */ false);
+ log_debug("Using plugins: %s", strna(x));
+
+ _cleanup_free_ char *y = strv_join_full(c->envp, "", "\n ", /* escape_separator = */ false);
+ log_debug("Plugin environment: %s", strna(y));
+
+ _cleanup_free_ char *z = strv_join(strv_skip(c->argv, 1), " ");
+ log_debug("Plugin arguments: %s", strna(z));
+ }
+
+ r = execute_strv(
+ /* name = */ NULL,
+ c->plugins,
+ /* root = */ NULL,
+ USEC_INFINITY,
+ /* callbacks = */ NULL,
+ /* callback_args = */ NULL,
+ c->argv,
+ c->envp,
+ EXEC_DIR_SKIP_REMAINING);
+ if (r < 0)
+ return r;
+
+ r = context_remove_entry_dir(c);
+ if (r < 0)
+ return r;
+
+ return 0;
+}
+
+static int verb_add(int argc, char *argv[], void *userdata) {
+ Context *c = ASSERT_PTR(userdata);
+ int r;
+
+ assert(argc >= 3);
+ assert(argv);
+
+ c->action = ACTION_ADD;
+
+ r = context_set_version(c, argv[1]);
+ if (r < 0)
+ return r;
+
+ r = context_set_kernel(c, argv[2]);
+ if (r < 0)
+ return r;
+
+ r = context_set_initrds(c, strv_skip(argv, 3));
+ if (r < 0)
+ return r;
+
+ r = context_prepare_execution(c);
+ if (r < 0)
+ return r;
+
+ return context_execute(c);
+}
+
+static int run_as_installkernel(int argc, char *argv[], Context *c) {
+ /* kernel's install.sh invokes us as
+ * /sbin/installkernel <version> <vmlinuz> <map> <installation-dir>
+ * We ignore the last two arguments. */
+ if (optind + 2 > argc)
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "'installkernel' command requires at least two arguments.");
+
+ return verb_add(3, STRV_MAKE("add", argv[optind], argv[optind+1]), c);
+}
+
+static int verb_remove(int argc, char *argv[], void *userdata) {
+ Context *c = ASSERT_PTR(userdata);
+ int r;
+
+ assert(argc == 2);
+ assert(argv);
+
+ c->action = ACTION_REMOVE;
+
+ r = context_set_version(c, argv[1]);
+ if (r < 0)
+ return r;
+
+ r = context_prepare_execution(c);
+ if (r < 0)
+ return r;
+
+ return context_execute(c);
+}
+
+static int verb_inspect(int argc, char *argv[], void *userdata) {
+ Context *c = ASSERT_PTR(userdata);
+ _cleanup_free_ char *joined = NULL;
+ int r;
+
+ c->action = ACTION_INSPECT;
+
+ if (argc >= 2) {
+ r = context_set_kernel(c, argv[1]);
+ if (r < 0)
+ return r;
+ }
+
+ r = context_prepare_execution(c);
+ if (r < 0)
+ return r;
+
+ printf("%sBoot Loader Entries:%s\n", ansi_underline(), ansi_normal());
+ printf(" $BOOT: %s\n", c->boot_root);
+ printf(" Token: %s\n", c->entry_token);
+ puts("");
+
+ printf("%sUsing plugins:%s\n", ansi_underline(), ansi_normal());
+ strv_print_full(c->plugins, " ");
+ puts("");
+
+ printf("%sPlugin environment:%s\n", ansi_underline(), ansi_normal());
+ strv_print_full(c->envp, " ");
+ puts("");
+
+ printf("%sPlugin arguments:%s\n", ansi_underline(), ansi_normal());
+ joined = strv_join(strv_skip(c->argv, 1), " ");
+ printf(" %s\n", strna(joined));
+
+ return 0;
+}
+
+static bool bypass(void) {
+ int r;
+
+ r = getenv_bool("KERNEL_INSTALL_BYPASS");
+ if (r < 0 && r != -ENXIO)
+ log_debug_errno(r, "Failed to parse $KERNEL_INSTALL_BYPASS, assuming no.");
+ if (r <= 0)
+ return false;
+
+ log_debug("$KERNEL_INSTALL_BYPASS is enabled, skipping execution.");
+ return true;
+}
+
+static int help(void) {
+ _cleanup_free_ char *link = NULL;
+ int r;
+
+ r = terminal_urlify_man("kernel-install", "8", &link);
+ if (r < 0)
+ return log_oom();
+
+ printf("%1$s [OPTIONS...] COMMAND ...\n\n"
+ "%2$sAdd and remove kernel and initrd images to and from /boot%3$s\n"
+ "\nUsage:\n"
+ " %1$s [OPTIONS...] add KERNEL-VERSION KERNEL-IMAGE [INITRD-FILE...]\n"
+ " %1$s [OPTIONS...] remove KERNEL-VERSION\n"
+ " %1$s [OPTIONS...] inspect [KERNEL-IMAGE]\n"
+ "\nOptions:\n"
+ " -h --help Show this help\n"
+ " --version Show package version\n"
+ " -v --verbose Increase verbosity\n"
+ " --esp-path=PATH Path to the EFI System Partition (ESP)\n"
+ " --boot-path=PATH Path to the $BOOT partition\n"
+ " --make-entry-directory=yes|no|auto\n"
+ " Create $BOOT/ENTRY-TOKEN/ directory\n"
+ " --entry-token=machine-id|os-id|os-image-id|auto|literal:…\n"
+ " Entry token to use for this installation\n"
+ "\nSee the %4$s for details.\n",
+ program_invocation_short_name,
+ ansi_highlight(),
+ ansi_normal(),
+ link);
+
+ return 0;
+}
+
+static int parse_argv(int argc, char *argv[], Context *c) {
+ enum {
+ ARG_VERSION = 0x100,
+ ARG_ESP_PATH,
+ ARG_BOOT_PATH,
+ ARG_MAKE_ENTRY_DIRECTORY,
+ ARG_ENTRY_TOKEN,
+ };
+ static const struct option options[] = {
+ { "help", no_argument, NULL, 'h' },
+ { "version", no_argument, NULL, ARG_VERSION },
+ { "verbose", no_argument, NULL, 'v' },
+ { "esp-path", required_argument, NULL, ARG_ESP_PATH },
+ { "boot-path", required_argument, NULL, ARG_BOOT_PATH },
+ { "make-entry-directory", required_argument, NULL, ARG_MAKE_ENTRY_DIRECTORY },
+ { "entry-token", required_argument, NULL, ARG_ENTRY_TOKEN },
+ {}
+ };
+ int t, r;
+
+ assert(argc >= 0);
+ assert(argv);
+ assert(c);
+
+ while ((t = getopt_long(argc, argv, "hv", options, NULL)) >= 0)
+ switch (t) {
+ case 'h':
+ return help();
+
+ case ARG_VERSION:
+ return version();
+
+ case 'v':
+ log_set_max_level(LOG_DEBUG);
+ arg_verbose = true;
+ break;
+
+ case ARG_ESP_PATH:
+ r = parse_path_argument(optarg, /* suppress_root = */ false, &arg_esp_path);
+ if (r < 0)
+ return log_oom();
+ break;
+
+ case ARG_BOOT_PATH:
+ r = parse_path_argument(optarg, /* suppress_root = */ false, &arg_xbootldr_path);
+ if (r < 0)
+ return log_oom();
+ break;
+
+ case ARG_MAKE_ENTRY_DIRECTORY:
+ if (streq(optarg, "auto"))
+ arg_make_entry_directory = -1;
+ else {
+ r = parse_boolean_argument("--make-entry-directory=", optarg, NULL);
+ if (r < 0)
+ return r;
+
+ arg_make_entry_directory = r;
+ }
+ break;
+
+ case ARG_ENTRY_TOKEN:
+ r = parse_boot_entry_token_type(optarg, &c->entry_token_type, &c->entry_token);
+ if (r < 0)
+ return r;
+ break;
+
+ case '?':
+ return -EINVAL;
+
+ default:
+ assert_not_reached();
+ }
+
+ return 1;
+}
+
+static int run(int argc, char* argv[]) {
+ static const Verb verbs[] = {
+ { "add", 3, VERB_ANY, 0, verb_add },
+ { "remove", 2, 2, 0, verb_remove },
+ { "inspect", 1, 2, VERB_DEFAULT, verb_inspect },
+ {}
+ };
+ _cleanup_(context_done) Context c = {
+ .rfd = -EBADF,
+ .action = _ACTION_INVALID,
+ .kernel_image_type = KERNEL_IMAGE_TYPE_UNKNOWN,
+ .layout = _LAYOUT_INVALID,
+ .entry_token_type = BOOT_ENTRY_TOKEN_AUTO,
+ };
+ int r;
+
+ log_setup();
+
+ if (bypass())
+ return 0;
+
+ r = parse_argv(argc, argv, &c);
+ if (r <= 0)
+ return r;
+
+ r = context_init(&c);
+ if (r < 0)
+ return r;
+
+ if (invoked_as(argv, "installkernel"))
+ return run_as_installkernel(argc, argv, &c);
+
+ return dispatch_verb(argc, argv, verbs, &c);
+}
+
+DEFINE_MAIN_FUNCTION(run);
+++ /dev/null
-#!/bin/sh
-# -*- mode: shell-script; indent-tabs-mode: nil; sh-basic-offset: 4; -*-
-# ex: ts=8 sw=4 sts=4 et filetype=sh
-# 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/>.
-
-skip_remaining=77
-
-set -e
-
-usage()
-{
- echo "Usage:"
- echo " kernel-install [OPTIONS...] add KERNEL-VERSION KERNEL-IMAGE [INITRD-FILE...]"
- echo " kernel-install [OPTIONS...] remove KERNEL-VERSION"
- echo " kernel-install [OPTIONS...] inspect"
- echo "Options:"
- echo " -h, --help Print this help and exit"
- echo " --version Print version string and exit"
- echo " -v, --verbose Increase verbosity"
-}
-
-dropindirs_sort()
-{
- suffix="$1"
- shift
-
- for d; do
- for i in "$d/"*"$suffix"; do
- [ -e "$i" ] && echo "${i##*/}"
- done
- done | sort -Vu | while read -r f; do
- for d; do
- if [ -e "$d/$f" ]; then
- [ -x "$d/$f" ] && echo "$d/$f"
- continue 2
- fi
- done
- done
-}
-
-export LC_COLLATE=C
-
-for i; do
- if [ "$i" = "--help" ] || [ "$i" = "-h" ]; then
- usage
- exit 0
- fi
-done
-
-for i; do
- if [ "$i" = "--version" ]; then
- echo "kernel-install {{PROJECT_VERSION}} ({{GIT_VERSION}})"
- exit 0
- fi
-done
-
-if [ "$KERNEL_INSTALL_BYPASS" = "1" ]; then
- echo "kernel-install: Skipping execution because KERNEL_INSTALL_BYPASS=1"
- exit 0
-fi
-
-export KERNEL_INSTALL_VERBOSE=0
-if [ "$1" = "--verbose" ] || [ "$1" = "-v" ]; then
- shift
- export KERNEL_INSTALL_VERBOSE=1
- log_verbose() { printf "%s\n" "$*"; }
-else
- log_verbose() { :; }
-fi
-
-if [ "${0##*/}" = "installkernel" ]; then
- COMMAND=add
- # kernel's install.sh invokes us as
- # /sbin/installkernel <version> <vmlinuz> <map> <installation-dir>
- # We ignore the last two arguments.
- set -- "${1:?}" "${2:?}"
-else
- COMMAND="$1"
- [ $# -ge 1 ] && shift
-fi
-
-if [ "$COMMAND" = "inspect" ]; then
- KERNEL_VERSION=""
-else
- if [ $# -lt 1 ]; then
- echo "Error: not enough arguments" >&2
- exit 1
- fi
-
- KERNEL_VERSION="$1"
- shift
-fi
-
-# These three settings are only settable via install.conf
-layout=
-initrd_generator=
-uki_generator=
-# These two settings can be inherited from the environment
-_MACHINE_ID_SAVED="$MACHINE_ID"
-_BOOT_ROOT_SAVED="$BOOT_ROOT"
-
-if [ -n "$KERNEL_INSTALL_CONF_ROOT" ]; then
- install_conf="$KERNEL_INSTALL_CONF_ROOT/install.conf"
-elif [ -f "/etc/kernel/install.conf" ]; then
- install_conf="/etc/kernel/install.conf"
-elif [ -f "/usr/lib/kernel/install.conf" ]; then
- install_conf="/usr/lib/kernel/install.conf"
-else
- install_conf=
-fi
-
-if [ -f "$install_conf" ]; then
- log_verbose "Reading $install_conf…"
- # shellcheck source=/dev/null
- . "$install_conf"
-fi
-
-[ -n "$layout" ] && log_verbose "$install_conf configures layout=$layout"
-[ -n "$initrd_generator" ] && \
- log_verbose "$install_conf configures initrd_generator=$initrd_generator"
-[ -n "$uki_generator" ] && \
- log_verbose "$install_conf configures uki_generator=$uki_generator"
-
-if [ -n "$_MACHINE_ID_SAVED" ]; then
- MACHINE_ID="$_MACHINE_ID_SAVED"
- log_verbose "MACHINE_ID=$MACHINE_ID set via environment"
-else
- [ -n "$MACHINE_ID" ] && log_verbose "MACHINE_ID=$MACHINE_ID set via install.conf"
-fi
-
-if [ -n "$_BOOT_ROOT_SAVED" ]; then
- BOOT_ROOT="$_BOOT_ROOT_SAVED"
- log_verbose "BOOT_ROOT=$BOOT_ROOT set via environment"
-else
- [ -n "$BOOT_ROOT" ] && log_verbose "BOOT_ROOT=$BOOT_ROOT set via install.conf"
-fi
-
-# If /etc/machine-id is initialized we'll use it, otherwise we'll use a freshly
-# generated one. If the user configured an explicit machine ID to use in
-# /etc/machine-info to use for our purpose, we'll use that instead (for
-# compatibility).
-# shellcheck source=/dev/null
-if [ -z "$MACHINE_ID" ] && [ -f /etc/machine-info ]; then
- . /etc/machine-info
- MACHINE_ID="$KERNEL_INSTALL_MACHINE_ID"
- [ -n "$MACHINE_ID" ] && \
- log_verbose "machine-id $MACHINE_ID acquired from /etc/machine-info"
-fi
-if [ -z "$MACHINE_ID" ] && [ -s /etc/machine-id ]; then
- read -r MACHINE_ID </etc/machine-id
- [ "$MACHINE_ID" = "uninitialized" ] && unset MACHINE_ID
- [ -n "$MACHINE_ID" ] && \
- log_verbose "machine-id $MACHINE_ID acquired from /etc/machine-id"
-fi
-if [ -z "$MACHINE_ID" ]; then
- MACHINE_ID="$(systemd-id128 new)" || exit 1
- log_verbose "new machine-id $MACHINE_ID generated"
-fi
-
-# Now that we determined the machine ID to use, let's determine the "token" for
-# the boot loader entry to generate. We use that for naming the directory below
-# $BOOT where we want to place the kernel/initrd and related resources, as well
-# for naming the .conf boot loader spec entry. Typically this is just the
-# machine ID, but it can be anything else, too, if we are told so.
-ENTRY_TOKEN_FILE="${KERNEL_INSTALL_CONF_ROOT:-/etc/kernel}/entry-token"
-
-if [ -z "$ENTRY_TOKEN" ] && [ -f "$ENTRY_TOKEN_FILE" ]; then
- read -r ENTRY_TOKEN <"$ENTRY_TOKEN_FILE"
- log_verbose "entry-token \"$ENTRY_TOKEN\" acquired from $ENTRY_TOKEN_FILE"
-fi
-if [ -z "$ENTRY_TOKEN" ]; then
- # If not configured explicitly, then use a few candidates: the machine ID,
- # the IMAGE_ID= and ID= fields from /etc/os-release and finally the fixed
- # string "Default"
- ENTRY_TOKEN_SEARCH="$MACHINE_ID"
- # shellcheck source=/dev/null
- [ -f /etc/os-release ] && . /etc/os-release
- [ -n "$IMAGE_ID" ] && ENTRY_TOKEN_SEARCH="$ENTRY_TOKEN_SEARCH $IMAGE_ID"
- [ -n "$ID" ] && ENTRY_TOKEN_SEARCH="$ENTRY_TOKEN_SEARCH $ID"
- ENTRY_TOKEN_SEARCH="$ENTRY_TOKEN_SEARCH Default"
-else
- ENTRY_TOKEN_SEARCH="$ENTRY_TOKEN"
-fi
-log_verbose "Entry-token candidates: $ENTRY_TOKEN_SEARCH"
-
-# NB: The $MACHINE_ID is guaranteed to be a valid machine ID, but
-# $ENTRY_TOKEN can be any string that fits into a VFAT filename, though
-# typically is just the machine ID.
-
-if [ -n "$BOOT_ROOT" ]; then
- # If this was already configured, don't try to guess
- BOOT_ROOT_SEARCH="$BOOT_ROOT"
-else
- BOOT_ROOT_SEARCH="/efi /boot /boot/efi"
-fi
-
-for pref in $BOOT_ROOT_SEARCH; do
- for suff in $ENTRY_TOKEN_SEARCH; do
- if [ -d "$pref/$suff" ]; then
- [ -z "$BOOT_ROOT" ] && BOOT_ROOT="$pref"
- [ -z "$ENTRY_TOKEN" ] && ENTRY_TOKEN="$suff"
-
- log_verbose "$pref/$suff exists, using BOOT_ROOT=$BOOT_ROOT, ENTRY_TOKEN=$ENTRY_TOKEN"
- break 2
- else
- log_verbose "$pref/$suff not found…"
- fi
- done
-
- if [ -d "$pref/loader/entries" ]; then
- [ -z "$BOOT_ROOT" ] && BOOT_ROOT="$pref"
- log_verbose "$pref/loader/entries exists, using BOOT_ROOT=$BOOT_ROOT"
- break
- else
- log_verbose "$pref/loader/entries not found…"
- fi
-done
-
-[ -z "$BOOT_ROOT" ] && for pref in "/efi" "/boot/efi"; do
- if mountpoint -q "$pref"; then
- BOOT_ROOT="$pref"
- log_verbose "$pref is a mount point, using BOOT_ROOT=$BOOT_ROOT"
- break
- else
- log_verbose "$pref is not a mount point…"
- fi
-done
-
-if [ -z "$BOOT_ROOT" ]; then
- BOOT_ROOT="/boot"
- log_verbose "KERNEL_INSTALL_BOOT_ROOT autodetection yielded no candidates, using \"$BOOT_ROOT\""
-fi
-
-if [ -z "$ENTRY_TOKEN" ]; then
- ENTRY_TOKEN="$MACHINE_ID"
- log_verbose "No entry-token candidate matched, using \"$ENTRY_TOKEN\" from machine-id"
-fi
-
-export KERNEL_INSTALL_IMAGE_TYPE=""
-if [ -f "$1" ]; then
- KERNEL_INSTALL_IMAGE_TYPE="$(bootctl kernel-identify "$1" 2>/dev/null || echo "unknown")"
-fi
-
-if [ "$layout" = "auto" ] || [ -z "$layout" ]; then
- # No layout configured by the administrator. Let's try to figure it out
- # automatically from metadata already contained in $BOOT_ROOT.
- if [ "$KERNEL_INSTALL_IMAGE_TYPE" = "uki" ]; then
- layout="uki"
- log_verbose "Kernel image is UKI, using layout=$layout"
- elif [ -e "$BOOT_ROOT/loader/entries.srel" ]; then
- read -r ENTRIES_SREL <"$BOOT_ROOT/loader/entries.srel"
- if [ "$ENTRIES_SREL" = "type1" ]; then
- # The loader/entries.srel file clearly indicates that the installed
- # boot loader implements the proper standard upstream boot loader
- # spec for Type #1 entries. Let's default to that, then.
- layout="bls"
- else
- # The loader/entries.srel file indicates some other spec is
- # implemented and owns the /loader/entries/ directory. Since we
- # have no idea what that means, let's stay away from it by default.
- layout="other"
- fi
- log_verbose "$BOOT_ROOT/loader/entries.srel with '$ENTRIES_SREL' found, using layout=$layout"
-
- elif [ -d "$BOOT_ROOT/$ENTRY_TOKEN" ]; then
- # If the metadata in $BOOT_ROOT doesn't tell us anything, then check if
- # the entry token directory already exists. If so, let's assume it's
- # the standard boot loader spec, too.
- layout="bls"
-
- log_verbose "$BOOT_ROOT/$ENTRY_TOKEN exists, using layout=$layout"
- else
- # There's no metadata in $BOOT_ROOT, and apparently no entry token
- # directory installed? Then we really don't know anything.
- layout="other"
-
- log_verbose "Entry-token directory not found, using layout=$layout"
- fi
-fi
-
-ENTRY_DIR_ABS="$BOOT_ROOT/$ENTRY_TOKEN/$KERNEL_VERSION"
-log_verbose "Using ENTRY_DIR_ABS=$ENTRY_DIR_ABS"
-
-# Provide a directory where to store generated initrds
-cleanup() {
- [ -n "$KERNEL_INSTALL_STAGING_AREA" ] && rm -rf "$KERNEL_INSTALL_STAGING_AREA"
-}
-
-trap cleanup EXIT
-
-KERNEL_INSTALL_STAGING_AREA="$(mktemp -d -t kernel-install.staging.XXXXXXX)"
-
-export KERNEL_INSTALL_MACHINE_ID="$MACHINE_ID"
-export KERNEL_INSTALL_ENTRY_TOKEN="$ENTRY_TOKEN"
-export KERNEL_INSTALL_BOOT_ROOT="$BOOT_ROOT"
-export KERNEL_INSTALL_LAYOUT="$layout"
-export KERNEL_INSTALL_INITRD_GENERATOR="$initrd_generator"
-export KERNEL_INSTALL_UKI_GENERATOR="$uki_generator"
-export KERNEL_INSTALL_STAGING_AREA
-
-MAKE_ENTRY_DIR_ABS=0
-[ "$layout" = "bls" ] || MAKE_ENTRY_DIR_ABS=1
-
-ret=0
-
-if [ -z "$KERNEL_INSTALL_PLUGINS" ]; then
- KERNEL_INSTALL_PLUGINS="$(
- dropindirs_sort ".install" \
- "/etc/kernel/install.d" \
- "/usr/lib/kernel/install.d"
- )"
-fi
-
-if [ "$KERNEL_INSTALL_VERBOSE" -gt 0 ]; then
- printf '%s\n' "Plugin files:"
- for f in $KERNEL_INSTALL_PLUGINS; do
- printf '%s\n' "$f"
- done
-fi
-
-case "$COMMAND" in
- add)
- if [ $# -lt 1 ]; then
- echo "Error: command 'add' requires a kernel image" >&2
- exit 1
- fi
-
- if ! [ -f "$1" ]; then
- echo "Error: kernel image argument $1 not a file" >&2
- exit 1
- fi
-
- if [ "$MAKE_ENTRY_DIR_ABS" -eq 0 ]; then
- # Compatibility with earlier versions that used the presence of $BOOT_ROOT/$ENTRY_TOKEN
- # to signal to 00-entry-directory to create $ENTRY_DIR_ABS
- # to serve as the indication to use or to not use the BLS
- if [ "$KERNEL_INSTALL_VERBOSE" -gt 0 ]; then
- echo "+mkdir -v -p $ENTRY_DIR_ABS"
- mkdir -v -p "$ENTRY_DIR_ABS" || exit 1
- else
- mkdir -p "$ENTRY_DIR_ABS" || exit 1
- fi
- fi
-
- for f in $KERNEL_INSTALL_PLUGINS; do
- log_verbose "+$f add $KERNEL_VERSION $ENTRY_DIR_ABS" "$@"
- err=0
- "$f" add "$KERNEL_VERSION" "$ENTRY_DIR_ABS" "$@" || err=$?
- [ $err -eq $skip_remaining ] && break
- [ $err -ne 0 ] && exit $err
- done
- ;;
-
- remove)
- for f in $KERNEL_INSTALL_PLUGINS; do
- log_verbose "+$f remove $KERNEL_VERSION $ENTRY_DIR_ABS"
- err=0
- "$f" remove "$KERNEL_VERSION" "$ENTRY_DIR_ABS" || err=$?
- [ $err -eq $skip_remaining ] && break
- [ $err -ne 0 ] && exit $err
- done
-
- if [ "$MAKE_ENTRY_DIR_ABS" -eq 0 ]; then
- log_verbose "Removing $ENTRY_DIR_ABS/"
- rm -rf "$ENTRY_DIR_ABS"
- fi
- ;;
-
- inspect)
- echo "KERNEL_INSTALL_MACHINE_ID: $KERNEL_INSTALL_MACHINE_ID"
- echo "KERNEL_INSTALL_ENTRY_TOKEN: $KERNEL_INSTALL_ENTRY_TOKEN"
- echo "KERNEL_INSTALL_BOOT_ROOT: $KERNEL_INSTALL_BOOT_ROOT"
- echo "KERNEL_INSTALL_LAYOUT: $KERNEL_INSTALL_LAYOUT"
- echo "KERNEL_INSTALL_INITRD_GENERATOR: $KERNEL_INSTALL_INITRD_GENERATOR"
- echo "KERNEL_INSTALL_UKI_GENERATOR: $KERNEL_INSTALL_UKI_GENERATOR"
- echo "ENTRY_DIR_ABS: $KERNEL_INSTALL_BOOT_ROOT/$ENTRY_TOKEN/\$KERNEL_VERSION"
-
- # Assert that ENTRY_DIR_ABS actually matches what we are printing here
- [ "${ENTRY_DIR_ABS%/*}" = "$KERNEL_INSTALL_BOOT_ROOT/$ENTRY_TOKEN" ] || { echo "Assertion didn't pass." >&2; exit 1; }
- ;;
-
- *)
- echo "Error: unknown command '$COMMAND'" >&2
- exit 1
- ;;
-esac
-
-exit "$ret"
# SPDX-License-Identifier: LGPL-2.1-or-later
-kernel_install_in = files('kernel-install.in')
-
ukify_install = custom_target(
'60-ukify.install',
input : '60-ukify.install.in',
grep -qE 'initrd' "$BOOT_ROOT/the-token/1.1.1/initrd"
"$kernel_install" inspect
+"$kernel_install" inspect "$D/sources/linux"
"$kernel_install" -v remove 1.1.1
test ! -e "$entry"
test ! -e "$BOOT_ROOT/the-token/1.1.1/linux"
test ! -e "$BOOT_ROOT/the-token/1.1.1/initrd"
fi
+
+###########################################
+# tests for --make-entry-directory=
+###########################################
+
+# disable all dropins
+cat >"$D/00-skip.install" <<EOF
+#!/bin/bash
+exit 77
+EOF
+chmod +x "$D/00-skip.install"
+export KERNEL_INSTALL_PLUGINS="$D/00-skip.install"
+
+# drop layout= from install.conf
+cat >"$D/sources/install.conf" <<EOF
+initrd_generator=none
+# those are overridden by envvars
+BOOT_ROOT="$D/badboot"
+MACHINE_ID=badbadbadbadbadbad6abadbadbadbad
+EOF
+export KERNEL_INSTALL_CONF_ROOT="$D/sources"
+
+rm -rf "$BOOT_ROOT"
+mkdir -p "$BOOT_ROOT"
+
+# 1. defaults to 'auto', and the entry directory is created only when the layout is BLS
+# 1.1 token directory does not exist -> layout is other.
+"$kernel_install" -v add 1.1.1 "$D/sources/linux" "$D/sources/initrd"
+test ! -e "$BOOT_ROOT/the-token/1.1.1"
+"$kernel_install" -v remove 1.1.1
+test ! -e "$BOOT_ROOT/the-token/1.1.1"
+
+# 1.2 token directory exists -> layout is BLS
+mkdir -p "$BOOT_ROOT/the-token"
+"$kernel_install" -v add 1.1.1 "$D/sources/linux" "$D/sources/initrd"
+test -d "$BOOT_ROOT/the-token/1.1.1"
+"$kernel_install" -v remove 1.1.1
+test ! -e "$BOOT_ROOT/the-token/1.1.1"
+rmdir "$BOOT_ROOT/the-token"
+
+# 2. --make-entry-directory=yes
+# 2.1 token directory does not exist -> layout is other.
+"$kernel_install" -v --make-entry-directory=yes add 1.1.1 "$D/sources/linux" "$D/sources/initrd"
+test -d "$BOOT_ROOT/the-token/1.1.1"
+"$kernel_install" -v --make-entry-directory=yes remove 1.1.1
+test ! -e "$BOOT_ROOT/the-token/1.1.1"
+test -d "$BOOT_ROOT/the-token"
+
+# 2.2 token directory exists -> layout is BLS
+mkdir -p "$BOOT_ROOT/the-token"
+"$kernel_install" -v --make-entry-directory=yes add 1.1.1 "$D/sources/linux" "$D/sources/initrd"
+test -d "$BOOT_ROOT/the-token/1.1.1"
+"$kernel_install" -v --make-entry-directory=yes remove 1.1.1
+test ! -e "$BOOT_ROOT/the-token/1.1.1"
+test -d "$BOOT_ROOT/the-token"
+rmdir "$BOOT_ROOT/the-token"
+
+# 3. --make-entry-directory=no
+# 3.1 token directory does not exist -> layout is other.
+"$kernel_install" -v --make-entry-directory=no add 1.1.1 "$D/sources/linux" "$D/sources/initrd"
+test ! -e "$BOOT_ROOT/the-token/1.1.1"
+"$kernel_install" -v --make-entry-directory=no remove 1.1.1
+test ! -e "$BOOT_ROOT/the-token/1.1.1"
+
+# 3.2 token directory exists -> layout is BLS
+mkdir -p "$BOOT_ROOT/the-token"
+"$kernel_install" -v --make-entry-directory=no add 1.1.1 "$D/sources/linux" "$D/sources/initrd"
+test ! -e "$BOOT_ROOT/the-token/1.1.1"
+"$kernel_install" -v --make-entry-directory=no remove 1.1.1
+test ! -e "$BOOT_ROOT/the-token/1.1.1"
+test -d "$BOOT_ROOT/the-token"
+rmdir "$BOOT_ROOT/the-token"
+
+###########################################
+# tests for --entry-token=
+###########################################
+"$kernel_install" -v --make-entry-directory=yes --entry-token=machine-id add 1.1.1 "$D/sources/linux" "$D/sources/initrd"
+test -d "$BOOT_ROOT/$MACHINE_ID/1.1.1"
+"$kernel_install" -v --make-entry-directory=yes --entry-token=machine-id remove 1.1.1
+test ! -e "$BOOT_ROOT/$MACHINE_ID/1.1.1"
+test -d "$BOOT_ROOT/$MACHINE_ID"
+rmdir "$BOOT_ROOT/$MACHINE_ID"
+
+"$kernel_install" -v --make-entry-directory=yes --entry-token=literal:hoge add 1.1.1 "$D/sources/linux" "$D/sources/initrd"
+test -d "$BOOT_ROOT/hoge/1.1.1"
+"$kernel_install" -v --make-entry-directory=yes --entry-token=literal:hoge remove 1.1.1
+test ! -e "$BOOT_ROOT/hoge/1.1.1"
+test -d "$BOOT_ROOT/hoge"
+rmdir "$BOOT_ROOT/hoge"
sd_pid_notify_barrier;
sd_event_source_leave_ratelimit;
sd_journal_step_one;
+ sd_session_get_leader;
} LIBSYSTEMD_253;
assert_se(bind(fd, &u.sa, sa_len) >= 0);
usleep(100 * USEC_PER_MSEC);
- assert_se(listen(fd, SOMAXCONN) >= 0);
+ assert_se(listen(fd, SOMAXCONN_DELUXE) >= 0);
usleep(100 * USEC_PER_MSEC);
assert_se(touch(path) >= 0);
struct cmsghdr *cmsg = NULL;
const char *e;
bool send_ucred;
- int r;
+ ssize_t n;
+ int type, r;
if (!state)
return -EINVAL;
if (address.sockaddr.vm.svm_family == AF_VSOCK && address.sockaddr.vm.svm_cid == VMADDR_CID_ANY)
return -EINVAL;
+ type = address.type == 0 ? SOCK_DGRAM : address.type;
+
/* At the time of writing QEMU does not yet support AF_VSOCK + SOCK_DGRAM and returns
* ENODEV. Fallback to SOCK_SEQPACKET in that case. */
- fd = socket(address.sockaddr.sa.sa_family, SOCK_DGRAM|SOCK_CLOEXEC, 0);
+ fd = socket(address.sockaddr.sa.sa_family, type|SOCK_CLOEXEC, 0);
if (fd < 0) {
- if (!(ERRNO_IS_NOT_SUPPORTED(errno) || errno == ENODEV) || address.sockaddr.sa.sa_family != AF_VSOCK)
- return log_debug_errno(errno, "Failed to open datagram notify socket to '%s': %m", e);
-
- fd = socket(address.sockaddr.sa.sa_family, SOCK_SEQPACKET|SOCK_CLOEXEC, 0);
+ if (!(ERRNO_IS_NOT_SUPPORTED(errno) || errno == ENODEV) || address.sockaddr.sa.sa_family != AF_VSOCK || address.type > 0)
+ return log_debug_errno(errno, "Failed to open %s notify socket to '%s': %m", socket_address_type_to_string(type), e);
+
+ type = SOCK_SEQPACKET;
+ fd = socket(address.sockaddr.sa.sa_family, type|SOCK_CLOEXEC, 0);
+ if (fd < 0 && ERRNO_IS_NOT_SUPPORTED(errno)) {
+ type = SOCK_STREAM;
+ fd = socket(address.sockaddr.sa.sa_family, type|SOCK_CLOEXEC, 0);
+ }
if (fd < 0)
- return log_debug_errno(errno, "Failed to open sequential packet socket to '%s': %m", e);
+ return log_debug_errno(errno, "Failed to open %s socket to '%s': %m", socket_address_type_to_string(type), e);
+ }
+ if (address.sockaddr.sa.sa_family == AF_VSOCK) {
r = vsock_bind_privileged_port(fd);
if (r < 0 && !ERRNO_IS_PRIVILEGE(r))
return log_debug_errno(r, "Failed to bind socket to privileged port: %m");
+ }
+ if (IN_SET(type, SOCK_STREAM, SOCK_SEQPACKET)) {
if (connect(fd, &address.sockaddr.sa, address.size) < 0)
return log_debug_errno(errno, "Failed to connect socket to '%s': %m", e);
msghdr.msg_name = NULL;
msghdr.msg_namelen = 0;
- } else if (address.sockaddr.sa.sa_family == AF_VSOCK) {
- r = vsock_bind_privileged_port(fd);
- if (r < 0 && !ERRNO_IS_PRIVILEGE(r))
- return log_debug_errno(r, "Failed to bind socket to privileged port: %m");
}
(void) fd_inc_sndbuf(fd, SNDBUF_SIZE);
}
}
- /* First try with fake ucred data, as requested */
- if (sendmsg(fd, &msghdr, MSG_NOSIGNAL) >= 0)
- return 1;
-
- /* If that failed, try with our own ucred instead */
- if (send_ucred) {
- msghdr.msg_controllen -= CMSG_SPACE(sizeof(struct ucred));
- if (msghdr.msg_controllen == 0)
+ do {
+ /* First try with fake ucred data, as requested */
+ n = sendmsg(fd, &msghdr, MSG_NOSIGNAL);
+ if (n < 0) {
+ if (!send_ucred)
+ return log_debug_errno(errno, "Failed to send notify message to '%s': %m", e);
+
+ /* If that failed, try with our own ucred instead */
+ msghdr.msg_controllen -= CMSG_SPACE(sizeof(struct ucred));
+ if (msghdr.msg_controllen == 0)
+ msghdr.msg_control = NULL;
+
+ n = 0;
+ send_ucred = false;
+ } else {
+ /* Unless we're using SOCK_STREAM, we expect to write all the contents immediately. */
+ if (type != SOCK_STREAM && (size_t) n < IOVEC_TOTAL_SIZE(msghdr.msg_iov, msghdr.msg_iovlen))
+ return -EIO;
+
+ /* Make sure we only send fds and ucred once, even if we're using SOCK_STREAM. */
msghdr.msg_control = NULL;
+ msghdr.msg_controllen = 0;
+ }
+ } while (!IOVEC_INCREMENT(msghdr.msg_iov, msghdr.msg_iovlen, n));
- if (sendmsg(fd, &msghdr, MSG_NOSIGNAL) >= 0)
- return 1;
- }
-
- return log_debug_errno(errno, "Failed to send notify message to '%s': %m", e);
+ return 1;
}
_public_ int sd_pid_notify_with_fds(
static const char* const event_source_type_table[_SOURCE_EVENT_SOURCE_TYPE_MAX] = {
[SOURCE_IO] = "io",
[SOURCE_TIME_REALTIME] = "realtime",
- [SOURCE_TIME_BOOTTIME] = "bootime",
+ [SOURCE_TIME_BOOTTIME] = "boottime",
[SOURCE_TIME_MONOTONIC] = "monotonic",
[SOURCE_TIME_REALTIME_ALARM] = "realtime-alarm",
[SOURCE_TIME_BOOTTIME_ALARM] = "boottime-alarm",
#include "string-util.h"
#include "strv.h"
#include "syslog-util.h"
+#include "uid-alloc-range.h"
#define JOURNAL_FILES_RECHECK_USEC (2 * USEC_PER_SEC)
static bool file_type_wanted(int flags, const char *filename) {
assert(filename);
- if (!endswith(filename, ".journal") && !endswith(filename, ".journal~"))
+ if (!ENDSWITH_SET(filename, ".journal", ".journal~"))
return false;
/* no flags set → every type is OK */
if (!(flags & (SD_JOURNAL_SYSTEM | SD_JOURNAL_CURRENT_USER)))
return true;
- if (flags & SD_JOURNAL_SYSTEM && file_has_type_prefix("system", filename))
- return true;
-
- if (flags & SD_JOURNAL_CURRENT_USER) {
+ if (FLAGS_SET(flags, SD_JOURNAL_CURRENT_USER)) {
char prefix[5 + DECIMAL_STR_MAX(uid_t) + 1];
- xsprintf(prefix, "user-"UID_FMT, getuid());
+ xsprintf(prefix, "user-" UID_FMT, getuid());
if (file_has_type_prefix(prefix, filename))
return true;
+
+ /* If SD_JOURNAL_CURRENT_USER is specified and we are invoked under a system UID, then
+ * automatically enable SD_JOURNAL_SYSTEM too, because journald will actually put system user
+ * data into the system journal. */
+
+ if (uid_for_system_journal(getuid()))
+ flags |= SD_JOURNAL_SYSTEM;
}
+ if (FLAGS_SET(flags, SD_JOURNAL_SYSTEM) && file_has_type_prefix("system", filename))
+ return true;
+
return false;
}
return session_get_string(session, "REMOTE_HOST", remote_host);
}
+_public_ int sd_session_get_leader(const char *session, pid_t *leader) {
+ _cleanup_free_ char *leader_string = NULL;
+ pid_t pid;
+ int r;
+
+ assert_return(leader, -EINVAL);
+
+ r = session_get_string(session, "LEADER", &leader_string);
+ if (r < 0)
+ return r;
+
+ r = parse_pid(leader_string, &pid);
+ if (r < 0)
+ return r;
+
+ *leader = pid;
+ return 0;
+}
+
_public_ int sd_seat_get_active(const char *seat, char **session, uid_t *uid) {
_cleanup_free_ char *p = NULL, *s = NULL, *t = NULL;
int r;
* while the socket sticks around we might get confused by replies from earlier runs coming
* in late — which is pretty likely if we'd start our sequence numbers always from 1. Hence,
* let's start with a value based on the system clock. This should make collisions much less
- * likely (though still theoretically possible). We use a 32 bit µs counter starting at boot
+ * likely (though still theoretically possible). We use a 32 bit μs counter starting at boot
* for this (and explicitly exclude the zero, see above). This counter will wrap around after
* a bit more than 1h, but that's hopefully OK as the kernel shouldn't take that long to
* reply to our requests.
*
* We only pick the initial start value this way. For each message we simply increase the
- * sequence number by 1. This means we could enqueue 1 netlink message per µs without risking
+ * sequence number by 1. This means we could enqueue 1 netlink message per μs without risking
* collisions, which should be OK.
*
* Note this means the serials will be in the range 1…UINT32_MAX here.
assert_se(sd_netlink_message_read_u32(reply, IFLA_GROUP, &u32_data) >= 0);
assert_se(sd_netlink_message_read_u32(reply, IFLA_TXQLEN, &u32_data) >= 0);
assert_se(sd_netlink_message_read_u32(reply, IFLA_NUM_TX_QUEUES, &u32_data) >= 0);
- assert_se(sd_netlink_message_read_u32(reply, IFLA_NUM_RX_QUEUES, &u32_data) >= 0);
/* string */
assert_se(sd_netlink_message_read_string(reply, IFLA_IFNAME, &str_data) >= 0);
ro-std ro pc105 std terminate:ctrl_alt_bksp
de-latin1 de pc105 - terminate:ctrl_alt_bksp
slovene si pc105 - terminate:ctrl_alt_bksp
-hu101 hu pc105 qwerty terminate:ctrl_alt_bksp
+hu hu pc105 - terminate:ctrl_alt_bksp
jp106 jp jp106 - terminate:ctrl_alt_bksp
croat hr pc105 - terminate:ctrl_alt_bksp
it2 it pc105 - terminate:ctrl_alt_bksp
-hu hu pc105 - terminate:ctrl_alt_bksp
+hu101 hu pc105 qwerty terminate:ctrl_alt_bksp
sr-latin rs pc105 latin terminate:ctrl_alt_bksp
fi fi pc105 - terminate:ctrl_alt_bksp
fr_CH ch pc105 fr terminate:ctrl_alt_bksp
} else {
char buf[DECIMAL_STR_MAX(uint64_t) + 1];
- xsprintf(buf, "%" PRIu64, x); /* µs granularity */
+ xsprintf(buf, "%" PRIu64, x); /* μs granularity */
r = write_string_file_atomic_label("/run/systemd/reboot-to-boot-loader-menu", buf);
if (r < 0)
{% if ENABLE_HOMED %}
-session optional pam_systemd_home.so
{% endif %}
+session optional pam_umask.so silent
session optional pam_systemd.so
{
BLOCK_SIGNALS(SIGINT);
- r = btrfs_subvol_snapshot(arg_directory, np,
- (arg_read_only ? BTRFS_SNAPSHOT_READ_ONLY : 0) |
- BTRFS_SNAPSHOT_FALLBACK_COPY |
- BTRFS_SNAPSHOT_FALLBACK_DIRECTORY |
- BTRFS_SNAPSHOT_RECURSIVE |
- BTRFS_SNAPSHOT_QUOTA |
- BTRFS_SNAPSHOT_SIGINT);
+ r = btrfs_subvol_snapshot_at(AT_FDCWD, arg_directory, AT_FDCWD, np,
+ (arg_read_only ? BTRFS_SNAPSHOT_READ_ONLY : 0) |
+ BTRFS_SNAPSHOT_FALLBACK_COPY |
+ BTRFS_SNAPSHOT_FALLBACK_DIRECTORY |
+ BTRFS_SNAPSHOT_RECURSIVE |
+ BTRFS_SNAPSHOT_QUOTA |
+ BTRFS_SNAPSHOT_SIGINT);
}
if (r == -EINTR) {
log_error_errno(r, "Interrupted while copying file system tree to %s, removed again.", np);
{
BLOCK_SIGNALS(SIGINT);
- r = btrfs_subvol_snapshot(arg_template, arg_directory,
- (arg_read_only ? BTRFS_SNAPSHOT_READ_ONLY : 0) |
- BTRFS_SNAPSHOT_FALLBACK_COPY |
- BTRFS_SNAPSHOT_FALLBACK_DIRECTORY |
- BTRFS_SNAPSHOT_FALLBACK_IMMUTABLE |
- BTRFS_SNAPSHOT_RECURSIVE |
- BTRFS_SNAPSHOT_QUOTA |
- BTRFS_SNAPSHOT_SIGINT);
+ r = btrfs_subvol_snapshot_at(AT_FDCWD, arg_template, AT_FDCWD, arg_directory,
+ (arg_read_only ? BTRFS_SNAPSHOT_READ_ONLY : 0) |
+ BTRFS_SNAPSHOT_FALLBACK_COPY |
+ BTRFS_SNAPSHOT_FALLBACK_DIRECTORY |
+ BTRFS_SNAPSHOT_FALLBACK_IMMUTABLE |
+ BTRFS_SNAPSHOT_RECURSIVE |
+ BTRFS_SNAPSHOT_QUOTA |
+ BTRFS_SNAPSHOT_SIGINT);
}
if (r == -EEXIST)
log_full(arg_quiet ? LOG_DEBUG : LOG_INFO,
static uint64_t arg_sector_size = 0;
static ImagePolicy *arg_image_policy = NULL;
static Architecture arg_architecture = _ARCHITECTURE_INVALID;
+static int arg_offline = -1;
STATIC_DESTRUCTOR_REGISTER(arg_root, freep);
STATIC_DESTRUCTOR_REGISTER(arg_image, freep);
return 0;
}
+typedef struct DecryptedPartitionTarget {
+ int fd;
+ char *dm_name;
+ char *volume;
+ struct crypt_device *device;
+} DecryptedPartitionTarget;
+
+static DecryptedPartitionTarget* decrypted_partition_target_free(DecryptedPartitionTarget *t) {
+#ifdef HAVE_LIBCRYPTSETUP
+ int r;
+
+ if (!t)
+ return NULL;
+
+ safe_close(t->fd);
+
+ /* udev or so might access out block device in the background while we are done. Let's hence
+ * force detach the volume. We sync'ed before, hence this should be safe. */
+ r = sym_crypt_deactivate_by_name(t->device, t->dm_name, CRYPT_DEACTIVATE_FORCE);
+ if (r < 0)
+ log_warning_errno(r, "Failed to deactivate LUKS device, ignoring: %m");
+
+ sym_crypt_free(t->device);
+ free(t->dm_name);
+ free(t->volume);
+ free(t);
+#endif
+ return NULL;
+}
+
typedef struct {
LoopDevice *loop;
int fd;
char *path;
int whole_fd;
+ DecryptedPartitionTarget *decrypted;
} PartitionTarget;
static int partition_target_fd(PartitionTarget *t) {
assert(t);
assert(t->loop || t->fd >= 0 || t->whole_fd >= 0);
- return t->loop ? t->loop->fd : t->fd >= 0 ? t->fd : t->whole_fd;
+
+ if (t->decrypted)
+ return t->decrypted->fd;
+
+ if (t->loop)
+ return t->loop->fd;
+
+ if (t->fd >= 0)
+ return t->fd;
+
+ return t->whole_fd;
}
static const char* partition_target_path(PartitionTarget *t) {
assert(t);
assert(t->loop || t->path);
- return t->loop ? t->loop->node : t->path;
+
+ if (t->decrypted)
+ return t->decrypted->volume;
+
+ if (t->loop)
+ return t->loop->node;
+
+ return t->path;
}
static PartitionTarget *partition_target_free(PartitionTarget *t) {
if (!t)
return NULL;
+ decrypted_partition_target_free(t->decrypted);
loop_device_unref(t->loop);
safe_close(t->fd);
unlink_and_free(t->path);
/* Loopback block devices are not only useful to turn regular files into block devices, but
* also to cut out sections of block devices into new block devices. */
- r = loop_device_make(whole_fd, O_RDWR, p->offset, size, 0, 0, LOCK_EX, &d);
- if (r < 0 && r != -ENOENT && !ERRNO_IS_PRIVILEGE(r))
- return log_error_errno(r, "Failed to make loopback device of future partition %" PRIu64 ": %m", p->partno);
- if (r >= 0) {
- t->loop = TAKE_PTR(d);
- *ret = TAKE_PTR(t);
- return 0;
+ if (arg_offline <= 0) {
+ r = loop_device_make(whole_fd, O_RDWR, p->offset, size, 0, 0, LOCK_EX, &d);
+ if (r < 0 && (arg_offline == 0 || (r != -ENOENT && !ERRNO_IS_PRIVILEGE(r))))
+ return log_error_errno(r, "Failed to make loopback device of future partition %" PRIu64 ": %m", p->partno);
+ if (r >= 0) {
+ t->loop = TAKE_PTR(d);
+ *ret = TAKE_PTR(t);
+ return 0;
+ }
+
+ log_debug_errno(r, "No access to loop devices, falling back to a regular file");
}
/* If we can't allocate a loop device, let's write to a regular file that we copy into the final
* reflinking support, we can take advantage of this and just reflink the result into the image.
*/
- log_debug_errno(r, "No access to loop devices, falling back to a regular file");
-
r = prepare_temporary_file(t, size);
if (r < 0)
return r;
int r;
assert(t);
+ assert(!t->decrypted);
if (t->loop) {
r = loop_device_refresh_size(t->loop, UINT64_MAX, size);
assert_se((whole_fd = fdisk_get_devfd(context->fdisk_context)) >= 0);
+ if (t->decrypted && fsync(t->decrypted->fd) < 0)
+ return log_error_errno(errno, "Failed to sync changes to '%s': %m", t->decrypted->volume);
+
if (t->loop) {
r = loop_device_sync(t->loop);
if (r < 0)
return 0;
}
-static int partition_encrypt(Context *context, Partition *p, const char *node) {
+static int partition_encrypt(Context *context, Partition *p, PartitionTarget *target, bool offline) {
#if HAVE_LIBCRYPTSETUP && HAVE_CRYPT_SET_DATA_OFFSET && HAVE_CRYPT_REENCRYPT_INIT_BY_PASSPHRASE && HAVE_CRYPT_REENCRYPT
+ const char *node = partition_target_path(target);
struct crypt_params_luks2 luks_params = {
.label = strempty(ASSERT_PTR(p)->new_label),
.sector_size = ASSERT_PTR(context)->sector_size,
- .data_device = node,
+ .data_device = offline ? node : NULL,
};
struct crypt_params_reencrypt reencrypt_params = {
.mode = CRYPT_REENCRYPT_ENCRYPT,
_cleanup_(sym_crypt_freep) struct crypt_device *cd = NULL;
_cleanup_(erase_and_freep) char *base64_encoded = NULL;
_cleanup_fclose_ FILE *h = NULL;
- _cleanup_free_ char *hp = NULL;
+ _cleanup_free_ char *hp = NULL, *vol = NULL, *dm_name = NULL;
const char *passphrase = NULL;
size_t passphrase_size = 0;
const char *vt;
log_info("Encrypting future partition %" PRIu64 "...", p->partno);
- r = var_tmp_dir(&vt);
- if (r < 0)
- return log_error_errno(r, "Failed to determine temporary files directory: %m");
+ if (offline) {
+ r = var_tmp_dir(&vt);
+ if (r < 0)
+ return log_error_errno(r, "Failed to determine temporary files directory: %m");
- r = fopen_temporary_child(vt, &h, &hp);
- if (r < 0)
- return log_error_errno(r, "Failed to create temporary LUKS header file: %m");
+ r = fopen_temporary_child(vt, &h, &hp);
+ if (r < 0)
+ return log_error_errno(r, "Failed to create temporary LUKS header file: %m");
- /* Weird cryptsetup requirement which requires the header file to be the size of at least one sector. */
- r = ftruncate(fileno(h), context->sector_size);
- if (r < 0)
- return log_error_errno(r, "Failed to grow temporary LUKS header file: %m");
+ /* Weird cryptsetup requirement which requires the header file to be the size of at least one
+ * sector. */
+ r = ftruncate(fileno(h), context->sector_size);
+ if (r < 0)
+ return log_error_errno(r, "Failed to grow temporary LUKS header file: %m");
+ } else {
+ if (asprintf(&dm_name, "luks-repart-%08" PRIx64, random_u64()) < 0)
+ return log_oom();
- r = sym_crypt_init(&cd, hp);
+ vol = path_join("/dev/mapper/", dm_name);
+ if (!vol)
+ return log_oom();
+ }
+
+ r = sym_crypt_init(&cd, offline ? hp : node);
if (r < 0)
return log_error_errno(r, "Failed to allocate libcryptsetup context for %s: %m", hp);
cryptsetup_enable_logging(cd);
- /* Disable kernel keyring usage by libcryptsetup as a workaround for
- * https://gitlab.com/cryptsetup/cryptsetup/-/merge_requests/273. This makes sure that we can do
- * offline encryption even when repart is running in a container. */
- r = sym_crypt_volume_key_keyring(cd, false);
- if (r < 0)
- return log_error_errno(r, "Failed to disable kernel keyring: %m");
+ if (offline) {
+ /* Disable kernel keyring usage by libcryptsetup as a workaround for
+ * https://gitlab.com/cryptsetup/cryptsetup/-/merge_requests/273. This makes sure that we can
+ * do offline encryption even when repart is running in a container. */
+ r = sym_crypt_volume_key_keyring(cd, false);
+ if (r < 0)
+ return log_error_errno(r, "Failed to disable kernel keyring: %m");
- r = sym_crypt_metadata_locking(cd, false);
- if (r < 0)
- return log_error_errno(r, "Failed to disable metadata locking: %m");
+ r = sym_crypt_metadata_locking(cd, false);
+ if (r < 0)
+ return log_error_errno(r, "Failed to disable metadata locking: %m");
- r = sym_crypt_set_data_offset(cd, LUKS2_METADATA_SIZE / 512);
- if (r < 0)
- return log_error_errno(r, "Failed to set data offset: %m");
+ r = sym_crypt_set_data_offset(cd, LUKS2_METADATA_SIZE / 512);
+ if (r < 0)
+ return log_error_errno(r, "Failed to set data offset: %m");
+ }
r = sym_crypt_format(cd,
CRYPT_LUKS2,
#endif
}
- r = sym_crypt_reencrypt_init_by_passphrase(
- cd,
- NULL,
- passphrase,
- passphrase_size,
- CRYPT_ANY_SLOT,
- 0,
- sym_crypt_get_cipher(cd),
- sym_crypt_get_cipher_mode(cd),
- &reencrypt_params);
- if (r < 0)
- return log_error_errno(r, "Failed to prepare for reencryption: %m");
+ if (offline) {
+ r = sym_crypt_reencrypt_init_by_passphrase(
+ cd,
+ NULL,
+ passphrase,
+ passphrase_size,
+ CRYPT_ANY_SLOT,
+ 0,
+ sym_crypt_get_cipher(cd),
+ sym_crypt_get_cipher_mode(cd),
+ &reencrypt_params);
+ if (r < 0)
+ return log_error_errno(r, "Failed to prepare for reencryption: %m");
- /* crypt_reencrypt_init_by_passphrase() doesn't actually put the LUKS header at the front, we have
- * to do that ourselves. */
+ /* crypt_reencrypt_init_by_passphrase() doesn't actually put the LUKS header at the front, we
+ * have to do that ourselves. */
- sym_crypt_free(cd);
- cd = NULL;
+ sym_crypt_free(cd);
+ cd = NULL;
- r = sym_crypt_init(&cd, node);
- if (r < 0)
- return log_error_errno(r, "Failed to allocate libcryptsetup context for %s: %m", node);
+ r = sym_crypt_init(&cd, node);
+ if (r < 0)
+ return log_error_errno(r, "Failed to allocate libcryptsetup context for %s: %m", node);
- r = sym_crypt_header_restore(cd, CRYPT_LUKS2, hp);
- if (r < 0)
- return log_error_errno(r, "Failed to place new LUKS header at head of %s: %m", node);
+ r = sym_crypt_header_restore(cd, CRYPT_LUKS2, hp);
+ if (r < 0)
+ return log_error_errno(r, "Failed to place new LUKS header at head of %s: %m", node);
- reencrypt_params.flags &= ~CRYPT_REENCRYPT_INITIALIZE_ONLY;
+ reencrypt_params.flags &= ~CRYPT_REENCRYPT_INITIALIZE_ONLY;
- r = sym_crypt_reencrypt_init_by_passphrase(
- cd,
- NULL,
- passphrase,
- passphrase_size,
- CRYPT_ANY_SLOT,
- 0,
- NULL,
- NULL,
- &reencrypt_params);
- if (r < 0)
- return log_error_errno(r, "Failed to load reencryption context: %m");
+ r = sym_crypt_reencrypt_init_by_passphrase(
+ cd,
+ NULL,
+ passphrase,
+ passphrase_size,
+ CRYPT_ANY_SLOT,
+ 0,
+ NULL,
+ NULL,
+ &reencrypt_params);
+ if (r < 0)
+ return log_error_errno(r, "Failed to load reencryption context: %m");
- r = sym_crypt_reencrypt(cd, NULL);
- if (r < 0)
- return log_error_errno(r, "Failed to encrypt %s: %m", node);
+ r = sym_crypt_reencrypt(cd, NULL);
+ if (r < 0)
+ return log_error_errno(r, "Failed to encrypt %s: %m", node);
+ } else {
+ _cleanup_free_ DecryptedPartitionTarget *t = NULL;
+ _cleanup_close_ int dev_fd = -1;
+
+ r = sym_crypt_activate_by_volume_key(
+ cd,
+ dm_name,
+ NULL,
+ VOLUME_KEY_SIZE,
+ arg_discard ? CRYPT_ACTIVATE_ALLOW_DISCARDS : 0);
+ if (r < 0)
+ return log_error_errno(r, "Failed to activate LUKS superblock: %m");
+
+ dev_fd = open(vol, O_RDWR|O_CLOEXEC|O_NOCTTY);
+ if (dev_fd < 0)
+ return log_error_errno(errno, "Failed to open LUKS volume '%s': %m", vol);
+
+ if (flock(dev_fd, LOCK_EX) < 0)
+ return log_error_errno(errno, "Failed to lock '%s': %m", vol);
+
+ t = new(DecryptedPartitionTarget, 1);
+ if (!t)
+ return log_oom();
+
+ *t = (DecryptedPartitionTarget) {
+ .fd = TAKE_FD(dev_fd),
+ .dm_name = TAKE_PTR(dm_name),
+ .volume = TAKE_PTR(vol),
+ .device = TAKE_PTR(cd),
+ };
+
+ target->decrypted = TAKE_PTR(t);
+ }
log_info("Successfully encrypted future partition %" PRIu64 ".", p->partno);
if (r < 0)
return r;
+ if (p->encrypt != ENCRYPT_OFF && t->loop) {
+ r = partition_encrypt(context, p, t, /* offline = */ false);
+ if (r < 0)
+ return r;
+ }
+
log_info("Copying in '%s' (%s) on block level into future partition %" PRIu64 ".",
p->copy_blocks_path, FORMAT_BYTES(p->copy_blocks_size), p->partno);
if (r < 0)
return log_error_errno(r, "Failed to copy in data from '%s': %m", p->copy_blocks_path);
- if (p->encrypt != ENCRYPT_OFF) {
- r = partition_encrypt(context, p, partition_target_path(t));
+ log_info("Copying in of '%s' on block level completed.", p->copy_blocks_path);
+
+ if (p->encrypt != ENCRYPT_OFF && !t->loop) {
+ r = partition_encrypt(context, p, t, /* offline = */ true);
if (r < 0)
return r;
}
if (r < 0)
return r;
- log_info("Copying in of '%s' on block level completed.", p->copy_blocks_path);
-
if (p->siblings[VERITY_HASH] && !partition_defer(p->siblings[VERITY_HASH])) {
r = partition_format_verity_hash(context, p->siblings[VERITY_HASH],
/* node = */ NULL, partition_target_path(t));
sfd, ".",
pfd, fn,
UID_INVALID, GID_INVALID,
- COPY_REFLINK|COPY_HOLES|COPY_MERGE|COPY_REPLACE|COPY_SIGINT|COPY_HARDLINKS|COPY_ALL_XATTRS|COPY_GRACEFUL_WARN,
+ COPY_REFLINK|COPY_HOLES|COPY_MERGE|COPY_REPLACE|COPY_SIGINT|COPY_HARDLINKS|COPY_ALL_XATTRS|COPY_GRACEFUL_WARN|COPY_TRUNCATE,
denylist);
} else
r = copy_tree_at(
sfd, ".",
tfd, ".",
UID_INVALID, GID_INVALID,
- COPY_REFLINK|COPY_HOLES|COPY_MERGE|COPY_REPLACE|COPY_SIGINT|COPY_HARDLINKS|COPY_ALL_XATTRS|COPY_GRACEFUL_WARN,
+ COPY_REFLINK|COPY_HOLES|COPY_MERGE|COPY_REPLACE|COPY_SIGINT|COPY_HARDLINKS|COPY_ALL_XATTRS|COPY_GRACEFUL_WARN|COPY_TRUNCATE,
denylist);
if (r < 0)
return log_error_errno(r, "Failed to copy '%s%s' to '%s%s': %m",
if (tfd < 0)
return log_error_errno(errno, "Failed to create target file '%s': %m", *target);
- r = copy_bytes(sfd, tfd, UINT64_MAX, COPY_REFLINK|COPY_HOLES|COPY_SIGINT);
+ r = copy_bytes(sfd, tfd, UINT64_MAX, COPY_REFLINK|COPY_HOLES|COPY_SIGINT|COPY_TRUNCATE);
if (r < 0)
return log_error_errno(r, "Failed to copy '%s' to '%s%s': %m", *source, strempty(arg_root), *target);
if (r < 0)
return r;
+ if (p->encrypt != ENCRYPT_OFF && t->loop) {
+ r = partition_target_grow(t, p->new_size);
+ if (r < 0)
+ return r;
+
+ r = partition_encrypt(context, p, t, /* offline = */ false);
+ if (r < 0)
+ return log_error_errno(r, "Failed to encrypt device: %m");
+ }
+
log_info("Formatting future partition %" PRIu64 ".", p->partno);
/* If we're not writing to a loop device or if we're populating a read-only filesystem, we
if (partition_needs_populate(p) && !root) {
assert(t->loop);
- r = partition_populate_filesystem(context, p, t->loop->node);
+ r = partition_populate_filesystem(context, p, partition_target_path(t));
if (r < 0)
return r;
}
- if (p->encrypt != ENCRYPT_OFF) {
+ if (p->encrypt != ENCRYPT_OFF && !t->loop) {
r = partition_target_grow(t, p->new_size);
if (r < 0)
return r;
- r = partition_encrypt(context, p, partition_target_path(t));
+ r = partition_encrypt(context, p, t, /* offline = */ true);
if (r < 0)
return log_error_errno(r, "Failed to encrypt device: %m");
}
if (lseek(fd, p->offset, SEEK_SET) < 0)
return log_error_errno(errno, "Failed to seek to partition offset: %m");
- r = copy_bytes(fd, fdt, p->new_size, COPY_REFLINK|COPY_HOLES);
+ r = copy_bytes(fd, fdt, p->new_size, COPY_REFLINK|COPY_HOLES|COPY_TRUNCATE);
if (r < 0)
return log_error_errno(r, "Failed to copy to split partition %s: %m", p->split_path);
}
" but don't populate them yet\n"
" --sector-size=SIZE Set the logical sector size for the image\n"
" --architecture=ARCH Set the generic architecture for the image\n"
+ " --offline=BOOL Whether to build the image offline\n"
"\nSee the %s for details.\n",
program_invocation_short_name,
ansi_highlight(),
ARG_SECTOR_SIZE,
ARG_SKIP_PARTITIONS,
ARG_ARCHITECTURE,
+ ARG_OFFLINE,
};
static const struct option options[] = {
{ "defer-partitions", required_argument, NULL, ARG_DEFER_PARTITIONS },
{ "sector-size", required_argument, NULL, ARG_SECTOR_SIZE },
{ "architecture", required_argument, NULL, ARG_ARCHITECTURE },
+ { "offline", required_argument, NULL, ARG_OFFLINE },
{}
};
arg_architecture = r;
break;
+ case ARG_OFFLINE:
+ if (streq(optarg, "auto"))
+ arg_offline = -1;
+ else {
+ r = parse_boolean_argument("--offline=", optarg, NULL);
+ if (r < 0)
+ return r;
+
+ arg_offline = r;
+ }
+
+ break;
+
case '?':
return -EINVAL;
_cleanup_(portable_metadata_unrefp) PortableMetadata *m = NULL;
_cleanup_(mac_selinux_freep) char *con = NULL;
_cleanup_close_ int fd = -EBADF;
+ struct stat st;
if (!unit_name_is_valid(de->d_name, UNIT_NAME_ANY))
continue;
continue;
}
+ /* Reject empty files, just in case */
+ if (fstat(fd, &st) < 0) {
+ log_debug_errno(errno, "Failed to stat unit file '%s', ignoring: %m", de->d_name);
+ continue;
+ }
+
+ if (st.st_size <= 0) {
+ log_debug("Unit file '%s' is empty, ignoring.", de->d_name);
+ continue;
+ }
+
#if HAVE_SELINUX
/* The units will be copied on the host's filesystem, so if they had a SELinux label
* we have to preserve it. Copy it out so that it can be applied later. */
}
}
- r = write_string_file(dropin, text, WRITE_STRING_FILE_CREATE|WRITE_STRING_FILE_ATOMIC);
+ r = write_string_file(dropin, text, WRITE_STRING_FILE_CREATE|WRITE_STRING_FILE_ATOMIC|WRITE_STRING_FILE_SYNC);
if (r < 0)
return log_debug_errno(r, "Failed to write '%s': %m", dropin);
if (flags & PORTABLE_PREFER_COPY) {
- r = copy_file_atomic(from, dropin, 0644, COPY_REFLINK);
+ r = copy_file_atomic(from, dropin, 0644, COPY_REFLINK|COPY_FSYNC);
if (r < 0)
return log_debug_errno(r, "Failed to copy %s %s %s: %m", from, special_glyph(SPECIAL_GLYPH_ARROW_RIGHT), dropin);
if (fchmod(fd, 0644) < 0)
return log_debug_errno(errno, "Failed to change unit file access mode for '%s': %m", path);
- r = link_tmpfile(fd, tmp, path, /* replace= */ false);
+ r = link_tmpfile(fd, tmp, path, LINK_TMPFILE_SYNC);
if (r < 0)
return log_debug_errno(r, "Failed to install unit file '%s': %m", path);
return verb_log_control_common(bus, "org.freedesktop.resolve1", argv[0], argc == 2 ? argv[1] : NULL);
}
-static int monitor_rkey_from_json(JsonVariant *v, DnsResourceKey **ret_key) {
- _cleanup_(dns_resource_key_unrefp) DnsResourceKey *key = NULL;
- uint16_t type = 0, class = 0;
- const char *name = NULL;
- int r;
-
- JsonDispatch dispatch_table[] = {
- { "class", JSON_VARIANT_INTEGER, json_dispatch_uint16, PTR_TO_SIZE(&class), JSON_MANDATORY },
- { "type", JSON_VARIANT_INTEGER, json_dispatch_uint16, PTR_TO_SIZE(&type), JSON_MANDATORY },
- { "name", JSON_VARIANT_STRING, json_dispatch_const_string, PTR_TO_SIZE(&name), JSON_MANDATORY },
- {}
- };
-
- assert(v);
- assert(ret_key);
-
- r = json_dispatch(v, dispatch_table, NULL, 0, NULL);
- if (r < 0)
- return r;
-
- key = dns_resource_key_new(class, type, name);
- if (!key)
- return -ENOMEM;
-
- *ret_key = TAKE_PTR(key);
- return 0;
-}
-
static int print_question(char prefix, const char *color, JsonVariant *question) {
JsonVariant *q = NULL;
int r;
_cleanup_(dns_resource_key_unrefp) DnsResourceKey *key = NULL;
char buf[DNS_RESOURCE_KEY_STRING_MAX];
- r = monitor_rkey_from_json(q, &key);
+ r = dns_resource_key_from_json(q, &key);
if (r < 0) {
log_warning_errno(r, "Received monitor message with invalid question key, ignoring: %m");
continue;
return c;
}
+static int dump_cache_item(JsonVariant *item) {
+
+ struct item_info {
+ JsonVariant *key;
+ JsonVariant *rrs;
+ const char *type;
+ uint64_t until;
+ } item_info = {};
+
+ static const JsonDispatch dispatch_table[] = {
+ { "key", JSON_VARIANT_OBJECT, json_dispatch_variant_noref, offsetof(struct item_info, key), JSON_MANDATORY },
+ { "rrs", JSON_VARIANT_ARRAY, json_dispatch_variant_noref, offsetof(struct item_info, rrs), 0 },
+ { "type", JSON_VARIANT_STRING, json_dispatch_const_string, offsetof(struct item_info, type), 0 },
+ { "until", JSON_VARIANT_UNSIGNED, json_dispatch_uint64, offsetof(struct item_info, until), 0 },
+ {},
+ };
+
+ _cleanup_(dns_resource_key_unrefp) DnsResourceKey *k = NULL;
+ int r, c = 0;
+
+ r = json_dispatch(item, dispatch_table, NULL, JSON_LOG, &item_info);
+ if (r < 0)
+ return r;
+
+ r = dns_resource_key_from_json(item_info.key, &k);
+ if (r < 0)
+ return log_error_errno(r, "Failed to turn JSON data to resource key: %m");
+
+ if (item_info.type)
+ printf("%s %s%s%s\n", DNS_RESOURCE_KEY_TO_STRING(k), ansi_highlight_red(), item_info.type, ansi_normal());
+ else {
+ JsonVariant *i;
+
+ JSON_VARIANT_ARRAY_FOREACH(i, item_info.rrs) {
+ _cleanup_(dns_resource_record_unrefp) DnsResourceRecord *rr = NULL;
+ _cleanup_free_ void *data = NULL;
+ JsonVariant *raw;
+ size_t size;
+
+ raw = json_variant_by_key(i, "raw");
+ if (!raw)
+ return log_error_errno(SYNTHETIC_ERRNO(ENOTRECOVERABLE), "raw field missing from RR JSON data.");
+
+ r = json_variant_unbase64(raw, &data, &size);
+ if (r < 0)
+ return log_error_errno(r, "Unable to decode raw RR JSON data: %m");
+
+ r = dns_resource_record_new_from_raw(&rr, data, size);
+ if (r < 0)
+ return log_error_errno(r, "Failed to parse DNS data: %m");
+
+ printf("%s\n", dns_resource_record_to_string(rr));
+ c++;
+ }
+ }
+
+ return c;
+}
+
+static int dump_cache_scope(JsonVariant *scope) {
+
+ struct scope_info {
+ const char *protocol;
+ int family;
+ int ifindex;
+ const char *ifname;
+ JsonVariant *cache;
+ } scope_info = {
+ .family = AF_UNSPEC,
+ };
+ JsonVariant *i;
+ int r, c = 0;
+
+ static const JsonDispatch dispatch_table[] = {
+ { "protocol", JSON_VARIANT_STRING, json_dispatch_const_string, offsetof(struct scope_info, protocol), JSON_MANDATORY },
+ { "family", JSON_VARIANT_INTEGER, json_dispatch_int, offsetof(struct scope_info, family), 0 },
+ { "ifindex", JSON_VARIANT_INTEGER, json_dispatch_int, offsetof(struct scope_info, ifindex), 0 },
+ { "ifname", JSON_VARIANT_STRING, json_dispatch_const_string, offsetof(struct scope_info, ifname), 0 },
+ { "cache", JSON_VARIANT_ARRAY, json_dispatch_variant_noref, offsetof(struct scope_info, cache), JSON_MANDATORY },
+ {},
+ };
+
+ r = json_dispatch(scope, dispatch_table, NULL, JSON_LOG, &scope_info);
+ if (r < 0)
+ return r;
+
+ printf("%sScope protocol=%s", ansi_underline(), scope_info.protocol);
+
+ if (scope_info.family != AF_UNSPEC)
+ printf(" family=%s", af_to_name(scope_info.family));
+
+ if (scope_info.ifindex > 0)
+ printf(" ifindex=%i", scope_info.ifindex);
+ if (scope_info.ifname)
+ printf(" ifname=%s", scope_info.ifname);
+
+ printf("%s\n", ansi_normal());
+
+ JSON_VARIANT_ARRAY_FOREACH(i, scope_info.cache) {
+ r = dump_cache_item(i);
+ if (r < 0)
+ return r;
+
+ c += r;
+ }
+
+ if (c == 0)
+ printf("%sNo entries.%s\n\n", ansi_grey(), ansi_normal());
+ else
+ printf("\n");
+
+ return 0;
+}
+
+static int verb_show_cache(int argc, char *argv[], void *userdata) {
+ _cleanup_(json_variant_unrefp) JsonVariant *d = NULL, *reply = NULL;
+ _cleanup_(varlink_unrefp) Varlink *vl = NULL;
+ int r;
+
+ r = varlink_connect_address(&vl, "/run/systemd/resolve/io.systemd.Resolve.Monitor");
+ if (r < 0)
+ return log_error_errno(r, "Failed to connect to query monitoring service /run/systemd/resolve/io.systemd.Resolve.Monitor: %m");
+
+ r = varlink_call(vl, "io.systemd.Resolve.Monitor.DumpCache", NULL, &reply, NULL, 0);
+ if (r < 0)
+ return log_error_errno(r, "Failed to issue DumpCache() varlink call: %m");
+
+ d = json_variant_by_key(reply, "dump");
+ if (!d)
+ return log_error_errno(SYNTHETIC_ERRNO(ENOTRECOVERABLE),
+ "DumpCache() response is missing 'dump' key.");
+
+ if (!json_variant_is_array(d))
+ return log_error_errno(SYNTHETIC_ERRNO(ENOTRECOVERABLE),
+ "DumpCache() response 'dump' field not an array");
+
+ if (FLAGS_SET(arg_json_format_flags, JSON_FORMAT_OFF)) {
+ JsonVariant *i;
+
+ JSON_VARIANT_ARRAY_FOREACH(i, d) {
+ r = dump_cache_scope(i);
+ if (r < 0)
+ return r;
+ }
+
+ return 0;
+ }
+
+ return json_variant_dump(d, arg_json_format_flags, NULL, NULL);
+}
+
static void help_protocol_types(void) {
if (arg_legend)
puts("Known protocol types:");
" flush-caches Flush all local DNS caches\n"
" reset-server-features Forget learnt DNS server feature levels\n"
" monitor Monitor DNS queries\n"
+ " show-cache Show cache contents\n"
" dns [LINK [SERVER...]] Get/set per-interface DNS server address\n"
" domain [LINK [DOMAIN...]] Get/set per-interface search domain\n"
" default-route [LINK [BOOL]] Get/set per-interface default route flag\n"
{ "revert", VERB_ANY, 2, 0, verb_revert_link },
{ "log-level", VERB_ANY, 2, 0, verb_log_level },
{ "monitor", VERB_ANY, 1, 0, verb_monitor },
+ { "show-cache", VERB_ANY, 1, 0, verb_show_cache },
{}
};
(uint64_t) m->n_dnssec_verdict[DNSSEC_INDETERMINATE]);
}
-static int bus_property_get_ntas(
- sd_bus *bus,
- const char *path,
- const char *interface,
- const char *property,
- sd_bus_message *reply,
- void *userdata,
- sd_bus_error *error) {
-
- Manager *m = ASSERT_PTR(userdata);
- const char *domain;
- int r;
-
- assert(reply);
-
- r = sd_bus_message_open_container(reply, 'a', "s");
- if (r < 0)
- return r;
-
- SET_FOREACH(domain, m->trust_anchor.negative_by_name) {
- r = sd_bus_message_append(reply, "s", domain);
- if (r < 0)
- return r;
- }
-
- return sd_bus_message_close_container(reply);
-}
-
static BUS_DEFINE_PROPERTY_GET_ENUM(bus_property_get_dns_stub_listener_mode, dns_stub_listener_mode, DnsStubListenerMode);
static BUS_DEFINE_PROPERTY_GET(bus_property_get_dnssec_supported, "b", Manager, manager_dnssec_supported);
static BUS_DEFINE_PROPERTY_GET2(bus_property_get_dnssec_mode, "s", Manager, manager_get_dnssec_mode, dnssec_mode_to_string);
SD_BUS_PROPERTY("DNSSEC", "s", bus_property_get_dnssec_mode, 0, 0),
SD_BUS_PROPERTY("DNSSECStatistics", "(tttt)", bus_property_get_dnssec_statistics, 0, 0),
SD_BUS_PROPERTY("DNSSECSupported", "b", bus_property_get_dnssec_supported, 0, 0),
- SD_BUS_PROPERTY("DNSSECNegativeTrustAnchors", "as", bus_property_get_ntas, 0, 0),
+ SD_BUS_PROPERTY("DNSSECNegativeTrustAnchors", "as", bus_property_get_string_set, offsetof(Manager, trust_anchor.negative_by_name), 0),
SD_BUS_PROPERTY("DNSStubListener", "s", bus_property_get_dns_stub_listener_mode, offsetof(Manager, dns_stub_listener_mode), 0),
SD_BUS_PROPERTY("ResolvConfMode", "s", bus_property_get_resolv_conf_mode, 0, 0),
}
}
+int dns_cache_dump_to_json(DnsCache *cache, JsonVariant **ret) {
+ _cleanup_(json_variant_unrefp) JsonVariant *c = NULL;
+ DnsCacheItem *i;
+ int r;
+
+ assert(cache);
+ assert(ret);
+
+ HASHMAP_FOREACH(i, cache->by_key) {
+ _cleanup_(json_variant_unrefp) JsonVariant *d = NULL, *k = NULL;
+
+ r = dns_resource_key_to_json(i->key, &k);
+ if (r < 0)
+ return r;
+
+ if (i->rr) {
+ _cleanup_(json_variant_unrefp) JsonVariant *l = NULL;
+
+ LIST_FOREACH(by_key, j, i) {
+ _cleanup_(json_variant_unrefp) JsonVariant *rj = NULL, *item = NULL;
+
+ assert(j->rr);
+
+ r = dns_resource_record_to_json(j->rr, &rj);
+ if (r < 0)
+ return r;
+
+ r = dns_resource_record_to_wire_format(j->rr, /* canonical= */ false); /* don't use DNSSEC canonical format, since it removes casing, but we want that for DNS_SD compat */
+ if (r < 0)
+ return r;
+
+ r = json_build(&item, JSON_BUILD_OBJECT(
+ JSON_BUILD_PAIR_VARIANT("rr", rj),
+ JSON_BUILD_PAIR_BASE64("raw", j->rr->wire_format, j->rr->wire_format_size)));
+ if (r < 0)
+ return r;
+
+ r = json_variant_append_array(&l, item);
+ if (r < 0)
+ return r;
+ }
+
+ if (!l) {
+ r = json_variant_new_array(&l, NULL, 0);
+ if (r < 0)
+ return r;
+ }
+
+ r = json_build(&d,
+ JSON_BUILD_OBJECT(
+ JSON_BUILD_PAIR_VARIANT("key", k),
+ JSON_BUILD_PAIR_VARIANT("rrs", l),
+ JSON_BUILD_PAIR_UNSIGNED("until", i->until)));
+ } else if (i->type == DNS_CACHE_NODATA) {
+ r = json_build(&d,
+ JSON_BUILD_OBJECT(
+ JSON_BUILD_PAIR_VARIANT("key", k),
+ JSON_BUILD_PAIR_EMPTY_ARRAY("rrs"),
+ JSON_BUILD_PAIR_UNSIGNED("until", i->until)));
+ } else
+ r = json_build(&d,
+ JSON_BUILD_OBJECT(
+ JSON_BUILD_PAIR_VARIANT("key", k),
+ JSON_BUILD_PAIR_STRING("type", dns_cache_item_type_to_string(i)),
+ JSON_BUILD_PAIR_UNSIGNED("until", i->until)));
+ if (r < 0)
+ return r;
+
+ r = json_variant_append_array(&c, d);
+ if (r < 0)
+ return r;
+ }
+
+ if (!c)
+ return json_variant_new_array(ret, NULL, 0);
+
+ *ret = TAKE_PTR(c);
+ return 0;
+}
+
bool dns_cache_is_empty(DnsCache *cache) {
if (!cache)
return true;
int dns_cache_check_conflicts(DnsCache *cache, DnsResourceRecord *rr, int owner_family, const union in_addr_union *owner_address);
void dns_cache_dump(DnsCache *cache, FILE *f);
+int dns_cache_dump_to_json(DnsCache *cache, JsonVariant **ret);
+
bool dns_cache_is_empty(DnsCache *cache);
unsigned dns_cache_size(DnsCache *cache);
if (r < 0)
goto fail;
- /* RFC 2782 states "Unless and until permitted by future standards
- * action, name compression is not to be used for this field." */
- r = dns_packet_append_name(p, rr->srv.name, false, true, NULL);
+ /* RFC 2782 states "Unless and until permitted by future standards action, name compression
+ * is not to be used for this field." Hence we turn off compression here. */
+ r = dns_packet_append_name(p, rr->srv.name, /* allow_compression= */ false, /* canonical_candidate= */ true, NULL);
break;
case DNS_TYPE_PTR:
r = dns_packet_read_uint16(p, &rr->srv.port, NULL);
if (r < 0)
return r;
- r = dns_packet_read_name(p, &rr->srv.name, true, NULL);
+
+ /* RFC 2782 states "Unless and until permitted by future standards action, name compression
+ * is not to be used for this field." Nonetheless, we support it here, in the interest of
+ * increasing compatibility with implementations that do not implement this correctly. After
+ * all we didn't do this right once upon a time ourselves (see
+ * https://github.com/systemd/systemd/issues/9793). */
+ r = dns_packet_read_name(p, &rr->srv.name, /* allow_compression= */ true, NULL);
break;
case DNS_TYPE_PTR:
JSON_BUILD_PAIR("name", JSON_BUILD_STRING(dns_resource_key_name(key)))));
}
+int dns_resource_key_from_json(JsonVariant *v, DnsResourceKey **ret) {
+ _cleanup_(dns_resource_key_unrefp) DnsResourceKey *key = NULL;
+ uint16_t type = 0, class = 0;
+ const char *name = NULL;
+ int r;
+
+ JsonDispatch dispatch_table[] = {
+ { "class", JSON_VARIANT_INTEGER, json_dispatch_uint16, PTR_TO_SIZE(&class), JSON_MANDATORY },
+ { "type", JSON_VARIANT_INTEGER, json_dispatch_uint16, PTR_TO_SIZE(&type), JSON_MANDATORY },
+ { "name", JSON_VARIANT_STRING, json_dispatch_const_string, PTR_TO_SIZE(&name), JSON_MANDATORY },
+ {}
+ };
+
+ assert(v);
+ assert(ret);
+
+ r = json_dispatch(v, dispatch_table, NULL, 0, NULL);
+ if (r < 0)
+ return r;
+
+ key = dns_resource_key_new(class, type, name);
+ if (!key)
+ return -ENOMEM;
+
+ *ret = TAKE_PTR(key);
+ return 0;
+}
+
static int type_bitmap_to_json(Bitmap *b, JsonVariant **ret) {
_cleanup_(json_variant_unrefp) JsonVariant *l = NULL;
unsigned t;
char* dns_resource_key_to_string(const DnsResourceKey *key, char *buf, size_t buf_size);
ssize_t dns_resource_record_payload(DnsResourceRecord *rr, void **out);
+#define DNS_RESOURCE_KEY_TO_STRING(key) \
+ dns_resource_key_to_string(key, (char[DNS_RESOURCE_KEY_STRING_MAX]) {}, DNS_RESOURCE_KEY_STRING_MAX)
+
DEFINE_TRIVIAL_CLEANUP_FUNC(DnsResourceKey*, dns_resource_key_unref);
static inline bool dns_key_is_shared(const DnsResourceKey *key) {
int dns_resource_record_new_from_raw(DnsResourceRecord **ret, const void *data, size_t size);
int dns_resource_key_to_json(DnsResourceKey *key, JsonVariant **ret);
+int dns_resource_key_from_json(JsonVariant *v, DnsResourceKey **ret);
int dns_resource_record_to_json(DnsResourceRecord *rr, JsonVariant **ret);
void dns_resource_record_hash_func(const DnsResourceRecord *i, struct siphash *state);
* volunteer as default route. */
return !dns_scope_has_route_only_domains(scope);
}
+
+int dns_scope_dump_cache_to_json(DnsScope *scope, JsonVariant **ret) {
+ _cleanup_(json_variant_unrefp) JsonVariant *cache = NULL;
+ int r;
+
+ assert(scope);
+ assert(ret);
+
+ r = dns_cache_dump_to_json(&scope->cache, &cache);
+ if (r < 0)
+ return r;
+
+ return json_build(ret,
+ JSON_BUILD_OBJECT(
+ JSON_BUILD_PAIR_STRING("protocol", dns_protocol_to_string(scope->protocol)),
+ JSON_BUILD_PAIR_CONDITION(scope->family != AF_UNSPEC, "family", JSON_BUILD_INTEGER(scope->family)),
+ JSON_BUILD_PAIR_CONDITION(scope->link, "ifindex", JSON_BUILD_INTEGER(scope->link ? scope->link->ifindex : 0)),
+ JSON_BUILD_PAIR_CONDITION(scope->link, "ifname", JSON_BUILD_STRING(scope->link ? scope->link->ifname : NULL)),
+ JSON_BUILD_PAIR_VARIANT("cache", cache)));
+}
int dns_scope_remove_dnssd_services(DnsScope *scope);
bool dns_scope_is_default_route(DnsScope *scope);
+
+int dns_scope_dump_cache_to_json(DnsScope *scope, JsonVariant **ret);
return -errno;
if (type == SOCK_STREAM &&
- listen(fd, SOMAXCONN) < 0)
+ listen(fd, SOMAXCONN_DELUXE) < 0)
return -errno;
r = sd_event_add_io(m->event, event_source, fd, EPOLLIN,
goto fail;
if (type == SOCK_STREAM &&
- listen(fd, SOMAXCONN) < 0) {
+ listen(fd, SOMAXCONN_DELUXE) < 0) {
r = -errno;
goto fail;
}
return sd_bus_message_append(reply, "t", mask);
}
-static int property_get_ntas(
- sd_bus *bus,
- const char *path,
- const char *interface,
- const char *property,
- sd_bus_message *reply,
- void *userdata,
- sd_bus_error *error) {
-
- Link *l = ASSERT_PTR(userdata);
- const char *name;
- int r;
-
- assert(reply);
-
- r = sd_bus_message_open_container(reply, 'a', "s");
- if (r < 0)
- return r;
-
- SET_FOREACH(name, l->dnssec_negative_trust_anchors) {
- r = sd_bus_message_append(reply, "s", name);
- if (r < 0)
- return r;
- }
-
- return sd_bus_message_close_container(reply);
-}
-
static int verify_unmanaged_link(Link *l, sd_bus_error *error) {
assert(l);
SD_BUS_PROPERTY("MulticastDNS", "s", property_get_mdns_support, 0, 0),
SD_BUS_PROPERTY("DNSOverTLS", "s", property_get_dns_over_tls_mode, 0, 0),
SD_BUS_PROPERTY("DNSSEC", "s", property_get_dnssec_mode, 0, 0),
- SD_BUS_PROPERTY("DNSSECNegativeTrustAnchors", "as", property_get_ntas, 0, 0),
+ SD_BUS_PROPERTY("DNSSECNegativeTrustAnchors", "as", bus_property_get_string_set, offsetof(Link, dnssec_negative_trust_anchors), 0),
SD_BUS_PROPERTY("DNSSECSupported", "b", property_get_dnssec_supported, 0, 0),
SD_BUS_METHOD_WITH_ARGS("SetDNS",
return log_error_errno(r, "LLMNR-IPv4(TCP): Failed to set SO_REUSEADDR: %m");
}
- r = listen(s, SOMAXCONN);
+ r = listen(s, SOMAXCONN_DELUXE);
if (r < 0)
return log_error_errno(errno, "LLMNR-IPv4(TCP): Failed to listen the stream: %m");
return log_error_errno(r, "LLMNR-IPv6(TCP): Failed to set SO_REUSEADDR: %m");
}
- r = listen(s, SOMAXCONN);
+ r = listen(s, SOMAXCONN_DELUXE);
if (r < 0)
return log_error_errno(errno, "LLMNR-IPv6(TCP): Failed to listen the stream: %m");
return 1;
}
+static int vl_method_dump_cache(Varlink *link, JsonVariant *parameters, VarlinkMethodFlags flags, void *userdata) {
+ _cleanup_(json_variant_unrefp) JsonVariant *list = NULL;
+ Manager *m;
+ int r;
+
+ assert(link);
+
+ if (json_variant_elements(parameters) > 0)
+ return varlink_error_invalid_parameter(link, parameters);
+
+ m = ASSERT_PTR(varlink_server_get_userdata(varlink_get_server(link)));
+
+ LIST_FOREACH(scopes, s, m->dns_scopes) {
+ _cleanup_(json_variant_unrefp) JsonVariant *j = NULL;
+
+ r = dns_scope_dump_cache_to_json(s, &j);
+ if (r < 0)
+ return r;
+
+ r = json_variant_append_array(&list, j);
+ if (r < 0)
+ return r;
+ }
+
+ if (!list) {
+ r = json_variant_new_array(&list, NULL, 0);
+ if (r < 0)
+ return r;
+ }
+
+ return varlink_replyb(link, JSON_BUILD_OBJECT(
+ JSON_BUILD_PAIR("dump", JSON_BUILD_VARIANT(list))));
+}
+
static int varlink_monitor_server_init(Manager *m) {
_cleanup_(varlink_server_unrefp) VarlinkServer *server = NULL;
int r;
varlink_server_set_userdata(server, m);
- r = varlink_server_bind_method(
+ r = varlink_server_bind_method_many(
server,
- "io.systemd.Resolve.Monitor.SubscribeQueryResults",
- vl_method_subscribe_dns_resolves);
+ "io.systemd.Resolve.Monitor.SubscribeQueryResults", vl_method_subscribe_dns_resolves,
+ "io.systemd.Resolve.Monitor.DumpCache", vl_method_dump_cache);
if (r < 0)
return log_error_errno(r, "Failed to register varlink methods: %m");
}
/* If we found a battery whose state we couldn't read, don't assume we are in low battery state */
- if (unsure)
+ if (unsure) {
+ log_debug("Found battery with unreadable state, assuming not in low battery state.");
return false;
+ }
- /* Found no charged battery, but did find low batteries */
- if (found_low)
- return true;
-
- /* Found neither charged nor low batteries? let's return that we aren't on low battery state */
- return false;
+ /* If found neither charged nor low batteries, assume that we aren't in low battery state */
+ return found_low;
}
#include "alloc-util.h"
#include "blockdev-util.h"
#include "btrfs-util.h"
+#include "chase.h"
#include "chattr-util.h"
#include "copy.h"
#include "fd-util.h"
return 0;
}
-int btrfs_is_subvol_fd(int fd) {
+int btrfs_is_subvol_at(int dir_fd, const char *path) {
struct stat st;
- assert(fd >= 0);
+ assert(dir_fd >= 0 || dir_fd == AT_FDCWD);
/* On btrfs subvolumes always have the inode 256 */
- if (fstat(fd, &st) < 0)
+ if (fstatat(dir_fd, strempty(path), &st, isempty(path) ? AT_EMPTY_PATH : 0) < 0)
return -errno;
if (!btrfs_might_be_subvol(&st))
return 0;
- return fd_is_fs_type(fd, BTRFS_SUPER_MAGIC);
-}
-
-int btrfs_is_subvol(const char *path) {
- _cleanup_close_ int fd = -EBADF;
-
- assert(path);
-
- fd = open(path, O_RDONLY|O_NOCTTY|O_CLOEXEC|O_DIRECTORY);
- if (fd < 0)
- return -errno;
-
- return btrfs_is_subvol_fd(fd);
+ return is_fs_type_at(dir_fd, path, BTRFS_SUPER_MAGIC);
}
int btrfs_subvol_make_fd(int fd, const char *subvolume) {
return 0; /* plain directory */
}
-int btrfs_subvol_set_read_only_fd(int fd, bool b) {
+int btrfs_subvol_set_read_only_at(int dir_fd, const char *path, bool b) {
+ _cleanup_close_ int fd = -EBADF;
uint64_t flags, nflags;
struct stat st;
- assert(fd >= 0);
+ assert(dir_fd >= 0 || dir_fd == AT_FDCWD);
+
+ fd = xopenat(dir_fd, path, O_RDONLY|O_NOCTTY|O_CLOEXEC|O_DIRECTORY, /* xopen_flags = */ 0, /* mode = */ 0);
+ if (fd < 0)
+ return fd;
if (fstat(fd, &st) < 0)
return -errno;
return RET_NERRNO(ioctl(fd, BTRFS_IOC_SUBVOL_SETFLAGS, &nflags));
}
-int btrfs_subvol_set_read_only(const char *path, bool b) {
- _cleanup_close_ int fd = -EBADF;
-
- fd = open(path, O_RDONLY|O_NOCTTY|O_CLOEXEC|O_DIRECTORY);
- if (fd < 0)
- return -errno;
-
- return btrfs_subvol_set_read_only_fd(fd, b);
-}
-
int btrfs_subvol_get_read_only_fd(int fd) {
uint64_t flags;
struct stat st;
return 0;
}
-int btrfs_subvol_remove(const char *path, BtrfsRemoveFlags flags) {
+int btrfs_subvol_remove_at(int dir_fd, const char *path, BtrfsRemoveFlags flags) {
_cleanup_free_ char *subvolume = NULL;
_cleanup_close_ int fd = -EBADF;
int r;
assert(path);
- r = extract_subvolume_name(path, &subvolume);
- if (r < 0)
- return r;
-
- fd = open_parent(path, O_CLOEXEC, 0);
+ fd = chase_and_openat(dir_fd, path, CHASE_PARENT|CHASE_EXTRACT_FILENAME, O_CLOEXEC, &subvolume);
if (fd < 0)
return fd;
- return subvol_remove_children(fd, subvolume, 0, flags);
-}
+ r = validate_subvolume_name(subvolume);
+ if (r < 0)
+ return r;
-int btrfs_subvol_remove_fd(int fd, const char *subvolume, BtrfsRemoveFlags flags) {
return subvol_remove_children(fd, subvolume, 0, flags);
}
return 0;
}
-int btrfs_subvol_snapshot_fd_full(
- int old_fd,
- const char *new_path,
+int btrfs_subvol_snapshot_at_full(
+ int dir_fdf,
+ const char *from,
+ int dir_fdt,
+ const char *to,
BtrfsSnapshotFlags flags,
copy_progress_path_t progress_path,
copy_progress_bytes_t progress_bytes,
void *userdata) {
_cleanup_free_ char *subvolume = NULL;
- _cleanup_close_ int new_fd = -EBADF;
+ _cleanup_close_ int old_fd = -EBADF, new_fd = -EBADF;
int r;
- assert(old_fd >= 0);
- assert(new_path);
+ assert(dir_fdf >= 0 || dir_fdf == AT_FDCWD);
+ assert(dir_fdt >= 0 || dir_fdt == AT_FDCWD);
+ assert(to);
+
+ old_fd = xopenat(dir_fdf, from, O_RDONLY|O_NOCTTY|O_CLOEXEC|O_DIRECTORY, /* xopen_flags = */ 0, /* mode = */ 0);
+ if (old_fd < 0)
+ return old_fd;
- r = btrfs_is_subvol_fd(old_fd);
+ new_fd = chase_and_openat(dir_fdt, to, CHASE_PARENT|CHASE_EXTRACT_FILENAME, O_CLOEXEC, &subvolume);
+ if (new_fd < 0)
+ return new_fd;
+
+ r = validate_subvolume_name(subvolume);
+ if (r < 0)
+ return r;
+
+ r = btrfs_is_subvol_at(dir_fdf, from);
if (r < 0)
return r;
if (r == 0) {
if (!(flags & BTRFS_SNAPSHOT_FALLBACK_COPY))
return -EISDIR;
- r = btrfs_subvol_make(new_path);
+ r = btrfs_subvol_make_fd(new_fd, subvolume);
if (ERRNO_IS_NOT_SUPPORTED(r) && (flags & BTRFS_SNAPSHOT_FALLBACK_DIRECTORY)) {
/* If the destination doesn't support subvolumes, then use a plain directory, if that's requested. */
- if (mkdir(new_path, 0755) < 0)
+ if (mkdirat(new_fd, subvolume, 0755) < 0)
return -errno;
plain_directory = true;
} else if (r < 0)
return r;
- r = copy_directory_fd_full(
- old_fd, new_path,
+ r = copy_directory_at_full(
+ dir_fdf, from,
+ new_fd, subvolume,
COPY_MERGE_EMPTY|
COPY_REFLINK|
COPY_SAME_MOUNT|
* it: the IMMUTABLE bit. Let's use this here, if this is requested. */
if (flags & BTRFS_SNAPSHOT_FALLBACK_IMMUTABLE)
- (void) chattr_path(new_path, FS_IMMUTABLE_FL, FS_IMMUTABLE_FL, NULL);
+ (void) chattr_at(new_fd, subvolume, FS_IMMUTABLE_FL, FS_IMMUTABLE_FL, NULL);
} else {
- r = btrfs_subvol_set_read_only(new_path, true);
+ r = btrfs_subvol_set_read_only_at(new_fd, subvolume, true);
if (r < 0)
goto fallback_fail;
}
return 0;
fallback_fail:
- (void) rm_rf(new_path, REMOVE_ROOT|REMOVE_PHYSICAL|REMOVE_SUBVOLUME);
+ (void) rm_rf_at(new_fd, subvolume, REMOVE_ROOT|REMOVE_PHYSICAL|REMOVE_SUBVOLUME);
return r;
}
- r = extract_subvolume_name(new_path, &subvolume);
- if (r < 0)
- return r;
-
- new_fd = open_parent(new_path, O_CLOEXEC, 0);
- if (new_fd < 0)
- return new_fd;
-
return subvol_snapshot_children(old_fd, new_fd, subvolume, 0, flags);
}
-int btrfs_subvol_snapshot_full(
- const char *old_path,
- const char *new_path,
- BtrfsSnapshotFlags flags,
- copy_progress_path_t progress_path,
- copy_progress_bytes_t progress_bytes,
- void *userdata) {
-
- _cleanup_close_ int old_fd = -EBADF;
-
- assert(old_path);
- assert(new_path);
-
- old_fd = open(old_path, O_RDONLY|O_NOCTTY|O_CLOEXEC|O_DIRECTORY);
- if (old_fd < 0)
- return -errno;
-
- return btrfs_subvol_snapshot_fd_full(old_fd, new_path, flags, progress_path, progress_bytes, userdata);
-}
-
int btrfs_qgroup_find_parents(int fd, uint64_t qgroupid, uint64_t **ret) {
struct btrfs_ioctl_search_args args = {
BTRFS_REMOVE_QUOTA = 1 << 1,
} BtrfsRemoveFlags;
-int btrfs_is_subvol_fd(int fd);
-int btrfs_is_subvol(const char *path);
+int btrfs_is_subvol_at(int dir_fd, const char *path);
+static inline int btrfs_is_subvol_fd(int fd) {
+ return btrfs_is_subvol_at(fd, NULL);
+}
+static inline int btrfs_is_subvol(const char *path) {
+ return btrfs_is_subvol_at(AT_FDCWD, path);
+}
int btrfs_get_block_device_at(int dir_fd, const char *path, dev_t *ret);
static inline int btrfs_get_block_device(const char *path, dev_t *ret) {
int btrfs_subvol_make_fallback(const char *path, mode_t);
-int btrfs_subvol_snapshot_fd_full(int old_fd, const char *new_path, BtrfsSnapshotFlags flags, copy_progress_path_t progress_path, copy_progress_bytes_t progress_bytes, void *userdata);
-static inline int btrfs_subvol_snapshot_fd(int old_fd, const char *new_path, BtrfsSnapshotFlags flags) {
- return btrfs_subvol_snapshot_fd_full(old_fd, new_path, flags, NULL, NULL, NULL);
+int btrfs_subvol_snapshot_at_full(int dir_fdf, const char *from, int dir_fdt, const char *to, BtrfsSnapshotFlags flags, copy_progress_path_t progress_path, copy_progress_bytes_t progress_bytes, void *userdata);
+static inline int btrfs_subvol_snapshot_at(int dir_fdf, const char *from, int dir_fdt, const char *to, BtrfsSnapshotFlags flags) {
+ return btrfs_subvol_snapshot_at_full(dir_fdf, from, dir_fdt, to, flags, NULL, NULL, NULL);
}
-int btrfs_subvol_snapshot_full(const char *old_path, const char *new_path, BtrfsSnapshotFlags flags, copy_progress_path_t progress_path, copy_progress_bytes_t progress_bytes, void *userdata);
-static inline int btrfs_subvol_snapshot(const char *old_path, const char *new_path, BtrfsSnapshotFlags flags) {
- return btrfs_subvol_snapshot_full(old_path, new_path, flags, NULL, NULL, NULL);
+int btrfs_subvol_remove_at(int dir_fd, const char *path, BtrfsRemoveFlags flags);
+static inline int btrfs_subvol_remove(const char *path, BtrfsRemoveFlags flags) {
+ return btrfs_subvol_remove_at(AT_FDCWD, path, flags);
}
-int btrfs_subvol_remove(const char *path, BtrfsRemoveFlags flags);
-int btrfs_subvol_remove_fd(int fd, const char *subvolume, BtrfsRemoveFlags flags);
+int btrfs_subvol_set_read_only_at(int dir_fd, const char *path, bool b);
+static inline int btrfs_subvol_set_read_only_fd(int fd, bool b) {
+ return btrfs_subvol_set_read_only_at(fd, NULL, b);
+}
+static inline int btrfs_subvol_set_read_only(const char *path, bool b) {
+ return btrfs_subvol_set_read_only_at(AT_FDCWD, path, b);
+}
-int btrfs_subvol_set_read_only_fd(int fd, bool b);
-int btrfs_subvol_set_read_only(const char *path, bool b);
int btrfs_subvol_get_read_only_fd(int fd);
int btrfs_subvol_get_id(int fd, const char *subvolume, uint64_t *ret);
return 1;
}
+ if (streq(field, "ImportCredential")) {
+ if (isempty(eq))
+ r = sd_bus_message_append(m, "(sv)", field, "as", 0);
+ else
+ r = sd_bus_message_append(m, "(sv)", field, "as", 1, eq);
+ if (r < 0)
+ return bus_log_create_error(r);
+
+ return 1;
+ }
+
if (streq(field, "LogExtraFields")) {
r = sd_bus_message_open_container(m, 'r', "sv");
if (r < 0)
.compare = trivial_compare_func,
.free_value = bus_message_unref_wrapper,
};
+
+int bus_message_append_string_set(sd_bus_message *m, Set *set) {
+ const char *s;
+ int r;
+
+ assert(m);
+
+ r = sd_bus_message_open_container(m, 'a', "s");
+ if (r < 0)
+ return r;
+
+ SET_FOREACH(s, set) {
+ r = sd_bus_message_append(m, "s", s);
+ if (r < 0)
+ return r;
+ }
+
+ return sd_bus_message_close_container(m);
+}
+
+int bus_property_get_string_set(
+ sd_bus *bus,
+ const char *path,
+ const char *interface,
+ const char *property,
+ sd_bus_message *reply,
+ void *userdata,
+ sd_bus_error *error) {
+
+ Set **s = ASSERT_PTR(userdata);
+
+ assert(bus);
+ assert(property);
+ assert(reply);
+
+ return bus_message_append_string_set(reply, *s);
+}
#include "errno-util.h"
#include "macro.h"
#include "runtime-scope.h"
+#include "set.h"
#include "string-util.h"
#include "time-util.h"
int bus_register_malloc_status(sd_bus *bus, const char *destination);
extern const struct hash_ops bus_message_hash_ops;
+
+int bus_message_append_string_set(sd_bus_message *m, Set *s);
+
+int bus_property_get_string_set(sd_bus *bus, const char *path, const char *interface, const char *property, sd_bus_message *reply, void *userdata, sd_bus_error *error);
if (repeat == 0)
return -ERANGE;
} else {
- /* If no repeat value is specified for the µs component, then let's explicitly refuse ranges
+ /* If no repeat value is specified for the μs component, then let's explicitly refuse ranges
* below 1s because our default repeat granularity is beyond that. */
/* Overflow check */
ssize_t n;
if (max_bytes <= 0)
- return 1; /* return > 0 if we hit the max_bytes limit */
+ break;
r = look_for_signals(copy_flags);
if (r < 0)
copied_something = true;
}
- return 0; /* return 0 if we hit EOF earlier than the size limit */
+ if (copy_flags & COPY_TRUNCATE) {
+ off_t off = lseek(fdt, 0, SEEK_CUR);
+ if (off < 0)
+ return -errno;
+
+ if (ftruncate(fdt, off) < 0)
+ return -errno;
+ }
+
+ return max_bytes <= 0; /* return 0 if we hit EOF earlier than the size limit */
}
static int fd_copy_symlink(
return 0;
}
-static int sync_dir_by_flags(const char *path, CopyFlags copy_flags) {
+static int sync_dir_by_flags(int dir_fd, const char *path, CopyFlags copy_flags) {
+ assert(dir_fd >= 0 || dir_fd == AT_FDCWD);
+ assert(path);
if (copy_flags & COPY_SYNCFS)
- return syncfs_path(AT_FDCWD, path);
+ return syncfs_path(dir_fd, path);
if (copy_flags & COPY_FSYNC_FULL)
- return fsync_parent_at(AT_FDCWD, path);
-
- return 0;
-}
-
-int copy_directory_fd_full(
- int dirfd,
- const char *to,
- CopyFlags copy_flags,
- copy_progress_path_t progress_path,
- copy_progress_bytes_t progress_bytes,
- void *userdata) {
-
- struct stat st;
- int r;
-
- assert(dirfd >= 0);
- assert(to);
-
- if (fstat(dirfd, &st) < 0)
- return -errno;
-
- r = stat_verify_directory(&st);
- if (r < 0)
- return r;
-
- r = fd_copy_directory(
- dirfd, NULL,
- &st,
- AT_FDCWD, to,
- st.st_dev,
- COPY_DEPTH_MAX,
- UID_INVALID, GID_INVALID,
- copy_flags,
- NULL, NULL, NULL,
- progress_path,
- progress_bytes,
- userdata);
- if (r < 0)
- return r;
-
- r = sync_dir_by_flags(to, copy_flags);
- if (r < 0)
- return r;
+ return fsync_parent_at(dir_fd, path);
return 0;
}
-int copy_directory_full(
+int copy_directory_at_full(
+ int dir_fdf,
const char *from,
+ int dir_fdt,
const char *to,
CopyFlags copy_flags,
copy_progress_path_t progress_path,
struct stat st;
int r;
- assert(from);
+ assert(dir_fdf >= 0 || dir_fdf == AT_FDCWD);
+ assert(dir_fdt >= 0 || dir_fdt == AT_FDCWD);
assert(to);
- if (lstat(from, &st) < 0)
+ if (fstatat(dir_fdf, strempty(from), &st, AT_SYMLINK_NOFOLLOW|(isempty(from) ? AT_EMPTY_PATH : 0)) < 0)
return -errno;
r = stat_verify_directory(&st);
return r;
r = fd_copy_directory(
- AT_FDCWD, from,
+ dir_fdf, from,
&st,
- AT_FDCWD, to,
+ dir_fdt, to,
st.st_dev,
COPY_DEPTH_MAX,
UID_INVALID, GID_INVALID,
if (r < 0)
return r;
- r = sync_dir_by_flags(to, copy_flags);
+ r = sync_dir_by_flags(dir_fdt, to, copy_flags);
if (r < 0)
return r;
return -errno;
}
- r = link_tmpfile_at(fdt, dir_fdt, t, to, copy_flags & COPY_REPLACE);
+ r = link_tmpfile_at(fdt, dir_fdt, t, to, (copy_flags & COPY_REPLACE) ? LINK_TMPFILE_REPLACE : 0);
if (r < 0)
return r;
COPY_ALL_XATTRS = 1 << 13, /* Preserve all xattrs when copying, not just those in the user namespace */
COPY_HOLES = 1 << 14, /* Copy holes */
COPY_GRACEFUL_WARN = 1 << 15, /* Skip copying file types that aren't supported by the target filesystem */
+ COPY_TRUNCATE = 1 << 16, /* Truncate to current file offset after copying */
} CopyFlags;
typedef enum DenyType {
return copy_tree_at_full(AT_FDCWD, from, AT_FDCWD, to, override_uid, override_gid, copy_flags, denylist, NULL, NULL, NULL);
}
-int copy_directory_fd_full(int dirfd, const char *to, CopyFlags copy_flags, copy_progress_path_t progress_path, copy_progress_bytes_t progress_bytes, void *userdata);
-static inline int copy_directory_fd(int dirfd, const char *to, CopyFlags copy_flags) {
- return copy_directory_fd_full(dirfd, to, copy_flags, NULL, NULL, NULL);
-}
-
-int copy_directory_full(const char *from, const char *to, CopyFlags copy_flags, copy_progress_path_t progress_path, copy_progress_bytes_t progress_bytes, void *userdata);
-static inline int copy_directory(const char *from, const char *to, CopyFlags copy_flags) {
- return copy_directory_full(from, to, copy_flags, NULL, NULL, NULL);
+int copy_directory_at_full(int dir_fdf, const char *from, int dir_fdt, const char *to, CopyFlags copy_flags, copy_progress_path_t progress_path, copy_progress_bytes_t progress_bytes, void *userdata);
+static inline int copy_directory_at(int dir_fdf, const char *from, int dir_fdt, const char *to, CopyFlags copy_flags) {
+ return copy_directory_at_full(dir_fdf, from, dir_fdt, to, copy_flags, NULL, NULL, NULL);
}
int copy_bytes_full(int fdf, int fdt, uint64_t max_bytes, CopyFlags copy_flags, void **ret_remains, size_t *ret_remains_size, copy_progress_bytes_t progress, void *userdata);
new_path = strjoina("/var/lib/machines/", new_name);
- r = btrfs_subvol_snapshot(i->path, new_path,
- (read_only ? BTRFS_SNAPSHOT_READ_ONLY : 0) |
- BTRFS_SNAPSHOT_FALLBACK_COPY |
- BTRFS_SNAPSHOT_FALLBACK_DIRECTORY |
- BTRFS_SNAPSHOT_FALLBACK_IMMUTABLE |
- BTRFS_SNAPSHOT_RECURSIVE |
- BTRFS_SNAPSHOT_QUOTA);
+ r = btrfs_subvol_snapshot_at(AT_FDCWD, i->path, AT_FDCWD, new_path,
+ (read_only ? BTRFS_SNAPSHOT_READ_ONLY : 0) |
+ BTRFS_SNAPSHOT_FALLBACK_COPY |
+ BTRFS_SNAPSHOT_FALLBACK_DIRECTORY |
+ BTRFS_SNAPSHOT_FALLBACK_IMMUTABLE |
+ BTRFS_SNAPSHOT_RECURSIVE |
+ BTRFS_SNAPSHOT_QUOTA);
if (r >= 0)
/* Enable "subtree" quotas for the copy, if we didn't copy any quota from the source. */
(void) btrfs_subvol_auto_qgroup(new_path, 0, true);
return -ERANGE;
cache_stat = new_stat;
- *ret = cache = sec * USEC_PER_SEC; /* return in µs */
+ *ret = cache = sec * USEC_PER_SEC; /* return in μs */
return 0;
}
--- /dev/null
+#!/usr/bin/env python3
+# SPDX-License-Identifier: LGPL-2.1-or-later
+
+import re
+import shlex
+import subprocess
+import sys
+
+OVERRIDES = {
+ 'autoneg' : 'autonegotiation',
+}
+
+mode, cpp, header = sys.argv[1:]
+xml = mode == '--xml'
+
+command = [*shlex.split(cpp), '-include', header, '-']
+out = subprocess.check_output(command, stdin=subprocess.DEVNULL, universal_newlines=True)
+
+lines = iter(out.splitlines())
+for line in lines:
+ if line.startswith('enum ethtool_link_mode_bit_indices {'):
+ break
+
+entries = []
+for line in lines:
+ if line.startswith('}'):
+ break
+ # ETHTOOL_LINK_MODE_10baseT_Half_BIT = 0,
+ m = re.match(r'^\s*(ETHTOOL_LINK_MODE_((\d*).*)_BIT)\s*=\s*(\d+),', line)
+ if not m:
+ continue
+ enum, name, speed, value = m.groups()
+
+ name = name.lower().replace('_', '-')
+ name = OVERRIDES.get(name, name)
+
+ duplex = name.split('-')[-1].lower()
+ if duplex not in {'half', 'full'}:
+ duplex = ''
+
+ entries += [(enum, name, speed, value, duplex)]
+
+if xml:
+ print(' <tbody>')
+
+ entries.sort(key=lambda entry: (int(entry[2]) if entry[2] else 1e20, entry[4], entry[1], entry[3]))
+
+for enum, name, speed, value, duplex in entries:
+ if xml:
+ print(f'''\
+ <row><entry><option>{name}</option></entry>
+ <entry>{speed}</entry><entry>{duplex}</entry></row>
+ ''')
+ else:
+ enum = f'[{enum}]'
+ print(f' {enum:50} = "{name}",')
+
+if xml:
+ print(' </tbody>')
+
+assert len(entries) >= 99
};
static const char* const ethtool_link_mode_bit_table[] = {
- [ETHTOOL_LINK_MODE_10baseT_Half_BIT] = "10baset-half",
- [ETHTOOL_LINK_MODE_10baseT_Full_BIT] = "10baset-full",
- [ETHTOOL_LINK_MODE_100baseT_Half_BIT] = "100baset-half",
- [ETHTOOL_LINK_MODE_100baseT_Full_BIT] = "100baset-full",
- [ETHTOOL_LINK_MODE_1000baseT_Half_BIT] = "1000baset-half",
- [ETHTOOL_LINK_MODE_1000baseT_Full_BIT] = "1000baset-full",
- [ETHTOOL_LINK_MODE_Autoneg_BIT] = "autonegotiation",
- [ETHTOOL_LINK_MODE_TP_BIT] = "tp",
- [ETHTOOL_LINK_MODE_AUI_BIT] = "aui",
- [ETHTOOL_LINK_MODE_MII_BIT] = "mii",
- [ETHTOOL_LINK_MODE_FIBRE_BIT] = "fibre",
- [ETHTOOL_LINK_MODE_BNC_BIT] = "bnc",
- [ETHTOOL_LINK_MODE_10000baseT_Full_BIT] = "10000baset-full",
- [ETHTOOL_LINK_MODE_Pause_BIT] = "pause",
- [ETHTOOL_LINK_MODE_Asym_Pause_BIT] = "asym-pause",
- [ETHTOOL_LINK_MODE_2500baseX_Full_BIT] = "2500basex-full",
- [ETHTOOL_LINK_MODE_Backplane_BIT] = "backplane",
- [ETHTOOL_LINK_MODE_1000baseKX_Full_BIT] = "1000basekx-full",
- [ETHTOOL_LINK_MODE_10000baseKX4_Full_BIT] = "10000basekx4-full",
- [ETHTOOL_LINK_MODE_10000baseKR_Full_BIT] = "10000basekr-full",
- [ETHTOOL_LINK_MODE_10000baseR_FEC_BIT] = "10000baser-fec",
- [ETHTOOL_LINK_MODE_20000baseMLD2_Full_BIT] = "20000basemld2-full",
- [ETHTOOL_LINK_MODE_20000baseKR2_Full_BIT] = "20000basekr2-full",
- [ETHTOOL_LINK_MODE_40000baseKR4_Full_BIT] = "40000basekr4-full",
- [ETHTOOL_LINK_MODE_40000baseCR4_Full_BIT] = "40000basecr4-full",
- [ETHTOOL_LINK_MODE_40000baseSR4_Full_BIT] = "40000basesr4-full",
- [ETHTOOL_LINK_MODE_40000baseLR4_Full_BIT] = "40000baselr4-full",
- [ETHTOOL_LINK_MODE_56000baseKR4_Full_BIT] = "56000basekr4-full",
- [ETHTOOL_LINK_MODE_56000baseCR4_Full_BIT] = "56000basecr4-full",
- [ETHTOOL_LINK_MODE_56000baseSR4_Full_BIT] = "56000basesr4-full",
- [ETHTOOL_LINK_MODE_56000baseLR4_Full_BIT] = "56000baselr4-full",
- [ETHTOOL_LINK_MODE_25000baseCR_Full_BIT] = "25000basecr-full",
- [ETHTOOL_LINK_MODE_25000baseKR_Full_BIT] = "25000basekr-full",
- [ETHTOOL_LINK_MODE_25000baseSR_Full_BIT] = "25000basesr-full",
- [ETHTOOL_LINK_MODE_50000baseCR2_Full_BIT] = "50000basecr2-full",
- [ETHTOOL_LINK_MODE_50000baseKR2_Full_BIT] = "50000basekr2-full",
- [ETHTOOL_LINK_MODE_100000baseKR4_Full_BIT] = "100000basekr4-full",
- [ETHTOOL_LINK_MODE_100000baseSR4_Full_BIT] = "100000basesr4-full",
- [ETHTOOL_LINK_MODE_100000baseCR4_Full_BIT] = "100000basecr4-full",
- [ETHTOOL_LINK_MODE_100000baseLR4_ER4_Full_BIT] = "100000baselr4-er4-full",
- [ETHTOOL_LINK_MODE_50000baseSR2_Full_BIT] = "50000basesr2-full",
- [ETHTOOL_LINK_MODE_1000baseX_Full_BIT] = "1000basex-full",
- [ETHTOOL_LINK_MODE_10000baseCR_Full_BIT] = "10000basecr-full",
- [ETHTOOL_LINK_MODE_10000baseSR_Full_BIT] = "10000basesr-full",
- [ETHTOOL_LINK_MODE_10000baseLR_Full_BIT] = "10000baselr-full",
- [ETHTOOL_LINK_MODE_10000baseLRM_Full_BIT] = "10000baselrm-full",
- [ETHTOOL_LINK_MODE_10000baseER_Full_BIT] = "10000baseer-full",
- [ETHTOOL_LINK_MODE_2500baseT_Full_BIT] = "2500baset-full",
- [ETHTOOL_LINK_MODE_5000baseT_Full_BIT] = "5000baset-full",
- [ETHTOOL_LINK_MODE_FEC_NONE_BIT] = "fec-none",
- [ETHTOOL_LINK_MODE_FEC_RS_BIT] = "fec-rs",
- [ETHTOOL_LINK_MODE_FEC_BASER_BIT] = "fec-baser",
- [ETHTOOL_LINK_MODE_50000baseKR_Full_BIT] = "50000basekr-full",
- [ETHTOOL_LINK_MODE_50000baseSR_Full_BIT] = "50000basesr-full",
- [ETHTOOL_LINK_MODE_50000baseCR_Full_BIT] = "50000basecr-full",
- [ETHTOOL_LINK_MODE_50000baseLR_ER_FR_Full_BIT] = "50000baselr-er-fr-full",
- [ETHTOOL_LINK_MODE_50000baseDR_Full_BIT] = "50000basedr-full",
- [ETHTOOL_LINK_MODE_100000baseKR2_Full_BIT] = "100000basekr2-full",
- [ETHTOOL_LINK_MODE_100000baseSR2_Full_BIT] = "100000basesr2-full",
- [ETHTOOL_LINK_MODE_100000baseCR2_Full_BIT] = "100000basecr2-full",
- [ETHTOOL_LINK_MODE_100000baseLR2_ER2_FR2_Full_BIT] = "100000baselr2-er2-fr2-full",
- [ETHTOOL_LINK_MODE_100000baseDR2_Full_BIT] = "100000basedr2-full",
- [ETHTOOL_LINK_MODE_200000baseKR4_Full_BIT] = "200000basekr4-full",
- [ETHTOOL_LINK_MODE_200000baseSR4_Full_BIT] = "200000basesr4-full",
- [ETHTOOL_LINK_MODE_200000baseLR4_ER4_FR4_Full_BIT] = "200000baselr4-er4-fr4-full",
- [ETHTOOL_LINK_MODE_200000baseDR4_Full_BIT] = "200000basedr4-full",
- [ETHTOOL_LINK_MODE_200000baseCR4_Full_BIT] = "200000basecr4-full",
- [ETHTOOL_LINK_MODE_100baseT1_Full_BIT] = "100baset1-full",
- [ETHTOOL_LINK_MODE_1000baseT1_Full_BIT] = "1000baset1-full",
- [ETHTOOL_LINK_MODE_400000baseKR8_Full_BIT] = "400000basekr8-full",
- [ETHTOOL_LINK_MODE_400000baseSR8_Full_BIT] = "400000basesr8-full",
- [ETHTOOL_LINK_MODE_400000baseLR8_ER8_FR8_Full_BIT] = "400000baselr8-er8-fr8-full",
- [ETHTOOL_LINK_MODE_400000baseDR8_Full_BIT] = "400000basedr8-full",
- [ETHTOOL_LINK_MODE_400000baseCR8_Full_BIT] = "400000basecr8-full",
- [ETHTOOL_LINK_MODE_FEC_LLRS_BIT] = "fec-llrs",
- [ETHTOOL_LINK_MODE_100000baseKR_Full_BIT] = "100000basekr-full",
- [ETHTOOL_LINK_MODE_100000baseSR_Full_BIT] = "100000basesr-full",
- [ETHTOOL_LINK_MODE_100000baseLR_ER_FR_Full_BIT] = "100000baselr-er-fr-full",
- [ETHTOOL_LINK_MODE_100000baseCR_Full_BIT] = "100000basecr-full",
- [ETHTOOL_LINK_MODE_100000baseDR_Full_BIT] = "100000basedr-full",
- [ETHTOOL_LINK_MODE_200000baseKR2_Full_BIT] = "200000basekr2-full",
- [ETHTOOL_LINK_MODE_200000baseSR2_Full_BIT] = "200000basesr2-full",
- [ETHTOOL_LINK_MODE_200000baseLR2_ER2_FR2_Full_BIT] = "200000baselr2-er2-fr2-full",
- [ETHTOOL_LINK_MODE_200000baseDR2_Full_BIT] = "200000basedr2-full",
- [ETHTOOL_LINK_MODE_200000baseCR2_Full_BIT] = "200000basecr2-full",
- [ETHTOOL_LINK_MODE_400000baseKR4_Full_BIT] = "400000basekr4-full",
- [ETHTOOL_LINK_MODE_400000baseSR4_Full_BIT] = "400000basesr4-full",
- [ETHTOOL_LINK_MODE_400000baseLR4_ER4_FR4_Full_BIT] = "400000baselr4-er4-fr4-full",
- [ETHTOOL_LINK_MODE_400000baseDR4_Full_BIT] = "400000basedr4-full",
- [ETHTOOL_LINK_MODE_400000baseCR4_Full_BIT] = "400000basecr4-full",
- [ETHTOOL_LINK_MODE_100baseFX_Half_BIT] = "100basefx-half",
- [ETHTOOL_LINK_MODE_100baseFX_Full_BIT] = "100basefx-full",
+# include "ethtool-link-mode.h"
};
/* Make sure the array is large enough to fit all bits */
assert_cc((ELEMENTSOF(ethtool_link_mode_bit_table)-1) / 32 < N_ADVERTISE);
#include "conf-parser.h"
#include "ether-addr-util.h"
-#define N_ADVERTISE 3
+#define N_ADVERTISE 4
/* we can't use DUPLEX_ prefix, as it
* clashes with <linux/ethtool.h> */
"Type=oneshot\n"
"RemainAfterExit=yes\n"
"ExecStart=" SYSTEMD_FSCK_PATH " %7$s\n"
- "TimeoutSec=0\n",
+ "TimeoutSec=infinity\n",
program_invocation_short_name,
escaped,
unit,
"Type=oneshot\n"
"RemainAfterExit=yes\n"
"ExecStart="SYSTEMD_MAKEFS_PATH " swap %s\n"
- "TimeoutSec=0\n",
+ "TimeoutSec=infinity\n",
program_invocation_short_name,
where_unit,
escaped);
"Type=oneshot\n"
"RemainAfterExit=yes\n"
"ExecStart="SYSTEMD_MAKEFS_PATH " %s %s\n"
- "TimeoutSec=0\n",
+ "TimeoutSec=infinity\n",
program_invocation_short_name,
fsck_unit,
where_unit,
"[Service]\n"
"Type=oneshot\n"
"RemainAfterExit=yes\n"
- "TimeoutSec=0\n" /* The binary handles timeouts on its own */
+ "TimeoutSec=infinity\n" /* The binary handles timeouts on its own */
"KeyringMode=shared\n" /* Make sure we can share cached keys among instances */
"OOMScoreAdjust=500\n" /* Unlocking can allocate a lot of memory if Argon2 is used */
"ExecStart=" SYSTEMD_CRYPTSETUP_PATH " attach '%s' '%s' '%s' '%s'\n"
return -errno;
fwrite(&h, sizeof(struct trie_header_f), 1, f);
- r = flink_tmpfile(f, filename_tmp, filename, /* replace= */ true);
+ r = flink_tmpfile(f, filename_tmp, filename, LINK_TMPFILE_REPLACE|LINK_TMPFILE_SYNC);
if (r < 0)
return r;
JsonVariant **p = ASSERT_PTR(userdata);
assert(variant);
+ /* Takes a reference */
JSON_VARIANT_REPLACE(*p, json_variant_ref(variant));
return 0;
}
+int json_dispatch_variant_noref(const char *name, JsonVariant *variant, JsonDispatchFlags flags, void *userdata) {
+ JsonVariant **p = ASSERT_PTR(userdata);
+ assert(variant);
+
+ /* Doesn't take a reference */
+ *p = variant;
+ return 0;
+}
+
int json_dispatch_uid_gid(const char *name, JsonVariant *variant, JsonDispatchFlags flags, void *userdata) {
uid_t *uid = userdata;
uint64_t k;
int json_dispatch_boolean(const char *name, JsonVariant *variant, JsonDispatchFlags flags, void *userdata);
int json_dispatch_tristate(const char *name, JsonVariant *variant, JsonDispatchFlags flags, void *userdata);
int json_dispatch_variant(const char *name, JsonVariant *variant, JsonDispatchFlags flags, void *userdata);
+int json_dispatch_variant_noref(const char *name, JsonVariant *variant, JsonDispatchFlags flags, void *userdata);
int json_dispatch_int64(const char *name, JsonVariant *variant, JsonDispatchFlags flags, void *userdata);
int json_dispatch_uint64(const char *name, JsonVariant *variant, JsonDispatchFlags flags, void *userdata);
int json_dispatch_uint32(const char *name, JsonVariant *variant, JsonDispatchFlags flags, void *userdata);
'tomoyo-util.c',
'tpm2-util.c',
'udev-util.c',
- 'uid-alloc-range.c',
'user-record-nss.c',
'user-record-show.c',
'user-record.c',
shared_generated_gperf_headers = [target1, target2]
shared_sources += shared_generated_gperf_headers
+fname = 'ethtool-link-mode.h'
+ethtool_link_mode_h = custom_target(
+ fname,
+ input : ['ethtool-link-mode.py', 'linux/ethtool.h'],
+ output : fname,
+ command : [python, '@INPUT0@', '--header', cpp, '@INPUT1@'],
+ capture : true)
+shared_sources += ethtool_link_mode_h
+
+fname = 'ethtool-link-mode.xml'
+ethtool_link_mode_xml = custom_target(
+ fname,
+ input : ['ethtool-link-mode.py', 'linux/ethtool.h'],
+ output : fname,
+ command : [python, '@INPUT0@', '--xml', cpp, '@INPUT1@'],
+ capture : true)
+man_page_depends += ethtool_link_mode_xml
+
libshared_name = 'systemd-shared-@0@'.format(shared_lib_tag)
libshared_deps = [threads,
versiondep]
libshared_sym_path = '@0@/libshared.sym'.format(meson.current_source_dir())
+libshared_build_dir = meson.current_build_dir()
libshared_static = static_library(
libshared_name,
if ((flags & REMOVE_SUBVOLUME) && btrfs_might_be_subvol(&st)) {
/* This could be a subvolume, try to remove it */
- r = btrfs_subvol_remove_fd(fd, fname, BTRFS_REMOVE_RECURSIVE|BTRFS_REMOVE_QUOTA);
+ r = btrfs_subvol_remove_at(fd, fname, BTRFS_REMOVE_RECURSIVE|BTRFS_REMOVE_QUOTA);
if (r < 0) {
if (!IN_SET(r, -ENOTTY, -EINVAL))
return r;
return ret;
}
-int rm_rf(const char *path, RemoveFlags flags) {
+int rm_rf_at(int dir_fd, const char *path, RemoveFlags flags) {
mode_t old_mode;
int fd, r, q = 0;
+ assert(dir_fd >= 0 || dir_fd == AT_FDCWD);
assert(path);
/* For now, don't support dropping subvols when also only dropping directories, since we can't do
/* We refuse to clean the root file system with this call. This is extra paranoia to never cause a
* really seriously broken system. */
- if (path_equal_or_inode_same(path, "/", AT_SYMLINK_NOFOLLOW))
+ if (path_is_root_at(dir_fd, path) > 0)
return log_error_errno(SYNTHETIC_ERRNO(EPERM),
- "Attempted to remove entire root file system (\"%s\"), and we can't allow that.",
- path);
+ "Attempted to remove entire root file system, and we can't allow that.");
if (FLAGS_SET(flags, REMOVE_SUBVOLUME | REMOVE_ROOT | REMOVE_PHYSICAL)) {
/* Try to remove as subvolume first */
- r = btrfs_subvol_remove(path, BTRFS_REMOVE_RECURSIVE|BTRFS_REMOVE_QUOTA);
+ r = btrfs_subvol_remove_at(dir_fd, path, BTRFS_REMOVE_RECURSIVE|BTRFS_REMOVE_QUOTA);
if (r >= 0)
return r;
/* Not btrfs or not a subvolume */
}
- fd = openat_harder(AT_FDCWD, path, O_RDONLY|O_NONBLOCK|O_DIRECTORY|O_CLOEXEC|O_NOFOLLOW|O_NOATIME, flags, &old_mode);
+ fd = openat_harder(dir_fd, path, O_RDONLY|O_NONBLOCK|O_DIRECTORY|O_CLOEXEC|O_NOFOLLOW|O_NOATIME, flags, &old_mode);
if (fd >= 0) {
/* We have a dir */
r = rm_rf_children_impl(fd, flags, NULL, old_mode);
if (FLAGS_SET(flags, REMOVE_ROOT))
- q = RET_NERRNO(rmdir(path));
+ q = RET_NERRNO(unlinkat(dir_fd, path, AT_REMOVEDIR));
} else {
r = fd;
if (FLAGS_SET(flags, REMOVE_MISSING_OK) && r == -ENOENT)
if (!FLAGS_SET(flags, REMOVE_PHYSICAL)) {
struct statfs s;
- if (statfs(path, &s) < 0)
- return -errno;
+ r = xstatfsat(dir_fd, path, &s);
+ if (r < 0)
+ return r;
if (is_physical_fs(&s))
return log_error_errno(SYNTHETIC_ERRNO(EPERM),
"Attempted to remove files from a disk file system under \"%s\", refusing.",
}
r = 0;
- q = RET_NERRNO(unlink(path));
+ q = RET_NERRNO(unlinkat(dir_fd, path, 0));
}
if (r < 0)
/* SPDX-License-Identifier: LGPL-2.1-or-later */
#pragma once
+#include <fcntl.h>
#include <sys/stat.h>
#include "alloc-util.h"
int rm_rf_children(int fd, RemoveFlags flags, const struct stat *root_dev);
int rm_rf_child(int fd, const char *name, RemoveFlags flags);
-int rm_rf(const char *path, RemoveFlags flags);
+int rm_rf_at(int dir_fd, const char *path, RemoveFlags flags);
+static inline int rm_rf(const char *path, RemoveFlags flags) {
+ return rm_rf_at(AT_FDCWD, path, flags);
+}
/* Useful for usage with _cleanup_(), destroys a directory and frees the pointer */
static inline char *rm_rf_physical_and_free(char *p) {
return ret;
}
+int deserialize_strv(char ***l, const char *value) {
+ ssize_t unescaped_len;
+ char *unescaped;
+
+ assert(l);
+ assert(value);
+
+ unescaped_len = cunescape(value, 0, &unescaped);
+ if (unescaped_len < 0)
+ return unescaped_len;
+
+ return strv_consume(l, unescaped);
+}
+
int deserialize_usec(const char *value, usec_t *ret) {
int r;
int deserialize_usec(const char *value, usec_t *timestamp);
int deserialize_dual_timestamp(const char *value, dual_timestamp *t);
int deserialize_environment(const char *value, char ***environment);
+int deserialize_strv(char ***l, const char *value);
int open_serialization_fd(const char *ident);
* Attempt to find the hibernation location by parsing /proc/swaps, /sys/power/resume, and
* /sys/power/resume_offset.
*
+ * Beware:
+ * Never use a device or file as location that hasn't been somehow specified by a user that would also be
+ * entrusted with full system memory access (for example via /sys/power/resume) or that isn't an already
+ * active swap area!
+ * Otherwise various security attacks might become possible, for example an attacker could silently attach
+ * such a device and circumvent full disk encryption when it would be automatically used for hibernation.
+ * Also, having a swap area on top of encryption is not per se enough to protect from all such attacks.
+ *
* Returns:
* 1 - Values are set in /sys/power/resume and /sys/power/resume_offset.
* ret_hibernate_location will represent matching /proc/swap entry if identified or NULL if not.
a.type = type;
- fd = socket_address_listen(&a, type | flags, SOMAXCONN, SOCKET_ADDRESS_DEFAULT,
+ fd = socket_address_listen(&a, type | flags, SOMAXCONN_DELUXE, SOCKET_ADDRESS_DEFAULT,
NULL, false, false, false, 0755, 0644, NULL);
if (fd < 0 || log_get_max_level() >= log_level) {
_cleanup_free_ char *p = NULL;
static TSS2_RC (*sym_Esys_ReadPublic)(ESYS_CONTEXT *esysContext, ESYS_TR objectHandle, ESYS_TR shandle1, ESYS_TR shandle2, ESYS_TR shandle3, TPM2B_PUBLIC **outPublic, TPM2B_NAME **name, TPM2B_NAME **qualifiedName) = NULL;
static TSS2_RC (*sym_Esys_StartAuthSession)(ESYS_CONTEXT *esysContext, ESYS_TR tpmKey, ESYS_TR bind, ESYS_TR shandle1, ESYS_TR shandle2, ESYS_TR shandle3, const TPM2B_NONCE *nonceCaller, TPM2_SE sessionType, const TPMT_SYM_DEF *symmetric, TPMI_ALG_HASH authHash, ESYS_TR *sessionHandle) = NULL;
static TSS2_RC (*sym_Esys_Startup)(ESYS_CONTEXT *esysContext, TPM2_SU startupType) = NULL;
+static TSS2_RC (*sym_Esys_TestParms)(ESYS_CONTEXT *esysContext, ESYS_TR shandle1, ESYS_TR shandle2, ESYS_TR shandle3, const TPMT_PUBLIC_PARMS *parameters) = NULL;
+static TSS2_RC (*sym_Esys_TR_Close)(ESYS_CONTEXT *esys_context, ESYS_TR *rsrc_handle) = NULL;
static TSS2_RC (*sym_Esys_TR_Deserialize)(ESYS_CONTEXT *esys_context, uint8_t const *buffer, size_t buffer_size, ESYS_TR *esys_handle) = NULL;
static TSS2_RC (*sym_Esys_TR_FromTPMPublic)(ESYS_CONTEXT *esysContext, TPM2_HANDLE tpm_handle, ESYS_TR optionalSession1, ESYS_TR optionalSession2, ESYS_TR optionalSession3, ESYS_TR *object) = NULL;
static TSS2_RC (*sym_Esys_TR_GetName)(ESYS_CONTEXT *esysContext, ESYS_TR handle, TPM2B_NAME **name) = NULL;
DLSYM_ARG(Esys_ReadPublic),
DLSYM_ARG(Esys_StartAuthSession),
DLSYM_ARG(Esys_Startup),
+ DLSYM_ARG(Esys_TestParms),
+ DLSYM_ARG(Esys_TR_Close),
DLSYM_ARG(Esys_TR_Deserialize),
DLSYM_ARG(Esys_TR_FromTPMPublic),
DLSYM_ARG(Esys_TR_GetName),
sym_Esys_Free(*(void**) p);
}
+/* Get a specific TPM capability (or capabilities).
+ *
+ * Returns 0 if there are no more capability properties of the requested type, or 1 if there are more, or < 0
+ * on any error. Both 0 and 1 indicate this completed successfully, but do not indicate how many capability
+ * properties were provided in 'ret_capability_data'. To find the number of provided properties, check the
+ * specific type's 'count' field (e.g. for TPM2_CAP_ALGS, check ret_capability_data->algorithms.count).
+ *
+ * This calls TPM2_GetCapability() and does not alter the provided data, so it is important to understand how
+ * that TPM function works. It is recommended to check the TCG TPM specification Part 3 ("Commands") section
+ * on TPM2_GetCapability() for full details, but a short summary is: if this returns 0, all available
+ * properties have been provided in ret_capability_data, or no properties were available. If this returns 1,
+ * there are between 1 and "count" properties provided in ret_capability_data, and there are more available.
+ * Note that this may provide less than "count" properties even if the TPM has more available. Also, each
+ * capability category may have more specific requirements than described here; see the spec for exact
+ * details. */
+static int tpm2_get_capability(
+ Tpm2Context *c,
+ TPM2_CAP capability,
+ uint32_t property,
+ uint32_t count,
+ TPMU_CAPABILITIES *ret_capability_data) {
+
+ _cleanup_(Esys_Freep) TPMS_CAPABILITY_DATA *capabilities = NULL;
+ TPMI_YES_NO more;
+ TSS2_RC rc;
+
+ assert(c);
+
+ log_debug("Getting TPM2 capability 0x%04" PRIx32 " property 0x%04" PRIx32 " count %" PRIu32 ".",
+ capability, property, count);
+
+ rc = sym_Esys_GetCapability(
+ c->esys_context,
+ ESYS_TR_NONE,
+ ESYS_TR_NONE,
+ ESYS_TR_NONE,
+ capability,
+ property,
+ count,
+ &more,
+ &capabilities);
+ if (rc != TSS2_RC_SUCCESS)
+ return log_error_errno(SYNTHETIC_ERRNO(ENOTRECOVERABLE),
+ "Failed to get TPM2 capability 0x%04" PRIx32 " property 0x%04" PRIx32 ": %s",
+ capability, property, sym_Tss2_RC_Decode(rc));
+
+ if (capabilities->capability != capability)
+ return log_error_errno(SYNTHETIC_ERRNO(ENOTRECOVERABLE),
+ "TPM provided wrong capability: 0x%04" PRIx32 " instead of 0x%04" PRIx32 ".",
+ capabilities->capability, capability);
+
+ if (ret_capability_data)
+ *ret_capability_data = capabilities->data;
+
+ return more == TPM2_YES;
+}
+
+static int tpm2_cache_capabilities(Tpm2Context *c) {
+ TPMU_CAPABILITIES capability;
+ int r;
+
+ assert(c);
+
+ /* Cache the PCR capabilities, which are safe to cache, as the only way they can change is
+ * TPM2_PCR_Allocate(), which changes the allocation after the next _TPM_Init(). If the TPM is
+ * reinitialized while we are using it, all our context and sessions will be invalid, so we can
+ * safely assume the TPM PCR allocation will not change while we are using it. */
+ r = tpm2_get_capability(
+ c,
+ TPM2_CAP_PCRS,
+ /* property= */ 0,
+ /* count= */ 1,
+ &capability);
+ if (r < 0)
+ return r;
+ if (r == 1)
+ /* This should never happen. Part 3 ("Commands") of the TCG TPM2 spec in the section for
+ * TPM2_GetCapability states: "TPM_CAP_PCRS – Returns the current allocation of PCR in a
+ * TPML_PCR_SELECTION. The property parameter shall be zero. The TPM will always respond to
+ * this command with the full PCR allocation and moreData will be NO." */
+ log_warning("TPM bug: reported multiple PCR sets; using only first set.");
+ c->capability_pcrs = capability.assignedPCR;
+
+ return 0;
+}
+
+#define tpm2_capability_pcrs(c) ((c)->capability_pcrs)
+
+/* Get the TPMA_ALGORITHM for a TPM2_ALG_ID.
+ *
+ * Returns 1 if the TPM supports the algorithm and the TPMA_ALGORITHM is provided, or 0 if the TPM does not
+ * support the algorithm, or < 0 for any errors. */
+static int tpm2_get_capability_alg(Tpm2Context *c, TPM2_ALG_ID alg, TPMA_ALGORITHM *ret) {
+ TPMU_CAPABILITIES capability;
+ int r;
+
+ assert(c);
+
+ /* The spec explicitly states the TPM2_ALG_ID should be cast to uint32_t. */
+ r = tpm2_get_capability(c, TPM2_CAP_ALGS, (uint32_t) alg, 1, &capability);
+ if (r < 0)
+ return r;
+
+ TPML_ALG_PROPERTY algorithms = capability.algorithms;
+ if (algorithms.count == 0 || algorithms.algProperties[0].alg != alg) {
+ log_debug("TPM does not support alg 0x%02" PRIx16 ".", alg);
+ return 0;
+ }
+
+ if (ret)
+ *ret = algorithms.algProperties[0].algProperties;
+
+ return 1;
+}
+
+/* Returns 1 if the TPM supports the alg, 0 if the TPM does not support the alg, or < 0 for any error. */
+int tpm2_supports_alg(Tpm2Context *c, TPM2_ALG_ID alg) {
+ return tpm2_get_capability_alg(c, alg, NULL);
+}
+
+/* Returns 1 if the TPM supports the ECC curve, 0 if not, or < 0 for any error. */
+static int tpm2_supports_ecc_curve(Tpm2Context *c, TPM2_ECC_CURVE curve) {
+ TPMU_CAPABILITIES capability;
+ int r;
+
+ /* The spec explicitly states the TPM2_ECC_CURVE should be cast to uint32_t. */
+ r = tpm2_get_capability(c, TPM2_CAP_ECC_CURVES, (uint32_t) curve, 1, &capability);
+ if (r < 0)
+ return r;
+
+ TPML_ECC_CURVE eccCurves = capability.eccCurves;
+ if (eccCurves.count == 0 || eccCurves.eccCurves[0] != curve) {
+ log_debug("TPM does not support ECC curve 0x%02" PRIx16 ".", curve);
+ return 0;
+ }
+
+ return 1;
+}
+
+/* Query the TPM for populated handles.
+ *
+ * This provides an array of handle indexes populated in the TPM, starting at the requested handle. The array will
+ * contain only populated handle addresses (which might not include the requested handle). The number of
+ * handles will be no more than the 'max' number requested. This will not search past the end of the handle
+ * range (i.e. handle & 0xff000000).
+ *
+ * Returns 0 if all populated handles in the range (starting at the requested handle) were provided (or no
+ * handles were in the range), or 1 if there are more populated handles in the range, or < 0 on any error. */
+static int tpm2_get_capability_handles(
+ Tpm2Context *c,
+ TPM2_HANDLE start,
+ size_t max,
+ TPM2_HANDLE **ret_handles,
+ size_t *ret_n_handles) {
+
+ _cleanup_free_ TPM2_HANDLE *handles = NULL;
+ size_t n_handles = 0;
+ TPM2_HANDLE current = start;
+ int r = 0;
+
+ assert(c);
+ assert(ret_handles);
+ assert(ret_n_handles);
+
+ while (max > 0) {
+ TPMU_CAPABILITIES capability;
+ r = tpm2_get_capability(c, TPM2_CAP_HANDLES, current, (uint32_t) max, &capability);
+ if (r < 0)
+ return r;
+
+ TPML_HANDLE handle_list = capability.handles;
+ if (handle_list.count == 0)
+ break;
+
+ assert(handle_list.count <= max);
+
+ if (n_handles > SIZE_MAX - handle_list.count)
+ return log_oom();
+
+ if (!GREEDY_REALLOC(handles, n_handles + handle_list.count))
+ return log_oom();
+
+ memcpy_safe(&handles[n_handles], handle_list.handle, sizeof(handles[0]) * handle_list.count);
+
+ max -= handle_list.count;
+ n_handles += handle_list.count;
+
+ /* Update current to the handle index after the last handle in the list. */
+ current = handles[n_handles - 1] + 1;
+
+ if (r == 0)
+ /* No more handles in this range. */
+ break;
+ }
+
+ *ret_handles = TAKE_PTR(handles);
+ *ret_n_handles = n_handles;
+
+ return r;
+}
+
+#define TPM2_HANDLE_RANGE(h) ((TPM2_HANDLE)((h) & TPM2_HR_RANGE_MASK))
+#define TPM2_HANDLE_TYPE(h) ((TPM2_HT)(TPM2_HANDLE_RANGE(h) >> TPM2_HR_SHIFT))
+
+/* Returns 1 if the handle is populated in the TPM, 0 if not, and < 0 on any error. */
+static int tpm2_get_capability_handle(Tpm2Context *c, TPM2_HANDLE handle) {
+ _cleanup_free_ TPM2_HANDLE *handles = NULL;
+ size_t n_handles = 0;
+ int r;
+
+ r = tpm2_get_capability_handles(c, handle, 1, &handles, &n_handles);
+ if (r < 0)
+ return r;
+
+ return n_handles == 0 ? false : handles[0] == handle;
+}
+
+/* Returns 1 if the TPM supports the parms, or 0 if the TPM does not support the parms. */
+bool tpm2_test_parms(Tpm2Context *c, TPMI_ALG_PUBLIC alg, const TPMU_PUBLIC_PARMS *parms) {
+ TSS2_RC rc;
+
+ assert(c);
+ assert(parms);
+
+ TPMT_PUBLIC_PARMS parameters = {
+ .type = alg,
+ .parameters = *parms,
+ };
+
+ rc = sym_Esys_TestParms(c->esys_context, ESYS_TR_NONE, ESYS_TR_NONE, ESYS_TR_NONE, ¶meters);
+ if (rc != TSS2_RC_SUCCESS)
+ /* The spec says if the parms are not supported the TPM returns "...the appropriate
+ * unmarshaling error if a parameter is not valid". Since the spec (currently) defines 15
+ * unmarshaling errors, instead of checking for them all here, let's just assume any error
+ * indicates unsupported parms, and log the specific error text. */
+ log_debug("TPM does not support tested parms: %s", sym_Tss2_RC_Decode(rc));
+
+ return rc == TSS2_RC_SUCCESS;
+}
+
+static inline bool tpm2_supports_tpmt_public(Tpm2Context *c, const TPMT_PUBLIC *public) {
+ assert(c);
+ assert(public);
+
+ return tpm2_test_parms(c, public->type, &public->parameters);
+}
+
+static inline bool tpm2_supports_tpmt_sym_def_object(Tpm2Context *c, const TPMT_SYM_DEF_OBJECT *parameters) {
+ assert(c);
+ assert(parameters);
+
+ TPMU_PUBLIC_PARMS parms = {
+ .symDetail.sym = *parameters,
+ };
+
+ return tpm2_test_parms(c, TPM2_ALG_SYMCIPHER, &parms);
+}
+
+static inline bool tpm2_supports_tpmt_sym_def(Tpm2Context *c, const TPMT_SYM_DEF *parameters) {
+ assert(c);
+ assert(parameters);
+
+ /* Unfortunately, TPMT_SYM_DEF and TPMT_SYM_DEF_OBEJECT are separately defined, even though they are
+ * functionally identical. */
+ TPMT_SYM_DEF_OBJECT object = {
+ .algorithm = parameters->algorithm,
+ .keyBits = parameters->keyBits,
+ .mode = parameters->mode,
+ };
+
+ return tpm2_supports_tpmt_sym_def_object(c, &object);
+}
+
static Tpm2Context *tpm2_context_free(Tpm2Context *c) {
if (!c)
return NULL;
DEFINE_TRIVIAL_REF_UNREF_FUNC(Tpm2Context, tpm2_context, tpm2_context_free);
+static const TPMT_SYM_DEF SESSION_TEMPLATE_SYM_AES_128_CFB = {
+ .algorithm = TPM2_ALG_AES,
+ .keyBits.aes = 128,
+ .mode.aes = TPM2_ALG_CFB, /* The spec requires sessions to use CFB. */
+};
+
int tpm2_context_new(const char *device, Tpm2Context **ret_context) {
- _cleanup_tpm2_context_ Tpm2Context *context = NULL;
+ _cleanup_(tpm2_context_unrefp) Tpm2Context *context = NULL;
TSS2_RC rc;
int r;
return log_error_errno(SYNTHETIC_ERRNO(ENOTRECOVERABLE),
"Failed to start up TPM: %s", sym_Tss2_RC_Decode(rc));
+ r = tpm2_cache_capabilities(context);
+ if (r < 0)
+ return r;
+
+ /* We require AES and CFB support for session encryption. */
+ r = tpm2_supports_alg(context, TPM2_ALG_AES);
+ if (r < 0)
+ return r;
+ if (r == 0)
+ return log_error_errno(SYNTHETIC_ERRNO(ENOTRECOVERABLE), "TPM does not support AES.");
+
+ r = tpm2_supports_alg(context, TPM2_ALG_CFB);
+ if (r < 0)
+ return r;
+ if (r == 0)
+ return log_error_errno(SYNTHETIC_ERRNO(ENOTRECOVERABLE), "TPM does not support CFB.");
+
+ if (!tpm2_supports_tpmt_sym_def(context, &SESSION_TEMPLATE_SYM_AES_128_CFB))
+ return log_error_errno(SYNTHETIC_ERRNO(ENOTRECOVERABLE), "TPM does not support AES-128-CFB.");
+
*ret_context = TAKE_PTR(context);
return 0;
}
-static void tpm2_handle_flush(ESYS_CONTEXT *esys_context, ESYS_TR esys_handle) {
+static void tpm2_handle_cleanup(ESYS_CONTEXT *esys_context, ESYS_TR esys_handle, bool flush) {
+ TSS2_RC rc;
+
if (!esys_context || esys_handle == ESYS_TR_NONE)
return;
- TSS2_RC rc = sym_Esys_FlushContext(esys_context, esys_handle);
+ /* Closing the handle removes its reference from the esys_context, but leaves the corresponding
+ * handle in the actual TPM. Flushing the handle removes its reference from the esys_context as well
+ * as removing its corresponding handle from the actual TPM. */
+ if (flush)
+ rc = sym_Esys_FlushContext(esys_context, esys_handle);
+ else
+ rc = sym_Esys_TR_Close(esys_context, &esys_handle);
if (rc != TSS2_RC_SUCCESS) /* We ignore failures here (besides debug logging), since this is called
* in error paths, where we cannot do anything about failures anymore. And
* when it is called in successful codepaths by this time we already did
* what we wanted to do, and got the results we wanted so there's no
* reason to make this fail more loudly than necessary. */
- log_debug("Failed to flush TPM handle, ignoring: %s", sym_Tss2_RC_Decode(rc));
+ log_debug("Failed to %s TPM handle, ignoring: %s", flush ? "flush" : "close", sym_Tss2_RC_Decode(rc));
}
Tpm2Handle *tpm2_handle_free(Tpm2Handle *handle) {
if (!handle)
return NULL;
- _cleanup_tpm2_context_ Tpm2Context *context = (Tpm2Context*)handle->tpm2_context;
- if (context && !handle->keep)
- tpm2_handle_flush(context->esys_context, handle->esys_handle);
+ _cleanup_(tpm2_context_unrefp) Tpm2Context *context = (Tpm2Context*)handle->tpm2_context;
+ if (context)
+ tpm2_handle_cleanup(context->esys_context, handle->esys_handle, handle->flush);
return mfree(handle);
}
int tpm2_handle_new(Tpm2Context *context, Tpm2Handle **ret_handle) {
- _cleanup_tpm2_handle_ Tpm2Handle *handle = NULL;
+ _cleanup_(tpm2_handle_freep) Tpm2Handle *handle = NULL;
assert(ret_handle);
*handle = (Tpm2Handle) {
.tpm2_context = tpm2_context_ref(context),
.esys_handle = ESYS_TR_NONE,
+ .flush = true,
};
*ret_handle = TAKE_PTR(handle);
return 0;
}
+/* Create a Tpm2Handle object that references a pre-existing handle in the TPM, at the TPM2_HANDLE address
+ * provided. This should be used only for persistent, transient, or NV handles. Returns 1 on success, 0 if
+ * the requested handle is not present in the TPM, or < 0 on error. */
+static int tpm2_esys_handle_from_tpm_handle(
+ Tpm2Context *c,
+ const Tpm2Handle *session,
+ TPM2_HANDLE tpm_handle,
+ Tpm2Handle **ret_handle) {
+
+ TSS2_RC rc;
+ int r;
+
+ assert(c);
+ assert(tpm_handle > 0);
+ assert(ret_handle);
+
+ /* Let's restrict this, at least for now, to allow only some handle types. */
+ switch (TPM2_HANDLE_TYPE(tpm_handle)) {
+ case TPM2_HT_PERSISTENT:
+ case TPM2_HT_NV_INDEX:
+ case TPM2_HT_TRANSIENT:
+ break;
+ case TPM2_HT_PCR:
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
+ "Refusing to create ESYS handle for PCR handle 0x%08" PRIx32 ".",
+ tpm_handle);
+ case TPM2_HT_HMAC_SESSION:
+ case TPM2_HT_POLICY_SESSION:
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
+ "Refusing to create ESYS handle for session handle 0x%08" PRIx32 ".",
+ tpm_handle);
+ case TPM2_HT_PERMANENT: /* Permanent handles are defined, e.g. ESYS_TR_RH_OWNER. */
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
+ "Refusing to create ESYS handle for permanent handle 0x%08" PRIx32 ".",
+ tpm_handle);
+ default:
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
+ "Refusing to create ESYS handle for unknown handle 0x%08" PRIx32 ".",
+ tpm_handle);
+ }
+
+ r = tpm2_get_capability_handle(c, tpm_handle);
+ if (r < 0)
+ return r;
+ if (r == 0) {
+ log_debug("TPM handle 0x%08" PRIx32 " not populated.", tpm_handle);
+ *ret_handle = NULL;
+ return 0;
+ }
+
+ _cleanup_(tpm2_handle_freep) Tpm2Handle *handle = NULL;
+ r = tpm2_handle_new(c, &handle);
+ if (r < 0)
+ return r;
+
+ /* Since we didn't create this handle in the TPM (this is only creating an ESYS_TR handle for the
+ * pre-existing TPM handle), we shouldn't flush (or evict) it on cleanup. */
+ handle->flush = false;
+
+ rc = sym_Esys_TR_FromTPMPublic(
+ c->esys_context,
+ tpm_handle,
+ session ? session->esys_handle : ESYS_TR_NONE,
+ ESYS_TR_NONE,
+ ESYS_TR_NONE,
+ &handle->esys_handle);
+ if (rc != TSS2_RC_SUCCESS)
+ return log_error_errno(SYNTHETIC_ERRNO(ENOTRECOVERABLE),
+ "Failed to read public info: %s", sym_Tss2_RC_Decode(rc));
+
+ *ret_handle = TAKE_PTR(handle);
+
+ return 1;
+}
+
#define TPM2_CREDIT_RANDOM_FLAG_PATH "/run/systemd/tpm-rng-credited"
static int tpm2_credit_random(Tpm2Context *c) {
return 0;
}
-const TPM2B_PUBLIC *tpm2_get_primary_template(Tpm2SRKTemplateFlags flags) {
+static int tpm2_read_public(
+ Tpm2Context *c,
+ const Tpm2Handle *session,
+ const Tpm2Handle *handle,
+ TPM2B_PUBLIC **ret_public,
+ TPM2B_NAME **ret_name,
+ TPM2B_NAME **ret_qname) {
- /*
- * Set up array so flags can be used directly as an input.
- *
- * Templates for SRK come from the spec:
- * - https://trustedcomputinggroup.org/wp-content/uploads/TCG-TPM-v2.0-Provisioning-Guidance-Published-v1r1.pdf
- *
- * However, note their is some lore here. On Linux, the SRK has it's unique field set to size 0 and
- * on Windows the SRK has their unique data set to keyLen in bytes of zeros.
- */
- assert(flags >= 0);
- assert(flags <= _TPM2_SRK_TEMPLATE_MAX);
-
- static const TPM2B_PUBLIC templ[_TPM2_SRK_TEMPLATE_MAX + 1] = {
- /* index 0 RSA old */
- [0] = {
- .publicArea = {
- .type = TPM2_ALG_RSA,
- .nameAlg = TPM2_ALG_SHA256,
- .objectAttributes = TPMA_OBJECT_RESTRICTED|TPMA_OBJECT_DECRYPT|TPMA_OBJECT_FIXEDTPM|TPMA_OBJECT_FIXEDPARENT|TPMA_OBJECT_SENSITIVEDATAORIGIN|TPMA_OBJECT_USERWITHAUTH,
- .parameters.rsaDetail = {
- .symmetric = {
- .algorithm = TPM2_ALG_AES,
- .keyBits.aes = 128,
- .mode.aes = TPM2_ALG_CFB,
- },
- .scheme.scheme = TPM2_ALG_NULL,
- .keyBits = 2048,
- },
+ TSS2_RC rc;
+
+ assert(c);
+ assert(handle);
+
+ rc = sym_Esys_ReadPublic(
+ c->esys_context,
+ handle->esys_handle,
+ session ? session->esys_handle : ESYS_TR_NONE,
+ ESYS_TR_NONE,
+ ESYS_TR_NONE,
+ ret_public,
+ ret_name,
+ ret_qname);
+ if (rc != TSS2_RC_SUCCESS)
+ return log_error_errno(SYNTHETIC_ERRNO(ENOTRECOVERABLE),
+ "Failed to read public info: %s", sym_Tss2_RC_Decode(rc));
+
+ return 0;
+}
+
+/* Get one of the legacy primary key templates.
+ *
+ * The legacy templates should only be used for older sealed data that did not use the SRK. Instead of a
+ * persistent SRK, a transient key was created to seal the data and then flushed; and the exact same template
+ * must be used to recreate the same transient key to unseal the data. The alg parameter must be TPM2_ALG_RSA
+ * or TPM2_ALG_ECC. This does not check if the alg is actually supported on this TPM. */
+static int tpm2_get_legacy_template(TPMI_ALG_PUBLIC alg, TPMT_PUBLIC *ret_template) {
+ /* Do not modify. */
+ static const TPMT_PUBLIC legacy_ecc = {
+ .type = TPM2_ALG_ECC,
+ .nameAlg = TPM2_ALG_SHA256,
+ .objectAttributes = TPMA_OBJECT_RESTRICTED|TPMA_OBJECT_DECRYPT|TPMA_OBJECT_FIXEDTPM|TPMA_OBJECT_FIXEDPARENT|TPMA_OBJECT_SENSITIVEDATAORIGIN|TPMA_OBJECT_USERWITHAUTH,
+ .parameters.eccDetail = {
+ .symmetric = {
+ .algorithm = TPM2_ALG_AES,
+ .keyBits.aes = 128,
+ .mode.aes = TPM2_ALG_CFB,
},
+ .scheme.scheme = TPM2_ALG_NULL,
+ .curveID = TPM2_ECC_NIST_P256,
+ .kdf.scheme = TPM2_ALG_NULL,
},
- [TPM2_SRK_TEMPLATE_ECC] = {
- .publicArea = {
- .type = TPM2_ALG_ECC,
- .nameAlg = TPM2_ALG_SHA256,
- .objectAttributes = TPMA_OBJECT_RESTRICTED|TPMA_OBJECT_DECRYPT|TPMA_OBJECT_FIXEDTPM|TPMA_OBJECT_FIXEDPARENT|TPMA_OBJECT_SENSITIVEDATAORIGIN|TPMA_OBJECT_USERWITHAUTH,
- .parameters.eccDetail = {
- .symmetric = {
- .algorithm = TPM2_ALG_AES,
- .keyBits.aes = 128,
- .mode.aes = TPM2_ALG_CFB,
- },
- .scheme.scheme = TPM2_ALG_NULL,
- .curveID = TPM2_ECC_NIST_P256,
- .kdf.scheme = TPM2_ALG_NULL,
- },
+ };
+
+ /* Do not modify. */
+ static const TPMT_PUBLIC legacy_rsa = {
+ .type = TPM2_ALG_RSA,
+ .nameAlg = TPM2_ALG_SHA256,
+ .objectAttributes = TPMA_OBJECT_RESTRICTED|TPMA_OBJECT_DECRYPT|TPMA_OBJECT_FIXEDTPM|TPMA_OBJECT_FIXEDPARENT|TPMA_OBJECT_SENSITIVEDATAORIGIN|TPMA_OBJECT_USERWITHAUTH,
+ .parameters.rsaDetail = {
+ .symmetric = {
+ .algorithm = TPM2_ALG_AES,
+ .keyBits.aes = 128,
+ .mode.aes = TPM2_ALG_CFB,
},
+ .scheme.scheme = TPM2_ALG_NULL,
+ .keyBits = 2048,
},
- [TPM2_SRK_TEMPLATE_NEW_STYLE] = {
- .publicArea = {
- .type = TPM2_ALG_RSA,
- .nameAlg = TPM2_ALG_SHA256,
- .objectAttributes = TPMA_OBJECT_FIXEDTPM|TPMA_OBJECT_FIXEDPARENT|TPMA_OBJECT_SENSITIVEDATAORIGIN|TPMA_OBJECT_RESTRICTED|TPMA_OBJECT_DECRYPT|TPMA_OBJECT_USERWITHAUTH|TPMA_OBJECT_NODA,
- .parameters.rsaDetail = {
- .symmetric = {
- .algorithm = TPM2_ALG_AES,
- .keyBits.aes = 128,
- .mode.aes = TPM2_ALG_CFB,
- },
- .scheme.scheme = TPM2_ALG_NULL,
- .keyBits = 2048,
- },
- },
+ };
+
+ assert(ret_template);
+
+ if (alg == TPM2_ALG_ECC)
+ *ret_template = legacy_ecc;
+ else if (alg == TPM2_ALG_RSA)
+ *ret_template = legacy_rsa;
+ else
+ return log_debug_errno(SYNTHETIC_ERRNO(EOPNOTSUPP),
+ "Unsupported legacy SRK alg: 0x%x", alg);
+
+ return 0;
+}
+
+/* Get a Storage Root Key (SRK) template.
+ *
+ * The SRK template values are recommended by the "TCG TPM v2.0 Provisioning Guidance" document in section
+ * 7.5.1 "Storage Primary Key (SRK) Templates", referencing "TCG EK Credential Profile for TPM Family 2.0".
+ * The EK Credential Profile version 2.0 provides only a single template each for RSA and ECC, while later EK
+ * Credential Profile versions provide more templates, and keep the original templates as "L-1" (for RSA) and
+ * "L-2" (for ECC).
+ *
+ * https://trustedcomputinggroup.org/resource/tcg-tpm-v2-0-provisioning-guidance
+ * https://trustedcomputinggroup.org/resource/http-trustedcomputinggroup-org-wp-content-uploads-tcg-ek-credential-profile
+ *
+ * These templates are only needed to create a new persistent SRK (or a new transient key that is
+ * SRK-compatible). Preferably, the TPM should contain a shared SRK located at the reserved shared SRK handle
+ * (see TPM2_SRK_HANDLE and tpm2_get_srk() below).
+ *
+ * The alg must be TPM2_ALG_RSA or TPM2_ALG_ECC. Returns error if the requested template is not supported on
+ * this TPM. */
+static int tpm2_get_srk_template(Tpm2Context *c, TPMI_ALG_PUBLIC alg, TPMT_PUBLIC *ret_template) {
+ /* The attributes are the same between ECC and RSA templates. This has the changes specified in the
+ * Provisioning Guidance document, specifically:
+ * TPMA_OBJECT_USERWITHAUTH is added.
+ * TPMA_OBJECT_ADMINWITHPOLICY is removed.
+ * TPMA_OBJECT_NODA is added. */
+ TPMA_OBJECT srk_attributes =
+ TPMA_OBJECT_DECRYPT |
+ TPMA_OBJECT_FIXEDPARENT |
+ TPMA_OBJECT_FIXEDTPM |
+ TPMA_OBJECT_NODA |
+ TPMA_OBJECT_RESTRICTED |
+ TPMA_OBJECT_SENSITIVEDATAORIGIN |
+ TPMA_OBJECT_USERWITHAUTH;
+
+ /* The symmetric configuration is the same between ECC and RSA templates. */
+ TPMT_SYM_DEF_OBJECT srk_symmetric = {
+ .algorithm = TPM2_ALG_AES,
+ .keyBits.aes = 128,
+ .mode.aes = TPM2_ALG_CFB,
+ };
+
+ /* Both templates have an empty authPolicy as specified by the Provisioning Guidance document. */
+
+ /* From the EK Credential Profile template "L-2". */
+ TPMT_PUBLIC srk_ecc = {
+ .type = TPM2_ALG_ECC,
+ .nameAlg = TPM2_ALG_SHA256,
+ .objectAttributes = srk_attributes,
+ .parameters.eccDetail = {
+ .symmetric = srk_symmetric,
+ .scheme.scheme = TPM2_ALG_NULL,
+ .curveID = TPM2_ECC_NIST_P256,
+ .kdf.scheme = TPM2_ALG_NULL,
},
- [TPM2_SRK_TEMPLATE_NEW_STYLE|TPM2_SRK_TEMPLATE_ECC] = {
- .publicArea = {
- .type = TPM2_ALG_ECC,
- .nameAlg = TPM2_ALG_SHA256,
- .objectAttributes = TPMA_OBJECT_FIXEDTPM|TPMA_OBJECT_FIXEDPARENT|TPMA_OBJECT_SENSITIVEDATAORIGIN|TPMA_OBJECT_RESTRICTED|TPMA_OBJECT_DECRYPT|TPMA_OBJECT_USERWITHAUTH|TPMA_OBJECT_NODA,
- .parameters.eccDetail = {
- .symmetric = {
- .algorithm = TPM2_ALG_AES,
- .keyBits.aes = 128,
- .mode.aes = TPM2_ALG_CFB,
- },
- .scheme.scheme = TPM2_ALG_NULL,
- .curveID = TPM2_ECC_NIST_P256,
- .kdf.scheme = TPM2_ALG_NULL,
- },
- },
+ };
+
+ /* From the EK Credential Profile template "L-1". */
+ TPMT_PUBLIC srk_rsa = {
+ .type = TPM2_ALG_RSA,
+ .nameAlg = TPM2_ALG_SHA256,
+ .objectAttributes = srk_attributes,
+ .parameters.rsaDetail = {
+ .symmetric = srk_symmetric,
+ .scheme.scheme = TPM2_ALG_NULL,
+ .keyBits = 2048,
},
};
- return &templ[flags];
+ assert(c);
+ assert(ret_template);
+
+ if (alg == TPM2_ALG_ECC) {
+ if (!tpm2_supports_alg(c, TPM2_ALG_ECC))
+ return log_debug_errno(SYNTHETIC_ERRNO(ENOTRECOVERABLE),
+ "TPM does not support ECC.");
+
+ if (!tpm2_supports_ecc_curve(c, srk_ecc.parameters.eccDetail.curveID))
+ return log_debug_errno(SYNTHETIC_ERRNO(ENOTRECOVERABLE),
+ "TPM does not support ECC-NIST-P256 curve.");
+
+ if (!tpm2_supports_tpmt_public(c, &srk_ecc))
+ return log_debug_errno(SYNTHETIC_ERRNO(ENOTRECOVERABLE),
+ "TPM does not support SRK ECC template L-2.");
+
+ *ret_template = srk_ecc;
+ return 0;
+ }
+
+ if (alg == TPM2_ALG_RSA) {
+ if (!tpm2_supports_alg(c, TPM2_ALG_RSA))
+ return log_debug_errno(SYNTHETIC_ERRNO(ENOTRECOVERABLE),
+ "TPM does not support RSA.");
+
+ if (!tpm2_supports_tpmt_public(c, &srk_rsa))
+ return log_debug_errno(SYNTHETIC_ERRNO(ENOTRECOVERABLE),
+ "TPM does not support SRK RSA template L-1.");
+
+ *ret_template = srk_rsa;
+ return 0;
+ }
+
+ return log_debug_errno(SYNTHETIC_ERRNO(EOPNOTSUPP), "Unsupported SRK alg: 0x%x.", alg);
}
-/*
- * Why and what is an SRK?
- * TL;DR provides a working space for those without owner auth. The user enrolling
- * the disk may not have access to the TPMs owner hierarchy auth, so they need a
- * working space. This working space is at the defined address of 0x81000001.
- * Details can be found here:
- * - https://trustedcomputinggroup.org/wp-content/uploads/TCG-TPM-v2.0-Provisioning-Guidance-Published-v1r1.pdf
- */
-#define SRK_HANDLE UINT32_C(0x81000001)
+/* The SRK handle is defined in the Provisioning Guidance document (see above) in the table "Reserved Handles
+ * for TPM Provisioning Fundamental Elements". The SRK is useful because it is "shared", meaning it has no
+ * authValue nor authPolicy set, and thus may be used by anyone on the system to generate derived keys or
+ * seal secrets. This is useful if the TPM has an auth (password) set for the 'owner hierarchy', which would
+ * prevent users from generating primary transient keys, unless they knew the owner hierarchy auth. See
+ * the Provisioning Guidance document for more details. */
+#define TPM2_SRK_HANDLE UINT32_C(0x81000001)
/*
* Retrieves the SRK handle if present. Returns 0 if SRK not present, 1 if present
*/
static int tpm2_get_srk(
Tpm2Context *c,
- TPMI_ALG_PUBLIC *ret_alg,
- Tpm2Handle *ret_primary) {
+ const Tpm2Handle *session,
+ TPM2B_PUBLIC **ret_public,
+ TPM2B_NAME **ret_name,
+ TPM2B_NAME **ret_qname,
+ Tpm2Handle **ret_handle) {
- TPMI_YES_NO more_data;
- ESYS_TR primary_tr = ESYS_TR_NONE;
- _cleanup_(Esys_Freep) TPMS_CAPABILITY_DATA *cap_data = NULL;
+ int r;
assert(c);
- assert(ret_primary);
-
- TSS2_RC rc = sym_Esys_GetCapability(c->esys_context,
- ESYS_TR_NONE,
- ESYS_TR_NONE,
- ESYS_TR_NONE,
- TPM2_CAP_HANDLES,
- SRK_HANDLE,
- 1,
- &more_data,
- &cap_data);
- if (rc != TSS2_RC_SUCCESS)
- return log_error_errno(SYNTHETIC_ERRNO(ENOTRECOVERABLE),
- "Failed to enumerate handles searching for SRK: %s",
- sym_Tss2_RC_Decode(rc));
-
- /* Did Not find SRK, indicate this by returning 0 */
- if (cap_data->data.handles.count == 0 || cap_data->data.handles.handle[0] != SRK_HANDLE) {
- ret_primary->esys_handle = ESYS_TR_NONE;
- if (ret_alg)
- *ret_alg = 0;
+ _cleanup_(tpm2_handle_freep) Tpm2Handle *handle = NULL;
+ r = tpm2_esys_handle_from_tpm_handle(c, session, TPM2_SRK_HANDLE, &handle);
+ if (r < 0)
+ return r;
+ if (r == 0) { /* SRK not found */
+ if (ret_public)
+ *ret_public = NULL;
+ if (ret_name)
+ *ret_name = NULL;
+ if (ret_qname)
+ *ret_qname = NULL;
+ if (ret_handle)
+ *ret_handle = NULL;
return 0;
}
- log_debug("Found SRK on TPM.");
-
- /* convert the raw handle to an ESYS_TR */
- TPM2_HANDLE handle = cap_data->data.handles.handle[0];
- rc = sym_Esys_TR_FromTPMPublic(c->esys_context,
- handle,
- ESYS_TR_NONE,
- ESYS_TR_NONE,
- ESYS_TR_NONE,
- &primary_tr);
- if (rc != TSS2_RC_SUCCESS)
- return log_error_errno(SYNTHETIC_ERRNO(ENOTRECOVERABLE),
- "Failed to convert ray handle to ESYS_TR for SRK: %s",
- sym_Tss2_RC_Decode(rc));
-
- /* Get the algorithm if the caller wants it */
- _cleanup_(Esys_Freep) TPM2B_PUBLIC *out_public = NULL;
- if (ret_alg) {
- rc = sym_Esys_ReadPublic(
- c->esys_context,
- primary_tr,
- ESYS_TR_NONE,
- ESYS_TR_NONE,
- ESYS_TR_NONE,
- &out_public,
- NULL,
- NULL);
- if (rc != TSS2_RC_SUCCESS)
- return log_error_errno(SYNTHETIC_ERRNO(ENOTRECOVERABLE),
- "Failed to convert ray handle to ESYS_TR for SRK: %s",
- sym_Tss2_RC_Decode(rc));
+ if (ret_public || ret_name || ret_qname) {
+ r = tpm2_read_public(c, session, handle, ret_public, ret_name, ret_qname);
+ if (r < 0)
+ return r;
}
- ret_primary->esys_handle = primary_tr;
-
- if (ret_alg)
- *ret_alg = out_public->publicArea.type;
+ if (ret_handle)
+ *ret_handle = TAKE_PTR(handle);
return 1;
}
static const TPM2B_SENSITIVE_CREATE primary_sensitive = {};
static const TPML_PCR_SELECTION creation_pcr = {};
- const TPM2B_PUBLIC *primary_template = NULL;
- Tpm2SRKTemplateFlags base_flags = use_srk_model ? TPM2_SRK_TEMPLATE_NEW_STYLE : 0;
+ TPM2B_PUBLIC primary_template = { .size = sizeof(TPMT_PUBLIC), };
_cleanup_(release_lock_file) LockFile srk_lock = LOCK_FILE_INIT;
TSS2_RC rc;
usec_t ts;
ts = now(CLOCK_MONOTONIC);
- _cleanup_tpm2_handle_ Tpm2Handle *primary = NULL;
- r = tpm2_handle_new(c, &primary);
- if (r < 0)
- return r;
+ _cleanup_(tpm2_handle_freep) Tpm2Handle *primary = NULL;
/* we only need the SRK lock when making the SRK since its not atomic, transient
* primary creations don't even matter if they stomp on each other, the TPM will
/* Find existing SRK and use it if present */
if (use_srk_model) {
- TPMI_ALG_PUBLIC got_alg = TPM2_ALG_NULL;
- r = tpm2_get_srk(c, &got_alg, primary);
+ _cleanup_(Esys_Freep) TPM2B_PUBLIC *primary_public = NULL;
+ r = tpm2_get_srk(c, NULL, &primary_public, NULL, NULL, &primary);
if (r < 0)
return log_error_errno(SYNTHETIC_ERRNO(ENOTRECOVERABLE),
"Failed to establish if SRK is present");
if (r == 1) {
log_debug("Discovered existing SRK");
+ TPMI_ALG_PUBLIC got_alg = primary_public->publicArea.type;
if (alg != 0 && alg != got_alg)
log_warning("Caller asked for specific algorithm %u, but existing SRK is %u, ignoring",
alg, got_alg);
log_debug("Did not find SRK, generating...");
}
+ r = tpm2_handle_new(c, &primary);
+ if (r < 0)
+ return r;
+
if (IN_SET(alg, 0, TPM2_ALG_ECC)) {
- primary_template = tpm2_get_primary_template(base_flags | TPM2_SRK_TEMPLATE_ECC);
+ if (use_srk_model)
+ r = tpm2_get_srk_template(c, TPM2_ALG_ECC, &primary_template.publicArea);
+ else
+ r = tpm2_get_legacy_template(TPM2_ALG_ECC, &primary_template.publicArea);
+ if (r < 0)
+ return r;
rc = sym_Esys_CreatePrimary(
c->esys_context,
ESYS_TR_NONE,
ESYS_TR_NONE,
&primary_sensitive,
- primary_template,
+ &primary_template,
NULL,
&creation_pcr,
&primary->esys_handle,
}
if (IN_SET(alg, 0, TPM2_ALG_RSA)) {
- primary_template = tpm2_get_primary_template(base_flags);
+ if (use_srk_model)
+ r = tpm2_get_srk_template(c, TPM2_ALG_RSA, &primary_template.publicArea);
+ else
+ r = tpm2_get_legacy_template(TPM2_ALG_RSA, &primary_template.publicArea);
+ if (r < 0)
+ return r;
rc = sym_Esys_CreatePrimary(
c->esys_context,
ESYS_TR_NONE,
ESYS_TR_NONE,
&primary_sensitive,
- primary_template,
+ &primary_template,
NULL,
&creation_pcr,
&primary->esys_handle,
if (use_srk_model) {
rc = sym_Esys_EvictControl(c->esys_context, ESYS_TR_RH_OWNER, primary->esys_handle,
- ESYS_TR_PASSWORD, ESYS_TR_NONE, ESYS_TR_NONE, SRK_HANDLE, &primary->esys_handle);
+ ESYS_TR_PASSWORD, ESYS_TR_NONE, ESYS_TR_NONE, TPM2_SRK_HANDLE, &primary->esys_handle);
if (rc != TSS2_RC_SUCCESS)
return log_error_errno(SYNTHETIC_ERRNO(ENOTRECOVERABLE),
"Failed to persist SRK within TPM: %s", sym_Tss2_RC_Decode(rc));
- primary->keep = true;
+ primary->flush = false;
}
if (ret_primary)
uint32_t pcr_mask,
TPMI_ALG_HASH *ret) {
- _cleanup_(Esys_Freep) TPMS_CAPABILITY_DATA *pcap = NULL;
+ TPML_PCR_SELECTION pcrs;
TPMI_ALG_HASH supported_hash = 0, hash_with_valid_pcr = 0;
- TPMI_YES_NO more;
- TSS2_RC rc;
int r;
assert(c);
+ assert(ret);
- rc = sym_Esys_GetCapability(
- c->esys_context,
- ESYS_TR_NONE,
- ESYS_TR_NONE,
- ESYS_TR_NONE,
- TPM2_CAP_PCRS,
- 0,
- 1,
- &more,
- &pcap);
- if (rc != TSS2_RC_SUCCESS)
- return log_error_errno(SYNTHETIC_ERRNO(ENOTRECOVERABLE),
- "Failed to determine TPM2 PCR bank capabilities: %s", sym_Tss2_RC_Decode(rc));
-
- assert(pcap->capability == TPM2_CAP_PCRS);
-
- for (size_t i = 0; i < pcap->data.assignedPCR.count; i++) {
+ pcrs = tpm2_capability_pcrs(c);
+ FOREACH_TPMS_PCR_SELECTION_IN_TPML_PCR_SELECTION(selection, &pcrs) {
+ TPMI_ALG_HASH hash = selection->hash;
int good;
/* For now we are only interested in the SHA1 and SHA256 banks */
- if (!IN_SET(pcap->data.assignedPCR.pcrSelections[i].hash, TPM2_ALG_SHA256, TPM2_ALG_SHA1))
+ if (!IN_SET(hash, TPM2_ALG_SHA256, TPM2_ALG_SHA1))
continue;
- r = tpm2_bank_has24(pcap->data.assignedPCR.pcrSelections + i);
+ r = tpm2_bank_has24(selection);
if (r < 0)
return r;
if (!r)
continue;
- good = tpm2_pcr_mask_good(c, pcap->data.assignedPCR.pcrSelections[i].hash, pcr_mask);
+ good = tpm2_pcr_mask_good(c, hash, pcr_mask);
if (good < 0)
return good;
- if (pcap->data.assignedPCR.pcrSelections[i].hash == TPM2_ALG_SHA256) {
+ if (hash == TPM2_ALG_SHA256) {
supported_hash = TPM2_ALG_SHA256;
if (good) {
/* Great, SHA256 is supported and has initialized PCR values, we are done. */
break;
}
} else {
- assert(pcap->data.assignedPCR.pcrSelections[i].hash == TPM2_ALG_SHA1);
+ assert(hash == TPM2_ALG_SHA1);
if (supported_hash == 0)
supported_hash = TPM2_ALG_SHA1;
TPMI_ALG_HASH **ret) {
_cleanup_free_ TPMI_ALG_HASH *good_banks = NULL, *fallback_banks = NULL;
- _cleanup_(Esys_Freep) TPMS_CAPABILITY_DATA *pcap = NULL;
+ TPML_PCR_SELECTION pcrs;
size_t n_good_banks = 0, n_fallback_banks = 0;
- TPMI_YES_NO more;
- TSS2_RC rc;
int r;
assert(c);
assert(ret);
- rc = sym_Esys_GetCapability(
- c->esys_context,
- ESYS_TR_NONE,
- ESYS_TR_NONE,
- ESYS_TR_NONE,
- TPM2_CAP_PCRS,
- 0,
- 1,
- &more,
- &pcap);
- if (rc != TSS2_RC_SUCCESS)
- return log_error_errno(SYNTHETIC_ERRNO(ENOTRECOVERABLE),
- "Failed to determine TPM2 PCR bank capabilities: %s", sym_Tss2_RC_Decode(rc));
-
- assert(pcap->capability == TPM2_CAP_PCRS);
-
- for (size_t i = 0; i < pcap->data.assignedPCR.count; i++) {
+ pcrs = tpm2_capability_pcrs(c);
+ FOREACH_TPMS_PCR_SELECTION_IN_TPML_PCR_SELECTION(selection, &pcrs) {
+ TPMI_ALG_HASH hash = selection->hash;
/* Let's see if this bank is superficially OK, i.e. has at least 24 enabled registers */
- r = tpm2_bank_has24(pcap->data.assignedPCR.pcrSelections + i);
+ r = tpm2_bank_has24(selection);
if (r < 0)
return r;
if (!r)
continue;
/* Let's now see if this bank has any of the selected PCRs actually initialized */
- r = tpm2_pcr_mask_good(c, pcap->data.assignedPCR.pcrSelections[i].hash, pcr_mask);
+ r = tpm2_pcr_mask_good(c, hash, pcr_mask);
if (r < 0)
return r;
if (!GREEDY_REALLOC(good_banks, n_good_banks+1))
return log_oom();
- good_banks[n_good_banks++] = pcap->data.assignedPCR.pcrSelections[i].hash;
+ good_banks[n_good_banks++] = hash;
} else {
if (!GREEDY_REALLOC(fallback_banks, n_fallback_banks+1))
return log_oom();
- fallback_banks[n_fallback_banks++] = pcap->data.assignedPCR.pcrSelections[i].hash;
+ fallback_banks[n_fallback_banks++] = hash;
}
}
const Tpm2Handle *bind_key,
Tpm2Handle **ret_session) {
- static const TPMT_SYM_DEF symmetric = {
- .algorithm = TPM2_ALG_AES,
- .keyBits.aes = 128,
- .mode.aes = TPM2_ALG_CFB,
- };
const TPMA_SESSION sessionAttributes = TPMA_SESSION_DECRYPT | TPMA_SESSION_ENCRYPT |
TPMA_SESSION_CONTINUESESSION;
TSS2_RC rc;
/* Start a salted, unbound HMAC session with a well-known key (e.g. primary key) as tpmKey, which
* means that the random salt will be encrypted with the well-known key. That way, only the TPM can
* recover the salt, which is then used for key derivation. */
- _cleanup_tpm2_handle_ Tpm2Handle *session = NULL;
+ _cleanup_(tpm2_handle_freep) Tpm2Handle *session = NULL;
r = tpm2_handle_new(c, &session);
if (r < 0)
return r;
ESYS_TR_NONE,
NULL,
TPM2_SE_HMAC,
- &symmetric,
+ &SESSION_TEMPLATE_SYM_AES_128_CFB,
TPM2_ALG_SHA256,
&session->esys_handle);
if (rc != TSS2_RC_SUCCESS)
bool trial,
Tpm2Handle **ret_session) {
- static const TPMT_SYM_DEF symmetric = {
- .algorithm = TPM2_ALG_AES,
- .keyBits.aes = 128,
- .mode.aes = TPM2_ALG_CFB,
- };
TPM2_SE session_type = trial ? TPM2_SE_TRIAL : TPM2_SE_POLICY;
TSS2_RC rc;
int r;
log_debug("Starting policy session.");
- _cleanup_tpm2_handle_ Tpm2Handle *session = NULL;
+ _cleanup_(tpm2_handle_freep) Tpm2Handle *session = NULL;
r = tpm2_handle_new(c, &session);
if (r < 0)
return r;
ESYS_TR_NONE,
NULL,
session_type,
- &symmetric,
+ &SESSION_TEMPLATE_SYM_AES_128_CFB,
TPM2_ALG_SHA256,
&session->esys_handle);
if (rc != TSS2_RC_SUCCESS)
log_debug("Adding PCR signature policy.");
- _cleanup_tpm2_handle_ Tpm2Handle *pubkey_handle = NULL;
+ _cleanup_(tpm2_handle_freep) Tpm2Handle *pubkey_handle = NULL;
r = tpm2_handle_new(c, &pubkey_handle);
if (r < 0)
return r;
CLEANUP_ERASE(hmac_sensitive);
- _cleanup_tpm2_context_ Tpm2Context *c = NULL;
+ _cleanup_(tpm2_context_unrefp) Tpm2Context *c = NULL;
r = tpm2_context_new(device, &c);
if (r < 0)
return r;
if (r < 0)
return log_error_errno(r, "Failed to generate secret key: %m");
- _cleanup_tpm2_handle_ Tpm2Handle *primary_handle = NULL;
+ _cleanup_(tpm2_handle_freep) Tpm2Handle *primary_handle = NULL;
TPMI_ALG_PUBLIC primary_alg;
r = tpm2_make_primary(c, /* alg = */0, !!ret_srk_buf, &primary_alg, &primary_handle);
if (r < 0)
return r;
- _cleanup_tpm2_handle_ Tpm2Handle *encryption_session = NULL;
+ _cleanup_(tpm2_handle_freep) Tpm2Handle *encryption_session = NULL;
r = tpm2_make_encryption_session(c, primary_handle, &TPM2_HANDLE_NONE, &encryption_session);
if (r < 0)
return r;
return log_error_errno(SYNTHETIC_ERRNO(ENOTRECOVERABLE),
"Failed to unmarshal public key: %s", sym_Tss2_RC_Decode(rc));
- _cleanup_tpm2_context_ Tpm2Context *c = NULL;
+ _cleanup_(tpm2_context_unrefp) Tpm2Context *c = NULL;
r = tpm2_context_new(device, &c);
if (r < 0)
return r;
/* If their is a primary key we trust, like an SRK, use it */
- _cleanup_tpm2_handle_ Tpm2Handle *primary = NULL;
+ _cleanup_(tpm2_handle_freep) Tpm2Handle *primary = NULL;
if (srk_buf) {
r = tpm2_handle_new(c, &primary);
if (r < 0)
return r;
- primary->keep = true;
+ primary->flush = false;
log_debug("Found existing SRK key to use, deserializing ESYS_TR");
rc = sym_Esys_TR_Deserialize(
* SRK model, the tpmKey is verified. In the non-srk model, with pin, the bindKey
* provides protections.
*/
- _cleanup_tpm2_handle_ Tpm2Handle *hmac_key = NULL;
+ _cleanup_(tpm2_handle_freep) Tpm2Handle *hmac_key = NULL;
r = tpm2_handle_new(c, &hmac_key);
if (r < 0)
return r;
if (r < 0)
return r;
- _cleanup_tpm2_handle_ Tpm2Handle *encryption_session = NULL;
+ _cleanup_(tpm2_handle_freep) Tpm2Handle *encryption_session = NULL;
r = tpm2_make_encryption_session(c, primary, hmac_key, &encryption_session);
if (r < 0)
return r;
for (unsigned i = RETRY_UNSEAL_MAX;; i--) {
- _cleanup_tpm2_handle_ Tpm2Handle *policy_session = NULL;
+ _cleanup_(tpm2_handle_freep) Tpm2Handle *policy_session = NULL;
_cleanup_(Esys_Freep) TPM2B_DIGEST *policy_digest = NULL;
r = tpm2_make_policy_session(
c,
TPM2_FLAGS_USE_PIN = 1 << 0,
} TPM2Flags;
-
-typedef enum Tpm2SRKTemplateFlags {
- TPM2_SRK_TEMPLATE_ECC = 1 << 0,
- TPM2_SRK_TEMPLATE_NEW_STYLE = 1 << 1,
- _TPM2_SRK_TEMPLATE_MAX = TPM2_SRK_TEMPLATE_NEW_STYLE|TPM2_SRK_TEMPLATE_ECC,
-} Tpm2SRKTemplateFlags;
-
/* As per https://trustedcomputinggroup.org/wp-content/uploads/TCG_PCClient_PFP_r1p05_v23_pub.pdf a
* TPM2 on a Client PC must have at least 24 PCRs. This hardcodes our expectation of 24. */
#define TPM2_PCRS_MAX 24U
void *tcti_dl;
TSS2_TCTI_CONTEXT *tcti_context;
ESYS_CONTEXT *esys_context;
+
+ /* Some selected cached capabilities of the TPM */
+ TPML_PCR_SELECTION capability_pcrs;
} Tpm2Context;
int tpm2_context_new(const char *device, Tpm2Context **ret_context);
Tpm2Context *tpm2_context_ref(Tpm2Context *context);
Tpm2Context *tpm2_context_unref(Tpm2Context *context);
DEFINE_TRIVIAL_CLEANUP_FUNC(Tpm2Context*, tpm2_context_unref);
-#define _cleanup_tpm2_context_ _cleanup_(tpm2_context_unrefp)
typedef struct {
Tpm2Context *tpm2_context;
ESYS_TR esys_handle;
- bool keep;
+
+ bool flush;
} Tpm2Handle;
#define _tpm2_handle(c, h) { .tpm2_context = (c), .esys_handle = (h), }
int tpm2_handle_new(Tpm2Context *context, Tpm2Handle **ret_handle);
Tpm2Handle *tpm2_handle_free(Tpm2Handle *handle);
DEFINE_TRIVIAL_CLEANUP_FUNC(Tpm2Handle*, tpm2_handle_free);
-#define _cleanup_tpm2_handle_ _cleanup_(tpm2_handle_freep)
+
+int tpm2_supports_alg(Tpm2Context *c, TPM2_ALG_ID alg);
+
+bool tpm2_test_parms(Tpm2Context *c, TPMI_ALG_PUBLIC alg, const TPMU_PUBLIC_PARMS *parms);
int tpm2_get_good_pcr_banks(Tpm2Context *c, uint32_t pcr_mask, TPMI_ALG_HASH **ret_banks);
int tpm2_get_good_pcr_banks_strv(Tpm2Context *c, uint32_t pcr_mask, char ***ret);
size_t tpm2_tpml_pcr_selection_weight(const TPML_PCR_SELECTION *l);
#define tpm2_tpml_pcr_selection_is_empty(l) (tpm2_tpml_pcr_selection_weight(l) == 0)
-const TPM2B_PUBLIC *tpm2_get_primary_template(Tpm2SRKTemplateFlags flags);
-
#else /* HAVE_TPM2 */
typedef struct {} Tpm2Context;
typedef struct {} Tpm2Handle;
return r;
}
- if (listen(fd, SOMAXCONN) < 0)
+ if (listen(fd, SOMAXCONN_DELUXE) < 0)
return -errno;
r = varlink_server_create_listen_fd_socket(s, fd, &ss);
/*
Returns > 0 if synchronization with systemd succeeded. Returns < 0
on error. Returns 0 if $NOTIFY_SOCKET was not set. Note that the
- timeout parameter of this function call takes the timeout in µs, and
+ timeout parameter of this function call takes the timeout in μs, and
will be passed to ppoll(2), hence the behaviour will be similar to
ppoll(2). This function can be called after sending a status message
to systemd, if one needs to synchronize against reception of the
Returns > 0 if the service manager expects watchdog keep-alive
events to be sent regularly via sd_notify(0, "WATCHDOG=1"). Returns
0 if it does not expect this. If the usec argument is non-NULL
- returns the watchdog timeout in µs after which the service manager
+ returns the watchdog timeout in μs after which the service manager
will act on a process that has not sent a watchdog keep alive
message. This function is useful to implement services that
recognize automatically if they are being run under supervision of
/* Determine the X11 display of this session. */
int sd_session_get_display(const char *session, char **display);
+/* Determine the leader process of this session. */
+int sd_session_get_leader(const char *session, pid_t *leader);
+
/* Determine the remote host of this session. */
int sd_session_get_remote_host(const char *session, char **remote_host);
'condition' : 'HAVE_ACL',
},
{
- 'sources' : [
- files('test-af-list.c'),
- generated_gperf_headers,
- ],
+ 'sources' : files('test-af-list.c') +
+ generated_gperf_headers,
},
{
- 'sources' : [
- files('test-arphrd-util.c'),
- generated_gperf_headers,
- ],
+ 'sources' : files('test-arphrd-util.c') +
+ generated_gperf_headers,
},
{
'sources' : files('test-ask-password-api.c'),
'type' : 'manual',
},
{
- 'sources' : [
- files('test-cap-list.c'),
- generated_gperf_headers,
- ],
+ 'sources' : files('test-cap-list.c') +
+ generated_gperf_headers,
'dependencies' : libcap,
},
{
'dependencies' : libp11kit_cflags
},
{
- 'sources' : [
- files('test-errno-list.c'),
- generated_gperf_headers,
- ],
+ 'sources' : files('test-errno-list.c') +
+ generated_gperf_headers,
},
{
'sources' : files('test-fd-util.c'),
'dependencies' : libseccomp,
},
{
- 'sources' : [files(
- 'test-hashmap.c',
- 'test-hashmap-plain.c'),
- test_hashmap_ordered_c,
- ],
+ 'sources' : files(
+ 'test-hashmap.c',
+ 'test-hashmap-plain.c',
+ ) + [
+ test_hashmap_ordered_c,
+ ],
'timeout' : 180,
},
{
- 'sources' : [
- files('test-ip-protocol-list.c'),
- shared_generated_gperf_headers,
- ],
+ 'sources' : files('test-ip-protocol-list.c') +
+ shared_generated_gperf_headers,
},
{
'sources' : files('test-ipcrm.c'),
if (r < 0)
log_error_errno(r, "Failed to write file: %m");
- r = btrfs_subvol_snapshot("/xxxtest", "/xxxtest2", 0);
+ r = btrfs_subvol_snapshot_at(AT_FDCWD, "/xxxtest", AT_FDCWD, "/xxxtest2", 0);
if (r < 0)
log_error_errno(r, "Failed to make snapshot: %m");
- r = btrfs_subvol_snapshot("/xxxtest", "/xxxtest3", BTRFS_SNAPSHOT_READ_ONLY);
+ r = btrfs_subvol_snapshot_at(AT_FDCWD, "/xxxtest", AT_FDCWD, "/xxxtest3", BTRFS_SNAPSHOT_READ_ONLY);
if (r < 0)
log_error_errno(r, "Failed to make snapshot: %m");
if (r < 0)
log_error_errno(r, "Failed to remove subvolume: %m");
- r = btrfs_subvol_snapshot("/etc", "/etc2", BTRFS_SNAPSHOT_READ_ONLY|BTRFS_SNAPSHOT_FALLBACK_COPY);
+ r = btrfs_subvol_snapshot_at(AT_FDCWD, "/etc", AT_FDCWD, "/etc2",
+ BTRFS_SNAPSHOT_READ_ONLY|BTRFS_SNAPSHOT_FALLBACK_COPY);
if (r < 0)
log_error_errno(r, "Failed to make snapshot: %m");
if (mkdir("/xxxrectest/mnt", 0755) < 0)
log_error_errno(errno, "Failed to make directory: %m");
- r = btrfs_subvol_snapshot("/xxxrectest", "/xxxrectest2", BTRFS_SNAPSHOT_RECURSIVE);
+ r = btrfs_subvol_snapshot_at(AT_FDCWD, "/xxxrectest", AT_FDCWD, "/xxxrectest2", BTRFS_SNAPSHOT_RECURSIVE);
if (r < 0)
log_error_errno(r, "Failed to snapshot subvolume: %m");
if (r < 0)
log_error_errno(r, "Failed to set up quota limit: %m");
- r = btrfs_subvol_snapshot("/xxxquotatest", "/xxxquotatest2", BTRFS_SNAPSHOT_RECURSIVE|BTRFS_SNAPSHOT_QUOTA);
+ r = btrfs_subvol_snapshot_at(AT_FDCWD, "/xxxquotatest", AT_FDCWD, "/xxxquotatest2",
+ BTRFS_SNAPSHOT_RECURSIVE|BTRFS_SNAPSHOT_QUOTA);
if (r < 0)
log_error_errno(r, "Failed to set up snapshot: %m");
static const char *arg_test_dir = NULL;
+static void test_chase_extract_filename_one(const char *path, const char *root, const char *expected) {
+ _cleanup_free_ char *ret1 = NULL, *ret2 = NULL, *fname = NULL;
+
+ log_debug("/* %s(path=%s, root=%s) */", __func__, path, strnull(root));
+
+ assert_se(chase(path, root, CHASE_EXTRACT_FILENAME, &ret1, NULL) > 0);
+ assert_se(streq(ret1, expected));
+
+ assert_se(chase(path, root, 0, &ret2, NULL) > 0);
+ assert_se(chase_extract_filename(ret2, root, &fname) >= 0);
+ assert_se(streq(fname, expected));
+}
+
TEST(chase) {
_cleanup_free_ char *result = NULL, *pwd = NULL;
_cleanup_close_ int pfd = -EBADF;
assert_se(path_equal(result, temp));
result = mfree(result);
+ /* Tests for CHASE_EXTRACT_FILENAME and chase_extract_filename() */
+
+ p = strjoina(temp, "/start");
+ pslash = strjoina(p, "/");
+ test_chase_extract_filename_one(p, NULL, "usr");
+ test_chase_extract_filename_one(pslash, NULL, "usr");
+ test_chase_extract_filename_one(p, temp, "usr");
+ test_chase_extract_filename_one(pslash, temp, "usr");
+
+ p = strjoina(temp, "/slash");
+ test_chase_extract_filename_one(p, NULL, ".");
+ test_chase_extract_filename_one(p, temp, ".");
+
/* Paths that would "escape" outside of the "root" */
p = strjoina(temp, "/6dots");
assert_se(chaseat(tfd, "i/../p", CHASE_MKDIR_0755|CHASE_NONEXISTENT, NULL, NULL) == -ENOENT);
- /* Test CHASE_FILENAME */
+ /* Test CHASE_EXTRACT_FILENAME */
assert_se(chaseat(tfd, "chase/parent", CHASE_AT_RESOLVE_IN_ROOT|CHASE_PARENT|CHASE_NOFOLLOW|CHASE_EXTRACT_FILENAME, &result, &fd) >= 0);
assert_se(faccessat(fd, result, F_OK, AT_SYMLINK_NOFOLLOW) >= 0);
assert_se(streq(result, "."));
result = mfree(result);
+ assert_se(chaseat(tfd, NULL, CHASE_PARENT|CHASE_AT_RESOLVE_IN_ROOT|CHASE_EXTRACT_FILENAME, &result, NULL) >= 0);
+ assert_se(streq(result, "."));
+ result = mfree(result);
+
/* Test chase_and_openat() */
fd = chase_and_openat(tfd, "o/p/e/n/f/i/l/e", CHASE_MKDIR_0755, O_CREAT|O_EXCL|O_CLOEXEC, NULL);
assert_se(fd_verify_directory(fd) >= 0);
fd = safe_close(fd);
+ fd = chase_and_openat(tfd, NULL, CHASE_PARENT|CHASE_EXTRACT_FILENAME, O_PATH|O_DIRECTORY|O_CLOEXEC, &result);
+ assert_se(fd >= 0);
+ assert_se(streq(result, "."));
+ fd = safe_close(fd);
+ result = mfree(result);
+
/* Test chase_and_openatdir() */
assert_se(chase_and_opendirat(tfd, "o/p/e/n/d/i", 0, &result, &dir) >= 0);
result = mfree(result);
}
-static int intro(void) {
- arg_test_dir = saved_argv[1];
- return EXIT_SUCCESS;
-}
-
TEST(chaseat_prefix_root) {
_cleanup_free_ char *cwd = NULL, *ret = NULL, *expected = NULL;
assert_se(streq(ret, expected));
}
+static int intro(void) {
+ arg_test_dir = saved_argv[1];
+ return EXIT_SUCCESS;
+}
+
DEFINE_TEST_MAIN_WITH_INTRO(LOG_INFO, intro);
"e \\\n" \
"f \n" \
"g=g\\ \n" \
- "h= ąęół\\ śćńźżµ \n" \
+ "h= ąęół\\ śćńźżμ \n" \
"i=i\\"
#define env_file_2 \
assert_se(streq(data[1], "b=bc"));
assert_se(streq(data[2], "d=de f"));
assert_se(streq(data[3], "g=g "));
- assert_se(streq(data[4], "h=ąęół śćńźżµ"));
+ assert_se(streq(data[4], "h=ąęół śćńźżμ"));
assert_se(streq(data[5], "i=i"));
assert_se(data[6] == NULL);
}
/* Copy unit files to make them accessible even when unprivileged. */
assert_se(get_testdata_dir("test-execute/", &unit_dir) >= 0);
- assert_se(copy_directory(unit_dir, PRIVATE_UNIT_DIR, COPY_MERGE_EMPTY) >= 0);
+ assert_se(copy_directory_at(AT_FDCWD, unit_dir, AT_FDCWD, PRIVATE_UNIT_DIR, COPY_MERGE_EMPTY) >= 0);
/* Prepare credstore like tmpfiles.d/credstore.conf for LoadCredential= tests. */
FOREACH_STRING(p, "/run/credstore", "/run/credstore.encrypted") {
q = now(CLOCK_MONOTONIC) - t;
- log_info("%lf µs each\n", (double) q / iterations);
+ log_info("%lf μs each\n", (double) q / iterations);
}
TEST(id128_at) {
test_path_startswith_one("/foo/bar/barfoo/", "////foo/bar/barfoo/", "/foo/bar/barfoo/", "");
test_path_startswith_one("/foo/bar/barfoo/", "/foo/bar/barfoo", "/foo/bar/barfoo/", "");
+ test_path_startswith_one("/foo/./bar///barfoo/./.", "/foo", "/foo/./", "bar///barfoo/./.");
+ test_path_startswith_one("/foo/./bar///barfoo/./.", "/foo/", "/foo/./", "bar///barfoo/./.");
+ test_path_startswith_one("/foo/./bar///barfoo/./.", "/", "/", "foo/./bar///barfoo/./.");
+ test_path_startswith_one("/foo/./bar///barfoo/./.", "////", "/", "foo/./bar///barfoo/./.");
+ test_path_startswith_one("/foo/./bar///barfoo/./.", "/foo//bar/////barfoo///", "/foo/./bar///barfoo/./.", "");
+ test_path_startswith_one("/foo/./bar///barfoo/./.", "/foo/bar/barfoo////", "/foo/./bar///barfoo/./.", "");
+ test_path_startswith_one("/foo/./bar///barfoo/./.", "/foo/bar///barfoo/", "/foo/./bar///barfoo/./.", "");
+ test_path_startswith_one("/foo/./bar///barfoo/./.", "/foo////bar/barfoo/", "/foo/./bar///barfoo/./.", "");
+ test_path_startswith_one("/foo/./bar///barfoo/./.", "////foo/bar/barfoo/", "/foo/./bar///barfoo/./.", "");
+ test_path_startswith_one("/foo/./bar///barfoo/./.", "/foo/bar/barfoo", "/foo/./bar///barfoo/./.", "");
+
test_path_startswith_one("/foo/bar/barfoo/", "/foo/bar/barfooa/", NULL, NULL);
test_path_startswith_one("/foo/bar/barfoo/", "/foo/bar/barfooa", NULL, NULL);
test_path_startswith_one("/foo/bar/barfoo/", "", NULL, NULL);
(void) getpid();
q = now(CLOCK_MONOTONIC) - t;
- log_info(" glibc getpid(): %lf µs each\n", (double) q / iterations);
+ log_info(" glibc getpid(): %lf μs each\n", (double) q / iterations);
iterations *= 50; /* _cached() is about 50 times faster, so we need more iterations */
(void) getpid_cached();
q = now(CLOCK_MONOTONIC) - t;
- log_info("getpid_cached(): %lf µs each\n", (double) q / iterations);
+ log_info("getpid_cached(): %lf μs each\n", (double) q / iterations);
}
TEST(safe_fork) {
const char *t = startswith(line, "strv3=");
assert_se(t);
-
- char *un;
- assert_se(cunescape(t, 0, &un) >= 0);
- assert_se(strv_consume(&strv2, un) >= 0);
+ assert_se(deserialize_strv(&strv2, t) >= 0);
}
assert_se(strv_equal(strv, strv2));
assert_se(flush_accept(listen_dgram) < 0);
assert_se(flush_accept(listen_seqpacket) < 0);
- assert_se(listen(listen_stream, SOMAXCONN) >= 0);
- assert_se(listen(listen_dgram, SOMAXCONN) < 0);
- assert_se(listen(listen_seqpacket, SOMAXCONN) >= 0);
+ assert_se(listen(listen_stream, SOMAXCONN_DELUXE) >= 0);
+ assert_se(listen(listen_dgram, SOMAXCONN_DELUXE) < 0);
+ assert_se(listen(listen_seqpacket, SOMAXCONN_DELUXE) >= 0);
assert_se(flush_accept(listen_stream) >= 0);
assert_se(flush_accept(listen_dgram) < 0);
assert_se(u == 700 * USEC_PER_MSEC);
assert_se(parse_sec("23us", &u) >= 0);
assert_se(u == 23);
- assert_se(parse_sec("23µs", &u) >= 0);
+ assert_se(parse_sec("23μs", &u) >= 0); /* greek small letter mu */
+ assert_se(u == 23);
+ assert_se(parse_sec("23µs", &u) >= 0); /* micro symbol */
assert_se(u == 23);
assert_se(parse_sec("infinity", &u) >= 0);
assert_se(u == USEC_INFINITY);
assert_se(write(fd, "foobar\n", 7) == 7);
assert_se(touch(d) >= 0);
- assert_se(link_tmpfile(fd, tmp, d, /* replace= */ false) == -EEXIST);
+ assert_se(link_tmpfile(fd, tmp, d, /* flags= */ 0) == -EEXIST);
assert_se(unlink(d) >= 0);
- assert_se(link_tmpfile(fd, tmp, d, /* replace= */ false) >= 0);
+ assert_se(link_tmpfile(fd, tmp, d, /* flags= */ 0) >= 0);
assert_se(read_one_line_file(d, &line) >= 0);
assert_se(streq(line, "foobar"));
assert_se(write(fd, "waumiau\n", 8) == 8);
- assert_se(link_tmpfile(fd, tmp, d, /* replace= */ false) == -EEXIST);
- assert_se(link_tmpfile(fd, tmp, d, /* replace= */ true) >= 0);
+ assert_se(link_tmpfile(fd, tmp, d, /* flags= */ 0) == -EEXIST);
+ assert_se(link_tmpfile(fd, tmp, d, LINK_TMPFILE_REPLACE) >= 0);
line = mfree(line);
assert_se(read_one_line_file(d, &line) >= 0);
expected2, expected2_count);
}
-/* this test includes TPM2 specific data structures */
-TEST(tpm2_get_primary_template) {
-
- /*
- * Verify that if someone changes the template code, they know they're breaking things.
- * Templates MUST be changed in a backwards compatible way.
- *
- */
- static const TPM2B_PUBLIC templ[] = {
- /* index 0 RSA old */
- [0] = {
- .publicArea = {
- .type = TPM2_ALG_RSA,
- .nameAlg = TPM2_ALG_SHA256,
- .objectAttributes = TPMA_OBJECT_RESTRICTED|TPMA_OBJECT_DECRYPT|TPMA_OBJECT_FIXEDTPM|TPMA_OBJECT_FIXEDPARENT|TPMA_OBJECT_SENSITIVEDATAORIGIN|TPMA_OBJECT_USERWITHAUTH,
- .parameters.rsaDetail = {
- .symmetric = {
- .algorithm = TPM2_ALG_AES,
- .keyBits.aes = 128,
- .mode.aes = TPM2_ALG_CFB,
- },
- .scheme.scheme = TPM2_ALG_NULL,
- .keyBits = 2048,
- },
- },
- },
- /* Index 1 ECC old */
- [TPM2_SRK_TEMPLATE_ECC] = {
- .publicArea = {
- .type = TPM2_ALG_ECC,
- .nameAlg = TPM2_ALG_SHA256,
- .objectAttributes = TPMA_OBJECT_RESTRICTED|TPMA_OBJECT_DECRYPT|TPMA_OBJECT_FIXEDTPM|TPMA_OBJECT_FIXEDPARENT|TPMA_OBJECT_SENSITIVEDATAORIGIN|TPMA_OBJECT_USERWITHAUTH,
- .parameters.eccDetail = {
- .symmetric = {
- .algorithm = TPM2_ALG_AES,
- .keyBits.aes = 128,
- .mode.aes = TPM2_ALG_CFB,
- },
- .scheme.scheme = TPM2_ALG_NULL,
- .curveID = TPM2_ECC_NIST_P256,
- .kdf.scheme = TPM2_ALG_NULL,
- },
- },
- },
- /* index 2 RSA SRK */
- [TPM2_SRK_TEMPLATE_NEW_STYLE] = {
- .publicArea = {
- .type = TPM2_ALG_RSA,
- .nameAlg = TPM2_ALG_SHA256,
- .objectAttributes = TPMA_OBJECT_FIXEDTPM|TPMA_OBJECT_FIXEDPARENT|TPMA_OBJECT_SENSITIVEDATAORIGIN|TPMA_OBJECT_RESTRICTED|TPMA_OBJECT_DECRYPT|TPMA_OBJECT_USERWITHAUTH|TPMA_OBJECT_NODA,
- .parameters.rsaDetail = {
- .symmetric = {
- .algorithm = TPM2_ALG_AES,
- .keyBits.aes = 128,
- .mode.aes = TPM2_ALG_CFB,
- },
- .scheme.scheme = TPM2_ALG_NULL,
- .keyBits = 2048,
- },
- },
- },
- /* Index 3 ECC SRK */
- [TPM2_SRK_TEMPLATE_NEW_STYLE | TPM2_SRK_TEMPLATE_ECC] = {
- .publicArea = {
- .type = TPM2_ALG_ECC,
- .nameAlg = TPM2_ALG_SHA256,
- .objectAttributes = TPMA_OBJECT_FIXEDTPM|TPMA_OBJECT_FIXEDPARENT|TPMA_OBJECT_SENSITIVEDATAORIGIN|TPMA_OBJECT_RESTRICTED|TPMA_OBJECT_DECRYPT|TPMA_OBJECT_USERWITHAUTH|TPMA_OBJECT_NODA,
- .parameters.eccDetail = {
- .symmetric = {
- .algorithm = TPM2_ALG_AES,
- .keyBits.aes = 128,
- .mode.aes = TPM2_ALG_CFB,
- },
- .scheme.scheme = TPM2_ALG_NULL,
- .curveID = TPM2_ECC_NIST_P256,
- .kdf.scheme = TPM2_ALG_NULL,
- },
- },
- },
- };
-
- assert_cc(ELEMENTSOF(templ) == _TPM2_SRK_TEMPLATE_MAX + 1);
-
- for (size_t i = 0; i < ELEMENTSOF(templ); i++) {
- /* the index counter lines up with the flags and the expected template received */
- const TPM2B_PUBLIC *got = tpm2_get_primary_template((Tpm2SRKTemplateFlags)i);
- assert_se(memcmp(&templ[i], got, sizeof(*got)) == 0);
- }
-}
-
static bool digest_check(const TPM2B_DIGEST *digest, const char *expect) {
_cleanup_free_ char *h = NULL;
assert_se(digest_check(&d, "7481fd1b116078eb3ac2456e4ad542c9b46b9b8eb891335771ca8e7c8f8e4415"));
}
+TEST(tpm_required_tests) {
+ int r;
+
+ _cleanup_(tpm2_context_unrefp) Tpm2Context *c = NULL;
+ r = tpm2_context_new(NULL, &c);
+ if (r < 0) {
+ log_tests_skipped("Could not find TPM");
+ return;
+ }
+
+ TPMU_PUBLIC_PARMS parms = {
+ .symDetail.sym = {
+ .algorithm = TPM2_ALG_AES,
+ .keyBits.aes = 128,
+ .mode.aes = TPM2_ALG_CFB,
+ },
+ };
+
+ /* Test with invalid parms */
+ assert_se(!tpm2_test_parms(c, TPM2_ALG_CFB, &parms));
+
+ TPMU_PUBLIC_PARMS invalid_parms = parms;
+ invalid_parms.symDetail.sym.keyBits.aes = 1;
+ assert_se(!tpm2_test_parms(c, TPM2_ALG_SYMCIPHER, &invalid_parms));
+
+ /* Test with valid parms */
+ assert_se(tpm2_test_parms(c, TPM2_ALG_SYMCIPHER, &parms));
+
+ /* Test invalid algs */
+ assert_se(tpm2_supports_alg(c, TPM2_ALG_ERROR) == 0);
+ assert_se(tpm2_supports_alg(c, TPM2_ALG_LAST + 1) == 0);
+
+ /* Test valid algs */
+ assert_se(tpm2_supports_alg(c, TPM2_ALG_RSA) == 1);
+ assert_se(tpm2_supports_alg(c, TPM2_ALG_AES) == 1);
+ assert_se(tpm2_supports_alg(c, TPM2_ALG_CFB) == 1);
+}
+
#endif /* HAVE_TPM2 */
DEFINE_TEST_MAIN(LOG_DEBUG);
"MESSAGE_ID=" SD_MESSAGE_TIME_SYNC_STR,
"MONOTONIC_USEC=" USEC_FMT, dts.monotonic,
"REALTIME_USEC=" USEC_FMT, dts.realtime,
- "BOOTIME_USEC=" USEC_FMT, dts.boottime);
+ "BOOTTIME_USEC=" USEC_FMT, dts.boottime);
}
r = manager_arm_timer(m, m->poll_interval_usec);
* different timestamp accuracy: traditional fat has 2s granularity, and even ext2 and friends expose
* different granularity depending on selected inode size during formatting! Hence, to ensure the
* timestamp definitely is increased, here's what we'll do: we'll first try to increase the timestamp
- * by 1µs, write that and read it back. If it was updated, great. But if it was not, we'll instead
- * increase the timestamp by 10µs, and do the same, then 100µs, then 1ms, and so on, until it works,
+ * by 1μs, write that and read it back. If it was updated, great. But if it was not, we'll instead
+ * increase the timestamp by 10μs, and do the same, then 100μs, then 1ms, and so on, until it works,
* or we reach 10s. If it still didn't work then, the fs is just broken and we give up. */
usec_t target = MAX3(now(CLOCK_REALTIME),
TIME_EPOCH * USEC_PER_SEC,
timespec_load(&st->st_mtim));
- for (usec_t a = 1; a <= 10 * USEC_PER_SEC; a *= 10) { /* 1µs, 10µs, 100µs, 1ms, … 10s */
+ for (usec_t a = 1; a <= 10 * USEC_PER_SEC; a *= 10) { /* 1μs, 10μs, 100μs, 1ms, … 10s */
struct timespec ts[2];
struct stat new_st;
return log_error_errno(procfs_fd, "Failed to re-open '%s': %m", path);
unsigned previous, current;
- r = chattr_full(NULL, procfs_fd, f, item->attribute_mask, &previous, ¤t, CHATTR_FALLBACK_BITWISE);
+ r = chattr_full(procfs_fd, NULL, f, item->attribute_mask, &previous, ¤t, CHATTR_FALLBACK_BITWISE);
if (r == -ENOANO)
log_warning("Cannot set file attributes for '%s', maybe due to incompatibility in specified attributes, "
"previous=0x%08x, current=0x%08x, expected=0x%08x, ignoring.",
install_dir : sysconfdir / 'udev')
endif
-custom_target(
+udev_pc = custom_target(
'udev.pc',
input : 'udev.pc.in',
output : 'udev.pc',
command : [jinja2_cmdline, '@INPUT@', '@OUTPUT@'],
install : pkgconfigdatadir != 'no',
+ install_tag : 'devel',
install_dir : pkgconfigdatadir)
if install_sysconfdir
return 0;
}
+static int node_get_current(const char *slink, int dirfd, char **ret_id, int *ret_prio) {
+ _cleanup_(sd_device_unrefp) sd_device *dev = NULL;
+ _cleanup_free_ char *id_dup = NULL;
+ const char *id;
+ int r;
+
+ assert(slink);
+ assert(dirfd >= 0);
+ assert(ret_id);
+
+ r = sd_device_new_from_devname(&dev, slink);
+ if (r < 0)
+ return r;
+
+ r = device_get_device_id(dev, &id);
+ if (r < 0)
+ return r;
+
+ id_dup = strdup(id);
+ if (!id_dup)
+ return -ENOMEM;
+
+ if (ret_prio) {
+ r = stack_directory_read_one(dirfd, id, NULL, ret_prio);
+ if (r < 0)
+ return r;
+ }
+
+ *ret_id = TAKE_PTR(id_dup);
+ return 0;
+}
+
static int link_update(sd_device *dev, const char *slink, bool add) {
+ _cleanup_free_ char *current_id = NULL, *devnode = NULL;
_cleanup_close_ int dirfd = -EBADF, lockfd = -EBADF;
- _cleanup_free_ char *devnode = NULL;
- int r;
+ int r, current_prio;
assert(dev);
assert(slink);
if (r < 0)
return r;
+ r = node_get_current(slink, dirfd, ¤t_id, add ? ¤t_prio : NULL);
+ if (r < 0 && !ERRNO_IS_DEVICE_ABSENT(r))
+ return log_device_debug_errno(dev, r, "Failed to get the current device node priority for '%s': %m", slink);
+
r = stack_directory_update(dev, dirfd, add);
if (r < 0)
return log_device_debug_errno(dev, r, "Failed to update stack directory for '%s': %m", slink);
+ if (current_id) {
+ const char *id;
+
+ r = device_get_device_id(dev, &id);
+ if (r < 0)
+ return log_device_debug_errno(dev, r, "Failed to get device id: %m");
+
+ if (add) {
+ int prio;
+
+ r = device_get_devlink_priority(dev, &prio);
+ if (r < 0)
+ return log_device_debug_errno(dev, r, "Failed to get devlink priority: %m");
+
+ if (streq(current_id, id)) {
+ if (current_prio <= prio)
+ /* The devlink is ours and already exists, and the new priority is
+ * equal or higher than the previous. Hence, it is not necessary to
+ * recreate it. */
+ return 0;
+
+ /* The devlink priority is downgraded. Another device may have a higher
+ * priority now. Let's find the device node with the highest priority. */
+ } else {
+ if (current_prio >= prio)
+ /* The devlink with equal or higher priority already exists and is
+ * owned by another device. Hence, it is not necessary to recreate it. */
+ return 0;
+
+ /* This device has a higher priority than the current. Let's create the
+ * devlink to our device node. */
+ return node_symlink(dev, NULL, slink);
+ }
+
+ } else {
+ if (!streq(current_id, id))
+ /* The devlink already exists and is owned by another device. Hence, it is
+ * not necessary to recreate it. */
+ return 0;
+
+ /* The current devlink is ours, and the target device will be removed. Hence, we need
+ * to search the device that has the highest priority. and update the devlink. */
+ }
+ } else {
+ /* The requested devlink does not exist, or the target device does not exist and the devlink
+ * points to a non-existing device. Let's search the deivce that has the highest priority,
+ * and update the devlink. */
+ ;
+ }
+
r = stack_directory_find_prioritized_devnode(dev, dirfd, add, &devnode);
if (r < 0)
return log_device_debug_errno(dev, r, "Failed to determine device node with the highest priority for '%s': %m", slink);
r = safe_atou(optarg, &i);
if (r < 0)
- return log_error_errno(r, "Failed to parse maximum number of events '%s': %m", optarg);
+ return log_error_errno(r, "Failed to parse maximum number of children '%s': %m", optarg);
r = udev_ctrl_send_set_children_max(uctrl, i);
if (r == -ENOANO)
if (r < 0)
return log_error_errno(r, "Failed to bind io.systemd.Multiplexer: %m");
- if (listen(m->listen_fd, SOMAXCONN) < 0)
+ if (listen(m->listen_fd, SOMAXCONN_DELUXE) < 0)
return log_error_errno(errno, "Failed to listen on socket: %m");
}
"ExitType=cgroup\n"
"ExecStart=:%s\n"
"Restart=no\n"
- "TimeoutSec=5s\n"
+ "TimeoutStopSec=5s\n"
"Slice=app.slice\n",
exec_start);
# SPDX-License-Identifier: LGPL-2.1-or-later
set -e
+# shellcheck source=test/test-functions
+. "${TEST_BASE_DIR:?}/test-functions"
+
TEST_DESCRIPTION="Run unit tests under containers"
RUN_IN_UNPRIVILEGED_CONTAINER=yes
# Some tests make collecting coverage impossible (like test-mount-util, which
# case
IGNORE_MISSING_COVERAGE=yes
-# embed some newlines in the kernel command line to stress our test suite
+# Embed some newlines in the kernel command line to stress our test suite
+# Also, pass $TEST_PREFER_NSPAWN to the VM/container if set
+#
+# shellcheck disable=SC2015
KERNEL_APPEND="
+$(get_bool "${TEST_PREFER_NSPAWN:-0}" && echo "systemd.setenv=TEST_PREFER_NSPAWN=1" || :)
frobnicate!
+systemd.setenv=TEST_CMDLINE_NEWLINE=foo
+systemd.setenv=TEST_CMDLINE_NEWLINE=bar
+
$KERNEL_APPEND
"
-
-# shellcheck source=test/test-functions
-. "${TEST_BASE_DIR:?}/test-functions"
+# Override $TEST_PREFER_NSPAWN if it was set to always run both the QEMU and
+# the nspawn part of the test
+TEST_PREFER_NSPAWN=no
test_append_files() {
if get_bool "$LOOKS_LIKE_SUSE"; then
fi
mkdir "$workspace/systemd-test-module"
- cp systemd_test.te "$workspace/systemd-test-module"
- cp systemd_test.if "$workspace/systemd-test-module"
- cp systemd_test.fc "$workspace/systemd-test-module"
+ cp -v systemd_test.* "$workspace/systemd-test-module/"
+ image_install checkmodule load_policy m4 make sefcontext_compile semodule semodule_package runcon
image_install -o sesearch
- image_install runcon
- image_install checkmodule semodule semodule_package m4 make load_policy sefcontext_compile
image_install -o /usr/libexec/selinux/hll/pp # Fedora/RHEL/...
image_install -o /usr/lib/selinux/hll/pp # Debian/Ubuntu/...
- if ! chroot "$workspace" make -C /systemd-test-module -f /usr/share/selinux/devel/Makefile clean systemd_test.pp; then
+ if ! chroot "$workspace" make -C /systemd-test-module -f /usr/share/selinux/devel/Makefile clean load systemd_test.pp QUIET=n; then
dfatal "Failed to build the systemd test module"
exit 1
fi
set -e
TEST_DESCRIPTION="EXTEND_TIMEOUT_USEC=usec start/runtime/stop tests"
-SKIP_INITRD=yes
TEST_NO_QEMU=1
# shellcheck source=test/test-functions
TEST_NO_NSPAWN=1
fi
-command -v dfuzzer >/dev/null || exit 0
+test_require_bin dfuzzer
if ! get_bool "$IS_BUILT_WITH_ASAN"; then
echo "systemd is built without ASan, skipping..."
TEST_DESCRIPTION="cryptsetup systemd setup"
IMAGE_NAME="cryptsetup"
+IMAGE_ADDITIONAL_DATA_SIZE=100
TEST_NO_NSPAWN=1
TEST_FORCE_NEWIMAGE=1
PART_UUID="deadbeef-dead-dead-beef-000000000000"
DM_NAME="test24_varcrypt"
-KERNEL_APPEND+=" rd.luks=1 luks.name=$PART_UUID=$DM_NAME luks.key=$PART_UUID=/keyfile:LABEL=varcrypt_keydev"
+KERNEL_OPTIONS=(
+ "rd.luks=1"
+ "luks.name=$PART_UUID=$DM_NAME"
+ "luks.key=$PART_UUID=/keyfile:LABEL=varcrypt_keydev"
+ "luks.options=$PART_UUID=x-initrd.attach"
+)
+KERNEL_APPEND+=" ${KERNEL_OPTIONS[*]}"
QEMU_OPTIONS+=" -drive format=raw,cache=unsafe,file=${STATEDIR:?}/keydev.img"
check_result_qemu() {
- local ret=1
+ local ret
mount_initdir
- [[ -e "${initdir:?}/testok" ]] && ret=0
- [[ -f "$initdir/failed" ]] && cp -a "$initdir/failed" "${TESTDIR:?}"
cryptsetup luksOpen "${LOOPDEV:?}p2" "${DM_NAME:?}" <"$TESTDIR/keyfile"
mount "/dev/mapper/$DM_NAME" "$initdir/var"
- save_journal "$initdir/var/log/journal"
- check_coverage_reports "${initdir:?}" || ret=5
+
+ check_result_common "${initdir:?}" && ret=0 || ret=$?
+
_umount_dir "$initdir/var"
_umount_dir "$initdir"
cryptsetup luksClose "/dev/mapper/$DM_NAME"
- [[ -f "$TESTDIR/failed" ]] && cat "$TESTDIR/failed"
- echo "${JOURNAL_LIST:-No journals were saved}"
-
- test -s "$TESTDIR/failed" && ret=1
return $ret
}
mkdir -p "$STATEDIR/keydev"
mount "$STATEDIR/keydev.img" "$STATEDIR/keydev"
echo -n test >"$STATEDIR/keydev/keyfile"
+ sync "$STATEDIR/keydev"
umount "$STATEDIR/keydev"
cat >>"$initdir/etc/fstab" <<EOF
}
cleanup_root_var() {
- ddebug "umount ${initdir:?}/var"
- mountpoint "$initdir/var" && umount "$initdir/var"
+ mountpoint -q "$initdir/var" && umount "$initdir/var"
[[ -b "/dev/mapper/${DM_NAME:?}" ]] && cryptsetup luksClose "/dev/mapper/$DM_NAME"
+ mountpoint -q "${STATEDIR:?}/keydev" && umount "$STATEDIR/keydev"
}
test_cleanup() {
. "${TEST_BASE_DIR:?}/test-functions"
has_user_dbus_socket || exit 0
-command -v mksquashfs >/dev/null 2>&1 || exit 0
+test_require_bin mksquashfs
test_append_files() {
inst_binary unsquashfs
. "${TEST_BASE_DIR:?}/test-functions"
get_bool "${NO_BUILD:-}" && HOMECTL_BIN="homectl" || HOMECTL_BIN="${BUILD_DIR:?}/homectl"
-if ! command -v "$HOMECTL_BIN" >/dev/null; then
- echo "Built without systemd-homed, skipping the test"
- exit 0
-fi
+test_require_bin "$HOMECTL_BIN"
# Need loop devices for mounting images
test_append_files() {
# shellcheck source=test/test-functions
. "${TEST_BASE_DIR:?}/test-functions"
-command -v mksquashfs >/dev/null 2>&1 || exit 0
-command -v veritysetup >/dev/null 2>&1 || exit 0
-command -v sfdisk >/dev/null 2>&1 || exit 0
+test_require_bin mksquashfs veritysetup sfdisk
test_append_files() {
instmods squashfs =squashfs
TEST_DESCRIPTION="test systemd-repart"
IMAGE_NAME="repart"
+IMAGE_ADDITIONAL_ROOT_SIZE=1000
TEST_FORCE_NEWIMAGE=1
# shellcheck source=test/test-functions
+++ /dev/null
-../TEST-01-BASIC/Makefile
\ No newline at end of file
+++ /dev/null
-#!/usr/bin/env bash
-# SPDX-License-Identifier: LGPL-2.1-or-later
-set -e
-
-TEST_DESCRIPTION="Run unit tests under qemu"
-# this subset of unit tests requires qemu, so they are ran here to avoid slowing down TEST-02
-TEST_NO_NSPAWN=1
-
-# embed some newlines in the kernel command line to stress our test suite
-KERNEL_APPEND="
-
-frobnicate!
-
-$KERNEL_APPEND
-"
-
-# shellcheck source=test/test-functions
-. "${TEST_BASE_DIR:?}/test-functions"
-
-check_result_nspawn() {
- check_result_nspawn_unittests "${1}"
-}
-
-check_result_qemu() {
- check_result_qemu_unittests
-}
-
-do_test "$@"
TEST_DESCRIPTION="cryptenroll/cryptsetup with TPM2 devices"
IMAGE_NAME="tpm2"
TEST_NO_NSPAWN=1
+TEST_SETUP_SWTPM=1
TEST_REQUIRE_INSTALL_TESTS=0
# shellcheck source=test/test-functions
. "${TEST_BASE_DIR:?}/test-functions"
-command -v swtpm >/dev/null 2>&1 || exit 0
-command -v tpm2_pcrextend >/dev/null 2>&1 || exit 0
+test_require_bin swtpm tpm2_pcrextend
test_append_files() {
- local workspace="${1:?}"
-
- instmods tpm tpm_tis tpm_ibmvtpm
- install_dmevent
- generate_module_dependencies
- inst_binary tpm2_pcrextend
- inst_binary tpm2_pcrread
- inst_binary openssl
+ local workspace="${1:?}"
+
+ instmods tpm tpm_tis tpm_ibmvtpm
+ install_dmevent
+ generate_module_dependencies
+ inst_binary tpm2_pcrextend
+ inst_binary tpm2_pcrread
+ inst_binary openssl
}
-TEST_70_TPM_DEVICE="tpm-tis"
-if [[ "$(uname -m)" == "ppc64le" ]]; then
- # tpm-spapr support was introduced in qemu 5.0.0. Skip test for old qemu versions.
- qemu_min_version "5.0.0" || exit 0
- TEST_70_TPM_DEVICE="tpm-spapr"
-fi
-
-TEST_70_at_exit() {
- [[ -n "${TEST_70_SWTPM_PID:-}" ]] && kill "$TEST_70_SWTPM_PID" &>/dev/null
- [[ -n "${TEST_70_TPM_STATE:-}" ]] && rm -rf "$TEST_70_TPM_STATE"
-}
-
-TEST_70_TPM_STATE="$(mktemp -d)"
-swtpm socket --tpm2 --tpmstate dir="$TEST_70_TPM_STATE" --ctrl type=unixio,path="$TEST_70_TPM_STATE/sock" &
-TEST_70_SWTPM_PID=$!
-add_at_exit_handler TEST_70_at_exit
-QEMU_OPTIONS+=" -chardev socket,id=chrtpm,path=$TEST_70_TPM_STATE/sock -tpmdev emulator,id=tpm0,chardev=chrtpm -device $TEST_70_TPM_DEVICE,tpmdev=tpm0"
-
do_test "$@"
# shellcheck source=test/test-functions
. "${TEST_BASE_DIR:?}/test-functions"
-if ! command -v knotd >/dev/null; then
- echo "This test requires Knot DNS server, skipping..."
- exit 0
-fi
+test_require_bin knotd
# We need at least Knot 3.0 which support (among others) the ds-push directive
if ! knotc -c "${TEST_BASE_DIR:?}/knot-data/knot.conf" conf-check; then
err=$("$SYSTEMD_HWDB" update --root "$D" 2>&1 >/dev/null) && rc= || rc=$?
if [ -n "$err" ]; then
echo "$err"
- exit ${rc:-1}
+ exit "${rc:-1}"
fi
if [ -n "$rc" ]; then
echo "$SYSTEMD_HWDB returned $rc"
- exit $rc
+ exit "$rc"
fi
if [ ! -e "$D/etc/udev/hwdb.bin" ]; then
err=$("$SYSTEMD_HWDB" update --root "$D" 2>&1 >/dev/null) && rc= || rc=$?
if [ -n "$rc" ]; then
echo "$SYSTEMD_HWDB returned $rc"
- exit $rc
+ exit "$rc"
fi
if [ -n "$err" ]; then
echo "Expected warnings"
ExecStart=test -w %S/xxx
ExecStart=test -w %S/xxx/yyy
ExecStart=test -w %S/xxx/zzz
+ExecStart=test -w %S/abc
ExecStart=test -w %S/aaa/111
ExecStart=test -w %S/aaa/222
ExecStart=test -w %S/aaa/333
+ExecStart=test -w %S/d:ef
ExecStart=test -d %S/waldo
ExecStart=test -d %S/quux/pief
ExecStart=test -d %S/xxx
ExecStart=test -d %S/xxx/yyy
ExecStart=test -d %S/xxx/zzz
+ExecStart=test -d %S/abc
ExecStart=test -L %S/aaa/111
ExecStart=test -L %S/aaa/222
ExecStart=test -L %S/aaa/333
+ExecStart=test -L %S/d:ef
ExecStart=touch %S/waldo/hoge
ExecStart=touch %S/quux/pief/hoge
ExecStart=touch %S/xxx/hoge
ExecStart=touch %S/xxx/yyy/hoge
ExecStart=touch %S/xxx/zzz/hoge
+ExecStart=touch %S/abc/hoge
ExecStart=touch %S/aaa/111/foo
ExecStart=touch %S/aaa/222/foo
ExecStart=touch %S/aaa/333/foo
+ExecStart=touch %S/d:ef/foo
ExecStart=test -f %S/waldo/hoge
ExecStart=test -f %S/quux/pief/hoge
ExecStart=test -f %S/xxx/hoge
ExecStart=test -f %S/xxx/yyy/hoge
ExecStart=test -f %S/xxx/zzz/hoge
+ExecStart=test -f %S/abc/hoge
ExecStart=test -f %S/aaa/111/foo
ExecStart=test -f %S/aaa/222/foo
ExecStart=test -f %S/aaa/333/foo
ExecStart=test -f %S/xxx/foo
ExecStart=test -f %S/xxx/yyy/foo
ExecStart=test -f %S/xxx/zzz/foo
+ExecStart=test -f %S/d:ef/foo
ExecStart=test -f %S/private/waldo/hoge
ExecStart=test -f %S/private/quux/pief/hoge
ExecStart=test -f %S/private/xxx/hoge
ExecStart=test -f %S/private/xxx/yyy/hoge
ExecStart=test -f %S/private/xxx/zzz/hoge
+ExecStart=test -f %S/private/abc/hoge
ExecStart=test -f %S/private/aaa/111/foo
ExecStart=test -f %S/private/aaa/222/foo
ExecStart=test -f %S/private/aaa/333/foo
ExecStart=test -f %S/private/xxx/yyy/foo
ExecStart=test -f %S/private/xxx/zzz/foo
-ExecStart=sh -x -c 'test "$$STATE_DIRECTORY" = "%S/aaa:%S/aaa/bbb:%S/aaa/ccc:%S/quux/pief:%S/waldo:%S/xxx:%S/xxx/yyy:%S/xxx/zzz"'
+ExecStart=sh -x -c 'test "$$STATE_DIRECTORY" = "%S/aaa:%S/aaa/bbb:%S/aaa/ccc:%S/abc:%S/quux/pief:%S/waldo:%S/xxx:%S/xxx/yyy:%S/xxx/zzz"'
Type=oneshot
DynamicUser=yes
-StateDirectory=waldo quux/pief aaa/bbb aaa aaa/ccc xxx/yyy:aaa/111 xxx:aaa/222 xxx/zzz:aaa/333
+StateDirectory=waldo quux/pief aaa/bbb aaa aaa/ccc xxx/yyy:aaa/111 xxx:aaa/222 xxx/zzz:aaa/333 abc:d\:ef
Type=oneshot
RemainAfterExit=yes
ExecStart=/usr/lib/systemd/systemd-fsck /dev/sdx1
-TimeoutSec=0
+TimeoutSec=infinity
Type=oneshot
RemainAfterExit=yes
ExecStart=/usr/lib/systemd/systemd-fsck /dev/disk/by-label/Root
-TimeoutSec=0
+TimeoutSec=infinity
Type=oneshot
RemainAfterExit=yes
ExecStart=/usr/lib/systemd/systemd-fsck /dev/disk/by-uuid/3f5ad593-4546-4a94-a374-bcfb68aa11f7
-TimeoutSec=0
+TimeoutSec=infinity
Type=oneshot
RemainAfterExit=yes
ExecStart=/usr/lib/systemd/systemd-fsck /dev/disk/by-partuuid/3f5ad593-4546-4a94-a374-bcfb68aa11f7
-TimeoutSec=0
+TimeoutSec=infinity
Type=oneshot
RemainAfterExit=yes
ExecStart=/usr/lib/systemd/systemd-fsck /dev/sdx1
-TimeoutSec=0
+TimeoutSec=infinity
Type=oneshot
RemainAfterExit=yes
ExecStart=/usr/lib/systemd/systemd-fsck /dev/sdx1
-TimeoutSec=0
+TimeoutSec=infinity
Type=oneshot
RemainAfterExit=yes
ExecStart=/usr/lib/systemd/systemd-makefs ext4 /dev/sdx12
-TimeoutSec=0
+TimeoutSec=infinity
Type=oneshot
RemainAfterExit=yes
ExecStart=/usr/lib/systemd/systemd-makefs swap /dev/sdy2
-TimeoutSec=0
+TimeoutSec=infinity
Type=oneshot
RemainAfterExit=yes
ExecStart=/usr/lib/systemd/systemd-makefs swap /dev/sdy3
-TimeoutSec=0
+TimeoutSec=infinity
# Run the EXIT handlers in reverse order
for ((i = ${#_AT_EXIT_HANDLERS[@]} - 1; i >= 0; i--)); do
ddebug "Running EXIT handler '${_AT_EXIT_HANDLERS[$i]}'"
- "${_AT_EXIT_HANDLERS[$i]}"
+ eval "${_AT_EXIT_HANDLERS[$i]}"
done
}
trap _at_exit EXIT
add_at_exit_handler() {
- local handler="${1?}"
-
- if [[ "$(type -t "$handler")" != "function" ]]; then
- dfatal "'$handler' is not a function"
- exit 1
- fi
-
- _AT_EXIT_HANDLERS+=("$handler")
+ _AT_EXIT_HANDLERS+=("${1:?}")
}
# Decide if we can (and want to) run qemu with KVM acceleration.
if get_bool "$IS_BUILT_WITH_ASAN"; then
PATH_TO_INIT="$ROOTLIBDIR/systemd-under-asan"
- SKIP_INITRD="${SKIP_INITRD:-yes}"
QEMU_MEM="${QEMU_MEM:-2G}"
QEMU_SMP="${QEMU_SMP:-4}"
echo "Detected ASan RT '$ASAN_RT_NAME' located at '$ASAN_RT_PATH'"
fi
+test_require_bin() {
+ local bin
+
+ for bin in "$@"; do
+ if ! command -v "$bin" >/dev/null; then
+ echo "Required binary $bin not available, skipping the test"
+ exit 0
+ fi
+ done
+}
+
find_qemu_bin() {
QEMU_BIN="${QEMU_BIN:-""}"
# SUSE and Red Hat call the binary qemu-kvm. Debian and Gentoo call it kvm.
fi
}
+qemu_setup_swtpm_socket() {
+ local pid state_dir tpm_device
+
+ if ! tpm_device="$(qemu_get_tpm_device)"; then
+ dinfo "Found QEMU version is too old for TPM2 on ppc64le"
+ exit 0
+ fi
+
+ state_dir="$(mktemp -d)"
+ swtpm socket --tpm2 --tpmstate dir="$state_dir" --ctrl type=unixio,path="$state_dir/sock" &
+ pid=$!
+ if ! kill -0 "$pid"; then
+ echo >&2 "Failed to setup swtpm socket"
+ return 1
+ fi
+
+ dinfo "Started swtpm as PID $pid with state dir $state_dir"
+
+ add_at_exit_handler "kill -TERM $pid 2>/dev/null; rm -rf '$state_dir'"
+
+ QEMU_OPTIONS+=" -chardev socket,id=chrtpm,path=$state_dir/sock -tpmdev emulator,id=tpm0,chardev=chrtpm -device $tpm_device,tpmdev=tpm0"
+ dinfo "Configured emulated TPM2 device $tpm_device"
+
+ return 0
+}
+
+qemu_get_tpm_device() {
+ local tpm_device="tpm-tis"
+
+ if [[ "$(uname -m)" == "ppc64le" ]]; then
+ # tpm-spapr support was introduced in qemu 5.0.0
+ if ! qemu_min_version "5.0.0"; then
+ return 1
+ fi
+
+ tpm_device="tpm-spapr"
+ fi
+
+ echo "$tpm_device"
+ return 0
+}
+
# Compares argument #1=X.Y.Z (X&Y&Z = numeric) to the version of the installed qemu
# returns 0 if newer or equal
# returns 1 if older
find_qemu_bin || return 1
+ if get_bool "${TEST_SETUP_SWTPM:-}"; then
+ qemu_setup_swtpm_socket || return 1
+ fi
+
# Umount initdir to avoid concurrent access to the filesystem
_umount_dir "$initdir"
kernel_params+=("${user_kernel_append[@]}")
fi
- if [[ "$INITRD" ]] && ! get_bool "$SKIP_INITRD"; then
+ if [[ -n "$INITRD" ]]; then
qemu_options+=(-initrd "$INITRD")
fi
fi
local conf_dir=/etc/selinux
- local fixfiles_tools=(bash uname cat sort uniq awk grep egrep head expr find rm secon setfiles)
+ local fixfiles_tools=(awk bash cat chcon expr egrep find grep head secon setfiles rm sort uname uniq)
# Make sure the following statement can't expand to "/" to prevent
# a potential where-are-my-backups situation
exit 1
fi
+ # We use a custom autorelabel service instead of the SELinux provided set
+ # of units & a generator, since the generator overrides the default target
+ # to the SELinux one when it detects /.autorelabel. However, we use
+ # systemd.unit= on the kernel command cmdline which always takes precedence,
+ # rendering all SELinux efforts useless. Also, pulling in selinux-autorelabel.service
+ # explicitly doesn't work either, as it doesn't check for the presence of /.autorelabel
+ # and does the relabeling unconditionally which always ends with a reboot, so
+ # we end up in a reboot loop (and it also spews quite a lot of errors as it
+ # wants /etc/fstab and dracut-initramfs-restore).
+
touch "$initdir/.autorelabel"
mkdir -p "$initdir/usr/lib/systemd/tests/testdata/units/basic.target.wants"
ln -sf ../autorelabel.service "$initdir/usr/lib/systemd/tests/testdata/units/basic.target.wants/"
# runtime ASan DSO is in a non-standard (library) path.
mkdir -p "${initdir:?}/etc/ld.so.conf.d/"
echo "${ASAN_RT_PATH%/*}" >"${initdir:?}/etc/ld.so.conf.d/asan-path-override.conf"
+ ldconfig -r "$initdir"
fi
# Create a simple environment file which can be included by systemd services
add_at_exit_handler cleanup_loopdev
create_empty_image() {
- if [ -z "${IMAGE_NAME:=}" ]; then
+ if [[ -z "${IMAGE_NAME:=}" ]]; then
echo "create_empty_image: \$IMAGE_NAME not set"
exit 1
fi
local data_size=100
if ! get_bool "$NO_BUILD"; then
if meson configure "${BUILD_DIR:?}" | grep 'static-lib\|standalone-binaries' | awk '{ print $2 }' | grep -q 'true'; then
- root_size=$((root_size+=200))
+ root_size=$((root_size + 200))
fi
if meson configure "${BUILD_DIR:?}" | grep 'link-.*-shared' | awk '{ print $2 }' | grep -q 'false'; then
- root_size=$((root_size+=200))
+ root_size=$((root_size + 200))
fi
if get_bool "$IS_BUILT_WITH_COVERAGE"; then
- root_size=$((root_size+=250))
+ root_size=$((root_size + 250))
fi
if get_bool "$IS_BUILT_WITH_ASAN"; then
root_size=$((root_size * 2))
fi
fi
- if [ "$IMAGE_NAME" = "repart" ]; then
- root_size=$((root_size+=1000))
+ if [[ "${IMAGE_ADDITIONAL_ROOT_SIZE:-0}" -gt 0 ]]; then
+ root_size=$((root_size + IMAGE_ADDITIONAL_ROOT_SIZE))
+ fi
+ if [[ "${IMAGE_ADDITIONAL_DATA_SIZE:-0}" -gt 0 ]]; then
+ data_size=$((data_size + IMAGE_ADDITIONAL_DATA_SIZE))
fi
echo "Setting up ${IMAGE_PUBLIC:?} (${root_size} MB)"
# Create the blank file to use as a root filesystem
truncate -s "${root_size}M" "$IMAGE_PUBLIC"
- LOOPDEV=$(losetup --show -P -f "$IMAGE_PUBLIC")
- [ -b "$LOOPDEV" ] || return 1
+ LOOPDEV="$(losetup --show -P -f "$IMAGE_PUBLIC")"
+ [[ -b "$LOOPDEV" ]] || return 1
# Create two partitions - a root one and a data one (utilized by some tests)
sfdisk "$LOOPDEV" <<EOF
label: gpt
fi
fi
- check_result_common "${workspace}"
- ret=$?
+ check_result_common "${workspace}" && ret=0 || ret=$?
_umount_dir "${initdir:?}"
fi
fi
- check_result_common "${initdir:?}"
- ret=$?
+ check_result_common "${initdir:?}" && ret=0 || ret=$?
_umount_dir "${initdir:?}"
inst "${src%/*}/.${src##*/}.hmac" "${target%/*}/.${target##*/}.hmac"
fi
ddebug "Installing $src"
- cp --sparse=always -pfL "$src" "${initdir}/$target"
+ cp --sparse=always --force --dereference --preserve=all "$src" "${initdir}/$target"
}
# find symlinks linked to given library file
# nsswitch.conf uses [SUCCESS=merge] (like on Arch Linux)
# delv, dig - pull in nss_resolve if `resolve` is in nsswitch.conf
# tar - called by machinectl in TEST-25
- bin_rx='/(chown|delv|dig|getent|id|login|ls|mkfs\.[a-z0-9]+|mksquashfs|mkswap|setfacl|setpriv|stat|su|tar|useradd|userdel)$'
+ bin_rx='/(agetty|chown|delv|dig|getfacl|getent|id|login|ls|mkfs\.[a-z0-9]+|mksquashfs|mkswap|setfacl|setpriv|stat|su|tar|useradd|userdel)$'
if get_bool "$IS_BUILT_WITH_ASAN" && [[ "$bin" =~ $bin_rx ]]; then
wrap_binary=1
fi
# shellcheck disable=SC2050
[ @ENABLE_COMPAT_MUTABLE_UID_BOUNDARIES@ = 1 ] && bound=555 || bound=$system_guid_max
- compare "${f%.*}" "(with login.defs)" $bound
+ compare "${f%.*}" "(with login.defs)" "$bound"
done
rm -f "$TESTDIR"/etc/sysusers.d/* "$TESTDIR"/usr/lib/sysusers.d/*
# shellcheck disable=SC2050
[ @ENABLE_COMPAT_MUTABLE_UID_BOUNDARIES@ = 1 ] && bound=555 || bound=$system_guid_max
- compare "${f%.*}" "(with login.defs symlinked)" $bound
+ compare "${f%.*}" "(with login.defs symlinked)" "$bound"
done
rm -f "$TESTDIR"/etc/sysusers.d/* "$TESTDIR"/usr/lib/sysusers.d/*
+++ /dev/null
-# SPDX-License-Identifier: LGPL-2.1-or-later
-[Unit]
-Description=Load systemd-test module
-DefaultDependencies=no
-Requires=local-fs.target
-Conflicts=shutdown.target
-After=local-fs.target
-Before=sysinit.target shutdown.target autorelabel.service
-ConditionSecurity=selinux
-
-[Service]
-ExecStart=sh -x -c 'echo 0 >/sys/fs/selinux/enforce && make -C /systemd-test-module -f /usr/share/selinux/devel/Makefile load'
-Type=oneshot
-TimeoutSec=0
-RemainAfterExit=yes
ConditionPathExists=|/.autorelabel
[Service]
-ExecStart=sh -x -c 'echo 0 >/sys/fs/selinux/enforce && fixfiles -f -F relabel && rm /.autorelabel && systemctl --force reboot'
+ExecStart=sh -xec 'echo 0 >/sys/fs/selinux/enforce; fixfiles -f -F relabel; rm /.autorelabel; systemctl --force reboot'
Type=oneshot
-TimeoutSec=0
+TimeoutSec=infinity
RemainAfterExit=yes
+StandardOutput=journal+console
[Install]
WantedBy=basic.target
set -eux
set -o pipefail
+if ! systemd-detect-virt -qc && [[ "${TEST_CMDLINE_NEWLINE:-}" != bar ]]; then
+ cat /proc/cmdline
+ echo >&2 "Expected TEST_CMDLINE_NEWLINE=bar from the kernel command line"
+ exit 1
+fi
+
+# If we're running with TEST_PREFER_NSPAWN=1 limit the set of tests we run
+# in QEMU to only those that can't run in a container to avoid running
+# the same tests again in a, most likely, very slow environment
+if ! systemd-detect-virt -qc && [[ "${TEST_PREFER_NSPAWN:-0}" -ne 0 ]]; then
+ TESTS_GLOB="test-loop-block"
+else
+ TESTS_GLOB=${TESTS_GLOB:-test-*}
+fi
+
NPROC=$(nproc)
MAX_QUEUE_SIZE=${NPROC:-2}
-TESTS_GLOB=${TESTS_GLOB:-test-*}
mapfile -t TEST_LIST < <(find /usr/lib/systemd/tests/unit-tests/ -maxdepth 1 -type f -name "${TESTS_GLOB}")
-# reset state
+# Reset state
rm -fv /failed-tests /skipped-tests /skipped
+if ! systemd-detect-virt -qc; then
+ # Make sure ping works for unprivileged users (for test-bpf-firewall)
+ sysctl net.ipv4.ping_group_range="0 2147483647"
+fi
+
# Check & report test results
# Arguments:
# $1: test path
[Unit]
Description=TEST-06-SELINUX
-Requires=load-systemd-test-module.service
-After=load-systemd-test-module.service
-
[Service]
ExecStartPre=rm -f /failed /testok
ExecStart=/usr/lib/systemd/tests/testdata/units/%N.sh
[Service]
ExecStartPre=rm -f /failed /testok
-ExecStart=sh -x -e -c 'mountpoint /var; systemctl --state=failed --no-legend --no-pager >/failed; echo OK >/testok'
+ExecStart=/usr/lib/systemd/tests/testdata/units/%N.sh
Type=oneshot
--- /dev/null
+#!/usr/bin/env bash
+# SPDX-License-Identifier: LGPL-2.1-or-later
+set -eux
+set -o pipefail
+
+# TODO:
+# - /proc/cmdline parsing
+# - figure out token support (apart from TPM2, as that's covered by TEST-70-TPM2)
+# - this might help https://www.qemu.org/docs/master/system/devices/ccid.html
+# - expect + interactive auth?
+
+# We set up an encrypted /var partition which should get mounted automatically
+# on boot
+mountpoint /var
+
+systemctl --state=failed --no-legend --no-pager | tee /failed
+if [[ -s /failed ]]; then
+ echo >&2 "Found units in failed state"
+ exit 1
+fi
+
+at_exit() {
+ set +e
+
+ mountpoint -q /proc/cmdline && umount /proc/cmdline
+ rm -f /etc/crypttab
+ [[ -e /tmp/crypttab.bak ]] && cp -fv /tmp/crypttab.bak /etc/crypttab
+ [[ -n "${STORE_LOOP:-}" ]] && losetup -d "$STORE_LOOP"
+ [[ -n "${WORKDIR:-}" ]] && rm -rf "$WORKDIR"
+
+ systemctl daemon-reload
+}
+
+trap at_exit EXIT
+
+cryptsetup_start_and_check() {
+ local expect_fail=0
+ local ec volume unit
+
+ if [[ "${1:?}" == "-f" ]]; then
+ expect_fail=1
+ shift
+ fi
+
+ for volume in "$@"; do
+ unit="systemd-cryptsetup@$volume.service"
+
+ # The unit existence check should always pass
+ [[ "$(systemctl show -P LoadState "$unit")" == loaded ]]
+ systemctl list-unit-files "$unit"
+
+ systemctl start "$unit" && ec=0 || ec=$?
+ if [[ "$expect_fail" -ne 0 ]]; then
+ if [[ "$ec" -eq 0 ]]; then
+ echo >&2 "Unexpected pass when starting $unit"
+ return 1
+ fi
+
+ return 0
+ fi
+
+ if [[ "$ec" -ne 0 ]]; then
+ echo >&2 "Unexpected fail when starting $unit"
+ return 1
+ fi
+
+ systemctl status "$unit"
+ test -e "/dev/mapper/$volume"
+ systemctl stop "$unit"
+ test ! -e "/dev/mapper/$volume"
+ done
+
+ return 0
+}
+
+# Note: some stuff (especially TPM-related) is already tested by TEST-70-TPM2,
+# so focus more on other areas instead
+
+# Use a common workdir to make the cleanup easier
+WORKDIR="$(mktemp -d)"
+
+# Prepare a couple of LUKS2-encrypted disk images
+#
+# 1) Image with an empty password
+IMAGE_EMPTY="$WORKDIR/empty.img)"
+IMAGE_EMPTY_KEYFILE="$WORKDIR/empty.keyfile"
+IMAGE_EMPTY_KEYFILE_ERASE="$WORKDIR/empty-erase.keyfile"
+IMAGE_EMPTY_KEYFILE_ERASE_FAIL="$WORKDIR/empty-erase-fail.keyfile)"
+truncate -s 32M "$IMAGE_EMPTY"
+echo -n passphrase >"$IMAGE_EMPTY_KEYFILE"
+chmod 0600 "$IMAGE_EMPTY_KEYFILE"
+cryptsetup luksFormat --batch-mode \
+ --pbkdf pbkdf2 \
+ --pbkdf-force-iterations 1000 \
+ --use-urandom \
+ "$IMAGE_EMPTY" "$IMAGE_EMPTY_KEYFILE"
+PASSWORD=passphrase NEWPASSWORD="" systemd-cryptenroll --password "$IMAGE_EMPTY"
+# Duplicate the key file to test keyfile-erase as well
+cp -v "$IMAGE_EMPTY_KEYFILE" "$IMAGE_EMPTY_KEYFILE_ERASE"
+# The key should get erased even on a failed attempt, so test that too
+cp -v "$IMAGE_EMPTY_KEYFILE" "$IMAGE_EMPTY_KEYFILE_ERASE_FAIL"
+
+# 2) Image with a detached header and a key file offset + size
+IMAGE_DETACHED="$WORKDIR/detached.img"
+IMAGE_DETACHED_KEYFILE="$WORKDIR/detached.keyfile"
+IMAGE_DETACHED_KEYFILE2="$WORKDIR/detached.keyfile2"
+IMAGE_DETACHED_HEADER="$WORKDIR/detached.header"
+truncate -s 32M "$IMAGE_DETACHED"
+dd if=/dev/urandom of="$IMAGE_DETACHED_KEYFILE" count=64 bs=1
+dd if=/dev/urandom of="$IMAGE_DETACHED_KEYFILE2" count=32 bs=1
+chmod 0600 "$IMAGE_DETACHED_KEYFILE" "$IMAGE_DETACHED_KEYFILE2"
+cryptsetup luksFormat --batch-mode \
+ --pbkdf pbkdf2 \
+ --pbkdf-force-iterations 1000 \
+ --use-urandom \
+ --header "$IMAGE_DETACHED_HEADER" \
+ --keyfile-offset 32 \
+ --keyfile-size 16 \
+ "$IMAGE_DETACHED" "$IMAGE_DETACHED_KEYFILE"
+# Also, add a second key file to key slot 8
+# Note: --key-slot= behaves as --new-key-slot= when used alone for backwards compatibility
+cryptsetup luksAddKey --batch-mode \
+ --header "$IMAGE_DETACHED_HEADER" \
+ --key-file "$IMAGE_DETACHED_KEYFILE" \
+ --keyfile-offset 32 \
+ --keyfile-size 16 \
+ --key-slot 8 \
+ "$IMAGE_DETACHED" "$IMAGE_DETACHED_KEYFILE2"
+
+# Prepare a couple of dummy devices we'll store a copy of the detached header
+# and one of the keys on to test if systemd-cryptsetup correctly mounts them
+# when necessary
+STORE_IMAGE="$WORKDIR/store.img"
+truncate -s 64M "$STORE_IMAGE"
+STORE_LOOP="$(losetup --show --find --partscan "$STORE_IMAGE")"
+sfdisk "$STORE_LOOP" <<EOF
+label: gpt
+type=0FC63DAF-8483-4772-8E79-3D69D8477DE4 name=header_store size=32M
+type=0FC63DAF-8483-4772-8E79-3D69D8477DE4 name=keyfile_store
+EOF
+udevadm settle --timeout=30
+mkdir -p /mnt
+mkfs.ext4 -L header_store "/dev/disk/by-partlabel/header_store"
+mount "/dev/disk/by-partlabel/header_store" /mnt
+cp "$IMAGE_DETACHED_HEADER" /mnt/header
+umount /mnt
+mkfs.ext4 -L keyfile_store "/dev/disk/by-partlabel/keyfile_store"
+mount "/dev/disk/by-partlabel/keyfile_store" /mnt
+cp "$IMAGE_DETACHED_KEYFILE2" /mnt/keyfile
+umount /mnt
+udevadm settle --timeout=30
+
+# Prepare our test crypttab
+[[ -e /etc/crypttab ]] && cp -fv /etc/crypttab /tmp/crypttab.bak
+cat >/etc/crypttab <<EOF
+# headless should translate to headless=1
+empty_key $IMAGE_EMPTY $IMAGE_EMPTY_KEYFILE headless,x-systemd.device-timeout=1m
+empty_key_erase $IMAGE_EMPTY $IMAGE_EMPTY_KEYFILE_ERASE headless=1,keyfile-erase=1
+empty_key_erase_fail $IMAGE_EMPTY $IMAGE_EMPTY_KEYFILE_ERASE_FAIL headless=1,keyfile-erase=1,keyfile-offset=4
+# Empty passphrase without try-empty-password(=yes) shouldn't work
+empty_fail0 $IMAGE_EMPTY - headless=1
+empty_fail1 $IMAGE_EMPTY - headless=1,try-empty-password=0
+empty0 $IMAGE_EMPTY - headless=1,try-empty-password
+empty1 $IMAGE_EMPTY - headless=1,try-empty-password=1
+# This one expects the key to be under /{etc,run}/cryptsetup-keys.d/empty_nokey.key
+empty_nokey $IMAGE_EMPTY - headless=1
+
+detached $IMAGE_DETACHED $IMAGE_DETACHED_KEYFILE headless=1,header=$IMAGE_DETACHED_HEADER,keyfile-offset=32,keyfile-size=16
+detached_store0 $IMAGE_DETACHED $IMAGE_DETACHED_KEYFILE headless=1,header=/header:LABEL=header_store,keyfile-offset=32,keyfile-size=16
+detached_store1 $IMAGE_DETACHED /keyfile:LABEL=keyfile_store headless=1,header=$IMAGE_DETACHED_HEADER
+detached_store2 $IMAGE_DETACHED /keyfile:LABEL=keyfile_store headless=1,header=/header:LABEL=header_store
+detached_fail0 $IMAGE_DETACHED $IMAGE_DETACHED_KEYFILE headless=1,header=$IMAGE_DETACHED_HEADER,keyfile-offset=32
+detached_fail1 $IMAGE_DETACHED $IMAGE_DETACHED_KEYFILE headless=1,header=$IMAGE_DETACHED_HEADER
+detached_fail2 $IMAGE_DETACHED $IMAGE_DETACHED_KEYFILE headless=1
+detached_fail3 $IMAGE_DETACHED $IMAGE_DETACHED_KEYFILE headless=1,header=$IMAGE_DETACHED_HEADER,keyfile-offset=16,keyfile-size=16
+detached_fail4 $IMAGE_DETACHED $IMAGE_DETACHED_KEYFILE headless=1,header=$IMAGE_DETACHED_HEADER,keyfile-offset=32,keyfile-size=8
+detached_slot0 $IMAGE_DETACHED $IMAGE_DETACHED_KEYFILE2 headless=1,header=$IMAGE_DETACHED_HEADER
+detached_slot1 $IMAGE_DETACHED $IMAGE_DETACHED_KEYFILE2 headless=1,header=$IMAGE_DETACHED_HEADER,key-slot=8
+detached_slot_fail $IMAGE_DETACHED $IMAGE_DETACHED_KEYFILE2 headless=1,header=$IMAGE_DETACHED_HEADER,key-slot=0
+EOF
+
+# Temporarily drop luks.name=/luks.uuid= from the kernel command line, as it makes
+# systemd-cryptsetup-generator ignore mounts from /etc/crypttab that are not also
+# specified on the kernel command line
+sed -r 's/luks.(name|uuid)=[^[:space:]+]//' /proc/cmdline >/tmp/cmdline.tmp
+mount --bind /tmp/cmdline.tmp /proc/cmdline
+# Run the systemd-cryptsetup-generator once explicitly, to collect coverage,
+# as during daemon-reload we run generators in a sandbox
+mkdir -p /tmp/systemd-cryptsetup-generator.out
+/usr/lib/systemd/system-generators/systemd-cryptsetup-generator /tmp/systemd-cryptsetup-generator.out/
+systemctl daemon-reload
+systemctl list-unit-files "systemd-cryptsetup@*"
+
+cryptsetup_start_and_check empty_key
+test -e "$IMAGE_EMPTY_KEYFILE_ERASE"
+cryptsetup_start_and_check empty_key_erase
+test ! -e "$IMAGE_EMPTY_KEYFILE_ERASE"
+test -e "$IMAGE_EMPTY_KEYFILE_ERASE_FAIL"
+cryptsetup_start_and_check -f empty_key_erase_fail
+test ! -e "$IMAGE_EMPTY_KEYFILE_ERASE_FAIL"
+cryptsetup_start_and_check -f empty_fail{0..1}
+cryptsetup_start_and_check empty{0..1}
+# First, check if we correctly fail without any key
+cryptsetup_start_and_check -f empty_nokey
+# And now provide the key via /{etc,run}/cryptsetup-keys.d/
+mkdir -p /run/cryptsetup-keys.d
+cp "$IMAGE_EMPTY_KEYFILE" /run/cryptsetup-keys.d/empty_nokey.key
+cryptsetup_start_and_check empty_nokey
+
+cryptsetup_start_and_check detached
+cryptsetup_start_and_check detached_store{0..2}
+cryptsetup_start_and_check -f detached_fail{0..4}
+cryptsetup_start_and_check detached_slot{0..1}
+cryptsetup_start_and_check -f detached_slot_fail
+
+echo OK >/testok
rm /tmp/ts54-concat
rm -rf /tmp/ts54-creds
+# Check that globs work as expected
+mkdir -p /run/credstore
+echo -n a >/run/credstore/test.creds.first
+echo -n b >/run/credstore/test.creds.second
+mkdir -p /etc/credstore
+echo -n c >/etc/credstore/test.creds.third
+systemd-run -p "ImportCredential=test.creds.*" \
+ --unit=test-54-ImportCredential.service \
+ -p DynamicUser=1 \
+ --wait \
+ --pipe \
+ cat '${CREDENTIALS_DIRECTORY}/test.creds.first' \
+ '${CREDENTIALS_DIRECTORY}/test.creds.second' \
+ '${CREDENTIALS_DIRECTORY}/test.creds.third' >/tmp/ts54-concat
+cmp /tmp/ts54-concat <(echo -n abc)
+
# Now test encrypted credentials (only supported when built with OpenSSL though)
if systemctl --version | grep -q -- +OPENSSL ; then
echo -n $RANDOM >/tmp/test-54-plaintext
local loop volume
defs="$(mktemp --directory "/tmp/test-repart.defs.XXXXXXXXXX")"
- imgs="$(runas testuser mktemp --directory "/var/tmp/test-repart.imgs.XXXXXXXXXX")"
+ imgs="$(mktemp --directory "/var/tmp/test-repart.imgs.XXXXXXXXXX")"
# shellcheck disable=SC2064
trap "rm -rf '$defs' '$imgs'" RETURN
chmod a+rx "$defs"
echo "*** 1. create an empty image ***"
- runas testuser systemd-repart --empty=create \
- --size=1G \
- --seed="$seed" \
- "$imgs/zzz"
+ systemd-repart --offline="$OFFLINE" \
+ --empty=create \
+ --size=1G \
+ --seed="$seed" \
+ "$imgs/zzz"
output=$(sfdisk -d "$imgs/zzz" | grep -v -e 'sector-size' -e '^$')
PaddingMinBytes=92M
EOF
- runas testuser systemd-repart --definitions="$defs" \
- --dry-run=no \
- --seed="$seed" \
- --include-partitions=home,swap \
- "$imgs/zzz"
+ systemd-repart --offline="$OFFLINE" \
+ --definitions="$defs" \
+ --dry-run=no \
+ --seed="$seed" \
+ --include-partitions=home,swap \
+ --offline="$OFFLINE" \
+ "$imgs/zzz"
output=$(sfdisk -d "$imgs/zzz" | grep -v -e 'sector-size' -e '^$')
$imgs/zzz1 : start= 2048, size= 1775576, type=933AC7E1-2EB4-4F13-B844-0E14E2AEF915, uuid=4980595D-D74A-483A-AA9E-9903879A0EE5, name=\"home-first\", attrs=\"GUID:59\"
$imgs/zzz2 : start= 1777624, size= 131072, type=0657FD6D-A4AB-43C4-84E5-0933C84B4F4F, uuid=78C92DB8-3D2B-4823-B0DC-792B78F66F1E, name=\"swap\""
- runas testuser systemd-repart --definitions="$defs" \
- --dry-run=no \
- --seed="$seed" \
- --empty=force \
- --defer-partitions=home,root \
- "$imgs/zzz"
+ systemd-repart --offline="$OFFLINE" \
+ --definitions="$defs" \
+ --dry-run=no \
+ --seed="$seed" \
+ --empty=force \
+ --defer-partitions=home,root \
+ "$imgs/zzz"
output=$(sfdisk -d "$imgs/zzz" | grep -v -e 'sector-size' -e '^$')
last-lba: 2097118
$imgs/zzz4 : start= 1777624, size= 131072, type=0657FD6D-A4AB-43C4-84E5-0933C84B4F4F, uuid=78C92DB8-3D2B-4823-B0DC-792B78F66F1E, name=\"swap\""
- runas testuser systemd-repart --definitions="$defs" \
- --dry-run=no \
- --seed="$seed" \
- "$imgs/zzz"
+ systemd-repart --offline="$OFFLINE" \
+ --definitions="$defs" \
+ --dry-run=no \
+ --seed="$seed" \
+ "$imgs/zzz"
output=$(sfdisk -d "$imgs/zzz" | grep -v -e 'sector-size' -e '^$')
echo "Label=ignored_label" >>"$defs/home.conf"
echo "UUID=b0b1b2b3b4b5b6b7b8b9babbbcbdbebf" >>"$defs/home.conf"
- runas testuser systemd-repart --definitions="$defs" \
- --dry-run=no \
- --seed="$seed" \
- "$imgs/zzz"
+ systemd-repart --offline="$OFFLINE" \
+ --definitions="$defs" \
+ --dry-run=no \
+ --seed="$seed" \
+ "$imgs/zzz"
output=$(sfdisk -d "$imgs/zzz" | grep -v -e 'sector-size' -e '^$')
echo "*** 4. Resizing to 2G ***"
- runas testuser systemd-repart --definitions="$defs" \
- --size=2G \
- --dry-run=no \
- --seed="$seed" \
- "$imgs/zzz"
+ systemd-repart --offline="$OFFLINE" \
+ --definitions="$defs" \
+ --size=2G \
+ --dry-run=no \
+ --seed="$seed" \
+ "$imgs/zzz"
output=$(sfdisk -d "$imgs/zzz" | grep -v -e 'sector-size' -e '^$')
CopyBlocks=$imgs/block-copy
EOF
- runas testuser systemd-repart --definitions="$defs" \
- --size=3G \
- --dry-run=no \
- --seed="$seed" \
- "$imgs/zzz"
+ systemd-repart --offline="$OFFLINE" \
+ --definitions="$defs" \
+ --size=3G \
+ --dry-run=no \
+ --seed="$seed" \
+ "$imgs/zzz"
output=$(sfdisk -d "$imgs/zzz" | grep -v -e 'sector-size' -e '^$')
SizeMinBytes=48M
EOF
- # CopyFiles will fail if we try to chown the target file to root.
- # Make the files owned by the user so that the invocation below works.
- chown testuser -R "$defs"
-
- runas testuser systemd-repart --definitions="$defs" \
- --size=auto \
- --dry-run=no \
- --seed="$seed" \
- "$imgs/zzz"
+ systemd-repart --offline="$OFFLINE" \
+ --definitions="$defs" \
+ --size=auto \
+ --dry-run=no \
+ --seed="$seed" \
+ "$imgs/zzz"
output=$(sfdisk -d "$imgs/zzz" | grep -v -e 'sector-size' -e '^$')
local defs imgs output
defs="$(mktemp --directory "/tmp/test-repart.defs.XXXXXXXXXX")"
- imgs="$(runas testuser mktemp --directory "/var/tmp/test-repart.imgs.XXXXXXXXXX")"
+ imgs="$(mktemp --directory "/var/tmp/test-repart.imgs.XXXXXXXXXX")"
# shellcheck disable=SC2064
trap "rm -rf '$defs' '$imgs'" RETURN
chmod a+rx "$defs"
Label=label2
EOF
- output=$(runas testuser systemd-repart --definitions="$defs" \
- --empty=create \
- --size=100M \
- --json=pretty \
- "$imgs/zzz")
+ output=$(systemd-repart --offline="$OFFLINE" \
+ --definitions="$defs" \
+ --empty=create \
+ --size=100M \
+ --json=pretty \
+ "$imgs/zzz")
diff -u <(echo "$output") - <<EOF
[
local defs imgs output
defs="$(mktemp --directory "/tmp/test-repart.defs.XXXXXXXXXX")"
- imgs="$(runas testuser mktemp --directory "/var/tmp/test-repart.imgs.XXXXXXXXXX")"
+ imgs="$(mktemp --directory "/var/tmp/test-repart.imgs.XXXXXXXXXX")"
# shellcheck disable=SC2064
trap "rm -rf '$defs' '$imgs'" RETURN
chmod a+rx "$defs"
Label=label2
EOF
- output=$(runas testuser systemd-repart --definitions="$defs/1" \
- --definitions="$defs/2" \
- --empty=create \
- --size=100M \
- --json=pretty \
- "$imgs/zzz")
+ output=$(systemd-repart --offline="$OFFLINE" \
+ --definitions="$defs/1" \
+ --definitions="$defs/2" \
+ --empty=create \
+ --size=100M \
+ --json=pretty \
+ "$imgs/zzz")
diff -u <(echo "$output") - <<EOF
[
local defs imgs output
defs="$(mktemp --directory "/tmp/test-repart.defs.XXXXXXXXXX")"
- imgs="$(runas testuser mktemp --directory "/var/tmp/test-repart.imgs.XXXXXXXXXX")"
+ imgs="$(mktemp --directory "/var/tmp/test-repart.imgs.XXXXXXXXXX")"
# shellcheck disable=SC2064
trap "rm -rf '$defs' '$imgs'" RETURN
chmod a+rx "$defs"
MakeDirectories=/usr /efi
EOF
- runas testuser systemd-repart --definitions="$defs" \
- --empty=create \
- --size=auto \
- --seed="$seed" \
- "$imgs/zzz"
+ systemd-repart --offline="$OFFLINE" \
+ --definitions="$defs" \
+ --empty=create \
+ --size=auto \
+ --seed="$seed" \
+ "$imgs/zzz"
output=$(sfdisk --dump "$imgs/zzz")
CopyBlocks=auto
EOF
- # --image needs root privileges so skip runas testuser here.
- systemd-repart --definitions="$defs" \
+ systemd-repart --offline="$OFFLINE" \
+ --definitions="$defs" \
--empty=create \
--size=auto \
--seed="$seed" \
local defs imgs output
defs="$(mktemp --directory "/tmp/test-repart.defs.XXXXXXXXXX")"
- imgs="$(runas testuser mktemp --directory "/var/tmp/test-repart.imgs.XXXXXXXXXX")"
+ imgs="$(mktemp --directory "/var/tmp/test-repart.imgs.XXXXXXXXXX")"
# shellcheck disable=SC2064
trap "rm -rf '$defs' '$imgs'" RETURN
chmod a+rx "$defs"
Type=root-${architecture}
EOF
- runas testuser truncate -s 10g "$imgs/unaligned"
+ truncate -s 10g "$imgs/unaligned"
sfdisk "$imgs/unaligned" <<EOF
label: gpt
start=71092, size=3591848
EOF
- runas testuser systemd-repart --definitions="$defs" \
- --seed="$seed" \
- --dry-run=no \
- "$imgs/unaligned"
+ systemd-repart --offline="$OFFLINE" \
+ --definitions="$defs" \
+ --seed="$seed" \
+ --dry-run=no \
+ "$imgs/unaligned"
output=$(sfdisk --dump "$imgs/unaligned")
local defs imgs output
defs="$(mktemp --directory "/tmp/test-repart.defs.XXXXXXXXXX")"
- imgs="$(runas testuser mktemp --directory "/var/tmp/test-repart.imgs.XXXXXXXXXX")"
+ imgs="$(mktemp --directory "/var/tmp/test-repart.imgs.XXXXXXXXXX")"
# shellcheck disable=SC2064
trap "rm -rf '$defs' '$imgs'" RETURN
chmod a+rx "$defs"
Type=root
EOF
- runas testuser truncate -s 100m "$imgs/21817.img"
+ truncate -s 100m "$imgs/21817.img"
sfdisk "$imgs/21817.img" <<EOF
label: gpt
,
EOF
- runas testuser systemd-repart --pretty=yes \
- --definitions "$imgs" \
- --seed="$seed" \
- --dry-run=no \
- "$imgs/21817.img"
+ systemd-repart --offline="$OFFLINE" \
+ --pretty=yes \
+ --definitions "$imgs" \
+ --seed="$seed" \
+ --dry-run=no \
+ "$imgs/21817.img"
output=$(sfdisk --dump "$imgs/21817.img")
local defs imgs output
defs="$(mktemp --directory "/tmp/test-repart.defs.XXXXXXXXXX")"
- imgs="$(runas testuser mktemp --directory "/var/tmp/test-repart.imgs.XXXXXXXXXX")"
+ imgs="$(mktemp --directory "/var/tmp/test-repart.imgs.XXXXXXXXXX")"
# shellcheck disable=SC2064
trap "rm -rf '$defs' '$imgs'" RETURN
chmod a+rx "$defs"
EOF
echo "*** 1. Operate on a small image compared with SizeMinBytes= ***"
- runas testuser truncate -s 8g "$imgs/zzz"
+ truncate -s 8g "$imgs/zzz"
sfdisk "$imgs/zzz" <"$imgs/partscript"
# This should fail, but not trigger assertions.
- assert_rc 1 runas testuser systemd-repart --definitions="$defs" \
- --seed="$seed" \
- --dry-run=no \
- "$imgs/zzz"
+ assert_rc 1 systemd-repart --offline="$OFFLINE" \
+ --definitions="$defs" \
+ --seed="$seed" \
+ --dry-run=no \
+ "$imgs/zzz"
output=$(sfdisk --dump "$imgs/zzz")
assert_in "$imgs/zzz2 : start= 524328, size= 14848000, type=${root_guid}, uuid=${root_uuid}, name=\"root-${architecture}\"" "$output"
echo "*** 2. Operate on an larger image compared with SizeMinBytes= ***"
rm -f "$imgs/zzz"
- runas testuser truncate -s 12g "$imgs/zzz"
+ truncate -s 12g "$imgs/zzz"
sfdisk "$imgs/zzz" <"$imgs/partscript"
# This should succeed.
- runas testuser systemd-repart --definitions="$defs" \
- --seed="$seed" \
- --dry-run=no \
- "$imgs/zzz"
+ systemd-repart --offline="$OFFLINE" \
+ --definitions="$defs" \
+ --seed="$seed" \
+ --dry-run=no \
+ "$imgs/zzz"
output=$(sfdisk --dump "$imgs/zzz")
assert_in "$imgs/zzz2 : start= 524328, size= 24641456, type=${root_guid}, uuid=${root_uuid}, name=\"root-${architecture}\"" "$output"
EOF
rm -f "$imgs/zzz"
- runas testuser truncate -s 8g "$imgs/zzz"
+ truncate -s 8g "$imgs/zzz"
sfdisk "$imgs/zzz" <"$imgs/partscript"
# This should also succeed, but root is not extended.
- runas testuser systemd-repart --definitions="$defs" \
- --seed="$seed" \
- --dry-run=no \
- "$imgs/zzz"
+ systemd-repart --offline="$OFFLINE" \
+ --definitions="$defs" \
+ --seed="$seed" \
+ --dry-run=no \
+ "$imgs/zzz"
output=$(sfdisk --dump "$imgs/zzz")
assert_in "$imgs/zzz2 : start= 524328, size= 14848000, type=${root_guid}, uuid=${root_uuid}, name=\"root-${architecture}\"" "$output"
echo "*** 4. Multiple partitions with Priority= (large disk) ***"
rm -f "$imgs/zzz"
- runas testuser truncate -s 12g "$imgs/zzz"
+ truncate -s 12g "$imgs/zzz"
sfdisk "$imgs/zzz" <"$imgs/partscript"
# This should also succeed, and root is extended.
- runas testuser systemd-repart --definitions="$defs" \
- --seed="$seed" \
- --dry-run=no \
- "$imgs/zzz"
+ systemd-repart --offline="$OFFLINE" \
+ --definitions="$defs" \
+ --seed="$seed" \
+ --dry-run=no \
+ "$imgs/zzz"
output=$(sfdisk --dump "$imgs/zzz")
assert_in "$imgs/zzz2 : start= 524328, size= 20971520, type=${root_guid}, uuid=${root_uuid}, name=\"root-${architecture}\"" "$output"
local defs imgs output
defs="$(mktemp --directory "/tmp/test-repart.defs.XXXXXXXXXX")"
- imgs="$(runas testuser mktemp --directory "/var/tmp/test-repart.imgs.XXXXXXXXXX")"
+ imgs="$(mktemp --directory "/var/tmp/test-repart.imgs.XXXXXXXXXX")"
# shellcheck disable=SC2064
trap "rm -rf '$defs' '$imgs'" RETURN
chmod a+rx "$defs"
UUID=null
EOF
- runas testuser systemd-repart --definitions="$defs" \
- --seed="$seed" \
- --dry-run=no \
- --empty=create \
- --size=auto \
- "$imgs/zero"
+ systemd-repart --offline="$OFFLINE" \
+ --definitions="$defs" \
+ --seed="$seed" \
+ --dry-run=no \
+ --empty=create \
+ --size=auto \
+ "$imgs/zero"
output=$(sfdisk --dump "$imgs/zero")
local defs imgs output
defs="$(mktemp --directory "/tmp/test-repart.defs.XXXXXXXXXX")"
- imgs="$(runas testuser mktemp --directory "/var/tmp/test-repart.imgs.XXXXXXXXXX")"
+ imgs="$(mktemp --directory "/var/tmp/test-repart.imgs.XXXXXXXXXX")"
# shellcheck disable=SC2064
trap "rm -rf '$defs' '$imgs'" RETURN
chmod a+rx "$defs"
-nodes
mkdir -p /run/verity.d
- ln -s "$defs/verity.crt" /run/verity.d/ok.crt
-
- # CopyFiles will fail if we try to chown the target file to root.
- # Make the files owned by the user so that the invocation below works.
- chown testuser -R "$defs"
-
- output=$(runas testuser systemd-repart --definitions="$defs" \
- --seed="$seed" \
- --dry-run=no \
- --empty=create \
- --size=auto \
- --json=pretty \
- --private-key="$defs/verity.key" \
- --certificate="$defs/verity.crt" \
- "$imgs/verity")
+ ln -sf "$defs/verity.crt" /run/verity.d/ok.crt
+
+ output=$(systemd-repart --offline="$OFFLINE" \
+ --definitions="$defs" \
+ --seed="$seed" \
+ --dry-run=no \
+ --empty=create \
+ --size=auto \
+ --json=pretty \
+ --private-key="$defs/verity.key" \
+ --certificate="$defs/verity.crt" \
+ "$imgs/verity")
drh=$(jq -r ".[] | select(.type == \"root-${architecture}\") | .roothash" <<<"$output")
hrh=$(jq -r ".[] | select(.type == \"root-${architecture}-verity\") | .roothash" <<<"$output")
local defs imgs root output
defs="$(mktemp --directory "/tmp/test-repart.defs.XXXXXXXXXX")"
- imgs="$(runas testuser mktemp --directory "/var/tmp/test-repart.imgs.XXXXXXXXXX")"
- root="$(runas testuser mktemp --directory "/var/tmp/test-repart.root.XXXXXXXXXX")"
+ imgs="$(mktemp --directory "/var/tmp/test-repart.imgs.XXXXXXXXXX")"
+ root="$(mktemp --directory "/var/tmp/test-repart.root.XXXXXXXXXX")"
# shellcheck disable=SC2064
trap "rm -rf '$defs' '$imgs' '$root'" RETURN
chmod a+rx "$defs"
ExcludeFiles=/usr/qed
EOF
- # CopyFiles will fail if we try to chown the target file to root.
- # Make the files owned by the user so that the invocation below works.
- chown testuser -R "$root"
-
- output=$(runas testuser systemd-repart --definitions="$defs" \
- --seed="$seed" \
- --dry-run=no \
- --empty=create \
- --size=auto \
- --json=pretty \
- --root="$root" \
- "$imgs/zzz")
+ output=$(systemd-repart --offline="$OFFLINE" \
+ --definitions="$defs" \
+ --seed="$seed" \
+ --dry-run=no \
+ --empty=create \
+ --size=auto \
+ --json=pretty \
+ --root="$root" \
+ "$imgs/zzz")
if systemd-detect-virt --quiet --container; then
echo "Skipping issue 24786 test loop/mount parts in container."
EOF
fi
- output=$(systemd-repart --definitions="$defs" \
+ output=$(systemd-repart --offline="$OFFLINE" \
+ --definitions="$defs" \
--seed="$seed" \
--dry-run=no \
--empty=create \
truncate -s 100m "$imgs/$sector.img"
loop=$(losetup -b "$sector" -P --show -f "$imgs/$sector.img" )
udevadm wait --timeout 60 --settle "${loop:?}"
- # This operates on a loop device which we don't support doing without root privileges so we skip runas
- # here.
- systemd-repart --pretty=yes \
+
+ systemd-repart --offline="$OFFLINE" \
+ --pretty=yes \
--definitions="$defs" \
--seed="$seed" \
--empty=require \
assert_in "${loop}p3 : start= *${start}, size= *${size}, type=0FC63DAF-8483-4772-8E79-3D69D8477DE4, uuid=DB081670-07AE-48CA-9F5E-813D5E40B976, name=\"linux-generic-2\"" "$output"
}
+OFFLINE="yes"
run_testcases
+# Online image builds need loop devices so we can't run them in nspawn.
+if ! systemd-detect-virt --container; then
+ OFFLINE="no"
+ run_testcases
+fi
+
# Valid block sizes on the Linux block layer are >= 512 and <= PAGE_SIZE, and
# must be powers of 2. Which leaves exactly four different ones to test on
# typical hardware
+++ /dev/null
-# SPDX-License-Identifier: LGPL-2.1-or-later
-[Unit]
-Description=TEST-61-UNITTESTS-QEMU
-
-[Service]
-ExecStartPre=rm -f /failed /testok
-ExecStart=/usr/lib/systemd/tests/testdata/units/%N.sh
-Type=oneshot
+++ /dev/null
-#!/usr/bin/env bash
-# SPDX-License-Identifier: LGPL-2.1-or-later
-# shellcheck disable=SC2317
-set -eux
-set -o pipefail
-
-TESTS_GLOB="test-loop-block"
-# shellcheck source=test/units/testsuite-02.sh
-. "$(dirname "$0")/testsuite-02.sh"
-
-exit 0
def resolve(self, url, id, context):
if 'custom-entities.ent' in url:
return self.resolve_filename('man/custom-entities.ent', context)
+ if 'ethtool-link-mode' in url:
+ return self.resolve_filename('src/shared/ethtool-link-mode.xml', context)
_parser = tree.XMLParser()
_parser.resolvers.add(CustomResolver())
[Service]
Type=forking
ExecStart={{RC_LOCAL_PATH}} start
-TimeoutSec=0
+TimeoutSec=infinity
RemainAfterExit=yes
GuessMainPID=no
# Optionally, pick up basic fields from credentials passed to the service
# manager. This is useful for importing this data from nspawn's
# --set-credential= switch.
-LoadCredential=passwd.hashed-password.root
-LoadCredential=passwd.plaintext-password.root
-LoadCredential=passwd.shell.root
-LoadCredential=firstboot.locale
-LoadCredential=firstboot.locale-messages
-LoadCredential=firstboot.keymap
-LoadCredential=firstboot.timezone
+ImportCredential=passwd.hashed-password.root
+ImportCredential=passwd.plaintext-password.root
+ImportCredential=passwd.shell.root
+ImportCredential=firstboot.*
Type=oneshot
RemainAfterExit=yes
ExecStart={{ROOTLIBEXECDIR}}/systemd-fsck
-TimeoutSec=0
+TimeoutSec=infinity
Type=oneshot
RemainAfterExit=yes
ExecStart={{ROOTLIBEXECDIR}}/systemd-fsck %f
-TimeoutSec=0
+TimeoutSec=infinity
Type=oneshot
RemainAfterExit=yes
ExecStart={{ROOTLIBEXECDIR}}/systemd-growfs /
-TimeoutSec=0
+TimeoutSec=infinity
Type=oneshot
RemainAfterExit=yes
ExecStart={{ROOTLIBEXECDIR}}/systemd-growfs %f
-TimeoutSec=0
+TimeoutSec=infinity
Type=oneshot
RemainAfterExit=yes
ExecStart={{ROOTLIBEXECDIR}}/systemd-quotacheck
-TimeoutSec=0
+TimeoutSec=infinity
SystemCallFilter=@system-service
Type=notify
User=systemd-resolve
-LoadCredential=network.dns
-LoadCredential=network.search_domains
+ImportCredential=network.dns
+ImportCredential=network.search_domains
{{SERVICE_WATCHDOG}}
[Install]
RemainAfterExit=yes
ExecStart={{ROOTLIBEXECDIR}}/systemd-sysctl
TimeoutSec=90s
-LoadCredential=sysctl.extra
+ImportCredential=sysctl.*
# Optionally, pick up a root password and shell for the root user from a
# credential passed to the service manager. This is useful for importing this
# data from nspawn's --set-credential= switch.
-LoadCredential=passwd.hashed-password.root
-LoadCredential=passwd.plaintext-password.root
-LoadCredential=passwd.shell.root
+ImportCredential=passwd.hashed-password.root
+ImportCredential=passwd.plaintext-password.root
+ImportCredential=passwd.shell.root
# Also, allow configuring extra sysusers lines via a credential
-LoadCredential=sysusers.extra
+ImportCredential=sysusers.*
ExecStart=systemd-tmpfiles --clean
SuccessExitStatus=DATAERR
IOSchedulingClass=idle
-LoadCredential=tmpfiles.extra
+ImportCredential=tmpfiles.*
RemainAfterExit=yes
ExecStart=systemd-tmpfiles --prefix=/dev --create --boot --graceful
SuccessExitStatus=DATAERR CANTCREAT
-LoadCredential=tmpfiles.extra
+ImportCredential=tmpfiles.*
RemainAfterExit=yes
ExecStart=systemd-tmpfiles --create --remove --boot
SuccessExitStatus=DATAERR CANTCREAT
-LoadCredential=tmpfiles.extra
-LoadCredential=login.motd
-LoadCredential=login.issue
-LoadCredential=network.hosts
-LoadCredential=ssh.authorized_keys.root
+ImportCredential=tmpfiles.*
+ImportCredential=login.motd
+ImportCredential=login.issue
+ImportCredential=network.hosts
+ImportCredential=ssh.authorized_keys.root
Type=oneshot
RemainAfterExit=yes
ExecStart={{ROOTLIBEXECDIR}}/systemd-vconsole-setup
-LoadCredential=vconsole.keymap
-LoadCredential=vconsole.keymap_toggle
-LoadCredential=vconsole.font
-LoadCredential=vconsole.font_map
-LoadCredential=vconsole.font_unimap
+ImportCredential=vconsole.*