"--optimization=0"
"--optimization=s"
"--optimization=3 -Db_lto=true -Ddns-over-tls=false"
- "--optimization=3 -Db_lto=false -Dtpm2=false -Dlibfido2=false -Dp11kit=false"
+ "--optimization=3 -Db_lto=false -Dtpm2=disabled -Dlibfido2=disabled -Dp11kit=disabled"
"--optimization=3 -Ddns-over-tls=openssl"
"--optimization=3 -Dfexecve=true -Dstandalone-binaries=true -Dstatic-libsystemd=true -Dstatic-libudev=true"
"-Db_ndebug=true"
export CXX=clang++
if [[ "$phase" == RUN_CLANG ]]; then
# The docs build is slow and is not affected by compiler/flags, so do it just once
- MESON_ARGS+=(-Dman=true)
+ MESON_ARGS+=(-Dman=enabled)
else
MESON_ARGS+=(-Dmode=release --optimization=2)
fi
MESON_ARGS+=(-Db_lundef=false -Dfuzz-tests=true)
if [[ "$phase" == "RUN_CLANG_ASAN_UBSAN_NO_DEPS" ]]; then
- MESON_ARGS+=(-Dskip-deps=true)
+ MESON_ARGS+=(--auto-features=disabled)
fi
fi
MESON_ARGS+=(--fatal-meson-warnings)
appropriate qemu cmdline. That way qemu payloads could talk sd_notify()
directly to host service manager.
-* sd-boot: add menu item for shutdown? or hotkey?
-
* sd-device has an API to create an sd_device object from a device id, but has
no api to query the device id
install_dir : catalogdir)
endforeach
-meson.add_install_script('sh', '-c',
+meson.add_install_script(sh, '-c',
'test -n "$DESTDIR" || @0@/journalctl --update-catalog'.format(bindir))
if install_sysconfdir
install_emptydir(sysconfdir / 'udev/hwdb.d')
- meson.add_install_script('sh', '-c',
+ meson.add_install_script(sh, '-c',
'test -n "$DESTDIR" || @0@/systemd-hwdb update'.format(bindir))
endif
want_man = get_option('man')
want_html = get_option('html')
xsltproc = find_program('xsltproc',
- required : want_man == 'true' or want_html == 'true')
-want_man = want_man != 'false' and xsltproc.found()
-want_html = want_html != 'false' and xsltproc.found()
+ required : want_man.enabled() or want_html.enabled())
+want_man = want_man.allowed() and xsltproc.found()
+want_html = want_html.allowed() and xsltproc.found()
xsltproc_flags = [
'--nonet',
<ulink url="https://tools.ietf.org/html/rfc3315#section-9">RFC 3315</ulink>
for a description of all the options.</para>
- <para>The following values are understood:
+ <para>This takes an integer in the range 0…65535, or one of the following string values:
<variablelist>
<varlistentry>
<term><option>vendor</option></term>
</para></listitem>
</varlistentry>
- <varlistentry>
- <term><option>custom</option></term>
- <listitem><para>If <literal>DUIDType=custom</literal>, then the <literal>DUIDRawData=</literal> value will
- used be as custom identifier. If <literal>DUIDType=custom</literal> is specified then the
- <literal>DUIDRawData=</literal> key is mandatory. Note it applies only on DHCPv6 clients.</para></listitem>
- </varlistentry>
-
<varlistentry>
<term><option>uuid</option></term>
<listitem><para>If <literal>DUIDType=uuid</literal>, and <varname>DUIDRawData=</varname> is not set,
<constant>ppc64-le</constant>, <constant>riscv32</constant>, <constant>riscv64</constant>,
<constant>s390</constant>, <constant>s390x</constant>, <constant>tilegx</constant>,
<constant>x86</constant> (32-bit, aka i386) and <constant>x86-64</constant> (64-bit, aka amd64).
+ </para>
- The supported identifiers are:</para>
+ <para>The supported identifiers are:</para>
<table>
<title>GPT partition type identifiers</title>
<example>
<title>Grow the root partition to the full disk size at first boot</title>
- <para>With the following file the root partition is automatically grown to the full disk if possible during boot.</para>
+ <para>With the following file the root partition is automatically grown to the full disk if possible
+ during boot.</para>
<para><programlisting># /usr/lib/repart.d/50-root.conf
[Partition]
</example>
<example>
- <title>Create a data and verity partition from a OS tree</title>
+ <title>Create a data partition and corresponding verity partitions from a OS tree</title>
- <para>Assuming we have an OS tree at /var/tmp/os-tree that we want to package in a root partition
- together with a matching verity partition, we can do so as follows:</para>
+ <para>Assuming we have an OS tree at <filename index='false'>/var/tmp/os-tree</filename> that we want
+ to package in a root partition together with matching verity partitions, we can do so as follows:
+ </para>
<para><programlisting># 50-root.conf
[Partition]
CopyFiles=/var/tmp/os-tree
Verity=data
VerityMatchKey=root
+Minimize=guess
</programlisting></para>
<para><programlisting># 60-root-verity.conf
# Explicitly set the hash and data block size to 4K
VerityDataBlockSizeBytes=4096
VerityHashBlockSizeBytes=4096
+Minimize=best
+</programlisting></para>
+
+<para><programlisting># 70-root-verity-sig.conf
+[Partition]
+Type=root-verity-sig
+Verity=signature
+VerityMatchKey=root
</programlisting></para>
</example>
''],
['systemd-boot-random-seed.service', '8', [], 'ENABLE_BOOTLOADER'],
['systemd-boot', '7', ['sd-boot'], 'ENABLE_BOOTLOADER'],
- ['systemd-bsod', '8', [], 'HAVE_QRENCODE'],
+ ['systemd-bsod.service', '8', ['systemd-bsod'], 'HAVE_QRENCODE'],
['systemd-cat', '1', [], ''],
['systemd-cgls', '1', [], ''],
['systemd-cgtop', '1', [], ''],
'8',
['systemd-tmpfiles-clean.service',
'systemd-tmpfiles-clean.timer',
+ 'systemd-tmpfiles-setup-dev-early.service',
'systemd-tmpfiles-setup-dev.service',
'systemd-tmpfiles-setup.service'],
''],
may also be reached with <keycap>F2</keycap>, <keycap>F10</keycap>, <keycap>Del</keycap> and
<keycap>Esc</keycap>.</para></listitem>
</varlistentry>
+
+ <varlistentry>
+ <term><keycombo><keycap>Shift</keycap><keycap>o</keycap></keycombo></term>
+ <listitem><para>Power off the system.</para></listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><keycombo><keycap>Shift</keycap><keycap>b</keycap></keycombo></term>
+ <listitem><para>Reboot the system.</para></listitem>
+ </varlistentry>
</variablelist>
<para>The following keys may be pressed during bootup or in the boot menu to directly boot a specific
"http://www.oasis-open.org/docbook/xml/4.2/docbookx.dtd">
<!-- SPDX-License-Identifier: LGPL-2.1-or-later -->
-<refentry id="systemd-bsod" conditional='HAVE_QRENCODE' xmlns:xi="http://www.w3.org/2001/XInclude">
+<refentry id="systemd-bsod.service" conditional='HAVE_QRENCODE' xmlns:xi="http://www.w3.org/2001/XInclude">
<refentryinfo>
<title>systemd-bsod</title>
</refentryinfo>
<refmeta>
- <refentrytitle>systemd-bsod</refentrytitle>
+ <refentrytitle>systemd-bsod.service</refentrytitle>
<manvolnum>8</manvolnum>
</refmeta>
<refnamediv>
+ <refname>systemd-bsod.service</refname>
<refname>systemd-bsod</refname>
- <refpurpose>Displays boot-time emergency log message full-screen.</refpurpose>
+ <refpurpose>Displays boot-time emergency log message in full screen.</refpurpose>
</refnamediv>
<refsynopsisdiv>
+ <para><filename>systemd-bsod.service</filename></para>
<cmdsynopsis>
<command>systemd-bsod</command>
<arg choice="opt" rep="repeat">OPTIONS</arg>
<refsect1>
<title>Description</title>
- <para><command>systemd-bsod</command> is used to display a blue screen which contains a message relating to
- a boot failure, including a QR code which users can scan with their phones to get helpful information
- about the cause of their boot failure.</para>
+ <para><filename>systemd-bsod.service</filename> is used to display a blue screen which contains a message
+ relating to a boot failure, including a QR code which can be scanned to get helpful information about the
+ failure.</para>
</refsect1>
<refsect1>
<title>Options</title>
- <para>The following options are understood:</para>
+ <para>The following options are understood by <command>systemd-bsod</command>:</para>
<variablelist>
<xi:include href="standard-options.xml" xpointer="help" />
<xi:include href="standard-options.xml" xpointer="version" />
- <varlistentry>
+ <varlistentry>
<term><option>-c</option></term>
<term><option>--continuous</option></term>
- <listitem><para> Used to make systemd-bsod wait continuously for changes in the
- journal's status if doesn't find any emergency messages on initial attempt.</para></listitem>
- </varlistentry>
+ <listitem><para>When specified, <command>systemd-bsod</command> waits continuously for changes in the
+ journal if it doesn't find any emergency messages on the initial attempt.</para></listitem>
+ </varlistentry>
</variablelist>
</refsect1>
<varlistentry>
<term><varname>HopLimit=</varname></term>
<listitem>
- <para> Configures per route hop limit. See also <varname>IPv6HopLimit=</varname>.</para>
+ <para>Configures per route hop limit. Takes an integer in the range 1…255. See also
+ <varname>IPv6HopLimit=</varname>.</para>
</listitem>
</varlistentry>
</varlistentry>
<varlistentry>
- <term><varname>TCPRetransmissionTimeOutSec=</varname></term>
+ <term><varname>TCPRetransmissionTimeoutSec=</varname></term>
<listitem>
- <para>Specifies the TCP Retransmission Time Out for the route. Takes time values in seconds.
- This value specifies the timeout of an alive TCP connection, when RTO retransmissions remain unacknowledged.
- When unset, the kernel's default will be used.</para>
+ <para>Specifies the TCP Retransmission Timeout (RTO) for the route. Takes time values in seconds.
+ This value specifies the timeout of an alive TCP connection, when retransmissions remain
+ unacknowledged. When unset, the kernel's default will be used.</para>
</listitem>
</varlistentry>
</listitem>
</varlistentry>
+ <varlistentry>
+ <term><varname>RetransmitSec=</varname></term>
+
+ <listitem><para>Takes a timespan. Configures the retransmit time, used by clients to retransmit Neighbor
+ Solicitation messages on address resolution and the Neighbor Unreachability Detection algorithm.
+ An integer the default unit of seconds, in the range 0…4294967295 msec. Defaults to 0.</para>
+ </listitem>
+ </varlistentry>
+
<varlistentry>
<term><varname>RouterPreference=</varname></term>
for details. Defaults to <literal>medium</literal>.</para></listitem>
</varlistentry>
+ <varlistentry>
+ <term><varname>HopLimit=</varname></term>
+ <listitem>
+ <para>Configures hop limit. Takes an integer in the range 0…255. See also
+ <varname>IPv6HopLimit=</varname>.</para>
+ </listitem>
+ </varlistentry>
+
<varlistentry>
<term><varname>UplinkInterface=</varname></term>
<listitem><para>Specifies the name or the index of the uplink interface, or one of the special
conf.set10('BUILD_MODE_DEVELOPER', get_option('mode') == 'developer',
description : 'tailor build to development or release builds')
-verification = get_option('log-message-verification')
-if verification == 'auto'
- verification = conf.get('BUILD_MODE_DEVELOPER') == 1
+feature = get_option('log-message-verification')
+if feature.auto()
+ have = conf.get('BUILD_MODE_DEVELOPER') == 1
else
- verification = verification == 'true'
+ have = feature.enabled()
endif
-conf.set10('LOG_MESSAGE_VERIFICATION', verification)
+conf.set10('LOG_MESSAGE_VERIFICATION', have)
want_ossfuzz = get_option('oss-fuzz')
want_libfuzzer = get_option('llvm-fuzz')
error('only one of oss-fuzz or llvm-fuzz can be specified')
endif
-skip_deps = want_ossfuzz or get_option('skip-deps')
fuzzer_build = want_ossfuzz or want_libfuzzer
# If we're building *not* for actual fuzzing, allow input samples of any size
conf.set10('HAVE_' + ident[0].to_upper(), have)
endforeach
-want_bpf_framework = get_option('bpf-framework')
+bpf_framework = get_option('bpf-framework')
bpf_compiler = get_option('bpf-compiler')
-bpf_framework_required = want_bpf_framework == 'true'
-
-libbpf_version_requirement = '>= 0.1.0'
-if bpf_compiler == 'gcc'
- libbpf_version_requirement = '>= 1.0.0'
-endif
-libbpf = dependency('libbpf', required : bpf_framework_required, version : libbpf_version_requirement)
+libbpf = dependency('libbpf',
+ required : bpf_framework,
+ version : bpf_compiler == 'gcc' ? '>= 1.0.0' : '>= 0.1.0')
conf.set10('HAVE_LIBBPF', libbpf.found())
-bpftool_strip_version_requirement = '>= 5.13.0'
-if bpf_compiler == 'gcc'
- bpftool_strip_version_requirement = '>= 7.0.0'
-endif
-
-if want_bpf_framework == 'false' or not libbpf.found() or skip_deps
+if not libbpf.found()
conf.set10('BPF_FRAMEWORK', false)
else
clang_found = false
# Support 'versioned' clang/llvm-strip binaries, as seen on Debian/Ubuntu
# (like clang-10/llvm-strip-10)
if meson.is_cross_build() or cc.get_id() != 'clang' or cc.cmd_array()[0].contains('afl-clang') or cc.cmd_array()[0].contains('hfuzz-clang')
- r = find_program('clang', required : bpf_framework_required, version : '>= 10.0.0')
+ r = find_program('clang',
+ required : bpf_framework,
+ version : '>= 10.0.0')
clang_found = r.found()
if clang_found
clang = r.full_path()
# Debian installs this in /usr/sbin/ which is not in $PATH.
# We check for 'bpftool' first, honouring $PATH, and in /usr/sbin/ for Debian.
# We use 'bpftool gen object' subcommand for bpftool strip, it was added by d80b2fcbe0a023619e0fc73112f2a02c2662f6ab (v5.13).
- bpftool_strip_required = bpf_framework_required and bpf_compiler == 'gcc'
bpftool = find_program('bpftool',
'/usr/sbin/bpftool',
- required : bpftool_strip_required,
- version : bpftool_strip_version_requirement)
+ required : bpf_framework.enabled() and bpf_compiler == 'gcc',
+ version : bpf_compiler == 'gcc' ? '>= 7.0.0' : '>= 5.13.0')
if bpftool.found()
bpftool_strip = true
# We require the 'bpftool gen skeleton' subcommand, it was added by 985ead416df39d6fe8e89580cc1db6aa273e0175 (v5.6).
bpftool = find_program('bpftool',
'/usr/sbin/bpftool',
- required : bpf_framework_required,
+ required : bpf_framework,
version : '>= 5.6.0')
endif
else
llvm_strip_bin = 'llvm-strip'
endif
- llvm_strip = find_program(llvm_strip_bin, required : bpf_framework_required, version : '>= 10.0.0')
+ llvm_strip = find_program(llvm_strip_bin,
+ required : bpf_framework,
+ version : '>= 10.0.0')
deps_found = llvm_strip.found()
endif
endif
libmount = dependency('mount',
version : fuzzer_build ? '>= 0' : '>= 2.30')
-want_libfdisk = get_option('fdisk')
-if want_libfdisk != 'false' and not skip_deps
- libfdisk = dependency('fdisk',
- version : '>= 2.32',
- required : want_libfdisk == 'true')
- have = libfdisk.found()
-else
- have = false
- libfdisk = []
-endif
-conf.set10('HAVE_LIBFDISK', have)
-
-want_passwdqc = get_option('passwdqc')
-want_pwquality = get_option('pwquality')
-if want_passwdqc == 'true' and want_pwquality == 'true'
- error('passwdqc and pwquality cannot be requested simultaneously')
-endif
-
-if want_pwquality != 'false' and want_passwdqc != 'true' and not skip_deps
- libpwquality = dependency('pwquality',
- version : '>= 1.4.1',
- required : want_pwquality == 'true')
- have = libpwquality.found()
-else
- have = false
- libpwquality = []
+libfdisk = dependency('fdisk',
+ version : '>= 2.32',
+ disabler : true,
+ required : get_option('fdisk'))
+conf.set10('HAVE_LIBFDISK', libfdisk.found())
+
+# This prefers pwquality if both are enabled or auto.
+feature = get_option('pwquality').disable_auto_if(get_option('passwdqc').enabled())
+libpwquality = dependency('pwquality',
+ version : '>= 1.4.1',
+ required : feature)
+have = libpwquality.found()
+if not have
+ # libpwquality is used for both features for simplicity
+ libpwquality = dependency('passwdqc',
+ required : get_option('passwdqc'))
endif
conf.set10('HAVE_PWQUALITY', have)
+conf.set10('HAVE_PASSWDQC', not have and libpwquality.found())
-if not have and want_passwdqc != 'false' and not skip_deps
- libpasswdqc = dependency('passwdqc',
- required : want_passwdqc == 'true')
- have = libpasswdqc.found()
-else
- have = false
- libpasswdqc = []
-endif
-conf.set10('HAVE_PASSWDQC', have)
+libseccomp = dependency('libseccomp',
+ version : '>= 2.3.1',
+ required : get_option('seccomp'))
+conf.set10('HAVE_SECCOMP', libseccomp.found())
-want_seccomp = get_option('seccomp')
-if want_seccomp != 'false' and not skip_deps
- libseccomp = dependency('libseccomp',
- version : '>= 2.3.1',
- required : want_seccomp == 'true')
- have = libseccomp.found()
-else
- have = false
- libseccomp = []
-endif
-conf.set10('HAVE_SECCOMP', have)
-
-want_selinux = get_option('selinux')
-if want_selinux != 'false' and not skip_deps
- libselinux = dependency('libselinux',
- version : '>= 2.1.9',
- required : want_selinux == 'true')
- have = libselinux.found()
-else
- have = false
- libselinux = []
-endif
-conf.set10('HAVE_SELINUX', have)
+libselinux = dependency('libselinux',
+ version : '>= 2.1.9',
+ required : get_option('selinux'))
+conf.set10('HAVE_SELINUX', libselinux.found())
-want_apparmor = get_option('apparmor')
-if want_apparmor != 'false' and not skip_deps
- libapparmor = dependency('libapparmor',
- version : '>= 2.13',
- required : want_apparmor == 'true')
- have = libapparmor.found()
-else
- have = false
- libapparmor = []
-endif
-conf.set10('HAVE_APPARMOR', have)
+libapparmor = dependency('libapparmor',
+ version : '>= 2.13',
+ required : get_option('apparmor'))
+conf.set10('HAVE_APPARMOR', libapparmor.found())
have = get_option('smack') and get_option('smack-run-label') != ''
conf.set10('HAVE_SMACK_RUN_LABEL', have)
conf.set_quoted('SMACK_DEFAULT_PROCESS_LABEL', get_option('smack-default-process-label'))
endif
-want_polkit = get_option('polkit')
-install_polkit = false
-install_polkit_pkla = false
-if want_polkit != 'false' and not skip_deps
- install_polkit = true
-
- libpolkit = dependency('polkit-gobject-1',
- required : false)
- if libpolkit.found() and libpolkit.version().version_compare('< 0.106')
- message('Old polkit detected, will install pkla files')
- install_polkit_pkla = true
- endif
+feature = get_option('polkit')
+libpolkit = dependency('polkit-gobject-1',
+ required : feature.disabled() ? feature : false)
+install_polkit = feature.allowed()
+install_polkit_pkla = libpolkit.found() and libpolkit.version().version_compare('< 0.106')
+if install_polkit_pkla
+ message('Old polkit detected, will install pkla files')
endif
conf.set10('ENABLE_POLKIT', install_polkit)
-want_acl = get_option('acl')
-if want_acl != 'false' and not skip_deps
- libacl = dependency('libacl', required : want_acl == 'true')
- have = libacl.found()
-else
- have = false
- libacl = []
-endif
-conf.set10('HAVE_ACL', have)
-
-want_audit = get_option('audit')
-if want_audit != 'false' and not skip_deps
- libaudit = dependency('audit', required : want_audit == 'true')
- have = libaudit.found()
-else
- have = false
- libaudit = []
-endif
-conf.set10('HAVE_AUDIT', have)
-
-want_blkid = get_option('blkid')
-if want_blkid != 'false' and not skip_deps
- libblkid = dependency('blkid', required : want_blkid == 'true')
- have = libblkid.found()
-
- conf.set10('HAVE_BLKID_PROBE_SET_HINT',
- have and cc.has_function('blkid_probe_set_hint', dependencies : libblkid))
-else
- have = false
- libblkid = []
-endif
-conf.set10('HAVE_BLKID', have)
-
-want_kmod = get_option('kmod')
-if want_kmod != 'false' and not skip_deps
- libkmod = dependency('libkmod',
- version : '>= 15',
- required : want_kmod == 'true')
- have = libkmod.found()
-else
- have = false
- libkmod = []
-endif
-conf.set10('HAVE_KMOD', have)
-
-want_xenctrl = get_option('xenctrl')
-if want_xenctrl != 'false' and not skip_deps
- libxenctrl = dependency('xencontrol',
- version : '>= 4.9',
- required : want_xenctrl == 'true')
- have = libxenctrl.found()
-else
- have = false
- libxenctrl = []
-endif
-conf.set10('HAVE_XENCTRL', have)
-
-want_pam = get_option('pam')
-if want_pam != 'false' and not skip_deps
- libpam = dependency('pam', required : false)
- if not libpam.found()
- # Debian older than bookworm and Ubuntu older than 22.10 do not provide the .pc file.
- libpam = cc.find_library('pam', required : want_pam == 'true')
+libacl = dependency('libacl',
+ required : get_option('acl'))
+conf.set10('HAVE_ACL', libacl.found())
+
+libaudit = dependency('audit',
+ required : get_option('audit'))
+conf.set10('HAVE_AUDIT', libaudit.found())
+
+libblkid = dependency('blkid',
+ required : get_option('blkid'))
+conf.set10('HAVE_BLKID', libblkid.found())
+conf.set10('HAVE_BLKID_PROBE_SET_HINT',
+ libblkid.found() and cc.has_function('blkid_probe_set_hint', dependencies : libblkid))
+
+libkmod = dependency('libkmod',
+ version : '>= 15',
+ required : get_option('kmod'))
+conf.set10('HAVE_KMOD', libkmod.found())
+
+libxenctrl = dependency('xencontrol',
+ version : '>= 4.9',
+ required : get_option('xenctrl'))
+conf.set10('HAVE_XENCTRL', libxenctrl.found())
+
+feature = get_option('pam')
+libpam = dependency('pam',
+ required : feature.disabled() ? feature : false)
+if not libpam.found()
+ # Debian older than bookworm and Ubuntu older than 22.10 do not provide the .pc file.
+ libpam = cc.find_library('pam', required : feature)
+endif
+libpam_misc = dependency('pam_misc',
+ required : feature.disabled() ? feature : false)
+if not libpam_misc.found()
+ libpam_misc = cc.find_library('pam_misc', required : feature)
+endif
+conf.set10('HAVE_PAM', libpam.found() and libpam_misc.found())
+
+libmicrohttpd = dependency('libmicrohttpd',
+ version : '>= 0.9.33',
+ required : get_option('microhttpd'))
+conf.set10('HAVE_MICROHTTPD', libmicrohttpd.found())
+
+libcryptsetup = get_option('libcryptsetup')
+libcryptsetup_plugins = get_option('libcryptsetup-plugins')
+if libcryptsetup_plugins.enabled()
+ if libcryptsetup.disabled()
+ error('libcryptsetup-plugins can not be requested without libcryptsetup')
endif
- libpam_misc = dependency('pam_misc', required : false)
- if not libpam_misc.found()
- libpam_misc = cc.find_library('pam_misc', required : want_pam == 'true')
- endif
- have = libpam.found() and libpam_misc.found()
-else
- have = false
- libpam = []
- libpam_misc = []
-endif
-conf.set10('HAVE_PAM', have)
-
-want_microhttpd = get_option('microhttpd')
-if want_microhttpd != 'false' and not skip_deps
- libmicrohttpd = dependency('libmicrohttpd',
- version : '>= 0.9.33',
- required : want_microhttpd == 'true')
- have = libmicrohttpd.found()
-else
- have = false
- libmicrohttpd = []
-endif
-conf.set10('HAVE_MICROHTTPD', have)
-
-want_libcryptsetup = get_option('libcryptsetup')
-want_libcryptsetup_plugins = get_option('libcryptsetup-plugins')
-
-if want_libcryptsetup_plugins == 'true' and want_libcryptsetup == 'false'
- error('libcryptsetup-plugins can not be requested without libcryptsetup')
-endif
-
-if want_libcryptsetup != 'false' and not skip_deps
- libcryptsetup = dependency('libcryptsetup',
- version : want_libcryptsetup_plugins == 'true' ? '>= 2.4.0' : '>= 2.0.1',
- required : want_libcryptsetup == 'true' or want_libcryptsetup_plugins == 'true')
- have = libcryptsetup.found()
-
- foreach ident : ['crypt_set_metadata_size',
- 'crypt_activate_by_signed_key',
- 'crypt_token_max',
- 'crypt_reencrypt_init_by_passphrase',
- 'crypt_reencrypt',
- 'crypt_set_data_offset']
- have_ident = have and cc.has_function(
- ident,
- prefix : '#include <libcryptsetup.h>',
- dependencies : libcryptsetup)
- conf.set10('HAVE_' + ident.to_upper(), have_ident)
- endforeach
-else
- have = false
- libcryptsetup = []
-endif
+ libcryptsetup = libcryptsetup_plugins
+endif
+
+libcryptsetup = dependency('libcryptsetup',
+ version : libcryptsetup_plugins.enabled() ? '>= 2.4.0' : '>= 2.0.1',
+ required : libcryptsetup)
+
+have = libcryptsetup.found()
+foreach ident : ['crypt_set_metadata_size',
+ 'crypt_activate_by_signed_key',
+ 'crypt_token_max',
+ 'crypt_reencrypt_init_by_passphrase',
+ 'crypt_reencrypt',
+ 'crypt_set_data_offset']
+ have_ident = have and cc.has_function(
+ ident,
+ prefix : '#include <libcryptsetup.h>',
+ dependencies : libcryptsetup)
+ conf.set10('HAVE_' + ident.to_upper(), have_ident)
+endforeach
conf.set10('HAVE_LIBCRYPTSETUP', have)
-if want_libcryptsetup_plugins != 'false' and not skip_deps
+# TODO: Use has_function(required : libcryptsetup_plugins) with meson >= 1.3.0
+if libcryptsetup_plugins.allowed()
have = (cc.has_function(
'crypt_activate_by_token_pin',
prefix : '#include <libcryptsetup.h>',
endif
conf.set10('HAVE_LIBCRYPTSETUP_PLUGINS', have)
-want_libcurl = get_option('libcurl')
-if want_libcurl != 'false' and not skip_deps
- libcurl = dependency('libcurl',
- version : '>= 7.32.0',
- required : want_libcurl == 'true')
- have = libcurl.found()
-else
- have = false
- libcurl = []
-endif
-conf.set10('HAVE_LIBCURL', have)
+libcurl = dependency('libcurl',
+ version : '>= 7.32.0',
+ required : get_option('libcurl'))
+conf.set10('HAVE_LIBCURL', libcurl.found())
conf.set10('CURL_NO_OLDIES', conf.get('BUILD_MODE_DEVELOPER') == 1)
-want_libidn = get_option('libidn')
-want_libidn2 = get_option('libidn2')
-if want_libidn == 'true' and want_libidn2 == 'true'
- error('libidn and libidn2 cannot be requested simultaneously')
-endif
-
-if want_libidn2 != 'false' and want_libidn != 'true' and not skip_deps
- libidn = dependency('libidn2',
- required : want_libidn2 == 'true')
- have = libidn.found()
-else
- have = false
- libidn = []
-endif
-conf.set10('HAVE_LIBIDN2', have)
-if not have and want_libidn != 'false' and not skip_deps
+feature = get_option('libidn2').disable_auto_if(get_option('libidn').enabled())
+libidn = dependency('libidn2',
+ required : feature)
+have = libidn.found()
+if not have
# libidn is used for both libidn and libidn2 objects
libidn = dependency('libidn',
- required : want_libidn == 'true')
- have = libidn.found()
-else
- have = false
+ required : get_option('libidn'))
endif
-conf.set10('HAVE_LIBIDN', have)
+conf.set10('HAVE_LIBIDN', not have and libidn.found())
+conf.set10('HAVE_LIBIDN2', have)
-want_libiptc = get_option('libiptc')
-if want_libiptc != 'false' and not skip_deps
- libiptc = dependency('libiptc',
- required : want_libiptc == 'true')
- have = libiptc.found()
-else
- have = false
- libiptc = []
-endif
-conf.set10('HAVE_LIBIPTC', have)
+libiptc = dependency('libiptc',
+ required : get_option('libiptc'))
+conf.set10('HAVE_LIBIPTC', libiptc.found())
-want_qrencode = get_option('qrencode')
-if want_qrencode != 'false' and not skip_deps
- libqrencode = dependency('libqrencode',
- version : '>= 3',
- required : want_qrencode == 'true')
- have = libqrencode.found()
-else
- have = false
- libqrencode = []
-endif
-conf.set10('HAVE_QRENCODE', have)
+libqrencode = dependency('libqrencode',
+ version : '>= 3',
+ required : get_option('qrencode'))
+conf.set10('HAVE_QRENCODE', libqrencode.found())
-want_gcrypt = get_option('gcrypt')
-if want_gcrypt != 'false' and not skip_deps
- libgcrypt = dependency('libgcrypt', required : want_gcrypt == 'true')
- libgpg_error = dependency('gpg-error', required : false)
- if not libgpg_error.found()
- # CentOS 8 does not provide the .pc file.
- libgpg_error = cc.find_library('gpg-error', required : want_gcrypt == 'true')
- endif
- have = libgcrypt.found() and libgpg_error.found()
-else
- have = false
+feature = get_option('gcrypt')
+libgcrypt = dependency('libgcrypt',
+ required : feature)
+libgpg_error = dependency('gpg-error',
+ required : feature.disabled() ? feature : false)
+if not libgpg_error.found()
+ # CentOS 8 does not provide the .pc file.
+ libgpg_error = cc.find_library('gpg-error', required : feature)
endif
+
+have = libgcrypt.found() and libgpg_error.found()
if not have
# link to neither of the libs if one is not found
libgcrypt = []
endif
conf.set10('HAVE_GCRYPT', have)
-want_gnutls = get_option('gnutls')
-if want_gnutls != 'false' and not skip_deps
- libgnutls = dependency('gnutls',
- version : '>= 3.1.4',
- required : want_gnutls == 'true')
- have = libgnutls.found()
-else
- have = false
- libgnutls = []
-endif
-conf.set10('HAVE_GNUTLS', have)
-
-want_openssl = get_option('openssl')
-if want_openssl != 'false' and not skip_deps
- libopenssl = dependency('openssl',
- version : '>= 1.1.0',
- required : want_openssl == 'true')
- have = libopenssl.found()
-else
- have = false
- libopenssl = []
-endif
-conf.set10('HAVE_OPENSSL', have)
-
-want_p11kit = get_option('p11kit')
-if want_p11kit != 'false' and not skip_deps
- libp11kit = dependency('p11-kit-1',
- version : '>= 0.23.3',
- required : want_p11kit == 'true')
- have = libp11kit.found()
- libp11kit_cflags = libp11kit.partial_dependency(includes: true, compile_args: true)
-else
- have = false
- libp11kit_cflags = []
- libp11kit = []
-endif
-conf.set10('HAVE_P11KIT', have)
-
-want_libfido2 = get_option('libfido2')
-if want_libfido2 != 'false' and not skip_deps
- if conf.get('HAVE_OPENSSL') == 1
- libfido2 = dependency('libfido2',
- required : want_libfido2 == 'true')
- have = libfido2.found()
- elif want_libfido2 == 'true'
- error('libfido2=true requires openssl')
- else
- have = false
- libfido2 = []
- endif
-else
- have = false
- libfido2 = []
-endif
-conf.set10('HAVE_LIBFIDO2', have)
-
-want_tpm2 = get_option('tpm2')
-if want_tpm2 != 'false' and not skip_deps
- tpm2 = dependency('tss2-esys tss2-rc tss2-mu tss2-tcti-device',
- required : want_tpm2 == 'true')
- have = tpm2.found()
- have_esys3 = tpm2.version().version_compare('>= 3.0.0')
-else
- have = false
- have_esys3 = false
- tpm2 = []
-endif
-conf.set10('HAVE_TPM2', have)
-conf.set10('HAVE_TSS2_ESYS3', have_esys3)
-
-want_elfutils = get_option('elfutils')
-if want_elfutils != 'false' and not skip_deps
- libdw = dependency('libdw',
- required : want_elfutils == 'true')
- have = libdw.found()
-
- # New in elfutils 0.177
- conf.set10('HAVE_DWELF_ELF_E_MACHINE_STRING',
- have and cc.has_function('dwelf_elf_e_machine_string', dependencies : libdw))
-else
- have = false
- libdw = []
-endif
-conf.set10('HAVE_ELFUTILS', have)
-
-want_zlib = get_option('zlib')
-if want_zlib != 'false' and not skip_deps
- libz = dependency('zlib',
- required : want_zlib == 'true')
- have = libz.found()
-else
- have = false
- libz = []
-endif
-conf.set10('HAVE_ZLIB', have)
-
-want_bzip2 = get_option('bzip2')
-if want_bzip2 != 'false' and not skip_deps
- libbzip2 = dependency('bzip2', required : false)
- if not libbzip2.found()
- # Debian and Ubuntu do not provide the .pc file.
- libbzip2 = cc.find_library('bz2', required : want_bzip2 == 'true')
- endif
- have = libbzip2.found()
-else
- have = false
- libbzip2 = []
-endif
-conf.set10('HAVE_BZIP2', have)
-
-want_xz = get_option('xz')
-if want_xz != 'false' and not skip_deps
- libxz = dependency('liblzma',
- required : want_xz == 'true')
- have_xz = libxz.found()
-else
- have_xz = false
- libxz = []
-endif
-conf.set10('HAVE_XZ', have_xz)
-
-want_lz4 = get_option('lz4')
-if want_lz4 != 'false' and not skip_deps
- liblz4 = dependency('liblz4',
- version : '>= 1.3.0',
- required : want_lz4 == 'true')
- have_lz4 = liblz4.found()
-else
- have_lz4 = false
- liblz4 = []
-endif
-conf.set10('HAVE_LZ4', have_lz4)
-
-want_zstd = get_option('zstd')
-if want_zstd != 'false' and not skip_deps
- libzstd = dependency('libzstd',
- required : want_zstd == 'true',
- version : '>= 1.4.0')
- have_zstd = libzstd.found()
-else
- have_zstd = false
- libzstd = []
-endif
-conf.set10('HAVE_ZSTD', have_zstd)
-
-conf.set10('HAVE_COMPRESSION', have_xz or have_lz4 or have_zstd)
+libgnutls = dependency('gnutls',
+ version : '>= 3.1.4',
+ required : get_option('gnutls'))
+conf.set10('HAVE_GNUTLS', libgnutls.found())
+
+libopenssl = dependency('openssl',
+ version : '>= 1.1.0',
+ required : get_option('openssl'))
+conf.set10('HAVE_OPENSSL', libopenssl.found())
+
+libp11kit = dependency('p11-kit-1',
+ version : '>= 0.23.3',
+ required : get_option('p11kit'))
+conf.set10('HAVE_P11KIT', libp11kit.found())
+libp11kit_cflags = libp11kit.partial_dependency(includes: true, compile_args: true)
+
+feature = get_option('libfido2').require(
+ conf.get('HAVE_OPENSSL') == 1,
+ error_message : 'openssl required')
+libfido2 = dependency('libfido2',
+ required : feature)
+conf.set10('HAVE_LIBFIDO2', libfido2.found())
+
+tpm2 = dependency('tss2-esys tss2-rc tss2-mu tss2-tcti-device',
+ required : get_option('tpm2'))
+conf.set10('HAVE_TPM2', tpm2.found())
+conf.set10('HAVE_TSS2_ESYS3', tpm2.found() and tpm2.version().version_compare('>= 3.0.0'))
+
+libdw = dependency('libdw',
+ required : get_option('elfutils'))
+conf.set10('HAVE_ELFUTILS', libdw.found())
+# New in elfutils 0.177
+conf.set10('HAVE_DWELF_ELF_E_MACHINE_STRING',
+ libdw.found() and cc.has_function('dwelf_elf_e_machine_string', dependencies : libdw))
+
+libz = dependency('zlib',
+ required : get_option('zlib'))
+conf.set10('HAVE_ZLIB', libz.found())
+
+feature = get_option('bzip2')
+libbzip2 = dependency('bzip2',
+ required : feature.disabled() ? feature : false)
+if not libbzip2.found()
+ # Debian and Ubuntu do not provide the .pc file.
+ libbzip2 = cc.find_library('bz2', required : feature)
+endif
+conf.set10('HAVE_BZIP2', libbzip2.found())
+
+libxz = dependency('liblzma',
+ required : get_option('xz'))
+conf.set10('HAVE_XZ', libxz.found())
+
+liblz4 = dependency('liblz4',
+ version : '>= 1.3.0',
+ required : get_option('lz4'))
+conf.set10('HAVE_LZ4', liblz4.found())
+
+libzstd = dependency('libzstd',
+ version : '>= 1.4.0',
+ required : get_option('zstd'))
+conf.set10('HAVE_ZSTD', libzstd.found())
+
+conf.set10('HAVE_COMPRESSION', libxz.found() or liblz4.found() or libzstd.found())
compression = get_option('default-compression')
if compression == 'auto'
- if have_zstd
+ if libzstd.found()
compression = 'zstd'
- elif have_lz4
+ elif liblz4.found()
compression = 'lz4'
- elif have_xz
+ elif libxz.found()
compression = 'xz'
else
compression = 'none'
endif
-elif compression == 'zstd' and not have_zstd
+elif compression == 'zstd' and not libzstd.found()
error('default-compression=zstd requires zstd')
-elif compression == 'lz4' and not have_lz4
+elif compression == 'lz4' and not liblz4.found()
error('default-compression=lz4 requires lz4')
-elif compression == 'xz' and not have_xz
+elif compression == 'xz' and not libxz.found()
error('default-compression=xz requires xz')
endif
conf.set('DEFAULT_COMPRESSION', 'COMPRESSION_@0@'.format(compression.to_upper()))
-want_xkbcommon = get_option('xkbcommon')
-if want_xkbcommon != 'false' and not skip_deps
- libxkbcommon = dependency('xkbcommon',
- version : '>= 0.3.0',
- required : want_xkbcommon == 'true')
- have = libxkbcommon.found()
-else
- have = false
- libxkbcommon = []
-endif
-conf.set10('HAVE_XKBCOMMON', have)
+libxkbcommon = dependency('xkbcommon',
+ version : '>= 0.3.0',
+ required : get_option('xkbcommon'))
+conf.set10('HAVE_XKBCOMMON', libxkbcommon.found())
-want_pcre2 = get_option('pcre2')
-if want_pcre2 != 'false' and not skip_deps
- libpcre2 = dependency('libpcre2-8',
- required : want_pcre2 == 'true')
- have = libpcre2.found()
-else
- have = false
- libpcre2 = []
-endif
-conf.set10('HAVE_PCRE2', have)
-
-want_glib = get_option('glib')
-if want_glib != 'false' and not skip_deps
- libglib = dependency('glib-2.0',
- version : '>= 2.22.0',
- required : want_glib == 'true')
- libgobject = dependency('gobject-2.0',
- version : '>= 2.22.0',
- required : want_glib == 'true')
- libgio = dependency('gio-2.0',
- required : want_glib == 'true')
- have = libglib.found() and libgobject.found() and libgio.found()
-else
- have = false
- libglib = []
- libgobject = []
- libgio = []
-endif
-conf.set10('HAVE_GLIB', have)
-
-want_dbus = get_option('dbus')
-if want_dbus != 'false' and not skip_deps
- libdbus = dependency('dbus-1',
- version : '>= 1.3.2',
- required : want_dbus == 'true')
- have = libdbus.found()
-else
- have = false
- libdbus = []
-endif
-conf.set10('HAVE_DBUS', have)
+libpcre2 = dependency('libpcre2-8',
+ required : get_option('pcre2'))
+conf.set10('HAVE_PCRE2', libpcre2.found())
-dbusdatadir = datadir / 'dbus-1'
-if conf.get('HAVE_DBUS') == 1
- dbusdatadir = libdbus.get_variable(pkgconfig: 'datadir', default_value: datadir) / 'dbus-1'
-endif
+libglib = dependency('glib-2.0',
+ version : '>= 2.22.0',
+ required : get_option('glib'))
+libgobject = dependency('gobject-2.0',
+ version : '>= 2.22.0',
+ required : get_option('glib'))
+libgio = dependency('gio-2.0',
+ required : get_option('glib'))
+conf.set10('HAVE_GLIB', libglib.found() and libgobject.found() and libgio.found())
+
+libdbus = dependency('dbus-1',
+ version : '>= 1.3.2',
+ required : get_option('dbus'))
+conf.set10('HAVE_DBUS', libdbus.found())
+
+dbusdatadir = libdbus.get_variable(pkgconfig: 'datadir', default_value: datadir) / 'dbus-1'
dbuspolicydir = get_option('dbuspolicydir')
if dbuspolicydir == ''
dbussessionservicedir = get_option('dbussessionservicedir')
if dbussessionservicedir == ''
- dbussessionservicedir = dbusdatadir / 'services'
- if conf.get('HAVE_DBUS') == 1
- dbussessionservicedir = libdbus.get_variable(pkgconfig: 'session_bus_services_dir', default_value: dbussessionservicedir)
- endif
+ dbussessionservicedir = libdbus.get_variable(pkgconfig: 'session_bus_services_dir', default_value: dbusdatadir / 'services')
endif
dbussystemservicedir = get_option('dbussystemservicedir')
if dbussystemservicedir == ''
- dbussystemservicedir = dbusdatadir / 'system-services'
- if conf.get('HAVE_DBUS') == 1
- dbussystemservicedir = libdbus.get_variable(pkgconfig: 'system_bus_services_dir', default_value: dbussystemservicedir)
- endif
+ dbussystemservicedir = libdbus.get_variable(pkgconfig: 'system_bus_services_dir', default_value: dbusdatadir / 'system-services')
endif
dbus_interfaces_dir = get_option('dbus-interfaces-dir')
dbus_interfaces_dir = 'no'
warning('Exporting D-Bus interface XML files is disabled during cross build. Pass path or "yes" to force enable.')
else
- dbus_interfaces_dir = dbusdatadir / 'interfaces'
- if conf.get('HAVE_DBUS') == 1
- dbus_interfaces_dir = libdbus.get_variable(pkgconfig: 'interfaces_dir', default_value: dbus_interfaces_dir)
- endif
+ dbus_interfaces_dir = libdbus.get_variable(pkgconfig: 'interfaces_dir', default_value: dbusdatadir / 'interfaces')
endif
endif
conf.set10('DNS_OVER_TLS_USE_OPENSSL', have_openssl)
default_dns_over_tls = get_option('default-dns-over-tls')
-if skip_deps
- default_dns_over_tls = 'no'
-endif
if default_dns_over_tls != 'no' and conf.get('ENABLE_DNS_OVER_TLS') == 0
message('default-dns-over-tls cannot be enabled or set to opportunistic when DNS-over-TLS support is disabled. Setting default-dns-over-tls to no.')
default_dns_over_tls = 'no'
'RESOLVE_SUPPORT_' + default_llmnr.to_upper())
conf.set_quoted('DEFAULT_LLMNR_MODE_STR', default_llmnr)
-want_repart = get_option('repart')
-if want_repart != 'false'
- have = conf.get('HAVE_LIBFDISK') == 1
- if want_repart == 'true' and not have
- error('repart support was requested, but dependencies are not available')
- endif
-else
- have = false
-endif
+have = get_option('repart').require(
+ conf.get('HAVE_LIBFDISK') == 1,
+ error_message : 'fdisk required').allowed()
conf.set10('ENABLE_REPART', have)
default_dnssec = get_option('default-dnssec')
-if skip_deps
- default_dnssec = 'no'
-endif
if default_dnssec != 'no' and conf.get('HAVE_OPENSSL_OR_GCRYPT') == 0
message('default-dnssec cannot be set to yes or allow-downgrade openssl and gcrypt are disabled. Setting default-dnssec to no.')
default_dnssec = 'no'
'DNSSEC_' + default_dnssec.underscorify().to_upper())
conf.set_quoted('DEFAULT_DNSSEC_MODE_STR', default_dnssec)
-want_sysupdate = get_option('sysupdate')
-if want_sysupdate != 'false'
- have = (conf.get('HAVE_OPENSSL') == 1 and
- conf.get('HAVE_LIBFDISK') == 1)
- if want_sysupdate == 'true' and not have
- error('sysupdate support was requested, but dependencies are not available')
- endif
-else
- have = false
-endif
+have = get_option('sysupdate').require(
+ conf.get('HAVE_OPENSSL') == 1 and
+ conf.get('HAVE_LIBFDISK') == 1,
+ error_message : 'fdisk and openssl required').allowed()
conf.set10('ENABLE_SYSUPDATE', have)
-want_importd = get_option('importd')
-if want_importd != 'false'
- have = (conf.get('HAVE_LIBCURL') == 1 and
- conf.get('HAVE_OPENSSL_OR_GCRYPT') == 1 and
- conf.get('HAVE_ZLIB') == 1 and
- conf.get('HAVE_XZ') == 1)
- if want_importd == 'true' and not have
- error('importd support was requested, but dependencies are not available')
- endif
-else
- have = false
-endif
+have = get_option('importd').require(
+ conf.get('HAVE_LIBCURL') == 1 and
+ conf.get('HAVE_OPENSSL_OR_GCRYPT') == 1 and
+ conf.get('HAVE_ZLIB') == 1 and
+ conf.get('HAVE_XZ') == 1,
+ error_message : 'curl, openssl/grypt, zlib and xz required').allowed()
conf.set10('ENABLE_IMPORTD', have)
-want_homed = get_option('homed')
-if want_homed != 'false'
- have = (conf.get('HAVE_OPENSSL') == 1 and
- conf.get('HAVE_LIBFDISK') == 1 and
- conf.get('HAVE_LIBCRYPTSETUP') == 1)
- if want_homed == 'true' and not have
- error('homed support was requested, but dependencies are not available')
- endif
-else
- have = false
-endif
+have = get_option('homed').require(
+ conf.get('HAVE_OPENSSL') == 1 and
+ conf.get('HAVE_LIBFDISK') == 1 and
+ conf.get('HAVE_LIBCRYPTSETUP') == 1,
+ error_message : 'openssl, fdisk and libcryptsetup required').allowed()
conf.set10('ENABLE_HOMED', have)
have = have and conf.get('HAVE_PAM') == 1
conf.set10('ENABLE_PAM_HOME', have)
-want_remote = get_option('remote')
-if want_remote != 'false'
- have_deps = [conf.get('HAVE_MICROHTTPD') == 1,
- conf.get('HAVE_LIBCURL') == 1]
- # sd-j-remote requires µhttpd, and sd-j-upload requires libcurl, so
- # it's possible to build one without the other. Complain only if
- # support was explicitly requested. The auxiliary files like sysusers
- # config should be installed when any of the programs are built.
- if want_remote == 'true' and not (have_deps[0] and have_deps[1])
- error('remote support was requested, but dependencies are not available')
- endif
- have = have_deps[0] or have_deps[1]
-else
- have = false
-endif
+feature = get_option('remote')
+have_deps = [conf.get('HAVE_MICROHTTPD') == 1,
+ conf.get('HAVE_LIBCURL') == 1]
+# sd-j-remote requires µhttpd, and sd-j-upload requires libcurl, so
+# it's possible to build one without the other. Complain only if
+# support was explicitly requested. The auxiliary files like sysusers
+# config should be installed when any of the programs are built.
+if feature.enabled() and not (have_deps[0] and have_deps[1])
+ error('remote support was requested, but dependencies are not available')
+endif
+have = feature.allowed() and (have_deps[0] or have_deps[1])
conf.set10('ENABLE_REMOTE', have)
foreach term : ['analyze',
foreach tuple : [['nss-mymachines', 'machined'],
['nss-resolve', 'resolve']]
want = get_option(tuple[0])
- if want != 'false'
+ if want.allowed()
have = get_option(tuple[1])
- if want == 'true' and not have
+ if want.enabled() and not have
error('@0@ is requested but @1@ is disabled'.format(tuple[0], tuple[1]))
endif
else
'x86' : 'ia32',
}.get(host_machine.cpu_family(), '')
-if get_option('bootloader') != 'false' and efi_arch != ''
- conf.set_quoted('EFI_MACHINE_TYPE_NAME', efi_arch)
-elif get_option('bootloader') == 'false' and efi_arch != ''
- # Ensure that if the option is explicitly set to false, then no EFI code is built, including tests
- efi_arch = ''
-elif get_option('bootloader') == 'true' and efi_arch == ''
- error('EFI not supported for this arch.')
-endif
+pyelftools = pymod.find_installation('python3',
+ required : get_option('bootloader'),
+ modules : ['elftools'])
+
+have = get_option('bootloader').require(
+ pyelftools.found() and get_option('efi') and efi_arch != '',
+ error_message : 'unsupported EFI arch or EFI support is disabled').allowed()
+conf.set10('ENABLE_BOOTLOADER', have)
+conf.set_quoted('EFI_MACHINE_TYPE_NAME', have ? efi_arch : '')
efi_arch_alt = ''
efi_cpu_family_alt = ''
-if efi_arch == 'x64' and cc.links('''
+if have and efi_arch == 'x64' and cc.links('''
#include <limits.h>
int main(int argc, char *argv[]) {
return __builtin_popcount(argc - CHAR_MAX);
efi_cpu_family_alt = 'x86'
endif
-have_pyelftools = pymod.find_installation('python3', required : false, modules : ['elftools']).found()
-if get_option('bootloader') == 'true' and not have_pyelftools
- error('EFI bootloader support requires pyelftools.')
-endif
-
-conf.set10(
- 'ENABLE_BOOTLOADER',
- get_option('efi') and
- get_option('bootloader') in ['auto', 'true'] and
- efi_arch != '' and
- have_pyelftools,
-)
-
-if get_option('ukify') == 'auto'
- want_ukify = python_39 and conf.get('ENABLE_BOOTLOADER') == 1
-elif get_option('ukify') == 'true' and (not python_39 or conf.get('ENABLE_BOOTLOADER') != 1)
- error('ukify requires Python >= 3.9 and -Dbootloader=true')
-else
- want_ukify = get_option('ukify') == 'true'
-endif
+want_ukify = get_option('ukify').require(
+ python_39 and conf.get('ENABLE_BOOTLOADER') == 1,
+ error_message : 'Python >= 3.9 and -Dbootloader=true required').allowed()
conf.set10('ENABLE_UKIFY', want_ukify)
############################################################
if is_nss
# We cannot use shared_module because it does not support version suffix.
# Unfortunately shared_library insists on creating the symlink…
- meson.add_install_script('sh', '-c', 'rm $DESTDIR@0@/lib@1@.so'.format(libdir, name),
+ meson.add_install_script(sh, '-c', 'rm $DESTDIR@0@/lib@1@.so'.format(libdir, name),
install_tag : 'nss')
endif
description : 'support for environment.d')
option('binfmt', type : 'boolean',
description : 'support for custom binary formats')
-option('repart', type : 'combo', choices : ['auto', 'true', 'false'],
+option('repart', type : 'feature', deprecated : { 'true' : 'enabled', 'false' : 'disabled' },
description : 'install the systemd-repart tool')
-option('sysupdate', type : 'combo', choices : ['auto', 'true', 'false'],
+option('sysupdate', type : 'feature', deprecated : { 'true' : 'enabled', 'false' : 'disabled' },
description : 'install the systemd-sysupdate tool')
option('coredump', type : 'boolean',
description : 'install the coredump handler')
description : 'install the systemd-sysext stack')
option('userdb', type : 'boolean',
description : 'install the systemd-userdbd stack')
-option('homed', type : 'combo', choices : ['auto', 'true', 'false'],
+option('homed', type : 'feature', deprecated : { 'true' : 'enabled', 'false' : 'disabled' },
description : 'install the systemd-homed stack')
option('networkd', type : 'boolean',
description : 'install the systemd-networkd stack')
description : 'install the systemd-timedated daemon')
option('timesyncd', type : 'boolean',
description : 'install the systemd-timesyncd daemon')
-option('remote', type : 'combo', choices : ['auto', 'true', 'false'],
+option('remote', type : 'feature', deprecated : { 'true' : 'enabled', 'false' : 'disabled' },
description : 'support for "journal over the network"')
option('create-log-dirs', type : 'boolean',
description : 'create /var/log/journal{,/remote}')
option('nss-myhostname', type : 'boolean',
description : 'install nss-myhostname module')
-option('nss-mymachines', type : 'combo', choices : ['auto', 'true', 'false'],
+option('nss-mymachines', type : 'feature', deprecated : { 'true' : 'enabled', 'false' : 'disabled' },
description : 'install nss-mymachines module')
-option('nss-resolve', type : 'combo', choices : ['auto', 'true', 'false'],
+option('nss-resolve', type : 'feature', deprecated : { 'true' : 'enabled', 'false' : 'disabled' },
description : 'install nss-resolve module')
option('nss-systemd', type : 'boolean',
description : 'install nss-systemd module')
description : 'support for the sysusers configuration')
option('tmpfiles', type : 'boolean',
description : 'support for tmpfiles.d')
-option('importd', type : 'combo', choices : ['auto', 'true', 'false'],
+option('importd', type : 'feature', deprecated : { 'true' : 'enabled', 'false' : 'disabled' },
description : 'install the systemd-importd daemon')
option('hwdb', type : 'boolean',
description : 'support for the hardware database')
description : 'support for the rfkill tools')
option('xdg-autostart', type : 'boolean',
description : 'install the xdg-autostart-generator and unit')
-option('man', type : 'combo', choices : ['auto', 'true', 'false'],
- value : 'false',
+option('man', type : 'feature', deprecated : { 'true' : 'enabled', 'false' : 'disabled' },
+ value : 'disabled',
description : 'build and install man pages')
-option('html', type : 'combo', choices : ['auto', 'true', 'false'],
- value : 'false',
+option('html', type : 'feature', deprecated : { 'true' : 'enabled', 'false' : 'disabled' },
+ value : 'disabled',
description : 'build and install html pages')
option('translations', type : 'boolean', value : true,
description : 'build and install translations')
description : 'the address and dir to upload docs too',
value : 'www.freedesktop.org:/srv/www.freedesktop.org/www/software/systemd')
-option('seccomp', type : 'combo', choices : ['auto', 'true', 'false'],
+option('seccomp', type : 'feature', deprecated : { 'true' : 'enabled', 'false' : 'disabled' },
description : 'SECCOMP support')
-option('selinux', type : 'combo', choices : ['auto', 'true', 'false'],
+option('selinux', type : 'feature', deprecated : { 'true' : 'enabled', 'false' : 'disabled' },
description : 'SELinux support')
-option('apparmor', type : 'combo', choices : ['auto', 'true', 'false'],
+option('apparmor', type : 'feature', deprecated : { 'true' : 'enabled', 'false' : 'disabled' },
description : 'AppArmor support')
option('smack', type : 'boolean',
description : 'SMACK support')
description : 'run systemd --system itself with a specific SMACK label')
option('smack-default-process-label', type : 'string',
description : 'default SMACK label for executed processes')
-option('polkit', type : 'combo', choices : ['auto', 'true', 'false'],
+option('polkit', type : 'feature', deprecated : { 'true' : 'enabled', 'false' : 'disabled' },
description : 'polkit support')
option('ima', type : 'boolean',
description : 'IMA support')
-option('acl', type : 'combo', choices : ['auto', 'true', 'false'],
+option('acl', type : 'feature', deprecated : { 'true' : 'enabled', 'false' : 'disabled' },
description : 'libacl support')
-option('audit', type : 'combo', choices : ['auto', 'true', 'false'],
+option('audit', type : 'feature', deprecated : { 'true' : 'enabled', 'false' : 'disabled' },
description : 'libaudit support')
-option('blkid', type : 'combo', choices : ['auto', 'true', 'false'],
+option('blkid', type : 'feature', deprecated : { 'true' : 'enabled', 'false' : 'disabled' },
description : 'libblkid support')
-option('fdisk', type : 'combo', choices : ['auto', 'true', 'false'],
+option('fdisk', type : 'feature', deprecated : { 'true' : 'enabled', 'false' : 'disabled' },
description : 'libfdisk support')
-option('kmod', type : 'combo', choices : ['auto', 'true', 'false'],
+option('kmod', type : 'feature', deprecated : { 'true' : 'enabled', 'false' : 'disabled' },
description : 'support for loadable modules')
-option('xenctrl', type : 'combo', choices : ['auto', 'true', 'false'],
+option('xenctrl', type : 'feature', deprecated : { 'true' : 'enabled', 'false' : 'disabled' },
description : 'support for Xen kexec')
-option('pam', type : 'combo', choices : ['auto', 'true', 'false'],
+option('pam', type : 'feature', deprecated : { 'true' : 'enabled', 'false' : 'disabled' },
description : 'PAM support')
-option('passwdqc', type : 'combo', choices : ['auto', 'true', 'false'],
+option('passwdqc', type : 'feature', deprecated : { 'true' : 'enabled', 'false' : 'disabled' },
description : 'libpasswdqc support')
-option('pwquality', type : 'combo', choices : ['auto', 'true', 'false'],
+option('pwquality', type : 'feature', deprecated : { 'true' : 'enabled', 'false' : 'disabled' },
description : 'libpwquality support')
-option('microhttpd', type : 'combo', choices : ['auto', 'true', 'false'],
+option('microhttpd', type : 'feature', deprecated : { 'true' : 'enabled', 'false' : 'disabled' },
description : 'libµhttpd support')
-option('libcryptsetup', type : 'combo', choices : ['auto', 'true', 'false'],
+option('libcryptsetup', type : 'feature', deprecated : { 'true' : 'enabled', 'false' : 'disabled' },
description : 'libcryptsetup support')
-option('libcryptsetup-plugins', type : 'combo', choices : ['auto', 'true', 'false'],
+option('libcryptsetup-plugins', type : 'feature', deprecated : { 'true' : 'enabled', 'false' : 'disabled' },
description : 'libcryptsetup LUKS2 external token handlers support (plugins)')
-option('libcurl', type : 'combo', choices : ['auto', 'true', 'false'],
+option('libcurl', type : 'feature', deprecated : { 'true' : 'enabled', 'false' : 'disabled' },
description : 'libcurl support')
option('idn', type : 'boolean',
description : 'use IDN when printing hostnames')
-option('libidn2', type : 'combo', choices : ['auto', 'true', 'false'],
+option('libidn2', type : 'feature', deprecated : { 'true' : 'enabled', 'false' : 'disabled' },
description : 'libidn2 support')
-option('libidn', type : 'combo', choices : ['auto', 'true', 'false'],
+option('libidn', type : 'feature', deprecated : { 'true' : 'enabled', 'false' : 'disabled' },
description : 'libidn support')
-option('libiptc', type : 'combo', choices : ['auto', 'true', 'false'],
+option('libiptc', type : 'feature', deprecated : { 'true' : 'enabled', 'false' : 'disabled' },
description : 'libiptc support')
-option('qrencode', type : 'combo', choices : ['auto', 'true', 'false'],
+option('qrencode', type : 'feature', deprecated : { 'true' : 'enabled', 'false' : 'disabled' },
description : 'libqrencode support')
-option('gcrypt', type : 'combo', choices : ['auto', 'true', 'false'],
+option('gcrypt', type : 'feature', deprecated : { 'true' : 'enabled', 'false' : 'disabled' },
description : 'gcrypt support')
-option('gnutls', type : 'combo', choices : ['auto', 'true', 'false'],
+option('gnutls', type : 'feature', deprecated : { 'true' : 'enabled', 'false' : 'disabled' },
description : 'gnutls support')
-option('openssl', type : 'combo', choices : ['auto', 'true', 'false'],
+option('openssl', type : 'feature', deprecated : { 'true' : 'enabled', 'false' : 'disabled' },
description : 'openssl support')
option('cryptolib', type : 'combo', choices : ['auto', 'openssl', 'gcrypt'],
description : 'whether to use openssl or gcrypt where both are supported')
-option('p11kit', type : 'combo', choices : ['auto', 'true', 'false'],
+option('p11kit', type : 'feature', deprecated : { 'true' : 'enabled', 'false' : 'disabled' },
description : 'p11kit support')
-option('libfido2', type : 'combo', choices : ['auto', 'true', 'false'],
+option('libfido2', type : 'feature', deprecated : { 'true' : 'enabled', 'false' : 'disabled' },
description : 'FIDO2 support')
-option('tpm2', type : 'combo', choices : ['auto', 'true', 'false'],
+option('tpm2', type : 'feature', deprecated : { 'true' : 'enabled', 'false' : 'disabled' },
description : 'TPM2 support')
-option('elfutils', type : 'combo', choices : ['auto', 'true', 'false'],
+option('elfutils', type : 'feature', deprecated : { 'true' : 'enabled', 'false' : 'disabled' },
description : 'elfutils support')
-option('zlib', type : 'combo', choices : ['auto', 'true', 'false'],
+option('zlib', type : 'feature', deprecated : { 'true' : 'enabled', 'false' : 'disabled' },
description : 'zlib compression support')
-option('bzip2', type : 'combo', choices : ['auto', 'true', 'false'],
+option('bzip2', type : 'feature', deprecated : { 'true' : 'enabled', 'false' : 'disabled' },
description : 'bzip2 compression support')
-option('xz', type : 'combo', choices : ['auto', 'true', 'false'],
+option('xz', type : 'feature', deprecated : { 'true' : 'enabled', 'false' : 'disabled' },
description : 'xz compression support')
-option('lz4', type : 'combo', choices : ['auto', 'true', 'false'],
+option('lz4', type : 'feature', deprecated : { 'true' : 'enabled', 'false' : 'disabled' },
description : 'lz4 compression support')
-option('zstd', type : 'combo', choices : ['auto', 'true', 'false'],
+option('zstd', type : 'feature', deprecated : { 'true' : 'enabled', 'false' : 'disabled' },
description : 'zstd compression support')
option('default-compression', type : 'combo', choices : ['auto', 'zstd', 'lz4', 'xz'], value: 'auto',
description : 'default compression algorithm')
-option('xkbcommon', type : 'combo', choices : ['auto', 'true', 'false'],
+option('xkbcommon', type : 'feature', deprecated : { 'true' : 'enabled', 'false' : 'disabled' },
description : 'xkbcommon keymap support')
-option('pcre2', type : 'combo', choices : ['auto', 'true', 'false'],
+option('pcre2', type : 'feature', deprecated : { 'true' : 'enabled', 'false' : 'disabled' },
description : 'regexp matching support using pcre2')
-option('glib', type : 'combo', choices : ['auto', 'true', 'false'],
+option('glib', type : 'feature', deprecated : { 'true' : 'enabled', 'false' : 'disabled' },
description : 'libglib support (for tests only)')
-option('dbus', type : 'combo', choices : ['auto', 'true', 'false'],
+option('dbus', type : 'feature', deprecated : { 'true' : 'enabled', 'false' : 'disabled' },
description : 'libdbus support (for tests only)')
-option('bootloader', type : 'combo', choices : ['auto', 'true', 'false'],
+option('bootloader', type : 'feature', deprecated : { 'true' : 'enabled', 'false' : 'disabled' },
description : 'sd-boot/stub and userspace tools')
option('sbat-distro', type : 'string', value : 'auto',
description : 'SBAT distribution ID, e.g. fedora, or auto for autodetection')
description : 'run the fuzzer regression tests by default (with sanitizers)')
option('install-tests', type : 'boolean', value : false,
description : 'install test executables')
-option('log-message-verification', type : 'combo', choices : ['auto', 'true', 'false'],
+option('log-message-verification', type : 'feature', deprecated : { 'true' : 'enabled', 'false' : 'disabled' },
description : 'do fake printf() calls to verify format strings')
option('ok-color', type : 'combo',
description : 'build against LLVM libFuzzer')
option('kernel-install', type: 'boolean', value: true,
description : 'install kernel-install and associated files')
-option('ukify', type : 'combo', choices : ['auto', 'true', 'false'],
+option('ukify', type : 'feature', deprecated : { 'true' : 'enabled', 'false' : 'disabled' },
description : 'install ukify')
option('analyze', type: 'boolean', value: true,
description : 'install systemd-analyze')
option('bpf-compiler', type : 'combo', choices : ['clang', 'gcc'],
description: 'compiler used to build BPF programs')
-option('bpf-framework', type : 'combo', choices : ['auto', 'true', 'false'],
+option('bpf-framework', type : 'feature', deprecated : { 'true' : 'enabled', 'false' : 'disabled' },
description: 'build BPF programs from source code in restricted C')
-
-option('skip-deps', type : 'boolean', value : false,
- description : 'skip optional dependencies')
+++ /dev/null
-[Match]
-Distribution=fedora
-Release=rawhide
-
-[Distribution]
-RepositoryKeyCheck=no
sysvinit_path=$(realpath /etc/init.d)
if [ "$ID" = "centos" ] && [ "$VERSION" = "8" ]; then
- UKIFY=false
+ UKIFY="disabled"
else
- UKIFY=true
+ UKIFY="enabled"
fi
# On Debian 'loadkeys us' fails
CONFIGURE_OPTS=(
-D sysvinit-path="$sysvinit_path"
- -D man=false
+ -D man=disabled
-D translations=false
-D version-tag="${VERSION_TAG}"
-D mode=developer
-D tpm=true
-D environment-d=true
-D binfmt=true
- -D repart=true
- -D sysupdate=true
+ -D repart=enabled
+ -D sysupdate=enabled
-D coredump=true
-D pstore=true
-D oomd=true
-D portabled=true
-D sysext=true
-D userdb=true
- -D homed=true
+ -D homed=enabled
-D networkd=true
-D timedated=true
-D timesyncd=true
- -D remote=true
+ -D remote=enabled
-D nss-myhostname=true
- -D nss-mymachines=true
- -D nss-resolve=true
+ -D nss-mymachines=enabled
+ -D nss-resolve=enabled
-D nss-systemd=true
-D firstboot=true
-D randomseed=true
-D quotacheck=true
-D sysusers=true
-D tmpfiles=true
- -D importd=true
+ -D importd=enabled
-D hwdb=true
-D rfkill=true
-D xdg-autostart=true
-D translations=true
- -D polkit=true
- -D acl=true
- -D audit=true
- -D blkid=true
- -D fdisk=true
- -D kmod=true
- -D pam=true
- -D pwquality=true
- -D microhttpd=true
- -D libcryptsetup=true
- -D libcurl=true
+ -D polkit=enabled
+ -D acl=enabled
+ -D audit=enabled
+ -D blkid=enabled
+ -D fdisk=enabled
+ -D kmod=enabled
+ -D pam=enabled
+ -D pwquality=enabled
+ -D microhttpd=enabled
+ -D libcryptsetup=enabled
+ -D libcurl=enabled
-D idn=true
- -D libidn2=true
- -D qrencode=true
- -D gcrypt=true
- -D gnutls=true
- -D openssl=true
+ -D libidn2=enabled
+ -D qrencode=enabled
+ -D gcrypt=enabled
+ -D gnutls=enabled
+ -D openssl=enabled
-D cryptolib=openssl
- -D p11kit=true
- -D libfido2=true
- -D tpm2=true
- -D elfutils=true
- -D zstd=true
- -D xkbcommon=true
- -D pcre2=true
- -D glib=true
- -D dbus=true
- -D bootloader=true
+ -D p11kit=enabled
+ -D libfido2=enabled
+ -D tpm2=enabled
+ -D elfutils=enabled
+ -D zstd=enabled
+ -D xkbcommon=enabled
+ -D pcre2=enabled
+ -D glib=enabled
+ -D dbus=enabled
+ -D bootloader=enabled
-D kernel-install=true
-D analyze=true
- -D bpf-framework=true
+ -D bpf-framework=enabled
-D ukify="$UKIFY"
- -D seccomp=true
+ -D seccomp=enabled
-D selinux=auto
-D apparmor=auto
-D smack=true
(char*) memdupa_suffix0(_t, strnlen(_t, (n))); \
})
+/* Free every element of the array. */
+static inline void free_many(void **p, size_t n) {
+ assert(p || n == 0);
+
+ FOREACH_ARRAY(i, p, n)
+ *i = mfree(*i);
+}
+
+/* Typesafe wrapper for char** rather than void**. Unfortunately C won't implicitly cast this. */
+static inline void free_many_charp(char **c, size_t n) {
+ free_many((void**) c, n);
+}
+
#include "memory-util.h"
r = -ENXIO;
fail:
- for (i = 0; i < n; i++)
- free(v[i]);
-
+ free_many_charp(v, n);
return r;
done:
int efi_set_variable_string(const char *variable, const char *value) {
_cleanup_free_ char16_t *u16 = NULL;
- u16 = utf8_to_utf16(value, strlen(value));
+ u16 = utf8_to_utf16(value, SIZE_MAX);
if (!u16)
return -ENOMEM;
r = extract_first_word(p, &l[c], separators, flags);
if (r < 0) {
- int j;
-
- for (j = 0; j < c; j++)
- free(l[j]);
-
+ free_many_charp(l, c);
return r;
}
}
void locale_variables_free(char *l[_VARIABLE_LC_MAX]) {
- if (!l)
- return;
-
- for (LocaleVariable i = 0; i < _VARIABLE_LC_MAX; i++)
- l[i] = mfree(l[i]);
+ free_many_charp(l, _VARIABLE_LC_MAX);
}
void locale_variables_simplify(char *l[_VARIABLE_LC_MAX]) {
}
void rlimit_free_all(struct rlimit **rl) {
- int i;
-
- if (!rl)
- return;
-
- for (i = 0; i < _RLIMIT_MAX; i++)
- rl[i] = mfree(rl[i]);
+ free_many((void**) rl, _RLIMIT_MAX);
}
int rlimit_nofile_bump(int limit) {
return true;
}
+
+ssize_t strlevenshtein(const char *x, const char *y) {
+ _cleanup_free_ size_t *t0 = NULL, *t1 = NULL, *t2 = NULL;
+ size_t xl, yl;
+
+ /* This is inspired from the Linux kernel's Levenshtein implementation */
+
+ if (streq_ptr(x, y))
+ return 0;
+
+ xl = strlen_ptr(x);
+ if (xl > SSIZE_MAX)
+ return -E2BIG;
+
+ yl = strlen_ptr(y);
+ if (yl > SSIZE_MAX)
+ return -E2BIG;
+
+ if (isempty(x))
+ return yl;
+ if (isempty(y))
+ return xl;
+
+ t0 = new0(size_t, yl + 1);
+ if (!t0)
+ return -ENOMEM;
+ t1 = new0(size_t, yl + 1);
+ if (!t1)
+ return -ENOMEM;
+ t2 = new0(size_t, yl + 1);
+ if (!t2)
+ return -ENOMEM;
+
+ for (size_t i = 0; i <= yl; i++)
+ t1[i] = i;
+
+ for (size_t i = 0; i < xl; i++) {
+ t2[0] = i + 1;
+
+ for (size_t j = 0; j < yl; j++) {
+ /* Substitution */
+ t2[j+1] = t1[j] + (x[i] != y[j]);
+
+ /* Swap */
+ if (i > 0 && j > 0 && x[i-1] == y[j] && x[i] == y[j-1] && t2[j+1] > t0[j-1] + 1)
+ t2[j+1] = t0[j-1] + 1;
+
+ /* Deletion */
+ if (t2[j+1] > t1[j+1] + 1)
+ t2[j+1] = t1[j+1] + 1;
+
+ /* Insertion */
+ if (t2[j+1] > t2[j] + 1)
+ t2[j+1] = t2[j] + 1;
+ }
+
+ size_t *dummy = t0;
+ t0 = t1;
+ t1 = t2;
+ t2 = dummy;
+ }
+
+ return t1[yl];
+}
bool version_is_valid(const char *s);
bool version_is_valid_versionspec(const char *s);
+
+ssize_t strlevenshtein(const char *x, const char *y);
return (int) i;
rollback:
- for (size_t j = 0; j < i; j++)
- free(t[p + j]);
-
+ free_many_charp(t + p, i);
t[p] = NULL;
return -ENOMEM;
}
const uint8_t *f;
char *r, *t;
+ if (length == 0)
+ return new0(char, 1);
+
assert(s);
+ if (length == SIZE_MAX) {
+ length = char16_strlen(s);
+
+ if (length > SIZE_MAX/2)
+ return NULL; /* overflow */
+
+ length *= 2;
+ }
+
/* Input length is in bytes, i.e. the shortest possible character takes 2 bytes. Each unicode character may
* take up to 4 bytes in UTF-8. Let's also account for a trailing NUL byte. */
- if (length * 2 < length)
+ if (length > (SIZE_MAX - 1) / 2)
return NULL; /* overflow */
r = new(char, length * 2 + 1);
char16_t *n, *p;
int r;
+ if (length == 0)
+ return new0(char16_t, 1);
+
assert(s);
+ if (length == SIZE_MAX)
+ length = strlen(s);
+
+ if (length > SIZE_MAX - 1)
+ return NULL; /* overflow */
+
n = new(char16_t, length + 1);
if (!n)
return NULL;
xsprintf(utf8, USEC_FMT, MIN(timeout / USEC_PER_SEC, UINT32_MAX));
- encoded = utf8_to_utf16(utf8, strlen(utf8));
+ encoded = utf8_to_utf16(utf8, SIZE_MAX);
if (!encoded)
return log_oom();
} else if (arg1[0] == '@' && !streq(arg1, "@saved"))
return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Unsupported special entry identifier: %s", arg1);
else {
- encoded = utf8_to_utf16(arg1, strlen(arg1));
+ encoded = utf8_to_utf16(arg1, SIZE_MAX);
if (!encoded)
return log_oom();
case KEYPRESS(0, 0, 'H'):
case KEYPRESS(0, 0, '?'):
/* This must stay below 80 characters! Q/v/Ctrl+l/f deliberately not advertised. */
- status = xstrdup16(u"(d)efault (t/T)timeout (e)dit (r/R)resolution (p)rint (h)elp");
+ status = xstrdup16(u"(d)efault (t/T)imeout (e)dit (r/R)esolution (p)rint (O)ff re(B)oot (h)elp");
break;
case KEYPRESS(0, 0, 'Q'):
case KEYPRESS(0, 0, 'e'):
case KEYPRESS(0, 0, 'E'):
/* only the options of configured entries can be edited */
- if (!config->editor || !IN_SET(config->entries[idx_highlight]->type,
- LOADER_EFI, LOADER_LINUX, LOADER_UNIFIED_LINUX))
+ if (!config->editor ||
+ !IN_SET(config->entries[idx_highlight]->type, LOADER_EFI, LOADER_LINUX, LOADER_UNIFIED_LINUX)) {
+ status = xstrdup16(u"Entry does not support editing the command line.");
break;
+ }
/* Unified kernels that are signed as a whole will not accept command line options
* when secure boot is enabled unless there is none embedded in the image. Do not try
* to pretend we can edit it to only have it be ignored. */
if (config->entries[idx_highlight]->type == LOADER_UNIFIED_LINUX &&
secure_boot_enabled() &&
- config->entries[idx_highlight]->options)
+ config->entries[idx_highlight]->options) {
+ status = xstrdup16(u"Entry not editable in SecureBoot mode.");
break;
+ }
/* The edit line may end up on the last line of the screen. And even though we're
* not telling the firmware to advance the line, it still does in this one case,
case KEYPRESS(EFI_CONTROL_PRESSED, 0, 'l'):
case KEYPRESS(EFI_CONTROL_PRESSED, 0, CHAR_CTRL('l')):
+ case 'L': /* only uppercase, do not conflict with lower-case 'l' which picks first Linux entry */
clear = true;
break;
status = xstrdup16(u"Reboot into firmware interface not supported.");
break;
+ case KEYPRESS(0, 0, 'O'): /* Only uppercase, so that it can't be hit so easily fat-fingered, but still works safely over serial */
+ RT->ResetSystem(EfiResetShutdown, EFI_SUCCESS, 0, NULL);
+ break;
+
+ case KEYPRESS(0, 0, 'B'): /* ditto */
+ RT->ResetSystem(EfiResetCold, EFI_SUCCESS, 0, NULL);
+ break;
+
default:
/* jump with a hotkey directly to a matching entry */
idx = entry_lookup_key(config, idx_highlight+1, KEYCHAR(key));
efi_config_h_dir = meson.current_build_dir()
efi_addon = ''
-if efi_arch != ''
- libefitest = static_library(
- 'efitest',
- files(
- 'bcd.c',
- 'efi-string.c',
- ),
- build_by_default : false,
- include_directories : [
- basic_includes,
- include_directories('.'),
- ],
- dependencies : userspace)
-
- efitest_base = {
- 'link_with' : [
- libefitest,
- libshared,
- ],
- }
- efi_test_template = test_template + efitest_base
- efi_fuzz_template = fuzz_template + efitest_base
-
- executables += [
- efi_test_template + {
- 'sources' : files('test-bcd.c'),
- 'dependencies' : libzstd,
- 'conditions' : ['HAVE_ZSTD'],
- },
- efi_test_template + {
- 'sources' : files('test-efi-string.c'),
- },
- efi_fuzz_template + {
- 'sources' : files('fuzz-bcd.c'),
- },
- efi_fuzz_template + {
- 'sources' : files('fuzz-efi-string.c'),
- },
- efi_fuzz_template + {
- 'sources' : files('fuzz-efi-printf.c'),
- },
- ]
-endif
+libefitest = static_library(
+ 'efitest',
+ files(
+ 'bcd.c',
+ 'efi-string.c',
+ ),
+ build_by_default : false,
+ include_directories : [
+ basic_includes,
+ include_directories('.'),
+ ],
+ dependencies : userspace)
+
+efitest_base = {
+ 'link_with' : [
+ libefitest,
+ libshared,
+ ],
+}
+efi_test_template = test_template + efitest_base
+efi_fuzz_template = fuzz_template + efitest_base
+
+executables += [
+ efi_test_template + {
+ 'sources' : files('test-bcd.c'),
+ 'dependencies' : libzstd,
+ 'conditions' : ['ENABLE_BOOTLOADER', 'HAVE_ZSTD'],
+ },
+ efi_test_template + {
+ 'sources' : files('test-efi-string.c'),
+ 'conditions' : ['ENABLE_BOOTLOADER'],
+ },
+ efi_fuzz_template + {
+ 'sources' : files('fuzz-bcd.c'),
+ },
+ efi_fuzz_template + {
+ 'sources' : files('fuzz-efi-string.c'),
+ },
+ efi_fuzz_template + {
+ 'sources' : files('fuzz-efi-printf.c'),
+ },
+]
if conf.get('ENABLE_BOOTLOADER') != 1
subdir_done()
char16_t *title = get_bcd_title(bcd, len);
if (title) {
- _cleanup_free_ char *title_utf8 = utf16_to_utf8(title, char16_strlen(title) * 2);
+ _cleanup_free_ char *title_utf8 = utf16_to_utf8(title, SIZE_MAX);
log_info("%s: \"%s\"", saved_argv[i], title_utf8);
} else
log_info("%s: Bad BCD", saved_argv[i]);
return path;
}
-void mangle_stub_cmdline(char16_t *cmdline) {
- char16_t *p = cmdline;
+static bool shall_be_whitespace(char16_t c) {
+ return c <= 0x20U || c == 0x7FU; /* All control characters + space */
+}
+
+char16_t* mangle_stub_cmdline(char16_t *cmdline) {
+ char16_t *p, *q, *e;
if (!cmdline)
- return;
+ return cmdline;
- for (; *cmdline != '\0'; cmdline++)
- /* Convert ASCII control characters to spaces. */
- if (*cmdline <= 0x1F)
- *cmdline = ' ';
+ p = q = cmdline;
- /* chomp the trailing whitespaces */
- while (cmdline != p) {
- --cmdline;
+ /* Skip initial whitespace */
+ while (shall_be_whitespace(*p))
+ p++;
- if (*cmdline != ' ')
- break;
+ /* Turn inner control characters into proper spaces */
+ for (e = p; *p != 0; p++) {
+ if (shall_be_whitespace(*p)) {
+ *(q++) = ' ';
+ continue;
+ }
- *cmdline = '\0';
+ *(q++) = *p;
+ e = q; /* remember last non-whitespace char */
}
+
+ /* Chop off trailing whitespace */
+ *e = 0;
+ return cmdline;
}
EFI_STATUS chunked_read(EFI_FILE *file, size_t *size, void *buf) {
void convert_efi_path(char16_t *path);
char16_t *xstr8_to_path(const char *stra);
-void mangle_stub_cmdline(char16_t *cmdline);
+char16_t *mangle_stub_cmdline(char16_t *cmdline);
EFI_STATUS chunked_read(EFI_FILE *file, size_t *size, void *buf);
EFI_STATUS file_read(EFI_FILE *dir, const char16_t *name, size_t off, size_t size, char **content, size_t *content_size);
printf("%" PRIu32 ":%s=%s\n", TPM_PCR_INDEX_KERNEL_IMAGE, pcr_states[i].bank, hd);
} else {
- _cleanup_(json_variant_unrefp) JsonVariant *bv = NULL, *array = NULL;
+ _cleanup_(json_variant_unrefp) JsonVariant *array = NULL;
array = json_variant_ref(json_variant_by_key(w, pcr_states[i].bank));
- r = json_build(&bv,
- JSON_BUILD_OBJECT(
- JSON_BUILD_PAIR_CONDITION(!isempty(*phase), "phase", JSON_BUILD_STRING(*phase)),
- JSON_BUILD_PAIR("pcr", JSON_BUILD_INTEGER(TPM_PCR_INDEX_KERNEL_IMAGE)),
- JSON_BUILD_PAIR("hash", JSON_BUILD_HEX(pcr_states[i].value, pcr_states[i].value_size))
- )
- );
- if (r < 0)
- return log_error_errno(r, "Failed to build JSON object: %m");
-
- r = json_variant_append_array(&array, bv);
+ r = json_variant_append_arrayb(
+ &array,
+ JSON_BUILD_OBJECT(
+ JSON_BUILD_PAIR_CONDITION(!isempty(*phase), "phase", JSON_BUILD_STRING(*phase)),
+ JSON_BUILD_PAIR("pcr", JSON_BUILD_INTEGER(TPM_PCR_INDEX_KERNEL_IMAGE)),
+ JSON_BUILD_PAIR("hash", JSON_BUILD_HEX(pcr_states[i].value, pcr_states[i].value_size))));
if (r < 0)
return log_error_errno(r, "Failed to append JSON object to array: %m");
--- /dev/null
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+
+#include <sys/mount.h>
+
+#include "acl-util.h"
+#include "credential.h"
+#include "creds-util.h"
+#include "execute.h"
+#include "fileio.h"
+#include "glob-util.h"
+#include "io-util.h"
+#include "label-util.h"
+#include "missing_syscall.h"
+#include "mkdir-label.h"
+#include "mount-util.h"
+#include "mountpoint-util.h"
+#include "process-util.h"
+#include "random-util.h"
+#include "recurse-dir.h"
+#include "rm-rf.h"
+#include "socket-util.h"
+#include "tmpfile-util.h"
+
+ExecSetCredential *exec_set_credential_free(ExecSetCredential *sc) {
+ if (!sc)
+ return NULL;
+
+ free(sc->id);
+ free(sc->data);
+ return mfree(sc);
+}
+
+ExecLoadCredential *exec_load_credential_free(ExecLoadCredential *lc) {
+ if (!lc)
+ return NULL;
+
+ free(lc->id);
+ free(lc->path);
+ return mfree(lc);
+}
+
+DEFINE_HASH_OPS_WITH_VALUE_DESTRUCTOR(
+ exec_set_credential_hash_ops,
+ char, string_hash_func, string_compare_func,
+ ExecSetCredential, exec_set_credential_free);
+
+DEFINE_HASH_OPS_WITH_VALUE_DESTRUCTOR(
+ exec_load_credential_hash_ops,
+ char, string_hash_func, string_compare_func,
+ ExecLoadCredential, exec_load_credential_free);
+
+bool exec_context_has_credentials(const ExecContext *c) {
+ assert(c);
+
+ return !hashmap_isempty(c->set_credentials) ||
+ !hashmap_isempty(c->load_credentials) ||
+ !set_isempty(c->import_credentials);
+}
+
+bool exec_context_has_encrypted_credentials(ExecContext *c) {
+ ExecLoadCredential *load_cred;
+ ExecSetCredential *set_cred;
+
+ assert(c);
+
+ HASHMAP_FOREACH(load_cred, c->load_credentials)
+ if (load_cred->encrypted)
+ return true;
+
+ HASHMAP_FOREACH(set_cred, c->set_credentials)
+ if (set_cred->encrypted)
+ return true;
+
+ return false;
+}
+
+static int get_credential_directory(
+ const char *runtime_prefix,
+ const char *unit,
+ char **ret) {
+
+ char *p;
+
+ assert(ret);
+
+ if (!runtime_prefix || !unit) {
+ *ret = NULL;
+ return 0;
+ }
+
+ p = path_join(runtime_prefix, "credentials", unit);
+ if (!p)
+ return -ENOMEM;
+
+ *ret = p;
+ return 1;
+}
+
+int unit_add_default_credential_dependencies(Unit *u, const ExecContext *c) {
+ _cleanup_free_ char *p = NULL, *m = NULL;
+ int r;
+
+ assert(u);
+ assert(c);
+
+ if (!exec_context_has_credentials(c))
+ return 0;
+
+ /* Let's make sure the credentials directory of this service is unmounted *after* the service itself
+ * shuts down. This only matters if mount namespacing is not used for the service, and hence the
+ * credentials mount appears on the host. */
+
+ r = get_credential_directory(u->manager->prefix[EXEC_DIRECTORY_RUNTIME], u->id, &p);
+ if (r <= 0)
+ return r;
+
+ r = unit_name_from_path(p, ".mount", &m);
+ if (r < 0)
+ return r;
+
+ return unit_add_dependency_by_name(u, UNIT_AFTER, m, /* add_reference= */ true, UNIT_DEPENDENCY_FILE);
+}
+
+int exec_context_destroy_credentials(const ExecContext *c, const char *runtime_prefix, const char *unit) {
+ _cleanup_free_ char *p = NULL;
+ int r;
+
+ assert(c);
+
+ r = get_credential_directory(runtime_prefix, unit, &p);
+ if (r <= 0)
+ return r;
+
+ /* This is either a tmpfs/ramfs of its own, or a plain directory. Either way, let's first try to
+ * unmount it, and afterwards remove the mount point */
+ (void) umount2(p, MNT_DETACH|UMOUNT_NOFOLLOW);
+ (void) rm_rf(p, REMOVE_ROOT|REMOVE_CHMOD);
+
+ return 0;
+}
+
+static int write_credential(
+ int dfd,
+ const char *id,
+ const void *data,
+ size_t size,
+ uid_t uid,
+ gid_t gid,
+ bool ownership_ok) {
+
+ _cleanup_(unlink_and_freep) char *tmp = NULL;
+ _cleanup_close_ int fd = -EBADF;
+ int r;
+
+ r = tempfn_random_child("", "cred", &tmp);
+ if (r < 0)
+ return r;
+
+ fd = openat(dfd, tmp, O_CREAT|O_RDWR|O_CLOEXEC|O_EXCL|O_NOFOLLOW|O_NOCTTY, 0600);
+ if (fd < 0) {
+ tmp = mfree(tmp);
+ return -errno;
+ }
+
+ r = loop_write(fd, data, size, /* do_poll = */ false);
+ if (r < 0)
+ return r;
+
+ if (fchmod(fd, 0400) < 0) /* Take away "w" bit */
+ return -errno;
+
+ if (uid_is_valid(uid) && uid != getuid()) {
+ r = fd_add_uid_acl_permission(fd, uid, ACL_READ);
+ if (r < 0) {
+ if (!ERRNO_IS_NOT_SUPPORTED(r) && !ERRNO_IS_PRIVILEGE(r))
+ return r;
+
+ if (!ownership_ok) /* Ideally we use ACLs, since we can neatly express what we want
+ * to express: that the user gets read access and nothing
+ * else. But if the backing fs can't support that (e.g. ramfs)
+ * then we can use file ownership instead. But that's only safe if
+ * we can then re-mount the whole thing read-only, so that the
+ * user can no longer chmod() the file to gain write access. */
+ return r;
+
+ if (fchown(fd, uid, gid) < 0)
+ return -errno;
+ }
+ }
+
+ if (renameat(dfd, tmp, dfd, id) < 0)
+ return -errno;
+
+ tmp = mfree(tmp);
+ return 0;
+}
+
+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. 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 (IN_SET(path, CREDENTIAL_SEARCH_PATH_ENCRYPTED, CREDENTIAL_SEARCH_PATH_ALL)) {
+ if (strv_extend(&l, params->received_encrypted_credentials_directory) < 0)
+ return NULL;
+
+ if (strv_extend_strv(&l, CONF_PATHS_STRV("credstore.encrypted"), /* filter_duplicates= */ true) < 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 (DEBUG_LOGGING) {
+ _cleanup_free_ char *t = strv_join(l, ":");
+
+ log_debug("Credential search path is: %s", strempty(t));
+ }
+
+ return TAKE_PTR(l);
+}
+
+static int maybe_decrypt_and_write_credential(
+ int dir_fd,
+ const char *id,
+ bool encrypted,
+ uid_t uid,
+ gid_t gid,
+ 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, gid, 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,
+ gid_t gid,
+ 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,
+ gid,
+ 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,
+ const char *id,
+ const char *path,
+ bool encrypted,
+ const char *unit,
+ int read_dfd,
+ int write_dfd,
+ uid_t uid,
+ gid_t gid,
+ bool ownership_ok,
+ uint64_t *left) {
+
+ ReadFullFileFlags flags = READ_FULL_FILE_SECURE|READ_FULL_FILE_FAIL_WHEN_LARGER;
+ _cleanup_strv_free_ char **search_path = NULL;
+ _cleanup_(erase_and_freep) char *data = NULL;
+ _cleanup_free_ char *bindname = NULL;
+ const char *source = NULL;
+ bool missing_ok = true;
+ size_t size, maxsz;
+ int r;
+
+ assert(context);
+ assert(params);
+ assert(id);
+ assert(path);
+ assert(unit);
+ assert(read_dfd >= 0 || read_dfd == AT_FDCWD);
+ assert(write_dfd >= 0);
+ assert(left);
+
+ if (read_dfd >= 0) {
+ /* If a directory fd is specified, then read the file directly from that dir. In this case we
+ * won't do AF_UNIX stuff (we simply don't want to recursively iterate down a tree of AF_UNIX
+ * IPC sockets). It's OK if a file vanishes here in the time we enumerate it and intend to
+ * open it. */
+
+ if (!filename_is_valid(path)) /* safety check */
+ return -EINVAL;
+
+ missing_ok = true;
+ source = path;
+
+ } else if (path_is_absolute(path)) {
+ /* If this is an absolute path, read the data directly from it, and support AF_UNIX
+ * sockets */
+
+ if (!path_is_valid(path)) /* safety check */
+ return -EINVAL;
+
+ flags |= READ_FULL_FILE_CONNECT_SOCKET;
+
+ /* Pass some minimal info about the unit and the credential name we are looking to acquire
+ * via the source socket address in case we read off an AF_UNIX socket. */
+ if (asprintf(&bindname, "@%" PRIx64"/unit/%s/%s", random_u64(), unit, id) < 0)
+ return -ENOMEM;
+
+ missing_ok = false;
+ source = path;
+
+ } else if (credential_name_valid(path)) {
+ /* If this is a relative path, take it as credential name relative to the credentials
+ * 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, CREDENTIAL_SEARCH_PATH_ALL);
+ if (!search_path)
+ return -ENOMEM;
+
+ missing_ok = true;
+ } else
+ source = NULL;
+
+ if (encrypted)
+ flags |= READ_FULL_FILE_UNBASE64;
+
+ maxsz = encrypted ? CREDENTIAL_ENCRYPTED_SIZE_MAX : CREDENTIAL_SIZE_MAX;
+
+ if (search_path) {
+ STRV_FOREACH(d, search_path) {
+ _cleanup_free_ char *j = NULL;
+
+ j = path_join(*d, path);
+ if (!j)
+ return -ENOMEM;
+
+ r = read_full_file_full(
+ AT_FDCWD, j, /* path is absolute, hence pass AT_FDCWD as nop dir fd here */
+ UINT64_MAX,
+ maxsz,
+ flags,
+ NULL,
+ &data, &size);
+ if (r != -ENOENT)
+ break;
+ }
+ } else if (source)
+ r = read_full_file_full(
+ read_dfd, source,
+ UINT64_MAX,
+ maxsz,
+ flags,
+ bindname,
+ &data, &size);
+ else
+ r = -ENOENT;
+
+ if (r == -ENOENT && (missing_ok || hashmap_contains(context->set_credentials, id))) {
+ /* Make a missing inherited credential non-fatal, let's just continue. After all apps
+ * will get clear errors if we don't pass such a missing credential on as they
+ * themselves will get ENOENT when trying to read them, which should not be much
+ * worse than when we handle the error here and make it fatal.
+ *
+ * Also, if the source file doesn't exist, but a fallback is set via SetCredentials=
+ * we are fine, too. */
+ log_debug_errno(r, "Couldn't read inherited credential '%s', skipping: %m", path);
+ return 0;
+ }
+ if (r < 0)
+ return log_debug_errno(r, "Failed to read credential '%s': %m", path);
+
+ return maybe_decrypt_and_write_credential(write_dfd, id, encrypted, uid, gid, ownership_ok, data, size, left);
+}
+
+struct load_cred_args {
+ const ExecContext *context;
+ const ExecParameters *params;
+ bool encrypted;
+ const char *unit;
+ int dfd;
+ uid_t uid;
+ gid_t gid;
+ bool ownership_ok;
+ uint64_t *left;
+};
+
+static int load_cred_recurse_dir_cb(
+ RecurseDirEvent event,
+ const char *path,
+ int dir_fd,
+ int inode_fd,
+ const struct dirent *de,
+ const struct statx *sx,
+ void *userdata) {
+
+ struct load_cred_args *args = ASSERT_PTR(userdata);
+ _cleanup_free_ char *sub_id = NULL;
+ int r;
+
+ if (event != RECURSE_DIR_ENTRY)
+ return RECURSE_DIR_CONTINUE;
+
+ if (!IN_SET(de->d_type, DT_REG, DT_SOCK))
+ return RECURSE_DIR_CONTINUE;
+
+ sub_id = strreplace(path, "/", "_");
+ if (!sub_id)
+ return -ENOMEM;
+
+ if (!credential_name_valid(sub_id))
+ return log_debug_errno(SYNTHETIC_ERRNO(EINVAL), "Credential would get ID %s, which is not valid, refusing", sub_id);
+
+ if (faccessat(args->dfd, sub_id, F_OK, AT_SYMLINK_NOFOLLOW) >= 0) {
+ log_debug("Skipping credential with duplicated ID %s at %s", sub_id, path);
+ return RECURSE_DIR_CONTINUE;
+ }
+ if (errno != ENOENT)
+ return log_debug_errno(errno, "Failed to test if credential %s exists: %m", sub_id);
+
+ r = load_credential(
+ args->context,
+ args->params,
+ sub_id,
+ de->d_name,
+ args->encrypted,
+ args->unit,
+ dir_fd,
+ args->dfd,
+ args->uid,
+ args->gid,
+ args->ownership_ok,
+ args->left);
+ if (r < 0)
+ return r;
+
+ return RECURSE_DIR_CONTINUE;
+}
+
+static int acquire_credentials(
+ const ExecContext *context,
+ const ExecParameters *params,
+ const char *unit,
+ const char *p,
+ uid_t uid,
+ gid_t gid,
+ bool ownership_ok) {
+
+ uint64_t left = CREDENTIALS_TOTAL_SIZE_MAX;
+ _cleanup_close_ int dfd = -EBADF;
+ const char *ic;
+ ExecLoadCredential *lc;
+ ExecSetCredential *sc;
+ int r;
+
+ assert(context);
+ assert(p);
+
+ dfd = open(p, O_DIRECTORY|O_CLOEXEC);
+ if (dfd < 0)
+ return -errno;
+
+ r = fd_acl_make_writable(dfd); /* Add the "w" bit, if we are reusing an already set up credentials dir where it was unset */
+ if (r < 0)
+ return r;
+
+ /* First, load credentials off disk (or acquire via AF_UNIX socket) */
+ HASHMAP_FOREACH(lc, context->load_credentials) {
+ _cleanup_close_ int sub_fd = -EBADF;
+
+ /* If this is an absolute path, then try to open it as a directory. If that works, then we'll
+ * recurse into it. If it is an absolute path but it isn't a directory, then we'll open it as
+ * a regular file. Finally, if it's a relative path we will use it as a credential name to
+ * propagate a credential passed to us from further up. */
+
+ if (path_is_absolute(lc->path)) {
+ sub_fd = open(lc->path, O_DIRECTORY|O_CLOEXEC|O_RDONLY);
+ if (sub_fd < 0 && !IN_SET(errno,
+ ENOTDIR, /* Not a directory */
+ ENOENT)) /* Doesn't exist? */
+ return log_debug_errno(errno, "Failed to open '%s': %m", lc->path);
+ }
+
+ if (sub_fd < 0)
+ /* Regular file (incl. a credential passed in from higher up) */
+ r = load_credential(
+ context,
+ params,
+ lc->id,
+ lc->path,
+ lc->encrypted,
+ unit,
+ AT_FDCWD,
+ dfd,
+ uid,
+ gid,
+ ownership_ok,
+ &left);
+ else
+ /* Directory */
+ r = recurse_dir(
+ sub_fd,
+ /* path= */ lc->id, /* recurse_dir() will suffix the subdir paths from here to the top-level id */
+ /* statx_mask= */ 0,
+ /* n_depth_max= */ UINT_MAX,
+ RECURSE_DIR_SORT|RECURSE_DIR_IGNORE_DOT|RECURSE_DIR_ENSURE_TYPE,
+ load_cred_recurse_dir_cb,
+ &(struct load_cred_args) {
+ .context = context,
+ .params = params,
+ .encrypted = lc->encrypted,
+ .unit = unit,
+ .dfd = dfd,
+ .uid = uid,
+ .gid = gid,
+ .ownership_ok = ownership_ok,
+ .left = &left,
+ });
+ if (r < 0)
+ return r;
+ }
+
+ /* 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,
+ gid,
+ 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,
+ gid,
+ 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;
+ size_t size, add;
+
+ /* Note that we check ahead of time here instead of relying on O_EXCL|O_CREAT later to return
+ * EEXIST if the credential already exists. That's because the TPM2-based decryption is kinda
+ * slow and involved, hence it's nice to be able to skip that if the credential already
+ * exists anyway. */
+ if (faccessat(dfd, sc->id, F_OK, AT_SYMLINK_NOFOLLOW) >= 0)
+ continue;
+ if (errno != ENOENT)
+ return log_debug_errno(errno, "Failed to test if credential %s exists: %m", sc->id);
+
+ if (sc->encrypted) {
+ r = decrypt_credential_and_warn(sc->id, now(CLOCK_REALTIME), NULL, NULL, sc->data, sc->size, &plaintext, &size);
+ if (r < 0)
+ return r;
+
+ data = plaintext;
+ } else {
+ data = sc->data;
+ size = sc->size;
+ }
+
+ add = strlen(sc->id) + size;
+ if (add > left)
+ return -E2BIG;
+
+ r = write_credential(dfd, sc->id, data, size, uid, gid, ownership_ok);
+ if (r < 0)
+ return r;
+
+ left -= add;
+ }
+
+ r = fd_acl_make_read_only(dfd); /* Now take away the "w" bit */
+ if (r < 0)
+ return r;
+
+ /* After we created all keys with the right perms, also make sure the credential store as a whole is
+ * accessible */
+
+ if (uid_is_valid(uid) && uid != getuid()) {
+ r = fd_add_uid_acl_permission(dfd, uid, ACL_READ | ACL_EXECUTE);
+ if (r < 0) {
+ if (!ERRNO_IS_NOT_SUPPORTED(r) && !ERRNO_IS_PRIVILEGE(r))
+ return r;
+
+ if (!ownership_ok)
+ return r;
+
+ if (fchown(dfd, uid, gid) < 0)
+ return -errno;
+ }
+ }
+
+ return 0;
+}
+
+static int setup_credentials_internal(
+ const ExecContext *context,
+ const ExecParameters *params,
+ const char *unit,
+ const char *final, /* This is where the credential store shall eventually end up at */
+ const char *workspace, /* This is where we can prepare it before moving it to the final place */
+ bool reuse_workspace, /* Whether to reuse any existing workspace mount if it already is a mount */
+ bool must_mount, /* Whether to require that we mount something, it's not OK to use the plain directory fall back */
+ uid_t uid,
+ gid_t gid,
+ int *ret_mount_fd) {
+
+ int r, workspace_mounted; /* negative if we don't know yet whether we have/can mount something; true
+ * if we mounted something; false if we definitely can't mount anything */
+ bool final_mounted;
+ const char *where;
+
+ assert(context);
+ assert(final);
+ assert(workspace);
+
+ if (reuse_workspace) {
+ r = path_is_mount_point(workspace, NULL, 0);
+ if (r < 0)
+ return r;
+ if (r > 0)
+ workspace_mounted = true; /* If this is already a mount, and we are supposed to reuse
+ * it, let's keep this in mind */
+ else
+ workspace_mounted = -1; /* We need to figure out if we can mount something to the workspace */
+ } else
+ workspace_mounted = -1; /* ditto */
+
+ r = path_is_mount_point(final, NULL, 0);
+ if (r < 0)
+ return r;
+ if (r > 0) {
+ /* If the final place already has something mounted, we use that. If the workspace also has
+ * something mounted we assume it's actually the same mount (but with MS_RDONLY
+ * different). */
+ final_mounted = true;
+
+ if (workspace_mounted < 0) {
+ /* If the final place is mounted, but the workspace isn't, then let's bind mount
+ * the final version to the workspace, and make it writable, so that we can make
+ * changes */
+
+ r = mount_nofollow_verbose(LOG_DEBUG, final, workspace, NULL, MS_BIND|MS_REC, NULL);
+ if (r < 0)
+ return r;
+
+ r = mount_nofollow_verbose(LOG_DEBUG, NULL, workspace, NULL, MS_BIND|MS_REMOUNT|credentials_fs_mount_flags(/* ro= */ false), NULL);
+ if (r < 0)
+ return r;
+
+ workspace_mounted = true;
+ }
+ } else
+ final_mounted = false;
+
+ if (workspace_mounted < 0) {
+ /* Nothing is mounted on the workspace yet, let's try to mount something now */
+
+ r = mount_credentials_fs(workspace, CREDENTIALS_TOTAL_SIZE_MAX, /* ro= */ false);
+ if (r < 0) {
+ /* If that didn't work, try to make a bind mount from the final to the workspace, so
+ * that we can make it writable there. */
+ r = mount_nofollow_verbose(LOG_DEBUG, final, workspace, NULL, MS_BIND|MS_REC, NULL);
+ if (r < 0) {
+ if (!ERRNO_IS_PRIVILEGE(r))
+ /* Propagate anything that isn't a permission problem. */
+ return r;
+
+ if (must_mount)
+ /* If it's not OK to use the plain directory fallback, propagate all
+ * errors too. */
+ return r;
+
+ /* If we lack privileges to bind mount stuff, then let's gracefully proceed
+ * for compat with container envs, and just use the final dir as is. */
+
+ workspace_mounted = false;
+ } else {
+ /* Make the new bind mount writable (i.e. drop MS_RDONLY) */
+ r = mount_nofollow_verbose(LOG_DEBUG, NULL, workspace, NULL, MS_BIND|MS_REMOUNT|credentials_fs_mount_flags(/* ro= */ false), NULL);
+ if (r < 0)
+ return r;
+
+ workspace_mounted = true;
+ }
+ } else
+ workspace_mounted = true;
+ }
+
+ assert(!must_mount || workspace_mounted > 0);
+ where = workspace_mounted ? workspace : final;
+
+ (void) label_fix_full(AT_FDCWD, where, final, 0);
+
+ r = acquire_credentials(context, params, unit, where, uid, gid, workspace_mounted);
+ if (r < 0)
+ return r;
+
+ if (workspace_mounted) {
+ bool install;
+
+ /* Determine if we should actually install the prepared mount in the final location by bind
+ * mounting it there. We do so only if the mount is not established there already, and if the
+ * mount is actually non-empty (i.e. carries at least one credential). Not that in the best
+ * case we are doing all this in a mount namespace, thus no one else will see that we
+ * allocated a file system we are getting rid of again here. */
+ if (final_mounted)
+ install = false; /* already installed */
+ else {
+ r = dir_is_empty(where, /* ignore_hidden_or_backup= */ false);
+ if (r < 0)
+ return r;
+
+ install = r == 0; /* install only if non-empty */
+ }
+
+ if (install) {
+ /* Make workspace read-only now, so that any bind mount we make from it defaults to
+ * read-only too */
+ r = mount_nofollow_verbose(LOG_DEBUG, NULL, workspace, NULL, MS_BIND|MS_REMOUNT|credentials_fs_mount_flags(/* ro= */ true), NULL);
+ if (r < 0)
+ return r;
+
+ if (ret_mount_fd) {
+ _cleanup_close_ int mount_fd = -EBADF;
+
+ r = mount_fd = RET_NERRNO(open_tree(AT_FDCWD, workspace, OPEN_TREE_CLONE | OPEN_TREE_CLOEXEC));
+ if (r >= 0) {
+ /* The workspace is already cloned in the above, and not necessary
+ * anymore. Even though the workspace is unmounted when the short-lived
+ * child process exits, let's explicitly unmount it here for safety. */
+ r = umount_verbose(LOG_DEBUG, workspace, MNT_DETACH|UMOUNT_NOFOLLOW);
+ if (r < 0)
+ return r;
+
+ *ret_mount_fd = TAKE_FD(mount_fd);
+ return 0;
+ }
+
+ /* Old kernel? Unprivileged? */
+ if (!ERRNO_IS_NOT_SUPPORTED(r) && !ERRNO_IS_PRIVILEGE(r))
+ return r;
+ }
+
+ /* And mount it to the final place, read-only */
+ r = mount_nofollow_verbose(LOG_DEBUG, workspace, final, NULL, MS_MOVE, NULL);
+ } else
+ /* Otherwise get rid of it */
+ r = umount_verbose(LOG_DEBUG, workspace, MNT_DETACH|UMOUNT_NOFOLLOW);
+ if (r < 0)
+ return r;
+ } else {
+ _cleanup_free_ char *parent = NULL;
+
+ /* If we do not have our own mount put used the plain directory fallback, then we need to
+ * open access to the top-level credential directory and the per-service directory now */
+
+ r = path_extract_directory(final, &parent);
+ if (r < 0)
+ return r;
+ if (chmod(parent, 0755) < 0)
+ return -errno;
+ }
+
+ if (ret_mount_fd)
+ *ret_mount_fd = -EBADF;
+ return 0;
+}
+
+int setup_credentials(
+ const ExecContext *context,
+ const ExecParameters *params,
+ const char *unit,
+ uid_t uid,
+ gid_t gid,
+ char **ret_path,
+ int *ret_mount_fd) {
+
+ _cleanup_close_pair_ int socket_pair[2] = PIPE_EBADF;
+ _cleanup_free_ char *p = NULL, *q = NULL;
+ _cleanup_close_ int mount_fd = -EBADF;
+ int r;
+
+ assert(context);
+ assert(params);
+ assert(ret_path);
+ assert(ret_mount_fd);
+
+ if (!exec_context_has_credentials(context)) {
+ *ret_path = NULL;
+ *ret_mount_fd = -EBADF;
+ return 0;
+ }
+
+ if (!params->prefix[EXEC_DIRECTORY_RUNTIME])
+ return -EINVAL;
+
+ /* This where we'll place stuff when we are done; this main credentials directory is world-readable,
+ * and the subdir we mount over with a read-only file system readable by the service's user */
+ q = path_join(params->prefix[EXEC_DIRECTORY_RUNTIME], "credentials");
+ if (!q)
+ return -ENOMEM;
+
+ r = mkdir_label(q, 0755); /* top-level dir: world readable/searchable */
+ if (r < 0 && r != -EEXIST)
+ return r;
+
+ p = path_join(q, unit);
+ if (!p)
+ return -ENOMEM;
+
+ r = mkdir_label(p, 0700); /* per-unit dir: private to user */
+ if (r < 0 && r != -EEXIST)
+ return r;
+
+ if (socketpair(AF_UNIX, SOCK_DGRAM | SOCK_CLOEXEC, 0, socket_pair) < 0)
+ return -errno;
+
+ r = safe_fork_full("(sd-mkdcreds)",
+ NULL, &socket_pair[1], 1,
+ FORK_DEATHSIG|FORK_CLOSE_ALL_FDS|FORK_WAIT|FORK_NEW_MOUNTNS|FORK_REOPEN_LOG, NULL);
+ if (r < 0) {
+ _cleanup_free_ char *t = NULL, *u = NULL;
+
+ /* If this is not a privilege or support issue then propagate the error */
+ if (!ERRNO_IS_NOT_SUPPORTED(r) && !ERRNO_IS_PRIVILEGE(r))
+ return r;
+
+ /* Temporary workspace, that remains inaccessible all the time. We prepare stuff there before moving
+ * it into place, so that users can't access half-initialized credential stores. */
+ t = path_join(params->prefix[EXEC_DIRECTORY_RUNTIME], "systemd/temporary-credentials");
+ if (!t)
+ return -ENOMEM;
+
+ /* We can't set up a mount namespace. In that case operate on a fixed, inaccessible per-unit
+ * directory outside of /run/credentials/ first, and then move it over to /run/credentials/
+ * after it is fully set up */
+ u = path_join(t, unit);
+ if (!u)
+ return -ENOMEM;
+
+ FOREACH_STRING(i, t, u) {
+ r = mkdir_label(i, 0700);
+ if (r < 0 && r != -EEXIST)
+ return r;
+ }
+
+ r = setup_credentials_internal(
+ context,
+ params,
+ unit,
+ p, /* final mount point */
+ u, /* temporary workspace to overmount */
+ true, /* reuse the workspace if it is already a mount */
+ false, /* it's OK to fall back to a plain directory if we can't mount anything */
+ uid,
+ gid,
+ NULL);
+
+ (void) rmdir(u); /* remove the workspace again if we can. */
+
+ if (r < 0)
+ return r;
+
+ } else if (r == 0) { /* child */
+
+ /* We managed to set up a mount namespace, and are now in a child. That's great. In this case
+ * we can use the same directory for all cases, after turning off propagation. Question
+ * though is: where do we turn off propagation exactly, and where do we place the workspace
+ * directory? We need some place that is guaranteed to be a mount point in the host, and
+ * which is guaranteed to have a subdir we can mount over. /run/ is not suitable for this,
+ * since we ultimately want to move the resulting file system there, i.e. we need propagation
+ * for /run/ eventually. We could use our own /run/systemd/bind mount on itself, but that
+ * would be visible in the host mount table all the time, which we want to avoid. Hence, what
+ * we do here instead we use /dev/ and /dev/shm/ for our purposes. We know for sure that
+ * /dev/ is a mount point and we now for sure that /dev/shm/ exists. Hence we can turn off
+ * propagation on the former, and then overmount the latter.
+ *
+ * Yes it's nasty playing games with /dev/ and /dev/shm/ like this, since it does not exist
+ * for this purpose, but there are few other candidates that work equally well for us, and
+ * given that we do this in a privately namespaced short-lived single-threaded process that
+ * no one else sees this should be OK to do. */
+
+ _cleanup_close_ int fd = -EBADF;
+
+ /* Turn off propagation from our namespace to host */
+ r = mount_nofollow_verbose(LOG_DEBUG, NULL, "/dev", NULL, MS_SLAVE|MS_REC, NULL);
+ if (r < 0)
+ goto child_fail;
+
+ r = setup_credentials_internal(
+ context,
+ params,
+ unit,
+ p, /* final mount point */
+ "/dev/shm", /* temporary workspace to overmount */
+ false, /* do not reuse /dev/shm if it is already a mount, under no circumstances */
+ true, /* insist that something is mounted, do not allow fallback to plain directory */
+ uid,
+ gid,
+ &fd);
+ if (r < 0)
+ goto child_fail;
+
+ r = send_one_fd_iov(socket_pair[1], fd,
+ &IOVEC_MAKE((int[]) { fd >= 0 }, sizeof(int)), 1,
+ MSG_DONTWAIT);
+ if (r < 0)
+ goto child_fail;
+
+ _exit(EXIT_SUCCESS);
+
+ child_fail:
+ _exit(EXIT_FAILURE);
+
+ } else { /* parent */
+
+ int ret;
+ struct iovec iov = IOVEC_MAKE(&ret, sizeof(int));
+
+ r = receive_one_fd_iov(socket_pair[0], &iov, 1, MSG_DONTWAIT, &mount_fd);
+ if (r < 0)
+ return r;
+ if (ret > 0 && mount_fd < 0)
+ return -EIO;
+ }
+
+ /* If the credentials dir is empty and not a mount point, then there's no point in having it. Let's
+ * try to remove it. This matters in particular if we created the dir as mount point but then didn't
+ * actually end up mounting anything on it. In that case we'd rather have ENOENT than EACCESS being
+ * seen by users when trying access this inode. */
+ (void) rmdir(p);
+
+ *ret_path = TAKE_PTR(p);
+ *ret_mount_fd = TAKE_FD(mount_fd);
+ return 0;
+}
--- /dev/null
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+#pragma once
+
+#include <stdbool.h>
+#include <stdint.h>
+#include <unistd.h>
+
+#include "hash-funcs.h"
+
+typedef struct ExecContext ExecContext;
+typedef struct ExecParameters ExecParameters;
+typedef struct Unit Unit;
+
+/* A credential configured with LoadCredential= */
+typedef struct ExecLoadCredential {
+ char *id, *path;
+ bool encrypted;
+} ExecLoadCredential;
+
+/* A credential configured with SetCredential= */
+typedef struct ExecSetCredential {
+ char *id;
+ bool encrypted;
+ void *data;
+ size_t size;
+} ExecSetCredential;
+
+ExecSetCredential *exec_set_credential_free(ExecSetCredential *sc);
+DEFINE_TRIVIAL_CLEANUP_FUNC(ExecSetCredential*, exec_set_credential_free);
+
+ExecLoadCredential *exec_load_credential_free(ExecLoadCredential *lc);
+DEFINE_TRIVIAL_CLEANUP_FUNC(ExecLoadCredential*, exec_load_credential_free);
+
+extern const struct hash_ops exec_set_credential_hash_ops;
+extern const struct hash_ops exec_load_credential_hash_ops;
+
+bool exec_context_has_encrypted_credentials(ExecContext *c);
+bool exec_context_has_credentials(const ExecContext *c);
+
+int unit_add_default_credential_dependencies(Unit *u, const ExecContext *c);
+
+int exec_context_destroy_credentials(const ExecContext *c, const char *runtime_root, const char *unit);
+int setup_credentials(
+ const ExecContext *context,
+ const ExecParameters *params,
+ const char *unit,
+ uid_t uid,
+ gid_t gid,
+ char **ret_path,
+ int *ret_mount_fd);
#include "cap-list.h"
#include "capability-util.h"
#include "cpu-set-util.h"
+#include "credential.h"
#include "creds-util.h"
#include "dbus-execute.h"
#include "dbus-util.h"
#include "sd-messages.h"
-#include "acl-util.h"
#include "af-list.h"
#include "alloc-util.h"
#if HAVE_APPARMOR
#include "chown-recursive.h"
#include "constants.h"
#include "cpu-set-util.h"
-#include "creds-util.h"
+#include "credential.h"
#include "data-fd-util.h"
#include "env-file.h"
#include "env-util.h"
#include "execute.h"
#include "exit-status.h"
#include "fd-util.h"
-#include "fileio.h"
#include "format-util.h"
#include "glob-util.h"
#include "hexdecoct.h"
#include "io-util.h"
#include "ioprio-util.h"
-#include "label-util.h"
#include "lock-util.h"
#include "log.h"
#include "macro.h"
#include "missing_fs.h"
#include "missing_ioprio.h"
#include "missing_prctl.h"
+#include "missing_syscall.h"
#include "mkdir-label.h"
-#include "mount-util.h"
-#include "mountpoint-util.h"
#include "namespace.h"
#include "parse-util.h"
#include "path-util.h"
#include "proc-cmdline.h"
#include "process-util.h"
#include "psi-util.h"
-#include "random-util.h"
-#include "recurse-dir.h"
#include "rlimit-util.h"
#include "rm-rf.h"
#include "seccomp-util.h"
context_has_syscall_logs(c);
}
-bool exec_context_has_credentials(const ExecContext *context) {
-
- assert(context);
-
- return !hashmap_isempty(context->set_credentials) ||
- !hashmap_isempty(context->load_credentials) ||
- !set_isempty(context->import_credentials);
-}
-
#if HAVE_SECCOMP
static bool skip_seccomp_unavailable(const Unit* u, const char* msg) {
dev_t journal_stream_dev,
ino_t journal_stream_ino,
const char *memory_pressure_path,
+ const char *creds_path,
char ***ret) {
_cleanup_strv_free_ char **our_env = NULL;
our_env[n_env++] = x;
}
- if (exec_context_has_credentials(c) && p->prefix[EXEC_DIRECTORY_RUNTIME]) {
- x = strjoin("CREDENTIALS_DIRECTORY=", p->prefix[EXEC_DIRECTORY_RUNTIME], "/credentials/", u->id);
+ if (creds_path) {
+ x = strjoin("CREDENTIALS_DIRECTORY=", creds_path);
if (!x)
return -ENOMEM;
return r;
}
-static int write_credential(
- int dfd,
- const char *id,
- const void *data,
- size_t size,
- uid_t uid,
- gid_t gid,
- bool ownership_ok) {
-
- _cleanup_(unlink_and_freep) char *tmp = NULL;
- _cleanup_close_ int fd = -EBADF;
- int r;
-
- r = tempfn_random_child("", "cred", &tmp);
- if (r < 0)
- return r;
-
- fd = openat(dfd, tmp, O_CREAT|O_RDWR|O_CLOEXEC|O_EXCL|O_NOFOLLOW|O_NOCTTY, 0600);
- if (fd < 0) {
- tmp = mfree(tmp);
- return -errno;
- }
-
- r = loop_write(fd, data, size, /* do_poll = */ false);
- if (r < 0)
- return r;
-
- if (fchmod(fd, 0400) < 0) /* Take away "w" bit */
- return -errno;
-
- if (uid_is_valid(uid) && uid != getuid()) {
- r = fd_add_uid_acl_permission(fd, uid, ACL_READ);
- if (r < 0) {
- if (!ERRNO_IS_NOT_SUPPORTED(r) && !ERRNO_IS_PRIVILEGE(r))
- return r;
-
- if (!ownership_ok) /* Ideally we use ACLs, since we can neatly express what we want
- * to express: that the user gets read access and nothing
- * else. But if the backing fs can't support that (e.g. ramfs)
- * then we can use file ownership instead. But that's only safe if
- * we can then re-mount the whole thing read-only, so that the
- * user can no longer chmod() the file to gain write access. */
- return r;
-
- if (fchown(fd, uid, gid) < 0)
- return -errno;
- }
- }
-
- if (renameat(dfd, tmp, dfd, id) < 0)
- return -errno;
-
- tmp = mfree(tmp);
- return 0;
-}
-
-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. 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 (IN_SET(path, CREDENTIAL_SEARCH_PATH_ENCRYPTED, CREDENTIAL_SEARCH_PATH_ALL)) {
- if (strv_extend(&l, params->received_encrypted_credentials_directory) < 0)
- return NULL;
-
- if (strv_extend_strv(&l, CONF_PATHS_STRV("credstore.encrypted"), /* filter_duplicates= */ true) < 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 (DEBUG_LOGGING) {
- _cleanup_free_ char *t = strv_join(l, ":");
-
- log_debug("Credential search path is: %s", strempty(t));
- }
-
- return TAKE_PTR(l);
-}
-
-static int maybe_decrypt_and_write_credential(
- int dir_fd,
- const char *id,
- bool encrypted,
- uid_t uid,
- gid_t gid,
- 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, gid, 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,
- gid_t gid,
- 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,
- gid,
- 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,
- const char *id,
- const char *path,
- bool encrypted,
- const char *unit,
- int read_dfd,
- int write_dfd,
- uid_t uid,
- gid_t gid,
- bool ownership_ok,
- uint64_t *left) {
-
- ReadFullFileFlags flags = READ_FULL_FILE_SECURE|READ_FULL_FILE_FAIL_WHEN_LARGER;
- _cleanup_strv_free_ char **search_path = NULL;
- _cleanup_(erase_and_freep) char *data = NULL;
- _cleanup_free_ char *bindname = NULL;
- const char *source = NULL;
- bool missing_ok = true;
- size_t size, maxsz;
- int r;
-
- assert(context);
- assert(params);
- assert(id);
- assert(path);
- assert(unit);
- assert(read_dfd >= 0 || read_dfd == AT_FDCWD);
- assert(write_dfd >= 0);
- assert(left);
-
- if (read_dfd >= 0) {
- /* If a directory fd is specified, then read the file directly from that dir. In this case we
- * won't do AF_UNIX stuff (we simply don't want to recursively iterate down a tree of AF_UNIX
- * IPC sockets). It's OK if a file vanishes here in the time we enumerate it and intend to
- * open it. */
-
- if (!filename_is_valid(path)) /* safety check */
- return -EINVAL;
-
- missing_ok = true;
- source = path;
-
- } else if (path_is_absolute(path)) {
- /* If this is an absolute path, read the data directly from it, and support AF_UNIX
- * sockets */
-
- if (!path_is_valid(path)) /* safety check */
- return -EINVAL;
-
- flags |= READ_FULL_FILE_CONNECT_SOCKET;
-
- /* Pass some minimal info about the unit and the credential name we are looking to acquire
- * via the source socket address in case we read off an AF_UNIX socket. */
- if (asprintf(&bindname, "@%" PRIx64"/unit/%s/%s", random_u64(), unit, id) < 0)
- return -ENOMEM;
-
- missing_ok = false;
- source = path;
-
- } else if (credential_name_valid(path)) {
- /* If this is a relative path, take it as credential name relative to the credentials
- * 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, CREDENTIAL_SEARCH_PATH_ALL);
- if (!search_path)
- return -ENOMEM;
-
- missing_ok = true;
- } else
- source = NULL;
-
- if (encrypted)
- flags |= READ_FULL_FILE_UNBASE64;
-
- maxsz = encrypted ? CREDENTIAL_ENCRYPTED_SIZE_MAX : CREDENTIAL_SIZE_MAX;
-
- if (search_path) {
- STRV_FOREACH(d, search_path) {
- _cleanup_free_ char *j = NULL;
-
- j = path_join(*d, path);
- if (!j)
- return -ENOMEM;
-
- r = read_full_file_full(
- AT_FDCWD, j, /* path is absolute, hence pass AT_FDCWD as nop dir fd here */
- UINT64_MAX,
- maxsz,
- flags,
- NULL,
- &data, &size);
- if (r != -ENOENT)
- break;
- }
- } else if (source)
- r = read_full_file_full(
- read_dfd, source,
- UINT64_MAX,
- maxsz,
- flags,
- bindname,
- &data, &size);
- else
- r = -ENOENT;
-
- if (r == -ENOENT && (missing_ok || hashmap_contains(context->set_credentials, id))) {
- /* Make a missing inherited credential non-fatal, let's just continue. After all apps
- * will get clear errors if we don't pass such a missing credential on as they
- * themselves will get ENOENT when trying to read them, which should not be much
- * worse than when we handle the error here and make it fatal.
- *
- * Also, if the source file doesn't exist, but a fallback is set via SetCredentials=
- * we are fine, too. */
- log_debug_errno(r, "Couldn't read inherited credential '%s', skipping: %m", path);
- return 0;
- }
- if (r < 0)
- return log_debug_errno(r, "Failed to read credential '%s': %m", path);
-
- return maybe_decrypt_and_write_credential(write_dfd, id, encrypted, uid, gid, ownership_ok, data, size, left);
-}
-
-struct load_cred_args {
- const ExecContext *context;
- const ExecParameters *params;
- bool encrypted;
- const char *unit;
- int dfd;
- uid_t uid;
- gid_t gid;
- bool ownership_ok;
- uint64_t *left;
-};
-
-static int load_cred_recurse_dir_cb(
- RecurseDirEvent event,
- const char *path,
- int dir_fd,
- int inode_fd,
- const struct dirent *de,
- const struct statx *sx,
- void *userdata) {
-
- struct load_cred_args *args = ASSERT_PTR(userdata);
- _cleanup_free_ char *sub_id = NULL;
- int r;
-
- if (event != RECURSE_DIR_ENTRY)
- return RECURSE_DIR_CONTINUE;
-
- if (!IN_SET(de->d_type, DT_REG, DT_SOCK))
- return RECURSE_DIR_CONTINUE;
-
- sub_id = strreplace(path, "/", "_");
- if (!sub_id)
- return -ENOMEM;
-
- if (!credential_name_valid(sub_id))
- return log_debug_errno(SYNTHETIC_ERRNO(EINVAL), "Credential would get ID %s, which is not valid, refusing", sub_id);
-
- if (faccessat(args->dfd, sub_id, F_OK, AT_SYMLINK_NOFOLLOW) >= 0) {
- log_debug("Skipping credential with duplicated ID %s at %s", sub_id, path);
- return RECURSE_DIR_CONTINUE;
- }
- if (errno != ENOENT)
- return log_debug_errno(errno, "Failed to test if credential %s exists: %m", sub_id);
-
- r = load_credential(
- args->context,
- args->params,
- sub_id,
- de->d_name,
- args->encrypted,
- args->unit,
- dir_fd,
- args->dfd,
- args->uid,
- args->gid,
- args->ownership_ok,
- args->left);
- if (r < 0)
- return r;
-
- return RECURSE_DIR_CONTINUE;
-}
-
-static int acquire_credentials(
- const ExecContext *context,
- const ExecParameters *params,
- const char *unit,
- const char *p,
- uid_t uid,
- gid_t gid,
- bool ownership_ok) {
-
- uint64_t left = CREDENTIALS_TOTAL_SIZE_MAX;
- _cleanup_close_ int dfd = -EBADF;
- const char *ic;
- ExecLoadCredential *lc;
- ExecSetCredential *sc;
- int r;
-
- assert(context);
- assert(p);
-
- dfd = open(p, O_DIRECTORY|O_CLOEXEC);
- if (dfd < 0)
- return -errno;
-
- r = fd_acl_make_writable(dfd); /* Add the "w" bit, if we are reusing an already set up credentials dir where it was unset */
- if (r < 0)
- return r;
-
- /* First, load credentials off disk (or acquire via AF_UNIX socket) */
- HASHMAP_FOREACH(lc, context->load_credentials) {
- _cleanup_close_ int sub_fd = -EBADF;
-
- /* If this is an absolute path, then try to open it as a directory. If that works, then we'll
- * recurse into it. If it is an absolute path but it isn't a directory, then we'll open it as
- * a regular file. Finally, if it's a relative path we will use it as a credential name to
- * propagate a credential passed to us from further up. */
-
- if (path_is_absolute(lc->path)) {
- sub_fd = open(lc->path, O_DIRECTORY|O_CLOEXEC|O_RDONLY);
- if (sub_fd < 0 && !IN_SET(errno,
- ENOTDIR, /* Not a directory */
- ENOENT)) /* Doesn't exist? */
- return log_debug_errno(errno, "Failed to open '%s': %m", lc->path);
- }
-
- if (sub_fd < 0)
- /* Regular file (incl. a credential passed in from higher up) */
- r = load_credential(
- context,
- params,
- lc->id,
- lc->path,
- lc->encrypted,
- unit,
- AT_FDCWD,
- dfd,
- uid,
- gid,
- ownership_ok,
- &left);
- else
- /* Directory */
- r = recurse_dir(
- sub_fd,
- /* path= */ lc->id, /* recurse_dir() will suffix the subdir paths from here to the top-level id */
- /* statx_mask= */ 0,
- /* n_depth_max= */ UINT_MAX,
- RECURSE_DIR_SORT|RECURSE_DIR_IGNORE_DOT|RECURSE_DIR_ENSURE_TYPE,
- load_cred_recurse_dir_cb,
- &(struct load_cred_args) {
- .context = context,
- .params = params,
- .encrypted = lc->encrypted,
- .unit = unit,
- .dfd = dfd,
- .uid = uid,
- .gid = gid,
- .ownership_ok = ownership_ok,
- .left = &left,
- });
- if (r < 0)
- return r;
- }
-
- /* 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,
- gid,
- 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,
- gid,
- 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;
- size_t size, add;
-
- /* Note that we check ahead of time here instead of relying on O_EXCL|O_CREAT later to return
- * EEXIST if the credential already exists. That's because the TPM2-based decryption is kinda
- * slow and involved, hence it's nice to be able to skip that if the credential already
- * exists anyway. */
- if (faccessat(dfd, sc->id, F_OK, AT_SYMLINK_NOFOLLOW) >= 0)
- continue;
- if (errno != ENOENT)
- return log_debug_errno(errno, "Failed to test if credential %s exists: %m", sc->id);
-
- if (sc->encrypted) {
- r = decrypt_credential_and_warn(sc->id, now(CLOCK_REALTIME), NULL, NULL, sc->data, sc->size, &plaintext, &size);
- if (r < 0)
- return r;
-
- data = plaintext;
- } else {
- data = sc->data;
- size = sc->size;
- }
-
- add = strlen(sc->id) + size;
- if (add > left)
- return -E2BIG;
-
- r = write_credential(dfd, sc->id, data, size, uid, gid, ownership_ok);
- if (r < 0)
- return r;
-
- left -= add;
- }
-
- r = fd_acl_make_read_only(dfd); /* Now take away the "w" bit */
- if (r < 0)
- return r;
-
- /* After we created all keys with the right perms, also make sure the credential store as a whole is
- * accessible */
-
- if (uid_is_valid(uid) && uid != getuid()) {
- r = fd_add_uid_acl_permission(dfd, uid, ACL_READ | ACL_EXECUTE);
- if (r < 0) {
- if (!ERRNO_IS_NOT_SUPPORTED(r) && !ERRNO_IS_PRIVILEGE(r))
- return r;
-
- if (!ownership_ok)
- return r;
-
- if (fchown(dfd, uid, gid) < 0)
- return -errno;
- }
- }
-
- return 0;
-}
-
-static int setup_credentials_internal(
- const ExecContext *context,
- const ExecParameters *params,
- const char *unit,
- const char *final, /* This is where the credential store shall eventually end up at */
- const char *workspace, /* This is where we can prepare it before moving it to the final place */
- bool reuse_workspace, /* Whether to reuse any existing workspace mount if it already is a mount */
- bool must_mount, /* Whether to require that we mount something, it's not OK to use the plain directory fall back */
- uid_t uid,
- gid_t gid) {
-
- int r, workspace_mounted; /* negative if we don't know yet whether we have/can mount something; true
- * if we mounted something; false if we definitely can't mount anything */
- bool final_mounted;
- const char *where;
-
- assert(context);
- assert(final);
- assert(workspace);
-
- if (reuse_workspace) {
- r = path_is_mount_point(workspace, NULL, 0);
- if (r < 0)
- return r;
- if (r > 0)
- workspace_mounted = true; /* If this is already a mount, and we are supposed to reuse it, let's keep this in mind */
- else
- workspace_mounted = -1; /* We need to figure out if we can mount something to the workspace */
- } else
- workspace_mounted = -1; /* ditto */
-
- r = path_is_mount_point(final, NULL, 0);
- if (r < 0)
- return r;
- if (r > 0) {
- /* If the final place already has something mounted, we use that. If the workspace also has
- * something mounted we assume it's actually the same mount (but with MS_RDONLY
- * different). */
- final_mounted = true;
-
- if (workspace_mounted < 0) {
- /* If the final place is mounted, but the workspace isn't, then let's bind mount
- * the final version to the workspace, and make it writable, so that we can make
- * changes */
-
- r = mount_nofollow_verbose(LOG_DEBUG, final, workspace, NULL, MS_BIND|MS_REC, NULL);
- if (r < 0)
- return r;
-
- r = mount_nofollow_verbose(LOG_DEBUG, NULL, workspace, NULL, MS_BIND|MS_REMOUNT|credentials_fs_mount_flags(/* ro= */ false), NULL);
- if (r < 0)
- return r;
-
- workspace_mounted = true;
- }
- } else
- final_mounted = false;
-
- if (workspace_mounted < 0) {
- /* Nothing is mounted on the workspace yet, let's try to mount something now */
-
- r = mount_credentials_fs(workspace, CREDENTIALS_TOTAL_SIZE_MAX, /* ro= */ false);
- if (r < 0) {
- /* If that didn't work, try to make a bind mount from the final to the workspace, so that we can make it writable there. */
- r = mount_nofollow_verbose(LOG_DEBUG, final, workspace, NULL, MS_BIND|MS_REC, NULL);
- if (r < 0) {
- if (!ERRNO_IS_PRIVILEGE(r)) /* Propagate anything that isn't a permission problem */
- return r;
-
- if (must_mount) /* If we it's not OK to use the plain directory
- * fallback, propagate all errors too */
- return r;
-
- /* If we lack privileges to bind mount stuff, then let's gracefully
- * proceed for compat with container envs, and just use the final dir
- * as is. */
-
- workspace_mounted = false;
- } else {
- /* Make the new bind mount writable (i.e. drop MS_RDONLY) */
- r = mount_nofollow_verbose(LOG_DEBUG, NULL, workspace, NULL, MS_BIND|MS_REMOUNT|credentials_fs_mount_flags(/* ro= */ false), NULL);
- if (r < 0)
- return r;
-
- workspace_mounted = true;
- }
- } else
- workspace_mounted = true;
- }
-
- assert(!must_mount || workspace_mounted > 0);
- where = workspace_mounted ? workspace : final;
-
- (void) label_fix_full(AT_FDCWD, where, final, 0);
-
- r = acquire_credentials(context, params, unit, where, uid, gid, workspace_mounted);
- if (r < 0)
- return r;
-
- if (workspace_mounted) {
- bool install;
-
- /* Determine if we should actually install the prepared mount in the final location by bind
- * mounting it there. We do so only if the mount is not established there already, and if the
- * mount is actually non-empty (i.e. carries at least one credential). Not that in the best
- * case we are doing all this in a mount namespace, thus no one else will see that we
- * allocated a file system we are getting rid of again here. */
- if (final_mounted)
- install = false; /* already installed */
- else {
- r = dir_is_empty(where, /* ignore_hidden_or_backup= */ false);
- if (r < 0)
- return r;
-
- install = r == 0; /* install only if non-empty */
- }
-
- if (install) {
- /* Make workspace read-only now, so that any bind mount we make from it defaults to read-only too */
- r = mount_nofollow_verbose(LOG_DEBUG, NULL, workspace, NULL, MS_BIND|MS_REMOUNT|credentials_fs_mount_flags(/* ro= */ true), NULL);
- if (r < 0)
- return r;
-
- /* And mount it to the final place, read-only */
- r = mount_nofollow_verbose(LOG_DEBUG, workspace, final, NULL, MS_MOVE, NULL);
- } else
- /* Otherwise get rid of it */
- r = umount_verbose(LOG_DEBUG, workspace, MNT_DETACH|UMOUNT_NOFOLLOW);
- if (r < 0)
- return r;
- } else {
- _cleanup_free_ char *parent = NULL;
-
- /* If we do not have our own mount put used the plain directory fallback, then we need to
- * open access to the top-level credential directory and the per-service directory now */
-
- r = path_extract_directory(final, &parent);
- if (r < 0)
- return r;
- if (chmod(parent, 0755) < 0)
- return -errno;
- }
-
- return 0;
-}
-
-static int setup_credentials(
- const ExecContext *context,
- const ExecParameters *params,
- const char *unit,
- uid_t uid,
- gid_t gid) {
-
- _cleanup_free_ char *p = NULL, *q = NULL;
- int r;
-
- assert(context);
- assert(params);
-
- if (!exec_context_has_credentials(context))
- return 0;
-
- if (!params->prefix[EXEC_DIRECTORY_RUNTIME])
- return -EINVAL;
-
- /* This where we'll place stuff when we are done; this main credentials directory is world-readable,
- * and the subdir we mount over with a read-only file system readable by the service's user */
- q = path_join(params->prefix[EXEC_DIRECTORY_RUNTIME], "credentials");
- if (!q)
- return -ENOMEM;
-
- r = mkdir_label(q, 0755); /* top-level dir: world readable/searchable */
- if (r < 0 && r != -EEXIST)
- return r;
-
- p = path_join(q, unit);
- if (!p)
- return -ENOMEM;
-
- r = mkdir_label(p, 0700); /* per-unit dir: private to user */
- if (r < 0 && r != -EEXIST)
- return r;
-
- r = safe_fork("(sd-mkdcreds)", FORK_DEATHSIG|FORK_WAIT|FORK_NEW_MOUNTNS, NULL);
- if (r < 0) {
- _cleanup_free_ char *t = NULL, *u = NULL;
-
- /* If this is not a privilege or support issue then propagate the error */
- if (!ERRNO_IS_NOT_SUPPORTED(r) && !ERRNO_IS_PRIVILEGE(r))
- return r;
-
- /* Temporary workspace, that remains inaccessible all the time. We prepare stuff there before moving
- * it into place, so that users can't access half-initialized credential stores. */
- t = path_join(params->prefix[EXEC_DIRECTORY_RUNTIME], "systemd/temporary-credentials");
- if (!t)
- return -ENOMEM;
-
- /* We can't set up a mount namespace. In that case operate on a fixed, inaccessible per-unit
- * directory outside of /run/credentials/ first, and then move it over to /run/credentials/
- * after it is fully set up */
- u = path_join(t, unit);
- if (!u)
- return -ENOMEM;
-
- FOREACH_STRING(i, t, u) {
- r = mkdir_label(i, 0700);
- if (r < 0 && r != -EEXIST)
- return r;
- }
-
- r = setup_credentials_internal(
- context,
- params,
- unit,
- p, /* final mount point */
- u, /* temporary workspace to overmount */
- true, /* reuse the workspace if it is already a mount */
- false, /* it's OK to fall back to a plain directory if we can't mount anything */
- uid,
- gid);
-
- (void) rmdir(u); /* remove the workspace again if we can. */
-
- if (r < 0)
- return r;
-
- } else if (r == 0) {
-
- /* We managed to set up a mount namespace, and are now in a child. That's great. In this case
- * we can use the same directory for all cases, after turning off propagation. Question
- * though is: where do we turn off propagation exactly, and where do we place the workspace
- * directory? We need some place that is guaranteed to be a mount point in the host, and
- * which is guaranteed to have a subdir we can mount over. /run/ is not suitable for this,
- * since we ultimately want to move the resulting file system there, i.e. we need propagation
- * for /run/ eventually. We could use our own /run/systemd/bind mount on itself, but that
- * would be visible in the host mount table all the time, which we want to avoid. Hence, what
- * we do here instead we use /dev/ and /dev/shm/ for our purposes. We know for sure that
- * /dev/ is a mount point and we now for sure that /dev/shm/ exists. Hence we can turn off
- * propagation on the former, and then overmount the latter.
- *
- * Yes it's nasty playing games with /dev/ and /dev/shm/ like this, since it does not exist
- * for this purpose, but there are few other candidates that work equally well for us, and
- * given that the we do this in a privately namespaced short-lived single-threaded process
- * that no one else sees this should be OK to do. */
-
- r = mount_nofollow_verbose(LOG_DEBUG, NULL, "/dev", NULL, MS_SLAVE|MS_REC, NULL); /* Turn off propagation from our namespace to host */
- if (r < 0)
- goto child_fail;
-
- r = setup_credentials_internal(
- context,
- params,
- unit,
- p, /* final mount point */
- "/dev/shm", /* temporary workspace to overmount */
- false, /* do not reuse /dev/shm if it is already a mount, under no circumstances */
- true, /* insist that something is mounted, do not allow fallback to plain directory */
- uid,
- gid);
- if (r < 0)
- goto child_fail;
-
- _exit(EXIT_SUCCESS);
-
- child_fail:
- _exit(EXIT_FAILURE);
- }
-
- /* If the credentials dir is empty and not a mount point, then there's no point in having it. Let's
- * try to remove it. This matters in particular if we created the dir as mount point but then didn't
- * actually end up mounting anything on it. In that case we'd rather have ENOENT than EACCESS being
- * seen by users when trying access this inode. */
- (void) rmdir(p);
- return 0;
-}
-
#if ENABLE_SMACK
static int setup_smack(
const Manager *manager,
const ExecParameters *params,
ExecRuntime *runtime,
const char *memory_pressure_path,
+ const char *creds_path,
+ int creds_fd,
char **error_path) {
_cleanup_(verity_settings_done) VeritySettings verity = VERITY_SETTINGS_DEFAULT;
_cleanup_strv_free_ char **empty_directories = NULL, **symlinks = NULL,
**read_write_paths_cleanup = NULL;
- _cleanup_free_ char *creds_path = NULL, *incoming_dir = NULL, *propagate_dir = NULL,
+ _cleanup_free_ char *incoming_dir = NULL, *propagate_dir = NULL,
*extension_dir = NULL, *host_os_release_stage = NULL;
const char *root_dir = NULL, *root_image = NULL, *tmp_dir = NULL, *var_tmp_dir = NULL;
char **read_write_paths;
if (context->mount_propagation_flag == MS_SHARED)
log_unit_debug(u, "shared mount propagation hidden by other fs namespacing unit settings: ignoring");
- if (exec_context_has_credentials(context) &&
- params->prefix[EXEC_DIRECTORY_RUNTIME] &&
- FLAGS_SET(params->flags, EXEC_WRITE_CREDENTIALS)) {
- creds_path = path_join(params->prefix[EXEC_DIRECTORY_RUNTIME], "credentials", u->id);
- if (!creds_path)
- return -ENOMEM;
- }
-
if (params->runtime_scope == RUNTIME_SCOPE_SYSTEM) {
propagate_dir = path_join("/run/systemd/propagate/", u->id);
if (!propagate_dir)
tmp_dir,
var_tmp_dir,
creds_path,
+ creds_fd,
context->log_namespace,
context->mount_propagation_flag,
&verity,
int r, ngids = 0, exec_fd;
_cleanup_free_ gid_t *supplementary_gids = NULL;
const char *username = NULL, *groupname = NULL;
- _cleanup_free_ char *home_buffer = NULL, *memory_pressure_path = NULL;
+ _cleanup_free_ char *home_buffer = NULL, *memory_pressure_path = NULL, *creds_path = NULL;
const char *home = NULL, *shell = NULL;
char **final_argv = NULL;
dev_t journal_stream_dev = 0;
int ngids_after_pam = 0;
_cleanup_free_ int *fds = NULL;
_cleanup_strv_free_ char **fdnames = NULL;
+ _cleanup_close_ int creds_fd = -EBADF;
assert(unit);
assert(command);
}
if (FLAGS_SET(params->flags, EXEC_WRITE_CREDENTIALS)) {
- r = setup_credentials(context, params, unit->id, uid, gid);
+ r = setup_credentials(context, params, unit->id, uid, gid, &creds_path, &creds_fd);
if (r < 0) {
*exit_status = EXIT_CREDENTIALS;
return log_unit_error_errno(unit, r, "Failed to set up credentials: %m");
journal_stream_dev,
journal_stream_ino,
memory_pressure_path,
+ creds_path,
&our_env);
if (r < 0) {
*exit_status = EXIT_MEMORY;
if (needs_mount_namespace) {
_cleanup_free_ char *error_path = NULL;
- r = apply_mount_namespace(unit, command->flags, context, params, runtime, memory_pressure_path, &error_path);
+ r = apply_mount_namespace(unit, command->flags, context, params, runtime, memory_pressure_path, creds_path, creds_fd, &error_path);
if (r < 0) {
*exit_status = EXIT_NAMESPACE;
return log_unit_error_errno(unit, r, "Failed to set up mount namespacing%s%s: %m",
}
}
+ if (creds_fd >= 0) {
+ assert(creds_path);
+
+ /* When a mount namespace is not requested, then the target directory may not exist yet.
+ * Here, we ignore the failure, as if it fails, the subsequent move_mount() will fail. */
+ (void) mkdir_p_label(creds_path, 0755);
+
+ if (move_mount(creds_fd, "", AT_FDCWD, creds_path, MOVE_MOUNT_F_EMPTY_PATH) < 0) {
+ *exit_status = EXIT_CREDENTIALS;
+ return log_unit_error_errno(unit, errno, "Failed to mount credentials directory on %s: %m", creds_path);
+ }
+ }
+
if (needs_sandboxing) {
r = apply_protect_hostname(unit, context, exit_status);
if (r < 0)
return 0;
}
-int exec_context_destroy_credentials(const ExecContext *c, const char *runtime_prefix, const char *unit) {
- _cleanup_free_ char *p = NULL;
-
- assert(c);
-
- if (!runtime_prefix || !unit)
- return 0;
-
- p = path_join(runtime_prefix, "credentials", unit);
- if (!p)
- return -ENOMEM;
-
- /* This is either a tmpfs/ramfs of its own, or a plain directory. Either way, let's first try to
- * unmount it, and afterwards remove the mount point */
- (void) umount2(p, MNT_DETACH|UMOUNT_NOFOLLOW);
- (void) rm_rf(p, REMOVE_ROOT|REMOVE_CHMOD);
-
- return 0;
-}
-
int exec_context_destroy_mount_ns_dir(Unit *u) {
_cleanup_free_ char *p = NULL;
return 0;
}
-bool exec_context_has_encrypted_credentials(ExecContext *c) {
- ExecLoadCredential *load_cred;
- ExecSetCredential *set_cred;
-
- assert(c);
-
- HASHMAP_FOREACH(load_cred, c->load_credentials)
- if (load_cred->encrypted)
- return true;
-
- HASHMAP_FOREACH(set_cred, c->set_credentials)
- if (set_cred->encrypted)
- return true;
-
- return false;
-}
-
void exec_status_start(ExecStatus *s, pid_t pid) {
assert(s);
p->exec_fd = safe_close(p->exec_fd);
}
-ExecSetCredential *exec_set_credential_free(ExecSetCredential *sc) {
- if (!sc)
- return NULL;
-
- free(sc->id);
- free(sc->data);
- return mfree(sc);
-}
-
-ExecLoadCredential *exec_load_credential_free(ExecLoadCredential *lc) {
- if (!lc)
- return NULL;
-
- free(lc->id);
- free(lc->path);
- return mfree(lc);
-}
-
void exec_directory_done(ExecDirectory *d) {
if (!d)
return;
return 1U << t;
}
-DEFINE_HASH_OPS_WITH_VALUE_DESTRUCTOR(exec_set_credential_hash_ops, char, string_hash_func, string_compare_func, ExecSetCredential, exec_set_credential_free);
-DEFINE_HASH_OPS_WITH_VALUE_DESTRUCTOR(exec_load_credential_hash_ops, char, string_hash_func, string_compare_func, ExecLoadCredential, exec_load_credential_free);
-
static const char* const exec_input_table[_EXEC_INPUT_MAX] = {
[EXEC_INPUT_NULL] = "null",
[EXEC_INPUT_TTY] = "tty",
_EXEC_CLEAN_MASK_INVALID = -EINVAL,
} ExecCleanMask;
-/* A credential configured with LoadCredential= */
-typedef struct ExecLoadCredential {
- char *id, *path;
- bool encrypted;
-} ExecLoadCredential;
-
-/* A credential configured with SetCredential= */
-typedef struct ExecSetCredential {
- char *id;
- bool encrypted;
- void *data;
- size_t size;
-} ExecSetCredential;
-
/* Encodes configuration parameters applied to invoked commands. Does not carry runtime data, but only configuration
* changes sourced from unit files and suchlike. ExecContext objects are usually embedded into Unit objects, and do not
* change after being loaded. */
void exec_context_dump(const ExecContext *c, FILE* f, const char *prefix);
int exec_context_destroy_runtime_directory(const ExecContext *c, const char *runtime_root);
-int exec_context_destroy_credentials(const ExecContext *c, const char *runtime_root, const char *unit);
int exec_context_destroy_mount_ns_dir(Unit *u);
const char* exec_context_fdname(const ExecContext *c, int fd_index);
bool exec_context_may_touch_console(const ExecContext *c);
bool exec_context_maintains_privileges(const ExecContext *c);
-bool exec_context_has_encrypted_credentials(ExecContext *c);
-bool exec_context_has_credentials(const ExecContext *context);
int exec_context_get_effective_ioprio(const ExecContext *c);
bool exec_context_get_effective_mount_apivfs(const ExecContext *c);
bool exec_context_get_cpu_affinity_from_numa(const ExecContext *c);
-ExecSetCredential *exec_set_credential_free(ExecSetCredential *sc);
-DEFINE_TRIVIAL_CLEANUP_FUNC(ExecSetCredential*, exec_set_credential_free);
-
-ExecLoadCredential *exec_load_credential_free(ExecLoadCredential *lc);
-DEFINE_TRIVIAL_CLEANUP_FUNC(ExecLoadCredential*, exec_load_credential_free);
-
void exec_directory_done(ExecDirectory *d);
int exec_directory_add(ExecDirectory *d, const char *path, const char *symlink);
void exec_directory_sort(ExecDirectory *d);
ExecCleanMask exec_clean_mask_from_string(const char *s);
-extern const struct hash_ops exec_set_credential_hash_ops;
-extern const struct hash_ops exec_load_credential_hash_ops;
-
const char* exec_output_to_string(ExecOutput i) _const_;
ExecOutput exec_output_from_string(const char *s) _pure_;
#include "conf-parser.h"
#include "core-varlink.h"
#include "cpu-set-util.h"
+#include "credential.h"
#include "creds-util.h"
#include "env-util.h"
#include "errno-list.h"
'bpf-socket-bind.c',
'cgroup.c',
'core-varlink.c',
+ 'credential.c',
'dbus-automount.c',
'dbus-cgroup.c',
'dbus-device.c',
EXTENSION_DIRECTORIES, /* Bind-mounted outside the root directory, and used by subsequent mounts */
EXTENSION_IMAGES, /* Mounted outside the root directory, and used by subsequent mounts */
MQUEUEFS,
- READWRITE_IMPLICIT, /* Should have the lowest priority. */
+ READWRITE_IMPLICIT, /* Should have the 2nd lowest priority. */
+ MKDIR, /* Should have the lowest priority. */
_MOUNT_MODE_MAX,
} MountMode;
};
static const char * const mount_mode_table[_MOUNT_MODE_MAX] = {
- [INACCESSIBLE] = "inaccessible",
- [OVERLAY_MOUNT] = "overlay",
- [BIND_MOUNT] = "bind",
- [BIND_MOUNT_RECURSIVE] = "rbind",
- [PRIVATE_TMP] = "private-tmp",
- [PRIVATE_DEV] = "private-dev",
- [BIND_DEV] = "bind-dev",
- [EMPTY_DIR] = "empty",
- [PRIVATE_SYSFS] = "private-sysfs",
- [BIND_SYSFS] = "bind-sysfs",
- [PROCFS] = "procfs",
- [READONLY] = "read-only",
- [READWRITE] = "read-write",
- [TMPFS] = "tmpfs",
- [MOUNT_IMAGES] = "mount-images",
- [READWRITE_IMPLICIT] = "rw-implicit",
- [EXEC] = "exec",
- [NOEXEC] = "noexec",
- [MQUEUEFS] = "mqueuefs",
+ [INACCESSIBLE] = "inaccessible",
+ [OVERLAY_MOUNT] = "overlay",
+ [MOUNT_IMAGES] = "mount-images",
+ [BIND_MOUNT] = "bind",
+ [BIND_MOUNT_RECURSIVE] = "rbind",
+ [PRIVATE_TMP] = "private-tmp",
+ [PRIVATE_TMP_READONLY] = "private-tmp-read-only",
+ [PRIVATE_DEV] = "private-dev",
+ [BIND_DEV] = "bind-dev",
+ [EMPTY_DIR] = "empty",
+ [PRIVATE_SYSFS] = "private-sysfs",
+ [BIND_SYSFS] = "bind-sysfs",
+ [PROCFS] = "procfs",
+ [READONLY] = "read-only",
+ [READWRITE] = "read-write",
+ [NOEXEC] = "noexec",
+ [EXEC] = "exec",
+ [TMPFS] = "tmpfs",
+ [RUN] = "run",
+ [EXTENSION_DIRECTORIES] = "extension-directories",
+ [EXTENSION_IMAGES] = "extension-images",
+ [MQUEUEFS] = "mqueuefs",
+ [READWRITE_IMPLICIT] = "read-write-implicit",
+ [MKDIR] = "mkdir",
};
/* Helper struct for naming simplicity and reusability */
if (r > 0) /* make this a NOP if /dev is already a mount point */
return 0;
- r = mount_nofollow_verbose(LOG_DEBUG, "/dev", mount_entry_path(m), NULL, MS_BIND|MS_REC, NULL);
- if (r < 0)
- return r;
-
- return 1;
-}
-
-static int mount_private_sysfs(const MountEntry *m) {
- const char *p = mount_entry_path(ASSERT_PTR(m));
- int r;
-
- (void) mkdir_p_label(p, 0755);
-
- r = remount_sysfs(p);
- if (r < 0 && (ERRNO_IS_PRIVILEGE(r) || ERRNO_IS_NOT_SUPPORTED(r))) {
- /* Running with an unprivileged user (PrivateUsers=yes), or the kernel seems old. Falling
- * back to bind mount the host's version so that we get all child mounts of it, too. */
-
- log_debug_errno(r, "Failed to remount sysfs on %s, falling back to bind mount: %m", p);
-
- (void) umount_recursive(p, 0);
-
- r = mount_nofollow_verbose(LOG_DEBUG, "/sys", p, NULL, MS_BIND|MS_REC, NULL);
- }
- if (r < 0)
- return log_debug_errno(r, "Failed to remount sysfs on %s: %m", p);
-
- return 1;
+ return mount_nofollow_verbose(LOG_DEBUG, "/dev", mount_entry_path(m), NULL, MS_BIND|MS_REC, NULL);
}
static int mount_bind_sysfs(const MountEntry *m) {
return 0;
/* Bind mount the host's version so that we get all child mounts of it, too. */
- r = mount_nofollow_verbose(LOG_DEBUG, "/sys", mount_entry_path(m), NULL, MS_BIND|MS_REC, NULL);
- if (r < 0)
+ return mount_nofollow_verbose(LOG_DEBUG, "/sys", mount_entry_path(m), NULL, MS_BIND|MS_REC, NULL);
+}
+
+static int mount_private_sysfs(const MountEntry *m) {
+ const char *entry_path = mount_entry_path(ASSERT_PTR(m));
+ int r, n;
+
+ (void) mkdir_p_label(entry_path, 0755);
+
+ n = umount_recursive(entry_path, 0);
+
+ r = mount_nofollow_verbose(LOG_DEBUG, "sysfs", entry_path, "sysfs", MS_NOSUID|MS_NOEXEC|MS_NODEV, NULL);
+ if (ERRNO_IS_NEG_PRIVILEGE(r)) {
+ /* When we do not have enough privileges to mount sysfs, fall back to use existing /sys. */
+
+ if (n > 0)
+ /* /sys or some of sub-mounts are umounted in the above. Refuse incomplete tree.
+ * Propagate the original error code returned by mount() in the above. */
+ return r;
+
+ return mount_bind_sysfs(m);
+
+ } else if (r < 0)
return r;
- return 1;
+ /* We mounted a new instance now. Let's bind mount the children over now. */
+ (void) bind_mount_submounts("/sys", entry_path);
+ return 0;
}
static int mount_procfs(const MountEntry *m, const NamespaceInfo *ns_info) {
* means we really don't want to use it, since it would affect our host's /proc
* mount. Hence let's gracefully fallback to a classic, unrestricted version. */
r = mount_nofollow_verbose(LOG_DEBUG, "proc", entry_path, "proc", MS_NOSUID|MS_NOEXEC|MS_NODEV, NULL);
- if (r == -EPERM) {
- /* When we do not have enough privileges to mount /proc, fallback to use existing /proc. */
+ if (ERRNO_IS_NEG_PRIVILEGE(r)) {
+ /* When we do not have enough privileges to mount /proc, fall back to use existing /proc. */
if (n > 0)
/* /proc or some of sub-mounts are umounted in the above. Refuse incomplete tree.
* Propagate the original error code returned by mount() in the above. */
- return -EPERM;
+ return r;
r = path_is_mount_point(entry_path, NULL, 0);
if (r < 0)
return log_debug_errno(r, "Unable to determine whether /proc is already mounted: %m");
- if (r == 0) {
- /* We lack permissions to mount a new instance of /proc, and it is not already
- * mounted. But we can access the host's, so as a final fallback bind-mount it to
- * the destination, as most likely we are inside a user manager in an unprivileged
- * user namespace. */
- r = mount_nofollow_verbose(LOG_DEBUG, "/proc", entry_path, NULL, MS_BIND|MS_REC, NULL);
- if (r < 0)
- return -EPERM;
- }
+ if (r > 0)
+ return 0;
+
+ /* We lack permissions to mount a new instance of /proc, and it is not already mounted. But
+ * we can access the host's, so as a final fallback bind-mount it to the destination, as most
+ * likely we are inside a user manager in an unprivileged user namespace. */
+ return mount_nofollow_verbose(LOG_DEBUG, "/proc", entry_path, NULL, MS_BIND|MS_REC, NULL);
+
} else if (r < 0)
return r;
- else
- /* We mounted a new instance now. Let's bind mount the children over now. This matters for
- * nspawn where a bunch of files are overmounted, in particular the boot id */
- (void) bind_mount_submounts("/proc", entry_path);
- return 1;
+ /* We mounted a new instance now. Let's bind mount the children over now. This matters for nspawn
+ * where a bunch of files are overmounted, in particular the boot id */
+ (void) bind_mount_submounts("/proc", entry_path);
+ return 0;
}
static int mount_tmpfs(const MountEntry *m) {
if (r < 0)
return log_debug_errno(r, "Failed to fix label of '%s' as '%s': %m", entry_path, inner_path);
- return 1;
+ return 0;
}
static int mount_run(const MountEntry *m) {
if (r < 0)
return log_debug_errno(r, "Failed to mount image %s on %s: %m", mount_entry_source(m), mount_entry_path(m));
- return 1;
+ return 0;
}
static int mount_overlay(const MountEntry *m) {
r = mount_nofollow_verbose(LOG_DEBUG, "overlay", mount_entry_path(m), "overlay", MS_RDONLY, options);
if (r == -ENOENT && m->ignore)
return 0;
- if (r < 0)
- return r;
- return 1;
+ return r;
}
static int follow_symlink(
case OVERLAY_MOUNT:
return mount_overlay(m);
+ case MKDIR:
+ r = mkdir_p_label(mount_entry_path(m), 0755);
+ if (r < 0)
+ return r;
+ return 1;
+
default:
assert_not_reached();
}
const char* tmp_dir,
const char* var_tmp_dir,
const char *creds_path,
+ int creds_fd,
const char *log_namespace,
unsigned long mount_propagation_flag,
VeritySettings *verity,
.flags = MS_NODEV|MS_STRICTATIME|MS_NOSUID|MS_NOEXEC,
};
- *(m++) = (MountEntry) {
- .path_const = creds_path,
- .mode = BIND_MOUNT,
- .read_only = true,
- .source_const = creds_path,
- .ignore = true,
- };
+ /* If we have mount fd for credentials directory, then it will be mounted after
+ * namespace is set up. So, here we only create the mount point. */
+
+ if (creds_fd < 0)
+ *(m++) = (MountEntry) {
+ .path_const = creds_path,
+ .mode = BIND_MOUNT,
+ .read_only = true,
+ .source_const = creds_path,
+ .ignore = true,
+ };
+ else
+ *(m++) = (MountEntry) {
+ .path_const = creds_path,
+ .mode = MKDIR,
+ };
} else {
/* If our service has no credentials store configured, then make the whole
* credentials tree inaccessible wholesale. */
const char *tmp_dir,
const char *var_tmp_dir,
const char *creds_path,
+ int creds_fd,
const char *log_namespace,
unsigned long mount_propagation_flag,
VeritySettings *verity,
p->n_auxiliary_fds = 0;
clear:
- for (size_t i = 0; i < n; ++i)
- free(ent[i]);
-
+ free_many((void**) ent, n);
return r;
}
#include "cgroup-util.h"
#include "chase.h"
#include "core-varlink.h"
+#include "credential.h"
#include "dbus-unit.h"
#include "dbus.h"
#include "dropin.h"
/* User manager might have its umask redefined by PAM or UMask=. In this
* case let the units it manages inherit this value by default. They can
* still tune this value through their own unit file */
- (void) get_process_umask(getpid_cached(), &ec->umask);
+ (void) get_process_umask(0, &ec->umask);
}
}
r = unit_add_two_dependencies_by_name(u, UNIT_AFTER, UNIT_REQUIRES, varlink_socket_unit, true, UNIT_DEPENDENCY_FILE);
if (r < 0)
return r;
- } else
+ } else {
r = unit_add_dependency_by_name(u, UNIT_AFTER, SPECIAL_JOURNALD_SOCKET, true, UNIT_DEPENDENCY_FILE);
- if (r < 0)
- return r;
-
- if (exec_context_has_credentials(c) && u->manager->prefix[EXEC_DIRECTORY_RUNTIME]) {
- _cleanup_free_ char *p = NULL, *m = NULL;
-
- /* Let's make sure the credentials directory of this service is unmounted *after* the service
- * itself shuts down. This only matters if mount namespacing is not used for the service, and
- * hence the credentials mount appears on the host. */
-
- p = path_join(u->manager->prefix[EXEC_DIRECTORY_RUNTIME], "credentials", u->id);
- if (!p)
- return -ENOMEM;
-
- r = unit_name_from_path(p, ".mount", &m);
- if (r < 0)
- return r;
-
- r = unit_add_dependency_by_name(u, UNIT_AFTER, m, /* add_reference= */ true, UNIT_DEPENDENCY_FILE);
if (r < 0)
return r;
}
+ r = unit_add_default_credential_dependencies(u, c);
+ if (r < 0)
+ return r;
+
return 0;
}
r = 0;
finish:
- for (size_t i = 0; i < n_message_parts; i++)
- free(message_parts[i]);
+ free_many_charp(message_parts, n_message_parts);
for (size_t i = 0; i < n_iovec; i++)
free(iovec[i].iov_base);
log_debug_errno(r, "json_variant_filter: %d/%m", r);
/* I assume we can merge v with itself… */
- r = json_variant_merge(&v, v);
+ r = json_variant_merge_object(&v, v);
log_debug_errno(r, "json_variant_merge: %d/%m", r);
r = json_variant_append_array(&v, v);
if (r < 0)
return log_error_errno(r, "Failed to filter identity: %m");
- r = json_variant_merge(&v, arg_identity_extra);
+ r = json_variant_merge_object(&v, arg_identity_extra);
if (r < 0)
return log_error_errno(r, "Failed to merge identities: %m");
if (!json_variant_equal(u, mmid))
continue;
- r = json_variant_merge(&add, z);
+ r = json_variant_merge_object(&add, z);
if (r < 0)
return log_error_errno(r, "Failed to merge perMachine entry: %m");
if (r < 0)
return log_error_errno(r, "Failed to filter perMachine: %m");
- r = json_variant_merge(&add, arg_identity_extra_this_machine);
+ r = json_variant_merge_object(&add, arg_identity_extra_this_machine);
if (r < 0)
return log_error_errno(r, "Failed to merge in perMachine fields: %m");
if (r < 0)
return log_error_errno(r, "Failed to filter resource limits: %m");
- r = json_variant_merge(&rlv, arg_identity_extra_rlimits);
+ r = json_variant_merge_object(&rlv, arg_identity_extra_rlimits);
if (r < 0)
return log_error_errno(r, "Failed to set resource limits: %m");
if (r < 0)
return log_error_errno(r, "Failed to filter identity (privileged part): %m");
- r = json_variant_merge(&privileged, arg_identity_extra_privileged);
+ r = json_variant_merge_object(&privileged, arg_identity_extra_privileged);
if (r < 0)
return log_error_errno(r, "Failed to merge identities (privileged part): %m");
}
case ARG_RLIMIT: {
- _cleanup_(json_variant_unrefp) JsonVariant *v = NULL, *jcur = NULL, *jmax = NULL;
+ _cleanup_(json_variant_unrefp) JsonVariant *jcur = NULL, *jmax = NULL;
_cleanup_free_ char *field = NULL, *t = NULL;
const char *eq;
struct rlimit rl;
if (r < 0)
return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Failed to allocate maximum integer: %m");
- r = json_build(&v,
- JSON_BUILD_OBJECT(
- JSON_BUILD_PAIR("cur", JSON_BUILD_VARIANT(jcur)),
- JSON_BUILD_PAIR("max", JSON_BUILD_VARIANT(jmax))));
- if (r < 0)
- return log_error_errno(r, "Failed to build resource limit: %m");
-
t = strjoin("RLIMIT_", rlimit_to_string(l));
if (!t)
return log_oom();
- r = json_variant_set_field(&arg_identity_extra_rlimits, t, v);
+ r = json_variant_set_fieldb(
+ &arg_identity_extra_rlimits, t,
+ JSON_BUILD_OBJECT(
+ JSON_BUILD_PAIR("cur", JSON_BUILD_VARIANT(jcur)),
+ JSON_BUILD_PAIR("max", JSON_BUILD_VARIANT(jmax))));
if (r < 0)
return log_error_errno(r, "Failed to set %s field: %m", rlimit_to_string(l));
if (r < 0)
return r;
- r = json_variant_merge(&m, status);
+ r = json_variant_merge_object(&m, status);
if (r < 0)
return r;
int user_record_sign(UserRecord *ur, EVP_PKEY *private_key, UserRecord **ret) {
_cleanup_(memstream_done) MemStream m = {};
- _cleanup_(json_variant_unrefp) JsonVariant *encoded = NULL, *v = NULL;
+ _cleanup_(json_variant_unrefp) JsonVariant *v = NULL;
_cleanup_(user_record_unrefp) UserRecord *signed_ur = NULL;
_cleanup_(EVP_MD_CTX_freep) EVP_MD_CTX *md_ctx = NULL;
_cleanup_free_ char *text = NULL, *key = NULL;
if (r < 0)
return r;
- r = json_build(&encoded, JSON_BUILD_ARRAY(
- JSON_BUILD_OBJECT(JSON_BUILD_PAIR("data", JSON_BUILD_BASE64(signature, signature_size)),
- JSON_BUILD_PAIR("key", JSON_BUILD_STRING(key)))));
- if (r < 0)
- return r;
-
v = json_variant_ref(ur->json);
- r = json_variant_set_field(&v, "signature", encoded);
+ r = json_variant_set_fieldb(
+ &v,
+ "signature",
+ JSON_BUILD_ARRAY(
+ JSON_BUILD_OBJECT(JSON_BUILD_PAIR("data", JSON_BUILD_BASE64(signature, signature_size)),
+ JSON_BUILD_PAIR("key", JSON_BUILD_STRING(key)))));
if (r < 0)
return r;
/* Merge the new entry with an old one, if that exists */
be = json_variant_ref(json_variant_by_key(binding, SD_ID128_TO_STRING(mid)));
if (be) {
- r = json_variant_merge(&be, new_binding_entry);
+ r = json_variant_merge_object(&be, new_binding_entry);
if (r < 0)
return r;
false,
UID_INVALID,
&c->polkit_registry,
- NULL);
+ error);
if (r == 0)
return 1; /* No authorization for now, but the async polkit stuff will call us again when it has it */
" -h --help Show this help\n"
" --version Show package version\n"
" -c --continuous Make systemd-bsod wait continuously\n"
- "for changes in the journal\n"
+ " for changes in the journal\n"
"\nSee the %s for details.\n",
program_invocation_short_name,
ansi_highlight(),
r = sd_journal_seek_head(j);
if (r < 0)
- return log_error_errno(r, "Failed to seek to start of jornal: %m");
+ return log_error_errno(r, "Failed to seek to start of journal: %m");
- for(;;) {
+ for (;;) {
r = sd_journal_next(j);
if (r < 0)
return log_error_errno(r, "Failed to read next journal entry: %m");
return 0;
}
- r = sd_journal_wait(j, (uint64_t) -1);
+ r = sd_journal_wait(j, UINT64_MAX);
if (r < 0)
return log_error_errno(r, "Failed to wait for changes: %m");
-
- continue;
}
r = sd_journal_get_data(j, "MESSAGE", &d, &l);
};
static const struct option options[] = {
- { "help", no_argument, NULL, 'h' },
- { "version", no_argument, NULL, ARG_VERSION },
- { "continuous", no_argument, NULL, 'c' },
+ { "help", no_argument, NULL, 'h' },
+ { "version", no_argument, NULL, ARG_VERSION },
+ { "continuous", no_argument, NULL, 'c' },
{}
};
default:
assert_not_reached();
}
+
if (optind < argc)
return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
"%s takes no argument.",
},
libexec_template + {
'name' : 'systemd-bsod',
+ 'public' : true,
'conditions' : ['HAVE_QRENCODE'],
'sources' : files('bsod.c'),
'link_with' : libshared,
install_mode : [ 'rwxr-xr-x', 'root', 'root' ])
if get_option('adm-group')
meson.add_install_script(
- 'sh', '-c',
+ sh, '-c',
'setfacl -nm g:adm:rx,d:g:adm:rx $DESTDIR/var/log/journal || :')
endif
if get_option('wheel-group')
meson.add_install_script(
- 'sh', '-c',
+ sh, '-c',
'setfacl -nm g:wheel:rx,d:g:wheel:rx $DESTDIR/var/log/journal || :')
endif
endif
#define USEC_2000 ((usec_t) 946684800000000) /* 2000-01-01 00:00:00 UTC */
static const char * const duid_type_table[_DUID_TYPE_MAX] = {
- [DUID_TYPE_LLT] = "DUID-LLT",
- [DUID_TYPE_EN] = "DUID-EN/Vendor",
- [DUID_TYPE_LL] = "DUID-LL",
- [DUID_TYPE_UUID] = "UUID",
- [DUID_TYPE_CUSTOM] = "Custom",
+ [DUID_TYPE_LLT] = "DUID-LLT",
+ [DUID_TYPE_EN] = "DUID-EN/Vendor",
+ [DUID_TYPE_LL] = "DUID-LL",
+ [DUID_TYPE_UUID] = "UUID",
};
DEFINE_STRING_TABLE_LOOKUP_TO_STRING(duid_type, DUIDType);
-int dhcp_validate_duid_len(DUIDType duid_type, size_t duid_len, bool strict) {
- struct duid d;
-
- assert_cc(sizeof(d.raw) >= MAX_DUID_LEN);
- if (duid_len > MAX_DUID_LEN)
- return -EINVAL;
-
- if (!strict)
- /* Strict validation is not requested. We only ensure that the
- * DUID is not too long. */
- return 0;
-
- switch (duid_type) {
- case DUID_TYPE_LLT:
- if (duid_len <= sizeof(d.llt))
- return -EINVAL;
- break;
- case DUID_TYPE_EN:
- if (duid_len != sizeof(d.en))
- return -EINVAL;
- break;
- case DUID_TYPE_LL:
- if (duid_len <= sizeof(d.ll))
- return -EINVAL;
- break;
- case DUID_TYPE_UUID:
- if (duid_len != sizeof(d.uuid))
- return -EINVAL;
- break;
- default:
- /* accept unknown type in order to be forward compatible */
- break;
- }
- return 0;
-}
-
-static int dhcp_identifier_set_duid_llt(
+int dhcp_identifier_set_duid_llt(
const struct hw_addr_data *hw_addr,
uint16_t arp_type,
usec_t t,
return 0;
}
-static int dhcp_identifier_set_duid_ll(
+int dhcp_identifier_set_duid_ll(
const struct hw_addr_data *hw_addr,
uint16_t arp_type,
struct duid *ret_duid,
/* a bit of snake-oil perhaps, but no need to expose the machine-id
* directly; duid->en.id might not be aligned, so we need to copy */
hash = htole64(siphash24(&machine_id, sizeof(machine_id), HASH_KEY.bytes));
- memcpy(ret_duid->en.id, &hash, sizeof(ret_duid->en.id));
+ memcpy(ret_duid->en.id, &hash, sizeof(hash));
- *ret_len = offsetof(struct duid, en.id) + sizeof(ret_duid->en.id);
+ *ret_len = offsetof(struct duid, en.id) + sizeof(hash);
if (test_mode)
assert_se(memcmp(ret_duid, (const uint8_t[]) { 0x00, 0x02, 0x00, 0x00, 0xab, 0x11, 0x61, 0x77, 0x40, 0xde, 0x13, 0x42, 0xc3, 0xa2 }, *ret_len) == 0);
return 0;
}
-static int dhcp_identifier_set_duid_uuid(struct duid *ret_duid, size_t *ret_len) {
+int dhcp_identifier_set_duid_uuid(struct duid *ret_duid, size_t *ret_len) {
sd_id128_t machine_id;
int r;
return 0;
}
-int dhcp_identifier_set_duid(
+int dhcp_identifier_set_duid_raw(
DUIDType duid_type,
- const struct hw_addr_data *hw_addr,
- uint16_t arp_type,
- usec_t llt_time,
- bool test_mode,
+ const uint8_t *buf,
+ size_t buf_len,
struct duid *ret_duid,
size_t *ret_len) {
- switch (duid_type) {
- case DUID_TYPE_LLT:
- return dhcp_identifier_set_duid_llt(hw_addr, arp_type, llt_time, ret_duid, ret_len);
- case DUID_TYPE_EN:
- return dhcp_identifier_set_duid_en(test_mode, ret_duid, ret_len);
- case DUID_TYPE_LL:
- return dhcp_identifier_set_duid_ll(hw_addr, arp_type, ret_duid, ret_len);
- case DUID_TYPE_UUID:
- return dhcp_identifier_set_duid_uuid(ret_duid, ret_len);
- default:
+ assert(buf || buf_len == 0);
+ assert(ret_duid);
+ assert(ret_len);
+
+ if (duid_type < 0 || duid_type > UINT16_MAX)
+ return -EINVAL;
+
+ if (buf_len > MAX_DUID_DATA_LEN)
return -EINVAL;
- }
+
+ unaligned_write_be16(&ret_duid->type, duid_type);
+ memcpy_safe(ret_duid->raw.data, buf, buf_len);
+
+ *ret_len = offsetof(struct duid, raw.data) + buf_len;
+ return 0;
}
int dhcp_identifier_set_iaid(
DUID_TYPE_EN = 2,
DUID_TYPE_LL = 3,
DUID_TYPE_UUID = 4,
- DUID_TYPE_CUSTOM = 5,
_DUID_TYPE_MAX,
_DUID_TYPE_INVALID = -EINVAL,
+ _DUID_TYPE_FORCE_U16 = UINT16_MAX,
} DUIDType;
/* RFC 3315 section 9.1:
* A DUID can be no more than 128 octets long (not including the type code).
*/
#define MAX_DUID_LEN 128
+#define MAX_DUID_DATA_LEN (MAX_DUID_LEN - sizeof(be16_t))
/* https://tools.ietf.org/html/rfc3315#section-9.1 */
struct duid {
/* DUID_TYPE_LLT */
be16_t htype;
be32_t time;
- uint8_t haddr[0];
+ uint8_t haddr[];
} _packed_ llt;
struct {
/* DUID_TYPE_EN */
be32_t pen;
- uint8_t id[8];
+ uint8_t id[];
} _packed_ en;
struct {
/* DUID_TYPE_LL */
be16_t htype;
- uint8_t haddr[0];
+ uint8_t haddr[];
} _packed_ ll;
struct {
/* DUID_TYPE_UUID */
sd_id128_t uuid;
} _packed_ uuid;
struct {
- uint8_t data[MAX_DUID_LEN];
+ uint8_t data[MAX_DUID_DATA_LEN];
} _packed_ raw;
};
} _packed_;
int dhcp_validate_duid_len(DUIDType duid_type, size_t duid_len, bool strict);
-int dhcp_identifier_set_duid_en(bool test_mode, struct duid *ret_duid, size_t *ret_len);
-int dhcp_identifier_set_duid(
- DUIDType duid_type,
+int dhcp_identifier_set_duid_llt(
+ const struct hw_addr_data *hw_addr,
+ uint16_t arp_type,
+ usec_t t,
+ struct duid *ret_duid,
+ size_t *ret_len);
+int dhcp_identifier_set_duid_ll(
const struct hw_addr_data *hw_addr,
uint16_t arp_type,
- usec_t llt_time,
- bool test_mode,
+ struct duid *ret_duid,
+ size_t *ret_len);
+int dhcp_identifier_set_duid_en(bool test_mode, struct duid *ret_duid, size_t *ret_len);
+int dhcp_identifier_set_duid_uuid(struct duid *ret_duid, size_t *ret_len);
+int dhcp_identifier_set_duid_raw(
+ DUIDType duid_type,
+ const uint8_t *buf,
+ size_t buf_len,
struct duid *ret_duid,
size_t *ret_len);
int dhcp_identifier_set_iaid(
#include "sd-dhcp6-lease.h"
#include "dhcp6-option.h"
+#include "dhcp6-protocol.h"
#include "macro.h"
#include "time-util.h"
uint8_t hop_limit;
uint8_t flags;
uint32_t mtu;
+ uint32_t retransmit_msec;
usec_t lifetime_usec; /* timespan */
int fd;
* without further modification. Otherwise, if duid_type is supported, DUID
* is set based on that type. Otherwise, an error is returned.
*/
-static int dhcp_client_set_iaid_duid_internal(
+static int dhcp_client_set_iaid(
sd_dhcp_client *client,
bool iaid_set,
- uint32_t iaid,
- DUIDType duid_type,
- const void *duid,
- size_t duid_len,
- usec_t llt_time) {
+ uint32_t iaid) {
- size_t len;
int r;
assert_return(client, -EINVAL);
assert_return(!sd_dhcp_client_is_running(client), -EBUSY);
- assert_return(duid_len == 0 || duid, -EINVAL);
-
- if (duid) {
- r = dhcp_validate_duid_len(duid_type, duid_len, true);
- if (r < 0)
- return log_dhcp_client_errno(client, r, "Failed to validate length of DUID: %m");
- }
zero(client->client_id);
client->client_id.type = 255;
return log_dhcp_client_errno(client, r, "Failed to set IAID: %m");
}
- if (duid) {
- client->client_id.ns.duid.type = htobe16(duid_type);
- memcpy(&client->client_id.ns.duid.raw.data, duid, duid_len);
- len = sizeof(client->client_id.ns.duid.type) + duid_len;
+ return 0;
+}
- } else {
- r = dhcp_identifier_set_duid(duid_type, &client->hw_addr,
- client->arp_type, llt_time, client->test_mode,
- &client->client_id.ns.duid, &len);
- if (r == -EOPNOTSUPP)
- return log_dhcp_client_errno(client, r,
- "Failed to set %s. MAC address is not set or "
- "interface type is not supported.",
- duid_type_to_string(duid_type));
- if (r < 0)
- return log_dhcp_client_errno(client, r, "Failed to set %s: %m",
- duid_type_to_string(duid_type));
- }
+int sd_dhcp_client_set_iaid_duid_llt(
+ sd_dhcp_client *client,
+ bool iaid_set,
+ uint32_t iaid,
+ usec_t llt_time) {
+
+ size_t len;
+ int r;
+
+ assert_return(client, -EINVAL);
+ assert_return(!sd_dhcp_client_is_running(client), -EBUSY);
+
+ r = dhcp_client_set_iaid(client, iaid_set, iaid);
+ if (r < 0)
+ return r;
+
+ r = dhcp_identifier_set_duid_llt(&client->hw_addr, client->arp_type, llt_time, &client->client_id.ns.duid, &len);
+ if (r < 0)
+ return log_dhcp_client_errno(client, r, "Failed to set DUID-LLT: %m");
client->client_id_len = sizeof(client->client_id.type) + sizeof(client->client_id.ns.iaid) + len;
return 0;
}
-int sd_dhcp_client_set_iaid_duid(
+int sd_dhcp_client_set_iaid_duid_ll(
sd_dhcp_client *client,
bool iaid_set,
- uint32_t iaid,
- uint16_t duid_type,
- const void *duid,
- size_t duid_len) {
- return dhcp_client_set_iaid_duid_internal(client, iaid_set, iaid, duid_type, duid, duid_len, 0);
+ uint32_t iaid) {
+
+ size_t len;
+ int r;
+
+ assert_return(client, -EINVAL);
+ assert_return(!sd_dhcp_client_is_running(client), -EBUSY);
+
+ r = dhcp_client_set_iaid(client, iaid_set, iaid);
+ if (r < 0)
+ return r;
+
+ r = dhcp_identifier_set_duid_ll(&client->hw_addr, client->arp_type, &client->client_id.ns.duid, &len);
+ if (r < 0)
+ return log_dhcp_client_errno(client, r, "Failed to set DUID-LL: %m");
+
+ client->client_id_len = sizeof(client->client_id.type) + sizeof(client->client_id.ns.iaid) + len;
+
+ return 0;
}
-int sd_dhcp_client_set_iaid_duid_llt(
+int sd_dhcp_client_set_iaid_duid_en(
+ sd_dhcp_client *client,
+ bool iaid_set,
+ uint32_t iaid) {
+
+ size_t len;
+ int r;
+
+ assert_return(client, -EINVAL);
+ assert_return(!sd_dhcp_client_is_running(client), -EBUSY);
+
+ r = dhcp_client_set_iaid(client, iaid_set, iaid);
+ if (r < 0)
+ return r;
+
+ r = dhcp_identifier_set_duid_en(client->test_mode, &client->client_id.ns.duid, &len);
+ if (r < 0)
+ return log_dhcp_client_errno(client, r, "Failed to set DUID-EN: %m");
+
+ client->client_id_len = sizeof(client->client_id.type) + sizeof(client->client_id.ns.iaid) + len;
+
+ return 0;
+}
+
+int sd_dhcp_client_set_iaid_duid_uuid(
+ sd_dhcp_client *client,
+ bool iaid_set,
+ uint32_t iaid) {
+
+ size_t len;
+ int r;
+
+ assert_return(client, -EINVAL);
+ assert_return(!sd_dhcp_client_is_running(client), -EBUSY);
+
+ r = dhcp_client_set_iaid(client, iaid_set, iaid);
+ if (r < 0)
+ return r;
+
+ r = dhcp_identifier_set_duid_uuid(&client->client_id.ns.duid, &len);
+ if (r < 0)
+ return log_dhcp_client_errno(client, r, "Failed to set DUID-UUID: %m");
+
+ client->client_id_len = sizeof(client->client_id.type) + sizeof(client->client_id.ns.iaid) + len;
+
+ return 0;
+}
+
+int sd_dhcp_client_set_iaid_duid_raw(
sd_dhcp_client *client,
bool iaid_set,
uint32_t iaid,
- usec_t llt_time) {
- return dhcp_client_set_iaid_duid_internal(client, iaid_set, iaid, DUID_TYPE_LLT, NULL, 0, llt_time);
+ uint16_t duid_type,
+ const uint8_t *duid,
+ size_t duid_len) {
+
+ size_t len;
+ int r;
+
+ assert_return(client, -EINVAL);
+ assert_return(!sd_dhcp_client_is_running(client), -EBUSY);
+ assert_return(duid || duid_len == 0, -EINVAL);
+
+ r = dhcp_client_set_iaid(client, iaid_set, iaid);
+ if (r < 0)
+ return r;
+
+ r = dhcp_identifier_set_duid_raw(duid_type, duid, duid_len, &client->client_id.ns.duid, &len);
+ if (r < 0)
+ return log_dhcp_client_errno(client, r, "Failed to set DUID: %m");
+
+ client->client_id_len = sizeof(client->client_id.type) + sizeof(client->client_id.ns.iaid) + len;
+
+ return 0;
}
void dhcp_client_set_test_mode(sd_dhcp_client *client, bool test_mode) {
/* If no client identifier exists, construct an RFC 4361-compliant one */
if (client->client_id_len == 0) {
- size_t duid_len;
-
- client->client_id.type = 255;
-
- r = dhcp_identifier_set_iaid(client->dev, &client->hw_addr,
- /* legacy_unstable_byteorder = */ true,
- &client->client_id.ns.iaid);
- if (r < 0)
- return r;
-
- r = dhcp_identifier_set_duid_en(client->test_mode, &client->client_id.ns.duid, &duid_len);
+ r = sd_dhcp_client_set_iaid_duid_en(client, /* iaid_set = */ false, /* iaid = */ 0);
if (r < 0)
return r;
-
- client->client_id_len = sizeof(client->client_id.type) + sizeof(client->client_id.ns.iaid) + duid_len;
}
/* Some DHCP servers will refuse to issue an DHCP lease if the Client
if (!options)
return NULL;
- for (unsigned i = 0; i < SD_DHCP_OPTION_PRIVATE_LAST - SD_DHCP_OPTION_PRIVATE_BASE + 1; i++)
- free(options[i]);
+ free_many_charp(options, SD_DHCP_OPTION_PRIVATE_LAST - SD_DHCP_OPTION_PRIVATE_BASE + 1);
return mfree(options);
}
* without further modification. Otherwise, if duid_type is supported, DUID
* is set based on that type. Otherwise, an error is returned.
*/
-static int dhcp6_client_set_duid_internal(
- sd_dhcp6_client *client,
- DUIDType duid_type,
- const void *duid,
- size_t duid_len,
- usec_t llt_time) {
+int sd_dhcp6_client_set_duid_llt(sd_dhcp6_client *client, uint64_t llt_time) {
int r;
assert_return(client, -EINVAL);
assert_return(!sd_dhcp6_client_is_running(client), -EBUSY);
- assert_return(duid_len == 0 || duid, -EINVAL);
- if (duid) {
- r = dhcp_validate_duid_len(duid_type, duid_len, true);
- if (r < 0) {
- r = dhcp_validate_duid_len(duid_type, duid_len, false);
- if (r < 0)
- return log_dhcp6_client_errno(client, r, "Failed to validate length of DUID: %m");
+ r = dhcp_identifier_set_duid_llt(&client->hw_addr, client->arp_type, llt_time, &client->duid, &client->duid_len);
+ if (r < 0)
+ return log_dhcp6_client_errno(client, r, "Failed to set DUID-LLT: %m");
- log_dhcp6_client(client, "Using DUID of type %i of incorrect length, proceeding.", duid_type);
- }
+ return 0;
+}
- if (duid_type == DUID_TYPE_CUSTOM) {
- memcpy(&client->duid, duid, duid_len);
- client->duid_len = duid_len;
- } else {
- client->duid.type = htobe16(duid_type);
- memcpy(&client->duid.raw.data, duid, duid_len);
- client->duid_len = sizeof(client->duid.type) + duid_len;
- }
- } else {
- r = dhcp_identifier_set_duid(duid_type, &client->hw_addr, client->arp_type, llt_time,
- client->test_mode, &client->duid, &client->duid_len);
- if (r == -EOPNOTSUPP)
- return log_dhcp6_client_errno(client, r,
- "Failed to set %s. MAC address is not set or "
- "interface type is not supported.",
- duid_type_to_string(duid_type));
- if (r < 0)
- return log_dhcp6_client_errno(client, r, "Failed to set %s: %m",
- duid_type_to_string(duid_type));
- }
+int sd_dhcp6_client_set_duid_ll(sd_dhcp6_client *client) {
+ int r;
+
+ assert_return(client, -EINVAL);
+ assert_return(!sd_dhcp6_client_is_running(client), -EBUSY);
+
+ r = dhcp_identifier_set_duid_ll(&client->hw_addr, client->arp_type, &client->duid, &client->duid_len);
+ if (r < 0)
+ return log_dhcp6_client_errno(client, r, "Failed to set DUID-LL: %m");
return 0;
}
-int sd_dhcp6_client_set_duid(
- sd_dhcp6_client *client,
- uint16_t duid_type,
- const void *duid,
- size_t duid_len) {
- return dhcp6_client_set_duid_internal(client, duid_type, duid, duid_len, 0);
+int sd_dhcp6_client_set_duid_en(sd_dhcp6_client *client) {
+ int r;
+
+ assert_return(client, -EINVAL);
+ assert_return(!sd_dhcp6_client_is_running(client), -EBUSY);
+
+ r = dhcp_identifier_set_duid_en(client->test_mode, &client->duid, &client->duid_len);
+ if (r < 0)
+ return log_dhcp6_client_errno(client, r, "Failed to set DUID-EN: %m");
+
+ return 0;
}
-int sd_dhcp6_client_set_duid_llt(
- sd_dhcp6_client *client,
- usec_t llt_time) {
- return dhcp6_client_set_duid_internal(client, DUID_TYPE_LLT, NULL, 0, llt_time);
+int sd_dhcp6_client_set_duid_uuid(sd_dhcp6_client *client) {
+ int r;
+
+ assert_return(client, -EINVAL);
+ assert_return(!sd_dhcp6_client_is_running(client), -EBUSY);
+
+ r = dhcp_identifier_set_duid_uuid(&client->duid, &client->duid_len);
+ if (r < 0)
+ return log_dhcp6_client_errno(client, r, "Failed to set DUID-UUID: %m");
+
+ return 0;
+}
+
+int sd_dhcp6_client_set_duid_raw(sd_dhcp6_client *client, uint16_t duid_type, const uint8_t *duid, size_t duid_len) {
+ int r;
+
+ assert_return(client, -EINVAL);
+ assert_return(!sd_dhcp6_client_is_running(client), -EBUSY);
+ assert_return(duid || duid_len == 0, -EINVAL);
+
+ r = dhcp_identifier_set_duid_raw(duid_type, duid, duid_len, &client->duid, &client->duid_len);
+ if (r < 0)
+ return log_dhcp6_client_errno(client, r, "Failed to set DUID: %m");
+
+ return 0;
}
int sd_dhcp6_client_duid_as_string(
adv.nd_ra_type = ND_ROUTER_ADVERT;
adv.nd_ra_curhoplimit = ra->hop_limit;
+ adv.nd_ra_retransmit = htobe32(ra->retransmit_msec);
adv.nd_ra_flags_reserved = ra->flags;
assert_cc(RADV_MAX_ROUTER_LIFETIME_USEC <= UINT16_MAX * USEC_PER_SEC);
adv.nd_ra_router_lifetime = htobe16(DIV_ROUND_UP(lifetime_usec, USEC_PER_SEC));
return 0;
}
+int sd_radv_set_retransmit(sd_radv *ra, uint32_t retransmit_msec) {
+ assert_return(ra, -EINVAL);
+
+ if (ra->state != RADV_STATE_IDLE)
+ return -EBUSY;
+
+ ra->retransmit_msec = retransmit_msec;
+
+ return 0;
+}
+
int sd_radv_set_router_lifetime(sd_radv *ra, uint64_t lifetime_usec) {
assert_return(ra, -EINVAL);
assert_se(sd_dhcp6_client_new(&client) >= 0);
assert_se(sd_dhcp6_client_set_iaid(client, 0xcc59117b) >= 0);
- assert_se(sd_dhcp6_client_set_duid(client, 2, duid, sizeof(duid)) >= 0);
+ assert_se(sd_dhcp6_client_set_duid_raw(client, 2, duid, sizeof(duid)) >= 0);
assert_se(dhcp6_lease_new_from_message(client, (const DHCP6Message*) msg, sizeof(msg), NULL, NULL, &lease) >= 0);
}
assert_se(sd_dhcp6_client_new(&client) >= 0);
assert_se(sd_dhcp6_client_set_iaid(client, 0xaabbccdd) >= 0);
- assert_se(sd_dhcp6_client_set_duid(client, 2, duid, sizeof(duid)) >= 0);
+ assert_se(sd_dhcp6_client_set_duid_raw(client, 2, duid, sizeof(duid)) >= 0);
assert_se(dhcp6_lease_new_from_message(client, (const DHCP6Message*) msg, sizeof(msg), NULL, NULL, &lease) >= 0);
}
DEFINE_STRING_TABLE_LOOKUP(dhcp_option_data_type, DHCPOptionDataType);
static const char* const duid_type_table[_DUID_TYPE_MAX] = {
- [DUID_TYPE_LLT] = "link-layer-time",
- [DUID_TYPE_EN] = "vendor",
- [DUID_TYPE_LL] = "link-layer",
- [DUID_TYPE_UUID] = "uuid",
- [DUID_TYPE_CUSTOM] = "custom",
+ [DUID_TYPE_LLT] = "link-layer-time",
+ [DUID_TYPE_EN] = "vendor",
+ [DUID_TYPE_LL] = "link-layer",
+ [DUID_TYPE_UUID] = "uuid",
};
DEFINE_PRIVATE_STRING_TABLE_LOOKUP_FROM_STRING(duid_type, DUIDType);
type = duid_type_from_string(type_string);
if (type < 0) {
- log_syntax(unit, LOG_WARNING, filename, line, type,
- "Failed to parse DUID type '%s', ignoring.", type_string);
- return 0;
+ uint16_t t;
+
+ r = safe_atou16(type_string, &t);
+ if (r < 0) {
+ log_syntax(unit, LOG_WARNING, filename, line, r,
+ "Failed to parse DUID type '%s', ignoring.", type_string);
+ return 0;
+ }
+
+ type = t;
+ assert(type == t); /* Check if type can store uint16_t. */
}
if (!isempty(p)) {
void *data,
void *userdata) {
- uint8_t raw_data[MAX_DUID_LEN];
+ uint8_t raw_data[MAX_DUID_DATA_LEN];
unsigned count = 0;
bool force = ltype;
DUID *duid = ASSERT_PTR(data);
if (r == 0)
break;
- if (count >= MAX_DUID_LEN) {
+ if (count >= MAX_DUID_DATA_LEN) {
log_syntax(unit, LOG_WARNING, filename, line, 0, "Max DUID length exceeded, ignoring assignment: %s.", rvalue);
return 0;
}
DUIDType type;
uint8_t raw_data_len;
- uint8_t raw_data[MAX_DUID_LEN];
+ uint8_t raw_data[MAX_DUID_DATA_LEN];
usec_t llt_time;
bool set;
} DUID;
/* If configured, apply user specified DUID and IAID */
const DUID *duid = link_get_dhcp4_duid(link);
- if (duid->type == DUID_TYPE_LLT && duid->raw_data_len == 0)
- r = sd_dhcp_client_set_iaid_duid_llt(link->dhcp_client,
+ if (duid->raw_data_len == 0)
+ switch (duid->type) {
+ case DUID_TYPE_LLT:
+ r = sd_dhcp_client_set_iaid_duid_llt(link->dhcp_client,
+ link->network->dhcp_iaid_set,
+ link->network->dhcp_iaid,
+ duid->llt_time);
+ break;
+ case DUID_TYPE_LL:
+ r = sd_dhcp_client_set_iaid_duid_ll(link->dhcp_client,
+ link->network->dhcp_iaid_set,
+ link->network->dhcp_iaid);
+ break;
+ case DUID_TYPE_EN:
+ r = sd_dhcp_client_set_iaid_duid_en(link->dhcp_client,
+ link->network->dhcp_iaid_set,
+ link->network->dhcp_iaid);
+ break;
+ case DUID_TYPE_UUID:
+ r = sd_dhcp_client_set_iaid_duid_uuid(link->dhcp_client,
+ link->network->dhcp_iaid_set,
+ link->network->dhcp_iaid);
+ break;
+ default:
+ r = sd_dhcp_client_set_iaid_duid_raw(link->dhcp_client,
+ link->network->dhcp_iaid_set,
+ link->network->dhcp_iaid,
+ duid->type, NULL, 0);
+ }
+ else
+ r = sd_dhcp_client_set_iaid_duid_raw(link->dhcp_client,
link->network->dhcp_iaid_set,
link->network->dhcp_iaid,
- duid->llt_time);
- else
- r = sd_dhcp_client_set_iaid_duid(link->dhcp_client,
- link->network->dhcp_iaid_set,
- link->network->dhcp_iaid,
- duid->type,
- duid->raw_data_len > 0 ? duid->raw_data : NULL,
- duid->raw_data_len);
+ duid->type, duid->raw_data, duid->raw_data_len);
if (r < 0)
- return log_link_debug_errno(link, r, "DHCPv4 CLIENT: Failed to set IAID+DUID: %m");
+ return r;
break;
}
case DHCP_CLIENT_ID_MAC: {
duid = link_get_dhcp6_duid(link);
- if (duid->type == DUID_TYPE_CUSTOM && duid->raw_data_len == 0)
- return log_link_debug_errno(link, SYNTHETIC_ERRNO(EINVAL),
- "DHCPv6 CLIENT: Missing DUID Raw Data as duid type set to 'custom': %m");
-
- if (duid->type == DUID_TYPE_LLT && duid->raw_data_len == 0)
- r = sd_dhcp6_client_set_duid_llt(client, duid->llt_time);
+ if (duid->raw_data_len == 0)
+ switch (duid->type) {
+ case DUID_TYPE_LLT:
+ r = sd_dhcp6_client_set_duid_llt(client, duid->llt_time);
+ break;
+ case DUID_TYPE_LL:
+ r = sd_dhcp6_client_set_duid_ll(client);
+ break;
+ case DUID_TYPE_EN:
+ r = sd_dhcp6_client_set_duid_en(client);
+ break;
+ case DUID_TYPE_UUID:
+ r = sd_dhcp6_client_set_duid_uuid(client);
+ break;
+ default:
+ r = sd_dhcp6_client_set_duid_raw(client, duid->type, NULL, 0);
+ }
else
- r = sd_dhcp6_client_set_duid(client,
- duid->type,
- duid->raw_data_len > 0 ? duid->raw_data : NULL,
- duid->raw_data_len);
+ r = sd_dhcp6_client_set_duid_raw(client, duid->type, duid->raw_data, duid->raw_data_len);
if (r < 0)
return r;
#include <linux/nexthop.h>
#include "dhcp-server-internal.h"
+#include "dhcp6-lease-internal.h"
#include "dns-domain.h"
#include "ip-protocol-list.h"
#include "netif-util.h"
assert(v);
assert(name);
- return json_append(v, JSON_BUILD_OBJECT(JSON_BUILD_PAIR_VARIANT_NON_NULL(name, w)));
+ return json_variant_merge_objectb(v, JSON_BUILD_OBJECT(JSON_BUILD_PAIR_VARIANT_NON_NULL(name, w)));
}
static int address_build_json(Address *address, JsonVariant **ret) {
assert(ret);
HASHMAP_FOREACH(g, nexthop->group) {
- _cleanup_(json_variant_unrefp) JsonVariant *e = NULL;
-
- r = json_build(&e, JSON_BUILD_OBJECT(
- JSON_BUILD_PAIR_UNSIGNED("ID", g->id),
- JSON_BUILD_PAIR_UNSIGNED("Weight", g->weight+1)));
- if (r < 0)
- return r;
-
- r = json_variant_append_array(&array, e);
+ r = json_variant_append_arrayb(
+ &array,
+ JSON_BUILD_OBJECT(
+ JSON_BUILD_PAIR_UNSIGNED("ID", g->id),
+ JSON_BUILD_PAIR_UNSIGNED("Weight", g->weight+1)));
if (r < 0)
return r;
}
if (!network)
return 0;
- return json_append(v, JSON_BUILD_OBJECT(
+ return json_variant_merge_objectb(
+ v, JSON_BUILD_OBJECT(
JSON_BUILD_PAIR_STRING("NetworkFile", network->filename),
JSON_BUILD_PAIR_STRV("NetworkFileDropins", network->dropins),
JSON_BUILD_PAIR_BOOLEAN("RequiredForOnline", network->required_for_online),
if (sd_device_get_property_value(device, "ID_MODEL_FROM_DATABASE", &model) < 0)
(void) sd_device_get_property_value(device, "ID_MODEL", &model);
- return json_append(v, JSON_BUILD_OBJECT(
+ return json_variant_merge_objectb(
+ v,
+ JSON_BUILD_OBJECT(
JSON_BUILD_PAIR_STRING_NON_EMPTY("LinkFile", link),
JSON_BUILD_PAIR_STRV_NON_EMPTY("LinkFileDropins", link_dropins),
JSON_BUILD_PAIR_STRING_NON_EMPTY("Path", path),
}
static int dns_append_json_one(Link *link, const struct in_addr_full *a, NetworkConfigSource s, const union in_addr_union *p, JsonVariant **array) {
- _cleanup_(json_variant_unrefp) JsonVariant *v = NULL;
- int r;
-
assert(link);
assert(a);
assert(array);
if (a->ifindex != 0 && a->ifindex != link->ifindex)
return 0;
- r = json_build(&v, JSON_BUILD_OBJECT(
- JSON_BUILD_PAIR_INTEGER("Family", a->family),
- JSON_BUILD_PAIR_IN_ADDR("Address", &a->address, a->family),
- JSON_BUILD_PAIR_UNSIGNED_NON_ZERO("Port", a->port),
- JSON_BUILD_PAIR_CONDITION(a->ifindex != 0, "InterfaceIndex", JSON_BUILD_INTEGER(a->ifindex)),
- JSON_BUILD_PAIR_STRING_NON_EMPTY("ServerName", a->server_name),
- JSON_BUILD_PAIR_STRING("ConfigSource", network_config_source_to_string(s)),
- JSON_BUILD_PAIR_IN_ADDR_NON_NULL("ConfigProvider", p, a->family)));
- if (r < 0)
- return r;
-
- return json_variant_append_array(array, v);
+ return json_variant_append_arrayb(
+ array,
+ JSON_BUILD_OBJECT(
+ JSON_BUILD_PAIR_INTEGER("Family", a->family),
+ JSON_BUILD_PAIR_IN_ADDR("Address", &a->address, a->family),
+ JSON_BUILD_PAIR_UNSIGNED_NON_ZERO("Port", a->port),
+ JSON_BUILD_PAIR_CONDITION(a->ifindex != 0, "InterfaceIndex", JSON_BUILD_INTEGER(a->ifindex)),
+ JSON_BUILD_PAIR_STRING_NON_EMPTY("ServerName", a->server_name),
+ JSON_BUILD_PAIR_STRING("ConfigSource", network_config_source_to_string(s)),
+ JSON_BUILD_PAIR_IN_ADDR_NON_NULL("ConfigProvider", p, a->family)));
}
static int dns_append_json(Link *link, JsonVariant **v) {
if (r <= 0)
return r;
- return json_append(v, JSON_BUILD_OBJECT(JSON_BUILD_PAIR_STRING("CaptivePortal", captive_portal)));
+ return json_variant_merge_objectb(v, JSON_BUILD_OBJECT(JSON_BUILD_PAIR_STRING("CaptivePortal", captive_portal)));
}
static int dhcp_server_offered_leases_append_json(Link *link, JsonVariant **v) {
return 0;
HASHMAP_FOREACH(lease, link->dhcp_server->bound_leases_by_client_id) {
- _cleanup_(json_variant_unrefp) JsonVariant *e = NULL;
struct in_addr address = { .s_addr = lease->address };
- r = json_build(&e,
- JSON_BUILD_OBJECT(
- JSON_BUILD_PAIR_BYTE_ARRAY(
- "ClientId",
- lease->client_id.data,
- lease->client_id.length),
- JSON_BUILD_PAIR_IN4_ADDR_NON_NULL("Address", &address),
- JSON_BUILD_PAIR_STRING_NON_EMPTY("Hostname", lease->hostname),
- JSON_BUILD_PAIR_FINITE_USEC(
- "ExpirationUSec", lease->expiration)));
- if (r < 0)
- return r;
-
- r = json_variant_append_array(&array, e);
+ r = json_variant_append_arrayb(
+ &array,
+ JSON_BUILD_OBJECT(
+ JSON_BUILD_PAIR_BYTE_ARRAY(
+ "ClientId",
+ lease->client_id.data,
+ lease->client_id.length),
+ JSON_BUILD_PAIR_IN4_ADDR_NON_NULL("Address", &address),
+ JSON_BUILD_PAIR_STRING_NON_EMPTY("Hostname", lease->hostname),
+ JSON_BUILD_PAIR_FINITE_USEC(
+ "ExpirationUSec", lease->expiration)));
if (r < 0)
return r;
}
return json_append_one(v, "DHCPServer", w);
}
+static int dhcp6_client_lease_append_json(Link *link, JsonVariant **v) {
+ _cleanup_(json_variant_unrefp) JsonVariant *w = NULL;
+ int r;
+
+ assert(link);
+ assert(v);
+
+ if (!link->dhcp6_lease)
+ return 0;
+
+ r = json_build(&w, JSON_BUILD_OBJECT(
+ JSON_BUILD_PAIR_FINITE_USEC("T1", link->dhcp6_lease->lifetime_t1),
+ JSON_BUILD_PAIR_FINITE_USEC("T2", link->dhcp6_lease->lifetime_t2)));
+ if (r < 0)
+ return r;
+
+ return json_append_one(v, "Lease", w);
+}
+
+static int dhcp6_client_append_json(Link *link, JsonVariant **v) {
+ _cleanup_(json_variant_unrefp) JsonVariant *w = NULL;
+ int r;
+
+ assert(link);
+ assert(v);
+
+ if (!link->dhcp6_client)
+ return 0;
+
+ r = dhcp6_client_lease_append_json(link, &w);
+ if (r < 0)
+ return r;
+
+ return json_append_one(v, "DHCPv6Client", w);
+}
+
int link_build_json(Link *link, JsonVariant **ret) {
_cleanup_(json_variant_unrefp) JsonVariant *v = NULL;
_cleanup_free_ char *type = NULL, *flags = NULL;
if (r < 0)
return r;
+ r = dhcp6_client_append_json(link, &v);
+ if (r < 0)
+ return r;
+
*ret = TAKE_PTR(v);
return 0;
}
static int ndisc_router_process_onlink_prefix(Link *link, sd_ndisc_router *rt) {
_cleanup_(route_freep) Route *route = NULL;
+ unsigned prefixlen, preference;
usec_t timestamp_usec;
uint32_t lifetime_sec;
struct in6_addr prefix;
- unsigned prefixlen;
int r;
assert(link);
if (r < 0)
return log_link_warning_errno(link, r, "Failed to get prefix length: %m");
+ /* Prefix Information option does not have preference, hence we use the 'main' preference here */
+ r = sd_ndisc_router_get_preference(rt, &preference);
+ if (r < 0)
+ log_link_warning_errno(link, r, "Failed to get default router preference from RA: %m");
+
r = route_new(&route);
if (r < 0)
return log_oom();
route->family = AF_INET6;
route->dst.in6 = prefix;
route->dst_prefixlen = prefixlen;
+ route->pref = preference;
route->lifetime_usec = sec_to_usec(lifetime_sec, timestamp_usec);
r = ndisc_request_route(TAKE_PTR(route), link, rt);
}
}
-static int ndisc_drop_outdated(Link *link, usec_t timestamp_usec) {
+static int ndisc_drop_outdated(Link *link, usec_t timestamp_usec, const struct in6_addr *router) {
bool updated = false;
NDiscDNSSL *dnssl;
NDiscRDNSS *rdnss;
if (route->lifetime_usec >= timestamp_usec)
continue; /* the route is still valid */
+ if (router && !in6_addr_equal(&route->provider.in6, router))
+ continue;
+
k = route_remove_and_drop(route);
if (k < 0)
r = log_link_warning_errno(link, k, "Failed to remove outdated SLAAC route, ignoring: %m");
if (address->lifetime_valid_usec >= timestamp_usec)
continue; /* the address is still valid */
+ if (router && !in6_addr_equal(&address->provider.in6, router))
+ continue;
+
k = address_remove_and_drop(address);
if (k < 0)
r = log_link_warning_errno(link, k, "Failed to remove outdated SLAAC address, ignoring: %m");
if (rdnss->lifetime_usec >= timestamp_usec)
continue; /* the DNS server is still valid */
+ if (router && !in6_addr_equal(&rdnss->router, router))
+ continue;
+
free(set_remove(link->ndisc_rdnss, rdnss));
updated = true;
}
if (dnssl->lifetime_usec >= timestamp_usec)
continue; /* the DNS domain is still valid */
+ if (router && !in6_addr_equal(&dnssl->router, router))
+ continue;
+
free(set_remove(link->ndisc_dnssl, dnssl));
updated = true;
}
if (cp->lifetime_usec >= timestamp_usec)
continue; /* the captive portal is still valid */
+ if (router && !in6_addr_equal(&cp->router, router))
+ continue;
+
ndisc_captive_portal_free(set_remove(link->ndisc_captive_portals, cp));
updated = true;
}
assert_se(sd_event_now(link->manager->event, CLOCK_BOOTTIME, &now_usec) >= 0);
- (void) ndisc_drop_outdated(link, now_usec);
+ (void) ndisc_drop_outdated(link, now_usec, NULL);
(void) ndisc_setup_expire(link);
return 0;
}
}
static int ndisc_router_handler(Link *link, sd_ndisc_router *rt) {
+ uint16_t router_lifetime_sec;
struct in6_addr router;
usec_t timestamp_usec;
int r;
log_link_debug(link, "Received RA without timestamp, ignoring.");
return 0;
}
+
+ r = ndisc_drop_outdated(link, timestamp_usec, NULL);
if (r < 0)
return r;
- r = ndisc_drop_outdated(link, timestamp_usec);
+ r = sd_ndisc_router_get_lifetime(rt, &router_lifetime_sec);
if (r < 0)
- return r;
+ return log_link_warning_errno(link, r, "Failed to get lifetime of RA message: %m");
+
+ /* https://datatracker.ietf.org/doc/html/rfc4861
+ * Router Lifetime: A Lifetime of 0 indicates that the router is not a default router
+ * and SHOULD NOT appear on the default router list.
+ */
+ if (router_lifetime_sec == 0) {
+ log_link_debug(link, "Received RA with lifetime = 0, dropping configurations.");
+
+ r = ndisc_drop_outdated(link, USEC_INFINITY, &router);
+ if (r < 0)
+ log_link_warning_errno(link, r, "Failed to process RA with zero lifetime, ignoring: %m");
+
+ return 0;
+ }
r = ndisc_start_dhcp6_client(link, rt);
if (r < 0)
Route.IPv6Preference, config_parse_ipv6_route_preference, 0, 0
Route.Protocol, config_parse_route_protocol, 0, 0
Route.Type, config_parse_route_type, 0, 0
-Route.TCPRetransmissionTimeOutSec, config_parse_route_tcp_rto, 0, 0
+Route.TCPRetransmissionTimeoutSec, config_parse_route_tcp_rto, 0, 0
Route.HopLimit, config_parse_route_hop_limit, 0, 0
Route.InitialCongestionWindow, config_parse_route_tcp_window, 0, 0
Route.InitialAdvertisedReceiveWindow, config_parse_route_tcp_window, 0, 0
DHCPPrefixDelegation.RouteMetric, config_parse_uint32, 0, offsetof(Network, dhcp_pd_route_metric)
DHCPPrefixDelegation.NetLabel, config_parse_string, CONFIG_PARSE_STRING_SAFE, offsetof(Network, dhcp_pd_netlabel)
IPv6SendRA.RouterLifetimeSec, config_parse_router_lifetime, 0, offsetof(Network, router_lifetime_usec)
+IPv6SendRA.RetransmitSec, config_parse_router_retransmit, 0, offsetof(Network, router_retransmit_usec)
IPv6SendRA.Managed, config_parse_bool, 0, offsetof(Network, router_managed)
IPv6SendRA.OtherInformation, config_parse_bool, 0, offsetof(Network, router_other_information)
IPv6SendRA.RouterPreference, config_parse_router_preference, 0, 0
+IPv6SendRA.HopLimit, config_parse_uint8, 0, offsetof(Network, router_hop_limit)
IPv6SendRA.EmitDNS, config_parse_bool, 0, offsetof(Network, router_emit_dns)
IPv6SendRA.DNS, config_parse_radv_dns, 0, 0
IPv6SendRA.EmitDomains, config_parse_bool, 0, offsetof(Network, router_emit_domains)
RADVPrefixDelegation router_prefix_delegation;
usec_t router_lifetime_usec;
uint8_t router_preference;
+ usec_t router_retransmit_usec;
+ uint8_t router_hop_limit;
bool router_managed;
bool router_other_information;
bool router_emit_dns;
if (r < 0)
return r;
+ r = sd_radv_set_hop_limit(link->radv, link->network->router_hop_limit);
+ if (r < 0)
+ return r;
+
if (link->network->router_lifetime_usec > 0) {
r = sd_radv_set_preference(link->radv, link->network->router_preference);
if (r < 0)
return r;
}
+ if (link->network->router_retransmit_usec > 0) {
+ r = sd_radv_set_retransmit(link->radv, DIV_ROUND_UP(link->network->router_retransmit_usec, USEC_PER_MSEC));
+ if (r < 0)
+ return r;
+ }
+
HASHMAP_FOREACH(p, link->network->prefixes_by_section) {
r = radv_set_prefix(link, p);
if (r < 0 && r != -EEXIST)
return 0;
}
+int config_parse_router_retransmit(
+ 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) {
+
+ usec_t usec, *router_retransmit_usec = ASSERT_PTR(data);
+ int r;
+
+ assert(filename);
+ assert(section);
+ assert(lvalue);
+ assert(rvalue);
+
+ if (isempty(rvalue)) {
+ *router_retransmit_usec = 0;
+ return 0;
+ }
+
+ r = parse_sec(rvalue, &usec);
+ if (r < 0) {
+ log_syntax(unit, LOG_WARNING, filename, line, r,
+ "Failed to parse %s=, ignoring assignment: %s", lvalue, rvalue);
+ return 0;
+ }
+
+ if (usec != USEC_INFINITY &&
+ DIV_ROUND_UP(usec, USEC_PER_MSEC) > UINT32_MAX) {
+ log_syntax(unit, LOG_WARNING, filename, line, 0,
+ "Invalid %s= must be in the range 0...%"PRIu32"Sec, ignoring: %s", lvalue, UINT32_MAX, rvalue);
+ return 0;
+ }
+
+ *router_retransmit_usec = usec;
+ return 0;
+}
+
int config_parse_router_preference(
const char *unit,
const char *filename,
CONFIG_PARSER_PROTOTYPE(config_parse_router_prefix_delegation);
CONFIG_PARSER_PROTOTYPE(config_parse_router_lifetime);
+CONFIG_PARSER_PROTOTYPE(config_parse_router_retransmit);
CONFIG_PARSER_PROTOTYPE(config_parse_router_preference);
CONFIG_PARSER_PROTOTYPE(config_parse_prefix);
CONFIG_PARSER_PROTOTYPE(config_parse_prefix_boolean);
}
if (route->tcp_rto_usec > 0) {
- r = sd_netlink_message_append_u32(m, RTAX_RTO_MIN, route->tcp_rto_usec / USEC_PER_MSEC);
+ r = sd_netlink_message_append_u32(m, RTAX_RTO_MIN, DIV_ROUND_UP(route->tcp_rto_usec, USEC_PER_MSEC));
if (r < 0)
return r;
}
r = safe_atou32(rvalue, &k);
if (r < 0) {
log_syntax(unit, LOG_WARNING, filename, line, r,
- "Could not parse route hop limit %s \"%s\", ignoring assignment: %m", lvalue, rvalue);
+ "Could not parse per route hop limit, ignoring assignment: %s", rvalue);
return 0;
}
if (k > 255) {
log_syntax(unit, LOG_WARNING, filename, line, 0,
- "Specified route hop limit %s \"%s\" is too large, ignoring assignment: %m", lvalue, rvalue);
+ "Specified per route hop limit \"%s\" is too large, ignoring assignment: %m", rvalue);
return 0;
}
if (k == 0) {
log_syntax(unit, LOG_WARNING, filename, line, 0,
- "Invalid route hop limit %s \"%s\", ignoring assignment: %m", lvalue, rvalue);
+ "Invalid per route hop limit \"%s\", ignoring assignment: %m", rvalue);
return 0;
}
r = parse_sec(rvalue, &usec);
if (r < 0) {
log_syntax(unit, LOG_WARNING, filename, line, r,
- "Failed to parse route TCP retransmission time out (RTO) sec '%s', ignoring: %m", rvalue);
+ "Failed to parse route TCP retransmission timeout (RTO), ignoring assignment: %s", rvalue);
return 0;
}
- if (usec != USEC_INFINITY &&
- DIV_ROUND_UP(usec, USEC_PER_SEC) > UINT32_MAX) {
+ if (IN_SET(usec, 0, USEC_INFINITY) ||
+ DIV_ROUND_UP(usec, USEC_PER_MSEC) > UINT32_MAX) {
log_syntax(unit, LOG_WARNING, filename, line, 0,
- "Route TCP retransmission time out (RTO) = must be in the range 0...%"PRIu32"ms, ignoring: %s", UINT32_MAX, rvalue);
+ "Route TCP retransmission timeout (RTO) must be in the range 0…%"PRIu32"ms, ignoring assignment: %s", UINT32_MAX, rvalue);
return 0;
}
assert_se(set_size(s) == 0);
}
-#define BYTES_0_128 "0:1:2:3:4:5:6:7:8:9:a:b:c:d:e:f:10:11:12:13:14:15:16:17:18:19:1a:1b:1c:1d:1e:1f:20:21:22:23:24:25:26:27:28:29:2a:2b:2c:2d:2e:2f:30:31:32:33:34:35:36:37:38:39:3a:3b:3c:3d:3e:3f:40:41:42:43:44:45:46:47:48:49:4a:4b:4c:4d:4e:4f:50:51:52:53:54:55:56:57:58:59:5a:5b:5c:5d:5e:5f:60:61:62:63:64:65:66:67:68:69:6a:6b:6c:6d:6e:6f:70:71:72:73:74:75:76:77:78:79:7a:7b:7c:7d:7e:7f:80"
-
-#define BYTES_1_128 {0x1,0x2,0x3,0x4,0x5,0x6,0x7,0x8,0x9,0xa,0xb,0xc,0xd,0xe,0xf,0x10,0x11,0x12,0x13,0x14,0x15,0x16,0x17,0x18,0x19,0x1a,0x1b,0x1c,0x1d,0x1e,0x1f,0x20,0x21,0x22,0x23,0x24,0x25,0x26,0x27,0x28,0x29,0x2a,0x2b,0x2c,0x2d,0x2e,0x2f,0x30,0x31,0x32,0x33,0x34,0x35,0x36,0x37,0x38,0x39,0x3a,0x3b,0x3c,0x3d,0x3e,0x3f,0x40,0x41,0x42,0x43,0x44,0x45,0x46,0x47,0x48,0x49,0x4a,0x4b,0x4c,0x4d,0x4e,0x4f,0x50,0x51,0x52,0x53,0x54,0x55,0x56,0x57,0x58,0x59,0x5a,0x5b,0x5c,0x5d,0x5e,0x5f,0x60,0x61,0x62,0x63,0x64,0x65,0x66,0x67,0x68,0x69,0x6a,0x6b,0x6c,0x6d,0x6e,0x6f,0x70,0x71,0x72,0x73,0x74,0x75,0x76,0x77,0x78,0x79,0x7a,0x7b,0x7c,0x7d,0x7e,0x7f,0x80}
+#define BYTES_0_126 \
+ "00:01:02:03:04:05:06:07:08:09:0a:0b:0c:0d:0e:0f:" \
+ "10:11:12:13:14:15:16:17:18:19:1a:1b:1c:1d:1e:1f:" \
+ "20:21:22:23:24:25:26:27:28:29:2a:2b:2c:2d:2e:2f:" \
+ "30:31:32:33:34:35:36:37:38:39:3a:3b:3c:3d:3e:3f:" \
+ "40:41:42:43:44:45:46:47:48:49:4a:4b:4c:4d:4e:4f:" \
+ "50:51:52:53:54:55:56:57:58:59:5a:5b:5c:5d:5e:5f:" \
+ "60:61:62:63:64:65:66:67:68:69:6a:6b:6c:6d:6e:6f:" \
+ "70:71:72:73:74:75:76:77:78:79:7a:7b:7c:7d:7e"
+
+#define BYTES_1_126 { \
+ 0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x08,0x09,0x0a,0x0b,0x0c,0x0d,0x0e,0x0f, \
+ 0x10,0x11,0x12,0x13,0x14,0x15,0x16,0x17,0x18,0x19,0x1a,0x1b,0x1c,0x1d,0x1e,0x1f, \
+ 0x20,0x21,0x22,0x23,0x24,0x25,0x26,0x27,0x28,0x29,0x2a,0x2b,0x2c,0x2d,0x2e,0x2f, \
+ 0x30,0x31,0x32,0x33,0x34,0x35,0x36,0x37,0x38,0x39,0x3a,0x3b,0x3c,0x3d,0x3e,0x3f, \
+ 0x40,0x41,0x42,0x43,0x44,0x45,0x46,0x47,0x48,0x49,0x4a,0x4b,0x4c,0x4d,0x4e,0x4f, \
+ 0x50,0x51,0x52,0x53,0x54,0x55,0x56,0x57,0x58,0x59,0x5a,0x5b,0x5c,0x5d,0x5e,0x5f, \
+ 0x60,0x61,0x62,0x63,0x64,0x65,0x66,0x67,0x68,0x69,0x6a,0x6b,0x6c,0x6d,0x6e,0x6f, \
+ 0x70,0x71,0x72,0x73,0x74,0x75,0x76,0x77,0x78,0x79,0x7a,0x7b,0x7c,0x7d,0x7e \
+}
TEST(config_parse_duid_rawdata) {
test_config_parse_duid_rawdata_one("", 0, &(DUID){});
&(DUID){0, 8, {0x0, 0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7}});
test_config_parse_duid_rawdata_one("11::", 0, &(DUID){0, 1, {0x11}}); /* FIXME: should this be an error? */
test_config_parse_duid_rawdata_one("abcdef", 0, &(DUID){});
- test_config_parse_duid_rawdata_one(BYTES_0_128, 0, &(DUID){});
- test_config_parse_duid_rawdata_one(&BYTES_0_128[2], 0, &(DUID){0, 128, BYTES_1_128});
+ test_config_parse_duid_rawdata_one(BYTES_0_126, 0, &(DUID){});
+ test_config_parse_duid_rawdata_one(&BYTES_0_126[3], 0, &(DUID){0, 126, BYTES_1_126});
}
TEST(config_parse_ether_addr) {
return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
"%s, interface name not valid: %s", iftype, a);
- if (isempty(interface)) {
- if (ifprefix)
- b = strjoin(ifprefix, a);
- else
- b = strdup(a);
- } else
+ /* Here, we only check the validity of the specified second name. If it is not specified,
+ * the copied or prefixed name should be already valid, except for its length. If it is too
+ * long, then it will be shortened later. */
+ if (!isempty(interface)) {
+ if (!ifname_valid(interface))
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
+ "%s, interface name not valid: %s", iftype, interface);
+
b = strdup(interface);
+ } else if (ifprefix)
+ b = strjoin(ifprefix, a);
+ else
+ b = strdup(a);
if (!b)
return log_oom();
- if (!ifname_valid(b))
- return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
- "%s, interface name not valid: %s", iftype, b);
-
r = strv_consume_pair(l, TAKE_PTR(a), TAKE_PTR(b));
if (r < 0)
return log_oom();
_cleanup_(table_unrefp) Table *t = NULL;
uint64_t sum_padding = 0, sum_size = 0;
int r;
- const size_t roothash_col = 13, dropin_files_col = 14, split_path_col = 15;
+ const size_t roothash_col = 14, dropin_files_col = 15, split_path_col = 16;
bool has_roothash = false, has_dropin_files = false, has_split_path = false;
if ((arg_json_format_flags & JSON_FORMAT_OFF) && context->n_partitions == 0) {
t = table_new("type",
"label",
"uuid",
+ "partno",
"file",
"node",
"offset",
if (!DEBUG_LOGGING) {
if (arg_json_format_flags & JSON_FORMAT_OFF)
(void) table_set_display(t, (size_t) 0, (size_t) 1, (size_t) 2, (size_t) 3, (size_t) 4,
- (size_t) 8, (size_t) 11, roothash_col, dropin_files_col,
+ (size_t) 8, (size_t) 9, (size_t) 12, roothash_col, dropin_files_col,
split_path_col);
else
(void) table_set_display(t, (size_t) 0, (size_t) 1, (size_t) 2, (size_t) 3, (size_t) 4,
- (size_t) 5, (size_t) 6, (size_t) 7, (size_t) 9, (size_t) 10,
- (size_t) 12, roothash_col, dropin_files_col,
+ (size_t) 5, (size_t) 6, (size_t) 7, (size_t) 8, (size_t) 10,
+ (size_t) 11, (size_t) 13, roothash_col, dropin_files_col,
split_path_col);
}
TABLE_STRING, gpt_partition_type_uuid_to_string_harder(p->type.uuid, uuid_buffer),
TABLE_STRING, empty_to_null(label) ?: "-", TABLE_SET_COLOR, empty_to_null(label) ? NULL : ansi_grey(),
TABLE_UUID, p->new_uuid_is_set ? p->new_uuid : p->current_uuid,
+ TABLE_UINT64, p->partno,
TABLE_PATH_BASENAME, p->definition_path, TABLE_SET_COLOR, p->definition_path ? NULL : ansi_grey(),
TABLE_STRING, partname ?: "-", TABLE_SET_COLOR, partname ? NULL : ansi_highlight(),
TABLE_UINT64, p->offset,
TABLE_EMPTY,
TABLE_EMPTY,
TABLE_EMPTY,
+ TABLE_EMPTY,
TABLE_STRING, a,
TABLE_EMPTY,
TABLE_EMPTY,
_cleanup_(json_variant_unrefp) JsonVariant *l = NULL;
LIST_FOREACH(by_key, j, i) {
- _cleanup_(json_variant_unrefp) JsonVariant *rj = NULL, *item = NULL;
+ _cleanup_(json_variant_unrefp) JsonVariant *rj = NULL;
assert(j->rr);
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);
+ r = json_variant_append_arrayb(
+ &l,
+ 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;
}
return log_error_errno(r, "Failed to convert question to JSON: %m");
DNS_ANSWER_FOREACH_ITEM(rri, answer) {
- _cleanup_(json_variant_unrefp) JsonVariant *v = NULL, *w = NULL;
+ _cleanup_(json_variant_unrefp) JsonVariant *v = NULL;
r = dns_resource_record_to_json(rri->rr, &v);
if (r < 0)
if (r < 0)
return log_error_errno(r, "Failed to generate RR wire format: %m");
- r = json_build(&w, JSON_BUILD_OBJECT(
- JSON_BUILD_PAIR_CONDITION(v, "rr", JSON_BUILD_VARIANT(v)),
- JSON_BUILD_PAIR("raw", JSON_BUILD_BASE64(rri->rr->wire_format, rri->rr->wire_format_size)),
- JSON_BUILD_PAIR_CONDITION(rri->ifindex > 0, "ifindex", JSON_BUILD_INTEGER(rri->ifindex))));
- if (r < 0)
- return log_error_errno(r, "Failed to make answer RR object: %m");
-
- r = json_variant_append_array(&janswer, w);
+ r = json_variant_append_arrayb(
+ &janswer,
+ JSON_BUILD_OBJECT(
+ JSON_BUILD_PAIR_CONDITION(v, "rr", JSON_BUILD_VARIANT(v)),
+ JSON_BUILD_PAIR("raw", JSON_BUILD_BASE64(rri->rr->wire_format, rri->rr->wire_format_size)),
+ JSON_BUILD_PAIR_CONDITION(rri->ifindex > 0, "ifindex", JSON_BUILD_INTEGER(rri->ifindex))));
if (r < 0)
return log_debug_errno(r, "Failed to append notification entry to array: %m");
}
question = dns_query_question_for_protocol(q, q->answer_protocol);
DNS_ANSWER_FOREACH_IFINDEX(rr, ifindex, q->answer) {
- _cleanup_(json_variant_unrefp) JsonVariant *entry = NULL;
_cleanup_free_ char *normalized = NULL;
r = dns_question_matches_rr(question, rr, NULL);
if (r < 0)
goto finish;
- r = json_build(&entry,
- JSON_BUILD_OBJECT(
- JSON_BUILD_PAIR_CONDITION(ifindex > 0, "ifindex", JSON_BUILD_INTEGER(ifindex)),
- JSON_BUILD_PAIR("name", JSON_BUILD_STRING(normalized))));
- if (r < 0)
- goto finish;
-
- r = json_variant_append_array(&array, entry);
+ r = json_variant_append_arrayb(
+ &array,
+ JSON_BUILD_OBJECT(
+ JSON_BUILD_PAIR_CONDITION(ifindex > 0, "ifindex", JSON_BUILD_INTEGER(ifindex)),
+ JSON_BUILD_PAIR("name", JSON_BUILD_STRING(normalized))));
if (r < 0)
goto finish;
}
return log_oom();
}
- r = json_append(&v, JSON_BUILD_OBJECT(
+ r = json_variant_merge_objectb(
+ &v, JSON_BUILD_OBJECT(
JSON_BUILD_PAIR("type", JSON_BUILD_STRING(boot_entry_type_json_to_string(e->type))),
JSON_BUILD_PAIR_CONDITION(e->id, "id", JSON_BUILD_STRING(e->id)),
JSON_BUILD_PAIR_CONDITION(e->path, "path", JSON_BUILD_STRING(e->path)),
/* Sanitizers (only memory sanitizer?) do not like function call with too many
* arguments and trigger false positive warnings. Let's not add too many json objects
* at once. */
- r = json_append(&v, JSON_BUILD_OBJECT(
+ r = json_variant_merge_objectb(
+ &v, JSON_BUILD_OBJECT(
JSON_BUILD_PAIR("isReported", JSON_BUILD_BOOLEAN(e->reported_by_loader)),
JSON_BUILD_PAIR_CONDITION(e->tries_left != UINT_MAX, "triesLeft", JSON_BUILD_UNSIGNED(e->tries_left)),
JSON_BUILD_PAIR_CONDITION(e->tries_done != UINT_MAX, "triesDone", JSON_BUILD_UNSIGNED(e->tries_done)),
assert(call);
assert(action);
assert(registry);
+ assert(ret_error);
r = check_good_user(call, good_user);
if (r != 0)
int probe_sector_size(int fd, uint32_t *ret) {
- struct gpt_header {
- char signature[8];
- le32_t revision;
- le32_t header_size;
- le32_t crc32;
- le32_t reserved;
- le64_t my_lba;
- le64_t alternate_lba;
- le64_t first_usable_lba;
- le64_t last_usable_lba;
- sd_id128_t disk_guid;
- le64_t partition_entry_lba;
- le32_t number_of_partition_entries;
- le32_t size_of_partition_entry;
- le32_t partition_entry_array_crc32;
- } _packed_;
-
/* Disk images might be for 512B or for 4096 sector sizes, let's try to auto-detect that by searching
* for the GPT headers at the relevant byte offsets */
- assert_cc(sizeof(struct gpt_header) == 92);
+ assert_cc(sizeof(GptHeader) == 92);
/* We expect a sector size in the range 512…4096. The GPT header is located in the second
* sector. Hence it could be at byte 512 at the earliest, and at byte 4096 at the latest. And we must
/* Let's see if we find the GPT partition header with various expected sector sizes */
for (uint32_t sz = 512; sz <= 4096; sz <<= 1) {
- struct gpt_header *p;
+ const GptHeader *p;
assert(sizeof(sectors) >= sz * 2);
- p = (struct gpt_header*) (sectors + sz);
-
- if (memcmp(p->signature, (const char[8]) { 'E', 'F', 'I', ' ', 'P', 'A', 'R', 'T' }, 8) != 0)
- continue;
-
- if (le32toh(p->revision) != UINT32_C(0x00010000)) /* the only known revision of the spec: 1.0 */
- continue;
-
- if (le32toh(p->header_size) < sizeof(struct gpt_header))
- continue;
-
- if (le32toh(p->header_size) > 4096) /* larger than a sector? something is off… */
- continue;
+ p = (const GptHeader*) (sectors + sz);
- if (le64toh(p->my_lba) != 1) /* this sector must claim to be at sector offset 1 */
+ if (!gpt_header_has_signature(p))
continue;
if (found != 0)
/* If we have a build-id, merge it in the same JSON object so that it appears all
* nicely together in the logs/metadata. */
if (id_json) {
- r = json_variant_merge(&v, id_json);
+ r = json_variant_merge_object(&v, id_json);
if (r < 0)
return log_error_errno(r, "json_variant_merge of package meta with buildId failed: %m");
}
if (r < 0)
return log_error_errno(r, "Failed to build JSON object: %m");
- r = json_variant_merge(c->package_metadata, w);
+ r = json_variant_merge_object(c->package_metadata, w);
if (r < 0)
return log_error_errno(r, "json_variant_merge of package meta with buildId failed: %m");
if (r < 0)
return log_warning_errno(r, "Failed to build JSON object: %m");
- r = json_variant_merge(&elf_metadata, json_architecture);
+ r = json_variant_merge_object(&elf_metadata, json_architecture);
if (r < 0)
return log_warning_errno(r, "Failed to merge JSON objects: %m");
#endif
/* We always at least have the ELF type, so merge that (and possibly the arch). */
- r = json_variant_merge(&elf_metadata, package_metadata);
+ r = json_variant_merge_object(&elf_metadata, package_metadata);
if (r < 0)
return log_warning_errno(r, "Failed to merge JSON objects: %m");
int gpt_partition_label_valid(const char *s) {
_cleanup_free_ char16_t *recoded = NULL;
- recoded = utf8_to_utf16(s, strlen(s));
+ recoded = utf8_to_utf16(s, SIZE_MAX);
if (!recoded)
return -ENOMEM;
PARTITION_XBOOTLDR,
PARTITION_SWAP);
}
+
+bool gpt_header_has_signature(const GptHeader *p) {
+ assert(p);
+
+ if (memcmp(p->signature, (const char[8]) { 'E', 'F', 'I', ' ', 'P', 'A', 'R', 'T' }, 8) != 0)
+ return false;
+
+ if (le32toh(p->revision) != UINT32_C(0x00010000)) /* the only known revision of the spec: 1.0 */
+ return false;
+
+ if (le32toh(p->header_size) < sizeof(GptHeader))
+ return false;
+
+ if (le32toh(p->header_size) > 4096) /* larger than a sector? something is off… */
+ return false;
+
+ if (le64toh(p->my_lba) != 1) /* this sector must claim to be at sector offset 1 */
+ return false;
+
+ return true;
+}
/* SPDX-License-Identifier: LGPL-2.1-or-later */
#pragma once
+#include <uchar.h>
+
#include "sd-gpt.h"
#include "sd-id128.h"
#include "architecture.h"
#include "id128-util.h"
+#include "sparse-endian.h"
/* maximum length of gpt label */
#define GPT_LABEL_MAX 36
bool gpt_partition_type_knows_read_only(GptPartitionType type);
bool gpt_partition_type_knows_growfs(GptPartitionType type);
bool gpt_partition_type_knows_no_auto(GptPartitionType type);
+
+typedef struct {
+ uint8_t partition_type_guid[16];
+ uint8_t unique_partition_guid[16];
+ le64_t starting_lba;
+ le64_t ending_lba;
+ le64_t attributes;
+ char16_t partition_name[36];
+} _packed_ GptPartitionEntry;
+
+typedef struct {
+ char signature[8];
+ le32_t revision;
+ le32_t header_size;
+ le32_t crc32;
+ le32_t reserved;
+ le64_t my_lba;
+ le64_t alternate_lba;
+ le64_t first_usable_lba;
+ le64_t last_usable_lba;
+ uint8_t disk_guid[16];
+ le64_t partition_entry_lba;
+ le32_t number_of_partition_entries;
+ le32_t size_of_partition_entry;
+ le32_t partition_entry_array_crc32;
+} _packed_ GptHeader;
+
+bool gpt_header_has_signature(const GptHeader *p);
return 1;
}
+int json_variant_set_fieldb(JsonVariant **v, const char *field, ...) {
+ _cleanup_(json_variant_unrefp) JsonVariant *w = NULL;
+ va_list ap;
+ int r;
+
+ va_start(ap, field);
+ r = json_buildv(&w, ap);
+ va_end(ap);
+ if (r < 0)
+ return r;
+
+ return json_variant_set_field(v, field, w);
+}
+
int json_variant_set_field_string(JsonVariant **v, const char *field, const char *value) {
_cleanup_(json_variant_unrefp) JsonVariant *m = NULL;
int r;
return json_variant_set_field(v, field, m);
}
-int json_variant_merge(JsonVariant **v, JsonVariant *m) {
+int json_variant_merge_object(JsonVariant **v, JsonVariant *m) {
_cleanup_(json_variant_unrefp) JsonVariant *w = NULL;
_cleanup_free_ JsonVariant **array = NULL;
size_t v_elements, m_elements, k;
return 1;
}
+int json_variant_merge_objectb(JsonVariant **v, ...) {
+ _cleanup_(json_variant_unrefp) JsonVariant *w = NULL;
+ va_list ap;
+ int r;
+
+ va_start(ap, v);
+ r = json_buildv(&w, ap);
+ va_end(ap);
+ if (r < 0)
+ return r;
+
+ return json_variant_merge_object(v, w);
+}
+
int json_variant_append_array(JsonVariant **v, JsonVariant *element) {
_cleanup_(json_variant_unrefp) JsonVariant *nv = NULL;
bool blank;
return 0;
}
+int json_variant_append_arrayb(JsonVariant **v, ...) {
+ _cleanup_(json_variant_unrefp) JsonVariant *w = NULL;
+ va_list ap;
+ int r;
+
+ va_start(ap, v);
+ r = json_buildv(&w, ap);
+ va_end(ap);
+ if (r < 0)
+ return r;
+
+ return json_variant_append_array(v, w);
+}
+
JsonVariant *json_variant_find(JsonVariant *haystack, JsonVariant *needle) {
JsonVariant *i;
return r;
}
-int json_appendv(JsonVariant **v, va_list ap) {
- _cleanup_(json_variant_unrefp) JsonVariant *w = NULL;
- int r;
-
- assert(v);
-
- r = json_buildv(&w, ap);
- if (r < 0)
- return r;
-
- return json_variant_merge(v, w);
-}
-
-int json_append(JsonVariant **v, ...) {
- va_list ap;
- int r;
-
- va_start(ap, v);
- r = json_appendv(v, ap);
- va_end(ap);
-
- return r;
-}
-
int json_log_internal(
JsonVariant *variant,
int level,
int json_variant_filter(JsonVariant **v, char **to_remove);
int json_variant_set_field(JsonVariant **v, const char *field, JsonVariant *value);
+int json_variant_set_fieldb(JsonVariant **v, const char *field, ...);
int json_variant_set_field_string(JsonVariant **v, const char *field, const char *value);
int json_variant_set_field_integer(JsonVariant **v, const char *field, int64_t value);
int json_variant_set_field_unsigned(JsonVariant **v, const char *field, uint64_t value);
JsonVariant *json_variant_find(JsonVariant *haystack, JsonVariant *needle);
int json_variant_append_array(JsonVariant **v, JsonVariant *element);
+int json_variant_append_arrayb(JsonVariant **v, ...);
int json_variant_append_array_nodup(JsonVariant **v, JsonVariant *element);
-int json_variant_merge(JsonVariant **v, JsonVariant *m);
+int json_variant_merge_object(JsonVariant **v, JsonVariant *m);
+int json_variant_merge_objectb(JsonVariant **v, ...);
int json_variant_strv(JsonVariant *v, char ***ret);
int json_build(JsonVariant **ret, ...);
int json_buildv(JsonVariant **ret, va_list ap);
- /* These two functions below are equivalent to json_build() (or json_buildv()) and json_variant_merge(). */
-int json_append(JsonVariant **v, ...);
-int json_appendv(JsonVariant **v, va_list ap);
/* A bitmask of flags used by the dispatch logic. Note that this is a combined bit mask, that is generated from the bit
* mask originally passed into json_dispatch(), the individual bitmask associated with the static JsonDispatch callout
'fdisk-util.c',
)
-if get_option('fdisk') != 'false'
- libshared_fdisk = static_library(
- 'shared-fdisk',
- shared_fdisk_sources,
- include_directories : includes,
- dependencies : [libfdisk,
- userspace],
- c_args : ['-fvisibility=default'],
- build_by_default : false)
-endif
+libshared_fdisk = static_library(
+ 'shared-fdisk',
+ shared_fdisk_sources,
+ include_directories : includes,
+ dependencies : [libfdisk,
+ userspace],
+ c_args : ['-fvisibility=default'],
+ build_by_default : false)
static int get_sub_mounts(
const char *prefix,
- bool clone_tree,
SubMount **ret_mounts,
size_t *ret_n_mounts) {
_cleanup_(mnt_free_tablep) struct libmnt_table *table = NULL;
continue;
}
- if (clone_tree)
- mount_fd = open_tree(AT_FDCWD, path, OPEN_TREE_CLONE | OPEN_TREE_CLOEXEC | AT_RECURSIVE);
- else
- mount_fd = open(path, O_CLOEXEC|O_PATH);
+ mount_fd = open(path, O_CLOEXEC|O_PATH);
if (mount_fd < 0) {
if (errno == ENOENT) /* The path may be hidden by another over-mount or already unmounted. */
continue;
return 0;
}
-static int move_sub_mounts(SubMount *mounts, size_t n) {
- assert(mounts || n == 0);
-
- for (size_t i = 0; i < n; i++) {
- if (!mounts[i].path || mounts[i].mount_fd < 0)
- continue;
-
- (void) mkdir_p_label(mounts[i].path, 0755);
-
- if (move_mount(mounts[i].mount_fd, "", AT_FDCWD, mounts[i].path, MOVE_MOUNT_F_EMPTY_PATH) < 0)
- return log_debug_errno(errno, "Failed to move mount_fd to '%s': %m", mounts[i].path);
- }
-
- return 0;
-}
-
-int remount_and_move_sub_mounts(
- const char *what,
- const char *where,
- const char *type,
- unsigned long flags,
- const char *options) {
-
- SubMount *mounts = NULL;
- size_t n = 0;
- int r;
-
- CLEANUP_ARRAY(mounts, n, sub_mount_array_free);
-
- assert(where);
-
- /* This is useful when creating a new network namespace. Unlike procfs, we need to remount sysfs,
- * otherwise properties of the network interfaces in the main network namespace are still accessible
- * through the old sysfs, e.g. /sys/class/net/eth0. All sub-mounts previously mounted on the sysfs
- * are moved onto the new sysfs mount. */
-
- r = path_is_mount_point(where, NULL, 0);
- if (r < 0)
- return log_debug_errno(r, "Failed to determine if '%s' is a mountpoint: %m", where);
- if (r == 0)
- /* Shortcut. Simply mount the requested filesystem. */
- return mount_nofollow_verbose(LOG_DEBUG, what, where, type, flags, options);
-
- /* Get the list of sub-mounts and duplicate them. */
- r = get_sub_mounts(where, /* clone_tree= */ true, &mounts, &n);
- if (r < 0)
- return r;
-
- /* Then, remount the mount and its sub-mounts. */
- (void) umount_recursive(where, 0);
-
- /* Remount the target filesystem. */
- r = mount_nofollow_verbose(LOG_DEBUG, what, where, type, flags, options);
- if (r < 0)
- return r;
-
- /* Finally, move the all sub-mounts on the new target mount point. */
- return move_sub_mounts(mounts, n);
-}
-
int bind_mount_submounts(
const char *source,
const char *target) {
CLEANUP_ARRAY(mounts, n, sub_mount_array_free);
- r = get_sub_mounts(source, /* clone_tree= */ false, &mounts, &n);
+ r = get_sub_mounts(source, &mounts, &n);
if (r < 0)
return r;
return ret;
}
-int remount_sysfs(const char *where) {
- return remount_and_move_sub_mounts("sysfs", where, "sysfs", MS_NOSUID|MS_NOEXEC|MS_NODEV, NULL);
-}
-
int make_mount_point_inode_from_stat(const struct stat *st, const char *dest, mode_t mode) {
assert(st);
assert(dest);
int remount_idmap_fd(const char *p, int userns_fd);
int remount_idmap(const char *p, uid_t uid_shift, uid_t uid_range, uid_t owner, RemountIdmapping idmapping);
-int remount_and_move_sub_mounts(
- const char *what,
- const char *where,
- const char *type,
- unsigned long flags,
- const char *options);
-int remount_sysfs(const char *where);
-
int bind_mount_submounts(
const char *source,
const char *target);
else if (r < 0)
return r;
else {
- r = json_variant_merge(&v, privileged_v);
+ r = json_variant_merge_object(&v, privileged_v);
if (r < 0)
return r;
else if (r < 0)
return r;
else {
- r = json_variant_merge(&v, privileged_v);
+ r = json_variant_merge_object(&v, privileged_v);
if (r < 0)
return r;
}
const Verb* verbs_find_verb(const char *name, const Verb verbs[]) {
+ assert(verbs);
+
for (size_t i = 0; verbs[i].dispatch; i++)
- if (streq_ptr(name, verbs[i].verb) ||
- (!name && FLAGS_SET(verbs[i].flags, VERB_DEFAULT)))
- return &verbs[i];
+ if (name ? streq(name, verbs[i].verb) : FLAGS_SET(verbs[i].flags, VERB_DEFAULT))
+ return verbs + i;
/* At the end of the list? */
return NULL;
}
+static const Verb* verbs_find_prefix_verb(const char *name, const Verb verbs[]) {
+ size_t best_distance = SIZE_MAX;
+ const Verb *best = NULL;
+
+ assert(verbs);
+
+ if (!name)
+ return NULL;
+
+ for (size_t i = 0; verbs[i].dispatch; i++) {
+ const char *e;
+ size_t l;
+
+ e = startswith(verbs[i].verb, name);
+ if (!e)
+ continue;
+
+ l = strlen(e);
+ if (l < best_distance) {
+ best_distance = l;
+ best = verbs + i;
+ }
+ }
+
+ return best;
+}
+
+static const Verb* verbs_find_closest_verb(const char *name, const Verb verbs[]) {
+ ssize_t best_distance = SSIZE_MAX;
+ const Verb *best = NULL;
+
+ assert(verbs);
+
+ if (!name)
+ return NULL;
+
+ for (size_t i = 0; verbs[i].dispatch; i++) {
+ ssize_t distance;
+
+ distance = strlevenshtein(verbs[i].verb, name);
+ if (distance < 0) {
+ log_debug_errno(distance, "Failed to determine Levenshtein distance between %s and %s: %m", verbs[i].verb, name);
+ return NULL;
+ }
+
+ if (distance > 5) /* If the distance is just too far off, don't make a bad suggestion */
+ continue;
+
+ if (distance < best_distance) {
+ best_distance = distance;
+ best = verbs + i;
+ }
+ }
+
+ return best;
+}
+
int dispatch_verb(int argc, char *argv[], const Verb verbs[], void *userdata) {
const Verb *verb;
const char *name;
verb = verbs_find_verb(name, verbs);
if (!verb) {
- if (name)
- return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
- "Unknown command verb %s.", name);
- else
- return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
- "Command verb required.");
+ if (name) {
+ /* Be helperful to the user, and give a hint what the user might have wanted to
+ * type. We search with two mechanisms: a simple prefix match and – if that didn't
+ * yield results –, a Levenshtein word distance based match. */
+ verb = verbs_find_prefix_verb(name, verbs);
+ if (!verb)
+ verb = verbs_find_closest_verb(name, verbs);
+ if (verb)
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
+ "Unknown command verb '%s', did you mean '%s'?", name, verb->verb);
+
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Unknown command verb '%s'.", name);
+ }
+
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Command verb required.");
}
if (!name)
if (verb->min_args != VERB_ANY &&
(unsigned) left < verb->min_args)
- return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
- "Too few arguments.");
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Too few arguments.");
if (verb->max_args != VERB_ANY &&
(unsigned) left > verb->max_args)
- return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
- "Too many arguments.");
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Too many arguments.");
if ((verb->flags & VERB_ONLINE_ONLY) && running_in_chroot_or_offline()) {
log_info("Running in chroot, ignoring command '%s'", name ?: verb->verb);
return 0;
}
- if (name)
- return verb->dispatch(left, argv, userdata);
- else {
- char* fake[2] = {
- (char*) verb->verb,
- NULL
- };
+ if (!name)
+ return verb->dispatch(1, STRV_MAKE(verb->verb), userdata);
- return verb->dispatch(1, fake, userdata);
+ return verb->dispatch(left, argv, userdata);
}
-}
}
static int daemon_reload(void) {
- sd_bus *bus;
+ _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL;
int r;
r = bus_connect_system_systemd(&bus);
uint8_t type,
const uint8_t *data,
size_t data_len);
-__extension__ int sd_dhcp_client_set_iaid_duid(
+__extension__ int sd_dhcp_client_set_iaid_duid_llt(
sd_dhcp_client *client,
bool iaid_set,
uint32_t iaid,
- uint16_t duid_type,
- const void *duid,
- size_t duid_len);
-__extension__ int sd_dhcp_client_set_iaid_duid_llt(
+ uint64_t llt_time);
+__extension__ int sd_dhcp_client_set_iaid_duid_ll(
+ sd_dhcp_client *client,
+ bool iaid_set,
+ uint32_t iaid);
+__extension__ int sd_dhcp_client_set_iaid_duid_en(
+ sd_dhcp_client *client,
+ bool iaid_set,
+ uint32_t iaid);
+__extension__ int sd_dhcp_client_set_iaid_duid_uuid(
+ sd_dhcp_client *client,
+ bool iaid_set,
+ uint32_t iaid);
+__extension__ int sd_dhcp_client_set_iaid_duid_raw(
sd_dhcp_client *client,
bool iaid_set,
uint32_t iaid,
- uint64_t llt_time);
+ uint16_t duid_type,
+ const uint8_t *duid,
+ size_t duid_len);
int sd_dhcp_client_get_client_id(
sd_dhcp_client *client,
uint8_t *ret_type,
const uint8_t *addr,
size_t addr_len,
uint16_t arp_type);
-int sd_dhcp6_client_set_duid(
- sd_dhcp6_client *client,
- uint16_t duid_type,
- const void *duid,
- size_t duid_len);
-int sd_dhcp6_client_set_duid_llt(
- sd_dhcp6_client *client,
- uint64_t llt_time);
+int sd_dhcp6_client_set_duid_llt(sd_dhcp6_client *client, uint64_t llt_time);
+int sd_dhcp6_client_set_duid_ll(sd_dhcp6_client *client);
+int sd_dhcp6_client_set_duid_en(sd_dhcp6_client *client);
+int sd_dhcp6_client_set_duid_uuid(sd_dhcp6_client *client);
+int sd_dhcp6_client_set_duid_raw(sd_dhcp6_client *client, uint16_t duid_type, const uint8_t *duid, size_t duid_len);
int sd_dhcp6_client_set_iaid(
sd_dhcp6_client *client,
uint32_t iaid);
int sd_radv_set_mac(sd_radv *ra, const struct ether_addr *mac_addr);
int sd_radv_set_mtu(sd_radv *ra, uint32_t mtu);
int sd_radv_set_hop_limit(sd_radv *ra, uint8_t hop_limit);
+int sd_radv_set_retransmit(sd_radv *ra, uint32_t retransmit_msec);
int sd_radv_set_router_lifetime(sd_radv *ra, uint64_t lifetime_usec);
int sd_radv_set_managed_information(sd_radv *ra, int managed);
int sd_radv_set_other_information(sd_radv *ra, int other);
static void test_exec_credentials(Manager *m) {
test(m, "exec-set-credential.service", 0, CLD_EXITED);
+ test(m, "exec-set-credential-with-mount-namespace.service", 0, CLD_EXITED);
+ test(m, "exec-set-credential-with-seccomp.service", 0, CLD_EXITED);
test(m, "exec-load-credential.service", MANAGER_IS_SYSTEM(m) ? 0 : EXIT_CREDENTIALS, CLD_EXITED);
+ test(m, "exec-load-credential-with-mount-namespace.service", MANAGER_IS_SYSTEM(m) ? 0 : EXIT_CREDENTIALS, CLD_EXITED);
+ test(m, "exec-load-credential-with-seccomp.service", MANAGER_IS_SYSTEM(m) ? 0 : EXIT_CREDENTIALS, CLD_EXITED);
test(m, "exec-credentials-dir-specifier.service", MANAGER_IS_SYSTEM(m) ? 0 : EXIT_CREDENTIALS, CLD_EXITED);
}
test_variant_one("[ 0, -0, 0.0, -0.0, 0.000, -0.000, 0e0, -0e0, 0e+0, -0e-0, 0e-0, -0e000, 0e+000 ]", test_zeroes);
}
-TEST(json_append) {
+TEST(json_variant_merge_objectb) {
_cleanup_(json_variant_unrefp) JsonVariant *v = NULL, *w = NULL;
assert_se(json_build(&v, JSON_BUILD_OBJECT(
JSON_BUILD_PAIR("c", JSON_BUILD_CONST_STRING("y")),
JSON_BUILD_PAIR("a", JSON_BUILD_CONST_STRING("z")))) >= 0);
- assert_se(json_append(&w, JSON_BUILD_OBJECT(JSON_BUILD_PAIR("b", JSON_BUILD_STRING("x")))) >= 0);
- assert_se(json_append(&w, JSON_BUILD_OBJECT(JSON_BUILD_PAIR("c", JSON_BUILD_STRING("y")))) >= 0);
- assert_se(json_append(&w, JSON_BUILD_OBJECT(JSON_BUILD_PAIR("a", JSON_BUILD_STRING("z")))) >= 0);
+ assert_se(json_variant_merge_objectb(&w, JSON_BUILD_OBJECT(JSON_BUILD_PAIR("b", JSON_BUILD_STRING("x")))) >= 0);
+ assert_se(json_variant_merge_objectb(&w, JSON_BUILD_OBJECT(JSON_BUILD_PAIR("c", JSON_BUILD_STRING("y")))) >= 0);
+ assert_se(json_variant_merge_objectb(&w, JSON_BUILD_OBJECT(JSON_BUILD_PAIR("a", JSON_BUILD_STRING("z")))) >= 0);
assert_se(json_variant_equal(v, w));
}
#include "tests.h"
#include "tmpfile-util.h"
-TEST(remount_and_move_sub_mounts) {
- int r;
-
- if (geteuid() != 0 || have_effective_cap(CAP_SYS_ADMIN) <= 0)
- return (void) log_tests_skipped("not running privileged");
-
- r = safe_fork("(remount-and-move-sub-mounts)",
- FORK_RESET_SIGNALS |
- FORK_CLOSE_ALL_FDS |
- FORK_DEATHSIG |
- FORK_WAIT |
- FORK_REOPEN_LOG |
- FORK_LOG |
- FORK_NEW_MOUNTNS |
- FORK_MOUNTNS_SLAVE,
- NULL);
- assert_se(r >= 0);
- if (r == 0) {
- _cleanup_free_ char *d = NULL, *fn = NULL;
-
- assert_se(mkdtemp_malloc(NULL, &d) >= 0);
-
- assert_se(mount_nofollow_verbose(LOG_DEBUG, "tmpfs", d, "tmpfs", MS_NOSUID|MS_NODEV, NULL) >= 0);
-
- assert_se(fn = path_join(d, "memo"));
- assert_se(write_string_file(fn, d, WRITE_STRING_FILE_CREATE|WRITE_STRING_FILE_AVOID_NEWLINE) >= 0);
- assert_se(access(fn, F_OK) >= 0);
-
- /* Create fs tree */
- FOREACH_STRING(p, "sub1", "sub1/hoge", "sub1/foo", "sub2", "sub2/aaa", "sub2/bbb") {
- _cleanup_free_ char *where = NULL, *filename = NULL;
-
- assert_se(where = path_join(d, p));
- assert_se(mkdir_p(where, 0755) >= 0);
- assert_se(mount_nofollow_verbose(LOG_DEBUG, "tmpfs", where, "tmpfs", MS_NOSUID|MS_NODEV, NULL) >= 0);
-
- assert_se(filename = path_join(where, "memo"));
- assert_se(write_string_file(filename, where, WRITE_STRING_FILE_CREATE|WRITE_STRING_FILE_AVOID_NEWLINE) >= 0);
- assert_se(access(filename, F_OK) >= 0);
- }
-
- /* Hide sub1. */
- FOREACH_STRING(p, "sub1", "sub1/hogehoge", "sub1/foofoo") {
- _cleanup_free_ char *where = NULL, *filename = NULL;
-
- assert_se(where = path_join(d, p));
- assert_se(mkdir_p(where, 0755) >= 0);
- assert_se(mount_nofollow_verbose(LOG_DEBUG, "tmpfs", where, "tmpfs", MS_NOSUID|MS_NODEV, NULL) >= 0);
-
- assert_se(filename = path_join(where, "memo"));
- assert_se(write_string_file(filename, where, WRITE_STRING_FILE_CREATE|WRITE_STRING_FILE_AVOID_NEWLINE) >= 0);
- assert_se(access(filename, F_OK) >= 0);
- }
-
- /* Remount the main fs. */
- r = remount_and_move_sub_mounts("tmpfs", d, "tmpfs", MS_NOSUID|MS_NODEV, NULL);
- if (r == -EINVAL || (r < 0 && ERRNO_IS_NOT_SUPPORTED(r))) {
- log_tests_skipped_errno(r, "The kernel seems too old: %m");
- _exit(EXIT_SUCCESS);
- }
-
- /* Check the file in the main fs does not exist. */
- assert_se(access(fn, F_OK) < 0 && errno == ENOENT);
-
- /* Check the files in sub-mounts are kept. */
- FOREACH_STRING(p, "sub1", "sub1/hogehoge", "sub1/foofoo", "sub2", "sub2/aaa", "sub2/bbb") {
- _cleanup_free_ char *where = NULL, *filename = NULL, *content = NULL;
-
- assert_se(where = path_join(d, p));
- assert_se(filename = path_join(where, "memo"));
- assert_se(read_full_file(filename, &content, NULL) >= 0);
- assert_se(streq(content, where));
- }
-
- /* umount sub1, and check if the previously hidden sub-mounts are dropped. */
- FOREACH_STRING(p, "sub1/hoge", "sub1/foo") {
- _cleanup_free_ char *where = NULL;
-
- assert_se(where = path_join(d, p));
- assert_se(access(where, F_OK) < 0 && errno == ENOENT);
- }
-
- _exit(EXIT_SUCCESS);
- }
-}
-
-TEST(remount_sysfs) {
- int r;
-
- if (geteuid() != 0 || have_effective_cap(CAP_SYS_ADMIN) <= 0)
- return (void) log_tests_skipped("not running privileged");
-
- if (path_is_fs_type("/sys", SYSFS_MAGIC) <= 0)
- return (void) log_tests_skipped("sysfs is not mounted on /sys");
-
- if (access("/sys/class/net/dummy-test-mnt", F_OK) < 0)
- return (void) log_tests_skipped_errno(errno, "The network interface dummy-test-mnt does not exit");
-
- r = safe_fork("(remount-sysfs)",
- FORK_RESET_SIGNALS |
- FORK_CLOSE_ALL_FDS |
- FORK_DEATHSIG |
- FORK_WAIT |
- FORK_REOPEN_LOG |
- FORK_LOG |
- FORK_NEW_MOUNTNS |
- FORK_MOUNTNS_SLAVE,
- NULL);
- assert_se(r >= 0);
- if (r == 0) {
- assert_se(unshare(CLONE_NEWNET) >= 0);
-
- /* Even unshare()ed, the interfaces in the main namespace can be accessed through sysfs. */
- assert_se(access("/sys/class/net/lo", F_OK) >= 0);
- assert_se(access("/sys/class/net/dummy-test-mnt", F_OK) >= 0);
-
- r = remount_sysfs("/sys");
- if (r == -EINVAL || (r < 0 && ERRNO_IS_NOT_SUPPORTED(r))) {
- log_tests_skipped_errno(r, "The kernel seems too old: %m");
- _exit(EXIT_SUCCESS);
- }
-
- /* After remounting sysfs, the interfaces in the main namespace cannot be accessed. */
- assert_se(access("/sys/class/net/lo", F_OK) >= 0);
- assert_se(access("/sys/class/net/dummy-test-mnt", F_OK) < 0 && errno == ENOENT);
-
- _exit(EXIT_SUCCESS);
- }
-}
-
TEST(mount_option_mangle) {
char *opts = NULL;
unsigned long f;
assert_se(umount_recursive(b, 0) >= 0);
}
-static int intro(void) {
- /* Create a dummy network interface for testing remount_sysfs(). */
- (void) system("ip link add dummy-test-mnt type dummy");
-
- return 0;
-}
-
-static int outro(void) {
- (void) system("ip link del dummy-test-mnt");
-
- return 0;
-}
-
-DEFINE_TEST_MAIN_FULL(LOG_DEBUG, intro, outro);
+DEFINE_TEST_MAIN(LOG_DEBUG);
NULL,
NULL,
NULL,
+ -EBADF,
NULL,
0,
NULL,
tmp_dir,
var_tmp_dir,
NULL,
+ -EBADF,
NULL,
0,
NULL,
x = mfree(x);
}
+TEST(strlevenshtein) {
+ assert_se(strlevenshtein(NULL, NULL) == 0);
+ assert_se(strlevenshtein("", "") == 0);
+ assert_se(strlevenshtein("", NULL) == 0);
+ assert_se(strlevenshtein(NULL, "") == 0);
+
+ assert_se(strlevenshtein("a", "a") == 0);
+ assert_se(strlevenshtein("a", "b") == 1);
+ assert_se(strlevenshtein("b", "a") == 1);
+ assert_se(strlevenshtein("a", "") == 1);
+ assert_se(strlevenshtein("", "a") == 1);
+
+ assert_se(strlevenshtein("xxx", "xxx") == 0);
+ assert_se(strlevenshtein("xxx", "yyy") == 3);
+ assert_se(strlevenshtein("yyy", "xxx") == 3);
+ assert_se(strlevenshtein("xx", "xxx") == 1);
+ assert_se(strlevenshtein("xxx", "xx") == 1);
+ assert_se(strlevenshtein("x", "xxx") == 2);
+ assert_se(strlevenshtein("xxx", "x") == 2);
+
+ assert_se(strlevenshtein("sitting", "kitten") == 3);
+ assert_se(strlevenshtein("sunday", "saturday") == 3);
+}
+
DEFINE_TEST_MAIN(LOG_DEBUG);
assert_se(b);
free(a);
- a = utf16_to_utf8(b, char16_strlen(b) * 2);
+ a = utf16_to_utf8(b, SIZE_MAX);
assert_se(a);
assert_se(strlen(a) == sizeof(utf8));
assert_se(memcmp(a, utf8, sizeof(utf8)) == 0);
_cleanup_free_ char16_t *a = NULL;
_cleanup_free_ char *b = NULL;
- a = utf8_to_utf16(p, strlen(p));
+ a = utf8_to_utf16(p, SIZE_MAX);
assert_se(a);
- b = utf16_to_utf8(a, char16_strlen(a) * 2);
+ b = utf16_to_utf8(a, SIZE_MAX);
assert_se(b);
assert_se(streq(p, b));
}
############################################################
+# TODO: Use native string formatting with meson >= 1.3.0
+if get_option('auto_features').enabled()
+ sanitize_auto_features = 'enabled'
+elif get_option('auto_features').disabled()
+ sanitize_auto_features = 'disabled'
+else
+ sanitize_auto_features = 'auto'
+endif
+
sanitize_address_undefined = custom_target(
'sanitize-address-undefined-fuzzers',
output : 'sanitize-address-undefined-fuzzers',
project_source_root,
'@OUTPUT@',
'fuzzers',
- '-Dfuzz-tests=true -Db_lundef=false -Db_sanitize=address,undefined --optimization=@0@ @1@ -Dc_args=@2@ -Dcpp_args=@2@ -Dskip-deps=@3@'.format(
+ '-Dfuzz-tests=true -Db_lundef=false -Db_sanitize=address,undefined --optimization=@0@ @1@ -Dc_args=@2@ -Dcpp_args=@2@ --auto-features=@3@'.format(
get_option('optimization'),
get_option('werror') ? '--werror' : '',
'-DFUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION',
- get_option('skip-deps')
+ sanitize_auto_features
),
' '.join(cc.cmd_array()),
cxx_cmd])
--- /dev/null
+# SPDX-License-Identifier: LGPL-2.1-or-later
+[Unit]
+Description=Test for LoadCredential=
+
+[Service]
+ExecStart=/bin/sh -x -c 'test "$$(cat %d/test-execute.load-credential)" = "foo"'
+Type=oneshot
+LoadCredential=test-execute.load-credential
+PrivateMounts=yes
--- /dev/null
+# SPDX-License-Identifier: LGPL-2.1-or-later
+[Unit]
+Description=Test for LoadCredential=
+
+[Service]
+ExecStart=/bin/sh -x -c 'test "$$(cat %d/test-execute.load-credential)" = "foo"'
+Type=oneshot
+LoadCredential=test-execute.load-credential
+SystemCallFilter=~open_tree move_mount
--- /dev/null
+# SPDX-License-Identifier: LGPL-2.1-or-later
+[Unit]
+Description=Test for SetCredential=
+
+[Service]
+ExecStart=/bin/sh -x -c 'test "$$(cat %d/test-execute.set-credential)" = "hoge"'
+Type=oneshot
+SetCredential=test-execute.set-credential:hoge
+PrivateMounts=yes
--- /dev/null
+# SPDX-License-Identifier: LGPL-2.1-or-later
+[Unit]
+Description=Test for SetCredential=
+
+[Service]
+ExecStart=/bin/sh -x -c 'test "$$(cat %d/test-execute.set-credential)" = "hoge"'
+Type=oneshot
+SetCredential=test-execute.set-credential:hoge
+SystemCallFilter=~open_tree move_mount
Destination=2001:1234:5:9fff:ff:ff:ff:ff/128
[DHCPv6]
-DUIDType=custom
+DUIDType=0x42
DUIDRawData=00:00:ab:11:f9:2a:c2:77:29:f9:5c:00
[Route]
Destination=2001:1234:5:8fff:ff:ff:ff:ff/128
TCPCongestionControlAlgorithm=dctcp
-TCPRetransmissionTimeOutSec=300s
+TCPRetransmissionTimeoutSec=300s
[Route]
Destination=149.10.124.66
TCPCongestionControlAlgorithm=dctcp
-TCPRetransmissionTimeOutSec=300s
+TCPRetransmissionTimeoutSec=300s
self.assertRegex(output, r'inet6 2600::[0-9a-f:]*/128 scope global dynamic noprefixroute')
self.assertNotIn('192.168.5', output)
- # checking semi-static route
- output = check_output('ip -6 route list dev veth99 2001:1234:5:9fff:ff:ff:ff:ff')
- print(output)
- self.assertRegex(output, 'via fe80::1034:56ff:fe78:9abd')
-
- # Confirm that ipv6 token is not set in the kernel
- output = check_output('ip token show dev veth99')
- print(output)
- self.assertRegex(output, 'token :: dev veth99')
-
print('## dnsmasq log')
output = read_dnsmasq_log_file()
print(output)
- self.assertIn('DHCPSOLICIT(veth-peer) 00:00:ab:11:f9:2a:c2:77:29:f9:5c:00', output)
+ self.assertIn('DHCPSOLICIT(veth-peer) 00:42:00:00:ab:11:f9:2a:c2:77:29:f9:5c:00', output)
self.assertNotIn('DHCPADVERTISE(veth-peer)', output)
self.assertNotIn('DHCPREQUEST(veth-peer)', output)
self.assertIn('DHCPREPLY(veth-peer)', output)
[[ "$(systemctl show -P UnitFileState foo-fstab-opt.mount)" == bad ]]
rm -f /etc/fstab
-[[ -e /tmp/fstab.bak ]] && mv -f /tmp/fstab /etc/fstab
+[[ -e /tmp/fstab.bak ]] && mv -f /tmp/fstab.bak /etc/fstab
systemctl daemon-reload
# c) Mount unit
rm -f "/etc/systemd/nspawn/$container.nspawn"
mkdir -p "$root/tmp" "$root"/opt/{tmp,inaccessible,also-inaccessible}
- for dev in sd-host-only sd-shared{1,2} sd-macvlan{1,2} sd-ipvlan{1,2}; do
+ for dev in sd-host-only sd-shared{1,2} sd-macvlan{1,2} sd-macvlanloong sd-ipvlan{1,2} sd-ipvlanlooong; do
ip link add "$dev" type dummy
done
udevadm settle
VirtualEthernetExtra=my-fancy-veth1
VirtualEthernetExtra=fancy-veth2:my-fancy-veth2
Interface=sd-shared1 sd-shared2:sd-shared2
-MACVLAN=sd-macvlan1 sd-macvlan2:my-macvlan2
-IPVLAN=sd-ipvlan1 sd-ipvlan2:my-ipvlan2
+MACVLAN=sd-macvlan1 sd-macvlan2:my-macvlan2 sd-macvlanloong
+IPVLAN=sd-ipvlan1 sd-ipvlan2:my-ipvlan2 sd-ipvlanlooong
Zone=sd-zone0
Port=80
Port=81:8181
"type" : "swap",
"label" : "label2",
"uuid" : "837c3d67-21b3-478e-be82-7e7f83bf96d3",
+ "partno" : 0,
"file" : "$defs/root.conf",
"node" : "$imgs/zzz1",
"offset" : 1048576,
"type" : "swap",
"label" : "label1",
"uuid" : "7b93d1f2-595d-4ce3-b0b9-837fbd9e63b0",
+ "partno" : 0,
"file" : "$defs/1/root1.conf",
"node" : "$imgs/zzz1",
"offset" : 1048576,
"type" : "swap",
"label" : "label2",
"uuid" : "837c3d67-21b3-478e-be82-7e7f83bf96d3",
+ "partno" : 1,
"file" : "$defs/2/root2.conf",
"node" : "$imgs/zzz2",
"offset" : 34603008,
rm -rf "$build"
mkdir -p "$build"
+meson_args=("-Db_lundef=false")
+
if [ -z "$FUZZING_ENGINE" ]; then
- fuzzflag="llvm-fuzz=true"
+ meson_args+=("-Dllvm-fuzz=true")
else
- fuzzflag="oss-fuzz=true"
+ meson_args+=("-Doss-fuzz=true" "--auto-features=disabled")
apt-get update
apt-get install -y gperf m4 gettext python3-pip \
fi
fi
-if ! meson setup "$build" "-D$fuzzflag" -Db_lundef=false; then
+if ! meson setup "$build" "${meson_args[@]}"; then
cat "$build/meson-logs/meson-log.txt"
exit 1
fi
},
{
'file' : 'systemd-bsod.service.in',
- 'conditions' : ['HAVE_QRENCODE','ENABLE_INITRD'],
+ 'conditions' : ['HAVE_QRENCODE', 'ENABLE_INITRD'],
'symlinks' : ['initrd.target.wants/'],
},
{
# (at your option) any later version.
[Unit]
-Description=Displays emergency message full screen.
-Documentation=man:systemd-bsod(8)
+Description=Displays emergency message in full screen.
+Documentation=man:systemd-bsod.service(8)
ConditionVirtualization=no
DefaultDependencies=no
-Conflicts=shutdown.target
-Before=shutdown.target
After=systemd-battery-check.service
+Before=shutdown.target
+Conflicts=shutdown.target
[Service]
RemainAfterExit=yes