I did the merge manually to resolve a trivial conflict.
- 'systemd-udevd'
- 'systemd-userdb'
- 'systemd-veritysetup'
+ - 'systemd-vmspawn'
- 'systemd-xdg-autostart-generator'
- 'timedatectl'
- 'udevadm'
- 'systemd-udevd'
- 'systemd-userdb'
- 'systemd-veritysetup'
+ - 'systemd-vmspawn'
- 'systemd-xdg-autostart-generator'
- 'timedatectl'
- 'udevadm'
- name: veritysetup
keys: ['systemd-veritysetup']
+ - name: vmspawn
+ keys: ['systemd-vmspawn']
+
- name: xdg-autostart
keys: ['systemd-xdg-autostart-generator']
mkosi:
- changed-files:
- any-glob-to-any-file: ['.mkosi/*', 'mkosi.build']
+mountfsd:
+ - changed-files:
+ - any-glob-to-any-file: ['src/mountfsd/*']
network:
- changed-files:
- any-glob-to-any-file: ['src/libsystemd-network/**/*', 'src/network/**/*']
nspawn:
- changed-files:
- any-glob-to-any-file: '**/*nspawn*'
+nsresource:
+ - changed-files:
+ - any-glob-to-any-file: '**/*nsresource*'
portable:
- changed-files:
- any-glob-to-any-file: 'src/portable/**/*'
fetch-depth: 0
- name: Differential ShellCheck
- uses: redhat-plumbers-in-action/differential-shellcheck@b9df2a9417f69c056e0aeaf870abd9a2065a403e
+ uses: redhat-plumbers-in-action/differential-shellcheck@c15070885a82a2c93db8a765d332c38c50dde8b3
with:
# exclude all `.in` files because they may contain unsupported syntax, and they have to be preprocessed first
# TEMPORARY: exclude bash completion files, they would generate too many defects in Code scanning dashboard (600+)
- uses: actions/checkout@9bb56186c3b09b4f86b1c65136769dd318469633
- name: Parse issue form
- uses: stefanbuck/github-issue-parser@c1a559d78bfb8dd05216dab9ffd2b91082ff5324
+ uses: stefanbuck/github-issue-parser@1e5bdee70d4b3e066a33aa0669ab782943825f94
id: issue-parser
with:
template-path: .github/ISSUE_TEMPLATE/${{ matrix.template }}
release: "39"
- distro: fedora
release: rawhide
- # TODO: Re-enable once https://lists.opensuse.org/archives/list/factory@lists.opensuse.org/thread/AU4NWTBXNA7MVAUXWR74XYCHCSZN4Z4K/
- # is resolved or https://build.opensuse.org/request/show/1152118 is merged.
- # - distro: opensuse
- # release: tumbleweed
+ - distro: opensuse
+ release: tumbleweed
- distro: centos
release: "9"
steps:
- uses: actions/checkout@9bb56186c3b09b4f86b1c65136769dd318469633
- - uses: systemd/mkosi@1445b389750af22756c0fde6facc1f2f343340b4
+ - uses: systemd/mkosi@a845d4108ac87ca443bd1ad78ab53520bffd2eda
# Freeing up disk space with rm -rf can take multiple minutes. Since we don't need the extra free space
# immediately, we remove the files in the background. However, we first move them to a different location
ToolsTree=default
ToolsTreeDistribution=fedora
QemuVsock=yes
- # Sometimes we run on a host with /dev/kvm, but it is broken, so explicitly disable it
QemuKvm=yes
# TODO: Drop once https://bugs.launchpad.net/ubuntu/+source/linux/+bug/2038777 is fixed in Github Actions
QemuFirmware=uefi
tee mkosi.conf.d/99-ci.conf <<EOF
[Host]
KernelCommandLineExtra=systemd.unit=mkosi-check-and-shutdown.service
+ systemd.log_level=debug
systemd.journald.max_level_console=debug
# udev's debug log output is very verbose, so up it to info in CI.
udev.log_level=info
verbatim from the Linux kernel source tree and are licensed under **GPL-2.0 WITH
Linux-syscall-note** and are used within the scope of the Linux-syscall-note
exception provisions
- * the src/shared/initreq.h header is licensed under original license,
- **LGPL-2.0-or-later**.
+ * the following sources are licensed under the **LGPL-2.0-or-later** license:
+ - src/basic/utf8.c
+ - src/shared/initreq.h
* the src/shared/linux/bpf_insn.h header is copied from the Linux kernel
source tree and is licensed under either **BSD-2-Clause** or **GPL-2.0-only**,
and thus is included in the systemd build under the BSD-2-Clause license.
Announcements of Future Feature Removals and Incompatible Changes:
- * Support for flushing of the nscd user/group database caches will be
- dropped in a future release.
+ * Support for automatic flushing of the nscd user/group database caches
+ will be dropped in a future release.
* Support for cgroup v1 ('legacy' and 'hybrid' hierarchies) is now
considered obsolete and systemd by default will refuse to boot under
that are not configured in the .network file are removed.
* systemd-gpt-auto-generator will stop generating units for ESP or
- XBOOTLDR partitions if it finds mount entries in the /boot/ or /efi/
- hierarchies in fstab. This is to prevent the generator from
- interfering with systems where ESP is explicitly configured to be
- mounted at some path, for example /boot/efi/ (this type of setup is
- obsolete but still commonly found).
+ XBOOTLDR partitions if it finds mount entries for or below the /boot/
+ or /efi/ hierarchies in /etc/fstab. This is to prevent the generator
+ from interfering with systems where the ESP is explicitly configured
+ to be mounted at some path, for example /boot/efi/ (this type of
+ setup is obsolete but still commonly found).
* The behavior of systemd-sleep and systemd-homed has been updated to
freeze user sessions when entering the various sleep modes or when
and related services, and SYSTEMD_HOME_LOCK_FREEZE_SESSION=false for
systemd-homed.service.
- * systemd-tmpfiles and systemd-sysusers, when given a relative path
- (with at least one directory separator '/'), will open the file
- directly, instead of searching for the given partial path in the
- standard locations. The old mode wasn't useful because tmpfiles.d and
- sysusers.d configuration has a flat structure with no subdirectories
- under the standard locations and this change makes it easier to work
- with local files with those tools.
+ * systemd-tmpfiles and systemd-sysusers, when given a relative
+ configuration file path (with at least one directory separator '/'),
+ will open the file directly, instead of searching for the given
+ partial path in the standard locations. The old mode wasn't useful
+ because tmpfiles.d/ and sysusers.d/ configuration has a flat
+ structure with no subdirectories under the standard locations and
+ this change makes it easier to work with local files with those
+ tools.
* systemd-tmpfiles now properly applies nested configuration to 'R' and
- 'D' stanzas. For example, with 'R /foo; x /foo/bar', /foo/bar will
- now be excluded from removal.
+ 'D' stanzas. For example, with the combination of 'R /foo' and 'x
+ /foo/bar', /foo/bar will now be excluded from removal.
General Changes and New Features:
- * Various programs will load the main configuration from under
- /usr/lib/, /usr/local/lib/, and /run/, not just from under /etc/. For
- example, systemd-logind will look for /etc/systemd/logind.conf,
- /run/systemd/logind.conf, /usr/local/lib/systemd/logind.conf, and
- /usr/lib/systemd/logind.conf, and use the first file that is found.
- This means that the location logic for the main config file and for
- drop-ins is now the same.
+ * Various programs will now attempt to load the main configuration file
+ from locations below /usr/lib/, /usr/local/lib/, and /run/, not just
+ below /etc/. For example, systemd-logind will look for
+ /etc/systemd/logind.conf, /run/systemd/logind.conf,
+ /usr/local/lib/systemd/logind.conf, and /usr/lib/systemd/logind.conf,
+ and use the first file that is found. This means that the search
+ logic for the main config file and for drop-ins is now the same.
- ukify will look for the config files in /usr/lib/kernel/ and the
- other locations, and now also supports drop-ins.
+ Similarly, kernel-install will look for the config files in
+ /usr/lib/kernel/ and the other search locations, and now also
+ supports drop-ins.
systemd-udevd now supports drop-ins for udev.conf.
* A new 'systemd-vpick' binary has been added. It implements the new
- vpick protocol, where a .v directory may contain multiple files with
- a version, following the UAPI version format specification, embedded
- in the file name. The files are ordered by version and the newest one
- is selected.
-
- systemd-nspawn, systemd-dissect, and the RootDirectory=, RootImage=,
- ExtensionImages=, and ExtensionDirectories= settings for units now
- support the vpick protocol and allow the latest version to be
- selected automatically if a "*.v/" directory is specified as the
- source.
-
- * Credentials can now be made accessible to and used by unprivileged
- users. 'systemd-creds --user --uid=<user>' will encrypt or decrypt a
- credential for a specific user.
-
- * With systemd-homed, it is now possible to log in and activate an
- encrypted home area over SSH.
-
- homectl is now installed as a multi-call binary. When invoked as
- systemd-home-fallback-shell it can be used as a temporary shell which
- allows the home area to interactively unlocked. When the home area
- becomes available, the temporary shell executes the normal one.
-
- systemd-homed gained new methods
- org.freedesktop.home1.Manager.RefHomeUnrestricted,
- org.freedesktop.home1.Home.RefUnrestricted,
- org.freedesktop.home1.Manager.ActivateHomeIfReferenced, and
- org.freedesktop.home1.Home.ActivateIfReferenced to allow logging in
- without activating the home area and then activating the home area
- later.
-
- * JSON User Records have been extended with a separate storage area
- called "User Record Blob Directories". This is intended to store the
- user's background image, avatar picture, and other similar items
- which are too large to fit into the User Record itself.
+ vpick protocol, where a "*.v/" directory may contain multiple files
+ which have versions (following the UAPI version format specification)
+ embedded in the file name. The files are ordered by version and
+ the newest one is selected.
- systemd-homed, userdbctl, and homectl gained support for blob
- directories.
+ systemd-nspawn --image=/--directory=, systemd-dissect, and the
+ RootDirectory=, RootImage=, ExtensionImages=, and
+ ExtensionDirectories= settings for units now support the vpick
+ protocol and allow the latest version to be selected automatically if
+ a "*.v/" directory is specified as the source.
+
+ * Encrypted service credentials may now be made accessible to
+ unprivileged users. systemd-creds gained new options --user/--uid=
+ for encrypting/decrypting a credential for a specific user.
* New command-line tool 'importctl' to download, import, and export
disk images via systemd-importd is added with the following verbs:
pull-tar, pull-raw, import-tar, import-raw, import-fs, export-tar,
- export-raw, list-transfers, cancel-transfer.
+ export-raw, list-transfers, cancel-transfer. This functionality was
+ previously available in "machinectl", where it was exclusively for
+ machine image. The new "importctl" generalizes this for sysext,
+ confext, portable service images, too.
- Service Manager:
+ Service Management:
- * New manager setting ProtectSystem= has been added. It is analogous to
- the unit setting, but applies to the whole system. It is enabled by
- default in the initrd.
+ * New system manager setting ProtectSystem= has been added. It is
+ analogous to the unit setting, but applies to the whole system. It is
+ enabled by default in the initrd.
* New unit setting WantsMountsFor= has been added. It is analogous to
RequiresMountsFor=, but with a Wants= dependency instead of
memory.zswap.writeback cgroup knob added in kernel 6.8.
* The manager gained a org.freedesktop.systemd1.StartAuxiliaryScope()
- method to devolve some processes from a service into a new scope.
- This new scope will remain even if the original service unit is
- restarted. Cgroup properties of the new scope are copied from the
- service, so various limits are retained.
+ D-Bus method to devolve some processes from a service into a new
+ scope. This new scope will remain even if the original service unit
+ is restarted. Control group properties of the new scope are copied
+ from the originating unit, so various limits are retained.
* Units now expose properties EffectiveMemoryMax=,
EffectiveMemoryHigh=, and EffectiveTasksMax=, which report the
most stringent limit systemd is aware of for the given unit.
- * A new specifier %D expands to $XDG_DATA_HOME.
+ * A new unit file specifier %D expands to $XDG_DATA_HOME (for user
+ services) or /usr/share/ (for system services).
* AllowedCPUs= now supports specifier expansion.
* PAMName= now implies SetLoginEnvironment=yes.
- * homectl gained a new verb 'firstboot', and a new
- systemd-homed-firstboot.service unit uses this verb to create users
- in a first boot environment, either from credentials or by querying
- interactively.
-
* systemd.firstboot=no can be used on the kernel command-line to
disable interactive queries, but allow other first boot configuration
to happen based on credentials.
- * A new kernel command-line option systemd.default_debug_tty= can be
- used to specify the TTY for the debug shell, independently of
- enabling or disabling it.
+ * The system's hostname can be configured via the systemd.hostname
+ system credential.
+
+ * The systemd binary will no longer chainload sysvinit's "telinit"
+ binary when called under the init/telinit name on a system that
+ isn't booted with systemd. This previously has been supported to make
+ sure a distribution that has both init systems installed can be
+ reasonably switched from one to the other via a simple
+ reboot. Distributions apparently have lost interest in this, and the
+ functionality has not been supported on the primary distribution this
+ was still intended for for a longer time, and hence has been removed
+ now.
- * Systemd hostname can be configured via the systemd.hostname
- credential.
+ * A new concept called "capsules" has been introduced. "Capsules"
+ encapsulate additional per-user service managers, whose users are
+ transient and are only defined as long as the service manager
+ is running (implemented via DynamicUser=1). These service managers run
+ off home directories defined in /var/lib/capsules/<name>, where
+ <name> is a the capsule's name. These home directories can contain
+ regular per-user services and other units. A capsule is started via a
+ simple "systemctl start capsule@<name>.service". See the
+ capsule@.service(5) man page for further details. Various systemd
+ tools (including, and most importantly, systemctl and systemd-run)
+ have been updated to interact with capsules via the new
+ "--capsule="/"-C" switch.
+
+ * .socket units gained a new setting PassFileDescriptorsToExec=, taking
+ a boolean value. If set to true the file descriptors the socket unit
+ encapsulates are passed to the ExecStartPost=, ExecStopPre=,
+ ExecStopPost= using the usual $LISTEN_FDS interface. This may be used
+ for doing additional initializations on the sockets once they are
+ allocated (for example, install an additional eBPF program on them).
+
+ * The .socket setting MaxConnectionsPerSource= (which so far put a
+ limit on concurrent connections per IP in Accept=yes socket units),
+ now also has an effect on AF_UNIX sockets: it will put a limit on the
+ number of simultaneous connections from the same source UID (as
+ determined via SO_PEERCRED). This is useful for implementing IPC
+ services in a simple Accept=yes mode.
+
+ * The service manager will now maintain a counter of soft reboot cycles
+ the system went through so far. It may be queried via the D-Bus APIs.
+
+ * systemd's execution logic now supports the new pidfd_spawn() API
+ introduced by glibc 2.39, which allows us to invoke a subprocess in a
+ target cgroup and get a pidfd back in a single operation.
+
+ * systemd/PID 1 will now send an additional sd_notify() message to its
+ supervising VMM or container manager reporting the selected hostname
+ ("X_SYSTEMD_HOSTNAME=") and machine ID ("X_SYSTEMD_MACHINE_ID=") at
+ boot. Moreover, the service manager will send additional sd_notify()
+ messages ("X_SYSTEMD_UNIT_ACTIVE=") whenever a target unit is
+ reached. This can be used by VMMs/container managers to schedule
+ access to the system precisely. For example, the moment a system
+ reports "ssh-access.target" being reached a VMM/container manager
+ knows it can now connect to the system via SSH. Finally, a new
+ sd_notify() message ("X_SYSTEMD_SIGNALS_LEVEL=2") is sent the moment
+ PID 1 successfully completed installation of its various UNIX process
+ signal handlers (i.e. the moment where SIGRTMIN+4 sent to PID 1 will
+ start to have the effect of shutting down the system cleanly).
- The Journal:
+ Journal:
* systemd-journald can now forward journal entries to a socket
(AF_INET, AF_INET6, AF_UNIX, or AF_VSOCK). The socket can be
specified in journald.conf via a new option ForwardAddress= or via
- the 'journald.forward_address' credential.
-
- * systemd-journal-remote now also accepts AF_VSOCK and AF_UNIX sockets
- (so it can be used to receive entries forwarded by systemd-journald).
+ the 'journald.forward_address' credential. Log records are sent in
+ the Journal Export Format. A related setting MaxLevelSocket= has been
+ added to control the maximum log levels for the messages sent to this
+ socket.
* systemd-vmspawn gained a new --forward-journal= option to forward the
virtual machine's journal entries to the host. This is done over a
* journalctl gained a new --list-namespaces option.
+ * systemd-journal-remote now also accepts AF_VSOCK and AF_UNIX sockets
+ (so it can be used to receive entries forwarded by systemd-journald).
+
* systemd-journal-gatewayd allows restricting the time range of
- retrieved entries with realtime=[<since>]:[<until>].
+ retrieved entries with a new "realtime=[<since>]:[<until>]" URL
+ parameter.
+
+ * systemd-cat gained a new option --namespace= to specify the target
+ journal namespace to which the output shall be connected.
+
+ * systemd-bsod gained a new option --tty= to specify the output TTY
Device Management:
- * Udev now creates symlinks that combine by-path and by-{label,uuid}
+ * /dev/ now contains symlinks that combine by-path and by-{label,uuid}
information:
- /dev/disk/by-path/<path>/by-<label|uuid|…>/<label|uuid|…>.
+
+ /dev/disk/by-path/<path>/by-<label|uuid|…>/<label|uuid|…>
+
This allows distinguishing partitions with identical contents on
multiple storage devices. This is useful, for example, when copying
raw disk contents between devices.
- * Udev now creates persistent /dev/media/by-path symlinks for media
- controllers. For example, the uvcvideo driver may create /dev/media0
- which will be linked as
+ * systemd-udevd now creates persistent /dev/media/by-path/ symlinks for
+ media controllers. For example, the uvcvideo driver may create
+ /dev/media0 which will be linked as
/dev/media/by-path/pci-0000:04:00.3-usb-0:1:1.0-media-controller.
+ * A new unit systemd-udev-load-credentials.service has been added
+ to pick up udev.conf drop-ins and udev rules from credentials.
+
* An allowlist/denylist may be specified to filter which sysfs
attributes are used when crafting network interface names. Those
- lists are stored as HWDB entries
+ lists are stored as hwdb entries
ID_NET_NAME_ALLOW_<sysfsattr>=0|1
and
ID_NET_NAME_ALLOW=0|1.
+
The goal is to avoid unexpected changes to interface names when the
kernel is updated and new sysfs attributes become visible.
* A new unit tpm2.target has been added to provide a synchronization
- point for units which expect the TPM hardware to be available.
+ point for units which expect the TPM hardware to be available. A new
+ generator "systemd-tpm2-generator" has been added that will insert
+ this target whenever it detects that the firmware has initialized a
+ TPM, but Linux hasn't loaded a driver for it yet.
* systemd-backlight now properly supports numbered devices which the
kernel creates to avoid collisions in the leds subsystem.
- * systemd-hwdb update operation can be disabled with environment
+ * systemd-hwdb update operation can be disabled with a new environment
variable SYSTEMD_HWDB_UPDATE_BYPASS=1.
- * systemd-logind gained a new org.freedesktop.login1.Manager.Sleep()
- method that automatically redirects to SuspendThenHibernate(),
- Suspend(), HybridSleep(), or Hibernate(), depending on what is
- supported and configured, a new configuration setting SleepOperation=,
- and an accompanying helper method
- org.freedesktop.login1.Manager.CanSleep() and property
- org.freedesktop.login1.Manager.SleepOperation.
+ systemd-hostnamed:
- 'systemctl sleep' calls the new method to automatically put the
- machine to sleep in the most appropriate way.
+ * systemd-hostnamed now exposes the machine ID and boot ID via
+ D-Bus. It also exposes the hosts AF_VSOCK CID, if available.
- * systemd-hostnamed now exposes the machine ID and boot ID via D-Bus.
+ * systemd-hostnamed now provides a basic Varlink interface.
- * systemd-hostnamed now provides a Varlink interface.
-
- * systemd-hostnamed exports the data in os-release(5) and
+ * systemd-hostnamed exports the full data in os-release(5) and
machine-info(5) via D-Bus and Varlink.
+ * hostnamectl now shows the system's product UUID and hardware serial
+ number if known.
+
Network Management:
- * systemd-networkd now provides a Varlink interface.
+ * systemd-networkd now provides a basic Varlink interface.
- * systemd-networkd's proxy support gained a new option to configure
- a private VLAN variant of the proxy ARP supported by the kernel
- under the name IPv4ProxyARPPrivateVLAN=.
+ * systemd-networkd's ARP proxy support gained a new option to configure
+ a private VLAN variant of the proxy ARP supported by the kernel under
+ the name IPv4ProxyARPPrivateVLAN=.
* systemd-networkd now exports the NamespaceId and NamespaceNSID
- properties via D-Bus and Varlink.
+ properties via D-Bus and Varlink. (which expose the inode and NSID of
+ the network namespace the networkd instance manages)
* systemd-networkd now supports IPv6RetransmissionTimeSec= and
UseRetransmissionTime= settings in .network files to configure
retransmission time for IPv6 neighbor solicitation messages.
- * networkctl gained new verbs 'mask' and 'unmask'.
+ * networkctl gained new verbs 'mask' and 'unmask' for masking networkd
+ configuration files such as .network files.
* 'networkctl edit --runtime' allows editing volatile configuration
under /run/systemd/network/.
* The implementation behind TTLPropagate= network setting has been
removed and the setting is now ignored.
- * systemd-network-generator will now pick up .netdev/.link/.network
- configuration from credentials.
+ * systemd-network-generator will now pick up .netdev/.link/.network/
+ networkd.conf configuration from system credentials.
- * systemd-networkd will now pick up wireguard configuration from
+ * systemd-networkd will now pick up wireguard secrets from
credentials.
- * systemd-ssh-proxy is a new SSH client plugin that allows connecting
- to AF_SOCK or AF_UNIX sockets.
+ * systemd-networkd's Varlink API now supports enumerating LLDP peers.
+
+ * .link files now support new Property=, ImportProperty=,
+ UnsetProperty= fields for setting udev properties on a link.
+
+ * The various .link files that systemd ships for interfaces that are
+ supposed to be managed by systemd-networkd only now carry a
+ ID_NET_MANAGED_BY=io.systemd.Network udev property ensuring that
+ other network management solutions honouring this udev property do
+ not come into conflict with networkd, trying to manage these
+ interfaces.
+
+ * .link files now support a new ReceivePacketSteeringCPUMask= setting
+ for configuring which CPUs to steer incoming packets to.
+
+ systemd-nspawn:
* systemd-nspawn now provides a /run/systemd/nspawn/unix-export/
directory where the container payload can expose AF_UNIX sockets to
allow them them to be accessed from outside.
- * systemd-nspawn will tint the background for container output.
- This can be controller with the new --backgroup= option.
+ * systemd-nspawn will tint the terminal background for containers in a
+ blueish color. This can be controller with the new --background=
+ switch.
- * systemd-nspawn gained support for the 'owneridmap' option for bind
+ * systemd-nspawn gained support for the 'owneridmap' option for --bind=
mounts to map the target directory owner from inside the container to
the owner of the directory bound from the host filesystem.
- * An sshd config drop-in to allow ssh keys acquired via userdbctl to be
- used for authorization.
+ * systemd-nspawn now supports moving Wi-Fi network devices into a
+ container, just like other network interfaces.
- * New generator systemd-ssh-generator can be used to bind a
- socket-activated SSH instance to a local AF_SOCK or AF_UNIX socket.
- This generator will automatically bind /run/host/unix-export/ssh.
+ systemd-resolved:
- * systemd-resolved now implements RFC 8914 EDE error codes.
+ * systemd-resolved now reads RFC 8914 EDE error codes provided by
+ upstream DNS services.
* systemd-resolved and resolvectl now support RFC 9460 SVCB and HTTPS
- records.
+ records, as well as RFC 2915 NAPTR records.
* resolvectl gained a new option --relax-single-label= to allow
- querying single-label hostnames via DNS.
-
- Systemd-boot and systemd-stub and Related Tools:
+ querying single-label hostnames via unicast DNS on a per-query basis.
+
+ * systemd-resolved's Varlink IPC interface now supports resolving
+ DNS-SD services as well as an API for resolving raw DNS RRs.
+
+ * systemd-resolved's .dnssd DNS_SD service description files now
+ support DNS-SD "subtypes" via the new SubType= setting.
+
+ * systemd-resolved's configuration may now be reloaded without
+ restarting the service. (i.e. "systemctl reload systemd-resolved" is
+ now supported)
+
+ SSH Integration:
+
+ * An sshd config drop-in to allow ssh keys acquired via userdbctl (for
+ example expose by homed accounts) to be used for authorization of
+ incoming SSH connections.
+
+ * A small new unit generator "systemd-ssh-generator" has been added. It
+ checks if the sshd binary is installed. If so, it binds it via
+ per-connection socket activation to various sockets depending on the
+ execution context:
+
+ • If the system is run in a VM providing AF_VSOCK support, it
+ automatically binds sshd to AF_VSOCK port 22.
+
+ • If the system is invoked as a full-OS container and the container
+ manager pre-mounts a directory /run/host/unix-export/, it will
+ bind sshd to an AF_UNIX socket /run/host/unix-export/ssh. The
+ idea is the container manager bind mounts the directory to an
+ appropriate place on the host as well, so that the AF_UNIX socket
+ may be used to easily connect from the host to the container.
+
+ • sshd is also bound to an AF_UNIX socket
+ /run/ssh-unix-local/socket, which may be to use ssh/sftp in a
+ "sudo"-like fashion to access resources of other local users.
+
+ • Via the kernel command line option "systemd.ssh_listen=" and the
+ system credential "ssh.listen" sshd may be bound to additional,
+ explicitly configured options, including AF_INET/AF_INET6 ports.
+
+ In particular the first two mechanisms should make dealing with local
+ VMs and full OS containers a lot easier, as SSH connections will
+ *just* *work* from the host – even if no networking is available
+ whatsoever.
+
+ systemd-ssh-generator optionally generates a per-connection
+ socket activation service file wrapping sshd. This is only done if
+ the distribution does not provide one on its own under the name
+ "sshd@.service". The generated unit only works correctly if the SSH
+ privilege separation ("privsep") directory exists. Unfortunately
+ distributions vary wildly where they place this directory. An
+ incomprehensive list:
+
+ • /usr/share/empty.sshd/ (new fedora)
+ • /var/empty/
+ • /var/empty/sshd/
+ • /run/sshd/ (debian/ubuntu?)
+
+ If the SSH privsep directory is placed below /var/ or /run/ care
+ needs to be taken that the directory is created automatically at boot
+ if needed, since these directories possibly or always come up
+ empty. This can be done via a tmpfiles.d/ drop-in. You may use the
+ "sshdprivsepdir" meson option provided by systemd to configure the
+ directory, in case you want systemd to create the directory as needed
+ automatically, if your distribution does not cover this natively.
+
+ Recommendations to distributions, in order to make things just work:
+
+ • Please provide a per-connection SSH service file under the name
+ "sshd@.service".
+
+ • Please move the SSH privsep dir into /usr/ (so that it is truly
+ immutable on image-based operating systems, is strictly under
+ package manager control, and never requires recreation if the
+ system boots up with an empty /run/ or /var/).
+
+ • As an extension of this: please consider following Fedora's lead
+ here, and use /usr/share/empty.sshd/ to minimize needless
+ differences between distributions.
+
+ • If your distribution insists on placing the directory in /var/ or
+ /run/ then please at least provide a tmpfiles.d/ drop-in to
+ recreate it automatically at boot, so that the sshd binary just
+ works, regardless in which context it is called.
+
+ * A small tool "systemd-ssh-proxy" has been added, which is supposed to
+ act as counterpart to "systemd-ssh-generator". It's a small plug-in
+ for the SSH client (via ProxyCommand/ProxyUseFdpass) to allow it to
+ connect to AF_VSOCK or AF_UNIX sockets. Example: "ssh vsock/4711"
+ connects to a local VM with cid 4711, or "ssh
+ unix/run/ssh-unix-local/socket" to connect to the local host via the
+ AF_UNIX socket /run/ssh-unix-local/socket.
+
+ systemd-boot and systemd-stub and Related Tools:
* TPM 1.2 PCR measurement support has been removed from systemd-stub.
TPM 1.2 is obsolete and – due to the (by today's standards) weak
of systemd's codebase never supported TPM 1.2, the support has now
been removed from systemd-stub as well.
- * Confexts are loaded by systemd-stub from the ESP as well.
+ * systemd-stub will now measure its payload via the new EFI
+ Confidential Computing APIs (CC), in addition to the pre-existing
+ measurements to TPM.
+
+ * confexts are loaded by systemd-stub from the ESP as well.
* The pcrlock policy is saved in an unencrypted credential file
"pcrlock.<entry-token>.cred" under XBOOTLDR/ESP in the
* systemd-pcrlock gained an --entry-token= option to configure the
entry-token.
- * systemd-pcrlock now provides a Varlink interface and can be
- run as a daemon via a template unit.
+ * systemd-pcrlock now provides a basic Varlink interface and can be run
+ as a daemon via a template unit.
- * bootctl now provides a Varlink interface and can be run as a daemon
- via a template unit.
+ * bootctl now provides a basic Varlink interface and can be run as a
+ daemon via a template unit.
+
+ * systemd-measure gained new options --certificate=, --private-key=,
+ and --private-key-source= to allow using OpenSSL's "engines" or
+ "providers" as the signing mechanism to use when creating signed
+ TPM2 PCR measurement values.
* ukify gained support for signing of PCR signatures via OpenSSL's
engines and providers.
* ukify now supports zboot kernels.
- Command-line tools:
+ * systemd-boot now supports passing additional kernel command line
+ switches to invoked kernels via an SMBIOS Type #11 string
+ "io.systemd.boot.kernel-cmdline-extra". This is similar to the
+ pre-existing support for this in systemd-stub, but also applies to
+ Type #1 Boot Loader Specification Entries.
+
+ * systemd-boot's automatic SecureBoot enrollment support gained support
+ for enrolling "dbx" too (Previously, only db/KEK/PK enrollment was
+ supported). It also now supports UEFI "Custom" mode.
+
+ systemd-run/run0:
* systemd-run is now a multi-call binary. When invoked as 'run0', it
provides as interface similar to 'sudo', with all arguments starting
- at the first non-option parameter being treated the command to
- invoke as root. Unlike 'sudo' and similar tools, it does not make use
- of setuid binaries or other privilege escalation methods, but instead
+ at the first non-option parameter being treated the command to invoke
+ as root. Unlike 'sudo' and similar tools, it does not make use of
+ setuid binaries or other privilege escalation methods, but instead
runs the specified command as a transient unit, which is started by
the system service manager, so privileges are dropped, rather than
- gained, thus implementing a much more robust and safe security model.
+ gained, thus implementing a much more robust and safe security
+ model. As usual, authorization is managed via Polkit.
+
+ * systemd-run/run0 will now tint the terminal background on supported
+ terminals: in a reddish tone when invoking a root service, in a
+ yellowish tone otherwise. This may be controlled and turned off via
+ the new --background= switch.
* systemd-run gained a new option '--ignore-failure' to suppress
command failures.
- * systemd-creds gained new options --user/--uid=.
+ Command-line tools:
* 'systemctl edit --stdin' allows creation of unit files and drop-ins
with contents supplied via standard input. This is useful when creating
* resolvectl now supports -j/--json= for --type=.
+ * systemd-tmpfiles gained a new option --dry-run to print what would be
+ done without actually taking action.
+
+ * varlinkctl gained a new --collect switch to collect all responses of
+ a method call that supports multiple replies and turns it into a
+ single JSON array.
+
+ * systemd-dissect gained a new --make-archive option to generate an
+ archive file (tar.gz and similar) from a disk image.
+
+ systemd-vmspawn:
+
* systemd-vmspawn gained a new --firmware= option to configure or list
firmware definitions for Qemu, a new --tpm= option to enable or
disable the use of a software TPM, a new --linux= option to specify a
* A new systemd-vmspawn@.service can be used to launch systemd-vmspawn
as a service.
- * varlinkctl gained support for the "ssh:" transport. This requires
- OpenSSH 9.4 or newer.
+ * systemd-vmspawn gained the new --console= and --background= switches
+ that control how to interact with the VM. As before, by default an
+ interactive terminal interface is provided, but now with a background
+ tinted with a greenish hue.
- * varlinkctl gained a new --collect switch to collect all responses of
- a method call emitted in JSON_SEQ mode and turn them into normal
- JSON.
+ * systemd-vmspawn can now register its VMs with systemd-machined,
+ controlled via the --register= switch.
- * systemd-sysext gained support for mutable system extensions, where a
- writeable upperdir is stored under /var/lib/extensions.mutable/, and
- a new --mutable option to configure this behaviour.
+ * machinectl's start command (and related) can now invoke images either
+ as containers via `systemd-nspawn` (switch is --runner=nspawn, the
+ default) or as VMs via `systemd-vmspawn` (switch is --runner=vmspawn,
+ or short -V).
- * systemd-dissect gained a new --make-archive-option to generate an
- archive file from a disk image.
+ * systemd-vmspawn now supports two switches --pass-ssh-key= and
+ --ssh-key-type= to optionally set up transient SSH keys to pass to the
+ invoked VMs in order to be able to SSH into them once booted.
+
+ systemd-repart:
* systemd-repart gained new options --generate-fstab= and
- --generate-crypttab= to write the fstab and crypttab files.
+ --generate-crypttab= to write out fstab and crypttab files matching the
+ generated partitions.
* systemd-repart gained a new option --private-key-source= to allow
using OpenSSL's "engines" or "providers" as the signing mechanism to
use when creating verity signature partitions.
- * systemd-measure gained new options --certificate=, --private-key=,
- and --private-key-source= to allow using OpenSSL's "engines" or
- "providers" as the signing mechanism to use when creating signed
- TPM2 PCR measurement values.
-
- * systemd-tmpfiles gained a new option --dry-run to print what would be
- done without actually taking action.
-
- * systemd-bsod gained a new option --tty= to specify the output TTY
-
- * timedatectl and machinectl gained option '-P', an alias for
- '--value --property=…'.
-
- * Various tools that pretty-print config files will now highlight
- configuration directives.
+ * systemd-repart gained a new DefaultSubvolume= setting in repart.d/
+ drop-ins that allow configuring the default btrfs subvolume for newly
+ formatted btrfs file systems.
Libraries:
- * libsystemd gained new call sd_bus_creds_new_from_pidfd to get a
+ * libsystemd gained new call sd_bus_creds_new_from_pidfd() to get a
credentials object for a pidfd and sd_bus_creds_get_pidfd_dup() to
retrieve the pidfd from a credentials object.
+ * sd-bus' credentials logic will now also acquire peer's UNIX group
+ lists and peer's pidfd if supported and requested.
+
* RPM macro %_kernel_install_dir has been added with the path
to the directory for kernel-install plugins.
- Other:
+ * The liblz4, libzstd, liblzma, libkmod, libgcrypt dependencies have
+ been changed from regular shared library dependencies into dlopen()
+ based ones.
+
+ * The sd-journal API gained a new call
+ sd_journal_stream_fd_with_namespace() which is just like
+ sd_journal_stream_fd() but creates a log stream targeted at a
+ specific log namespace.
+
+ systemd-cryptsetup/systemd-cryptenroll:
+
+ * systemd-cryptenroll can now enroll directly with a PKCS11 public key
+ (instead of a certificate).
+
+ * systemd-cryptsetup/systemd-cryptenroll now may lock a disk against a
+ PKCS#11 provided EC key (before it only supported RSA).
+
+ * systemd-cryptsetup gained support for crypttab option
+ link-volume-key= to link the volume key into the kernel keyring when
+ the volume is opened.
+
+ * systemd-cryptenroll will no longer enable Dictionary Attack
+ Protection (i.e. turn on NO_DA) for TPM enrollments that do not
+ involve a PIN. DA should not be necessary in that case (since key
+ entropy is high enough to make this unnecessary), but risks
+ accidental lock-out in case of unexpected PCR changes.
+
+ * systemd-cryptenroll now supports enrolling a new slot while unlocking
+ the old slot via TPM2 (previously unlocking only worked via password
+ or FIDO2).
+
+ Documentation:
+
+ * The remaining documentation that was on
+ https://freedesktop.org/wiki/Software/systemd/ has been moved to
+ https://systemd.io/.
+
+ * A new text describing the VM integration interfaces of systemd has
+ been added:
+
+ https://systemd.io/VM_INTERFACE
+
+ * The sd_notify() man page has gained an example with C code that shows
+ how to implement the interface in C without involving libsystemd.
+
+ systemd-homed, systemd-logind, systemd-userdbd:
+
+ * systemd-homed now supports unlocking of home directories when logging
+ in via SSH. Previously home directories needed to be unlocked before
+ an SSH login is attempted.
+
+ * JSON User Records have been extended with a separate public storage
+ area called "User Record Blob Directories". This is intended to store
+ the user's background image, avatar picture, and other similar items
+ which are too large to fit into the User Record itself.
+
+ systemd-homed, userdbctl, and homectl gained support for blob
+ directories. homectl gained --avatar= and --login-background= to
+ control two specific items of the blob directories.
+
+ * A new "additionalLanguages" field has been added to JSON user records
+ (as supported by systemd-homed and systemd-userdbd), which is closely
+ related to the pre-existing "preferredLanguage", and allows
+ specifying multiple additional languages for the user account. It is
+ used to initialize the $LANGUAGES environment variable when used.
+
+ * A new pair of "preferredSessionType" and "preferredSessionLauncher"
+ fields have been added to JSON user records, that may be used to
+ control which kind of desktop session to preferable activate on
+ logins of the user.
+
+ * homectl gained a new verb 'firstboot', and a new
+ systemd-homed-firstboot.service unit uses this verb to create users
+ in a first boot environment, either from system credentials or by
+ querying interactively.
* systemd-logind now supports a new "background-light" session class
which does not pull in the user@.service unit. This is intended in
- particular for cron jobs.
+ particular for lighter weight per-user cron jobs which do require any
+ per-user service manager to be around.
- systemd-logind now also supports a new "user-incomplete" session
- class for a user session that does not have a running user manager,
- but may be upgraded to a full "user" session later on. This has
- been hooked into the PAM stack to appropriately classify sessions
- while they are being started.
+ * The per-user service manager will now be tracked as a distinct "manager"
+ session type among logind sessions of each user.
- systemd-logind gained a new org.freedesktop.login1.Session.SetClass()
- method to change the session class.
-
- systemd-logind will not allow background, background-light, manager,
- and manager-early session types to take control of devices or change
- the session type.
+ * homectl now supports an --offline mode, by which certain account
+ properties can be changed without unlocking the home directory.
* systemd-logind gained a new
org.freedesktop.login1.Manager.ListSessionsEx() method that provides
additional metadata compared to ListSessions(). loginctl makes use of
this to list additional fields in list-sessions.
- * systemd-cryptenroll can now enroll directly with a PKCS11 public key
- (instead of a certificate).
+ * systemd-logind gained a new org.freedesktop.login1.Manager.Sleep()
+ method that automatically redirects to SuspendThenHibernate(),
+ Suspend(), HybridSleep(), or Hibernate(), depending on what is
+ supported and configured, a new configuration setting SleepOperation=,
+ and an accompanying helper method
+ org.freedesktop.login1.Manager.CanSleep() and property
+ org.freedesktop.login1.Manager.SleepOperation.
- * Core dumps are now retained for two weeks by default.
+ 'systemctl sleep' calls the new method to automatically put the
+ machine to sleep in the most appropriate way.
- * systemd-cryptsetup gained support for crypttab option
- link-volume-key= to enter the volume key into the kernel keyring when
- the volume is opened.
+ Credential Management:
+
+ * systemd-creds now provides a Varlink IPC API for encrypting and
+ decrypting credentials.
+
+ * systemd-creds' "tpm2-absent" key selection has been renamed to
+ "null", since that's what it actually does: "encrypt" and "sign"
+ with a fixed null key. --with-key=null should only be used in very
+ specific cases, as it provides zero integrity or confidentiality
+ protections. (i.e. it's only safe to use as fallback in environments
+ lacking both a TPM and access to the root fs to use the host
+ encryption key, or when integrity is provided some other way.)
+
+ * systemd-creds gained a new switch --allow-null. If specified, the
+ "decrypt" verb will decode encrypted credentials that use the "null"
+ key (by default this is refused, since using the "null" key defeats
+ the authenticated encryption normally done).
+
+ Suspend & Hibernate:
+
+ * The sleep.conf configuration file gained a new MemorySleepMode=
+ setting for configuring the sleep mode in more detail.
+
+ * A tiny new service systemd-hibernate-clear.service has been added
+ which clears hibernation information from the HibernateLocation EFI
+ variable, in case the resume device is gone. Normally, this variable
+ is supposed to be cleaned up by the code that initiates the resume
+ from hibernation image. But when the device is missing and that code
+ doesn't run, this service will now do the necessary work, ensuring
+ that no outdated hibernation image information remains on subsequent
+ boots.
+
+ Unprivileged User Namespaces & Mounts:
+
+ * A small new service systemd-nsresourced.service has been added. It
+ provides a Varlink IPC API that assigns a free, transiently allocated
+ 64K UID/GID range to an uninitialized user namespace a client
+ provides. It may be used to implement unprivileged container managers
+ and other programs that need dynamic user ID ranges. It also provides
+ interfaces to then delegate mount file descriptors, control groups
+ and network interfaces to user namespaces set up this way.
+
+ * A small new service systemd-mountfsd.service has been added. It
+ provides a Varlink IPC API for mounting DDI images, and returning a set
+ of mount file descriptors for it. If a user namespace fd is provided
+ as input, then the mounts are registered with the user namespace. To
+ ensure trust in the image it must provide Verity information (or
+ alternatively interactive polkit authentication is required).
+
+ * The systemd-dissect tool now can access DDIs fully unprivileged by
+ using systemd-nsresourced/systemd-mountfsd.
+
+ * If the service manager runs unprivileged (i.e. systemd --user) it now
+ supports RootImage= for accessing DDI images, also implemented via
+ the systemd-nsresourced/systemd-mountfsd.
+
+ * systemd-nspawn may now operate without privileges, if a suitable DDI
+ is provided via --image=, again implemented via
+ systemd-nsresourced/systemd-mountfsd.
+
+ Other:
+
+ * timedatectl and machinectl gained option '-P', an alias for
+ '--value --property=…'.
+
+ * Various tools that pretty-print config files will now highlight
+ configuration directives.
+
+ * varlinkctl gained support for the "ssh:" transport. This requires
+ OpenSSH 9.4 or newer.
+
+ * systemd-sysext gained support for enabling system extensions in
+ mutable fashion, where a writeable upperdir is stored under
+ /var/lib/extensions.mutable/, and a new --mutable= option to
+ configure this behaviour. An "ephemeral" mode is not also supported
+ where the mutable layer is configured to be a tmpfs that is
+ automatically released when the system extensions are reattached.
+
+ * Coredumps are now retained for two weeks by default (instead of three
+ days, as before).
* portablectl --copy= parameter gained a new 'mixed' argument, that will
result in resources owned by the OS (e.g.: portable profiles) to be linked
but resources owned by the portable image (e.g.: the unit files and the
images themselves) to be copied.
- * The remaining documentation that was on
- https://freedesktop.org/wiki/Software/systemd/ has been moved to
- https://systemd.io.
+ * systemd will now register MIME types for various of its file types
+ (e.g. journal files, DDIs, encrypted credentials …) via the XDG
+ shared-mime-info infrastructure. (Files of these types will thus be
+ recognized as their own thing in desktop file managers such as GNOME
+ Files.)
+ * systemd-dissect will now show the detected sector size of a given DDI
+ in its default output.
+
+ * systemd-portabled now generates recognizable structured log messages
+ whenever a portable service is attached or detached.
+
+ * Verity signature checking in userspace (i.e. checking against
+ /etc/verity.d/ keys) when activating DDIs can now be turned on/off
+ via a kernel command line option systemd.allow_userspace_verity= and
+ an environment variable SYSTEMD_ALLOW_USERSPACE_VERITY=.
+
+ * ext4/xfs file system quota handling has been reworked, so that
+ quotacheck and quotaon are now invoked as per-file-system templated
+ services (as opposed to single system-wide singletons), similar in
+ style to the fsck, growfs, pcrfs logic. This means file systems with
+ quota enabled can now be reasonably enabled at runtime of the system,
+ not just at boot.
+
+ * "systemd-analyze dot" will now also show BindsTo= dependencies.
+
+ * systemd-debug-generator gained the ability add in arbitrary units
+ based on them being passed in via system credentials.
+
+ * A new kernel command-line option systemd.default_debug_tty= can be
+ used to specify the TTY for the debug shell, independently of
+ enabling or disabling it.
CHANGES WITH 255:
If you are looking for support, please contact our [mailing list](https://lists.freedesktop.org/mailman/listinfo/systemd-devel), join our [IRC channel #systemd on libera.chat](https://web.libera.chat/#systemd) or [Matrix channel](https://matrix.to/#/#systemd-project:matrix.org)
Stable branches with backported patches are available in the [stable repo](https://github.com/systemd/systemd-stable).
+
+We have a security bug bounty program sponsored by the [Sovereign Tech Fund](https://www.sovereigntechfund.de/) hosted on [YesWeHack](https://yeswehack.com/programs/systemd-bug-bounty-program)
Features:
+* systemd-nspawn should get the same SSH key support that vmspawn now has.
+
* insert the new pidfs inode number as a third field into PidRef, so that
PidRef are reasonably serializable without having to pass around fds.
* use udev rule networkd ownership property to take ownership of network
interfaces nspawn creates
+* mountfsd/nsresourced
+ - userdb: maybe allow callers to map one uid to their own uid
+ - bpflsm: allow writes if resulting UID on disk would be userns' owner UID
+ - make encrypted DDIs work (password…)
+ - add API for creating a new file system from scratch (together with some
+ dm-integrity/HMAC key). Should probably work using systemd-repart (access
+ via varlink).
+ - add api to make an existing file "trusted" via dm-integry/HMAC key
+ - port: portabled
+ - port: tmpfiles, sysusers and similar
+ - lets see if we can make runtime bind mounts into unpriv nspawn work
+
* add a kernel cmdline switch (and cred?) for marking a system to be
"headless", in which case we never open /dev/console for reading, only for
writing. This would then mean: systemd-firstboot would process creds but not
* make us use dynamically fewer deps for containers in general purpose distros:
o turn into dlopen() deps:
- - kmod-libs (only when called from PID 1)
- libblkid (only in RootImage= handling in PID 1, but not elsewhere)
- libpam (only when called from PID 1)
- - bzip2 (always — gzip should probably stay static dep the way it is,
- since it's so basic and our defaults)
* seccomp: maybe use seccomp_merge() to merge our filters per-arch if we can.
Apparently kernel performance is much better with fewer larger seccomp
good idea where it might end up running inside of libsystemd.so or
similar. Hence, use TLS (i.e. `thread_local`) where appropriate, and maybe
the occasional `pthread_once()`.
+
+## Tests
+
+- Use the assertion macros from `tests.h` (`ASSERT_GE()`, `ASSERT_OK()`, ...) to
+ make sure a descriptive error is logged when an assertion fails. If no assertion
+ macro exists for your specific use case, please add a new assertion macro in a
+ separate commit.
+
+- When modifying existing tests, please convert the test to use the new assertion
+ macros from `tests.h` if it is not already using those.
service. Takes a file system path: if specified the tool will listen on an
`AF_UNIX` stream socket on the specified path in addition to whatever else it
would listen on.
+
+`systemd-mountfsd`:
+
+* `$SYSTEMD_MOUNTFSD_TRUSTED_DIRECTORIES` – takes a boolean argument. If true
+ disk images from the usual disk image directories (`/var/lib/machines/`,
+ `/var/lib/confexts/`, …) will be considered "trusted", i.e. are validated
+ with a more relaxed image policy (typically not requiring Verity signature
+ checking) than those from other directories (where Verity signature checks
+ are mandatory). If false all images are treated the same, regardless if
+ placed in the usual disk image directories or elsewhere. If not set defaults
+ to a compile time setting.
+
+* `$SYSTEMD_MOUNTFSD_IMAGE_POLICY_TRUSTED`,
+ `$SYSTEMD_MOUNTFSD_IMAGE_POLICY_UNTRUSTED` – the default image policy to
+ apply to trusted and untrusted disk images. An image is considered trusted if
+ placed in a trusted disk image directory (see above), or if suitable polkit
+ authentication was acquired. See `systemd.image-policy(7)` for the valid
+ syntax for image policy strings.
For features at a higher level, tests in `src/test/` are very strongly recommended.
If that is not possible, integration tests in `test/` are encouraged.
-Please also have a look at our list of [code quality tools](CODE_QUALITY) we have setup for systemd, to ensure our codebase stays in good shape.
+```shell
+$ git config submodule.recurse true
+$ git config fetch.recurseSubmodules on-demand
+$ git config push.recurseSubmodules no
+$ cp .git/hooks/pre-commit.sample .git/hooks/pre-commit
+$ cp tools/git-post-rewrite-hook.sh .git/hooks/post-rewrite
+```
Please always test your work before submitting a PR.
For many of the components of systemd testing is straightforward as you can simply compile systemd and run the relevant tool from the build directory.
QemuFirmware=uefi
```
+To avoid having to build a new image all the time when iterating on a patch,
+add the following to `mkosi.local.conf`:
+
+```conf
+[Host]
+RuntimeBuildSources=yes
+```
+
+After enabling this setting, the source and build directories will be mounted to
+`/work/src` and `/work/build` respectively when booting the image as a container
+or virtual machine. To build the latest changes and re-install, run
+`meson install -C /work/build --only-changed` in the container or virtual machine
+and optionally restart the daemon(s) you're working on using
+`systemctl restart <units>` or `systemctl daemon-reexec` if you're working on pid1
+or `systemctl soft-reboot` to restart everything.
+
Putting this all together, here's a series of commands for preparing a patch for systemd:
```sh
./infra/helper.py coverage --no-corpus-download systemd
```
-If you find a bug that impacts the security of systemd, please follow the guidance in [CONTRIBUTING.md](CONTRIBUTING) on how to report a security vulnerability.
+If you find a bug that impacts the security of systemd,
+please follow the guidance in [CONTRIBUTING.md](CONTRIBUTING) on how to report a security vulnerability.
For more details on building fuzzers and integrating with OSS-Fuzz, visit:
To simplify debugging systemd when testing changes using mkosi, we're going to show how to attach [VSCode](https://code.visualstudio.com/)'s debugger to an instance of systemd running in a mkosi image using QEMU.
-To allow VSCode's debugger to attach to systemd running in a mkosi image, we have to make sure it can access the virtual machine spawned by mkosi where systemd is running.
-mkosi makes this possible via a handy SSH option that makes the generated image accessible via SSH when booted.
-Thus you must build the image with `mkosi --ssh`.
-The easiest way to set the option is to create a file `mkosi.local.conf` in the root of the repository and add the following contents:
-
-```conf
-[Host]
-Ssh=yes
-RuntimeTrees=.
-```
-
-Also make sure that the SSH agent is running on your system and that you've added your SSH key to it with `ssh-add`.
-Also make sure that `virtiofsd` is installed.
-
-After rebuilding the image and booting it with `mkosi qemu`,
+To allow VSCode's debugger to attach to systemd running in a mkosi image,
+we have to make sure it can access the virtual machine spawned by mkosi where systemd is running.
+After booting the image with `mkosi qemu`,
you should now be able to connect to it by running `mkosi ssh` from the same directory in another terminal window.
Now we need to configure VSCode.
"name": "systemd",
"pipeTransport": {
"pipeProgram": "mkosi",
- "pipeArgs": [
- "-C",
- "/path/to/systemd/repo/directory/on/host/system/",
- "ssh"
- ],
+ "pipeArgs": ["-C", "${workspaceFolder}", "ssh"],
"debuggerPath": "/usr/bin/gdb"
},
"MIMode": "gdb",
"sourceFileMap": {
- "/root/src/systemd": {
+ "/work/src": {
"editorPath": "${workspaceFolder}",
"useForBreakpoints": false
},
Note however that the output generated by these commands is generally not included in the promise, unless it is documented in the man page.
Example: the output of `systemctl status` is not stable, but that of `systemctl show` is, because the former is intended to be human readable and the latter computer readable, and this is documented in the man page.
-* **The protocol spoken on the socket referred to by `$NOTIFY_SOCKET`**, as documented in [sd_notify(3)](https://www.freedesktop.org/software/systemd/man/sd_notify.html).
+* **The protocol spoken on the socket referred to by `$NOTIFY_SOCKET`**, as documented in
+ [sd_notify(3)](https://www.freedesktop.org/software/systemd/man/sd_notify.html). Note that, although using
+ libsystemd is a good choice, this protocol can also be reimplemented without external dependencies, as
+ demonstrated in the example listed in
+ [sd_notify(3)](https://www.freedesktop.org/software/systemd/man/devel/sd_notify.html#Notes)
* Some of the **"special" unit names** and their semantics.
To be precise the ones that are necessary for normal services, and not those required only for early boot and late shutdown, with very few exceptions.
EVDEV_ABS_01=::200
# HUION Kamvas 19 Pro (GT-1902)
-evdev:input:b0003v256Cp006Be0110*
- EVDEV_ABS_00=::68
- EVDEV_ABS_01=::123
+evdev:input:b0003v256Cp006B*
+ EVDEV_ABS_00=::40
+ EVDEV_ABS_01=::42
+ EVDEV_ABS_35=::40
+ EVDEV_ABS_36=::42
#########################################
# Lenovo
ACCEL_MOUNT_MATRIX=-1, 0, 0; 0, 1, 0; 0, 0, 1
# Chuwi Ubook X (CWI535)
-sensor:modalias:acpi:MXC6655*:dmi*:svnCHUWIInnovationAndTechnology*:pnUBookX:*
+sensor:modalias:acpi:MXC6655*:dmi:*:svnCHUWIInnovationAndTechnology*:pnUBookX:*
ACCEL_MOUNT_MATRIX=0, -1, 0; -1, 0, 0; 0, 0, -1
#########################################
</varlistentry>
<varlistentry>
- <term><option>--json=</option><replaceable>MODE</replaceable></term>
+ <term><option>--json=<replaceable>MODE</replaceable></option></term>
<listitem>
<para>When used with the <command>call</command> or <command>get-property</command> command, shows output
</varlistentry>
<varlistentry>
- <term><option>--expect-reply=</option><replaceable>BOOL</replaceable></term>
+ <term><option>--expect-reply=<replaceable>BOOL</replaceable></option></term>
<listitem>
<para>When used with the <command>call</command> command,
</varlistentry>
<varlistentry>
- <term><option>--auto-start=</option><replaceable>BOOL</replaceable></term>
+ <term><option>--auto-start=<replaceable>BOOL</replaceable></option></term>
<listitem>
<para>When used with the <command>call</command> or <command>emit</command> command, specifies
</varlistentry>
<varlistentry>
- <term><option>--allow-interactive-authorization=</option><replaceable>BOOL</replaceable></term>
+ <term><option>--allow-interactive-authorization=<replaceable>BOOL</replaceable></option></term>
<listitem>
<para>When used with the <command>call</command> command,
</varlistentry>
<varlistentry>
- <term><option>--timeout=</option><replaceable>SECS</replaceable></term>
+ <term><option>--timeout=<replaceable>SECS</replaceable></option></term>
<listitem>
<para>When used with the <command>call</command> command,
</varlistentry>
<varlistentry>
- <term><option>--augment-creds=</option><replaceable>BOOL</replaceable></term>
+ <term><option>--augment-creds=<replaceable>BOOL</replaceable></option></term>
<listitem>
<para>Controls whether credential data reported by
</varlistentry>
<varlistentry>
- <term><option>--watch-bind=</option><replaceable>BOOL</replaceable></term>
+ <term><option>--watch-bind=<replaceable>BOOL</replaceable></option></term>
<listitem>
<para>Controls whether to wait for the specified <constant>AF_UNIX</constant> bus socket to appear in the
</varlistentry>
<varlistentry>
- <term><option>--destination=</option><replaceable>SERVICE</replaceable></term>
+ <term><option>--destination=<replaceable>SERVICE</replaceable></option></term>
<listitem>
<para>Takes a service name. When used with the <command>emit</command> command, a signal is
<varlistentry>
<term><option>-F</option> <replaceable>FIELD</replaceable></term>
- <term><option>--field=</option><replaceable>FIELD</replaceable></term>
+ <term><option>--field=<replaceable>FIELD</replaceable></option></term>
<listitem><para>Print all possible data values the specified
field takes in matching core dump entries of the
<varlistentry>
<term><option>-o</option> <replaceable>FILE</replaceable></term>
- <term><option>--output=</option><replaceable>FILE</replaceable></term>
+ <term><option>--output=<replaceable>FILE</replaceable></option></term>
<listitem><para>Write the core to <option>FILE</option>.
</para>
</varlistentry>
<varlistentry>
- <term><option>--debugger=</option><replaceable>DEBUGGER</replaceable></term>
+ <term><option>--debugger=<replaceable>DEBUGGER</replaceable></option></term>
<listitem><para>Use the given debugger for the <command>debug</command>
command. If not given and <varname>$SYSTEMD_DEBUGGER</varname> is unset, then
<varlistentry>
<term><option>-A</option> <replaceable>ARGS</replaceable></term>
- <term><option>--debugger-arguments=</option><replaceable>ARGS</replaceable></term>
+ <term><option>--debugger-arguments=<replaceable>ARGS</replaceable></option></term>
<listitem><para>Pass the given <replaceable>ARGS</replaceable> as extra command line arguments
to the debugger. Quote as appropriate when <replaceable>ARGS</replaceable> contain whitespace.
<varlistentry>
<term><option>-D</option> <replaceable>DIR</replaceable></term>
- <term><option>--directory=</option><replaceable>DIR</replaceable></term>
+ <term><option>--directory=<replaceable>DIR</replaceable></option></term>
<listitem><para>Use the journal files in the specified <option>DIR</option>.
</para>
<!ENTITY DEFAULT_TIMEOUT "{{DEFAULT_TIMEOUT_SEC}} s">
<!ENTITY DEFAULT_USER_TIMEOUT "{{DEFAULT_USER_TIMEOUT_SEC}} s">
<!ENTITY DEFAULT_KEYMAP "{{SYSTEMD_DEFAULT_KEYMAP}}">
-<!ENTITY fedora_latest_version "38">
-<!ENTITY fedora_cloud_release "1.6">
+<!ENTITY fedora_latest_version "40">
</a>
</xsl:template>
-<xsl:template match="citerefentry[@project='wireguard']">
- <a>
- <xsl:attribute name="href">
- <xsl:text>https://git.zx2c4.com/WireGuard/about/src/tools/</xsl:text>
- <xsl:value-of select="refentrytitle"/>
- <xsl:text>.</xsl:text>
- <xsl:value-of select="manvolnum"/>
- </xsl:attribute>
- <xsl:call-template name="inline.charseq"/>
- </a>
-</xsl:template>
-
<xsl:template match="citerefentry[@project='mankier']">
<a>
<xsl:attribute name="href">
<xsl:template match="citerefentry[@project='archlinux']">
<a>
<xsl:attribute name="href">
- <xsl:text>https://www.archlinux.org/</xsl:text>
- <xsl:value-of select="refentrytitle"/>
- <xsl:text>/</xsl:text>
+ <xsl:text>https://man.archlinux.org/man/</xsl:text>
<xsl:value-of select="refentrytitle"/>
<xsl:text>.</xsl:text>
<xsl:value-of select="manvolnum"/>
- <xsl:text>.html</xsl:text>
+ <xsl:text>.en.html</xsl:text>
</xsl:attribute>
<xsl:call-template name="inline.charseq"/>
</a>
/* SPDX-License-Identifier: MIT-0 */
+#define _GNU_SOURCE 1
#include <assert.h>
#include <stdio.h>
#include <unistd.h>
-#include <sd-event.h>
+#include <systemd/sd-event.h>
int main(int argc, char **argv) {
pid_t pid = fork();
<variablelist>
<varlistentry>
- <term><option>--identity=</option><replaceable>FILE</replaceable></term>
+ <term><option>--identity=<replaceable>FILE</replaceable></option></term>
<listitem><para>Read the user's JSON record from the specified file. If passed as
<literal>-</literal> read the user record from standard input. The supplied JSON object must follow
</varlistentry>
<varlistentry>
- <term><option>--json=</option><replaceable>FORMAT</replaceable></term>
+ <term><option>--json=<replaceable>FORMAT</replaceable></option></term>
<term><option>-j</option></term>
<listitem><para>Controls whether to output the user record in JSON format, if the
</varlistentry>
<varlistentry>
- <term><option>--export-format=</option><replaceable>FORMAT</replaceable></term>
+ <term><option>--export-format=<replaceable>FORMAT</replaceable></option></term>
<term><option>-E</option></term>
<term><option>-EE</option></term>
<variablelist>
<varlistentry>
- <term><option>--real-name=</option><replaceable>NAME</replaceable></term>
+ <term><option>--real-name=<replaceable>NAME</replaceable></option></term>
<term><option>-c</option> <replaceable>NAME</replaceable></term>
<listitem><para>The real name for the user. This corresponds with the GECOS field on classic UNIX NSS
</varlistentry>
<varlistentry>
- <term><option>--realm=</option><replaceable>REALM</replaceable></term>
+ <term><option>--realm=<replaceable>REALM</replaceable></option></term>
<listitem><para>The realm for the user. The realm associates a user with a specific organization or
installation, and allows distinguishing users of the same name defined in different contexts. The
</varlistentry>
<varlistentry>
- <term><option>--email-address=</option><replaceable>EMAIL</replaceable></term>
+ <term><option>--email-address=<replaceable>EMAIL</replaceable></option></term>
<listitem><para>Takes an electronic mail address to associate with the user. On log-in the
<varname>$EMAIL</varname> environment variable is initialized from this value.</para>
</varlistentry>
<varlistentry>
- <term><option>--location=</option><replaceable>TEXT</replaceable></term>
+ <term><option>--location=<replaceable>TEXT</replaceable></option></term>
<listitem><para>Takes location specification for this user. This is free-form text, which might or
might not be usable by geo-location applications. Example: <option>--location="Berlin,
</varlistentry>
<varlistentry>
- <term><option>--icon-name=</option><replaceable>ICON</replaceable></term>
+ <term><option>--icon-name=<replaceable>ICON</replaceable></option></term>
<listitem><para>Takes an icon name to associate with the user, following the scheme defined by the <ulink
url="https://standards.freedesktop.org/icon-naming-spec/icon-naming-spec-latest.html">Icon Naming
</varlistentry>
<varlistentry>
- <term><option>--home-dir=</option><replaceable>PATH</replaceable></term>
- <term><option>-d</option><replaceable>PATH</replaceable></term>
+ <term><option>--home-dir=<replaceable>PATH</replaceable></option></term>
+ <term><option>-d<replaceable>PATH</replaceable></option></term>
<listitem><para>Takes a path to use as home directory for the user. Note that this is the directory
the user's home directory is mounted to while the user is logged in. This is not where the user's
</varlistentry>
<varlistentry>
- <term><option>--uid=</option><replaceable>UID</replaceable></term>
+ <term><option>--uid=<replaceable>UID</replaceable></option></term>
<listitem><para>Takes a preferred numeric UNIX UID to assign this user. If a user is to be created
with the specified UID and it is already taken by a different user on the local system then creation
</varlistentry>
<varlistentry>
- <term><option>--member-of=</option><replaceable>GROUP</replaceable></term>
+ <term><option>--member-of=<replaceable>GROUP</replaceable></option></term>
<term><option>-G</option> <replaceable>GROUP</replaceable></term>
<listitem><para>Takes a comma-separated list of auxiliary UNIX groups this user shall belong
</varlistentry>
<varlistentry>
- <term><option>--capability-bounding-set=</option><replaceable>CAPABILITIES</replaceable></term>
- <term><option>--capability-ambient-set=</option><replaceable>CAPABILITIES</replaceable></term>
+ <term><option>--capability-bounding-set=<replaceable>CAPABILITIES</replaceable></option></term>
+ <term><option>--capability-ambient-set=<replaceable>CAPABILITIES</replaceable></option></term>
<listitem><para>These options take a space separated list of process capabilities
(e.g. <constant>CAP_WAKE_ALARM</constant>, <constant>CAP_BLOCK_SUSPEND</constant>, …) that shall be
</varlistentry>
<varlistentry>
- <term><option>--skel=</option><replaceable>PATH</replaceable></term>
+ <term><option>--skel=<replaceable>PATH</replaceable></option></term>
<listitem><para>Takes a file system path to a directory. Specifies the skeleton directory to
initialize the home directory with. All files and directories in the specified path are copied into
</varlistentry>
<varlistentry>
- <term><option>--shell=</option><replaceable>SHELL</replaceable></term>
+ <term><option>--shell=<replaceable>SHELL</replaceable></option></term>
<listitem><para>Takes a file system path. Specifies the shell binary to execute on terminal
logins. If not specified defaults to <filename>/bin/bash</filename>.</para>
</varlistentry>
<varlistentry>
- <term><option>--setenv=</option><replaceable>VARIABLE</replaceable>[=<replaceable>VALUE</replaceable>]</term>
+ <term><option>--setenv=<replaceable>VARIABLE</replaceable>[=<replaceable>VALUE</replaceable>]</option></term>
<listitem><para>Takes an environment variable assignment to set for all user processes. May be used
multiple times to set multiple environment variables. When <literal>=</literal> and
</varlistentry>
<varlistentry>
- <term><option>--timezone=</option><replaceable>TIMEZONE</replaceable></term>
+ <term><option>--timezone=<replaceable>TIMEZONE</replaceable></option></term>
<listitem><para>Takes a time zone location name that sets the timezone for the specified user. When
the user logs in the <varname>$TZ</varname> environment variable is initialized from this
</varlistentry>
<varlistentry>
- <term><option>--language=</option><replaceable>LANG</replaceable></term>
+ <term><option>--language=<replaceable>LANG</replaceable></option></term>
<listitem><para>Takes a comma- or colon-separated list of languages preferred by the user, ordered
by descending priority. The <varname>$LANG</varname> and <varname>$LANGUAGE</varname> environment
</varlistentry>
<varlistentry>
- <term><option>--ssh-authorized-keys=</option><replaceable>KEYS</replaceable></term>
+ <term><option>--ssh-authorized-keys=<replaceable>KEYS</replaceable></option></term>
<listitem><para>Either takes a SSH authorized key line to associate with the user record or a
<literal>@</literal> character followed by a path to a file to read one or more such lines from. SSH
keys configured this way are made available to SSH to permit access to this home directory and user
</varlistentry>
<varlistentry>
- <term><option>--pkcs11-token-uri=</option><replaceable>URI</replaceable></term>
+ <term><option>--pkcs11-token-uri=<replaceable>URI</replaceable></option></term>
<listitem><para>Takes an RFC 7512 PKCS#11 URI referencing a security token (e.g. YubiKey or PIV
smartcard) that shall be able to unlock the user account. The security token URI should reference a
security token with exactly one pair of X.509 certificate and private key. A random secret key is
</varlistentry>
<varlistentry>
- <term><option>--fido2-credential-algorithm=</option><replaceable>STRING</replaceable></term>
+ <term><option>--fido2-credential-algorithm=<replaceable>STRING</replaceable></option></term>
<listitem><para>Specify COSE algorithm used in credential generation. The default value is
<literal>es256</literal>. Supported values are <literal>es256</literal>, <literal>rs256</literal>
and <literal>eddsa</literal>.</para>
</varlistentry>
<varlistentry>
- <term><option>--fido2-device=</option><replaceable>PATH</replaceable></term>
+ <term><option>--fido2-device=<replaceable>PATH</replaceable></option></term>
<listitem><para>Takes a path to a Linux <literal>hidraw</literal> device
(e.g. <filename>/dev/hidraw1</filename>), referring to a FIDO2 security token implementing the
</varlistentry>
<varlistentry>
- <term><option>--fido2-with-client-pin=</option><replaceable>BOOL</replaceable></term>
+ <term><option>--fido2-with-client-pin=<replaceable>BOOL</replaceable></option></term>
<listitem><para>When enrolling a FIDO2 security token, controls whether to require the user to enter
a PIN when unlocking the account (the FIDO2 <literal>clientPin</literal> feature). Defaults to
</varlistentry>
<varlistentry>
- <term><option>--fido2-with-user-presence=</option><replaceable>BOOL</replaceable></term>
+ <term><option>--fido2-with-user-presence=<replaceable>BOOL</replaceable></option></term>
<listitem><para>When enrolling a FIDO2 security token, controls whether to require the user to
verify presence (tap the token, the FIDO2 <literal>up</literal> feature) when unlocking the account.
</varlistentry>
<varlistentry>
- <term><option>--fido2-with-user-verification=</option><replaceable>BOOL</replaceable></term>
+ <term><option>--fido2-with-user-verification=<replaceable>BOOL</replaceable></option></term>
<listitem><para>When enrolling a FIDO2 security token, controls whether to require user verification
when unlocking the account (the FIDO2 <literal>uv</literal> feature). Defaults to
</varlistentry>
<varlistentry>
- <term><option>--recovery-key=</option><replaceable>BOOL</replaceable></term>
+ <term><option>--recovery-key=<replaceable>BOOL</replaceable></option></term>
<listitem><para>Accepts a boolean argument. If enabled a recovery key is configured for the
account. A recovery key is a computer generated access key that may be used to regain access to an
</varlistentry>
<varlistentry>
- <term><option>--blob=</option><replaceable>PATH</replaceable></term>
+ <term><option>--blob=<replaceable>PATH</replaceable></option></term>
<term><option>-b</option> <replaceable>PATH</replaceable></term>
- <term><option>--blob=</option><replaceable>FILENAME</replaceable>=<replaceable>PATH</replaceable></term>
+ <term><option>--blob=<replaceable>FILENAME</replaceable>=<replaceable>PATH</replaceable></option></term>
<term><option>-b</option> <replaceable>FILENAME</replaceable>=<replaceable>PATH</replaceable></term>
<listitem><para>Accepts either a directory path, or a file name followed by a file path. If just a
</varlistentry>
<varlistentry>
- <term><option>--avatar=</option><replaceable>PATH</replaceable></term>
- <term><option>--login-background=</option><replaceable>PATH</replaceable></term>
+ <term><option>--avatar=<replaceable>PATH</replaceable></option></term>
+ <term><option>--login-background=<replaceable>PATH</replaceable></option></term>
<listitem><para>Accept a file path. If set, the specified file is used to overwrite the
corresponding file in the user's blob directory. If blank, the corresponding file is deleted
from the blob directory. Essentially, these options are shortcuts to
- <option>--blob=</option><replaceable>FILENAME</replaceable>=<replaceable>PATH</replaceable>
+ <option>--blob=<replaceable>FILENAME</replaceable>=<replaceable>PATH</replaceable></option>
for the known filenames defined in
<ulink url="https://systemd.io/USER_RECORD_BLOB_DIRS">User Record Blob Directories</ulink>.</para>
</varlistentry>
<varlistentry>
- <term><option>--locked=</option><replaceable>BOOLEAN</replaceable></term>
+ <term><option>--locked=<replaceable>BOOLEAN</replaceable></option></term>
<listitem><para>Takes a boolean argument. Specifies whether this user account shall be locked. If
true logins into this account are prohibited, if false (the default) they are permitted (of course,
</varlistentry>
<varlistentry>
- <term><option>--not-before=</option><replaceable>TIMESTAMP</replaceable></term>
- <term><option>--not-after=</option><replaceable>TIMESTAMP</replaceable></term>
+ <term><option>--not-before=<replaceable>TIMESTAMP</replaceable></option></term>
+ <term><option>--not-after=<replaceable>TIMESTAMP</replaceable></option></term>
<listitem><para>These options take a timestamp string, in the format documented in
<citerefentry><refentrytitle>systemd.time</refentrytitle><manvolnum>7</manvolnum></citerefentry> and
</varlistentry>
<varlistentry>
- <term><option>--rate-limit-interval=</option><replaceable>SECS</replaceable></term>
- <term><option>--rate-limit-burst=</option><replaceable>NUMBER</replaceable></term>
+ <term><option>--rate-limit-interval=<replaceable>SECS</replaceable></option></term>
+ <term><option>--rate-limit-burst=<replaceable>NUMBER</replaceable></option></term>
<listitem><para>Configures a rate limit on authentication attempts for this user. If the user
attempts to authenticate more often than the specified number, on a specific system, within the
</varlistentry>
<varlistentry>
- <term><option>--password-hint=</option><replaceable>TEXT</replaceable></term>
+ <term><option>--password-hint=<replaceable>TEXT</replaceable></option></term>
<listitem><para>Takes a password hint to store alongside the user record. This string is stored
accessible only to privileged users and the user itself and may not be queried by other users.
</varlistentry>
<varlistentry>
- <term><option>--enforce-password-policy=</option><replaceable>BOOL</replaceable></term>
+ <term><option>--enforce-password-policy=<replaceable>BOOL</replaceable></option></term>
<term><option>-P</option></term>
<listitem><para>Takes a boolean argument. Configures whether to enforce the system's password policy
</varlistentry>
<varlistentry>
- <term><option>--password-change-now=</option><replaceable>BOOL</replaceable></term>
+ <term><option>--password-change-now=<replaceable>BOOL</replaceable></option></term>
<listitem><para>Takes a boolean argument. If true the user is asked to change their password on next
login.</para>
</varlistentry>
<varlistentry>
- <term><option>--password-change-min=</option><replaceable>TIME</replaceable></term>
- <term><option>--password-change-max=</option><replaceable>TIME</replaceable></term>
- <term><option>--password-change-warn=</option><replaceable>TIME</replaceable></term>
- <term><option>--password-change-inactive=</option><replaceable>TIME</replaceable></term>
+ <term><option>--password-change-min=<replaceable>TIME</replaceable></option></term>
+ <term><option>--password-change-max=<replaceable>TIME</replaceable></option></term>
+ <term><option>--password-change-warn=<replaceable>TIME</replaceable></option></term>
+ <term><option>--password-change-inactive=<replaceable>TIME</replaceable></option></term>
<listitem><para>Each of these options takes a time span specification as argument (in the syntax
documented in
</varlistentry>
<varlistentry>
- <term><option>--disk-size=</option><replaceable>BYTES</replaceable></term>
+ <term><option>--disk-size=<replaceable>BYTES</replaceable></option></term>
<listitem><para>Either takes a size in bytes as argument (possibly using the usual K, M, G, …
suffixes for 1024 base values), a percentage value, or the special strings <literal>min</literal> or
<literal>max</literal>, and configures the disk space to assign to the user. If a percentage value is
</varlistentry>
<varlistentry>
- <term><option>--access-mode=</option><replaceable>MODE</replaceable></term>
+ <term><option>--access-mode=<replaceable>MODE</replaceable></option></term>
<listitem><para>Takes a UNIX file access mode written in octal. Configures the access mode of the
home directory itself. Note that this is only used when the directory is first created, and the user
</varlistentry>
<varlistentry>
- <term><option>--umask=</option><replaceable>MASK</replaceable></term>
+ <term><option>--umask=<replaceable>MASK</replaceable></option></term>
<listitem><para>Takes the access mode mask (in octal syntax) to apply to newly created files and
directories of the user ("umask"). If set this controls the initial umask set for all login sessions of
</varlistentry>
<varlistentry>
- <term><option>--nice=</option><replaceable>NICE</replaceable></term>
+ <term><option>--nice=<replaceable>NICE</replaceable></option></term>
<listitem><para>Takes the numeric scheduling priority ("nice level") to apply to the processes of the user at login
time. Takes a numeric value in the range -20 (highest priority) to 19 (lowest priority).</para>
</varlistentry>
<varlistentry>
- <term><option>--rlimit=</option><replaceable>LIMIT</replaceable>=<replaceable>VALUE</replaceable><optional>:<replaceable>VALUE</replaceable></optional></term>
+ <term><option>--rlimit=<replaceable>LIMIT</replaceable>=<replaceable>VALUE</replaceable><optional>:<replaceable>VALUE</replaceable></optional></option></term>
<listitem><para>Allows configuration of resource limits for processes of this user, see <citerefentry
project='man-pages'><refentrytitle>getrlimit</refentrytitle><manvolnum>2</manvolnum></citerefentry>
</varlistentry>
<varlistentry>
- <term><option>--tasks-max=</option><replaceable>TASKS</replaceable></term>
+ <term><option>--tasks-max=<replaceable>TASKS</replaceable></option></term>
<listitem><para>Takes a non-zero unsigned integer as argument. Configures the maximum number of tasks
(i.e. threads, where each process is at least one thread) the user may have at any given time. This
</varlistentry>
<varlistentry>
- <term><option>--memory-high=</option><replaceable>BYTES</replaceable></term>
- <term><option>--memory-max=</option><replaceable>BYTES</replaceable></term>
+ <term><option>--memory-high=<replaceable>BYTES</replaceable></option></term>
+ <term><option>--memory-max=<replaceable>BYTES</replaceable></option></term>
<listitem><para>Set a limit on the memory a user may take up on a system at any given time in bytes
(the usual K, M, G, … suffixes are supported, to the base of 1024). This includes all memory used by
</varlistentry>
<varlistentry>
- <term><option>--cpu-weight=</option><replaceable>WEIGHT</replaceable></term>
- <term><option>--io-weight=</option><replaceable>WEIGHT</replaceable></term>
+ <term><option>--cpu-weight=<replaceable>WEIGHT</replaceable></option></term>
+ <term><option>--io-weight=<replaceable>WEIGHT</replaceable></option></term>
<listitem><para>Set CPU and IO scheduling weights of the processes of the user, including those of
processes forked off by the user that changed user credentials. Takes a numeric value in the range
</varlistentry>
<varlistentry>
- <term><option>--storage=</option><replaceable>STORAGE</replaceable></term>
+ <term><option>--storage=<replaceable>STORAGE</replaceable></option></term>
<listitem><para>Selects the storage mechanism to use for this home directory. Takes one of
<literal>luks</literal>, <literal>fscrypt</literal>, <literal>directory</literal>,
</varlistentry>
<varlistentry>
- <term><option>--image-path=</option><replaceable>PATH</replaceable></term>
+ <term><option>--image-path=<replaceable>PATH</replaceable></option></term>
<listitem><para>Takes a file system path. Configures where to place the user's home directory. When
LUKS2 storage is used refers to the path to the loopback file, otherwise to the path to the home
</varlistentry>
<varlistentry>
- <term><option>--drop-caches=</option><replaceable>BOOL</replaceable></term>
+ <term><option>--drop-caches=<replaceable>BOOL</replaceable></option></term>
<listitem><para>Automatically flush OS file system caches on logout. This is useful in combination
with the fscrypt storage backend to ensure the OS does not keep decrypted versions of the files and
</varlistentry>
<varlistentry>
- <term><option>--fs-type=</option><replaceable>TYPE</replaceable></term>
+ <term><option>--fs-type=<replaceable>TYPE</replaceable></option></term>
<listitem><para>When LUKS2 storage is used configures the file system type to use inside the home
directory LUKS2 container. One of <literal>btrfs</literal>, <literal>ext4</literal>,
</varlistentry>
<varlistentry>
- <term><option>--luks-discard=</option><replaceable>BOOL</replaceable></term>
+ <term><option>--luks-discard=<replaceable>BOOL</replaceable></option></term>
<listitem><para>When LUKS2 storage is used configures whether to enable the
<literal>discard</literal> feature of the file system. If enabled the file system on top of the LUKS2
</varlistentry>
<varlistentry>
- <term><option>--luks-offline-discard=</option><replaceable>BOOL</replaceable></term>
+ <term><option>--luks-offline-discard=<replaceable>BOOL</replaceable></option></term>
<listitem><para>Similar to <option>--luks-discard=</option>, controls the trimming of the file
system. However, while <option>--luks-discard=</option> controls what happens when the home directory
</varlistentry>
<varlistentry>
- <term><option>--luks-extra-mount-options=</option><replaceable>OPTIONS</replaceable></term>
+ <term><option>--luks-extra-mount-options=<replaceable>OPTIONS</replaceable></option></term>
<listitem><para>Takes a string containing additional mount options to use when mounting the LUKS
volume. If specified, this string will be appended to the default, built-in mount
</varlistentry>
<varlistentry>
- <term><option>--luks-cipher=</option><replaceable>CIPHER</replaceable></term>
- <term><option>--luks-cipher-mode=</option><replaceable>MODE</replaceable></term>
- <term><option>--luks-volume-key-size=</option><replaceable>BYTES</replaceable></term>
- <term><option>--luks-pbkdf-type=</option><replaceable>TYPE</replaceable></term>
- <term><option>--luks-pbkdf-hash-algorithm=</option><replaceable>ALGORITHM</replaceable></term>
- <term><option>--luks-pbkdf-force-iterations=</option><replaceable>ITERATIONS</replaceable></term>
- <term><option>--luks-pbkdf-time-cost=</option><replaceable>SECONDS</replaceable></term>
- <term><option>--luks-pbkdf-memory-cost=</option><replaceable>BYTES</replaceable></term>
- <term><option>--luks-pbkdf-parallel-threads=</option><replaceable>THREADS</replaceable></term>
- <term><option>--luks-sector-size=</option><replaceable>BYTES</replaceable></term>
+ <term><option>--luks-cipher=<replaceable>CIPHER</replaceable></option></term>
+ <term><option>--luks-cipher-mode=<replaceable>MODE</replaceable></option></term>
+ <term><option>--luks-volume-key-size=<replaceable>BYTES</replaceable></option></term>
+ <term><option>--luks-pbkdf-type=<replaceable>TYPE</replaceable></option></term>
+ <term><option>--luks-pbkdf-hash-algorithm=<replaceable>ALGORITHM</replaceable></option></term>
+ <term><option>--luks-pbkdf-force-iterations=<replaceable>ITERATIONS</replaceable></option></term>
+ <term><option>--luks-pbkdf-time-cost=<replaceable>SECONDS</replaceable></option></term>
+ <term><option>--luks-pbkdf-memory-cost=<replaceable>BYTES</replaceable></option></term>
+ <term><option>--luks-pbkdf-parallel-threads=<replaceable>THREADS</replaceable></option></term>
+ <term><option>--luks-sector-size=<replaceable>BYTES</replaceable></option></term>
<listitem><para>Configures various cryptographic parameters for the LUKS2 storage mechanism. See
<citerefentry
</varlistentry>
<varlistentry>
- <term><option>--nosuid=</option><replaceable>BOOL</replaceable></term>
- <term><option>--nodev=</option><replaceable>BOOL</replaceable></term>
- <term><option>--noexec=</option><replaceable>BOOL</replaceable></term>
+ <term><option>--nosuid=<replaceable>BOOL</replaceable></option></term>
+ <term><option>--nodev=<replaceable>BOOL</replaceable></option></term>
+ <term><option>--noexec=<replaceable>BOOL</replaceable></option></term>
<listitem><para>Configures the <literal>nosuid</literal>, <literal>nodev</literal> and
<literal>noexec</literal> mount options for the home directories. By default <literal>nodev</literal>
</varlistentry>
<varlistentry>
- <term><option>--cifs-domain=</option><replaceable>DOMAIN</replaceable></term>
- <term><option>--cifs-user-name=</option><replaceable>USER</replaceable></term>
- <term><option>--cifs-service=</option><replaceable>SERVICE</replaceable></term>
- <term><option>--cifs-extra-mount-options=</option><replaceable>OPTIONS</replaceable></term>
+ <term><option>--cifs-domain=<replaceable>DOMAIN</replaceable></option></term>
+ <term><option>--cifs-user-name=<replaceable>USER</replaceable></option></term>
+ <term><option>--cifs-service=<replaceable>SERVICE</replaceable></option></term>
+ <term><option>--cifs-extra-mount-options=<replaceable>OPTIONS</replaceable></option></term>
<listitem><para>Configures the Windows File Sharing (CIFS) domain and user to associate with the home
directory/user account, as well as the file share ("service") to mount as directory. The latter is
</varlistentry>
<varlistentry>
- <term><option>--stop-delay=</option><replaceable>SECS</replaceable></term>
+ <term><option>--stop-delay=<replaceable>SECS</replaceable></option></term>
<listitem><para>Configures the time the per-user service manager shall continue to run after the all
sessions of the user ended. The default is configured in
</varlistentry>
<varlistentry>
- <term><option>--kill-processes=</option><replaceable>BOOL</replaceable></term>
+ <term><option>--kill-processes=<replaceable>BOOL</replaceable></option></term>
<listitem><para>Configures whether to kill all processes of the user on logout. The default is
configured in
</varlistentry>
<varlistentry>
- <term><option>--auto-login=</option><replaceable>BOOL</replaceable></term>
+ <term><option>--auto-login=<replaceable>BOOL</replaceable></option></term>
<listitem><para>Takes a boolean argument. Configures whether the graphical UI of the system should
automatically log this user in if possible. Defaults to off. If less or more than one user is marked
</varlistentry>
<varlistentry>
- <term><option>--session-launcher=</option><replaceable>LAUNCHER</replaceable></term>
+ <term><option>--session-launcher=<replaceable>LAUNCHER</replaceable></option></term>
<listitem><para>Takes a string argument. Configures the user's preferred session launcher
.desktop entry file (i.e. <literal>gnome</literal>, <literal>plasma</literal>, or other names that
</varlistentry>
<varlistentry>
- <term><option>--session-type=</option><replaceable>TYPE</replaceable></term>
+ <term><option>--session-type=<replaceable>TYPE</replaceable></option></term>
<listitem><para>Takes a string argument. Configures the user's preferred session type
(i.e. <literal>x11</literal>, <literal>wayland</literal>, and other values accepted by
<varlistentry>
<term><command>create</command> <replaceable>USER</replaceable></term>
- <term><command>create</command> <option>--identity=</option><replaceable>PATH</replaceable> <optional><replaceable>USER</replaceable></optional></term>
+ <term><command>create</command> <option>--identity=<replaceable>PATH</replaceable></option> <optional><replaceable>USER</replaceable></optional></term>
<listitem><para>Create a new home directory/user account of the specified name. Use the various
user record property options (as documented above) to control various aspects of the home directory
<varlistentry>
<term><command>update</command> <replaceable>USER</replaceable></term>
- <term><command>update</command> <option>--identity=</option><replaceable>PATH</replaceable> <optional><replaceable>USER</replaceable></optional></term>
+ <term><command>update</command> <option>--identity=<replaceable>PATH</replaceable></option> <optional><replaceable>USER</replaceable></optional></term>
<listitem><para>Update a home directory/user account. Use the various user record property options
(as documented above) to make changes to the account, or alternatively provide a full, updated JSON
/* SPDX-License-Identifier: MIT-0 */
+#define _GNU_SOURCE 1
#include <stdio.h>
#include <stdint.h>
-#include <sd-hwdb.h>
+#include <systemd/sd-hwdb.h>
int print_usb_properties(uint16_t vid, uint16_t pid) {
- char match[STRLEN("usb:vp") + DECIMAL_STR_MAX(uint16_t) * 2];
+ char match[128];
sd_hwdb *hwdb;
const char *key, *value;
int r;
/* Match this USB vendor and product ID combination */
- xsprintf(match, "usb:v%04Xp%04X", vid, pid);
+ snprintf(match, sizeof match, "usb:v%04Xp%04X", vid, pid);
r = sd_hwdb_new(&hwdb);
if (r < 0)
r = sd_journal_open(&j, SD_JOURNAL_LOCAL_ONLY);
if (r < 0) {
- errno = -r;
- fprintf(stderr, "Failed to open journal: %m\n");
+ fprintf(stderr, "Failed to open journal: %s\n", strerror(-r));
return 1;
}
SD_JOURNAL_FOREACH_FIELD(j, field)
int main(int argc, char *argv[]) {
int r;
sd_journal *j;
+
r = sd_journal_open(&j, SD_JOURNAL_LOCAL_ONLY);
if (r < 0) {
- errno = -r;
- fprintf(stderr, "Failed to open journal: %m\n");
+ fprintf(stderr, "Failed to open journal: %s\n", strerror(-r));
return 1;
}
SD_JOURNAL_FOREACH(j) {
r = sd_journal_get_data(j, "MESSAGE", (const void **)&d, &l);
if (r < 0) {
- errno = -r;
- fprintf(stderr, "Failed to read message field: %m\n");
+ fprintf(stderr, "Failed to read message field: %s\n", strerror(-r));
continue;
}
/* SPDX-License-Identifier: MIT-0 */
+#define _GNU_SOURCE 1
#include <poll.h>
#include <time.h>
#include <systemd/sd-journal.h>
r = sd_journal_open(&j, SD_JOURNAL_LOCAL_ONLY);
if (r < 0) {
- errno = -r;
- fprintf(stderr, "Failed to open journal: %m\n");
+ fprintf(stderr, "Failed to open journal: %s\n", strerror(-r));
return 1;
}
r = sd_journal_query_unique(j, "_SYSTEMD_UNIT");
if (r < 0) {
- errno = -r;
- fprintf(stderr, "Failed to query journal: %m\n");
+ fprintf(stderr, "Failed to query journal: %s\n", strerror(-r));
return 1;
}
SD_JOURNAL_FOREACH_UNIQUE(j, d, l)
int main(int argc, char *argv[]) {
int r;
sd_journal *j;
+
r = sd_journal_open(&j, SD_JOURNAL_LOCAL_ONLY);
if (r < 0) {
- errno = -r;
- fprintf(stderr, "Failed to open journal: %m\n");
+ fprintf(stderr, "Failed to open journal: %s\n", strerror(-r));
return 1;
}
+
for (;;) {
const void *d;
size_t l;
r = sd_journal_next(j);
if (r < 0) {
- errno = -r;
- fprintf(stderr, "Failed to iterate to next entry: %m\n");
+ fprintf(stderr, "Failed to iterate to next entry: %s\n", strerror(-r));
break;
}
if (r == 0) {
/* Reached the end, let's wait for changes, and try again */
r = sd_journal_wait(j, (uint64_t) -1);
if (r < 0) {
- errno = -r;
- fprintf(stderr, "Failed to wait for changes: %m\n");
+ fprintf(stderr, "Failed to wait for changes: %s\n", strerror(-r));
break;
}
continue;
}
r = sd_journal_get_data(j, "MESSAGE", &d, &l);
if (r < 0) {
- errno = -r;
- fprintf(stderr, "Failed to read message field: %m\n");
+ fprintf(stderr, "Failed to read message field: %s\n", strerror(-r));
continue;
}
printf("%.*s\n", (int) l, (const char*) d);
}
+
sd_journal_close(j);
return 0;
}
/* SPDX-License-Identifier: MIT-0 */
+#define _GNU_SOURCE 1
#include <errno.h>
#include <syslog.h>
#include <stdio.h>
int main(int argc, char *argv[]) {
int fd;
FILE *log;
+
fd = sd_journal_stream_fd("test", LOG_INFO, 1);
if (fd < 0) {
- errno = -fd;
- fprintf(stderr, "Failed to create stream fd: %m\n");
+ fprintf(stderr, "Failed to create stream fd: %s\n", strerror(-fd));
return 1;
}
+
log = fdopen(fd, "w");
if (!log) {
- fprintf(stderr, "Failed to create file object: %m\n");
+ fprintf(stderr, "Failed to create file object: %s\n", strerror(errno));
close(fd);
return 1;
}
<refsynopsisdiv>
<para><simplelist>
<member><filename>/etc/systemd/journald.conf</filename></member>
+ <member><filename>/run/systemd/journald.conf</filename></member>
+ <member><filename>/usr/lib/systemd/journald.conf</filename></member>
<member><filename>/etc/systemd/journald.conf.d/*.conf</filename></member>
<member><filename>/run/systemd/journald.conf.d/*.conf</filename></member>
<member><filename>/usr/lib/systemd/journald.conf.d/*.conf</filename></member>
<term><varname>resumeflags=</varname></term>
<listitem>
- <para>Enables resume from hibernation using the specified
- device and mount options. All
- <citerefentry project='man-pages'><refentrytitle>fstab</refentrytitle><manvolnum>5</manvolnum></citerefentry>-like
- paths are supported. For details, see
+ <para>Enable resume from hibernation using the specified device and timeout options. All
+ <citerefentry project='man-pages'><refentrytitle>fstab</refentrytitle><manvolnum>5</manvolnum></citerefentry>-style
+ device identifiers are supported. For details, see
<citerefentry><refentrytitle>systemd-hibernate-resume-generator</refentrytitle><manvolnum>8</manvolnum></citerefentry>.</para>
<xi:include href="version-info.xml" xpointer="v217"/>
</listitem>
</varlistentry>
+ <varlistentry>
+ <term><varname>resume_offset=</varname></term>
+
+ <listitem><para>Configure the page offset of the swap space from the resume device. For details, see
+ <citerefentry><refentrytitle>systemd-hibernate-resume-generator</refentrytitle><manvolnum>8</manvolnum></citerefentry>.
+ </para>
+
+ <xi:include href="version-info.xml" xpointer="v254"/></listitem>
+ </varlistentry>
+
<varlistentry>
<term><varname>systemd.firstboot=</varname></term>
#define _cleanup_(f) __attribute__((cleanup(f)))
-#define check(log_level, x) ({ \
- int _r = (x); \
- errno = _r < 0 ? -_r : 0; \
- sd_journal_print((log_level), #x ": %m"); \
- if (_r < 0) \
- return EXIT_FAILURE; \
- })
+static int log_error(int log_level, int error, const char *str) {
+ sd_journal_print(log_level, "%s failed: %s", str, strerror(-error));
+ return error;
+}
typedef enum LogTarget {
LOG_TARGET_JOURNAL,
return r;
if (strcmp(property, "LogLevel") == 0) {
- for (int i = 0; i < LOG_DEBUG + 1; i++)
+ int i;
+ for (i = 0; i < LOG_DEBUG + 1; i++)
if (strcmp(value, log_level_table[i]) == 0) {
o->log_level = i;
setlogmask(LOG_UPTO(i));
}
if (strcmp(property, "LogTarget") == 0) {
- for (LogTarget i = 0; i < _LOG_TARGET_MAX; i++)
+ LogTarget i;
+ for (i = 0; i < _LOG_TARGET_MAX; i++)
if (strcmp(value, log_target_table[i]) == 0) {
o->log_target = i;
return 0;
.log_target = LOG_TARGET_JOURNAL,
.syslog_identifier = "example",
};
+ int r;
/* https://man7.org/linux/man-pages/man3/setlogmask.3.html
* Programs using syslog() instead of sd_journal can use this API to cut logs
/* Acquire a connection to the bus, letting the library work out the details.
* https://www.freedesktop.org/software/systemd/man/sd_bus_default.html
*/
- check(o.log_level, sd_bus_default(&bus));
+ r = sd_bus_default(&bus);
+ if (r < 0)
+ return log_error(o.log_level, r, "sd_bus_default()");
/* Publish an interface on the bus, specifying our well-known object access
* path and public interface name.
* https://www.freedesktop.org/software/systemd/man/sd_bus_add_object.html
* https://dbus.freedesktop.org/doc/dbus-tutorial.html
*/
- check(o.log_level, sd_bus_add_object_vtable(bus, NULL,
- "/org/freedesktop/LogControl1",
- "org.freedesktop.LogControl1",
- vtable,
- &o));
+ r = sd_bus_add_object_vtable(bus, NULL,
+ "/org/freedesktop/LogControl1",
+ "org.freedesktop.LogControl1",
+ vtable,
+ &o);
+ if (r < 0)
+ return log_error(o.log_level, r, "sd_bus_add_object_vtable()");
/* By default the service is assigned an ephemeral name. Also add a fixed
* one, so that clients know whom to call.
* https://www.freedesktop.org/software/systemd/man/sd_bus_request_name.html
*/
- check(o.log_level, sd_bus_request_name(bus, "org.freedesktop.Example", 0));
+ r = sd_bus_request_name(bus, "org.freedesktop.Example", 0);
+ if (r < 0)
+ return log_error(o.log_level, r, "sd_bus_request_name()");
for (;;) {
/* https://www.freedesktop.org/software/systemd/man/sd_bus_wait.html
*/
- check(o.log_level, sd_bus_wait(bus, UINT64_MAX));
+ r = sd_bus_wait(bus, UINT64_MAX);
+ if (r < 0)
+ return log_error(o.log_level, r, "sd_bus_wait()");
/* https://www.freedesktop.org/software/systemd/man/sd_bus_process.html
*/
- check(o.log_level, sd_bus_process(bus, NULL));
+ r = sd_bus_process(bus, NULL);
+ if (r < 0)
+ return log_error(o.log_level, r, "sd_bus_process()");
}
/* https://www.freedesktop.org/software/systemd/man/sd_bus_release_name.html
*/
- check(o.log_level, sd_bus_release_name(bus, "org.freedesktop.Example"));
+ r = sd_bus_release_name(bus, "org.freedesktop.Example");
+ if (r < 0)
+ return log_error(o.log_level, r, "sd_bus_release_name()");
return 0;
}
'@0@/man/*.xml'.format(project_source_root),
'@0@/rules/meson.build'.format(meson.current_source_dir())],
depends : man_page_depends)
+
+############################################################
+
+simple_examples = files(
+ 'event-quick-child.c',
+ 'hwdb-usb-device.c',
+ 'id128-app-specific.c',
+ 'inotify-watch-tmp.c',
+ 'journal-enumerate-fields.c',
+ 'journal-iterate-foreach.c',
+ 'journal-iterate-poll.c',
+ 'journal-iterate-unique.c',
+ 'journal-iterate-wait.c',
+ 'journal-stream-fd.c',
+ 'logcontrol-example.c',
+ 'notify-selfcontained-example.c',
+ 'path-documents.c',
+ 'print-unit-path-call-method.c',
+ 'print-unit-path.c',
+ 'sd-bus-container-append.c',
+ 'sd-bus-container-read.c',
+ 'sd_bus_error-example.c',
+ 'sd_bus_service_reconnect.c',
+ 'send-unit-files-changed.c',
+ 'vtable-example.c',
+)
+
+examples = []
+foreach example : simple_examples
+ examples += [ { 'file' : example } ]
+endforeach
+
+if conf.get('HAVE_GLIB') == 1
+ examples += [
+ {
+ 'file' : files('glib-event-glue.c'),
+ 'opts' : [
+ '-I', libglib.get_variable('includedir') / 'glib-2.0',
+ '-I', libglib.get_variable('libdir') / 'glib-2.0/include',
+ ],
+ },
+ ]
+endif
+
+default_args = [
+ cc.cmd_array(),
+ '-c',
+ '-x', 'c',
+ '-pedantic',
+ '-Wall',
+ '-Werror',
+ '-Wextra',
+ '-Wno-unused-parameter',
+ '-o', '/dev/null',
+ '-I', meson.current_source_dir() / '../src',
+]
+
+std_args_in = [
+ [ '-std=c90', '-Wno-pedantic', '-Wno-variadic-macros', ],
+ [ '-std=c99', ],
+ [ '-std=c11', ],
+ [ '-std=c17', ],
+ [ '-std=c23', ],
+ [ '-std=gnu90', '-Wno-pedantic', '-Wno-variadic-macros', ],
+ [ '-std=gnu99', ],
+ [ '-std=gnu11', ],
+ [ '-std=gnu17', ],
+ [ '-std=gnu23', ],
+]
+
+std_args = []
+foreach std : std_args_in
+ if cc.has_argument(std[0])
+ std_args += [std]
+ endif
+endforeach
+
+foreach item : examples
+ foreach std : std_args
+ file = item.get('file')
+ test('cc-' + fs.stem(file) + '-' + std[0].split('=')[1],
+ env,
+ suite : 'example',
+ args : default_args + std + item.get('opts', []) + [file])
+ endforeach
+endforeach
<varlistentry>
<term>
<command>cat</command>
- <replaceable>FILE</replaceable>|<replaceable>@DEVICE</replaceable>…
+ <optional><replaceable>FILE</replaceable>|<replaceable>@DEVICE</replaceable>…</optional>
</term>
- <listitem><para>Show network configuration files. This command honors
- the <literal>@</literal> prefix in the same way as <command>edit</command>.</para>
+ <listitem>
+ <para>Show network configuration files. This command honors the <literal>@</literal> prefix in the
+ same way as <command>edit</command>. When no argument is specified,
+ <citerefentry><refentrytitle>networkd.conf</refentrytitle><manvolnum>5</manvolnum></citerefentry>
+ and its drop-in files will be shown.</para>
- <xi:include href="version-info.xml" xpointer="v254"/></listitem>
+ <xi:include href="version-info.xml" xpointer="v254"/>
+ </listitem>
</varlistentry>
<varlistentry>
</varlistentry>
<varlistentry>
- <term><option>--drop-in=</option><replaceable>NAME</replaceable></term>
+ <term><option>--drop-in=<replaceable>NAME</replaceable></option></term>
<listitem>
<para>When used with <command>edit</command>, edit the drop-in file <replaceable>NAME</replaceable>
<member><filename>/run/systemd/networkd.conf</filename></member>
<member><filename>/usr/lib/systemd/networkd.conf</filename></member>
<member><filename>/etc/systemd/networkd.conf.d/*.conf</filename></member>
+ <member><filename>/run/systemd/networkd.conf.d/*.conf</filename></member>
<member><filename>/usr/lib/systemd/networkd.conf.d/*.conf</filename></member>
</simplelist></para>
</refsynopsisdiv>
<xi:include href="version-info.xml" xpointer="v230"/>
</listitem>
</varlistentry>
+
+ <varlistentry>
+ <term><varname>UseDomains=</varname></term>
+ <listitem><para>Specifies the default value for per-network <varname>UseDomains=</varname>.
+ Takes a boolean. See for details in
+ <citerefentry><refentrytitle>systemd.network</refentrytitle><manvolnum>5</manvolnum></citerefentry>.
+ Defaults to <literal>no</literal>.</para>
+
+ <xi:include href="version-info.xml" xpointer="v256"/>
+ </listitem>
+ </varlistentry>
</variablelist>
</refsect1>
<xi:include href="version-info.xml" xpointer="v249"/></listitem>
</varlistentry>
+
+ <varlistentry>
+ <term><varname>UseDomains=</varname></term>
+ <listitem><para>As in the [DHCPv4] section.</para>
+
+ <xi:include href="version-info.xml" xpointer="v256"/></listitem>
+ </varlistentry>
+ </variablelist>
+ </refsect1>
+
+ <refsect1>
+ <title>[DHCPServer] Section Options</title>
+
+ <para>This section configures the default setting of the DHCP server. The following options are available
+ in the [DHCPServer] section:</para>
+
+ <variablelist class='network-directives'>
+ <varlistentry>
+ <term><varname>PersistLeases=</varname></term>
+ <listitem>
+ <para>Specifies the default value for per-network <varname>PersistLeases=</varname>.
+ Takes a boolean. See for details in
+ <citerefentry><refentrytitle>systemd.network</refentrytitle><manvolnum>5</manvolnum></citerefentry>.
+ Defaults to <literal>yes</literal>.</para>
+
+ <xi:include href="version-info.xml" xpointer="v256"/>
+ </listitem>
+ </varlistentry>
</variablelist>
</refsect1>
--- /dev/null
+/* SPDX-License-Identifier: MIT-0 */
+
+/* Implement the systemd notify protocol without external dependencies.
+ * Supports both readiness notification on startup and on reloading,
+ * according to the protocol defined at:
+ * https://www.freedesktop.org/software/systemd/man/latest/sd_notify.html
+ * This protocol is guaranteed to be stable as per:
+ * https://systemd.io/PORTABILITY_AND_STABILITY/ */
+
+#define _GNU_SOURCE 1
+#include <errno.h>
+#include <inttypes.h>
+#include <signal.h>
+#include <stdbool.h>
+#include <stddef.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <sys/socket.h>
+#include <sys/un.h>
+#include <time.h>
+#include <unistd.h>
+
+#define _cleanup_(f) __attribute__((cleanup(f)))
+
+static void closep(int *fd) {
+ if (!fd || *fd < 0)
+ return;
+
+ close(*fd);
+ *fd = -1;
+}
+
+static int notify(const char *message) {
+ union sockaddr_union {
+ struct sockaddr sa;
+ struct sockaddr_un sun;
+ } socket_addr = {
+ .sun.sun_family = AF_UNIX,
+ };
+ size_t path_length, message_length;
+ _cleanup_(closep) int fd = -1;
+ const char *socket_path;
+
+ /* Verify the argument first */
+ if (!message)
+ return -EINVAL;
+
+ message_length = strlen(message);
+ if (message_length == 0)
+ return -EINVAL;
+
+ /* If the variable is not set, the protocol is a noop */
+ socket_path = getenv("NOTIFY_SOCKET");
+ if (!socket_path)
+ return 0; /* Not set? Nothing to do */
+
+ /* Only AF_UNIX is supported, with path or abstract sockets */
+ if (socket_path[0] != '/' && socket_path[0] != '@')
+ return -EAFNOSUPPORT;
+
+ path_length = strlen(socket_path);
+ /* Ensure there is room for NUL byte */
+ if (path_length >= sizeof(socket_addr.sun.sun_path))
+ return -E2BIG;
+
+ memcpy(socket_addr.sun.sun_path, socket_path, path_length);
+
+ /* Support for abstract socket */
+ if (socket_addr.sun.sun_path[0] == '@')
+ socket_addr.sun.sun_path[0] = 0;
+
+ fd = socket(AF_UNIX, SOCK_DGRAM|SOCK_CLOEXEC, 0);
+ if (fd < 0)
+ return -errno;
+
+ if (connect(fd, &socket_addr.sa, offsetof(struct sockaddr_un, sun_path) + path_length) != 0)
+ return -errno;
+
+ ssize_t written = write(fd, message, message_length);
+ if (written != (ssize_t) message_length)
+ return written < 0 ? -errno : -EPROTO;
+
+ return 1; /* Notified! */
+}
+
+static int notify_ready(void) {
+ return notify("READY=1");
+}
+
+static int notify_reloading(void) {
+ /* A buffer with length sufficient to format the maximum UINT64 value. */
+ char reload_message[sizeof("RELOADING=1\nMONOTONIC_USEC=18446744073709551615")];
+ struct timespec ts;
+ uint64_t now;
+
+ /* Notify systemd that we are reloading, including a CLOCK_MONOTONIC timestamp in usec
+ * so that the program is compatible with a Type=notify-reload service. */
+
+ if (clock_gettime(CLOCK_MONOTONIC, &ts) < 0)
+ return -errno;
+
+ if (ts.tv_sec < 0 || ts.tv_nsec < 0 ||
+ (uint64_t) ts.tv_sec > (UINT64_MAX - (ts.tv_nsec / 1000ULL)) / 1000000ULL)
+ return -EINVAL;
+
+ now = (uint64_t) ts.tv_sec * 1000000ULL + (uint64_t) ts.tv_nsec / 1000ULL;
+
+ if (snprintf(reload_message, sizeof(reload_message), "RELOADING=1\nMONOTONIC_USEC=%" PRIu64, now) < 0)
+ return -EINVAL;
+
+ return notify(reload_message);
+}
+
+static int notify_stopping(void) {
+ return notify("STOPPING=1");
+}
+
+static volatile sig_atomic_t reloading = 0;
+static volatile sig_atomic_t terminating = 0;
+
+static void signal_handler(int sig) {
+ if (sig == SIGHUP)
+ reloading = 1;
+ else if (sig == SIGINT || sig == SIGTERM)
+ terminating = 1;
+}
+
+int main(int argc, char **argv) {
+ struct sigaction sa = {
+ .sa_handler = signal_handler,
+ .sa_flags = SA_RESTART,
+ };
+ int r;
+
+ /* Setup signal handlers */
+ sigemptyset(&sa.sa_mask);
+ sigaction(SIGHUP, &sa, NULL);
+ sigaction(SIGINT, &sa, NULL);
+ sigaction(SIGTERM, &sa, NULL);
+
+ /* Do more service initialization work here … */
+
+ /* Now that all the preparations steps are done, signal readiness */
+
+ r = notify_ready();
+ if (r < 0) {
+ fprintf(stderr, "Failed to notify readiness to $NOTIFY_SOCKET: %s\n", strerror(-r));
+ return EXIT_FAILURE;
+ }
+
+ while (!terminating) {
+ if (reloading) {
+ reloading = false;
+
+ /* As a separate but related feature, we can also notify the manager
+ * when reloading configuration. This allows accurate state-tracking,
+ * and also automated hook-in of 'systemctl reload' without having to
+ * specify manually an ExecReload= line in the unit file. */
+
+ r = notify_reloading();
+ if (r < 0) {
+ fprintf(stderr, "Failed to notify reloading to $NOTIFY_SOCKET: %s\n", strerror(-r));
+ return EXIT_FAILURE;
+ }
+
+ /* Do some reconfiguration work here … */
+
+ r = notify_ready();
+ if (r < 0) {
+ fprintf(stderr, "Failed to notify readiness to $NOTIFY_SOCKET: %s\n", strerror(-r));
+ return EXIT_FAILURE;
+ }
+ }
+
+ /* Do some daemon work here … */
+ sleep(5);
+ }
+
+ r = notify_stopping();
+ if (r < 0) {
+ fprintf(stderr, "Failed to report termination to $NOTIFY_SOCKET: %s\n", strerror(-r));
+ return EXIT_FAILURE;
+ }
+
+ /* Do some shutdown work here … */
+
+ return EXIT_SUCCESS;
+}
<para><function>DetachImageWithExtensions()</function> detaches a portable image from the system.
This method is a superset of <function>DetachImage()</function> with the addition of
a list of extensions as input parameter, which were overlaid on top of the main
- image via <function>AttachImageWithExtensions()</function>.
- The <varname>flag</varname> parameter is currently unused and reserved for future purposes.</para>
+ image via <function>AttachImageWithExtensions()</function>.</para>
<para><function>ReattachImage()</function> combines the effects of the
<function>AttachImage()</function> method and the <function>DetachImage()</function> method.
image. For more details on this functionality, see the <varname>MountImages=</varname> entry on
<citerefentry><refentrytitle>systemd.exec</refentrytitle><manvolnum>5</manvolnum></citerefentry>
and <citerefentry><refentrytitle>systemd-sysext</refentrytitle><manvolnum>8</manvolnum></citerefentry>.
- The <varname>flag</varname> parameter is currently unused and reserved for future purposes</para>
+ </para>
<para><function>RemoveImage()</function> removes the image with the specified name.</para>
@org.freedesktop.DBus.Property.EmitsChangedSignal("const")
readonly t FinishTimestampMonotonic = ...;
@org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+ readonly t SoftRebootStartTimestamp = ...;
+ @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+ readonly t SoftRebootStartTimestampMonotonic = ...;
+ @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
readonly t SecurityStartTimestamp = ...;
@org.freedesktop.DBus.Property.EmitsChangedSignal("const")
readonly t SecurityStartTimestampMonotonic = ...;
readonly i DefaultOOMScoreAdjust = ...;
@org.freedesktop.DBus.Property.EmitsChangedSignal("const")
readonly s CtrlAltDelBurstAction = '...';
+ @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+ readonly u SoftRebootsCount = ...;
};
interface org.freedesktop.DBus.Peer { ... };
interface org.freedesktop.DBus.Introspectable { ... };
<variablelist class="dbus-property" generated="True" extra-ref="FinishTimestampMonotonic"/>
+ <variablelist class="dbus-property" generated="True" extra-ref="SoftRebootStartTimestamp"/>
+
+ <variablelist class="dbus-property" generated="True" extra-ref="SoftRebootStartTimestampMonotonic"/>
+
<variablelist class="dbus-property" generated="True" extra-ref="SecurityStartTimestamp"/>
<variablelist class="dbus-property" generated="True" extra-ref="SecurityStartTimestampMonotonic"/>
<variablelist class="dbus-property" generated="True" extra-ref="CtrlAltDelBurstAction"/>
+ <variablelist class="dbus-property" generated="True" extra-ref="SoftRebootsCount"/>
+
<!--End of Autogenerated section-->
<refsect2>
<varname>KernelTimestamp</varname>, <varname>KernelTimestampMonotonic</varname>,
<varname>InitRDTimestamp</varname>, <varname>InitRDTimestampMonotonic</varname>,
<varname>UserspaceTimestamp</varname>, <varname>UserspaceTimestampMonotonic</varname>,
- <varname>FinishTimestamp</varname>, and <varname>FinishTimestampMonotonic</varname> encode
- <constant>CLOCK_REALTIME</constant> and <constant>CLOCK_MONOTONIC</constant> microsecond timestamps
- taken when the firmware first began execution, when the boot loader first began execution, when the
- kernel first began execution, when the initrd first began execution, when the main systemd instance
- began execution and finally, when all queued startup jobs finished execution. These values are useful
- for determining boot-time performance. Note that as monotonic time begins with the kernel startup, the
- <varname>KernelTimestampMonotonic</varname> timestamp will always be 0 and
- <varname>FirmwareTimestampMonotonic</varname> and <varname>LoaderTimestampMonotonic</varname> are to
- be read as negative values. Also, not all fields are always available, depending on the used firmware,
- boot loader or initrd implementation. In these cases the respective pairs of timestamps are both 0,
- indicating that no data is available.</para>
+ <varname>FinishTimestamp</varname>, <varname>FinishTimestampMonotonic</varname>,
+ <varname>SoftRebootStartTimestamp</varname> and <varname>SoftRebootStartTimestampMonotonic</varname>
+ encode <constant>CLOCK_REALTIME</constant> and <constant>CLOCK_MONOTONIC</constant> microsecond
+ timestamps taken when the firmware first began execution, when the boot loader first began execution,
+ when the kernel first began execution, when the initrd first began execution, when the main systemd
+ instance began execution, when all queued startup jobs finished execution and finally, when a
+ <citerefentry><refentrytitle>systemd-soft-reboot.service</refentrytitle><manvolnum>8</manvolnum></citerefentry>
+ operation first began execution. These values are useful for determining boot-time performance. Note
+ that as monotonic time begins with the kernel startup, the <varname>KernelTimestampMonotonic</varname>
+ timestamp will always be 0 and <varname>FirmwareTimestampMonotonic</varname> and
+ <varname>LoaderTimestampMonotonic</varname> are to be read as negative values. Also, not all fields
+ are always available, depending on the used firmware, boot loader or initrd implementation. In these
+ cases the respective pairs of timestamps are both 0, indicating that no data is available.</para>
<para><varname>UnitsLoadTimestamp</varname> and <varname>UnitsLoadTimestampMonotonic</varname> encode
<constant>CLOCK_REALTIME</constant> and <constant>CLOCK_MONOTONIC</constant> microseconds timestamps
<para><varname>UnitPath</varname> encodes the currently active unit file search path. It is an array of
file system paths encoded as strings.</para>
+ <para><varname>SoftRebootsCount</varname> encodes how many soft-reboots were successfully completed
+ since the last full boot. Starts at <literal>0</literal>.</para>
+
<para><varname>Virtualization</varname> contains a short ID string describing the virtualization
technology the system runs in. On bare-metal hardware this is the empty string. Otherwise, it contains
an identifier such as <literal>kvm</literal>, <literal>vmware</literal> and so on. For a full list of
<function>QueueSignalUnit()</function>,
<function>SoftReboot()</function>, and
<function>DumpUnitFileDescriptorStore()</function> were added in version 254.</para>
- <para><function>StartAuxiliaryScope()</function> was added in version 256.</para>
+ <para><function>StartAuxiliaryScope()</function>,
+ <varname>SoftRebootStartTimestamp</varname>,
+ <varname>SoftRebootStartTimestampMonotonic</varname> and
+ <varname>SoftRebootsCount</varname> were added in version 256.</para>
</refsect2>
<refsect2>
<title>Unit Objects</title>
#include <stdio.h>
#include <stdlib.h>
-#include <sd-path.h>
+#include <systemd/sd-path.h>
int main(void) {
int r;
<varlistentry>
<term><option>-p</option> <replaceable>PROFILE</replaceable></term>
- <term><option>--profile=</option><replaceable>PROFILE</replaceable></term>
+ <term><option>--profile=<replaceable>PROFILE</replaceable></option></term>
<listitem><para>When attaching an image, select the profile to use. By default the <literal>default</literal>
profile is used. For details about profiles, see below.</para>
</varlistentry>
<varlistentry>
- <term><option>--extension=</option><replaceable>PATH</replaceable></term>
+ <term><option>--extension=<replaceable>PATH</replaceable></option></term>
<listitem><para>Add an additional image <replaceable>PATH</replaceable> as an overlay on
top of <replaceable>IMAGE</replaceable> when attaching/detaching. This argument can be specified
#define MEMBER "GetUnitByPID"
static int log_error(int error, const char *message) {
- errno = -error;
- fprintf(stderr, "%s: %m\n", message);
+ fprintf(stderr, "%s: %s\n", message, strerror(-error));
return error;
}
#define MEMBER "GetUnitByPID"
static int log_error(int error, const char *message) {
- errno = -error;
- fprintf(stderr, "%s: %m\n", message);
+ fprintf(stderr, "%s: %s\n", message, strerror(-error));
return error;
}
<xi:include href="version-info.xml" xpointer="v255"/></listitem>
</varlistentry>
+ <varlistentry>
+ <term><varname>DefaultSubvolume=</varname></term>
+
+ <listitem><para>Takes an absolute path specifying the default subvolume within the new filesystem.
+ Note that this setting does not create the subvolume itself, that can be configured with
+ <varname>Subvolumes=</varname>.</para>
+
+ <para>Note that this option only takes effect if the target filesystem supports subvolumes, such as
+ <literal>btrfs</literal>.</para>
+
+ <para>Note that due to limitations of <literal>mkfs.btrfs</literal>, this option is only supported
+ when running with <option>--offline=no</option>.</para>
+
+ <xi:include href="version-info.xml" xpointer="v256"/></listitem>
+ </varlistentry>
+
<varlistentry>
<term><varname>Encrypt=</varname></term>
<varlistentry>
<term><option>-i</option> <replaceable>INTERFACE</replaceable></term>
- <term><option>--interface=</option><replaceable>INTERFACE</replaceable></term>
+ <term><option>--interface=<replaceable>INTERFACE</replaceable></option></term>
<listitem><para>Specifies the network interface to execute the query on. This may either be specified as numeric
interface index or as network interface string (e.g. <literal>en0</literal>). Note that this option has no
<varlistentry>
<term><option>-p</option> <replaceable>PROTOCOL</replaceable></term>
- <term><option>--protocol=</option><replaceable>PROTOCOL</replaceable></term>
+ <term><option>--protocol=<replaceable>PROTOCOL</replaceable></option></term>
<listitem><para>Specifies the network protocol for the query. May be one of <literal>dns</literal>
(i.e. classic unicast DNS), <literal>llmnr</literal> (<ulink
<varlistentry>
<term><option>-t</option> <replaceable>TYPE</replaceable></term>
- <term><option>--type=</option><replaceable>TYPE</replaceable></term>
+ <term><option>--type=<replaceable>TYPE</replaceable></option></term>
<term><option>-c</option> <replaceable>CLASS</replaceable></term>
- <term><option>--class=</option><replaceable>CLASS</replaceable></term>
+ <term><option>--class=<replaceable>CLASS</replaceable></option></term>
<listitem><para>When used in conjunction with the <command>query</command> command, specifies the DNS
resource record type (e.g. <constant class='dns'>A</constant>, <constant class='dns'>AAAA</constant>,
</varlistentry>
<varlistentry>
- <term><option>--service-address=</option><replaceable>BOOL</replaceable></term>
+ <term><option>--service-address=<replaceable>BOOL</replaceable></option></term>
<listitem><para>Takes a boolean parameter. If true (the default), when doing a service lookup with
<option>--service</option> the hostnames contained in the <constant class='dns'>SRV</constant>
</varlistentry>
<varlistentry>
- <term><option>--service-txt=</option><replaceable>BOOL</replaceable></term>
+ <term><option>--service-txt=<replaceable>BOOL</replaceable></option></term>
<listitem><para>Takes a boolean parameter. If true (the default), when doing a DNS-SD service lookup
with <option>--service</option> the <constant class='dns'>TXT</constant> service metadata record is
</varlistentry>
<varlistentry>
- <term><option>--cname=</option><replaceable>BOOL</replaceable></term>
+ <term><option>--cname=<replaceable>BOOL</replaceable></option></term>
<listitem><para>Takes a boolean parameter. If true (the default), DNS <constant
class='dns'>CNAME</constant> or <constant class='dns'>DNAME</constant> redirections are
</varlistentry>
<varlistentry>
- <term><option>--validate=</option><replaceable>BOOL</replaceable></term>
+ <term><option>--validate=<replaceable>BOOL</replaceable></option></term>
<listitem><para>Takes a boolean parameter; used in conjunction with <command>query</command>. If true
(the default), DNSSEC validation is applied as usual — under the condition that it is enabled for the
</varlistentry>
<varlistentry>
- <term><option>--synthesize=</option><replaceable>BOOL</replaceable></term>
+ <term><option>--synthesize=<replaceable>BOOL</replaceable></option></term>
<listitem><para>Takes a boolean parameter; used in conjunction with <command>query</command>. If true
(the default), select domains are resolved on the local system, among them
</varlistentry>
<varlistentry>
- <term><option>--cache=</option><replaceable>BOOL</replaceable></term>
+ <term><option>--cache=<replaceable>BOOL</replaceable></option></term>
<listitem><para>Takes a boolean parameter; used in conjunction with <command>query</command>. If true
(the default), lookups use the local DNS resource record cache. If false, lookups are routed to the
</varlistentry>
<varlistentry>
- <term><option>--zone=</option><replaceable>BOOL</replaceable></term>
+ <term><option>--zone=<replaceable>BOOL</replaceable></option></term>
<listitem><para>Takes a boolean parameter; used in conjunction with <command>query</command>. If true
(the default), lookups are answered from locally registered LLMNR or mDNS resource records, if
</varlistentry>
<varlistentry>
- <term><option>--trust-anchor=</option><replaceable>BOOL</replaceable></term>
+ <term><option>--trust-anchor=<replaceable>BOOL</replaceable></option></term>
<listitem><para>Takes a boolean parameter; used in conjunction with <command>query</command>. If true
(the default), lookups for DS and DNSKEY are answered from the local DNSSEC trust anchors if
</varlistentry>
<varlistentry>
- <term><option>--network=</option><replaceable>BOOL</replaceable></term>
+ <term><option>--network=<replaceable>BOOL</replaceable></option></term>
<listitem><para>Takes a boolean parameter; used in conjunction with <command>query</command>. If true
(the default), lookups are answered via DNS, LLMNR or mDNS network requests if they cannot be
</varlistentry>
<varlistentry>
- <term><option>--search=</option><replaceable>BOOL</replaceable></term>
+ <term><option>--search=<replaceable>BOOL</replaceable></option></term>
<listitem><para>Takes a boolean parameter. If true (the default), any specified single-label
hostnames will be searched in the domains configured in the search domain list, if it is
</varlistentry>
<varlistentry>
- <term><option>--legend=</option><replaceable>BOOL</replaceable></term>
+ <term><option>--legend=<replaceable>BOOL</replaceable></option></term>
<listitem><para>Takes a boolean parameter. If true (the default), column headers and meta information about the
query response are shown. Otherwise, this output is suppressed.</para>
</varlistentry>
<varlistentry>
- <term><option>--stale-data=</option><replaceable>BOOL</replaceable></term>
+ <term><option>--stale-data=<replaceable>BOOL</replaceable></option></term>
<listitem><para>Takes a boolean parameter; used in conjunction with <command>query</command>. If true
(the default), lookups are answered with stale data (expired resource records) if
</varlistentry>
<varlistentry>
- <term><option>--relax-single-label=</option><replaceable>BOOL</replaceable></term>
+ <term><option>--relax-single-label=<replaceable>BOOL</replaceable></option></term>
<listitem><para>Takes a boolean parameter; used in conjunction with <command>query</command>. If
true, rules regarding routing of single-label names are relaxed. Defaults to false. By default,
['systemd-hibernate-resume-generator', '8', [], 'ENABLE_HIBERNATE'],
['systemd-hibernate-resume.service',
'8',
- ['systemd-hibernate-resume'],
+ ['systemd-hibernate-clear.service', 'systemd-hibernate-resume'],
'ENABLE_HIBERNATE'],
['systemd-homed.service', '8', ['systemd-homed'], 'ENABLE_HOMED'],
['systemd-hostnamed.service', '8', ['systemd-hostnamed'], 'ENABLE_HOSTNAMED'],
['systemd-measure', '1', [], 'HAVE_TPM2 HAVE_BLKID HAVE_OPENSSL'],
['systemd-modules-load.service', '8', ['systemd-modules-load'], 'HAVE_KMOD'],
['systemd-mount', '1', ['systemd-umount'], ''],
+ ['systemd-mountfsd.service', '8', ['systemd-mountfsd'], 'ENABLE_MOUNTFSD'],
['systemd-network-generator.service', '8', ['systemd-network-generator'], ''],
['systemd-networkd-wait-online.service',
'8',
['systemd-networkd.service', '8', ['systemd-networkd'], 'ENABLE_NETWORKD'],
['systemd-notify', '1', [], ''],
['systemd-nspawn', '1', [], ''],
+ ['systemd-nsresourced.service',
+ '8',
+ ['systemd-nsresourced'],
+ 'ENABLE_NSRESOURCED'],
['systemd-oomd.service', '8', ['systemd-oomd'], 'ENABLE_OOMD'],
['systemd-path', '1', [], ''],
['systemd-pcrlock',
#include <systemd/sd-bus.h>
int append_strings_to_message(sd_bus_message *m, const char *const *arr) {
+ const char *s;
int r;
r = sd_bus_message_open_container(m, 'a', "s");
if (r < 0)
return r;
- for (const char *s = *arr; *s; s++) {
+ for (s = *arr; *s; s++) {
r = sd_bus_message_append(m, "s", s);
if (r < 0)
return r;
— are fully thread-safe and may be called from multiple threads in parallel.</para>
</refsect1>
+ <refsect1>
+ <title>Optional dependencies</title>
+
+ <para>Depending on which build-time options are enabled, functions that operate on
+ <structname>sd_journal</structname> objects might cause optional shared libraries to be dynamically
+ loaded via
+ <citerefentry project='man-pages'><refentrytitle>dlopen</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
+ such as decompression libraries (xz, lz4, zstd) or cryptographic libraries (gcrypt).
+ </para>
+ </refsect1>
+
<xi:include href="libsystemd-pkgconfig.xml" />
<refsect1>
#include <errno.h>
#include <string.h>
#include <unistd.h>
-#include <sd-bus.h>
+#include <systemd/sd-bus.h>
int writer_with_negative_errno_return(int fd, sd_bus_error *error) {
const char *message = "Hello, World!\n";
/* On error, initialize the error structure, and also propagate the errno
* value that write(2) set for us. */
- return sd_bus_error_set_errnof(error, errno, "Failed to write to fd %i: %m", fd);
+ return sd_bus_error_set_errnof(error, errno, "Failed to write to fd %i: %s", fd, strerror(errno));
}
#define _cleanup_(f) __attribute__((cleanup(f)))
-#define check(x) ({ \
- int _r = (x); \
- errno = _r < 0 ? -_r : 0; \
- printf(#x ": %m\n"); \
- if (_r < 0) \
- return EXIT_FAILURE; \
- })
+static int log_error(int r, const char *str) {
+ fprintf(stderr, "%s failed: %s\n", str, strerror(-r));
+ return r;
+}
typedef struct object {
const char *example;
static int setup(object *o);
static int on_disconnect(sd_bus_message *message, void *userdata, sd_bus_error *ret_error) {
- check(setup((object *)userdata));
- return 0;
+ int r;
+
+ r = setup((object *)userdata);
+ if (r < 0) {
+ object *o = userdata;
+ r = sd_event_exit(*o->event, r);
+ if (r < 0)
+ return log_error(r, "sd_event_exit()");
+ }
+
+ return 1;
}
/* Ensure the event loop exits with a clear error if acquiring the well-known
* service name fails */
static int request_name_callback(sd_bus_message *m, void *userdata, sd_bus_error *ret_error) {
+ int r;
+
if (!sd_bus_message_is_method_error(m, NULL))
return 1;
if (sd_bus_error_has_names(error, SD_BUS_ERROR_TIMEOUT, SD_BUS_ERROR_NO_REPLY))
return 1; /* The bus is not available, try again later */
- printf("Failed to request name: %s\n", error->message);
+ fprintf(stderr, "Failed to request name: %s\n", error->message);
object *o = userdata;
- check(sd_event_exit(*o->event, -sd_bus_error_get_errno(error)));
+ r = sd_event_exit(*o->event, -sd_bus_error_get_errno(error));
+ if (r < 0)
+ return log_error(r, "sd_event_exit()");
return 1;
}
static int setup(object *o) {
+ int r;
+
/* If we are reconnecting, then the bus object needs to be closed, detached
* from the event loop and recreated.
* https://www.freedesktop.org/software/systemd/man/sd_bus_detach_event.html
* https://www.freedesktop.org/software/systemd/man/sd_bus_close_unref.html
*/
if (*o->bus) {
- check(sd_bus_detach_event(*o->bus));
+ r = sd_bus_detach_event(*o->bus);
+ if (r < 0)
+ return log_error(r, "sd_bus_detach_event()");
*o->bus = sd_bus_close_unref(*o->bus);
}
* https://www.freedesktop.org/software/systemd/man/sd_bus_set_connected_signal.html
* https://www.freedesktop.org/software/systemd/man/sd_bus_start.html
*/
- check(sd_bus_new(o->bus));
- check(sd_bus_set_address(*o->bus, "unix:path=/run/dbus/system_bus_socket"));
- check(sd_bus_set_bus_client(*o->bus, 1));
- check(sd_bus_negotiate_creds(*o->bus, 1, SD_BUS_CREDS_UID|SD_BUS_CREDS_EUID|SD_BUS_CREDS_EFFECTIVE_CAPS));
- check(sd_bus_set_watch_bind(*o->bus, 1));
- check(sd_bus_start(*o->bus));
+ r = sd_bus_new(o->bus);
+ if (r < 0)
+ return log_error(r, "sd_bus_new()");
+ r = sd_bus_set_address(*o->bus, "unix:path=/run/dbus/system_bus_socket");
+ if (r < 0)
+ return log_error(r, "sd_bus_set_address()");
+ r = sd_bus_set_bus_client(*o->bus, 1);
+ if (r < 0)
+ return log_error(r, "sd_bus_set_bus_client()");
+ r = sd_bus_negotiate_creds(*o->bus, 1, SD_BUS_CREDS_UID|SD_BUS_CREDS_EUID|SD_BUS_CREDS_EFFECTIVE_CAPS);
+ if (r < 0)
+ return log_error(r, "sd_bus_negotiate_creds()");
+ r = sd_bus_set_watch_bind(*o->bus, 1);
+ if (r < 0)
+ return log_error(r, "sd_bus_set_watch_bind()");
+ r = sd_bus_start(*o->bus);
+ if (r < 0)
+ return log_error(r, "sd_bus_start()");
/* Publish an interface on the bus, specifying our well-known object access
* path and public interface name.
* https://www.freedesktop.org/software/systemd/man/sd_bus_add_object.html
* https://dbus.freedesktop.org/doc/dbus-tutorial.html
*/
- check(sd_bus_add_object_vtable(*o->bus,
- NULL,
- "/org/freedesktop/ReconnectExample",
- "org.freedesktop.ReconnectExample",
- vtable,
- o));
+ r = sd_bus_add_object_vtable(*o->bus,
+ NULL,
+ "/org/freedesktop/ReconnectExample",
+ "org.freedesktop.ReconnectExample",
+ vtable,
+ o);
+ if (r < 0)
+ return log_error(r, "sd_bus_add_object_vtable()");
/* By default the service is only assigned an ephemeral name. Also add a
* well-known one, so that clients know whom to call. This needs to be
* asynchronous, as D-Bus might not be yet available. The callback will check
* whether the error is expected or not, in case it fails.
* https://www.freedesktop.org/software/systemd/man/sd_bus_request_name.html
*/
- check(sd_bus_request_name_async(*o->bus,
- NULL,
- "org.freedesktop.ReconnectExample",
- 0,
- request_name_callback,
- o));
+ r = sd_bus_request_name_async(*o->bus,
+ NULL,
+ "org.freedesktop.ReconnectExample",
+ 0,
+ request_name_callback,
+ o);
+ if (r < 0)
+ return log_error(r, "sd_bus_request_name_async()");
/* When D-Bus is disconnected this callback will be invoked, which will set up
* the connection again. This needs to be asynchronous, as D-Bus might not yet
* be available.
* https://www.freedesktop.org/software/systemd/man/sd_bus_match_signal_async.html
*/
- check(sd_bus_match_signal_async(*o->bus,
- NULL,
- "org.freedesktop.DBus.Local",
- NULL,
- "org.freedesktop.DBus.Local",
- "Disconnected",
- on_disconnect,
- NULL,
- o));
+ r = sd_bus_match_signal_async(*o->bus,
+ NULL,
+ "org.freedesktop.DBus.Local",
+ NULL,
+ "org.freedesktop.DBus.Local",
+ "Disconnected",
+ on_disconnect,
+ NULL,
+ o);
+ if (r < 0)
+ return log_error(r, "sd_bus_match_signal_async()");
/* Attach the bus object to the event loop so that calls and signals are
* processed.
* https://www.freedesktop.org/software/systemd/man/sd_bus_attach_event.html
*/
- check(sd_bus_attach_event(*o->bus, *o->event, 0));
+ r = sd_bus_attach_event(*o->bus, *o->event, 0);
+ if (r < 0)
+ return log_error(r, "sd_bus_attach_event()");
return 0;
}
.bus = &bus,
.event = &event,
};
+ int r;
/* Create an event loop data structure, with default parameters.
* https://www.freedesktop.org/software/systemd/man/sd_event_default.html
*/
- check(sd_event_default(&event));
+ r = sd_event_default(&event);
+ if (r < 0)
+ return log_error(r, "sd_event_default()");
/* By default the event loop will terminate when all sources have disappeared,
* so we have to keep it 'occupied'. Register signal handling to do so.
* https://www.freedesktop.org/software/systemd/man/sd_event_add_signal.html
*/
- check(sd_event_add_signal(event, NULL, SIGINT|SD_EVENT_SIGNAL_PROCMASK, NULL, NULL));
- check(sd_event_add_signal(event, NULL, SIGTERM|SD_EVENT_SIGNAL_PROCMASK, NULL, NULL));
+ r = sd_event_add_signal(event, NULL, SIGINT|SD_EVENT_SIGNAL_PROCMASK, NULL, NULL);
+ if (r < 0)
+ return log_error(r, "sd_event_add_signal(SIGINT)");
+
+ r = sd_event_add_signal(event, NULL, SIGTERM|SD_EVENT_SIGNAL_PROCMASK, NULL, NULL);
+ if (r < 0)
+ return log_error(r, "sd_event_add_signal(SIGTERM)");
- check(setup(&o));
+ r = setup(&o);
+ if (r < 0)
+ return EXIT_FAILURE;
/* Enter the main loop, it will exit only on sigint/sigterm.
* https://www.freedesktop.org/software/systemd/man/sd_event_loop.html
*/
- check(sd_event_loop(event));
+ r = sd_event_loop(event);
+ if (r < 0)
+ return log_error(r, "sd_event_loop()");
/* https://www.freedesktop.org/software/systemd/man/sd_bus_release_name.html */
- check(sd_bus_release_name(bus, "org.freedesktop.ReconnectExample"));
+ r = sd_bus_release_name(bus, "org.freedesktop.ReconnectExample");
+ if (r < 0)
+ return log_error(r, "sd_bus_release_name()");
return 0;
}
privileged port (i.e.: lower than 1024), as an attempt to address concerns that unprivileged processes in
the guest might try to send malicious notifications to the host, driving it to make destructive decisions
based on them.</para>
+
+ <para>Note that, while using this library should be preferred in order to avoid code duplication, it is
+ also possible to reimplement the simple readiness notification protocol without external dependencies,
+ as demonstrated in the following self-contained example:
+ <programlisting><xi:include href="notify-selfcontained-example.c" parse="text"/></programlisting></para>
</refsect1>
<refsect1>
<programlisting>
sd_notifyf(0, "READY=1\n"
- "STATUS=Processing requests…\n"
- "MAINPID=%lu",
+ "STATUS=Processing requests…\n"
+ "MAINPID=%lu",
(unsigned long) getpid());</programlisting>
</example>
<programlisting>
sd_notifyf(0, "STATUS=Failed to start up: %s\n"
- "ERRNO=%i",
+ "ERRNO=%i",
strerror_r(errnum, (char[1024]){}, 1024),
errnum);</programlisting>
</example>
</varlistentry>
<varlistentry id='legend'>
- <term><option>--legend=</option><replaceable>BOOL</replaceable></term>
+ <term><option>--legend=<replaceable>BOOL</replaceable></option></term>
<listitem>
<para>Enable or disable printing of the legend, i.e. column headers and the footer with hints. The
</varlistentry>
<varlistentry id='json'>
- <term><option>--json=</option><replaceable>MODE</replaceable></term>
+ <term><option>--json=<replaceable>MODE</replaceable></option></term>
<listitem><para>Shows output formatted as JSON. Expects one of <literal>short</literal> (for the
shortest possible output without any redundant whitespace or line breaks), <literal>pretty</literal>
</varlistentry>
<varlistentry>
- <term><option>--kill-value=</option><replaceable>INT</replaceable></term>
+ <term><option>--kill-value=<replaceable>INT</replaceable></option></term>
<listitem><para>If used with the <command>kill</command> command, enqueues a signal along with the
specified integer value parameter to the specified process(es). This operation is only available for
</varlistentry>
<varlistentry>
- <term><option>--drop-in=</option><replaceable>NAME</replaceable></term>
+ <term><option>--drop-in=<replaceable>NAME</replaceable></option></term>
<listitem><para>When used with <command>edit</command>, use <replaceable>NAME</replaceable> as the
drop-in file name instead of <filename>override.conf</filename>.</para>
</varlistentry>
<varlistentry>
- <term><option>--fuzz=</option><replaceable>timespan</replaceable></term>
+ <term><option>--fuzz=<replaceable>timespan</replaceable></option></term>
<listitem><para>When used in conjunction with the
<command>critical-chain</command> command (see above), also
</varlistentry>
<varlistentry>
- <term><option>--tty=</option><replaceable></replaceable></term>
+ <term><option>--tty=<replaceable></replaceable></option></term>
<listitem><para>Specify the TTY to output to. By default <command>systemd-bsod</command> will
automatically find a free VT to display the message on. If this option is specified a TTY may be
</varlistentry>
<varlistentry>
- <term><option>--name=</option><replaceable>name</replaceable></term>
+ <term><option>--name=<replaceable>name</replaceable></option></term>
<listitem><para>When specified with the <command>encrypt</command> command controls the credential
name to embed in the encrypted credential data. If not specified the name is chosen automatically
</varlistentry>
<varlistentry>
- <term><option>--timestamp=</option><replaceable>timestamp</replaceable></term>
+ <term><option>--timestamp=<replaceable>timestamp</replaceable></option></term>
<listitem><para>When specified with the <command>encrypt</command> command controls the timestamp to
embed into the encrypted credential. Defaults to the current time. Takes a timestamp specification in
</varlistentry>
<varlistentry>
- <term><option>--not-after=</option><replaceable>timestamp</replaceable></term>
+ <term><option>--not-after=<replaceable>timestamp</replaceable></option></term>
<listitem><para>When specified with the <command>encrypt</command> command controls the time when the
credential shall not be used anymore. This embeds the specified timestamp in the encrypted
</varlistentry>
<varlistentry>
- <term><option>--tpm2-device=</option><replaceable>PATH</replaceable></term>
+ <term><option>--tpm2-device=<replaceable>PATH</replaceable></option></term>
<listitem><para>Controls the TPM2 device to use. Expects a device node path referring to the TPM2
chip (e.g. <filename>/dev/tpmrm0</filename>). Alternatively the special value <literal>auto</literal>
</varlistentry>
<varlistentry>
- <term><option>--tpm2-pcrs=</option><replaceable>PCR<optional>+PCR...</optional></replaceable></term>
+ <term><option>--tpm2-pcrs=<replaceable>PCR<optional>+PCR...</optional></replaceable></option></term>
<listitem><para>Configures the TPM2 PCRs (Platform Configuration Registers) to bind the encryption
key to. Takes a <literal>+</literal> separated list of numeric PCR indexes in the range 0…23. If not
</varlistentry>
<varlistentry>
- <term><option>--tpm2-public-key=</option><replaceable>PATH</replaceable></term>
- <term><option>--tpm2-public-key-pcrs=</option><replaceable>PCR<optional>+PCR...</optional></replaceable></term>
+ <term><option>--tpm2-public-key=<replaceable>PATH</replaceable></option></term>
+ <term><option>--tpm2-public-key-pcrs=<replaceable>PCR<optional>+PCR...</optional></replaceable></option></term>
<listitem><para>Configures a TPM2 signed PCR policy to bind encryption to, for use with the
<command>encrypt</command> command. The <option>--tpm2-public-key=</option> option accepts a path to
</varlistentry>
<varlistentry>
- <term><option>--tpm2-signature=</option><replaceable>PATH</replaceable></term>
+ <term><option>--tpm2-signature=<replaceable>PATH</replaceable></option></term>
<listitem><para>Takes a path to a TPM2 PCR signature file as generated by the
<citerefentry><refentrytitle>systemd-measure</refentrytitle><manvolnum>1</manvolnum></citerefentry>
<xi:include href="version-info.xml" xpointer="v252"/></listitem>
</varlistentry>
+ <varlistentry>
+ <term><option>--allow-null</option></term>
+
+ <listitem><para>Allow decrypting credentials that use an empty key.</para>
+
+ <xi:include href="version-info.xml" xpointer="v256"/></listitem>
+ </varlistentry>
+
<varlistentry>
<term><option>--quiet</option></term>
<term><option>-q</option></term>
</varlistentry>
<varlistentry>
- <term><option>--unlock-key-file=</option><replaceable>PATH</replaceable></term>
+ <term><option>--unlock-key-file=<replaceable>PATH</replaceable></option></term>
<listitem><para>Use a file instead of a password/passphrase read from stdin to unlock the volume.
Expects the PATH to the file containing your key to unlock the volume. Currently there is nothing like
</varlistentry>
<varlistentry>
- <term><option>--unlock-fido2-device=</option><replaceable>PATH</replaceable></term>
+ <term><option>--unlock-fido2-device=<replaceable>PATH</replaceable></option></term>
<listitem><para>Use a FIDO2 device instead of a password/passphrase read from stdin to unlock the
volume. Expects a <filename>hidraw</filename> device referring to the FIDO2 device (e.g.
</varlistentry>
<varlistentry>
- <term><option>--unlock-tpm2-device=</option><replaceable>PATH</replaceable></term>
+ <term><option>--unlock-tpm2-device=<replaceable>PATH</replaceable></option></term>
<listitem><para>Use a TPM2 device instead of a password/passhprase read from stdin to unlock the
volume. Expects a device node path referring to the TPM2 chip (e.g. <filename>/dev/tpmrm0</filename>).
</varlistentry>
<varlistentry>
- <term><option>--pkcs11-token-uri=</option><replaceable>URI</replaceable></term>
+ <term><option>--pkcs11-token-uri=<replaceable>URI</replaceable></option></term>
<listitem><para>Enroll a PKCS#11 security token or smartcard (e.g. a YubiKey). Expects a PKCS#11 URI
that allows to find an X.509 certificate or a public key on the token. The URI must also be suitable
</varlistentry>
<varlistentry>
- <term><option>--fido2-credential-algorithm=</option><replaceable>STRING</replaceable></term>
+ <term><option>--fido2-credential-algorithm=<replaceable>STRING</replaceable></option></term>
<listitem><para>Specify COSE algorithm used in credential generation. The default value is
<literal>es256</literal>. Supported values are <literal>es256</literal>, <literal>rs256</literal>
and <literal>eddsa</literal>.</para>
</varlistentry>
<varlistentry>
- <term><option>--fido2-device=</option><replaceable>PATH</replaceable></term>
+ <term><option>--fido2-device=<replaceable>PATH</replaceable></option></term>
<listitem><para>Enroll a FIDO2 security token that implements the <literal>hmac-secret</literal>
extension (e.g. a YubiKey). Expects a <filename>hidraw</filename> device referring to the FIDO2
</varlistentry>
<varlistentry>
- <term><option>--fido2-with-client-pin=</option><replaceable>BOOL</replaceable></term>
+ <term><option>--fido2-with-client-pin=<replaceable>BOOL</replaceable></option></term>
<listitem><para>When enrolling a FIDO2 security token, controls whether to require the user to enter
a PIN when unlocking the volume (the FIDO2 <literal>clientPin</literal> feature). Defaults to
</varlistentry>
<varlistentry>
- <term><option>--fido2-with-user-presence=</option><replaceable>BOOL</replaceable></term>
+ <term><option>--fido2-with-user-presence=<replaceable>BOOL</replaceable></option></term>
<listitem><para>When enrolling a FIDO2 security token, controls whether to require the user to
verify presence (tap the token, the FIDO2 <literal>up</literal> feature) when unlocking the volume.
</varlistentry>
<varlistentry>
- <term><option>--fido2-with-user-verification=</option><replaceable>BOOL</replaceable></term>
+ <term><option>--fido2-with-user-verification=<replaceable>BOOL</replaceable></option></term>
<listitem><para>When enrolling a FIDO2 security token, controls whether to require user verification
when unlocking the volume (the FIDO2 <literal>uv</literal> feature). Defaults to
</varlistentry>
<varlistentry>
- <term><option>--tpm2-device=</option><replaceable>PATH</replaceable></term>
+ <term><option>--tpm2-device=<replaceable>PATH</replaceable></option></term>
<listitem><para>Enroll a TPM2 security chip. Expects a device node path referring to the TPM2 chip
(e.g. <filename>/dev/tpmrm0</filename>). Alternatively the special value <literal>auto</literal> may
</varlistentry>
<varlistentry>
- <term><option>--tpm2-device-key=</option><replaceable>PATH</replaceable></term>
+ <term><option>--tpm2-device-key=<replaceable>PATH</replaceable></option></term>
<listitem><para>Enroll a TPM2 security chip using its public key. Expects a path referring to the
TPM2 public key in TPM2B_PUBLIC format. This cannot be used with <option>--tpm2-device=</option>, as
</varlistentry>
<varlistentry>
- <term><option>--tpm2-seal-key-handle=</option><replaceable>HANDLE</replaceable></term>
+ <term><option>--tpm2-seal-key-handle=<replaceable>HANDLE</replaceable></option></term>
<listitem><para>Configures which parent key to use for sealing, using the TPM handle (index) of the
key. This is used to "seal" (encrypt) a secret and must be used later to "unseal" (decrypt) the
</varlistentry>
<varlistentry>
- <term><option>--tpm2-pcrs=</option><replaceable>PCR<optional>+PCR...</optional></replaceable></term>
+ <term><option>--tpm2-pcrs=<replaceable>PCR<optional>+PCR...</optional></replaceable></option></term>
<listitem><para>Configures the TPM2 PCRs (Platform Configuration Registers) to bind to when
enrollment is requested via <option>--tpm2-device=</option>. Takes a list of PCR entries, where each
</varlistentry>
<varlistentry>
- <term><option>--tpm2-with-pin=</option><replaceable>BOOL</replaceable></term>
+ <term><option>--tpm2-with-pin=<replaceable>BOOL</replaceable></option></term>
<listitem><para>When enrolling a TPM2 device, controls whether to require the user to enter a PIN
when unlocking the volume in addition to PCR binding, based on TPM2 policy authentication. Defaults
</varlistentry>
<varlistentry>
- <term><option>--tpm2-public-key=</option><replaceable>PATH</replaceable></term>
- <term><option>--tpm2-public-key-pcrs=</option><replaceable>PCR<optional>+PCR...</optional></replaceable></term>
- <term><option>--tpm2-signature=</option><replaceable>PATH</replaceable></term>
+ <term><option>--tpm2-public-key=<replaceable>PATH</replaceable></option></term>
+ <term><option>--tpm2-public-key-pcrs=<replaceable>PCR<optional>+PCR...</optional></replaceable></option></term>
+ <term><option>--tpm2-signature=<replaceable>PATH</replaceable></option></term>
<listitem><para>Configures a TPM2 signed PCR policy to bind encryption to. The
<option>--tpm2-public-key=</option> option accepts a path to a PEM encoded RSA public key, to bind
</varlistentry>
<varlistentry>
- <term><option>--tpm2-pcrlock=</option><replaceable>PATH</replaceable></term>
+ <term><option>--tpm2-pcrlock=<replaceable>PATH</replaceable></option></term>
<listitem><para>Configures a TPM2 pcrlock policy to bind encryption to. Expects a path to a pcrlock
policy file as generated by the
</varlistentry>
<varlistentry>
- <term><option>--wipe-slot=</option><replaceable>SLOT<optional>,SLOT...</optional></replaceable></term>
+ <term><option>--wipe-slot=<replaceable>SLOT<optional>,SLOT...</optional></replaceable></option></term>
<listitem><para>Wipes one or more LUKS2 key slots. Takes a comma separated list of numeric slot
indexes, or the special strings <literal>all</literal> (for wiping all key slots),
%entities;
]>
<!-- SPDX-License-Identifier: LGPL-2.1-or-later -->
-<refentry id="systemd-debug-generator">
+<refentry id="systemd-debug-generator" xmlns:xi="http://www.w3.org/2001/XInclude">
<refentryinfo>
<title>systemd-debug-generator</title>
<citerefentry><refentrytitle>systemd.generator</refentrytitle><manvolnum>7</manvolnum></citerefentry>.</para>
</refsect1>
+ <refsect1>
+ <title>System Credentials</title>
+
+ <variablelist class='system-credentials'>
+ <varlistentry>
+ <term><varname>systemd.extra-unit.*</varname></term>
+
+ <listitem><para>Credentials prefixed with <literal>systemd.extra-unit.</literal> specify additional
+ units to add to the final system. Note that these additional units are added to both the initrd and
+ the final system. <varname>ConditionPathExists=!/etc/initrd-release</varname> can be used to make
+ sure the unit is conditioned out in the initrd.</para>
+
+ <xi:include href="version-info.xml" xpointer="v256"/></listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><varname>systemd.unit-dropin.*</varname></term>
+
+ <listitem><para>Credentials prefixed with <literal>systemd.unit-dropin.</literal> add drop-ins for
+ the corresponding units in the final system. Each credential must be suffixed with the full unit name
+ including the unit extension. Its contents must be a valid unit drop-in file. Only one drop-in per
+ unit can be specified. The name of the generated drop-in will be
+ <literal>50-credential.conf</literal>. Note that these additional drop-ins are added to both the
+ initrd and the final system.</para>
+
+ <xi:include href="version-info.xml" xpointer="v256"/></listitem>
+ </varlistentry>
+ </variablelist>
+ </refsect1>
+
<refsect1>
<title>See Also</title>
<para><simplelist type="inline">
<refnamediv>
<refname>systemd-hibernate-resume.service</refname>
+ <refname>systemd-hibernate-clear.service</refname>
<refname>systemd-hibernate-resume</refname>
<refpurpose>Resume from hibernation</refpurpose>
</refnamediv>
<refsynopsisdiv>
<para><filename>systemd-hibernate-resume.service</filename></para>
+ <para><filename>systemd-hibernate-clear.service</filename></para>
<para><filename>/usr/lib/systemd/systemd-hibernate-resume</filename></para>
</refsynopsisdiv>
<filename>/sys/power/resume</filename>, along with the offset in memory pages
(<filename>/sys/power/resume_offset</filename>) if supported.</para>
+ <para>The resume device node is either passed directly through arguments, or automatically acquired
+ from kernel command line options and/or <varname>HibernateLocation</varname> EFI variable. The latter
+ will normally be cleared by <filename>systemd-hibernate-resume.service</filename> on resumption.
+ If a stale variable is detected, it would be cleared by
+ <filename>systemd-hibernate-clear.service</filename>.</para>
+
<para>Failing to initiate a resume is not an error condition. It may mean that there was
no resume image (e. g. if the system has been simply powered off and not hibernated).
In such cases, the boot is ordinarily continued.</para>
</varlistentry>
<varlistentry>
- <term><option>--tpm2-device=</option><replaceable>PATH</replaceable></term>
+ <term><option>--tpm2-device=<replaceable>PATH</replaceable></option></term>
<listitem><para>Controls which TPM2 device to use. Expects a device node path referring to the TPM2
chip (e.g. <filename>/dev/tpmrm0</filename>). Alternatively the special value <literal>auto</literal>
</varlistentry>
<varlistentry>
- <term><option>--phase=</option><replaceable>PHASE</replaceable></term>
+ <term><option>--phase=<replaceable>PHASE</replaceable></option></term>
<listitem><para>Controls which boot phases to calculate expected PCR 11 values for. This takes a
series of colon-separated strings that encode boot "paths" for entering a specific phase of the boot
</varlistentry>
<varlistentry>
- <term><option>--append=</option><replaceable>PATH</replaceable></term>
+ <term><option>--append=<replaceable>PATH</replaceable></option></term>
<listitem><para>When generating a PCR JSON signature (via the <command>sign</command> command),
combine it with a previously generated PCR JSON signature, and output it as one. The specified path
--- /dev/null
+<?xml version='1.0'?> <!--*-nxml-*-->
+<!DOCTYPE refentry PUBLIC "-//OASIS//DTD DocBook XML V4.5//EN"
+ "http://www.oasis-open.org/docbook/xml/4.2/docbookx.dtd">
+<!-- SPDX-License-Identifier: LGPL-2.1-or-later -->
+
+<refentry id="systemd-mountfsd.service" conditional='ENABLE_MOUNTFSD'>
+
+ <refentryinfo>
+ <title>systemd-mountfsd.service</title>
+ <productname>systemd</productname>
+ </refentryinfo>
+
+ <refmeta>
+ <refentrytitle>systemd-mountfsd.service</refentrytitle>
+ <manvolnum>8</manvolnum>
+ </refmeta>
+
+ <refnamediv>
+ <refname>systemd-mountfsd.service</refname>
+ <refname>systemd-mountfsd</refname>
+ <refpurpose>Disk Image File System Mount Service</refpurpose>
+ </refnamediv>
+
+ <refsynopsisdiv>
+ <para><filename>systemd-mountfsd.service</filename></para>
+ <para><filename>/usr/lib/systemd/systemd-mountfsd</filename></para>
+ </refsynopsisdiv>
+
+ <refsect1>
+ <title>Description</title>
+
+ <para><command>systemd-mountfsd</command> is a system service that dissects disk images, and returns mount
+ file descriptors for the file systems contained therein to clients, via a Varlink IPC API.</para>
+
+ <para>The disk images provided must contain a raw file system image or must follow the <ulink
+ url="https://uapi-group.org/specifications/specs/discoverable_partitions_specification/">Discoverable
+ Partitions Specification</ulink>. Before mounting any file systems authenticity of the disk image is
+ established in one or a combination of the following ways:</para>
+
+ <orderedlist>
+ <listitem><para>If the disk image is located in a regular file in one of the directories
+ <filename>/var/lib/machines/</filename>, <filename>/var/lib/portables/</filename>,
+ <filename>/var/lib/extensions/</filename>, <filename>/var/lib/confexts/</filename> or their
+ counterparts in the <filename>/etc/</filename>, <filename>/run/</filename>,
+ <filename>/usr/lib/</filename> it is assumed to be trusted.</para></listitem>
+
+ <listitem><para>If the disk image contains a Verity enabled disk image, along with a signature
+ partition with a key in the kernel keyring or in <filename>/etc/verity.d/</filename> (and related
+ directories) the disk image is considered trusted.</para></listitem>
+ </orderedlist>
+
+ <para>This service provides one <ulink url="https://varlink.org/">Varlink</ulink> service:
+ <constant>io.systemd.MountFileSystem</constant> which accepts a file descriptor to a regular file or
+ block device, and returns a number of file descriptors referring to an <function>fsmount()</function>
+ file descriptor the client may then attach to a path of their choice.</para>
+
+ <para>The returned mounts are automatically allowlisted in the per-user-namespace allowlist maintained by
+ <citerefentry><refentrytitle>systemd-nsresourced.service</refentrytitle><manvolnum>8</manvolnum></citerefentry>.</para>
+
+ <para>The file systems are automatically fsck'ed before mounting.</para>
+ </refsect1>
+
+ <refsect1>
+ <title>See Also</title>
+ <para>
+ <citerefentry><refentrytitle>systemd</refentrytitle><manvolnum>1</manvolnum></citerefentry>,
+ <citerefentry><refentrytitle>systemd-nsresourced.service</refentrytitle><manvolnum>8</manvolnum></citerefentry>
+ </para>
+ </refsect1>
+</refentry>
<variablelist class='system-credentials'>
<varlistentry>
- <term><varname>network.netdev.*</varname></term>
+ <term><varname>network.conf.*</varname></term>
<term><varname>network.link.*</varname></term>
+ <term><varname>network.netdev.*</varname></term>
<term><varname>network.network.*</varname></term>
<listitem><para>These credentials should contain valid
- <citerefentry><refentrytitle>systemd.netdev</refentrytitle><manvolnum>5</manvolnum></citerefentry>,
+ <citerefentry><refentrytitle>networkd.conf</refentrytitle><manvolnum>5</manvolnum></citerefentry>,
<citerefentry><refentrytitle>systemd.link</refentrytitle><manvolnum>5</manvolnum></citerefentry>,
+ <citerefentry><refentrytitle>systemd.netdev</refentrytitle><manvolnum>5</manvolnum></citerefentry>,
<citerefentry><refentrytitle>systemd.network</refentrytitle><manvolnum>5</manvolnum></citerefentry>
configuration data. From each matching credential a separate file is created. Example: a passed
credential <filename>network.link.50-foobar</filename> will be copied into a configuration file
<variablelist>
<varlistentry>
<term><option>-i</option> <replaceable>INTERFACE</replaceable><optional>:<replaceable>MIN_OPERSTATE</replaceable><optional>:<replaceable>MAX_OPERSTATE</replaceable></optional></optional></term>
- <term><option>--interface=</option><replaceable>INTERFACE</replaceable><optional>:<replaceable>MIN_OPERSTATE</replaceable><optional>:<replaceable>MAX_OPERSTATE</replaceable></optional></optional></term>
+ <term><option>--interface=<replaceable>INTERFACE</replaceable><optional>:<replaceable>MIN_OPERSTATE</replaceable><optional>:<replaceable>MAX_OPERSTATE</replaceable></optional></optional></option></term>
<listitem><para>Network interface to wait for before deciding if the system is online. This
is useful when a system has several interfaces which will be configured, but a particular
</varlistentry>
<varlistentry>
- <term><option>--ignore=</option><replaceable>INTERFACE</replaceable></term>
+ <term><option>--ignore=<replaceable>INTERFACE</replaceable></option></term>
<listitem><para>Network interfaces to be ignored when deciding
if the system is online. By default, only the loopback
<varlistentry>
<term><option>-o</option> <replaceable>MIN_OPERSTATE</replaceable><optional>:<replaceable>MAX_OPERSTATE</replaceable></optional></term>
- <term><option>--operational-state=</option><replaceable>MIN_OPERSTATE</replaceable><optional>:<replaceable>MAX_OPERSTATE</replaceable></optional></term>
+ <term><option>--operational-state=<replaceable>MIN_OPERSTATE</replaceable><optional>:<replaceable>MAX_OPERSTATE</replaceable></optional></option></term>
<listitem><para>Takes a minimum operational state and an optional maximum operational state.
Please see <citerefentry><refentrytitle>networkctl</refentrytitle><manvolnum>1</manvolnum></citerefentry>
</varlistentry>
<varlistentry>
- <term><option>--timeout=</option><replaceable>SECS</replaceable></term>
+ <term><option>--timeout=<replaceable>SECS</replaceable></option></term>
<listitem><para>Fail the service if the network is not online
by the time the timeout elapses. A timeout of 0 disables the
</varlistentry>
<varlistentry>
- <term><option>--uid=</option><replaceable>USER</replaceable></term>
+ <term><option>--uid=<replaceable>USER</replaceable></option></term>
<listitem><para>Set the user ID to send the notification from. Takes a UNIX user name or numeric UID. When
specified the notification message will be sent with the specified UID as sender, in place of the user the
keeps track of running containers, and provides programming interfaces to interact with them.</para>
</refsect1>
+ <refsect1>
+ <title>Unprivileged Operation</title>
+
+ <para><command>systemd-nspawn</command> may be invoked with or without privileges. The full functionality
+ is currently only available when invoked with privileges. When invoked without privileges, various
+ limitations apply, including, but not limited to:</para>
+
+ <itemizedlist>
+ <listitem><para>Only disk image based containers are supported (i.e. <option>--image=</option>).
+ Directory based ones (i.e. <option>--directory=</option>) are not supported.</para></listitem>
+
+ <listitem><para>Machine registration via <option>--machine=</option> is not supported.</para></listitem>
+
+ <listitem><para>Only <option>--private-network</option> and <option>--network-veth</option> networking modes are supported.</para></listitem>
+ </itemizedlist>
+
+ <para>When running in unprivileged mode, some needed functionality is provided via
+ <citerefentry><refentrytitle>systemd-mountfsd.service</refentrytitle><manvolnum>8</manvolnum></citerefentry>
+ and
+ <citerefentry><refentrytitle>systemd-nsresourced.service</refentrytitle><manvolnum>8</manvolnum></citerefentry></para>
+ </refsect1>
+
<refsect1>
<title>Options</title>
</varlistentry>
<varlistentry>
- <term><option>--settings=</option><replaceable>MODE</replaceable></term>
+ <term><option>--settings=<replaceable>MODE</replaceable></option></term>
<listitem><para>Controls whether
<command>systemd-nspawn</command> shall search for and use
<varlistentry>
<term><option>--volatile</option></term>
- <term><option>--volatile=</option><replaceable>MODE</replaceable></term>
+ <term><option>--volatile=<replaceable>MODE</replaceable></option></term>
<listitem><para>Boots the container in volatile mode. When no mode parameter is passed or when mode is
specified as <option>yes</option>, full volatile mode is enabled. This means the root directory is mounted as a
<variablelist>
<varlistentry>
- <term><option>--console=</option><replaceable>MODE</replaceable></term>
+ <term><option>--console=<replaceable>MODE</replaceable></option></term>
<listitem><para>Configures how to set up standard input, output and error output for the container
payload, as well as the <filename>/dev/console</filename> device for the container. Takes one of
<variablelist>
<varlistentry>
- <term><option>--load-credential=</option><replaceable>ID</replaceable>:<replaceable>PATH</replaceable></term>
- <term><option>--set-credential=</option><replaceable>ID</replaceable>:<replaceable>VALUE</replaceable></term>
+ <term><option>--load-credential=<replaceable>ID</replaceable>:<replaceable>PATH</replaceable></option></term>
+ <term><option>--set-credential=<replaceable>ID</replaceable>:<replaceable>VALUE</replaceable></option></term>
<listitem><para>Pass a credential to the container. These two options correspond to the
<varname>LoadCredential=</varname> and <varname>SetCredential=</varname> settings in unit files. See
<member><citerefentry><refentrytitle>systemd.slice</refentrytitle><manvolnum>5</manvolnum></citerefentry></member>
<member><citerefentry><refentrytitle>machinectl</refentrytitle><manvolnum>1</manvolnum></citerefentry></member>
<member><citerefentry><refentrytitle>importctl</refentrytitle><manvolnum>1</manvolnum></citerefentry></member>
+ <member><citerefentry><refentrytitle>systemd-mountfsd.service</refentrytitle><manvolnum>8</manvolnum></citerefentry></member>
+ <member><citerefentry><refentrytitle>systemd-nsresourced.service</refentrytitle><manvolnum>8</manvolnum></citerefentry></member>
<member><citerefentry project='url'><refentrytitle url='https://btrfs.readthedocs.io/en/latest/btrfs.html'>btrfs</refentrytitle><manvolnum>8</manvolnum></citerefentry></member>
</simplelist></para>
</refsect1>
--- /dev/null
+<?xml version='1.0'?> <!--*-nxml-*-->
+<!DOCTYPE refentry PUBLIC "-//OASIS//DTD DocBook XML V4.5//EN"
+ "http://www.oasis-open.org/docbook/xml/4.2/docbookx.dtd">
+<!-- SPDX-License-Identifier: LGPL-2.1-or-later -->
+
+<refentry id="systemd-nsresourced.service" conditional='ENABLE_NSRESOURCED'>
+
+ <refentryinfo>
+ <title>systemd-nsresourced.service</title>
+ <productname>systemd</productname>
+ </refentryinfo>
+
+ <refmeta>
+ <refentrytitle>systemd-nsresourced.service</refentrytitle>
+ <manvolnum>8</manvolnum>
+ </refmeta>
+
+ <refnamediv>
+ <refname>systemd-nsresourced.service</refname>
+ <refname>systemd-nsresourced</refname>
+ <refpurpose>User Namespace Resource Delegation Service</refpurpose>
+ </refnamediv>
+
+ <refsynopsisdiv>
+ <para><filename>systemd-nsresourced.service</filename></para>
+ <para><filename>/usr/lib/systemd/systemd-nsresourced</filename></para>
+ </refsynopsisdiv>
+
+ <refsect1>
+ <title>Description</title>
+
+ <para><command>systemd-nsresourced</command> is a system service that permits transient delegation of a a
+ UID/GID range to a user namespace (see <citerefentry
+ project='man-pages'><refentrytitle>user_namespaces</refentrytitle><manvolnum>7</manvolnum></citerefentry>)
+ allocated by a client, via a Varlink IPC API.</para>
+
+ <para>Unprivileged clients may allocate a user namespace, and then request a UID/GID range to be assigned
+ to it via this service. The user namespace may then be used to run containers and other sandboxes, and/or
+ apply it to an id-mapped mount.</para>
+
+ <para>Allocations of UIDs/GIDs this way are transient: when a user namespace goes away, its UID/GID range
+ is returned to the pool of available ranges. In order to ensure that clients cannot gain persistency in
+ their transient UID/GID range a BPF-LSM based policy is enforced that ensures that user namespaces set up
+ this way can only write to file systems they allocate themselves or that are explicitly allowlisted via
+ <command>systemd-nsresourced</command>.</para>
+
+ <para><command>systemd-nsresourced</command> automatically ensures that any registered UID ranges show up
+ in the system's NSS database via the <ulink url="https://systemd.io/USER_GROUP_API">User/Group Record
+ Lookup API via Varlink</ulink>.</para>
+
+ <para>Currently, only UID/GID ranges consisting of either exactly 1 or exactly 65536 UIDs/GIDs can be
+ registered with this service. Moreover, UIDs and GIDs are always allocated together, and
+ symmetrically.</para>
+
+ <para>The service provides API calls to allowlist mounts (referenced via their mount file descriptors as
+ per Linux <function>fsmount()</function> API), to pass ownership of a cgroup subtree to the user
+ namespace and to delegate a virtual Ethernet device pair to the user namespace. When used in combination
+ this is sufficient to implement fully unprivileged container environments, as implemented by
+ <citerefentry><refentrytitle>systemd-nspawn</refentrytitle><manvolnum>1</manvolnum></citerefentry>, fully
+ unprivileged <varname>RootImage=</varname> (see
+ <citerefentry><refentrytitle>systemd.exec</refentrytitle><manvolnum>5</manvolnum></citerefentry>) or
+ fully unprivileged disk image tools such as
+ <citerefentry><refentrytitle>systemd-dissect</refentrytitle><manvolnum>1</manvolnum></citerefentry>.</para>
+
+ <para>This service provides one <ulink url="https://varlink.org/">Varlink</ulink> service:
+ <constant>io.systemd.NamespaceResource</constant> allows registering user namespaces, and assign mounts,
+ cgroups and network interfaces to it.</para>
+ </refsect1>
+
+ <refsect1>
+ <title>See Also</title>
+ <para>
+ <citerefentry><refentrytitle>systemd</refentrytitle><manvolnum>1</manvolnum></citerefentry>,
+ <citerefentry><refentrytitle>systemd-mountfsd.service</refentrytitle><manvolnum>8</manvolnum></citerefentry>,
+ <citerefentry><refentrytitle>systemd-nspawn</refentrytitle><manvolnum>1</manvolnum></citerefentry>,
+ <citerefentry><refentrytitle>systemd.exec</refentrytitle><manvolnum>5</manvolnum></citerefentry>,
+ <citerefentry><refentrytitle>systemd-dissect</refentrytitle><manvolnum>1</manvolnum></citerefentry>,
+ <citerefentry project='man-pages'><refentrytitle>user_namespaces</refentrytitle><manvolnum>7</manvolnum></citerefentry>
+ </para>
+ </refsect1>
+</refentry>
</varlistentry>
<varlistentry>
- <term><option>--tpm2-device=</option><replaceable>PATH</replaceable></term>
+ <term><option>--tpm2-device=<replaceable>PATH</replaceable></option></term>
<listitem><para>Controls which TPM2 device to use. Expects a device node path referring to the TPM2
chip (e.g. <filename>/dev/tpmrm0</filename>). Alternatively the special value <literal>auto</literal>
</varlistentry>
<varlistentry>
- <term><option>--tpm2-device-key=</option><replaceable>PATH</replaceable></term>
- <term><option>--tpm2-seal-key-handle=</option><replaceable>HANDLE</replaceable></term>
+ <term><option>--tpm2-device-key=<replaceable>PATH</replaceable></option></term>
+ <term><option>--tpm2-seal-key-handle=<replaceable>HANDLE</replaceable></option></term>
<listitem><para>Configures a TPM2 SRK key to bind encryption to. See
<citerefentry><refentrytitle>systemd-cryptenroll</refentrytitle><manvolnum>1</manvolnum></citerefentry>
</varlistentry>
<varlistentry>
- <term><option>--tpm2-public-key=</option><replaceable>PATH</replaceable></term>
- <term><option>--tpm2-public-key-pcrs=</option><replaceable>PCR<optional>+PCR...</optional></replaceable></term>
+ <term><option>--tpm2-public-key=<replaceable>PATH</replaceable></option></term>
+ <term><option>--tpm2-public-key-pcrs=<replaceable>PCR<optional>+PCR...</optional></replaceable></option></term>
<listitem><para>Configures a TPM2 signed PCR policy to bind encryption to. See
<citerefentry><refentrytitle>systemd-cryptenroll</refentrytitle><manvolnum>1</manvolnum></citerefentry>
</varlistentry>
<varlistentry>
- <term><option>--tpm2-pcrlock=</option><replaceable>PATH</replaceable></term>
+ <term><option>--tpm2-pcrlock=<replaceable>PATH</replaceable></option></term>
<listitem><para>Configures a TPM2 pcrlock policy to bind encryption to. See
<citerefentry><refentrytitle>systemd-cryptenroll</refentrytitle><manvolnum>1</manvolnum></citerefentry>
</varlistentry>
<varlistentry>
- <term><option>--split=</option><replaceable>BOOL</replaceable></term>
+ <term><option>--split=<replaceable>BOOL</replaceable></option></term>
<listitem><para>Enables generation of split artifacts from partitions configured with
<varname>SplitName=</varname>. If enabled, for each partition with <varname>SplitName=</varname> set,
</varlistentry>
<varlistentry>
- <term><option>--include-partitions=</option><replaceable>PARTITIONS</replaceable></term>
- <term><option>--exclude-partitions=</option><replaceable>PARTITIONS</replaceable></term>
+ <term><option>--include-partitions=<replaceable>PARTITIONS</replaceable></option></term>
+ <term><option>--exclude-partitions=<replaceable>PARTITIONS</replaceable></option></term>
<listitem><para>These options specify which partition types <command>systemd-repart</command> should
operate on. If <option>--include-partitions=</option> is used, all partitions that aren't specified
</varlistentry>
<varlistentry>
- <term><option>--defer-partitions=</option><replaceable>PARTITIONS</replaceable></term>
+ <term><option>--defer-partitions=<replaceable>PARTITIONS</replaceable></option></term>
<listitem><para>This option specifies for which partition types <command>systemd-repart</command>
should defer. All partitions that are deferred using this option are still taken into account when
</varlistentry>
<varlistentry>
- <term><option>--sector-size=</option><replaceable>BYTES</replaceable></term>
+ <term><option>--sector-size=<replaceable>BYTES</replaceable></option></term>
<listitem><para>This option allows configuring the sector size of the image produced by
<command>systemd-repart</command>. It takes a value that is a power of <literal>2</literal> between
</varlistentry>
<varlistentry>
- <term><option>--architecture=</option><replaceable>ARCH</replaceable></term>
+ <term><option>--architecture=<replaceable>ARCH</replaceable></option></term>
<listitem><para>This option allows overriding the architecture used for architecture specific
partition types. For example, if set to <literal>arm64</literal> a partition type of
</varlistentry>
<varlistentry>
- <term><option>--offline=</option><replaceable>BOOL</replaceable></term>
+ <term><option>--offline=<replaceable>BOOL</replaceable></option></term>
<listitem><para>Instructs <command>systemd-repart</command> to build the image offline. Takes a
boolean or <literal>auto</literal>. Defaults to <literal>auto</literal>. If enabled, the image is
</varlistentry>
<varlistentry>
- <term><option>--copy-from=</option><replaceable>IMAGE</replaceable></term>
+ <term><option>--copy-from=<replaceable>IMAGE</replaceable></option></term>
<listitem><para>Instructs <command>systemd-repart</command> to synthesize partition definitions from
the partition table in the given image. This option can be specified multiple times to synthesize
</varlistentry>
<varlistentry>
- <term><option>--copy-source=</option><replaceable>PATH</replaceable></term>
+ <term><option>--copy-source=<replaceable>PATH</replaceable></option></term>
<term><option>-s</option> <replaceable>PATH</replaceable></term>
<listitem><para>Specifies a source directory all <varname>CopyFiles=</varname> source paths shall be
</varlistentry>
<varlistentry>
- <term><option>--make-ddi=</option><replaceable>TYPE</replaceable></term>
+ <term><option>--make-ddi=<replaceable>TYPE</replaceable></option></term>
<listitem><para>Takes one of <literal>sysext</literal>, <literal>confext</literal> or
<literal>portable</literal>. Generates a Discoverable Disk Image (DDI) for a system extension
</varlistentry>
<varlistentry>
- <term><option>--generate-fstab=</option><replaceable>PATH</replaceable></term>
+ <term><option>--generate-fstab=<replaceable>PATH</replaceable></option></term>
<listitem><para>Specifies a path where to write fstab entries for the mountpoints configured with
<option>MountPoint=</option> in the root directory specified with <option>--copy-source=</option> or
</varlistentry>
<varlistentry>
- <term><option>--generate-crypttab=</option><replaceable>PATH</replaceable></term>
+ <term><option>--generate-crypttab=<replaceable>PATH</replaceable></option></term>
<listitem><para>Specifies a path where to write crypttab entries for the encrypted volumes configured
with <option>EncryptedVolume=</option> in the root directory specified with
<xi:include href="version-info.xml" xpointer="v240"/></listitem>
</varlistentry>
+ <varlistentry>
+ <term><varname>SuspendState=</varname></term>
+
+ <listitem><para>The string to be written to <filename>/sys/power/state</filename> by <citerefentry>
+ <refentrytitle>systemd-suspend.service</refentrytitle><manvolnum>8</manvolnum></citerefentry>.
+ More than one value can be specified by separating multiple values with whitespace. They will be
+ tried in turn, until one is written without error. If none of the writes succeed, the operation will
+ be aborted.</para>
+
+ <para>The allowed set of values is determined by the kernel and is shown in the file itself (use
+ <command>cat /sys/power/state</command> to display). See <ulink
+ url="https://docs.kernel.org/admin-guide/pm/sleep-states.html#basic-sysfs-interfaces-for-system-suspend-and-hibernation">
+ Basic sysfs Interfaces for System Suspend and Hibernation</ulink> for more details.</para>
+
+ <para>
+ <citerefentry><refentrytitle>systemd-suspend-then-hibernate.service</refentrytitle><manvolnum>8</manvolnum></citerefentry>
+ uses this value when suspending.</para>
+
+ <xi:include href="version-info.xml" xpointer="v203"/></listitem>
+ </varlistentry>
+
<varlistentry>
<term><varname>HibernateMode=</varname></term>
</varlistentry>
<varlistentry>
- <term><varname>SuspendState=</varname></term>
+ <term><varname>MemorySleepMode=</varname></term>
- <listitem><para>The string to be written to <filename>/sys/power/state</filename> by <citerefentry>
- <refentrytitle>systemd-suspend.service</refentrytitle><manvolnum>8</manvolnum></citerefentry>.
+ <listitem><para>The string to be written to <filename>/sys/power/mem_sleep</filename>
+ when <option>SuspendState=mem</option> or <command>hybrid-sleep</command> is used.
More than one value can be specified by separating multiple values with whitespace. They will be
tried in turn, until one is written without error. If none of the writes succeed, the operation will
- be aborted.</para>
+ be aborted. Defaults to empty, i.e. the kernel default or kernel command line option
+ <varname>mem_sleep_default=</varname> is respected.</para>
<para>The allowed set of values is determined by the kernel and is shown in the file itself (use
- <command>cat /sys/power/state</command> to display). See <ulink
- url="https://docs.kernel.org/admin-guide/pm/sleep-states.html#basic-sysfs-interfaces-for-system-suspend-and-hibernation">
+ <command>cat /sys/power/mem_sleep</command> to display). See the kernel documentation page
+ <ulink url="https://docs.kernel.org/admin-guide/pm/sleep-states.html#basic-sysfs-interfaces-for-system-suspend-and-hibernation">
Basic sysfs Interfaces for System Suspend and Hibernation</ulink> for more details.</para>
- <para>
- <citerefentry><refentrytitle>systemd-suspend-then-hibernate.service</refentrytitle><manvolnum>8</manvolnum></citerefentry>
- uses this value when suspending.</para>
-
- <xi:include href="version-info.xml" xpointer="v203"/></listitem>
+ <xi:include href="version-info.xml" xpointer="v256"/></listitem>
</varlistentry>
<varlistentry>
</varlistentry>
<varlistentry>
- <term><option>--fdname=</option><replaceable>NAME</replaceable><optional>:<replaceable>NAME</replaceable>…</optional></term>
+ <term><option>--fdname=<replaceable>NAME</replaceable><optional>:<replaceable>NAME</replaceable>…</optional></option></term>
<listitem><para>Specify names for the file descriptors passed. This is equivalent to setting
<varname>FileDescriptorName=</varname> in socket unit files, and enables use of
</varlistentry>
<varlistentry>
- <term><option>--mutable=</option><replaceable>BOOL</replaceable>|<replaceable>auto</replaceable>|<replaceable>import</replaceable></term>
+ <term><option>--mutable=<replaceable>BOOL</replaceable>|<replaceable>auto</replaceable>|<replaceable>import</replaceable></option></term>
<listitem><para>Set mutable mode.</para>
<variablelist>
</varlistentry>
<varlistentry>
- <term><option>--noexec=</option><replaceable>BOOL</replaceable></term>
+ <term><option>--noexec=<replaceable>BOOL</replaceable></option></term>
<listitem><para>When merging configuration extensions into <filename>/etc/</filename> the
<literal>MS_NOEXEC</literal> mount flag is used by default. This option can be used to disable
<term><varname>ReloadLimitIntervalSec=</varname></term>
<term><varname>ReloadLimitBurst=</varname></term>
- <listitem><para>Rate limiting for daemon-reload requests. Default to unset, and any number of daemon-reload
- operations can be requested at any time. <varname>ReloadLimitIntervalSec=</varname> takes a value in seconds
- to configure the rate limit window, and <varname>ReloadLimitBurst=</varname> takes a positive integer to
- configure the maximum allowed number of reloads within the configured time window.</para>
+ <listitem><para>Rate limiting for daemon-reload and (since v256) daemon-reexec requests. The setting
+ applies to both operations, but the rate limits are tracked separately. Defaults to unset, and any
+ number of operations can be requested at any time. <varname>ReloadLimitIntervalSec=</varname> takes
+ a value in seconds to configure the rate limit window, and <varname>ReloadLimitBurst=</varname>
+ takes a positive integer to configure the maximum allowed number of operations within the configured
+ time window.</para>
<xi:include href="version-info.xml" xpointer="v253"/></listitem>
</varlistentry>
<refsect1>
<title>Description</title>
- <para><command>systemd-vmspawn</command> may be used to start a virtual machine from an OS image. In many ways it is similar to <citerefentry
- project='man-pages'><refentrytitle>systemd-nspawn</refentrytitle><manvolnum>1</manvolnum></citerefentry>, but
+ <para><command>systemd-vmspawn</command> may be used to start a virtual machine from an OS image. In many ways it is similar to <citerefentry>
+ <refentrytitle>systemd-nspawn</refentrytitle><manvolnum>1</manvolnum></citerefentry>, but
launches a full virtual machine instead of using namespaces.</para>
<para>File descriptors for <filename>/dev/kvm</filename> and <filename>/dev/vhost-vsock</filename> can be
<variablelist>
<varlistentry>
- <term><option>--cpus=</option><replaceable>CPUS</replaceable></term>
+ <term><option>--cpus=<replaceable>CPUS</replaceable></option></term>
<listitem><para>Configures the number of CPUs to start the virtual machine with.
Defaults to 1.</para>
</varlistentry>
<varlistentry>
- <term><option>--ram=</option><replaceable>BYTES</replaceable></term>
+ <term><option>--ram=<replaceable>BYTES</replaceable></option></term>
<listitem><para>Configures the amount of memory to start the virtual machine with.
Defaults to 2G.</para>
</varlistentry>
<varlistentry>
- <term><option>--kvm=</option><replaceable>BOOL</replaceable></term>
+ <term><option>--kvm=<replaceable>BOOL</replaceable></option></term>
<listitem><para>Configures whether to use KVM. If the option is not specified KVM support will be
detected automatically. If true, KVM is always used, and if false, KVM is never used.</para>
</varlistentry>
<varlistentry>
- <term><option>--vsock=</option><replaceable>BOOL</replaceable></term>
+ <term><option>--vsock=<replaceable>BOOL</replaceable></option></term>
<listitem>
<para>Configure whether to use VSOCK networking.</para>
</varlistentry>
<varlistentry>
- <term><option>--vsock-cid=</option><replaceable>CID</replaceable></term>
+ <term><option>--vsock-cid=<replaceable>CID</replaceable></option></term>
<listitem>
<para>Configure vmspawn to use a specific CID for the guest.</para>
</varlistentry>
<varlistentry>
- <term><option>--tpm=</option><replaceable>BOOL</replaceable></term>
+ <term><option>--tpm=<replaceable>BOOL</replaceable></option></term>
<listitem>
<para>Configure whether to use VM with a virtual TPM or not.</para>
- <para>If the option is not specified vmspawn will detect the presence of <citerefentry project='man-pages'>
+ <para>If the option is not specified vmspawn will detect the presence of <citerefentry project='debian'>
<refentrytitle>swtpm</refentrytitle><manvolnum>8</manvolnum></citerefentry> and use it if available.
- If yes is specified <citerefentry project='man-pages'><refentrytitle>swtpm</refentrytitle><manvolnum>8</manvolnum></citerefentry>
- is always used, and vice versa if no is set <citerefentry project='man-pages'><refentrytitle>swtpm</refentrytitle>
+ If yes is specified <citerefentry project='debian'><refentrytitle>swtpm</refentrytitle><manvolnum>8</manvolnum></citerefentry>
+ is always used, and vice versa if no is set <citerefentry project='debian'><refentrytitle>swtpm</refentrytitle>
<manvolnum>8</manvolnum></citerefentry> is never used.</para>
<para>Note: the virtual TPM used may change in future.</para>
</varlistentry>
<varlistentry>
- <term><option>--linux=</option><replaceable>PATH</replaceable></term>
+ <term><option>--linux=<replaceable>PATH</replaceable></option></term>
<listitem>
<para>Set the linux kernel image to use for direct kernel boot.</para>
</varlistentry>
<varlistentry>
- <term><option>--initrd=</option><replaceable>PATH</replaceable></term>
+ <term><option>--initrd=<replaceable>PATH</replaceable></option></term>
<listitem>
<para>Set the initrd to use for direct kernel boot.</para>
</varlistentry>
<varlistentry>
- <term><option>--firmware=</option><replaceable>PATH</replaceable></term>
+ <term><option>--firmware=<replaceable>PATH</replaceable></option></term>
<listitem><para>Takes an absolute path, or a relative path beginning with
<filename>./</filename>. Specifies a JSON firmware definition file, which allows selecting the
</varlistentry>
<varlistentry>
- <term><option>--secure-boot=</option><replaceable>BOOL</replaceable></term>
+ <term><option>--discard-disk=<replaceable>BOOL</replaceable></option></term>
+
+ <listitem><para>Controls whether qemu processes discard requests from the VM.
+ This prevents long running VMs from using more disk space than required.
+ This is enabled by default.</para>
+
+ <xi:include href="version-info.xml" xpointer="v256"/></listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><option>--secure-boot=<replaceable>BOOL</replaceable></option></term>
<listitem><para>Configure whether to search for firmware which supports Secure Boot.</para>
<variablelist>
<varlistentry>
- <term><option>--private-users=</option><replaceable>UID_SHIFT[:UID_RANGE]</replaceable></term>
+ <term><option>--private-users=<replaceable>UID_SHIFT[:UID_RANGE]</replaceable></option></term>
<listitem><para>Controls user namespacing under <option>--directory=</option>.
- If enabled, <citerefentry project='man-pages'><refentrytitle>virtiofsd</refentrytitle><manvolnum>1</manvolnum></citerefentry>
- is instructed to map user and group ids (UIDs and GIDs). This involves mapping the private UIDs/GIDs used in the virtual machine
- (starting with the virtual machine's root user 0 and up) to a range of UIDs/GIDs on the host that are not used for other
- purposes (usually in the range beyond the host's UID/GID 65536).</para>
+ If enabled, <command>virtiofsd</command> is instructed to map user and group ids (UIDs and GIDs).
+ This involves mapping the private UIDs/GIDs used in the virtual machine (starting with the virtual machine's
+ root user 0 and up) to a range of UIDs/GIDs on the host that are not used for other purposes (usually in the
+ range beyond the host's UID/GID 65536).</para>
<para>If one or two colon-separated numbers are specified, user namespacing is turned on. <replaceable>UID_SHIFT</replaceable>
specifies the first host UID/GID to map, <replaceable>UID_RANGE</replaceable> is optional and specifies number of host
<variablelist>
<varlistentry>
- <term><option>--bind=</option><replaceable>PATH</replaceable></term>
- <term><option>--bind-ro=</option><replaceable>PATH</replaceable></term>
+ <term><option>--bind=<replaceable>PATH</replaceable></option></term>
+ <term><option>--bind-ro=<replaceable>PATH</replaceable></option></term>
<listitem><para>Mount a directory from the host into the virtual machine. Takes one of: a path
argument — in which case the specified path will be mounted from the host to the same path in the virtual machine, or
</varlistentry>
<varlistentry>
- <term><option>--extra-drive=</option><replaceable>PATH</replaceable></term>
+ <term><option>--extra-drive=<replaceable>PATH</replaceable></option></term>
<listitem><para>Takes a disk image or block device on the host and supplies it to the virtual machine as another drive.</para>
<variablelist>
<varlistentry>
- <term><option>--forward-journal=</option><replaceable>FILE|DIR</replaceable></term>
+ <term><option>--forward-journal=<replaceable>FILE|DIR</replaceable></option></term>
<listitem><para>Forward the virtual machine's journal to the host.
<citerefentry><refentrytitle>systemd-journal-remote</refentrytitle><manvolnum>8</manvolnum></citerefentry>
</varlistentry>
<varlistentry>
- <term><option>--pass-ssh-key=</option><replaceable>BOOL</replaceable></term>
+ <term><option>--pass-ssh-key=<replaceable>BOOL</replaceable></option></term>
<listitem><para>By default an SSH key is generated to allow <command>systemd-vmspawn</command> to open
a D-Bus connection to the VM's systemd bus. Setting this to "no" will disable SSH key generation.</para>
</varlistentry>
<varlistentry>
- <term><option>--ssh-key-type=</option><replaceable>TYPE</replaceable></term>
+ <term><option>--ssh-key-type=<replaceable>TYPE</replaceable></option></term>
<listitem><para>Configures the type of SSH key to generate, see
- <citerefentry><refentrytitle>ssh-keygen</refentrytitle><manvolnum>1</manvolnum></citerefentry>
+ <citerefentry project="man-pages"><refentrytitle>ssh-keygen</refentrytitle><manvolnum>1</manvolnum></citerefentry>
for more information.</para>
<para>By default <literal>ed25519</literal> keys are generated, however <literal>rsa</literal> keys
<variablelist>
<varlistentry>
- <term><option>--console=</option><replaceable>MODE</replaceable></term>
+ <term><option>--console=<replaceable>MODE</replaceable></option></term>
<listitem><para>Configures how to set up the console of the VM. Takes one of
<literal>interactive</literal>, <literal>read-only</literal>, <literal>native</literal>,
<variablelist>
<varlistentry>
- <term><option>--load-credential=</option><replaceable>ID</replaceable>:<replaceable>PATH</replaceable></term>
- <term><option>--set-credential=</option><replaceable>ID</replaceable>:<replaceable>VALUE</replaceable></term>
+ <term><option>--load-credential=<replaceable>ID</replaceable>:<replaceable>PATH</replaceable></option></term>
+ <term><option>--set-credential=<replaceable>ID</replaceable>:<replaceable>VALUE</replaceable></option></term>
<listitem><para>Pass a credential to the virtual machine. These two options correspond to the
<varname>LoadCredential=</varname> and <varname>SetCredential=</varname> settings in unit files. See
<title>See Also</title>
<para><simplelist type="inline">
<member><citerefentry><refentrytitle>systemd</refentrytitle><manvolnum>1</manvolnum></citerefentry></member>
- <member><citerefentry><refentrytitle>mkosi</refentrytitle><manvolnum>1</manvolnum></citerefentry></member>
+ <member><citerefentry project='debian'><refentrytitle>mkosi</refentrytitle><manvolnum>1</manvolnum></citerefentry></member>
<member><citerefentry><refentrytitle>machinectl</refentrytitle><manvolnum>1</manvolnum></citerefentry></member>
<member><citerefentry><refentrytitle>importctl</refentrytitle><manvolnum>1</manvolnum></citerefentry></member>
</simplelist></para>
units, it only enables sharing of the <filename>/tmp/</filename> and <filename>/var/tmp/</filename>
directories.</para>
- <para>Other file system namespace unit settings — <varname>PrivateMounts=</varname>,
- <varname>PrivateTmp=</varname>, <varname>PrivateDevices=</varname>, <varname>ProtectSystem=</varname>,
- <varname>ProtectHome=</varname>, <varname>ReadOnlyPaths=</varname>, <varname>InaccessiblePaths=</varname>,
- <varname>ReadWritePaths=</varname>, … — also enable file system namespacing in a fashion equivalent to this
- option. Hence it is primarily useful to explicitly request this behaviour if none of the other settings are
- used.</para>
+ <para>Other file system namespace unit settings — <varname>PrivateTmp=</varname>,
+ <varname>PrivateDevices=</varname>, <varname>ProtectSystem=</varname>,
+ <varname>ProtectHome=</varname>, <varname>ReadOnlyPaths=</varname>,
+ <varname>InaccessiblePaths=</varname>, <varname>ReadWritePaths=</varname>, … — also enable file
+ system namespacing in a fashion equivalent to this option. Hence it is primarily useful to explicitly
+ request this behaviour if none of the other settings are used.</para>
<xi:include href="system-or-user-ns.xml" xpointer="singular"/>
<literal>\x7efoobar</literal> would add a pattern matching <literal>~foobar</literal> to the allow list.</para>
<para>Log messages are tested against denied patterns (if any), then against allowed patterns
- (if any). If a log message matches any of the denied patterns, it will be discarded, whatever the
- allowed patterns. Then, remaining log messages are tested against allowed patterns. Messages matching
+ (if any). If a log message matches any of the denied patterns, it is discarded immediately without considering
+ allowed patterns. Remaining log messages are tested against allowed patterns. Messages matching
against none of the allowed pattern are discarded. If no allowed patterns are defined, then all
messages are processed directly after going through denied filters.</para>
<listitem>
<para>The Base64 encoded private key for the interface. It can be generated using
the <command>wg genkey</command> command
- (see <citerefentry project="wireguard"><refentrytitle>wg</refentrytitle><manvolnum>8</manvolnum></citerefentry>).
+ (see <citerefentry project='man-pages'><refentrytitle>wg</refentrytitle><manvolnum>8</manvolnum></citerefentry>).
Specially, if the specified key is prefixed with <literal>@</literal>, it is interpreted as
the name of the credential from which the actual key shall be read. <command>systemd-networkd.service</command>
automatically imports credentials matching <literal>network.wireguard.*</literal>. For more details
<term><varname>PublicKey=</varname></term>
<listitem>
<para>Sets a Base64 encoded public key calculated by <command>wg pubkey</command>
- (see <citerefentry project="wireguard"><refentrytitle>wg</refentrytitle><manvolnum>8</manvolnum></citerefentry>)
+ (see <citerefentry project='man-pages'><refentrytitle>wg</refentrytitle><manvolnum>8</manvolnum></citerefentry>)
from a private key, and usually transmitted out of band to the author of the configuration file.
This option honors the <literal>@</literal> prefix in the same way as the <option>PrivateKey=</option>
setting of the <option>[WireGuard]</option> section. This option is mandatory for this section.</para>
Defaults to <literal>no</literal>. Further settings for the DHCP server may be set in the
[DHCPServer] section described below.</para>
- <para>Even if this is enabled, the DHCP server will not be started automatically. It will be
- started after <filename>systemd-networkd-persistent-storage.service</filename> is started, which
- calls <command>networkctl persistent-storage yes</command>. See
+ <para>Even if this is enabled, the DHCP server will not be started automatically and wait for the
+ persistent storage being ready to load/save leases in the storage, unless
+ <varname>RelayTarget=</varname> or <varname>PersistLeases=no</varname> are specified in the
+ [DHCPServer] section. It will be started after
+ <filename>systemd-networkd-persistent-storage.service</filename> is started, which calls
+ <command>networkctl persistent-storage yes</command>. See
<citerefentry><refentrytitle>networkctl</refentrytitle><manvolnum>1</manvolnum></citerefentry>
for more details.</para>
effect of the <option>Domains=</option> setting. If set to <option>route</option>, the domain name
received from the DHCP server will be used for routing DNS queries only, but not for searching,
similarly to the effect of the <option>Domains=</option> setting when the argument is prefixed with
- <literal>~</literal>. Defaults to false.</para>
+ <literal>~</literal>. When unspecified, the value specified in the same setting in
+ <citerefentry><refentrytitle>networkd.conf</refentrytitle><manvolnum>5</manvolnum></citerefentry>,
+ which defaults to <literal>no</literal>, will be used.</para>
<para>It is recommended to enable this option only on trusted networks, as setting this
affects resolution of all hostnames, in particular of single-label names. It is generally
with the <varname>IPv6AcceptRA=</varname> setting described above:</para>
<variablelist class='network-directives'>
+ <varlistentry>
+ <term><varname>UseRedirect=</varname></term>
+ <listitem>
+ <para>When true (the default), Redirect message sent by the current first-hop router will be
+ accepted, and configures routes to redirected nodes will be configured.</para>
+
+ <xi:include href="version-info.xml" xpointer="v256"/>
+ </listitem>
+ </varlistentry>
+
<varlistentry>
<term><varname>Token=</varname></term>
<listitem>
</listitem>
</varlistentry>
+ <varlistentry>
+ <term><varname>PersistLeases=</varname></term>
+ <listitem>
+ <para>Takes a boolean. When true, the DHCP server will load and save leases in the persistent
+ storage. When false, the DHCP server will neither load nor save leases in the persistent storage.
+ Hence, bound leases will be lost when the interface is reconfigured e.g. by
+ <command>networkctl reconfigure</command>, or <filename>systemd-networkd.service</filename>
+ is restarted. That may cause address conflict on the network. So, please take an extra care when
+ disable this setting. When unspecified, the value specified in the same setting in
+ <citerefentry><refentrytitle>networkd.conf</refentrytitle><manvolnum>5</manvolnum></citerefentry>,
+ which defaults to <literal>yes</literal>, will be used.</para>
+
+ <xi:include href="version-info.xml" xpointer="v256"/>
+ </listitem>
+ </varlistentry>
+
</variablelist>
</refsect1>
Description=Simple notifying service
[Service]
-Type=notify
+Type=notify-reload
ExecStart=/usr/sbin/simple-notifying-service
[Install]
<citerefentry><refentrytitle>systemd.kill</refentrytitle><manvolnum>5</manvolnum></citerefentry>
for details on how you can influence the way systemd terminates
the service.</para>
+
+ <para>To avoid code duplication, it is preferable to use
+ <citerefentry><refentrytitle>sd_notify</refentrytitle><manvolnum>3</manvolnum></citerefentry>
+ when possible, especially when other APIs provided by
+ <citerefentry><refentrytitle>libsystemd</refentrytitle><manvolnum>3</manvolnum></citerefentry> are
+ also used, but note that the notification protocol is very simple and guaranteed to be stable as per
+ the <ulink url="https://systemd.io/PORTABILITY_AND_STABILITY/">Interface Portability and Stability
+ Promise</ulink>, so it can be reimplemented by services with no external dependencies. For a
+ self-contained example, see
+ <citerefentry><refentrytitle>sd_notify</refentrytitle><manvolnum>3</manvolnum></citerefentry>.</para>
</example>
</refsect1>
<varlistentry>
<term><varname>MaxConnections=</varname></term>
- <listitem><para>The maximum number of connections to
- simultaneously run services instances for, when
- <option>Accept=yes</option> is set. If more concurrent
- connections are coming in, they will be refused until at least
- one existing connection is terminated. This setting has no
- effect on sockets configured with
- <option>Accept=no</option> or datagram sockets. Defaults to
- 64.</para></listitem>
+ <listitem><para>The maximum number of connections to simultaneously run services instances for, when
+ <option>Accept=yes</option> is set. If more concurrent connections are coming in, they will be refused
+ until at least one existing connection is terminated. This setting has no effect on sockets configured
+ with <option>Accept=no</option> or datagram sockets. Defaults to 64.</para></listitem>
</varlistentry>
<varlistentry>
<listitem><para>The maximum number of connections for a service per source IP address (in case of
IPv4/IPv6), per source CID (in case of <constant>AF_VSOCK</constant>), or source UID (in case of
<constant>AF_UNIX</constant>). This is very similar to the <varname>MaxConnections=</varname>
- directive above. Disabled by default.</para>
+ directive above. Defaults to 0, i.e. disabled.</para>
<xi:include href="version-info.xml" xpointer="v232"/>
</listitem>
<varlistentry>
<term><varname>TCPCongestion=</varname></term>
<listitem><para>Takes a string value. Controls the TCP congestion algorithm used by this
- socket. Should be one of <literal>westwood</literal>, <literal>veno</literal>,
+ socket. Should be one of <literal>westwood</literal>, <literal>reno</literal>,
<literal>cubic</literal>, <literal>lp</literal> or any other available algorithm supported by the IP
stack. This setting applies only to stream sockets.</para></listitem>
</varlistentry>
</varlistentry>
<varlistentry>
- <term><varname>network.netdev.*</varname></term>
+ <term><varname>network.conf.*</varname></term>
<term><varname>network.link.*</varname></term>
+ <term><varname>network.netdev.*</varname></term>
<term><varname>network.network.*</varname></term>
<listitem>
<para>Configures network devices. Read by
- <citerefentry><refentrytitle>systemd-network-generator.service</refentrytitle><manvolnum>8</manvolnum></citerefentry>. These
- credentials directly translate to a matching <filename>*.netdev</filename>,
- <filename>*.link</filename> or <filename>*.network</filename> file. Example: the contents of a
- credential <filename>network.link.50-foobar</filename> will be copied into a file
- <filename>50-foobar.link</filename>. See
- <citerefentry><refentrytitle>systemd.netdev</refentrytitle><manvolnum>5</manvolnum></citerefentry>,
+ <citerefentry><refentrytitle>systemd-network-generator.service</refentrytitle><manvolnum>8</manvolnum></citerefentry>.
+ These credentials should contain valid
+ <citerefentry><refentrytitle>networkd.conf</refentrytitle><manvolnum>5</manvolnum></citerefentry>,
<citerefentry><refentrytitle>systemd.link</refentrytitle><manvolnum>5</manvolnum></citerefentry>,
+ <citerefentry><refentrytitle>systemd.netdev</refentrytitle><manvolnum>5</manvolnum></citerefentry>,
<citerefentry><refentrytitle>systemd.network</refentrytitle><manvolnum>5</manvolnum></citerefentry>
- for details.</para>
+ configuration data. From each matching credential a separate file is created. Example: the contents
+ of a credential <filename>network.link.50-foobar</filename> will be copied into a file
+ <filename>50-foobar.link</filename>.</para>
<para>Note that the resulting files are created world-readable, it's hence recommended to not include
secrets in these credentials, but supply them via separate credentials directly to
<xi:include href="version-info.xml" xpointer="v256"/>
</listitem>
</varlistentry>
+
+ <varlistentry>
+ <term><varname>systemd.extra-unit.*</varname></term>
+ <term><varname>systemd.unit-dropin.*</varname></term>
+
+ <listitem><para>These credentials specify extra units and drop-ins to add to the system. For details
+ see <citerefentry><refentrytitle>systemd-debug-generator</refentrytitle><manvolnum>8</manvolnum></citerefentry>.</para>
+
+ <xi:include href="version-info.xml" xpointer="v256"/></listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><varname>udev.conf.*</varname></term>
+ <term><varname>udev.rules.*</varname></term>
+
+ <listitem>
+ <para>Configures udev configuration file and udev rules. Read by
+ <filename>systemd-udev-load-credentials.service</filename>, which invokes
+ <command>udevadm control --load-credentials</command>. These credentials directly translate to a
+ matching
+ <citerefentry><refentrytitle>udev.conf</refentrytitle><manvolnum>5</manvolnum></citerefentry> or
+ <citerefentry><refentrytitle>udev</refentrytitle><manvolnum>7</manvolnum></citerefentry> rules
+ file. Example: the contents of a credential
+ <filename>udev.conf.50-foobar</filename> will be copied into a file
+ <filename>/run/udev/udev.conf.d/50-foobar.conf</filename>, and
+ <filename>udev.rules.50-foobar</filename> will be copied into a file
+ <filename>/run/udev/rules.d/50-foobar.rules</filename>. See
+ <citerefentry><refentrytitle>udev</refentrytitle><manvolnum>7</manvolnum></citerefentry>,
+ <citerefentry><refentrytitle>udev.conf</refentrytitle><manvolnum>5</manvolnum></citerefentry>, and
+ <citerefentry><refentrytitle>udevadm</refentrytitle><manvolnum>8</manvolnum></citerefentry>
+ for details.</para>
+
+ <xi:include href="version-info.xml" xpointer="v256"/>
+ </listitem>
+ </varlistentry>
</variablelist>
</refsect1>
</varlistentry>
<varlistentry>
- <term><option>--crash-vt=</option><replaceable>VT</replaceable></term>
+ <term><option>--crash-vt=<replaceable>VT</replaceable></option></term>
<listitem><para>Switch to a specific virtual console (VT) on crash. This switch has no effect when
running as user instance. Same as <varname>systemd.crash_chvt=</varname> above (but not the
Base64 decoding is applied to the credential contents.</para>
<para>Note that for all line types that result in creation of any kind of file node
- (i.e. <varname>f</varname>/<varname>F</varname>,
+ (i.e. <varname>f</varname>,
<varname>d</varname>/<varname>D</varname>/<varname>v</varname>/<varname>q</varname>/<varname>Q</varname>,
<varname>p</varname>, <varname>L</varname>, <varname>c</varname>/<varname>b</varname> and <varname>C</varname>)
leading directories are implicitly created if needed, owned by root with an access mode of 0755. In order to
<para>For <varname>L</varname> lines determines the destination path of the symlink. For <varname>c</varname> and
<varname>b</varname>, determines the major/minor of the device node, with major and minor formatted as integers,
- separated by <literal>:</literal>, e.g. <literal>1:3</literal>. For <varname>f</varname>, <varname>F</varname>,
+ separated by <literal>:</literal>, e.g. <literal>1:3</literal>. For <varname>f</varname>
and <varname>w</varname>, the argument may be used to specify a short string that is written to the file,
suffixed by a newline. For <varname>C</varname>, specifies the source file or directory. For <varname>t</varname>
and <varname>T</varname>, determines extended attributes to be set. For <varname>a</varname> and
</varlistentry>
<varlistentry>
<term><option>-m</option></term>
- <term><option>--children-max=</option><replaceable>value</replaceable></term>
+ <term><option>--children-max=<replaceable>value</replaceable></option></term>
<listitem>
<para>Set the maximum number of events, systemd-udevd will handle at the same time. When 0 is
specified, then the maximum is determined based on the system resources.</para>
</varlistentry>
<varlistentry>
<term><option>-t</option></term>
- <term><option>--timeout=</option><replaceable>seconds</replaceable></term>
+ <term><option>--timeout=<replaceable>seconds</replaceable></option></term>
<listitem>
<para>The maximum number of seconds to wait for a reply from systemd-udevd.</para>
</listitem>
</varlistentry>
+ <varlistentry>
+ <term><option>--load-credentials</option></term>
+ <listitem>
+ <para>When specified, the following credentials are used when passed in:</para>
+
+ <variablelist>
+ <varlistentry>
+ <term><varname>udev.conf.*</varname></term>
+ <listitem>
+ <para>These credentials should contain valid
+ <citerefentry><refentrytitle>udev.conf</refentrytitle><manvolnum>5</manvolnum></citerefentry>
+ configuration data. From each matching credential a separate file is created. Example: a
+ passed credential <filename>udev.conf.50-foobar</filename> will be copied into a
+ configuration file <filename>/run/udev/udev.conf.d/50-foobar.conf</filename>.</para>
+
+ <xi:include href="version-info.xml" xpointer="v256"/>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term><varname>udev.rules.*</varname></term>
+ <listitem>
+ <para>These credentials should contain valid
+ <citerefentry><refentrytitle>udev</refentrytitle><manvolnum>7</manvolnum></citerefentry>
+ rules. From each matching credential a separate file is created. Example: a passed credential
+ <filename>udev.rules.50-foobar</filename> will be copied into a configuration file
+ <filename>/run/udev/rules.d/50-foobar.rules</filename>.</para>
+
+ <xi:include href="version-info.xml" xpointer="v256"/>
+ </listitem>
+ </varlistentry>
+ </variablelist>
+
+ <para>Note, this <emphasis>does not</emphasis> imply <option>--reload</option> option. So, if
+ <command>systemd-udevd</command> is already running, please consider to also specify
+ <option>-reload</option> to make the copied udev rules files used by
+ <command>systemd-udevd</command>.</para>
+
+ <xi:include href="version-info.xml" xpointer="v256"/>
+ </listitem>
+ </varlistentry>
+
<xi:include href="standard-options.xml" xpointer="help" />
</variablelist>
</refsect2>
<variablelist>
<varlistentry>
- <term><option>--output=</option><replaceable>MODE</replaceable></term>
+ <term><option>--output=<replaceable>MODE</replaceable></option></term>
<listitem><para>Choose the output mode, takes one of <literal>classic</literal>,
<literal>friendly</literal>, <literal>table</literal>, <literal>json</literal>. If
</varlistentry>
<varlistentry>
- <term><option>--json=</option><replaceable>FORMAT</replaceable></term>
+ <term><option>--json=<replaceable>FORMAT</replaceable></option></term>
<listitem><para>Selects JSON output mode (like <option>--output=json</option>) and selects the
precise display mode. Takes one of <literal>pretty</literal> or <literal>short</literal>. If
</varlistentry>
<varlistentry>
- <term><option>--service=</option><replaceable>SERVICE</replaceable><optional>:<replaceable>SERVICE…</replaceable></optional></term>
+ <term><option>--service=<replaceable>SERVICE</replaceable><optional>:<replaceable>SERVICE…</replaceable></optional></option></term>
<term><option>-s</option> <replaceable>SERVICE</replaceable>:<replaceable>SERVICE…</replaceable></term>
<listitem><para>Controls which services to query for users/groups. Takes a list of one or more
</varlistentry>
<varlistentry>
- <term><option>--with-nss=</option><replaceable>BOOL</replaceable></term>
+ <term><option>--with-nss=<replaceable>BOOL</replaceable></option></term>
<listitem><para>Controls whether to include classic glibc/NSS user/group lookups in the output. If
<option>--with-nss=no</option> is used any attempts to resolve or enumerate users/groups provided
</varlistentry>
<varlistentry>
- <term><option>--with-varlink=</option><replaceable>BOOL</replaceable></term>
+ <term><option>--with-varlink=<replaceable>BOOL</replaceable></option></term>
<listitem><para>Controls whether to include Varlink user/group lookups in the output, i.e. those done
via the <ulink url="https://systemd.io/USER_GROUP_API">User/Group Record Lookup API via
</varlistentry>
<varlistentry>
- <term><option>--with-dropin=</option><replaceable>BOOL</replaceable></term>
+ <term><option>--with-dropin=<replaceable>BOOL</replaceable></option></term>
<listitem><para>Controls whether to include user/group lookups in the output that are defined using
drop-in files in <filename>/etc/userdb/</filename>, <filename>/run/userdb/</filename>,
</varlistentry>
<varlistentry>
- <term><option>--synthesize=</option><replaceable>BOOL</replaceable></term>
+ <term><option>--synthesize=<replaceable>BOOL</replaceable></option></term>
<listitem><para>Controls whether to synthesize records for the root and nobody users/groups if they
aren't defined otherwise. By default (or <literal>yes</literal>) such records are implicitly
</varlistentry>
<varlistentry>
- <term><option>--multiplexer=</option><replaceable>BOOL</replaceable></term>
+ <term><option>--multiplexer=<replaceable>BOOL</replaceable></option></term>
<listitem><para>Controls whether to do lookups via the multiplexer service (if specified as true, the
default) or do lookups in the client (if specified as false). Using the multiplexer service is
</varlistentry>
<varlistentry>
- <term><option>--json=</option><replaceable>MODE</replaceable></term>
+ <term><option>--json=<replaceable>MODE</replaceable></option></term>
<listitem>
<para>Selects the JSON output formatting, one of <literal>pretty</literal> (for nicely indented,
/* SPDX-License-Identifier: MIT-0 */
+#define _GNU_SOURCE 1
#include <errno.h>
#include <stdbool.h>
#include <stddef.h>
#define _cleanup_(f) __attribute__((cleanup(f)))
-#define check(x) ({ \
- int r = (x); \
- errno = r < 0 ? -r : 0; \
- printf(#x ": %m\n"); \
- if (r < 0) \
- return EXIT_FAILURE; \
- })
-
typedef struct object {
char *name;
uint32_t number;
} object;
static int method(sd_bus_message *m, void *userdata, sd_bus_error *error) {
+ int r;
+
printf("Got called with userdata=%p\n", userdata);
if (sd_bus_message_is_method_call(m,
return 1;
const char *string;
- check(sd_bus_message_read(m, "s", &string));
- check(sd_bus_reply_method_return(m, "s", string));
+ r = sd_bus_message_read(m, "s", &string);
+ if (r < 0) {
+ fprintf(stderr, "sd_bus_message_read() failed: %s\n", strerror(-r));
+ return 0;
+ }
+
+ r = sd_bus_reply_method_return(m, "s", string);
+ if (r < 0) {
+ fprintf(stderr, "sd_bus_reply_method_return() failed: %s\n", strerror(-r));
+ return 0;
+ }
return 1;
}
int main(int argc, char **argv) {
_cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL;
+ int r;
sd_bus_default(&bus);
object object = { .number = 666 };
- check((object.name = strdup("name")) != NULL);
+ object.name = strdup("name");
+ if (!object.name) {
+ fprintf(stderr, "OOM\n");
+ return EXIT_FAILURE;
+ }
- check(sd_bus_add_object_vtable(bus, NULL,
- "/org/freedesktop/systemd/VtableExample",
- "org.freedesktop.systemd.VtableExample",
- vtable,
- &object));
+ r = sd_bus_add_object_vtable(bus, NULL,
+ "/org/freedesktop/systemd/VtableExample",
+ "org.freedesktop.systemd.VtableExample",
+ vtable,
+ &object);
+ if (r < 0) {
+ fprintf(stderr, "sd_bus_add_object_vtable() failed: %s\n", strerror(-r));
+ return EXIT_FAILURE;
+ }
- check(sd_bus_request_name(bus,
- "org.freedesktop.systemd.VtableExample",
- 0));
+ r = sd_bus_request_name(bus,
+ "org.freedesktop.systemd.VtableExample",
+ 0);
+ if (r < 0) {
+ fprintf(stderr, "sd_bus_request_name() failed: %s\n", strerror(-r));
+ return EXIT_FAILURE;
+ }
for (;;) {
- check(sd_bus_wait(bus, UINT64_MAX));
- check(sd_bus_process(bus, NULL));
+ r = sd_bus_wait(bus, UINT64_MAX);
+ if (r < 0) {
+ fprintf(stderr, "sd_bus_wait() failed: %s\n", strerror(-r));
+ return EXIT_FAILURE;
+ }
+
+ r = sd_bus_process(bus, NULL);
+ if (r < 0) {
+ fprintf(stderr, "sd_bus_process() failed: %s\n", strerror(-r));
+ return EXIT_FAILURE;
+ }
+ }
+
+ r = sd_bus_release_name(bus, "org.freedesktop.systemd.VtableExample");
+ if (r < 0) {
+ fprintf(stderr, "sd_bus_release_name() failed: %s\n", strerror(-r));
+ return EXIT_FAILURE;
}
- check(sd_bus_release_name(bus, "org.freedesktop.systemd.VtableExample"));
free(object.name);
return 0;
sshdconfdir = sysconfdir / 'ssh/sshd_config.d'
endif
+sshdprivsepdir = get_option('sshdprivsepdir')
+conf.set10('CREATE_SSHDPRIVSEPDIR', sshdprivsepdir != 'no' and not sshdprivsepdir.startswith('/usr/'))
+conf.set('SSHDPRIVSEPDIR', sshdprivsepdir, description : 'SSH privilege separation directory')
+
libcryptsetup_plugins_dir = get_option('libcryptsetup-plugins-dir')
if libcryptsetup_plugins_dir == ''
libcryptsetup_plugins_dir = libdir / 'cryptsetup'
conf.set_quoted('SYSTEMD_TTY_ASK_PASSWORD_AGENT_BINARY_PATH', bindir / 'systemd-tty-ask-password-agent')
conf.set_quoted('SYSTEMD_UPDATE_HELPER_PATH', libexecdir / 'systemd-update-helper')
conf.set_quoted('SYSTEMD_USERWORK_PATH', libexecdir / 'systemd-userwork')
+conf.set_quoted('SYSTEMD_MOUNTWORK_PATH', libexecdir / 'systemd-mountwork')
+conf.set_quoted('SYSTEMD_NSRESOURCEWORK_PATH', libexecdir / 'systemd-nsresourcework')
conf.set_quoted('SYSTEMD_VERITYSETUP_PATH', libexecdir / 'systemd-veritysetup')
conf.set_quoted('SYSTEM_CONFIG_UNIT_DIR', pkgsysconfdir / 'system')
conf.set_quoted('SYSTEM_DATA_UNIT_DIR', systemunitdir)
version : '>= 15',
required : get_option('kmod'))
conf.set10('HAVE_KMOD', libkmod.found())
+libkmod_cflags = libkmod.partial_dependency(includes: true, compile_args: true)
libxenctrl = dependency('xencontrol',
version : '>= 4.9',
# link to neither of the libs if one is not found
libgcrypt = []
libgpg_error = []
+ libgcrypt_cflags = []
+else
+ libgcrypt_cflags = libgcrypt.partial_dependency(includes: true, compile_args: true)
endif
conf.set10('HAVE_GCRYPT', have)
feature = get_option('vmspawn').disable_auto_if(conf.get('BUILD_MODE_DEVELOPER') == 0)
conf.set10('ENABLE_VMSPAWN', feature.allowed())
+conf.set10('DEFAULT_MOUNTFSD_TRUSTED_DIRECTORIES', get_option('default-mountfsd-trusted-directories'))
+
foreach term : ['analyze',
'backlight',
'binfmt',
'localed',
'logind',
'machined',
+ 'mountfsd',
'networkd',
'nscd',
+ 'nsresourced',
'nss-myhostname',
'nss-systemd',
'oomd',
bpf_gcc_flags = [
'-std=gnu11',
'-fno-stack-protector',
+ '-fno-ssa-phiopt',
'-O2',
'-mcpu=v3',
'-mco-re',
#####################################################################
check_efi_alignment_py = find_program('tools/check-efi-alignment.py')
+
+#####################################################################
+
+use_provided_vmlinux_h = false
+use_generated_vmlinux_h = false
+provided_vmlinux_h_path = get_option('vmlinux-h-path')
+
+# For the more complex BPF programs we really want a vmlinux.h (which is arch
+# specific, but only somewhat bound to kernel version). Ideally the kernel
+# development headers would ship that, but right now they don't. Hence address
+# this in two ways:
+#
+# 1. Provide a vmlinux.h at build time
+# 2. Generate the file on the fly where possible (which requires /sys/ to be mounted)
+#
+# We generally prefer the former (to support reproducible builds), but will
+# fallback to the latter.
+
+if conf.get('BPF_FRAMEWORK') == 1
+ enable_vmlinux_h = get_option('vmlinux-h')
+
+ if enable_vmlinux_h == 'auto'
+ if provided_vmlinux_h_path != ''
+ use_provided_vmlinux_h = true
+ elif fs.exists('/sys/kernel/btf/vmlinux') and \
+ bpftool.found() and \
+ (host_machine.cpu_family() == build_machine.cpu_family()) and \
+ host_machine.cpu_family() in ['x86_64', 'aarch64']
+
+ # We will only generate a vmlinux.h from the running
+ # kernel if the host and build machine are of the same
+ # family. Also for now we focus on x86_64 and aarch64,
+ # since other archs don't seem to be ready yet.
+
+ use_generated_vmlinux_h = true
+ endif
+ elif enable_vmlinux_h == 'provided'
+ use_provided_vmlinux_h = true
+ elif enable_vmlinux_h == 'generated'
+ if not fs.exists('/sys/kernel/btf/vmlinux')
+ error('BTF data from kernel not available (/sys/kernel/btf/vmlinux missing), cannot generate vmlinux.h, but was asked to.')
+ endif
+ if not bpftool.found()
+ error('bpftool not available, cannot generate vmlinux.h, but was asked to.')
+ endif
+ use_generated_vmlinux_h = true
+ endif
+endif
+
+if use_provided_vmlinux_h
+ if not fs.exists(provided_vmlinux_h_path)
+ error('Path to provided vmlinux.h does not exist.')
+ endif
+ vmlinux_h_dependency = []
+ bpf_o_unstripped_cmd += ['-I' + fs.parent(provided_vmlinux_h_path)]
+ message('Using provided @0@'.format(provided_vmlinux_h_path))
+elif use_generated_vmlinux_h
+ vmlinux_h_dependency = custom_target(
+ 'vmlinux.h',
+ output: 'vmlinux.h',
+ command : [ bpftool, 'btf', 'dump', 'file', '/sys/kernel/btf/vmlinux', 'format', 'c' ],
+ capture : true)
+
+ bpf_o_unstripped_cmd += ['-I' + fs.parent(vmlinux_h_dependency.full_path())]
+ message('Using generated @0@'.format(vmlinux_h_dependency.full_path()))
+else
+ message('Using neither provided nor generated vmlinux.h, some features will not be available.')
+endif
+
+conf.set10('HAVE_VMLINUX_H', use_provided_vmlinux_h or use_generated_vmlinux_h)
+
+#####################################################################
+
check_version_history_py = find_program('tools/check-version-history.py')
elf2efi_py = find_program('tools/elf2efi.py')
export_dbus_interfaces_py = find_program('tools/dbus_exporter.py')
include_directories : libsystemd_includes,
link_args : ['-shared',
'-Wl,--version-script=' + libsystemd_sym_path],
- link_with : [libbasic,
- libbasic_gcrypt],
+ link_with : [libbasic],
link_whole : [libsystemd_static],
dependencies : [librt,
threads,
'systemd',
libsystemd_sources,
basic_sources,
- basic_gcrypt_sources,
fundamental_sources,
include_directories : libsystemd_includes,
build_by_default : static_libsystemd != 'false',
dependencies : [libblkid,
libcap,
libdl,
- libgcrypt,
+ libgcrypt_cflags,
liblz4_cflags,
libmount,
libopenssl,
subdir('src/login')
subdir('src/machine')
subdir('src/machine-id-setup')
+subdir('src/mountfsd')
subdir('src/modules-load')
subdir('src/mount')
subdir('src/network')
subdir('src/notify')
subdir('src/nspawn')
+subdir('src/nsresourced')
subdir('src/nss-myhostname')
subdir('src/nss-mymachines')
subdir('src/nss-resolve')
'PAM modules directory' : pamlibdir,
'PAM configuration directory' : pamconfdir,
'ssh server configuration directory' : sshdconfdir,
+ 'ssh server privilege separation directory' : sshdprivsepdir,
'ssh client configuration directory' : sshconfdir,
'libcryptsetup plugins directory' : libcryptsetup_plugins_dir,
'RPM macros directory' : rpmmacrosdir,
description : 'install the systemd-portabled stack')
option('sysext', type : 'boolean',
description : 'install the systemd-sysext stack')
+option('mountfsd', type : 'boolean',
+ description : 'install the systemd-mountfsd stack')
option('userdb', type : 'boolean',
description : 'install the systemd-userdbd stack')
option('homed', 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('nsresourced', type : 'boolean',
+ description : 'install the systemd-nsresourced stack')
option('nss-myhostname', type : 'boolean',
description : 'install nss-myhostname module')
option('nss-mymachines', type : 'feature', deprecated : { 'true' : 'enabled', 'false' : 'disabled' },
description : 'directory for SSH client configuration ["no" disables]')
option('sshdconfdir', type : 'string',
description : 'directory for SSH server configuration ["no" disables]')
+option('sshdprivsepdir', type : 'string',
+ description : 'directory for SSH privilege separation ["no" disables]', value : '/run/sshd')
option('libcryptsetup-plugins-dir', type : 'string',
description : 'directory for libcryptsetup plugins')
option('docdir', type : 'string',
description : 'install systemd-analyze')
option('bpf-compiler', type : 'combo', choices : ['clang', 'gcc'],
- description: 'compiler used to build BPF programs')
+ description : 'compiler used to build BPF programs')
option('bpf-framework', type : 'feature', deprecated : { 'true' : 'enabled', 'false' : 'disabled' },
- description: 'build BPF programs from source code in restricted C')
+ description : 'build BPF programs from source code in restricted C')
+option('vmlinux-h', type : 'combo', choices : ['auto', 'provided', 'generated', 'disabled'],
+ description : 'which vmlinux.h to use')
+option('vmlinux-h-path', type : 'string', value : '',
+ description : 'path to vmlinux.h to use')
+
+option('default-mountfsd-trusted-directories', type : 'boolean', value: false,
+ description : 'controls whether mountfsd should apply a relaxed policy on DDIs in system DDI directories')
[Config]
Images=system
-MinimumVersion=21
+MinimumVersion=23~devel
[Output]
@OutputDirectory=mkosi.output
[Host]
@Incremental=yes
-# TODO: Drop to 2G again once the next Noble kernel update ships and we can use linux-image-virtual.
-@QemuMem=4G
@RuntimeSize=8G
+@RuntimeBuildSources=yes
ToolsTreePackages=virtiofsd
KernelCommandLineExtra=systemd.crash_shell
- systemd.log_level=debug
+ systemd.log_level=debug,console:info
systemd.log_ratelimit_kmsg=0
systemd.journald.forward_to_console
systemd.journald.max_level_console=warning
+++ /dev/null
-# SPDX-License-Identifier: LGPL-2.1-or-later
-
-[Output]
-Format=directory
-
-[Content]
-Autologin=yes
-# Make sure we build the default initrd as part of the base image as it will have access to the systemd and
-# udev rpms which are built by the build scripts that are part of the base image.
-Bootable=yes
-# we want to build the UKI as part of the system image so make sure none are built here.
-Bootloader=none
-CleanPackageMetadata=no
-
-Packages=
- acl
- bash-completion
- coreutils
- diffutils
- dnsmasq
- dosfstools
- e2fsprogs
- findutils
- gcc # Sanitizer libraries
- gdb
- git
- grep
- gzip
- jq
- kbd
- kexec-tools
- kmod
- less
- man
- mtools
- nano
- nftables
- openssl
- python3
- qrencode
- rsync
- sed
- socat
- strace
- systemd
- tar
- tmux
- tree
- udev
- util-linux
- valgrind
- wireguard-tools
- xfsprogs
- zsh
- zstd
+++ /dev/null
-#!/bin/bash
-# SPDX-License-Identifier: LGPL-2.1-or-later
-set -e
-
-mkosi-install systemd systemd-sysvcompat
+++ /dev/null
-#!/bin/bash
-# SPDX-License-Identifier: LGPL-2.1-or-later
-set -e
-
-if [ ! -f "pkg/$PKG_SUBDIR/PKGBUILD" ]; then
- echo "PKGBUILD not found at pkg/$PKG_SUBDIR/PKGBUILD, run mkosi once with -ff to make sure the PKGBUILD is cloned" >&2
- exit 1
-fi
-
-if [ "$1" = "final" ]; then
- # We get depends and optdepends from .SRCINFO as getting them from the PKGBUILD is rather complex.
- sed --expression 's/^[ \t]*//' "pkg/$PKG_SUBDIR/.SRCINFO" |
- grep --regexp '^depends =' --regexp '^optdepends =' |
- sed --expression 's/^depends = //' --expression 's/^optdepends = //' --expression 's/:.*//' |
- xargs --delimiter '\n' mkosi-install
-else
- # We get makedepends from the PKGBUILD as .SRCINFO can't encode conditional dependencies depending on
- # whether some environment variable is set or not.
- # shellcheck source=/dev/null
- UPSTREAM=1 . "pkg/$PKG_SUBDIR/PKGBUILD"
-
- # shellcheck disable=SC2154
- mkosi-install "${makedepends[@]}"
-fi
+++ /dev/null
-#!/bin/bash
-# SPDX-License-Identifier: LGPL-2.1-or-later
-set -e
-
-mkosi-install systemd systemd-udev
+++ /dev/null
-# SPDX-License-Identifier: LGPL-2.1-or-later
-
-[Match]
-Distribution=centos
-
-[Content]
-Environment=
- PKG_SUBDIR="centos"
-
-Packages=
- kernel-modules # For squashfs support
- rpmautospec-rpm-macros
+++ /dev/null
-# SPDX-License-Identifier: LGPL-2.1-or-later
-
-[Match]
-Distribution=|debian
-Distribution=|ubuntu
-
-[Config]
-InitrdInclude=initrd/
-
-[Content]
-Environment=
- PKG_SUBDIR="debian"
- SYSTEMD_PACKAGES="systemd
- systemd-userdbd
- systemd-oomd
- systemd-sysv
- systemd-tests
- systemd-timesyncd
- systemd-resolved
- systemd-homed
- systemd-coredump
- systemd-journal-remote
- systemd-container
- systemd-boot
- systemd-ukify
- udev"
-
-Packages=
- ^libtss2-esys-[0-9.]+-0$
- ^libtss2-mu-[0-9.]+-0$
- apt
- btrfs-progs
- cryptsetup-bin
- dbus-broker
- default-dbus-session-bus
- dmsetup
- f2fs-tools
- fdisk
- iproute2
- isc-dhcp-server
- libcap-ng-utils
- libtss2-rc0
- libtss2-tcti-device0
- man-db
- netcat-openbsd
- openssh-client
- openssh-server
- passwd
- policykit-1
- procps
- quota
- sbsigntool
- tzdata
- xxd
-
-InitrdPackages=
- btrfs-progs
- tpm2-tools
-
-BuildPackages=
- dpkg-dev
+++ /dev/null
-#!/bin/bash
-# SPDX-License-Identifier: LGPL-2.1-or-later
-set -e
-
-if [ "$1" = "final" ]; then
- exit 0
-fi
-
-if [ ! -d "pkg/$PKG_SUBDIR/debian" ]; then
- echo "deb rules not found at pkg/$PKG_SUBDIR/debian, run mkosi once with -ff to make sure the rules are cloned" >&2
- exit 1
-fi
-
-cd "pkg/$PKG_SUBDIR"
-DEB_BUILD_PROFILES="pkg.systemd.upstream" apt-get build-dep .
+++ /dev/null
-# SPDX-License-Identifier: LGPL-2.1-or-later
-
-[Match]
-Distribution=ubuntu
-
-[Content]
-Packages=
- # We would like to use linux-virtual but it does not have support for SMBIOS credentials.
- linux-image-generic
- linux-tools-common
- linux-tools-generic
+++ /dev/null
-set debuginfod enabled off
-set build-id-verbose 0
-set substitute-path ../src /root/src/systemd
+++ /dev/null
-#!/bin/bash
-# SPDX-License-Identifier: LGPL-2.1-or-later
-set -e
-
-# shellcheck disable=SC2086
-mkosi-install $SYSTEMD_PACKAGES
+++ /dev/null
-#!/bin/bash
-# SPDX-License-Identifier: LGPL-2.1-or-later
-set -e
-
-if [ -z "$(ls --almost-all "pkg/$PKG_SUBDIR")" ] || [ -f "pkg/$PKG_SUBDIR/.git" ]; then
- git submodule sync "pkg/$PKG_SUBDIR"
- git submodule update --init "pkg/$PKG_SUBDIR"
-fi
--- /dev/null
+#!/bin/bash
+set -e
+
+rm -f "$OUTPUTDIR"/*.{rpm,deb,pkg.tar}
# SPDX-License-Identifier: LGPL-2.1-or-later
-[Config]
-Dependencies=base
-
-[Distribution]
-CacheOnly=metadata
-
[Output]
@Format=directory
[Content]
-BaseTrees=%O/base
-Initrds=%O/base.initrd
+Autologin=yes
+Packages=
+ acl
+ bash-completion
+ coreutils
+ diffutils
+ dnsmasq
+ dosfstools
+ e2fsprogs
+ findutils
+ gdb
+ grep
+ gzip
+ jq
+ kbd
+ kexec-tools
+ kmod
+ less
+ man
+ mtools
+ nano
+ nftables
+ openssl
+ python3
+ qrencode
+ rsync
+ sed
+ socat
+ strace
+ systemd
+ tar
+ tmux
+ tree
+ udev
+ util-linux
+ valgrind
+ which
+ wireguard-tools
+ xfsprogs
+ zsh
+ zstd
[Validation]
@SecureBoot=yes
# SPDX-License-Identifier: LGPL-2.1-or-later
set -ex
-if [ ! -f "pkg/$PKG_SUBDIR/PKGBUILD" ]; then
- echo "PKGBUILD not found at pkg/$PKG_SUBDIR/PKGBUILD, run mkosi once with -ff to make sure the PKGBUILD is cloned" >&2
+# shellcheck source=/dev/null
+. /usr/lib/os-release
+
+if [ ! -f "pkg/$ID/PKGBUILD" ]; then
+ echo "PKGBUILD not found at pkg/$ID/PKGBUILD, run mkosi once with -ff to make sure the PKGBUILD is cloned" >&2
exit 1
fi
# We can't configure the source or build directory so we use bind mounts instead to make sure they are in the
# expected locations.
-mount --mkdir --bind "$SRCDIR" "pkg/$PKG_SUBDIR/systemd-stable/"
-mount --mkdir --bind "$BUILDDIR" "pkg/$PKG_SUBDIR/build/"
+mount --mkdir --bind "$SRCDIR" "pkg/$ID/systemd-stable/"
+mount --mkdir --bind "$BUILDDIR" "pkg/$ID/build/"
# Because we run with --noextract we are responsible for making sure the source files appear in src/.
-mount --mkdir --rbind "$PWD/pkg/$PKG_SUBDIR" "pkg/$PKG_SUBDIR/src/"
+mount --mkdir --rbind "$PWD/pkg/$ID" "pkg/$ID/src/"
# shellcheck source=/dev/null
. /etc/makepkg.conf
# tmpfs during the build script so these changes don't end up in the image itself.
tee --append /etc/makepkg.conf >/dev/null <<EOF
CFLAGS="$CFLAGS -Og"
-OPTIONS=(!strip docs !libtool !staticlibs emptydirs !zipman purge !debug !lto)
+OPTIONS=(
+ docs
+ !libtool
+ !staticlibs
+ emptydirs
+ !zipman
+ purge
+ $( ((WITH_DEBUG)) && echo strip || echo !strip)
+ $( ((WITH_DEBUG)) && echo debug || echo !debug)
+ !lto
+)
EOF
# Linting the PKGBUILD takes multiple seconds every build so avoid that by nuking all the linting functions.
TS="${SOURCE_DATE_EPOCH:-$(date +%s)}"
fi
-sed --in-place "pkg/$PKG_SUBDIR/PKGBUILD" \
+sed --in-place "pkg/$ID/PKGBUILD" \
--expression "s/^_tag=.*/_tag=$(cat meson.version)/" \
--expression "s/^pkgrel=.*/pkgrel=$(date "+%Y%m%d%H%M%S" --date "@$TS")/"
# We get around makepkg's root check by setting EUID to something else.
# shellcheck disable=SC2046
-env --chdir="pkg/$PKG_SUBDIR" \
+env --chdir="pkg/$ID" \
EUID=123 \
makepkg \
--noextract \
--force \
_systemd_UPSTREAM=1 \
_systemd_QUIET=1 \
- BUILDDIR="$PWD/pkg/$PKG_SUBDIR" \
- PKGDEST="$PACKAGEDIR" \
+ BUILDDIR="$PWD/pkg/$ID" \
+ PKGDEST="$OUTPUTDIR" \
PKGEXT=".pkg.tar" \
MESON_EXTRA_CONFIGURE_OPTIONS="-D mode=developer -D b_sanitize=${SANITIZERS:-none}"
+
+cp "$OUTPUTDIR"/*.pkg.tar "$PACKAGEDIR"
[Match]
Distribution=arch
-[Config]
-InitrdInclude=initrd/
-
-# TODO: Switch to https://gitlab.archlinux.org/archlinux/packaging/packages/systemd once
-# https://gitlab.archlinux.org/archlinux/packaging/packages/systemd/-/merge_requests/8 is merged.
[Content]
-Environment=
- PKG_SUBDIR="arch"
- SYSTEMD_PACKAGES="systemd systemd-ukify systemd-sysvcompat systemd-resolvconf"
+VolatilePackages=
+ systemd
+ systemd-ukify
+ systemd-sysvcompat
+ systemd-resolvconf
+ systemd-tests
Packages=
bpf
btrfs-progs
compsize
cryptsetup
- dbus
+ dbus-broker
+ dbus-broker-units
+ debugedit
dhcp
f2fs-tools
+ fakeroot
+ git
gnutls
iproute
linux
openssh
openssl
pacman
+ pkgconf
polkit
- qrencode
quota-tools
sbsigntools
shadow
btrfs-progs
tpm2-tools
-BuildPackages=
- fakeroot
- pkgconf
- debugedit
+InitrdVolatilePackages=
+ systemd
+ systemd-sysvcompat
--- /dev/null
+#!/bin/bash
+# SPDX-License-Identifier: LGPL-2.1-or-later
+set -e
+
+if [ "$1" = "build" ]; then
+ exit 0
+fi
+
+# shellcheck source=/dev/null
+. "$BUILDROOT/usr/lib/os-release"
+
+if [ ! -f "pkg/$ID/PKGBUILD" ]; then
+ echo "PKGBUILD not found at pkg/$ID/PKGBUILD, run mkosi once with -ff to make sure the PKGBUILD is cloned" >&2
+ exit 1
+fi
+
+# We get depends and optdepends from .SRCINFO as getting them from the PKGBUILD is rather complex.
+sed --expression 's/^[ \t]*//' "pkg/$ID/.SRCINFO" |
+ grep --regexp '^depends =' --regexp '^optdepends =' |
+ sed --expression 's/^depends = //' --expression 's/^optdepends = //' --expression 's/:.*//' |
+ xargs --delimiter '\n' mkosi-install
+
+# We get makedepends from the PKGBUILD as .SRCINFO can't encode conditional dependencies depending on
+# whether some environment variable is set or not.
+# shellcheck source=/dev/null
+_systemd_UPSTREAM=1 . "pkg/$ID/PKGBUILD"
+
+# shellcheck disable=SC2154
+mkosi-install "${makedepends[@]}"
# SPDX-License-Identifier: LGPL-2.1-or-later
set -e
-if [ ! -f "pkg/$PKG_SUBDIR/systemd.spec" ]; then
- echo "spec not found at pkg/$PKG_SUBDIR/systemd.spec, run mkosi once with -ff to make sure the spec is cloned" >&2
+# shellcheck source=/dev/null
+. /usr/lib/os-release
+
+if [ ! -f "pkg/$ID/systemd.spec" ]; then
+ echo "spec not found at pkg/$ID/systemd.spec, run mkosi once with -ff to make sure the spec is cloned" >&2
exit 1
fi
TS="${SOURCE_DATE_EPOCH:-$(date +%s)}"
fi
+# Fix the %install override so debuginfo packages are generated even when --build-in-place is used.
+# See https://github.com/rpm-software-management/rpm/issues/3042.
+tee --append /usr/lib/rpm/redhat/macros <<'EOF'
+%install %{?_enable_debug_packages:%{debug_package}}\
+%%install\
+%{nil}
+EOF
+
+IFS=
# TODO: Replace meson_build and meson_install overrides with "--undefine __meson_verbose" once
# https://github.com/mesonbuild/meson/pull/12835 is available.
# shellcheck disable=SC2046
-bb \
--build-in-place \
--with upstream \
- $( ((WITH_TESTS)) || echo --nocheck) \
+ $( ((WITH_TESTS)) || echo "--nocheck") \
+ $( ((WITH_DOCS)) || echo "--without docs") \
--define "_topdir /var/tmp" \
- --define "_sourcedir pkg/$PKG_SUBDIR" \
- --define "_rpmdir $PACKAGEDIR" \
+ --define "_sourcedir pkg/$ID" \
+ --define "_rpmdir $OUTPUTDIR" \
${BUILDDIR:+--define} \
${BUILDDIR:+"_vpath_builddir $BUILDDIR"} \
--define "_build_name_fmt %%{NAME}-%%{VERSION}-%%{RELEASE}.%%{ARCH}.rpm" \
--define "_binary_payload w.ufdio" \
- --define "debug_package %{nil}" \
+ $( ((WITH_DEBUG)) || echo --define) \
+ $( ((WITH_DEBUG)) || echo "debug_package %{nil}") \
--define "version_override $(cat meson.version)" \
--define "release_override $(date "+%Y%m%d%H%M%S" --date "@$TS")" \
--define "_distro_extra_cflags -Og" \
--define "meson_build %{shrink:%{__meson} compile -C %{_vpath_builddir} -j %{_smp_build_ncpus} %{nil}}" \
--define "meson_install %{shrink:DESTDIR=%{buildroot} %{__meson} install -C %{_vpath_builddir} --no-rebuild --quiet %{nil}}" \
--define "meson_extra_configure_options -D mode=developer -D b_sanitize=${SANITIZERS:-none}" \
- --define "__brp_strip %{nil}" \
+ $( ((WITH_DEBUG)) || echo --define) \
+ $( ((WITH_DEBUG)) || echo "__brp_strip %{nil}") \
--define "__brp_compress %{nil}" \
--define "__brp_mangle_shebangs %{nil}" \
--define "__brp_strip_comment_note %{nil}" \
--define "__elf_exclude_path ^/usr/lib/systemd/tests/unit-tests/.*$" \
--define "__script_requires %{nil}" \
--undefine _lto_cflags \
- "pkg/$PKG_SUBDIR/systemd.spec"
+ --noclean \
+ "pkg/$ID/systemd.spec"
+
+cp "$OUTPUTDIR"/*.rpm "$PACKAGEDIR"
Distribution=|centos
Distribution=|fedora
-[Config]
-InitrdInclude=initrd/
-
[Content]
-Environment=
- SYSTEMD_PACKAGES="systemd
- systemd-udev
- systemd-container
- systemd-repart
- systemd-resolved
- systemd-networkd
- systemd-boot
- systemd-tests
- systemd-ukify
- systemd-pam
- systemd-oomd-defaults
- systemd-journal-remote
- systemd-networkd-defaults"
+VolatilePackages=
+ systemd
+ systemd-udev
+ systemd-container
+ systemd-repart
+ systemd-resolved
+ systemd-networkd
+ systemd-boot
+ systemd-tests
+ systemd-ukify
+ systemd-pam
+ systemd-oomd-defaults
+ systemd-journal-remote
+ systemd-networkd-defaults
+
Packages=
bpftool
cryptsetup
dhcp-server
dnf
+ git-core
gnutls
integritysetup
iproute
rpm
rpm-build
rpmautospec
+ selinux-policy
+ selinux-policy-targeted
+ setools-console
+ policycoreutils
util-linux
vim-common
InitrdPackages=
- setools
- selinux-policy
tpm2-tools
+
+InitrdVolatilePackages=
+ systemd
+ systemd-udev
# SPDX-License-Identifier: LGPL-2.1-or-later
set -e
-if [ ! -f "pkg/$PKG_SUBDIR/systemd.spec" ]; then
- echo "spec not found at pkg/$PKG_SUBDIR/systemd.spec, run mkosi with -ff to make sure the spec is cloned" >&2
- exit 1
-fi
-
-if [ "$1" = "final" ]; then
- DEPS="--requires"
-else
- DEPS="--buildrequires"
+if [ "$1" = "build" ]; then
+ exit 0
fi
-mkosi-chroot \
- rpmspec \
- --with upstream \
- --query \
- "$DEPS" \
- --define "_topdir /var/tmp" \
- --define "_sourcedir pkg/$PKG_SUBDIR" \
- "pkg/$PKG_SUBDIR/systemd.spec" |
- grep --invert-match --regexp systemd --regexp /bin/sh --regexp "rpmlib(" --regexp udev |
- sort --unique |
- tee /tmp/buildrequires |
- xargs --delimiter '\n' mkosi-install
+# shellcheck source=/dev/null
+. "$BUILDROOT/usr/lib/os-release"
-if [ "$1" = "final" ]; then
- exit 0
+if [ ! -f "pkg/$ID/systemd.spec" ]; then
+ echo "spec not found at pkg/$ID/systemd.spec, run mkosi with -ff to make sure the spec is cloned" >&2
+ exit 1
fi
+for DEPS in --requires --buildrequires; do
+ mkosi-chroot \
+ rpmspec \
+ --with upstream \
+ --query \
+ "$DEPS" \
+ --define "_topdir /var/tmp" \
+ --define "_sourcedir pkg/$ID" \
+ "pkg/$ID/systemd.spec" |
+ grep --invert-match --regexp systemd --regexp /bin/sh --regexp "rpmlib(" --regexp udev |
+ sort --unique |
+ tee /tmp/buildrequires |
+ xargs --delimiter '\n' mkosi-install
+done
+
# rpmbuild -br tries to build a source package which means all source files have to exist which isn't the
# case when using --build-in-place so we get rid of the source file that doesn't exist to make it happy.
# TODO: Use -bd instead of -br and get rid of this once we don't need to build on CentOS Stream 9 anymore.
-sed '/Source0/d' --in-place "pkg/$PKG_SUBDIR/systemd.spec"
+sed '/Source0/d' --in-place "pkg/$ID/systemd.spec"
until mkosi-chroot \
rpmbuild \
--build-in-place \
--with upstream \
--define "_topdir /var/tmp" \
- --define "_sourcedir pkg/$PKG_SUBDIR" \
+ --define "_sourcedir pkg/$ID" \
--define "_build_name_fmt %%{NAME}-%%{VERSION}-%%{RELEASE}.%%{ARCH}.rpm" \
- "pkg/$PKG_SUBDIR/systemd.spec"
+ "pkg/$ID/systemd.spec"
do
EXIT_STATUS=$?
if [ $EXIT_STATUS -ne 11 ]; then
[Match]
Distribution=centos
+
+[Content]
+Packages=
+ kernel-modules # For squashfs support
+ rpmautospec-rpm-macros
# SPDX-License-Identifier: LGPL-2.1-or-later
set -e
-if [ ! -d "pkg/$PKG_SUBDIR/debian" ]; then
- echo "deb rules not found at pkg/$PKG_SUBDIR/debian, run mkosi once with -ff to make sure the rules are cloned" >&2
+# shellcheck source=/dev/null
+. /usr/lib/os-release
+
+if [ ! -d "pkg/$ID/debian" ]; then
+ echo "deb rules not found at pkg/$ID/debian, run mkosi once with -ff to make sure the rules are cloned" >&2
exit 1
fi
# We transplant the debian/ folder from the deb package sources into the upstream sources.
-mount --mkdir --bind "$SRCDIR/pkg/$PKG_SUBDIR/debian" "$SRCDIR"/debian
+mount --mkdir --bind "$SRCDIR/pkg/$ID/debian" "$SRCDIR"/debian
# We hide the patches/ directory by mounting an empty directory on top so they don't get applied.
TMP=$(mktemp -d)
# Add a new changelog entry to update the version. We use a fixed date since a dynamic one causes a full
# rebuild every time.
cat >debian/changelog.new <<EOF
-systemd ($(cat meson.version).$(date "+%Y%m%d%H%M%S" --date "@$TS")) UNRELEASED; urgency=low
+systemd ($(cat meson.version)-$(date "+%Y%m%d%H%M%S" --date "@$TS")) UNRELEASED; urgency=low
* Automatic build from mkosi
mv debian/changelog.new debian/changelog
build() {
- DEB_BUILD_OPTIONS="$( ((WITH_TESTS)) || echo nocheck) $( ((WITH_DOCS)) || echo nodoc) nostrip terse optimize=-lto" \
- DEB_BUILD_PROFILES="$( ((WITH_TESTS)) || echo nocheck) $( ((WITH_DOCS)) || echo nodoc) pkg.systemd.upstream" \
+ DEB_BUILD_OPTIONS="\
+ $( ((WITH_TESTS)) || echo nocheck) \
+ $( ((WITH_DOCS)) || echo nodoc) \
+ $( ((WITH_DEBUG)) || echo nostrip) \
+ terse
+ optimize=-lto \
+ " \
+ DEB_BUILD_PROFILES="\
+ $( ((WITH_TESTS)) || echo nocheck) \
+ $( ((WITH_DOCS)) || echo nodoc) \
+ pkg.systemd.upstream \
+ " \
DEB_CFLAGS_APPEND="-Og" \
DPKG_FORCE="unsafe-io" \
DPKG_DEB_COMPRESSOR_TYPE="none" \
build
fi
-mv ../*.deb "$PACKAGEDIR"
+cp ../*.deb "$PACKAGEDIR"
+cp ../*.deb "$OUTPUTDIR"
--- /dev/null
+# SPDX-License-Identifier: LGPL-2.1-or-later
+
+[Match]
+Distribution=|debian
+Distribution=|ubuntu
+
+[Content]
+VolatilePackages=
+ systemd
+ systemd-userdbd
+ systemd-oomd
+ systemd-sysv
+ systemd-tests
+ systemd-timesyncd
+ systemd-resolved
+ systemd-homed
+ systemd-coredump
+ systemd-journal-remote
+ systemd-container
+ systemd-boot
+ systemd-ukify
+ udev
+
+Packages=
+ ^libasan[0-9]+$
+ ^libtss2-esys-[0-9.]+-0$
+ ^libtss2-mu-[0-9.]+-0$
+ ^libubsan[0-9]+$
+ apt
+ btrfs-progs
+ cryptsetup-bin
+ dbus-broker
+ dbus-user-session
+ dmsetup
+ dpkg-dev
+ f2fs-tools
+ fdisk
+ git-core
+ iproute2
+ isc-dhcp-server
+ libcap-ng-utils
+ libtss2-rc0
+ libtss2-tcti-device0
+ man-db
+ netcat-openbsd
+ openssh-client
+ openssh-server
+ passwd
+ policykit-1
+ procps
+ quota
+ sbsigntool
+ tzdata
+ xxd
+
+InitrdPackages=
+ btrfs-progs
+ tpm2-tools
+
+InitrdVolatilePackages=
+ systemd
+ udev
--- /dev/null
+#!/bin/bash
+# SPDX-License-Identifier: LGPL-2.1-or-later
+set -e
+
+if [ "$1" = "build" ]; then
+ exit 0
+fi
+
+# shellcheck source=/dev/null
+. "$BUILDROOT/usr/lib/os-release"
+
+if [ ! -d "pkg/$ID/debian" ]; then
+ echo "deb rules not found at pkg/$ID/debian, run mkosi once with -ff to make sure the rules are cloned" >&2
+ exit 1
+fi
+
+cd "pkg/$ID"
+DEB_BUILD_PROFILES="pkg.systemd.upstream" apt-get build-dep .
Distribution=fedora
[Content]
-Environment=
- PKG_SUBDIR="fedora"
-
Packages=
btrfs-progs
compsize
f2fs-tools
glibc-langpack-en
sbsigntools
+ dnf5
InitrdPackages=
btrfs-progs
-#!/bin/sh
+#!/bin/bash
# SPDX-License-Identifier: LGPL-2.1-or-later
set -e
# OpenSUSE insists on blacklisting erofs by default because its supposedly a legacy filesystem.
# See https://github.com/openSUSE/suse-module-tools/pull/71
rm -f "$BUILDROOT/usr/lib/modprobe.d/60-blacklist_fs-erofs.conf"
-
-mkosi-install systemd udev systemd-experimental
# SPDX-License-Identifier: LGPL-2.1-or-later
set -e
-if [ ! -f "pkg/$PKG_SUBDIR/systemd.spec" ]; then
- echo "spec not found at pkg/$PKG_SUBDIR/systemd.spec, run mkosi once with -ff to make sure the spec is cloned" >&2
+# shellcheck source=/dev/null
+. /usr/lib/os-release
+ID="${ID%-*}"
+
+if [ ! -f "pkg/$ID/systemd.spec" ]; then
+ echo "spec not found at pkg/$ID/systemd.spec, run mkosi once with -ff to make sure the spec is cloned" >&2
exit 1
fi
# The openSUSE filelists hardcode the manpage compression extension. This causes rpmbuild errors since we
# disable manpage compression as the files cannot be found. Fix the issue by removing the compression
# extension.
-find "pkg/$PKG_SUBDIR" -name "files.*" -exec sed --in-place 's/\.gz$//' {} \;
+find "pkg/$ID" -name "files.*" -exec sed --in-place 's/\.gz$//' {} \;
+
+# Fix the %install override so debuginfo packages are generated.
+tee --append /usr/lib/rpm/suse/macros <<'EOF'
+%install %{debug_package}\
+%%install\
+%{nil}
+EOF
build() {
+ IFS=
# TODO: Replace meson_build and meson_install overrides with "--undefine __meson_verbose" once
# https://github.com/mesonbuild/meson/pull/12835 is available.
# shellcheck disable=SC2046
--with upstream \
$( ((WITH_TESTS)) || echo --nocheck) \
--define "_topdir /var/tmp" \
- --define "_sourcedir pkg/$PKG_SUBDIR" \
- --define "_rpmdir $PACKAGEDIR" \
+ --define "_sourcedir pkg/$ID" \
+ --define "_rpmdir $OUTPUTDIR" \
${BUILDDIR:+--define} \
${BUILDDIR:+"_vpath_builddir $BUILDDIR"} \
--define "_build_name_fmt %%{NAME}-%%{VERSION}-%%{RELEASE}.%%{ARCH}.rpm" \
--define "_binary_payload w.ufdio" \
- --define "debug_package %{nil}" \
+ $( ((WITH_DEBUG)) || echo --define) \
+ $( ((WITH_DEBUG)) || echo "debug_package %{nil}") \
--define "vendor openSUSE" \
--define "version_override $(cat meson.version)" \
--define "release_override $(date "+%Y%m%d%H%M%S" --date "@$TS")" \
--define "__os_install_post /usr/lib/rpm/brp-suse %{nil}" \
--define "__elf_exclude_path ^/usr/lib/systemd/tests/unit-tests/.*$" \
--define "__script_requires %{nil}" \
+ --noclean \
"$@" \
- "pkg/$PKG_SUBDIR/systemd.spec"
+ "pkg/$ID/systemd.spec"
}
if ! build; then
# warnings.
rm systemd.lang
- cat /tmp/unpackaged-files >>"pkg/$PKG_SUBDIR/files.systemd"
+ cat /tmp/unpackaged-files >>"pkg/$ID/files.systemd"
build --noprep --nocheck
fi
+
+cp "$OUTPUTDIR"/*.rpm "$PACKAGEDIR"
InitrdInclude=initrd/
[Content]
-Environment=
- PKG_SUBDIR="opensuse"
- SYSTEMD_PACKAGES="systemd
- udev
- systemd-experimental
- systemd-boot
- systemd-container
- systemd-homed
- systemd-network
- systemd-portable
- systemd-sysvcompat
- systemd-testsuite"
+VolatilePackages=
+ systemd
+ udev
+ systemd-experimental
+ systemd-boot
+ systemd-container
+ systemd-homed
+ systemd-network
+ systemd-portable
+ systemd-sysvcompat
+ systemd-testsuite
# We install gawk, gzip, grep, xz, sed, rsync and docbook-xsl-stylesheets here explicitly so that the busybox
# versions don't get installed instead.
cryptsetup
dbus-broker
device-mapper
- distribution-release
docbook-xsl-stylesheets
f2fs-tools
gawk
+ git-core
glibc-locale-base
grep
gzip
kernel-kvmsmall
+ kmod
+ libasan8
+ libkmod2
+ libubsan1
openssh-clients
openssh-server
pam
+ patterns-base-minimal_base
python3-pefile
quota
rpm-build
InitrdPackages=
btrfs-progs
+ kmod
+ libkmod2
tpm2.0-tools
+
+InitrdVolatilePackages=
+ systemd
+ udev
+ systemd-experimental
# SPDX-License-Identifier: LGPL-2.1-or-later
set -e
-if [ ! -f "pkg/$PKG_SUBDIR/systemd.spec" ]; then
- echo "spec not found at pkg/$PKG_SUBDIR/systemd.spec, run mkosi once with -ff to make sure the spec is cloned" >&2
- exit 1
-fi
-
-if [ "$1" = "final" ]; then
- DEPS="--requires"
-else
- DEPS="--buildrequires"
+if [ "$1" = "build" ]; then
+ exit 0
fi
-mkosi-chroot \
- rpmspec \
- --with upstream \
- --query \
- "$DEPS" \
- --define "_topdir /var/tmp" \
- --define "_sourcedir pkg/$PKG_SUBDIR" \
- "pkg/$PKG_SUBDIR/systemd.spec" |
- grep --invert-match --regexp systemd --regexp /bin/sh --regexp "rpmlib(" --regexp udev |
- sort --unique |
- tee /tmp/buildrequires |
- xargs --delimiter '\n' mkosi-install
+# shellcheck source=/dev/null
+. "$BUILDROOT/usr/lib/os-release"
+ID="${ID%-*}"
-if [ "$1" = "final" ]; then
- exit 0
+if [ ! -f "pkg/$ID/systemd.spec" ]; then
+ echo "spec not found at pkg/$ID/systemd.spec, run mkosi once with -ff to make sure the spec is cloned" >&2
+ exit 1
fi
+for DEPS in --requires --buildrequires; do
+ mkosi-chroot \
+ rpmspec \
+ --with upstream \
+ --query \
+ "$DEPS" \
+ --define "_topdir /var/tmp" \
+ --define "_sourcedir pkg/$ID" \
+ "pkg/$ID/systemd.spec" |
+ grep --invert-match --regexp systemd --regexp /bin/sh --regexp "rpmlib(" --regexp udev |
+ sort --unique |
+ tee /tmp/buildrequires |
+ xargs --delimiter '\n' mkosi-install
+done
+
until mkosi-chroot \
rpmbuild \
-bd \
--build-in-place \
--with upstream \
--define "_topdir /var/tmp" \
- --define "_sourcedir pkg/$PKG_SUBDIR" \
+ --define "_sourcedir pkg/$ID" \
--define "_build_name_fmt %%{NAME}-%%{VERSION}-%%{RELEASE}.%%{ARCH}.rpm" \
- "pkg/$PKG_SUBDIR/systemd.spec"
+ "pkg/$ID/systemd.spec"
do
EXIT_STATUS=$?
if [ $EXIT_STATUS -ne 11 ]; then
--- /dev/null
+# SPDX-License-Identifier: LGPL-2.1-or-later
+
+[Match]
+Distribution=ubuntu
+
+[Content]
+Packages=
+ linux-image-virtual
+ linux-tools-common
+ linux-tools-virtual
#!/bin/sh
# SPDX-License-Identifier: LGPL-2.1-or-later
+mkdir -p "$BUILDROOT"/usr/share/factory/mkosi
cp --archive --recursive --no-target-directory --reflink=auto "$BUILDROOT"/etc "$BUILDROOT"/usr/share/factory/mkosi
--- /dev/null
+#!/bin/bash
+# SPDX-License-Identifier: LGPL-2.1-or-later
+set -e
+
+if [ -z "$(ls --almost-all "pkg/$DISTRIBUTION")" ] || [ -f "pkg/$DISTRIBUTION/.git" ]; then
+ PKG_SUBDIR="$(realpath "pkg/$DISTRIBUTION" --relative-to "$PWD")"
+ git submodule sync "$PKG_SUBDIR"
+ git submodule update --init "$PKG_SUBDIR"
+fi
--- /dev/null
+# SPDX-License-Identifier: MIT-0
+#
+# This config file is installed as part of systemd.
+# It may be freely copied and edited (following the MIT No Attribution license).
+#
+# To make local modifications, one of the following methods may be used:
+# 1. add a drop-in file that extends this file by creating the
+# /etc/systemd/network/80-namespace-ns.network.d/ directory and creating a
+# new .conf file there.
+# 2. copy this file into /etc/systemd/network or one of the other paths checked
+# by systemd-networkd and edit it there.
+# This file should not be edited in place, because it'll be overwritten on upgrades.
+
+# This network file matches the host-side of the virtual Ethernet link
+# created by systemd-nsresourced's network support. See systemd-nsresourced(1) for
+# details.
+
+[Match]
+Kind=veth
+Name=ns-*
+
+[Network]
+# Default to using a /28 prefix, giving up to 13 addresses per namespace
+Address=0.0.0.0/28
+LinkLocalAddressing=yes
+DHCPServer=yes
+IPMasquerade=both
+LLDP=yes
+EmitLLDP=customer-bridge
+IPv6AcceptRA=no
+IPv6SendRA=yes
'80-container-ve.link',
'80-container-vz.network',
'80-container-vz.link',
+ '80-namespace-ns.network',
'80-vm-vt.network',
'80-vm-vt.link',
'80-wifi-adhoc.network',
-Subproject commit 3b86b9146b84d499789ba924a9dd4ac643d796ab
+Subproject commit ccc32ea10164a9b6ca3098765e63f653cddc6817
-Subproject commit 3cf45106c8bc5a050901851be6b2ac85b6f5c571
+Subproject commit ad880b10ee6bbfbe266c518fc87b8c7a3df962da
-Subproject commit 1932e19d92daef5928a1402073ad3b5aa6fc0767
+Subproject commit 30c77a7332b5f44cbade27155c0b8e816a75ae7f
-Subproject commit f1d38667ef013aa832f43ea7b5861efd29b09fee
+Subproject commit a37923658fbe9f511c36d31f556eaada782691eb
--- /dev/null
+debian
\ No newline at end of file
msgid ""
msgstr ""
"Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2024-03-04 10:09+0100\n"
+"POT-Creation-Date: 2024-04-10 07:06+0900\n"
"PO-Revision-Date: 2021-09-09 03:04+0000\n"
"Last-Translator: Takuro Onoue <kusanaginoturugi@gmail.com>\n"
"Language-Team: Japanese <https://translate.fedoraproject.org/projects/"
msgstr "ユーザのホーム領域のパスワードを変更するには認証が必要です。"
#: src/home/org.freedesktop.home1.policy:73
-msgid "Inhibit automatic lock of a home area"
-msgstr ""
-
-#: src/home/org.freedesktop.home1.policy:74
-#, fuzzy
-msgid ""
-"Authentication is required to inhibit automatic lock of a user's home area."
-msgstr "ユーザのホーム領域の更新には認証が必要です。"
-
-#: src/home/org.freedesktop.home1.policy:83
-#, fuzzy
msgid "Activate a home area"
-msgstr "ホーム領域の作成"
+msgstr "ホーム領域の有効化"
-#: src/home/org.freedesktop.home1.policy:84
-#, fuzzy
+#: src/home/org.freedesktop.home1.policy:74
msgid "Authentication is required to activate a user's home area."
-msgstr "ユーザのホーム領域を作成するには認証が必要です。"
+msgstr "ユーザのホーム領域を有効化するには認証が必要です。"
#: src/home/pam_systemd_home.c:293
#, c-format
msgstr "システムの説明を取得するには認証が必要です。"
#: src/import/org.freedesktop.import1.policy:22
-#, fuzzy
msgid "Import a disk image"
-msgstr "仮想マシンもしくはコンテナイメージの読込"
+msgstr "ディスクイメージのの読込"
#: src/import/org.freedesktop.import1.policy:23
-#, fuzzy
msgid "Authentication is required to import an image"
-msgstr "仮想マシンもしくはコンテナイメージを読込むには認証が必要です"
+msgstr "ディスクイメージを読込むには認証が必要です"
#: src/import/org.freedesktop.import1.policy:32
-#, fuzzy
msgid "Export a disk image"
-msgstr "仮想マシンもしくはコンテナイメージの書出し"
+msgstr "ディスクイメージの書出し"
#: src/import/org.freedesktop.import1.policy:33
-#, fuzzy
msgid "Authentication is required to export disk image"
-msgstr "仮想マシンもしくはコンテナイメージを書出すには認証が必要です"
+msgstr "ディスクイメージを書出すには認証が必要です"
#: src/import/org.freedesktop.import1.policy:42
-#, fuzzy
msgid "Download a disk image"
-msgstr "仮想マシンもしくはコンテナイメージのダウンロード"
+msgstr "ディスクイメージのダウンロード"
#: src/import/org.freedesktop.import1.policy:43
-#, fuzzy
msgid "Authentication is required to download a disk image"
-msgstr "仮想マシンもしくはコンテナイメージをダウンロードするには認証が必要です"
+msgstr "ディスクイメージをダウンロードするには認証が必要です"
#: src/import/org.freedesktop.import1.policy:52
msgid "Cancel transfer of a disk image"
-msgstr ""
+msgstr "ディスクイメージの転送を中止"
#: src/import/org.freedesktop.import1.policy:53
-#, fuzzy
msgid ""
"Authentication is required to cancel the ongoing transfer of a disk image"
-msgstr "ã\83¦ã\83¼ã\82¶ã\81®ã\83\9bã\83¼ã\83 é \98å\9f\9fã\81®ã\83\91ã\82¹ã\83¯ã\83¼ã\83\89ã\82\92å¤\89æ\9b´ã\81\99ã\82\8bã\81«ã\81¯èª\8d証ã\81\8cå¿\85è¦\81ã\81§ã\81\99ã\80\82"
+msgstr "ã\83\87ã\82£ã\82¹ã\82¯ã\82¤ã\83¡ã\83¼ã\82¸ã\81®è»¢é\80\81ã\82\92ä¸æ¢ã\81\99ã\82\8bã\81«ã\81¯èª\8d証ã\81\8cå¿\85è¦\81ã\81§ã\81\99"
#: src/locale/org.freedesktop.locale1.policy:22
msgid "Set system locale"
msgid "Authentication is required to reconfigure network interface."
msgstr "ネットワークインターフェイスの再設定には認証が必要です。"
+#: src/network/org.freedesktop.network1.policy:187
+msgid "Specify whether persistent storage for systemd-networkd is available."
+msgstr "systemd-networkdの永続的ストレージの設定"
+
+#: src/network/org.freedesktop.network1.policy:188
+msgid ""
+"Authentication is required to specify whether persistent storage for systemd-"
+"networkd is available."
+msgstr "systemd-networkdの永続的ストレージを設定するには認証が必要です。"
+
#: src/portable/org.freedesktop.portable1.policy:13
msgid "Inspect a portable service image"
msgstr "ポータブルサービスイメージの読み込み"
enable systemd-homed-activate.service
enable systemd-homed-firstboot.service
enable systemd-journald-audit.socket
+enable systemd-mountfsd.socket
enable systemd-network-generator.service
enable systemd-networkd.service
enable systemd-networkd-wait-online.service
+enable systemd-nsresourced.socket
enable systemd-pstore.service
enable systemd-resolved.service
enable systemd-sysext.service
SUBSYSTEM=="block", TAG+="systemd"
-# We can't make any conclusions about suspended DM devices so let's just import previous SYSTEMD_READY state and skip other rules
-SUBSYSTEM=="block", ENV{DM_SUSPENDED}=="1", IMPORT{db}="SYSTEMD_READY"
-SUBSYSTEM=="block", ENV{DM_SUSPENDED}=="1", GOTO="systemd_end"
+# When a dm device is first created, it's just an empty container. Ignore it.
+# DM_NAME is not set in this case, but it's set on spurious "add" events that occur later.
+SUBSYSTEM=="block", ACTION=="add", KERNEL=="dm-*", ENV{DM_NAME}!="?*", ENV{SYSTEMD_READY}="0"
-SUBSYSTEM=="block", ACTION=="add", ENV{DM_UDEV_DISABLE_OTHER_RULES_FLAG}=="1", ENV{SYSTEMD_READY}="0"
+# DM_UDEV_DISABLE_OTHER_RULES_FLAG==1 means that the device shouldn't be probed.
+# Import previous SYSTEMD_READY state.
+SUBSYSTEM=="block", ENV{DM_UDEV_DISABLE_OTHER_RULES_FLAG}=="1", ENV{SYSTEMD_READY}=="", IMPORT{db}="SYSTEMD_READY"
# Ignore encrypted devices with no identified superblock on it, since
# we are probably still calling mke2fs or mkswap on it.
SUBSYSTEM=="block", ENV{DM_UUID}=="CRYPT-*", ENV{ID_PART_TABLE_TYPE}=="", ENV{ID_FS_USAGE}=="", ENV{SYSTEMD_READY}="0"
-# Explicitly set SYSTEMD_READY=1 for DM devices that don't have it set yet, so that we always have something to import above
-SUBSYSTEM=="block", ENV{DM_UUID}=="?*", ENV{SYSTEMD_READY}=="", ENV{SYSTEMD_READY}="1"
-
# add symlink to GPT root disk
SUBSYSTEM=="block", ENV{ID_PART_GPT_AUTO_ROOT}=="1", ENV{ID_FS_TYPE}!="crypto_LUKS", SYMLINK+="gpt-auto-root"
SUBSYSTEM=="block", ENV{ID_PART_GPT_AUTO_ROOT}=="1", ENV{ID_FS_TYPE}=="crypto_LUKS", SYMLINK+="gpt-auto-root-luks"
}
_arguments \
- {-h,--help}'[Prints a short help text and exits.]' \
- '--version[Prints a short version string and exits.]' \
+ '(- *)'{-h,--help}'[Prints a short help text and exits.]' \
+ '(- *)--version[Prints a short version string and exits.]' \
'--esp-path=[Path to the EFI System Partition (ESP)]:path:_directories' \
'--boot-path=[Path to the $BOOT partition]:path:_directories' \
- {-p,--print-esp-path}'[Print path to the EFI system partition]' \
- {-x,--print-boot-path}'[Print path to the $BOOT partition]' \
+ '(-p --print-esp-path)'{-p,--print-esp-path}'[Print path to the EFI system partition]' \
+ '(-x --print-boot-path)'{-x,--print-boot-path}'[Print path to the $BOOT partition]' \
'--make-machine-id-directory=[Control creation and deletion of the top-level machine ID directory.]:options:(yes no auto)' \
'--no-variables[Do not touch EFI variables]' \
'--no-pager[Do not pipe output into a pager]' \
local _bus_address=${${words:*_modes}[(R)(${(j.|.)_modes})]}
local curcontext=$curcontext state line
_arguments \
- {-h,--help}'[Prints a short help text and exits.]' \
- '--version[Prints a short version string and exits.]' \
+ '(- *)'{-h,--help}'[Prints a short help text and exits.]' \
+ '(- *)--version[Prints a short version string and exits.]' \
'--no-pager[Do not pipe output into a pager]' \
'--no-legend[Do not show the headers and footers]' \
'--system[Connect to system manager]' \
'--user[Connect to user service manager]' \
- {-H+,--host=}'[Operate on remote host]:userathost:_sd_hosts_or_user_at_host' \
- {-M+,--machine=}'[Operate on local container]:machines:_sd_machines' \
+ '(-H --host)'{-H+,--host=}'[Operate on remote host]:userathost:_sd_hosts_or_user_at_host' \
+ '(-M --machine)'{-M+,--machine=}'[Operate on local container]:machines:_sd_machines' \
'--address=[Connect to the bus specified by address]:address' \
'--show-machine[Show machine ID column in list]' \
'--unique[Only show unique names]' \
'--activatable[Only show activatable names]' \
'--match=[Only show matching messages]:match:__dbus_matchspec' \
'--list[Do not show tree, but simple object path list]' \
- {-q,--quiet}'[Do not show method call reply]'\
+ '(-q --quiet)'{-q,--quiet}'[Do not show method call reply]'\
'--verbose[Show result values in long format]' \
'--xml-interface[Dump the XML description in introspect command]' \
'--json=[Show result values in long format]:format:_busctl_get_json' \
}
_arguments \
- {-o+,--output=}'[Write output to FILE]:output file:_files' \
- {-F+,--field=}'[Show field in list output]:field' \
+ '(-o --output)'{-o+,--output=}'[Write output to FILE]:output file:_files' \
+ '(-F --field)'{-F+,--field=}'[Show field in list output]:field' \
'-1[Show information about most recent entry only]' \
- {-S,--since}'[Print entries since the specified date]' \
- {-U,--until}'[Print entries until the specified date]' \
- {-r,--reverse}'[Show the newest entries first]' \
+ '(-S --since)'{-S,--since}'[Print entries since the specified date]' \
+ '(-U --until)'{-U,--until}'[Print entries until the specified date]' \
+ '(-r --reverse)'{-r,--reverse}'[Show the newest entries first]' \
'--no-pager[Do not pipe output into a pager]' \
'--no-legend[Do not print the column headers]' \
- {-h,--help}'[Show this help]' \
- '--version[Show package version]' \
+ '(- *)'{-h,--help}'[Show this help]' \
+ '(- *)--version[Show package version]' \
'--debugger=[Use the given debugger]:debugger: _command_names -e' \
- {-D,--directory=}'[Use the journal files in the specified dir]:directory: _directories' \
- {-q,--quiet}'[Do not show info messages and privilege warning]' \
+ '(-D --directory)'{-D,--directory=}'[Use the journal files in the specified dir]:directory: _directories' \
+ '(-q --quiet)'{-q,--quiet}'[Do not show info messages and privilege warning]' \
'--all[Look at all journal files instead of local ones]' \
'*::coredumpctl commands:_coredumpctl_commands'
}
_arguments -s \
- {-h,--help}'[Show this help]' \
- '--version[Show package version]' \
+ '(- *)'{-h,--help}'[Show this help]' \
+ '(- *)--version[Show package version]' \
'--transient[Only set transient hostname]' \
'--static[Only set static hostname]' \
'--pretty[Only set pretty hostname]' \
'--no-ask-password[Do not prompt for password]' \
- {-H+,--host=}'[Operate on remote host]:userathost:_sd_hosts_or_user_at_host' \
- {-M+,--machine=}'[Operate on local container]:machines:_sd_machines' \
- '--json[Shows output formatted as JSON]:format:_hostnamectl_get_json' \
+ '(-H --host)'{-H+,--host=}'[Operate on remote host]:userathost:_sd_hosts_or_user_at_host' \
+ '(-M --machine)'{-M+,--machine=}'[Operate on local container]:machines:_sd_machines' \
+ '--json=[Shows output formatted as JSON]:format:_hostnamectl_get_json' \
'*::hostnamectl commands:_hostnamectl_commands'
fi
done
_arguments -s \
- {-h,--help}'[Show this help]' \
- '--version[Show package version]' \
+ '(- *)'{-h,--help}'[Show this help]' \
+ '(- *)--version[Show package version]' \
'--no-pager[Do not pipe output into a pager]' \
--no-hostname"[Don't show the hostname of local log messages]" \
- {-l,--full}'[Show long fields in full]' \
- {-a,--all}'[Show all fields, including long and unprintable]' \
- {-f,--follow}'[Follow journal]' \
- {-e,--pager-end}'[Jump to the end of the journal in the pager]' \
- {-n+,--lines=}'[Number of journal entries to show]:integer' \
+ '(-l --full)'{-l,--full}'[Show long fields in full]' \
+ '(-a --all)'{-a,--all}'[Show all fields, including long and unprintable]' \
+ '(-f --follow)'{-f,--follow}'[Follow journal]' \
+ '(-e --pager-end)'{-e,--pager-end}'[Jump to the end of the journal in the pager]' \
+ '(-n --lines)'{-n+,--lines=}'[Number of journal entries to show]:integer' \
'--no-tail[Show all lines, even in follow mode]' \
- {-r,--reverse}'[Reverse output]' \
- {-o+,--output=}'[Change journal output mode]:output modes:_sd_outputmodes' \
- {-x,--catalog}'[Show explanatory texts with each log line]' \
- {-q,--quiet}"[Don't show privilege warning]" \
- {-m,--merge}'[Show entries from all available journals]' \
- {-b+,--boot=}'[Show data only from the specified boot or offset]::boot id or offset:_journalctl_boots' \
+ '(-r --reverse)'{-r,--reverse}'[Reverse output]' \
+ '(-o --output)'{-o+,--output=}'[Change journal output mode]:output modes:_sd_outputmodes' \
+ '(-x --catalog)'{-x,--catalog}'[Show explanatory texts with each log line]' \
+ '(-q --quiet)'{-q,--quiet}"[Don't show privilege warning]" \
+ '(-m --merge)'{-m,--merge}'[Show entries from all available journals]' \
+ '(-b --boot)'{-b+,--boot=}'[Show data only from the specified boot or offset]::boot id or offset:_journalctl_boots' \
'--list-boots[List boots ordered by time]' \
- {-k,--dmesg}'[Show only kernel messages from the current boot]' \
- {-u+,--unit=}'[Show data only from the specified unit]:units:_journalctl_field_values _SYSTEMD_UNIT' \
+ '(-k --dmesg)'{-k,--dmesg}'[Show only kernel messages from the current boot]' \
+ '(-u --unit)'{-u+,--unit=}'[Show data only from the specified unit]:units:_journalctl_field_values _SYSTEMD_UNIT' \
'--user-unit=[Show data only from the specified user session unit]:units:_journalctl_field_values USER_UNIT' \
- {-p+,--priority=}'[Show only messages within the specified priority range]:priority:_journalctl_field_values PRIORITY' \
+ '(-p --priority)'{-p+,--priority=}'[Show only messages within the specified priority range]:priority:_journalctl_field_values PRIORITY' \
'--facility=[Filter messages by facility]:facility:_journalctl_facilities' \
- {-t+,--identifier=}'[Filter messages by syslog identifier]:identifier:_journalctl_field_values SYSLOG_IDENTIFIER' \
- {-c+,--cursor=}'[Start showing entries from the specified cursor]:cursors:_journalctl_field_values __CURSORS' \
+ '(-t --identifier)'{-t+,--identifier=}'[Filter messages by syslog identifier]:identifier:_journalctl_field_values SYSLOG_IDENTIFIER' \
+ '(-c --cursor)'{-c+,--cursor=}'[Start showing entries from the specified cursor]:cursors:_journalctl_field_values __CURSORS' \
'--cursor-file=[Show entries using cursor stored in file]:file:_files' \
'--after-cursor=[Start showing entries from after the specified cursor]:cursors:_journalctl_field_values __CURSORS' \
'--since=[Start showing entries on or newer than the specified date]:YYYY-MM-DD HH\:MM\:SS' \
'--until=[Stop showing entries on or older than the specified date]:YYYY-MM-DD HH\:MM\:SS' \
- {-g+,--grep=}'[Show entries with MESSAGE field matching PCRE pattern]' \
+ '(-g --grep)'{-g+,--grep=}'[Show entries with MESSAGE field matching PCRE pattern]' \
'--case-sensitive=[Force case sensitive or insensitive matching]:boolean:(true false)' \
- {-F,--field=}'[List all values a certain field takes]:Fields:_journalctl_fields' \
+ '(-F --field)'{-F,--field=}'[List all values a certain field takes]:Fields:_journalctl_fields' \
'--system[Show system and kernel messages]' \
'--user[Show messages from user services]' \
'(--directory -D -M --machine --root --file)'{-M+,--machine=}'[Operate on local container]:machines:_sd_machines' \
}
_arguments \
+ '(- :)'{-h,--help}'[Show help]' \
+ '(- :)--version[Show package version]' \
'1::add or remove:(add remove)' \
'2::kernel versions:_kernel-install_kernels' \
'3::kernel images:_kernel-install_images'
}
_arguments \
- {-h,--help}'[Show this help]' \
+ '(- *)'{-h,--help}'[Show this help]' \
'--version[Show package version]' \
"--no-convert[Don't convert keyboard mappings]" \
'--no-pager[Do not pipe output into a pager]' \
'--no-ask-password[Do not prompt for password]' \
- {-H+,--host=}'[Operate on remote host]:userathost:_sd_hosts_or_user_at_host' \
- {-M+,--machine=}'[Operate on local container]:machine' \
+ '(-H --host)'{-H+,--host=}'[Operate on remote host]:userathost:_sd_hosts_or_user_at_host' \
+ '(-M --machine)'{-M+,--machine=}'[Operate on local container]:machine' \
'*::localectl commands:_localectl_commands'
_arguments -s \
- {-h,--help}'[Show help]' \
- '--version[Show package version]' \
- \*{-p+,--property=}'[Show only properties by this name]:unit property' \
- {-a,--all}'[Show all properties, including empty ones]' \
+ '(- *)'{-h,--help}'[Show help]' \
+ '(- *)--version[Show package version]' \
+ '*'{-p+,--property=}'[Show only properties by this name]:unit property' \
+ '(-a --all)'{-a,--all}'[Show all properties, including empty ones]' \
'--kill-whom=[Whom to send signal to]:killwhom:(main control all)' \
- {-s+,--signal=}'[Which signal to send]:signal:_signals' \
- {-H+,--host=}'[Operate on remote host]:userathost:_sd_hosts_or_user_at_host' \
- {-M+,--machine=}'[Operate on local container]:machine:_sd_machines' \
- {-l,--full}'[Do not ellipsize output]' \
+ '(-s --signal)'{-s+,--signal=}'[Which signal to send]:signal:_signals' \
+ '(-H --host)'{-H+,--host=}'[Operate on remote host]:userathost:_sd_hosts_or_user_at_host' \
+ '(-M --machine)'{-M+,--machine=}'[Operate on local container]:machine:_sd_machines' \
+ '(-l --full)'{-l,--full}'[Do not ellipsize output]' \
'--no-pager[Do not pipe output into a pager]' \
'--no-legend[Do not show the headers and footers]' \
'--no-ask-password[Do not ask for system passwords]' \
- {-n+,--lines=}'[Number of journal entries to show]' \
- {-o+,--output=}'[Change journal output mode]:output modes:_sd_outputmodes' \
+ '(-n --lines)'{-n+,--lines=}'[Number of journal entries to show]' \
+ '(-o --output)'{-o+,--output=}'[Change journal output mode]:output modes:_sd_outputmodes' \
'*::loginctl command:_loginctl_commands'
}
_arguments \
- {-h,--help}'[Prints a short help text and exits.]' \
- '--version[Prints a short version string and exits.]' \
+ '(- *)'{-h,--help}'[Prints a short help text and exits.]' \
+ '(- *)--version[Prints a short version string and exits.]' \
'--no-pager[Do not pipe output into a pager.]' \
'--no-legend[Do not show the headers and footers.]' \
'--no-ask-password[Do not ask for system passwords.]' \
- {-H+,--host=}'[Operate on remote host.]:userathost:_sd_hosts_or_user_at_host' \
- {-M+,--machine=}'[Operate on local container.]:machine:_sd_machines' \
- {-p+,--property=}'[Limit output to specified property.]:property:(Name Id Timestamp TimestampMonotonic Service Scope Leader Class State RootDirectory)' \
- {-a,--all}'[Show all properties.]' \
- {-q,--quiet}'[Suppress output.]' \
- {-l,--full}'[Do not ellipsize cgroup members.]' \
+ '(-H --host)'{-H+,--host=}'[Operate on remote host.]:userathost:_sd_hosts_or_user_at_host' \
+ '(-M --machine)'{-M+,--machine=}'[Operate on local container.]:machine:_sd_machines' \
+ '(-p --property)'{-p+,--property=}'[Limit output to specified property.]:property:(Name Id Timestamp TimestampMonotonic Service Scope Leader Class State RootDirectory)' \
+ '(-a --all)'{-a,--all}'[Show all properties.]' \
+ '(-q --quiet)'{-q,--quiet}'[Suppress output.]' \
+ '(-l --full)'{-l,--full}'[Do not ellipsize cgroup members.]' \
'--kill-whom=[Whom to send signal to.]:killwhom:(leader all)' \
- {-s+,--signal=}'[Which signal to send.]:signal:_signals' \
+ '(-s --signal)'{-s+,--signal=}'[Which signal to send.]:signal:_signals' \
'--read-only[Create read-only bind mount.]' \
'--mkdir[Create directory before bind mounting, if missing.]' \
- {-n+,--lines=}'[Number of journal entries to show.]:integer' \
- {-o+,--output=}'[Change journal output mode.]:output modes:_sd_outputmodes' \
+ '(-n --lines)'{-n+,--lines=}'[Number of journal entries to show.]:integer' \
+ '(-o --output)'{-o+,--output=}'[Change journal output mode.]:output modes:_sd_outputmodes' \
'--verify=[Verification mode for downloaded images.]:verify:(no checksum signature)' \
'--force[Download image even if already exists.]' \
'*::machinectl command:_machinectl_commands'
}
_arguments \
- {-a,--all}'[Show all links with status]' \
+ '(-a --all)'{-a,--all}'[Show all links with status]' \
'--no-pager[Do not pipe output into a pager]' \
'--no-legend[Do not print the column headers]' \
- {-h,--help}'[Show this help]' \
- '--version[Show package version]' \
- '--drop-in=[Use the given drop-in file name]' \
+ '(- *)'{-h,--help}'[Show this help]' \
+ '(- *)--version[Show package version]' \
+ '--drop-in=[Use the given drop-in file name]:NAME' \
'--no-reload[Do not reload the network manager state when editing]' \
- '--json[Shows output formatted as JSON]:format:_networkctl_get_json' \
+ '--json=[Shows output formatted as JSON]:format:_networkctl_get_json' \
'*::networkctl commands:_networkctl_commands'
}
_arguments \
- {-h,--help}'[Prints a short help text and exits.]' \
- '--version[Prints a short version string and exits.]' \
+ '(- *)'{-h,--help}'[Prints a short help text and exits.]' \
+ '(- *)--version[Prints a short version string and exits.]' \
'--no-pager[Do not pipe output into a pager]' \
'*::oomctl command:_oomctl_commands'
}
_arguments \
- {-h,--help}'[Print a short help text and exit]' \
- '--version[Print a short version string and exit]' \
- '--legend=no[Do not show headers and footers]' \
+ '(- *)'{-h,--help}'[Print a short help text and exit]' \
+ '(- *)--version[Print a short version string and exit]' \
+ '--legend=[Do not show headers and footers]:BOOL:(yes no)' \
'-4[Resolve IPv4 addresses]' \
'-6[Resolve IPv6 addresses]' \
- {-i+,--interface=}'[Look on interface]:interface:_net_interfaces' \
- {-p+,--protocol=}'[Look via protocol]:protocol:_resolvectl_protocols' \
- {-t+,--type=}'[Query RR with DNS type]:type:_resolvectl_types' \
- {-c+,--class=}'[Query RR with DNS class]:class:_resolvectl_classes' \
+ '(-i --interface)'{-i+,--interface=}'[Look on interface]:interface:_net_interfaces' \
+ '(-p --protocol)'{-p+,--protocol=}'[Look via protocol]:protocol:_resolvectl_protocols' \
+ '(-t --type)'{-t+,--type=}'[Query RR with DNS type]:type:_resolvectl_types' \
+ '(-c --class)'{-c+,--class=}'[Query RR with DNS class]:class:_resolvectl_classes' \
'--service[Resolve services]' \
- '--service-address=no[Do not resolve address for services]' \
- '--service-txt=no[Do not resolve TXT records for services]' \
- '--cname=no[Do not follow CNAME redirects]' \
- '--search=no[Do not use search domains]' \
+ '--service-address=[Do not resolve address for services]:BOOL:(yes no)' \
+ '--service-txt=[Do not resolve TXT records for services]:BOOL:(yes no)' \
+ '--cname=[Do not follow CNAME redirects]:BOOL:(yes no)' \
+ '--search=[Do not use search domains]:BOOL:(yes no)' \
'*::default: _resolvectl_commands'
# Use the last mode, or --system (they are exclusive and the last one is used).
local _sys_service_mgr=${words[(R)(--user|--system)]:---system}
_arguments -s \
- {-h,--help}'[Show help]' \
- '--version[Show package version]' \
- {-t+,--type=}'[List only units of a particular type]:unit type:_systemctl_unit_types' \
+ '(- *)'{-h,--help}'[Show help]' \
+ '(- *)--version[Show package version]' \
+ '(-t --type)'{-t+,--type=}'[List only units of a particular type]:unit type:_systemctl_unit_types' \
'--state=[Display units in the specified state]:unit state:_systemctl_unit_states' \
'--job-mode=[Specify how to deal with other jobs]:mode:_systemctl_job_modes' \
- {-p+,--property=}'[Show only properties by specific name]:unit property:_systemctl_unit_properties' \
- {-a,--all}'[Show all units/properties, including dead/empty ones]' \
+ '(-p --property)'{-p+,--property=}'[Show only properties by specific name]:unit property:_systemctl_unit_properties' \
+ '(-a --all)'{-a,--all}'[Show all units/properties, including dead/empty ones]' \
'--reverse[Show reverse dependencies]' \
'--after[Show units ordered after]' \
'--before[Show units ordered before]' \
- {-l,--full}"[Don't ellipsize unit names on output]" \
+ '(-l --full)'{-l,--full}"[Don't ellipsize unit names on output]" \
'--show-types[When showing sockets, show socket type]' \
'--check-inhibitors[Specify if inhibitors should be checked]:mode:_systemctl_check_inhibitors' \
- {-q,--quiet}'[Suppress output]' \
+ '(-q --quiet)'{-q,--quiet}'[Suppress output]' \
'--no-warn[Suppress several warnings shown by default]' \
'--no-block[Do not wait until operation finished]' \
'--legend=no[Do not print a legend, i.e. the column headers and the footer with hints]' \
"--no-reload[When enabling/disabling unit files, don't reload daemon configuration]" \
'--no-ask-password[Do not ask for system passwords]' \
'--kill-whom=[Whom to send signal to]:killwhom:(main control all)' \
- {-s+,--signal=}'[Which signal to send]:signal:_signals' \
- {-f,--force}'[When enabling unit files, override existing symlinks. When shutting down, execute action immediately]' \
+ '(-s --signal)'{-s+,--signal=}'[Which signal to send]:signal:_signals' \
+ '(-f --force)'{-f,--force}'[When enabling unit files, override existing symlinks. When shutting down, execute action immediately]' \
'--root=[Enable/disable/mask unit files in the specified root directory]:directory:_directories' \
'--runtime[Enable/disable/mask unit files only temporarily until next reboot]' \
- {-H+,--host=}'[Operate on remote host]:userathost:_sd_hosts_or_user_at_host' \
- {-P,--privileged}'[Acquire privileges before execution]' \
- {-n+,--lines=}'[Journal entries to show]:number of entries' \
- {-o+,--output=}'[Change journal output mode]:modes:_sd_outputmodes' \
+ '(-H --host)'{-H+,--host=}'[Operate on remote host]:userathost:_sd_hosts_or_user_at_host' \
+ '(-P --privileged)'{-P,--privileged}'[Acquire privileges before execution]' \
+ '(-n --lines)'{-n+,--lines=}'[Journal entries to show]:number of entries' \
+ '(-o --output)'{-o+,--output=}'[Change journal output mode]:modes:_sd_outputmodes' \
'--firmware-setup[Tell the firmware to show the setup menu on next boot]' \
'--plain[When used with list-dependencies, print output as a list]' \
'--failed[Show failed units]' \
case "$service" in
systemd-ask-password)
_arguments \
- {-h,--help}'[Show this help]' \
+ '(- *)'{-h,--help}'[Show this help]' \
'--icon=[Icon name]:icon name:' \
'--timeout=[Timeout in sec]:timeout (seconds):' \
'--no-tty[Ask question via agent even on TTY]' \
;;
systemd-cat)
_arguments \
- {-h,--help}'[Show this help]' \
- '--version[Show package version.]' \
- {-t+,--identifier=}'[Set syslog identifier.]:syslog identifier:' \
- {-p+,--priority=}'[Set priority value.]:value:({0..7})' \
+ '(- *)'{-h,--help}'[Show this help]' \
+ '(- *)--version[Show package version.]' \
+ '(-t --identifier)'{-t+,--identifier=}'[Set syslog identifier.]:syslog identifier:' \
+ '(-p --priority)'{-p+,--priority=}'[Set priority value.]:value:({0..7})' \
'--level-prefix=[Control whether level prefix shall be parsed.]:boolean:(1 0)' \
'--namespace=[Connect to specified journal namespace.]:journal namespace:' \
':Message'
;;
systemd-cgls)
_arguments \
- {-h,--help}'[Show this help]' \
- '--version[Show package version]' \
+ '(- *)'{-h,--help}'[Show this help]' \
+ '(- *)--version[Show package version]' \
'--no-pager[Do not pipe output into a pager]' \
- {-a,--all}'[Show all groups, including empty]' \
+ '(-a --all)'{-a,--all}'[Show all groups, including empty]' \
'-k[Include kernel threads in output]' \
':cgroups:(cpuset cpu cpuacct memory devices freezer blkio)'
;;
systemd-cgtop)
_arguments \
- {-h,--help}'[Show this help]' \
- '--version[Print version and exit]' \
+ '(-)'{-h,--help}'[Show this help]' \
+ '(-)--version[Print version and exit]' \
'(-c -m -i -t)-p[Order by path]' \
'(-c -p -m -i)-t[Order by number of tasks]' \
'(-m -p -i -t)-c[Order by CPU load]' \
'(-c -p -i -t)-m[Order by memory load]' \
'(-c -m -p -t)-i[Order by IO load]' \
- {-d+,--delay=}'[Specify delay]:delay:' \
- {-n+,--iterations=}'[Run for N iterations before exiting]:number of iterations:' \
- {-b,--batch}'[Run in batch mode, accepting no input]' \
+ '(-d --delay)'{-d+,--delay=}'[Specify delay]:delay:' \
+ '(-n --iterations)'{-n+,--iterations=}'[Run for N iterations before exiting]:number of iterations:' \
+ '(-b --batch)'{-b,--batch}'[Run in batch mode, accepting no input]' \
'--depth=[Maximum traversal depth]:maximum depth:'
;;
systemd-detect-virt)
_arguments \
- {-h,--help}'[Show this help]' \
- '--version[Show package version]' \
- {-c,--container}'[Only detect whether we are run in a container]' \
- {-v,--vm}'[Only detect whether we are run in a VM]' \
- {-q,--quiet}"[Don't output anything, just set return value]"
+ '(-)'{-h,--help}'[Show this help]' \
+ '(-)--version[Show package version]' \
+ '(-c --container)'{-c,--container}'[Only detect whether we are run in a container]' \
+ '(-v --vm)'{-v,--vm}'[Only detect whether we are run in a VM]' \
+ '(-q --quiet)'{-q,--quiet}"[Don't output anything, just set return value]"
;;
systemd-machine-id-setup)
_arguments \
- {-h,--help}'[Show this help]' \
- '--version[Show package version]'
+ '(-)'{-h,--help}'[Show this help]' \
+ '(-)--version[Show package version]'
;;
systemd-notify)
_arguments \
- {-h,--help}'[Show this help]' \
- '--version[Show package version]' \
+ '(-)'{-h,--help}'[Show this help]' \
+ '(-)--version[Show package version]' \
'--ready[Inform the init system about service start-up completion.]' \
'--pid=[Inform the init system about the main PID of the daemon]:daemon main PID:_pids' \
'--status=[Send a free-form status string for the daemon to the init systemd]:status string:' \
;;
systemd-tty-ask-password-agent)
_arguments \
- {-h,--help}'[Prints a short help text and exits.]' \
- '--version[Prints a short version string and exits.]' \
- '--list[Lists all currently pending system password requests.]' \
+ '(-)'{-h,--help}'[Prints a short help text and exits.]' \
+ '(-)--version[Prints a short version string and exits.]' \
+ '--list[Lists all currently pending system password requests.]' \
'--query[Process all currently pending system password requests by querying the user on the calling TTY.]' \
'--watch[Continuously process password requests.]' \
'--wall[Forward password requests to wall(1).]' \
}
_arguments \
- {-h,--help}'[Show help text]' \
- '--version[Show package version]' \
+ '(- *)'{-h,--help}'[Show help text]' \
+ '(- *)--version[Show package version]' \
'--system[Operate on system systemd instance]' \
'--user[Operate on user systemd instance]' \
'--global[Show global user instance config]' \
'--fuzz=[When printing the tree of the critical chain, print also services, which finished TIMESPAN earlier, than the latest in the branch]:TIMESPAN' \
'--from-pattern=[When generating a dependency graph, filter only origins]:GLOB' \
'--to-pattern=[When generating a dependency graph, filter only destinations]:GLOB' \
- {-H+,--host=}'[Operate on remote host]:userathost:_sd_hosts_or_user_at_host' \
- {-M+,--machine=}'[Operate on local container]:machine:_sd_machines' \
+ '(-H --host)'{-H+,--host=}'[Operate on remote host]:userathost:_sd_hosts_or_user_at_host' \
+ '(-M --machine)'{-M+,--machine=}'[Operate on local container]:machine:_sd_machines' \
'--quiet[Do not show hints]' \
'*::systemd-analyze commands:_systemd-analyze_commands'
}
_arguments \
- {-h,--help}'[Show this help]' \
- '--version[Show package version]' \
+ '(- :)'{-h,--help}'[Show this help]' \
+ '(- :)--version[Show package version]' \
'--no-pager[Do not pipe output into a pager]' \
'--diff=[Show a diff when overridden files differ]:boolean:(1 0)' \
{-t+,--type=}'[Only display a selected set of override types]:types:_systemd-delta_types' \
}
_arguments \
- {-h,--help}'[Show this help]' \
- '--version[Show package version]' \
+ '(- *)'{-h,--help}'[Show this help]' \
+ '(- *)--version[Show package version]' \
+ '--no-pager[Do not pipe output into a pager]' \
+ '--no-legend[Do not show the headers and footers]' \
'--what=[Operations to inhibit]:options:_systemd-inhibit_what' \
'--who=[A descriptive string who is inhibiting]:who is inhibiting:' \
'--why=[A descriptive string why is being inhibited]:reason for the lock:' \
}
_arguments \
- {-h,--help}'[Show this help.]' \
- '--version[Print a short version string and exit.]' \
- {--quiet,-q}'[Turns off any status output by the tool itself.]' \
- {--directory=,-D+}'[Directory to use as file system root for the namespace container. If omitted the current directory will be used.]:directories:_directories' \
+ '(- *)'{-h,--help}'[Show this help.]' \
+ '(- *)--version[Print a short version string and exit.]' \
+ '(--quiet -q)'{--quiet,-q}'[Turns off any status output by the tool itself.]' \
+ '(--directory -D)'{--directory=,-D+}'[Directory to use as file system root for the namespace container. If omitted the current directory will be used.]:directories:_directories' \
'--template=[Initialize root directory from template directory, if missing.]:template:_directories' \
- {--ephemeral,-x}'[Run container with snapshot of root directory, and remove it after exit.]' \
- {--image=,-i+}'[Disk image to mount the root directory for the container from.]:disk image: _files' \
- {--boot,-b}'[Automatically search for an init binary and invoke it instead of a shell or a user supplied program.]' \
- {--user=,-u+}'[Run the command under specified user, create home directory and cd into it.]:user:_users' \
- {--machine=,-M+}'[Sets the machine name for this container.]: : _message "container name"' \
+ '(--ephemeral -x)'{--ephemeral,-x}'[Run container with snapshot of root directory, and remove it after exit.]' \
+ '(--image -i)'{--image=,-i+}'[Disk image to mount the root directory for the container from.]:disk image: _files' \
+ '(--boot -b)'{--boot,-b}'[Automatically search for an init binary and invoke it instead of a shell or a user supplied program.]' \
+ '(--user -u)'{--user=,-u+}'[Run the command under specified user, create home directory and cd into it.]:user:_users' \
+ '(--machine -M)'{--machine=,-M+}'[Sets the machine name for this container.]: : _message "container name"' \
'--uuid=[Set the specified uuid for the container.]: : _message "container UUID"' \
- {--slice=,-S+}'[Make the container part of the specified slice, instead of the default machine.slice.]: : _message slice' \
+ '(--slice -S)'{--slice=,-S+}'[Make the container part of the specified slice, instead of the default machine.slice.]: : _message slice' \
'--private-network[Disconnect networking of the container from the host.]' \
'--network-interface=[Assign the specified network interface to the container.]: : _net_interfaces' \
'--network-macvlan=[Create a "macvlan" interface of the specified Ethernet network interface and add it to the container.]: : _net_interfaces' \
'--network-ipvlan=[Create an "ipvlan" network interface based on an existing network interface to the container.]: : _net_interfaces' \
- {--network-veth,-n}'[Create a virtual Ethernet link (veth) between host and container.]' \
+ '(--network-veth -n)'{--network-veth,-n}'[Create a virtual Ethernet link (veth) between host and container.]' \
'--network-bridge=[Adds the host side of the Ethernet link created with --network-veth to the specified bridge.]: : _net_interfaces' \
- {--port=,-p+}'[Expose a container IP port on the host.]: : _message port' \
- {--selinux-context=,-Z+}'[Sets the SELinux security context to be used to label processes in the container.]: : _message "SELinux context"' \
- {--selinux-apifs-context=,-L+}'[Sets the SELinux security context to be used to label files in the virtual API file systems in the container.]: : _message "SELinux context"' \
+ '(--port -p)'{--port=,-p+}'[Expose a container IP port on the host.]: : _message port' \
+ '(--selinux-context -Z)'{--selinux-context=,-Z+}'[Sets the SELinux security context to be used to label processes in the container.]: : _message "SELinux context"' \
+ '(--selinux-apifs-context -L)'{--selinux-apifs-context=,-L+}'[Sets the SELinux security context to be used to label files in the virtual API file systems in the container.]: : _message "SELinux context"' \
'--capability=[List one or more additional capabilities to grant the container.]:capabilities:_systemd-nspawn_caps' \
'--drop-capability=[Specify one or more additional capabilities to drop for the containerm]:capabilities:_systemd-nspawn_caps' \
"--link-journal=[Control whether the container's journal shall be made visible to the host system.]:options:(no host guest auto)" \
typeset -A sdpath=( ${$(systemd-path)/:/} )
_arguments -S \
- '(-h --help)'{-h,--help}'[Print help text and exit]' \
- '(-v --version)'{-v,--version}'[Print a version string and exit]' \
- '--suffix=[Append a suffix to the paths]' \
+ '(- *)'{-h,--help}'[Print help text and exit]' \
+ '(- *)'{-v,--version}'[Print a version string and exit]' \
+ '--suffix=[Append a suffix to the paths]:SUFFIX' \
'*:pathname:compadd -k sdpath'
}
_arguments \
- {-G,--collect}'[Unload the transient unit after it completed]' \
+ '(-G --collect)'{-G,--collect}'[Unload the transient unit after it completed]' \
'--description=[Description for unit]:description' \
'--gid=[Run as system group]:group:_groups' \
- {-h,--help}'[Show help message]' \
- {-H+,--host=}'[Operate on remote host]:[user@]host:_sd_hosts_or_user_at_host' \
- {-M+,--machine=}'[Operate on local container]:machines:_sd_machines' \
+ '(- *)'{-h,--help}'[Show help message]' \
+ '(-H --host)'{-H+,--host=}'[Operate on remote host]:[user@]host:_sd_hosts_or_user_at_host' \
+ '(-M --machine)'{-M+,--machine=}'[Operate on local container]:machines:_sd_machines' \
'--nice=[Nice level]:nice level' \
'--no-ask-password[Do not query the user for authentication]' \
'--no-block[Do not synchronously wait for the unit start operation to finish]' \
'--on-unit-active=[Run SEC seconds after the last activation]:SEC' \
'--on-unit-inactive=[Run SEC seconds after the last deactivation]:SEC' \
'--path-property=[Set path unit property]:NAME=VALUE' \
- {-P,--pipe}'[Inherit standard input, output, and error]' \
- {-p+,--property=}'[Set unit property]:NAME=VALUE:(( \
+ '(-P --pipe)'{-P,--pipe}'[Inherit standard input, output, and error]' \
+ '(-p --property)'{-p+,--property=}'[Set unit property]:NAME=VALUE:(( \
CPUAccounting= MemoryAccounting= BlockIOAccounting= SendSIGHUP= \
SendSIGKILL= MemoryLimit= CPUShares= BlockIOWeight= User= Group= \
DevicePolicy= KillMode= ExitType= DeviceAllow= BlockIOReadBandwidth= \
ReadOnlyPaths= InaccessiblePaths= EnvironmentFile= \
ProtectSystem= ProtectHome= RuntimeDirectory= PassEnvironment= \
))' \
- {-t,--pty}'[The service connects to the terminal]' \
- {-q,--quiet}'[Suppresses additional informational output]' \
- {-r,--remain-after-exit}'[Leave service around until explicitly stopped]' \
- {-d,--same-dir}'[Run on the current working directory]' \
+ '(-t --pty)'{-t,--pty}'[The service connects to the terminal]' \
+ '(-q --quiet)'{-q,--quiet}'[Suppresses additional informational output]' \
+ '(-r --remain-after-exit)'{-r,--remain-after-exit}'[Leave service around until explicitly stopped]' \
+ '(-d --same-dir)'{-d,--same-dir}'[Run on the current working directory]' \
'--scope[Run this as scope rather than service]' \
'--send-sighup[Send SIGHUP when terminating]' \
'--service-type=[Service type]:type:(simple forking oneshot dbus notify idle)' \
- {-E+,--setenv=}'[Set environment]:NAME=VALUE' \
- {-S,--shell}'[requests an interactive shell in the current working directory]' \
+ '(-E --setenv)'{-E+,--setenv=}'[Set environment]:NAME=VALUE' \
+ '(-S --shell)'{-S,--shell}'[requests an interactive shell in the current working directory]' \
'--slice=[Run in the specified slice]:slices:__systemd-run_slices' \
'--slice-inherit[Run in the inherited slice]' \
'--socket-property=[Set socket unit property]:NAME=VALUE' \
'--system[Run as system unit]' \
'--timer-property=[Set timer unit property]:NAME=VALUE' \
'--uid=[Run as system user]:user:_users' \
- {-u+,--unit=}'[Run under the specified unit name]:unit name' \
+ '(-u --unit)'{-u+,--unit=}'[Run under the specified unit name]:unit name' \
'--user[Run as user unit]' \
'--version[Show package version]' \
'--wait=[Wait until service stopped again]' \
# SPDX-License-Identifier: LGPL-2.1-or-later
_arguments \
- {-h,--help}'[Show help]' \
- '--version[Show package version]' \
+ '(- *)'{-h,--help}'[Show help]' \
+ '--user[Execute user configuration]' \
+ '(- *)--version[Show package version]' \
+ '--cat-config[Show configuration files]' \
+ '--tldr[Show non-comment parts of configuration]' \
'--create[Create, set ownership/permissions based on the config files.]' \
'--clean[Clean up all files and directories with an age parameter configured.]' \
'--remove[All files and directories marked with r, R in the configuration files are removed.]' \
'--boot[Execute actions only safe at boot]' \
- '--prefix=[Only apply rules that apply to paths with the specified prefix.]' \
- '--exclude-prefix=[Ignore rules that apply to paths with the specified prefix.]' \
+ '--graceful[Quietly ignore unknown users or groups]' \
+ '--prefix=[Only apply rules that apply to paths with the specified prefix.]:PATH' \
+ '--exclude-prefix=[Ignore rules that apply to paths with the specified prefix.]:PATH' \
+ '-E[Ignore rules prefixed with /dev, /proc, /run, /sys]' \
'--root=[Operate on an alternate filesystem root]:directory:_directories' \
+ '--image=[Operate on disk image as filesystem root]:image' \
+ '--image-policy=[Specify disk image dissection policy]:policy' \
+ '--replace=[Treat arguments as replacement for PATH]:PATH' \
+ '--no-pager[Do not pipe output into a pager]' \
'*::files:_files'
}
_arguments -s \
- {-h,--help}'[Show this help]' \
- '--version[Show package version]' \
+ '(- *)'{-h,--help}'[Show this help]' \
+ '(- *)--version[Show package version]' \
'--adjust-system-clock[Adjust system clock when changing local RTC mode]' \
'--no-pager[Do not pipe output into a pager]' \
'--no-ask-password[Do not prompt for password]' \
- {-H+,--host=}'[Operate on remote host]:userathost:_sd_hosts_or_user_at_host' \
- {-M+,--machine=}'[Operate on local container]:machines:_sd_machines' \
+ '(-H --host)'{-H+,--host=}'[Operate on remote host]:userathost:_sd_hosts_or_user_at_host' \
+ '(-M --machine)'{-M+,--machine=}'[Operate on local container]:machines:_sd_machines' \
'*::timedatectl commands:_timedatectl_command'
(( $+functions[_udevadm_info] )) ||
_udevadm_info(){
_arguments \
+ '(-)'{-h,--help}'[Print help]' \
+ '(-)'{-V,--version}'[Print version of the program]' \
'--query=[Query the database for specified type of device data. It needs the --path or --name to identify the specified device.]:type:(name symlink path property all)' \
'--path=[The devpath of the device to query.]:sys files:_files -P /sys/ -W /sys' \
'--name=[The name of the device node or a symlink to query]:device files:_files -P /dev/ -W /dev' \
'--export-db[Export the content of the udev database.]' \
'--cleanup-db[Cleanup the udev database.]' \
'--value[When showing properties, print only their values.]' \
- '--property=[Show only properties by this name.]'
+ '--property=[Show only properties by this name.]:NAME'
}
(( $+functions[_udevadm_trigger] )) ||
_udevadm_trigger(){
_arguments \
+ '(-)'{-h,--help}'[Show help]' \
+ '(-)'{-V,--version}'[Show package version]' \
'--verbose[Print the list of devices which will be triggered.]' \
'--dry-run[Do not actually trigger the event.]' \
'--quiet[Suppress error logging in triggering events.]' \
'--type=[Trigger a specific type of devices.]:types:(all devices subsystems failed)' \
'--action=[Type of event to be triggered.]:actions:(add change remove move online offline bind unbind)' \
- '--subsystem-match=[Trigger events for devices which belong to a matching subsystem.]' \
- '--subsystem-nomatch=[Do not trigger events for devices which belong to a matching subsystem.]' \
- '--attr-match=attribute=[Trigger events for devices with a matching sysfs attribute.]' \
- '--attr-nomatch=attribute=[Do not trigger events for devices with a matching sysfs attribute.]' \
- '--property-match=[Trigger events for devices with a matching property value.]' \
- '--tag-match=property[Trigger events for devices with a matching tag.]' \
- '--sysname-match=[Trigger events for devices with a matching sys device name.]' \
- '--parent-match=[Trigger events for all children of a given device.]' \
+ '--subsystem-match=[Trigger events for devices which belong to a matching subsystem.]:SUBSYSTEM' \
+ '--subsystem-nomatch=[Do not trigger events for devices which belong to a matching subsystem.]:SUBSYSTEM' \
+ '--attr-match=attribute=[Trigger events for devices with a matching sysfs attribute.]:FILE' \
+ '--attr-nomatch=attribute=[Do not trigger events for devices with a matching sysfs attribute.]:FILE' \
+ '--property-match=[Trigger events for devices with a matching property value.]:KEY=VALUE' \
+ '--tag-match=[Trigger events for devices with a matching tag.]:TAG' \
+ '--sysname-match=[Trigger events for devices with a matching sys device name.]:NAME' \
+ '--parent-match=[Trigger events for all children of a given device.]:NAME' \
'--initialized-match[Trigger events for devices that are already initialized.]' \
'--initialized-nomatch[Trigger events for devices that are not initialized yet.]' \
'--uuid[Print synthetic uevent UUID.]' \
- '--prioritized-subsystem=[Trigger events for devices which belong to a matching subsystem earlier.]'
+ '--prioritized-subsystem=[Trigger events for devices which belong to a matching subsystem earlier.]:SUBSYSTEM'
}
(( $+functions[_udevadm_settle] )) ||
_udevadm_settle(){
_arguments \
- '--timeout=[Maximum number of seconds to wait for the event queue to become empty.]' \
- '--seq-start=[Wait only for events after the given sequence number.]' \
- '--seq-end=[Wait only for events before the given sequence number.]' \
- '--exit-if-exists=[Stop waiting if file exists.]:files:_files' \
- '--quiet[Do not print any output, like the remaining queue entries when reaching the timeout.]' \
- '--help[Print help text.]'
+ '(-)'{-h,--help}'[Print help]' \
+ '(-)'{-V,--version}'[Print version of the program]' \
+ '(-t --timeout)'{-t,--timeout=}'[Maximum number of seconds to wait for the event queue to become empty.]:SEC' \
+ '(-E --exit-if-exists)'{-E,--exit-if-exists=}'[Stop waiting if file exists.]:files:_files'
}
(( $+functions[_udevadm_control] )) ||
_udevadm_control(){
_arguments \
- '--exit[Signal and wait for systemd-udevd to exit.]' \
- '--log-priority=[Set the internal log level of systemd-udevd.]:priorities:(err info debug)' \
- '--stop-exec-queue[Signal systemd-udevd to stop executing new events. Incoming events will be queued.]' \
- '--start-exec-queue[Signal systemd-udevd to enable the execution of events.]' \
- '--reload[Signal systemd-udevd to reload the rules files and other databases like the kernel module index.]' \
- '--property=[Set a global property for all events.]' \
- '--children-max=[Set the maximum number of events.]' \
- '--timeout=[The maximum number of seconds to wait for a reply from systemd-udevd.]' \
- '--help[Print help text.]'
+ '(-)'{-h,--help}'[Show help]' \
+ '(-)'{-V,--version}'[Show package version]' \
+ '(-e --exit)'{-e,--exit}'[Signal and wait for systemd-udevd to exit.]' \
+ '(-l --log-level)'{-l,--log-level=}'[Set the internal log level of systemd-udevd.]:LEVEL:(err info debug)' \
+ '(-s --stop-exec-queue)'{-s,--stop-exec-queue}'[Signal systemd-udevd to stop executing new events. Incoming events will be queued.]' \
+ '(-S --start-exec-queue)'{-S,--start-exec-queue}'[Signal systemd-udevd to enable the execution of events.]' \
+ '(-R --reload)'{-R,--reload}'[Signal systemd-udevd to reload the rules files and other databases like the kernel module index.]' \
+ '(-p --property)'{-p,--property=}'[Set a global property for all events.]:KEY=VALUE' \
+ '(-m --children-max=)'{-m,--children-max=}'[Set the maximum number of events.]:N' \
+ '(-t --timeout=)'{-t,--timeout=}'[The maximum number of seconds to wait for a reply from systemd-udevd.]:SECONDS'
}
(( $+functions[_udevadm_monitor] )) ||
_udevadm_monitor(){
_arguments \
- '--kernel[Print the kernel uevents.]' \
- '--udev[Print the udev event after the rule processing.]' \
- '--property[Also print the properties of the event.]' \
- '--subsystem-match=[Filter events by subsystem/\[devtype\].]' \
- '--tag-match=[Filter events by property.]' \
- '--help[Print help text.]'
+ '(-)'{-h,--help}'[Show help]' \
+ '(-)'{-V,--version}'[Show package version]' \
+ '(-k --kernel)'{-k,--kernel}'[Print the kernel uevents.]' \
+ '(-u --udev)'{-u,--udev}'[Print the udev event after the rule processing.]' \
+ '(-p --property)'{-p,--property}'[Also print the properties of the event.]' \
+ '(-s --subsystem-match)'{-s,--subsystem-match=}'[Filter events by subsystem/\[devtype\].]:SUBSYSTEM' \
+ '(-t --tag-match)'{-t,--tag-match=}'[Filter events by property.]:TAG'
}
(( $+functions[_udevadm_test] )) ||
_udevadm_test(){
_arguments \
+ '(-)'{-h,--help}'[Show help]' \
+ '(-)'{-V,--version}'[Show package version]' \
'--action=[The action string.]:actions:(add change remove move online offline bind unbind)' \
'--subsystem=[The subsystem string.]' \
- '--help[Print help text.]' \
'*::devpath:_files -P /sys/ -W /sys'
}
_udevadm_test-builtin(){
if (( CURRENT == 2 )); then
_arguments \
+ '(- *)'{-h,--help}'[Print help]' \
+ '(- *)'{-V,--version}'[Print version of the program]' \
'--action=[The action string.]:actions:(add change remove move online offline bind unbind)' \
- '--help[Print help text]' \
'*::builtins:(blkid btrfs hwdb input_id net_id net_setup_link kmod path_id usb_id uaccess)'
elif (( CURRENT == 3 )); then
_arguments \
'--action=[The action string.]:actions:(add change remove move online offline bind unbind)' \
- '--help[Print help text]' \
+ '(- *)--help[Print help text]' \
'*::syspath:_files -P /sys -W /sys'
else
_arguments \
'--action=[The action string.]:actions:(add change remove move online offline bind unbind)' \
- '--help[Print help text]'
+ '(- *)--help[Print help text]'
fi
}
(( $+functions[_udevadm_verify] )) ||
_udevadm_verify(){
_arguments \
- {-N+,--resolve-names=}'[When to resolve names.]:resolve:(early never)' \
+ '(- *)'{-h,--help}'[Show help]' \
+ '(- *)'{-V,--version}'[Show package version]' \
+ '(-N --resolve-names)'{-N+,--resolve-names=}'[When to resolve names.]:resolve:(early never)' \
'--root=[Operate on catalog hierarchy under specified directory]:directories:_directories' \
- {--no-summary}'[Do not show summary.]' \
- {--no-style}'[Ignore style issues.]' \
- {-h,--help}'[Print help text.]' \
+ --no-summary'[Do not show summary.]' \
+ --no-style'[Ignore style issues.]' \
'*::files:_files'
}
(( $+functions[_udevadm_wait] )) ||
_udevadm_wait(){
_arguments \
- '--timeout=[Maximum number of seconds to wait for the devices being created.]' \
+ '(- *)'{-h,--help}'[Print help]' \
+ '(- *)'{-V,--version}'[Print version of the program]' \
+ '--timeout=[Maximum number of seconds to wait for the devices being created.]:SEC' \
'--initialized=[Wait for devices being initialized by systemd-udevd.]:boolean:(yes no)' \
'--removed[Wait for devices being removed.]' \
'--settle[Also wait for udev queue being empty.]' \
- '--help[Print help text.]' \
'*::devpath:_files -P /dev/ -W /dev'
}
(( $+functions[_udevadm_lock] )) ||
_udevadm_lock(){
_arguments \
- '--timeout=[Maximum number of seconds to wait for the devices being locked.]' \
- '--device=[Block device to lock.]' \
- '--backing=[File whose backing block device to lock.]' \
- '--print[Only show which block device the lock would be taken on.]' \
- '--help[Print help text.]'
+ '(- *)'{-h,--help}'[Print help]' \
+ '(- *)'{-V,--version}'[Print version of the program]' \
+ '(-t --timeout)'{-t,--timeout=}'[Maximum number of seconds to wait for the devices being locked.]:SECS' \
+ '(-d --device)'{-d,--device=}'[Block device to lock.]:DEVICE' \
+ '(-b --backing)'{-b,--backing=}'[File whose backing block device to lock.]:FILE' \
+ '(-p --print)'{-p,--print}'[Only show which block device the lock would be taken on.]'
}
(( $+functions[_udevadm_mounts] )) ||
_arguments \
'--debug[Print debug messages to stderr]' \
- '--version[Print version number]' \
- '--help[Print help text]' \
+ '(- *)--version[Print version number]' \
+ '(- *)--help[Print help text]' \
'*::udevadm commands:_udevadm_commands'
strempty(host->virtualization));
svg("<g transform=\"translate(%.3f,100)\">\n", 20.0 + (SCALE_X * boot->firmware_time));
- svg_graph_box(m, -(double) boot->firmware_time, boot->finish_time);
+ if (timestamp_is_set(boot->softreboot_start_time))
+ svg_graph_box(m, 0, boot->finish_time);
+ else
+ svg_graph_box(m, -(double) boot->firmware_time, boot->finish_time);
if (timestamp_is_set(boot->firmware_time)) {
svg_bar("firmware", -(double) boot->firmware_time, -(double) boot->loader_time, y);
svg_text(true, boot->initrd_time, y, "initrd");
y++;
}
+ if (timestamp_is_set(boot->softreboot_start_time)) {
+ svg_bar("soft-reboot", 0, boot->userspace_time, y);
+ svg_text(true, 0, y, "soft-reboot");
+ y++;
+ }
for (u = times; u->has_data; u++) {
if (u->activating >= boot->userspace_time)
{ "FinishTimestampMonotonic", "t", NULL, offsetof(BootTimes, finish_time) },
{ "SecurityStartTimestampMonotonic", "t", NULL, offsetof(BootTimes, security_start_time) },
{ "SecurityFinishTimestampMonotonic", "t", NULL, offsetof(BootTimes, security_finish_time) },
+ { "SoftRebootStartTimestampMonotonic", "t", NULL, offsetof(BootTimes, softreboot_start_time) },
{ "GeneratorsStartTimestampMonotonic", "t", NULL, offsetof(BootTimes, generators_start_time) },
{ "GeneratorsFinishTimestampMonotonic", "t", NULL, offsetof(BootTimes, generators_finish_time) },
{ "UnitsLoadStartTimestampMonotonic", "t", NULL, offsetof(BootTimes, unitsload_start_time) },
{ "InitRDGeneratorsFinishTimestampMonotonic", "t", NULL, offsetof(BootTimes, initrd_generators_finish_time) },
{ "InitRDUnitsLoadStartTimestampMonotonic", "t", NULL, offsetof(BootTimes, initrd_unitsload_start_time) },
{ "InitRDUnitsLoadFinishTimestampMonotonic", "t", NULL, offsetof(BootTimes, initrd_unitsload_finish_time) },
+ { "SoftRebootsCount", "t", NULL, offsetof(BootTimes, soft_reboots_count) },
{},
};
_cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
if (require_finished && times.finish_time <= 0)
return log_not_finished(times.finish_time);
- if (arg_runtime_scope == RUNTIME_SCOPE_SYSTEM && times.security_start_time > 0) {
+ if (arg_runtime_scope == RUNTIME_SCOPE_SYSTEM && timestamp_is_set(times.softreboot_start_time)) {
+ /* On soft-reboot ignore kernel/firmware/initrd times as they are from the previous boot */
+ times.firmware_time = times.loader_time = times.kernel_time = times.initrd_time =
+ times.initrd_security_start_time = times.initrd_security_finish_time =
+ times.initrd_generators_start_time = times.initrd_generators_finish_time =
+ times.initrd_unitsload_start_time = times.initrd_unitsload_finish_time = 0;
+ times.reverse_offset = times.softreboot_start_time;
+
+ /* Clamp all timestamps to avoid showing huge graphs */
+ if (timestamp_is_set(times.finish_time))
+ subtract_timestamp(×.finish_time, times.reverse_offset);
+ subtract_timestamp(×.userspace_time, times.reverse_offset);
+
+ subtract_timestamp(×.generators_start_time, times.reverse_offset);
+ subtract_timestamp(×.generators_finish_time, times.reverse_offset);
+
+ subtract_timestamp(×.unitsload_start_time, times.reverse_offset);
+ subtract_timestamp(×.unitsload_finish_time, times.reverse_offset);
+ } else if (arg_runtime_scope == RUNTIME_SCOPE_SYSTEM && timestamp_is_set(times.security_start_time)) {
/* security_start_time is set when systemd is not running under container environment. */
if (times.initrd_time > 0)
times.kernel_done_time = times.initrd_time;
return log_oom();
if (timestamp_is_set(t->initrd_time) && !strextend(&text, FORMAT_TIMESPAN(t->userspace_time - t->initrd_time, USEC_PER_MSEC), " (initrd) + "))
return log_oom();
+ if (timestamp_is_set(t->softreboot_start_time) && strextendf(&text, "%s (soft reboot #%" PRIu64 ") + ", FORMAT_TIMESPAN(t->userspace_time, USEC_PER_MSEC), t->soft_reboots_count) < 0)
+ return log_oom();
if (!strextend(&text, FORMAT_TIMESPAN(t->finish_time - t->userspace_time, USEC_PER_MSEC), " (userspace) "))
return log_oom();
return log_oom();
if (unit_id && timestamp_is_set(activated_time)) {
- usec_t base = timestamp_is_set(t->userspace_time) ? t->userspace_time : t->reverse_offset;
+ usec_t base;
+
+ /* On soft-reboot times are clamped to avoid showing huge graphs */
+ if (timestamp_is_set(t->softreboot_start_time) && timestamp_is_set(t->userspace_time))
+ base = t->userspace_time + t->reverse_offset;
+ else
+ base = timestamp_is_set(t->userspace_time) ? t->userspace_time : t->reverse_offset;
if (!strextend(&text, "\n", unit_id, " reached after ", FORMAT_TIMESPAN(activated_time - base, USEC_PER_MSEC), " in userspace."))
return log_oom();
return log_error_errno(r, "Failed to get timestamp properties of unit %s: %s",
u.id, bus_error_message(&error, r));
+ /* Activated in the previous soft-reboot iteration? Ignore it, we want new activations */
+ if ((t->activated > 0 && t->activated < boot_times->softreboot_start_time) ||
+ (t->activating > 0 && t->activating < boot_times->softreboot_start_time))
+ continue;
+
subtract_timestamp(&t->activating, boot_times->reverse_offset);
subtract_timestamp(&t->activated, boot_times->reverse_offset);
- subtract_timestamp(&t->deactivating, boot_times->reverse_offset);
- subtract_timestamp(&t->deactivated, boot_times->reverse_offset);
+
+ /* If the last deactivation was in the previous soft-reboot, ignore it */
+ if (timestamp_is_set(boot_times->softreboot_start_time)) {
+ if (t->deactivating < boot_times->reverse_offset)
+ t->deactivating = 0;
+ else
+ subtract_timestamp(&t->deactivating, boot_times->reverse_offset);
+ if (t->deactivated < boot_times->reverse_offset)
+ t->deactivated = 0;
+ else
+ subtract_timestamp(&t->deactivated, boot_times->reverse_offset);
+ } else {
+ subtract_timestamp(&t->deactivating, boot_times->reverse_offset);
+ subtract_timestamp(&t->deactivated, boot_times->reverse_offset);
+ }
if (t->activated >= t->activating)
t->time = t->activated - t->activating;
usec_t initrd_time;
usec_t userspace_time;
usec_t finish_time;
+ usec_t softreboot_start_time;
usec_t security_start_time;
usec_t security_finish_time;
usec_t generators_start_time;
usec_t initrd_generators_finish_time;
usec_t initrd_unitsload_start_time;
usec_t initrd_unitsload_finish_time;
+ /* Not strictly a timestamp, but we are going to show it next to the other timestamps */
+ uint64_t soft_reboots_count;
/*
* If we're analyzing the user instance, all timestamps will be offset by its own start-up timestamp,
if (r <= 0)
return r;
- if (arg_timeout > 0)
- timeout = usec_add(now(CLOCK_MONOTONIC), arg_timeout);
- else
- timeout = 0;
+ timeout = arg_timeout > 0 ? usec_add(now(CLOCK_MONOTONIC), arg_timeout) : 0;
AskPasswordRequest req = {
.message = arg_message,
if (r < 0)
return r;
+ r = sd_device_enumerator_allow_uninitialized(e);
+ if (r < 0)
+ return r;
+
r = sd_device_enumerator_add_match_subsystem(e, "pci", /* match = */ true);
if (r < 0)
return r;
#include "log.h"
#include "login-util.h"
#include "macro.h"
+#include "missing_fs.h"
#include "missing_magic.h"
#include "missing_threads.h"
#include "mkdir.h"
#include "user-util.h"
#include "xattr-util.h"
+int cg_path_open(const char *controller, const char *path) {
+ _cleanup_free_ char *fs = NULL;
+ int r;
+
+ r = cg_get_path(controller, path, /* item=*/ NULL, &fs);
+ if (r < 0)
+ return r;
+
+ return RET_NERRNO(open(fs, O_DIRECTORY|O_CLOEXEC));
+}
+
+int cg_cgroupid_open(int cgroupfs_fd, uint64_t id) {
+ _cleanup_close_ int fsfd = -EBADF;
+
+ if (cgroupfs_fd < 0) {
+ fsfd = open("/sys/fs/cgroup", O_CLOEXEC|O_DIRECTORY);
+ if (fsfd < 0)
+ return -errno;
+
+ cgroupfs_fd = fsfd;
+ }
+
+ cg_file_handle fh = CG_FILE_HANDLE_INIT;
+ CG_FILE_HANDLE_CGROUPID(fh) = id;
+
+ int fd = open_by_handle_at(cgroupfs_fd, &fh.file_handle, O_DIRECTORY|O_CLOEXEC);
+ if (fd < 0)
+ return -errno;
+
+ return fd;
+}
+
static int cg_enumerate_items(const char *controller, const char *path, FILE **ret, const char *item) {
_cleanup_free_ char *fs = NULL;
FILE *f;
int cg_path_get_cgroupid(const char *path, uint64_t *ret) {
cg_file_handle fh = CG_FILE_HANDLE_INIT;
- int mnt_id = -1;
+ int mnt_id;
assert(path);
assert(ret);
return 0;
}
+int cg_fd_get_cgroupid(int fd, uint64_t *ret) {
+ cg_file_handle fh = CG_FILE_HANDLE_INIT;
+ int mnt_id = -1;
+
+ assert(fd >= 0);
+ assert(ret);
+
+ if (name_to_handle_at(fd, "", &fh.file_handle, &mnt_id, AT_EMPTY_PATH) < 0)
+ return -errno;
+
+ *ret = CG_FILE_HANDLE_CGROUPID(fh);
+ return 0;
+}
+
int cg_path_get_session(const char *path, char **ret_session) {
_cleanup_free_ char *unit = NULL;
char *start, *end;
* generate paths with multiple adjacent / removed.
*/
+int cg_path_open(const char *controller, const char *path);
+int cg_cgroupid_open(int fsfd, uint64_t id);
+
int cg_enumerate_processes(const char *controller, const char *path, FILE **ret);
int cg_read_pid(FILE *f, pid_t *ret);
int cg_read_pidref(FILE *f, PidRef *ret);
int cg_get_root_path(char **path);
int cg_path_get_cgroupid(const char *path, uint64_t *ret);
+int cg_fd_get_cgroupid(int fd, uint64_t *ret);
int cg_path_get_session(const char *path, char **ret_session);
int cg_path_get_owner_uid(const char *path, uid_t *ret_uid);
int cg_path_get_unit(const char *path, char **ret_unit);
uint8_t space[offsetof(struct file_handle, f_handle) + sizeof(uint64_t)];
} cg_file_handle;
-#define CG_FILE_HANDLE_INIT { .file_handle.handle_bytes = sizeof(uint64_t) }
+#define CG_FILE_HANDLE_INIT \
+ (cg_file_handle) { \
+ .file_handle.handle_bytes = sizeof(uint64_t), \
+ .file_handle.handle_type = FILEID_KERNFS, \
+ }
+
#define CG_FILE_HANDLE_CGROUPID(fh) (*(uint64_t*) (fh).file_handle.f_handle)
return log_debug_errno(SYNTHETIC_ERRNO(EOPNOTSUPP),
"%s is not installed: %s", filename, dlerror());
+ log_debug("Loaded '%s' via dlopen()", filename);
+
va_list ap;
va_start(ap, log_level);
r = dlsym_many_or_warnv(dl, log_level, ap);
}
int efi_set_variable(const char *variable, const void *value, size_t size) {
+ static const uint32_t attr = EFI_VARIABLE_NON_VOLATILE|EFI_VARIABLE_BOOTSERVICE_ACCESS|EFI_VARIABLE_RUNTIME_ACCESS;
+
struct var {
uint32_t attr;
char buf[];
} _packed_ * _cleanup_free_ buf = NULL;
_cleanup_close_ int fd = -EBADF;
- uint32_t attr = EFI_VARIABLE_NON_VOLATILE|EFI_VARIABLE_BOOTSERVICE_ACCESS|EFI_VARIABLE_RUNTIME_ACCESS;
bool saved_flags_valid = false;
unsigned saved_flags;
int r;
assert(variable);
assert(value || size == 0);
- const char *p = strjoina("/sys/firmware/efi/efivars/", variable);
-
/* size 0 means removal, empty variable would not be enough for that */
if (size > 0 && efi_verify_variable(variable, attr, value, size) > 0) {
log_debug("Variable '%s' is already in wanted state, skipping write.", variable);
return 0;
}
+ const char *p = strjoina("/sys/firmware/efi/efivars/", variable);
+
/* Newer efivarfs protects variables that are not in an allow list with FS_IMMUTABLE_FL by default,
* to protect them for accidental removal and modification. We are not changing these variables
* accidentally however, hence let's unset the bit first. */
/* For some reason efivarfs doesn't update mtime automatically. Let's do it manually then. This is
* useful for processes that cache EFI variables to detect when changes occurred. */
- if (futimens(fd, (struct timespec[2]) {
+ if (futimens(fd, (const struct timespec[2]) {
{ .tv_nsec = UTIME_NOW },
{ .tv_nsec = UTIME_NOW }
- }) < 0)
+ }) < 0)
log_debug_errno(errno, "Failed to update mtime/atime on %s, ignoring: %m", p);
r = 0;
#include "gcrypt-util.h"
#include "hexdecoct.h"
-void initialize_libgcrypt(bool secmem) {
- if (gcry_control(GCRYCTL_INITIALIZATION_FINISHED_P))
- return;
+static void *gcrypt_dl = NULL;
- gcry_control(GCRYCTL_SET_PREFERRED_RNG_TYPE, GCRY_RNG_TYPE_SYSTEM);
- assert_se(gcry_check_version("1.4.5"));
+static DLSYM_FUNCTION(gcry_control);
+static DLSYM_FUNCTION(gcry_check_version);
+DLSYM_FUNCTION(gcry_md_close);
+DLSYM_FUNCTION(gcry_md_copy);
+DLSYM_FUNCTION(gcry_md_ctl);
+DLSYM_FUNCTION(gcry_md_get_algo_dlen);
+DLSYM_FUNCTION(gcry_md_open);
+DLSYM_FUNCTION(gcry_md_read);
+DLSYM_FUNCTION(gcry_md_reset);
+DLSYM_FUNCTION(gcry_md_setkey);
+DLSYM_FUNCTION(gcry_md_write);
+DLSYM_FUNCTION(gcry_mpi_add);
+DLSYM_FUNCTION(gcry_mpi_add_ui);
+DLSYM_FUNCTION(gcry_mpi_cmp);
+DLSYM_FUNCTION(gcry_mpi_cmp_ui);
+DLSYM_FUNCTION(gcry_mpi_get_nbits);
+DLSYM_FUNCTION(gcry_mpi_invm);
+DLSYM_FUNCTION(gcry_mpi_mod);
+DLSYM_FUNCTION(gcry_mpi_mul);
+DLSYM_FUNCTION(gcry_mpi_mulm);
+DLSYM_FUNCTION(gcry_mpi_new);
+DLSYM_FUNCTION(gcry_mpi_powm);
+DLSYM_FUNCTION(gcry_mpi_print);
+DLSYM_FUNCTION(gcry_mpi_release);
+DLSYM_FUNCTION(gcry_mpi_scan);
+DLSYM_FUNCTION(gcry_mpi_set_ui);
+DLSYM_FUNCTION(gcry_mpi_sub);
+DLSYM_FUNCTION(gcry_mpi_subm);
+DLSYM_FUNCTION(gcry_mpi_sub_ui);
+DLSYM_FUNCTION(gcry_prime_check);
+DLSYM_FUNCTION(gcry_randomize);
+DLSYM_FUNCTION(gcry_strerror);
+
+static int dlopen_gcrypt(void) {
+ return dlopen_many_sym_or_warn(
+ &gcrypt_dl,
+ "libgcrypt.so.20", LOG_DEBUG,
+ DLSYM_ARG(gcry_control),
+ DLSYM_ARG(gcry_check_version),
+ DLSYM_ARG(gcry_md_close),
+ DLSYM_ARG(gcry_md_copy),
+ DLSYM_ARG(gcry_md_ctl),
+ DLSYM_ARG(gcry_md_get_algo_dlen),
+ DLSYM_ARG(gcry_md_open),
+ DLSYM_ARG(gcry_md_read),
+ DLSYM_ARG(gcry_md_reset),
+ DLSYM_ARG(gcry_md_setkey),
+ DLSYM_ARG(gcry_md_write),
+ DLSYM_ARG(gcry_mpi_add),
+ DLSYM_ARG(gcry_mpi_add_ui),
+ DLSYM_ARG(gcry_mpi_cmp),
+ DLSYM_ARG(gcry_mpi_cmp_ui),
+ DLSYM_ARG(gcry_mpi_get_nbits),
+ DLSYM_ARG(gcry_mpi_invm),
+ DLSYM_ARG(gcry_mpi_mod),
+ DLSYM_ARG(gcry_mpi_mul),
+ DLSYM_ARG(gcry_mpi_mulm),
+ DLSYM_ARG(gcry_mpi_new),
+ DLSYM_ARG(gcry_mpi_powm),
+ DLSYM_ARG(gcry_mpi_print),
+ DLSYM_ARG(gcry_mpi_release),
+ DLSYM_ARG(gcry_mpi_scan),
+ DLSYM_ARG(gcry_mpi_set_ui),
+ DLSYM_ARG(gcry_mpi_sub),
+ DLSYM_ARG(gcry_mpi_subm),
+ DLSYM_ARG(gcry_mpi_sub_ui),
+ DLSYM_ARG(gcry_prime_check),
+ DLSYM_ARG(gcry_randomize),
+ DLSYM_ARG(gcry_strerror));
+}
+
+int initialize_libgcrypt(bool secmem) {
+ int r;
+
+ r = dlopen_gcrypt();
+ if (r < 0)
+ return r;
+
+ if (sym_gcry_control(GCRYCTL_INITIALIZATION_FINISHED_P))
+ return 0;
+
+ sym_gcry_control(GCRYCTL_SET_PREFERRED_RNG_TYPE, GCRY_RNG_TYPE_SYSTEM);
+ assert_se(sym_gcry_check_version("1.4.5"));
/* Turn off "secmem". Clients which wish to make use of this
* feature should initialize the library manually */
if (!secmem)
- gcry_control(GCRYCTL_DISABLE_SECMEM);
+ sym_gcry_control(GCRYCTL_DISABLE_SECMEM);
- gcry_control(GCRYCTL_INITIALIZATION_FINISHED, 0);
+ sym_gcry_control(GCRYCTL_INITIALIZATION_FINISHED, 0);
+
+ return 0;
}
# if !PREFER_OPENSSL
int string_hashsum(const char *s, size_t len, int md_algorithm, char **out) {
- _cleanup_(gcry_md_closep) gcry_md_hd_t md = NULL;
+ _cleanup_(sym_gcry_md_closep) gcry_md_hd_t md = NULL;
gcry_error_t err;
size_t hash_size;
void *hash;
char *enc;
+ int r;
- initialize_libgcrypt(false);
+ r = initialize_libgcrypt(false);
+ if (r < 0)
+ return r;
- hash_size = gcry_md_get_algo_dlen(md_algorithm);
+ hash_size = sym_gcry_md_get_algo_dlen(md_algorithm);
assert(hash_size > 0);
- err = gcry_md_open(&md, md_algorithm, 0);
+ err = sym_gcry_md_open(&md, md_algorithm, 0);
if (gcry_err_code(err) != GPG_ERR_NO_ERROR || !md)
return -EIO;
- gcry_md_write(md, s, len);
+ sym_gcry_md_write(md, s, len);
- hash = gcry_md_read(md, 0);
+ hash = sym_gcry_md_read(md, 0);
if (!hash)
return -EIO;
#if HAVE_GCRYPT
#include <gcrypt.h>
+#include "dlfcn-util.h"
#include "macro.h"
-void initialize_libgcrypt(bool secmem);
+DLSYM_PROTOTYPE(gcry_md_close);
+DLSYM_PROTOTYPE(gcry_md_copy);
+DLSYM_PROTOTYPE(gcry_md_ctl);
+DLSYM_PROTOTYPE(gcry_md_get_algo_dlen);
+DLSYM_PROTOTYPE(gcry_md_open);
+DLSYM_PROTOTYPE(gcry_md_read);
+DLSYM_PROTOTYPE(gcry_md_reset);
+DLSYM_PROTOTYPE(gcry_md_setkey);
+DLSYM_PROTOTYPE(gcry_md_write);
+DLSYM_PROTOTYPE(gcry_mpi_add);
+DLSYM_PROTOTYPE(gcry_mpi_add_ui);
+DLSYM_PROTOTYPE(gcry_mpi_cmp);
+DLSYM_PROTOTYPE(gcry_mpi_cmp_ui);
+DLSYM_PROTOTYPE(gcry_mpi_get_nbits);
+DLSYM_PROTOTYPE(gcry_mpi_invm);
+DLSYM_PROTOTYPE(gcry_mpi_mod);
+DLSYM_PROTOTYPE(gcry_mpi_mul);
+DLSYM_PROTOTYPE(gcry_mpi_mulm);
+DLSYM_PROTOTYPE(gcry_mpi_new);
+DLSYM_PROTOTYPE(gcry_mpi_powm);
+DLSYM_PROTOTYPE(gcry_mpi_print);
+DLSYM_PROTOTYPE(gcry_mpi_release);
+DLSYM_PROTOTYPE(gcry_mpi_scan);
+DLSYM_PROTOTYPE(gcry_mpi_set_ui);
+DLSYM_PROTOTYPE(gcry_mpi_sub);
+DLSYM_PROTOTYPE(gcry_mpi_subm);
+DLSYM_PROTOTYPE(gcry_mpi_sub_ui);
+DLSYM_PROTOTYPE(gcry_prime_check);
+DLSYM_PROTOTYPE(gcry_randomize);
+DLSYM_PROTOTYPE(gcry_strerror);
+int initialize_libgcrypt(bool secmem);
+
+static inline gcry_md_hd_t* sym_gcry_md_closep(gcry_md_hd_t *md) {
+ if (!md || !*md)
+ return NULL;
+ sym_gcry_md_close(*md);
+
+ return NULL;
+}
DEFINE_TRIVIAL_CLEANUP_FUNC_FULL(gcry_md_hd_t, gcry_md_close, NULL);
+
+/* Copied from gcry_md_putc from gcrypt.h due to the need to call the sym_ variant */
+#define sym_gcry_md_putc(h,c) \
+ do { \
+ gcry_md_hd_t h__ = (h); \
+ if ((h__)->bufpos == (h__)->bufsize) \
+ sym_gcry_md_write((h__), NULL, 0); \
+ (h__)->buf[(h__)->bufpos++] = (c) & 0xff; \
+ } while(false)
#endif
#if !PREFER_OPENSSL
* struct btrfs_qgroup_inherit.flags
*/
#define BTRFS_QGROUP_INHERIT_SET_LIMITS (1ULL << 0)
+#define BTRFS_QGROUP_INHERIT_FLAGS_SUPP (BTRFS_QGROUP_INHERIT_SET_LIMITS)
struct btrfs_qgroup_inherit {
__u64 flags;
IFLA_BOND_AD_LACP_ACTIVE,
IFLA_BOND_MISSED_MAX,
IFLA_BOND_NS_IP6_TARGET,
+ IFLA_BOND_COUPLED_CONTROL,
__IFLA_BOND_MAX,
};
#define IPV6_TLV_PADN 1
#define IPV6_TLV_ROUTERALERT 5
#define IPV6_TLV_CALIPSO 7 /* RFC 5570 */
-#define IPV6_TLV_IOAM 49 /* TEMPORARY IANA allocation for IOAM */
+#define IPV6_TLV_IOAM 49 /* RFC 9486 */
#define IPV6_TLV_JUMBO 194
#define IPV6_TLV_HAO 201 /* home address option */
--- /dev/null
+/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */
+#ifndef __LINUX_MAGIC_H__
+#define __LINUX_MAGIC_H__
+
+#define ADFS_SUPER_MAGIC 0xadf5
+#define AFFS_SUPER_MAGIC 0xadff
+#define AFS_SUPER_MAGIC 0x5346414F
+#define AUTOFS_SUPER_MAGIC 0x0187
+#define CEPH_SUPER_MAGIC 0x00c36400
+#define CODA_SUPER_MAGIC 0x73757245
+#define CRAMFS_MAGIC 0x28cd3d45 /* some random number */
+#define CRAMFS_MAGIC_WEND 0x453dcd28 /* magic number with the wrong endianess */
+#define DEBUGFS_MAGIC 0x64626720
+#define SECURITYFS_MAGIC 0x73636673
+#define SELINUX_MAGIC 0xf97cff8c
+#define SMACK_MAGIC 0x43415d53 /* "SMAC" */
+#define RAMFS_MAGIC 0x858458f6 /* some random number */
+#define TMPFS_MAGIC 0x01021994
+#define HUGETLBFS_MAGIC 0x958458f6 /* some random number */
+#define SQUASHFS_MAGIC 0x73717368
+#define ECRYPTFS_SUPER_MAGIC 0xf15f
+#define EFS_SUPER_MAGIC 0x414A53
+#define EROFS_SUPER_MAGIC_V1 0xE0F5E1E2
+#define EXT2_SUPER_MAGIC 0xEF53
+#define EXT3_SUPER_MAGIC 0xEF53
+#define XENFS_SUPER_MAGIC 0xabba1974
+#define EXT4_SUPER_MAGIC 0xEF53
+#define BTRFS_SUPER_MAGIC 0x9123683E
+#define NILFS_SUPER_MAGIC 0x3434
+#define F2FS_SUPER_MAGIC 0xF2F52010
+#define HPFS_SUPER_MAGIC 0xf995e849
+#define ISOFS_SUPER_MAGIC 0x9660
+#define JFFS2_SUPER_MAGIC 0x72b6
+#define XFS_SUPER_MAGIC 0x58465342 /* "XFSB" */
+#define PSTOREFS_MAGIC 0x6165676C
+#define EFIVARFS_MAGIC 0xde5e81e4
+#define HOSTFS_SUPER_MAGIC 0x00c0ffee
+#define OVERLAYFS_SUPER_MAGIC 0x794c7630
+#define FUSE_SUPER_MAGIC 0x65735546
+
+#define MINIX_SUPER_MAGIC 0x137F /* minix v1 fs, 14 char names */
+#define MINIX_SUPER_MAGIC2 0x138F /* minix v1 fs, 30 char names */
+#define MINIX2_SUPER_MAGIC 0x2468 /* minix v2 fs, 14 char names */
+#define MINIX2_SUPER_MAGIC2 0x2478 /* minix v2 fs, 30 char names */
+#define MINIX3_SUPER_MAGIC 0x4d5a /* minix v3 fs, 60 char names */
+
+#define MSDOS_SUPER_MAGIC 0x4d44 /* MD */
+#define EXFAT_SUPER_MAGIC 0x2011BAB0
+#define NCP_SUPER_MAGIC 0x564c /* Guess, what 0x564c is :-) */
+#define NFS_SUPER_MAGIC 0x6969
+#define OCFS2_SUPER_MAGIC 0x7461636f
+#define OPENPROM_SUPER_MAGIC 0x9fa1
+#define QNX4_SUPER_MAGIC 0x002f /* qnx4 fs detection */
+#define QNX6_SUPER_MAGIC 0x68191122 /* qnx6 fs detection */
+#define AFS_FS_MAGIC 0x6B414653
+
+
+#define REISERFS_SUPER_MAGIC 0x52654973 /* used by gcc */
+ /* used by file system utilities that
+ look at the superblock, etc. */
+#define REISERFS_SUPER_MAGIC_STRING "ReIsErFs"
+#define REISER2FS_SUPER_MAGIC_STRING "ReIsEr2Fs"
+#define REISER2FS_JR_SUPER_MAGIC_STRING "ReIsEr3Fs"
+
+#define SMB_SUPER_MAGIC 0x517B
+#define CIFS_SUPER_MAGIC 0xFF534D42 /* the first four bytes of SMB PDUs */
+#define SMB2_SUPER_MAGIC 0xFE534D42
+
+#define CGROUP_SUPER_MAGIC 0x27e0eb
+#define CGROUP2_SUPER_MAGIC 0x63677270
+
+#define RDTGROUP_SUPER_MAGIC 0x7655821
+
+#define STACK_END_MAGIC 0x57AC6E9D
+
+#define TRACEFS_MAGIC 0x74726163
+
+#define V9FS_MAGIC 0x01021997
+
+#define BDEVFS_MAGIC 0x62646576
+#define DAXFS_MAGIC 0x64646178
+#define BINFMTFS_MAGIC 0x42494e4d
+#define DEVPTS_SUPER_MAGIC 0x1cd1
+#define BINDERFS_SUPER_MAGIC 0x6c6f6f70
+#define FUTEXFS_SUPER_MAGIC 0xBAD1DEA
+#define PIPEFS_MAGIC 0x50495045
+#define PROC_SUPER_MAGIC 0x9fa0
+#define SOCKFS_MAGIC 0x534F434B
+#define SYSFS_MAGIC 0x62656572
+#define USBDEVICE_SUPER_MAGIC 0x9fa2
+#define MTD_INODE_FS_MAGIC 0x11307854
+#define ANON_INODE_FS_MAGIC 0x09041934
+#define BTRFS_TEST_MAGIC 0x73727279
+#define NSFS_MAGIC 0x6e736673
+#define BPF_FS_MAGIC 0xcafe4a11
+#define AAFS_MAGIC 0x5a3c69f0
+#define ZONEFS_MAGIC 0x5a4f4653
+
+/* Since UDF 2.01 is ISO 13346 based... */
+#define UDF_SUPER_MAGIC 0x15013346
+#define DMA_BUF_MAGIC 0x444d4142 /* "DMAB" */
+#define DEVMEM_MAGIC 0x454d444d /* "DMEM" */
+#define SECRETMEM_MAGIC 0x5345434d /* "SECM" */
+#define PID_FS_MAGIC 0x50494446 /* "PIDF" */
+
+#endif /* __LINUX_MAGIC_H__ */
* enum nft_table_flags - nf_tables table flags
*
* @NFT_TABLE_F_DORMANT: this table is not active
+ * @NFT_TABLE_F_OWNER: this table is owned by a process
+ * @NFT_TABLE_F_PERSIST: this table shall outlive its owner
*/
enum nft_table_flags {
NFT_TABLE_F_DORMANT = 0x1,
NFT_TABLE_F_OWNER = 0x2,
+ NFT_TABLE_F_PERSIST = 0x4,
};
#define NFT_TABLE_F_MASK (NFT_TABLE_F_DORMANT | \
- NFT_TABLE_F_OWNER)
+ NFT_TABLE_F_OWNER | \
+ NFT_TABLE_F_PERSIST)
/**
* enum nft_table_attributes - nf_tables table netlink attributes
#define NEXTHOP_GRP_TYPE_MAX (__NEXTHOP_GRP_TYPE_MAX - 1)
+#define NHA_OP_FLAG_DUMP_STATS BIT(0)
+#define NHA_OP_FLAG_DUMP_HW_STATS BIT(1)
+
enum {
NHA_UNSPEC,
NHA_ID, /* u32; id for nexthop. id == 0 means auto-assign */
/* nested; nexthop bucket attributes */
NHA_RES_BUCKET,
+ /* u32; operation-specific flags */
+ NHA_OP_FLAGS,
+
+ /* nested; nexthop group stats */
+ NHA_GROUP_STATS,
+
+ /* u32; nexthop hardware stats enable */
+ NHA_HW_STATS_ENABLE,
+
+ /* u32; read-only; whether any driver collects HW stats */
+ NHA_HW_STATS_USED,
+
__NHA_MAX,
};
#define NHA_RES_BUCKET_MAX (__NHA_RES_BUCKET_MAX - 1)
+enum {
+ NHA_GROUP_STATS_UNSPEC,
+
+ /* nested; nexthop group entry stats */
+ NHA_GROUP_STATS_ENTRY,
+
+ __NHA_GROUP_STATS_MAX,
+};
+
+#define NHA_GROUP_STATS_MAX (__NHA_GROUP_STATS_MAX - 1)
+
+enum {
+ NHA_GROUP_STATS_ENTRY_UNSPEC,
+
+ /* u32; nexthop id of the nexthop group entry */
+ NHA_GROUP_STATS_ENTRY_ID,
+
+ /* uint; number of packets forwarded via the nexthop group entry */
+ NHA_GROUP_STATS_ENTRY_PACKETS,
+
+ /* uint; number of packets forwarded via the nexthop group entry in
+ * hardware
+ */
+ NHA_GROUP_STATS_ENTRY_PACKETS_HW,
+
+ __NHA_GROUP_STATS_ENTRY_MAX,
+};
+
+#define NHA_GROUP_STATS_ENTRY_MAX (__NHA_GROUP_STATS_ENTRY_MAX - 1)
+
#endif
* Copyright 2008 Jouni Malinen <jouni.malinen@atheros.com>
* Copyright 2008 Colin McCabe <colin@cozybit.com>
* Copyright 2015-2017 Intel Deutschland GmbH
- * Copyright (C) 2018-2023 Intel Corporation
+ * Copyright (C) 2018-2024 Intel Corporation
*
* Permission to use, copy, modify, and/or distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* %NL80211_ATTR_REASON_CODE can optionally be used to specify which type
* of disconnection indication should be sent to the station
* (Deauthentication or Disassociation frame and reason code for that
- * frame).
+ * frame). %NL80211_ATTR_MLO_LINK_ID can be used optionally to remove
+ * stations connected and using at least that link as one of its links.
*
* @NL80211_CMD_GET_MPATH: Get mesh path attributes for mesh path to
* destination %NL80211_ATTR_MAC on the interface identified by
* mapping is as defined in section 9.4.2.314 (TID-To-Link Mapping element)
* in Draft P802.11be_D4.0.
*
+ * @NL80211_ATTR_ASSOC_SPP_AMSDU: flag attribute used with
+ * %NL80211_CMD_ASSOCIATE indicating the SPP A-MSDUs
+ * are used on this connection
+ *
* @NUM_NL80211_ATTR: total number of nl80211_attrs available
* @NL80211_ATTR_MAX: highest attribute number currently defined
* @__NL80211_ATTR_AFTER_LAST: internal use
NL80211_ATTR_MLO_TTLM_DLINK,
NL80211_ATTR_MLO_TTLM_ULINK,
+ NL80211_ATTR_ASSOC_SPP_AMSDU,
+
/* add attributes here, update the policy in nl80211.c */
__NL80211_ATTR_AFTER_LAST,
* @NL80211_STA_FLAG_ASSOCIATED: station is associated; used with drivers
* that support %NL80211_FEATURE_FULL_AP_CLIENT_STATE to transition a
* previously added station into associated state
+ * @NL80211_STA_FLAG_SPP_AMSDU: station supports SPP A-MSDUs
* @NL80211_STA_FLAG_MAX: highest station flag number currently defined
* @__NL80211_STA_FLAG_AFTER_LAST: internal use
*/
NL80211_STA_FLAG_AUTHENTICATED,
NL80211_STA_FLAG_TDLS_PEER,
NL80211_STA_FLAG_ASSOCIATED,
+ NL80211_STA_FLAG_SPP_AMSDU,
/* keep last */
__NL80211_STA_FLAG_AFTER_LAST,
* allowed for peer-to-peer or adhoc communication under the control
* of a DFS master which operates on the same channel (FCC-594280 D01
* Section B.3). Should be used together with %NL80211_RRF_DFS only.
- * @NL80211_FREQUENCY_ATTR_NO_UHB_VLP_CLIENT: Client connection to VLP AP
+ * @NL80211_FREQUENCY_ATTR_NO_6GHZ_VLP_CLIENT: Client connection to VLP AP
* not allowed using this channel
- * @NL80211_FREQUENCY_ATTR_NO_UHB_AFC_CLIENT: Client connection to AFC AP
+ * @NL80211_FREQUENCY_ATTR_NO_6GHZ_AFC_CLIENT: Client connection to AFC AP
* not allowed using this channel
+ * @NL80211_FREQUENCY_ATTR_CAN_MONITOR: This channel can be used in monitor
+ * mode despite other (regulatory) restrictions, even if the channel is
+ * otherwise completely disabled.
* @NL80211_FREQUENCY_ATTR_MAX: highest frequency attribute number
* currently defined
* @__NL80211_FREQUENCY_ATTR_AFTER_LAST: internal use
NL80211_FREQUENCY_ATTR_NO_EHT,
NL80211_FREQUENCY_ATTR_PSD,
NL80211_FREQUENCY_ATTR_DFS_CONCURRENT,
- NL80211_FREQUENCY_ATTR_NO_UHB_VLP_CLIENT,
- NL80211_FREQUENCY_ATTR_NO_UHB_AFC_CLIENT,
+ NL80211_FREQUENCY_ATTR_NO_6GHZ_VLP_CLIENT,
+ NL80211_FREQUENCY_ATTR_NO_6GHZ_AFC_CLIENT,
+ NL80211_FREQUENCY_ATTR_CAN_MONITOR,
/* keep last */
__NL80211_FREQUENCY_ATTR_AFTER_LAST,
#define NL80211_FREQUENCY_ATTR_NO_IR NL80211_FREQUENCY_ATTR_NO_IR
#define NL80211_FREQUENCY_ATTR_GO_CONCURRENT \
NL80211_FREQUENCY_ATTR_IR_CONCURRENT
+#define NL80211_FREQUENCY_ATTR_NO_UHB_VLP_CLIENT \
+ NL80211_FREQUENCY_ATTR_NO_6GHZ_VLP_CLIENT
+#define NL80211_FREQUENCY_ATTR_NO_UHB_AFC_CLIENT \
+ NL80211_FREQUENCY_ATTR_NO_6GHZ_AFC_CLIENT
/**
* enum nl80211_bitrate_attr - bitrate attributes
* value as specified by &struct nl80211_bss_select_rssi_adjust.
* @NL80211_SCHED_SCAN_MATCH_ATTR_BSSID: BSSID to be used for matching
* (this cannot be used together with SSID).
- * @NL80211_SCHED_SCAN_MATCH_PER_BAND_RSSI: Nested attribute that carries the
- * band specific minimum rssi thresholds for the bands defined in
- * enum nl80211_band. The minimum rssi threshold value(s32) specific to a
- * band shall be encapsulated in attribute with type value equals to one
- * of the NL80211_BAND_* defined in enum nl80211_band. For example, the
- * minimum rssi threshold value for 2.4GHZ band shall be encapsulated
- * within an attribute of type NL80211_BAND_2GHZ. And one or more of such
- * attributes will be nested within this attribute.
+ * @NL80211_SCHED_SCAN_MATCH_PER_BAND_RSSI: Obsolete
* @NL80211_SCHED_SCAN_MATCH_ATTR_MAX: highest scheduled scan filter
* attribute number currently defined
* @__NL80211_SCHED_SCAN_MATCH_ATTR_AFTER_LAST: internal use
NL80211_SCHED_SCAN_MATCH_ATTR_RELATIVE_RSSI,
NL80211_SCHED_SCAN_MATCH_ATTR_RSSI_ADJUST,
NL80211_SCHED_SCAN_MATCH_ATTR_BSSID,
- NL80211_SCHED_SCAN_MATCH_PER_BAND_RSSI,
+ NL80211_SCHED_SCAN_MATCH_PER_BAND_RSSI, /* obsolete */
/* keep last */
__NL80211_SCHED_SCAN_MATCH_ATTR_AFTER_LAST,
peer-to-peer or adhoc communication under the control of a DFS master
which operates on the same channel (FCC-594280 D01 Section B.3).
Should be used together with %NL80211_RRF_DFS only.
- * @NL80211_RRF_NO_UHB_VLP_CLIENT: Client connection to VLP AP not allowed
- * @NL80211_RRF_NO_UHB_AFC_CLIENT: Client connection to AFC AP not allowed
+ * @NL80211_RRF_NO_6GHZ_VLP_CLIENT: Client connection to VLP AP not allowed
+ * @NL80211_RRF_NO_6GHZ_AFC_CLIENT: Client connection to AFC AP not allowed
*/
enum nl80211_reg_rule_flags {
NL80211_RRF_NO_OFDM = 1<<0,
NL80211_RRF_NO_EHT = 1<<19,
NL80211_RRF_PSD = 1<<20,
NL80211_RRF_DFS_CONCURRENT = 1<<21,
- NL80211_RRF_NO_UHB_VLP_CLIENT = 1<<22,
- NL80211_RRF_NO_UHB_AFC_CLIENT = 1<<23,
+ NL80211_RRF_NO_6GHZ_VLP_CLIENT = 1<<22,
+ NL80211_RRF_NO_6GHZ_AFC_CLIENT = 1<<23,
};
#define NL80211_RRF_PASSIVE_SCAN NL80211_RRF_NO_IR
#define NL80211_RRF_NO_HT40 (NL80211_RRF_NO_HT40MINUS |\
NL80211_RRF_NO_HT40PLUS)
#define NL80211_RRF_GO_CONCURRENT NL80211_RRF_IR_CONCURRENT
+#define NL80211_RRF_NO_UHB_VLP_CLIENT NL80211_RRF_NO_6GHZ_VLP_CLIENT
+#define NL80211_RRF_NO_UHB_AFC_CLIENT NL80211_RRF_NO_6GHZ_AFC_CLIENT
/* For backport compatibility with older userspace */
#define NL80211_RRF_NO_IR_ALL (NL80211_RRF_NO_IR | __NL80211_RRF_NO_IBSS)
* BSS isn't possible
* @NL80211_BSS_CANNOT_USE_NSTR_NONPRIMARY: NSTR nonprimary links aren't
* supported by the device, and this BSS entry represents one.
- * @NL80211_BSS_CANNOT_USE_UHB_PWR_MISMATCH: STA is not supporting
+ * @NL80211_BSS_CANNOT_USE_6GHZ_PWR_MISMATCH: STA is not supporting
* the AP power type (SP, VLP, AP) that the AP uses.
*/
enum nl80211_bss_cannot_use_reasons {
NL80211_BSS_CANNOT_USE_NSTR_NONPRIMARY = 1 << 0,
- NL80211_BSS_CANNOT_USE_UHB_PWR_MISMATCH = 1 << 1,
+ NL80211_BSS_CANNOT_USE_6GHZ_PWR_MISMATCH = 1 << 1,
};
+#define NL80211_BSS_CANNOT_USE_UHB_PWR_MISMATCH \
+ NL80211_BSS_CANNOT_USE_6GHZ_PWR_MISMATCH
+
/**
* enum nl80211_bss - netlink attributes for a BSS
*
* %NL80211_ATTR_SCAN_FREQUENCIES contains more than one
* frequency, it means that the match occurred in more than one
* channel.
+ * @NL80211_WOWLAN_TRIG_UNPROTECTED_DEAUTH_DISASSOC: For wakeup reporting only.
+ * Wake up happened due to unprotected deauth or disassoc frame in MFP.
* @NUM_NL80211_WOWLAN_TRIG: number of wake on wireless triggers
* @MAX_NL80211_WOWLAN_TRIG: highest wowlan trigger attribute number
*
NL80211_WOWLAN_TRIG_WAKEUP_TCP_NOMORETOKENS,
NL80211_WOWLAN_TRIG_NET_DETECT,
NL80211_WOWLAN_TRIG_NET_DETECT_RESULTS,
+ NL80211_WOWLAN_TRIG_UNPROTECTED_DEAUTH_DISASSOC,
/* keep last */
NUM_NL80211_WOWLAN_TRIG,
* @NL80211_EXT_FEATURE_AP_PMKSA_CACHING: Driver/device supports PMKSA caching
* (set/del PMKSA operations) in AP mode.
*
- * @NL80211_EXT_FEATURE_SCHED_SCAN_BAND_SPECIFIC_RSSI_THOLD: Driver supports
- * filtering of sched scan results using band specific RSSI thresholds.
+ * @NL80211_EXT_FEATURE_SCHED_SCAN_BAND_SPECIFIC_RSSI_THOLD: Obsolete
*
* @NL80211_EXT_FEATURE_STA_TX_PWR: This driver supports controlling tx power
* to a station.
* DFS master on the same channel as described in FCC-594280 D01
* (Section B.3). This, for example, allows P2P GO and P2P clients to
* operate on DFS channels as long as there's a concurrent BSS connection.
+ *
+ * @NL80211_EXT_FEATURE_SPP_AMSDU_SUPPORT: The driver has support for SPP
+ * (signaling and payload protected) A-MSDUs and this shall be advertised
+ * in the RSNXE.
+ *
* @NUM_NL80211_EXT_FEATURES: number of extended features.
* @MAX_NL80211_EXT_FEATURES: highest extended feature index.
*/
NL80211_EXT_FEATURE_ENABLE_FTM_RESPONDER,
NL80211_EXT_FEATURE_AIRTIME_FAIRNESS,
NL80211_EXT_FEATURE_AP_PMKSA_CACHING,
- NL80211_EXT_FEATURE_SCHED_SCAN_BAND_SPECIFIC_RSSI_THOLD,
+ NL80211_EXT_FEATURE_SCHED_SCAN_BAND_SPECIFIC_RSSI_THOLD, /* obsolete */
NL80211_EXT_FEATURE_EXT_KEY_ID,
NL80211_EXT_FEATURE_STA_TX_PWR,
NL80211_EXT_FEATURE_SAE_OFFLOAD,
NL80211_EXT_FEATURE_OWE_OFFLOAD,
NL80211_EXT_FEATURE_OWE_OFFLOAD_AP,
NL80211_EXT_FEATURE_DFS_CONCURRENT,
+ NL80211_EXT_FEATURE_SPP_AMSDU_SUPPORT,
/* add new features before the definition below */
NUM_NL80211_EXT_FEATURES,
int make_lock_file_for(const char *p, int operation, LockFile *ret);
void release_lock_file(LockFile *f);
-#define LOCK_FILE_INIT { .dir_fd = -EBADF, .fd = -EBADF }
+#define LOCK_FILE_INIT (LockFile) { .dir_fd = -EBADF, .fd = -EBADF }
/* POSIX locks with the same interface as flock(). */
int posix_lock(int fd, int operation);
} LogTarget;
/* This log level disables logging completely. It can only be passed to log_set_max_level() and cannot be
- * used a regular log level. */
+ * used as a regular log level. */
#define LOG_NULL (LOG_EMERG - 1)
/* Note to readers: << and >> have lower precedence (are evaluated earlier) than & and | */
'filesystems.c',
'format-util.c',
'fs-util.c',
+ 'gcrypt-util.c',
'glob-util.c',
'glyph-util.c',
'gunicode.c',
############################################################
-filesystem_includes = ['linux/magic.h',
- 'linux/gfs2_ondisk.h']
+filesystem_includes = files(
+ 'linux/magic.h',
+ 'missing_magic.h',
+)
check_filesystems = find_program('check-filesystems.sh')
r = run_command([check_filesystems, cpp, files('filesystems-gperf.gperf')] + filesystem_includes, check: false)
if r.returncode() != 0
- error('Unknown filesystems defined in kernel headers:\n\n' + r.stdout())
+ warning('Unknown filesystems defined in kernel headers:\n\n' + r.stdout())
endif
filesystems_gperf_h = custom_target(
include_directories : basic_includes,
dependencies : [libcap,
libdl,
+ libgcrypt_cflags,
liblz4_cflags,
libm,
librt,
userspace],
c_args : ['-fvisibility=default'],
build_by_default : false)
-
-############################################################
-
-basic_gcrypt_sources = files(
- 'gcrypt-util.c',
-)
-
-# A convenience library that is separate from libbasic to avoid
-# unnecessary linking to libgcrypt.
-libbasic_gcrypt = static_library(
- 'basic-gcrypt',
- basic_gcrypt_sources,
- include_directories : basic_includes,
- dependencies : [libgcrypt,
- userspace],
- c_args : ['-fvisibility=default'],
- build_by_default : false)
#else
assert_cc(FS_KEY_DESCRIPTOR_SIZE == 8);
#endif
+
+/* linux/exportfs.h */
+#ifndef FILEID_KERNFS
+#define FILEID_KERNFS 0xfe
+#endif
#include <linux/magic.h>
-/* 62aa81d7c4c24b90fdb61da70ac0dbbc414f9939 (4.13) */
-#ifndef OCFS2_SUPER_MAGIC
-# define OCFS2_SUPER_MAGIC 0x7461636f
-#else
-assert_cc(OCFS2_SUPER_MAGIC == 0x7461636f);
-#endif
-
-/* 67e9c74b8a873408c27ac9a8e4c1d1c8d72c93ff (4.5) */
-#ifndef CGROUP2_SUPER_MAGIC
-# define CGROUP2_SUPER_MAGIC 0x63677270
-#else
-assert_cc(CGROUP2_SUPER_MAGIC == 0x63677270);
-#endif
-
-/* 4282d60689d4f21b40692029080440cc58e8a17d (4.1) */
-#ifndef TRACEFS_MAGIC
-# define TRACEFS_MAGIC 0x74726163
-#else
-assert_cc(TRACEFS_MAGIC == 0x74726163);
-#endif
-
-/* e149ed2b805fefdccf7ccdfc19eca22fdd4514ac (3.19) */
-#ifndef NSFS_MAGIC
-# define NSFS_MAGIC 0x6e736673
-#else
-assert_cc(NSFS_MAGIC == 0x6e736673);
-#endif
-
-/* b2197755b2633e164a439682fb05a9b5ea48f706 (4.4) */
-#ifndef BPF_FS_MAGIC
-# define BPF_FS_MAGIC 0xcafe4a11
-#else
-assert_cc(BPF_FS_MAGIC == 0xcafe4a11);
-#endif
-
/* Not exposed yet (4.20). Defined at ipc/mqueue.c */
#ifndef MQUEUE_MAGIC
# define MQUEUE_MAGIC 0x19800202
assert_cc(MQUEUE_MAGIC == 0x19800202);
#endif
-/* Not exposed yet (as of Linux 5.4). Defined in fs/xfs/libxfs/xfs_format.h */
-#ifndef XFS_SB_MAGIC
-# define XFS_SB_MAGIC 0x58465342
-#else
-assert_cc(XFS_SB_MAGIC == 0x58465342);
-#endif
-
-/* dea2903719283c156b53741126228c4a1b40440f (5.17) */
-#ifndef CIFS_SUPER_MAGIC
-# define CIFS_SUPER_MAGIC 0xFF534D42
-#else
-assert_cc(CIFS_SUPER_MAGIC == 0xFF534D42);
-#endif
-
-/* dea2903719283c156b53741126228c4a1b40440f (5.17) */
-#ifndef SMB2_SUPER_MAGIC
-# define SMB2_SUPER_MAGIC 0xFE534D42
-#else
-assert_cc(SMB2_SUPER_MAGIC == 0xFE534D42);
-#endif
-
-/* 257f871993474e2bde6c497b54022c362cf398e1 (4.5) */
-#ifndef OVERLAYFS_SUPER_MAGIC
-# define OVERLAYFS_SUPER_MAGIC 0x794c7630
-#else
-assert_cc(OVERLAYFS_SUPER_MAGIC == 0x794c7630);
-#endif
-
-/* 2a28900be20640fcd1e548b1e3bad79e8221fcf9 (4.7) */
-#ifndef UDF_SUPER_MAGIC
-# define UDF_SUPER_MAGIC 0x15013346
-#else
-assert_cc(UDF_SUPER_MAGIC == 0x15013346);
-#endif
-
-/* b1123ea6d3b3da25af5c8a9d843bd07ab63213f4 (4.8) */
+/* b1123ea6d3b3da25af5c8a9d843bd07ab63213f4 (4.8), dropped by 68f2736a858324c3ec852f6c2cddd9d1c777357d (v6.0) */
#ifndef BALLOON_KVM_MAGIC
# define BALLOON_KVM_MAGIC 0x13661366
#else
assert_cc(BALLOON_KVM_MAGIC == 0x13661366);
#endif
-/* 48b4800a1c6af2cdda344ea4e2c843dcc1f6afc9 (4.8) */
+/* 48b4800a1c6af2cdda344ea4e2c843dcc1f6afc9 (4.8), dropped by 68f2736a858324c3ec852f6c2cddd9d1c777357d (v6.0) */
#ifndef ZSMALLOC_MAGIC
# define ZSMALLOC_MAGIC 0x58295829
#else
assert_cc(ZSMALLOC_MAGIC == 0x58295829);
#endif
-/* 3bc52c45bac26bf7ed1dc8d287ad1aeaed1250b6 (4.9) */
-#ifndef DAXFS_MAGIC
-# define DAXFS_MAGIC 0x64646178
-#else
-assert_cc(DAXFS_MAGIC == 0x64646178);
-#endif
-
-/* 5ff193fbde20df5d80fec367cea3e7856c057320 (4.10) */
-#ifndef RDTGROUP_SUPER_MAGIC
-# define RDTGROUP_SUPER_MAGIC 0x7655821
-#else
-assert_cc(RDTGROUP_SUPER_MAGIC == 0x7655821);
-#endif
-
-/* a481f4d917835cad86701fc0d1e620c74bb5cd5f (4.13) */
-#ifndef AAFS_MAGIC
-# define AAFS_MAGIC 0x5a3c69f0
-#else
-assert_cc(AAFS_MAGIC == 0x5a3c69f0);
-#endif
-
-/* f044c8847bb61eff5e1e95b6f6bb950e7f4a73a4 (4.15) */
-#ifndef AFS_FS_MAGIC
-# define AFS_FS_MAGIC 0x6b414653
-#else
-assert_cc(AFS_FS_MAGIC == 0x6b414653);
-#endif
-
-/* dddde68b8f06dd83486124b8d245e7bfb15c185d (4.20) */
-#ifndef XFS_SUPER_MAGIC
-# define XFS_SUPER_MAGIC 0x58465342
-#else
-assert_cc(XFS_SUPER_MAGIC == 0x58465342);
-#endif
-
-/* 3ad20fe393b31025bebfc2d76964561f65df48aa (5.0) */
-#ifndef BINDERFS_SUPER_MAGIC
-# define BINDERFS_SUPER_MAGIC 0x6c6f6f70
-#else
-assert_cc(BINDERFS_SUPER_MAGIC == 0x6c6f6f70);
-#endif
-
-/* ed63bb1d1f8469586006a9ca63c42344401aa2ab (5.3) */
-#ifndef DMA_BUF_MAGIC
-# define DMA_BUF_MAGIC 0x444d4142
-#else
-assert_cc(DMA_BUF_MAGIC == 0x444d4142);
-#endif
-
-/* ea8157ab2ae5e914dd427e5cfab533b6da3819cd (5.3) */
+/* ea8157ab2ae5e914dd427e5cfab533b6da3819cd (5.3), dropped by 68f2736a858324c3ec852f6c2cddd9d1c777357d (v6.0) */
#ifndef Z3FOLD_MAGIC
# define Z3FOLD_MAGIC 0x33
#else
assert_cc(Z3FOLD_MAGIC == 0x33);
#endif
-/* 47e4937a4a7ca4184fd282791dfee76c6799966a (5.4) */
-#ifndef EROFS_SUPER_MAGIC_V1
-# define EROFS_SUPER_MAGIC_V1 0xe0f5e1e2
-#else
-assert_cc(EROFS_SUPER_MAGIC_V1 == 0xe0f5e1e2);
-#endif
-
-/* fe030c9b85e6783bc52fe86449c0a4b8aa16c753 (5.5) */
+/* fe030c9b85e6783bc52fe86449c0a4b8aa16c753 (5.5), dropped by 68f2736a858324c3ec852f6c2cddd9d1c777357d (v6.0) */
#ifndef PPC_CMM_MAGIC
# define PPC_CMM_MAGIC 0xc7571590
#else
assert_cc(PPC_CMM_MAGIC == 0xc7571590);
#endif
-/* 8dcc1a9d90c10fa4143e5c17821082e5e60e46a1 (5.6) */
-#ifndef ZONEFS_MAGIC
-# define ZONEFS_MAGIC 0x5a4f4653
-#else
-assert_cc(ZONEFS_MAGIC == 0x5a4f4653);
-#endif
-
-/* 3234ac664a870e6ea69ae3a57d824cd7edbeacc5 (5.8) */
-#ifndef DEVMEM_MAGIC
-# define DEVMEM_MAGIC 0x454d444d
-#else
-assert_cc(DEVMEM_MAGIC == 0x454d444d);
-#endif
-
-/* cb12fd8e0dabb9a1c8aef55a6a41e2c255fcdf4b (6.8) */
-#ifndef PID_FS_MAGIC
-# define PID_FS_MAGIC 0x50494446
-#else
-assert_cc(PID_FS_MAGIC == 0x50494446);
-#endif
-
/* Not in mainline but included in Ubuntu */
#ifndef SHIFTFS_MAGIC
# define SHIFTFS_MAGIC 0x6a656a62
assert_cc(SHIFTFS_MAGIC == 0x6a656a62);
#endif
-/* 1507f51255c9ff07d75909a84e7c0d7f3c4b2f49 (5.14) */
-#ifndef SECRETMEM_MAGIC
-# define SECRETMEM_MAGIC 0x5345434d
-#else
-assert_cc(SECRETMEM_MAGIC == 0x5345434d);
-#endif
-
-/* Not exposed yet. Defined at fs/fuse/inode.c */
-#ifndef FUSE_SUPER_MAGIC
-# define FUSE_SUPER_MAGIC 0x65735546
-#else
-assert_cc(FUSE_SUPER_MAGIC == 0x65735546);
-#endif
-
/* Not exposed yet. Defined at fs/fuse/control.c */
#ifndef FUSE_CTL_SUPER_MAGIC
# define FUSE_CTL_SUPER_MAGIC 0x65735543
assert_cc(FUSE_CTL_SUPER_MAGIC == 0x65735543);
#endif
-/* Not exposed yet. Defined at fs/ceph/super.h */
-#ifndef CEPH_SUPER_MAGIC
-# define CEPH_SUPER_MAGIC 0x00c36400
-#else
-assert_cc(CEPH_SUPER_MAGIC == 0x00c36400);
-#endif
-
/* Not exposed yet. Defined at fs/orangefs/orangefs-kernel.h */
#ifndef ORANGEFS_DEVREQ_MAGIC
# define ORANGEFS_DEVREQ_MAGIC 0x20030529
assert_cc(VBOXSF_SUPER_MAGIC == 0x786f4256);
#endif
-/* Not exposed yet. Defined at fs/exfat/exfat_fs.h */
-#ifndef EXFAT_SUPER_MAGIC
-# define EXFAT_SUPER_MAGIC 0x2011BAB0UL
-#else
-assert_cc(EXFAT_SUPER_MAGIC == 0x2011BAB0UL);
-#endif
-
/* Not exposed yet, internally actually called RPCAUTH_GSSMAGIC. Defined in net/sunrpc/rpc_pipe.c */
#ifndef RPC_PIPEFS_SUPER_MAGIC
# define RPC_PIPEFS_SUPER_MAGIC 0x67596969
#define pid_namespace_path(pid, type) procfs_file_alloca(pid, namespace_info[type].proc_path)
+static NamespaceType clone_flag_to_namespace_type(unsigned long clone_flag) {
+ for (NamespaceType t = 0; t < _NAMESPACE_TYPE_MAX; t++)
+ if (((namespace_info[t].clone_flag ^ clone_flag) & (CLONE_NEWCGROUP|CLONE_NEWIPC|CLONE_NEWNET|CLONE_NEWNS|CLONE_NEWPID|CLONE_NEWUSER|CLONE_NEWUTS|CLONE_NEWTIME)) == 0)
+ return t;
+
+ return _NAMESPACE_TYPE_INVALID;
+}
+
int namespace_open(
pid_t pid,
int *ret_pidns_fd,
return 0;
}
+int detach_mount_namespace_harder(uid_t target_uid, gid_t target_gid) {
+ int r;
+
+ /* Tried detach_mount_namespace() first. If that doesn't work due to permissions, opens up an
+ * unprivileged user namespace with a mapping of the originating UID/GID to the specified target
+ * UID/GID. Then, tries detach_mount_namespace() again.
+ *
+ * Or in other words: tries much harder to get a mount namespace, making use of unprivileged user
+ * namespaces if need be.
+ *
+ * Note that after this function completed:
+ *
+ * → if we had privs, afterwards uids/gids on files and processes are as before
+ *
+ * → if we had no privs, our own id and all our files will show up owned by target_uid/target_gid,
+ * and everything else owned by nobody.
+ *
+ * Yes, that's quite a difference. */
+
+ if (!uid_is_valid(target_uid))
+ return -EINVAL;
+ if (!gid_is_valid(target_gid))
+ return -EINVAL;
+
+ r = detach_mount_namespace();
+ if (r != -EPERM)
+ return r;
+
+ if (unshare(CLONE_NEWUSER) < 0)
+ return log_debug_errno(errno, "Failed to acquire user namespace: %m");
+
+ r = write_string_filef("/proc/self/uid_map", 0,
+ UID_FMT " " UID_FMT " 1\n", target_uid, getuid());
+ if (r < 0)
+ return log_debug_errno(r, "Failed to write uid map: %m");
+
+ r = write_string_file("/proc/self/setgroups", "deny", 0);
+ if (r < 0)
+ return log_debug_errno(r, "Failed to write setgroups file: %m");
+
+ r = write_string_filef("/proc/self/gid_map", 0,
+ GID_FMT " " GID_FMT " 1\n", target_gid, getgid());
+ if (r < 0)
+ return log_debug_errno(r, "Failed to write gid map: %m");
+
+ return detach_mount_namespace();
+}
+
+int detach_mount_namespace_userns(int userns_fd) {
+ int r;
+
+ assert(userns_fd >= 0);
+
+ if (setns(userns_fd, CLONE_NEWUSER) < 0)
+ return log_debug_errno(errno, "Failed to join user namespace: %m");
+
+ r = reset_uid_gid();
+ if (r < 0)
+ return log_debug_errno(r, "Failed to become root in user namespace: %m");
+
+ return detach_mount_namespace();
+}
+
+int userns_acquire_empty(void) {
+ _cleanup_(sigkill_waitp) pid_t pid = 0;
+ _cleanup_close_ int userns_fd = -EBADF;
+ int r;
+
+ r = safe_fork("(sd-mkuserns)", FORK_CLOSE_ALL_FDS|FORK_DEATHSIG_SIGKILL|FORK_NEW_USERNS, &pid);
+ if (r < 0)
+ return r;
+ if (r == 0)
+ /* Child. We do nothing here, just freeze until somebody kills us. */
+ freeze();
+
+ r = namespace_open(pid, NULL, NULL, NULL, &userns_fd, NULL);
+ if (r < 0)
+ return log_error_errno(r, "Failed to open userns fd: %m");
+
+ return TAKE_FD(userns_fd);
+}
+
int userns_acquire(const char *uid_map, const char *gid_map) {
char path[STRLEN("/proc//uid_map") + DECIMAL_STR_MAX(pid_t) + 1];
_cleanup_(sigkill_waitp) pid_t pid = 0;
return 0;
}
+
+int namespace_open_by_type(NamespaceType type) {
+ const char *p;
+ int fd;
+
+ assert(type >= 0);
+ assert(type < _NAMESPACE_TYPE_MAX);
+
+ p = pid_namespace_path(0, type);
+
+ fd = RET_NERRNO(open(p, O_RDONLY|O_NOCTTY|O_CLOEXEC));
+ if (fd == -ENOENT && proc_mounted() == 0)
+ return -ENOSYS;
+
+ return fd;
+}
+
+int is_our_namespace(int fd, NamespaceType request_type) {
+ int clone_flag;
+
+ assert(fd >= 0);
+
+ clone_flag = ioctl(fd, NS_GET_NSTYPE);
+ if (clone_flag < 0)
+ return -errno;
+
+ NamespaceType found_type = clone_flag_to_namespace_type(clone_flag);
+ if (found_type < 0)
+ return -EBADF; /* Uh? Unknown namespace type? */
+
+ if (request_type >= 0 && request_type != found_type) /* It's a namespace, but not of the right type? */
+ return -EUCLEAN;
+
+ struct stat st_fd, st_ours;
+ if (fstat(fd, &st_fd) < 0)
+ return -errno;
+
+ const char *p = pid_namespace_path(0, found_type);
+ if (stat(p, &st_ours) < 0) {
+ if (errno == ENOENT)
+ return proc_mounted() == 0 ? -ENOSYS : -ENOENT;
+
+ return -errno;
+ }
+
+ return stat_inode_same(&st_ours, &st_fd);
+}
int fd_is_ns(int fd, unsigned long nsflag);
int detach_mount_namespace(void);
+int detach_mount_namespace_harder(uid_t target_uid, gid_t target_gid);
+int detach_mount_namespace_userns(int userns_fd);
static inline bool userns_shift_range_valid(uid_t shift, uid_t range) {
/* Checks that the specified userns range makes sense, i.e. contains at least one UID, and the end
return true;
}
+int userns_acquire_empty(void);
int userns_acquire(const char *uid_map, const char *gid_map);
+
int netns_acquire(void);
+
int in_same_namespace(pid_t pid1, pid_t pid2, NamespaceType type);
int parse_userns_uid_range(const char *s, uid_t *ret_uid_shift, uid_t *ret_uid_range);
+
+int namespace_open_by_type(NamespaceType type);
+
+int is_our_namespace(int fd, NamespaceType type);
return true;
}
+int path_extract_image_name(const char *path, char **ret) {
+ _cleanup_free_ char *fn = NULL;
+ int r;
+
+ assert(path);
+ assert(ret);
+
+ /* Extract last component from path, without any "/" suffixes. */
+ r = path_extract_filename(path, &fn);
+ if (r < 0)
+ return r;
+ if (r != O_DIRECTORY) {
+ /* Chop off any image suffixes we recognize (unless we already know this must refer to some dir) */
+ char *m = ENDSWITH_SET(fn, ".sysext.raw", ".confext.raw", ".raw");
+ if (m)
+ *m = 0;
+ }
+
+ /* Truncate the version/counting suffixes */
+ fn[strcspn(fn, "_+")] = 0;
+
+ if (!image_name_is_valid(fn))
+ return -EINVAL;
+
+ *ret = TAKE_PTR(fn);
+ return 0;
+}
+
int path_is_extension_tree(ImageClass image_class, const char *path, const char *extension, bool relax_extension_release_check) {
int r;
continue;
}
- if (!relax_extension_release_check &&
- extension_release_strict_xattr_value(fd, dir_path, de->d_name) != 0)
- continue;
+ if (!relax_extension_release_check) {
+ _cleanup_free_ char *base_image_name = NULL, *base_extension = NULL;
+
+ r = path_extract_image_name(image_name, &base_image_name);
+ if (r < 0) {
+ log_debug_errno(r, "Failed to extract image name from %s/%s, ignoring: %m", dir_path, de->d_name);
+ continue;
+ }
+
+ r = path_extract_image_name(extension, &base_extension);
+ if (r < 0) {
+ log_debug_errno(r, "Failed to extract image name from %s, ignoring: %m", extension);
+ continue;
+ }
+
+ if (!streq(base_image_name, base_extension) &&
+ extension_release_strict_xattr_value(fd, dir_path, image_name) != 0)
+ continue;
+ }
/* We already found what we were looking for, but there's another candidate? We treat this as
* an error, as we want to enforce that there are no ambiguities in case we are in the
* in accordance with the OS extension specification, rather than for /usr/lib/ or /etc/os-release. */
bool image_name_is_valid(const char *s) _pure_;
+int path_extract_image_name(const char *path, char **ret);
int path_is_extension_tree(ImageClass image_class, const char *path, const char *extension, bool relax_extension_release_check);
static inline int path_is_os_tree(const char *path) {
s = *p;
/* accept any number of digits, strtoull is limited to 19 */
- for (size_t i = 0; i < digits; i++,s++) {
+ for (size_t i = 0; i < digits; i++, s++) {
if (!ascii_isdigit(*s)) {
if (i == 0)
return -EINVAL;
}
int pidref_new_from_pid(pid_t pid, PidRef **ret) {
- _cleanup_(pidref_freep) PidRef *n = 0;
+ _cleanup_(pidref_freep) PidRef *n = NULL;
int r;
assert(ret);
#include "alloc-util.h"
#include "architecture.h"
#include "argv-util.h"
+#include "cgroup-util.h"
#include "dirent-util.h"
#include "env-file.h"
#include "env-util.h"
return FLAGS_SET(flags, POSIX_SPAWN_SETCGROUP);
}
- if (!(ERRNO_IS_NOT_SUPPORTED(r) || ERRNO_IS_PRIVILEGE(r)))
+ if (ERRNO_IS_NOT_SUPPORTED(r)) {
+ /* clone3() could also return EOPNOTSUPP if the target cgroup is in threaded mode. */
+ if (cgroup && cg_is_threaded(cgroup) > 0)
+ return -EUCLEAN;
+
+ /* clone3() not available? */
+ } else if (!ERRNO_IS_PRIVILEGE(r))
return -r;
/* Compiled on a newer host, or seccomp&friends blocking clone3()? Fallback, but need to change the
if (r < 0)
return r;
- r = sigtimedwait(&ss, NULL, &(struct timespec) { 0, 0 });
+ r = sigtimedwait(&ss, NULL, &(const struct timespec) {});
if (r < 0) {
if (errno == EAGAIN)
return 0;
/* SPDX-License-Identifier: LGPL-2.1-or-later */
+/* Make sure the net/if.h header is included before any linux/ one */
+#include <net/if.h>
#include <arpa/inet.h>
#include <errno.h>
#include <limits.h>
-#include <net/if.h>
#include <netdb.h>
#include <netinet/ip.h>
#include <poll.h>
int r;
assert(sa);
- assert(salen > sizeof(sa_family_t));
+ assert(salen >= sizeof(sa_family_t));
assert(ret);
r = getnameinfo(sa, salen, host, sizeof(host), /* service= */ NULL, /* service_len= */ 0, IDN_FLAGS);
#include "extract-word.h"
#include "fd-util.h"
#include "fileio.h"
+#include "glyph-util.h"
#include "gunicode.h"
#include "locale-util.h"
#include "macro.h"
}
static int write_ellipsis(char *buf, bool unicode) {
- if (unicode || is_locale_utf8()) {
- buf[0] = 0xe2; /* tri-dot ellipsis: … */
- buf[1] = 0x80;
- buf[2] = 0xa6;
- } else {
- buf[0] = '.';
- buf[1] = '.';
- buf[2] = '.';
- }
-
+ const char *s = special_glyph_full(SPECIAL_GLYPH_ELLIPSIS, unicode);
+ assert(strlen(s) == 3);
+ memcpy(buf, s, 3);
return 3;
}
x = ((new_length - need_space) * percent + 50) / 100;
assert(x <= new_length - need_space);
- memcpy(t, s, x);
- write_ellipsis(t + x, false);
+ write_ellipsis(mempcpy(t, s, x), /* unicode = */ false);
suffix_len = new_length - x - need_space;
memcpy(t + x + 3, s + old_length - suffix_len, suffix_len);
*(t + x + 3 + suffix_len) = '\0';
if (!e)
return NULL;
- /*
- printf("old_length=%zu new_length=%zu x=%zu len=%zu len2=%zu k=%zu\n",
- old_length, new_length, x, len, len2, k);
- */
-
memcpy_safe(e, s, len);
- write_ellipsis(e + len, true);
+ write_ellipsis(e + len, /* unicode = */ true);
char *dst = e + len + 3;
size_t i = 0, last_char_width[4] = {}, k = 0;
+ assert(buf);
assert(len > 0); /* at least a terminating NUL */
+ assert(s);
for (;;) {
char four[4];
}
if (i + 4 <= len) /* yay, enough space */
- i += write_ellipsis(buf + i, false);
+ i += write_ellipsis(buf + i, /* unicode = */ false);
else if (i + 3 <= len) { /* only space for ".." */
buf[i++] = '.';
buf[i++] = '.';
else
assert(i + 1 <= len);
- done:
+done:
buf[i] = '\0';
return buf;
}
return 0;
oom:
- /* truncate the bytes added after the first vsnprintf() attempt again */
+ /* truncate the bytes added after memcpy_safe() again */
(*x)[m] = 0;
return -ENOMEM;
}
}
}
-int string_contains_word_strv(const char *string, const char *separators, char **words, const char **ret_word) {
- /* In the default mode with no separators specified, we split on whitespace and
- * don't coalesce separators. */
+int string_contains_word_strv(const char *string, const char *separators, char * const *words, const char **ret_word) {
+ /* In the default mode with no separators specified, we split on whitespace and coalesce separators. */
const ExtractFlags flags = separators ? EXTRACT_DONT_COALESCE_SEPARATORS : 0;
-
const char *found = NULL;
+ int r;
- for (const char *p = string;;) {
+ for (;;) {
_cleanup_free_ char *w = NULL;
- int r;
- r = extract_first_word(&p, &w, separators, flags);
+ r = extract_first_word(&string, &w, separators, flags);
if (r < 0)
return r;
if (r == 0)
int string_truncate_lines(const char *s, size_t n_lines, char **ret);
int string_extract_line(const char *s, size_t i, char **ret);
-int string_contains_word_strv(const char *string, const char *separators, char **words, const char **ret_word);
+int string_contains_word_strv(const char *string, const char *separators, char * const *words, const char **ret_word);
static inline int string_contains_word(const char *string, const char *separators, const char *word) {
return string_contains_word_strv(string, separators, STRV_MAKE(word), NULL);
}
#define TIME_T_MAX (time_t)((UINTMAX_C(1) << ((sizeof(time_t) << 3) - 1)) - 1)
-#define DUAL_TIMESTAMP_NULL ((struct dual_timestamp) {})
-#define TRIPLE_TIMESTAMP_NULL ((struct triple_timestamp) {})
+#define DUAL_TIMESTAMP_NULL ((dual_timestamp) {})
+#define TRIPLE_TIMESTAMP_NULL ((triple_timestamp) {})
usec_t now(clockid_t clock);
nsec_t now_nsec(clockid_t clock);
#include "format-util.h"
#include "macro.h"
#include "path-util.h"
+#include "process-util.h"
#include "sort-util.h"
#include "stat-util.h"
#include "uid-range.h"
if (!range)
return false;
- for (size_t i = 0; i < range->n_entries; i++)
- if (start >= range->entries[i].start &&
- start + nr <= range->entries[i].start + range->entries[i].nr)
+ FOREACH_ARRAY(i, range->entries, range->n_entries)
+ if (start >= i->start &&
+ start + nr <= i->start + i->nr)
return true;
return false;
return 0;
}
-int uid_range_load_userns(UIDRange **ret, const char *path) {
+int uid_range_load_userns(const char *path, UIDRangeUsernsMode mode, UIDRange **ret) {
_cleanup_(uid_range_freep) UIDRange *range = NULL;
_cleanup_fclose_ FILE *f = NULL;
int r;
*
* To simplify things this will modify the passed array in case of later failure. */
+ assert(mode >= 0);
+ assert(mode < _UID_RANGE_USERNS_MODE_MAX);
assert(ret);
if (!path)
- path = "/proc/self/uid_map";
+ path = IN_SET(mode, UID_RANGE_USERNS_INSIDE, UID_RANGE_USERNS_OUTSIDE) ? "/proc/self/uid_map" : "/proc/self/gid_map";
f = fopen(path, "re");
if (!f) {
if (r < 0)
return r;
- r = uid_range_add_internal(&range, uid_base, uid_range, /* coalesce = */ false);
+ r = uid_range_add_internal(
+ &range,
+ IN_SET(mode, UID_RANGE_USERNS_INSIDE, GID_RANGE_USERNS_INSIDE) ? uid_base : uid_shift,
+ uid_range,
+ /* coalesce = */ false);
if (r < 0)
return r;
}
*ret = TAKE_PTR(range);
return 0;
}
+
+int uid_range_load_userns_by_fd(int userns_fd, UIDRangeUsernsMode mode, UIDRange **ret) {
+ _cleanup_(close_pairp) int pfd[2] = EBADF_PAIR;
+ _cleanup_(sigkill_waitp) pid_t pid = 0;
+ ssize_t n;
+ char x;
+ int r;
+
+ assert(userns_fd >= 0);
+ assert(mode >= 0);
+ assert(mode < _UID_RANGE_USERNS_MODE_MAX);
+ assert(ret);
+
+ if (pipe2(pfd, O_CLOEXEC) < 0)
+ return -errno;
+
+ r = safe_fork_full(
+ "(sd-mkuserns)",
+ /* stdio_fds= */ NULL,
+ (int[]) { pfd[1], userns_fd }, 2,
+ FORK_CLOSE_ALL_FDS|FORK_DEATHSIG_SIGKILL,
+ &pid);
+ if (r < 0)
+ return r;
+ if (r == 0) {
+ /* Child. */
+
+ if (setns(userns_fd, CLONE_NEWUSER) < 0) {
+ log_debug_errno(errno, "Failed to join userns: %m");
+ _exit(EXIT_FAILURE);
+ }
+
+ userns_fd = safe_close(userns_fd);
+
+ n = write(pfd[1], &(const char) { 'x' }, 1);
+ if (n < 0) {
+ log_debug_errno(errno, "Failed to write to fifo: %m");
+ _exit(EXIT_FAILURE);
+ }
+ assert(n == 1);
+
+ freeze();
+ }
+
+ pfd[1] = safe_close(pfd[1]);
+
+ n = read(pfd[0], &x, 1);
+ if (n < 0)
+ return -errno;
+ if (n == 0)
+ return -EPROTO;
+ assert(n == 1);
+ assert(x == 'x');
+
+ const char *p = procfs_file_alloca(
+ pid,
+ IN_SET(mode, UID_RANGE_USERNS_INSIDE, UID_RANGE_USERNS_OUTSIDE) ? "uid_map" : "gid_map");
+
+ return uid_range_load_userns(p, mode, ret);
+}
+
+bool uid_range_overlaps(const UIDRange *range, uid_t start, uid_t nr) {
+
+ if (!range)
+ return false;
+
+ /* Avoid overflow */
+ if (start > UINT32_MAX - nr)
+ nr = UINT32_MAX - start;
+
+ if (nr == 0)
+ return false;
+
+ FOREACH_ARRAY(entry, range->entries, range->n_entries)
+ if (start < entry->start + entry->nr &&
+ start + nr >= entry->start)
+ return true;
+
+ return false;
+}
+
+bool uid_range_equal(const UIDRange *a, const UIDRange *b) {
+ if (a == b)
+ return true;
+
+ if (!a || !b)
+ return false;
+
+ if (a->n_entries != b->n_entries)
+ return false;
+
+ for (size_t i = 0; i < a->n_entries; i++) {
+ if (a->entries[i].start != b->entries[i].start)
+ return false;
+ if (a->entries[i].nr != b->entries[i].nr)
+ return false;
+ }
+
+ return true;
+}
int uid_map_read_one(FILE *f, uid_t *ret_base, uid_t *ret_shift, uid_t *ret_range);
-int uid_range_load_userns(UIDRange **ret, const char *path);
+static inline size_t uid_range_entries(const UIDRange *range) {
+ return range ? range->n_entries : 0;
+}
+
+static inline unsigned uid_range_size(const UIDRange *range) {
+ if (!range)
+ return 0;
+
+ unsigned n = 0;
+
+ FOREACH_ARRAY(e, range->entries, range->n_entries)
+ n += e->nr;
+
+ return n;
+}
+
+static inline bool uid_range_is_empty(const UIDRange *range) {
+
+ if (!range)
+ return true;
+
+ FOREACH_ARRAY(e, range->entries, range->n_entries)
+ if (e->nr > 0)
+ return false;
+
+ return true;
+}
+
+bool uid_range_equal(const UIDRange *a, const UIDRange *b);
+
+typedef enum UIDRangeUsernsMode {
+ UID_RANGE_USERNS_INSIDE,
+ UID_RANGE_USERNS_OUTSIDE,
+ GID_RANGE_USERNS_INSIDE,
+ GID_RANGE_USERNS_OUTSIDE,
+ _UID_RANGE_USERNS_MODE_MAX,
+ _UID_RANGE_USERNS_MODE_INVALID = -EINVAL,
+} UIDRangeUsernsMode;
+
+int uid_range_load_userns(const char *path, UIDRangeUsernsMode mode, UIDRange **ret);
+int uid_range_load_userns_by_fd(int userns_fd, UIDRangeUsernsMode mode, UIDRange **ret);
+
+bool uid_range_overlaps(const UIDRange *range, uid_t start, uid_t nr);
[UNIT_BAD_SETTING] = "bad-setting",
[UNIT_ERROR] = "error",
[UNIT_MERGED] = "merged",
- [UNIT_MASKED] = "masked"
+ [UNIT_MASKED] = "masked",
};
DEFINE_STRING_TABLE_LOOKUP(unit_load_state, UnitLoadState);
[AUTOMOUNT_DEAD] = "dead",
[AUTOMOUNT_WAITING] = "waiting",
[AUTOMOUNT_RUNNING] = "running",
- [AUTOMOUNT_FAILED] = "failed"
+ [AUTOMOUNT_FAILED] = "failed",
};
DEFINE_STRING_TABLE_LOOKUP(automount_state, AutomountState);
[PATH_DEAD] = "dead",
[PATH_WAITING] = "waiting",
[PATH_RUNNING] = "running",
- [PATH_FAILED] = "failed"
+ [PATH_FAILED] = "failed",
};
DEFINE_STRING_TABLE_LOOKUP(path_state, PathState);
static const char* const slice_state_table[_SLICE_STATE_MAX] = {
[SLICE_DEAD] = "dead",
- [SLICE_ACTIVE] = "active"
+ [SLICE_ACTIVE] = "active",
};
DEFINE_STRING_TABLE_LOOKUP(slice_state, SliceState);
static const char* const target_state_table[_TARGET_STATE_MAX] = {
[TARGET_DEAD] = "dead",
- [TARGET_ACTIVE] = "active"
+ [TARGET_ACTIVE] = "active",
};
DEFINE_STRING_TABLE_LOOKUP(target_state, TargetState);
[TIMER_WAITING] = "waiting",
[TIMER_RUNNING] = "running",
[TIMER_ELAPSED] = "elapsed",
- [TIMER_FAILED] = "failed"
+ [TIMER_FAILED] = "failed",
};
DEFINE_STRING_TABLE_LOOKUP(timer_state, TimerState);
[NOTIFY_NONE] = "none",
[NOTIFY_MAIN] = "main",
[NOTIFY_EXEC] = "exec",
- [NOTIFY_ALL] = "all"
+ [NOTIFY_ALL] = "all",
};
DEFINE_STRING_TABLE_LOOKUP(notify_access, NotifyAccess);
-/* SPDX-License-Identifier: LGPL-2.1-or-later */
+/* SPDX-License-Identifier: LGPL-2.0-or-later */
-/* Parts of this file are based on the GLIB utf8 validation functions. The
- * original license text follows. */
-
-/* gutf8.c - Operations on UTF-8 strings.
+/* Parts of this file are based on the GLIB utf8 validation functions. The original copyright follows.
*
+ * gutf8.c - Operations on UTF-8 strings.
* Copyright (C) 1999 Tom Tromey
* Copyright (C) 2000 Red Hat, Inc.
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Library General Public
- * License as published by the Free Software Foundation; either
- * version 2 of the License, or (at your option) any later version.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Library General Public License for more details.
- *
- * You should have received a copy of the GNU Library General Public
- * License along with this library; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include <errno.h>
#include "generator.h"
#include "initrd-util.h"
#include "log.h"
-#include "mkdir.h"
#include "special.h"
#include "string-util.h"
#include "virt.h"
* boot as "good" if we manage to boot up far enough. */
static int run(const char *dest, const char *dest_early, const char *dest_late) {
+ assert(dest_early);
if (in_initrd()) {
log_debug("Skipping generator, running in the initrd.");
}
if (access(EFIVAR_PATH(EFI_LOADER_VARIABLE(LoaderBootCountPath)), F_OK) < 0) {
-
if (errno == ENOENT) {
log_debug_errno(errno, "Skipping generator, not booted with boot counting in effect.");
return 0;
/* We pull this in from basic.target so that it ends up in all "regular" boot ups, but not in
* rescue.target or even emergency.target. */
- const char *p = strjoina(dest_early, "/" SPECIAL_BASIC_TARGET ".wants/systemd-bless-boot.service");
- (void) mkdir_parents(p, 0755);
- if (symlink(SYSTEM_DATA_UNIT_DIR "/systemd-bless-boot.service", p) < 0)
- return log_error_errno(errno, "Failed to create symlink '%s': %m", p);
-
- return 0;
+ return generator_add_symlink(dest_early, SPECIAL_BASIC_TARGET, "wants", "systemd-bless-boot.service");
}
DEFINE_MAIN_GENERATOR_FUNCTION(run);
char *buffer;
size_t size;
} sb_vars[] = {
- { u"db", u"db.auth", EFI_IMAGE_SECURITY_DATABASE_GUID, true, NULL, 0 },
- { u"dbx", u"dbx.auth", EFI_IMAGE_SECURITY_DATABASE_GUID, false, NULL, 0 },
- { u"KEK", u"KEK.auth", EFI_GLOBAL_VARIABLE, true, NULL, 0 },
- { u"PK", u"PK.auth", EFI_GLOBAL_VARIABLE, true, NULL, 0 },
+ { u"db", u"db.auth", EFI_IMAGE_SECURITY_DATABASE_GUID, true },
+ { u"dbx", u"dbx.auth", EFI_IMAGE_SECURITY_DATABASE_GUID, false },
+ { u"KEK", u"KEK.auth", EFI_GLOBAL_VARIABLE, true },
+ { u"PK", u"PK.auth", EFI_GLOBAL_VARIABLE, true },
};
/* Make sure all keys files exist before we start enrolling them by loading them from the disk first. */
EFI_VARIABLE_RUNTIME_ACCESS |
EFI_VARIABLE_TIME_BASED_AUTHENTICATED_WRITE_ACCESS;
- if (sb_vars[i].size == 0)
+ if (!sb_vars[i].buffer)
continue;
+
err = efivar_set_raw(&sb_vars[i].vendor, sb_vars[i].name, sb_vars[i].buffer, sb_vars[i].size, sb_vars_opts);
if (err != EFI_SUCCESS) {
log_error_status(err, "Failed to write %ls secure boot variable: %m", sb_vars[i].name);
#include "unit.h"
static const UnitActiveState state_translation_table[_AUTOMOUNT_STATE_MAX] = {
- [AUTOMOUNT_DEAD] = UNIT_INACTIVE,
+ [AUTOMOUNT_DEAD] = UNIT_INACTIVE,
[AUTOMOUNT_WAITING] = UNIT_ACTIVE,
[AUTOMOUNT_RUNNING] = UNIT_ACTIVE,
- [AUTOMOUNT_FAILED] = UNIT_FAILED
+ [AUTOMOUNT_FAILED] = UNIT_FAILED,
};
static int open_dev_autofs(Manager *m);
/* SPDX-License-Identifier: LGPL-2.1-or-later */
+/* Make sure the net/if.h header is included before any linux/ one */
+#include <net/if.h>
#include <arpa/inet.h>
#include <assert.h>
#include <errno.h>
#include <fcntl.h>
#include <linux/bpf_insn.h>
#include <net/ethernet.h>
-#include <net/if.h>
#include <netinet/ip.h>
#include <netinet/ip6.h>
#include <stddef.h>
CGroupRuntime *crt = unit_get_cgroup_runtime(u);
if (!crt || !crt->cgroup_realized)
- return -EBUSY;
+ return 0; /* No cgroup = nothing running to freeze */
unit_next_freezer_state(u, action, &next, &target);
sd_bus_message *message,
UnitWriteFlags flags,
sd_bus_error *error) {
- bool is_ex_prop = endswith(name, "Ex");
- unsigned n = 0;
+
+ const char *ex_prop = endswith(ASSERT_PTR(name), "Ex");
+ size_t n = 0;
int r;
+ assert(u);
+ assert(exec_command);
+ assert(message);
+ assert(error);
+
/* Drop Ex from the written setting. E.g. ExecStart=, not ExecStartEx=. */
- const char *written_name = is_ex_prop ? strndupa_safe(name, strlen(name) - 2) : name;
+ const char *written_name = ex_prop ? strndupa_safe(name, ex_prop - name) : name;
- r = sd_bus_message_enter_container(message, 'a', is_ex_prop ? "(sasas)" : "(sasb)");
+ r = sd_bus_message_enter_container(message, 'a', ex_prop ? "(sasas)" : "(sasb)");
if (r < 0)
return r;
- while ((r = sd_bus_message_enter_container(message, 'r', is_ex_prop ? "sasas" : "sasb")) > 0) {
+ while ((r = sd_bus_message_enter_container(message, 'r', ex_prop ? "sasas" : "sasb")) > 0) {
_cleanup_strv_free_ char **argv = NULL, **ex_opts = NULL;
const char *path;
int b;
return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS,
"\"%s\" argv cannot be empty", name);
- r = is_ex_prop ? sd_bus_message_read_strv(message, &ex_opts) : sd_bus_message_read(message, "b", &b);
+ r = ex_prop ? sd_bus_message_read_strv(message, &ex_opts) : sd_bus_message_read(message, "b", &b);
if (r < 0)
return r;
return r;
if (!UNIT_WRITE_FLAGS_NOOP(flags)) {
- ExecCommand *c;
+ _cleanup_(exec_command_freep) ExecCommand *c = NULL;
- c = new0(ExecCommand, 1);
+ c = new(ExecCommand, 1);
if (!c)
return -ENOMEM;
- c->path = strdup(path);
- if (!c->path) {
- free(c);
- return -ENOMEM;
- }
+ *c = (ExecCommand) {
+ .argv = TAKE_PTR(argv),
+ };
- c->argv = TAKE_PTR(argv);
+ r = path_simplify_alloc(path, &c->path);
+ if (r < 0)
+ return r;
- if (is_ex_prop) {
+ if (ex_prop) {
r = exec_command_flags_from_strv(ex_opts, &c->flags);
if (r < 0)
return r;
- } else
- c->flags = b ? EXEC_COMMAND_IGNORE_FAILURE : 0;
+ } else if (b)
+ c->flags |= EXEC_COMMAND_IGNORE_FAILURE;
- path_simplify(c->path);
- exec_command_append_list(exec_command, c);
+ exec_command_append_list(exec_command, TAKE_PTR(c));
}
n++;
assert(message);
assert(m);
+ assert(handler);
/* Read the first argument from the command and pass the operation to the specified per-unit
* method. */
}
static int method_get_unit_processes(sd_bus_message *message, void *userdata, sd_bus_error *error) {
- /* Don't load a unit (since it won't have any processes if it's not loaded), but don't insist on the
- * unit being loaded (because even improperly loaded units might still have processes around */
- return method_generic_unit_operation(message, userdata, error, bus_unit_method_get_processes, 0);
+ /* Don't load a unit actively (since it won't have any processes if it's not loaded), but don't
+ * insist on the unit being loaded either (because even improperly loaded units might still have
+ * processes around). */
+ return method_generic_unit_operation(message, userdata, error, bus_unit_method_get_processes, /* flags = */ 0);
}
static int method_attach_processes_to_unit(sd_bus_message *message, void *userdata, sd_bus_error *error) {
log_caller(message, m, "Reloading");
/* Check the rate limit after the authorization succeeds, to avoid denial-of-service issues. */
- if (!ratelimit_below(&m->reload_ratelimit)) {
+ if (!ratelimit_below(&m->reload_reexec_ratelimit)) {
log_warning("Reloading request rejected due to rate limit.");
return sd_bus_error_setf(error,
SD_BUS_ERROR_LIMITS_EXCEEDED,
/* Write a log message noting the unit or process who requested the Reexecute() */
log_caller(message, m, "Reexecuting");
+ /* Check the rate limit after the authorization succeeds, to avoid denial-of-service issues. */
+ if (!ratelimit_below(&m->reload_reexec_ratelimit)) {
+ log_warning("Reexecuting request rejected due to rate limit.");
+ return sd_bus_error_setf(error,
+ SD_BUS_ERROR_LIMITS_EXCEEDED,
+ "Reexecute() request rejected due to rate limit.");
+ }
+
/* We don't send a reply back here, the client should
* just wait for us disconnecting. */
BUS_PROPERTY_DUAL_TIMESTAMP("InitRDTimestamp", offsetof(Manager, timestamps[MANAGER_TIMESTAMP_INITRD]), SD_BUS_VTABLE_PROPERTY_CONST),
BUS_PROPERTY_DUAL_TIMESTAMP("UserspaceTimestamp", offsetof(Manager, timestamps[MANAGER_TIMESTAMP_USERSPACE]), SD_BUS_VTABLE_PROPERTY_CONST),
BUS_PROPERTY_DUAL_TIMESTAMP("FinishTimestamp", offsetof(Manager, timestamps[MANAGER_TIMESTAMP_FINISH]), SD_BUS_VTABLE_PROPERTY_CONST),
+ BUS_PROPERTY_DUAL_TIMESTAMP("SoftRebootStartTimestamp", offsetof(Manager, timestamps[MANAGER_TIMESTAMP_SOFTREBOOT_START]), SD_BUS_VTABLE_PROPERTY_CONST),
BUS_PROPERTY_DUAL_TIMESTAMP("SecurityStartTimestamp", offsetof(Manager, timestamps[MANAGER_TIMESTAMP_SECURITY_START]), SD_BUS_VTABLE_PROPERTY_CONST),
BUS_PROPERTY_DUAL_TIMESTAMP("SecurityFinishTimestamp", offsetof(Manager, timestamps[MANAGER_TIMESTAMP_SECURITY_FINISH]), SD_BUS_VTABLE_PROPERTY_CONST),
BUS_PROPERTY_DUAL_TIMESTAMP("GeneratorsStartTimestamp", offsetof(Manager, timestamps[MANAGER_TIMESTAMP_GENERATORS_START]), SD_BUS_VTABLE_PROPERTY_CONST),
SD_BUS_PROPERTY("DefaultOOMPolicy", "s", bus_property_get_oom_policy, offsetof(Manager, defaults.oom_policy), SD_BUS_VTABLE_PROPERTY_CONST),
SD_BUS_PROPERTY("DefaultOOMScoreAdjust", "i", property_get_oom_score_adjust, 0, SD_BUS_VTABLE_PROPERTY_CONST),
SD_BUS_PROPERTY("CtrlAltDelBurstAction", "s", bus_property_get_emergency_action, offsetof(Manager, cad_burst_action), SD_BUS_VTABLE_PROPERTY_CONST),
+ SD_BUS_PROPERTY("SoftRebootsCount", "u", bus_property_get_unsigned, offsetof(Manager, soft_reboots_count), SD_BUS_VTABLE_PROPERTY_CONST),
SD_BUS_METHOD_WITH_ARGS("GetUnit",
SD_BUS_ARGS("s", name),
SD_BUS_VTABLE_START(0),
SD_BUS_PROPERTY("Where", "s", NULL, offsetof(Mount, where), SD_BUS_VTABLE_PROPERTY_CONST),
SD_BUS_PROPERTY("What", "s", property_get_what, 0, SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
- SD_BUS_PROPERTY("Options","s", property_get_options, 0, SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
+ SD_BUS_PROPERTY("Options", "s", property_get_options, 0, SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
SD_BUS_PROPERTY("Type", "s", property_get_type, 0, SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
SD_BUS_PROPERTY("TimeoutUSec", "t", bus_property_get_usec, offsetof(Mount, timeout_usec), SD_BUS_VTABLE_PROPERTY_CONST),
SD_BUS_PROPERTY("ControlPID", "u", bus_property_get_pid, offsetof(Mount, control_pid.pid), SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
_cleanup_set_free_ Set *ready_units = NULL, *not_ready_units = NULL;
Device *d;
+ if (device_is_processed(dev) <= 0)
+ continue;
+
if (device_setup_units(m, dev, &ready_units, ¬_ready_units) < 0)
continue;
* the lock on the socket taken. */
k = receive_one_fd_iov(d->storage_socket[0], &iov, 1, MSG_DONTWAIT, &lock_fd);
- if (k < 0)
+ if (k < 0) {
+ assert(errno_is_valid(-k));
return (int) k;
+ }
*ret_uid = uid;
*ret_lock_fd = lock_fd;
#include "virt.h"
static const char* const emergency_action_table[_EMERGENCY_ACTION_MAX] = {
- [EMERGENCY_ACTION_NONE] = "none",
- [EMERGENCY_ACTION_REBOOT] = "reboot",
- [EMERGENCY_ACTION_REBOOT_FORCE] = "reboot-force",
- [EMERGENCY_ACTION_REBOOT_IMMEDIATE] = "reboot-immediate",
- [EMERGENCY_ACTION_POWEROFF] = "poweroff",
- [EMERGENCY_ACTION_POWEROFF_FORCE] = "poweroff-force",
+ [EMERGENCY_ACTION_NONE] = "none",
+ [EMERGENCY_ACTION_REBOOT] = "reboot",
+ [EMERGENCY_ACTION_REBOOT_FORCE] = "reboot-force",
+ [EMERGENCY_ACTION_REBOOT_IMMEDIATE] = "reboot-immediate",
+ [EMERGENCY_ACTION_POWEROFF] = "poweroff",
+ [EMERGENCY_ACTION_POWEROFF_FORCE] = "poweroff-force",
[EMERGENCY_ACTION_POWEROFF_IMMEDIATE] = "poweroff-immediate",
- [EMERGENCY_ACTION_EXIT] = "exit",
- [EMERGENCY_ACTION_EXIT_FORCE] = "exit-force",
- [EMERGENCY_ACTION_SOFT_REBOOT] = "soft-reboot",
- [EMERGENCY_ACTION_SOFT_REBOOT_FORCE] = "soft-reboot-force",
- [EMERGENCY_ACTION_KEXEC] = "kexec",
- [EMERGENCY_ACTION_KEXEC_FORCE] = "kexec-force",
- [EMERGENCY_ACTION_HALT] = "halt",
- [EMERGENCY_ACTION_HALT_FORCE] = "halt-force",
- [EMERGENCY_ACTION_HALT_IMMEDIATE] = "halt-immediate",
+ [EMERGENCY_ACTION_EXIT] = "exit",
+ [EMERGENCY_ACTION_EXIT_FORCE] = "exit-force",
+ [EMERGENCY_ACTION_SOFT_REBOOT] = "soft-reboot",
+ [EMERGENCY_ACTION_SOFT_REBOOT_FORCE] = "soft-reboot-force",
+ [EMERGENCY_ACTION_KEXEC] = "kexec",
+ [EMERGENCY_ACTION_KEXEC_FORCE] = "kexec-force",
+ [EMERGENCY_ACTION_HALT] = "halt",
+ [EMERGENCY_ACTION_HALT_FORCE] = "halt-force",
+ [EMERGENCY_ACTION_HALT_IMMEDIATE] = "halt-immediate",
};
static void log_and_status(Manager *m, bool warn, const char *message, const char *reason) {
/* And this table also maps ExecDirectoryType, to the environment variable we pass the selected directory to
* the service payload in. */
static const char* const exec_directory_env_name_table[_EXEC_DIRECTORY_TYPE_MAX] = {
- [EXEC_DIRECTORY_RUNTIME] = "RUNTIME_DIRECTORY",
- [EXEC_DIRECTORY_STATE] = "STATE_DIRECTORY",
- [EXEC_DIRECTORY_CACHE] = "CACHE_DIRECTORY",
- [EXEC_DIRECTORY_LOGS] = "LOGS_DIRECTORY",
+ [EXEC_DIRECTORY_RUNTIME] = "RUNTIME_DIRECTORY",
+ [EXEC_DIRECTORY_STATE] = "STATE_DIRECTORY",
+ [EXEC_DIRECTORY_CACHE] = "CACHE_DIRECTORY",
+ [EXEC_DIRECTORY_LOGS] = "LOGS_DIRECTORY",
[EXEC_DIRECTORY_CONFIGURATION] = "CONFIGURATION_DIRECTORY",
};
int *exit_status) {
static const int exit_status_table[_EXEC_DIRECTORY_TYPE_MAX] = {
- [EXEC_DIRECTORY_RUNTIME] = EXIT_RUNTIME_DIRECTORY,
- [EXEC_DIRECTORY_STATE] = EXIT_STATE_DIRECTORY,
- [EXEC_DIRECTORY_CACHE] = EXIT_CACHE_DIRECTORY,
- [EXEC_DIRECTORY_LOGS] = EXIT_LOGS_DIRECTORY,
+ [EXEC_DIRECTORY_RUNTIME] = EXIT_RUNTIME_DIRECTORY,
+ [EXEC_DIRECTORY_STATE] = EXIT_STATE_DIRECTORY,
+ [EXEC_DIRECTORY_CACHE] = EXIT_CACHE_DIRECTORY,
+ [EXEC_DIRECTORY_LOGS] = EXIT_LOGS_DIRECTORY,
[EXEC_DIRECTORY_CONFIGURATION] = EXIT_CONFIGURATION_DIRECTORY,
};
int r;
}
static int connect_unix_harder(const ExecContext *c, const ExecParameters *p, const OpenFile *of, int ofd) {
+ static const int socket_types[] = { SOCK_DGRAM, SOCK_STREAM, SOCK_SEQPACKET };
+
union sockaddr_union addr = {
.un.sun_family = AF_UNIX,
};
socklen_t sa_len;
- static const int socket_types[] = { SOCK_DGRAM, SOCK_STREAM, SOCK_SEQPACKET };
int r;
assert(c);
r = sockaddr_un_set_path(&addr.un, FORMAT_PROC_FD_PATH(ofd));
if (r < 0)
- return log_exec_error_errno(c, p, r, "Failed to set sockaddr for %s: %m", of->path);
-
+ return log_exec_error_errno(c, p, r, "Failed to set sockaddr for '%s': %m", of->path);
sa_len = r;
- for (size_t i = 0; i < ELEMENTSOF(socket_types); i++) {
+ FOREACH_ARRAY(i, socket_types, ELEMENTSOF(socket_types)) {
_cleanup_close_ int fd = -EBADF;
- fd = socket(AF_UNIX, socket_types[i] | SOCK_CLOEXEC, 0);
+ fd = socket(AF_UNIX, *i|SOCK_CLOEXEC, 0);
if (fd < 0)
- return log_exec_error_errno(c,
- p,
- errno,
- "Failed to create socket for %s: %m",
+ return log_exec_error_errno(c, p,
+ errno, "Failed to create socket for '%s': %m",
of->path);
r = RET_NERRNO(connect(fd, &addr.sa, sa_len));
- if (r == -EPROTOTYPE)
- continue;
- if (r < 0)
- return log_exec_error_errno(c,
- p,
- r,
- "Failed to connect socket for %s: %m",
+ if (r >= 0)
+ return TAKE_FD(fd);
+ if (r != -EPROTOTYPE)
+ return log_exec_error_errno(c, p,
+ r, "Failed to connect to socket for '%s': %m",
of->path);
-
- return TAKE_FD(fd);
}
- return log_exec_error_errno(c,
- p,
- SYNTHETIC_ERRNO(EPROTOTYPE), "Failed to connect socket for \"%s\".",
+ return log_exec_error_errno(c, p,
+ SYNTHETIC_ERRNO(EPROTOTYPE), "No suitable socket type to connect to socket '%s'.",
of->path);
}
static int get_open_file_fd(const ExecContext *c, const ExecParameters *p, const OpenFile *of) {
- struct stat st;
_cleanup_close_ int fd = -EBADF, ofd = -EBADF;
+ struct stat st;
assert(c);
assert(p);
ofd = open(of->path, O_PATH | O_CLOEXEC);
if (ofd < 0)
- return log_exec_error_errno(c, p, errno, "Could not open \"%s\": %m", of->path);
+ return log_exec_error_errno(c, p, errno, "Failed to open '%s' as O_PATH: %m", of->path);
if (fstat(ofd, &st) < 0)
- return log_exec_error_errno(c, p, errno, "Failed to stat %s: %m", of->path);
+ return log_exec_error_errno(c, p, errno, "Failed to stat '%s': %m", of->path);
if (S_ISSOCK(st.st_mode)) {
fd = connect_unix_harder(c, p, of, ofd);
return fd;
if (FLAGS_SET(of->flags, OPENFILE_READ_ONLY) && shutdown(fd, SHUT_WR) < 0)
- return log_exec_error_errno(c, p, errno, "Failed to shutdown send for socket %s: %m",
+ return log_exec_error_errno(c, p,
+ errno, "Failed to shutdown send for socket '%s': %m",
of->path);
- log_exec_debug(c, p, "socket %s opened (fd=%d)", of->path, fd);
+ log_exec_debug(c, p, "Opened socket '%s' as fd %d.", of->path, fd);
} else {
int flags = FLAGS_SET(of->flags, OPENFILE_READ_ONLY) ? O_RDONLY : O_RDWR;
if (FLAGS_SET(of->flags, OPENFILE_APPEND))
fd = fd_reopen(ofd, flags | O_CLOEXEC);
if (fd < 0)
- return log_exec_error_errno(c, p, fd, "Failed to open file %s: %m", of->path);
+ return log_exec_error_errno(c, p, fd, "Failed to reopen file '%s': %m", of->path);
- log_exec_debug(c, p, "file %s opened (fd=%d)", of->path, fd);
+ log_exec_debug(c, p, "Opened file '%s' as fd %d.", of->path, fd);
}
return TAKE_FD(fd);
fd = get_open_file_fd(c, p, of);
if (fd < 0) {
if (FLAGS_SET(of->flags, OPENFILE_GRACEFUL)) {
- log_exec_debug_errno(c, p, fd, "Failed to get OpenFile= file descriptor for %s, ignoring: %m", of->path);
+ log_exec_warning_errno(c, p, fd,
+ "Failed to get OpenFile= file descriptor for '%s', ignoring: %m",
+ of->path);
continue;
}
if (r < 0)
return r;
- p->fds[*n_fds] = TAKE_FD(fd);
-
- (*n_fds)++;
+ p->fds[(*n_fds)++] = TAKE_FD(fd);
}
return 0;
r = cg_attach_everywhere(params->cgroup_supported, p, 0, NULL, NULL);
if (r == -EUCLEAN) {
*exit_status = EXIT_CGROUP;
- return log_exec_error_errno(context, params, r, "Failed to attach process to cgroup %s "
+ return log_exec_error_errno(context, params, r,
+ "Failed to attach process to cgroup '%s', "
"because the cgroup or one of its parents or "
- "siblings is in the threaded mode: %m", p);
+ "siblings is in the threaded mode.", p);
}
if (r < 0) {
*exit_status = EXIT_CGROUP;
return log_exec_error_errno(context, params, r, "Failed to set up standard input: %m");
}
- r = setup_output(context, params, STDOUT_FILENO, socket_fd, named_iofds, basename(command->path), uid, gid, &journal_stream_dev, &journal_stream_ino);
+ _cleanup_free_ char *fname = NULL;
+ r = path_extract_filename(command->path, &fname);
+ if (r < 0) {
+ *exit_status = EXIT_STDOUT;
+ return log_exec_error_errno(context, params, r, "Failed to extract filename from path %s: %m", command->path);
+ }
+
+ r = setup_output(context, params, STDOUT_FILENO, socket_fd, named_iofds, fname, uid, gid, &journal_stream_dev, &journal_stream_ino);
if (r < 0) {
*exit_status = EXIT_STDOUT;
return log_exec_error_errno(context, params, r, "Failed to set up standard output: %m");
}
- r = setup_output(context, params, STDERR_FILENO, socket_fd, named_iofds, basename(command->path), uid, gid, &journal_stream_dev, &journal_stream_ino);
+ r = setup_output(context, params, STDERR_FILENO, socket_fd, named_iofds, fname, uid, gid, &journal_stream_dev, &journal_stream_ino);
if (r < 0) {
*exit_status = EXIT_STDERR;
return log_exec_error_errno(context, params, r, "Failed to set up standard error output: %m");
if (base64mem(sc->data, sc->size, &data) < 0)
return log_oom_debug();
- r = serialize_item_format(f, "exec-context-set-credentials", "%s %s %s", sc->id, yes_no(sc->encrypted), data);
+ r = serialize_item_format(f, "exec-context-set-credentials", "%s %s %s", sc->id, data, yes_no(sc->encrypted));
if (r < 0)
return r;
}
ExecLoadCredential *lc;
HASHMAP_FOREACH(lc, c->load_credentials) {
- r = serialize_item_format(f, "exec-context-load-credentials", "%s %s %s", lc->id, yes_no(lc->encrypted), lc->path);
+ r = serialize_item_format(f, "exec-context-load-credentials", "%s %s %s", lc->id, lc->path, yes_no(lc->encrypted));
if (r < 0)
return r;
}
_cleanup_(exec_set_credential_freep) ExecSetCredential *sc = NULL;
_cleanup_free_ char *id = NULL, *encrypted = NULL, *data = NULL;
- r = extract_many_words(&val, " ", 0, &id, &encrypted, &data);
+ r = extract_many_words(&val, " ", EXTRACT_DONT_COALESCE_SEPARATORS, &id, &data, &encrypted);
if (r < 0)
return r;
if (r != 3)
_cleanup_(exec_load_credential_freep) ExecLoadCredential *lc = NULL;
_cleanup_free_ char *id = NULL, *encrypted = NULL, *path = NULL;
- r = extract_many_words(&val, " ", 0, &id, &encrypted, &path);
+ r = extract_many_words(&val, " ", EXTRACT_DONT_COALESCE_SEPARATORS, &id, &path, &encrypted);
if (r < 0)
return r;
if (r != 3)
environ,
cg_unified() > 0 ? subcgroup_path : NULL,
&pidref);
+ if (r == -EUCLEAN && subcgroup_path)
+ return log_unit_error_errno(unit, r,
+ "Failed to spawn process into cgroup '%s', because the cgroup "
+ "or one of its parents or siblings is in the threaded mode.",
+ subcgroup_path);
if (r < 0)
return log_unit_error_errno(unit, r, "Failed to spawn executor: %m");
/* We add the new process to the cgroup both in the child (so that we can be sure that no user code is ever
exec_command_done(i);
}
+ExecCommand* exec_command_free(ExecCommand *c) {
+ if (!c)
+ return NULL;
+
+ exec_command_done(c);
+ return mfree(c);
+}
+
ExecCommand* exec_command_free_list(ExecCommand *c) {
ExecCommand *i;
- while ((i = LIST_POP(command, c))) {
- exec_command_done(i);
- free(i);
- }
+ while ((i = LIST_POP(command, c)))
+ exec_command_free(i);
return NULL;
}
}
static const char* const exec_input_table[_EXEC_INPUT_MAX] = {
- [EXEC_INPUT_NULL] = "null",
- [EXEC_INPUT_TTY] = "tty",
+ [EXEC_INPUT_NULL] = "null",
+ [EXEC_INPUT_TTY] = "tty",
[EXEC_INPUT_TTY_FORCE] = "tty-force",
- [EXEC_INPUT_TTY_FAIL] = "tty-fail",
- [EXEC_INPUT_SOCKET] = "socket",
- [EXEC_INPUT_NAMED_FD] = "fd",
- [EXEC_INPUT_DATA] = "data",
- [EXEC_INPUT_FILE] = "file",
+ [EXEC_INPUT_TTY_FAIL] = "tty-fail",
+ [EXEC_INPUT_SOCKET] = "socket",
+ [EXEC_INPUT_NAMED_FD] = "fd",
+ [EXEC_INPUT_DATA] = "data",
+ [EXEC_INPUT_FILE] = "file",
};
DEFINE_STRING_TABLE_LOOKUP(exec_input, ExecInput);
static const char* const exec_output_table[_EXEC_OUTPUT_MAX] = {
- [EXEC_OUTPUT_INHERIT] = "inherit",
- [EXEC_OUTPUT_NULL] = "null",
- [EXEC_OUTPUT_TTY] = "tty",
- [EXEC_OUTPUT_KMSG] = "kmsg",
- [EXEC_OUTPUT_KMSG_AND_CONSOLE] = "kmsg+console",
- [EXEC_OUTPUT_JOURNAL] = "journal",
+ [EXEC_OUTPUT_INHERIT] = "inherit",
+ [EXEC_OUTPUT_NULL] = "null",
+ [EXEC_OUTPUT_TTY] = "tty",
+ [EXEC_OUTPUT_KMSG] = "kmsg",
+ [EXEC_OUTPUT_KMSG_AND_CONSOLE] = "kmsg+console",
+ [EXEC_OUTPUT_JOURNAL] = "journal",
[EXEC_OUTPUT_JOURNAL_AND_CONSOLE] = "journal+console",
- [EXEC_OUTPUT_SOCKET] = "socket",
- [EXEC_OUTPUT_NAMED_FD] = "fd",
- [EXEC_OUTPUT_FILE] = "file",
- [EXEC_OUTPUT_FILE_APPEND] = "append",
- [EXEC_OUTPUT_FILE_TRUNCATE] = "truncate",
+ [EXEC_OUTPUT_SOCKET] = "socket",
+ [EXEC_OUTPUT_NAMED_FD] = "fd",
+ [EXEC_OUTPUT_FILE] = "file",
+ [EXEC_OUTPUT_FILE_APPEND] = "append",
+ [EXEC_OUTPUT_FILE_TRUNCATE] = "truncate",
};
DEFINE_STRING_TABLE_LOOKUP(exec_output, ExecOutput);
static const char* const exec_utmp_mode_table[_EXEC_UTMP_MODE_MAX] = {
- [EXEC_UTMP_INIT] = "init",
+ [EXEC_UTMP_INIT] = "init",
[EXEC_UTMP_LOGIN] = "login",
- [EXEC_UTMP_USER] = "user",
+ [EXEC_UTMP_USER] = "user",
};
DEFINE_STRING_TABLE_LOOKUP(exec_utmp_mode, ExecUtmpMode);
static const char* const exec_preserve_mode_table[_EXEC_PRESERVE_MODE_MAX] = {
- [EXEC_PRESERVE_NO] = "no",
- [EXEC_PRESERVE_YES] = "yes",
+ [EXEC_PRESERVE_NO] = "no",
+ [EXEC_PRESERVE_YES] = "yes",
[EXEC_PRESERVE_RESTART] = "restart",
};
/* This table maps ExecDirectoryType to the setting it is configured with in the unit */
static const char* const exec_directory_type_table[_EXEC_DIRECTORY_TYPE_MAX] = {
- [EXEC_DIRECTORY_RUNTIME] = "RuntimeDirectory",
- [EXEC_DIRECTORY_STATE] = "StateDirectory",
- [EXEC_DIRECTORY_CACHE] = "CacheDirectory",
- [EXEC_DIRECTORY_LOGS] = "LogsDirectory",
+ [EXEC_DIRECTORY_RUNTIME] = "RuntimeDirectory",
+ [EXEC_DIRECTORY_STATE] = "StateDirectory",
+ [EXEC_DIRECTORY_CACHE] = "CacheDirectory",
+ [EXEC_DIRECTORY_LOGS] = "LogsDirectory",
[EXEC_DIRECTORY_CONFIGURATION] = "ConfigurationDirectory",
};
* one is supposed to be generic enough to be used for unit types that don't use ExecContext and per-unit
* directories, specifically .timer units with their timestamp touch file. */
static const char* const exec_resource_type_table[_EXEC_DIRECTORY_TYPE_MAX] = {
- [EXEC_DIRECTORY_RUNTIME] = "runtime",
- [EXEC_DIRECTORY_STATE] = "state",
- [EXEC_DIRECTORY_CACHE] = "cache",
- [EXEC_DIRECTORY_LOGS] = "logs",
+ [EXEC_DIRECTORY_RUNTIME] = "runtime",
+ [EXEC_DIRECTORY_STATE] = "state",
+ [EXEC_DIRECTORY_CACHE] = "cache",
+ [EXEC_DIRECTORY_LOGS] = "logs",
[EXEC_DIRECTORY_CONFIGURATION] = "configuration",
};
static const char* const exec_keyring_mode_table[_EXEC_KEYRING_MODE_MAX] = {
[EXEC_KEYRING_INHERIT] = "inherit",
[EXEC_KEYRING_PRIVATE] = "private",
- [EXEC_KEYRING_SHARED] = "shared",
+ [EXEC_KEYRING_SHARED] = "shared",
};
DEFINE_STRING_TABLE_LOOKUP(exec_keyring_mode, ExecKeyringMode);
void exec_command_done(ExecCommand *c);
void exec_command_done_array(ExecCommand *c, size_t n);
+ExecCommand* exec_command_free(ExecCommand *c);
+DEFINE_TRIVIAL_CLEANUP_FUNC(ExecCommand*, exec_command_free);
ExecCommand* exec_command_free_list(ExecCommand *c);
void exec_command_free_array(ExecCommand **c, size_t n);
void exec_command_reset_status_array(ExecCommand *c, size_t n);
void exec_command_reset_status_list_array(ExecCommand **c, size_t n);
+
void exec_command_dump_list(ExecCommand *c, FILE *f, const char *prefix);
void exec_command_append_list(ExecCommand **l, ExecCommand *e);
int exec_command_set(ExecCommand *c, const char *path, ...) _sentinel_;
#include "fileio.h"
#include "kmod-setup.h"
#include "macro.h"
+#include "module-util.h"
#include "recurse-dir.h"
#include "string-util.h"
#include "strv.h"
#include "virt.h"
#if HAVE_KMOD
-#include "module-util.h"
-
-static void systemd_kmod_log(
- void *data,
- int priority,
- const char *file, int line,
- const char *fn,
- const char *format,
- va_list args) {
-
- /* library logging is enabled at debug only */
- DISABLE_WARNING_FORMAT_NONLITERAL;
- log_internalv(LOG_DEBUG, 0, file, line, fn, format, args);
- REENABLE_WARNING;
-}
-
static int match_modalias_recurse_dir_cb(
RecurseDirEvent event,
const char *path,
#endif
};
+ int r;
+
if (have_effective_cap(CAP_SYS_MODULE) <= 0)
return 0;
- _cleanup_(kmod_unrefp) struct kmod_ctx *ctx = NULL;
-
+ _cleanup_(sym_kmod_unrefp) struct kmod_ctx *ctx = NULL;
FOREACH_ARRAY(kmod, kmod_table, ELEMENTSOF(kmod_table)) {
if (kmod->path && access(kmod->path, F_OK) >= 0)
continue;
"this by loading the module...", kmod->module);
if (!ctx) {
- ctx = kmod_new(NULL, NULL);
- if (!ctx)
- return log_oom();
-
- kmod_set_log_fn(ctx, systemd_kmod_log, NULL);
- kmod_load_resources(ctx);
+ r = module_setup_context(&ctx);
+ if (r < 0)
+ return log_error_errno(r, "Failed to initialize kmod context: %m");
}
(void) module_load_and_warn(ctx, kmod->module, kmod->warn_if_unavailable);
void *userdata) {
ExecCommand **e = ASSERT_PTR(data);
- const Unit *u = userdata;
- const char *p;
- bool semicolon;
+ const Unit *u = ASSERT_PTR(userdata);
int r;
assert(filename);
return 0;
}
- p = rvalue;
+ const char *p = rvalue;
+ bool semicolon;
+
do {
_cleanup_free_ char *path = NULL, *firstword = NULL;
- ExecCommandFlags flags = 0;
- bool ignore = false, separate_argv0 = false;
- _cleanup_free_ ExecCommand *nce = NULL;
- _cleanup_strv_free_ char **n = NULL;
- size_t nlen = 0;
- const char *f;
semicolon = false;
continue;
}
- f = firstword;
- for (;;) {
- /* We accept an absolute path as first argument. If it's prefixed with - and the path doesn't
- * exist, we ignore it instead of erroring out; if it's prefixed with @, we allow overriding of
- * argv[0]; if it's prefixed with :, we will not do environment variable substitution;
- * if it's prefixed with +, it will be run with full privileges and no sandboxing; if
- * it's prefixed with '!' we apply sandboxing, but do not change user/group credentials; if
- * it's prefixed with '!!', then we apply user/group credentials if the kernel supports ambient
- * capabilities -- if it doesn't we don't apply the credentials themselves, but do apply most
- * other sandboxing, with some special exceptions for changing UID.
+ const char *f = firstword;
+ bool ignore, separate_argv0 = false;
+ ExecCommandFlags flags = 0;
+
+ for (;; f++) {
+ /* We accept an absolute path as first argument. Valid prefixes and their effect:
+ *
+ * "-": Ignore if the path doesn't exist
+ * "@": Allow overriding argv[0] (supplied as a separate argument)
+ * ":": Disable environment variable substitution
+ * "+": Run with full privileges and no sandboxing
+ * "!": Apply sandboxing except for user/group credentials
+ * "!!": Apply user/group credentials if the kernel supports ambient capabilities -
+ * if it doesn't we don't apply the credentials themselves, but do apply
+ * most other sandboxing, with some special exceptions for changing UID.
*
- * The idea is that '!!' may be used to write services that can take benefit of systemd's
- * UID/GID dropping if the kernel supports ambient creds, but provide an automatic fallback to
- * privilege dropping within the daemon if the kernel does not offer that. */
+ * The idea is that '!!' may be used to write services that can take benefit of
+ * systemd's UID/GID dropping if the kernel supports ambient creds, but provide
+ * an automatic fallback to privilege dropping within the daemon if the kernel
+ * does not offer that. */
- if (*f == '-' && !(flags & EXEC_COMMAND_IGNORE_FAILURE)) {
+ if (*f == '-' && !(flags & EXEC_COMMAND_IGNORE_FAILURE))
flags |= EXEC_COMMAND_IGNORE_FAILURE;
- ignore = true;
- } else if (*f == '@' && !separate_argv0)
+ else if (*f == '@' && !separate_argv0)
separate_argv0 = true;
else if (*f == ':' && !(flags & EXEC_COMMAND_NO_ENV_EXPAND))
flags |= EXEC_COMMAND_NO_ENV_EXPAND;
flags |= EXEC_COMMAND_AMBIENT_MAGIC;
} else
break;
- f++;
}
+ ignore = FLAGS_SET(flags, EXEC_COMMAND_IGNORE_FAILURE);
+
r = unit_path_printf(u, f, &path);
if (r < 0) {
log_syntax(unit, ignore ? LOG_WARNING : LOG_ERR, filename, line, r,
}
if (isempty(path)) {
- /* First word is either "-" or "@" with no command. */
log_syntax(unit, ignore ? LOG_WARNING : LOG_ERR, filename, line, 0,
- "Empty path in command line%s: '%s'",
+ "Empty path in command line%s: %s",
ignore ? ", ignoring" : "", rvalue);
return ignore ? 0 : -ENOEXEC;
}
if (!string_is_safe(path)) {
log_syntax(unit, ignore ? LOG_WARNING : LOG_ERR, filename, line, 0,
- "Executable name contains special characters%s: %s",
+ "Executable path contains special characters%s: %s",
ignore ? ", ignoring" : "", path);
return ignore ? 0 : -ENOEXEC;
}
- if (endswith(path, "/")) {
+ if (path_implies_directory(path)) {
log_syntax(unit, ignore ? LOG_WARNING : LOG_ERR, filename, line, 0,
"Executable path specifies a directory%s: %s",
ignore ? ", ignoring" : "", path);
return ignore ? 0 : -ENOEXEC;
}
- if (!separate_argv0) {
- char *w = NULL;
+ _cleanup_strv_free_ char **args = NULL;
- if (!GREEDY_REALLOC0(n, nlen + 2))
+ if (!separate_argv0)
+ if (strv_extend(&args, path) < 0)
return log_oom();
- w = strdup(path);
- if (!w)
- return log_oom();
- n[nlen++] = w;
- n[nlen] = NULL;
- }
-
- path_simplify(path);
-
while (!isempty(p)) {
_cleanup_free_ char *word = NULL, *resolved = NULL;
- /* Check explicitly for an unquoted semicolon as
- * command separator token. */
+ /* Check explicitly for an unquoted semicolon as command separator token. */
if (p[0] == ';' && (!p[1] || strchr(WHITESPACE, p[1]))) {
p++;
- p += strspn(p, WHITESPACE);
+ p = skip_leading_chars(p, /* bad = */ NULL);
semicolon = true;
break;
}
/* Check for \; explicitly, to not confuse it with \\; or "\;" or "\\;" etc.
- * extract_first_word() would return the same for all of those. */
+ * extract_first_word() would return the same for all of those. */
if (p[0] == '\\' && p[1] == ';' && (!p[2] || strchr(WHITESPACE, p[2]))) {
- char *w;
-
p += 2;
- p += strspn(p, WHITESPACE);
+ p = skip_leading_chars(p, /* bad = */ NULL);
- if (!GREEDY_REALLOC0(n, nlen + 2))
+ if (strv_extend(&args, ";") < 0)
return log_oom();
- w = strdup(";");
- if (!w)
- return log_oom();
- n[nlen++] = w;
- n[nlen] = NULL;
continue;
}
r = extract_first_word_and_warn(&p, &word, NULL, EXTRACT_UNQUOTE|EXTRACT_CUNESCAPE, unit, filename, line, rvalue);
- if (r == 0)
- break;
if (r < 0)
return ignore ? 0 : -ENOEXEC;
+ if (r == 0)
+ break;
r = unit_full_printf(u, word, &resolved);
if (r < 0) {
log_syntax(unit, ignore ? LOG_WARNING : LOG_ERR, filename, line, r,
- "Failed to resolve unit specifiers in %s%s: %m",
+ "Failed to resolve unit specifiers in '%s'%s: %m",
word, ignore ? ", ignoring" : "");
return ignore ? 0 : -ENOEXEC;
}
- if (!GREEDY_REALLOC(n, nlen + 2))
+ if (strv_consume(&args, TAKE_PTR(resolved)) < 0)
return log_oom();
-
- n[nlen++] = TAKE_PTR(resolved);
- n[nlen] = NULL;
}
- if (!n || !n[0]) {
+ if (strv_isempty(args)) {
log_syntax(unit, ignore ? LOG_WARNING : LOG_ERR, filename, line, 0,
"Empty executable name or zeroeth argument%s: %s",
ignore ? ", ignoring" : "", rvalue);
return ignore ? 0 : -ENOEXEC;
}
- nce = new0(ExecCommand, 1);
- if (!nce)
+ ExecCommand *nec = new(ExecCommand, 1);
+ if (!nec)
return log_oom();
- nce->argv = TAKE_PTR(n);
- nce->path = TAKE_PTR(path);
- nce->flags = flags;
-
- exec_command_append_list(e, nce);
+ *nec = (ExecCommand) {
+ .path = path_simplify(TAKE_PTR(path)),
+ .argv = TAKE_PTR(args),
+ .flags = flags,
+ };
- /* Do not _cleanup_free_ these. */
- nce = NULL;
+ exec_command_append_list(e, nec);
rvalue = p;
} while (semicolon);
m->cad_burst_action = arg_cad_burst_action;
/* Note that we don't do structured initialization here, otherwise it will reset the rate limit
* counter on every daemon-reload. */
- m->reload_ratelimit.interval = arg_reload_limit_interval_sec;
- m->reload_ratelimit.burst = arg_reload_limit_burst;
+ m->reload_reexec_ratelimit.interval = arg_reload_limit_interval_sec;
+ m->reload_reexec_ratelimit.burst = arg_reload_limit_burst;
manager_set_watchdog(m, WATCHDOG_RUNTIME, arg_runtime_watchdog);
manager_set_watchdog(m, WATCHDOG_REBOOT, arg_reboot_watchdog);
r = seccomp_restrict_archs(arg_syscall_archs);
if (r < 0)
- return log_error_errno(r, "Failed to enforce system call architecture restrication: %m");
+ return log_error_errno(r, "Failed to enforce system call architecture restriction: %m");
#endif
return 0;
}
manager_send_reloading(m);
manager_set_switching_root(m, true);
+ dual_timestamp_now(m->timestamps + MANAGER_TIMESTAMP_SOFTREBOOT_START);
+
r = prepare_reexecute(m, &arg_serialization, ret_fds, /* switching_root= */ true);
if (r < 0) {
*ret_error_message = "Failed to prepare for reexecution";
goto finish;
}
+ /* If we got a SoftRebootStart timestamp during deserialization, then we are in a new soft-reboot
+ * iteration, so bump the counter now before starting units, so that they can reliably read it. */
+ if (dual_timestamp_is_set(&m->timestamps[MANAGER_TIMESTAMP_SOFTREBOOT_START]))
+ m->soft_reboots_count++;
+
/* This will close all file descriptors that were opened, but not claimed by any unit. */
fds = fdset_free(fds);
arg_serialization = safe_fclose(arg_serialization);
return open_serialization_file("systemd-state", ret_f);
}
-static bool manager_timestamp_shall_serialize(ManagerTimestamp t) {
- if (!in_initrd())
+static bool manager_timestamp_shall_serialize(ManagerObjective o, ManagerTimestamp t) {
+ if (!in_initrd() && o != MANAGER_SOFT_REBOOT)
return true;
- /* The following timestamps only apply to the host system, hence only serialize them there */
+ /* The following timestamps only apply to the host system (or first boot in case of soft-reboot),
+ * hence only serialize them there. */
return !IN_SET(t,
MANAGER_TIMESTAMP_USERSPACE, MANAGER_TIMESTAMP_FINISH,
MANAGER_TIMESTAMP_SECURITY_START, MANAGER_TIMESTAMP_SECURITY_FINISH,
(void) serialize_usec(f, "pretimeout-watchdog-overridden", m->watchdog_overridden[WATCHDOG_PRETIMEOUT]);
(void) serialize_item(f, "pretimeout-watchdog-governor-overridden", m->watchdog_pretimeout_governor_overridden);
+ (void) serialize_item_format(f, "soft-reboots-count", "%u", m->soft_reboots_count);
+
for (ManagerTimestamp q = 0; q < _MANAGER_TIMESTAMP_MAX; q++) {
_cleanup_free_ char *joined = NULL;
- if (!manager_timestamp_shall_serialize(q))
+ if (!manager_timestamp_shall_serialize(m->objective, q))
continue;
joined = strjoin(manager_timestamp_to_string(q), "-timestamp");
}
(void) serialize_ratelimit(f, "dump-ratelimit", &m->dump_ratelimit);
+ (void) serialize_ratelimit(f, "reload-reexec-ratelimit", &m->reload_reexec_ratelimit);
bus_track_serialize(m->subscribed, f, "subscribed");
(void) varlink_server_deserialize_one(m->varlink_server, val, fds);
} else if ((val = startswith(l, "dump-ratelimit=")))
deserialize_ratelimit(&m->dump_ratelimit, "dump-ratelimit", val);
- else {
+ else if ((val = startswith(l, "reload-reexec-ratelimit=")))
+ deserialize_ratelimit(&m->reload_reexec_ratelimit, "reload-reexec-ratelimit", val);
+ else if ((val = startswith(l, "soft-reboots-count="))) {
+ unsigned n;
+
+ if (safe_atou(val, &n) < 0)
+ log_notice("Failed to parse soft reboots counter '%s', ignoring.", val);
+ else
+ m->soft_reboots_count = n;
+ } else {
ManagerTimestamp q;
for (q = 0; q < _MANAGER_TIMESTAMP_MAX; q++) {
};
static const struct table_entry paths_system[_EXEC_DIRECTORY_TYPE_MAX] = {
- [EXEC_DIRECTORY_RUNTIME] = { SD_PATH_SYSTEM_RUNTIME, NULL },
- [EXEC_DIRECTORY_STATE] = { SD_PATH_SYSTEM_STATE_PRIVATE, NULL },
- [EXEC_DIRECTORY_CACHE] = { SD_PATH_SYSTEM_STATE_CACHE, NULL },
- [EXEC_DIRECTORY_LOGS] = { SD_PATH_SYSTEM_STATE_LOGS, NULL },
+ [EXEC_DIRECTORY_RUNTIME] = { SD_PATH_SYSTEM_RUNTIME, NULL },
+ [EXEC_DIRECTORY_STATE] = { SD_PATH_SYSTEM_STATE_PRIVATE, NULL },
+ [EXEC_DIRECTORY_CACHE] = { SD_PATH_SYSTEM_STATE_CACHE, NULL },
+ [EXEC_DIRECTORY_LOGS] = { SD_PATH_SYSTEM_STATE_LOGS, NULL },
[EXEC_DIRECTORY_CONFIGURATION] = { SD_PATH_SYSTEM_CONFIGURATION, NULL },
};
static const struct table_entry paths_user[_EXEC_DIRECTORY_TYPE_MAX] = {
- [EXEC_DIRECTORY_RUNTIME] = { SD_PATH_USER_RUNTIME, NULL },
- [EXEC_DIRECTORY_STATE] = { SD_PATH_USER_STATE_PRIVATE, NULL },
- [EXEC_DIRECTORY_CACHE] = { SD_PATH_USER_STATE_CACHE, NULL },
- [EXEC_DIRECTORY_LOGS] = { SD_PATH_USER_STATE_PRIVATE, "log" },
+ [EXEC_DIRECTORY_RUNTIME] = { SD_PATH_USER_RUNTIME, NULL },
+ [EXEC_DIRECTORY_STATE] = { SD_PATH_USER_STATE_PRIVATE, NULL },
+ [EXEC_DIRECTORY_CACHE] = { SD_PATH_USER_STATE_CACHE, NULL },
+ [EXEC_DIRECTORY_LOGS] = { SD_PATH_USER_STATE_PRIVATE, "log" },
[EXEC_DIRECTORY_CONFIGURATION] = { SD_PATH_USER_CONFIGURATION, NULL },
};
if (n_targets < 0)
return n_targets;
- for (int i = 0; i < n_targets; i++) {
- r = unit_add_default_target_dependency(u, targets[i]);
+ FOREACH_ARRAY(i, targets, n_targets) {
+ r = unit_add_default_target_dependency(u, *i);
if (r < 0)
return r;
}
UNIT_VTABLE(u)->notify_message(u, ucred, tags, fds);
else if (DEBUG_LOGGING) {
- _cleanup_free_ char *buf = NULL, *x = NULL, *y = NULL;
+ _cleanup_free_ char *joined = strv_join(tags, ", ");
+ char buf[CELLESCAPE_DEFAULT_LENGTH];
- buf = strv_join(tags, ", ");
- if (buf)
- x = ellipsize(buf, 20, 90);
- if (x)
- y = cescape(x);
-
- log_unit_debug(u, "Got notification message \"%s\", ignoring.", strnull(y));
+ log_unit_debug(u, "Got notification message from unexpected unit type, ignoring: %s",
+ joined ? cellescape(buf, sizeof(buf), joined) : "(null)");
}
}
static int manager_dispatch_notify_fd(sd_event_source *source, int fd, uint32_t revents, void *userdata) {
-
- _cleanup_fdset_free_ FDSet *fds = NULL;
Manager *m = ASSERT_PTR(userdata);
+ _cleanup_fdset_free_ FDSet *fds = NULL;
char buf[NOTIFY_BUFFER_MAX+1];
struct iovec iovec = {
.iov_base = buf,
const char *target;
JobMode mode;
} target_table[] = {
- [0] = { SPECIAL_DEFAULT_TARGET, JOB_ISOLATE },
- [1] = { SPECIAL_RESCUE_TARGET, JOB_ISOLATE },
- [2] = { SPECIAL_EMERGENCY_TARGET, JOB_ISOLATE },
+ [0] = { SPECIAL_DEFAULT_TARGET, JOB_ISOLATE },
+ [1] = { SPECIAL_RESCUE_TARGET, JOB_ISOLATE },
+ [2] = { SPECIAL_EMERGENCY_TARGET, JOB_ISOLATE },
[3] = { SPECIAL_HALT_TARGET, JOB_REPLACE_IRREVERSIBLY },
[4] = { SPECIAL_POWEROFF_TARGET, JOB_REPLACE_IRREVERSIBLY },
[5] = { SPECIAL_REBOOT_TARGET, JOB_REPLACE_IRREVERSIBLY },
if (MANAGER_IS_TEST_RUN(m))
return;
- if (MANAGER_IS_SYSTEM(m) && detect_container() <= 0) {
+ if (MANAGER_IS_SYSTEM(m) && dual_timestamp_is_set(&m->timestamps[MANAGER_TIMESTAMP_SOFTREBOOT_START])) {
+ /* The soft-reboot case, where we only report data for the last reboot */
+ firmware_usec = loader_usec = initrd_usec = kernel_usec = 0;
+ total_usec = userspace_usec = usec_sub_unsigned(m->timestamps[MANAGER_TIMESTAMP_FINISH].monotonic, m->timestamps[MANAGER_TIMESTAMP_SOFTREBOOT_START].monotonic);
+
+ log_struct(LOG_INFO,
+ "MESSAGE_ID=" SD_MESSAGE_STARTUP_FINISHED_STR,
+ "USERSPACE_USEC="USEC_FMT, userspace_usec,
+ LOG_MESSAGE("Soft-reboot finished in %s.",
+ FORMAT_TIMESPAN(total_usec, USEC_PER_MSEC)));
+ } else if (MANAGER_IS_SYSTEM(m) && detect_container() <= 0) {
char buf[FORMAT_TIMESPAN_MAX + STRLEN(" (firmware) + ") + FORMAT_TIMESPAN_MAX + STRLEN(" (loader) + ")]
= {};
char *p = buf;
_cleanup_strv_free_ char **paths = NULL;
void* args[] = {
[STDOUT_GENERATE] = &tmp,
- [STDOUT_COLLECT] = &tmp,
- [STDOUT_CONSUME] = &m->transient_environment,
+ [STDOUT_COLLECT] = &tmp,
+ [STDOUT_CONSUME] = &m->transient_environment,
};
int r;
/* Taint systemd if we the UID range assigned to this environment doesn't at least cover 0…65534,
* i.e. from root to nobody. */
- r = uid_range_load_userns(&p, path);
+ r = uid_range_load_userns(path, UID_RANGE_USERNS_INSIDE, &p);
if (ERRNO_IS_NEG_NOT_SUPPORTED(r))
return false;
if (r < 0)
[MANAGER_TIMESTAMP_INITRD] = "initrd",
[MANAGER_TIMESTAMP_USERSPACE] = "userspace",
[MANAGER_TIMESTAMP_FINISH] = "finish",
+ [MANAGER_TIMESTAMP_SOFTREBOOT_START] = "softreboot-start",
[MANAGER_TIMESTAMP_SECURITY_START] = "security-start",
[MANAGER_TIMESTAMP_SECURITY_FINISH] = "security-finish",
[MANAGER_TIMESTAMP_GENERATORS_START] = "generators-start",
MANAGER_TIMESTAMP_INITRD_GENERATORS_FINISH,
MANAGER_TIMESTAMP_INITRD_UNITS_LOAD_START,
MANAGER_TIMESTAMP_INITRD_UNITS_LOAD_FINISH,
+
+ MANAGER_TIMESTAMP_SOFTREBOOT_START,
+
_MANAGER_TIMESTAMP_MAX,
_MANAGER_TIMESTAMP_INVALID = -EINVAL,
} ManagerTimestamp;
/* Reference to RestrictFileSystems= BPF program */
struct restrict_fs_bpf *restrict_fs;
- /* Allow users to configure a rate limit for Reload() operations */
- RateLimit reload_ratelimit;
+ /* Allow users to configure a rate limit for Reload()/Reexecute() operations */
+ RateLimit reload_reexec_ratelimit;
/* Dump*() are slow, so always rate limit them to 10 per 10 minutes */
RateLimit dump_ratelimit;
/* Pin the systemd-executor binary, so that it never changes until re-exec, ensuring we don't have
* serialization/deserialization compatibility issues during upgrades. */
int executor_fd;
+
+ unsigned soft_reboots_count;
};
static inline usec_t manager_default_timeout_abort_usec(Manager *m) {
libaudit,
libblkid,
libdl,
- libkmod,
+ libkmod_cflags,
libm,
libmount,
libpam,
#define RETRY_UMOUNT_MAX 32
static const UnitActiveState state_translation_table[_MOUNT_STATE_MAX] = {
- [MOUNT_DEAD] = UNIT_INACTIVE,
- [MOUNT_MOUNTING] = UNIT_ACTIVATING,
- [MOUNT_MOUNTING_DONE] = UNIT_ACTIVATING,
- [MOUNT_MOUNTED] = UNIT_ACTIVE,
- [MOUNT_REMOUNTING] = UNIT_RELOADING,
- [MOUNT_UNMOUNTING] = UNIT_DEACTIVATING,
+ [MOUNT_DEAD] = UNIT_INACTIVE,
+ [MOUNT_MOUNTING] = UNIT_ACTIVATING,
+ [MOUNT_MOUNTING_DONE] = UNIT_ACTIVATING,
+ [MOUNT_MOUNTED] = UNIT_ACTIVE,
+ [MOUNT_REMOUNTING] = UNIT_RELOADING,
+ [MOUNT_UNMOUNTING] = UNIT_DEACTIVATING,
[MOUNT_REMOUNTING_SIGTERM] = UNIT_RELOADING,
[MOUNT_REMOUNTING_SIGKILL] = UNIT_RELOADING,
[MOUNT_UNMOUNTING_SIGTERM] = UNIT_DEACTIVATING,
[MOUNT_UNMOUNTING_SIGKILL] = UNIT_DEACTIVATING,
- [MOUNT_FAILED] = UNIT_FAILED,
- [MOUNT_CLEANING] = UNIT_MAINTENANCE,
+ [MOUNT_FAILED] = UNIT_FAILED,
+ [MOUNT_CLEANING] = UNIT_MAINTENANCE,
};
static int mount_dispatch_timer(sd_event_source *source, usec_t usec, void *userdata);
static int mount_dispatch_io(sd_event_source *source, int fd, uint32_t revents, void *userdata);
-static void mount_enter_dead(Mount *m, MountResult f);
+static void mount_enter_dead(Mount *m, MountResult f, bool flush_result);
static void mount_enter_mounted(Mount *m, MountResult f);
static void mount_cycle_clear(Mount *m);
static int mount_process_proc_self_mountinfo(Manager *m);
break;
case MOUNT_MOUNTED:
assert(!pidref_is_set(&m->control_pid));
- mount_enter_dead(m, MOUNT_SUCCESS);
+ mount_enter_dead(m, MOUNT_SUCCESS, /* flush_result = */ false);
break;
default:
break;
return 0;
}
-static void mount_enter_dead(Mount *m, MountResult f) {
+static void mount_enter_dead(Mount *m, MountResult f, bool flush_result) {
assert(m);
- if (m->result == MOUNT_SUCCESS)
+ if (m->result == MOUNT_SUCCESS || flush_result)
m->result = f;
unit_log_result(UNIT(m), m->result == MOUNT_SUCCESS, mount_result_to_string(m->result));
mount_set_state(m, MOUNT_MOUNTED);
}
-static void mount_enter_dead_or_mounted(Mount *m, MountResult f) {
+static void mount_enter_dead_or_mounted(Mount *m, MountResult f, bool flush_result) {
assert(m);
- /* Enter DEAD or MOUNTED state, depending on what the kernel currently says about the mount point. We use this
- * whenever we executed an operation, so that our internal state reflects what the kernel says again, after all
- * ultimately we just mirror the kernel's internal state on this. */
+ /* Enter DEAD or MOUNTED state, depending on what the kernel currently says about the mount point.
+ * We use this whenever we executed an operation, so that our internal state reflects what
+ * the kernel says again, after all ultimately we just mirror the kernel's internal state on this.
+ *
+ * Note that flush_result only applies to mount_enter_dead(), since that's when the result gets
+ * turned into unit end state. */
if (m->from_proc_self_mountinfo)
mount_enter_mounted(m, f);
else
- mount_enter_dead(m, f);
+ mount_enter_dead(m, f, flush_result);
}
static int state_to_kill_operation(MountState state) {
else if (state == MOUNT_UNMOUNTING_SIGTERM && m->kill_context.send_sigkill)
mount_enter_signal(m, MOUNT_UNMOUNTING_SIGKILL, MOUNT_SUCCESS);
else
- mount_enter_dead_or_mounted(m, MOUNT_SUCCESS);
+ mount_enter_dead_or_mounted(m, MOUNT_SUCCESS, /* flush_result = */ false);
return;
fail:
- mount_enter_dead_or_mounted(m, MOUNT_FAILURE_RESOURCES);
+ mount_enter_dead_or_mounted(m, MOUNT_FAILURE_RESOURCES, /* flush_result = */ false);
}
static int mount_set_umount_command(Mount *m, ExecCommand *c) {
return;
fail:
- mount_enter_dead_or_mounted(m, MOUNT_FAILURE_RESOURCES);
+ mount_enter_dead_or_mounted(m, MOUNT_FAILURE_RESOURCES, /* flush_result = */ false);
}
static int mount_set_mount_command(Mount *m, ExecCommand *c, const MountParameters *p) {
return;
fail:
- mount_enter_dead_or_mounted(m, MOUNT_FAILURE_RESOURCES);
+ mount_enter_dead_or_mounted(m, MOUNT_FAILURE_RESOURCES, /* flush_result = */ false);
}
static void mount_set_reload_result(Mount *m, MountResult result) {
fail:
mount_set_reload_result(m, MOUNT_FAILURE_RESOURCES);
- mount_enter_dead_or_mounted(m, MOUNT_SUCCESS);
+ mount_enter_dead_or_mounted(m, MOUNT_SUCCESS, /* flush_result = */ false);
}
static void mount_cycle_clear(Mount *m) {
} else if (streq(key, "control-pid")) {
- pidref_done(&m->control_pid);
- (void) deserialize_pidref(fds, value, &m->control_pid);
+ if (!pidref_is_set(&m->control_pid))
+ (void) deserialize_pidref(fds, value, &m->control_pid);
} else if (streq(key, "control-command")) {
MountExecCommand id;
log_unit_warning(UNIT(m), "Mount process finished, but there is no mount.");
f = MOUNT_FAILURE_PROTOCOL;
}
- mount_enter_dead(m, f);
+ mount_enter_dead(m, f, /* flush_result = */ false);
break;
case MOUNT_MOUNTING_DONE:
case MOUNT_REMOUNTING:
case MOUNT_REMOUNTING_SIGTERM:
case MOUNT_REMOUNTING_SIGKILL:
- mount_enter_dead_or_mounted(m, MOUNT_SUCCESS);
+ mount_enter_dead_or_mounted(m, MOUNT_SUCCESS, /* flush_result = */ false);
break;
case MOUNT_UNMOUNTING:
mount_enter_unmounting(m);
} else {
log_unit_warning(u, "Mount still present after %u attempts to unmount, giving up.", m->n_retry_umount);
- mount_enter_mounted(m, f);
+ mount_enter_mounted(m, MOUNT_FAILURE_PROTOCOL);
}
} else if (f == MOUNT_FAILURE_EXIT_CODE && !m->from_proc_self_mountinfo) {
/* Hmm, umount process spawned by us failed, but the mount disappeared anyway?
* Maybe someone else is trying to unmount at the same time. */
log_unit_notice(u, "Mount disappeared even though umount process failed, continuing.");
- mount_enter_dead(m, MOUNT_SUCCESS);
+ mount_enter_dead(m, MOUNT_SUCCESS, /* flush_result = */ true);
} else
- mount_enter_dead_or_mounted(m, f);
+ /* At this point, either the unmount succeeded or unexpected error occurred. We usually
+ * remember the first error in 'result', but here let's update that forcibly, since
+ * there could previous failed attempts yet we only care about the most recent
+ * attempt. IOW, if we eventually managed to unmount the stuff, don't enter failed
+ * end state. */
+ mount_enter_dead_or_mounted(m, f, /* flush_result = */ true);
break;
case MOUNT_UNMOUNTING_SIGTERM:
case MOUNT_UNMOUNTING_SIGKILL:
- mount_enter_dead_or_mounted(m, f);
+ mount_enter_dead_or_mounted(m, f, /* flush_result = */ false);
break;
case MOUNT_CLEANING:
if (m->clean_result == MOUNT_SUCCESS)
m->clean_result = f;
- mount_enter_dead(m, MOUNT_SUCCESS);
+ mount_enter_dead(m, MOUNT_SUCCESS, /* flush_result = */ false);
break;
default:
mount_enter_signal(m, MOUNT_REMOUNTING_SIGKILL, MOUNT_SUCCESS);
} else {
log_unit_warning(UNIT(m), "Remounting timed out. Skipping SIGKILL. Ignoring.");
- mount_enter_dead_or_mounted(m, MOUNT_SUCCESS);
+ mount_enter_dead_or_mounted(m, MOUNT_SUCCESS, /* flush_result = */ false);
}
break;
mount_set_reload_result(m, MOUNT_FAILURE_TIMEOUT);
log_unit_warning(UNIT(m), "Mount process still around after SIGKILL. Ignoring.");
- mount_enter_dead_or_mounted(m, MOUNT_SUCCESS);
+ mount_enter_dead_or_mounted(m, MOUNT_SUCCESS, /* flush_result = */ false);
break;
case MOUNT_UNMOUNTING:
mount_enter_signal(m, MOUNT_UNMOUNTING_SIGKILL, MOUNT_FAILURE_TIMEOUT);
} else {
log_unit_warning(UNIT(m), "Mount process timed out. Skipping SIGKILL. Ignoring.");
- mount_enter_dead_or_mounted(m, MOUNT_FAILURE_TIMEOUT);
+ mount_enter_dead_or_mounted(m, MOUNT_FAILURE_TIMEOUT, /* flush_result = */ false);
}
break;
case MOUNT_UNMOUNTING_SIGKILL:
log_unit_warning(UNIT(m), "Mount process still around after SIGKILL. Ignoring.");
- mount_enter_dead_or_mounted(m, MOUNT_FAILURE_TIMEOUT);
+ mount_enter_dead_or_mounted(m, MOUNT_FAILURE_TIMEOUT, /* flush_result = */ false);
break;
case MOUNT_CLEANING:
switch (mount->state) {
case MOUNT_MOUNTED:
- /* This has just been unmounted by somebody else, follow the state change. */
- mount_enter_dead(mount, MOUNT_SUCCESS);
+ /* This has just been unmounted by somebody else, follow the state change.
+ * Also explicitly override the result (see the comment in mount_sigchld_event()),
+ * but more aggressively here since the state change is extrinsic. */
+ mount_cycle_clear(mount);
+ mount_enter_dead(mount, MOUNT_SUCCESS, /* flush_result = */ true);
break;
case MOUNT_MOUNTING_DONE:
r = unit_test_start_limit(u);
if (r < 0) {
- mount_enter_dead(m, MOUNT_FAILURE_START_LIMIT_HIT);
+ mount_enter_dead(m, MOUNT_FAILURE_START_LIMIT_HIT, /* flush_result = */ false);
return r;
}
_cleanup_(dissected_image_unrefp) DissectedImage *dissected_image = NULL;
_cleanup_strv_free_ char **hierarchies = NULL;
_cleanup_(mount_list_done) MountList ml = {};
+ _cleanup_close_ int userns_fd = -EBADF;
bool require_prefix = false;
const char *root;
DissectImageFlags dissect_image_flags =
SET_FLAG(dissect_image_flags, DISSECT_IMAGE_NO_PARTITION_TABLE, p->verity && p->verity->data_path);
- r = loop_device_make_by_path(
- p->root_image,
- FLAGS_SET(dissect_image_flags, DISSECT_IMAGE_DEVICE_READ_ONLY) ? O_RDONLY : -1 /* < 0 means writable if possible, read-only as fallback */,
- /* sector_size= */ UINT32_MAX,
- FLAGS_SET(dissect_image_flags, DISSECT_IMAGE_NO_PARTITION_TABLE) ? 0 : LO_FLAGS_PARTSCAN,
- LOCK_SH,
- &loop_device);
- if (r < 0)
- return log_debug_errno(r, "Failed to create loop device for root image: %m");
-
- r = dissect_loop_device(
- loop_device,
- p->verity,
- p->root_image_options,
- p->root_image_policy,
- dissect_image_flags,
- &dissected_image);
- if (r < 0)
- return log_debug_errno(r, "Failed to dissect image: %m");
+ if (p->runtime_scope == RUNTIME_SCOPE_SYSTEM) {
+ /* In system mode we mount directly */
- r = dissected_image_load_verity_sig_partition(
- dissected_image,
- loop_device->fd,
- p->verity);
- if (r < 0)
- return r;
+ r = loop_device_make_by_path(
+ p->root_image,
+ FLAGS_SET(dissect_image_flags, DISSECT_IMAGE_DEVICE_READ_ONLY) ? O_RDONLY : -1 /* < 0 means writable if possible, read-only as fallback */,
+ /* sector_size= */ UINT32_MAX,
+ FLAGS_SET(dissect_image_flags, DISSECT_IMAGE_NO_PARTITION_TABLE) ? 0 : LO_FLAGS_PARTSCAN,
+ LOCK_SH,
+ &loop_device);
+ if (r < 0)
+ return log_debug_errno(r, "Failed to create loop device for root image: %m");
+
+ r = dissect_loop_device(
+ loop_device,
+ p->verity,
+ p->root_image_options,
+ p->root_image_policy,
+ dissect_image_flags,
+ &dissected_image);
+ if (r < 0)
+ return log_debug_errno(r, "Failed to dissect image: %m");
- r = dissected_image_decrypt(
- dissected_image,
- NULL,
- p->verity,
- dissect_image_flags);
- if (r < 0)
- return log_debug_errno(r, "Failed to decrypt dissected image: %m");
+ r = dissected_image_load_verity_sig_partition(
+ dissected_image,
+ loop_device->fd,
+ p->verity);
+ if (r < 0)
+ return r;
+
+ r = dissected_image_decrypt(
+ dissected_image,
+ NULL,
+ p->verity,
+ dissect_image_flags);
+ if (r < 0)
+ return log_debug_errno(r, "Failed to decrypt dissected image: %m");
+ } else {
+ userns_fd = namespace_open_by_type(NAMESPACE_USER);
+ if (userns_fd < 0)
+ return log_debug_errno(userns_fd, "Failed to open our own user namespace: %m");
+
+ r = mountfsd_mount_image(
+ p->root_image,
+ userns_fd,
+ p->root_image_policy,
+ dissect_image_flags,
+ &dissected_image);
+ if (r < 0)
+ return r;
+ }
}
if (p->root_directory)
root,
/* uid_shift= */ UID_INVALID,
/* uid_range= */ UID_INVALID,
- /* userns_fd= */ -EBADF,
+ userns_fd,
dissect_image_flags);
if (r < 0)
return log_debug_errno(r, "Failed to mount root image: %m");
/* Now release the block device lock, so that udevd is free to call BLKRRPART on the device
* if it likes. */
- r = loop_device_flock(loop_device, LOCK_UN);
- if (r < 0)
- return log_debug_errno(r, "Failed to release lock on loopback block device: %m");
+ if (loop_device) {
+ r = loop_device_flock(loop_device, LOCK_UN);
+ if (r < 0)
+ return log_debug_errno(r, "Failed to release lock on loopback block device: %m");
+ }
r = dissected_image_relinquish(dissected_image);
if (r < 0)
#include "user-util.h"
static const UnitActiveState state_translation_table[_SCOPE_STATE_MAX] = {
- [SCOPE_DEAD] = UNIT_INACTIVE,
- [SCOPE_START_CHOWN] = UNIT_ACTIVATING,
- [SCOPE_RUNNING] = UNIT_ACTIVE,
- [SCOPE_ABANDONED] = UNIT_ACTIVE,
+ [SCOPE_DEAD] = UNIT_INACTIVE,
+ [SCOPE_START_CHOWN] = UNIT_ACTIVATING,
+ [SCOPE_RUNNING] = UNIT_ACTIVE,
+ [SCOPE_ABANDONED] = UNIT_ACTIVE,
[SCOPE_STOP_SIGTERM] = UNIT_DEACTIVATING,
[SCOPE_STOP_SIGKILL] = UNIT_DEACTIVATING,
- [SCOPE_FAILED] = UNIT_FAILED,
+ [SCOPE_FAILED] = UNIT_FAILED,
};
static int scope_dispatch_timer(sd_event_source *source, usec_t usec, void *userdata);
}
if (state != old_state)
- log_debug("%s changed %s -> %s", UNIT(s)->id, scope_state_to_string(old_state), scope_state_to_string(state));
+ log_unit_debug(UNIT(s), "Changed %s -> %s",
+ scope_state_to_string(old_state), scope_state_to_string(state));
unit_notify(UNIT(s), state_translation_table[old_state], state_translation_table[state], /* reload_success = */ true);
}
} else if (streq(key, "pids")) {
_cleanup_(pidref_done) PidRef pidref = PIDREF_NULL;
+ /* We don't check if we already received the pid before here because unit_watch_pidref()
+ * does this check internally and discards the new pidref if we already received it before. */
if (deserialize_pidref(fds, value, &pidref) >= 0) {
r = unit_watch_pidref(u, &pidref, /* exclusive= */ false);
if (r < 0)
#define service_spawn(...) service_spawn_internal(__func__, __VA_ARGS__)
static const UnitActiveState state_translation_table[_SERVICE_STATE_MAX] = {
- [SERVICE_DEAD] = UNIT_INACTIVE,
- [SERVICE_CONDITION] = UNIT_ACTIVATING,
- [SERVICE_START_PRE] = UNIT_ACTIVATING,
- [SERVICE_START] = UNIT_ACTIVATING,
- [SERVICE_START_POST] = UNIT_ACTIVATING,
- [SERVICE_RUNNING] = UNIT_ACTIVE,
- [SERVICE_EXITED] = UNIT_ACTIVE,
- [SERVICE_RELOAD] = UNIT_RELOADING,
- [SERVICE_RELOAD_SIGNAL] = UNIT_RELOADING,
- [SERVICE_RELOAD_NOTIFY] = UNIT_RELOADING,
- [SERVICE_STOP] = UNIT_DEACTIVATING,
- [SERVICE_STOP_WATCHDOG] = UNIT_DEACTIVATING,
- [SERVICE_STOP_SIGTERM] = UNIT_DEACTIVATING,
- [SERVICE_STOP_SIGKILL] = UNIT_DEACTIVATING,
- [SERVICE_STOP_POST] = UNIT_DEACTIVATING,
- [SERVICE_FINAL_WATCHDOG] = UNIT_DEACTIVATING,
- [SERVICE_FINAL_SIGTERM] = UNIT_DEACTIVATING,
- [SERVICE_FINAL_SIGKILL] = UNIT_DEACTIVATING,
- [SERVICE_FAILED] = UNIT_FAILED,
- [SERVICE_DEAD_BEFORE_AUTO_RESTART] = UNIT_INACTIVE,
+ [SERVICE_DEAD] = UNIT_INACTIVE,
+ [SERVICE_CONDITION] = UNIT_ACTIVATING,
+ [SERVICE_START_PRE] = UNIT_ACTIVATING,
+ [SERVICE_START] = UNIT_ACTIVATING,
+ [SERVICE_START_POST] = UNIT_ACTIVATING,
+ [SERVICE_RUNNING] = UNIT_ACTIVE,
+ [SERVICE_EXITED] = UNIT_ACTIVE,
+ [SERVICE_RELOAD] = UNIT_RELOADING,
+ [SERVICE_RELOAD_SIGNAL] = UNIT_RELOADING,
+ [SERVICE_RELOAD_NOTIFY] = UNIT_RELOADING,
+ [SERVICE_STOP] = UNIT_DEACTIVATING,
+ [SERVICE_STOP_WATCHDOG] = UNIT_DEACTIVATING,
+ [SERVICE_STOP_SIGTERM] = UNIT_DEACTIVATING,
+ [SERVICE_STOP_SIGKILL] = UNIT_DEACTIVATING,
+ [SERVICE_STOP_POST] = UNIT_DEACTIVATING,
+ [SERVICE_FINAL_WATCHDOG] = UNIT_DEACTIVATING,
+ [SERVICE_FINAL_SIGTERM] = UNIT_DEACTIVATING,
+ [SERVICE_FINAL_SIGKILL] = UNIT_DEACTIVATING,
+ [SERVICE_FAILED] = UNIT_FAILED,
+ [SERVICE_DEAD_BEFORE_AUTO_RESTART] = UNIT_INACTIVE,
[SERVICE_FAILED_BEFORE_AUTO_RESTART] = UNIT_FAILED,
- [SERVICE_DEAD_RESOURCES_PINNED] = UNIT_INACTIVE,
- [SERVICE_AUTO_RESTART] = UNIT_ACTIVATING,
- [SERVICE_AUTO_RESTART_QUEUED] = UNIT_ACTIVATING,
- [SERVICE_CLEANING] = UNIT_MAINTENANCE,
+ [SERVICE_DEAD_RESOURCES_PINNED] = UNIT_INACTIVE,
+ [SERVICE_AUTO_RESTART] = UNIT_ACTIVATING,
+ [SERVICE_AUTO_RESTART_QUEUED] = UNIT_ACTIVATING,
+ [SERVICE_CLEANING] = UNIT_MAINTENANCE,
};
/* For Type=idle we never want to delay any other jobs, hence we
* consider idle jobs active as soon as we start working on them */
static const UnitActiveState state_translation_table_idle[_SERVICE_STATE_MAX] = {
- [SERVICE_DEAD] = UNIT_INACTIVE,
- [SERVICE_CONDITION] = UNIT_ACTIVE,
- [SERVICE_START_PRE] = UNIT_ACTIVE,
- [SERVICE_START] = UNIT_ACTIVE,
- [SERVICE_START_POST] = UNIT_ACTIVE,
- [SERVICE_RUNNING] = UNIT_ACTIVE,
- [SERVICE_EXITED] = UNIT_ACTIVE,
- [SERVICE_RELOAD] = UNIT_RELOADING,
- [SERVICE_RELOAD_SIGNAL] = UNIT_RELOADING,
- [SERVICE_RELOAD_NOTIFY] = UNIT_RELOADING,
- [SERVICE_STOP] = UNIT_DEACTIVATING,
- [SERVICE_STOP_WATCHDOG] = UNIT_DEACTIVATING,
- [SERVICE_STOP_SIGTERM] = UNIT_DEACTIVATING,
- [SERVICE_STOP_SIGKILL] = UNIT_DEACTIVATING,
- [SERVICE_STOP_POST] = UNIT_DEACTIVATING,
- [SERVICE_FINAL_WATCHDOG] = UNIT_DEACTIVATING,
- [SERVICE_FINAL_SIGTERM] = UNIT_DEACTIVATING,
- [SERVICE_FINAL_SIGKILL] = UNIT_DEACTIVATING,
- [SERVICE_FAILED] = UNIT_FAILED,
- [SERVICE_DEAD_BEFORE_AUTO_RESTART] = UNIT_INACTIVE,
+ [SERVICE_DEAD] = UNIT_INACTIVE,
+ [SERVICE_CONDITION] = UNIT_ACTIVE,
+ [SERVICE_START_PRE] = UNIT_ACTIVE,
+ [SERVICE_START] = UNIT_ACTIVE,
+ [SERVICE_START_POST] = UNIT_ACTIVE,
+ [SERVICE_RUNNING] = UNIT_ACTIVE,
+ [SERVICE_EXITED] = UNIT_ACTIVE,
+ [SERVICE_RELOAD] = UNIT_RELOADING,
+ [SERVICE_RELOAD_SIGNAL] = UNIT_RELOADING,
+ [SERVICE_RELOAD_NOTIFY] = UNIT_RELOADING,
+ [SERVICE_STOP] = UNIT_DEACTIVATING,
+ [SERVICE_STOP_WATCHDOG] = UNIT_DEACTIVATING,
+ [SERVICE_STOP_SIGTERM] = UNIT_DEACTIVATING,
+ [SERVICE_STOP_SIGKILL] = UNIT_DEACTIVATING,
+ [SERVICE_STOP_POST] = UNIT_DEACTIVATING,
+ [SERVICE_FINAL_WATCHDOG] = UNIT_DEACTIVATING,
+ [SERVICE_FINAL_SIGTERM] = UNIT_DEACTIVATING,
+ [SERVICE_FINAL_SIGKILL] = UNIT_DEACTIVATING,
+ [SERVICE_FAILED] = UNIT_FAILED,
+ [SERVICE_DEAD_BEFORE_AUTO_RESTART] = UNIT_INACTIVE,
[SERVICE_FAILED_BEFORE_AUTO_RESTART] = UNIT_FAILED,
- [SERVICE_DEAD_RESOURCES_PINNED] = UNIT_INACTIVE,
- [SERVICE_AUTO_RESTART] = UNIT_ACTIVATING,
- [SERVICE_AUTO_RESTART_QUEUED] = UNIT_ACTIVATING,
- [SERVICE_CLEANING] = UNIT_MAINTENANCE,
+ [SERVICE_DEAD_RESOURCES_PINNED] = UNIT_INACTIVE,
+ [SERVICE_AUTO_RESTART] = UNIT_ACTIVATING,
+ [SERVICE_AUTO_RESTART_QUEUED] = UNIT_ACTIVATING,
+ [SERVICE_CLEANING] = UNIT_MAINTENANCE,
};
static int service_dispatch_inotify_io(sd_event_source *source, int fd, uint32_t events, void *userdata);
s->pid_file_pathspec = mfree(s->pid_file_pathspec);
}
-static int service_set_main_pidref(Service *s, PidRef *pidref) {
+static int service_set_main_pidref(Service *s, PidRef pidref_consume) {
+ _cleanup_(pidref_done) PidRef pidref = pidref_consume;
int r;
assert(s);
- /* Takes ownership of the specified pidref on success, but not on failure. */
+ /* Takes ownership of the specified pidref on both success and failure. */
- if (!pidref_is_set(pidref))
+ if (!pidref_is_set(&pidref))
return -ESRCH;
- if (pidref->pid <= 1)
+ if (pidref.pid <= 1)
return -EINVAL;
- if (pidref_is_self(pidref))
+ if (pidref_is_self(&pidref))
return -EINVAL;
- if (pidref_equal(&s->main_pid, pidref) && s->main_pid_known) {
- pidref_done(pidref);
+ if (s->main_pid_known && pidref_equal(&s->main_pid, &pidref))
return 0;
- }
- if (!pidref_equal(&s->main_pid, pidref)) {
+ if (!pidref_equal(&s->main_pid, &pidref)) {
service_unwatch_main_pid(s);
- exec_status_start(&s->main_exec_status, pidref->pid);
+ exec_status_start(&s->main_exec_status, pidref.pid);
}
- s->main_pid = TAKE_PIDREF(*pidref);
+ s->main_pid = TAKE_PIDREF(pidref);
s->main_pid_known = true;
r = pidref_is_my_child(&s->main_pid);
if (r < 0)
log_unit_warning_errno(UNIT(s), r, "Can't determine if process "PID_FMT" is our child, assuming it is not: %m", s->main_pid.pid);
- else if (r == 0)
+ else if (r == 0) // FIXME: Supervise through pidfd here
log_unit_warning(UNIT(s), "Supervising process "PID_FMT" which is not our child. We'll most likely not notice when it exits.", s->main_pid.pid);
-
s->main_pid_alien = r <= 0;
+
return 0;
}
} else
log_unit_debug(UNIT(s), "Main PID loaded: "PID_FMT, pidref.pid);
- r = service_set_main_pidref(s, &pidref);
+ r = service_set_main_pidref(s, TAKE_PIDREF(pidref));
if (r < 0)
return r;
return;
log_unit_debug(UNIT(s), "Main PID guessed: "PID_FMT, pid.pid);
- if (service_set_main_pidref(s, &pid) < 0)
+ if (service_set_main_pidref(s, TAKE_PIDREF(pid)) < 0)
return;
r = unit_watch_pidref(UNIT(s), &s->main_pid, /* exclusive= */ false);
UNIT_FOREACH_DEPENDENCY(u, UNIT(s), UNIT_ATOM_TRIGGERED_BY) {
_cleanup_free_ int *cfds = NULL;
- Socket *sock;
int cn_fds;
+ Socket *sock;
sock = SOCKET(u);
if (!sock)
if (!rfds) {
rfds = TAKE_PTR(cfds);
rn_socket_fds = cn_fds;
- } else {
- int *t;
-
- t = reallocarray(rfds, rn_socket_fds + cn_fds, sizeof(int));
- if (!t)
- return -ENOMEM;
-
- memcpy(t + rn_socket_fds, cfds, cn_fds * sizeof(int));
-
- rfds = t;
- rn_socket_fds += cn_fds;
- }
+ } else if (!GREEDY_REALLOC_APPEND(rfds, rn_socket_fds, cfds, cn_fds))
+ return -ENOMEM;
r = strv_extend_n(&rfd_names, socket_fdname(sock), cn_fds);
if (r < 0)
} else if (s->result == SERVICE_SKIP_CONDITION) {
unit_log_skip(UNIT(s), service_result_to_string(s->result));
end_state = service_determine_dead_state(s);
- restart_state = SERVICE_DEAD_BEFORE_AUTO_RESTART;
+ restart_state = _SERVICE_STATE_INVALID; /* Never restart if skipped due to condition failure */
} else {
unit_log_failure(UNIT(s), service_result_to_string(s->result));
end_state = SERVICE_FAILED;
if (allow_restart) {
usec_t restart_usec_next;
+ assert(restart_state >= 0 && restart_state < _SERVICE_STATE_MAX);
+
/* We make two state changes here: one that maps to the high-level UNIT_INACTIVE/UNIT_FAILED
* state (i.e. a state indicating deactivation), and then one that that maps to the
* high-level UNIT_STARTING state (i.e. a state indicating activation). We do this so that
/* For simple services we immediately start
* the START_POST binaries. */
- (void) service_set_main_pidref(s, &pidref);
+ (void) service_set_main_pidref(s, TAKE_PIDREF(pidref));
service_enter_start_post(s);
} else if (s->type == SERVICE_FORKING) {
/* For D-Bus services we know the main pid right away, but wait for the bus name to appear on the
* bus. 'notify' and 'exec' services are similar. */
- (void) service_set_main_pidref(s, &pidref);
+ (void) service_set_main_pidref(s, TAKE_PIDREF(pidref));
service_set_state(s, SERVICE_START);
} else
assert_not_reached();
service_exec_flags(s->control_command_id, /* cred_flag = */ 0),
s->timeout_start_usec,
&s->control_pid);
-
if (r < 0) {
log_unit_warning_errno(UNIT(s), r, "Failed to spawn 'exec-condition' task: %m");
goto fail;
return;
}
- (void) service_set_main_pidref(s, &pidref);
+ (void) service_set_main_pidref(s, TAKE_PIDREF(pidref));
}
static int service_start(Unit *u) {
s->reload_result = f;
} else if (streq(key, "control-pid")) {
- pidref_done(&s->control_pid);
- (void) deserialize_pidref(fds, value, &s->control_pid);
+ if (!pidref_is_set(&s->control_pid))
+ (void) deserialize_pidref(fds, value, &s->control_pid);
} else if (streq(key, "main-pid")) {
- _cleanup_(pidref_done) PidRef pidref = PIDREF_NULL;
+ PidRef pidref;
- if (deserialize_pidref(fds, value, &pidref) >= 0)
- (void) service_set_main_pidref(s, &pidref);
+ if (!pidref_is_set(&s->main_pid) && deserialize_pidref(fds, value, &pidref) >= 0)
+ (void) service_set_main_pidref(s, pidref);
} else if (streq(key, "main-pid-known")) {
int b;
log_unit_debug(u, "New main PID "PID_FMT" does not belong to service, but we'll accept it as the request to change it came from a privileged process.", new_main_pid.pid);
r = 1;
} else
- log_unit_debug(u, "New main PID "PID_FMT" does not belong to service, refusing.", new_main_pid.pid);
+ log_unit_warning(u, "New main PID "PID_FMT" does not belong to service, refusing.", new_main_pid.pid);
}
if (r > 0) {
- (void) service_set_main_pidref(s, &new_main_pid);
+ (void) service_set_main_pidref(s, TAKE_PIDREF(new_main_pid));
r = unit_watch_pidref(UNIT(s), &s->main_pid, /* exclusive= */ false);
if (r < 0)
log_unit_debug(UNIT(s), "D-Bus name %s is now owned by process " PID_FMT, s->bus_name, pidref.pid);
- (void) service_set_main_pidref(s, &pidref);
+ (void) service_set_main_pidref(s, TAKE_PIDREF(pidref));
(void) unit_watch_pidref(UNIT(s), &s->main_pid, /* exclusive= */ false);
return 1;
}
Service *s,
int fd,
Socket *sock,
- SocketPeer *peer,
+ SocketPeer *peer, /* reference to object is donated to us on success */
bool selinux_context_net) {
_cleanup_free_ char *peer_text = NULL;
static const char* const service_restart_mode_table[_SERVICE_RESTART_MODE_MAX] = {
[SERVICE_RESTART_MODE_NORMAL] = "normal",
- [SERVICE_RESTART_MODE_DIRECT] = "direct",
+ [SERVICE_RESTART_MODE_DIRECT] = "direct",
};
DEFINE_STRING_TABLE_LOOKUP(service_restart_mode, ServiceRestartMode);
int status_vprintf(const char *status, ShowStatusFlags flags, const char *format, va_list ap) {
static const char status_indent[] = " "; /* "[" STATUS "] " */
+ static bool prev_ephemeral = false;
+
_cleanup_free_ char *s = NULL;
_cleanup_close_ int fd = -EBADF;
struct iovec iovec[7] = {};
int n = 0;
- static bool prev_ephemeral;
assert(format);
if (c <= 0)
c = 80;
- sl = status ? sizeof(status_indent)-1 : 0;
+ sl = status ? strlen(status_indent) : 0;
emax = c - sl - 1;
if (emax < 3)
#include "unit.h"
static const UnitActiveState state_translation_table[_SLICE_STATE_MAX] = {
- [SLICE_DEAD] = UNIT_INACTIVE,
- [SLICE_ACTIVE] = UNIT_ACTIVE
+ [SLICE_DEAD] = UNIT_INACTIVE,
+ [SLICE_ACTIVE] = UNIT_ACTIVE,
};
static void slice_init(Unit *u) {
u->ignore_on_isolate = true;
}
-static void slice_set_state(Slice *t, SliceState state) {
+static void slice_set_state(Slice *s, SliceState state) {
SliceState old_state;
- assert(t);
+ assert(s);
- if (t->state != state)
- bus_unit_send_pending_change_signal(UNIT(t), false);
+ if (s->state != state)
+ bus_unit_send_pending_change_signal(UNIT(s), false);
- old_state = t->state;
- t->state = state;
+ old_state = s->state;
+ s->state = state;
if (state != old_state)
- log_debug("%s changed %s -> %s",
- UNIT(t)->id,
- slice_state_to_string(old_state),
- slice_state_to_string(state));
+ log_unit_debug(UNIT(s), "Changed %s -> %s",
+ slice_state_to_string(old_state), slice_state_to_string(state));
- unit_notify(UNIT(t), state_translation_table[old_state], state_translation_table[state], /* reload_success = */ true);
+ unit_notify(UNIT(s), state_translation_table[old_state], state_translation_table[state], /* reload_success = */ true);
}
static int slice_add_parent_slice(Slice *s) {
};
static const UnitActiveState state_translation_table[_SOCKET_STATE_MAX] = {
- [SOCKET_DEAD] = UNIT_INACTIVE,
- [SOCKET_START_PRE] = UNIT_ACTIVATING,
- [SOCKET_START_CHOWN] = UNIT_ACTIVATING,
- [SOCKET_START_POST] = UNIT_ACTIVATING,
- [SOCKET_LISTENING] = UNIT_ACTIVE,
- [SOCKET_RUNNING] = UNIT_ACTIVE,
- [SOCKET_STOP_PRE] = UNIT_DEACTIVATING,
+ [SOCKET_DEAD] = UNIT_INACTIVE,
+ [SOCKET_START_PRE] = UNIT_ACTIVATING,
+ [SOCKET_START_CHOWN] = UNIT_ACTIVATING,
+ [SOCKET_START_POST] = UNIT_ACTIVATING,
+ [SOCKET_LISTENING] = UNIT_ACTIVE,
+ [SOCKET_RUNNING] = UNIT_ACTIVE,
+ [SOCKET_STOP_PRE] = UNIT_DEACTIVATING,
[SOCKET_STOP_PRE_SIGTERM] = UNIT_DEACTIVATING,
[SOCKET_STOP_PRE_SIGKILL] = UNIT_DEACTIVATING,
- [SOCKET_STOP_POST] = UNIT_DEACTIVATING,
- [SOCKET_FINAL_SIGTERM] = UNIT_DEACTIVATING,
- [SOCKET_FINAL_SIGKILL] = UNIT_DEACTIVATING,
- [SOCKET_FAILED] = UNIT_FAILED,
- [SOCKET_CLEANING] = UNIT_MAINTENANCE,
+ [SOCKET_STOP_POST] = UNIT_DEACTIVATING,
+ [SOCKET_FINAL_SIGTERM] = UNIT_DEACTIVATING,
+ [SOCKET_FINAL_SIGKILL] = UNIT_DEACTIVATING,
+ [SOCKET_FAILED] = UNIT_FAILED,
+ [SOCKET_CLEANING] = UNIT_MAINTENANCE,
};
static int socket_dispatch_io(sd_event_source *source, int fd, uint32_t revents, void *userdata);
else
s->n_refused += k;
} else if (streq(key, "control-pid")) {
- pidref_done(&s->control_pid);
- (void) deserialize_pidref(fds, value, &s->control_pid);
+
+ if (!pidref_is_set(&s->control_pid))
+ (void) deserialize_pidref(fds, value, &s->control_pid);
} else if (streq(key, "control-command")) {
SocketExecCommand id;
[SOCKET_EXEC_START_CHOWN] = "ExecStartChown",
[SOCKET_EXEC_START_POST] = "ExecStartPost",
[SOCKET_EXEC_STOP_PRE] = "ExecStopPre",
- [SOCKET_EXEC_STOP_POST] = "ExecStopPost"
+ [SOCKET_EXEC_STOP_POST] = "ExecStopPost",
};
DEFINE_STRING_TABLE_LOOKUP(socket_exec_command, SocketExecCommand);
[SOCKET_FAILURE_CORE_DUMP] = "core-dump",
[SOCKET_FAILURE_START_LIMIT_HIT] = "start-limit-hit",
[SOCKET_FAILURE_TRIGGER_LIMIT_HIT] = "trigger-limit-hit",
- [SOCKET_FAILURE_SERVICE_START_LIMIT_HIT] = "service-start-limit-hit"
+ [SOCKET_FAILURE_SERVICE_START_LIMIT_HIT] = "service-start-limit-hit",
};
DEFINE_STRING_TABLE_LOOKUP(socket_result, SocketResult);
#include "virt.h"
static const UnitActiveState state_translation_table[_SWAP_STATE_MAX] = {
- [SWAP_DEAD] = UNIT_INACTIVE,
- [SWAP_ACTIVATING] = UNIT_ACTIVATING,
- [SWAP_ACTIVATING_DONE] = UNIT_ACTIVE,
- [SWAP_ACTIVE] = UNIT_ACTIVE,
- [SWAP_DEACTIVATING] = UNIT_DEACTIVATING,
+ [SWAP_DEAD] = UNIT_INACTIVE,
+ [SWAP_ACTIVATING] = UNIT_ACTIVATING,
+ [SWAP_ACTIVATING_DONE] = UNIT_ACTIVE,
+ [SWAP_ACTIVE] = UNIT_ACTIVE,
+ [SWAP_DEACTIVATING] = UNIT_DEACTIVATING,
[SWAP_DEACTIVATING_SIGTERM] = UNIT_DEACTIVATING,
[SWAP_DEACTIVATING_SIGKILL] = UNIT_DEACTIVATING,
- [SWAP_FAILED] = UNIT_FAILED,
- [SWAP_CLEANING] = UNIT_MAINTENANCE,
+ [SWAP_FAILED] = UNIT_FAILED,
+ [SWAP_CLEANING] = UNIT_MAINTENANCE,
};
static int swap_dispatch_timer(sd_event_source *source, usec_t usec, void *userdata);
s->result = f;
} else if (streq(key, "control-pid")) {
- pidref_done(&s->control_pid);
- (void) deserialize_pidref(fds, value, &s->control_pid);
+ if (!pidref_is_set(&s->control_pid))
+ (void) deserialize_pidref(fds, value, &s->control_pid);
} else if (streq(key, "control-command")) {
SwapExecCommand id;
#include "unit.h"
static const UnitActiveState state_translation_table[_TARGET_STATE_MAX] = {
- [TARGET_DEAD] = UNIT_INACTIVE,
- [TARGET_ACTIVE] = UNIT_ACTIVE
+ [TARGET_DEAD] = UNIT_INACTIVE,
+ [TARGET_ACTIVE] = UNIT_ACTIVE,
};
static void target_set_state(Target *t, TargetState state) {
TargetState old_state;
+
assert(t);
if (t->state != state)
t->state = state;
if (state != old_state)
- log_debug("%s changed %s -> %s",
- UNIT(t)->id,
- target_state_to_string(old_state),
- target_state_to_string(state));
+ log_unit_debug(UNIT(t), "Changed %s -> %s",
+ target_state_to_string(old_state), target_state_to_string(state));
unit_notify(UNIT(t), state_translation_table[old_state], state_translation_table[state], /* reload_success = */ true);
}
if (n_others < 0)
return n_others;
- for (int i = 0; i < n_others; i++) {
- r = unit_add_default_target_dependency(others[i], UNIT(t));
+ FOREACH_ARRAY(i, others, n_others) {
+ r = unit_add_default_target_dependency(*i, UNIT(t));
if (r < 0)
return r;
}
#include "virt.h"
static const UnitActiveState state_translation_table[_TIMER_STATE_MAX] = {
- [TIMER_DEAD] = UNIT_INACTIVE,
+ [TIMER_DEAD] = UNIT_INACTIVE,
[TIMER_WAITING] = UNIT_ACTIVE,
[TIMER_RUNNING] = UNIT_ACTIVE,
[TIMER_ELAPSED] = UNIT_ACTIVE,
- [TIMER_FAILED] = UNIT_FAILED
+ [TIMER_FAILED] = UNIT_FAILED,
};
static int timer_dispatch(sd_event_source *s, uint64_t usec, void *userdata);
[TIMER_STARTUP] = "OnStartupSec",
[TIMER_UNIT_ACTIVE] = "OnUnitActiveSec",
[TIMER_UNIT_INACTIVE] = "OnUnitInactiveSec",
- [TIMER_CALENDAR] = "OnCalendar"
+ [TIMER_CALENDAR] = "OnCalendar",
};
DEFINE_STRING_TABLE_LOOKUP(timer_base, TimerBase);
* the graph over 'before' edges in the actual job execution order. We traverse over both unit
* ordering dependencies and we test with job_compare() whether it is the 'before' edge in the job
* execution ordering. */
- for (size_t d = 0; d < ELEMENTSOF(directions); d++) {
+ FOREACH_ARRAY(d, directions, ELEMENTSOF(directions)) {
Unit *u;
- UNIT_FOREACH_DEPENDENCY(u, j->unit, directions[d]) {
+ UNIT_FOREACH_DEPENDENCY(u, j->unit, *d) {
Job *o;
/* Is there a job for this unit? */
}
/* Cut traversing if the job j is not really *before* o. */
- if (job_compare(j, o, directions[d]) >= 0)
+ if (job_compare(j, o, *d) >= 0)
continue;
r = transaction_verify_order_one(tr, o, j, generation, e);
#define NOTICEWORTHY_IP_BYTES (128 * U64_MB)
const UnitVTable * const unit_vtable[_UNIT_TYPE_MAX] = {
- [UNIT_SERVICE] = &service_vtable,
- [UNIT_SOCKET] = &socket_vtable,
- [UNIT_TARGET] = &target_vtable,
- [UNIT_DEVICE] = &device_vtable,
- [UNIT_MOUNT] = &mount_vtable,
+ [UNIT_SERVICE] = &service_vtable,
+ [UNIT_SOCKET] = &socket_vtable,
+ [UNIT_TARGET] = &target_vtable,
+ [UNIT_DEVICE] = &device_vtable,
+ [UNIT_MOUNT] = &mount_vtable,
[UNIT_AUTOMOUNT] = &automount_vtable,
- [UNIT_SWAP] = &swap_vtable,
- [UNIT_TIMER] = &timer_vtable,
- [UNIT_PATH] = &path_vtable,
- [UNIT_SLICE] = &slice_vtable,
- [UNIT_SCOPE] = &scope_vtable,
+ [UNIT_SWAP] = &swap_vtable,
+ [UNIT_TIMER] = &timer_vtable,
+ [UNIT_PATH] = &path_vtable,
+ [UNIT_SLICE] = &slice_vtable,
+ [UNIT_SCOPE] = &scope_vtable,
};
Unit* unit_new(Manager *m, size_t size) {
if (!u->manager->prefix[dt])
continue;
- for (size_t i = 0; i < c->directories[dt].n_items; i++) {
+ FOREACH_ARRAY(i, c->directories[dt].items, c->directories[dt].n_items) {
_cleanup_free_ char *p = NULL;
- p = path_join(u->manager->prefix[dt], c->directories[dt].items[i].path);
+ p = path_join(u->manager->prefix[dt], i->path);
if (!p)
return -ENOMEM;
}
static const char* const collect_mode_table[_COLLECT_MODE_MAX] = {
- [COLLECT_INACTIVE] = "inactive",
+ [COLLECT_INACTIVE] = "inactive",
[COLLECT_INACTIVE_OR_FAILED] = "inactive-or-failed",
};
}
const ActivationDetailsVTable * const activation_details_vtable[_UNIT_TYPE_MAX] = {
- [UNIT_PATH] = &activation_details_path_vtable,
+ [UNIT_PATH] = &activation_details_path_vtable,
[UNIT_TIMER] = &activation_details_timer_vtable,
};
}
static inline bool UNIT_IS_LOAD_COMPLETE(UnitLoadState t) {
- return t >= 0 && t < _UNIT_LOAD_STATE_MAX && t != UNIT_STUB && t != UNIT_MERGED;
+ return t >= 0 && t < _UNIT_LOAD_STATE_MAX && !IN_SET(t, UNIT_STUB, UNIT_MERGED);
}
static inline bool UNIT_IS_LOAD_ERROR(UnitLoadState t) {
if (field)
r = journal_add_match_pair(j, field, match);
else
- r = sd_journal_add_match(j, match, 0);
+ r = sd_journal_add_match(j, match, SIZE_MAX);
if (r < 0)
return log_error_errno(r, "Failed to add match \"%s%s%s\": %m",
strempty(field), field ? "=" : "", match);
static int add_matches(sd_journal *j, char **matches) {
int r;
- r = sd_journal_add_match(j, "MESSAGE_ID=" SD_MESSAGE_COREDUMP_STR, 0);
+ r = sd_journal_add_match(j, "MESSAGE_ID=" SD_MESSAGE_COREDUMP_STR, SIZE_MAX);
if (r < 0)
return log_error_errno(r, "Failed to add match \"%s\": %m", "MESSAGE_ID=" SD_MESSAGE_COREDUMP_STR);
- r = sd_journal_add_match(j, "MESSAGE_ID=" SD_MESSAGE_BACKTRACE_STR, 0);
+ r = sd_journal_add_match(j, "MESSAGE_ID=" SD_MESSAGE_BACKTRACE_STR, SIZE_MAX);
if (r < 0)
return log_error_errno(r, "Failed to add match \"%s\": %m", "MESSAGE_ID=" SD_MESSAGE_BACKTRACE_STR);
static bool arg_quiet = false;
static bool arg_varlink = false;
static uid_t arg_uid = UID_INVALID;
+static bool arg_allow_null = false;
STATIC_DESTRUCTOR_REGISTER(arg_tpm2_public_key, freep);
STATIC_DESTRUCTOR_REGISTER(arg_tpm2_signature, freep);
arg_tpm2_signature,
arg_uid,
&input,
- /* flags= */ 0,
+ arg_allow_null ? CREDENTIAL_ALLOW_NULL : 0,
&plaintext);
if (r < 0)
return r;
" --timestamp=TIME Include specified timestamp in encrypted credential\n"
" --not-after=TIME Include specified invalidation time in encrypted\n"
" credential\n"
- " --with-key=host|tpm2|host+tpm2|tpm2-absent|auto|auto-initrd\n"
+ " --with-key=host|tpm2|host+tpm2|null|auto|auto-initrd\n"
" Which keys to encrypt with\n"
" -H Shortcut for --with-key=host\n"
" -T Shortcut for --with-key=tpm2\n"
" Specify signature for public key PCR policy\n"
" --user Select user-scoped credential encryption\n"
" --uid=UID Select user for scoped credentials\n"
+ " --allow-null Allow decrypting credentials with empty key\n"
" -q --quiet Suppress output for 'has-tpm2' verb\n"
"\nSee the %2$s for details.\n",
program_invocation_short_name,
ARG_NOT_AFTER,
ARG_USER,
ARG_UID,
+ ARG_ALLOW_NULL,
};
static const struct option options[] = {
{ "quiet", no_argument, NULL, 'q' },
{ "user", no_argument, NULL, ARG_USER },
{ "uid", required_argument, NULL, ARG_UID },
+ { "allow-null", no_argument, NULL, ARG_ALLOW_NULL },
{}
};
}
break;
+ case ARG_ALLOW_NULL:
+ arg_allow_null = true;
+ break;
+
case 'q':
arg_quiet = true;
break;
const struct iovec *pubkey,
uint32_t pubkey_pcr_mask,
const char *signature_path,
- const char *pcrlock_path,
const char *pin,
+ const char *pcrlock_path,
uint16_t primary_alg,
const struct iovec *key_data,
const struct iovec *policy_hash,
* - text descriptions prefixed with "%:" or "%keyring:".
* - text description with no prefix.
* - numeric keyring id (ignored in current patch set). */
- if (*val == '@' || *val == '%')
+ if (IN_SET(*val, '@', '%'))
keyring = strndup(val, sep - val);
else
/* add type prefix if missing (crypt_set_keyring_to_link() expects it) */
#include <unistd.h>
#include "alloc-util.h"
+#include "creds-util.h"
#include "dropin.h"
+#include "errno-util.h"
+#include "fd-util.h"
+#include "fileio-label.h"
#include "generator.h"
#include "initrd-util.h"
-#include "mkdir-label.h"
#include "parse-util.h"
#include "path-util.h"
#include "proc-cmdline.h"
+#include "recurse-dir.h"
#include "special.h"
#include "string-util.h"
#include "strv.h"
return r;
}
-static void install_debug_shell_dropin(const char *dir) {
+static void install_debug_shell_dropin(void) {
const char *tty = arg_debug_tty ?: arg_default_debug_tty;
int r;
if (!tty || path_equal(tty, skip_dev_prefix(DEBUGTTY)))
return;
- r = write_drop_in_format(dir, "debug-shell.service", 50, "tty",
+ r = write_drop_in_format(arg_dest, "debug-shell.service", 50, "tty",
"[Unit]\n"
"Description=Early root shell on /dev/%s FOR DEBUGGING ONLY\n"
"ConditionPathExists=\n"
log_warning_errno(r, "Failed to write drop-in for debug-shell.service, ignoring: %m");
}
+static int process_unit_credentials(const char *credentials_dir) {
+ int r;
+
+ assert(credentials_dir);
+
+ _cleanup_free_ DirectoryEntries *des = NULL;
+ r = readdir_all_at(AT_FDCWD, credentials_dir, RECURSE_DIR_SORT|RECURSE_DIR_IGNORE_DOT|RECURSE_DIR_ENSURE_TYPE, &des);
+ if (r < 0)
+ return log_error_errno(r, "Failed to enumerate credentials from credentials directory '%s': %m", credentials_dir);
+
+ FOREACH_ARRAY(i, des->entries, des->n_entries) {
+ _cleanup_free_ void *d = NULL;
+ struct dirent *de = *i;
+ const char *unit, *dropin;
+
+ if (de->d_type != DT_REG)
+ continue;
+
+ unit = startswith(de->d_name, "systemd.extra-unit.");
+ dropin = startswith(de->d_name, "systemd.unit-dropin.");
+
+ if (!unit && !dropin)
+ continue;
+
+ if (!unit_name_is_valid(unit ?: dropin, UNIT_NAME_ANY)) {
+ log_warning("Invalid unit name '%s' in credential '%s', ignoring.",
+ unit ?: dropin, de->d_name);
+ continue;
+ }
+
+ r = read_credential_with_decryption(de->d_name, &d, NULL);
+ if (r < 0)
+ continue;
+
+ if (unit) {
+ _cleanup_free_ char *p = NULL;
+
+ p = path_join(arg_dest, unit);
+ if (!p)
+ return log_oom();
+
+ r = write_string_file_atomic_label(p, d);
+ if (r < 0) {
+ log_warning_errno(r, "Failed to write unit file '%s' from credential '%s', ignoring: %m",
+ unit, de->d_name);
+ continue;
+ }
+
+ log_debug("Wrote unit file '%s' from credential '%s'", unit, de->d_name);
+
+ } else {
+ r = write_drop_in(arg_dest, dropin, 50, "credential", d);
+ if (r < 0) {
+ log_warning_errno(r, "Failed to write drop-in for unit '%s' from credential '%s', ignoring: %m",
+ dropin, de->d_name);
+ continue;
+ }
+
+ log_debug("Wrote drop-in for unit '%s' from credential '%s'", dropin, de->d_name);
+ }
+ }
+
+ return 0;
+}
+
static int run(const char *dest, const char *dest_early, const char *dest_late) {
- int r, q;
+ const char *credentials_dir;
+ int r = 0;
assert_se(arg_dest = dest_early);
if (r < 0)
return log_oom();
- install_debug_shell_dropin(arg_dest);
+ install_debug_shell_dropin();
}
- r = generate_mask_symlinks();
- q = generate_wants_symlinks();
+ if (get_credentials_dir(&credentials_dir) >= 0)
+ RET_GATHER(r, process_unit_credentials(credentials_dir));
+
+ if (get_encrypted_credentials_dir(&credentials_dir) >= 0)
+ RET_GATHER(r, process_unit_credentials(credentials_dir));
- return r < 0 ? r : q;
+ RET_GATHER(r, generate_mask_symlinks());
+ RET_GATHER(r, generate_wants_symlinks());
+
+ return r;
}
DEFINE_MAIN_GENERATOR_FUNCTION(run);
#include "log.h"
#include "loop-util.h"
#include "main-func.h"
+#include "missing_syscall.h"
#include "mkdir.h"
#include "mount-util.h"
#include "mountpoint-util.h"
#include "namespace-util.h"
+#include "nsresource.h"
#include "parse-argument.h"
#include "parse-util.h"
#include "path-util.h"
static char *arg_loop_ref = NULL;
static ImagePolicy *arg_image_policy = NULL;
static bool arg_mtree_hash = true;
+static bool arg_via_service = false;
STATIC_DESTRUCTOR_REGISTER(arg_image, freep);
STATIC_DESTRUCTOR_REGISTER(arg_root, freep);
assert_not_reached();
}
+ r = getenv_bool("SYSTEMD_USE_MOUNTFSD");
+ if (r < 0) {
+ if (r != -ENXIO)
+ return log_error_errno(r, "Failed to parse $SYSTEMD_USE_MOUNTFSD: %m");
+ } else
+ arg_via_service = r;
+
+ if (!IN_SET(arg_action, ACTION_DISSECT, ACTION_LIST, ACTION_MTREE, ACTION_COPY_FROM, ACTION_COPY_TO, ACTION_DISCOVER, ACTION_VALIDATE) && geteuid() != 0)
+ return log_error_errno(SYNTHETIC_ERRNO(EPERM), "Need to be root.");
+
+ SET_FLAG(arg_flags, DISSECT_IMAGE_ALLOW_INTERACTIVE_AUTH, isatty(STDIN_FILENO));
+
return 1;
}
return 1;
}
-static int action_dissect(DissectedImage *m, LoopDevice *d) {
+static int action_dissect(
+ DissectedImage *m,
+ LoopDevice *d,
+ int userns_fd) {
+
_cleanup_(json_variant_unrefp) JsonVariant *v = NULL;
_cleanup_(table_unrefp) Table *t = NULL;
_cleanup_free_ char *bn = NULL;
int r;
assert(m);
- assert(d);
r = path_extract_filename(arg_image, &bn);
if (r < 0)
fflush(stdout);
}
- r = dissected_image_acquire_metadata(m, 0);
+ r = dissected_image_acquire_metadata(m, userns_fd, /* extra_flags= */ 0);
if (r == -ENXIO)
return log_error_errno(r, "No root partition discovered.");
if (r == -EUCLEAN)
int r;
assert(m);
- assert(d);
assert(arg_action == ACTION_MOUNT);
r = dissected_image_mount_and_warn(
if (r < 0)
return r;
- r = loop_device_flock(d, LOCK_UN);
- if (r < 0)
- return log_error_errno(r, "Failed to unlock loopback block device: %m");
+ if (d) {
+ r = loop_device_flock(d, LOCK_UN);
+ if (r < 0)
+ return log_error_errno(r, "Failed to unlock loopback block device: %m");
+ }
r = dissected_image_relinquish(m);
if (r < 0)
}
#endif
-static int action_list_or_mtree_or_copy_or_make_archive(DissectedImage *m, LoopDevice *d) {
- _cleanup_(umount_and_rmdir_and_freep) char *mounted_dir = NULL;
+static int action_list_or_mtree_or_copy_or_make_archive(DissectedImage *m, LoopDevice *d, int userns_fd) {
+ _cleanup_(umount_and_freep) char *mounted_dir = NULL;
_cleanup_free_ char *t = NULL;
const char *root;
int r;
if (arg_image) {
assert(m);
- assert(d);
- r = detach_mount_namespace();
+ if (userns_fd < 0)
+ r = detach_mount_namespace_harder(0, 0);
+ else
+ r = detach_mount_namespace_userns(userns_fd);
if (r < 0)
return log_error_errno(r, "Failed to detach mount namespace: %m");
+ /* Create a place we can mount things onto soon. We use a fixed path shared by all invocations. Given
+ * the mounts are done in a mount namespace there's not going to be a collision here */
r = get_common_dissect_directory(&t);
if (r < 0)
return log_error_errno(r, "Failed generate private mount directory: %m");
mounted_dir = TAKE_PTR(t);
- r = loop_device_flock(d, LOCK_UN);
- if (r < 0)
- return log_error_errno(r, "Failed to unlock loopback block device: %m");
+ if (d) {
+ r = loop_device_flock(d, LOCK_UN);
+ if (r < 0)
+ return log_error_errno(r, "Failed to unlock loopback block device: %m");
+ }
r = dissected_image_relinquish(m);
if (r < 0)
root = mounted_dir ?: arg_root;
+ dissected_image_close(m);
+
switch (arg_action) {
case ACTION_COPY_FROM: {
int r, rcode;
assert(m);
- assert(d);
assert(arg_action == ACTION_WITH);
r = tempfn_random_child(NULL, program_invocation_short_name, &temp);
if (r < 0)
return log_error_errno(r, "Failed to relinquish DM and loopback block devices: %m");
- r = loop_device_flock(d, LOCK_UN);
- if (r < 0)
- return log_error_errno(r, "Failed to unlock loopback block device: %m");
+ if (d) {
+ r = loop_device_flock(d, LOCK_UN);
+ if (r < 0)
+ return log_error_errno(r, "Failed to unlock loopback block device: %m");
+ }
rcode = safe_fork("(with)", FORK_CLOSE_ALL_FDS|FORK_LOG|FORK_WAIT, NULL);
if (rcode == 0) {
}
/* Let's manually detach everything, to make things synchronous */
- r = loop_device_flock(d, LOCK_SH);
- if (r < 0)
- log_warning_errno(r, "Failed to lock loopback block device, ignoring: %m");
+ if (d) {
+ r = loop_device_flock(d, LOCK_SH);
+ if (r < 0)
+ log_warning_errno(r, "Failed to lock loopback block device, ignoring: %m");
+ }
r = umount_recursive(mounted_dir, 0);
if (r < 0)
log_warning_errno(r, "Failed to unmount '%s', ignoring: %m", mounted_dir);
- else
+ else if (d)
loop_device_unrelinquish(d); /* Let's try to destroy the loopback device */
created_dir = TAKE_PTR(mounted_dir);
static int run(int argc, char *argv[]) {
_cleanup_(dissected_image_unrefp) DissectedImage *m = NULL;
_cleanup_(loop_device_unrefp) LoopDevice *d = NULL;
- uint32_t loop_flags;
- int open_flags, r;
+ _cleanup_close_ int userns_fd = -EBADF;
+ int r;
log_setup();
return action_discover();
default:
- /* All other actions need the image dissected */
+ /* All other actions need the image dissected (except for ACTION_VALIDATE, see below) */
break;
}
* hence if there's external Verity data
* available we turn off partition table
* support */
+ }
- if (arg_action == ACTION_VALIDATE)
- return action_validate();
+ if (arg_action == ACTION_VALIDATE)
+ return action_validate();
- open_flags = FLAGS_SET(arg_flags, DISSECT_IMAGE_DEVICE_READ_ONLY) ? O_RDONLY : O_RDWR;
- loop_flags = FLAGS_SET(arg_flags, DISSECT_IMAGE_NO_PARTITION_TABLE) ? 0 : LO_FLAGS_PARTSCAN;
- if (arg_in_memory)
- r = loop_device_make_by_path_memory(arg_image, open_flags, /* sector_size= */ UINT32_MAX, loop_flags, LOCK_SH, &d);
- else
- r = loop_device_make_by_path(arg_image, open_flags, /* sector_size= */ UINT32_MAX, loop_flags, LOCK_SH, &d);
- if (r < 0)
- return log_error_errno(r, "Failed to set up loopback device for %s: %m", arg_image);
+ if (arg_image) {
+ /* First try locally, if we are allowed to */
+ if (!arg_via_service) {
+ uint32_t loop_flags;
+ int open_flags;
+
+ open_flags = FLAGS_SET(arg_flags, DISSECT_IMAGE_DEVICE_READ_ONLY) ? O_RDONLY : O_RDWR;
+ loop_flags = FLAGS_SET(arg_flags, DISSECT_IMAGE_NO_PARTITION_TABLE) ? 0 : LO_FLAGS_PARTSCAN;
+
+ if (arg_in_memory)
+ r = loop_device_make_by_path_memory(arg_image, open_flags, /* sector_size= */ UINT32_MAX, loop_flags, LOCK_SH, &d);
+ else
+ r = loop_device_make_by_path(arg_image, open_flags, /* sector_size= */ UINT32_MAX, loop_flags, LOCK_SH, &d);
+ if (r < 0) {
+ if (!ERRNO_IS_PRIVILEGE(r) || !IN_SET(arg_action, ACTION_DISSECT, ACTION_LIST, ACTION_MTREE, ACTION_COPY_FROM, ACTION_COPY_TO))
+ return log_error_errno(r, "Failed to set up loopback device for %s: %m", arg_image);
- if (arg_loop_ref) {
- r = loop_device_set_filename(d, arg_loop_ref);
- if (r < 0)
- log_warning_errno(r, "Failed to set loop reference string to '%s', ignoring: %m", arg_loop_ref);
+ log_debug_errno(r, "Lacking permissions to set up loopback block device for %s, using service: %m", arg_image);
+ arg_via_service = true;
+ } else {
+ if (arg_loop_ref) {
+ r = loop_device_set_filename(d, arg_loop_ref);
+ if (r < 0)
+ log_warning_errno(r, "Failed to set loop reference string to '%s', ignoring: %m", arg_loop_ref);
+ }
+
+ r = dissect_loop_device_and_warn(
+ d,
+ &arg_verity_settings,
+ /* mount_options= */ NULL,
+ arg_image_policy,
+ arg_flags,
+ &m);
+ if (r < 0)
+ return r;
+
+ if (arg_action == ACTION_ATTACH)
+ return action_attach(m, d);
+
+ r = dissected_image_load_verity_sig_partition(
+ m,
+ d->fd,
+ &arg_verity_settings);
+ if (r < 0)
+ return log_error_errno(r, "Failed to load verity signature partition: %m");
+
+ if (arg_action != ACTION_DISSECT) {
+ r = dissected_image_decrypt_interactively(
+ m, NULL,
+ &arg_verity_settings,
+ arg_flags);
+ if (r < 0)
+ return r;
+ }
+ }
}
- r = dissect_loop_device_and_warn(
- d,
- &arg_verity_settings,
- /* mount_options= */ NULL,
- arg_image_policy,
- arg_flags,
- &m);
- if (r < 0)
- return r;
+ /* Try via service */
+ if (arg_via_service) {
+ if (arg_in_memory)
+ return log_error_errno(SYNTHETIC_ERRNO(EOPNOTSUPP), "--in-memory= not supported when operating via systemd-mountfsd.");
- if (arg_action == ACTION_ATTACH)
- return action_attach(m, d);
+ if (arg_loop_ref)
+ return log_error_errno(SYNTHETIC_ERRNO(EOPNOTSUPP), "--loop-ref= not supported when operating via systemd-mountfsd.");
- r = dissected_image_load_verity_sig_partition(
- m,
- d->fd,
- &arg_verity_settings);
- if (r < 0)
- return log_error_errno(r, "Failed to load verity signature partition: %m");
+ if (verity_settings_set(&arg_verity_settings))
+ return log_error_errno(SYNTHETIC_ERRNO(EOPNOTSUPP), "Externally configured verity settings not supported when operating via systemd-mountfsd.");
+
+ /* Don't run things in private userns, if the mount shall be attached to the host */
+ if (!IN_SET(arg_action, ACTION_MOUNT, ACTION_WITH)) {
+ userns_fd = nsresource_allocate_userns(/* name= */ NULL, UINT64_C(0x10000)); /* allocate 64K users by default */
+ if (userns_fd < 0)
+ return log_error_errno(userns_fd, "Failed to allocate user namespace with 64K users: %m");
+ }
- if (arg_action != ACTION_DISSECT) {
- r = dissected_image_decrypt_interactively(
- m, NULL,
- &arg_verity_settings,
- arg_flags);
+ r = mountfsd_mount_image(
+ arg_image,
+ userns_fd,
+ arg_image_policy,
+ arg_flags,
+ &m);
if (r < 0)
return r;
}
switch (arg_action) {
case ACTION_DISSECT:
- return action_dissect(m, d);
+ return action_dissect(m, d, userns_fd);
case ACTION_MOUNT:
return action_mount(m, d);
case ACTION_COPY_FROM:
case ACTION_COPY_TO:
case ACTION_MAKE_ARCHIVE:
- return action_list_or_mtree_or_copy_or_make_archive(m, d);
+ return action_list_or_mtree_or_copy_or_make_archive(m, d, userns_fd);
case ACTION_WITH:
return action_with(m, d);
DEFINE_TRIVIAL_CLEANUP_FUNC(KernelHibernateLocation*, kernel_hibernate_location_free);
-typedef struct EFIHibernateLocation {
- char *device;
-
- sd_id128_t uuid;
- uint64_t offset;
-
- char *kernel_version;
- char *id;
- char *image_id;
- char *version_id;
- char *image_version;
-} EFIHibernateLocation;
-
-static EFIHibernateLocation* efi_hibernate_location_free(EFIHibernateLocation *e) {
+EFIHibernateLocation* efi_hibernate_location_free(EFIHibernateLocation *e) {
if (!e)
return NULL;
return mfree(e);
}
-DEFINE_TRIVIAL_CLEANUP_FUNC(EFIHibernateLocation*, efi_hibernate_location_free);
-
void hibernate_info_done(HibernateInfo *info) {
assert(info);
if (!streq_ptr(id, e->id) ||
!streq_ptr(image_id, e->image_id)) {
- log_notice("HibernateLocation system identifier doesn't match currently running system, not resuming from it.");
+ log_notice("HibernateLocation system identifier doesn't match currently running system, would not resume from it.");
return false;
}
return true;
}
+#endif
-static int get_efi_hibernate_location(EFIHibernateLocation **ret) {
-
+int get_efi_hibernate_location(EFIHibernateLocation **ret) {
+#if ENABLE_EFI
static const JsonDispatch dispatch_table[] = {
{ "uuid", JSON_VARIANT_STRING, json_dispatch_id128, offsetof(EFIHibernateLocation, uuid), JSON_MANDATORY },
{ "offset", _JSON_VARIANT_TYPE_INVALID, json_dispatch_uint64, offsetof(EFIHibernateLocation, offset), JSON_MANDATORY },
_cleanup_free_ char *location_str = NULL;
int r;
- assert(ret);
-
if (!is_efi_boot())
goto skip;
if (asprintf(&e->device, "/dev/disk/by-uuid/" SD_ID128_UUID_FORMAT_STR, SD_ID128_FORMAT_VAL(e->uuid)) < 0)
return log_oom();
- *ret = TAKE_PTR(e);
+ if (ret)
+ *ret = TAKE_PTR(e);
return 1;
skip:
- *ret = NULL;
+#endif
+ if (ret)
+ *ret = NULL;
return 0;
}
void compare_hibernate_location_and_warn(const HibernateInfo *info) {
+#if ENABLE_EFI
int r;
assert(info);
if (info->cmdline->offset != info->efi->offset)
log_warning("resume_offset=%" PRIu64 " doesn't match with EFI HibernateLocation offset %" PRIu64 ", proceeding anyway with resume_offset=.",
info->cmdline->offset, info->efi->offset);
-}
#endif
+}
int acquire_hibernate_info(HibernateInfo *ret) {
_cleanup_(hibernate_info_done) HibernateInfo i = {};
if (r < 0)
return r;
-#if ENABLE_EFI
r = get_efi_hibernate_location(&i.efi);
if (r < 0)
return r;
-#endif
if (i.cmdline) {
i.device = i.cmdline->device;
#include "sd-id128.h"
+#include "macro.h"
+
typedef struct KernelHibernateLocation KernelHibernateLocation;
-typedef struct EFIHibernateLocation EFIHibernateLocation;
+
+typedef struct EFIHibernateLocation {
+ char *device;
+
+ sd_id128_t uuid;
+ uint64_t offset;
+
+ char *kernel_version;
+ char *id;
+ char *image_id;
+ char *version_id;
+ char *image_version;
+} EFIHibernateLocation;
+
+EFIHibernateLocation* efi_hibernate_location_free(EFIHibernateLocation *e);
+DEFINE_TRIVIAL_CLEANUP_FUNC(EFIHibernateLocation*, efi_hibernate_location_free);
+
+int get_efi_hibernate_location(EFIHibernateLocation **ret);
typedef struct HibernateInfo {
const char *device;
int acquire_hibernate_info(HibernateInfo *ret);
-#if ENABLE_EFI
-
void compare_hibernate_location_and_warn(const HibernateInfo *info);
-
-#else
-
-static inline void compare_hibernate_location_and_warn(const HibernateInfo *info) {
- return;
-}
-
-#endif
/* SPDX-License-Identifier: LGPL-2.1-or-later */
#include <errno.h>
+#include <getopt.h>
#include <sys/stat.h>
+#include "build.h"
#include "devnum-util.h"
#include "hibernate-resume-config.h"
#include "hibernate-util.h"
#include "log.h"
#include "main-func.h"
#include "parse-util.h"
+#include "pretty-print.h"
#include "static-destruct.h"
+#include "terminal-util.h"
static HibernateInfo arg_info = {};
+static bool arg_clear = false;
STATIC_DESTRUCTOR_REGISTER(arg_info, hibernate_info_done);
+static int help(void) {
+ _cleanup_free_ char *link = NULL;
+ int r;
+
+ r = terminal_urlify_man("systemd-hibernate-resume", "8", &link);
+ if (r < 0)
+ return log_oom();
+
+ printf("%s [OPTIONS...] [DEVICE [OFFSET]]\n"
+ "\n%sInitiate resume from hibernation.%s\n\n"
+ " -h --help Show this help\n"
+ " --version Show package version\n"
+ " --clear Clear hibernation storage information from EFI and exit\n"
+ "\nSee the %s for details.\n",
+ program_invocation_short_name,
+ ansi_highlight(),
+ ansi_normal(),
+ link);
+
+ return 0;
+}
+
+static int parse_argv(int argc, char *argv[]) {
+
+ enum {
+ ARG_VERSION = 0x100,
+ ARG_CLEAR,
+ };
+
+ static const struct option options[] = {
+ { "help", no_argument, NULL, 'h' },
+ { "version", no_argument, NULL, ARG_VERSION },
+ { "clear", no_argument, NULL, ARG_CLEAR },
+ {}
+ };
+
+ int c;
+
+ assert(argc >= 0);
+ assert(argv);
+
+ while ((c = getopt_long(argc, argv, "h", options, NULL)) >= 0)
+
+ switch (c) {
+
+ case 'h':
+ return help();
+
+ case ARG_VERSION:
+ return version();
+
+ case ARG_CLEAR:
+ arg_clear = true;
+ break;
+
+ case '?':
+ return -EINVAL;
+
+ default:
+ assert_not_reached();
+ }
+
+ if (argc > optind && arg_clear)
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
+ "Extraneous arguments specified with --clear, refusing.");
+
+ return 1;
+}
+
static int setup_hibernate_info_and_warn(void) {
int r;
return 1;
}
+static int action_clear(void) {
+ int r;
+
+ assert(arg_clear);
+
+ /* Let's insist that the system identifier is verified still. After all if things don't match,
+ * the resume wouldn't get triggered in the first place. We should not erase the var if booted
+ * from LiveCD/portable systems/... */
+ r = get_efi_hibernate_location(/* ret = */ NULL);
+ if (r <= 0)
+ return r;
+
+ r = clear_efi_hibernate_location_and_warn();
+ if (r > 0)
+ log_notice("Successfully cleared HibernateLocation EFI variable.");
+ return r;
+}
+
static int run(int argc, char *argv[]) {
struct stat st;
int r;
log_setup();
- if (argc < 1 || argc > 3)
+ r = parse_argv(argc, argv);
+ if (r <= 0)
+ return r;
+
+ if (argc - optind > 2)
return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "This program expects zero, one, or two arguments.");
umask(0022);
- if (!in_initrd())
- return 0;
+ if (arg_clear)
+ return action_clear();
- if (argc > 1) {
- arg_info.device = argv[1];
+ if (!in_initrd())
+ return log_error_errno(SYNTHETIC_ERRNO(ENOTRECOVERABLE),
+ "Not running in initrd, refusing to initiate resume from hibernation.");
- if (argc == 3) {
- r = safe_atou64(argv[2], &arg_info.offset);
- if (r < 0)
- return log_error_errno(r, "Failed to parse resume offset %s: %m", argv[2]);
- }
- } else {
+ if (argc <= optind) {
r = setup_hibernate_info_and_warn();
if (r <= 0)
return r;
if (arg_info.efi)
- clear_efi_hibernate_location_and_warn();
+ (void) clear_efi_hibernate_location_and_warn();
+ } else {
+ arg_info.device = ASSERT_PTR(argv[optind]);
+
+ if (argc - optind == 2) {
+ r = safe_atou64(argv[optind + 1], &arg_info.offset);
+ if (r < 0)
+ return log_error_errno(r, "Failed to parse resume offset %s: %m", argv[optind + 1]);
+ }
}
if (stat(arg_info.device, &st) < 0)
return log_error_errno(errno, "Failed to stat resume device '%s': %m", arg_info.device);
if (!S_ISBLK(st.st_mode))
- return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
+ return log_error_errno(SYNTHETIC_ERRNO(ENOTBLK),
"Resume device '%s' is not a block device.", arg_info.device);
/* The write shall not return if a resume takes place. */
};
static const struct option options[] = {
- { "help", no_argument, NULL, 'h' },
- { "version", no_argument, NULL, ARG_VERSION },
- { "no-pager", no_argument, NULL, ARG_NO_PAGER },
- { "no-legend", no_argument, NULL, ARG_NO_LEGEND },
- { "no-ask-password", no_argument, NULL, ARG_NO_ASK_PASSWORD },
- { "offline", no_argument, NULL, ARG_OFFLINE },
- { "host", required_argument, NULL, 'H' },
- { "machine", required_argument, NULL, 'M' },
- { "identity", required_argument, NULL, 'I' },
- { "real-name", required_argument, NULL, 'c' },
- { "comment", required_argument, NULL, 'c' }, /* Compat alias to keep thing in sync with useradd(8) */
- { "realm", required_argument, NULL, ARG_REALM },
- { "email-address", required_argument, NULL, ARG_EMAIL_ADDRESS },
- { "location", required_argument, NULL, ARG_LOCATION },
- { "password-hint", required_argument, NULL, ARG_PASSWORD_HINT },
- { "icon-name", required_argument, NULL, ARG_ICON_NAME },
- { "home-dir", required_argument, NULL, 'd' }, /* Compatible with useradd(8) */
- { "uid", required_argument, NULL, 'u' }, /* Compatible with useradd(8) */
- { "member-of", required_argument, NULL, 'G' },
- { "groups", required_argument, NULL, 'G' }, /* Compat alias to keep thing in sync with useradd(8) */
- { "skel", required_argument, NULL, 'k' }, /* Compatible with useradd(8) */
- { "shell", required_argument, NULL, 's' }, /* Compatible with useradd(8) */
- { "setenv", required_argument, NULL, ARG_SETENV },
- { "timezone", required_argument, NULL, ARG_TIMEZONE },
- { "language", required_argument, NULL, ARG_LANGUAGE },
- { "locked", required_argument, NULL, ARG_LOCKED },
- { "not-before", required_argument, NULL, ARG_NOT_BEFORE },
- { "not-after", required_argument, NULL, ARG_NOT_AFTER },
- { "expiredate", required_argument, NULL, 'e' }, /* Compat alias to keep thing in sync with useradd(8) */
- { "ssh-authorized-keys", required_argument, NULL, ARG_SSH_AUTHORIZED_KEYS },
- { "disk-size", required_argument, NULL, ARG_DISK_SIZE },
- { "access-mode", required_argument, NULL, ARG_ACCESS_MODE },
- { "umask", required_argument, NULL, ARG_UMASK },
- { "nice", required_argument, NULL, ARG_NICE },
- { "rlimit", required_argument, NULL, ARG_RLIMIT },
- { "tasks-max", required_argument, NULL, ARG_TASKS_MAX },
- { "memory-high", required_argument, NULL, ARG_MEMORY_HIGH },
- { "memory-max", required_argument, NULL, ARG_MEMORY_MAX },
- { "cpu-weight", required_argument, NULL, ARG_CPU_WEIGHT },
- { "io-weight", required_argument, NULL, ARG_IO_WEIGHT },
- { "storage", required_argument, NULL, ARG_STORAGE },
- { "image-path", required_argument, NULL, ARG_IMAGE_PATH },
- { "fs-type", required_argument, NULL, ARG_FS_TYPE },
- { "luks-discard", required_argument, NULL, ARG_LUKS_DISCARD },
- { "luks-offline-discard", required_argument, NULL, ARG_LUKS_OFFLINE_DISCARD },
- { "luks-cipher", required_argument, NULL, ARG_LUKS_CIPHER },
- { "luks-cipher-mode", required_argument, NULL, ARG_LUKS_CIPHER_MODE },
- { "luks-volume-key-size", required_argument, NULL, ARG_LUKS_VOLUME_KEY_SIZE },
- { "luks-pbkdf-type", required_argument, NULL, ARG_LUKS_PBKDF_TYPE },
- { "luks-pbkdf-hash-algorithm", required_argument, NULL, ARG_LUKS_PBKDF_HASH_ALGORITHM },
- { "luks-pbkdf-force-iterations", required_argument, NULL, ARG_LUKS_PBKDF_FORCE_ITERATIONS },
- { "luks-pbkdf-time-cost", required_argument, NULL, ARG_LUKS_PBKDF_TIME_COST },
- { "luks-pbkdf-memory-cost", required_argument, NULL, ARG_LUKS_PBKDF_MEMORY_COST },
- { "luks-pbkdf-parallel-threads", required_argument, NULL, ARG_LUKS_PBKDF_PARALLEL_THREADS },
- { "luks-sector-size", required_argument, NULL, ARG_LUKS_SECTOR_SIZE },
- { "nosuid", required_argument, NULL, ARG_NOSUID },
- { "nodev", required_argument, NULL, ARG_NODEV },
- { "noexec", required_argument, NULL, ARG_NOEXEC },
- { "cifs-user-name", required_argument, NULL, ARG_CIFS_USER_NAME },
- { "cifs-domain", required_argument, NULL, ARG_CIFS_DOMAIN },
- { "cifs-service", required_argument, NULL, ARG_CIFS_SERVICE },
- { "cifs-extra-mount-options", required_argument, NULL, ARG_CIFS_EXTRA_MOUNT_OPTIONS },
- { "rate-limit-interval", required_argument, NULL, ARG_RATE_LIMIT_INTERVAL },
- { "rate-limit-burst", required_argument, NULL, ARG_RATE_LIMIT_BURST },
- { "stop-delay", required_argument, NULL, ARG_STOP_DELAY },
- { "kill-processes", required_argument, NULL, ARG_KILL_PROCESSES },
- { "enforce-password-policy", required_argument, NULL, ARG_ENFORCE_PASSWORD_POLICY },
- { "password-change-now", required_argument, NULL, ARG_PASSWORD_CHANGE_NOW },
- { "password-change-min", required_argument, NULL, ARG_PASSWORD_CHANGE_MIN },
- { "password-change-max", required_argument, NULL, ARG_PASSWORD_CHANGE_MAX },
- { "password-change-warn", required_argument, NULL, ARG_PASSWORD_CHANGE_WARN },
- { "password-change-inactive", required_argument, NULL, ARG_PASSWORD_CHANGE_INACTIVE },
- { "auto-login", required_argument, NULL, ARG_AUTO_LOGIN },
- { "session-launcher", required_argument, NULL, ARG_SESSION_LAUNCHER, },
- { "session-type", required_argument, NULL, ARG_SESSION_TYPE, },
- { "json", required_argument, NULL, ARG_JSON },
- { "export-format", required_argument, NULL, ARG_EXPORT_FORMAT },
- { "pkcs11-token-uri", required_argument, NULL, ARG_PKCS11_TOKEN_URI },
- { "fido2-credential-algorithm", required_argument, NULL, ARG_FIDO2_CRED_ALG },
- { "fido2-device", required_argument, NULL, ARG_FIDO2_DEVICE },
- { "fido2-with-client-pin", required_argument, NULL, ARG_FIDO2_WITH_PIN },
- { "fido2-with-user-presence", required_argument, NULL, ARG_FIDO2_WITH_UP },
- { "fido2-with-user-verification",required_argument, NULL, ARG_FIDO2_WITH_UV },
- { "recovery-key", required_argument, NULL, ARG_RECOVERY_KEY },
- { "and-resize", required_argument, NULL, ARG_AND_RESIZE },
- { "and-change-password", required_argument, NULL, ARG_AND_CHANGE_PASSWORD },
- { "drop-caches", required_argument, NULL, ARG_DROP_CACHES },
- { "luks-extra-mount-options", required_argument, NULL, ARG_LUKS_EXTRA_MOUNT_OPTIONS },
- { "auto-resize-mode", required_argument, NULL, ARG_AUTO_RESIZE_MODE },
- { "rebalance-weight", required_argument, NULL, ARG_REBALANCE_WEIGHT },
- { "capability-bounding-set", required_argument, NULL, ARG_CAPABILITY_BOUNDING_SET },
- { "capability-ambient-set", required_argument, NULL, ARG_CAPABILITY_AMBIENT_SET },
- { "prompt-new-user", no_argument, NULL, ARG_PROMPT_NEW_USER },
- { "blob", required_argument, NULL, 'b' },
- { "avatar", required_argument, NULL, ARG_AVATAR },
- { "login-background", required_argument, NULL, ARG_LOGIN_BACKGROUND },
+ { "help", no_argument, NULL, 'h' },
+ { "version", no_argument, NULL, ARG_VERSION },
+ { "no-pager", no_argument, NULL, ARG_NO_PAGER },
+ { "no-legend", no_argument, NULL, ARG_NO_LEGEND },
+ { "no-ask-password", no_argument, NULL, ARG_NO_ASK_PASSWORD },
+ { "offline", no_argument, NULL, ARG_OFFLINE },
+ { "host", required_argument, NULL, 'H' },
+ { "machine", required_argument, NULL, 'M' },
+ { "identity", required_argument, NULL, 'I' },
+ { "real-name", required_argument, NULL, 'c' },
+ { "comment", required_argument, NULL, 'c' }, /* Compat alias to keep thing in sync with useradd(8) */
+ { "realm", required_argument, NULL, ARG_REALM },
+ { "email-address", required_argument, NULL, ARG_EMAIL_ADDRESS },
+ { "location", required_argument, NULL, ARG_LOCATION },
+ { "password-hint", required_argument, NULL, ARG_PASSWORD_HINT },
+ { "icon-name", required_argument, NULL, ARG_ICON_NAME },
+ { "home-dir", required_argument, NULL, 'd' }, /* Compatible with useradd(8) */
+ { "uid", required_argument, NULL, 'u' }, /* Compatible with useradd(8) */
+ { "member-of", required_argument, NULL, 'G' },
+ { "groups", required_argument, NULL, 'G' }, /* Compat alias to keep thing in sync with useradd(8) */
+ { "skel", required_argument, NULL, 'k' }, /* Compatible with useradd(8) */
+ { "shell", required_argument, NULL, 's' }, /* Compatible with useradd(8) */
+ { "setenv", required_argument, NULL, ARG_SETENV },
+ { "timezone", required_argument, NULL, ARG_TIMEZONE },
+ { "language", required_argument, NULL, ARG_LANGUAGE },
+ { "locked", required_argument, NULL, ARG_LOCKED },
+ { "not-before", required_argument, NULL, ARG_NOT_BEFORE },
+ { "not-after", required_argument, NULL, ARG_NOT_AFTER },
+ { "expiredate", required_argument, NULL, 'e' }, /* Compat alias to keep thing in sync with useradd(8) */
+ { "ssh-authorized-keys", required_argument, NULL, ARG_SSH_AUTHORIZED_KEYS },
+ { "disk-size", required_argument, NULL, ARG_DISK_SIZE },
+ { "access-mode", required_argument, NULL, ARG_ACCESS_MODE },
+ { "umask", required_argument, NULL, ARG_UMASK },
+ { "nice", required_argument, NULL, ARG_NICE },
+ { "rlimit", required_argument, NULL, ARG_RLIMIT },
+ { "tasks-max", required_argument, NULL, ARG_TASKS_MAX },
+ { "memory-high", required_argument, NULL, ARG_MEMORY_HIGH },
+ { "memory-max", required_argument, NULL, ARG_MEMORY_MAX },
+ { "cpu-weight", required_argument, NULL, ARG_CPU_WEIGHT },
+ { "io-weight", required_argument, NULL, ARG_IO_WEIGHT },
+ { "storage", required_argument, NULL, ARG_STORAGE },
+ { "image-path", required_argument, NULL, ARG_IMAGE_PATH },
+ { "fs-type", required_argument, NULL, ARG_FS_TYPE },
+ { "luks-discard", required_argument, NULL, ARG_LUKS_DISCARD },
+ { "luks-offline-discard", required_argument, NULL, ARG_LUKS_OFFLINE_DISCARD },
+ { "luks-cipher", required_argument, NULL, ARG_LUKS_CIPHER },
+ { "luks-cipher-mode", required_argument, NULL, ARG_LUKS_CIPHER_MODE },
+ { "luks-volume-key-size", required_argument, NULL, ARG_LUKS_VOLUME_KEY_SIZE },
+ { "luks-pbkdf-type", required_argument, NULL, ARG_LUKS_PBKDF_TYPE },
+ { "luks-pbkdf-hash-algorithm", required_argument, NULL, ARG_LUKS_PBKDF_HASH_ALGORITHM },
+ { "luks-pbkdf-force-iterations", required_argument, NULL, ARG_LUKS_PBKDF_FORCE_ITERATIONS },
+ { "luks-pbkdf-time-cost", required_argument, NULL, ARG_LUKS_PBKDF_TIME_COST },
+ { "luks-pbkdf-memory-cost", required_argument, NULL, ARG_LUKS_PBKDF_MEMORY_COST },
+ { "luks-pbkdf-parallel-threads", required_argument, NULL, ARG_LUKS_PBKDF_PARALLEL_THREADS },
+ { "luks-sector-size", required_argument, NULL, ARG_LUKS_SECTOR_SIZE },
+ { "nosuid", required_argument, NULL, ARG_NOSUID },
+ { "nodev", required_argument, NULL, ARG_NODEV },
+ { "noexec", required_argument, NULL, ARG_NOEXEC },
+ { "cifs-user-name", required_argument, NULL, ARG_CIFS_USER_NAME },
+ { "cifs-domain", required_argument, NULL, ARG_CIFS_DOMAIN },
+ { "cifs-service", required_argument, NULL, ARG_CIFS_SERVICE },
+ { "cifs-extra-mount-options", required_argument, NULL, ARG_CIFS_EXTRA_MOUNT_OPTIONS },
+ { "rate-limit-interval", required_argument, NULL, ARG_RATE_LIMIT_INTERVAL },
+ { "rate-limit-burst", required_argument, NULL, ARG_RATE_LIMIT_BURST },
+ { "stop-delay", required_argument, NULL, ARG_STOP_DELAY },
+ { "kill-processes", required_argument, NULL, ARG_KILL_PROCESSES },
+ { "enforce-password-policy", required_argument, NULL, ARG_ENFORCE_PASSWORD_POLICY },
+ { "password-change-now", required_argument, NULL, ARG_PASSWORD_CHANGE_NOW },
+ { "password-change-min", required_argument, NULL, ARG_PASSWORD_CHANGE_MIN },
+ { "password-change-max", required_argument, NULL, ARG_PASSWORD_CHANGE_MAX },
+ { "password-change-warn", required_argument, NULL, ARG_PASSWORD_CHANGE_WARN },
+ { "password-change-inactive", required_argument, NULL, ARG_PASSWORD_CHANGE_INACTIVE },
+ { "auto-login", required_argument, NULL, ARG_AUTO_LOGIN },
+ { "session-launcher", required_argument, NULL, ARG_SESSION_LAUNCHER, },
+ { "session-type", required_argument, NULL, ARG_SESSION_TYPE, },
+ { "json", required_argument, NULL, ARG_JSON },
+ { "export-format", required_argument, NULL, ARG_EXPORT_FORMAT },
+ { "pkcs11-token-uri", required_argument, NULL, ARG_PKCS11_TOKEN_URI },
+ { "fido2-credential-algorithm", required_argument, NULL, ARG_FIDO2_CRED_ALG },
+ { "fido2-device", required_argument, NULL, ARG_FIDO2_DEVICE },
+ { "fido2-with-client-pin", required_argument, NULL, ARG_FIDO2_WITH_PIN },
+ { "fido2-with-user-presence", required_argument, NULL, ARG_FIDO2_WITH_UP },
+ { "fido2-with-user-verification", required_argument, NULL, ARG_FIDO2_WITH_UV },
+ { "recovery-key", required_argument, NULL, ARG_RECOVERY_KEY },
+ { "and-resize", required_argument, NULL, ARG_AND_RESIZE },
+ { "and-change-password", required_argument, NULL, ARG_AND_CHANGE_PASSWORD },
+ { "drop-caches", required_argument, NULL, ARG_DROP_CACHES },
+ { "luks-extra-mount-options", required_argument, NULL, ARG_LUKS_EXTRA_MOUNT_OPTIONS },
+ { "auto-resize-mode", required_argument, NULL, ARG_AUTO_RESIZE_MODE },
+ { "rebalance-weight", required_argument, NULL, ARG_REBALANCE_WEIGHT },
+ { "capability-bounding-set", required_argument, NULL, ARG_CAPABILITY_BOUNDING_SET },
+ { "capability-ambient-set", required_argument, NULL, ARG_CAPABILITY_AMBIENT_SET },
+ { "prompt-new-user", no_argument, NULL, ARG_PROMPT_NEW_USER },
+ { "blob", required_argument, NULL, 'b' },
+ { "avatar", required_argument, NULL, ARG_AVATAR },
+ { "login-background", required_argument, NULL, ARG_LOGIN_BACKGROUND },
{}
};
case HOME_ABSENT:
r = sd_bus_error_setf(&error, BUS_ERROR_HOME_ABSENT,
"Home %s is currently missing or not plugged in.", h->user_name);
- goto check;
+ operation_result(o, r, &error);
+ return 1;
case HOME_INACTIVE:
case HOME_DIRTY:
if (r >= 0)
r = call(h, o->secret, for_state, &error);
- check:
if (r != 0) /* failure or completed */
operation_result(o, r, &error);
else /* ongoing */
assert(o);
assert(o->type == OPERATION_RELEASE);
- if (home_is_referenced(h))
+ if (home_is_referenced(h)) {
/* If there's now a reference again, then let's abort the release attempt */
r = sd_bus_error_setf(&error, BUS_ERROR_HOME_BUSY, "Home %s is currently referenced.", h->user_name);
- else {
- switch (home_get_state(h)) {
-
- case HOME_UNFIXATED:
- case HOME_ABSENT:
- case HOME_INACTIVE:
- case HOME_DIRTY:
- r = 1; /* done */
- break;
-
- case HOME_LOCKED:
- r = sd_bus_error_setf(&error, BUS_ERROR_HOME_LOCKED, "Home %s is currently locked.", h->user_name);
- break;
-
- case HOME_ACTIVE:
- case HOME_LINGERING:
- r = home_deactivate_internal(h, false, &error);
- break;
-
- default:
- /* All other cases means we are currently executing an operation, which means the job remains
- * pending. */
- return 0;
- }
+ operation_result(o, r, &error);
+ return 1;
+ }
+
+ switch (home_get_state(h)) {
+
+ case HOME_UNFIXATED:
+ case HOME_ABSENT:
+ case HOME_INACTIVE:
+ case HOME_DIRTY:
+ r = 1; /* done */
+ break;
+
+ case HOME_LOCKED:
+ r = sd_bus_error_setf(&error, BUS_ERROR_HOME_LOCKED, "Home %s is currently locked.", h->user_name);
+ break;
+
+ case HOME_ACTIVE:
+ case HOME_LINGERING:
+ r = home_deactivate_internal(h, false, &error);
+ break;
+
+ default:
+ /* All other cases means we are currently executing an operation, which means the job remains
+ * pending. */
+ return 0;
}
assert(!h->current_operation);
bool home_shall_suspend(Home *h);
HomeState home_get_state(Home *h);
-int home_get_disk_status(Home *h, uint64_t *ret_disk_size,uint64_t *ret_disk_usage, uint64_t *ret_disk_free, uint64_t *ret_disk_ceiling, uint64_t *ret_disk_floor, statfs_f_type_t *ret_fstype, mode_t *ret_access_mode);
+int home_get_disk_status(
+ Home *h,
+ uint64_t *ret_disk_size,
+ uint64_t *ret_disk_usage,
+ uint64_t *ret_disk_free,
+ uint64_t *ret_disk_ceiling,
+ uint64_t *ret_disk_floor,
+ statfs_f_type_t *ret_fstype,
+ mode_t *ret_access_mode);
void home_process_notify(Home *h, char **l, int fd);
if (r < 0)
return r;
- FOREACH_DEVICE(e, d)
+ FOREACH_DEVICE(e, d) {
+ if (device_is_processed(d) <= 0)
+ continue;
(void) manager_add_device(m, d);
+ }
return 0;
}
/* btrfs can grow and shrink online */
- } else if (is_fs_type(&sfs, XFS_SB_MAGIC)) {
+ } else if (is_fs_type(&sfs, XFS_SUPER_MAGIC)) {
if (new_size < XFS_MINIMAL_SIZE)
return log_error_errno(SYNTHETIC_ERRNO(ERANGE), "New file system size too small for xfs (needs to be 14M at least).");
return r;
if (r == 0) {
/* Child */
- execlp("e2fsck" ,"e2fsck", "-fp", setup->dm_node, NULL);
+ execlp("e2fsck", "e2fsck", "-fp", setup->dm_node, NULL);
log_open();
log_error_errno(errno, "Failed to execute e2fsck: %m");
_exit(EXIT_FAILURE);
return r;
if (r == 0) {
/* Child */
- execlp("resize2fs" ,"resize2fs", setup->dm_node, size_str, NULL);
+ execlp("resize2fs", "resize2fs", setup->dm_node, size_str, NULL);
log_open();
log_error_errno(errno, "Failed to execute resize2fs: %m");
_exit(EXIT_FAILURE);
if (statfs(path, &sfs) < 0)
return log_error_errno(errno, "Failed to statfs() file system: %m");
- if (is_fs_type(&sfs, XFS_SB_MAGIC) ||
+ if (is_fs_type(&sfs, XFS_SUPER_MAGIC) ||
is_fs_type(&sfs, EXT4_SUPER_MAGIC))
return home_update_quota_classic(h, path);
threads,
],
},
+ test_template + {
+ 'sources' : files('test-homed-regression-31896.c'),
+ 'conditions' : ['ENABLE_HOMED'],
+ 'type' : 'manual',
+ },
]
modules += [
--- /dev/null
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+
+#include "bus-locator.h"
+#include "main-func.h"
+#include "tests.h"
+
+static int run(int argc, char **argv) {
+ _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL;
+ _cleanup_(sd_bus_message_unrefp) sd_bus_message *ref = NULL;
+ _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
+ const char *username = NULL;
+
+ /* This is a regression test for the following bug:
+ * https://github.com/systemd/systemd/pull/31896
+ * It is run as part of TEST-46-HOMED
+ */
+
+ test_setup_logging(LOG_DEBUG);
+ assert_se(sd_bus_open_system(&bus) >= 0);
+
+ assert_se(argc == 2);
+ username = argv[1];
+
+ assert_se(bus_call_method(bus, bus_home_mgr, "RefHomeUnrestricted", NULL, &ref, "sb", username, true) >= 0);
+
+ assert_se(bus_call_method_async(bus, NULL, bus_home_mgr, "AuthenticateHome", NULL, NULL, "ss", username, "{}") >= 0);
+ assert_se(sd_bus_flush(bus) >= 0);
+
+ (void) bus_call_method(bus, bus_home_mgr, "ReleaseHome", &error, NULL, "s", username);
+ assert_se(!sd_bus_error_has_name(&error, SD_BUS_ERROR_NO_REPLY)); /* Make sure we didn't crash */
+
+ return 0;
+}
+
+DEFINE_MAIN_FUNCTION(run);
return log_error_errno(SYNTHETIC_ERRNO(EIO),
"Failed to initialize hash context.");
#else
- initialize_libgcrypt(false);
+ r = initialize_libgcrypt(false);
+ if (r < 0)
+ return log_error_errno(r, "Failed to load libgcrypt: %m");
if (gcry_md_open(&j->checksum_ctx, GCRY_MD_SHA256, 0) != 0)
return log_error_errno(SYNTHETIC_ERRNO(EIO),
STATIC_DESTRUCTOR_REGISTER(arg_key_pem, erase_and_freep);
STATIC_DESTRUCTOR_REGISTER(arg_cert_pem, freep);
STATIC_DESTRUCTOR_REGISTER(arg_trust_pem, freep);
+STATIC_DESTRUCTOR_REGISTER(arg_file, strv_freep);
typedef struct RequestMeta {
sd_journal *journal;
}
range_after_eq = startswith(range, "realtime=");
- if (startswith(range, "realtime=")) {
+ if (range_after_eq) {
range_after_eq += strspn(range_after_eq, WHITESPACE);
return request_parse_range_time(m, range_after_eq);
}
}
if (r) {
- sd_id128_t bid;
-
- r = sd_id128_get_boot(&bid);
- if (r < 0) {
- log_error_errno(r, "Failed to get boot ID: %m");
- return MHD_NO;
- }
-
- r = add_match_boot_id(m->journal, bid);
+ r = add_match_boot_id(m->journal, SD_ID128_NULL);
if (r < 0) {
m->argument_parse_error = r;
return MHD_NO;
if (epoll_fd < 0)
return log_error_errno(SYNTHETIC_ERRNO(EUCLEAN), "μhttp epoll fd is invalid");
- r = sd_event_add_io(s->events, &d->io_event,
+ r = sd_event_add_io(s->event, &d->io_event,
epoll_fd, EPOLLIN,
dispatch_http_event, d);
if (r < 0)
if (r < 0)
return log_error_errno(r, "Failed to set source name: %m");
- r = sd_event_add_time(s->events, &d->timer_event,
+ r = sd_event_add_time(s->event, &d->timer_event,
CLOCK_MONOTONIC, UINT64_MAX, 0,
null_timer_event_handler, d);
if (r < 0)
**********************************************************************
**********************************************************************/
-static int setup_signals(RemoteServer *s) {
- int r;
-
- assert(s);
-
- assert_se(sigprocmask_many(SIG_SETMASK, NULL, SIGINT, SIGTERM) >= 0);
-
- r = sd_event_add_signal(s->events, &s->sigterm_event, SIGTERM, NULL, s);
- if (r < 0)
- return r;
-
- r = sd_event_add_signal(s->events, &s->sigint_event, SIGINT, NULL, s);
- if (r < 0)
- return r;
-
- return 0;
-}
-
static int setup_raw_socket(RemoteServer *s, const char *address) {
int fd;
if (r < 0)
return r;
- r = setup_signals(s);
+ r = sd_event_set_signal_exit(s->event, true);
if (r < 0)
- return log_error_errno(r, "Failed to set up signals: %m");
+ return log_error_errno(r, "Failed to install SIGINT/SIGTERM handlers: %m");
n = sd_listen_fds(true);
if (n < 0)
if (r < 0)
return r;
- r = sd_event_set_watchdog(s.events, true);
+ r = sd_event_set_watchdog(s.event, true);
if (r < 0)
return log_error_errno(r, "Failed to enable watchdog: %m");
notify_message = notify_start(NOTIFY_READY, NOTIFY_STOPPING);
while (s.active) {
- r = sd_event_get_state(s.events);
+ r = sd_event_get_state(s.event);
if (r < 0)
return r;
if (r == SD_EVENT_FINISHED)
break;
- r = sd_event_run(s.events, -1);
+ r = sd_event_run(s.event, -1);
if (r < 0)
return log_error_errno(r, "Failed to run event loop: %m");
}
return r;
}
- r = sd_event_add_io(s->events, &source->event,
+ r = sd_event_add_io(s->event, &source->event,
fd, EPOLLIN|EPOLLRDHUP|EPOLLPRI,
dispatch_raw_source_event, source);
if (r == 0) {
/* Add additional source for buffer processing. It will be
* enabled later. */
- r = sd_event_add_defer(s->events, &source->buffer_event,
+ r = sd_event_add_defer(s->event, &source->buffer_event,
dispatch_raw_source_until_block, source);
if (r == 0)
r = sd_event_source_set_enabled(source->buffer_event, SD_EVENT_OFF);
} else if (r == -EPERM) {
log_debug("Falling back to sd_event_add_defer for fd:%d (%s)", fd, name);
- r = sd_event_add_defer(s->events, &source->event,
+ r = sd_event_add_defer(s->event, &source->event,
dispatch_blocking_source_event, source);
if (r == 0)
r = sd_event_source_set_enabled(source->event, SD_EVENT_ON);
assert(s);
assert(fd >= 0);
- r = sd_event_add_io(s->events, &s->listen_event,
+ r = sd_event_add_io(s->event, &s->listen_event,
fd, EPOLLIN,
dispatch_raw_connection_event, s);
if (r < 0)
else
assert_not_reached();
- r = sd_event_default(&s->events);
+ r = sd_event_default(&s->event);
if (r < 0)
return log_error_errno(r, "Failed to allocate event loop: %m");
writer_unref(s->_single_writer);
hashmap_free(s->writers);
- sd_event_source_unref(s->sigterm_event);
- sd_event_source_unref(s->sigint_event);
sd_event_source_unref(s->listen_event);
- sd_event_unref(s->events);
+ sd_event_unref(s->event);
if (s == journal_remote_server_global)
journal_remote_server_global = NULL;
RemoteSource **sources;
size_t active;
- sd_event *events;
- sd_event_source *sigterm_event, *sigint_event, *listen_event;
+ sd_event *event;
+ sd_event_source *listen_event;
Hashmap *writers;
Writer *_single_writer;
else
u->timeout = JOURNAL_UPLOAD_POLL_TIMEOUT;
- r = sd_event_add_io(u->events, &u->input_event,
+ r = sd_event_add_io(u->event, &u->input_event,
fd, events, dispatch_journal_input, u);
if (r < 0)
return log_error_errno(r, "Failed to register input event: %m");
static const char *arg_save_state = NULL;
static usec_t arg_network_timeout_usec = USEC_INFINITY;
+STATIC_DESTRUCTOR_REGISTER(arg_file, strv_freep);
+
static void close_fd_input(Uploader *u);
#define SERVER_ANSWER_KEEP 2048
u->input = fd;
if (arg_follow != 0) {
- r = sd_event_add_io(u->events, &u->input_event,
+ r = sd_event_add_io(u->event, &u->input_event,
fd, EPOLLIN, dispatch_fd_input, u);
if (r < 0) {
if (r != -EPERM || arg_follow > 0)
return r;
}
-static int dispatch_sigterm(sd_event_source *event,
- const struct signalfd_siginfo *si,
- void *userdata) {
- Uploader *u = ASSERT_PTR(userdata);
-
- log_received_signal(LOG_INFO, si);
-
- close_fd_input(u);
- close_journal_input(u);
-
- sd_event_exit(u->events, 0);
- return 0;
-}
-
-static int setup_signals(Uploader *u) {
- int r;
-
- assert(u);
-
- assert_se(sigprocmask_many(SIG_SETMASK, NULL, SIGINT, SIGTERM) >= 0);
-
- r = sd_event_add_signal(u->events, &u->sigterm_event, SIGTERM, dispatch_sigterm, u);
- if (r < 0)
- return r;
-
- r = sd_event_add_signal(u->events, &u->sigint_event, SIGINT, dispatch_sigterm, u);
- if (r < 0)
- return r;
-
- return 0;
-}
-
static int setup_uploader(Uploader *u, const char *url, const char *state_file) {
int r;
const char *host, *proto = "";
u->state_file = state_file;
- r = sd_event_default(&u->events);
+ r = sd_event_default(&u->event);
if (r < 0)
return log_error_errno(r, "sd_event_default failed: %m");
- r = setup_signals(u);
+ r = sd_event_set_signal_exit(u->event, true);
if (r < 0)
- return log_error_errno(r, "Failed to set up signals: %m");
+ return log_error_errno(r, "Failed to install SIGINT/SIGTERM handlers: %m");
(void) sd_watchdog_enabled(false, &u->watchdog_usec);
close_fd_input(u);
close_journal_input(u);
- sd_event_source_unref(u->sigterm_event);
- sd_event_source_unref(u->sigint_event);
- sd_event_unref(u->events);
+ sd_event_unref(u->event);
}
static int perform_upload(Uploader *u) {
if (r < 0)
return r;
- sd_event_set_watchdog(u.events, true);
+ sd_event_set_watchdog(u.event, true);
r = check_cursor_updating(&u);
if (r < 0)
NOTIFY_STOPPING);
for (;;) {
- r = sd_event_get_state(u.events);
+ r = sd_event_get_state(u.event);
if (r < 0)
return r;
if (r == SD_EVENT_FINISHED)
return r;
}
- r = sd_event_run(u.events, u.timeout);
+ r = sd_event_run(u.event, u.timeout);
if (r < 0)
return log_error_errno(r, "Failed to run event loop: %m");
}
} entry_state;
typedef struct Uploader {
- sd_event *events;
- sd_event_source *sigint_event, *sigterm_event;
+ sd_event *event;
char *url;
CURL *easy;
if (r < 0)
return log_error_errno(r, "Failed to add boot ID filter: %m");
- r = sd_journal_add_match(j, "_UID=0", 0);
+ r = sd_journal_add_match(j, "_UID=0", SIZE_MAX);
if (r < 0)
return log_error_errno(r, "Failed to add User ID filter: %m");
assert_cc(0 == LOG_EMERG);
- r = sd_journal_add_match(j, "PRIORITY=0", 0);
+ r = sd_journal_add_match(j, "PRIORITY=0", SIZE_MAX);
if (r < 0)
return log_error_errno(r, "Failed to add Emergency filter: %m");
#include "journalctl.h"
#include "journalctl-authenticate.h"
#include "memstream-util.h"
+#include "path-util.h"
#include "qrcode-util.h"
#include "random-util.h"
+#include "stat-util.h"
#include "terminal-util.h"
#include "tmpfile-util.h"
-static int format_journal_url(
+static int format_key(
const void *seed,
size_t seed_size,
uint64_t start,
uint64_t interval,
- const char *hn,
- sd_id128_t machine,
- bool full,
- char **ret_url) {
+ char **ret) {
_cleanup_(memstream_done) MemStream m = {};
FILE *f;
assert(seed);
assert(seed_size > 0);
+ assert(ret);
f = memstream_init(&m);
if (!f)
return -ENOMEM;
- if (full)
- fputs("fss://", f);
-
for (size_t i = 0; i < seed_size; i++) {
if (i > 0 && i % 3 == 0)
fputc('-', f);
fprintf(f, "/%"PRIx64"-%"PRIx64, start, interval);
- if (full) {
- fprintf(f, "?machine=" SD_ID128_FORMAT_STR, SD_ID128_FORMAT_VAL(machine));
- if (hn)
- fprintf(f, ";hostname=%s", hn);
- }
-
- return memstream_finalize(&m, ret_url, NULL);
+ return memstream_finalize(&m, ret, NULL);
}
int action_setup_keys(void) {
+ _cleanup_(unlink_and_freep) char *tmpfile = NULL;
+ _cleanup_close_ int fd = -EBADF;
+ _cleanup_free_ char *path = NULL;
size_t mpk_size, seed_size, state_size;
- _cleanup_(unlink_and_freep) char *k = NULL;
- _cleanup_free_ char *p = NULL;
uint8_t *mpk, *seed, *state;
- _cleanup_close_ int fd = -EBADF;
sd_id128_t machine, boot;
- struct stat st;
uint64_t n;
int r;
assert(arg_action == ACTION_SETUP_KEYS);
- r = stat("/var/log/journal", &st);
- if (r < 0 && !IN_SET(errno, ENOENT, ENOTDIR))
- return log_error_errno(errno, "stat(\"%s\") failed: %m", "/var/log/journal");
-
- if (r < 0 || !S_ISDIR(st.st_mode)) {
- log_error("%s is not a directory, must be using persistent logging for FSS.",
- "/var/log/journal");
- return r < 0 ? -errno : -ENOTDIR;
- }
+ r = is_dir("/var/log/journal/", /* follow = */ false);
+ if (r == 0)
+ return log_error_errno(SYNTHETIC_ERRNO(ENOTDIR),
+ "/var/log/journal is not a directory, must be using persistent logging for FSS.");
+ if (r == -ENOENT)
+ return log_error_errno(r, "Directory /var/log/journal/ does not exist, must be using persistent logging for FSS.");
+ if (r < 0)
+ return log_error_errno(r, "Failed to check if /var/log/journal/ is a directory: %m");
r = sd_id128_get_machine(&machine);
if (r < 0)
if (r < 0)
return log_error_errno(r, "Failed to get boot ID: %m");
- if (asprintf(&p, "/var/log/journal/" SD_ID128_FORMAT_STR "/fss",
- SD_ID128_FORMAT_VAL(machine)) < 0)
+ path = path_join("/var/log/journal/", SD_ID128_TO_STRING(machine), "/fss");
+ if (!path)
return log_oom();
if (arg_force) {
- r = unlink(p);
- if (r < 0 && errno != ENOENT)
- return log_error_errno(errno, "unlink(\"%s\") failed: %m", p);
- } else if (access(p, F_OK) >= 0)
+ if (unlink(path) < 0 && errno != ENOENT)
+ return log_error_errno(errno, "Failed to remove \"%s\": %m", path);
+ } else if (access(path, F_OK) >= 0)
return log_error_errno(SYNTHETIC_ERRNO(EEXIST),
- "Sealing key file %s exists already. Use --force to recreate.", p);
-
- if (asprintf(&k, "/var/log/journal/" SD_ID128_FORMAT_STR "/fss.tmp.XXXXXX",
- SD_ID128_FORMAT_VAL(machine)) < 0)
- return log_oom();
+ "Sealing key file %s exists already. Use --force to recreate.", path);
mpk_size = FSPRG_mskinbytes(FSPRG_RECOMMENDED_SECPAR);
mpk = alloca_safe(mpk_size);
return log_error_errno(r, "Failed to acquire random seed: %m");
log_info("Generating key pair...");
- FSPRG_GenMK(NULL, mpk, seed, seed_size, FSPRG_RECOMMENDED_SECPAR);
+ r = FSPRG_GenMK(NULL, mpk, seed, seed_size, FSPRG_RECOMMENDED_SECPAR);
+ if (r < 0)
+ return log_error_errno(r, "Failed to generate key pair: %m");
log_info("Generating sealing key...");
- FSPRG_GenState0(state, mpk, seed, seed_size);
+ r = FSPRG_GenState0(state, mpk, seed, seed_size);
+ if (r < 0)
+ return log_error_errno(r, "Failed to generate sealing key: %m");
assert(arg_interval > 0);
-
n = now(CLOCK_REALTIME);
n /= arg_interval;
- safe_close(fd);
- fd = mkostemp_safe(k);
+ fd = open_tmpfile_linkable(path, O_WRONLY|O_CLOEXEC, &tmpfile);
if (fd < 0)
- return log_error_errno(fd, "Failed to open %s: %m", k);
+ return log_error_errno(fd, "Failed to open a temporary file for %s: %m", path);
r = chattr_secret(fd, CHATTR_WARN_UNSUPPORTED_FLAGS);
if (r < 0)
log_full_errno(ERRNO_IS_NOT_SUPPORTED(r) ? LOG_DEBUG : LOG_WARNING,
- r, "Failed to set file attributes on '%s', ignoring: %m", k);
+ r, "Failed to set file attributes on a temporary file for '%s', ignoring: %m", path);
struct FSSHeader h = {
.signature = { 'K', 'S', 'H', 'H', 'R', 'H', 'L', 'P' },
if (r < 0)
return log_error_errno(r, "Failed to write state: %m");
- if (rename(k, p) < 0)
- return log_error_errno(errno, "Failed to link file: %m");
-
- k = mfree(k);
+ r = link_tmpfile(fd, tmpfile, path, /* flags = */ 0);
+ if (r < 0)
+ return log_error_errno(r, "Failed to link file: %m");
- _cleanup_free_ char *hn = NULL, *key = NULL;
+ tmpfile = mfree(tmpfile);
- r = format_journal_url(seed, seed_size, n, arg_interval, hn, machine, false, &key);
+ _cleanup_free_ char *key = NULL;
+ r = format_key(seed, seed_size, n, arg_interval, &key);
if (r < 0)
return r;
- if (on_tty()) {
- hn = gethostname_malloc();
- if (hn)
- hostname_cleanup(hn);
-
- fprintf(stderr,
- "\nNew keys have been generated for host %s%s" SD_ID128_FORMAT_STR ".\n"
- "\n"
- "The %ssecret sealing key%s has been written to the following local file.\n"
- "This key file is automatically updated when the sealing key is advanced.\n"
- "It should not be used on multiple hosts.\n"
- "\n"
- "\t%s\n"
- "\n"
- "The sealing key is automatically changed every %s.\n"
- "\n"
- "Please write down the following %ssecret verification key%s. It should be stored\n"
- "in a safe location and should not be saved locally on disk.\n"
- "\n\t%s",
- strempty(hn), hn ? "/" : "",
- SD_ID128_FORMAT_VAL(machine),
- ansi_highlight(), ansi_normal(),
- p,
- FORMAT_TIMESPAN(arg_interval, 0),
- ansi_highlight(), ansi_normal(),
- ansi_highlight_red());
- fflush(stderr);
+ if (!on_tty()) {
+ /* If we are not on a TTY, show only the key. */
+ puts(key);
+ return 0;
}
+ _cleanup_free_ char *hn = NULL;
+ hn = gethostname_malloc();
+ if (hn)
+ hostname_cleanup(hn);
+
+ fprintf(stderr,
+ "\nNew keys have been generated for host %s%s" SD_ID128_FORMAT_STR ".\n"
+ "\n"
+ "The %ssecret sealing key%s has been written to the following local file.\n"
+ "This key file is automatically updated when the sealing key is advanced.\n"
+ "It should not be used on multiple hosts.\n"
+ "\n"
+ "\t%s\n"
+ "\n"
+ "The sealing key is automatically changed every %s.\n"
+ "\n"
+ "Please write down the following %ssecret verification key%s. It should be stored\n"
+ "in a safe location and should not be saved locally on disk.\n"
+ "\n\t%s",
+ strempty(hn), hn ? "/" : "",
+ SD_ID128_FORMAT_VAL(machine),
+ ansi_highlight(), ansi_normal(),
+ path,
+ FORMAT_TIMESPAN(arg_interval, 0),
+ ansi_highlight(), ansi_normal(),
+ ansi_highlight_red());
+ fflush(stderr);
+
puts(key);
- if (on_tty()) {
- fprintf(stderr, "%s", ansi_normal());
+ fputs(ansi_normal(), stderr);
+
#if HAVE_QRENCODE
- _cleanup_free_ char *url = NULL;
- r = format_journal_url(seed, seed_size, n, arg_interval, hn, machine, true, &url);
- if (r < 0)
- return r;
-
- (void) print_qrcode(stderr,
- "To transfer the verification key to your phone scan the QR code below",
- url);
+ _cleanup_free_ char *url = NULL;
+ url = strjoin("fss://", key, "?machine=", SD_ID128_TO_STRING(machine), hn ? ";hostname=" : "", hn);
+ if (!url)
+ return log_oom();
+
+ (void) print_qrcode(stderr,
+ "To transfer the verification key to your phone scan the QR code below",
+ url);
#endif
- }
return 0;
}
if (!arg_dmesg)
return 0;
- r = sd_journal_add_match(j, "_TRANSPORT=kernel",
- STRLEN("_TRANSPORT=kernel"));
+ r = sd_journal_add_match(j, "_TRANSPORT=kernel", SIZE_MAX);
if (r < 0)
- return log_error_errno(r, "Failed to add match: %m");
-
- r = sd_journal_add_conjunction(j);
- if (r < 0)
- return log_error_errno(r, "Failed to add conjunction: %m");
+ return r;
- return 0;
+ return sd_journal_add_conjunction(j);
}
static int get_possible_units(
sd_journal *j,
const char *fields,
char **patterns,
- Set **units) {
+ Set **ret) {
- _cleanup_set_free_free_ Set *found = NULL;
+ _cleanup_set_free_ Set *found = NULL;
int r;
- found = set_new(&string_hash_ops);
- if (!found)
- return -ENOMEM;
+ assert(j);
+ assert(fields);
+ assert(ret);
NULSTR_FOREACH(field, fields) {
const void *data;
return r;
SD_JOURNAL_FOREACH_UNIQUE(j, data, size) {
- char *eq;
- size_t prefix;
_cleanup_free_ char *u = NULL;
+ char *eq;
eq = memchr(data, '=', size);
- if (eq)
- prefix = eq - (char*) data + 1;
- else
- prefix = 0;
+ if (eq) {
+ size -= eq - (char*) data + 1;
+ data = ++eq;
+ }
- u = strndup((char*) data + prefix, size - prefix);
+ u = strndup(data, size);
if (!u)
return -ENOMEM;
- STRV_FOREACH(pattern, patterns)
- if (fnmatch(*pattern, u, FNM_NOESCAPE) == 0) {
- log_debug("Matched %s with pattern %s=%s", u, field, *pattern);
+ size_t i;
+ if (!strv_fnmatch_full(patterns, u, FNM_NOESCAPE, &i))
+ continue;
- r = set_consume(found, u);
- u = NULL;
- if (r < 0 && r != -EEXIST)
- return r;
-
- break;
- }
+ log_debug("Matched %s with pattern %s=%s", u, field, patterns[i]);
+ r = set_ensure_consume(&found, &string_hash_ops_free, TAKE_PTR(u));
+ if (r < 0)
+ return r;
}
}
- *units = TAKE_PTR(found);
-
+ *ret = TAKE_PTR(found);
return 0;
}
static int add_units(sd_journal *j) {
_cleanup_strv_free_ char **patterns = NULL;
- int r, count = 0;
+ bool added = false;
+ int r;
assert(j);
+ if (strv_isempty(arg_system_units) && strv_isempty(arg_user_units))
+ return 0;
+
STRV_FOREACH(i, arg_system_units) {
_cleanup_free_ char *u = NULL;
return r;
if (string_is_glob(u)) {
- r = strv_push(&patterns, u);
+ r = strv_consume(&patterns, TAKE_PTR(u));
if (r < 0)
return r;
- u = NULL;
} else {
r = add_matches_for_unit(j, u);
if (r < 0)
r = sd_journal_add_disjunction(j);
if (r < 0)
return r;
- count++;
+ added = true;
}
}
if (!strv_isempty(patterns)) {
- _cleanup_set_free_free_ Set *units = NULL;
+ _cleanup_set_free_ Set *units = NULL;
char *u;
r = get_possible_units(j, SYSTEM_UNITS, patterns, &units);
r = sd_journal_add_disjunction(j);
if (r < 0)
return r;
- count++;
+ added = true;
}
}
return r;
if (string_is_glob(u)) {
- r = strv_push(&patterns, u);
+ r = strv_consume(&patterns, TAKE_PTR(u));
if (r < 0)
return r;
- u = NULL;
} else {
r = add_matches_for_user_unit(j, u, getuid());
if (r < 0)
r = sd_journal_add_disjunction(j);
if (r < 0)
return r;
- count++;
+ added = true;
}
}
if (!strv_isempty(patterns)) {
- _cleanup_set_free_free_ Set *units = NULL;
+ _cleanup_set_free_ Set *units = NULL;
char *u;
r = get_possible_units(j, USER_UNITS, patterns, &units);
r = sd_journal_add_disjunction(j);
if (r < 0)
return r;
- count++;
+ added = true;
}
}
- /* Complain if the user request matches but nothing whatsoever was
- * found, since otherwise everything would be matched. */
- if (!(strv_isempty(arg_system_units) && strv_isempty(arg_user_units)) && count == 0)
+ /* Complain if the user request matches but nothing whatsoever was found, since otherwise everything
+ * would be matched. */
+ if (!added)
return -ENODATA;
- r = sd_journal_add_conjunction(j);
- if (r < 0)
- return r;
-
- return 0;
+ return sd_journal_add_conjunction(j);
}
static int add_syslog_identifier(sd_journal *j) {
assert(j);
- STRV_FOREACH(i, arg_syslog_identifier) {
- _cleanup_free_ char *u = NULL;
+ if (strv_isempty(arg_syslog_identifier))
+ return 0;
- u = strjoin("SYSLOG_IDENTIFIER=", *i);
- if (!u)
- return -ENOMEM;
- r = sd_journal_add_match(j, u, 0);
+ STRV_FOREACH(i, arg_syslog_identifier) {
+ r = journal_add_match_pair(j, "SYSLOG_IDENTIFIER", *i);
if (r < 0)
return r;
r = sd_journal_add_disjunction(j);
return r;
}
- r = sd_journal_add_conjunction(j);
- if (r < 0)
- return r;
-
- return 0;
+ return sd_journal_add_conjunction(j);
}
static int add_exclude_identifier(sd_journal *j) {
}
static int add_priorities(sd_journal *j) {
- char match[] = "PRIORITY=0";
- int i, r;
+ int r;
assert(j);
- if (arg_priorities == 0xFF)
+ if (arg_priorities == 0)
return 0;
- for (i = LOG_EMERG; i <= LOG_DEBUG; i++)
+ for (int i = LOG_EMERG; i <= LOG_DEBUG; i++)
if (arg_priorities & (1 << i)) {
- match[sizeof(match)-2] = '0' + i;
-
- r = sd_journal_add_match(j, match, strlen(match));
+ r = journal_add_matchf(j, "PRIORITY=%d", i);
if (r < 0)
- return log_error_errno(r, "Failed to add match: %m");
+ return r;
}
- r = sd_journal_add_conjunction(j);
- if (r < 0)
- return log_error_errno(r, "Failed to add conjunction: %m");
-
- return 0;
+ return sd_journal_add_conjunction(j);
}
static int add_facilities(sd_journal *j) {
- void *p;
int r;
+ assert(j);
+
+ if (set_isempty(arg_facilities))
+ return 0;
+
+ void *p;
SET_FOREACH(p, arg_facilities) {
- char match[STRLEN("SYSLOG_FACILITY=") + DECIMAL_STR_MAX(int)];
+ r = journal_add_matchf(j, "SYSLOG_FACILITY=%d", PTR_TO_INT(p));
+ if (r < 0)
+ return r;
+ }
+
+ return sd_journal_add_conjunction(j);
+}
+
+static int add_matches_for_executable(sd_journal *j, const char *path) {
+ _cleanup_free_ char *interpreter = NULL;
+ int r;
- xsprintf(match, "SYSLOG_FACILITY=%d", PTR_TO_INT(p));
+ assert(j);
+ assert(path);
+
+ if (executable_is_script(path, &interpreter) > 0) {
+ _cleanup_free_ char *comm = NULL;
+
+ r = path_extract_filename(path, &comm);
+ if (r < 0)
+ return log_error_errno(r, "Failed to extract filename of '%s': %m", path);
- r = sd_journal_add_match(j, match, strlen(match));
+ r = journal_add_match_pair(j, "_COMM", strshorten(comm, TASK_COMM_LEN-1));
+ if (r < 0)
+ return log_error_errno(r, "Failed to add match: %m");
+
+ /* Append _EXE only if the interpreter is not a link. Otherwise, it might be outdated often. */
+ path = is_symlink(interpreter) > 0 ? interpreter : NULL;
+ }
+
+ if (path) {
+ r = journal_add_match_pair(j, "_EXE", path);
if (r < 0)
return log_error_errno(r, "Failed to add match: %m");
}
static int add_matches_for_device(sd_journal *j, const char *devpath) {
_cleanup_(sd_device_unrefp) sd_device *device = NULL;
- sd_device *d = NULL;
- struct stat st;
int r;
assert(j);
assert(devpath);
- if (!path_startswith(devpath, "/dev/"))
- return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
- "Devpath does not start with /dev/");
-
- if (stat(devpath, &st) < 0)
- return log_error_errno(errno, "Couldn't stat file: %m");
-
- r = sd_device_new_from_stat_rdev(&device, &st);
+ r = sd_device_new_from_devname(&device, devpath);
if (r < 0)
- return log_error_errno(r, "Failed to get device from devnum " DEVNUM_FORMAT_STR ": %m", DEVNUM_FORMAT_VAL(st.st_rdev));
+ return log_error_errno(r, "Failed to get device '%s': %m", devpath);
- for (d = device; d; ) {
- _cleanup_free_ char *match = NULL;
- const char *subsys, *sysname, *devnode;
- sd_device *parent;
+ for (sd_device *d = device; d; ) {
+ const char *subsys, *sysname;
r = sd_device_get_subsystem(d, &subsys);
if (r < 0)
if (r < 0)
goto get_parent;
- match = strjoin("_KERNEL_DEVICE=+", subsys, ":", sysname);
- if (!match)
- return log_oom();
-
- r = sd_journal_add_match(j, match, 0);
+ r = journal_add_matchf(j, "_KERNEL_DEVICE=+%s:%s", subsys, sysname);
if (r < 0)
return log_error_errno(r, "Failed to add match: %m");
- if (sd_device_get_devname(d, &devnode) >= 0) {
- _cleanup_free_ char *match1 = NULL;
-
- r = stat(devnode, &st);
- if (r < 0)
- return log_error_errno(r, "Failed to stat() device node \"%s\": %m", devnode);
-
- r = asprintf(&match1, "_KERNEL_DEVICE=%c" DEVNUM_FORMAT_STR, S_ISBLK(st.st_mode) ? 'b' : 'c', DEVNUM_FORMAT_VAL(st.st_rdev));
- if (r < 0)
- return log_oom();
-
- r = sd_journal_add_match(j, match1, 0);
+ dev_t devnum;
+ if (sd_device_get_devnum(d, &devnum) >= 0) {
+ r = journal_add_matchf(j, "_KERNEL_DEVICE=%c" DEVNUM_FORMAT_STR,
+ streq(subsys, "block") ? 'b' : 'c',
+ DEVNUM_FORMAT_VAL(devnum));
if (r < 0)
return log_error_errno(r, "Failed to add match: %m");
}
get_parent:
- if (sd_device_get_parent(d, &parent) < 0)
+ if (sd_device_get_parent(d, &d) < 0)
break;
-
- d = parent;
}
- r = add_match_this_boot(j, arg_machine);
+ return add_match_boot_id(j, SD_ID128_NULL);
+}
+
+static int add_matches_for_path(sd_journal *j, const char *path) {
+ _cleanup_free_ char *p = NULL;
+ struct stat st;
+ int r;
+
+ assert(j);
+ assert(path);
+
+ if (arg_root || arg_machine)
+ return log_error_errno(SYNTHETIC_ERRNO(EOPNOTSUPP),
+ "An extra path in match filter is currently not supported with --root, --image, or -M/--machine.");
+
+ r = chase_and_stat(path, NULL, 0, &p, &st);
if (r < 0)
- return log_error_errno(r, "Failed to add match for the current boot: %m");
+ return log_error_errno(r, "Couldn't canonicalize path '%s': %m", path);
- return 0;
+ if (S_ISREG(st.st_mode) && (0111 & st.st_mode))
+ return add_matches_for_executable(j, p);
+
+ if (S_ISCHR(st.st_mode) || S_ISBLK(st.st_mode))
+ return add_matches_for_device(j, p);
+
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "File is neither a device node nor executable: %s", p);
}
static int add_matches(sd_journal *j, char **args) {
bool have_term = false;
+ int r;
assert(j);
- STRV_FOREACH(i, args) {
- int r;
+ if (strv_isempty(args))
+ return 0;
+ STRV_FOREACH(i, args)
if (streq(*i, "+")) {
if (!have_term)
break;
+
r = sd_journal_add_disjunction(j);
+ if (r < 0)
+ return log_error_errno(r, "Failed to add disjunction: %m");
+
have_term = false;
} else if (path_is_absolute(*i)) {
- _cleanup_free_ char *p = NULL, *t = NULL, *t2 = NULL, *interpreter = NULL;
- struct stat st;
-
- r = chase(*i, NULL, CHASE_TRAIL_SLASH, &p, NULL);
+ r = add_matches_for_path(j, *i);
if (r < 0)
- return log_error_errno(r, "Couldn't canonicalize path: %m");
-
- if (lstat(p, &st) < 0)
- return log_error_errno(errno, "Couldn't stat file: %m");
-
- if (S_ISREG(st.st_mode) && (0111 & st.st_mode)) {
- if (executable_is_script(p, &interpreter) > 0) {
- _cleanup_free_ char *comm = NULL;
-
- r = path_extract_filename(p, &comm);
- if (r < 0)
- return log_error_errno(r, "Failed to extract filename of '%s': %m", p);
-
- t = strjoin("_COMM=", strshorten(comm, TASK_COMM_LEN-1));
- if (!t)
- return log_oom();
-
- /* Append _EXE only if the interpreter is not a link.
- Otherwise, it might be outdated often. */
- if (lstat(interpreter, &st) == 0 && !S_ISLNK(st.st_mode)) {
- t2 = strjoin("_EXE=", interpreter);
- if (!t2)
- return log_oom();
- }
- } else {
- t = strjoin("_EXE=", p);
- if (!t)
- return log_oom();
- }
-
- r = sd_journal_add_match(j, t, 0);
-
- if (r >=0 && t2)
- r = sd_journal_add_match(j, t2, 0);
-
- } else if (S_ISCHR(st.st_mode) || S_ISBLK(st.st_mode)) {
- r = add_matches_for_device(j, p);
- if (r < 0)
- return r;
- } else
- return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
- "File is neither a device node, nor regular file, nor executable: %s",
- *i);
-
+ return r;
have_term = true;
+
} else {
- r = sd_journal_add_match(j, *i, 0);
+ r = sd_journal_add_match(j, *i, SIZE_MAX);
+ if (r < 0)
+ return log_error_errno(r, "Failed to add match '%s': %m", *i);
have_term = true;
}
- if (r < 0)
- return log_error_errno(r, "Failed to add match '%s': %m", *i);
- }
-
- if (!strv_isempty(args) && !have_term)
- return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
- "\"+\" can only be used between terms");
+ if (!have_term)
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "\"+\" can only be used between terms.");
return 0;
}
r = add_dmesg(j);
if (r < 0)
- return r;
+ return log_error_errno(r, "Failed to add filter for dmesg: %m");
r = add_units(j);
if (r < 0)
r = add_priorities(j);
if (r < 0)
- return r;
+ return log_error_errno(r, "Failed to add filter for priorities: %m");
r = add_facilities(j);
if (r < 0)
- return r;
+ return log_error_errno(r, "Failed to add filter for facilities: %m");
r = add_matches(j, matches);
if (r < 0)
int action_list_namespaces(void) {
_cleanup_(table_unrefp) Table *table = NULL;
sd_id128_t machine;
- char machine_id[SD_ID128_STRING_MAX];
int r;
assert(arg_action == ACTION_LIST_NAMESPACES);
if (r < 0)
return log_error_errno(r, "Failed to get machine ID: %m");
- sd_id128_to_string(machine, machine_id);
-
table = table_new("namespace");
if (!table)
return log_oom();
}
FOREACH_DIRENT(de, dirp, return log_error_errno(errno, "Failed to iterate through %s: %m", path)) {
- char *dot;
- if (!startswith(de->d_name, machine_id))
+ const char *e = strchr(de->d_name, '.');
+ if (!e)
+ continue;
+
+ _cleanup_free_ char *ids = strndup(de->d_name, e - de->d_name);
+ if (!ids)
+ return log_oom();
+
+ sd_id128_t id;
+ r = sd_id128_from_string(ids, &id);
+ if (r < 0)
continue;
- dot = strchr(de->d_name, '.');
- if (!dot)
+ if (!sd_id128_equal(machine, id))
continue;
- if (!log_namespace_name_valid(dot + 1))
+ e++;
+
+ if (!log_namespace_name_valid(e))
continue;
- r = table_add_cell(table, NULL, TABLE_STRING, dot + 1);
+ r = table_add_cell(table, NULL, TABLE_STRING, e);
if (r < 0)
return table_log_add_error(r);
}
}
- r = table_print_with_pager(table, arg_json_format_flags, arg_pager_flags, !arg_quiet);
- if (r < 0)
- return table_log_print_error(r);
+ if (table_isempty(table) && FLAGS_SET(arg_json_format_flags, JSON_FORMAT_OFF)) {
+ if (!arg_quiet)
+ log_notice("No namespaces found.");
+ } else {
+ r = table_print_with_pager(table, arg_json_format_flags, arg_pager_flags, !arg_quiet);
+ if (r < 0)
+ return r;
+ }
return 0;
}
/* SPDX-License-Identifier: LGPL-2.1-or-later */
+#include <unistd.h>
+
#include "sd-event.h"
#include "fileio.h"
#include "logs-show.h"
#include "terminal-util.h"
-#define PROCESS_INOTIFY_INTERVAL 1024 /* Every 1,024 messages processed */
+#define PROCESS_INOTIFY_INTERVAL 1024 /* Every 1024 messages processed */
typedef struct Context {
sd_journal *journal;
/* SPDX-License-Identifier: LGPL-2.1-or-later */
+#include <unistd.h>
+
#include "journal-util.h"
#include "journalctl.h"
#include "journalctl-util.h"
const char *arg_directory = NULL;
char **arg_file = NULL;
bool arg_file_stdin = false;
-int arg_priorities = 0xFF;
+int arg_priorities = 0;
Set *arg_facilities = NULL;
char *arg_verify_key = NULL;
#if HAVE_GCRYPT
" -N --fields List all field names currently used\n"
" -F --field=FIELD List all values that a specified field takes\n"
" --list-boots Show terse information about recorded boots\n"
+ " --list-namespaces Show list of journal namespaces\n"
" --disk-usage Show total disk usage of all journal files\n"
" --vacuum-size=BYTES Reduce disk usage below specified size\n"
" --vacuum-files=INT Leave only the specified number of journal files\n"
journalctl_link_with = [libshared]
else
journalctl_link_with = [
- libbasic_gcrypt,
libshared_static,
libsystemd_static,
]
. /usr/lib/os-release
fi
-[ -n "$PRETTY_NAME" ] || PRETTY_NAME="Linux"
+[ -n "$PRETTY_NAME" ] || PRETTY_NAME="Linux $KERNEL_VERSION"
SORT_KEY="$IMAGE_ID"
[ -z "$SORT_KEY" ] && SORT_KEY="$ID"
{
echo "# Boot Loader Specification type#1 entry"
echo "# File created by $0 (systemd {{VERSION_TAG}})"
- echo "title $PRETTY_NAME $KERNEL_VERSION"
+ echo "title $PRETTY_NAME"
echo "version $KERNEL_VERSION"
if [ "$ENTRY_TOKEN" = "$MACHINE_ID" ]; then
# See similar logic above for the systemd.machine_id= kernel command line option
Copyright © 2013 Intel Corporation. All rights reserved.
***/
+/* Make sure the net/if.h header is included before any linux/ one */
+#include <net/if.h>
#include <errno.h>
+#include <linux/filter.h>
+#include <linux/if_infiniband.h>
+#include <linux/if_packet.h>
#include <net/ethernet.h>
-#include <net/if.h>
#include <net/if_arp.h>
#include <stdio.h>
#include <string.h>
-#include <linux/filter.h>
-#include <linux/if_infiniband.h>
-#include <linux/if_packet.h>
#include "dhcp-network.h"
#include "dhcp-protocol.h"
bool send_release;
};
-int dhcp6_network_bind_udp_socket(int ifindex, struct in6_addr *address);
-int dhcp6_network_send_udp_socket(int s, struct in6_addr *address,
- const void *packet, size_t len);
+int dhcp6_network_bind_udp_socket(int ifindex, const struct in6_addr *address);
+int dhcp6_network_send_udp_socket(int s, const struct in6_addr *address, const void *packet, size_t len);
int dhcp6_client_send_message(sd_dhcp6_client *client);
int dhcp6_client_set_transaction_id(sd_dhcp6_client *client, uint32_t transaction_id);
#include "fd-util.h"
#include "socket-util.h"
-int dhcp6_network_bind_udp_socket(int ifindex, struct in6_addr *local_address) {
+int dhcp6_network_bind_udp_socket(int ifindex, const struct in6_addr *local_address) {
union sockaddr_union src = {
.in6.sin6_family = AF_INET6,
+ .in6.sin6_addr = *ASSERT_PTR(local_address),
.in6.sin6_port = htobe16(DHCP6_PORT_CLIENT),
.in6.sin6_scope_id = ifindex,
};
int r;
assert(ifindex > 0);
- assert(local_address);
-
- src.in6.sin6_addr = *local_address;
s = socket(AF_INET6, SOCK_DGRAM | SOCK_CLOEXEC | SOCK_NONBLOCK, IPPROTO_UDP);
if (s < 0)
return TAKE_FD(s);
}
-int dhcp6_network_send_udp_socket(int s, struct in6_addr *server_address,
- const void *packet, size_t len) {
+int dhcp6_network_send_udp_socket(int s, const struct in6_addr *server_address, const void *packet, size_t len) {
union sockaddr_union dest = {
.in6.sin6_family = AF_INET6,
+ .in6.sin6_addr = *ASSERT_PTR(server_address),
.in6.sin6_port = htobe16(DHCP6_PORT_SERVER),
};
- int r;
-
- assert(server_address);
- memcpy(&dest.in6.sin6_addr, server_address, sizeof(dest.in6.sin6_addr));
-
- r = sendto(s, packet, len, 0, &dest.sa, sizeof(dest.in6));
- if (r < 0)
+ if (sendto(s, packet, len, 0, &dest.sa, sizeof(dest.in6)) < 0)
return -errno;
return 0;
#define DHCP6_MIN_OPTIONS_SIZE \
1280 - sizeof(struct ip6_hdr) - sizeof(struct udphdr)
-#define IN6ADDR_ALL_DHCP6_RELAY_AGENTS_AND_SERVERS_INIT \
- { { { 0xff, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x02 } } }
+#define IN6_ADDR_ALL_DHCP6_RELAY_AGENTS_AND_SERVERS \
+ ((const struct in6_addr) { { { \
+ 0xff, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x02, \
+ } } } )
enum {
DHCP6_PORT_SERVER = 547,
static int test_dhcp_fd[2] = EBADF_PAIR;
-int dhcp6_network_send_udp_socket(int s, struct in6_addr *server_address, const void *packet, size_t len) {
+int dhcp6_network_send_udp_socket(int s, const struct in6_addr *server_address, const void *packet, size_t len) {
return len;
}
-int dhcp6_network_bind_udp_socket(int index, struct in6_addr *local_address) {
+int dhcp6_network_bind_udp_socket(int index, const struct in6_addr *local_address) {
assert_se(socketpair(AF_UNIX, SOCK_STREAM | SOCK_CLOEXEC | SOCK_NONBLOCK, 0, test_dhcp_fd) >= 0);
return TAKE_FD(test_dhcp_fd[0]);
}
#include <unistd.h>
#include "sd-ndisc.h"
+#include "sd-radv.h"
#include "alloc-util.h"
#include "fd-util.h"
assert_se(write(test_fd[1], data, size) == (ssize_t) size);
(void) sd_event_run(e, UINT64_MAX);
assert_se(sd_ndisc_stop(nd) >= 0);
- close(test_fd[1]);
+ test_fd[1] = safe_close(test_fd[1]);
+ TAKE_FD(test_fd[0]); /* It should be already closed by sd_ndisc_stop(). */
}
-static void test_with_icmp6_packet(const uint8_t *data, size_t size) {
- static const struct sockaddr_in6 dst = {
- .sin6_family = AF_INET6,
- .sin6_addr = IN6ADDR_ALL_ROUTERS_MULTICAST_INIT,
+static void test_with_sd_radv(const uint8_t *data, size_t size) {
+ struct ether_addr mac_addr = {
+ .ether_addr_octet = {'A', 'B', 'C', '1', '2', '3'}
};
+ _cleanup_(sd_event_unrefp) sd_event *e = NULL;
+ _cleanup_(sd_radv_unrefp) sd_radv *ra = NULL;
+
+ assert_se(socketpair(AF_UNIX, SOCK_SEQPACKET | SOCK_CLOEXEC | SOCK_NONBLOCK, 0, test_fd) >= 0);
+ assert_se(sd_event_new(&e) >= 0);
+ assert_se(sd_radv_new(&ra) >= 0);
+ assert_se(sd_radv_attach_event(ra, e, 0) >= 0);
+ assert_se(sd_radv_set_ifindex(ra, 42) >= 0);
+ assert_se(sd_radv_set_mac(ra, &mac_addr) >= 0);
+ assert_se(sd_radv_start(ra) >= 0);
+ assert_se(write(test_fd[0], data, size) == (ssize_t) size);
+ (void) sd_event_run(e, UINT64_MAX);
+ assert_se(sd_radv_stop(ra) >= 0);
+ test_fd[0] = safe_close(test_fd[0]);
+ TAKE_FD(test_fd[1]); /* It should be already closed by sd_radv_stop(). */
+}
+
+static void test_with_icmp6_packet(const uint8_t *data, size_t size) {
_cleanup_close_pair_ int fd_pair[2] = EBADF_PAIR;
_cleanup_(icmp6_packet_unrefp) ICMP6Packet *packet = NULL;
_cleanup_set_free_ Set *options = NULL;
if (ndisc_parse_options(packet, &options) < 0)
return;
- if (ndisc_send(fd_pair[1], &dst, icmp6_packet_get_header(packet), options) < 0)
+ if (ndisc_send(fd_pair[1], &IN6_ADDR_ALL_ROUTERS_MULTICAST,
+ icmp6_packet_get_header(packet), options, /* timestamp = */ 0) < 0)
return;
packet = icmp6_packet_unref(packet);
if (icmp6_packet_receive(fd_pair[0], &packet) < 0)
return;
- (void) ndisc_parse_options(packet, &options);
+ assert_se(ndisc_parse_options(packet, &options) >= 0);
}
int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) {
fuzz_setup_logging();
test_with_sd_ndisc(data, size);
+ test_with_sd_radv(data, size);
test_with_icmp6_packet(data, size);
return 0;
}
return p;
}
+int icmp6_packet_set_sender_address(ICMP6Packet *p, const struct in6_addr *addr) {
+ assert(p);
+
+ if (addr)
+ p->sender_address = *addr;
+ else
+ p->sender_address = (const struct in6_addr) {};
+
+ return 0;
+}
+
int icmp6_packet_get_sender_address(ICMP6Packet *p, struct in6_addr *ret) {
assert(p);
ICMP6Packet* icmp6_packet_unref(ICMP6Packet *p);
DEFINE_TRIVIAL_CLEANUP_FUNC(ICMP6Packet*, icmp6_packet_unref);
+int icmp6_packet_set_sender_address(ICMP6Packet *p, const struct in6_addr *addr);
int icmp6_packet_get_sender_address(ICMP6Packet *p, struct in6_addr *ret);
int icmp6_packet_get_timestamp(ICMP6Packet *p, clockid_t clock, usec_t *ret);
const struct icmp6_hdr* icmp6_packet_get_header(ICMP6Packet *p);
return test_fd[is_router];
}
-int icmp6_send(int fd, const struct sockaddr_in6 *dst, const struct iovec *iov, size_t n_iov) {
+int icmp6_send(int fd, const struct in6_addr *dst, const struct iovec *iov, size_t n_iov) {
return writev(fd, iov, n_iov);
}
Copyright © 2014 Intel Corporation. All rights reserved.
***/
+/* Make sure the net/if.h header is included before any linux/ one */
+#include <net/if.h>
#include <errno.h>
+#include <linux/if_packet.h>
#include <netinet/icmp6.h>
#include <netinet/in.h>
#include <netinet/ip6.h>
#include <string.h>
#include <sys/types.h>
#include <unistd.h>
-#include <net/if.h>
-#include <linux/if_packet.h>
#include "fd-util.h"
#include "icmp6-util.h"
ICMP6_FILTER_SETBLOCKALL(&filter);
if (is_router) {
mreq = (struct ipv6_mreq) {
- .ipv6mr_multiaddr = IN6ADDR_ALL_ROUTERS_MULTICAST_INIT,
+ .ipv6mr_multiaddr = IN6_ADDR_ALL_ROUTERS_MULTICAST,
.ipv6mr_interface = ifindex,
};
ICMP6_FILTER_SETPASS(ND_ROUTER_SOLICIT, &filter);
} else {
mreq = (struct ipv6_mreq) {
- .ipv6mr_multiaddr = IN6ADDR_ALL_NODES_MULTICAST_INIT,
+ .ipv6mr_multiaddr = IN6_ADDR_ALL_NODES_MULTICAST,
.ipv6mr_interface = ifindex,
};
ICMP6_FILTER_SETPASS(ND_ROUTER_ADVERT, &filter);
+ ICMP6_FILTER_SETPASS(ND_NEIGHBOR_ADVERT, &filter);
+ ICMP6_FILTER_SETPASS(ND_REDIRECT, &filter);
}
s = socket(AF_INET6, SOCK_RAW | SOCK_CLOEXEC | SOCK_NONBLOCK, IPPROTO_ICMPV6);
return TAKE_FD(s);
}
-int icmp6_send(int fd, const struct sockaddr_in6 *dst, const struct iovec *iov, size_t n_iov) {
+int icmp6_send(int fd, const struct in6_addr *dst, const struct iovec *iov, size_t n_iov) {
+ struct sockaddr_in6 sa = {
+ .sin6_family = AF_INET6,
+ .sin6_addr = *ASSERT_PTR(dst),
+ };
struct msghdr msg = {
- .msg_name = (struct sockaddr_in6*) dst,
+ .msg_name = &sa,
.msg_namelen = sizeof(struct sockaddr_in6),
.msg_iov = (struct iovec*) iov,
.msg_iovlen = n_iov,
};
+ assert(fd >= 0);
+
if (sendmsg(fd, &msg, 0) < 0)
return -errno;
#include "time-util.h"
-#define IN6ADDR_ALL_ROUTERS_MULTICAST_INIT \
- { { { 0xff, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02 } } }
+#define IN6_ADDR_ALL_ROUTERS_MULTICAST \
+ ((const struct in6_addr) { { { \
+ 0xff, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, \
+ } } } )
-#define IN6ADDR_ALL_NODES_MULTICAST_INIT \
- { { { 0xff, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01 } } }
+#define IN6_ADDR_ALL_NODES_MULTICAST \
+ ((const struct in6_addr) { { { \
+ 0xff, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, \
+ } } } )
int icmp6_bind(int ifindex, bool is_router);
-int icmp6_send(int fd, const struct sockaddr_in6 *dst, const struct iovec *iov, size_t n_iov);
+int icmp6_send(int fd, const struct in6_addr *dst, const struct iovec *iov, size_t n_iov);
int icmp6_receive(
int fd,
void *buffer,
'sd-lldp-rx.c',
'sd-lldp-tx.c',
'sd-ndisc.c',
+ 'sd-ndisc-neighbor.c',
+ 'sd-ndisc-redirect.c',
'sd-ndisc-router.c',
+ 'sd-ndisc-router-solicit.c',
'sd-radv.c',
)
'icmp6-util-unix.c',
),
},
+ network_test_template + {
+ 'sources' : files('test-ndisc-send.c'),
+ 'type' : 'manual',
+ },
network_test_template + {
'sources' : files('test-sd-dhcp-lease.c'),
},
--- /dev/null
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+#pragma once
+
+#include "sd-ndisc.h"
+
+#include "icmp6-packet.h"
+#include "set.h"
+
+struct sd_ndisc_neighbor {
+ unsigned n_ref;
+
+ ICMP6Packet *packet;
+
+ uint32_t flags;
+ struct in6_addr target_address;
+
+ Set *options;
+};
+
+sd_ndisc_neighbor* ndisc_neighbor_new(ICMP6Packet *packet);
+int ndisc_neighbor_parse(sd_ndisc *nd, sd_ndisc_neighbor *na);
case SD_NDISC_OPTION_TARGET_LL_ADDRESS:
case SD_NDISC_OPTION_REDIRECTED_HEADER:
case SD_NDISC_OPTION_MTU:
+ case SD_NDISC_OPTION_HOME_AGENT:
case SD_NDISC_OPTION_FLAGS_EXTENSION:
case SD_NDISC_OPTION_CAPTIVE_PORTAL:
/* These options cannot be specified multiple times. */
case SD_NDISC_OPTION_TARGET_LL_ADDRESS:
case SD_NDISC_OPTION_REDIRECTED_HEADER:
case SD_NDISC_OPTION_MTU:
+ case SD_NDISC_OPTION_HOME_AGENT:
case SD_NDISC_OPTION_FLAGS_EXTENSION:
case SD_NDISC_OPTION_CAPTIVE_PORTAL:
break;
ndisc_option_free);
static int ndisc_option_consume(Set **options, sd_ndisc_option *p) {
+ assert(options);
+ assert(p);
+
if (set_size(*options) >= MAX_OPTIONS) {
ndisc_option_free(p);
return -ETOOMANYREFS; /* recognizable error code */
return set_ensure_consume(options, &ndisc_option_hash_ops, p);
}
-int ndisc_option_add_raw(Set **options, size_t offset, size_t length, const uint8_t *bytes) {
+int ndisc_option_set_raw(Set **options, size_t length, const uint8_t *bytes) {
_cleanup_free_ uint8_t *copy = NULL;
assert(options);
if (!copy)
return -ENOMEM;
- sd_ndisc_option *p = ndisc_option_new(/* type = */ 0, offset);
+ sd_ndisc_option *p = ndisc_option_new(/* type = */ 0, /* offset = */ 0);
if (!p)
return -ENOMEM;
return 0;
}
-int ndisc_option_add_link_layer_address(Set **options, uint8_t opt, size_t offset, const struct ether_addr *mac) {
+int ndisc_option_add_link_layer_address(Set **options, uint8_t type, size_t offset, const struct ether_addr *mac) {
assert(options);
- assert(IN_SET(opt, SD_NDISC_OPTION_SOURCE_LL_ADDRESS, SD_NDISC_OPTION_TARGET_LL_ADDRESS));
- assert(mac);
+ assert(IN_SET(type, SD_NDISC_OPTION_SOURCE_LL_ADDRESS, SD_NDISC_OPTION_TARGET_LL_ADDRESS));
- if (ether_addr_is_null(mac))
- return -EINVAL;
+ if (!mac || ether_addr_is_null(mac)) {
+ ndisc_option_remove_by_type(*options, type);
+ return 0;
+ }
- sd_ndisc_option *p = ndisc_option_new(opt, offset);
+ sd_ndisc_option *p = ndisc_option_get_by_type(*options, type);
+ if (p) {
+ /* offset == 0 means that we are now building a packet to be sent, and in that case we allow
+ * to override the option we previously set.
+ * offset != 0 means that we are now parsing a received packet, and we refuse to override
+ * conflicting options. */
+ if (offset != 0)
+ return -EEXIST;
+
+ p->mac = *mac;
+ return 0;
+ }
+
+ p = ndisc_option_new(type, offset);
if (!p)
return -ENOMEM;
struct ether_addr mac;
memcpy(&mac, opt + 2, sizeof(struct ether_addr));
+ if (ether_addr_is_null(&mac))
+ return -EBADMSG;
+
return ndisc_option_add_link_layer_address(options, opt[0], offset, &mac);
}
return 0;
}
-int ndisc_option_add_prefix(
+int ndisc_option_add_prefix_internal(
Set **options,
size_t offset,
uint8_t flags,
uint8_t prefixlen,
const struct in6_addr *address,
usec_t valid_lifetime,
- usec_t preferred_lifetime) {
+ usec_t preferred_lifetime,
+ usec_t valid_until,
+ usec_t preferred_until) {
assert(options);
assert(address);
if (prefixlen > 128)
return -EINVAL;
- if (in6_addr_is_link_local(address))
+ struct in6_addr addr = *address;
+ in6_addr_mask(&addr, prefixlen);
+
+ /* RFC 4861 and 4862 only state that link-local prefix should be ignored.
+ * But here we also ignore null and multicast addresses. */
+ if (in6_addr_is_link_local(&addr) || in6_addr_is_null(&addr) || in6_addr_is_multicast(&addr))
return -EINVAL;
if (preferred_lifetime > valid_lifetime)
return -EINVAL;
- sd_ndisc_option *p = ndisc_option_new(SD_NDISC_OPTION_PREFIX_INFORMATION, offset);
+ if (preferred_until > valid_until)
+ return -EINVAL;
+
+ sd_ndisc_option *p = ndisc_option_get(
+ *options,
+ &(const sd_ndisc_option) {
+ .type = SD_NDISC_OPTION_PREFIX_INFORMATION,
+ .prefix.prefixlen = prefixlen,
+ .prefix.address = addr,
+ });
+ if (p) {
+ if (offset != 0)
+ return -EEXIST;
+
+ p->prefix.flags = flags;
+ p->prefix.valid_lifetime = valid_lifetime;
+ p->prefix.preferred_lifetime = preferred_lifetime;
+ p->prefix.valid_until = valid_until;
+ p->prefix.preferred_until = preferred_until;
+ return 0;
+ }
+
+ p = ndisc_option_new(SD_NDISC_OPTION_PREFIX_INFORMATION, offset);
if (!p)
return -ENOMEM;
p->prefix = (sd_ndisc_prefix) {
.flags = flags,
.prefixlen = prefixlen,
- .address = *address,
+ .address = addr,
.valid_lifetime = valid_lifetime,
.preferred_lifetime = preferred_lifetime,
+ .valid_until = valid_until,
+ .preferred_until = preferred_until,
};
- in6_addr_mask(&p->prefix.address, p->prefix.prefixlen);
-
return ndisc_option_consume(options, p);
}
if (FLAGS_SET(flags, ND_OPT_PI_FLAG_AUTO) && pi->nd_opt_pi_prefix_len != 64)
flags &= ~ND_OPT_PI_FLAG_AUTO;
- return ndisc_option_add_prefix(options, offset, flags, pi->nd_opt_pi_prefix_len, &pi->nd_opt_pi_prefix, valid, pref);
+ return ndisc_option_add_prefix(options, offset, flags,
+ pi->nd_opt_pi_prefix_len, &pi->nd_opt_pi_prefix,
+ valid, pref);
}
-static int ndisc_option_build_prefix(const sd_ndisc_option *option, uint8_t **ret) {
+static int ndisc_option_build_prefix(const sd_ndisc_option *option, usec_t timestamp, uint8_t **ret) {
assert(option);
assert(option->type == SD_NDISC_OPTION_PREFIX_INFORMATION);
assert(ret);
if (!buf)
return -ENOMEM;
+ usec_t valid = MIN(option->prefix.valid_lifetime,
+ usec_sub_unsigned(option->prefix.valid_until, timestamp));
+ usec_t pref = MIN3(valid,
+ option->prefix.preferred_lifetime,
+ usec_sub_unsigned(option->prefix.preferred_until, timestamp));
+
*buf = (struct nd_opt_prefix_info) {
.nd_opt_pi_type = SD_NDISC_OPTION_PREFIX_INFORMATION,
.nd_opt_pi_len = sizeof(struct nd_opt_prefix_info) / 8,
.nd_opt_pi_prefix_len = option->prefix.prefixlen,
.nd_opt_pi_flags_reserved = option->prefix.flags,
- .nd_opt_pi_valid_time = usec_to_be32_sec(option->prefix.valid_lifetime),
- .nd_opt_pi_preferred_time = usec_to_be32_sec(option->prefix.preferred_lifetime),
+ .nd_opt_pi_valid_time = usec_to_be32_sec(valid),
+ .nd_opt_pi_preferred_time = usec_to_be32_sec(pref),
.nd_opt_pi_prefix = option->prefix.address,
};
int ndisc_option_add_redirected_header(Set **options, size_t offset, const struct ip6_hdr *hdr) {
assert(options);
- assert(hdr);
- sd_ndisc_option *p = ndisc_option_new(SD_NDISC_OPTION_REDIRECTED_HEADER, offset);
+ if (!hdr) {
+ ndisc_option_remove_by_type(*options, SD_NDISC_OPTION_REDIRECTED_HEADER);
+ return 0;
+ }
+
+ sd_ndisc_option *p = ndisc_option_get_by_type(*options, SD_NDISC_OPTION_REDIRECTED_HEADER);
+ if (p) {
+ if (offset != 0)
+ return -EEXIST;
+
+ memcpy(&p->hdr, hdr, sizeof(struct ip6_hdr));
+ return 0;
+ }
+
+ p = ndisc_option_new(SD_NDISC_OPTION_REDIRECTED_HEADER, offset);
if (!p)
return -ENOMEM;
if (mtu < IPV6_MIN_MTU)
return -EINVAL;
- sd_ndisc_option *p = ndisc_option_new(SD_NDISC_OPTION_MTU, offset);
+ sd_ndisc_option *p = ndisc_option_get_by_type(*options, SD_NDISC_OPTION_MTU);
+ if (p) {
+ if (offset != 0)
+ return -EEXIST;
+
+ p->mtu = mtu;
+ return 0;
+ }
+
+ p = ndisc_option_new(SD_NDISC_OPTION_MTU, offset);
if (!p)
return -ENOMEM;
return 0;
}
-int ndisc_option_add_route(
+int ndisc_option_add_home_agent_internal(
+ Set **options,
+ size_t offset,
+ uint16_t preference,
+ usec_t lifetime,
+ usec_t valid_until) {
+
+ assert(options);
+
+ if (lifetime > UINT16_MAX * USEC_PER_SEC)
+ return -EINVAL;
+
+ sd_ndisc_option *p = ndisc_option_get_by_type(*options, SD_NDISC_OPTION_HOME_AGENT);
+ if (p) {
+ if (offset != 0)
+ return -EEXIST;
+
+ p->home_agent = (sd_ndisc_home_agent) {
+ .preference = preference,
+ .lifetime = lifetime,
+ .valid_until = valid_until,
+ };
+ return 0;
+ }
+
+ p = ndisc_option_new(SD_NDISC_OPTION_HOME_AGENT, offset);
+ if (!p)
+ return -ENOMEM;
+
+ p->home_agent = (sd_ndisc_home_agent) {
+ .preference = preference,
+ .lifetime = lifetime,
+ .valid_until = valid_until,
+ };
+
+ return ndisc_option_consume(options, p);
+}
+
+static int ndisc_option_parse_home_agent(Set **options, size_t offset, size_t len, const uint8_t *opt) {
+ const struct nd_opt_home_agent_info *p = (const struct nd_opt_home_agent_info*) ASSERT_PTR(opt);
+
+ assert(options);
+
+ if (len != sizeof(struct nd_opt_home_agent_info))
+ return -EBADMSG;
+
+ if (p->nd_opt_home_agent_info_type != SD_NDISC_OPTION_HOME_AGENT)
+ return -EBADMSG;
+
+ return ndisc_option_add_home_agent(
+ options, offset,
+ be16toh(p->nd_opt_home_agent_info_preference),
+ be16_sec_to_usec(p->nd_opt_home_agent_info_lifetime, /* max_as_infinity = */ false));
+}
+
+static int ndisc_option_build_home_agent(const sd_ndisc_option *option, usec_t timestamp, uint8_t **ret) {
+ assert(option);
+ assert(option->type == SD_NDISC_OPTION_HOME_AGENT);
+ assert(ret);
+
+ assert_cc(sizeof(struct nd_opt_home_agent_info) % 8 == 0);
+
+ usec_t lifetime = MIN(option->home_agent.lifetime,
+ usec_sub_unsigned(option->home_agent.valid_until, timestamp));
+
+ _cleanup_free_ struct nd_opt_home_agent_info *buf = new(struct nd_opt_home_agent_info, 1);
+ if (!buf)
+ return -ENOMEM;
+
+ *buf = (struct nd_opt_home_agent_info) {
+ .nd_opt_home_agent_info_type = SD_NDISC_OPTION_HOME_AGENT,
+ .nd_opt_home_agent_info_len = sizeof(struct nd_opt_home_agent_info) / 8,
+ .nd_opt_home_agent_info_preference = htobe16(option->home_agent.preference),
+ .nd_opt_home_agent_info_lifetime = usec_to_be16_sec(lifetime),
+ };
+
+ *ret = (uint8_t*) TAKE_PTR(buf);
+ return 0;
+}
+
+int ndisc_option_add_route_internal(
Set **options,
size_t offset,
uint8_t preference,
uint8_t prefixlen,
const struct in6_addr *prefix,
- usec_t lifetime) {
+ usec_t lifetime,
+ usec_t valid_until) {
assert(options);
assert(prefix);
if (!IN_SET(preference, SD_NDISC_PREFERENCE_LOW, SD_NDISC_PREFERENCE_MEDIUM, SD_NDISC_PREFERENCE_HIGH))
return -EINVAL;
- sd_ndisc_option *p = ndisc_option_new(SD_NDISC_OPTION_ROUTE_INFORMATION, offset);
+ struct in6_addr addr = *prefix;
+ in6_addr_mask(&addr, prefixlen);
+
+ sd_ndisc_option *p = ndisc_option_get(
+ *options,
+ &(const sd_ndisc_option) {
+ .type = SD_NDISC_OPTION_ROUTE_INFORMATION,
+ .route.prefixlen = prefixlen,
+ .route.address = addr,
+ });
+ if (p) {
+ if (offset != 0)
+ return -EEXIST;
+
+ p->route.preference = preference;
+ p->route.lifetime = lifetime;
+ p->route.valid_until = valid_until;
+ return 0;
+ }
+
+ p = ndisc_option_new(SD_NDISC_OPTION_ROUTE_INFORMATION, offset);
if (!p)
return -ENOMEM;
p->route = (sd_ndisc_route) {
.preference = preference,
.prefixlen = prefixlen,
- .address = *prefix,
+ .address = addr,
.lifetime = lifetime,
+ .valid_until = valid_until,
};
- in6_addr_mask(&p->route.address, p->route.prefixlen);
-
return ndisc_option_consume(options, p);
}
return ndisc_option_add_route(options, offset, preference, prefixlen, &prefix, lifetime);
}
-static int ndisc_option_build_route(const sd_ndisc_option *option, uint8_t **ret) {
+static int ndisc_option_build_route(const sd_ndisc_option *option, usec_t timestamp, uint8_t **ret) {
assert(option);
assert(option->type == SD_NDISC_OPTION_ROUTE_INFORMATION);
assert(option->route.prefixlen <= 128);
assert(ret);
size_t len = 1 + DIV_ROUND_UP(option->route.prefixlen, 64);
- be32_t lifetime = usec_to_be32_sec(option->route.lifetime);
+ be32_t lifetime = usec_to_be32_sec(MIN(option->route.lifetime,
+ usec_sub_unsigned(option->route.valid_until, timestamp)));
_cleanup_free_ uint8_t *buf = new(uint8_t, len * 8);
if (!buf)
return 0;
}
-int ndisc_option_add_rdnss(
+int ndisc_option_add_rdnss_internal(
Set **options,
size_t offset,
size_t n_addresses,
const struct in6_addr *addresses,
- usec_t lifetime) {
+ usec_t lifetime,
+ usec_t valid_until) {
assert(options);
assert(addresses);
.n_addresses = n_addresses,
.addresses = TAKE_PTR(addrs),
.lifetime = lifetime,
+ .valid_until = valid_until,
};
return ndisc_option_consume(options, p);
return ndisc_option_add_rdnss(options, offset, n_addrs, (const struct in6_addr*) (opt + 8), lifetime);
}
-static int ndisc_option_build_rdnss(const sd_ndisc_option *option, uint8_t **ret) {
+static int ndisc_option_build_rdnss(const sd_ndisc_option *option, usec_t timestamp, uint8_t **ret) {
assert(option);
assert(option->type == SD_NDISC_OPTION_RDNSS);
assert(ret);
size_t len = option->rdnss.n_addresses * 2 + 1;
- be32_t lifetime = usec_to_be32_sec(option->rdnss.lifetime);
+ be32_t lifetime = usec_to_be32_sec(MIN(option->rdnss.lifetime,
+ usec_sub_unsigned(option->rdnss.valid_until, timestamp)));
_cleanup_free_ uint8_t *buf = new(uint8_t, len * 8);
if (!buf)
if ((flags & UINT64_C(0x00ffffffffffff00)) != flags)
return -EINVAL;
- sd_ndisc_option *p = ndisc_option_new(SD_NDISC_OPTION_FLAGS_EXTENSION, offset);
+ sd_ndisc_option *p = ndisc_option_get_by_type(*options, SD_NDISC_OPTION_FLAGS_EXTENSION);
+ if (p) {
+ if (offset != 0)
+ return -EEXIST;
+
+ p->extended_flags = flags;
+ return 0;
+ }
+
+ p = ndisc_option_new(SD_NDISC_OPTION_FLAGS_EXTENSION, offset);
if (!p)
return -ENOMEM;
return 0;
}
-int ndisc_option_add_dnssl(Set **options, size_t offset, char * const *domains, usec_t lifetime) {
+int ndisc_option_add_dnssl_internal(
+ Set **options,
+ size_t offset, char *
+ const *domains,
+ usec_t lifetime,
+ usec_t valid_until) {
+
int r;
assert(options);
p->dnssl = (sd_ndisc_dnssl) {
.domains = TAKE_PTR(copy),
.lifetime = lifetime,
+ .valid_until = valid_until,
};
return ndisc_option_consume(options, p);
return ndisc_option_add_dnssl(options, offset, l, lifetime);
}
-static int ndisc_option_build_dnssl(const sd_ndisc_option *option, uint8_t **ret) {
+ static int ndisc_option_build_dnssl(const sd_ndisc_option *option, usec_t timestamp, uint8_t **ret) {
int r;
assert(option);
len += strlen(*s) + 2;
len = DIV_ROUND_UP(len, 8);
- be32_t lifetime = usec_to_be32_sec(option->dnssl.lifetime);
+ be32_t lifetime = usec_to_be32_sec(MIN(option->dnssl.lifetime,
+ usec_sub_unsigned(option->dnssl.valid_until, timestamp)));
_cleanup_free_ uint8_t *buf = new(uint8_t, len * 8);
if (!buf)
remaining -= r;
}
- if (remaining > 0)
- memset(p, 0, remaining);
+ memzero(p, remaining);
*ret = TAKE_PTR(buf);
return 0;
if (!in_charset(portal, URI_VALID))
return -EINVAL;
+ sd_ndisc_option *p = ndisc_option_get_by_type(*options, SD_NDISC_OPTION_CAPTIVE_PORTAL);
+ if (p) {
+ if (offset != 0)
+ return -EEXIST;
+
+ return free_and_strdup(&p->captive_portal, portal);
+ }
+
_cleanup_free_ char *copy = strdup(portal);
if (!copy)
return -ENOMEM;
- sd_ndisc_option *p = ndisc_option_new(SD_NDISC_OPTION_CAPTIVE_PORTAL, offset);
+ p = ndisc_option_new(SD_NDISC_OPTION_CAPTIVE_PORTAL, offset);
if (!p)
return -ENOMEM;
uint8_t *p = mempcpy(buf + 2, option->captive_portal, len_portal);
size_t remaining = len * 8 - 2 - len_portal;
- if (remaining > 0)
- memset(p, 0, remaining);
+
+ memzero(p, remaining);
*ret = TAKE_PTR(buf);
return 0;
return 0;
}
-int ndisc_option_add_prefix64(
+int ndisc_option_add_prefix64_internal(
Set **options,
size_t offset,
uint8_t prefixlen,
const struct in6_addr *prefix,
- usec_t lifetime) {
+ usec_t lifetime,
+ usec_t valid_until) {
int r;
if (lifetime > PREF64_MAX_LIFETIME_USEC)
return -EINVAL;
- sd_ndisc_option *p = ndisc_option_new(SD_NDISC_OPTION_PREF64, offset);
+ struct in6_addr addr = *prefix;
+ in6_addr_mask(&addr, prefixlen);
+
+ sd_ndisc_option *p = ndisc_option_get(
+ *options,
+ &(const sd_ndisc_option) {
+ .type = SD_NDISC_OPTION_PREF64,
+ .prefix64.prefixlen = prefixlen,
+ .prefix64.prefix = addr,
+ });
+ if (p) {
+ if (offset != 0)
+ return -EEXIST;
+
+ p->prefix64.lifetime = lifetime;
+ p->prefix64.valid_until = valid_until;
+ return 0;
+ }
+
+ p = ndisc_option_new(SD_NDISC_OPTION_PREF64, offset);
if (!p)
return -ENOMEM;
p->prefix64 = (sd_ndisc_prefix64) {
.prefixlen = prefixlen,
- .prefix = *prefix,
+ .prefix = addr,
.lifetime = lifetime,
+ .valid_until = valid_until,
};
- in6_addr_mask(&p->prefix64.prefix, p->prefix64.prefixlen);
-
return ndisc_option_consume(options, p);
}
return ndisc_option_add_prefix64(options, offset, prefixlen, &prefix, lifetime);
}
-static int ndisc_option_build_prefix64(const sd_ndisc_option *option, uint8_t **ret) {
+static int ndisc_option_build_prefix64(const sd_ndisc_option *option, usec_t timestamp, uint8_t **ret) {
int r;
assert(option);
if (r < 0)
return r;
- uint16_t lifetime;
- if (option->prefix64.lifetime >= PREF64_SCALED_LIFETIME_MASK * USEC_PER_SEC)
- lifetime = PREF64_SCALED_LIFETIME_MASK;
- else
- lifetime = (uint16_t) DIV_ROUND_UP(option->prefix64.lifetime, USEC_PER_SEC) & PREF64_SCALED_LIFETIME_MASK;
+ uint16_t lifetime = (uint16_t) DIV_ROUND_UP(MIN(option->prefix64.lifetime,
+ usec_sub_unsigned(option->prefix64.valid_until, timestamp)),
+ USEC_PER_SEC) & PREF64_SCALED_LIFETIME_MASK;
_cleanup_free_ uint8_t *buf = new(uint8_t, 2 * 8);
if (!buf)
r = ndisc_option_parse_mtu(&options, offset, length, opt);
break;
+ case SD_NDISC_OPTION_HOME_AGENT:
+ r = ndisc_option_parse_home_agent(&options, offset, length, opt);
+ break;
+
case SD_NDISC_OPTION_ROUTE_INFORMATION:
r = ndisc_option_parse_route(&options, offset, length, opt);
break;
return 0;
}
-int ndisc_send(int fd, const struct sockaddr_in6 *dst, const struct icmp6_hdr *hdr, Set *options) {
+int ndisc_send(int fd, const struct in6_addr *dst, const struct icmp6_hdr *hdr, Set *options, usec_t timestamp) {
int r;
assert(fd >= 0);
break;
case SD_NDISC_OPTION_PREFIX_INFORMATION:
- r = ndisc_option_build_prefix(option, &buf);
+ r = ndisc_option_build_prefix(option, timestamp, &buf);
break;
case SD_NDISC_OPTION_REDIRECTED_HEADER:
r = ndisc_option_build_mtu(option, &buf);
break;
+ case SD_NDISC_OPTION_HOME_AGENT:
+ r = ndisc_option_build_home_agent(option, timestamp, &buf);
+ break;
+
case SD_NDISC_OPTION_ROUTE_INFORMATION:
- r = ndisc_option_build_route(option, &buf);
+ r = ndisc_option_build_route(option, timestamp, &buf);
break;
case SD_NDISC_OPTION_RDNSS:
- r = ndisc_option_build_rdnss(option, &buf);
+ r = ndisc_option_build_rdnss(option, timestamp, &buf);
break;
case SD_NDISC_OPTION_FLAGS_EXTENSION:
break;
case SD_NDISC_OPTION_DNSSL:
- r = ndisc_option_build_dnssl(option, &buf);
+ r = ndisc_option_build_dnssl(option, timestamp, &buf);
break;
case SD_NDISC_OPTION_CAPTIVE_PORTAL:
break;
case SD_NDISC_OPTION_PREF64:
- r = ndisc_option_build_prefix64(option, &buf);
+ r = ndisc_option_build_prefix64(option, timestamp, &buf);
break;
default:
struct in6_addr address;
usec_t valid_lifetime;
usec_t preferred_lifetime;
+ /* timestamp in CLOCK_BOOTTIME, used when sending option for adjusting lifetime. */
+ usec_t valid_until;
+ usec_t preferred_until;
} sd_ndisc_prefix;
+typedef struct sd_ndisc_home_agent {
+ uint16_t preference;
+ usec_t lifetime;
+ usec_t valid_until;
+} sd_ndisc_home_agent;
+
typedef struct sd_ndisc_route {
uint8_t preference;
uint8_t prefixlen;
struct in6_addr address;
usec_t lifetime;
+ usec_t valid_until;
} sd_ndisc_route;
typedef struct sd_ndisc_rdnss {
size_t n_addresses;
struct in6_addr *addresses;
usec_t lifetime;
+ usec_t valid_until;
} sd_ndisc_rdnss;
typedef struct sd_ndisc_dnssl {
char **domains;
usec_t lifetime;
+ usec_t valid_until;
} sd_ndisc_dnssl;
typedef struct sd_ndisc_prefix64 {
uint8_t prefixlen;
struct in6_addr prefix;
usec_t lifetime;
+ usec_t valid_until;
} sd_ndisc_prefix64;
typedef struct sd_ndisc_option {
size_t offset;
union {
- sd_ndisc_raw raw; /* for testing or unsupported options */
- struct ether_addr mac; /* SD_NDISC_OPTION_SOURCE_LL_ADDRESS or SD_NDISC_OPTION_TARGET_LL_ADDRESS */
- sd_ndisc_prefix prefix; /* SD_NDISC_OPTION_PREFIX_INFORMATION */
- struct ip6_hdr hdr; /* SD_NDISC_OPTION_REDIRECTED_HEADER */
- uint32_t mtu; /* SD_NDISC_OPTION_MTU */
- sd_ndisc_route route; /* SD_NDISC_OPTION_ROUTE_INFORMATION */
- sd_ndisc_rdnss rdnss; /* SD_NDISC_OPTION_RDNSS */
- uint64_t extended_flags; /* SD_NDISC_OPTION_FLAGS_EXTENSION */
- sd_ndisc_dnssl dnssl; /* SD_NDISC_OPTION_DNSSL */
- char *captive_portal; /* SD_NDISC_OPTION_CAPTIVE_PORTAL */
- sd_ndisc_prefix64 prefix64; /* SD_NDISC_OPTION_PREF64 */
+ sd_ndisc_raw raw; /* for testing or unsupported options */
+ struct ether_addr mac; /* SD_NDISC_OPTION_SOURCE_LL_ADDRESS or SD_NDISC_OPTION_TARGET_LL_ADDRESS */
+ sd_ndisc_prefix prefix; /* SD_NDISC_OPTION_PREFIX_INFORMATION */
+ struct ip6_hdr hdr; /* SD_NDISC_OPTION_REDIRECTED_HEADER */
+ uint32_t mtu; /* SD_NDISC_OPTION_MTU */
+ sd_ndisc_home_agent home_agent; /* SD_NDISC_OPTION_HOME_AGENT */
+ sd_ndisc_route route; /* SD_NDISC_OPTION_ROUTE_INFORMATION */
+ sd_ndisc_rdnss rdnss; /* SD_NDISC_OPTION_RDNSS */
+ uint64_t extended_flags; /* SD_NDISC_OPTION_FLAGS_EXTENSION */
+ sd_ndisc_dnssl dnssl; /* SD_NDISC_OPTION_DNSSL */
+ char *captive_portal; /* SD_NDISC_OPTION_CAPTIVE_PORTAL */
+ sd_ndisc_prefix64 prefix64; /* SD_NDISC_OPTION_PREF64 */
};
} sd_ndisc_option;
ndisc_option_remove(options, &(const sd_ndisc_option) { .type = type });
}
-int ndisc_option_add_raw(
+int ndisc_option_set_raw(
Set **options,
- size_t offset,
size_t length,
const uint8_t *bytes);
int ndisc_option_add_link_layer_address(
Set **options,
- uint8_t opt,
+ uint8_t type,
size_t offset,
const struct ether_addr *mac);
-int ndisc_option_add_prefix(
+static inline int ndisc_option_set_link_layer_address(
+ Set **options,
+ uint8_t type,
+ const struct ether_addr *mac) {
+ return ndisc_option_add_link_layer_address(options, type, 0, mac);
+}
+int ndisc_option_add_prefix_internal(
Set **options,
size_t offset,
uint8_t flags,
uint8_t prefixlen,
const struct in6_addr *address,
usec_t valid_lifetime,
- usec_t preferred_lifetime);
+ usec_t preferred_lifetime,
+ usec_t valid_until,
+ usec_t preferred_until);
+static inline int ndisc_option_add_prefix(
+ Set **options,
+ size_t offset,
+ uint8_t flags,
+ uint8_t prefixlen,
+ const struct in6_addr *address,
+ usec_t valid_lifetime,
+ usec_t preferred_lifetime) {
+ return ndisc_option_add_prefix_internal(options, offset, flags, prefixlen, address,
+ valid_lifetime, preferred_lifetime,
+ USEC_INFINITY, USEC_INFINITY);
+}
+static inline int ndisc_option_set_prefix(
+ Set **options,
+ uint8_t flags,
+ uint8_t prefixlen,
+ const struct in6_addr *address,
+ usec_t valid_lifetime,
+ usec_t preferred_lifetime,
+ usec_t valid_until,
+ usec_t preferred_until) {
+ return ndisc_option_add_prefix_internal(options, 0, flags, prefixlen, address,
+ valid_lifetime, preferred_lifetime,
+ valid_until, preferred_until);
+}
int ndisc_option_add_redirected_header(
Set **options,
size_t offset,
Set **options,
size_t offset,
uint32_t mtu);
-int ndisc_option_add_route(
+static inline int ndisc_option_set_mtu(
+ Set **options,
+ uint32_t mtu) {
+ return ndisc_option_add_mtu(options, 0, mtu);
+}
+int ndisc_option_add_home_agent_internal(
+ Set **options,
+ size_t offset,
+ uint16_t preference,
+ usec_t lifetime,
+ usec_t valid_until);
+static inline int ndisc_option_add_home_agent(
+ Set **options,
+ size_t offset,
+ uint16_t preference,
+ usec_t lifetime) {
+ return ndisc_option_add_home_agent_internal(options, offset, preference, lifetime, USEC_INFINITY);
+}
+static inline int ndisc_option_set_home_agent(
+ Set **options,
+ uint16_t preference,
+ usec_t lifetime,
+ usec_t valid_until) {
+ return ndisc_option_add_home_agent_internal(options, 0, preference, lifetime, valid_until);
+}
+int ndisc_option_add_route_internal(
Set **options,
size_t offset,
uint8_t preference,
uint8_t prefixlen,
const struct in6_addr *prefix,
- usec_t lifetime);
-int ndisc_option_add_rdnss(
+ usec_t lifetime,
+ usec_t valid_until);
+static inline int ndisc_option_add_route(
Set **options,
size_t offset,
+ uint8_t preference,
+ uint8_t prefixlen,
+ const struct in6_addr *prefix,
+ usec_t lifetime) {
+ return ndisc_option_add_route_internal(options, offset, preference, prefixlen, prefix, lifetime, USEC_INFINITY);
+}
+static inline int ndisc_option_set_route(
+ Set **options,
+ uint8_t preference,
+ uint8_t prefixlen,
+ const struct in6_addr *prefix,
+ usec_t lifetime,
+ usec_t valid_until) {
+ return ndisc_option_add_route_internal(options, 0, preference, prefixlen, prefix, lifetime, valid_until);
+}
+int ndisc_option_add_rdnss_internal(
+ Set **options,
+ size_t offset,
+ size_t n_addresses,
+ const struct in6_addr *addresses,
+ usec_t lifetime,
+ usec_t valid_until);
+static inline int ndisc_option_add_rdnss(
+ Set **options,
+ size_t offset,
+ size_t n_addresses,
+ const struct in6_addr *addresses,
+ usec_t lifetime) {
+ return ndisc_option_add_rdnss_internal(options, offset, n_addresses, addresses, lifetime, USEC_INFINITY);
+}
+static inline int ndisc_option_set_rdnss(
+ Set **options,
size_t n_addresses,
const struct in6_addr *addresses,
- usec_t lifetime);
+ usec_t lifetime,
+ usec_t valid_until) {
+ return ndisc_option_add_rdnss_internal(options, 0, n_addresses, addresses, lifetime, valid_until);
+}
int ndisc_option_add_flags_extension(
Set **options,
size_t offset,
uint64_t flags);
-int ndisc_option_add_dnssl(
+int ndisc_option_add_dnssl_internal(
+ Set **options,
+ size_t offset,
+ char * const *domains,
+ usec_t lifetime,
+ usec_t valid_until);
+static inline int ndisc_option_add_dnssl(
Set **options,
size_t offset,
char * const *domains,
- usec_t lifetime);
+ usec_t lifetime) {
+ return ndisc_option_add_dnssl_internal(options, offset, domains, lifetime, USEC_INFINITY);
+}
+static inline int ndisc_option_set_dnssl(
+ Set **options,
+ char * const *domains,
+ usec_t lifetime,
+ usec_t valid_until) {
+ return ndisc_option_add_dnssl_internal(options, 0, domains, lifetime, valid_until);
+}
int ndisc_option_add_captive_portal(
Set **options,
size_t offset,
const char *portal);
-int ndisc_option_add_prefix64(
+static inline int ndisc_option_set_captive_portal(
+ Set **options,
+ const char *portal) {
+ return ndisc_option_add_captive_portal(options, 0, portal);
+}
+int ndisc_option_add_prefix64_internal(
+ Set **options,
+ size_t offset,
+ uint8_t prefixlen,
+ const struct in6_addr *prefix,
+ usec_t lifetime,
+ usec_t valid_until);
+static inline int ndisc_option_add_prefix64(
Set **options,
size_t offset,
uint8_t prefixlen,
const struct in6_addr *prefix,
- usec_t lifetime);
+ usec_t lifetime) {
+ return ndisc_option_add_prefix64_internal(options, offset, prefixlen, prefix, lifetime, USEC_INFINITY);
+}
+static inline int ndisc_option_set_prefix64(
+ Set **options,
+ uint8_t prefixlen,
+ const struct in6_addr *prefix,
+ usec_t lifetime,
+ usec_t valid_until) {
+ return ndisc_option_add_prefix64_internal(options, 0, prefixlen, prefix, lifetime, valid_until);
+}
-int ndisc_send(int fd, const struct sockaddr_in6 *dst, const struct icmp6_hdr *hdr, Set *options);
+int ndisc_send(int fd, const struct in6_addr *dst, const struct icmp6_hdr *hdr, Set *options, usec_t timestamp);
--- /dev/null
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+#pragma once
+
+#include "sd-ndisc.h"
+
+#include "icmp6-packet.h"
+#include "set.h"
+
+struct sd_ndisc_redirect {
+ unsigned n_ref;
+
+ ICMP6Packet *packet;
+
+ struct in6_addr target_address;
+ struct in6_addr destination_address;
+
+ Set *options;
+};
+
+sd_ndisc_redirect* ndisc_redirect_new(ICMP6Packet *packet);
+int ndisc_redirect_parse(sd_ndisc *nd, sd_ndisc_redirect *rd);
--- /dev/null
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+#pragma once
+
+#include "sd-radv.h"
+
+#include "icmp6-packet.h"
+#include "set.h"
+
+struct sd_ndisc_router_solicit {
+ unsigned n_ref;
+
+ ICMP6Packet *packet;
+
+ Set *options;
+};
+
+sd_ndisc_router_solicit* ndisc_router_solicit_new(ICMP6Packet *packet);
+int ndisc_router_solicit_parse(sd_radv *ra, sd_ndisc_router_solicit *rs);
int r;
assert_return(server, -EINVAL);
+ assert_return(!path || (dir_fd >= 0 || dir_fd == AT_FDCWD), -EBADF);
assert_return(!sd_dhcp_server_is_running(server), -EBUSY);
if (!path) {
if (!path_is_safe(path))
return -EINVAL;
- if (dir_fd < 0 && dir_fd != AT_FDCWD)
- return -EBADF;
-
- _cleanup_close_ int fd = -EBADF;
- fd = fd_reopen(dir_fd, O_CLOEXEC | O_DIRECTORY | O_PATH);
+ _cleanup_close_ int fd = fd_reopen(dir_fd, O_CLOEXEC | O_DIRECTORY | O_PATH);
if (fd < 0)
return fd;
if (r < 0)
return r;
- server->lease_dir_fd = TAKE_FD(fd);
+ close_and_replace(server->lease_dir_fd, fd);
+
return 0;
}
int dhcp6_client_send_message(sd_dhcp6_client *client) {
_cleanup_free_ uint8_t *buf = NULL;
- struct in6_addr all_servers =
- IN6ADDR_ALL_DHCP6_RELAY_AGENTS_AND_SERVERS_INIT;
struct sd_dhcp6_option *j;
usec_t elapsed_usec, time_now;
be16_t elapsed_time;
if (r < 0)
return r;
- r = dhcp6_network_send_udp_socket(client->fd, &all_servers, buf, offset);
+ r = dhcp6_network_send_udp_socket(client->fd, &IN6_ADDR_ALL_DHCP6_RELAY_AGENTS_AND_SERVERS, buf, offset);
if (r < 0)
return r;
}
int sd_dhcp6_client_is_running(sd_dhcp6_client *client) {
- assert_return(client, -EINVAL);
+ if (!client)
+ return false;
return client->state != DHCP6_STATE_STOPPED;
}
}
int sd_ipv4acd_is_running(sd_ipv4acd *acd) {
- assert_return(acd, false);
+ if (!acd)
+ return false;
return acd->state != IPV4ACD_STATE_INIT;
}
}
int sd_ipv4ll_is_running(sd_ipv4ll *ll) {
- assert_return(ll, false);
+ if (!ll)
+ return false;
return sd_ipv4acd_is_running(ll->acd);
}
--- /dev/null
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+
+#include <netinet/icmp6.h>
+
+#include "sd-ndisc.h"
+
+#include "alloc-util.h"
+#include "in-addr-util.h"
+#include "ndisc-internal.h"
+#include "ndisc-neighbor-internal.h"
+#include "ndisc-option.h"
+
+static sd_ndisc_neighbor* ndisc_neighbor_free(sd_ndisc_neighbor *na) {
+ if (!na)
+ return NULL;
+
+ icmp6_packet_unref(na->packet);
+ set_free(na->options);
+ return mfree(na);
+}
+
+DEFINE_PUBLIC_TRIVIAL_REF_UNREF_FUNC(sd_ndisc_neighbor, sd_ndisc_neighbor, ndisc_neighbor_free);
+
+sd_ndisc_neighbor* ndisc_neighbor_new(ICMP6Packet *packet) {
+ sd_ndisc_neighbor *na;
+
+ assert(packet);
+
+ na = new(sd_ndisc_neighbor, 1);
+ if (!na)
+ return NULL;
+
+ *na = (sd_ndisc_neighbor) {
+ .n_ref = 1,
+ .packet = icmp6_packet_ref(packet),
+ };
+
+ return na;
+}
+
+int ndisc_neighbor_parse(sd_ndisc *nd, sd_ndisc_neighbor *na) {
+ int r;
+
+ assert(na);
+
+ if (na->packet->raw_size < sizeof(struct nd_neighbor_advert))
+ return log_ndisc_errno(nd, SYNTHETIC_ERRNO(EBADMSG),
+ "Too small to be a neighbor advertisement, ignoring datagram.");
+
+ /* Neighbor advertisement packets are neatly aligned to 64-bit boundaries, hence we can access them directly */
+ const struct nd_neighbor_advert *a = (const struct nd_neighbor_advert*) na->packet->raw_packet;
+ assert(a->nd_na_type == ND_NEIGHBOR_ADVERT);
+ assert(a->nd_na_code == 0);
+
+ na->flags = a->nd_na_flags_reserved; /* the first 3 bits */
+ na->target_address = a->nd_na_target;
+
+ /* RFC 4861 section 4.4:
+ * For solicited advertisements, the Target Address field in the Neighbor Solicitation message that
+ * prompted this advertisement. For an unsolicited advertisement, the address whose link-layer
+ * address has changed. The Target Address MUST NOT be a multicast address.
+ *
+ * Here, we only check if the target address is a link-layer address (or a null address, for safety)
+ * when the message is an unsolicited neighbor advertisement. */
+ if (!FLAGS_SET(na->flags, ND_NA_FLAG_SOLICITED))
+ if (!in6_addr_is_link_local(&na->target_address) && !in6_addr_is_null(&na->target_address))
+ return log_ndisc_errno(nd, SYNTHETIC_ERRNO(EBADMSG),
+ "Received ND packet with an invalid target address (%s), ignoring datagram.",
+ IN6_ADDR_TO_STRING(&na->target_address));
+
+ r = ndisc_parse_options(na->packet, &na->options);
+ if (r < 0)
+ return log_ndisc_errno(nd, r, "Failed to parse NDisc options in neighbor advertisement message, ignoring: %m");
+
+ return 0;
+}
+
+int sd_ndisc_neighbor_get_sender_address(sd_ndisc_neighbor *na, struct in6_addr *ret) {
+ assert_return(na, -EINVAL);
+
+ return icmp6_packet_get_sender_address(na->packet, ret);
+}
+
+int sd_ndisc_neighbor_get_target_address(sd_ndisc_neighbor *na, struct in6_addr *ret) {
+ assert_return(na, -EINVAL);
+
+ if (in6_addr_is_null(&na->target_address))
+ /* fall back to the sender address, for safety. */
+ return sd_ndisc_neighbor_get_sender_address(na, ret);
+
+ if (ret)
+ *ret = na->target_address;
+ return 0;
+}
+
+int sd_ndisc_neighbor_get_target_mac(sd_ndisc_neighbor *na, struct ether_addr *ret) {
+ assert_return(na, -EINVAL);
+
+ return ndisc_option_get_mac(na->options, SD_NDISC_OPTION_TARGET_LL_ADDRESS, ret);
+}
+
+int sd_ndisc_neighbor_get_flags(sd_ndisc_neighbor *na, uint32_t *ret) {
+ assert_return(na, -EINVAL);
+
+ if (ret)
+ *ret = na->flags;
+ return 0;
+}
+
+int sd_ndisc_neighbor_is_router(sd_ndisc_neighbor *na) {
+ assert_return(na, -EINVAL);
+
+ return FLAGS_SET(na->flags, ND_NA_FLAG_ROUTER);
+}
+
+int sd_ndisc_neighbor_is_solicited(sd_ndisc_neighbor *na) {
+ assert_return(na, -EINVAL);
+
+ return FLAGS_SET(na->flags, ND_NA_FLAG_SOLICITED);
+}
+
+int sd_ndisc_neighbor_is_override(sd_ndisc_neighbor *na) {
+ assert_return(na, -EINVAL);
+
+ return FLAGS_SET(na->flags, ND_NA_FLAG_OVERRIDE);
+}
--- /dev/null
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+
+#include <netinet/icmp6.h>
+
+#include "sd-ndisc.h"
+
+#include "alloc-util.h"
+#include "in-addr-util.h"
+#include "ndisc-internal.h"
+#include "ndisc-option.h"
+#include "ndisc-redirect-internal.h"
+
+static sd_ndisc_redirect* ndisc_redirect_free(sd_ndisc_redirect *rd) {
+ if (!rd)
+ return NULL;
+
+ icmp6_packet_unref(rd->packet);
+ set_free(rd->options);
+ return mfree(rd);
+}
+
+DEFINE_PUBLIC_TRIVIAL_REF_UNREF_FUNC(sd_ndisc_redirect, sd_ndisc_redirect, ndisc_redirect_free);
+
+sd_ndisc_redirect* ndisc_redirect_new(ICMP6Packet *packet) {
+ sd_ndisc_redirect *rd;
+
+ assert(packet);
+
+ rd = new(sd_ndisc_redirect, 1);
+ if (!rd)
+ return NULL;
+
+ *rd = (sd_ndisc_redirect) {
+ .n_ref = 1,
+ .packet = icmp6_packet_ref(packet),
+ };
+
+ return rd;
+}
+
+int ndisc_redirect_parse(sd_ndisc *nd, sd_ndisc_redirect *rd) {
+ int r;
+
+ assert(rd);
+ assert(rd->packet);
+
+ if (rd->packet->raw_size < sizeof(struct nd_redirect))
+ return log_ndisc_errno(nd, SYNTHETIC_ERRNO(EBADMSG),
+ "Too small to be a redirect message, ignoring.");
+
+ const struct nd_redirect *a = (const struct nd_redirect*) rd->packet->raw_packet;
+ assert(a->nd_rd_type == ND_REDIRECT);
+ assert(a->nd_rd_code == 0);
+
+ rd->target_address = a->nd_rd_target;
+ rd->destination_address = a->nd_rd_dst;
+
+ /* RFC 4861 section 8.1
+ * The ICMP Destination Address field in the redirect message does not contain a multicast address. */
+ if (in6_addr_is_null(&rd->destination_address) || in6_addr_is_multicast(&rd->destination_address))
+ return log_ndisc_errno(nd, SYNTHETIC_ERRNO(EBADMSG),
+ "Received Redirect message with an invalid destination address, ignoring datagram: %m");
+
+ /* RFC 4861 section 8.1
+ * The ICMP Target Address is either a link-local address (when redirected to a router) or the same
+ * as the ICMP Destination Address (when redirected to the on-link destination). */
+ if (!in6_addr_is_link_local(&rd->target_address) && !in6_addr_equal(&rd->target_address, &rd->destination_address))
+ return log_ndisc_errno(nd, SYNTHETIC_ERRNO(EBADMSG),
+ "Received Redirect message with an invalid target address, ignoring datagram: %m");
+
+ r = ndisc_parse_options(rd->packet, &rd->options);
+ if (r < 0)
+ return log_ndisc_errno(nd, r, "Failed to parse NDisc options in Redirect message, ignoring datagram: %m");
+
+ return 0;
+}
+
+int sd_ndisc_redirect_set_sender_address(sd_ndisc_redirect *rd, const struct in6_addr *addr) {
+ assert_return(rd, -EINVAL);
+
+ return icmp6_packet_set_sender_address(rd->packet, addr);
+}
+
+int sd_ndisc_redirect_get_sender_address(sd_ndisc_redirect *rd, struct in6_addr *ret) {
+ assert_return(rd, -EINVAL);
+
+ return icmp6_packet_get_sender_address(rd->packet, ret);
+}
+
+int sd_ndisc_redirect_get_target_address(sd_ndisc_redirect *rd, struct in6_addr *ret) {
+ assert_return(rd, -EINVAL);
+
+ if (in6_addr_is_null(&rd->target_address))
+ return -ENODATA;
+
+ if (ret)
+ *ret = rd->target_address;
+ return 0;
+}
+
+int sd_ndisc_redirect_get_destination_address(sd_ndisc_redirect *rd, struct in6_addr *ret) {
+ assert_return(rd, -EINVAL);
+
+ if (in6_addr_is_null(&rd->destination_address))
+ return -ENODATA;
+
+ if (ret)
+ *ret = rd->destination_address;
+ return 0;
+}
+
+int sd_ndisc_redirect_get_target_mac(sd_ndisc_redirect *rd, struct ether_addr *ret) {
+ assert_return(rd, -EINVAL);
+
+ return ndisc_option_get_mac(rd->options, SD_NDISC_OPTION_TARGET_LL_ADDRESS, ret);
+}
+
+int sd_ndisc_redirect_get_redirected_header(sd_ndisc_redirect *rd, struct ip6_hdr *ret) {
+ assert_return(rd, -EINVAL);
+
+ sd_ndisc_option *p = ndisc_option_get_by_type(rd->options, SD_NDISC_OPTION_REDIRECTED_HEADER);
+ if (!p)
+ return -ENODATA;
+
+ if (ret)
+ *ret = p->hdr;
+ return 0;
+}
--- /dev/null
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+
+#include <netinet/icmp6.h>
+
+#include "sd-radv.h"
+
+#include "alloc-util.h"
+#include "in-addr-util.h"
+#include "ndisc-option.h"
+#include "ndisc-router-solicit-internal.h"
+#include "radv-internal.h"
+
+static sd_ndisc_router_solicit* ndisc_router_solicit_free(sd_ndisc_router_solicit *rs) {
+ if (!rs)
+ return NULL;
+
+ icmp6_packet_unref(rs->packet);
+ set_free(rs->options);
+ return mfree(rs);
+}
+
+DEFINE_PUBLIC_TRIVIAL_REF_UNREF_FUNC(sd_ndisc_router_solicit, sd_ndisc_router_solicit, ndisc_router_solicit_free);
+
+sd_ndisc_router_solicit* ndisc_router_solicit_new(ICMP6Packet *packet) {
+ sd_ndisc_router_solicit *rs;
+
+ assert(packet);
+
+ rs = new(sd_ndisc_router_solicit, 1);
+ if (!rs)
+ return NULL;
+
+ *rs = (sd_ndisc_router_solicit) {
+ .n_ref = 1,
+ .packet = icmp6_packet_ref(packet),
+ };
+
+ return rs;
+}
+
+int ndisc_router_solicit_parse(sd_radv *ra, sd_ndisc_router_solicit *rs) {
+ int r;
+
+ assert(rs);
+ assert(rs->packet);
+
+ if (rs->packet->raw_size < sizeof(struct nd_router_solicit))
+ return log_radv_errno(ra, SYNTHETIC_ERRNO(EBADMSG),
+ "Too small to be a router solicit, ignoring.");
+
+ const struct nd_router_solicit *a = (const struct nd_router_solicit*) rs->packet->raw_packet;
+ assert(a);
+ assert(a->nd_rs_type == ND_ROUTER_SOLICIT);
+ assert(a->nd_rs_code == 0);
+
+ r = ndisc_parse_options(rs->packet, &rs->options);
+ if (r < 0)
+ return log_radv_errno(ra, r, "Failed to parse NDisc options in router solicit, ignoring datagram: %m");
+
+ /* RFC 4861 section 4.1.
+ * Source link-layer address:
+ * The link-layer address of the sender, if known. MUST NOT be included if the Source
+ * Address is the unspecified address. Otherwise, it SHOULD be included on link
+ * layers that have addresses. */
+ if (ndisc_option_get_mac(rs->options, SD_NDISC_OPTION_SOURCE_LL_ADDRESS, NULL) >= 0&&
+ sd_ndisc_router_solicit_get_sender_address(rs, NULL) == -ENODATA)
+ return log_radv_errno(ra, SYNTHETIC_ERRNO(EBADMSG),
+ "Router Solicitation message from null address unexpectedly contains source link-layer address option, ignoring datagaram.");
+
+ return 0;
+}
+
+int sd_ndisc_router_solicit_get_sender_address(sd_ndisc_router_solicit *rs, struct in6_addr *ret) {
+ assert_return(rs, -EINVAL);
+
+ return icmp6_packet_get_sender_address(rs->packet, ret);
+}
+
+int sd_ndisc_router_solicit_get_sender_mac(sd_ndisc_router_solicit *rs, struct ether_addr *ret) {
+ assert_return(rs, -EINVAL);
+
+ return ndisc_option_get_mac(rs->options, SD_NDISC_OPTION_SOURCE_LL_ADDRESS, ret);
+}
return rt;
}
+int sd_ndisc_router_set_sender_address(sd_ndisc_router *rt, const struct in6_addr *addr) {
+ assert_return(rt, -EINVAL);
+
+ return icmp6_packet_set_sender_address(rt->packet, addr);
+}
+
int sd_ndisc_router_get_sender_address(sd_ndisc_router *rt, struct in6_addr *ret) {
assert_return(rt, -EINVAL);
#include "in-addr-util.h"
#include "memory-util.h"
#include "ndisc-internal.h"
+#include "ndisc-neighbor-internal.h"
+#include "ndisc-redirect-internal.h"
#include "ndisc-router-internal.h"
#include "network-common.h"
#include "random-util.h"
#define NDISC_TIMEOUT_NO_RA_USEC (NDISC_ROUTER_SOLICITATION_INTERVAL * NDISC_MAX_ROUTER_SOLICITATIONS)
static const char * const ndisc_event_table[_SD_NDISC_EVENT_MAX] = {
- [SD_NDISC_EVENT_TIMEOUT] = "timeout",
- [SD_NDISC_EVENT_ROUTER] = "router",
+ [SD_NDISC_EVENT_TIMEOUT] = "timeout",
+ [SD_NDISC_EVENT_ROUTER] = "router",
+ [SD_NDISC_EVENT_NEIGHBOR] = "neighbor",
+ [SD_NDISC_EVENT_REDIRECT] = "redirect",
};
DEFINE_STRING_TABLE_LOOKUP(ndisc_event, sd_ndisc_event_t);
return 0;
}
+static int ndisc_handle_neighbor(sd_ndisc *nd, ICMP6Packet *packet) {
+ _cleanup_(sd_ndisc_neighbor_unrefp) sd_ndisc_neighbor *na = NULL;
+ struct in6_addr a;
+ int r;
+
+ assert(nd);
+ assert(packet);
+
+ na = ndisc_neighbor_new(packet);
+ if (!na)
+ return -ENOMEM;
+
+ r = ndisc_neighbor_parse(nd, na);
+ if (r < 0)
+ return r;
+
+ r = sd_ndisc_neighbor_get_sender_address(na, &a);
+ if (r < 0)
+ return r;
+
+ log_ndisc(nd, "Received Neighbor Advertisement from %s: Router=%s, Solicited=%s, Override=%s",
+ IN6_ADDR_TO_STRING(&a),
+ yes_no(sd_ndisc_neighbor_is_router(na) > 0),
+ yes_no(sd_ndisc_neighbor_is_solicited(na) > 0),
+ yes_no(sd_ndisc_neighbor_is_override(na) > 0));
+
+ ndisc_callback(nd, SD_NDISC_EVENT_NEIGHBOR, na);
+ return 0;
+}
+
+static int ndisc_handle_redirect(sd_ndisc *nd, ICMP6Packet *packet) {
+ _cleanup_(sd_ndisc_redirect_unrefp) sd_ndisc_redirect *rd = NULL;
+ struct in6_addr a;
+ int r;
+
+ assert(nd);
+ assert(packet);
+
+ rd = ndisc_redirect_new(packet);
+ if (!rd)
+ return -ENOMEM;
+
+ r = ndisc_redirect_parse(nd, rd);
+ if (r < 0)
+ return r;
+
+ r = sd_ndisc_redirect_get_sender_address(rd, &a);
+ if (r < 0)
+ return r;
+
+ log_ndisc(nd, "Received Redirect message from %s: Target=%s, Destination=%s",
+ IN6_ADDR_TO_STRING(&a),
+ IN6_ADDR_TO_STRING(&rd->target_address),
+ IN6_ADDR_TO_STRING(&rd->destination_address));
+
+ ndisc_callback(nd, SD_NDISC_EVENT_REDIRECT, rd);
+ return 0;
+}
+
static int ndisc_recv(sd_event_source *s, int fd, uint32_t revents, void *userdata) {
_cleanup_(icmp6_packet_unrefp) ICMP6Packet *packet = NULL;
sd_ndisc *nd = ASSERT_PTR(userdata);
(void) ndisc_handle_router(nd, packet);
break;
+ case ND_NEIGHBOR_ADVERT:
+ (void) ndisc_handle_neighbor(nd, packet);
+ break;
+
+ case ND_REDIRECT:
+ (void) ndisc_handle_redirect(nd, packet);
+ break;
+
default:
log_ndisc(nd, "Received an ICMPv6 packet with unexpected type %i, ignoring.", r);
}
}
static int ndisc_send_router_solicitation(sd_ndisc *nd) {
- static const struct sockaddr_in6 dst = {
- .sin6_family = AF_INET6,
- .sin6_addr = IN6ADDR_ALL_ROUTERS_MULTICAST_INIT,
- };
static const struct nd_router_solicit header = {
.nd_rs_type = ND_ROUTER_SOLICIT,
};
assert(nd);
if (!ether_addr_is_null(&nd->mac_addr)) {
- r = ndisc_option_add_link_layer_address(&options, SD_NDISC_OPTION_SOURCE_LL_ADDRESS, 0, &nd->mac_addr);
+ r = ndisc_option_set_link_layer_address(&options, SD_NDISC_OPTION_SOURCE_LL_ADDRESS, &nd->mac_addr);
if (r < 0)
return r;
}
- return ndisc_send(nd->fd, &dst, &header.nd_rs_hdr, options);
+ return ndisc_send(nd->fd, &IN6_ADDR_ALL_ROUTERS_MULTICAST, &header.nd_rs_hdr, options, USEC_INFINITY);
}
static usec_t ndisc_timeout_compute_random(usec_t val) {
#include "iovec-util.h"
#include "macro.h"
#include "memory-util.h"
+#include "ndisc-router-solicit-internal.h"
#include "network-common.h"
#include "radv-internal.h"
#include "random-util.h"
}
int sd_radv_is_running(sd_radv *ra) {
- assert_return(ra, false);
+ if (!ra)
+ return false;
return ra->state != RADV_STATE_IDLE;
}
struct sockaddr_in6 dst_addr = {
.sin6_family = AF_INET6,
- .sin6_addr = IN6ADDR_ALL_NODES_MULTICAST_INIT,
+ .sin6_addr = IN6_ADDR_ALL_NODES_MULTICAST,
};
struct nd_router_advert adv = {
.nd_ra_type = ND_ROUTER_ADVERT,
return 0;
}
-static int radv_recv(sd_event_source *s, int fd, uint32_t revents, void *userdata) {
- sd_radv *ra = ASSERT_PTR(userdata);
- struct in6_addr src;
- triple_timestamp timestamp;
+static int radv_process_packet(sd_radv *ra, ICMP6Packet *packet) {
int r;
- assert(s);
- assert(ra->event);
+ assert(ra);
+ assert(packet);
- ssize_t buflen = next_datagram_size_fd(fd);
- if (ERRNO_IS_NEG_TRANSIENT(buflen) || ERRNO_IS_NEG_DISCONNECT(buflen))
- return 0;
- if (buflen < 0) {
- log_radv_errno(ra, buflen, "Failed to determine datagram size to read, ignoring: %m");
- return 0;
- }
+ if (icmp6_packet_get_type(packet) != ND_ROUTER_SOLICIT)
+ return log_radv_errno(ra, SYNTHETIC_ERRNO(EBADMSG), "Received ICMP6 packet with unexpected type, ignoring.");
- _cleanup_free_ char *buf = new0(char, buflen);
- if (!buf)
- return -ENOMEM;
+ _cleanup_(sd_ndisc_router_solicit_unrefp) sd_ndisc_router_solicit *rs = NULL;
+ rs = ndisc_router_solicit_new(packet);
+ if (!rs)
+ return log_oom_debug();
- r = icmp6_receive(fd, buf, buflen, &src, ×tamp);
- if (ERRNO_IS_NEG_TRANSIENT(r) || ERRNO_IS_NEG_DISCONNECT(r))
- return 0;
+ r = ndisc_router_solicit_parse(ra, rs);
if (r < 0)
- switch (r) {
- case -EADDRNOTAVAIL:
- log_radv(ra, "Received RS from neither link-local nor null address, ignoring.");
- return 0;
+ return r;
- case -EMULTIHOP:
- log_radv(ra, "Received RS with invalid hop limit, ignoring.");
- return 0;
+ struct in6_addr src = {};
+ r = sd_ndisc_router_solicit_get_sender_address(rs, &src);
+ if (r < 0 && r != -ENODATA) /* null address is allowed */
+ return log_radv_errno(ra, r, "Failed to get sender address of RS, ignoring: %m");
- case -EPFNOSUPPORT:
- log_radv(ra, "Received invalid source address from ICMPv6 socket, ignoring.");
- return 0;
+ r = radv_send_router(ra, &src, ra->lifetime_usec);
+ if (r < 0)
+ return log_radv_errno(ra, r, "Unable to send solicited Router Advertisement to %s, ignoring: %m", IN6_ADDR_TO_STRING(&src));
- default:
- log_radv_errno(ra, r, "Unexpected error receiving from ICMPv6 socket, ignoring: %m");
- return 0;
- }
+ log_radv(ra, "Sent solicited Router Advertisement to %s.", IN6_ADDR_TO_STRING(&src));
+ return 0;
+}
- if ((size_t) buflen < sizeof(struct nd_router_solicit)) {
- log_radv(ra, "Too short packet received, ignoring");
- return 0;
- }
+static int radv_recv(sd_event_source *s, int fd, uint32_t revents, void *userdata) {
+ _cleanup_(icmp6_packet_unrefp) ICMP6Packet *packet = NULL;
+ sd_radv *ra = ASSERT_PTR(userdata);
+ int r;
- /* TODO: if the sender address is null, check that the message does not have the source link-layer
- * address option. See RFC 4861 Section 6.1.1. */
+ assert(fd >= 0);
- const char *addr = IN6_ADDR_TO_STRING(&src);
- r = radv_send_router(ra, &src, ra->lifetime_usec);
- if (r < 0)
- log_radv_errno(ra, r, "Unable to send solicited Router Advertisement to %s, ignoring: %m", addr);
- else
- log_radv(ra, "Sent solicited Router Advertisement to %s.", addr);
+ r = icmp6_packet_receive(fd, &packet);
+ if (r < 0) {
+ log_radv_errno(ra, r, "Failed to receive ICMPv6 packet, ignoring: %m");
+ return 0;
+ }
+ (void) radv_process_packet(ra, packet);
return 0;
}
/* SPDX-License-Identifier: LGPL-2.1-or-later */
+/* Make sure the net/if.h header is included before any linux/ one */
+#include <net/if.h>
#include <errno.h>
+#include <linux/veth.h>
#include <stdlib.h>
#include <unistd.h>
-#include <linux/veth.h>
-#include <net/if.h>
#include "sd-event.h"
#include "sd-ipv4acd.h"
static const struct in6_addr local_address =
{ { { 0xfe, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, } } };
-static const struct in6_addr mcast_address =
- IN6ADDR_ALL_DHCP6_RELAY_AGENTS_AND_SERVERS_INIT;
+static const struct in6_addr mcast_address = IN6_ADDR_ALL_DHCP6_RELAY_AGENTS_AND_SERVERS;
static const struct in6_addr ia_na_address1 = { { { IA_NA_ADDRESS1_BYTES } } };
static const struct in6_addr ia_na_address2 = { { { IA_NA_ADDRESS2_BYTES } } };
static const struct in6_addr ia_pd_prefix1 = { { { IA_PD_PREFIX1_BYTES } } };
}
}
-int dhcp6_network_send_udp_socket(int s, struct in6_addr *a, const void *packet, size_t len) {
+int dhcp6_network_send_udp_socket(int s, const struct in6_addr *a, const void *packet, size_t len) {
log_debug("/* %s(count=%u) */", __func__, test_client_sent_message_count);
assert_se(a);
return len;
}
-int dhcp6_network_bind_udp_socket(int ifindex, struct in6_addr *a) {
+int dhcp6_network_bind_udp_socket(int ifindex, const struct in6_addr *a) {
assert_se(ifindex == test_ifindex);
assert_se(a);
assert_se(in6_addr_equal(a, &local_address));
/* SPDX-License-Identifier: LGPL-2.1-or-later */
-#include <errno.h>
+/* Make sure the net/if.h header is included before any linux/ one */
#include <net/if.h>
+#include <errno.h>
+#include <linux/veth.h>
#include <stdlib.h>
#include <unistd.h>
-#include <linux/veth.h>
#include "sd-event.h"
#include "sd-ipv4ll.h"
return 0;
}
-static void test_callback(sd_ndisc *nd, sd_ndisc_event_t event, void *message, void *userdata) {
+static void test_callback_ra(sd_ndisc *nd, sd_ndisc_event_t event, void *message, void *userdata) {
sd_event *e = userdata;
static unsigned idx = 0;
uint64_t flags_array[] = {
assert_se(sd_ndisc_set_ifindex(nd, 42) >= 0);
assert_se(sd_ndisc_set_mac(nd, &mac_addr) >= 0);
- assert_se(sd_ndisc_set_callback(nd, test_callback, e) >= 0);
+ assert_se(sd_ndisc_set_callback(nd, test_callback_ra, e) >= 0);
assert_se(sd_event_add_time_relative(e, NULL, CLOCK_BOOTTIME,
30 * USEC_PER_SEC, 0,
assert_se(sd_ndisc_set_ifindex(nd, 42) >= 0);
assert_se(sd_ndisc_set_mac(nd, &mac_addr) >= 0);
- assert_se(sd_ndisc_set_callback(nd, test_callback, e) >= 0);
+ assert_se(sd_ndisc_set_callback(nd, test_callback_ra, e) >= 0);
assert_se(sd_event_add_time_relative(e, NULL, CLOCK_BOOTTIME,
30 * USEC_PER_SEC, 0,
test_fd[1] = -EBADF;
}
+static void neighbor_dump(sd_ndisc_neighbor *na) {
+ struct in6_addr addr;
+ uint32_t flags;
+
+ assert_se(na);
+
+ log_info("--");
+ assert_se(sd_ndisc_neighbor_get_sender_address(na, &addr) >= 0);
+ log_info("Sender: %s", IN6_ADDR_TO_STRING(&addr));
+
+ assert_se(sd_ndisc_neighbor_get_flags(na, &flags) >= 0);
+ log_info("Flags: Router:%s, Solicited:%s, Override: %s",
+ yes_no(flags & ND_NA_FLAG_ROUTER),
+ yes_no(flags & ND_NA_FLAG_SOLICITED),
+ yes_no(flags & ND_NA_FLAG_OVERRIDE));
+
+ assert_se(sd_ndisc_neighbor_is_router(na) == FLAGS_SET(flags, ND_NA_FLAG_ROUTER));
+ assert_se(sd_ndisc_neighbor_is_solicited(na) == FLAGS_SET(flags, ND_NA_FLAG_SOLICITED));
+ assert_se(sd_ndisc_neighbor_is_override(na) == FLAGS_SET(flags, ND_NA_FLAG_OVERRIDE));
+}
+
+static int send_na(uint32_t flags) {
+ uint8_t advertisement[] = {
+ /* struct nd_neighbor_advert */
+ 0x88, 0x00, 0xde, 0x83, 0x00, 0x00, 0x00, 0x00,
+ 0xfe, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0xfe, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01,
+ /* type = 0x02 (SD_NDISC_OPTION_TARGET_LL_ADDRESS), length = 8 */
+ 0x01, 0x01, 'A', 'B', 'C', '1', '2', '3',
+ };
+
+ ((struct nd_neighbor_advert*) advertisement)->nd_na_flags_reserved = flags;
+
+ assert_se(write(test_fd[1], advertisement, sizeof(advertisement)) == sizeof(advertisement));
+ if (verbose)
+ printf(" sent NA with flag 0x%02x\n", flags);
+
+ return 0;
+}
+
+static void test_callback_na(sd_ndisc *nd, sd_ndisc_event_t event, void *message, void *userdata) {
+ sd_event *e = userdata;
+ static unsigned idx = 0;
+ uint32_t flags_array[] = {
+ 0,
+ 0,
+ ND_NA_FLAG_ROUTER,
+ ND_NA_FLAG_SOLICITED,
+ ND_NA_FLAG_SOLICITED | ND_NA_FLAG_OVERRIDE,
+ };
+ uint32_t flags;
+
+ assert_se(nd);
+
+ if (event != SD_NDISC_EVENT_NEIGHBOR)
+ return;
+
+ sd_ndisc_neighbor *rt = ASSERT_PTR(message);
+
+ neighbor_dump(rt);
+
+ assert_se(sd_ndisc_neighbor_get_flags(rt, &flags) >= 0);
+ assert_se(flags == flags_array[idx]);
+ idx++;
+
+ if (verbose)
+ printf(" got event 0x%02" PRIx32 "\n", flags);
+
+ if (idx < ELEMENTSOF(flags_array)) {
+ send_na(flags_array[idx]);
+ return;
+ }
+
+ idx = 0;
+ sd_event_exit(e, 0);
+}
+
+static int on_recv_rs_na(sd_event_source *s, int fd, uint32_t revents, void *userdata) {
+ _cleanup_(icmp6_packet_unrefp) ICMP6Packet *packet = NULL;
+ assert_se(icmp6_packet_receive(fd, &packet) >= 0);
+
+ return send_na(0);
+}
+
+TEST(na) {
+ _cleanup_(sd_event_unrefp) sd_event *e = NULL;
+ _cleanup_(sd_event_source_unrefp) sd_event_source *s = NULL;
+ _cleanup_(sd_ndisc_unrefp) sd_ndisc *nd = NULL;
+
+ assert_se(sd_event_new(&e) >= 0);
+
+ assert_se(sd_ndisc_new(&nd) >= 0);
+ assert_se(nd);
+
+ assert_se(sd_ndisc_attach_event(nd, e, 0) >= 0);
+
+ assert_se(sd_ndisc_set_ifindex(nd, 42) >= 0);
+ assert_se(sd_ndisc_set_mac(nd, &mac_addr) >= 0);
+ assert_se(sd_ndisc_set_callback(nd, test_callback_na, e) >= 0);
+
+ assert_se(sd_event_add_time_relative(e, NULL, CLOCK_BOOTTIME,
+ 30 * USEC_PER_SEC, 0,
+ NULL, INT_TO_PTR(-ETIMEDOUT)) >= 0);
+
+ assert_se(sd_ndisc_start(nd) >= 0);
+
+ assert_se(sd_event_add_io(e, &s, test_fd[1], EPOLLIN, on_recv_rs_na, nd) >= 0);
+ assert_se(sd_event_source_set_io_fd_own(s, true) >= 0);
+
+ assert_se(sd_event_loop(e) >= 0);
+
+ test_fd[1] = -EBADF;
+}
+
static int on_recv_rs_timeout(sd_event_source *s, int fd, uint32_t revents, void *userdata) {
_cleanup_(icmp6_packet_unrefp) ICMP6Packet *packet = NULL;
sd_ndisc *nd = ASSERT_PTR(userdata);
--- /dev/null
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+
+#include <getopt.h>
+
+#include "build.h"
+#include "ether-addr-util.h"
+#include "fd-util.h"
+#include "hexdecoct.h"
+#include "icmp6-util.h"
+#include "in-addr-util.h"
+#include "main-func.h"
+#include "ndisc-option.h"
+#include "netlink-util.h"
+#include "network-common.h"
+#include "parse-util.h"
+#include "socket-util.h"
+#include "strv.h"
+#include "time-util.h"
+
+static int arg_ifindex = 0;
+static int arg_icmp6_type = 0;
+static union in_addr_union arg_dest = IN_ADDR_NULL;
+static uint8_t arg_hop_limit = 0;
+static uint8_t arg_ra_flags = 0;
+static uint8_t arg_preference = false;
+static usec_t arg_lifetime = 0;
+static usec_t arg_reachable = 0;
+static usec_t arg_retransmit = 0;
+static uint32_t arg_na_flags = 0;
+static union in_addr_union arg_target_address = IN_ADDR_NULL;
+static union in_addr_union arg_redirect_destination = IN_ADDR_NULL;
+static bool arg_set_source_mac = false;
+static struct ether_addr arg_source_mac = {};
+static bool arg_set_target_mac = false;
+static struct ether_addr arg_target_mac = {};
+static struct ip6_hdr *arg_redirected_header = NULL;
+static bool arg_set_mtu = false;
+static uint32_t arg_mtu = 0;
+
+STATIC_DESTRUCTOR_REGISTER(arg_redirected_header, freep);
+
+static int parse_icmp6_type(const char *str) {
+ if (STR_IN_SET(str, "router-solicit", "rs", "RS"))
+ return ND_ROUTER_SOLICIT;
+ if (STR_IN_SET(str, "router-advertisement", "ra", "RA"))
+ return ND_ROUTER_ADVERT;
+ if (STR_IN_SET(str, "neighbor-solicit", "ns", "NS"))
+ return ND_NEIGHBOR_SOLICIT;
+ if (STR_IN_SET(str, "neighbor-advertisement", "na", "NA"))
+ return ND_NEIGHBOR_ADVERT;
+ if (STR_IN_SET(str, "redirect", "rd", "RD"))
+ return ND_REDIRECT;
+ return -EINVAL;
+}
+
+static int parse_preference(const char *str) {
+ if (streq(str, "low"))
+ return SD_NDISC_PREFERENCE_LOW;
+ if (streq(str, "medium"))
+ return SD_NDISC_PREFERENCE_MEDIUM;
+ if (streq(str, "high"))
+ return SD_NDISC_PREFERENCE_HIGH;
+ if (streq(str, "reserved"))
+ return SD_NDISC_PREFERENCE_RESERVED;
+ return -EINVAL;
+}
+
+static int parse_argv(int argc, char *argv[]) {
+ enum {
+ ARG_VERSION = 0x100,
+ ARG_RA_HOP_LIMIT,
+ ARG_RA_MANAGED,
+ ARG_RA_OTHER,
+ ARG_RA_HOME_AGENT,
+ ARG_RA_PREFERENCE,
+ ARG_RA_LIFETIME,
+ ARG_RA_REACHABLE,
+ ARG_RA_RETRANSMIT,
+ ARG_NA_ROUTER,
+ ARG_NA_SOLICITED,
+ ARG_NA_OVERRIDE,
+ ARG_TARGET_ADDRESS,
+ ARG_REDIRECT_DESTINATION,
+ ARG_OPTION_SOURCE_LL,
+ ARG_OPTION_TARGET_LL,
+ ARG_OPTION_REDIRECTED_HEADER,
+ ARG_OPTION_MTU,
+ };
+
+ static const struct option options[] = {
+ { "version", no_argument, NULL, ARG_VERSION },
+ { "interface", required_argument, NULL, 'i' },
+ { "type", required_argument, NULL, 't' },
+ { "dest", required_argument, NULL, 'd' },
+ /* For Router Advertisement */
+ { "hop-limit", required_argument, NULL, ARG_RA_HOP_LIMIT },
+ { "managed", required_argument, NULL, ARG_RA_MANAGED },
+ { "other", required_argument, NULL, ARG_RA_OTHER },
+ { "home-agent", required_argument, NULL, ARG_RA_HOME_AGENT },
+ { "preference", required_argument, NULL, ARG_RA_PREFERENCE },
+ { "lifetime", required_argument, NULL, ARG_RA_LIFETIME },
+ { "reachable-time", required_argument, NULL, ARG_RA_REACHABLE },
+ { "retransmit-timer", required_argument, NULL, ARG_RA_RETRANSMIT },
+ /* For Neighbor Advertisement */
+ { "is-router", required_argument, NULL, ARG_NA_ROUTER },
+ { "is-solicited", required_argument, NULL, ARG_NA_SOLICITED },
+ { "is-override", required_argument, NULL, ARG_NA_OVERRIDE },
+ /* For Neighbor Solicit, Neighbor Advertisement, and Redirect */
+ { "target-address", required_argument, NULL, ARG_TARGET_ADDRESS },
+ /* For Redirect */
+ { "redirect-destination", required_argument, NULL, ARG_REDIRECT_DESTINATION },
+ /* Options */
+ { "source-ll-address", required_argument, NULL, ARG_OPTION_SOURCE_LL },
+ { "target-ll-address", required_argument, NULL, ARG_OPTION_TARGET_LL },
+ { "redirected-header", required_argument, NULL, ARG_OPTION_REDIRECTED_HEADER },
+ { "mtu", required_argument, NULL, ARG_OPTION_MTU },
+ {}
+ };
+
+ _cleanup_(sd_netlink_unrefp) sd_netlink *rtnl = NULL;
+ int c, r;
+
+ assert(argc >= 0);
+ assert(argv);
+
+ while ((c = getopt_long(argc, argv, "i:t:d:", options, NULL)) >= 0) {
+
+ switch (c) {
+
+ case ARG_VERSION:
+ return version();
+
+ case 'i':
+ r = rtnl_resolve_interface_or_warn(&rtnl, optarg);
+ if (r < 0)
+ return r;
+ arg_ifindex = r;
+ break;
+
+ case 't':
+ r = parse_icmp6_type(optarg);
+ if (r < 0)
+ return log_error_errno(r, "Failed to parse message type: %m");
+ arg_icmp6_type = r;
+ break;
+
+ case 'd':
+ r = in_addr_from_string(AF_INET6, optarg, &arg_dest);
+ if (r < 0)
+ return log_error_errno(r, "Failed to parse destination address: %m");
+ if (!in6_addr_is_link_local(&arg_dest.in6))
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
+ "The destination address %s is not a link-local address.", optarg);
+ break;
+
+ case ARG_RA_HOP_LIMIT:
+ r = safe_atou8(optarg, &arg_hop_limit);
+ if (r < 0)
+ return log_error_errno(r, "Failed to parse hop limit: %m");
+ break;
+
+ case ARG_RA_MANAGED:
+ r = parse_boolean(optarg);
+ if (r < 0)
+ return log_error_errno(r, "Failed to parse managed flag: %m");
+ SET_FLAG(arg_ra_flags, ND_RA_FLAG_MANAGED, r);
+ break;
+
+ case ARG_RA_OTHER:
+ r = parse_boolean(optarg);
+ if (r < 0)
+ return log_error_errno(r, "Failed to parse other flag: %m");
+ SET_FLAG(arg_ra_flags, ND_RA_FLAG_OTHER, r);
+ break;
+
+ case ARG_RA_HOME_AGENT:
+ r = parse_boolean(optarg);
+ if (r < 0)
+ return log_error_errno(r, "Failed to parse home-agent flag: %m");
+ SET_FLAG(arg_ra_flags, ND_RA_FLAG_HOME_AGENT, r);
+ break;
+
+ case ARG_RA_PREFERENCE:
+ r = parse_preference(optarg);
+ if (r < 0)
+ return log_error_errno(r, "Failed to parse preference: %m");
+ arg_preference = r;
+ break;
+
+ case ARG_RA_LIFETIME:
+ r = parse_sec(optarg, &arg_lifetime);
+ if (r < 0)
+ return log_error_errno(r, "Failed to parse lifetime: %m");
+ break;
+
+ case ARG_RA_REACHABLE:
+ r = parse_sec(optarg, &arg_reachable);
+ if (r < 0)
+ return log_error_errno(r, "Failed to parse reachable time: %m");
+ break;
+
+ case ARG_RA_RETRANSMIT:
+ r = parse_sec(optarg, &arg_retransmit);
+ if (r < 0)
+ return log_error_errno(r, "Failed to parse retransmit timer: %m");
+ break;
+
+ case ARG_NA_ROUTER:
+ r = parse_boolean(optarg);
+ if (r < 0)
+ return log_error_errno(r, "Failed to parse is-router flag: %m");
+ SET_FLAG(arg_na_flags, ND_NA_FLAG_ROUTER, r);
+ break;
+
+ case ARG_NA_SOLICITED:
+ r = parse_boolean(optarg);
+ if (r < 0)
+ return log_error_errno(r, "Failed to parse is-solicited flag: %m");
+ SET_FLAG(arg_na_flags, ND_NA_FLAG_SOLICITED, r);
+ break;
+
+ case ARG_NA_OVERRIDE:
+ r = parse_boolean(optarg);
+ if (r < 0)
+ return log_error_errno(r, "Failed to parse is-override flag: %m");
+ SET_FLAG(arg_na_flags, ND_NA_FLAG_OVERRIDE, r);
+ break;
+
+ case ARG_TARGET_ADDRESS:
+ r = in_addr_from_string(AF_INET6, optarg, &arg_target_address);
+ if (r < 0)
+ return log_error_errno(r, "Failed to parse target address: %m");
+ break;
+
+ case ARG_REDIRECT_DESTINATION:
+ r = in_addr_from_string(AF_INET6, optarg, &arg_redirect_destination);
+ if (r < 0)
+ return log_error_errno(r, "Failed to parse destination address: %m");
+ break;
+
+ case ARG_OPTION_SOURCE_LL:
+ r = parse_boolean(optarg);
+ if (r < 0)
+ return log_error_errno(r, "Failed to parse source LL address option: %m");
+ arg_set_source_mac = r;
+ break;
+
+ case ARG_OPTION_TARGET_LL:
+ r = parse_ether_addr(optarg, &arg_target_mac);
+ if (r < 0)
+ return log_error_errno(r, "Failed to parse target LL address option: %m");
+ arg_set_target_mac = true;
+ break;
+
+ case ARG_OPTION_REDIRECTED_HEADER: {
+ _cleanup_free_ void *p = NULL;
+ size_t len;
+
+ r = unbase64mem(optarg, &p, &len);
+ if (r < 0)
+ return log_error_errno(r, "Failed to parse redirected header: %m");
+
+ if (len < sizeof(struct ip6_hdr))
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Invalid redirected header.");
+
+ arg_redirected_header = TAKE_PTR(p);
+ break;
+ }
+ case ARG_OPTION_MTU:
+ r = safe_atou32(optarg, &arg_mtu);
+ if (r < 0)
+ return log_error_errno(r, "Failed to parse MTU: %m");
+ arg_set_mtu = true;
+ break;
+
+ case '?':
+ return -EINVAL;
+
+ default:
+ assert_not_reached();
+ }
+ }
+
+ if (arg_ifindex <= 0)
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "--interface/-i option is mandatory.");
+
+ if (arg_icmp6_type <= 0)
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "--type/-t option is mandatory.");
+
+ if (in6_addr_is_null(&arg_dest.in6)) {
+ if (IN_SET(arg_icmp6_type, ND_ROUTER_ADVERT, ND_NEIGHBOR_ADVERT, ND_REDIRECT))
+ arg_dest.in6 = IN6_ADDR_ALL_NODES_MULTICAST;
+ else
+ arg_dest.in6 = IN6_ADDR_ALL_ROUTERS_MULTICAST;
+ }
+
+ if (arg_set_source_mac) {
+ struct hw_addr_data hw_addr;
+
+ r = rtnl_get_link_info(&rtnl, arg_ifindex,
+ /* ret_iftype = */ NULL,
+ /* ret_flags = */ NULL,
+ /* ret_kind = */ NULL,
+ &hw_addr,
+ /* ret_permanent_hw_addr = */ NULL);
+ if (r < 0)
+ return log_error_errno(r, "Failed to get the source link-layer address: %m");
+
+ if (hw_addr.length != sizeof(struct ether_addr))
+ return log_error_errno(SYNTHETIC_ERRNO(EOPNOTSUPP),
+ "Unsupported hardware address length %zu: %m",
+ hw_addr.length);
+
+ arg_source_mac = hw_addr.ether;
+ }
+
+ return 1;
+}
+
+static int send_icmp6(int fd, const struct icmp6_hdr *hdr) {
+ _cleanup_set_free_ Set *options = NULL;
+ int r;
+
+ assert(fd >= 0);
+ assert(hdr);
+
+ if (arg_set_source_mac) {
+ r = ndisc_option_add_link_layer_address(&options, 0, SD_NDISC_OPTION_SOURCE_LL_ADDRESS, &arg_source_mac);
+ if (r < 0)
+ return r;
+ }
+
+ if (arg_set_target_mac) {
+ r = ndisc_option_add_link_layer_address(&options, 0, SD_NDISC_OPTION_TARGET_LL_ADDRESS, &arg_target_mac);
+ if (r < 0)
+ return r;
+ }
+
+ if (arg_redirected_header) {
+ r = ndisc_option_add_redirected_header(&options, 0, arg_redirected_header);
+ if (r < 0)
+ return r;
+ }
+
+ if (arg_set_mtu) {
+ r = ndisc_option_add_mtu(&options, 0, arg_mtu);
+ if (r < 0)
+ return r;
+ }
+
+ return ndisc_send(fd, &arg_dest.in6, hdr, options, now(CLOCK_BOOTTIME));
+}
+
+static int send_router_solicit(int fd) {
+ struct nd_router_solicit hdr = {
+ .nd_rs_type = ND_ROUTER_SOLICIT,
+ };
+
+ assert(fd >= 0);
+
+ return send_icmp6(fd, &hdr.nd_rs_hdr);
+}
+
+static int send_router_advertisement(int fd) {
+ struct nd_router_advert hdr = {
+ .nd_ra_type = ND_ROUTER_ADVERT,
+ .nd_ra_router_lifetime = usec_to_be16_sec(arg_lifetime),
+ .nd_ra_reachable = usec_to_be32_msec(arg_reachable),
+ .nd_ra_retransmit = usec_to_be32_msec(arg_retransmit),
+ };
+
+ assert(fd >= 0);
+
+ /* The nd_ra_curhoplimit and nd_ra_flags_reserved fields cannot specified with nd_ra_router_lifetime
+ * simultaneously in the structured initializer in the above. */
+ hdr.nd_ra_curhoplimit = arg_hop_limit;
+ hdr.nd_ra_flags_reserved = arg_ra_flags;
+
+ return send_icmp6(fd, &hdr.nd_ra_hdr);
+}
+
+static int send_neighbor_solicit(int fd) {
+ struct nd_neighbor_solicit hdr = {
+ .nd_ns_type = ND_NEIGHBOR_SOLICIT,
+ .nd_ns_target = arg_target_address.in6,
+ };
+
+ assert(fd >= 0);
+
+ return send_icmp6(fd, &hdr.nd_ns_hdr);
+}
+
+static int send_neighbor_advertisement(int fd) {
+ struct nd_neighbor_advert hdr = {
+ .nd_na_type = ND_NEIGHBOR_ADVERT,
+ .nd_na_flags_reserved = arg_na_flags,
+ .nd_na_target = arg_target_address.in6,
+ };
+
+ assert(fd >= 0);
+
+ return send_icmp6(fd, &hdr.nd_na_hdr);
+}
+
+static int send_redirect(int fd) {
+ struct nd_redirect hdr = {
+ .nd_rd_type = ND_REDIRECT,
+ .nd_rd_target = arg_target_address.in6,
+ .nd_rd_dst = arg_redirect_destination.in6,
+ };
+
+ assert(fd >= 0);
+
+ return send_icmp6(fd, &hdr.nd_rd_hdr);
+}
+
+static int run(int argc, char *argv[]) {
+ _cleanup_close_ int fd = -EBADF;
+ int r;
+
+ log_setup();
+
+ r = parse_argv(argc, argv);
+ if (r <= 0)
+ return r;
+
+ fd = icmp6_bind(arg_ifindex, /* is_router = */ false);
+ if (fd < 0)
+ return log_error_errno(fd, "Failed to bind socket to interface: %m");
+
+ switch (arg_icmp6_type) {
+ case ND_ROUTER_SOLICIT:
+ return send_router_solicit(fd);
+ case ND_ROUTER_ADVERT:
+ return send_router_advertisement(fd);
+ case ND_NEIGHBOR_SOLICIT:
+ return send_neighbor_solicit(fd);
+ case ND_NEIGHBOR_ADVERT:
+ return send_neighbor_advertisement(fd);
+ case ND_REDIRECT:
+ return send_redirect(fd);
+ default:
+ assert_not_reached();
+ }
+
+ return 0;
+}
+
+DEFINE_MAIN_FUNCTION(run);
va_start(ap, format);
r = sd_bus_error_setfv(e, name, format, ap);
- assert(!name || r < 0);
+ if (name)
+ assert(r < 0);
va_end(ap);
return r;
}
r = sd_bus_error_set(e, name, NULL);
- assert(!name || r < 0);
+ if (name)
+ assert(r < 0);
return r;
}
return true;
if (!m->mapped_userns_uid_range) {
- r = uid_range_load_userns(&m->mapped_userns_uid_range, NULL);
+ r = uid_range_load_userns(/* path = */ NULL, UID_RANGE_USERNS_INSIDE, &m->mapped_userns_uid_range);
if (r < 0)
log_monitor_errno(m, r, "Failed to load UID ranges mapped to the current user namespace, ignoring: %m");
}
#include "hexdecoct.h"
#include "id128-util.h"
#include "io-util.h"
+#include "namespace-util.h"
+#include "process-util.h"
#include "sha256.h"
#include "stdio-util.h"
#include "string-util.h"
return id128_make_v4_uuid(id);
}
+
+int id128_get_boot_for_machine(const char *machine, sd_id128_t *ret) {
+ _cleanup_close_ int pidnsfd = -EBADF, mntnsfd = -EBADF, rootfd = -EBADF;
+ _cleanup_close_pair_ int pair[2] = EBADF_PAIR;
+ pid_t pid, child;
+ sd_id128_t id;
+ ssize_t k;
+ int r;
+
+ assert(ret);
+
+ if (isempty(machine))
+ return sd_id128_get_boot(ret);
+
+ r = container_get_leader(machine, &pid);
+ if (r < 0)
+ return r;
+
+ r = namespace_open(pid, &pidnsfd, &mntnsfd, /* ret_netns_fd = */ NULL, /* ret_userns_fd = */ NULL, &rootfd);
+ if (r < 0)
+ return r;
+
+ if (socketpair(AF_UNIX, SOCK_DGRAM, 0, pair) < 0)
+ return -errno;
+
+ r = namespace_fork("(sd-bootidns)", "(sd-bootid)", NULL, 0, FORK_RESET_SIGNALS|FORK_DEATHSIG_SIGKILL,
+ pidnsfd, mntnsfd, -1, -1, rootfd, &child);
+ if (r < 0)
+ return r;
+ if (r == 0) {
+ pair[0] = safe_close(pair[0]);
+
+ r = id128_get_boot(&id);
+ if (r < 0)
+ _exit(EXIT_FAILURE);
+
+ k = send(pair[1], &id, sizeof(id), MSG_NOSIGNAL);
+ if (k != sizeof(id))
+ _exit(EXIT_FAILURE);
+
+ _exit(EXIT_SUCCESS);
+ }
+
+ pair[1] = safe_close(pair[1]);
+
+ r = wait_for_terminate_and_check("(sd-bootidns)", child, 0);
+ if (r < 0)
+ return r;
+ if (r != EXIT_SUCCESS)
+ return -EIO;
+
+ k = recv(pair[0], &id, sizeof(id), 0);
+ if (k != sizeof(id))
+ return -EIO;
+
+ if (sd_id128_is_null(id))
+ return -EIO;
+
+ *ret = id;
+ return 0;
+}
sd_id128_t id128_digest(const void *data, size_t size);
+int id128_get_boot(sd_id128_t *ret);
+int id128_get_boot_for_machine(const char *machine, sd_id128_t *ret);
+
/* A helper to check for the three relevant cases of "machine ID not initialized" */
#define ERRNO_IS_NEG_MACHINE_ID_UNSET(r) \
IN_SET(r, \
return id128_read_fd(fd, ID128_FORMAT_PLAIN | ID128_REFUSE_NULL, ret);
}
+int id128_get_boot(sd_id128_t *ret) {
+ int r;
+
+ assert(ret);
+
+ r = id128_read("/proc/sys/kernel/random/boot_id", ID128_FORMAT_UUID | ID128_REFUSE_NULL, ret);
+ if (r == -ENOENT && proc_mounted() == 0)
+ return -ENOSYS;
+
+ return r;
+}
+
_public_ int sd_id128_get_boot(sd_id128_t *ret) {
static thread_local sd_id128_t saved_boot_id = {};
int r;
if (sd_id128_is_null(saved_boot_id)) {
- r = id128_read("/proc/sys/kernel/random/boot_id", ID128_FORMAT_UUID | ID128_REFUSE_NULL, &saved_boot_id);
- if (r == -ENOENT && proc_mounted() == 0)
- return -ENOSYS;
+ r = id128_get_boot(&saved_boot_id);
if (r < 0)
return r;
}
* fsprg v0.1 - (seekable) forward-secure pseudorandom generator
* Copyright © 2012 B. Poettering
* Contact: fsprg@point-at-infinity.org
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 2.1 of the License, or (at your option) any later version.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
- * 02110-1301 USA
*/
/*
unsigned len;
size_t nwritten;
- assert(gcry_mpi_cmp_ui(x, 0) >= 0);
- len = (gcry_mpi_get_nbits(x) + 7) / 8;
+ assert(sym_gcry_mpi_cmp_ui(x, 0) >= 0);
+ len = (sym_gcry_mpi_get_nbits(x) + 7) / 8;
assert(len <= buflen);
memzero(buf, buflen);
- gcry_mpi_print(GCRYMPI_FMT_USG, buf + (buflen - len), len, &nwritten, x);
+ sym_gcry_mpi_print(GCRYMPI_FMT_USG, buf + (buflen - len), len, &nwritten, x);
assert(nwritten == len);
}
gcry_mpi_t h;
_unused_ unsigned len;
- assert_se(gcry_mpi_scan(&h, GCRYMPI_FMT_USG, buf, buflen, NULL) == 0);
- len = (gcry_mpi_get_nbits(h) + 7) / 8;
+ assert_se(sym_gcry_mpi_scan(&h, GCRYMPI_FMT_USG, buf, buflen, NULL) == 0);
+ len = (sym_gcry_mpi_get_nbits(h) + 7) / 8;
assert(len <= buflen);
- assert(gcry_mpi_cmp_ui(h, 0) >= 0);
+ assert(sym_gcry_mpi_cmp_ui(h, 0) >= 0);
return h;
}
gcry_error_t err;
uint32_t ctr;
- olen = gcry_md_get_algo_dlen(RND_HASH);
- err = gcry_md_open(&hd, RND_HASH, 0);
+ olen = sym_gcry_md_get_algo_dlen(RND_HASH);
+ err = sym_gcry_md_open(&hd, RND_HASH, 0);
assert_se(gcry_err_code(err) == GPG_ERR_NO_ERROR); /* This shouldn't happen */
- gcry_md_write(hd, seed, seedlen);
- gcry_md_putc(hd, (idx >> 24) & 0xff);
- gcry_md_putc(hd, (idx >> 16) & 0xff);
- gcry_md_putc(hd, (idx >> 8) & 0xff);
- gcry_md_putc(hd, (idx >> 0) & 0xff);
+ sym_gcry_md_write(hd, seed, seedlen);
+ sym_gcry_md_putc(hd, (idx >> 24) & 0xff);
+ sym_gcry_md_putc(hd, (idx >> 16) & 0xff);
+ sym_gcry_md_putc(hd, (idx >> 8) & 0xff);
+ sym_gcry_md_putc(hd, (idx >> 0) & 0xff);
for (ctr = 0; buflen; ctr++) {
- err = gcry_md_copy(&hd2, hd);
+ err = sym_gcry_md_copy(&hd2, hd);
assert_se(gcry_err_code(err) == GPG_ERR_NO_ERROR); /* This shouldn't happen */
- gcry_md_putc(hd2, (ctr >> 24) & 0xff);
- gcry_md_putc(hd2, (ctr >> 16) & 0xff);
- gcry_md_putc(hd2, (ctr >> 8) & 0xff);
- gcry_md_putc(hd2, (ctr >> 0) & 0xff);
- gcry_md_final(hd2);
+ sym_gcry_md_putc(hd2, (ctr >> 24) & 0xff);
+ sym_gcry_md_putc(hd2, (ctr >> 16) & 0xff);
+ sym_gcry_md_putc(hd2, (ctr >> 8) & 0xff);
+ sym_gcry_md_putc(hd2, (ctr >> 0) & 0xff);
+ sym_gcry_md_ctl(hd2, GCRYCTL_FINALIZE, NULL, 0);
cpylen = (buflen < olen) ? buflen : olen;
- memcpy(buf, gcry_md_read(hd2, RND_HASH), cpylen);
- gcry_md_close(hd2);
+ memcpy(buf, sym_gcry_md_read(hd2, RND_HASH), cpylen);
+ sym_gcry_md_close(hd2);
buf += cpylen;
buflen -= cpylen;
}
- gcry_md_close(hd);
+ sym_gcry_md_close(hd);
}
/* deterministically generate from seed/idx a prime of length `bits' that is 3 (mod 4) */
buf[buflen - 1] |= 0x03; /* set lower two bits, to have result 3 (mod 4) */
p = mpi_import(buf, buflen);
- while (gcry_prime_check(p, 0))
- gcry_mpi_add_ui(p, p, 4);
+ while (sym_gcry_prime_check(p, 0))
+ sym_gcry_mpi_add_ui(p, p, 4);
return p;
}
det_randomize(buf, buflen, seed, seedlen, idx);
buf[0] &= 0x7f; /* clear upper bit, so that we have x < n */
x = mpi_import(buf, buflen);
- assert(gcry_mpi_cmp(x, n) < 0);
- gcry_mpi_mulm(x, x, x, n);
+ assert(sym_gcry_mpi_cmp(x, n) < 0);
+ sym_gcry_mpi_mulm(x, x, x, n);
return x;
}
gcry_mpi_t phi, r;
int n;
- phi = gcry_mpi_new(0);
- gcry_mpi_sub_ui(phi, p, 1);
+ phi = sym_gcry_mpi_new(0);
+ sym_gcry_mpi_sub_ui(phi, p, 1);
/* count number of used bits in m */
for (n = 0; (1ULL << n) <= m; n++)
;
- r = gcry_mpi_new(0);
- gcry_mpi_set_ui(r, 1);
+ r = sym_gcry_mpi_new(0);
+ sym_gcry_mpi_set_ui(r, 1);
while (n) { /* square and multiply algorithm for fast exponentiation */
n--;
- gcry_mpi_mulm(r, r, r, phi);
+ sym_gcry_mpi_mulm(r, r, r, phi);
if (m & ((uint64_t)1 << n)) {
- gcry_mpi_add(r, r, r);
- if (gcry_mpi_cmp(r, phi) >= 0)
- gcry_mpi_sub(r, r, phi);
+ sym_gcry_mpi_add(r, r, r);
+ if (sym_gcry_mpi_cmp(r, phi) >= 0)
+ sym_gcry_mpi_sub(r, r, phi);
}
}
- gcry_mpi_release(phi);
+ sym_gcry_mpi_release(phi);
return r;
}
/* Decompose $x \in Z_n$ into $(xp,xq) \in Z_p \times Z_q$ using Chinese Remainder Theorem */
static void CRT_decompose(gcry_mpi_t *xp, gcry_mpi_t *xq, const gcry_mpi_t x, const gcry_mpi_t p, const gcry_mpi_t q) {
- *xp = gcry_mpi_new(0);
- *xq = gcry_mpi_new(0);
- gcry_mpi_mod(*xp, x, p);
- gcry_mpi_mod(*xq, x, q);
+ *xp = sym_gcry_mpi_new(0);
+ *xq = sym_gcry_mpi_new(0);
+ sym_gcry_mpi_mod(*xp, x, p);
+ sym_gcry_mpi_mod(*xq, x, q);
}
/* Compose $(xp,xq) \in Z_p \times Z_q$ into $x \in Z_n$ using Chinese Remainder Theorem */
static void CRT_compose(gcry_mpi_t *x, const gcry_mpi_t xp, const gcry_mpi_t xq, const gcry_mpi_t p, const gcry_mpi_t q) {
gcry_mpi_t a, u;
- a = gcry_mpi_new(0);
- u = gcry_mpi_new(0);
- *x = gcry_mpi_new(0);
- gcry_mpi_subm(a, xq, xp, q);
- gcry_mpi_invm(u, p, q);
- gcry_mpi_mulm(a, a, u, q); /* a = (xq - xp) / p (mod q) */
- gcry_mpi_mul(*x, p, a);
- gcry_mpi_add(*x, *x, xp); /* x = p * ((xq - xp) / p mod q) + xp */
- gcry_mpi_release(a);
- gcry_mpi_release(u);
+ a = sym_gcry_mpi_new(0);
+ u = sym_gcry_mpi_new(0);
+ *x = sym_gcry_mpi_new(0);
+ sym_gcry_mpi_subm(a, xq, xp, q);
+ sym_gcry_mpi_invm(u, p, q);
+ sym_gcry_mpi_mulm(a, a, u, q); /* a = (xq - xp) / p (mod q) */
+ sym_gcry_mpi_mul(*x, p, a);
+ sym_gcry_mpi_add(*x, *x, xp); /* x = p * ((xq - xp) / p mod q) + xp */
+ sym_gcry_mpi_release(a);
+ sym_gcry_mpi_release(u);
}
/******************************************************************************/
return 16 * (secpar + 1);
}
-void FSPRG_GenMK(void *msk, void *mpk, const void *seed, size_t seedlen, unsigned _secpar) {
+int FSPRG_GenMK(void *msk, void *mpk, const void *seed, size_t seedlen, unsigned _secpar) {
uint8_t iseed[FSPRG_RECOMMENDED_SEEDLEN];
gcry_mpi_t n, p, q;
uint16_t secpar;
+ int r;
VALIDATE_SECPAR(_secpar);
secpar = _secpar;
- initialize_libgcrypt(false);
+ r = initialize_libgcrypt(false);
+ if (r < 0)
+ return r;
if (!seed) {
- gcry_randomize(iseed, FSPRG_RECOMMENDED_SEEDLEN, GCRY_STRONG_RANDOM);
+ sym_gcry_randomize(iseed, FSPRG_RECOMMENDED_SEEDLEN, GCRY_STRONG_RANDOM);
seed = iseed;
seedlen = FSPRG_RECOMMENDED_SEEDLEN;
}
}
if (mpk) {
- n = gcry_mpi_new(0);
- gcry_mpi_mul(n, p, q);
- assert(gcry_mpi_get_nbits(n) == secpar);
+ n = sym_gcry_mpi_new(0);
+ sym_gcry_mpi_mul(n, p, q);
+ assert(sym_gcry_mpi_get_nbits(n) == secpar);
store_secpar(mpk + 0, secpar);
mpi_export(mpk + 2, secpar / 8, n);
- gcry_mpi_release(n);
+ sym_gcry_mpi_release(n);
}
- gcry_mpi_release(p);
- gcry_mpi_release(q);
+ sym_gcry_mpi_release(p);
+ sym_gcry_mpi_release(q);
+
+ return 0;
}
-void FSPRG_GenState0(void *state, const void *mpk, const void *seed, size_t seedlen) {
+int FSPRG_GenState0(void *state, const void *mpk, const void *seed, size_t seedlen) {
gcry_mpi_t n, x;
uint16_t secpar;
+ int r;
- initialize_libgcrypt(false);
+ r = initialize_libgcrypt(false);
+ if (r < 0)
+ return r;
secpar = read_secpar(mpk + 0);
n = mpi_import(mpk + 2, secpar / 8);
mpi_export(state + 2 + 1 * secpar / 8, secpar / 8, x);
memzero(state + 2 + 2 * secpar / 8, 8);
- gcry_mpi_release(n);
- gcry_mpi_release(x);
+ sym_gcry_mpi_release(n);
+ sym_gcry_mpi_release(x);
+
+ return 0;
}
-void FSPRG_Evolve(void *state) {
+int FSPRG_Evolve(void *state) {
gcry_mpi_t n, x;
uint16_t secpar;
uint64_t epoch;
+ int r;
- initialize_libgcrypt(false);
+ r = initialize_libgcrypt(false);
+ if (r < 0)
+ return r;
secpar = read_secpar(state + 0);
n = mpi_import(state + 2 + 0 * secpar / 8, secpar / 8);
x = mpi_import(state + 2 + 1 * secpar / 8, secpar / 8);
epoch = uint64_import(state + 2 + 2 * secpar / 8, 8);
- gcry_mpi_mulm(x, x, x, n);
+ sym_gcry_mpi_mulm(x, x, x, n);
epoch++;
mpi_export(state + 2 + 1 * secpar / 8, secpar / 8, x);
uint64_export(state + 2 + 2 * secpar / 8, 8, epoch);
- gcry_mpi_release(n);
- gcry_mpi_release(x);
+ sym_gcry_mpi_release(n);
+ sym_gcry_mpi_release(x);
+
+ return 0;
}
uint64_t FSPRG_GetEpoch(const void *state) {
return uint64_import(state + 2 + 2 * secpar / 8, 8);
}
-void FSPRG_Seek(void *state, uint64_t epoch, const void *msk, const void *seed, size_t seedlen) {
+int FSPRG_Seek(void *state, uint64_t epoch, const void *msk, const void *seed, size_t seedlen) {
gcry_mpi_t p, q, n, x, xp, xq, kp, kq, xm;
uint16_t secpar;
+ int r;
- initialize_libgcrypt(false);
+ r = initialize_libgcrypt(false);
+ if (r < 0)
+ return r;
secpar = read_secpar(msk + 0);
p = mpi_import(msk + 2 + 0 * (secpar / 2) / 8, (secpar / 2) / 8);
q = mpi_import(msk + 2 + 1 * (secpar / 2) / 8, (secpar / 2) / 8);
- n = gcry_mpi_new(0);
- gcry_mpi_mul(n, p, q);
+ n = sym_gcry_mpi_new(0);
+ sym_gcry_mpi_mul(n, p, q);
x = gensquare(n, seed, seedlen, RND_GEN_X, secpar);
CRT_decompose(&xp, &xq, x, p, q); /* split (mod n) into (mod p) and (mod q) using CRT */
kp = twopowmodphi(epoch, p); /* compute 2^epoch (mod phi(p)) */
kq = twopowmodphi(epoch, q); /* compute 2^epoch (mod phi(q)) */
- gcry_mpi_powm(xp, xp, kp, p); /* compute x^(2^epoch) (mod p) */
- gcry_mpi_powm(xq, xq, kq, q); /* compute x^(2^epoch) (mod q) */
+ sym_gcry_mpi_powm(xp, xp, kp, p); /* compute x^(2^epoch) (mod p) */
+ sym_gcry_mpi_powm(xq, xq, kq, q); /* compute x^(2^epoch) (mod q) */
CRT_compose(&xm, xp, xq, p, q); /* combine (mod p) and (mod q) to (mod n) using CRT */
mpi_export(state + 2 + 1 * secpar / 8, secpar / 8, xm);
uint64_export(state + 2 + 2 * secpar / 8, 8, epoch);
- gcry_mpi_release(p);
- gcry_mpi_release(q);
- gcry_mpi_release(n);
- gcry_mpi_release(x);
- gcry_mpi_release(xp);
- gcry_mpi_release(xq);
- gcry_mpi_release(kp);
- gcry_mpi_release(kq);
- gcry_mpi_release(xm);
+ sym_gcry_mpi_release(p);
+ sym_gcry_mpi_release(q);
+ sym_gcry_mpi_release(n);
+ sym_gcry_mpi_release(x);
+ sym_gcry_mpi_release(xp);
+ sym_gcry_mpi_release(xq);
+ sym_gcry_mpi_release(kp);
+ sym_gcry_mpi_release(kq);
+ sym_gcry_mpi_release(xm);
+
+ return 0;
}
-void FSPRG_GetKey(const void *state, void *key, size_t keylen, uint32_t idx) {
+int FSPRG_GetKey(const void *state, void *key, size_t keylen, uint32_t idx) {
uint16_t secpar;
+ int r;
- initialize_libgcrypt(false);
+ r = initialize_libgcrypt(false);
+ if (r < 0)
+ return r;
secpar = read_secpar(state + 0);
det_randomize(key, keylen, state + 2, 2 * secpar / 8 + 8, idx);
+
+ return 0;
}
* fsprg v0.1 - (seekable) forward-secure pseudorandom generator
* Copyright © 2012 B. Poettering
* Contact: fsprg@point-at-infinity.org
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 2.1 of the License, or (at your option) any later version.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
- * 02110-1301 USA
*/
#include <inttypes.h>
size_t FSPRG_stateinbytes(unsigned secpar) _const_;
/* Setup msk and mpk. Providing seed != NULL makes this algorithm deterministic. */
-void FSPRG_GenMK(void *msk, void *mpk, const void *seed, size_t seedlen, unsigned secpar);
+int FSPRG_GenMK(void *msk, void *mpk, const void *seed, size_t seedlen, unsigned secpar);
/* Initialize state deterministically in dependence on seed. */
/* Note: in case one wants to run only one GenState0 per GenMK it is safe to use
the same seed for both GenMK and GenState0.
*/
-void FSPRG_GenState0(void *state, const void *mpk, const void *seed, size_t seedlen);
+int FSPRG_GenState0(void *state, const void *mpk, const void *seed, size_t seedlen);
-void FSPRG_Evolve(void *state);
+int FSPRG_Evolve(void *state);
uint64_t FSPRG_GetEpoch(const void *state) _pure_;
/* Seek to any arbitrary state (by providing msk together with seed from GenState0). */
-void FSPRG_Seek(void *state, uint64_t epoch, const void *msk, const void *seed, size_t seedlen);
+int FSPRG_Seek(void *state, uint64_t epoch, const void *msk, const void *seed, size_t seedlen);
-void FSPRG_GetKey(const void *state, void *key, size_t keylen, uint32_t idx);
+int FSPRG_GetKey(const void *state, void *key, size_t keylen, uint32_t idx);
#ifdef __cplusplus
}
return r;
/* Get the HMAC tag and store it in the object */
- memcpy(o->tag.tag, gcry_md_read(f->hmac, 0), TAG_LENGTH);
+ memcpy(o->tag.tag, sym_gcry_md_read(f->hmac, 0), TAG_LENGTH);
f->hmac_running = false;
return 0;
int journal_file_hmac_start(JournalFile *f) {
uint8_t key[256 / 8]; /* Let's pass 256 bit from FSPRG to HMAC */
gcry_error_t err;
+ int r;
assert(f);
return 0;
/* Prepare HMAC for next cycle */
- gcry_md_reset(f->hmac);
- FSPRG_GetKey(f->fsprg_state, key, sizeof(key), 0);
- err = gcry_md_setkey(f->hmac, key, sizeof(key));
+ sym_gcry_md_reset(f->hmac);
+
+ r = FSPRG_GetKey(f->fsprg_state, key, sizeof(key), 0);
+ if (r < 0)
+ return r;
+
+ err = sym_gcry_md_setkey(f->hmac, key, sizeof(key));
if (gcry_err_code(err) != GPG_ERR_NO_ERROR)
return log_debug_errno(SYNTHETIC_ERRNO(EIO),
- "gcry_md_setkey() failed with error code: %s",
- gcry_strerror(err));
+ "sym_gcry_md_setkey() failed with error code: %s",
+ sym_gcry_strerror(err));
f->hmac_running = true;
if (epoch == goal)
return 0;
- FSPRG_Evolve(f->fsprg_state);
+ r = FSPRG_Evolve(f->fsprg_state);
+ if (r < 0)
+ return r;
+
epoch = FSPRG_GetEpoch(f->fsprg_state);
if (epoch < goal) {
r = journal_file_append_tag(f);
int journal_file_fsprg_seek(JournalFile *f, uint64_t goal) {
void *msk;
uint64_t epoch;
+ int r;
assert(f);
if (goal == epoch)
return 0;
- if (goal == epoch + 1) {
- FSPRG_Evolve(f->fsprg_state);
- return 0;
- }
+ if (goal == epoch + 1)
+ return FSPRG_Evolve(f->fsprg_state);
} else {
f->fsprg_state_size = FSPRG_stateinbytes(FSPRG_RECOMMENDED_SECPAR);
f->fsprg_state = malloc(f->fsprg_state_size);
log_debug("Seeking FSPRG key to %"PRIu64".", goal);
msk = alloca_safe(FSPRG_mskinbytes(FSPRG_RECOMMENDED_SECPAR));
- FSPRG_GenMK(msk, NULL, f->fsprg_seed, f->fsprg_seed_size, FSPRG_RECOMMENDED_SECPAR);
- FSPRG_Seek(f->fsprg_state, goal, msk, f->fsprg_seed, f->fsprg_seed_size);
- return 0;
+ r = FSPRG_GenMK(msk, NULL, f->fsprg_seed, f->fsprg_seed_size, FSPRG_RECOMMENDED_SECPAR);
+ if (r < 0)
+ return r;
+
+ return FSPRG_Seek(f->fsprg_state, goal, msk, f->fsprg_seed, f->fsprg_seed_size);
}
int journal_file_maybe_append_tag(JournalFile *f, uint64_t realtime) {
} else if (type > OBJECT_UNUSED && o->object.type != type)
return -EBADMSG;
- gcry_md_write(f->hmac, o, offsetof(ObjectHeader, payload));
+ sym_gcry_md_write(f->hmac, o, offsetof(ObjectHeader, payload));
switch (o->object.type) {
case OBJECT_DATA:
/* All but hash and payload are mutable */
- gcry_md_write(f->hmac, &o->data.hash, sizeof(o->data.hash));
- gcry_md_write(f->hmac, journal_file_data_payload_field(f, o), le64toh(o->object.size) - journal_file_data_payload_offset(f));
+ sym_gcry_md_write(f->hmac, &o->data.hash, sizeof(o->data.hash));
+ sym_gcry_md_write(f->hmac, journal_file_data_payload_field(f, o), le64toh(o->object.size) - journal_file_data_payload_offset(f));
break;
case OBJECT_FIELD:
/* Same here */
- gcry_md_write(f->hmac, &o->field.hash, sizeof(o->field.hash));
- gcry_md_write(f->hmac, o->field.payload, le64toh(o->object.size) - offsetof(Object, field.payload));
+ sym_gcry_md_write(f->hmac, &o->field.hash, sizeof(o->field.hash));
+ sym_gcry_md_write(f->hmac, o->field.payload, le64toh(o->object.size) - offsetof(Object, field.payload));
break;
case OBJECT_ENTRY:
/* All */
- gcry_md_write(f->hmac, &o->entry.seqnum, le64toh(o->object.size) - offsetof(Object, entry.seqnum));
+ sym_gcry_md_write(f->hmac, &o->entry.seqnum, le64toh(o->object.size) - offsetof(Object, entry.seqnum));
break;
case OBJECT_FIELD_HASH_TABLE:
case OBJECT_TAG:
/* All but the tag itself */
- gcry_md_write(f->hmac, &o->tag.seqnum, sizeof(o->tag.seqnum));
- gcry_md_write(f->hmac, &o->tag.epoch, sizeof(o->tag.epoch));
+ sym_gcry_md_write(f->hmac, &o->tag.seqnum, sizeof(o->tag.seqnum));
+ sym_gcry_md_write(f->hmac, &o->tag.epoch, sizeof(o->tag.epoch));
break;
default:
return -EINVAL;
* tail_entry_monotonic, n_data, n_fields, n_tags,
* n_entry_arrays. */
- gcry_md_write(f->hmac, f->header->signature, offsetof(Header, state) - offsetof(Header, signature));
- gcry_md_write(f->hmac, &f->header->file_id, offsetof(Header, tail_entry_boot_id) - offsetof(Header, file_id));
- gcry_md_write(f->hmac, &f->header->seqnum_id, offsetof(Header, arena_size) - offsetof(Header, seqnum_id));
- gcry_md_write(f->hmac, &f->header->data_hash_table_offset, offsetof(Header, tail_object_offset) - offsetof(Header, data_hash_table_offset));
+ sym_gcry_md_write(f->hmac, f->header->signature, offsetof(Header, state) - offsetof(Header, signature));
+ sym_gcry_md_write(f->hmac, &f->header->file_id, offsetof(Header, tail_entry_boot_id) - offsetof(Header, file_id));
+ sym_gcry_md_write(f->hmac, &f->header->seqnum_id, offsetof(Header, arena_size) - offsetof(Header, seqnum_id));
+ sym_gcry_md_write(f->hmac, &f->header->data_hash_table_offset, offsetof(Header, tail_object_offset) - offsetof(Header, data_hash_table_offset));
return 0;
}
int journal_file_hmac_setup(JournalFile *f) {
gcry_error_t e;
+ int r;
if (!JOURNAL_HEADER_SEALED(f->header))
return 0;
- initialize_libgcrypt(true);
+ r = initialize_libgcrypt(true);
+ if (r < 0)
+ return r;
- e = gcry_md_open(&f->hmac, GCRY_MD_SHA256, GCRY_MD_FLAG_HMAC);
+ e = sym_gcry_md_open(&f->hmac, GCRY_MD_SHA256, GCRY_MD_FLAG_HMAC);
if (e != 0)
return -EOPNOTSUPP;
#include "fd-util.h"
#include "format-util.h"
#include "fs-util.h"
+#include "gcrypt-util.h"
#include "id128-util.h"
#include "journal-authenticate.h"
#include "journal-def.h"
free(f->fsprg_seed);
if (f->hmac)
- gcry_md_close(f->hmac);
+ sym_gcry_md_close(f->hmac);
#endif
return mfree(f);
return -ENODATA;
if (!VALID_REALTIME(le64toh(f->header->tail_entry_realtime)))
return -ENODATA;
- if (!VALID_MONOTONIC(le64toh(f->header->tail_entry_realtime)))
+ if (!VALID_MONOTONIC(le64toh(f->header->tail_entry_monotonic)))
return -ENODATA;
} else {
/* Otherwise, the fields must be zero. */
return -ENODATA;
if (f->header->tail_entry_realtime != 0)
return -ENODATA;
- if (f->header->tail_entry_realtime != 0)
+ if (f->header->tail_entry_monotonic != 0)
return -ENODATA;
}
}
ts->realtime);
if (!VALID_MONOTONIC(ts->monotonic))
return log_debug_errno(SYNTHETIC_ERRNO(EBADMSG),
- "Invalid monotomic timestamp %" PRIu64 ", refusing entry.",
+ "Invalid monotonic timestamp %" PRIu64 ", refusing entry.",
ts->monotonic);
} else {
dual_timestamp_now(&_ts);
#include "fd-util.h"
#include "fileio.h"
#include "fs-util.h"
+#include "gcrypt-util.h"
#include "journal-authenticate.h"
#include "journal-def.h"
#include "journal-file.h"
if (r < 0)
goto fail;
- if (memcmp(o->tag.tag, gcry_md_read(f->hmac, 0), TAG_LENGTH) != 0) {
+ if (memcmp(o->tag.tag, sym_gcry_md_read(f->hmac, 0), TAG_LENGTH) != 0) {
error(p, "Tag failed verification");
r = -EBADMSG;
goto fail;
assert_return(!journal_origin_changed(j), -ECHILD);
assert_return(data, -EINVAL);
- if (size == 0)
+ /* If the size is unspecified, assume it's a string. Note: 0 is the public value we document for
+ * this, for historical reasons. Internally, we pretty widely started using SIZE_MAX for this in
+ * similar cases however, hence accept that too. And internally we actually prefer it, to make things
+ * less surprising. */
+ if (IN_SET(size, 0, SIZE_MAX))
size = strlen(data);
if (!match_is_valid(data, size))
if (!s)
return -ENOMEM;
- return sd_journal_add_match(j, s, 0);
+ return sd_journal_add_match(j, s, SIZE_MAX);
}
int journal_add_matchf(sd_journal *j, const char *format, ...) {
if (r < 0)
return -ENOMEM;
- return sd_journal_add_match(j, s, 0);
+ return sd_journal_add_match(j, s, SIZE_MAX);
}
_public_ int sd_journal_add_conjunction(sd_journal *j) {
assert_se(sd_journal_open(&j, SD_JOURNAL_LOCAL_ONLY | SD_JOURNAL_ASSUME_IMMUTABLE) >= 0);
- assert_se(sd_journal_add_match(j, "_TRANSPORT=syslog", 0) >= 0);
- assert_se(sd_journal_add_match(j, "_UID=0", 0) >= 0);
+ assert_se(sd_journal_add_match(j, "_TRANSPORT=syslog", SIZE_MAX) >= 0);
+ assert_se(sd_journal_add_match(j, "_UID=0", SIZE_MAX) >= 0);
SD_JOURNAL_FOREACH_BACKWARDS(j) {
const void *d;
assert_se(sd_journal_open(&j, SD_JOURNAL_ASSUME_IMMUTABLE) >= 0);
- assert_se(sd_journal_add_match(j, "foobar", 0) < 0);
- assert_se(sd_journal_add_match(j, "foobar=waldo", 0) < 0);
- assert_se(sd_journal_add_match(j, "", 0) < 0);
- assert_se(sd_journal_add_match(j, "=", 0) < 0);
- assert_se(sd_journal_add_match(j, "=xxxxx", 0) < 0);
+ assert_se(sd_journal_add_match(j, "foobar", SIZE_MAX) < 0);
+ assert_se(sd_journal_add_match(j, "foobar=waldo", SIZE_MAX) < 0);
+ assert_se(sd_journal_add_match(j, "", SIZE_MAX) < 0);
+ assert_se(sd_journal_add_match(j, "=", SIZE_MAX) < 0);
+ assert_se(sd_journal_add_match(j, "=xxxxx", SIZE_MAX) < 0);
assert_se(sd_journal_add_match(j, (uint8_t[4]){'A', '=', '\1', '\2'}, 4) >= 0);
assert_se(sd_journal_add_match(j, (uint8_t[5]){'B', '=', 'C', '\0', 'D'}, 5) >= 0);
- assert_se(sd_journal_add_match(j, "HALLO=WALDO", 0) >= 0);
- assert_se(sd_journal_add_match(j, "QUUX=mmmm", 0) >= 0);
- assert_se(sd_journal_add_match(j, "QUUX=xxxxx", 0) >= 0);
- assert_se(sd_journal_add_match(j, "HALLO=", 0) >= 0);
- assert_se(sd_journal_add_match(j, "QUUX=xxxxx", 0) >= 0);
- assert_se(sd_journal_add_match(j, "QUUX=yyyyy", 0) >= 0);
- assert_se(sd_journal_add_match(j, "PIFF=paff", 0) >= 0);
+ assert_se(sd_journal_add_match(j, "HALLO=WALDO", SIZE_MAX) >= 0);
+ assert_se(sd_journal_add_match(j, "QUUX=mmmm", SIZE_MAX) >= 0);
+ assert_se(sd_journal_add_match(j, "QUUX=xxxxx", SIZE_MAX) >= 0);
+ assert_se(sd_journal_add_match(j, "HALLO=", SIZE_MAX) >= 0);
+ assert_se(sd_journal_add_match(j, "QUUX=xxxxx", SIZE_MAX) >= 0);
+ assert_se(sd_journal_add_match(j, "QUUX=yyyyy", SIZE_MAX) >= 0);
+ assert_se(sd_journal_add_match(j, "PIFF=paff", SIZE_MAX) >= 0);
assert_se(sd_journal_add_disjunction(j) >= 0);
- assert_se(sd_journal_add_match(j, "ONE=one", 0) >= 0);
- assert_se(sd_journal_add_match(j, "ONE=two", 0) >= 0);
- assert_se(sd_journal_add_match(j, "TWO=two", 0) >= 0);
+ assert_se(sd_journal_add_match(j, "ONE=one", SIZE_MAX) >= 0);
+ assert_se(sd_journal_add_match(j, "ONE=two", SIZE_MAX) >= 0);
+ assert_se(sd_journal_add_match(j, "TWO=two", SIZE_MAX) >= 0);
assert_se(sd_journal_add_conjunction(j) >= 0);
- assert_se(sd_journal_add_match(j, "L4_1=yes", 0) >= 0);
- assert_se(sd_journal_add_match(j, "L4_1=ok", 0) >= 0);
- assert_se(sd_journal_add_match(j, "L4_2=yes", 0) >= 0);
- assert_se(sd_journal_add_match(j, "L4_2=ok", 0) >= 0);
+ assert_se(sd_journal_add_match(j, "L4_1=yes", SIZE_MAX) >= 0);
+ assert_se(sd_journal_add_match(j, "L4_1=ok", SIZE_MAX) >= 0);
+ assert_se(sd_journal_add_match(j, "L4_2=yes", SIZE_MAX) >= 0);
+ assert_se(sd_journal_add_match(j, "L4_2=ok", SIZE_MAX) >= 0);
assert_se(sd_journal_add_disjunction(j) >= 0);
- assert_se(sd_journal_add_match(j, "L3=yes", 0) >= 0);
- assert_se(sd_journal_add_match(j, "L3=ok", 0) >= 0);
+ assert_se(sd_journal_add_match(j, "L3=yes", SIZE_MAX) >= 0);
+ assert_se(sd_journal_add_match(j, "L3=ok", SIZE_MAX) >= 0);
assert_se(t = journal_make_match_string(j));
assert_se(sd_journal_open_directory(&j, t, SD_JOURNAL_ASSUME_IMMUTABLE) >= 0);
- assert_se(sd_journal_add_match(j, "MAGIC=quux", 0) >= 0);
+ assert_se(sd_journal_add_match(j, "MAGIC=quux", SIZE_MAX) >= 0);
SD_JOURNAL_FOREACH_BACKWARDS(j) {
_cleanup_free_ char *c;
verify_contents(j, 1);
printf("NEXT TEST\n");
- assert_se(sd_journal_add_match(j, "MAGIC=quux", 0) >= 0);
+ assert_se(sd_journal_add_match(j, "MAGIC=quux", SIZE_MAX) >= 0);
assert_se(z = journal_make_match_string(j));
printf("resulting match expression is: %s\n", z);
printf("NEXT TEST\n");
sd_journal_flush_matches(j);
- assert_se(sd_journal_add_match(j, "MAGIC=waldo", 0) >= 0);
- assert_se(sd_journal_add_match(j, "NUMBER=10", 0) >= 0);
- assert_se(sd_journal_add_match(j, "NUMBER=11", 0) >= 0);
- assert_se(sd_journal_add_match(j, "NUMBER=12", 0) >= 0);
+ assert_se(sd_journal_add_match(j, "MAGIC=waldo", SIZE_MAX) >= 0);
+ assert_se(sd_journal_add_match(j, "NUMBER=10", SIZE_MAX) >= 0);
+ assert_se(sd_journal_add_match(j, "NUMBER=11", SIZE_MAX) >= 0);
+ assert_se(sd_journal_add_match(j, "NUMBER=12", SIZE_MAX) >= 0);
assert_se(z = journal_make_match_string(j));
printf("resulting match expression is: %s\n", z);
/* SPDX-License-Identifier: LGPL-2.1-or-later */
+/* Make sure the net/if.h header is included before any linux/ one */
#include <net/if.h>
#include <netinet/ether.h>
#include <netinet/in.h>
/* SPDX-License-Identifier: LGPL-2.1-or-later */
+/* Make sure the net/if.h header is included before any linux/ one */
+#include <net/if.h>
#include <ctype.h>
#include <dirent.h>
#include <errno.h>
#include <fcntl.h>
#include <linux/sockios.h>
-#include <net/if.h>
#include <stdbool.h>
#include <stddef.h>
#include <stdio.h>
"Both reboot via kexec and soft reboot selected, which is not supported");
if (action != HANDLE_REBOOT) {
- if (flags & SD_LOGIND_REBOOT_VIA_KEXEC)
+ if (FLAGS_SET(flags, SD_LOGIND_REBOOT_VIA_KEXEC))
return sd_bus_error_set(error, SD_BUS_ERROR_INVALID_ARGS,
"Reboot via kexec option is only applicable with reboot operations");
- if ((flags & SD_LOGIND_SOFT_REBOOT) || (flags & SD_LOGIND_SOFT_REBOOT_IF_NEXTROOT_SET_UP))
+ if (flags & (SD_LOGIND_SOFT_REBOOT|SD_LOGIND_SOFT_REBOOT_IF_NEXTROOT_SET_UP))
return sd_bus_error_set(error, SD_BUS_ERROR_INVALID_ARGS,
"Soft reboot option is only applicable with reboot operations");
}
const HandleActionData *a = NULL;
- if ((flags & SD_LOGIND_SOFT_REBOOT) ||
- ((flags & SD_LOGIND_SOFT_REBOOT_IF_NEXTROOT_SET_UP) && path_is_os_tree("/run/nextroot") > 0))
+ if (FLAGS_SET(flags, SD_LOGIND_SOFT_REBOOT) ||
+ (FLAGS_SET(flags, SD_LOGIND_SOFT_REBOOT_IF_NEXTROOT_SET_UP) && path_is_os_tree("/run/nextroot") > 0))
a = handle_action_lookup(HANDLE_SOFT_REBOOT);
- else if ((flags & SD_LOGIND_REBOOT_VIA_KEXEC) && kexec_loaded())
+ else if (FLAGS_SET(flags, SD_LOGIND_REBOOT_VIA_KEXEC) && kexec_loaded())
a = handle_action_lookup(HANDLE_KEXEC);
if (action == HANDLE_SLEEP) {
case SLEEP_RESUME_NOT_SUPPORTED:
return sd_bus_error_set(error, BUS_ERROR_SLEEP_VERB_NOT_SUPPORTED,
- "Not running on EFI and resume= is not set. No available method to resume from hibernation");
+ "Not running on EFI and resume= is not set, or noresume is set. No available method to resume from hibernation");
case SLEEP_NOT_ENOUGH_SWAP_SPACE:
return sd_bus_error_set(error, BUS_ERROR_SLEEP_VERB_NOT_SUPPORTED,
char **ret_job) {
_cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL, *reply = NULL;
+ _cleanup_(sd_bus_error_free) sd_bus_error e = SD_BUS_ERROR_NULL;
int r;
assert(manager);
if (r < 0)
return r;
- r = sd_bus_call(manager->bus, m, 0, error, &reply);
+ r = sd_bus_call(manager->bus, m, 0, &e, &reply);
if (r < 0) {
/* If this failed with a property we couldn't write, this is quite likely because the server
* doesn't support PIDFDs yet, let's try without. */
if (allow_pidfd &&
- sd_bus_error_has_names(error, SD_BUS_ERROR_UNKNOWN_PROPERTY, SD_BUS_ERROR_PROPERTY_READ_ONLY))
+ sd_bus_error_has_names(&e, SD_BUS_ERROR_UNKNOWN_PROPERTY, SD_BUS_ERROR_PROPERTY_READ_ONLY))
return manager_start_scope(
manager,
scope,
error,
ret_job);
- return r;
+ return sd_bus_error_move(error, &e);
}
return strdup_job(reply, ret_job);
r = 0;
- FOREACH_DEVICE(e, d)
+ FOREACH_DEVICE(e, d) {
+ if (device_is_processed(d) <= 0)
+ continue;
RET_GATHER(r, manager_process_seat_device(m, d));
+ }
return r;
}
r = 0;
- FOREACH_DEVICE(e, d)
+ FOREACH_DEVICE(e, d) {
+ if (device_is_processed(d) <= 0)
+ continue;
RET_GATHER(r, manager_process_button_device(m, d));
+ }
return r;
}
'name' : 'systemd-modules-load',
'conditions' : ['HAVE_KMOD'],
'sources' : files('modules-load.c'),
- 'dependencies' : libkmod,
+ 'dependencies' : libkmod_cflags,
},
]
STATIC_DESTRUCTOR_REGISTER(arg_proc_cmdline_modules, strv_freep);
-static void systemd_kmod_log(void *data, int priority, const char *file, int line,
- const char *fn, const char *format, va_list args) {
-
- DISABLE_WARNING_FORMAT_NONLITERAL;
- log_internalv(priority, 0, file, line, fn, format, args);
- REENABLE_WARNING;
-}
-
static int add_modules(const char *p) {
_cleanup_strv_free_ char **k = NULL;
}
static int run(int argc, char *argv[]) {
- _cleanup_(kmod_unrefp) struct kmod_ctx *ctx = NULL;
+ _cleanup_(sym_kmod_unrefp) struct kmod_ctx *ctx = NULL;
int r, k;
r = parse_argv(argc, argv);
if (r < 0)
log_warning_errno(r, "Failed to parse kernel command line, ignoring: %m");
- ctx = kmod_new(NULL, NULL);
- if (!ctx)
- return log_oom();
-
- kmod_load_resources(ctx);
- kmod_set_log_fn(ctx, systemd_kmod_log, NULL);
+ r = module_setup_context(&ctx);
+ if (r < 0)
+ return log_error_errno(r, "Failed to initialize libkmod context: %m");
r = 0;
--- /dev/null
+<?xml version="1.0" encoding="UTF-8"?> <!--*-nxml-*-->
+<!DOCTYPE policyconfig PUBLIC "-//freedesktop//DTD PolicyKit Policy Configuration 1.0//EN"
+ "https://www.freedesktop.org/standards/PolicyKit/1/policyconfig.dtd">
+
+<!--
+ SPDX-License-Identifier: LGPL-2.1-or-later
+
+ This file is part of systemd.
+
+ systemd is free software; you can redistribute it and/or modify it
+ under the terms of the GNU Lesser General Public License as published by
+ the Free Software Foundation; either version 2.1 of the License, or
+ (at your option) any later version.
+-->
+
+<policyconfig>
+
+ <vendor>The systemd Project</vendor>
+ <vendor_url>https://systemd.io</vendor_url>
+
+ <!-- Allow mounting DDIs into the host user namespace -->
+ <action id="io.systemd.mount-file-system.mount-image">
+ <!-- This action is generally checked first: we'll first try to mount the image with
+ signature checks on. If that fails, we'll retry with the untrusted action below. -->
+ <description gettext-domain="systemd">Allow mounting of file system image</description>
+ <message gettext-domain="systemd">Authentication is required for an application to mount a file system image.</message>
+ <defaults>
+ <allow_any>auth_admin_keep</allow_any>
+ <allow_inactive>auth_admin_keep</allow_inactive>
+ <allow_active>yes</allow_active>
+ </defaults>
+ </action>
+
+ <action id="io.systemd.mount-file-system.mount-untrusted-image">
+ <!-- If the image cannot be mounted via the regular action because it is not signed by a
+ recognized key, we'll try this action. -->
+ <description gettext-domain="systemd">Allow mounting of untrusted file system image</description>
+ <message gettext-domain="systemd">Authentication is required for an application to mount a cryptographically unsigned file system image or an image whose cryptographic signature is not recognized.</message>
+ <defaults>
+ <allow_any>auth_admin</allow_any>
+ <allow_inactive>auth_admin</allow_inactive>
+ <allow_active>auth_admin</allow_active>
+ </defaults>
+
+ <annotate key="org.freedesktop.policykit.imply">io.systemd.mount-file-system.mount-image</annotate>
+ </action>
+
+ <!-- Allow mounting DDIs into a private user namespace -->
+ <action id="io.systemd.mount-file-system.mount-image-privately">
+ <description gettext-domain="systemd">Allow private mounting of trusted file system image</description>
+ <message gettext-domain="systemd">Authentication is required for an application to privately mount a file system image or an image whose cryptographic signature is recognized.</message>
+ <defaults>
+ <allow_any>yes</allow_any>
+ <allow_inactive>yes</allow_inactive>
+ <allow_active>yes</allow_active>
+ </defaults>
+ </action>
+
+ <action id="io.systemd.mount-file-system.mount-untrusted-image-privately">
+ <description gettext-domain="systemd">Allow private mounting of untrusted file system image</description>
+ <message gettext-domain="systemd">Authentication is required for an application to privately mount a cryptographically unsigned file system image or an image whose cryptographic signature is not recognized.</message>
+ <defaults>
+ <allow_any>auth_admin</allow_any>
+ <allow_inactive>auth_admin</allow_inactive>
+ <allow_active>auth_admin</allow_active>
+ </defaults>
+
+ <annotate key="org.freedesktop.policykit.imply">io.systemd.mount-file-system.mount-image-privately</annotate>
+ </action>
+</policyconfig>
--- /dev/null
+# SPDX-License-Identifier: LGPL-2.1-or-later
+
+systemd_mountwork_sources = files(
+ 'mountwork.c',
+)
+
+systemd_mountfsd_sources = files(
+ 'mountfsd.c',
+ 'mountfsd-manager.c',
+)
+
+executables += [
+ libexec_template + {
+ 'name' : 'systemd-mountfsd',
+ 'conditions' : ['ENABLE_MOUNTFSD'],
+ 'sources' : systemd_mountfsd_sources,
+ },
+ libexec_template + {
+ 'name' : 'systemd-mountwork',
+ 'conditions' : ['ENABLE_MOUNTFSD'],
+ 'sources' : systemd_mountwork_sources,
+ 'link_with' : common_libs,
+ 'dependencies' : common_deps,
+ },
+]
+
+install_data('io.systemd.mount-file-system.policy',
+ install_dir : polkitpolicydir)
--- /dev/null
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+#include <sys/wait.h>
+
+#include "sd-daemon.h"
+
+#include "build-path.h"
+#include "common-signal.h"
+#include "env-util.h"
+#include "fd-util.h"
+#include "fs-util.h"
+#include "mkdir.h"
+#include "mountfsd-manager.h"
+#include "process-util.h"
+#include "set.h"
+#include "signal-util.h"
+#include "socket-util.h"
+#include "stdio-util.h"
+#include "umask-util.h"
+
+#define LISTEN_TIMEOUT_USEC (25 * USEC_PER_SEC)
+
+static int start_workers(Manager *m, bool explicit_request);
+
+static size_t manager_current_workers(Manager *m) {
+ assert(m);
+
+ return set_size(m->workers_fixed) + set_size(m->workers_dynamic);
+}
+
+static int on_worker_exit(sd_event_source *s, const siginfo_t *si, void *userdata) {
+ Manager *m = ASSERT_PTR(userdata);
+
+ assert(s);
+
+ assert_se(!set_remove(m->workers_dynamic, s) != !set_remove(m->workers_fixed, s));
+ sd_event_source_disable_unref(s);
+
+ if (si->si_code == CLD_EXITED) {
+ if (si->si_status == EXIT_SUCCESS)
+ log_debug("Worker " PID_FMT " exited successfully.", si->si_pid);
+ else
+ log_warning("Worker " PID_FMT " died with a failure exit status %i, ignoring.", si->si_pid, si->si_status);
+ } else if (si->si_code == CLD_KILLED)
+ log_warning("Worker " PID_FMT " was killed by signal %s, ignoring.", si->si_pid, signal_to_string(si->si_status));
+ else if (si->si_code == CLD_DUMPED)
+ log_warning("Worker " PID_FMT " dumped core by signal %s, ignoring.", si->si_pid, signal_to_string(si->si_status));
+ else
+ log_warning("Got unexpected exit code via SIGCHLD, ignoring.");
+
+ (void) start_workers(m, /* explicit_request= */ false); /* Fill up workers again if we fell below the low watermark */
+ return 0;
+}
+
+static int on_sigusr2(sd_event_source *s, const struct signalfd_siginfo *si, void *userdata) {
+ Manager *m = ASSERT_PTR(userdata);
+
+ (void) start_workers(m, /* explicit_request= */ true); /* Workers told us there's more work, let's add one more worker as long as we are below the high watermark */
+ return 0;
+}
+
+DEFINE_PRIVATE_HASH_OPS_WITH_KEY_DESTRUCTOR(
+ event_source_hash_ops,
+ sd_event_source,
+ (void (*)(const sd_event_source*, struct siphash*)) trivial_hash_func,
+ (int (*)(const sd_event_source*, const sd_event_source*)) trivial_compare_func,
+ sd_event_source_disable_unref);
+
+int manager_new(Manager **ret) {
+ _cleanup_(manager_freep) Manager *m = NULL;
+ int r;
+
+ m = new(Manager, 1);
+ if (!m)
+ return -ENOMEM;
+
+ *m = (Manager) {
+ .listen_fd = -EBADF,
+ .worker_ratelimit = {
+ .interval = 5 * USEC_PER_SEC,
+ .burst = 50,
+ },
+ };
+
+ r = sd_event_new(&m->event);
+ if (r < 0)
+ return r;
+
+ r = sd_event_set_signal_exit(m->event, true);
+ if (r < 0)
+ return r;
+
+ r = sd_event_add_signal(m->event, NULL, (SIGRTMIN+18)|SD_EVENT_SIGNAL_PROCMASK, sigrtmin18_handler, NULL);
+ if (r < 0)
+ return r;
+
+ r = sd_event_add_memory_pressure(m->event, NULL, NULL, NULL);
+ if (r < 0)
+ log_debug_errno(r, "Failed allocate memory pressure event source, ignoring: %m");
+
+ r = sd_event_set_watchdog(m->event, true);
+ if (r < 0)
+ log_debug_errno(r, "Failed to enable watchdog handling, ignoring: %m");
+
+ r = sd_event_add_signal(m->event, NULL, SIGUSR2|SD_EVENT_SIGNAL_PROCMASK, on_sigusr2, m);
+ if (r < 0)
+ return r;
+
+ *ret = TAKE_PTR(m);
+ return 0;
+}
+
+Manager* manager_free(Manager *m) {
+ if (!m)
+ return NULL;
+
+ set_free(m->workers_fixed);
+ set_free(m->workers_dynamic);
+
+ /* Note: we rely on PR_DEATHSIG to kill the workers for us */
+
+ sd_event_unref(m->event);
+
+ return mfree(m);
+}
+
+static int start_one_worker(Manager *m) {
+ _cleanup_(sd_event_source_disable_unrefp) sd_event_source *source = NULL;
+ bool fixed;
+ pid_t pid;
+ int r;
+
+ assert(m);
+
+ fixed = set_size(m->workers_fixed) < MOUNTFS_WORKERS_MIN;
+
+ r = safe_fork_full(
+ "(sd-worker)",
+ /* stdio_fds= */ NULL,
+ &m->listen_fd, 1,
+ FORK_RESET_SIGNALS|FORK_DEATHSIG_SIGTERM|FORK_REOPEN_LOG|FORK_LOG|FORK_CLOSE_ALL_FDS,
+ &pid);
+ if (r < 0)
+ return log_error_errno(r, "Failed to fork new worker child: %m");
+ if (r == 0) {
+ char pids[DECIMAL_STR_MAX(pid_t)];
+ /* Child */
+
+ if (m->listen_fd == 3) {
+ r = fd_cloexec(3, false);
+ if (r < 0) {
+ log_error_errno(r, "Failed to turn off O_CLOEXEC for fd 3: %m");
+ _exit(EXIT_FAILURE);
+ }
+ } else {
+ if (dup2(m->listen_fd, 3) < 0) { /* dup2() creates with O_CLOEXEC off */
+ log_error_errno(errno, "Failed to move listen fd to 3: %m");
+ _exit(EXIT_FAILURE);
+ }
+
+ safe_close(m->listen_fd);
+ }
+
+ xsprintf(pids, PID_FMT, pid);
+ if (setenv("LISTEN_PID", pids, 1) < 0) {
+ log_error_errno(errno, "Failed to set $LISTEN_PID: %m");
+ _exit(EXIT_FAILURE);
+ }
+
+ if (setenv("LISTEN_FDS", "1", 1) < 0) {
+ log_error_errno(errno, "Failed to set $LISTEN_FDS: %m");
+ _exit(EXIT_FAILURE);
+ }
+
+ if (setenv("MOUNTFS_FIXED_WORKER", one_zero(fixed), 1) < 0) {
+ log_error_errno(errno, "Failed to set $MOUNTFS_FIXED_WORKER: %m");
+ _exit(EXIT_FAILURE);
+ }
+
+ r = setenv_systemd_log_level();
+ if (r < 0) {
+ log_error_errno(r, "Failed to set $SYSTEMD_LOG_LEVEL: %m");
+ _exit(EXIT_FAILURE);
+ }
+
+ r = invoke_callout_binary(SYSTEMD_MOUNTWORK_PATH, STRV_MAKE("systemd-mountwork", "xxxxxxxxxxxxxxxx")); /* With some extra space rename_process() can make use of */
+ log_error_errno(r, "Failed start worker process: %m");
+ _exit(EXIT_FAILURE);
+ }
+
+ r = sd_event_add_child(m->event, &source, pid, WEXITED, on_worker_exit, m);
+ if (r < 0)
+ return log_error_errno(r, "Failed to watch child " PID_FMT ": %m", pid);
+
+ r = set_ensure_put(
+ fixed ? &m->workers_fixed : &m->workers_dynamic,
+ &event_source_hash_ops,
+ source);
+ if (r < 0)
+ return log_error_errno(r, "Failed to add child process to set: %m");
+
+ TAKE_PTR(source);
+
+ return 0;
+}
+
+static int start_workers(Manager *m, bool explicit_request) {
+ int r;
+
+ assert(m);
+
+ for (;;) {
+ size_t n;
+
+ n = manager_current_workers(m);
+
+ log_debug("%zu workers running.", n);
+
+ if (n >= MOUNTFS_WORKERS_MIN && (!explicit_request || n >= MOUNTFS_WORKERS_MAX))
+ break;
+
+ if (!ratelimit_below(&m->worker_ratelimit)) {
+ /* If we keep starting workers too often, let's fail the whole daemon, something is wrong */
+ sd_event_exit(m->event, EXIT_FAILURE);
+
+ return log_error_errno(SYNTHETIC_ERRNO(EUCLEAN), "Worker threads requested too frequently, something is wrong.");
+ }
+
+ r = start_one_worker(m);
+ if (r < 0)
+ return r;
+
+ explicit_request = false;
+ }
+
+ return 0;
+}
+
+int manager_startup(Manager *m) {
+ int n;
+
+ assert(m);
+ assert(m->listen_fd < 0);
+
+ n = sd_listen_fds(false);
+ if (n < 0)
+ return log_error_errno(n, "Failed to determine number of passed file descriptors: %m");
+ if (n > 1)
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Expected one listening fd, got %i.", n);
+ if (n == 1)
+ m->listen_fd = SD_LISTEN_FDS_START;
+ else {
+ static const union sockaddr_union sockaddr = {
+ .un.sun_family = AF_UNIX,
+ .un.sun_path = "/run/systemd/io.systemd.MountFileSystem",
+ };
+
+ m->listen_fd = socket(AF_UNIX, SOCK_STREAM|SOCK_CLOEXEC, 0);
+ if (m->listen_fd < 0)
+ return log_error_errno(errno, "Failed to bind on socket: %m");
+
+ (void) sockaddr_un_unlink(&sockaddr.un);
+
+ WITH_UMASK(0000)
+ if (bind(m->listen_fd, &sockaddr.sa, SOCKADDR_UN_LEN(sockaddr.un)) < 0)
+ return log_error_errno(errno, "Failed to bind socket: %m");
+
+ if (listen(m->listen_fd, SOMAXCONN) < 0)
+ return log_error_errno(errno, "Failed to listen on socket: %m");
+ }
+
+ /* Let's make sure every accept() call on this socket times out after 25s. This allows workers to be
+ * GC'ed on idle */
+ if (setsockopt(m->listen_fd, SOL_SOCKET, SO_RCVTIMEO, TIMEVAL_STORE(LISTEN_TIMEOUT_USEC), sizeof(struct timeval)) < 0)
+ return log_error_errno(errno, "Failed to se SO_RCVTIMEO: %m");
+
+ return start_workers(m, /* explicit_request= */ false);
+}
--- /dev/null
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+#pragma once
+
+#include "sd-bus.h"
+#include "sd-event.h"
+
+typedef struct Manager Manager;
+
+#include "hashmap.h"
+#include "ratelimit.h"
+
+#define MOUNTFS_WORKERS_MIN 3
+#define MOUNTFS_WORKERS_MAX 4096
+
+struct Manager {
+ sd_event *event;
+
+ Set *workers_fixed; /* Workers 0…MOUNTFS_WORKERS_MIN */
+ Set *workers_dynamic; /* Workers MOUNTFS_WORKERS_MIN+1…MOUNTFS_WORKERS_MAX */
+
+ int listen_fd;
+
+ RateLimit worker_ratelimit;
+};
+
+int manager_new(Manager **ret);
+Manager* manager_free(Manager *m);
+DEFINE_TRIVIAL_CLEANUP_FUNC(Manager*, manager_free);
+
+int manager_startup(Manager *m);
--- /dev/null
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+
+#include <sys/stat.h>
+#include <sys/types.h>
+
+#include "daemon-util.h"
+#include "log.h"
+#include "main-func.h"
+#include "mountfsd-manager.h"
+#include "signal-util.h"
+
+static int run(int argc, char *argv[]) {
+ _unused_ _cleanup_(notify_on_cleanup) const char *notify_stop = NULL;
+ _cleanup_(manager_freep) Manager *m = NULL;
+ int r;
+
+ log_setup();
+
+ umask(0022);
+
+ if (argc != 1)
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "This program takes no arguments.");
+
+ assert_se(sigprocmask_many(SIG_BLOCK, NULL, SIGCHLD) >= 0);
+
+ r = manager_new(&m);
+ if (r < 0)
+ return log_error_errno(r, "Could not create manager: %m");
+
+ r = manager_startup(m);
+ if (r < 0)
+ return log_error_errno(r, "Failed to start up daemon: %m");
+
+ notify_stop = notify_start(NOTIFY_READY, NOTIFY_STOPPING);
+
+ r = sd_event_loop(m->event);
+ if (r < 0)
+ return log_error_errno(r, "Event loop failed: %m");
+
+ return 0;
+}
+
+DEFINE_MAIN_FUNCTION(run);
--- /dev/null
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+
+#include "sd-daemon.h"
+
+#include "argv-util.h"
+#include "bus-polkit.h"
+#include "chase.h"
+#include "discover-image.h"
+#include "dissect-image.h"
+#include "env-util.h"
+#include "errno-util.h"
+#include "fd-util.h"
+#include "io-util.h"
+#include "main-func.h"
+#include "missing_loop.h"
+#include "namespace-util.h"
+#include "nsresource.h"
+#include "nulstr-util.h"
+#include "os-util.h"
+#include "process-util.h"
+#include "stat-util.h"
+#include "user-util.h"
+#include "varlink.h"
+#include "varlink-io.systemd.MountFileSystem.h"
+
+#define ITERATIONS_MAX 64U
+#define RUNTIME_MAX_USEC (5 * USEC_PER_MINUTE)
+#define PRESSURE_SLEEP_TIME_USEC (50 * USEC_PER_MSEC)
+#define LISTEN_IDLE_USEC (90 * USEC_PER_SEC)
+
+static const ImagePolicy image_policy_untrusted = {
+ .n_policies = 2,
+ .policies = {
+ { PARTITION_ROOT, PARTITION_POLICY_SIGNED|PARTITION_POLICY_ABSENT },
+ { PARTITION_USR, PARTITION_POLICY_SIGNED|PARTITION_POLICY_ABSENT },
+ },
+ .default_flags = PARTITION_POLICY_IGNORE,
+};
+
+static int json_dispatch_image_policy(const char *name, JsonVariant *variant, JsonDispatchFlags flags, void *userdata) {
+ _cleanup_(image_policy_freep) ImagePolicy *q = NULL;
+ ImagePolicy **p = ASSERT_PTR(userdata);
+ int r;
+
+ assert(p);
+
+ if (json_variant_is_null(variant)) {
+ *p = image_policy_free(*p);
+ return 0;
+ }
+
+ if (!json_variant_is_string(variant))
+ return json_log(variant, flags, SYNTHETIC_ERRNO(EINVAL), "JSON field '%s' is not a string.", strna(name));
+
+ r = image_policy_from_string(json_variant_string(variant), &q);
+ if (r < 0)
+ return json_log(variant, flags, r, "JSON field '%s' is not a valid image policy.", strna(name));
+
+ image_policy_free(*p);
+ *p = TAKE_PTR(q);
+ return 0;
+}
+
+typedef struct MountImageParameters {
+ unsigned image_fd_idx;
+ unsigned userns_fd_idx;
+ int read_only;
+ int growfs;
+ char *password;
+ ImagePolicy *image_policy;
+} MountImageParameters;
+
+static void mount_image_parameters_done(MountImageParameters *p) {
+ assert(p);
+
+ p->password = erase_and_free(p->password);
+ p->image_policy = image_policy_free(p->image_policy);
+}
+
+static int validate_image_fd(int fd, MountImageParameters *p) {
+ int r, fl;
+
+ assert(fd >= 0);
+ assert(p);
+
+ r = fd_verify_regular(fd);
+ if (r < 0)
+ return r;
+
+ fl = fd_verify_safe_flags(fd);
+ if (fl < 0)
+ return log_debug_errno(fl, "Image file descriptor has unsafe flags set: %m");
+
+ switch (fl & O_ACCMODE) {
+
+ case O_RDONLY:
+ p->read_only = true;
+ break;
+
+ case O_RDWR:
+ break;
+
+ default:
+ return -EBADF;
+ }
+
+ return 0;
+}
+
+static int verify_trusted_image_fd_by_path(int fd) {
+ _cleanup_free_ char *p = NULL;
+ struct stat sta;
+ int r;
+
+ assert(fd >= 0);
+
+ r = secure_getenv_bool("SYSTEMD_MOUNTFSD_TRUSTED_DIRECTORIES");
+ if (r == -ENXIO) {
+ if (!DEFAULT_MOUNTFSD_TRUSTED_DIRECTORIES) {
+ log_debug("Trusted directory mechanism disabled at compile time.");
+ return false;
+ }
+ } else if (r < 0) {
+ log_debug_errno(r, "Failed to parse $SYSTEMD_MOUNTFSD_TRUSTED_DIRECTORIES environment variable, not trusting any image.");
+ return false;
+ } else if (!r) {
+ log_debug("Trusted directory mechanism disabled via $SYSTEMD_MOUNTFSD_TRUSTED_DIRECTORIES environment variable.");
+ return false;
+ }
+
+ r = fd_get_path(fd, &p);
+ if (r < 0)
+ return log_debug_errno(r, "Failed to get path of passed image file descriptor: %m");
+ if (fstat(fd, &sta) < 0)
+ return log_debug_errno(errno, "Failed to stat() passed image file descriptor: %m");
+
+ log_debug("Checking if image '%s' is in trusted directories.", p);
+
+ for (ImageClass c = 0; c < _IMAGE_CLASS_MAX; c++)
+ NULSTR_FOREACH(s, image_search_path[c]) {
+ _cleanup_close_ int dir_fd = -EBADF, inode_fd = -EBADF;
+ _cleanup_free_ char *q = NULL;
+ struct stat stb;
+ const char *e;
+
+ r = chase(s, NULL, CHASE_SAFE, &q, &dir_fd);
+ if (r == -ENOENT)
+ continue;
+ if (r < 0) {
+ log_warning_errno(r, "Failed to resolve search path '%s', ignoring: %m", s);
+ continue;
+ }
+
+ /* Check that the inode refers to a file immediately inside the image directory,
+ * i.e. not the image directory itself, and nothing further down the tree */
+ e = path_startswith(p, q);
+ if (isempty(e))
+ continue;
+
+ e += strspn(e, "/");
+ if (!filename_is_valid(e))
+ continue;
+
+ r = chaseat(dir_fd, e, CHASE_SAFE, NULL, &inode_fd);
+ if (r < 0)
+ return log_error_errno(r, "Couldn't verify that specified image '%s' is in search path '%s': %m", p, s);
+
+ if (fstat(inode_fd, &stb) < 0)
+ return log_error_errno(errno, "Failed to stat image file '%s/%s': %m", q, e);
+
+ if (stat_inode_same(&sta, &stb)) {
+ log_debug("Image '%s' is *in* trusted directories.", p);
+ return true; /* Yay */
+ }
+ }
+
+ log_debug("Image '%s' is *not* in trusted directories.", p);
+ return false;
+}
+
+static int determine_image_policy(
+ int image_fd,
+ bool trusted,
+ ImagePolicy *client_policy,
+ ImagePolicy **ret) {
+
+ _cleanup_(image_policy_freep) ImagePolicy *envvar_policy = NULL;
+ const ImagePolicy *default_policy;
+ const char *envvar, *e;
+ int r;
+
+ assert(image_fd >= 0);
+ assert(ret);
+
+ if (trusted) {
+ envvar = "SYSTEMD_MOUNTFSD_IMAGE_POLICY_TRUSTED";
+ default_policy = &image_policy_allow;
+ } else {
+ envvar = "SYSTEMD_MOUNTFSD_IMAGE_POLICY_UNTRUSTED";
+ default_policy = &image_policy_untrusted;
+ }
+
+ e = secure_getenv(envvar);
+ if (e) {
+ r = image_policy_from_string(e, &envvar_policy);
+ if (r < 0)
+ return log_error_errno(r, "Failed to parse image policy supplied via $%s: %m", envvar);
+
+ default_policy = envvar_policy;
+ }
+
+ return image_policy_intersect(default_policy, client_policy, ret);
+}
+
+static int validate_userns(Varlink *link, int *userns_fd) {
+ int r;
+
+ assert(link);
+ assert(userns_fd);
+
+ if (*userns_fd < 0)
+ return 0;
+
+ r = fd_verify_safe_flags(*userns_fd);
+ if (r < 0)
+ return log_debug_errno(r, "User namespace file descriptor has unsafe flags set: %m");
+
+ r = fd_is_ns(*userns_fd, CLONE_NEWUSER);
+ if (r < 0)
+ return r;
+ if (r == 0)
+ return varlink_error_invalid_parameter_name(link, "userNamespaceFileDescriptor");
+
+ /* Our own host user namespace? Then close the fd, and handle it as if none was specified. */
+ r = is_our_namespace(*userns_fd, NAMESPACE_USER);
+ if (r < 0)
+ return log_debug_errno(r, "Failed to determine if user namespace provided by client is our own.");
+ if (r > 0) {
+ log_debug("User namespace provided by client is our own.");
+ *userns_fd = safe_close(*userns_fd);
+ }
+
+ return 0;
+}
+
+static int vl_method_mount_image(
+ Varlink *link,
+ JsonVariant *parameters,
+ VarlinkMethodFlags flags,
+ void *userdata) {
+
+ static const JsonDispatch dispatch_table[] = {
+ { "imageFileDescriptor", JSON_VARIANT_UNSIGNED, json_dispatch_uint, offsetof(MountImageParameters, image_fd_idx), JSON_MANDATORY },
+ { "userNamespaceFileDescriptor", JSON_VARIANT_UNSIGNED, json_dispatch_uint, offsetof(MountImageParameters, userns_fd_idx), 0 },
+ { "readOnly", JSON_VARIANT_BOOLEAN, json_dispatch_tristate, offsetof(MountImageParameters, read_only), 0 },
+ { "growFileSystems", JSON_VARIANT_BOOLEAN, json_dispatch_tristate, offsetof(MountImageParameters, growfs), 0 },
+ { "password", JSON_VARIANT_STRING, json_dispatch_string, offsetof(MountImageParameters, password), 0 },
+ { "imagePolicy", JSON_VARIANT_STRING, json_dispatch_image_policy, offsetof(MountImageParameters, image_policy), 0 },
+ VARLINK_DISPATCH_POLKIT_FIELD,
+ {}
+ };
+
+ _cleanup_(verity_settings_done) VeritySettings verity = VERITY_SETTINGS_DEFAULT;
+ _cleanup_(mount_image_parameters_done) MountImageParameters p = {
+ .image_fd_idx = UINT_MAX,
+ .userns_fd_idx = UINT_MAX,
+ .read_only = -1,
+ .growfs = -1,
+ };
+ _cleanup_(dissected_image_unrefp) DissectedImage *di = NULL;
+ _cleanup_(loop_device_unrefp) LoopDevice *loop = NULL;
+ _cleanup_(json_variant_unrefp) JsonVariant *aj = NULL;
+ _cleanup_close_ int image_fd = -EBADF, userns_fd = -EBADF;
+ _cleanup_(image_policy_freep) ImagePolicy *use_policy = NULL;
+ Hashmap **polkit_registry = ASSERT_PTR(userdata);
+ _cleanup_free_ char *ps = NULL;
+ bool image_is_trusted = false;
+ uid_t peer_uid;
+ int r;
+
+ assert(link);
+ assert(parameters);
+
+ json_variant_sensitive(parameters); /* might contain passwords */
+
+ r = varlink_get_peer_uid(link, &peer_uid);
+ if (r < 0)
+ return log_debug_errno(r, "Failed to get client UID: %m");
+
+ r = varlink_dispatch(link, parameters, dispatch_table, &p);
+ if (r != 0)
+ return r;
+
+ if (p.image_fd_idx != UINT_MAX) {
+ image_fd = varlink_peek_dup_fd(link, p.image_fd_idx);
+ if (image_fd < 0)
+ return log_debug_errno(image_fd, "Failed to peek image fd from client: %m");
+ }
+
+ if (p.userns_fd_idx != UINT_MAX) {
+ userns_fd = varlink_peek_dup_fd(link, p.userns_fd_idx);
+ if (userns_fd < 0)
+ return log_debug_errno(userns_fd, "Failed to peek user namespace fd from client: %m");
+ }
+
+ r = validate_image_fd(image_fd, &p);
+ if (r < 0)
+ return r;
+
+ r = validate_userns(link, &userns_fd);
+ if (r != 0)
+ return r;
+
+ r = verify_trusted_image_fd_by_path(image_fd);
+ if (r < 0)
+ return r;
+ image_is_trusted = r;
+
+ const char *polkit_details[] = {
+ "read_only", one_zero(p.read_only > 0),
+ NULL,
+ };
+
+ const char *polkit_action, *polkit_untrusted_action;
+ PolkitFlags polkit_flags;
+ if (userns_fd < 0) {
+ /* Mount into the host user namespace */
+ polkit_action = "io.systemd.mount-file-system.mount-image";
+ polkit_untrusted_action = "io.systemd.mount-file-system.mount-untrusted-image";
+ polkit_flags = 0;
+ } else {
+ /* Mount into a private user namespace */
+ polkit_action = "io.systemd.mount-file-system.mount-image-privately";
+ polkit_untrusted_action = "io.systemd.mount-file-system.mount-untrusted-image-privately";
+
+ /* If polkit is not around, let's allow mounting authenticated images by default */
+ polkit_flags = POLKIT_DEFAULT_ALLOW;
+ }
+
+ /* Let's definitely acquire the regular action privilege, for mounting properly signed images */
+ r = varlink_verify_polkit_async_full(
+ link,
+ /* bus= */ NULL,
+ polkit_action,
+ polkit_details,
+ /* good_user= */ UID_INVALID,
+ polkit_flags,
+ polkit_registry);
+ if (r <= 0)
+ return r;
+
+ /* Generate the common dissection directory here. We are not going to use it, but the clients might,
+ * and they likely are unprivileged, hence cannot create it themselves. Hence let's just create it
+ * here, if it is missing. */
+ r = get_common_dissect_directory(NULL);
+ if (r < 0)
+ return r;
+
+ r = loop_device_make(
+ image_fd,
+ p.read_only == 0 ? O_RDONLY : O_RDWR,
+ 0,
+ UINT64_MAX,
+ UINT32_MAX,
+ LO_FLAGS_PARTSCAN,
+ LOCK_EX,
+ &loop);
+ if (r < 0)
+ return r;
+
+ DissectImageFlags dissect_flags =
+ (p.read_only == 0 ? DISSECT_IMAGE_READ_ONLY : 0) |
+ (p.growfs != 0 ? DISSECT_IMAGE_GROWFS : 0) |
+ DISSECT_IMAGE_DISCARD_ANY |
+ DISSECT_IMAGE_FSCK |
+ DISSECT_IMAGE_ADD_PARTITION_DEVICES |
+ DISSECT_IMAGE_PIN_PARTITION_DEVICES |
+ DISSECT_IMAGE_ALLOW_USERSPACE_VERITY;
+
+ /* Let's see if we have acquired the privilege to mount untrusted images already */
+ bool polkit_have_untrusted_action =
+ varlink_has_polkit_action(link, polkit_untrusted_action, polkit_details, polkit_registry);
+
+ for (;;) {
+ use_policy = image_policy_free(use_policy);
+ ps = mfree(ps);
+
+ /* We use the image policy for trusted images if either the path is below a trusted
+ * directory, or if we have already acquired a PK authentication that tells us that untrusted
+ * images are OK */
+ bool use_trusted_policy =
+ image_is_trusted ||
+ polkit_have_untrusted_action;
+
+ r = determine_image_policy(
+ image_fd,
+ use_trusted_policy,
+ p.image_policy,
+ &use_policy);
+ if (r < 0)
+ return r;
+
+ r = image_policy_to_string(use_policy, /* simplify= */ true, &ps);
+ if (r < 0)
+ return r;
+
+ log_debug("Using image policy: %s", ps);
+
+ r = dissect_loop_device(
+ loop,
+ &verity,
+ /* mount_options= */ NULL,
+ use_policy,
+ dissect_flags,
+ &di);
+ if (r == -ENOPKG)
+ return varlink_error(link, "io.systemd.MountFileSystem.IncompatibleImage", NULL);
+ if (r == -ENOTUNIQ)
+ return varlink_error(link, "io.systemd.MountFileSystem.MultipleRootPartitionsFound", NULL);
+ if (r == -ENXIO)
+ return varlink_error(link, "io.systemd.MountFileSystem.RootPartitionNotFound", NULL);
+ if (r == -ERFKILL) {
+ /* The image policy refused this, let's retry after trying to get PolicyKit */
+
+ if (!polkit_have_untrusted_action) {
+ log_debug("Denied by image policy. Trying a stronger polkit authentication before continuing.");
+ r = varlink_verify_polkit_async_full(
+ link,
+ /* bus= */ NULL,
+ polkit_untrusted_action,
+ polkit_details,
+ /* good_user= */ UID_INVALID,
+ /* flags= */ 0, /* NB: the image cannot be authenticated, hence unless PK is around to allow this anyway, fail! */
+ polkit_registry);
+ if (r <= 0 && !ERRNO_IS_NEG_PRIVILEGE(r))
+ return r;
+ if (r > 0) {
+ /* Try again, now that we know the client has enough privileges. */
+ log_debug("Denied by image policy, retrying after polkit authentication.");
+ polkit_have_untrusted_action = true;
+ continue;
+ }
+ }
+
+ return varlink_error(link, "io.systemd.MountFileSystem.DeniedByImagePolicy", NULL);
+ }
+ if (r < 0)
+ return r;
+
+ /* Success */
+ break;
+ }
+
+ r = dissected_image_load_verity_sig_partition(
+ di,
+ loop->fd,
+ &verity);
+ if (r < 0)
+ return r;
+
+ r = dissected_image_decrypt(
+ di,
+ p.password,
+ &verity,
+ dissect_flags);
+ if (r == -ENOKEY) /* new dm-verity userspace returns ENOKEY if the dm-verity signature key is not in
+ * key chain. That's great. */
+ return varlink_error(link, "io.systemd.MountFileSystem.KeyNotFound", NULL);
+ if (r == -EBUSY) /* DM kernel subsystem is shit with returning useful errors hence we keep retrying
+ * under the assumption that some errors are transitional. Which the errors might
+ * not actually be. After all retries failed we return EBUSY. Let's turn that into a
+ * generic Verity error. It's not very helpful, could mean anything, but at least it
+ * gives client a clear idea that this has to do with Verity. */
+ return varlink_error(link, "io.systemd.MountFileSystem.VerityFailure", NULL);
+ if (r < 0)
+ return r;
+
+ r = dissected_image_mount(
+ di,
+ /* where= */ NULL,
+ /* uid_shift= */ UID_INVALID,
+ /* uid_range= */ UID_INVALID,
+ userns_fd,
+ dissect_flags);
+ if (r < 0)
+ return r;
+
+ for (PartitionDesignator d = 0; d < _PARTITION_DESIGNATOR_MAX; d++) {
+ _cleanup_(json_variant_unrefp) JsonVariant *pj = NULL;
+ DissectedPartition *pp = di->partitions + d;
+ int fd_idx;
+
+ if (!pp->found)
+ continue;
+
+ if (pp->fsmount_fd < 0)
+ continue;
+
+ if (userns_fd >= 0) {
+ r = nsresource_add_mount(userns_fd, pp->fsmount_fd);
+ if (r < 0)
+ return r;
+ }
+
+ fd_idx = varlink_push_fd(link, pp->fsmount_fd);
+ if (fd_idx < 0)
+ return fd_idx;
+
+ TAKE_FD(pp->fsmount_fd);
+
+ r = json_build(&pj,
+ JSON_BUILD_OBJECT(
+ JSON_BUILD_PAIR("designator", JSON_BUILD_STRING(partition_designator_to_string(d))),
+ JSON_BUILD_PAIR("writable", JSON_BUILD_BOOLEAN(pp->rw)),
+ JSON_BUILD_PAIR("growFileSystem", JSON_BUILD_BOOLEAN(pp->growfs)),
+ JSON_BUILD_PAIR_CONDITION(pp->partno > 0, "partitionNumber", JSON_BUILD_INTEGER(pp->partno)),
+ JSON_BUILD_PAIR_CONDITION(pp->architecture > 0, "architecture", JSON_BUILD_STRING(architecture_to_string(pp->architecture))),
+ JSON_BUILD_PAIR_CONDITION(!sd_id128_is_null(pp->uuid), "partitionUuid", JSON_BUILD_UUID(pp->uuid)),
+ JSON_BUILD_PAIR("fileSystemType", JSON_BUILD_STRING(dissected_partition_fstype(pp))),
+ JSON_BUILD_PAIR_CONDITION(pp->label, "partitionLabel", JSON_BUILD_STRING(pp->label)),
+ JSON_BUILD_PAIR("size", JSON_BUILD_INTEGER(pp->size)),
+ JSON_BUILD_PAIR("offset", JSON_BUILD_INTEGER(pp->offset)),
+ JSON_BUILD_PAIR("mountFileDescriptor", JSON_BUILD_INTEGER(fd_idx))));
+ if (r < 0)
+ return r;
+
+ r = json_variant_append_array(&aj, pj);
+ if (r < 0)
+ return r;
+ }
+
+ loop_device_relinquish(loop);
+
+ r = varlink_replyb(link, JSON_BUILD_OBJECT(
+ JSON_BUILD_PAIR("partitions", JSON_BUILD_VARIANT(aj)),
+ JSON_BUILD_PAIR("imagePolicy", JSON_BUILD_STRING(ps)),
+ JSON_BUILD_PAIR("imageSize", JSON_BUILD_INTEGER(di->image_size)),
+ JSON_BUILD_PAIR("sectorSize", JSON_BUILD_INTEGER(di->sector_size)),
+ JSON_BUILD_PAIR_CONDITION(!sd_id128_is_null(di->image_uuid), "imageUuid", JSON_BUILD_UUID(di->image_uuid))));
+ if (r < 0)
+ return r;
+
+ return r;
+}
+
+static int process_connection(VarlinkServer *server, int _fd) {
+ _cleanup_close_ int fd = TAKE_FD(_fd); /* always take possession */
+ _cleanup_(varlink_close_unrefp) Varlink *vl = NULL;
+ _cleanup_(sd_event_unrefp) sd_event *event = NULL;
+ int r;
+
+ r = sd_event_new(&event);
+ if (r < 0)
+ return r;
+
+ r = varlink_server_attach_event(server, event, 0);
+ if (r < 0)
+ return log_error_errno(r, "Failed to attach Varlink server to event loop: %m");
+
+ r = varlink_server_add_connection(server, fd, &vl);
+ if (r < 0)
+ return log_error_errno(r, "Failed to add connection: %m");
+
+ TAKE_FD(fd);
+ vl = varlink_ref(vl);
+
+ r = varlink_set_allow_fd_passing_input(vl, true);
+ if (r < 0)
+ return log_error_errno(r, "Failed to enable fd passing for read: %m");
+
+ r = varlink_set_allow_fd_passing_output(vl, true);
+ if (r < 0)
+ return log_error_errno(r, "Failed to enable fd passing for write: %m");
+
+ r = sd_event_loop(event);
+ if (r < 0)
+ return log_error_errno(r, "Failed to run event loop: %m");
+
+ r = varlink_server_detach_event(server);
+ if (r < 0)
+ return log_error_errno(r, "Failed to detach Varlink server from event loop: %m");
+
+ return 0;
+}
+
+static int run(int argc, char *argv[]) {
+ usec_t start_time, listen_idle_usec, last_busy_usec = USEC_INFINITY;
+ _cleanup_(varlink_server_unrefp) VarlinkServer *server = NULL;
+ _cleanup_(hashmap_freep) Hashmap *polkit_registry = NULL;
+ _cleanup_(pidref_done) PidRef parent = PIDREF_NULL;
+ unsigned n_iterations = 0;
+ int m, listen_fd, r;
+
+ log_setup();
+
+ m = sd_listen_fds(false);
+ if (m < 0)
+ return log_error_errno(m, "Failed to determine number of listening fds: %m");
+ if (m == 0)
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "No socket to listen on received.");
+ if (m > 1)
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Worker can only listen on a single socket at a time.");
+
+ listen_fd = SD_LISTEN_FDS_START;
+
+ r = fd_nonblock(listen_fd, false);
+ if (r < 0)
+ return log_error_errno(r, "Failed to turn off non-blocking mode for listening socket: %m");
+
+ r = varlink_server_new(&server, VARLINK_SERVER_INHERIT_USERDATA);
+ if (r < 0)
+ return log_error_errno(r, "Failed to allocate server: %m");
+
+ r = varlink_server_add_interface(server, &vl_interface_io_systemd_MountFileSystem);
+ if (r < 0)
+ return log_error_errno(r, "Failed to add MountFileSystem interface to varlink server: %m");
+
+ r = varlink_server_bind_method_many(
+ server,
+ "io.systemd.MountFileSystem.MountImage", vl_method_mount_image);
+ if (r < 0)
+ return log_error_errno(r, "Failed to bind methods: %m");
+
+ varlink_server_set_userdata(server, &polkit_registry);
+
+ r = varlink_server_set_exit_on_idle(server, true);
+ if (r < 0)
+ return log_error_errno(r, "Failed to enable exit-on-idle mode: %m");
+
+ r = getenv_bool("MOUNTFS_FIXED_WORKER");
+ if (r < 0)
+ return log_error_errno(r, "Failed to parse MOUNTFSD_FIXED_WORKER: %m");
+ listen_idle_usec = r ? USEC_INFINITY : LISTEN_IDLE_USEC;
+
+ r = pidref_set_parent(&parent);
+ if (r < 0)
+ return log_error_errno(r, "Failed to acquire pidfd of parent process: %m");
+
+ start_time = now(CLOCK_MONOTONIC);
+
+ for (;;) {
+ _cleanup_close_ int fd = -EBADF;
+ usec_t n;
+
+ /* Exit the worker in regular intervals, to flush out all memory use */
+ if (n_iterations++ > ITERATIONS_MAX) {
+ log_debug("Exiting worker, processed %u iterations, that's enough.", n_iterations);
+ break;
+ }
+
+ n = now(CLOCK_MONOTONIC);
+ if (n >= usec_add(start_time, RUNTIME_MAX_USEC)) {
+ log_debug("Exiting worker, ran for %s, that's enough.",
+ FORMAT_TIMESPAN(usec_sub_unsigned(n, start_time), 0));
+ break;
+ }
+
+ if (last_busy_usec == USEC_INFINITY)
+ last_busy_usec = n;
+ else if (listen_idle_usec != USEC_INFINITY && n >= usec_add(last_busy_usec, listen_idle_usec)) {
+ log_debug("Exiting worker, been idle for %s.",
+ FORMAT_TIMESPAN(usec_sub_unsigned(n, last_busy_usec), 0));
+ break;
+ }
+
+ (void) rename_process("systemd-mountwork: waiting...");
+ fd = RET_NERRNO(accept4(listen_fd, NULL, NULL, SOCK_NONBLOCK|SOCK_CLOEXEC));
+ (void) rename_process("systemd-mountwork: processing...");
+
+ if (fd == -EAGAIN)
+ continue; /* The listening socket has SO_RECVTIMEO set, hence a timeout is expected
+ * after a while, let's check if it's time to exit though. */
+ if (fd == -EINTR)
+ continue; /* Might be that somebody attached via strace, let's just continue in that
+ * case */
+ if (fd < 0)
+ return log_error_errno(fd, "Failed to accept() from listening socket: %m");
+
+ if (now(CLOCK_MONOTONIC) <= usec_add(n, PRESSURE_SLEEP_TIME_USEC)) {
+ /* We only slept a very short time? If so, let's see if there are more sockets
+ * pending, and if so, let's ask our parent for more workers */
+
+ r = fd_wait_for_event(listen_fd, POLLIN, 0);
+ if (r < 0)
+ return log_error_errno(r, "Failed to test for POLLIN on listening socket: %m");
+
+ if (FLAGS_SET(r, POLLIN)) {
+ r = pidref_kill(&parent, SIGUSR2);
+ if (r == -ESRCH)
+ return log_error_errno(r, "Parent already died?");
+ if (r < 0)
+ return log_error_errno(r, "Failed to send SIGUSR2 signal to parent. %m");
+ }
+ }
+
+ (void) process_connection(server, TAKE_FD(fd));
+ last_busy_usec = USEC_INFINITY;
+ }
+
+ return 0;
+}
+
+DEFINE_MAIN_FUNCTION(run);
#include <getopt.h>
#include "build.h"
-#include "copy.h"
#include "creds-util.h"
#include "fd-util.h"
#include "fs-util.h"
#include "network-generator.h"
#include "path-util.h"
#include "proc-cmdline.h"
-#include "recurse-dir.h"
-#define NETWORKD_UNIT_DIRECTORY "/run/systemd/network"
+#define NETWORK_UNIT_DIRECTORY "/run/systemd/network/"
static const char *arg_root = NULL;
r = conservative_rename(temp_path, p);
if (r < 0)
- return r;
+ return log_error_errno(r, "Failed to rename '%s' to '%s': %m", temp_path, p);
temp_path = mfree(temp_path);
return 0;
r = conservative_rename(temp_path, p);
if (r < 0)
- return r;
+ return log_error_errno(r, "Failed to rename '%s' to '%s': %m", temp_path, p);
temp_path = mfree(temp_path);
return 0;
r = conservative_rename(temp_path, p);
if (r < 0)
- return r;
+ return log_error_errno(r, "Failed to rename '%s' to '%s': %m", temp_path, p);
temp_path = mfree(temp_path);
return 0;
Link *link;
int r;
- const char *p = prefix_roota(arg_root, NETWORKD_UNIT_DIRECTORY);
+ const char *p = prefix_roota(arg_root, NETWORK_UNIT_DIRECTORY);
r = mkdir_p(p, 0755);
if (r < 0)
- return log_error_errno(r, "Failed to create directory " NETWORKD_UNIT_DIRECTORY ": %m");
+ return log_error_errno(r, "Failed to create directory " NETWORK_UNIT_DIRECTORY ": %m");
HASHMAP_FOREACH(network, context->networks_by_name)
RET_GATHER(r, network_save(network, p));
return r;
}
-static int pick_up_credentials(void) {
- _cleanup_close_ int credential_dir_fd = -EBADF;
- int r, ret = 0;
-
- credential_dir_fd = open_credentials_dir();
- if (IN_SET(credential_dir_fd, -ENXIO, -ENOENT)) /* Credential env var not set, or dir doesn't exist. */
- return 0;
- if (credential_dir_fd < 0)
- return log_error_errno(credential_dir_fd, "Failed to open credentials directory: %m");
-
- _cleanup_free_ DirectoryEntries *des = NULL;
- r = readdir_all(credential_dir_fd, RECURSE_DIR_SORT|RECURSE_DIR_IGNORE_DOT|RECURSE_DIR_ENSURE_TYPE, &des);
- if (r < 0)
- return log_error_errno(r, "Failed to enumerate credentials: %m");
-
- FOREACH_ARRAY(i, des->entries, des->n_entries) {
- static const struct {
- const char *credential_prefix;
- const char *filename_suffix;
- } table[] = {
- { "network.link.", ".link" },
- { "network.netdev.", ".netdev" },
- { "network.network.", ".network" },
- };
-
- _cleanup_free_ char *fn = NULL;
- struct dirent *de = *i;
-
- if (de->d_type != DT_REG)
- continue;
-
- FOREACH_ARRAY(t, table, ELEMENTSOF(table)) {
- const char *e = startswith(de->d_name, t->credential_prefix);
-
- if (e) {
- fn = strjoin(e, t->filename_suffix);
- if (!fn)
- return log_oom();
-
- break;
- }
- }
-
- if (!fn)
- continue;
-
- if (!filename_is_valid(fn)) {
- log_warning("Passed credential '%s' would result in invalid filename '%s', ignoring.", de->d_name, fn);
- continue;
- }
-
- _cleanup_free_ char *output = path_join(NETWORKD_UNIT_DIRECTORY, fn);
- if (!output)
- return log_oom();
-
- r = copy_file_at(
- credential_dir_fd, de->d_name,
- AT_FDCWD, output,
- /* open_flags= */ 0,
- 0644,
- /* flags= */ 0);
- if (r < 0)
- RET_GATHER(ret, log_warning_errno(r, "Failed to copy credential %s → file %s: %m", de->d_name, output));
- else
- log_info("Installed %s from credential.", output);
- }
-
- return ret;
-}
-
static int help(void) {
printf("%s [OPTIONS...] [-- KERNEL_CMDLINE]\n"
" -h --help Show this help\n"
return log_warning_errno(r, "Failed to merge multiple command line options: %m");
RET_GATHER(ret, context_save(&context));
- RET_GATHER(ret, pick_up_credentials());
+
+ static const PickUpCredential table[] = {
+ { "network.conf.", "/run/systemd/networkd.conf.d/", ".conf" },
+ { "network.link.", NETWORK_UNIT_DIRECTORY, ".link" },
+ { "network.netdev.", NETWORK_UNIT_DIRECTORY, ".netdev" },
+ { "network.network.", NETWORK_UNIT_DIRECTORY, ".network" },
+ };
+ RET_GATHER(ret, pick_up_credentials(table, ELEMENTSOF(table)));
return ret;
}
t = dracut_dhcp_type_from_string(dhcp_type);
if (t < 0)
- return t;
+ return log_debug_errno(t, "Invalid DHCP type '%s'", dhcp_type);
network = network_get(context, ifname);
if (!network) {
r = network_new(context, ifname, &network);
if (r < 0)
- return r;
+ return log_debug_errno(r, "Failed to create network for '%s': %m", ifname);
}
network->dhcp_type = t;
network = network_get(context, ifname);
if (!network)
- return -ENODEV;
+ return log_debug_errno(SYNTHETIC_ERRNO(ENODEV), "No network found for '%s'", ifname);
return free_and_strdup(&network->hostname, hostname);
}
static int network_set_mtu(Context *context, const char *ifname, const char *mtu) {
Network *network;
+ int r;
assert(context);
assert(ifname);
network = network_get(context, ifname);
if (!network)
- return -ENODEV;
+ return log_debug_errno(SYNTHETIC_ERRNO(ENODEV), "No network found for '%s'", ifname);
- return parse_mtu(AF_UNSPEC, mtu, &network->mtu);
+ r = parse_mtu(AF_UNSPEC, mtu, &network->mtu);
+ if (r < 0)
+ return log_debug_errno(r, "Invalid MTU '%s' for '%s': %m", mtu, ifname);
+
+ return r;
}
static int network_set_mac_address(Context *context, const char *ifname, const char *mac) {
Network *network;
+ int r;
assert(context);
assert(ifname);
network = network_get(context, ifname);
if (!network)
- return -ENODEV;
+ return log_debug_errno(SYNTHETIC_ERRNO(ENODEV), "No network found for '%s'", ifname);
- return parse_ether_addr(mac, &network->mac);
+ r = parse_ether_addr(mac, &network->mac);
+ if (r < 0)
+ return log_debug_errno(r, "Invalid MAC address '%s' for '%s'", mac, ifname);
+
+ return r;
}
static int network_set_address(Context *context, const char *ifname, int family, unsigned char prefixlen,
network = network_get(context, ifname);
if (!network)
- return -ENODEV;
+ return log_debug_errno(SYNTHETIC_ERRNO(ENODEV), "No network found for '%s'", ifname);
return address_new(network, family, prefixlen, addr, peer, NULL);
}
if (!network) {
r = network_new(context, ifname, &network);
if (r < 0)
- return r;
+ return log_debug_errno(r, "Failed to create network for '%s': %m", ifname);
}
return route_new(network, family, prefixlen, dest, gateway, NULL);
else
r = in_addr_from_string(family, dns, &a);
if (r < 0)
- return r;
+ return log_debug_errno(r, "Invalid DNS address '%s' for '%s'", dns, ifname);
network = network_get(context, ifname);
if (!network) {
r = network_new(context, ifname, &network);
if (r < 0)
- return r;
+ return log_debug_errno(r, "Failed to create network for '%s': %m", ifname);
}
return strv_extend(&network->dns, dns);
if (!network) {
r = network_new(context, ifname, &network);
if (r < 0)
- return r;
+ return log_debug_errno(r, "Failed to create network for '%s': %m", ifname);
}
network->dhcp_use_dns = value;
if (!network) {
r = network_new(context, ifname, &network);
if (r < 0)
- return r;
+ return log_debug_errno(r, "Failed to create network for '%s': %m", ifname);
}
return free_and_strdup(&network->vlan, value);
if (!network) {
r = network_new(context, ifname, &network);
if (r < 0)
- return r;
+ return log_debug_errno(r, "Failed to create network for '%s': %m", ifname);
}
return free_and_strdup(&network->bridge, value);
if (!network) {
r = network_new(context, ifname, &network);
if (r < 0)
- return r;
+ return log_debug_errno(r, "Failed to create network for '%s': %m", ifname);
}
return free_and_strdup(&network->bond, value);
if (family == AF_INET6) {
if (p[0] != '[')
- return -EINVAL;
+ return log_debug_errno(SYNTHETIC_ERRNO(EINVAL), "Invalid IPv6 address '%s'", p);
q = strchr(p + 1, ']');
if (!q)
- return -EINVAL;
+ return log_debug_errno(SYNTHETIC_ERRNO(EINVAL), "Invalid IPv6 address '%s'", p);
if (q[1] != ':')
- return -EINVAL;
+ return log_debug_errno(SYNTHETIC_ERRNO(EINVAL), "Invalid IPv6 address '%s'", p);
buf = strndupa_safe(p + 1, q - p - 1);
p = q + 2;
} else {
q = strchr(p, ':');
if (!q)
- return -EINVAL;
+ log_debug_errno(SYNTHETIC_ERRNO(EINVAL), "Invalid IPv4 address '%s'", p);
buf = strndupa_safe(p, q - p);
p = q + 1;
r = in_addr_from_string(family, buf, ret);
if (r < 0)
- return r;
+ return log_debug_errno(r, "Invalid IP address '%s': %m", buf);
*value = p;
return 1;
if (r > 0) {
if (family == AF_INET6)
/* TODO: Not supported yet. */
- return -EINVAL;
+ return log_debug_errno(SYNTHETIC_ERRNO(EOPNOTSUPP), "IPv6 prefix length is not supported yet");
*ret = in4_addr_netmask_to_prefixlen(&netmask.in);
} else if (r == 0)
else {
p = strchr(*value, ':');
if (!p)
- return -EINVAL;
+ return log_debug_errno(SYNTHETIC_ERRNO(EINVAL), "Invalid netmask or prefix length '%s'", *value);
q = strndupa_safe(*value, p - *value);
r = safe_atou8(q, ret);
if (r < 0)
- return r;
+ return log_debug_errno(r, "Invalid netmask or prefix length '%s': %m", q);
*value = p + 1;
}
if (p[0] == '[') {
q = strchr(p + 1, ']');
- if (!q)
- return -EINVAL;
- if (!IN_SET(q[1], ':', '\0'))
- return -EINVAL;
+ if (!q || !IN_SET(q[1], ':', '\0'))
+ return log_debug_errno(SYNTHETIC_ERRNO(EINVAL), "Invalid IP DNS address '%s'", p);
buf = strndupa_safe(p + 1, q - p - 1);
p = q + 1;
/* hostname */
p = strchr(value, ':');
if (!p)
- return -EINVAL;
+ return log_debug_errno(SYNTHETIC_ERRNO(EINVAL), "Invalid IP address '%s'", value);
if (p != value) {
hostname = strndupa_safe(value, p - value);
if (!hostname_is_valid(hostname, 0))
- return -EINVAL;
+ return log_debug_errno(SYNTHETIC_ERRNO(EINVAL), "Invalid hostname '%s'", hostname);
}
value = p + 1;
/* ifname */
p = strchr(value, ':');
if (!p)
- return -EINVAL;
+ return log_debug_errno(SYNTHETIC_ERRNO(EINVAL), "Invalid IP address '%s'", value);
ifname = strndupa_safe(value, p - value);
/* refuse unexpected trailing strings */
if (!isempty(value))
- return -EINVAL;
+ return log_debug_errno(SYNTHETIC_ERRNO(EINVAL), "Invalid IP address '%s'", value);
return 0;
}
p = strchr(value, ':');
if (!p)
- return -EINVAL;
+ return log_debug_errno(SYNTHETIC_ERRNO(EINVAL), "Invalid IP address '%s'", value);
ifname = strndupa_safe(value, p - value);
assert(key);
if (proc_cmdline_value_missing(key, value))
- return -EINVAL;
+ return log_debug_errno(SYNTHETIC_ERRNO(EINVAL), "Missing value for '%s'", key);
p = strchr(value, ':');
if (!p)
/* rd.route=<net>/<netmask>:<gateway>[:<interface>] */
if (proc_cmdline_value_missing(key, value))
- return -EINVAL;
+ return log_debug_errno(SYNTHETIC_ERRNO(EINVAL), "Missing value for '%s'", key);
if (value[0] == '[') {
p = strchr(value, ']');
if (!p)
- return -EINVAL;
+ return log_debug_errno(SYNTHETIC_ERRNO(EINVAL), "Invalid IPv6 address '%s'", value);
if (p[1] != ':')
- return -EINVAL;
+ return log_debug_errno(SYNTHETIC_ERRNO(EINVAL), "Invalid IPv6 address '%s'", value);
buf = strndupa_safe(value + 1, p - value - 1);
value = p + 2;
} else {
p = strchr(value, ':');
if (!p)
- return -EINVAL;
+ return log_debug_errno(SYNTHETIC_ERRNO(EINVAL), "Invalid IPv4 address '%s'", value);
buf = strndupa_safe(value, p - value);
value = p + 1;
r = in_addr_prefix_from_string(buf, family, &addr, &prefixlen);
if (r < 0)
- return r;
+ return log_debug_errno(r, "Invalid IP address '%s': %m", buf);
p = strchr(value, ':');
if (!p)
assert(key);
if (proc_cmdline_value_missing(key, value))
- return -EINVAL;
+ return log_debug_errno(SYNTHETIC_ERRNO(EINVAL), "Missing value for '%s'", key);
return network_set_dns(context, "", AF_UNSPEC, value);
}
r = parse_boolean(value);
if (r < 0)
- return r;
+ return log_debug_errno(r, "Invalid boolean value '%s'", value);
return network_set_dhcp_use_dns(context, "", r);
}
assert(key);
if (proc_cmdline_value_missing(key, value))
- return -EINVAL;
+ return log_debug_errno(SYNTHETIC_ERRNO(EINVAL), "Missing value for '%s'", key);
p = strchr(value, ':');
if (!p)
- return -EINVAL;
+ return log_debug_errno(SYNTHETIC_ERRNO(EINVAL), "Invalid VLAN value '%s'", value);
name = strndupa_safe(value, p - value);
if (!netdev) {
r = netdev_new(context, "vlan", name, &netdev);
if (r < 0)
- return r;
+ return log_debug_errno(r, "Failed to create VLAN device for '%s': %m", name);
}
return network_set_vlan(context, p + 1, name);
assert(key);
if (proc_cmdline_value_missing(key, value))
- return -EINVAL;
+ return log_debug_errno(SYNTHETIC_ERRNO(EINVAL), "Missing value for '%s'", key);
p = strchr(value, ':');
if (!p)
- return -EINVAL;
+ return log_debug_errno(SYNTHETIC_ERRNO(EINVAL), "Invalid bridge value '%s'", value);
name = strndupa_safe(value, p - value);
if (!netdev) {
r = netdev_new(context, "bridge", name, &netdev);
if (r < 0)
- return r;
+ return log_debug_errno(r, "Failed to create bridge device for '%s': %m", name);
}
p++;
if (isempty(p))
- return -EINVAL;
+ return log_debug_errno(SYNTHETIC_ERRNO(EINVAL), "Missing slave interfaces for bridge '%s'", name);
for (;;) {
_cleanup_free_ char *word = NULL;
r = extract_first_word(&p, &word, ",", 0);
- if (r <= 0)
- return r;
+ if (r < 0)
+ return log_debug_errno(r, "Failed to parse slave interfaces for bridge '%s'", name);
+ if (r == 0)
+ return 0;
r = network_set_bridge(context, word, name);
if (r < 0)
assert(key);
if (proc_cmdline_value_missing(key, value))
- return -EINVAL;
+ return log_debug_errno(SYNTHETIC_ERRNO(EINVAL), "Missing value for '%s'", key);
p = strchr(value, ':');
if (!p)
- return -EINVAL;
+ return log_debug_errno(SYNTHETIC_ERRNO(EINVAL), "Invalid bond value '%s'", value);
name = strndupa_safe(value, p - value);
if (!netdev) {
r = netdev_new(context, "bond", name, &netdev);
if (r < 0)
- return r;
+ return log_debug_errno(r, "Failed to create bond device for '%s': %m", name);
}
value = p + 1;
slaves = strndupa_safe(value, p - value);
if (isempty(slaves))
- return -EINVAL;
+ return log_debug_errno(SYNTHETIC_ERRNO(EINVAL), "Missing slave interfaces for bond '%s'", name);
for (const char *q = slaves; ; ) {
_cleanup_free_ char *word = NULL;
if (r == 0)
break;
if (r < 0)
- return r;
+ return log_debug_errno(r, "Failed to parse slave interfaces for bond '%s'", name);
r = network_set_bond(context, word, name);
if (r < 0)
/* ifname=<interface>:<MAC> */
if (proc_cmdline_value_missing(key, value))
- return -EINVAL;
+ return log_debug_errno(SYNTHETIC_ERRNO(EINVAL), "Missing value for '%s'", key);
p = strchr(value, ':');
if (!p)
- return -EINVAL;
+ return log_debug_errno(SYNTHETIC_ERRNO(EINVAL), "Invalid ifname value '%s'", value);
name = strndupa_safe(value, p - value);
r = parse_hw_addr(p + 1, &mac);
if (r < 0)
- return r;
+ return log_debug_errno(r, "Invalid MAC address '%s' for '%s'", p + 1, name);
- return link_new(context, name, &mac, NULL);
+ r = link_new(context, name, &mac, NULL);
+ if (r < 0)
+ return log_debug_errno(r, "Failed to create link for '%s': %m", name);
+
+ return 0;
}
static int parse_cmdline_ifname_policy(Context *context, const char *key, const char *value) {
/* net.ifname_policy=policy1[,policy2,...][,<MAC>] */
if (proc_cmdline_value_missing(key, value))
- return -EINVAL;
+ return log_debug_errno(SYNTHETIC_ERRNO(EINVAL), "Missing value for '%s'", key);
for (const char *q = value; ; ) {
_cleanup_free_ char *word = NULL;
if (r == 0)
break;
if (r < 0)
- return r;
+ return log_debug_errno(r, "Failed to parse ifname policy '%s'", value);
p = name_policy_from_string(word);
if (p < 0) {
r = parse_hw_addr(word, &mac);
if (r < 0)
- return r;
+ return log_debug_errno(r, "Invalid MAC address '%s'", word);
if (hw_addr_is_null(&mac))
- return -EINVAL;
+ return log_debug_errno(SYNTHETIC_ERRNO(EINVAL), "MAC address is not set");
if (!isempty(q))
- return -EINVAL;
+ return log_debug_errno(SYNTHETIC_ERRNO(EINVAL), "Unexpected trailing string '%s' in ifname policy '%s'", q, value);
break;
}
if (alternative_names_policy_from_string(word) >= 0) {
r = strv_extend(&alt_policies, word);
if (r < 0)
- return r;
+ return log_oom_debug();
}
r = strv_consume(&policies, TAKE_PTR(word));
if (r < 0)
- return r;
+ return log_oom_debug();
}
if (strv_isempty(policies))
- return -EINVAL;
+ return log_debug_errno(SYNTHETIC_ERRNO(EINVAL), "No ifname policy specified");
r = link_new(context, NULL, &mac, &link);
if (r < 0)
- return r;
+ return log_debug_errno(r, "Failed to create link: %m");
link->policies = TAKE_PTR(policies);
link->alt_policies = TAKE_PTR(alt_policies);
r = strv_extend_strv(&network->dns, all->dns, false);
if (r < 0)
- return r;
+ return log_oom_debug();
LIST_FOREACH(routes, route, all->routes) {
r = route_new(network, route->family, route->prefixlen, &route->dest, &route->gateway, NULL);
if (r < 0)
- return r;
+ return log_debug_errno(r, "Failed to copy route: %m");
}
}
f = memstream_init(&m);
if (!f)
- return -ENOMEM;
+ return log_oom_debug();
network_dump(network, f);
f = memstream_init(&m);
if (!f)
- return -ENOMEM;
+ return log_oom_debug();
netdev_dump(netdev, f);
f = memstream_init(&m);
if (!f)
- return -ENOMEM;
+ return log_oom_debug();
link_dump(link, f);
networkd_link_with = [libshared]
else
networkd_link_with = [libsystemd_static,
- libshared_static,
- libbasic_gcrypt]
+ libshared_static]
endif
network_includes = [libsystemd_network_includes, include_directories(['.', 'netdev', 'tc'])]
/* SPDX-License-Identifier: LGPL-2.1-or-later */
+/* Make sure the net/if.h header is included before any linux/ one */
#include <net/if.h>
-#include <netinet/in.h>
#include <linux/if_arp.h>
#include <linux/if_bridge.h>
+#include <netinet/in.h>
#include "bridge.h"
#include "netlink-util.h"
/* SPDX-License-Identifier: LGPL-2.1-or-later */
-#include <linux/fou.h>
+/* Make sure the net/if.h header is included before any linux/ one */
#include <net/if.h>
+#include <linux/fou.h>
#include <netinet/in.h>
#include <linux/ip.h>
/* SPDX-License-Identifier: LGPL-2.1-or-later */
+/* Make sure the net/if.h header is included before any linux/ one */
#include <net/if.h>
-#include <netinet/in.h>
#include <linux/if_arp.h>
+#include <netinet/in.h>
#include "alloc-util.h"
#include "conf-parser.h"
/* SPDX-License-Identifier: LGPL-2.1-or-later */
+/* Make sure the net/if.h header is included before any linux/ one */
#include <net/if.h>
#include <netinet/in.h>
#include <linux/if_arp.h>
/* SPDX-License-Identifier: LGPL-2.1-or-later */
+/* Make sure the net/if.h header is included before any linux/ one */
#include <net/if.h>
#include <netinet/in.h>
#include <linux/if_arp.h>
/* SPDX-License-Identifier: LGPL-2.1-or-later */
+/* Make sure the net/if.h header is included before any linux/ one */
#include <net/if.h>
#include <netinet/in.h>
#include <linux/if_arp.h>
/* SPDX-License-Identifier: LGPL-2.1-or-later */
+/* Make sure the net/if.h header is included before any linux/ one */
+#include <net/if.h>
#include <errno.h>
#include <fcntl.h>
-#include <net/if.h>
+#include <linux/if_tun.h>
#include <netinet/if_ether.h>
#include <sys/ioctl.h>
#include <sys/stat.h>
#include <sys/types.h>
-#include <linux/if_tun.h>
#include "alloc-util.h"
#include "daemon-util.h"
/* SPDX-License-Identifier: LGPL-2.1-or-later */
-#include <errno.h>
+/* Make sure the net/if.h header is included before any linux/ one */
#include <net/if.h>
-#include <netinet/in.h>
+#include <errno.h>
#include <linux/if_arp.h>
#include <linux/veth.h>
+#include <netinet/in.h>
#include "netlink-util.h"
#include "veth.h"
/* SPDX-License-Identifier: LGPL-2.1-or-later */
-#include <errno.h>
+/* Make sure the net/if.h header is included before any linux/ one */
#include <net/if.h>
+#include <errno.h>
#include <linux/if_arp.h>
#include <linux/if_vlan.h>
/* SPDX-License-Identifier: LGPL-2.1-or-later */
+/* Make sure the net/if.h header is included before any linux/ one */
#include <net/if.h>
-#include <netinet/in.h>
#include <linux/if_arp.h>
+#include <netinet/in.h>
#include "vrf.h"
/* SPDX-License-Identifier: LGPL-2.1-or-later */
+/* Make sure the net/if.h header is included before any linux/ one */
#include <net/if.h>
#include <netinet/in.h>
#include <linux/if_arp.h>
Copyright © 2015-2017 Jason A. Donenfeld <Jason@zx2c4.com>. All Rights Reserved.
***/
-#include <sys/ioctl.h>
+/* Make sure the net/if.h header is included before any linux/ one */
#include <net/if.h>
-#include <netinet/in.h>
#include <linux/if_arp.h>
#include <linux/ipv6_route.h>
+#include <netinet/in.h>
+#include <sys/ioctl.h>
#include "sd-resolve.h"
int verb_cat(int argc, char *argv[], void *userdata) {
_cleanup_(sd_netlink_unrefp) sd_netlink *rtnl = NULL;
+ char **args = strv_skip(argv, 1);
int r, ret = 0;
pager_open(arg_pager_flags);
+ if (strv_isempty(args))
+ return conf_files_cat(NULL, "systemd/networkd.conf", CAT_FORMAT_HAS_SECTIONS);
+
bool first = true;
- STRV_FOREACH(name, strv_skip(argv, 1)) {
+ STRV_FOREACH(name, args) {
_cleanup_strv_free_ char **dropins = NULL;
_cleanup_free_ char *path = NULL;
const char *link_config;
/* SPDX-License-Identifier: LGPL-2.1-or-later */
+/* Make sure the net/if.h header is included before any linux/ one */
+#include <net/if.h>
#include <arpa/inet.h>
#include <getopt.h>
#include <linux/if_addrlabel.h>
-#include <net/if.h>
#include <stdbool.h>
#include <sys/stat.h>
#include <sys/types.h>
" reconfigure DEVICES... Reconfigure interfaces\n"
" reload Reload .network and .netdev files\n"
" edit FILES|DEVICES... Edit network configuration files\n"
- " cat FILES|DEVICES... Show network configuration files\n"
+ " cat [FILES|DEVICES...] Show network configuration files\n"
" mask FILES... Mask network configuration files\n"
" unmask FILES... Unmask network configuration files\n"
" persistent-storage BOOL\n"
{ "reconfigure", 2, VERB_ANY, VERB_ONLINE_ONLY, verb_reconfigure },
{ "reload", 1, 1, VERB_ONLINE_ONLY, verb_reload },
{ "edit", 2, VERB_ANY, 0, verb_edit },
- { "cat", 2, VERB_ANY, 0, verb_cat },
+ { "cat", 1, VERB_ANY, 0, verb_cat },
{ "mask", 2, VERB_ANY, 0, verb_mask },
{ "unmask", 2, VERB_ANY, 0, verb_unmask },
{ "persistent-storage", 2, 2, 0, verb_persistent_storage },
_ADDRESS_GENERATION_TYPE_INVALID = -EINVAL,
} AddressGenerationType;
-typedef struct IPv6Token {
+struct IPv6Token {
+ unsigned n_ref;
AddressGenerationType type;
struct in6_addr address;
sd_id128_t secret_key;
-} IPv6Token;
+};
+
+DEFINE_TRIVIAL_REF_UNREF_FUNC(IPv6Token, ipv6_token, mfree);
+DEFINE_TRIVIAL_CLEANUP_FUNC(IPv6Token*, ipv6_token_unref);
+
+static void ipv6_token_hash_func(const IPv6Token *p, struct siphash *state) {
+ siphash24_compress_typesafe(p->type, state);
+ siphash24_compress_typesafe(p->address, state);
+ id128_hash_func(&p->secret_key, state);
+}
+
+static int ipv6_token_compare_func(const IPv6Token *a, const IPv6Token *b) {
+ int r;
+
+ r = CMP(a->type, b->type);
+ if (r != 0)
+ return r;
+
+ r = memcmp(&a->address, &b->address, sizeof(struct in6_addr));
+ if (r != 0)
+ return r;
+
+ return id128_compare_func(&a->secret_key, &b->secret_key);
+}
+
+DEFINE_PRIVATE_HASH_OPS_WITH_KEY_DESTRUCTOR(
+ ipv6_token_hash_ops,
+ IPv6Token,
+ ipv6_token_hash_func,
+ ipv6_token_compare_func,
+ ipv6_token_unref);
+
+DEFINE_PRIVATE_HASH_OPS_FULL(
+ ipv6_token_by_addr_hash_ops,
+ struct in6_addr,
+ in6_addr_hash_func,
+ in6_addr_compare_func,
+ free,
+ IPv6Token,
+ ipv6_token_unref);
+
+static int ipv6_token_new(AddressGenerationType type, const struct in6_addr *addr, const sd_id128_t *secret_key, IPv6Token **ret) {
+ IPv6Token *p;
+
+ assert(type >= 0 && type < _ADDRESS_GENERATION_TYPE_MAX);
+ assert(addr);
+ assert(secret_key);
+ assert(ret);
+
+ p = new(IPv6Token, 1);
+ if (!p)
+ return -ENOMEM;
+
+ *p = (IPv6Token) {
+ .n_ref = 1,
+ .type = type,
+ .address = *addr,
+ .secret_key = *secret_key,
+ };
+
+ *ret = p;
+ return 0;
+}
+
+static int ipv6_token_add(Set **tokens, AddressGenerationType type, const struct in6_addr *addr, const sd_id128_t *secret_key) {
+ IPv6Token *p;
+ int r;
+
+ assert(tokens);
+
+ r = ipv6_token_new(type, addr, secret_key, &p);
+ if (r < 0)
+ return r;
+
+ return set_ensure_consume(tokens, &ipv6_token_hash_ops, p);
+}
+
+static int ipv6_token_put_by_addr(Hashmap **tokens_by_address, const struct in6_addr *addr, IPv6Token *token) {
+ _cleanup_free_ struct in6_addr *copy = NULL;
+ int r;
+
+ assert(tokens_by_address);
+ assert(addr);
+ assert(token);
+
+ copy = newdup(struct in6_addr, addr, 1);
+ if (!copy)
+ return -ENOMEM;
+
+ r = hashmap_ensure_put(tokens_by_address, &ipv6_token_by_addr_hash_ops, copy, token);
+ if (r == -EEXIST)
+ return 0;
+ if (r < 0)
+ return r;
+
+ TAKE_PTR(copy);
+ ipv6_token_ref(token);
+ return 1;
+}
+
+static int ipv6_token_type_put_by_addr(Hashmap **tokens_by_addr, const struct in6_addr *addr, AddressGenerationType type) {
+ _cleanup_(ipv6_token_unrefp) IPv6Token *token = NULL;
+ int r;
+
+ assert(tokens_by_addr);
+ assert(addr);
+
+ r = ipv6_token_new(type, &(struct in6_addr) {}, &SD_ID128_NULL, &token);
+ if (r < 0)
+ return r;
+
+ return ipv6_token_put_by_addr(tokens_by_addr, addr, token);
+}
+
static int generate_eui64_address(const Link *link, const struct in6_addr *prefix, struct in6_addr *ret) {
assert(link);
const sd_id128_t *app_id,
const sd_id128_t *secret_key,
const struct in6_addr *prefix,
+ const struct in6_addr *previous,
struct in6_addr *ret) {
sd_id128_t secret_machine_key;
struct in6_addr addr;
+ bool found = false;
uint8_t i;
int r;
for (i = 0; i < DAD_CONFLICTS_IDGEN_RETRIES_RFC7217; i++) {
generate_stable_private_address_one(link, secret_key, prefix, i, &addr);
- if (stable_private_address_is_valid(&addr))
- break;
+ if (!stable_private_address_is_valid(&addr))
+ continue;
+
+ /* When 'previous' is non-NULL, then this is called after DAD in the kernel triggered.
+ * Let's increment the counter and provide the next address. */
+ if (previous && !found) {
+ found = in6_addr_equal(previous, &addr);
+ continue;
+ }
+
+ break;
}
- if (i >= DAD_CONFLICTS_IDGEN_RETRIES_RFC7217)
+ if (i >= DAD_CONFLICTS_IDGEN_RETRIES_RFC7217) {
/* propagate recognizable errors. */
- return log_link_debug_errno(link, SYNTHETIC_ERRNO(ENOANO),
+ if (previous && !found)
+ return -EADDRNOTAVAIL;
+
+ return log_link_debug_errno(link, SYNTHETIC_ERRNO(EADDRINUSE),
"Failed to generate stable private address.");
+ }
*ret = addr;
- return 0;
+ return 1;
}
static int generate_addresses(
const sd_id128_t *app_id,
const struct in6_addr *prefix,
uint8_t prefixlen,
- Set **ret) {
+ Hashmap **ret) {
- _cleanup_set_free_ Set *addresses = NULL;
- struct in6_addr masked;
+ _cleanup_hashmap_free_ Hashmap *tokens_by_address = NULL;
+ struct in6_addr masked, addr;
IPv6Token *j;
int r;
in6_addr_mask(&masked, prefixlen);
SET_FOREACH(j, tokens) {
- struct in6_addr addr, *copy;
-
switch (j->type) {
case ADDRESS_GENERATION_EUI64:
if (generate_eui64_address(link, &masked, &addr) < 0)
if (in6_addr_is_set(&j->address) && !in6_addr_equal(&j->address, &masked))
continue;
- if (generate_stable_private_address(link, app_id, &j->secret_key, &masked, &addr) < 0)
+ if (generate_stable_private_address(link, app_id, &j->secret_key, &masked, /* previous = */ NULL, &addr) < 0)
continue;
break;
assert_not_reached();
}
- copy = newdup(struct in6_addr, &addr, 1);
- if (!copy)
- return -ENOMEM;
-
- r = set_ensure_consume(&addresses, &in6_addr_hash_ops_free, copy);
+ r = ipv6_token_put_by_addr(&tokens_by_address, &addr, j);
if (r < 0)
return r;
}
/* fall back to EUI-64 if no token is provided */
- if (set_isempty(addresses)) {
- _cleanup_free_ struct in6_addr *addr = NULL;
+ if (hashmap_isempty(tokens_by_address)) {
+ AddressGenerationType type;
- addr = new(struct in6_addr, 1);
- if (!addr)
- return -ENOMEM;
-
- if (IN_SET(link->iftype, ARPHRD_ETHER, ARPHRD_INFINIBAND))
- r = generate_eui64_address(link, &masked, addr);
- else
- r = generate_stable_private_address(link, app_id, &SD_ID128_NULL, &masked, addr);
+ if (IN_SET(link->iftype, ARPHRD_ETHER, ARPHRD_INFINIBAND)) {
+ type = ADDRESS_GENERATION_EUI64;
+ r = generate_eui64_address(link, &masked, &addr);
+ } else {
+ type = ADDRESS_GENERATION_PREFIXSTABLE;
+ r = generate_stable_private_address(link, app_id, &SD_ID128_NULL, &masked, /* previous = */ NULL, &addr);
+ }
if (r < 0)
return r;
- r = set_ensure_consume(&addresses, &in6_addr_hash_ops_free, TAKE_PTR(addr));
+ r = ipv6_token_type_put_by_addr(&tokens_by_address, &addr, type);
if (r < 0)
return r;
}
- *ret = TAKE_PTR(addresses);
+ *ret = TAKE_PTR(tokens_by_address);
return 0;
}
-int dhcp_pd_generate_addresses(Link *link, const struct in6_addr *prefix, Set **ret) {
+int dhcp_pd_generate_addresses(Link *link, const struct in6_addr *prefix, Hashmap **ret) {
return generate_addresses(link, link->network->dhcp_pd_tokens, &DHCP_PD_APP_ID, prefix, 64, ret);
}
-int ndisc_generate_addresses(Link *link, const struct in6_addr *prefix, uint8_t prefixlen, Set **ret) {
+int ndisc_generate_addresses(Link *link, const struct in6_addr *prefix, uint8_t prefixlen, Hashmap **ret) {
return generate_addresses(link, link->network->ndisc_tokens, &NDISC_APP_ID, prefix, prefixlen, ret);
}
-int radv_generate_addresses(Link *link, Set *tokens, const struct in6_addr *prefix, uint8_t prefixlen, Set **ret) {
+int radv_generate_addresses(Link *link, Set *tokens, const struct in6_addr *prefix, uint8_t prefixlen, Hashmap **ret) {
return generate_addresses(link, tokens, &RADV_APP_ID, prefix, prefixlen, ret);
}
-static void ipv6_token_hash_func(const IPv6Token *p, struct siphash *state) {
- siphash24_compress_typesafe(p->type, state);
- siphash24_compress_typesafe(p->address, state);
- id128_hash_func(&p->secret_key, state);
-}
-
-static int ipv6_token_compare_func(const IPv6Token *a, const IPv6Token *b) {
- int r;
-
- r = CMP(a->type, b->type);
- if (r != 0)
- return r;
-
- r = memcmp(&a->address, &b->address, sizeof(struct in6_addr));
- if (r != 0)
- return r;
-
- return id128_compare_func(&a->secret_key, &b->secret_key);
-}
-
-DEFINE_PRIVATE_HASH_OPS_WITH_KEY_DESTRUCTOR(
- ipv6_token_hash_ops,
- IPv6Token,
- ipv6_token_hash_func,
- ipv6_token_compare_func,
- free);
+int regenerate_address(Address *address, Link *link) {
+ struct in6_addr masked;
+ sd_id128_t app_id;
-static int ipv6_token_add(Set **tokens, AddressGenerationType type, const struct in6_addr *addr, const sd_id128_t *secret_key) {
- IPv6Token *p;
+ assert(link);
+ assert(address);
+ assert(address->family == AF_INET6);
+ assert(!address->link && !address->network);
- assert(tokens);
- assert(type >= 0 && type < _ADDRESS_GENERATION_TYPE_MAX);
- assert(addr);
- assert(secret_key);
+ if (!address->token ||
+ address->token->type != ADDRESS_GENERATION_PREFIXSTABLE)
+ return 0;
- p = new(IPv6Token, 1);
- if (!p)
- return -ENOMEM;
+ switch (address->source) {
+ case NETWORK_CONFIG_SOURCE_STATIC:
+ app_id = RADV_APP_ID;
+ break;
+ case NETWORK_CONFIG_SOURCE_DHCP_PD:
+ app_id = DHCP_PD_APP_ID;
+ break;
+ case NETWORK_CONFIG_SOURCE_NDISC:
+ app_id = NDISC_APP_ID;
+ break;
+ default:
+ assert_not_reached();
+ }
- *p = (IPv6Token) {
- .type = type,
- .address = *addr,
- .secret_key = *secret_key,
- };
+ masked = address->in_addr.in6;
+ in6_addr_mask(&masked, address->prefixlen);
- return set_ensure_consume(tokens, &ipv6_token_hash_ops, p);
+ return generate_stable_private_address(link, &app_id, &address->token->secret_key, &masked, &address->in_addr.in6, &address->in_addr.in6);
}
int config_parse_address_generation_type(
#pragma once
#include "conf-parser.h"
+#include "hashmap.h"
#include "in-addr-util.h"
-#include "set.h"
+typedef struct Address Address;
+typedef struct IPv6Token IPv6Token;
typedef struct Link Link;
-int dhcp_pd_generate_addresses(Link *link, const struct in6_addr *prefix, Set **ret);
-int ndisc_generate_addresses(Link *link, const struct in6_addr *prefix, uint8_t prefixlen, Set **ret);
-int radv_generate_addresses(Link *link, Set *tokens, const struct in6_addr *prefix, uint8_t prefixlen, Set **ret);
+IPv6Token* ipv6_token_ref(IPv6Token *token);
+IPv6Token* ipv6_token_unref(IPv6Token *token);
+
+int dhcp_pd_generate_addresses(Link *link, const struct in6_addr *prefix, Hashmap **ret);
+int ndisc_generate_addresses(Link *link, const struct in6_addr *prefix, uint8_t prefixlen, Hashmap **ret);
+int radv_generate_addresses(Link *link, Set *tokens, const struct in6_addr *prefix, uint8_t prefixlen, Hashmap **ret);
+
+int regenerate_address(Address *address, Link *link);
CONFIG_PARSER_PROTOTYPE(config_parse_address_generation_type);
/* SPDX-License-Identifier: LGPL-2.1-or-later */
+/* Make sure the net/if.h header is included before any linux/ one */
#include <net/if.h>
#include <linux/if_addrlabel.h>
#include "netlink-util.h"
#include "networkd-address-pool.h"
#include "networkd-address.h"
+#include "networkd-dhcp-prefix-delegation.h"
#include "networkd-dhcp-server.h"
#include "networkd-ipv4acd.h"
#include "networkd-manager.h"
+#include "networkd-ndisc.h"
#include "networkd-netlabel.h"
#include "networkd-network.h"
#include "networkd-queue.h"
config_section_free(address->section);
free(address->label);
free(address->netlabel);
+ ipv6_token_unref(address->token);
nft_set_context_clear(&address->nft_set_context);
return mfree(address);
}
dest->section = NULL;
dest->link = NULL;
dest->label = NULL;
+ dest->token = ipv6_token_ref(src->token);
dest->netlabel = NULL;
dest->nft_set_context.sets = NULL;
dest->nft_set_context.n_sets = 0;
return 0;
}
-static int address_drop(Address *address) {
- Link *link = ASSERT_PTR(ASSERT_PTR(address)->link);
+static int address_removed_maybe_kernel_dad(Link *link, Address *address) {
+ int r;
+
+ assert(link);
+ assert(address);
+
+ if (!IN_SET(link->state, LINK_STATE_CONFIGURING, LINK_STATE_CONFIGURED))
+ return 0;
+
+ if (address->family != AF_INET6)
+ return 0;
+
+ if (!FLAGS_SET(address->flags, IFA_F_TENTATIVE))
+ return 0;
+
+ log_link_info(link, "Address %s with tentative flag is removed, maybe a duplicated address is assigned on another node or link?",
+ IN6_ADDR_TO_STRING(&address->in_addr.in6));
+
+ /* Reset the address state, as the object may be reused in the below. */
+ address->state = 0;
+
+ switch (address->source) {
+ case NETWORK_CONFIG_SOURCE_STATIC:
+ r = link_reconfigure_radv_address(address, link);
+ break;
+ case NETWORK_CONFIG_SOURCE_DHCP_PD:
+ r = dhcp_pd_reconfigure_address(address, link);
+ break;
+ case NETWORK_CONFIG_SOURCE_NDISC:
+ r = ndisc_reconfigure_address(address, link);
+ break;
+ default:
+ r = 0;
+ }
+ if (r < 0)
+ return log_link_warning_errno(link, r, "Failed to configure an alternative address: %m");
+
+ return 0;
+}
+
+static int address_drop(Address *in, bool removed_by_us) {
+ _cleanup_(address_unrefp) Address *address = address_ref(ASSERT_PTR(in));
+ Link *link = ASSERT_PTR(address->link);
int r;
r = address_set_masquerade(address, /* add = */ false);
address_detach(address);
+ if (!removed_by_us) {
+ r = address_removed_maybe_kernel_dad(link, address);
+ if (r < 0) {
+ link_enter_failed(link);
+ return r;
+ }
+ }
+
link_update_operstate(link, /* also_update_master = */ true);
link_check_ready(link);
return 0;
if (address_get_request(link, address, &req) >= 0)
address_enter_removed(req->userdata);
- (void) address_drop(address);
+ (void) address_drop(address, /* removed_by_us = */ true);
}
}
assert(ret);
r = address_acquire_from_dhcp_server_leases_file(link, address, ret);
- if (r != -ENOENT)
+ if (!IN_SET(r, -ENOENT, -ENXIO, -EINVAL))
return r;
r = address_pool_acquire(link->manager, address->family, address->prefixlen, &a);
if (type == RTM_DELADDR) {
if (address) {
+ bool removed_by_us = FLAGS_SET(address->state, NETWORK_CONFIG_STATE_REMOVING);
+
address_enter_removed(address);
log_address_debug(address, "Forgetting removed", link);
- (void) address_drop(address);
+ (void) address_drop(address, removed_by_us);
} else
log_address_debug(tmp, "Kernel removed unknown", link);
(void) nft_set_context_dup(&a->nft_set_context, &address->nft_set_context);
address->requested_as_null = a->requested_as_null;
address->callback = a->callback;
+
+ ipv6_token_ref(a->token);
+ ipv6_token_unref(address->token);
+ address->token = a->token;
}
/* Then, update miscellaneous info. */
#include "hash-funcs.h"
#include "in-addr-util.h"
#include "network-util.h"
+#include "networkd-address-generation.h"
#include "networkd-link.h"
#include "networkd-util.h"
#include "time-util.h"
* To control DAD for IPv6 dynamic addresses, set IFA_F_NODAD to flags. */
AddressFamily duplicate_address_detection;
+ /* Used by address generator. */
+ IPv6Token *token;
+
/* Called when address become ready */
address_ready_callback_t callback;
/* SPDX-License-Identifier: LGPL-2.1-or-later */
+/* Make sure the net/if.h header is included before any linux/ one */
#include <net/if.h>
#include <linux/if_bridge.h>
/* SPDX-License-Identifier: LGPL-2.1-or-later */
+/* Make sure the net/if.h header is included before any linux/ one */
#include <net/if.h>
#include <linux/can/netlink.h>
"Network\0"
"DHCPv4\0"
"DHCPv6\0"
+ "DHCPServer\0"
"DHCP\0",
config_item_perf_lookup, networkd_gperf_lookup,
CONFIG_PARSE_WARN,
return 0;
}
+DEFINE_CONFIG_PARSE_ENUM(config_parse_default_dhcp_use_domains, dhcp_use_domains, DHCPUseDomains, "Failed to parse UseDomains=")
+
int config_parse_dhcp_use_ntp(
const char* unit,
const char *filename,
CONFIG_PARSER_PROTOTYPE(config_parse_dhcp_send_hostname);
CONFIG_PARSER_PROTOTYPE(config_parse_dhcp_use_dns);
CONFIG_PARSER_PROTOTYPE(config_parse_dhcp_use_domains);
+CONFIG_PARSER_PROTOTYPE(config_parse_default_dhcp_use_domains);
CONFIG_PARSER_PROTOTYPE(config_parse_dhcp_use_ntp);
CONFIG_PARSER_PROTOTYPE(config_parse_iaid);
CONFIG_PARSER_PROTOTYPE(config_parse_dhcp_or_ra_route_table);
FORMAT_LIFETIME(address->lifetime_preferred_usec));
}
+static int dhcp_pd_request_address_one(Address *address, Link *link) {
+ Address *existing;
+
+ assert(address);
+ assert(link);
+
+ log_dhcp_pd_address(link, address);
+
+ if (address_get(link, address, &existing) < 0)
+ link->dhcp_pd_configured = false;
+ else
+ address_unmark(existing);
+
+ return link_request_address(link, address, &link->dhcp_pd_messages, dhcp_pd_address_handler, NULL);
+}
+
+int dhcp_pd_reconfigure_address(Address *address, Link *link) {
+ int r;
+
+ assert(address);
+ assert(address->source == NETWORK_CONFIG_SOURCE_DHCP_PD);
+ assert(link);
+
+ r = regenerate_address(address, link);
+ if (r <= 0)
+ return r;
+
+ r = dhcp_pd_request_address_one(address, link);
+ if (r < 0)
+ return r;
+
+ if (!link->dhcp_pd_configured)
+ link_set_state(link, LINK_STATE_CONFIGURING);
+
+ link_check_ready(link);
+ return 0;
+}
+
static int dhcp_pd_request_address(
Link *link,
const struct in6_addr *prefix,
usec_t lifetime_preferred_usec,
usec_t lifetime_valid_usec) {
- _cleanup_set_free_ Set *addresses = NULL;
- struct in6_addr *a;
int r;
assert(link);
if (!link->network->dhcp_pd_assign)
return 0;
- r = dhcp_pd_generate_addresses(link, prefix, &addresses);
+ _cleanup_hashmap_free_ Hashmap *tokens_by_address = NULL;
+ r = dhcp_pd_generate_addresses(link, prefix, &tokens_by_address);
if (r < 0)
return log_link_warning_errno(link, r, "Failed to generate addresses for acquired DHCP delegated prefix: %m");
- SET_FOREACH(a, addresses) {
+ IPv6Token *token;
+ struct in6_addr *a;
+ HASHMAP_FOREACH_KEY(token, a, tokens_by_address) {
_cleanup_(address_unrefp) Address *address = NULL;
- Address *existing;
r = address_new(&address);
if (r < 0)
address->lifetime_valid_usec = lifetime_valid_usec;
SET_FLAG(address->flags, IFA_F_MANAGETEMPADDR, link->network->dhcp_pd_manage_temporary_address);
address->route_metric = link->network->dhcp_pd_route_metric;
-
- log_dhcp_pd_address(link, address);
+ address->token = ipv6_token_ref(token);
r = free_and_strdup_warn(&address->netlabel, link->network->dhcp_pd_netlabel);
if (r < 0)
return r;
- if (address_get(link, address, &existing) < 0)
- link->dhcp_pd_configured = false;
- else
- address_unmark(existing);
-
- r = link_request_address(link, address, &link->dhcp_pd_messages,
- dhcp_pd_address_handler, NULL);
+ r = dhcp_pd_request_address_one(address, link);
if (r < 0)
return log_link_error_errno(link, r, "Failed to request DHCP delegated prefix address: %m");
}
#include "conf-parser.h"
+typedef struct Address Address;
typedef struct Link Link;
bool link_dhcp_pd_is_enabled(Link *link);
int dhcp6_pd_prefix_acquired(Link *uplink);
void dhcp_pd_prefix_lost(Link *uplink);
void dhcp4_pd_prefix_lost(Link *uplink);
+int dhcp_pd_reconfigure_address(Address *address, Link *link);
CONFIG_PARSER_PROTOTYPE(config_parse_dhcp_pd_subnet_id);
return 0;
}
+static bool dhcp_server_persist_leases(Link *link) {
+ assert(link);
+ assert(link->manager);
+ assert(link->network);
+
+ if (in4_addr_is_set(&link->network->dhcp_server_relay_target))
+ return false; /* On relay mode. Nothing saved in the persistent storage. */
+
+ if (link->network->dhcp_server_persist_leases >= 0)
+ return link->network->dhcp_server_persist_leases;
+
+ return link->manager->dhcp_server_persist_leases;
+}
+
int address_acquire_from_dhcp_server_leases_file(Link *link, const Address *address, union in_addr_union *ret) {
struct in_addr a;
uint8_t prefixlen;
if (!link_dhcp4_server_enabled(link))
return -ENOENT;
+ if (!dhcp_server_persist_leases(link))
+ return -ENOENT;
+
if (link->manager->persistent_storage_fd < 0)
return -EBUSY; /* The persistent storage is not ready, try later again. */
lease_file,
&a,
&prefixlen);
- if (r < 0)
+ if (r == -ENOENT)
return r;
+ if (r < 0)
+ return log_warning_errno(r, "Failed to load lease file %s: %s",
+ lease_file,
+ r == -ENXIO ? "expected JSON content not found" :
+ r == -EINVAL ? "invalid JSON" :
+ STRERROR(r));
if (prefixlen != address->prefixlen)
return -ENOENT;
/* TODO: Maybe, also check the system time is synced. If the system does not have RTC battery, then
* the realtime clock in not usable in the early boot stage, and all saved leases may be wrongly
* handled as expired and dropped. */
- if (!sd_dhcp_server_is_in_relay_mode(link->dhcp_server)) {
+ if (dhcp_server_persist_leases(link)) {
if (link->manager->persistent_storage_fd < 0)
return 0; /* persistent storage is not ready. */
HASHMAP_FOREACH(link, manager->links_by_index) {
if (!link->dhcp_server)
continue;
- if (sd_dhcp_server_is_in_relay_mode(link->dhcp_server))
+ if (!dhcp_server_persist_leases(link))
continue;
/* Even if 'start' is true, first we need to stop the server. Otherwise, we cannot (re)set
assert(link);
assert(link->manager);
+ if (!link->dhcp6_lease)
+ return 0;
+
log_link_info(link, "DHCPv6 lease lost");
if (sd_dhcp6_lease_has_pd_prefix(link->dhcp6_lease))
Network.IPv4Forwarding, config_parse_tristate, 0, offsetof(Manager, ip_forwarding[0])
Network.IPv6Forwarding, config_parse_tristate, 0, offsetof(Manager, ip_forwarding[1])
Network.IPv6PrivacyExtensions, config_parse_ipv6_privacy_extensions, 0, offsetof(Manager, ipv6_privacy_extensions)
+DHCPv4.UseDomains, config_parse_default_dhcp_use_domains, 0, offsetof(Manager, dhcp_use_domains)
DHCPv4.DUIDType, config_parse_duid_type, 0, offsetof(Manager, dhcp_duid)
DHCPv4.DUIDRawData, config_parse_duid_rawdata, 0, offsetof(Manager, dhcp_duid)
+DHCPv6.UseDomains, config_parse_default_dhcp_use_domains, 0, offsetof(Manager, dhcp6_use_domains)
DHCPv6.DUIDType, config_parse_duid_type, 0, offsetof(Manager, dhcp6_duid)
DHCPv6.DUIDRawData, config_parse_duid_rawdata, 0, offsetof(Manager, dhcp6_duid)
+DHCPServer.PersistLeases, config_parse_bool, 0, offsetof(Manager, dhcp_server_persist_leases)
/* Deprecated */
DHCP.DUIDType, config_parse_manager_duid_type, 0, 0
DHCP.DUIDRawData, config_parse_manager_duid_rawdata, 0, 0
/* SPDX-License-Identifier: LGPL-2.1-or-later */
+/* Make sure the net/if.h header is included before any linux/ one */
#include <net/if.h>
#include <netinet/in.h>
#include <linux/if.h>
return 0;
}
- r = sd_device_get_is_initialized(device);
+ r = device_is_processed(device);
if (r < 0)
- return log_link_warning_errno(link, r, "Could not determine whether the device is initialized: %m");
+ return log_link_warning_errno(link, r, "Could not determine whether the device is processed by udevd: %m");
if (r == 0) {
/* not yet ready */
log_link_debug(link, "link pending udev initialization...");
return 0;
}
- r = device_is_processing(device);
- if (r < 0)
- return log_link_warning_errno(link, r, "Failed to determine whether the device is being processed: %m");
- if (r > 0) {
- log_link_debug(link, "Interface is being processed by udevd, pending initialization.");
- return 0;
- }
-
return link_initialized(link, device);
}
/* We set the ipv6 mtu after the device mtu, but the kernel resets
* ipv6 mtu on NETDEV_UP, so we need to reset it. */
- r = link_set_ipv6_mtu(link);
+ r = link_set_ipv6_mtu(link, LOG_INFO);
if (r < 0)
log_link_warning_errno(link, r, "Cannot set IPv6 MTU, ignoring: %m");
link->mtu = mtu;
+ if (IN_SET(link->state, LINK_STATE_CONFIGURING, LINK_STATE_CONFIGURED)) {
+ /* The kernel resets IPv6 MTU after changing device MTU. So, we need to re-set IPv6 MTU again. */
+ r = link_set_ipv6_mtu(link, LOG_INFO);
+ if (r < 0)
+ log_link_warning_errno(link, r, "Failed to set IPv6 MTU, ignoring: %m");
+ }
+
if (link->dhcp_client) {
r = sd_dhcp_client_set_mtu(link->dhcp_client, link->mtu);
if (r < 0)
sd_dhcp_server *dhcp_server;
sd_ndisc *ndisc;
+ sd_ndisc_router *ndisc_default_router;
sd_event_source *ndisc_expire;
Set *ndisc_rdnss;
Set *ndisc_dnssl;
Set *ndisc_captive_portals;
Set *ndisc_pref64;
+ Set *ndisc_redirects;
+ uint32_t ndisc_mtu;
unsigned ndisc_messages;
bool ndisc_configured:1;
}
if (fstat(fd, &st) < 0)
- return log_warning_errno(r, "Failed to stat the passed persistent storage fd: %m");
+ return log_warning_errno(errno, "Failed to stat the passed persistent storage fd: %m");
r = stat_verify_directory(&st);
if (r < 0)
if (r <= 0)
return -EBADF;
- fd = open("/var/lib/systemd/network/", O_CLOEXEC | O_DIRECTORY | O_PATH);
+ fd = open("/var/lib/systemd/network/", O_CLOEXEC | O_DIRECTORY);
if (fd < 0)
return log_debug_errno(errno, "Failed to open /var/lib/systemd/network/, ignoring: %m");
.dhcp_duid.type = DUID_TYPE_EN,
.dhcp6_duid.type = DUID_TYPE_EN,
.duid_product_uuid.type = DUID_TYPE_UUID,
+ .dhcp_server_persist_leases = true,
.ip_forwarding = { -1, -1, },
};
bool manage_foreign_routes;
bool manage_foreign_rules;
bool manage_foreign_nexthops;
+ bool dhcp_server_persist_leases;
Set *dirty_links;
Set *new_wlan_ifindices;
OrderedSet *address_pools;
Set *dhcp_pd_subnet_ids;
+ DHCPUseDomains dhcp_use_domains;
+ DHCPUseDomains dhcp6_use_domains;
+
DUID dhcp_duid;
DUID dhcp6_duid;
DUID duid_product_uuid;
}
}
-static int ndisc_request_route(Route *route, Link *link, sd_ndisc_router *rt) {
- struct in6_addr router;
- uint8_t hop_limit = 0;
- uint32_t mtu = 0;
- bool is_new;
+static int ndisc_request_route(Route *route, Link *link) {
int r;
assert(route);
assert(link);
assert(link->manager);
assert(link->network);
- assert(rt);
-
- r = sd_ndisc_router_get_sender_address(rt, &router);
- if (r < 0)
- return r;
-
- if (link->network->ndisc_use_mtu) {
- r = sd_ndisc_router_get_mtu(rt, &mtu);
- if (r < 0 && r != -ENODATA)
- return log_link_warning_errno(link, r, "Failed to get MTU from RA: %m");
- }
-
- if (link->network->ndisc_use_hop_limit) {
- r = sd_ndisc_router_get_hop_limit(rt, &hop_limit);
- if (r < 0 && r != -ENODATA)
- return log_link_warning_errno(link, r, "Failed to get hop limit from RA: %m");
- }
route->source = NETWORK_CONFIG_SOURCE_NDISC;
- route->provider.in6 = router;
+
if (!route->table_set)
route->table = link_get_ndisc_route_table(link);
- if (!route->protocol_set)
- route->protocol = RTPROT_RA;
- r = route_metric_set(&route->metric, RTAX_MTU, mtu);
- if (r < 0)
- return r;
- r = route_metric_set(&route->metric, RTAX_HOPLIMIT, hop_limit);
- if (r < 0)
- return r;
+
r = route_metric_set(&route->metric, RTAX_QUICKACK, link->network->ndisc_quickack);
if (r < 0)
return r;
route->pref = pref_original;
ndisc_set_route_priority(link, route);
- is_new = route_get(link->manager, route, NULL) < 0;
+ bool is_new = route_get(link->manager, route, NULL) < 0;
r = link_request_route(link, route, &link->ndisc_messages, ndisc_route_handler);
if (r < 0)
return 0;
}
+static int ndisc_request_router_route(Route *route, Link *link, sd_ndisc_router *rt) {
+ int r;
+
+ assert(route);
+ assert(link);
+ assert(rt);
+
+ r = sd_ndisc_router_get_sender_address(rt, &route->provider.in6);
+ if (r < 0)
+ return r;
+
+ if (!route->protocol_set)
+ route->protocol = RTPROT_RA;
+
+ return ndisc_request_route(route, link);
+}
+
static int ndisc_remove_route(Route *route, Link *link) {
int r;
return 1;
}
-static int ndisc_request_address(Address *address, Link *link, sd_ndisc_router *rt) {
- struct in6_addr router;
+static int ndisc_request_address(Address *address, Link *link) {
bool is_new;
int r;
assert(address);
assert(link);
- assert(rt);
-
- r = sd_ndisc_router_get_sender_address(rt, &router);
- if (r < 0)
- return r;
address->source = NETWORK_CONFIG_SOURCE_NDISC;
- address->provider.in6 = router;
r = free_and_strdup_warn(&address->netlabel, link->network->ndisc_netlabel);
if (r < 0)
return 0;
}
+int ndisc_reconfigure_address(Address *address, Link *link) {
+ int r;
+
+ assert(address);
+ assert(address->source == NETWORK_CONFIG_SOURCE_NDISC);
+ assert(link);
+
+ r = regenerate_address(address, link);
+ if (r <= 0)
+ return r;
+
+ r = ndisc_request_address(address, link);
+ if (r < 0)
+ return r;
+
+ if (!link->ndisc_configured)
+ link_set_state(link, LINK_STATE_CONFIGURING);
+
+ link_check_ready(link);
+ return 0;
+}
+
+static int ndisc_redirect_route_new(sd_ndisc_redirect *rd, Route **ret) {
+ _cleanup_(route_unrefp) Route *route = NULL;
+ struct in6_addr gateway, destination;
+ int r;
+
+ assert(rd);
+ assert(ret);
+
+ r = sd_ndisc_redirect_get_target_address(rd, &gateway);
+ if (r < 0)
+ return r;
+
+ r = sd_ndisc_redirect_get_destination_address(rd, &destination);
+ if (r < 0)
+ return r;
+
+ r = route_new(&route);
+ if (r < 0)
+ return r;
+
+ route->family = AF_INET6;
+ if (!in6_addr_equal(&gateway, &destination)) {
+ route->nexthop.gw.in6 = gateway;
+ route->nexthop.family = AF_INET6;
+ }
+ route->dst.in6 = destination;
+ route->dst_prefixlen = 128;
+ route->protocol = RTPROT_REDIRECT;
+
+ r = sd_ndisc_redirect_get_sender_address(rd, &route->provider.in6);
+ if (r < 0)
+ return r;
+
+ *ret = TAKE_PTR(route);
+ return 0;
+}
+
+static int ndisc_remove_redirect_route(Link *link, sd_ndisc_redirect *rd) {
+ _cleanup_(route_unrefp) Route *route = NULL;
+ int r;
+
+ assert(link);
+ assert(rd);
+
+ r = ndisc_redirect_route_new(rd, &route);
+ if (r < 0)
+ return r;
+
+ return ndisc_remove_route(route, link);
+}
+
+static void ndisc_redirect_hash_func(const sd_ndisc_redirect *x, struct siphash *state) {
+ struct in6_addr dest = {};
+
+ assert(x);
+ assert(state);
+
+ (void) sd_ndisc_redirect_get_destination_address((sd_ndisc_redirect*) x, &dest);
+
+ siphash24_compress_typesafe(dest, state);
+}
+
+static int ndisc_redirect_compare_func(const sd_ndisc_redirect *x, const sd_ndisc_redirect *y) {
+ struct in6_addr dest_x = {}, dest_y = {};
+
+ assert(x);
+ assert(y);
+
+ (void) sd_ndisc_redirect_get_destination_address((sd_ndisc_redirect*) x, &dest_x);
+ (void) sd_ndisc_redirect_get_destination_address((sd_ndisc_redirect*) y, &dest_y);
+
+ return memcmp(&dest_x, &dest_y, sizeof(dest_x));
+}
+
+DEFINE_PRIVATE_HASH_OPS_WITH_KEY_DESTRUCTOR(
+ ndisc_redirect_hash_ops,
+ sd_ndisc_redirect,
+ ndisc_redirect_hash_func,
+ ndisc_redirect_compare_func,
+ sd_ndisc_redirect_unref);
+
+static int ndisc_redirect_equal(sd_ndisc_redirect *x, sd_ndisc_redirect *y) {
+ struct in6_addr a, b;
+ int r;
+
+ assert(x);
+ assert(y);
+
+ r = sd_ndisc_redirect_get_destination_address(x, &a);
+ if (r < 0)
+ return r;
+
+ r = sd_ndisc_redirect_get_destination_address(y, &b);
+ if (r < 0)
+ return r;
+
+ if (!in6_addr_equal(&a, &b))
+ return false;
+
+ r = sd_ndisc_redirect_get_target_address(x, &a);
+ if (r < 0)
+ return r;
+
+ r = sd_ndisc_redirect_get_target_address(y, &b);
+ if (r < 0)
+ return r;
+
+ return in6_addr_equal(&a, &b);
+}
+
+static int ndisc_redirect_drop_conflict(Link *link, sd_ndisc_redirect *rd) {
+ _cleanup_(sd_ndisc_redirect_unrefp) sd_ndisc_redirect *existing = NULL;
+ int r;
+
+ assert(link);
+ assert(rd);
+
+ existing = set_remove(link->ndisc_redirects, rd);
+ if (!existing)
+ return 0;
+
+ r = ndisc_redirect_equal(rd, existing);
+ if (r != 0)
+ return r;
+
+ return ndisc_remove_redirect_route(link, existing);
+}
+
+static int ndisc_redirect_verify_sender(Link *link, sd_ndisc_redirect *rd) {
+ sd_ndisc_redirect *existing;
+ struct in6_addr router, sender;
+ int r;
+
+ assert(link);
+ assert(rd);
+
+ /* RFC 4861 section 8.1
+ * The IP source address of the Redirect is the same as the current first-hop router for the specified
+ * ICMP Destination Address. */
+
+ r = sd_ndisc_redirect_get_sender_address(rd, &sender);
+ if (r < 0)
+ return r;
+
+ existing = set_get(link->ndisc_redirects, rd);
+ if (existing) {
+ struct in6_addr target, dest;
+
+ /* If we have received Redirect message for the host, the sender must be the previous target. */
+
+ r = sd_ndisc_redirect_get_target_address(existing, &target);
+ if (r < 0)
+ return r;
+
+ if (in6_addr_equal(&sender, &target))
+ return true;
+
+ /* If the existing redirect route is on-link, that is, the destination and target address are
+ * equivalent, then also accept Redirect message from the current default router. This is not
+ * mentioned by the RFC, but without this, we cannot update on-link redirect route. */
+ r = sd_ndisc_redirect_get_destination_address(existing, &dest);
+ if (r < 0)
+ return r;
+
+ if (!in6_addr_equal(&dest, &target))
+ return false;
+ }
+
+ if (!link->ndisc_default_router)
+ return false;
+
+ r = sd_ndisc_router_get_sender_address(link->ndisc_default_router, &router);
+ if (r < 0)
+ return r;
+
+ /* The sender must be the default router. */
+ return in6_addr_equal(&sender, &router);
+}
+
+static int ndisc_redirect_handler(Link *link, sd_ndisc_redirect *rd) {
+ int r;
+
+ assert(link);
+ assert(link->network);
+ assert(rd);
+
+ if (!link->network->ndisc_use_redirect)
+ return 0;
+
+ r = ndisc_redirect_verify_sender(link, rd);
+ if (r <= 0)
+ return r;
+
+ /* First, drop conflicting redirect route, if exists. */
+ r = ndisc_redirect_drop_conflict(link, rd);
+ if (r < 0)
+ return r;
+
+ /* Then, remember the received message. */
+ r = set_ensure_put(&link->ndisc_redirects, &ndisc_redirect_hash_ops, rd);
+ if (r < 0)
+ return r;
+
+ sd_ndisc_redirect_ref(rd);
+
+ /* Finally, request the corresponding route. */
+ _cleanup_(route_unrefp) Route *route = NULL;
+ r = ndisc_redirect_route_new(rd, &route);
+ if (r < 0)
+ return r;
+
+ return ndisc_request_route(route, link);
+}
+
+static int ndisc_drop_redirect(Link *link, const struct in6_addr *router) {
+ int r, ret = 0;
+
+ assert(link);
+
+ sd_ndisc_redirect *rd;
+ SET_FOREACH(rd, link->ndisc_redirects) {
+ if (router) {
+ struct in6_addr target;
+
+ r = sd_ndisc_redirect_get_target_address(rd, &target);
+ if (r < 0)
+ return r;
+
+ if (!in6_addr_equal(&target, router))
+ continue;
+ }
+
+ r = ndisc_remove_redirect_route(link, rd);
+ if (r < 0)
+ RET_GATHER(ret, log_link_warning_errno(link, r, "Failed to remove redirect route, ignoring: %m"));
+
+ sd_ndisc_redirect_unref(set_remove(link->ndisc_redirects, rd));
+ }
+
+ return ret;
+}
+
+static int ndisc_update_redirect_sender(Link *link, const struct in6_addr *original_address, const struct in6_addr *current_address) {
+ int r;
+
+ assert(link);
+ assert(original_address);
+ assert(current_address);
+
+ sd_ndisc_redirect *rd;
+ SET_FOREACH(rd, link->ndisc_redirects) {
+ struct in6_addr sender;
+
+ r = sd_ndisc_redirect_get_sender_address(rd, &sender);
+ if (r < 0)
+ return r;
+
+ if (!in6_addr_equal(&sender, original_address))
+ continue;
+
+ r = sd_ndisc_redirect_set_sender_address(rd, current_address);
+ if (r < 0)
+ return r;
+ }
+
+ return 0;
+}
+
static int ndisc_router_drop_default(Link *link, sd_ndisc_router *rt) {
_cleanup_(route_unrefp) Route *route = NULL;
struct in6_addr gateway;
route->nexthop.gw.in6 = gateway;
route->lifetime_usec = lifetime_usec;
- r = ndisc_request_route(route, link, rt);
+ r = ndisc_request_router_route(route, link, rt);
if (r < 0)
return log_link_warning_errno(link, r, "Could not request default route: %m");
}
route->pref = preference;
route->lifetime_usec = lifetime_usec;
- r = ndisc_request_route(route, link, rt);
+ r = ndisc_request_router_route(route, link, rt);
if (r < 0)
return log_link_warning_errno(link, r, "Could not request gateway: %m");
}
return 0;
}
+static int update_default_router_address(Link *link, const struct in6_addr *original_address, const struct in6_addr *current_address) {
+ struct in6_addr a;
+ int r;
+
+ assert(link);
+ assert(original_address);
+ assert(current_address);
+
+ if (!link->ndisc_default_router)
+ return 0;
+
+ r = sd_ndisc_router_get_sender_address(link->ndisc_default_router, &a);
+ if (r < 0)
+ return r;
+
+ if (!in6_addr_equal(&a, original_address))
+ return 0;
+
+ return sd_ndisc_router_set_sender_address(link->ndisc_default_router, current_address);
+}
+
+static int drop_default_router(Link *link, const struct in6_addr *router, usec_t timestamp_usec) {
+ usec_t lifetime_usec;
+ int r;
+
+ assert(link);
+
+ if (!link->ndisc_default_router)
+ return 0;
+
+ if (router) {
+ struct in6_addr a;
+
+ r = sd_ndisc_router_get_sender_address(link->ndisc_default_router, &a);
+ if (r < 0)
+ return r;
+
+ if (!in6_addr_equal(&a, router))
+ return 0;
+ }
+
+ r = sd_ndisc_router_get_lifetime_timestamp(link->ndisc_default_router, CLOCK_BOOTTIME, &lifetime_usec);
+ if (r < 0)
+ return r;
+
+ if (lifetime_usec > timestamp_usec)
+ return 0;
+
+ link->ndisc_default_router = sd_ndisc_router_unref(link->ndisc_default_router);
+ return 0;
+}
+
+static int accept_default_router(sd_ndisc_router *new_router, sd_ndisc_router *existing_router) {
+ usec_t lifetime_usec;
+ struct in6_addr a, b;
+ uint8_t p, q;
+ int r;
+
+ assert(new_router);
+
+ r = sd_ndisc_router_get_lifetime(new_router, &lifetime_usec);
+ if (r < 0)
+ return r;
+
+ if (lifetime_usec == 0)
+ return false; /* Received a new RA about revoking the router, ignoring. */
+
+ if (!existing_router)
+ return true;
+
+ /* lifetime of the existing router is already checked in ndisc_drop_outdated(). */
+
+ r = sd_ndisc_router_get_sender_address(new_router, &a);
+ if (r < 0)
+ return r;
+
+ r = sd_ndisc_router_get_sender_address(existing_router, &b);
+ if (r < 0)
+ return r;
+
+ if (in6_addr_equal(&a, &b))
+ return true; /* Received a new RA from the remembered router. Replace the remembered RA. */
+
+ r = sd_ndisc_router_get_preference(new_router, &p);
+ if (r < 0)
+ return r;
+
+ r = sd_ndisc_router_get_preference(existing_router, &q);
+ if (r < 0)
+ return r;
+
+ if (p == q)
+ return true;
+
+ if (p == SD_NDISC_PREFERENCE_HIGH)
+ return true;
+
+ if (p == SD_NDISC_PREFERENCE_MEDIUM && q == SD_NDISC_PREFERENCE_LOW)
+ return true;
+
+ return false;
+}
+
+static int ndisc_remember_default_router(Link *link, sd_ndisc_router *rt) {
+ int r;
+
+ assert(link);
+ assert(rt);
+
+ r = accept_default_router(rt, link->ndisc_default_router);
+ if (r <= 0)
+ return r;
+
+ sd_ndisc_router_ref(rt);
+ sd_ndisc_router_unref(link->ndisc_default_router);
+ link->ndisc_default_router = rt;
+
+ return 1; /* The received router advertisement is from the default router. */
+}
+
static int ndisc_router_process_reachable_time(Link *link, sd_ndisc_router *rt) {
usec_t reachable_time, msec;
int r;
return 0;
}
+static int ndisc_router_process_mtu(Link *link, sd_ndisc_router *rt) {
+ uint32_t mtu;
+ int r;
+
+ assert(link);
+ assert(link->network);
+ assert(rt);
+
+ if (!link->network->ndisc_use_mtu)
+ return 0;
+
+ /* Ignore the MTU option if the lifetime is zero. */
+ r = sd_ndisc_router_get_lifetime(rt, NULL);
+ if (r <= 0)
+ return r;
+
+ r = sd_ndisc_router_get_mtu(rt, &mtu);
+ if (r == -ENODATA)
+ return 0;
+ if (r < 0)
+ return log_link_warning_errno(link, r, "Failed to get MTU from RA: %m");
+
+ link->ndisc_mtu = mtu;
+
+ r = link_set_ipv6_mtu(link, LOG_DEBUG);
+ if (r < 0)
+ log_link_warning_errno(link, r, "Failed to apply IPv6 MTU (%"PRIu32"), ignoring: %m", mtu);
+
+ return 0;
+}
+
+static int ndisc_address_set_lifetime(Address *address, Link *link, sd_ndisc_router *rt) {
+ Address *existing;
+ usec_t t;
+ int r;
+
+ assert(address);
+ assert(link);
+ assert(rt);
+
+ /* This is mostly based on RFC 4862 section 5.5.3 (e). However, the definition of 'RemainingLifetime'
+ * is ambiguous, and there is no clear explanation when the address is not assigned yet. If we assume
+ * that 'RemainingLifetime' is zero in that case, then IPv6 Core Conformance test [v6LC.3.2.5 Part C]
+ * fails. So, in such case, we skip the conditions about 'RemainingLifetime'. */
+
+ r = sd_ndisc_router_prefix_get_valid_lifetime_timestamp(rt, CLOCK_BOOTTIME, &address->lifetime_valid_usec);
+ if (r < 0)
+ return r;
+
+ r = sd_ndisc_router_prefix_get_preferred_lifetime_timestamp(rt, CLOCK_BOOTTIME, &address->lifetime_preferred_usec);
+ if (r < 0)
+ return r;
+
+ /* RFC 4862 section 5.5.3 (e)
+ * 1. If the received Valid Lifetime is greater than 2 hours or greater than RemainingLifetime,
+ * set the valid lifetime of the corresponding address to the advertised Valid Lifetime. */
+ r = sd_ndisc_router_prefix_get_valid_lifetime(rt, &t);
+ if (r < 0)
+ return r;
+
+ if (t > 2 * USEC_PER_HOUR)
+ return 0;
+
+ r = sd_ndisc_router_get_timestamp(rt, CLOCK_BOOTTIME, &t);
+ if (r < 0)
+ return r;
+
+ if (address_get(link, address, &existing) >= 0 && existing->source == NETWORK_CONFIG_SOURCE_NDISC) {
+ if (address->lifetime_valid_usec > existing->lifetime_valid_usec)
+ return 0;
+
+ /* 2. If RemainingLifetime is less than or equal to 2 hours, ignore the Prefix Information
+ * option with regards to the valid lifetime, unless the Router Advertisement from which
+ * this option was obtained has been authenticated (e.g., via Secure Neighbor Discovery
+ * [RFC3971]). If the Router Advertisement was authenticated, the valid lifetime of the
+ * corresponding address should be set to the Valid Lifetime in the received option.
+ *
+ * Currently, authentication is not supported. So check the lifetime of the existing address. */
+ if (existing->lifetime_valid_usec <= usec_add(t, 2 * USEC_PER_HOUR)) {
+ address->lifetime_valid_usec = existing->lifetime_valid_usec;
+ return 0;
+ }
+ }
+
+ /* 3. Otherwise, reset the valid lifetime of the corresponding address to 2 hours. */
+ address->lifetime_valid_usec = usec_add(t, 2 * USEC_PER_HOUR);
+ return 0;
+}
+
static int ndisc_router_process_autonomous_prefix(Link *link, sd_ndisc_router *rt) {
usec_t lifetime_valid_usec, lifetime_preferred_usec;
- _cleanup_set_free_ Set *addresses = NULL;
- struct in6_addr prefix, *a;
+ struct in6_addr prefix, router;
uint8_t prefixlen;
int r;
if (!link->network->ndisc_use_autonomous_prefix)
return 0;
+ r = sd_ndisc_router_get_sender_address(rt, &router);
+ if (r < 0)
+ return log_link_warning_errno(link, r, "Failed to get router address: %m");
+
r = sd_ndisc_router_prefix_get_address(rt, &prefix);
if (r < 0)
return log_link_warning_errno(link, r, "Failed to get prefix address: %m");
return 0;
}
- r = sd_ndisc_router_prefix_get_valid_lifetime_timestamp(rt, CLOCK_BOOTTIME, &lifetime_valid_usec);
+ r = sd_ndisc_router_prefix_get_valid_lifetime(rt, &lifetime_valid_usec);
if (r < 0)
return log_link_warning_errno(link, r, "Failed to get prefix valid lifetime: %m");
- r = sd_ndisc_router_prefix_get_preferred_lifetime_timestamp(rt, CLOCK_BOOTTIME, &lifetime_preferred_usec);
+ r = sd_ndisc_router_prefix_get_preferred_lifetime(rt, &lifetime_preferred_usec);
if (r < 0)
return log_link_warning_errno(link, r, "Failed to get prefix preferred lifetime: %m");
- /* The preferred lifetime is never greater than the valid lifetime */
+ /* RFC 4862 section 5.5.3 (c)
+ * If the preferred lifetime is greater than the valid lifetime, silently ignore the Prefix
+ * Information option. */
if (lifetime_preferred_usec > lifetime_valid_usec)
return 0;
- r = ndisc_generate_addresses(link, &prefix, prefixlen, &addresses);
+ _cleanup_hashmap_free_ Hashmap *tokens_by_address = NULL;
+ r = ndisc_generate_addresses(link, &prefix, prefixlen, &tokens_by_address);
if (r < 0)
return log_link_warning_errno(link, r, "Failed to generate SLAAC addresses: %m");
- SET_FOREACH(a, addresses) {
+ IPv6Token *token;
+ struct in6_addr *a;
+ HASHMAP_FOREACH_KEY(token, a, tokens_by_address) {
_cleanup_(address_unrefp) Address *address = NULL;
r = address_new(&address);
if (r < 0)
return log_oom();
+ address->provider.in6 = router;
address->family = AF_INET6;
address->in_addr.in6 = *a;
address->prefixlen = prefixlen;
address->flags = IFA_F_NOPREFIXROUTE|IFA_F_MANAGETEMPADDR;
- address->lifetime_valid_usec = lifetime_valid_usec;
- address->lifetime_preferred_usec = lifetime_preferred_usec;
+ address->token = ipv6_token_ref(token);
- /* draft-ietf-6man-slaac-renum-07 section 4.2
- * https://datatracker.ietf.org/doc/html/draft-ietf-6man-slaac-renum-07#section-4.2
- *
- * If the advertised prefix is equal to the prefix of an address configured by stateless
- * autoconfiguration in the list, the valid lifetime and the preferred lifetime of the
- * address should be updated by processing the Valid Lifetime and the Preferred Lifetime
- * (respectively) in the received advertisement. */
- if (lifetime_valid_usec == 0) {
- r = address_remove_and_cancel(address, link);
- if (r < 0)
- return log_link_warning_errno(link, r, "Could not remove SLAAC address: %m");
- } else {
- r = ndisc_request_address(address, link, rt);
- if (r < 0)
- return log_link_warning_errno(link, r, "Could not request SLAAC address: %m");
- }
+ r = ndisc_address_set_lifetime(address, link, rt);
+ if (r < 0)
+ return log_link_warning_errno(link, r, "Failed to set lifetime of SLAAC address: %m");
+
+ assert(address->lifetime_preferred_usec <= address->lifetime_valid_usec);
+
+ r = ndisc_request_address(address, link);
+ if (r < 0)
+ return log_link_warning_errno(link, r, "Could not request SLAAC address: %m");
}
return 0;
route->pref = preference;
route->lifetime_usec = lifetime_usec;
- r = ndisc_request_route(route, link, rt);
- if (r < 0)
- return log_link_warning_errno(link, r, "Could not request prefix route: %m");
-
- return 0;
-}
-
-static int ndisc_router_drop_onlink_prefix(Link *link, sd_ndisc_router *rt) {
- _cleanup_(route_unrefp) Route *route = NULL;
- uint8_t prefixlen;
- struct in6_addr prefix;
- usec_t lifetime_usec;
- int r;
-
- assert(link);
- assert(link->network);
- assert(rt);
-
- /* RFC 4861 section 6.3.4.
- * Note, however, that a Prefix Information option with the on-link flag set to zero conveys no
- * information concerning on-link determination and MUST NOT be interpreted to mean that addresses
- * covered by the prefix are off-link. The only way to cancel a previous on-link indication is to
- * advertise that prefix with the L-bit set and the Lifetime set to zero. */
-
- if (!link->network->ndisc_use_onlink_prefix)
- return 0;
-
- r = sd_ndisc_router_prefix_get_valid_lifetime(rt, &lifetime_usec);
- if (r < 0)
- return log_link_warning_errno(link, r, "Failed to get prefix lifetime: %m");
-
- if (lifetime_usec != 0)
- return 0;
-
- r = sd_ndisc_router_prefix_get_address(rt, &prefix);
- if (r < 0)
- return log_link_warning_errno(link, r, "Failed to get prefix address: %m");
-
- r = sd_ndisc_router_prefix_get_prefixlen(rt, &prefixlen);
- if (r < 0)
- return log_link_warning_errno(link, r, "Failed to get prefix length: %m");
-
- r = route_new(&route);
- if (r < 0)
- return log_oom();
-
- route->family = AF_INET6;
- route->dst.in6 = prefix;
- route->dst_prefixlen = prefixlen;
-
- r = ndisc_remove_route(route, link);
- if (r < 0)
- return log_link_warning_errno(link, r, "Could not remove prefix route: %m");
+ /* RFC 4861 section 6.3.4:
+ * - If the prefix is not already present in the Prefix List, and the Prefix Information option's
+ * Valid Lifetime field is non-zero, create a new entry for the prefix and initialize its
+ * invalidation timer to the Valid Lifetime value in the Prefix Information option.
+ *
+ * - If the prefix is already present in the host's Prefix List as the result of a previously
+ * received advertisement, reset its invalidation timer to the Valid Lifetime value in the Prefix
+ * Information option. If the new Lifetime value is zero, time-out the prefix immediately. */
+ if (lifetime_usec == 0) {
+ r = ndisc_remove_route(route, link);
+ if (r < 0)
+ return log_link_warning_errno(link, r, "Failed to remove prefix route: %m");
+ } else {
+ r = ndisc_request_router_route(route, link, rt);
+ if (r < 0)
+ return log_link_warning_errno(link, r, "Failed to request prefix route: %m");
+ }
return 0;
}
if (r < 0)
return log_link_warning_errno(link, r, "Failed to get RA prefix flags: %m");
- if (FLAGS_SET(flags, ND_OPT_PI_FLAG_ONLINK))
+ if (FLAGS_SET(flags, ND_OPT_PI_FLAG_ONLINK)) {
r = ndisc_router_process_onlink_prefix(link, rt);
- else
- r = ndisc_router_drop_onlink_prefix(link, rt);
- if (r < 0)
- return r;
+ if (r < 0)
+ return r;
+ }
if (FLAGS_SET(flags, ND_OPT_PI_FLAG_AUTO)) {
r = ndisc_router_process_autonomous_prefix(link, rt);
if (r < 0)
return log_link_warning_errno(link, r, "Failed to get route prefix length: %m");
- if (in6_addr_is_null(&dst) && prefixlen == 0) {
- log_link_debug(link, "Route prefix is ::/0, ignoring");
- return 0;
- }
-
if (in6_prefix_is_filtered(&dst, prefixlen,
link->network->ndisc_allow_listed_route_prefix,
link->network->ndisc_deny_listed_route_prefix)) {
route->dst_prefixlen = prefixlen;
route->lifetime_usec = lifetime_usec;
- r = ndisc_request_route(route, link, rt);
- if (r < 0)
- return log_link_warning_errno(link, r, "Could not request additional route: %m");
+ if (lifetime_usec != 0) {
+ r = ndisc_request_router_route(route, link, rt);
+ if (r < 0)
+ return log_link_warning_errno(link, r, "Could not request additional route: %m");
+ } else {
+ r = ndisc_remove_route(route, link);
+ if (r < 0)
+ return log_link_warning_errno(link, r, "Could not remove additional route with zero lifetime: %m");
+ }
return 0;
}
}
}
-static int ndisc_drop_outdated(Link *link, usec_t timestamp_usec) {
+static int ndisc_drop_outdated(Link *link, const struct in6_addr *router, usec_t timestamp_usec) {
bool updated = false;
NDiscDNSSL *dnssl;
NDiscRDNSS *rdnss;
* valid lifetimes to improve the reaction of SLAAC to renumbering events.
* See draft-ietf-6man-slaac-renum-02, section 4.2. */
+ r = drop_default_router(link, router, timestamp_usec);
+ if (r < 0)
+ RET_GATHER(ret, log_link_warning_errno(link, r, "Failed to drop outdated default router, ignoring: %m"));
+
SET_FOREACH(route, link->manager->routes) {
if (route->source != NETWORK_CONFIG_SOURCE_NDISC)
continue;
if (route->nexthop.ifindex != link->ifindex)
continue;
+ if (route->protocol == RTPROT_REDIRECT)
+ continue; /* redirect route will be dropped by ndisc_drop_redirect(). */
+
if (route->lifetime_usec > timestamp_usec)
continue; /* the route is still valid */
+ if (router && !in6_addr_equal(&route->provider.in6, router))
+ continue;
+
r = route_remove_and_cancel(route, link->manager);
if (r < 0)
RET_GATHER(ret, log_link_warning_errno(link, r, "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;
+
r = address_remove_and_cancel(address, link);
if (r < 0)
RET_GATHER(ret, log_link_warning_errno(link, r, "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;
}
if (p64->lifetime_usec > timestamp_usec)
continue; /* the pref64 prefix is still valid */
+ if (router && !in6_addr_equal(&p64->router, router))
+ continue;
+
free(set_remove(link->ndisc_pref64, p64));
/* The pref64 prefix is not exported through the state file, hence it is not necessary to set
* the 'updated' flag. */
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, /* router = */ NULL, now_usec);
(void) ndisc_setup_expire(link);
return 0;
}
if (r < 0)
return r;
- r = ndisc_drop_outdated(link, timestamp_usec);
+ r = ndisc_drop_outdated(link, /* router = */ NULL, timestamp_usec);
+ if (r < 0)
+ return r;
+
+ r = ndisc_remember_default_router(link, rt);
if (r < 0)
return r;
if (r < 0)
return r;
+ r = ndisc_router_process_mtu(link, rt);
+ if (r < 0)
+ return r;
+
r = ndisc_router_process_options(link, rt);
if (r < 0)
return r;
if (r < 0)
return r;
+ if (sd_ndisc_router_get_lifetime(rt, NULL) <= 0)
+ (void) ndisc_drop_redirect(link, &router);
+
if (link->ndisc_messages == 0)
link->ndisc_configured = true;
else
return 0;
}
+static int ndisc_neighbor_handle_non_router_message(Link *link, sd_ndisc_neighbor *na) {
+ struct in6_addr address;
+ int r;
+
+ assert(link);
+ assert(na);
+
+ /* Received Neighbor Advertisement message without Router flag. The node might have been a router,
+ * and now it is not. Let's drop all configurations based on RAs sent from the node. */
+
+ r = sd_ndisc_neighbor_get_target_address(na, &address);
+ if (r == -ENODATA)
+ return 0;
+ if (r < 0)
+ return r;
+
+ (void) ndisc_drop_outdated(link, /* router = */ &address, /* timestamp_usec = */ USEC_INFINITY);
+ (void) ndisc_drop_redirect(link, &address);
+
+ return 0;
+}
+
+static int ndisc_neighbor_handle_router_message(Link *link, sd_ndisc_neighbor *na) {
+ struct in6_addr current_address, original_address;
+ int r;
+
+ assert(link);
+ assert(link->manager);
+ assert(na);
+
+ /* Received Neighbor Advertisement message with Router flag. If the router address is changed, update
+ * the provider field of configurations. */
+
+ r = sd_ndisc_neighbor_get_sender_address(na, ¤t_address);
+ if (r == -ENODATA)
+ return 0;
+ if (r < 0)
+ return r;
+
+ r = sd_ndisc_neighbor_get_target_address(na, &original_address);
+ if (r == -ENODATA)
+ return 0;
+ if (r < 0)
+ return r;
+
+ if (in6_addr_equal(¤t_address, &original_address))
+ return 0; /* the router address is not changed */
+
+ r = update_default_router_address(link, &original_address, ¤t_address);
+ if (r < 0)
+ return r;
+
+ r = ndisc_update_redirect_sender(link, &original_address, ¤t_address);
+ if (r < 0)
+ return r;
+
+ Route *route;
+ SET_FOREACH(route, link->manager->routes) {
+ if (route->source != NETWORK_CONFIG_SOURCE_NDISC)
+ continue;
+
+ if (route->nexthop.ifindex != link->ifindex)
+ continue;
+
+ if (!in6_addr_equal(&route->provider.in6, &original_address))
+ continue;
+
+ route->provider.in6 = current_address;
+ }
+
+ Address *address;
+ SET_FOREACH(address, link->addresses) {
+ if (address->source != NETWORK_CONFIG_SOURCE_NDISC)
+ continue;
+
+ if (!in6_addr_equal(&address->provider.in6, &original_address))
+ continue;
+
+ address->provider.in6 = current_address;
+ }
+
+ NDiscRDNSS *rdnss;
+ SET_FOREACH(rdnss, link->ndisc_rdnss) {
+ if (!in6_addr_equal(&rdnss->router, &original_address))
+ continue;
+
+ rdnss->router = current_address;
+ }
+
+ NDiscDNSSL *dnssl;
+ SET_FOREACH(dnssl, link->ndisc_dnssl) {
+ if (!in6_addr_equal(&dnssl->router, &original_address))
+ continue;
+
+ dnssl->router = current_address;
+ }
+
+ NDiscCaptivePortal *cp;
+ SET_FOREACH(cp, link->ndisc_captive_portals) {
+ if (!in6_addr_equal(&cp->router, &original_address))
+ continue;
+
+ cp->router = current_address;
+ }
+
+ NDiscPREF64 *p64;
+ SET_FOREACH(p64, link->ndisc_pref64) {
+ if (!in6_addr_equal(&p64->router, &original_address))
+ continue;
+
+ p64->router = current_address;
+ }
+
+ return 0;
+}
+
+static int ndisc_neighbor_handler(Link *link, sd_ndisc_neighbor *na) {
+ int r;
+
+ assert(link);
+ assert(na);
+
+ r = sd_ndisc_neighbor_is_router(na);
+ if (r < 0)
+ return r;
+ if (r == 0)
+ r = ndisc_neighbor_handle_non_router_message(link, na);
+ else
+ r = ndisc_neighbor_handle_router_message(link, na);
+ if (r < 0)
+ return r;
+
+ return 0;
+}
+
static void ndisc_handler(sd_ndisc *nd, sd_ndisc_event_t event, void *message, void *userdata) {
Link *link = ASSERT_PTR(userdata);
int r;
}
break;
+ case SD_NDISC_EVENT_NEIGHBOR:
+ r = ndisc_neighbor_handler(link, ASSERT_PTR(message));
+ if (r < 0 && r != -EBADMSG) {
+ link_enter_failed(link);
+ return;
+ }
+ break;
+
+ case SD_NDISC_EVENT_REDIRECT:
+ r = ndisc_redirect_handler(link, ASSERT_PTR(message));
+ if (r < 0 && r != -EBADMSG) {
+ log_link_warning_errno(link, r, "Failed to process Redirect message: %m");
+ link_enter_failed(link);
+ return;
+ }
+ break;
+
case SD_NDISC_EVENT_TIMEOUT:
log_link_debug(link, "NDisc handler get timeout event");
if (link->ndisc_messages == 0) {
assert(link);
/* Remove all addresses, routes, RDNSS, DNSSL, and Captive Portal entries, without exception. */
- (void) ndisc_drop_outdated(link, /* timestamp_usec = */ USEC_INFINITY);
+ (void) ndisc_drop_outdated(link, /* router = */ NULL, /* timestamp_usec = */ USEC_INFINITY);
+ (void) ndisc_drop_redirect(link, /* router = */ NULL);
link->ndisc_rdnss = set_free(link->ndisc_rdnss);
link->ndisc_dnssl = set_free(link->ndisc_dnssl);
link->ndisc_captive_portals = set_free(link->ndisc_captive_portals);
link->ndisc_pref64 = set_free(link->ndisc_pref64);
+ link->ndisc_redirects = set_free(link->ndisc_redirects);
+ link->ndisc_mtu = 0;
}
static const char* const ndisc_start_dhcp6_client_table[_IPV6_ACCEPT_RA_START_DHCP6_CLIENT_MAX] = {
#include "conf-parser.h"
#include "time-util.h"
+typedef struct Address Address;
typedef struct Link Link;
typedef struct Network Network;
void ndisc_flush(Link *link);
int link_request_ndisc(Link *link);
+int ndisc_reconfigure_address(Address *address, Link *link);
CONFIG_PARSER_PROTOTYPE(config_parse_ndisc_start_dhcp6_client);
CONFIG_PARSER_PROTOTYPE(config_parse_ndisc_use_domains);
DHCPv6.NetLabel, config_parse_string, CONFIG_PARSE_STRING_SAFE, offsetof(Network, dhcp6_netlabel)
DHCPv6.SendRelease, config_parse_bool, 0, offsetof(Network, dhcp6_send_release)
DHCPv6.NFTSet, config_parse_nft_set, NFT_SET_PARSE_NETWORK, offsetof(Network, dhcp6_nft_set_context)
+IPv6AcceptRA.UseRedirect, config_parse_bool, 0, offsetof(Network, ndisc_use_redirect)
IPv6AcceptRA.UseGateway, config_parse_bool, 0, offsetof(Network, ndisc_use_gateway)
IPv6AcceptRA.UseRoutePrefix, config_parse_bool, 0, offsetof(Network, ndisc_use_route_prefix)
IPv6AcceptRA.UseAutonomousPrefix, config_parse_bool, 0, offsetof(Network, ndisc_use_autonomous_prefix)
DHCPServer.BootServerName, config_parse_dns_name, 0, offsetof(Network, dhcp_server_boot_server_name)
DHCPServer.BootFilename, config_parse_string, CONFIG_PARSE_STRING_SAFE_AND_ASCII, offsetof(Network, dhcp_server_boot_filename)
DHCPServer.RapidCommit, config_parse_bool, 0, offsetof(Network, dhcp_server_rapid_commit)
+DHCPServer.PersistLeases, config_parse_tristate, 0, offsetof(Network, dhcp_server_persist_leases)
DHCPServerStaticLease.Address, config_parse_dhcp_static_lease_address, 0, 0
DHCPServerStaticLease.MACAddress, config_parse_dhcp_static_lease_hwaddr, 0, 0
Bridge.Cost, config_parse_uint32, 0, offsetof(Network, cost)
/* SPDX-License-Identifier: LGPL-2.1-or-later */
+/* Make sure the net/if.h header is included before any linux/ one */
#include <net/if.h>
#include <netinet/in.h>
#include <linux/netdevice.h>
.dhcp_use_captive_portal = true,
.dhcp_use_dns = true,
.dhcp_routes_to_dns = true,
+ .dhcp_use_domains = manager->dhcp_use_domains,
.dhcp_use_hostname = true,
.dhcp_use_routes = true,
.dhcp_use_gateway = -1,
.dhcp6_use_address = true,
.dhcp6_use_pd_prefix = true,
.dhcp6_use_dns = true,
+ .dhcp6_use_domains = manager->dhcp6_use_domains,
.dhcp6_use_hostname = true,
.dhcp6_use_ntp = true,
.dhcp6_use_captive_portal = true,
.dhcp_server_emit_router = true,
.dhcp_server_emit_timezone = true,
.dhcp_server_rapid_commit = true,
+ .dhcp_server_persist_leases = -1,
.router_lifetime_usec = RADV_DEFAULT_ROUTER_LIFETIME_USEC,
.router_dns_lifetime_usec = RADV_DEFAULT_VALID_LIFETIME_USEC,
.ipv4_rp_filter = _IP_REVERSE_PATH_FILTER_INVALID,
.ndisc = -1,
+ .ndisc_use_redirect = true,
.ndisc_use_dns = true,
.ndisc_use_gateway = true,
.ndisc_use_captive_portal = true,
char *dhcp_server_boot_filename;
usec_t dhcp_server_ipv6_only_preferred_usec;
bool dhcp_server_rapid_commit;
+ int dhcp_server_persist_leases;
/* link-local addressing support */
AddressFamily link_local;
/* NDisc support */
int ndisc;
+ bool ndisc_use_redirect;
bool ndisc_use_dns;
bool ndisc_use_gateway;
bool ndisc_use_route_prefix;
* Copyright © 2019 VMware, Inc.
*/
+/* Make sure the net/if.h header is included before any linux/ one */
#include <net/if.h>
#include <linux/nexthop.h>
return 0;
HASHMAP_FOREACH(p, link->network->prefixes_by_section) {
- _cleanup_set_free_ Set *addresses = NULL;
- struct in6_addr *a;
-
if (!p->assign)
continue;
if (p->prefixlen > 64)
continue;
- r = radv_generate_addresses(link, p->tokens, &p->prefix, p->prefixlen, &addresses);
+ _cleanup_hashmap_free_ Hashmap *tokens_by_address = NULL;
+ r = radv_generate_addresses(link, p->tokens, &p->prefix, p->prefixlen, &tokens_by_address);
if (r < 0)
return r;
- SET_FOREACH(a, addresses) {
+ IPv6Token *token;
+ struct in6_addr *a;
+ HASHMAP_FOREACH_KEY(token, a, tokens_by_address) {
_cleanup_(address_unrefp) Address *address = NULL;
r = address_new(&address);
address->in_addr.in6 = *a;
address->prefixlen = p->prefixlen;
address->route_metric = p->route_metric;
+ address->token = ipv6_token_ref(token);
r = link_request_static_address(link, address);
if (r < 0)
return 0;
}
+int link_reconfigure_radv_address(Address *address, Link *link) {
+ int r;
+
+ assert(address);
+ assert(address->source == NETWORK_CONFIG_SOURCE_STATIC);
+ assert(link);
+
+ r = regenerate_address(address, link);
+ if (r <= 0)
+ return r;
+
+ r = link_request_static_address(link, address);
+ if (r < 0)
+ return r;
+
+ if (link->static_address_messages != 0) {
+ link->static_addresses_configured = false;
+ link_set_state(link, LINK_STATE_CONFIGURING);
+ }
+
+ return 0;
+}
+
static int radv_set_prefix(Link *link, Prefix *prefix) {
_cleanup_(sd_radv_prefix_unrefp) sd_radv_prefix *p = NULL;
int r;
p->assign = false;
}
- if (p->valid_lifetime == 0)
- return log_warning_errno(SYNTHETIC_ERRNO(EINVAL),
- "%s: The valid lifetime of prefix cannot be zero. "
- "Ignoring [IPv6Prefix] section from line %u.",
- p->section->filename, p->section->line);
-
if (p->preferred_lifetime > p->valid_lifetime)
return log_warning_errno(SYNTHETIC_ERRNO(EINVAL),
"%s: The preferred lifetime %s is longer than the valid lifetime %s. "
"Valid range is 0…128. Ignoring [IPv6RoutePrefix] section from line %u.",
p->section->filename, p->prefixlen, p->section->line);
- if (p->lifetime == 0)
- return log_warning_errno(SYNTHETIC_ERRNO(EINVAL),
- "%s: The lifetime of route cannot be zero. "
- "Ignoring [IPv6RoutePrefix] section from line %u.",
- p->section->filename, p->section->line);
-
return 0;
}
return 0;
}
- (void) in6_addr_mask(&a.in6,prefixlen);
+ (void) in6_addr_mask(&a.in6, prefixlen);
p->prefix = a.in6;
p->prefixlen = prefixlen;
void network_adjust_radv(Network *network);
int link_request_radv_addresses(Link *link);
+int link_reconfigure_radv_address(Address *address, Link *link);
bool link_radv_enabled(Link *link);
int radv_start(Link *link);
/* SPDX-License-Identifier: LGPL-2.1-or-later */
+/* Make sure the net/if.h header is included before any linux/ one */
#include <net/if.h>
#include <linux/fib_rules.h>
}
static int link_set_mtu_handler(sd_netlink *rtnl, sd_netlink_message *m, Request *req, Link *link, void *userdata) {
- int r;
-
- r = set_link_handler_internal(rtnl, m, req, link, /* ignore = */ true, get_link_default_handler);
- if (r <= 0)
- return r;
-
- /* The kernel resets ipv6 mtu after changing device mtu;
- * we must set this here, after we've set device mtu */
- r = link_set_ipv6_mtu(link);
- if (r < 0)
- log_link_warning_errno(link, r, "Failed to set IPv6 MTU, ignoring: %m");
-
- return 0;
+ return set_link_handler_internal(rtnl, m, req, link, /* ignore = */ true, get_link_default_handler);
}
static int link_configure_fill_message(
return true;
}
+static uint32_t link_adjust_mtu(Link *link, uint32_t mtu) {
+ const char *origin;
+ uint32_t min_mtu;
+
+ assert(link);
+ assert(link->network);
+
+ min_mtu = link->min_mtu;
+ origin = "the minimum MTU of the interface";
+ if (link_ipv6_enabled(link)) {
+ /* IPv6 protocol requires a minimum MTU of IPV6_MTU_MIN(1280) bytes on the interface. Bump up
+ * MTU bytes to IPV6_MTU_MIN. */
+ if (min_mtu < IPV6_MIN_MTU) {
+ min_mtu = IPV6_MIN_MTU;
+ origin = "the minimum IPv6 MTU";
+ }
+ if (min_mtu < link->network->ipv6_mtu) {
+ min_mtu = link->network->ipv6_mtu;
+ origin = "the requested IPv6 MTU in IPv6MTUBytes=";
+ }
+ }
+
+ if (mtu < min_mtu) {
+ log_link_warning(link, "Bumping the requested MTU %"PRIu32" to %s (%"PRIu32")",
+ mtu, origin, min_mtu);
+ mtu = min_mtu;
+ }
+
+ if (mtu > link->max_mtu) {
+ log_link_warning(link, "Reducing the requested MTU %"PRIu32" to the interface's maximum MTU %"PRIu32".",
+ mtu, link->max_mtu);
+ mtu = link->max_mtu;
+ }
+
+ return mtu;
+}
+
static int link_is_ready_to_set_link(Link *link, Request *req) {
int r;
}))
return false;
- /* Changing FD mode may affect MTU. */
+ /* Changing FD mode may affect MTU.
+ * See https://docs.kernel.org/networking/can.html#can-fd-flexible-data-rate-driver-support
+ * MTU = 16 (CAN_MTU) => Classical CAN device
+ * MTU = 72 (CANFD_MTU) => CAN FD capable device */
if (ordered_set_contains(link->manager->request_queue,
&(const Request) {
.link = link,
.type = REQUEST_TYPE_SET_LINK_CAN,
}))
return false;
+
+ /* Now, it is ready to set MTU, but before setting, adjust requested MTU. */
+ uint32_t mtu = link_adjust_mtu(link, PTR_TO_UINT32(req->userdata));
+ if (mtu == link->mtu)
+ return -EALREADY; /* Not necessary to set the same value. */
+
+ req->userdata = UINT32_TO_PTR(mtu);
+ return true;
}
default:
break;
}
int link_request_to_set_mtu(Link *link, uint32_t mtu) {
- const char *origin;
- uint32_t min_mtu, max_mtu;
Request *req;
int r;
assert(link);
- assert(link->network);
-
- min_mtu = link->min_mtu;
- origin = "the minimum MTU of the interface";
- if (link_ipv6_enabled(link)) {
- /* IPv6 protocol requires a minimum MTU of IPV6_MTU_MIN(1280) bytes on the interface. Bump up
- * MTU bytes to IPV6_MTU_MIN. */
- if (min_mtu < IPV6_MIN_MTU) {
- min_mtu = IPV6_MIN_MTU;
- origin = "the minimum IPv6 MTU";
- }
- if (min_mtu < link->network->ipv6_mtu) {
- min_mtu = link->network->ipv6_mtu;
- origin = "the requested IPv6 MTU in IPv6MTUBytes=";
- }
- }
-
- if (mtu < min_mtu) {
- log_link_warning(link, "Bumping the requested MTU %"PRIu32" to %s (%"PRIu32")",
- mtu, origin, min_mtu);
- mtu = min_mtu;
- }
-
- max_mtu = link->max_mtu;
- if (link->iftype == ARPHRD_CAN)
- /* The maximum MTU may be changed when FD mode is changed.
- * See https://docs.kernel.org/networking/can.html#can-fd-flexible-data-rate-driver-support
- * MTU = 16 (CAN_MTU) => Classical CAN device
- * MTU = 72 (CANFD_MTU) => CAN FD capable device
- * So, even if the current maximum is 16, we should not reduce the requested value now. */
- max_mtu = MAX(max_mtu, 72u);
-
- if (mtu > max_mtu) {
- log_link_warning(link, "Reducing the requested MTU %"PRIu32" to the interface's maximum MTU %"PRIu32".",
- mtu, max_mtu);
- mtu = max_mtu;
- }
- if (link->mtu == mtu)
+ if (mtu == 0)
return 0;
r = link_request_set_link(link, REQUEST_TYPE_SET_LINK_MTU,
NDiscRDNSS *a;
SET_FOREACH(a, link->ndisc_rdnss) {
- r = ordered_set_put_in6_addrv(s, &a->router, 1);
+ r = ordered_set_put_in6_addrv(s, &a->address, 1);
if (r < 0)
return r;
}
assert(link->network);
assert(s);
- if (link->dhcp_lease && link->network->dhcp_use_ntp) {
+ if (link->dhcp_lease && link->network->dhcp_use_sip) {
const struct in_addr *addresses;
r = sd_dhcp_lease_get_sip(link->dhcp_lease, &addresses);
return sysctl_write_ip_property_boolean(AF_INET6, link->ifname, "proxy_ndp", v);
}
-int link_set_ipv6_mtu(Link *link) {
- uint32_t mtu;
+int link_set_ipv6_mtu(Link *link, int log_level) {
+ uint32_t mtu = 0;
assert(link);
if (!link_is_configured_for_family(link, AF_INET6))
return 0;
- if (link->network->ipv6_mtu == 0)
+ assert(link->network);
+
+ if (link->network->ndisc_use_mtu)
+ mtu = link->ndisc_mtu;
+ if (mtu == 0)
+ mtu = link->network->ipv6_mtu;
+ if (mtu == 0)
return 0;
- mtu = link->network->ipv6_mtu;
- if (mtu > link->max_mtu) {
- log_link_warning(link, "Reducing requested IPv6 MTU %"PRIu32" to the interface's maximum MTU %"PRIu32".",
- mtu, link->max_mtu);
- mtu = link->max_mtu;
+ if (mtu > link->mtu) {
+ log_link_full(link, log_level,
+ "Reducing requested IPv6 MTU %"PRIu32" to the interface's maximum MTU %"PRIu32".",
+ mtu, link->mtu);
+ mtu = link->mtu;
}
return sysctl_write_ip_property_uint32(AF_INET6, link->ifname, "mtu", mtu);
if (r < 0)
log_link_warning_errno(link, r, "Cannot set IPv6 proxy NDP, ignoring: %m");
- r = link_set_ipv6_mtu(link);
+ r = link_set_ipv6_mtu(link, LOG_INFO);
if (r < 0)
log_link_warning_errno(link, r, "Cannot set IPv6 MTU, ignoring: %m");
int link_get_ip_forwarding(Link *link, int family);
int link_set_sysctl(Link *link);
-int link_set_ipv6_mtu(Link *link);
+int link_set_ipv6_mtu(Link *link, int log_level);
const char* ipv6_privacy_extensions_to_string(IPv6PrivacyExtensions i) _const_;
IPv6PrivacyExtensions ipv6_privacy_extensions_from_string(const char *s) _pure_;
[DHCPv4]
#DUIDType=vendor
#DUIDRawData=
+#UseDomains=no
[DHCPv6]
#DUIDType=vendor
#DUIDRawData=
+#UseDomains=no
+
+[DHCPServer]
+#PersistLeases=yes
link = ASSERT_PTR(qdisc->link);
+ qdisc_mark(qdisc); /* To avoid stack overflow. */
+
/* also drop all child classes assigned to the qdisc. */
SET_FOREACH(tclass, link->tclasses) {
+ if (tclass_is_marked(tclass))
+ continue;
+
if (TC_H_MAJ(tclass->classid) != qdisc->handle)
continue;
tclass_drop(tclass);
}
+ qdisc_unmark(qdisc);
qdisc_enter_removed(qdisc);
if (qdisc->state == 0) {
link = ASSERT_PTR(tclass->link);
+ tclass_mark(tclass); /* To avoid stack overflow. */
+
/* Also drop all child qdiscs assigned to the class. */
SET_FOREACH(qdisc, link->qdiscs) {
+ if (qdisc_is_marked(qdisc))
+ continue;
+
if (qdisc->parent != tclass->classid)
continue;
qdisc_drop(qdisc);
}
+ tclass_unmark(tclass);
tclass_enter_removed(tclass);
if (tclass->state == 0) {
/* SPDX-License-Identifier: LGPL-2.1-or-later */
+/* Make sure the net/if.h header is included before any linux/ one */
+#include <net/if.h>
+#include <linux/if.h>
+
#include "bond.h"
#include "dhcp6-internal.h"
#include "dhcp6-protocol.h"
#include "mountpoint-util.h"
#include "nspawn-cgroup.h"
#include "nspawn-mount.h"
+#include "nsresource.h"
#include "path-util.h"
#include "rm-rf.h"
#include "string-util.h"
return 0;
}
-int chown_cgroup(pid_t pid, CGroupUnified unified_requested, uid_t uid_shift) {
- _cleanup_free_ char *path = NULL, *fs = NULL;
- int r;
-
- r = cg_pid_get_path(NULL, pid, &path);
- if (r < 0)
- return log_error_errno(r, "Failed to get container cgroup path: %m");
-
- r = cg_get_path(SYSTEMD_CGROUP_CONTROLLER, path, NULL, &fs);
- if (r < 0)
- return log_error_errno(r, "Failed to get file system path for container cgroup: %m");
-
- r = chown_cgroup_path(fs, uid_shift);
- if (r < 0)
- return log_error_errno(r, "Failed to chown() cgroup %s: %m", fs);
-
- if (unified_requested == CGROUP_UNIFIED_SYSTEMD || (unified_requested == CGROUP_UNIFIED_NONE && cg_unified_controller(SYSTEMD_CGROUP_CONTROLLER) > 0)) {
- _cleanup_free_ char *lfs = NULL;
- /* Always propagate access rights from unified to legacy controller */
-
- r = cg_get_path(SYSTEMD_CGROUP_CONTROLLER_LEGACY, path, NULL, &lfs);
- if (r < 0)
- return log_error_errno(r, "Failed to get file system path for container cgroup: %m");
-
- r = chown_cgroup_path(lfs, uid_shift);
- if (r < 0)
- return log_error_errno(r, "Failed to chown() cgroup %s: %m", lfs);
- }
-
- return 0;
-}
-
int sync_cgroup(pid_t pid, CGroupUnified unified_requested, uid_t uid_shift) {
_cleanup_free_ char *cgroup = NULL;
char tree[] = "/tmp/unifiedXXXXXX", pid_string[DECIMAL_STR_MAX(pid) + 1];
return r;
}
-int create_subcgroup(pid_t pid, bool keep_unit, CGroupUnified unified_requested) {
+int create_subcgroup(
+ pid_t pid,
+ bool keep_unit,
+ CGroupUnified unified_requested,
+ uid_t uid_shift,
+ int userns_fd,
+ bool privileged) {
+
_cleanup_free_ char *cgroup = NULL, *payload = NULL;
CGroupMask supported;
char *e;
if (!payload)
return log_oom();
- r = cg_create_and_attach(SYSTEMD_CGROUP_CONTROLLER, payload, pid);
+ if (privileged)
+ r = cg_create_and_attach(SYSTEMD_CGROUP_CONTROLLER, payload, pid);
+ else
+ r = cg_create(SYSTEMD_CGROUP_CONTROLLER, payload);
if (r < 0)
return log_error_errno(r, "Failed to create %s subcgroup: %m", payload);
+ if (privileged) {
+ _cleanup_free_ char *fs = NULL;
+ r = cg_get_path(SYSTEMD_CGROUP_CONTROLLER, payload, NULL, &fs);
+ if (r < 0)
+ return log_error_errno(r, "Failed to get file system path for container cgroup: %m");
+
+ r = chown_cgroup_path(fs, uid_shift);
+ if (r < 0)
+ return log_error_errno(r, "Failed to chown() cgroup %s: %m", fs);
+
+ } else if (userns_fd >= 0) {
+ _cleanup_close_ int cgroup_fd = -EBADF;
+
+ cgroup_fd = cg_path_open(SYSTEMD_CGROUP_CONTROLLER, payload);
+ if (cgroup_fd < 0)
+ return log_error_errno(cgroup_fd, "Failed to open cgroup %s: %m", payload);
+
+ r = cg_fd_attach(cgroup_fd, pid);
+ if (r < 0)
+ return log_error_errno(r, "Failed to add process " PID_FMT " to cgroup %s: %m", pid, payload);
+
+ r = nsresource_add_cgroup(userns_fd, cgroup_fd);
+ if (r < 0)
+ return log_error_errno(r, "Failed to add cgroup %s to userns: %m", payload);
+ }
+
+ if (unified_requested == CGROUP_UNIFIED_SYSTEMD || (unified_requested == CGROUP_UNIFIED_NONE && cg_unified_controller(SYSTEMD_CGROUP_CONTROLLER) > 0)) {
+ _cleanup_free_ char *lfs = NULL;
+ /* Always propagate access rights from unified to legacy controller */
+
+ r = cg_get_path(SYSTEMD_CGROUP_CONTROLLER_LEGACY, payload, NULL, &lfs);
+ if (r < 0)
+ return log_error_errno(r, "Failed to get file system path for container cgroup: %m");
+
+ r = chown_cgroup_path(lfs, uid_shift);
+ if (r < 0)
+ return log_error_errno(r, "Failed to chown() cgroup %s: %m", lfs);
+ }
+
if (keep_unit) {
_cleanup_free_ char *supervisor = NULL;
-
supervisor = path_join(cgroup, "supervisor");
if (!supervisor)
return log_oom();
#include "cgroup-util.h"
-int chown_cgroup(pid_t pid, CGroupUnified unified_requested, uid_t uid_shift);
int sync_cgroup(pid_t pid, CGroupUnified unified_requested, uid_t uid_shift);
-int create_subcgroup(pid_t pid, bool keep_unit, CGroupUnified unified_requested);
+int create_subcgroup(pid_t pid, bool keep_unit, CGroupUnified unified_requested, uid_t uid_shift, int userns_fd, bool privileged);
int mount_cgroups(const char *dest, CGroupUnified unified_requested, bool userns, uid_t uid_shift, uid_t uid_range, const char *selinux_apifs_context, bool use_cgns);
int mount_systemd_cgroup_writable(const char *dest, CGroupUnified unified_requested);
}
int mount_sysfs(const char *dest, MountSettingsMask mount_settings) {
- const char *full, *top;
- int r;
+ _cleanup_free_ char *top = NULL, *full = NULL;;
unsigned long extra_flags = 0;
+ int r;
- top = prefix_roota(dest, "/sys");
- r = path_is_fs_type(top, SYSFS_MAGIC);
+ top = path_join(dest, "/sys");
+ if (!top)
+ return log_oom();
+
+ r = path_is_mount_point(top);
if (r < 0)
- return log_error_errno(r, "Failed to determine filesystem type of %s: %m", top);
- /* /sys might already be mounted as sysfs by the outer child in the
- * !netns case. In this case, it's all good. Don't touch it because we
- * don't have the right to do so, see https://github.com/systemd/systemd/issues/1555.
- */
- if (r > 0)
- return 0;
+ return log_error_errno(r, "Failed to determine if '%s' is a mountpoint: %m", top);
+ if (r == 0) {
+ /* If this is not a mount point yet, then mount a tmpfs there */
+ r = mount_nofollow_verbose(LOG_ERR, "tmpfs", top, "tmpfs", MS_NOSUID|MS_NOEXEC|MS_NODEV, "mode=0555" TMPFS_LIMITS_SYS);
+ if (r < 0)
+ return r;
+ } else {
+ r = path_is_fs_type(top, SYSFS_MAGIC);
+ if (r < 0)
+ return log_error_errno(r, "Failed to determine filesystem type of %s: %m", top);
+
+ /* /sys/ might already be mounted as sysfs by the outer child in the !netns case. In this case, it's
+ * all good. Don't touch it because we don't have the right to do so, see
+ * https://github.com/systemd/systemd/issues/1555.
+ */
+ if (r > 0)
+ return 0;
+ }
- full = prefix_roota(top, "/full");
+ full = path_join(top, "/full");
+ if (!full)
+ return log_oom();
(void) mkdir(full, 0755);
if (rmdir(full) < 0)
return log_error_errno(errno, "Failed to remove %s: %m", full);
- /* Create mountpoint for cgroups. Otherwise we are not allowed since we
- * remount /sys read-only.
- */
- const char *x = prefix_roota(top, "/fs/cgroup");
+ /* Create mountpoint for cgroups. Otherwise we are not allowed since we remount /sys/ read-only. */
+ _cleanup_free_ char *x = path_join(top, "/fs/cgroup");
+ if (!x)
+ return log_oom();
+
(void) mkdir_p(x, 0755);
return mount_nofollow_verbose(LOG_ERR, NULL, top, NULL,
} MountPoint;
static const MountPoint mount_table[] = {
- /* First we list inner child mounts (i.e. mounts applied *after* entering user namespacing) */
+ /* First we list inner child mounts (i.e. mounts applied *after* entering user namespacing when we are privileged) */
{ "proc", "/proc", "proc", NULL, PROC_DEFAULT_MOUNT_FLAGS,
MOUNT_FATAL|MOUNT_IN_USERNS|MOUNT_MKDIR|MOUNT_FOLLOW_SYMLINKS }, /* we follow symlinks here since not following them requires /proc/ already being mounted, which we don't have here. */
{ "mqueue", "/dev/mqueue", "mqueue", NULL, MS_NOSUID|MS_NOEXEC|MS_NODEV,
MOUNT_IN_USERNS|MOUNT_MKDIR },
- /* Then we list outer child mounts (i.e. mounts applied *before* entering user namespacing) */
+ /* Then we list outer child mounts (i.e. mounts applied *before* entering user namespacing when we are privileged) */
{ "tmpfs", "/tmp", "tmpfs", "mode=01777" NESTED_TMPFS_LIMITS, MS_NOSUID|MS_NODEV|MS_STRICTATIME,
MOUNT_FATAL|MOUNT_APPLY_TMPFS_TMP|MOUNT_MKDIR },
{ "tmpfs", "/sys", "tmpfs", "mode=0555" TMPFS_LIMITS_SYS, MS_NOSUID|MS_NOEXEC|MS_NODEV,
- MOUNT_FATAL|MOUNT_APPLY_APIVFS_NETNS|MOUNT_MKDIR },
+ MOUNT_FATAL|MOUNT_APPLY_APIVFS_NETNS|MOUNT_MKDIR|MOUNT_PRIVILEGED },
{ "sysfs", "/sys", "sysfs", NULL, SYS_DEFAULT_MOUNT_FLAGS,
- MOUNT_FATAL|MOUNT_APPLY_APIVFS_RO|MOUNT_MKDIR }, /* skipped if above was mounted */
+ MOUNT_FATAL|MOUNT_APPLY_APIVFS_RO|MOUNT_MKDIR|MOUNT_PRIVILEGED }, /* skipped if above was mounted */
{ "sysfs", "/sys", "sysfs", NULL, MS_NOSUID|MS_NOEXEC|MS_NODEV,
- MOUNT_FATAL|MOUNT_MKDIR }, /* skipped if above was mounted */
+ MOUNT_FATAL|MOUNT_MKDIR|MOUNT_PRIVILEGED }, /* skipped if above was mounted */
{ "tmpfs", "/dev", "tmpfs", "mode=0755" TMPFS_LIMITS_PRIVATE_DEV, MS_NOSUID|MS_STRICTATIME,
MOUNT_FATAL|MOUNT_MKDIR },
{ "tmpfs", "/dev/shm", "tmpfs", "mode=01777" NESTED_TMPFS_LIMITS, MS_NOSUID|MS_NODEV|MS_STRICTATIME,
MOUNT_FATAL|MOUNT_IN_USERNS },
#if HAVE_SELINUX
{ "/sys/fs/selinux", "/sys/fs/selinux", NULL, NULL, MS_BIND,
- MOUNT_MKDIR }, /* Bind mount first (mkdir/chown the mount point in case /sys/ is mounted as minimal skeleton tmpfs) */
+ MOUNT_MKDIR|MOUNT_PRIVILEGED }, /* Bind mount first (mkdir/chown the mount point in case /sys/ is mounted as minimal skeleton tmpfs) */
{ NULL, "/sys/fs/selinux", NULL, NULL, MS_BIND|MS_RDONLY|MS_NOSUID|MS_NOEXEC|MS_NODEV|MS_REMOUNT,
- 0 }, /* Then, make it r/o (don't mkdir/chown the mount point here, the previous entry already did that) */
+ MOUNT_PRIVILEGED }, /* Then, make it r/o (don't mkdir/chown the mount point here, the previous entry already did that) */
{ NULL, "/sys/fs/selinux", NULL, NULL, MS_PRIVATE,
- 0 }, /* Turn off propagation (we only want that for the mount propagation tunnel dir) */
+ MOUNT_PRIVILEGED }, /* Turn off propagation (we only want that for the mount propagation tunnel dir) */
#endif
};
bool ro = FLAGS_SET(mount_settings, MOUNT_APPLY_APIVFS_RO);
bool in_userns = FLAGS_SET(mount_settings, MOUNT_IN_USERNS);
bool tmpfs_tmp = FLAGS_SET(mount_settings, MOUNT_APPLY_TMPFS_TMP);
+ bool privileged = FLAGS_SET(mount_settings, MOUNT_PRIVILEGED);
int r;
for (size_t k = 0; k < ELEMENTSOF(mount_table); k++) {
bool fatal = FLAGS_SET(mount_table[k].mount_settings, MOUNT_FATAL);
const char *o;
+ /* If we are not privileged but the entry is marked as privileged and to be mounted outside the user namespace, then skip it */
+ if (!privileged && FLAGS_SET(mount_table[k].mount_settings, MOUNT_PRIVILEGED) && !FLAGS_SET(mount_table[k].mount_settings, MOUNT_IN_USERNS))
+ continue;
+
if (in_userns != FLAGS_SET(mount_table[k].mount_settings, MOUNT_IN_USERNS))
continue;
MOUNT_TOUCH = 1 << 9, /* if set, touch file to mount over first */
MOUNT_PREFIX_ROOT = 1 << 10,/* if set, prefix the source path with the container's root directory */
MOUNT_FOLLOW_SYMLINKS = 1 << 11,/* if set, we'll follow symlinks for the mount target */
+ MOUNT_PRIVILEGED = 1 << 12,/* if set, we'll only mount this in in the outer child if we are running in privileged mode */
} MountSettingsMask;
typedef enum CustomMountType {
/* SPDX-License-Identifier: LGPL-2.1-or-later */
+/* Make sure the net/if.h header is included before any linux/ one */
#include <net/if.h>
#include <linux/if.h>
#include <linux/nl80211.h>
if (r < 0)
return log_error_errno(r, "Failed to get device %s: %m", name);
- r = sd_device_get_is_initialized(d);
+ r = device_is_processed(d);
if (r < 0)
return log_error_errno(r, "Failed to determine whether interface %s is initialized: %m", name);
if (r == 0)
#include "nspawn-stub-pid1.h"
#include "nspawn-util.h"
#include "nspawn.h"
+#include "nsresource.h"
#include "nulstr-util.h"
#include "os-util.h"
#include "pager.h"
static Architecture arg_architecture = _ARCHITECTURE_INVALID;
static ImagePolicy *arg_image_policy = NULL;
static char *arg_background = NULL;
+static bool arg_privileged = false;
STATIC_DESTRUCTOR_REGISTER(arg_directory, freep);
STATIC_DESTRUCTOR_REGISTER(arg_template, freep);
static int detect_unified_cgroup_hierarchy_from_image(const char *directory) {
int r;
+ if (!arg_privileged) {
+ /* We only support the unified mode when running unprivileged */
+ arg_unified_cgroup_hierarchy = CGROUP_UNIFIED_ALL;
+ return 0;
+ }
+
/* Let's inherit the mode to use from the host system, but let's take into consideration what systemd
* in the image actually supports. */
r = cg_all_unified();
e = getenv("SYSTEMD_NSPAWN_API_VFS_WRITABLE");
if (streq_ptr(e, "network"))
arg_mount_settings |= MOUNT_APPLY_APIVFS_RO|MOUNT_APPLY_APIVFS_NETNS;
-
else if (e) {
r = parse_boolean(e);
if (r < 0)
static int verify_arguments(void) {
int r;
+ SET_FLAG(arg_mount_settings, MOUNT_PRIVILEGED, arg_privileged);
+
+ if (!arg_privileged) {
+ /* machined is not accessible to unpriv clients */
+ if (arg_register) {
+ log_notice("Automatically implying --register=no, since machined is not accessible to unprivileged clients.");
+ arg_register = false;
+ }
+
+ if (!arg_private_network) {
+ log_notice("Automatically implying --private-network, since mounting /sys/ in an unprivileged user namespaces requires network namespacing.");
+ arg_private_network = true;
+ }
+ }
+
if (arg_start_mode == START_PID2 && arg_unified_cgroup_hierarchy == CGROUP_UNIFIED_UNKNOWN) {
/* If we are running the stub init in the container, we don't need to look at what the init
* in the container supports, because we are not using it. Let's immediately pick the right
if ((arg_clone_ns_flags & CLONE_NEWPID) == 0)
return 0;
+ if (!arg_privileged)
+ return 0;
+
r = read_one_line_file("/proc/self/loginuid", &p);
if (r == -ENOENT)
return 0;
const char *p, *q;
int r;
+ if (!arg_privileged) {
+ log_debug("Not digging mount tunnel, because running unprivileged.");
+ return 0;
+ }
+
(void) mkdir_p("/run/systemd/nspawn/", 0755);
(void) mkdir_p("/run/systemd/nspawn/propagate", 0600);
p = strjoina("/run/systemd/nspawn/propagate/", arg_machine);
static int mount_tunnel_open(void) {
int r;
+ if (!arg_privileged) {
+ log_debug("Not opening up mount tunnel, because running unprivileged.");
+ return 0;
+ }
+
r = mount_follow_verbose(LOG_ERR, NULL, NSPAWN_MOUNT_TUNNEL, NULL, MS_SLAVE, NULL);
if (r < 0)
return r;
return r;
if (!arg_network_namespace_path && arg_private_network) {
- r = unshare(CLONE_NEWNET);
+ _cleanup_close_ int netns_fd = -EBADF;
+
+ if (arg_privileged) {
+ if (unshare(CLONE_NEWNET) < 0)
+ return log_error_errno(errno, "Failed to unshare network namespace: %m");
+ }
+
+ netns_fd = namespace_open_by_type(NAMESPACE_NET);
+ if (netns_fd < 0)
+ return log_error_errno(netns_fd, "Failed to open newly allocate network namespace: %m");
+
+ r = send_one_fd(fd_inner_socket, netns_fd, 0);
if (r < 0)
- return log_error_errno(errno, "Failed to unshare network namespace: %m");
+ return log_error_errno(r, "Failed to send network namespace to supervisor: %m");
/* Tell the parent that it can setup network interfaces. */
(void) barrier_place(barrier); /* #3 */
}
- r = mount_sysfs(NULL, arg_mount_settings);
- if (r < 0)
- return r;
+ if (arg_privileged) {
+ r = mount_sysfs(NULL, arg_mount_settings);
+ if (r < 0)
+ return r;
+ }
- /* Wait until we are cgroup-ified, so that we
- * can mount the right cgroup path writable */
+ /* Wait until we are cgroup-ified, so that we can mount the right cgroup path writable */
if (!barrier_place_and_sync(barrier)) /* #4 */
return log_error_errno(SYNTHETIC_ERRNO(ESRCH),
"Parent died too early");
return log_error_errno(errno, "execv(%s) failed: %m", exec_target);
}
-static int setup_notify_child(void) {
+static int setup_notify_child(const void *directory) {
_cleanup_close_ int fd = -EBADF;
- static const union sockaddr_union sa = {
+ _cleanup_free_ char *j = NULL;
+ union sockaddr_union sa = {
.un.sun_family = AF_UNIX,
- .un.sun_path = NSPAWN_NOTIFY_SOCKET_PATH,
};
int r;
if (fd < 0)
return log_error_errno(errno, "Failed to allocate notification socket: %m");
- (void) mkdir_parents(NSPAWN_NOTIFY_SOCKET_PATH, 0755);
+ if (directory) {
+ j = path_join(directory, NSPAWN_NOTIFY_SOCKET_PATH);
+ if (!j)
+ return log_oom();
+ }
+
+ r = sockaddr_un_set_path(&sa.un, j ?: NSPAWN_NOTIFY_SOCKET_PATH);
+ if (r < 0)
+ return log_error_errno(r, "Failed to set AF_UNIX path to %s: %m", j ?: NSPAWN_NOTIFY_SOCKET_PATH);
+
+ (void) mkdir_parents(sa.un.sun_path, 0755);
(void) sockaddr_un_unlink(&sa.un);
WITH_UMASK(0577) { /* only set "w" bit, which is all that's necessary for connecting from the container */
return log_error_errno(errno, "bind(" NSPAWN_NOTIFY_SOCKET_PATH ") failed: %m");
}
- r = userns_lchown(NSPAWN_NOTIFY_SOCKET_PATH, 0, 0);
+ r = userns_lchown(sa.un.sun_path, 0, 0);
if (r < 0)
return log_error_errno(r, "Failed to chown " NSPAWN_NOTIFY_SOCKET_PATH ": %m");
assert(ret);
+ if (!arg_privileged) {
+ log_debug("Not digging socket tunnel, because running unprivileged.");
+ return 0;
+ }
+
_cleanup_free_ char *p = NULL;
p = path_join("/run/systemd/nspawn/unix-export", arg_machine);
if (!p)
int r;
assert(directory);
+
+ if (!arg_privileged)
+ return 0;
+
assert(unix_export_path);
r = make_run_host(directory);
static DissectImageFlags determine_dissect_image_flags(void) {
return
+ DISSECT_IMAGE_GENERIC_ROOT |
+ DISSECT_IMAGE_REQUIRE_ROOT |
+ DISSECT_IMAGE_RELAX_VAR_CHECK |
DISSECT_IMAGE_USR_NO_ROOT |
DISSECT_IMAGE_DISCARD_ON_LOOP |
+ DISSECT_IMAGE_ADD_PARTITION_DEVICES |
+ DISSECT_IMAGE_PIN_PARTITION_DEVICES |
(arg_read_only ? DISSECT_IMAGE_READ_ONLY : DISSECT_IMAGE_FSCK|DISSECT_IMAGE_GROWFS) |
- DISSECT_IMAGE_ALLOW_USERSPACE_VERITY;
+ DISSECT_IMAGE_ALLOW_USERSPACE_VERITY |
+ (arg_console_mode == CONSOLE_INTERACTIVE ? DISSECT_IMAGE_ALLOW_INTERACTIVE_AUTH : 0);
}
static int outer_child(
return r;
}
- /* Mark everything as shared so our mounts get propagated down. This is required to make new bind
- * mounts available in systemd services inside the container that create a new mount namespace. See
- * https://github.com/systemd/systemd/issues/3860 Further submounts (such as /dev) done after this
- * will inherit the shared propagation mode.
- *
- * IMPORTANT: Do not overmount the root directory anymore from now on to enable moving the root
- * directory mount to root later on.
- * https://github.com/systemd/systemd/issues/3847#issuecomment-562735251
- */
- r = mount_switch_root(directory, MS_SHARED);
- if (r < 0)
- return log_error_errno(r, "Failed to move root directory: %m");
+ /* We have different codepaths here for privileged and non-privileged mode. In privileged mode we'll
+ * now switch into the target directory, and then do the final setup from there. If a user namespace
+ * is then allocated for the container, the root mount and everything else will be out of reach for
+ * it. For unprivileged containers we cannot do that however, since we couldn't mount a sysfs and
+ * procfs then anymore, since that only works if there's an unobstructed instance currently
+ * visible. Hence there we do it the other way round: we first allocate a new set set of namespaces
+ * (and fork for it) for which we then mount sysfs/procfs, and only then switch root. */
- /* We finished setting up the rootfs which is a shared mount. The mount tunnel needs to be a
- * dependent mount otherwise we can't MS_MOVE mounts that were propagated from the host into
- * the container. */
- r = mount_tunnel_open();
- if (r < 0)
- return r;
+ if (arg_privileged) {
+ /* Mark everything as shared so our mounts get propagated down. This is required to make new
+ * bind mounts available in systemd services inside the container that create a new mount
+ * namespace. See https://github.com/systemd/systemd/issues/3860 Further submounts (such as
+ * /dev/) done after this will inherit the shared propagation mode.
+ *
+ * IMPORTANT: Do not overmount the root directory anymore from now on to enable moving the root
+ * directory mount to root later on.
+ * https://github.com/systemd/systemd/issues/3847#issuecomment-562735251
+ */
+ r = mount_switch_root(directory, MS_SHARED);
+ if (r < 0)
+ return log_error_errno(r, "Failed to move root directory: %m");
- if (arg_userns_mode != USER_NAMESPACE_NO) {
- /* In order to mount procfs and sysfs in an unprivileged container the kernel
- * requires that a fully visible instance is already present in the target mount
- * namespace. Mount one here so the inner child can mount its own instances. Later
- * we umount the temporary instances created here before we actually exec the
- * payload. Since the rootfs is shared the umount will propagate into the container.
- * Note, the inner child wouldn't be able to unmount the instances on its own since
- * it doesn't own the originating mount namespace. IOW, the outer child needs to do
- * this. */
- r = pin_fully_visible_fs();
+ /* We finished setting up the rootfs which is a shared mount. The mount tunnel needs to be a
+ * dependent mount otherwise we can't MS_MOVE mounts that were propagated from the host into
+ * the container. */
+ r = mount_tunnel_open();
if (r < 0)
return r;
- }
- fd = setup_notify_child();
+ if (arg_userns_mode != USER_NAMESPACE_NO) {
+ /* In order to mount procfs and sysfs in an unprivileged container the kernel
+ * requires that a fully visible instance is already present in the target mount
+ * namespace. Mount one here so the inner child can mount its own instances. Later
+ * we umount the temporary instances created here before we actually exec the
+ * payload. Since the rootfs is shared the umount will propagate into the container.
+ * Note, the inner child wouldn't be able to unmount the instances on its own since
+ * it doesn't own the originating mount namespace. IOW, the outer child needs to do
+ * this. */
+ r = pin_fully_visible_fs();
+ if (r < 0)
+ return r;
+ }
+
+ fd = setup_notify_child(NULL);
+ } else
+ fd = setup_notify_child(directory);
if (fd < 0)
return fd;
pid = raw_clone(SIGCHLD|CLONE_NEWNS|
arg_clone_ns_flags |
- (arg_userns_mode != USER_NAMESPACE_NO ? CLONE_NEWUSER : 0));
+ (arg_userns_mode != USER_NAMESPACE_NO ? CLONE_NEWUSER : 0) |
+ ((arg_private_network && !arg_privileged) ? CLONE_NEWNET : 0));
if (pid < 0)
return log_error_errno(errno, "Failed to fork inner child: %m");
if (pid == 0) {
return log_error_errno(r, "Failed to join network namespace: %m");
}
+ if (!arg_privileged) {
+ /* In unprivileged operation, sysfs + procfs are special, we'll have to mount them
+ * inside the inner namespaces, but before we switch root. Hence do so here. */
+ _cleanup_free_ char *j = path_join(directory, "/proc");
+ if (!j)
+ return log_oom();
+
+ r = mount_follow_verbose(LOG_ERR, "proc", j, "proc", MS_NOSUID|MS_NOEXEC|MS_NODEV, NULL);
+ if (r < 0)
+ return r;
+
+ r = mount_sysfs(directory, arg_mount_settings);
+ if (r < 0)
+ return r;
+
+ r = mount_switch_root(directory, MS_SHARED);
+ if (r < 0)
+ return log_error_errno(r, "Failed to move root directory: %m");
+ }
+
r = inner_child(barrier, fd_inner_socket, fds, os_release_pairs);
if (r < 0)
_exit(EXIT_FAILURE);
static int setup_notify_parent(sd_event *event, int fd, pid_t *inner_child_pid, sd_event_source **notify_event_source) {
int r;
+ if (fd < 0)
+ return 0;
+
r = sd_event_add_io(event, notify_event_source, fd, EPOLLIN, nspawn_dispatch_notify_fd, inner_child_pid);
if (r < 0)
return log_error_errno(r, "Failed to allocate notify event source: %m");
return 0;
/* We first look in the admin's directories in /etc and /run */
- FOREACH_STRING(i, "/etc/systemd/nspawn", "/run/systemd/nspawn") {
- _cleanup_free_ char *j = NULL;
+ if (arg_privileged) {
+ FOREACH_STRING(i, "/etc/systemd/nspawn", "/run/systemd/nspawn") {
+ _cleanup_free_ char *j = NULL;
- j = path_join(i, arg_settings_filename);
- if (!j)
- return log_oom();
+ j = path_join(i, arg_settings_filename);
+ if (!j)
+ return log_oom();
- f = fopen(j, "re");
- if (f) {
- p = TAKE_PTR(j);
+ f = fopen(j, "re");
+ if (f) {
+ p = TAKE_PTR(j);
- /* By default, we trust configuration from /etc and /run */
- if (arg_settings_trusted < 0)
- arg_settings_trusted = true;
+ /* By default, we trust configuration from /etc and /run */
+ if (arg_settings_trusted < 0)
+ arg_settings_trusted = true;
- break;
- }
+ break;
+ }
- if (errno != ENOENT)
- return log_error_errno(errno, "Failed to open %s: %m", j);
+ if (errno != ENOENT)
+ return log_error_errno(errno, "Failed to open %s: %m", j);
+ }
}
if (!f) {
static int run_container(
DissectedImage *dissected_image,
+ int userns_fd,
FDSet *fds,
- char veth_name[IFNAMSIZ], bool *veth_created,
+ char veth_name[IFNAMSIZ],
+ bool *veth_created,
struct ExposeArgs *expose_args,
- int *master, pid_t *pid, int *ret) {
+ int *master,
+ pid_t *pid,
+ int *ret) {
static const struct sigaction sa = {
.sa_handler = nop_signal_handler,
"Path %s doesn't refer to a network namespace, refusing.", arg_network_namespace_path);
}
- *pid = raw_clone(SIGCHLD|CLONE_NEWNS);
- if (*pid < 0)
- return log_error_errno(errno, "clone() failed%s: %m",
- errno == EINVAL ?
- ", do you have namespace support enabled in your kernel? (You need UTS, IPC, PID and NET namespacing built in)" : "");
+ if (arg_privileged) {
+ assert(userns_fd < 0);
+
+ /* If we have no user namespace then we'll clone and create a new mount namespace right-away. */
+
+ *pid = raw_clone(SIGCHLD|CLONE_NEWNS);
+ if (*pid < 0)
+ return log_error_errno(errno, "clone() failed%s: %m",
+ errno == EINVAL ?
+ ", do you have namespace support enabled in your kernel? (You need UTS, IPC, PID and NET namespacing built in)" : "");
+ } else {
+ assert(userns_fd >= 0);
+
+ /* If we have a user namespace then we'll clone() first, and then join the user namespace,
+ * and then open the mount namespace, so that it is owned by the user namespace */
+
+ *pid = raw_clone(SIGCHLD);
+ if (*pid < 0)
+ return log_error_errno(errno, "clone() failed: %m");
+
+ if (*pid == 0) {
+ if (setns(userns_fd, CLONE_NEWUSER) < 0) {
+ log_error_errno(errno, "Failed to join allocate user namespace: %m");
+ _exit(EXIT_FAILURE);
+ }
+
+ r = reset_uid_gid();
+ if (r < 0) {
+ log_error_errno(r, "Failed to reset UID/GID to root: %m");
+ _exit(EXIT_FAILURE);
+ }
+
+ if (unshare(CLONE_NEWNS) < 0) {
+ log_error_errno(errno, "Failed to unshare file system namespace: %m");
+ _exit(EXIT_FAILURE);
+ }
+ }
+ }
if (*pid == 0) {
/* The outer child only has a file system namespace. */
/* Wait until the child has unshared its network namespace. */
if (!barrier_place_and_sync(&barrier)) /* #3 */
return log_error_errno(SYNTHETIC_ERRNO(ESRCH), "Child died too early");
- }
- if (child_netns_fd < 0) {
- /* Make sure we have an open file descriptor to the child's network
- * namespace so it stays alive even if the child exits. */
- r = namespace_open(*pid,
- /* ret_pidns_fd = */ NULL,
- /* ret_mntns_fd = */ NULL,
- &child_netns_fd,
- /* ret_userns_fd = */ NULL,
- /* ret_root_fd = */ NULL);
- if (r < 0)
- return log_error_errno(r, "Failed to open child network namespace: %m");
+ /* Make sure we have an open file descriptor to the child's network namespace so it
+ * stays alive even if the child exits. */
+ assert(child_netns_fd < 0);
+ child_netns_fd = receive_one_fd(fd_inner_socket_pair[0], 0);
+ if (child_netns_fd < 0)
+ return log_error_errno(r, "Failed to receive child network namespace: %m");
}
r = move_network_interfaces(child_netns_fd, arg_network_interfaces);
return r;
if (arg_network_veth) {
- r = setup_veth(arg_machine, *pid, veth_name,
- arg_network_bridge || arg_network_zone, &arg_network_provided_mac);
- if (r < 0)
- return r;
- else if (r > 0)
- ifi = r;
+ if (arg_privileged) {
+ r = setup_veth(arg_machine, *pid, veth_name,
+ arg_network_bridge || arg_network_zone, &arg_network_provided_mac);
+ if (r < 0)
+ return r;
+ else if (r > 0)
+ ifi = r;
+ } else {
+ _cleanup_free_ char *host_ifname = NULL;
+
+ r = nsresource_add_netif(userns_fd, child_netns_fd, /* namespace_ifname= */ NULL, &host_ifname, /* ret_namespace_ifname= */ NULL);
+ if (r < 0)
+ return log_error_errno(r, "Failed to add network interface to container: %m");
+
+ ifi = if_nametoindex(host_ifname);
+ if (ifi == 0)
+ return log_error_errno(errno, "Failed to resolve interface '%s': %m", host_ifname);
+
+ if (strlen(host_ifname) >= IFNAMSIZ)
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Host interface name too long?");
+
+ strcpy(veth_name, host_ifname);
+ }
if (arg_network_bridge) {
/* Add the interface to a bridge */
}
if (arg_register || !arg_keep_unit) {
- r = sd_bus_default_system(&bus);
+ if (arg_privileged)
+ r = sd_bus_default_system(&bus);
+ else
+ r = sd_bus_default_user(&bus);
if (r < 0)
- return log_error_errno(r, "Failed to open system bus: %m");
+ return log_error_errno(r, "Failed to open bus: %m");
r = sd_bus_set_close_on_exit(bus, false);
if (r < 0)
} else if (arg_slice || arg_property)
log_notice("Machine and scope registration turned off, --slice= and --property= settings will have no effect.");
- r = create_subcgroup(*pid, arg_keep_unit, arg_unified_cgroup_hierarchy);
+ r = create_subcgroup(
+ *pid,
+ arg_keep_unit,
+ arg_unified_cgroup_hierarchy,
+ arg_uid_shift,
+ userns_fd,
+ arg_privileged);
if (r < 0)
return r;
if (r < 0)
return r;
- r = chown_cgroup(*pid, arg_unified_cgroup_hierarchy, arg_uid_shift);
- if (r < 0)
- return r;
-
- /* Notify the child that the parent is ready with all
- * its setup (including cgroup-ification), and that
- * the child can now hand over control to the code to
- * run inside the container. */
+ /* Notify the child that the parent is ready with all its setup (including cgroup-ification), and
+ * that the child can now hand over control to the code to run inside the container. */
(void) barrier_place(&barrier); /* #4 */
/* Block SIGCHLD here, before notifying child.
fd_kmsg_fifo = safe_close(fd_kmsg_fifo);
- if (arg_private_network) {
+ if (arg_private_network && arg_privileged) {
r = move_back_network_interfaces(child_netns_fd, arg_network_interfaces);
if (r < 0)
return r;
if (r == -ENOENT || ERRNO_IS_NEG_DISCONNECT(r))
return log_error_errno(SYNTHETIC_ERRNO(EOPNOTSUPP),
"Sorry, but --image= requires access to the host's /run/ hierarchy, since we need access to udev.");
+ if (ERRNO_IS_NEG_PRIVILEGE(r)) {
+ log_debug_errno(r, "Can't connect to udev control socket, assuming we are in same netns.");
+ return 0;
+ }
if (r < 0)
return log_error_errno(r, "Failed to connect socket to udev control socket: %m");
static int run(int argc, char *argv[]) {
bool remove_directory = false, remove_image = false, veth_created = false, remove_tmprootdir = false;
- _cleanup_close_ int master = -EBADF;
+ _cleanup_close_ int master = -EBADF, userns_fd = -EBADF;
_cleanup_fdset_free_ FDSet *fds = NULL;
int r, n_fd_passed, ret = EXIT_SUCCESS;
char veth_name[IFNAMSIZ] = "";
log_parse_environment();
log_open();
+ arg_privileged = getuid() == 0;
+
r = parse_argv(argc, argv);
if (r <= 0)
goto finish;
- if (geteuid() != 0) {
- r = log_warning_errno(SYNTHETIC_ERRNO(EPERM),
- argc >= 2 ? "Need to be root." :
- "Need to be root (and some arguments are usually required).\nHint: try --help");
- goto finish;
- }
-
r = cant_be_in_netns();
if (r < 0)
goto finish;
if (!arg_private_network && arg_userns_mode != USER_NAMESPACE_NO && arg_uid_shift > 0)
arg_caps_retain &= ~(UINT64_C(1) << CAP_NET_BIND_SERVICE);
- r = cg_unified();
+ r = cg_unified(); /* initialize cache early */
if (r < 0) {
log_error_errno(r, "Failed to determine whether the unified cgroups hierarchy is used: %m");
goto finish;
/* Reapply environment settings. */
(void) detect_unified_cgroup_hierarchy_from_environment();
+ if (!arg_privileged) {
+ r = cg_all_unified();
+ if (r < 0) {
+ log_error_errno(r, "Failed to determine if we are in unified cgroupv2 mode: %m");
+ goto finish;
+ }
+ if (r == 0)
+ return log_error_errno(SYNTHETIC_ERRNO(EOPNOTSUPP), "Unprivileged operation only supported in unified cgroupv2 mode.");
+ }
+
/* Ignore SIGPIPE here, because we use splice() on the ptyfwd stuff and that will generate SIGPIPE if
* the result is closed. Note that the container payload child will reset signal mask+handler anyway,
* so just turning this off here means we only turn it off in nspawn itself, not any children. */
* the child. Functions like copy_devnodes() change the umask temporarily. */
umask(0022);
+ if (arg_console_mode < 0)
+ arg_console_mode = isatty(STDIN_FILENO) && isatty(STDOUT_FILENO) ?
+ CONSOLE_INTERACTIVE : CONSOLE_READ_ONLY;
+
+ if (arg_console_mode == CONSOLE_PIPE) /* if we pass STDERR on to the container, don't add our own logs into it too */
+ arg_quiet = true;
+
if (arg_directory) {
assert(!arg_image);
+ if (!arg_privileged) {
+ r = log_error_errno(SYNTHETIC_ERRNO(EOPNOTSUPP), "Invoking container from plain directory tree is currently not supported if called without privileges.");
+ goto finish;
+ }
+
/* Safety precaution: let's not allow running images from the live host OS image, as long as
* /var from the host will propagate into container dynamically (because bad things happen if
* two systems write to the same /var). Let's allow it for the special cases where /var is
/* We take an exclusive lock on this image, since it's our private, ephemeral copy
* only owned by us and no one else. */
- r = image_path_lock(np, LOCK_EX|LOCK_NB, &tree_global_lock, &tree_local_lock);
+ r = image_path_lock(
+ np,
+ LOCK_EX|LOCK_NB,
+ arg_privileged ? &tree_global_lock : NULL,
+ &tree_local_lock);
if (r < 0) {
log_error_errno(r, "Failed to lock %s: %m", np);
goto finish;
if (r < 0)
goto finish;
- r = image_path_lock(arg_directory, (arg_read_only ? LOCK_SH : LOCK_EX) | LOCK_NB, &tree_global_lock, &tree_local_lock);
+ r = image_path_lock(
+ arg_directory,
+ (arg_read_only ? LOCK_SH : LOCK_EX) | LOCK_NB,
+ arg_privileged ? &tree_global_lock : NULL,
+ &tree_local_lock);
if (r == -EBUSY) {
log_error_errno(r, "Directory tree %s is currently busy.", arg_directory);
goto finish;
} else {
DissectImageFlags dissect_image_flags =
- DISSECT_IMAGE_GENERIC_ROOT |
- DISSECT_IMAGE_REQUIRE_ROOT |
- DISSECT_IMAGE_RELAX_VAR_CHECK |
- DISSECT_IMAGE_USR_NO_ROOT |
- DISSECT_IMAGE_ADD_PARTITION_DEVICES |
- DISSECT_IMAGE_PIN_PARTITION_DEVICES;
+ determine_dissect_image_flags();
+
assert(arg_image);
assert(!arg_template);
+
r = chase_and_update(&arg_image, 0);
if (r < 0)
goto finish;
}
/* Always take an exclusive lock on our own ephemeral copy. */
- r = image_path_lock(np, LOCK_EX|LOCK_NB, &tree_global_lock, &tree_local_lock);
+ r = image_path_lock(
+ np,
+ LOCK_EX|LOCK_NB,
+ arg_privileged ? &tree_global_lock : NULL,
+ &tree_local_lock);
if (r < 0) {
log_error_errno(r, "Failed to create image lock: %m");
goto finish;
free_and_replace(arg_image, np);
remove_image = true;
} else {
- r = image_path_lock(arg_image, (arg_read_only ? LOCK_SH : LOCK_EX) | LOCK_NB, &tree_global_lock, &tree_local_lock);
+ r = image_path_lock(
+ arg_image,
+ (arg_read_only ? LOCK_SH : LOCK_EX) | LOCK_NB,
+ arg_privileged ? &tree_global_lock : NULL,
+ &tree_local_lock);
if (r == -EBUSY) {
log_error_errno(r, "Disk image %s is currently busy.", arg_image);
goto finish;
goto finish;
}
- r = loop_device_make_by_path(
- arg_image,
- arg_read_only ? O_RDONLY : O_RDWR,
- /* sector_size= */ UINT32_MAX,
- FLAGS_SET(dissect_image_flags, DISSECT_IMAGE_NO_PARTITION_TABLE) ? 0 : LO_FLAGS_PARTSCAN,
- LOCK_SH,
- &loop);
- if (r < 0) {
- log_error_errno(r, "Failed to set up loopback block device: %m");
- goto finish;
- }
+ if (arg_privileged) {
+ r = loop_device_make_by_path(
+ arg_image,
+ arg_read_only ? O_RDONLY : O_RDWR,
+ /* sector_size= */ UINT32_MAX,
+ FLAGS_SET(dissect_image_flags, DISSECT_IMAGE_NO_PARTITION_TABLE) ? 0 : LO_FLAGS_PARTSCAN,
+ LOCK_SH,
+ &loop);
+ if (r < 0) {
+ log_error_errno(r, "Failed to set up loopback block device: %m");
+ goto finish;
+ }
- r = dissect_loop_device_and_warn(
- loop,
- &arg_verity_settings,
- /* mount_options=*/ NULL,
- arg_image_policy ?: &image_policy_container,
- dissect_image_flags,
- &dissected_image);
- if (r == -ENOPKG) {
- /* dissected_image_and_warn() already printed a brief error message. Extend on that with more details */
- log_notice("Note that the disk image needs to\n"
- " a) either contain only a single MBR partition of type 0x83 that is marked bootable\n"
- " b) or contain a single GPT partition of type 0FC63DAF-8483-4772-8E79-3D69D8477DE4\n"
- " c) or follow https://uapi-group.org/specifications/specs/discoverable_partitions_specification\n"
- " d) or contain a file system without a partition table\n"
- "in order to be bootable with systemd-nspawn.");
- goto finish;
- }
- if (r < 0)
- goto finish;
+ r = dissect_loop_device_and_warn(
+ loop,
+ &arg_verity_settings,
+ /* mount_options=*/ NULL,
+ arg_image_policy ?: &image_policy_container,
+ dissect_image_flags,
+ &dissected_image);
+ if (r == -ENOPKG) {
+ /* dissected_image_and_warn() already printed a brief error message. Extend on that with more details */
+ log_notice("Note that the disk image needs to\n"
+ " a) either contain only a single MBR partition of type 0x83 that is marked bootable\n"
+ " b) or contain a single GPT partition of type 0FC63DAF-8483-4772-8E79-3D69D8477DE4\n"
+ " c) or follow https://uapi-group.org/specifications/specs/discoverable_partitions_specification\n"
+ " d) or contain a file system without a partition table\n"
+ "in order to be bootable with systemd-nspawn.");
+ goto finish;
+ }
+ if (r < 0)
+ goto finish;
- r = dissected_image_load_verity_sig_partition(
- dissected_image,
- loop->fd,
- &arg_verity_settings);
- if (r < 0)
- goto finish;
+ r = dissected_image_load_verity_sig_partition(
+ dissected_image,
+ loop->fd,
+ &arg_verity_settings);
+ if (r < 0)
+ goto finish;
- if (dissected_image->has_verity && !arg_verity_settings.root_hash && !dissected_image->has_verity_sig)
- log_notice("Note: image %s contains verity information, but no root hash specified and no embedded "
- "root hash signature found! Proceeding without integrity checking.", arg_image);
+ if (dissected_image->has_verity && !arg_verity_settings.root_hash && !dissected_image->has_verity_sig)
+ log_notice("Note: image %s contains verity information, but no root hash specified and no embedded "
+ "root hash signature found! Proceeding without integrity checking.", arg_image);
- r = dissected_image_decrypt_interactively(
- dissected_image,
- NULL,
- &arg_verity_settings,
- 0);
- if (r < 0)
- goto finish;
+ r = dissected_image_decrypt_interactively(
+ dissected_image,
+ NULL,
+ &arg_verity_settings,
+ dissect_image_flags);
+ if (r < 0)
+ goto finish;
+ } else {
+ _cleanup_free_ char *userns_name = strjoin("nspawn-", arg_machine);
+ if (!userns_name) {
+ r = log_oom();
+ goto finish;
+ }
+
+ /* if we are unprivileged, let's allocate a 64K userns first */
+ userns_fd = nsresource_allocate_userns(userns_name, UINT64_C(0x10000));
+ if (userns_fd < 0) {
+ r = log_error_errno(userns_fd, "Failed to allocate user namespace with 64K users: %m");
+ goto finish;
+ }
+
+ r = mountfsd_mount_image(
+ arg_image,
+ userns_fd,
+ arg_image_policy,
+ dissect_image_flags,
+ &dissected_image);
+ if (r < 0)
+ goto finish;
+ }
/* Now that we mounted the image, let's try to remove it again, if it is ephemeral */
if (remove_image && unlink(arg_image) >= 0)
if (r < 0)
goto finish;
- if (arg_console_mode < 0)
- arg_console_mode = isatty(STDIN_FILENO) && isatty(STDOUT_FILENO) ?
- CONSOLE_INTERACTIVE : CONSOLE_READ_ONLY;
-
- if (arg_console_mode == CONSOLE_PIPE) /* if we pass STDERR on to the container, don't add our own logs into it too */
- arg_quiet = true;
-
if (!arg_quiet) {
const char *t = arg_image ?: arg_directory;
_cleanup_free_ char *u = NULL;
expose_args.fw_ctx = fw_ctx;
}
for (;;) {
- r = run_container(dissected_image,
- fds,
- veth_name, &veth_created,
- &expose_args, &master,
- &pid, &ret);
+ r = run_container(
+ dissected_image,
+ userns_fd,
+ fds,
+ veth_name, &veth_created,
+ &expose_args, &master,
+ &pid, &ret);
if (r <= 0)
break;
}
log_debug_errno(errno, "Can't remove temporary root directory '%s', ignoring: %m", tmprootdir);
}
- if (arg_machine) {
+ if (arg_machine && arg_privileged) {
const char *p;
p = strjoina("/run/systemd/nspawn/propagate/", arg_machine);
expose_port_flush(&fw_ctx, arg_expose_ports, AF_INET, &expose_args.address4);
expose_port_flush(&fw_ctx, arg_expose_ports, AF_INET6, &expose_args.address6);
- if (veth_created)
- (void) remove_veth_links(veth_name, arg_network_veth_extra);
- (void) remove_bridge(arg_network_zone);
+ if (arg_privileged) {
+ if (veth_created)
+ (void) remove_veth_links(veth_name, arg_network_veth_extra);
+ (void) remove_bridge(arg_network_zone);
+ }
custom_mount_free_all(arg_custom_mounts, arg_n_custom_mounts);
expose_port_free_all(arg_expose_ports);
--- /dev/null
+# SPDX-License-Identifier: LGPL-2.1-or-later
+
+if conf.get('HAVE_VMLINUX_H') != 1
+ subdir_done()
+endif
+
+userns_restrict_bpf_o_unstripped = custom_target(
+ 'userns-restrict.bpf.unstripped.o',
+ input : 'userns-restrict.bpf.c',
+ output : 'userns-restrict.bpf.unstripped.o',
+ command : bpf_o_unstripped_cmd,
+ depends : vmlinux_h_dependency)
+
+userns_restrict_bpf_o = custom_target(
+ 'userns-restrict.bpf.o',
+ input : userns_restrict_bpf_o_unstripped,
+ output : 'userns-restrict.bpf.o',
+ command : bpf_o_cmd)
+
+userns_restrict_skel_h = custom_target(
+ 'userns-restrict.skel.h',
+ input : userns_restrict_bpf_o,
+ output : 'userns-restrict.skel.h',
+ command : skel_h_cmd,
+ capture : true)
--- /dev/null
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+
+/* The SPDX header above is actually correct in claiming this was
+ * LGPL-2.1-or-later, because it is. Since the kernel doesn't consider that
+ * compatible with GPL we will claim this to be GPL however, which should be
+ * fine given that LGPL-2.1-or-later downgrades to GPL if needed.
+ */
+
+#include "bpf-dlopen.h"
+
+/* libbpf is used via dlopen(), so rename symbols */
+#define bpf_object__attach_skeleton sym_bpf_object__attach_skeleton
+#define bpf_object__destroy_skeleton sym_bpf_object__destroy_skeleton
+#define bpf_object__load_skeleton sym_bpf_object__load_skeleton
+#define bpf_object__open_skeleton sym_bpf_object__open_skeleton
+
+#include "bpf/userns_restrict/userns-restrict.skel.h"
--- /dev/null
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+
+/* The SPDX header above is actually correct in claiming this was
+ * LGPL-2.1-or-later, because it is. Since the kernel doesn't consider that
+ * compatible with GPL we will claim this to be GPL however, which should be
+ * fine given that LGPL-2.1-or-later downgrades to GPL if needed.
+ */
+
+/* If offsetof() is implemented via __builtin_offset() then it doesn't work on current compilers, since the
+ * built-ins do not understand CO-RE. Let's undefine any such macros here, to force bpf_helpers.h to define
+ * its own definitions for this. (In new versions it will do so automatically, but at least in libbpf 1.1.0
+ * it does not.) */
+#undef offsetof
+#undef container_of
+
+#include "vmlinux.h"
+
+#include <bpf/bpf_helpers.h>
+#include <bpf/bpf_tracing.h>
+#include <bpf/bpf_core_read.h>
+#include <errno.h>
+
+#ifndef bpf_core_cast
+/* bpf_rdonly_cast() was introduced in libbpf commit 688879f together with
+ * the definition of a bpf_core_cast macro. So use that one to avoid
+ * defining a prototype for bpf_rdonly_cast */
+void *bpf_rdonly_cast(void *, __u32) __ksym;
+#endif
+
+/* BPF module that implements an allowlist of mounts (identified by mount ID) for user namespaces (identified
+ * by their inode number in nsfs) that restricts creation of inodes (which would inherit the callers UID/GID)
+ * or changing of ownership (similar).
+ *
+ * This hooks into the various path-based LSM entrypoints that control inode creation as well as chmod(), and
+ * then looks up the calling process' user namespace in a global map of namespaces, which points us to
+ * another map that is simply a list of allowed mnt_ids. */
+
+// FIXME: ACL adjustments are currently not blocked. There's no path-based LSM hook available in the kernel
+// for setting xattrs or ACLs, hence we cannot easily block them, even though we want that. We can get away
+// with ignoring this for now, as ACLs never define ownership, but purely access: i.e. ACLs never allow
+// taking possession of an object, but only control access to it. Thus, things like suid access modes should
+// not be reachable through it. It still sucks though that a user can persistently add an ACL entry to a file
+// with their transient UIDs/GIDs.
+
+/* kernel currently enforces a maximum usernamespace nesting depth of 32, see create_user_ns() in the kernel sources */
+#define USER_NAMESPACE_DEPTH_MAX 32U
+
+struct mnt_id_map {
+ __uint(type, BPF_MAP_TYPE_HASH);
+ __uint(max_entries, 1); /* placeholder, configured otherwise by nsresourced */
+ __type(key, int);
+ __type(value, int);
+};
+
+struct {
+ __uint(type, BPF_MAP_TYPE_HASH_OF_MAPS);
+ __uint(max_entries, 1); /* placeholder, configured otherwise by nsresourced */
+ __type(key, unsigned); /* userns inode */
+ __array(values, struct mnt_id_map);
+} userns_mnt_id_hash SEC(".maps");
+
+struct {
+ __uint(type, BPF_MAP_TYPE_RINGBUF);
+ __uint(max_entries, 4096);
+} userns_ringbuf SEC(".maps");
+
+static inline struct mount *real_mount(struct vfsmount *mnt) {
+ return container_of(mnt, struct mount, mnt);
+}
+
+static int validate_inode_on_mount(struct inode *inode, struct vfsmount *v) {
+ struct user_namespace *mount_userns, *task_userns, *p;
+ unsigned task_userns_inode;
+ struct task_struct *task;
+ void *mnt_id_map;
+ struct mount *m;
+ int mnt_id;
+
+ /* Get user namespace from vfsmount */
+ m = bpf_rdonly_cast(real_mount(v), bpf_core_type_id_kernel(struct mount));
+ mount_userns = m->mnt_ns->user_ns;
+
+ /* Get user namespace from task */
+ task = (struct task_struct*) bpf_get_current_task_btf();
+ task_userns = task->cred->user_ns;
+
+ /* Is the file on a mount that belongs to our own user namespace or a child of it? If so, say
+ * yes immediately. */
+ p = mount_userns;
+ for (unsigned i = 0; i < USER_NAMESPACE_DEPTH_MAX; i++) {
+ if (p == task_userns)
+ return 0; /* our task's user namespace (or a child thereof) owns this superblock: allow! */
+
+ p = p->parent;
+ if (!p)
+ break;
+ }
+
+ /* Hmm, something is fishy if there's more than 32 levels of namespaces involved. Let's better be
+ * safe than sorry, and refuse. */
+ if (p)
+ return -EPERM;
+
+ /* This is a mount foreign to our task's user namespace, let's consult our allow list */
+ task_userns_inode = task_userns->ns.inum;
+
+ mnt_id_map = bpf_map_lookup_elem(&userns_mnt_id_hash, &task_userns_inode);
+ if (!mnt_id_map) /* No rules installed for this userns? Then say yes, too! */
+ return 0;
+
+ mnt_id = m->mnt_id;
+
+ /* Otherwise, say yes if the mount ID is allowlisted */
+ if (bpf_map_lookup_elem(mnt_id_map, &mnt_id))
+ return 0;
+
+ return -EPERM;
+}
+
+static int validate_path(const struct path *path, int ret) {
+ struct inode *inode;
+ struct vfsmount *v;
+
+ if (ret != 0) /* propagate earlier error */
+ return ret;
+
+ inode = path->dentry->d_inode;
+ v = path->mnt;
+
+ return validate_inode_on_mount(inode, v);
+}
+
+SEC("lsm/path_chown")
+int BPF_PROG(userns_restrict_path_chown, struct path *path, void* uid, void *gid, int ret) {
+ return validate_path(path, ret);
+}
+
+SEC("lsm/path_mkdir")
+int BPF_PROG(userns_restrict_path_mkdir, struct path *dir, struct dentry *dentry, umode_t mode, int ret) {
+ return validate_path(dir, ret);
+}
+
+SEC("lsm/path_mknod")
+int BPF_PROG(userns_restrict_path_mknod, const struct path *dir, struct dentry *dentry, umode_t mode, unsigned int dev, int ret) {
+ return validate_path(dir, ret);
+}
+
+SEC("lsm/path_symlink")
+int BPF_PROG(userns_restrict_path_symlink, const struct path *dir, struct dentry *dentry, const char *old_name, int ret) {
+ return validate_path(dir, ret);
+}
+
+SEC("lsm/path_link")
+int BPF_PROG(userns_restrict_path_link, struct dentry *old_dentry, const struct path *new_dir, struct dentry *new_dentry, int ret) {
+ return validate_path(new_dir, ret);
+}
+
+SEC("kprobe/free_user_ns")
+void BPF_KPROBE(userns_restrict_free_user_ns, struct work_struct *work) {
+ struct user_namespace *userns;
+ unsigned inode;
+ void *mnt_id_map;
+
+ /* Inform userspace that a user namespace just went away. I wish there was a nicer way to hook into
+ * user namespaces being deleted than using kprobes, but couldn't find any. */
+
+ userns = bpf_rdonly_cast(container_of(work, struct user_namespace, work),
+ bpf_core_type_id_kernel(struct user_namespace));
+
+ inode = userns->ns.inum;
+
+ mnt_id_map = bpf_map_lookup_elem(&userns_mnt_id_hash, &inode);
+ if (!mnt_id_map) /* No rules installed for this userns? Then send no notification. */
+ return;
+
+ bpf_ringbuf_output(&userns_ringbuf, &inode, sizeof(inode), 0);
+}
+
+static const char _license[] SEC("license") = "GPL";
--- /dev/null
+# SPDX-License-Identifier: LGPL-2.1-or-later
+
+subdir('bpf/userns_restrict')
+
+systemd_nsresourcework_sources = files(
+ 'nsresourcework.c',
+ 'userns-restrict.c',
+ 'userns-registry.c',
+)
+
+systemd_nsresourced_sources = files(
+ 'nsresourced-manager.c',
+ 'nsresourced.c',
+ 'userns-restrict.c',
+ 'userns-registry.c',
+)
+
+userns_restrict_include = include_directories('.')
+
+if conf.get('HAVE_VMLINUX_H') == 1
+ systemd_nsresourcework_sources += userns_restrict_skel_h
+ systemd_nsresourced_sources += userns_restrict_skel_h
+
+ executables += [
+ test_template + {
+ 'sources' : files('test-userns-restrict.c', 'userns-restrict.c') + userns_restrict_skel_h,
+ 'conditions' : ['ENABLE_NSRESOURCED', 'HAVE_VMLINUX_H'],
+ 'include_directories' : [ includes, userns_restrict_include ],
+ },
+ ]
+endif
+
+executables += [
+ libexec_template + {
+ 'name' : 'systemd-nsresourcework',
+ 'conditions' : ['ENABLE_NSRESOURCED'],
+ 'sources' : systemd_nsresourcework_sources,
+ 'dependencies' : threads,
+ 'include_directories' : [ includes, userns_restrict_include ],
+ },
+ libexec_template + {
+ 'name' : 'systemd-nsresourced',
+ 'conditions' : ['ENABLE_NSRESOURCED'],
+ 'sources' : systemd_nsresourced_sources,
+ 'dependencies' : threads,
+ 'include_directories' : [ includes, userns_restrict_include ],
+ },
+]
--- /dev/null
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+
+#include <sys/mount.h>
+#include <sys/wait.h>
+
+#include "sd-daemon.h"
+
+#include "bpf-dlopen.h"
+#include "build-path.h"
+#include "common-signal.h"
+#include "env-util.h"
+#include "fd-util.h"
+#include "fs-util.h"
+#include "mkdir.h"
+#include "nsresourced-manager.h"
+#include "parse-util.h"
+#include "process-util.h"
+#include "recurse-dir.h"
+#include "set.h"
+#include "signal-util.h"
+#include "socket-util.h"
+#include "stat-util.h"
+#include "stdio-util.h"
+#include "strv.h"
+#include "umask-util.h"
+#include "unaligned.h"
+#include "user-util.h"
+#include "userns-registry.h"
+#include "userns-restrict.h"
+
+#define LISTEN_TIMEOUT_USEC (25 * USEC_PER_SEC)
+
+static int start_workers(Manager *m, bool explicit_request);
+
+static int on_worker_exit(sd_event_source *s, const siginfo_t *si, void *userdata) {
+ Manager *m = ASSERT_PTR(userdata);
+
+ assert(s);
+
+ assert_se(!set_remove(m->workers_dynamic, s) != !set_remove(m->workers_fixed, s));
+ sd_event_source_disable_unref(s);
+
+ if (si->si_code == CLD_EXITED) {
+ if (si->si_status == EXIT_SUCCESS)
+ log_debug("Worker " PID_FMT " exited successfully.", si->si_pid);
+ else
+ log_warning("Worker " PID_FMT " died with a failure exit status %i, ignoring.", si->si_pid, si->si_status);
+ } else if (si->si_code == CLD_KILLED)
+ log_warning("Worker " PID_FMT " was killed by signal %s, ignoring.", si->si_pid, signal_to_string(si->si_status));
+ else if (si->si_code == CLD_DUMPED)
+ log_warning("Worker " PID_FMT " dumped core by signal %s, ignoring.", si->si_pid, signal_to_string(si->si_status));
+ else
+ log_warning("Got unexpected exit code via SIGCHLD, ignoring.");
+
+ (void) start_workers(m, /* explicit_request= */ false); /* Fill up workers again if we fell below the low watermark */
+ return 0;
+}
+
+static int on_sigusr2(sd_event_source *s, const struct signalfd_siginfo *si, void *userdata) {
+ Manager *m = ASSERT_PTR(userdata);
+
+ assert(s);
+
+ (void) start_workers(m, /* explicit_request=*/ true); /* Workers told us there's more work, let's add one more worker as long as we are below the high watermark */
+ return 0;
+}
+
+static int on_deferred_start_worker(sd_event_source *s, uint64_t usec, void *userdata) {
+ Manager *m = ASSERT_PTR(userdata);
+
+ assert(s);
+
+ m->deferred_start_worker_event_source = sd_event_source_unref(m->deferred_start_worker_event_source);
+
+ (void) start_workers(m, /* explicit_request=*/ false);
+ return 0;
+}
+
+DEFINE_PRIVATE_HASH_OPS_WITH_KEY_DESTRUCTOR(
+ event_source_hash_ops,
+ sd_event_source,
+ (void (*)(const sd_event_source*, struct siphash*)) trivial_hash_func,
+ (int (*)(const sd_event_source*, const sd_event_source*)) trivial_compare_func,
+ sd_event_source_disable_unref);
+
+int manager_new(Manager **ret) {
+ _cleanup_(manager_freep) Manager *m = NULL;
+ int r;
+
+ m = new(Manager, 1);
+ if (!m)
+ return -ENOMEM;
+
+ *m = (Manager) {
+ .listen_fd = -EBADF,
+ .worker_ratelimit = {
+ .interval = 2 * USEC_PER_SEC,
+ .burst = 250,
+ },
+ .registry_fd = -EBADF,
+ };
+
+ r = sd_event_new(&m->event);
+ if (r < 0)
+ return r;
+
+ r = sd_event_set_signal_exit(m->event, true);
+ if (r < 0)
+ return r;
+
+ r = sd_event_add_signal(m->event, NULL, (SIGRTMIN+18)|SD_EVENT_SIGNAL_PROCMASK, sigrtmin18_handler, NULL);
+ if (r < 0)
+ return r;
+
+ r = sd_event_add_memory_pressure(m->event, NULL, NULL, NULL);
+ if (r < 0)
+ log_debug_errno(r, "Failed allocate memory pressure event source, ignoring: %m");
+
+ r = sd_event_set_watchdog(m->event, true);
+ if (r < 0)
+ log_debug_errno(r, "Failed to enable watchdog handling, ignoring: %m");
+
+ r = sd_event_add_signal(m->event, NULL, SIGUSR2|SD_EVENT_SIGNAL_PROCMASK, on_sigusr2, m);
+ if (r < 0)
+ return r;
+
+ *ret = TAKE_PTR(m);
+ return 0;
+}
+
+Manager* manager_free(Manager *m) {
+ if (!m)
+ return NULL;
+
+ set_free(m->workers_fixed);
+ set_free(m->workers_dynamic);
+
+ m->deferred_start_worker_event_source = sd_event_source_unref(m->deferred_start_worker_event_source);
+
+ safe_close(m->listen_fd);
+
+#if HAVE_VMLINUX_H
+ sd_event_source_disable_unref(m->userns_restrict_bpf_ring_buffer_event_source);
+ if (m->userns_restrict_bpf_ring_buffer)
+ sym_ring_buffer__free(m->userns_restrict_bpf_ring_buffer);
+ userns_restrict_bpf_free(m->userns_restrict_bpf);
+#endif
+
+ safe_close(m->registry_fd);
+
+ sd_event_unref(m->event);
+
+ return mfree(m);
+}
+
+static size_t manager_current_workers(Manager *m) {
+ assert(m);
+
+ return set_size(m->workers_fixed) + set_size(m->workers_dynamic);
+}
+
+static int start_one_worker(Manager *m) {
+ _cleanup_(sd_event_source_disable_unrefp) sd_event_source *source = NULL;
+ bool fixed;
+ pid_t pid;
+ int r;
+
+ assert(m);
+
+ fixed = set_size(m->workers_fixed) < NSRESOURCE_WORKERS_MIN;
+
+ r = safe_fork_full(
+ "(sd-worker)",
+ /* stdio_fds= */ NULL,
+ &m->listen_fd, 1,
+ FORK_RESET_SIGNALS|FORK_DEATHSIG_SIGTERM|FORK_REOPEN_LOG|FORK_LOG|FORK_CLOSE_ALL_FDS,
+ &pid);
+ if (r < 0)
+ return log_error_errno(r, "Failed to fork new worker child: %m");
+ if (r == 0) {
+ char pids[DECIMAL_STR_MAX(pid_t)];
+ /* Child */
+
+ if (m->listen_fd == 3) {
+ r = fd_cloexec(3, false);
+ if (r < 0) {
+ log_error_errno(r, "Failed to turn off O_CLOEXEC for fd 3: %m");
+ _exit(EXIT_FAILURE);
+ }
+ } else {
+ if (dup2(m->listen_fd, 3) < 0) { /* dup2() creates with O_CLOEXEC off */
+ log_error_errno(errno, "Failed to move listen fd to 3: %m");
+ _exit(EXIT_FAILURE);
+ }
+
+ safe_close(m->listen_fd);
+ }
+
+ xsprintf(pids, PID_FMT, pid);
+ if (setenv("LISTEN_PID", pids, 1) < 0) {
+ log_error_errno(errno, "Failed to set $LISTEN_PID: %m");
+ _exit(EXIT_FAILURE);
+ }
+
+ if (setenv("LISTEN_FDS", "1", 1) < 0) {
+ log_error_errno(errno, "Failed to set $LISTEN_FDS: %m");
+ _exit(EXIT_FAILURE);
+ }
+
+ if (setenv("NSRESOURCE_FIXED_WORKER", one_zero(fixed), 1) < 0) {
+ log_error_errno(errno, "Failed to set $NSRESOURCE_FIXED_WORKER: %m");
+ _exit(EXIT_FAILURE);
+ }
+
+#if HAVE_VMLINUX_H
+ bool supported = m->userns_restrict_bpf;
+#else
+ bool supported = false;
+#endif
+
+ /* Tell the workers whether to enable the userns API */
+ if (setenv("NSRESOURCE_API", one_zero(supported), 1) < 0) {
+ log_error_errno(errno, "Failed to set $NSRESOURCE_API: %m");
+ _exit(EXIT_FAILURE);
+ }
+
+ r = setenv_systemd_log_level();
+ if (r < 0) {
+ log_error_errno(r, "Failed to set $SYSTEMD_LOG_LEVEL: %m");
+ _exit(EXIT_FAILURE);
+ }
+
+ r = invoke_callout_binary(SYSTEMD_NSRESOURCEWORK_PATH, STRV_MAKE("systemd-nsresourcework", "xxxxxxxxxxxxxxxx")); /* With some extra space rename_process() can make use of */
+ log_error_errno(r, "Failed start worker process: %m");
+ _exit(EXIT_FAILURE);
+ }
+
+ r = sd_event_add_child(m->event, &source, pid, WEXITED, on_worker_exit, m);
+ if (r < 0)
+ return log_error_errno(r, "Failed to watch child " PID_FMT ": %m", pid);
+
+ r = set_ensure_put(
+ fixed ? &m->workers_fixed : &m->workers_dynamic,
+ &event_source_hash_ops,
+ source);
+ if (r < 0)
+ return log_error_errno(r, "Failed to add child process to set: %m");
+
+ TAKE_PTR(source);
+
+ return 0;
+}
+
+static int start_workers(Manager *m, bool explicit_request) {
+ int r;
+
+ assert(m);
+
+ for (;;) {
+ size_t n;
+
+ n = manager_current_workers(m);
+ if (n >= NSRESOURCE_WORKERS_MIN && (!explicit_request || n >= NSRESOURCE_WORKERS_MAX))
+ break;
+
+ if (!ratelimit_below(&m->worker_ratelimit)) {
+
+ /* If we keep starting workers too often but none sticks, let's fail the whole
+ * daemon, something is wrong */
+ if (n == 0) {
+ sd_event_exit(m->event, EXIT_FAILURE);
+ return log_error_errno(SYNTHETIC_ERRNO(EUCLEAN), "Worker threads requested too frequently, but worker count is zero, something is wrong.");
+ }
+
+ /* Otherwise, let's stop spawning more for a while. */
+ log_warning("Worker threads requested too frequently, not starting new ones for a while.");
+
+ if (!m->deferred_start_worker_event_source) {
+ r = sd_event_add_time(
+ m->event,
+ &m->deferred_start_worker_event_source,
+ CLOCK_MONOTONIC,
+ ratelimit_end(&m->worker_ratelimit),
+ /* accuracy_usec= */ 0,
+ on_deferred_start_worker,
+ m);
+ if (r < 0)
+ return log_error_errno(r, "Failed to allocate deferred start worker event source: %m");
+ }
+
+ break;
+ }
+
+ r = start_one_worker(m);
+ if (r < 0)
+ return r;
+
+ explicit_request = false;
+ }
+
+ return 0;
+}
+
+static void manager_release_userns_bpf(Manager *m, uint64_t inode) {
+#if HAVE_VMLINUX_H
+ int r;
+
+ assert(m);
+
+ if (inode == 0)
+ return;
+
+ assert(m->userns_restrict_bpf);
+
+ r = userns_restrict_reset_by_inode(m->userns_restrict_bpf, inode);
+ if (r < 0)
+ return (void) log_warning_errno(r, "Failed to remove namespace inode from BPF map, ignoring: %m");
+#endif
+}
+
+static void manager_release_userns_fds(Manager *m, uint64_t inode) {
+ int r;
+
+ assert(m);
+ assert(inode != 0);
+
+ r = sd_notifyf(/* unset_environment= */ false,
+ "FDSTOREREMOVE=1\n"
+ "FDNAME=userns-%" PRIu64 "\n", inode);
+ if (r < 0)
+ log_warning_errno(r, "Failed to send fd store removal message, ignoring: %m");
+}
+
+static void manager_release_userns_by_inode(Manager *m, uint64_t inode) {
+ _cleanup_(userns_info_freep) UserNamespaceInfo *userns_info = NULL;
+ _cleanup_close_ int lock_fd = -EBADF;
+ int r;
+
+ assert(m);
+ assert(inode != 0);
+
+ lock_fd = userns_registry_lock(m->registry_fd);
+ if (lock_fd < 0)
+ return (void) log_error_errno(lock_fd, "Failed to lock registry: %m");
+
+ r = userns_registry_load_by_userns_inode(m->registry_fd, inode, &userns_info);
+ if (r < 0)
+ log_full_errno(r == -ENOENT ? LOG_DEBUG : LOG_WARNING, r,
+ "Failed to find userns for inode %" PRIu64 ", ignoring: %m", inode);
+
+ if (userns_info && uid_is_valid(userns_info->start))
+ log_debug("Removing user namespace mapping %" PRIu64 " for UID " UID_FMT ".", inode, userns_info->start);
+ else
+ log_debug("Removing user namespace mapping %" PRIu64 ".", inode);
+
+ /* Remove the BPF rules */
+ manager_release_userns_bpf(m, inode);
+
+ /* Remove the resources from the fdstore */
+ manager_release_userns_fds(m, inode);
+
+ /* And finally remove the resources file from disk */
+ if (userns_info) {
+ /* Remove the cgroups of this userns */
+ r = userns_info_remove_cgroups(userns_info);
+ if (r < 0)
+ log_warning_errno(r, "Failed to remove cgroups of user namespace: %m");
+
+ r = userns_registry_remove(m->registry_fd, userns_info);
+ if (r < 0)
+ log_warning_errno(r, "Failed to remove user namespace '%s', ignoring.", userns_info->name);
+ }
+}
+
+static int manager_scan_registry(Manager *m, Set **registry_inodes) {
+ _cleanup_free_ DirectoryEntries *de = NULL;
+ int r;
+
+ assert(m);
+ assert(registry_inodes);
+ assert(m->registry_fd >= 0);
+
+ r = readdir_all(m->registry_fd, RECURSE_DIR_IGNORE_DOT, &de);
+ if (r < 0)
+ return log_error_errno(r, "Failed to enumerate registry.");
+
+ for (size_t i = 0; i < de->n_entries; i++) {
+ struct dirent *dentry = de->entries[i];
+ _cleanup_free_ char *u = NULL;
+ const char *e, *p;
+ uint64_t inode;
+
+ p = startswith(dentry->d_name, "i");
+ if (!p)
+ continue;
+
+ e = endswith(p, ".userns");
+ if (!e)
+ continue;
+
+ u = strndup(p, e - p);
+ if (!u)
+ return log_oom();
+
+ r = safe_atou64(u, &inode);
+ if (r < 0) {
+ log_warning_errno(r, "Failed to parse userns inode number from '%s', skipping: %m", dentry->d_name);
+ continue;
+ }
+
+ if (inode > UINT32_MAX) { /* namespace inode numbers are 23bit only right now */
+ log_warning("userns inode number outside of 32bit range, skipping.");
+ continue;
+ }
+
+ if (set_ensure_put(registry_inodes, NULL, UINT32_TO_PTR(inode)) < 0)
+ return log_oom();
+
+ log_debug("Found user namespace %" PRIu64 " in registry directory", inode);
+ }
+
+ return 0;
+}
+
+static int manager_make_listen_socket(Manager *m) {
+ static const union sockaddr_union sockaddr = {
+ .un.sun_family = AF_UNIX,
+ .un.sun_path = "/run/systemd/io.systemd.NamespaceResource",
+ };
+ int r;
+
+ assert(m);
+
+ if (m->listen_fd >= 0)
+ return 0;
+
+ m->listen_fd = socket(AF_UNIX, SOCK_STREAM|SOCK_CLOEXEC, 0);
+ if (m->listen_fd < 0)
+ return log_error_errno(errno, "Failed to bind on socket: %m");
+
+ (void) sockaddr_un_unlink(&sockaddr.un);
+
+ WITH_UMASK(0000)
+ if (bind(m->listen_fd, &sockaddr.sa, SOCKADDR_UN_LEN(sockaddr.un)) < 0)
+ return log_error_errno(errno, "Failed to bind socket: %m");
+
+ r = mkdir_p("/run/systemd/userdb", 0755);
+ if (r < 0)
+ return log_error_errno(r, "Failed to create /run/systemd/userdb: %m");
+
+ r = symlink_idempotent("../io.systemd.NamespaceResource", "/run/systemd/userdb/io.systemd.NamespaceResource", /* make_relative= */ false);
+ if (r < 0)
+ return log_error_errno(r, "Failed to symlink userdb socket: %m");
+
+ if (listen(m->listen_fd, SOMAXCONN) < 0)
+ return log_error_errno(errno, "Failed to listen on socket: %m");
+
+ return 1;
+}
+
+static int manager_scan_listen_fds(Manager *m, Set **fdstore_inodes) {
+ _cleanup_strv_free_ char **names = NULL;
+ int n, r;
+
+ assert(m);
+ assert(fdstore_inodes);
+
+ n = sd_listen_fds_with_names(/* unset_environment= */ true, &names);
+ if (n < 0)
+ return log_error_errno(n, "Failed to determine number of passed file descriptors: %m");
+
+ for (int i = 0; i < n; i++) {
+ _cleanup_close_ int fd = SD_LISTEN_FDS_START + i; /* Take possession */
+ const char *e;
+
+ /* If this is a BPF allowlist related fd, just close it, but remember which start UIDs this covers */
+ e = startswith(names[i], "userns-");
+ if (e) {
+ uint64_t inode;
+
+ r = safe_atou64(e, &inode);
+ if (r < 0) {
+ log_warning_errno(r, "Failed to parse UID from fd name '%s', ignoring: %m", e);
+ continue;
+ }
+
+ if (inode > UINT32_MAX) {
+ log_warning("Inode number outside of 32bit range, ignoring");
+ continue;
+ }
+
+ if (set_ensure_put(fdstore_inodes, NULL, UINT32_TO_PTR(inode)) < 0)
+ return log_oom();
+
+ continue;
+ }
+
+ /* We don't check the name for the stream socket, for compatibility with older versions */
+ r = sd_is_socket(fd, AF_UNIX, SOCK_STREAM, 1);
+ if (r < 0)
+ return log_error_errno(r, "Failed to detect if passed file descriptor is a socket: %m");
+ if (r > 0) {
+ if (m->listen_fd >= 0)
+ return log_error_errno(SYNTHETIC_ERRNO(ENOTUNIQ), "Passed more than one AF_UNIX/SOCK_STREAM socket, refusing.");
+
+ m->listen_fd = TAKE_FD(fd);
+ continue;
+ }
+
+ log_warning("Closing passed file descriptor %i (%s) we don't recognize.", fd, names[i]);
+ }
+
+ return 0;
+}
+
+#if HAVE_VMLINUX_H
+static int ringbuf_event(void *userdata, void *data, size_t size) {
+ Manager *m = ASSERT_PTR(userdata);
+ size_t n;
+
+ if ((size % sizeof(unsigned int)) != 0) /* Not multiples of "unsigned int"? */
+ return -EIO;
+
+ n = size / sizeof(unsigned int);
+ for (size_t i = 0; i < n; i++) {
+ const void *d;
+ uint64_t inode;
+
+ d = (const uint8_t*) data + i * sizeof(unsigned int);
+ inode = unaligned_read_ne32(d);
+
+ log_debug("Got BPF ring buffer notification that user namespace %" PRIu64 " is now dead.", inode);
+ manager_release_userns_by_inode(m, inode);
+ }
+
+ return 0;
+}
+
+static int on_ringbuf_io(sd_event_source *s, int fd, uint32_t revents, void *userdata) {
+ Manager *m = ASSERT_PTR(userdata);
+ int r;
+
+ r = sym_ring_buffer__poll(m->userns_restrict_bpf_ring_buffer, 0);
+ if (r < 0)
+ return log_error_errno(r, "Got failure reading from BPF ring buffer: %m");
+
+ return 0;
+}
+
+static int manager_setup_bpf(Manager *m) {
+ int rb_fd = -EBADF, poll_fd = -EBADF, r;
+
+ assert(m);
+ assert(!m->userns_restrict_bpf);
+ assert(!m->userns_restrict_bpf_ring_buffer);
+ assert(!m->userns_restrict_bpf_ring_buffer_event_source);
+
+ r = userns_restrict_install(/* pin= */ true, &m->userns_restrict_bpf);
+ if (r < 0) {
+ log_notice_errno(r, "Proceeding with user namespace interfaces disabled.");
+ return 0;
+ }
+
+ rb_fd = sym_bpf_map__fd(m->userns_restrict_bpf->maps.userns_ringbuf);
+ if (rb_fd < 0)
+ return log_error_errno(rb_fd, "Failed to get fd of ring buffer: %m");
+
+ m->userns_restrict_bpf_ring_buffer = sym_ring_buffer__new(rb_fd, ringbuf_event, m, NULL);
+ if (!m->userns_restrict_bpf_ring_buffer)
+ return log_error_errno(errno, "Failed to allocate BPF ring buffer object: %m");
+
+ poll_fd = sym_ring_buffer__epoll_fd(m->userns_restrict_bpf_ring_buffer);
+ if (poll_fd < 0)
+ return log_error_errno(poll_fd, "Failed to get poll fd of ring buffer: %m");
+
+ r = sd_event_add_io(
+ m->event,
+ &m->userns_restrict_bpf_ring_buffer_event_source,
+ poll_fd,
+ EPOLLIN,
+ on_ringbuf_io,
+ m);
+ if (r < 0)
+ return log_error_errno(r, "Failed to allocate event source for BPF ring buffer: %m");
+
+ return 0;
+}
+#else
+static int manager_setup_bpf(Manager *m) {
+ log_notice("Not setting up BPF subsystem, as functionality has been disabled at compile time.");
+ return 0;
+}
+#endif
+
+int manager_startup(Manager *m) {
+ _cleanup_(set_freep) Set *fdstore_inodes = NULL, *registry_inodes = NULL;
+ void *p;
+ int r;
+
+ assert(m);
+ assert(m->registry_fd < 0);
+ assert(m->listen_fd < 0);
+
+ m->registry_fd = userns_registry_open_fd();
+ if (m->registry_fd < 0)
+ return log_error_errno(m->registry_fd, "Failed to open registry directory: %m");
+
+ r = manager_setup_bpf(m);
+ if (r < 0)
+ return r;
+
+ r = manager_scan_listen_fds(m, &fdstore_inodes);
+ if (r < 0)
+ return r;
+
+ r = manager_scan_registry(m, ®istry_inodes);
+ if (r < 0)
+ return r;
+
+ /* If there are resources tied to UIDs not found in the registry, then release them */
+ SET_FOREACH(p, fdstore_inodes) {
+ uint64_t inode;
+
+ if (set_contains(registry_inodes, p))
+ continue;
+
+ inode = PTR_TO_UINT32(p);
+
+ log_debug("Found stale fd store entry for user namespace %" PRIu64 ", removing.", inode);
+ manager_release_userns_by_inode(m, inode);
+ }
+
+ r = manager_make_listen_socket(m);
+ if (r < 0)
+ return r;
+
+ /* Let's make sure every accept() call on this socket times out after 25s. This allows workers to be
+ * GC'ed on idle */
+ if (setsockopt(m->listen_fd, SOL_SOCKET, SO_RCVTIMEO, TIMEVAL_STORE(LISTEN_TIMEOUT_USEC), sizeof(struct timeval)) < 0)
+ return log_error_errno(errno, "Failed to se SO_RCVTIMEO: %m");
+
+ r = start_workers(m, /* explicit_request= */ false);
+ if (r < 0)
+ return r;
+
+ return 0;
+}
--- /dev/null
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+#pragma once
+
+#include "sd-bus.h"
+#include "sd-event.h"
+
+typedef struct Manager Manager;
+
+#include "hashmap.h"
+#include "ratelimit.h"
+
+#define NSRESOURCE_WORKERS_MIN 5
+#define NSRESOURCE_WORKERS_MAX 4096
+
+struct Manager {
+ sd_event *event;
+
+ Set *workers_fixed; /* Workers 0…NSRESOURCE_WORKERS_MIN */
+ Set *workers_dynamic; /* Workers NSRESOURCES_WORKERS_MIN+1…NSRESOURCES_WORKERS_MAX */
+
+ int listen_fd;
+
+ RateLimit worker_ratelimit;
+
+ sd_event_source *deferred_start_worker_event_source;
+
+#if HAVE_VMLINUX_H
+ struct userns_restrict_bpf *userns_restrict_bpf;
+ struct ring_buffer *userns_restrict_bpf_ring_buffer;
+ sd_event_source *userns_restrict_bpf_ring_buffer_event_source;
+#endif
+
+ int registry_fd;
+};
+
+int manager_new(Manager **ret);
+Manager* manager_free(Manager *m);
+DEFINE_TRIVIAL_CLEANUP_FUNC(Manager*, manager_free);
+
+int manager_startup(Manager *m);
--- /dev/null
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+
+#include <sys/stat.h>
+#include <sys/types.h>
+
+#include "daemon-util.h"
+#include "nsresourced-manager.h"
+#include "log.h"
+#include "main-func.h"
+#include "signal-util.h"
+
+static int run(int argc, char *argv[]) {
+ _cleanup_(manager_freep) Manager *m = NULL;
+ int r;
+
+ log_setup();
+
+ umask(0022);
+
+ if (argc != 1)
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "This program takes no arguments.");
+
+ if (setenv("SYSTEMD_BYPASS_USERDB", "io.systemd.NamespaceResource", 1) < 0)
+ return log_error_errno(errno, "Failed to set $SYSTEMD_BYPASS_USERDB: %m");
+
+ assert_se(sigprocmask_many(SIG_BLOCK, NULL, SIGCHLD) >= 0);
+
+ r = manager_new(&m);
+ if (r < 0)
+ return log_error_errno(r, "Could not create manager: %m");
+
+ r = manager_startup(m);
+ if (r < 0)
+ return log_error_errno(r, "Failed to start up daemon: %m");
+
+ _unused_ _cleanup_(notify_on_cleanup) const char *notify_stop = NULL;
+ notify_stop = notify_start(NOTIFY_READY, NOTIFY_STOPPING);
+
+ r = sd_event_loop(m->event);
+ if (r < 0)
+ return log_error_errno(r, "Event loop failed: %m");
+
+ return 0;
+}
+
+DEFINE_MAIN_FUNCTION(run);
--- /dev/null
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+
+#include <fcntl.h>
+#include <linux/nsfs.h>
+#include <linux/veth.h>
+#include <sys/eventfd.h>
+#include <sys/stat.h>
+#include <sys/wait.h>
+
+#include "sd-daemon.h"
+#include "sd-netlink.h"
+
+#include "env-util.h"
+#include "fd-util.h"
+#include "fileio.h"
+#include "fs-util.h"
+#include "group-record.h"
+#include "io-util.h"
+#include "lock-util.h"
+#include "main-func.h"
+#include "missing_magic.h"
+#include "missing_mount.h"
+#include "missing_syscall.h"
+#include "mount-util.h"
+#include "mountpoint-util.h"
+#include "namespace-util.h"
+#include "netlink-util.h"
+#include "process-util.h"
+#include "random-util.h"
+#include "socket-util.h"
+#include "stat-util.h"
+#include "strv.h"
+#include "time-util.h"
+#include "uid-classification.h"
+#include "uid-range.h"
+#include "user-record-nss.h"
+#include "user-record.h"
+#include "user-util.h"
+#include "userdb.h"
+#include "userns-registry.h"
+#include "userns-restrict.h"
+#include "varlink-io.systemd.NamespaceResource.h"
+#include "varlink-io.systemd.UserDatabase.h"
+#include "varlink.h"
+
+#define ITERATIONS_MAX 64U
+#define RUNTIME_MAX_USEC (5 * USEC_PER_MINUTE)
+#define PRESSURE_SLEEP_TIME_USEC (50 * USEC_PER_MSEC)
+#define CONNECTION_IDLE_USEC (15 * USEC_PER_SEC)
+#define LISTEN_IDLE_USEC (90 * USEC_PER_SEC)
+#define USERNS_PER_UID 256
+
+typedef struct LookupParameters {
+ const char *user_name;
+ const char *group_name;
+ union {
+ uid_t uid;
+ gid_t gid;
+ };
+ const char *service;
+} LookupParameters;
+
+static int build_user_json(UserNamespaceInfo *userns_info, uid_t offset, JsonVariant **ret) {
+ _cleanup_free_ char *name = NULL, *realname = NULL;
+ UserDisposition disposition;
+ int r;
+
+ assert(userns_info);
+ assert(offset < userns_info->size);
+
+ if (asprintf(&name, "ns-%s-" UID_FMT, userns_info->name, offset) < 0)
+ return -ENOMEM;
+
+ if (userns_info->size > 1) {
+ disposition = USER_CONTAINER;
+ r = asprintf(&realname, "User " UID_FMT " of Allocated Namespace %s", offset, userns_info->name);
+ } else {
+ disposition = USER_DYNAMIC;
+ r = asprintf(&realname, "Allocated Namespace %s", userns_info->name);
+ }
+ if (r < 0)
+ return -ENOMEM;
+
+ return json_build(ret, JSON_BUILD_OBJECT(
+ JSON_BUILD_PAIR("userName", JSON_BUILD_STRING(name)),
+ JSON_BUILD_PAIR("uid", JSON_BUILD_UNSIGNED(userns_info->start + offset)),
+ JSON_BUILD_PAIR("gid", JSON_BUILD_UNSIGNED(GID_NOBODY)),
+ JSON_BUILD_PAIR("realName", JSON_BUILD_STRING(realname)),
+ JSON_BUILD_PAIR("homeDirectory", JSON_BUILD_CONST_STRING("/")),
+ JSON_BUILD_PAIR("shell", JSON_BUILD_STRING(NOLOGIN)),
+ JSON_BUILD_PAIR("locked", JSON_BUILD_BOOLEAN(true)),
+ JSON_BUILD_PAIR("service", JSON_BUILD_CONST_STRING("io.systemd.NamespaceResource")),
+ JSON_BUILD_PAIR("disposition", JSON_BUILD_STRING(user_disposition_to_string(disposition)))));
+}
+
+static int vl_method_get_user_record(Varlink *link, JsonVariant *parameters, VarlinkMethodFlags flags, void *userdata) {
+
+ static const JsonDispatch dispatch_table[] = {
+ { "uid", JSON_VARIANT_UNSIGNED, json_dispatch_uid_gid, offsetof(LookupParameters, uid), 0 },
+ { "userName", JSON_VARIANT_STRING, json_dispatch_const_string, offsetof(LookupParameters, user_name), 0 },
+ { "service", JSON_VARIANT_STRING, json_dispatch_const_string, offsetof(LookupParameters, service), 0 },
+ {}
+ };
+
+ _cleanup_(userns_info_freep) UserNamespaceInfo *userns_info = NULL;
+ _cleanup_(json_variant_unrefp) JsonVariant *v = NULL;
+ LookupParameters p = {
+ .uid = UID_INVALID,
+ };
+ uid_t offset;
+ int r;
+
+ assert(parameters);
+
+ r = varlink_dispatch(link, parameters, dispatch_table, &p);
+ if (r != 0)
+ return r;
+
+ if (!streq_ptr(p.service, "io.systemd.NamespaceResource"))
+ return varlink_error(link, "io.systemd.UserDatabase.BadService", NULL);
+
+ if (p.user_name) {
+ _cleanup_free_ char *n = NULL;
+ const char *e, *f;
+
+ e = startswith(p.user_name, "ns-");
+ if (!e)
+ goto not_found;
+
+ f = strrchr(e, '-');
+ if (!f)
+ goto not_found;
+
+ if (parse_uid(f+1, &offset) < 0)
+ goto not_found;
+
+ n = strndup(e, f - e);
+ if (!n)
+ return log_oom();
+
+ r = userns_registry_load_by_name(
+ /* registry_fd= */ -EBADF,
+ n,
+ &userns_info);
+ if (r == -ENOENT)
+ goto not_found;
+ if (r < 0)
+ return r;
+
+ if (offset >= userns_info->size) /* Outside of range? */
+ goto not_found;
+
+ if (uid_is_valid(p.uid) && p.uid != userns_info->start + offset)
+ return varlink_error(link, "io.systemd.UserDatabase.ConflictingRecordFound", NULL);
+
+ } else if (uid_is_valid(p.uid)) {
+ uid_t start, uidmask;
+
+ if (uid_is_container(p.uid))
+ uidmask = (uid_t) UINT32_C(0xFFFF0000);
+ else if (uid_is_dynamic(p.uid))
+ uidmask = (uid_t) UINT32_C(0xFFFFFFFF);
+ else
+ goto not_found;
+
+ start = p.uid & uidmask;
+ offset = p.uid - start;
+
+ r = userns_registry_load_by_start_uid(
+ /* registry_fd= */ -EBADF,
+ start,
+ &userns_info);
+ if (r == -ENOENT)
+ goto not_found;
+ if (r < 0)
+ return r;
+
+ if (offset >= userns_info->size) /* Outside of range? */
+ goto not_found;
+ } else
+ return varlink_error(link, "io.systemd.UserDatabase.EnumerationNotSupported", NULL);
+
+ r = build_user_json(userns_info, offset, &v);
+ if (r < 0)
+ return r;
+
+ return varlink_replyb(link, JSON_BUILD_OBJECT(JSON_BUILD_PAIR("record", JSON_BUILD_VARIANT(v))));
+
+not_found:
+ return varlink_error(link, "io.systemd.UserDatabase.NoRecordFound", NULL);
+}
+
+static int build_group_json(UserNamespaceInfo *userns_info, gid_t offset, JsonVariant **ret) {
+ _cleanup_free_ char *name = NULL, *description = NULL;
+ UserDisposition disposition;
+ int r;
+
+ assert(userns_info);
+ assert(offset < userns_info->size);
+
+ if (asprintf(&name, "ns-%s-" GID_FMT, userns_info->name, offset) < 0)
+ return -ENOMEM;
+
+ if (userns_info->size > 1) {
+ disposition = USER_CONTAINER;
+ r = asprintf(&description, "Group " GID_FMT " of Allocated Namespace %s", offset, userns_info->name);
+ } else {
+ disposition = USER_DYNAMIC;
+ r = asprintf(&description, "Allocated Namespace %s", userns_info->name);
+ }
+ if (r < 0)
+ return -ENOMEM;
+
+ return json_build(ret, JSON_BUILD_OBJECT(
+ JSON_BUILD_PAIR("groupName", JSON_BUILD_STRING(name)),
+ JSON_BUILD_PAIR("gid", JSON_BUILD_UNSIGNED(userns_info->start + offset)),
+ JSON_BUILD_PAIR("description", JSON_BUILD_STRING(description)),
+ JSON_BUILD_PAIR("service", JSON_BUILD_CONST_STRING("io.systemd.NamespaceResource")),
+ JSON_BUILD_PAIR("disposition", JSON_BUILD_STRING(user_disposition_to_string(disposition)))));
+}
+
+static int vl_method_get_group_record(Varlink *link, JsonVariant *parameters, VarlinkMethodFlags flags, void *userdata) {
+
+ static const JsonDispatch dispatch_table[] = {
+ { "gid", JSON_VARIANT_UNSIGNED, json_dispatch_uid_gid, offsetof(LookupParameters, gid), 0 },
+ { "groupName", JSON_VARIANT_STRING, json_dispatch_const_string, offsetof(LookupParameters, group_name), 0 },
+ { "service", JSON_VARIANT_STRING, json_dispatch_const_string, offsetof(LookupParameters, service), 0 },
+ {}
+ };
+
+ _cleanup_(userns_info_freep) UserNamespaceInfo *userns_info = NULL;
+ _cleanup_(json_variant_unrefp) JsonVariant *v = NULL;
+ LookupParameters p = {
+ .gid = GID_INVALID,
+ };
+ gid_t offset;
+ int r;
+
+ assert(parameters);
+
+ r = varlink_dispatch(link, parameters, dispatch_table, &p);
+ if (r != 0)
+ return r;
+
+ if (!streq_ptr(p.service, "io.systemd.NamespaceResource"))
+ return varlink_error(link, "io.systemd.UserDatabase.BadService", NULL);
+
+ if (p.group_name) {
+ _cleanup_free_ char *n = NULL;
+ const char *e, *f;
+
+ e = startswith(p.group_name, "ns-");
+ if (!e)
+ goto not_found;
+
+ f = strrchr(e, '-');
+ if (!f)
+ goto not_found;
+
+ if (parse_gid(f+1, &offset) < 0)
+ goto not_found;
+
+ n = strndup(e, f - e);
+ if (!n)
+ return log_oom();
+
+ r = userns_registry_load_by_name(
+ /* registry_fd= */ -EBADF,
+ n,
+ &userns_info);
+ if (r == -ENOENT)
+ goto not_found;
+ if (r < 0)
+ return r;
+
+ if (offset >= userns_info->size) /* Outside of range? */
+ goto not_found;
+
+ if (gid_is_valid(p.gid) && p.uid != userns_info->start + offset)
+ return varlink_error(link, "io.systemd.UserDatabase.ConflictingRecordFound", NULL);
+
+ } else if (gid_is_valid(p.gid)) {
+ gid_t start, gidmask;
+
+ if (gid_is_container(p.gid))
+ gidmask = (gid_t) UINT32_C(0xFFFF0000);
+ else if (gid_is_dynamic(p.gid))
+ gidmask = (gid_t) UINT32_C(0xFFFFFFFF);
+ else
+ goto not_found;
+
+ start = p.gid & gidmask;
+ offset = p.gid - start;
+
+ r = userns_registry_load_by_start_uid(
+ /* registry_fd= */ -EBADF,
+ (uid_t) start,
+ &userns_info);
+ if (r == -ENOENT)
+ goto not_found;
+ if (r < 0)
+ return r;
+
+ if (offset >= userns_info->size) /* Outside of range? */
+ goto not_found;
+ } else
+ return varlink_error(link, "io.systemd.UserDatabase.EnumerationNotSupported", NULL);
+
+ r = build_group_json(userns_info, offset, &v);
+ if (r < 0)
+ return r;
+
+ return varlink_replyb(link, JSON_BUILD_OBJECT(JSON_BUILD_PAIR("record", JSON_BUILD_VARIANT(v))));
+
+not_found:
+ return varlink_error(link, "io.systemd.UserDatabase.NoRecordFound", NULL);
+}
+
+static int vl_method_get_memberships(Varlink *link, JsonVariant *parameters, VarlinkMethodFlags flags, void *userdata) {
+ static const JsonDispatch dispatch_table[] = {
+ { "userName", JSON_VARIANT_STRING, json_dispatch_const_string, offsetof(LookupParameters, user_name), 0 },
+ { "groupName", JSON_VARIANT_STRING, json_dispatch_const_string, offsetof(LookupParameters, group_name), 0 },
+ { "service", JSON_VARIANT_STRING, json_dispatch_const_string, offsetof(LookupParameters, service), 0 },
+ {}
+ };
+
+ LookupParameters p = {};
+ int r;
+
+ assert(parameters);
+
+ r = varlink_dispatch(link, parameters, dispatch_table, &p);
+ if (r != 0)
+ return r;
+
+ if (!streq_ptr(p.service, "io.systemd.NamespaceResource"))
+ return varlink_error(link, "io.systemd.UserDatabase.BadService", NULL);
+
+ /* We don't support auxiliary groups for namespace allocations */
+ return varlink_error(link, "io.systemd.UserDatabase.NoRecordFound", NULL);
+}
+
+static int uid_is_available(
+ int registry_dir_fd,
+ uid_t candidate) {
+
+ int r;
+
+ assert(registry_dir_fd >= 0);
+
+ log_debug("Checking if UID " UID_FMT " is available.", candidate);
+
+ r = userns_registry_uid_exists(registry_dir_fd, candidate);
+ if (r < 0)
+ return r;
+ if (r > 0)
+ return false;
+
+ r = userdb_by_uid(candidate, USERDB_AVOID_MULTIPLEXER, NULL);
+ if (r >= 0)
+ return false;
+ if (r != -ESRCH)
+ return r;
+
+ r = groupdb_by_gid(candidate, USERDB_AVOID_MULTIPLEXER, NULL);
+ if (r >= 0)
+ return false;
+ if (r != -ESRCH)
+ return r;
+
+ log_debug("UID " UID_FMT " is available.", candidate);
+
+ return true;
+}
+
+static int name_is_available(
+ int registry_dir_fd,
+ const char *name) {
+
+ _cleanup_free_ char *user_name = NULL;
+ int r;
+
+ assert(registry_dir_fd >= 0);
+ assert(name);
+
+ r = userns_registry_name_exists(registry_dir_fd, name);
+ if (r < 0)
+ return r;
+ if (r > 0)
+ return false;
+
+ user_name = strjoin("ns-", name, "-0");
+ if (!user_name)
+ return -ENOMEM;
+
+ r = userdb_by_name(user_name, USERDB_AVOID_MULTIPLEXER, NULL);
+ if (r >= 0)
+ return false;
+ if (r != -ESRCH)
+ return r;
+
+ r = groupdb_by_name(user_name, USERDB_AVOID_MULTIPLEXER, NULL);
+ if (r >= 0)
+ return false;
+ if (r != -ESRCH)
+ return r;
+
+ log_debug("Namespace name '%s' is available.", name);
+
+ return true;
+}
+
+static int allocate_now(
+ int registry_dir_fd,
+ UserNamespaceInfo *info,
+ int *ret_lock_fd) {
+
+ static const uint8_t hash_key[16] = {
+ 0xd4, 0xd7, 0x33, 0xa7, 0x4d, 0xd3, 0x42, 0xcd,
+ 0xaa, 0xe9, 0x45, 0xd0, 0xfb, 0xec, 0x79, 0xee,
+ };
+
+ _cleanup_(uid_range_freep) UIDRange *valid_range = NULL;
+ uid_t candidate, uidmin, uidmax, uidmask;
+ unsigned n_tries = 100;
+ int r;
+
+ /* Returns the following error codes:
+ *
+ * EBUSY → all UID candidates we checked are already taken
+ * EEXIST → the name for the userns already exists
+ * EDEADLK → the userns is already registered in the registry
+ */
+
+ assert(registry_dir_fd >= 0);
+ assert(info);
+
+ switch (info->size) {
+
+ case 0x10000U:
+ uidmin = CONTAINER_UID_BASE_MIN;
+ uidmax = CONTAINER_UID_BASE_MAX;
+ uidmask = (uid_t) UINT32_C(0xFFFF0000);
+ break;
+
+ case 1U:
+ uidmin = DYNAMIC_UID_MIN;
+ uidmax = DYNAMIC_UID_MAX;
+ uidmask = (uid_t) UINT32_C(0xFFFFFFFF);
+ break;
+
+ default:
+ assert_not_reached();
+ }
+
+ r = uid_range_load_userns(/* path= */ NULL, UID_RANGE_USERNS_INSIDE, &valid_range);
+ if (r < 0)
+ return r;
+
+ /* Check early whether we have any chance at all given our own uid range */
+ if (!uid_range_overlaps(valid_range, uidmin, uidmax))
+ return log_debug_errno(SYNTHETIC_ERRNO(EHOSTDOWN), "Relevant UID range not delegated, can't allocate.");
+
+ _cleanup_close_ int lock_fd = -EBADF;
+ lock_fd = userns_registry_lock(registry_dir_fd);
+ if (lock_fd < 0)
+ return log_debug_errno(lock_fd, "Failed to open nsresource registry lock file: %m");
+
+ /* Enforce limit on user namespaces per UID */
+ r = userns_registry_per_uid(registry_dir_fd, info->owner);
+ if (r < 0)
+ return log_debug_errno(r, "Failed to determine number of currently registered user namespaces per UID " UID_FMT ": %m", info->owner);
+ if (r >= USERNS_PER_UID)
+ return log_debug_errno(SYNTHETIC_ERRNO(EUSERS), "User already registered %i user namespaces, refusing.", r);
+
+ r = userns_registry_inode_exists(registry_dir_fd, info->userns_inode);
+ if (r < 0)
+ return r;
+ if (r > 0)
+ return -EDEADLK;
+
+ r = name_is_available(registry_dir_fd, info->name);
+ if (r < 0)
+ return r;
+ if (r == 0)
+ return -EEXIST;
+
+ for (candidate = siphash24_string(info->name, hash_key) & UINT32_MAX;; /* Start from a hash of the input name */
+ candidate = random_u32()) { /* Use random values afterwards */
+
+ if (--n_tries <= 0)
+ return log_debug_errno(SYNTHETIC_ERRNO(EBUSY), "Try limit hit, no UIDs available.");
+
+ candidate = (candidate % (uidmax - uidmin)) + uidmin;
+ candidate &= uidmask;
+
+ if (!uid_range_covers(valid_range, candidate, info->size))
+ continue;
+
+ /* We only check the base UID for each range (!) */
+ r = uid_is_available(registry_dir_fd, candidate);
+ if (r < 0)
+ return log_debug_errno(r, "Can't determine if UID range " UID_FMT " is available: %m", candidate);
+ if (r > 0) {
+ info->start = candidate;
+
+ log_debug("Allocating UID range " UID_FMT "…" UID_FMT, candidate, candidate + info->size - 1);
+
+ if (ret_lock_fd)
+ *ret_lock_fd = TAKE_FD(lock_fd);
+
+ return 0;
+ }
+
+ log_debug("UID range " UID_FMT " already taken.", candidate);
+ }
+}
+
+static int write_userns(int usernsfd, const UserNamespaceInfo *userns_info) {
+ _cleanup_(sigkill_waitp) pid_t pid = 0;
+ _cleanup_close_ int efd = -EBADF;
+ uint64_t u;
+ int r;
+
+ assert(usernsfd >= 0);
+ assert(userns_info);
+ assert(uid_is_valid(userns_info->target));
+ assert(uid_is_valid(userns_info->start));
+ assert(userns_info->size > 0);
+ assert(userns_info->size <= UINT32_MAX - userns_info->start);
+
+ efd = eventfd(0, EFD_CLOEXEC);
+ if (efd < 0)
+ return log_error_errno(errno, "Failed to allocate eventfd(): %m");
+
+ r = safe_fork("(sd-userns)", FORK_RESET_SIGNALS|FORK_DEATHSIG_SIGKILL|FORK_LOG, &pid);
+ if (r < 0)
+ return r;
+ if (r == 0) {
+ /* child */
+
+ if (setns(usernsfd, CLONE_NEWUSER) < 0) {
+ log_error_errno(errno, "Failed to join user namespace: %m");
+ goto child_fail;
+ }
+
+ if (eventfd_write(efd, 1) < 0) {
+ log_error_errno(errno, "Failed to ping event fd: %m");
+ goto child_fail;
+ }
+
+ freeze();
+
+ child_fail:
+ _exit(EXIT_FAILURE);
+ }
+
+ /* Wait until child joined the user namespace */
+ if (eventfd_read(efd, &u) < 0)
+ return log_error_errno(errno, "Failed to wait for event fd: %m");
+
+ /* Now write mapping */
+
+ _cleanup_free_ char *pmap = NULL;
+
+ if (asprintf(&pmap, "/proc/" PID_FMT "/uid_map", pid) < 0)
+ return log_oom();
+
+ r = write_string_filef(pmap, 0, UID_FMT " " UID_FMT " " UID_FMT "\n", userns_info->target, userns_info->start, userns_info->size);
+ if (r < 0)
+ return log_error_errno(r, "Failed to write 'uid_map' file of user namespace: %m");
+
+ pmap = mfree(pmap);
+ if (asprintf(&pmap, "/proc/" PID_FMT "/gid_map", pid) < 0)
+ return log_oom();
+
+ r = write_string_filef(pmap, 0, GID_FMT " " GID_FMT " " GID_FMT "\n", (gid_t) userns_info->target, (gid_t) userns_info->start, (gid_t) userns_info->size);
+ if (r < 0)
+ return log_error_errno(r, "Failed to write 'gid_map' file of user namespace: %m");
+
+ /* We are done! */
+
+ log_debug("Successfully configured user namespace.");
+ return 0;
+}
+
+static int test_userns_api_support(Varlink *link) {
+ int r;
+
+ assert(link);
+
+ /* We only expose the userns API if our manager daemon told us this OK to do. It will set this
+ * boolean only if it managed to set up BPF correctly for itself (i.e. watches for userns going away
+ * via BPF APIs). This should make very sure we don't accidentally allow any of the userns stuff to
+ * go through without the BPF LSM in effect. */
+
+ r = getenv_bool("NSRESOURCE_API");
+ if (r < 0)
+ return log_error_errno(r, "Failed to parse $NSRESOURCE_API: %m");
+ if (r == 0)
+ return varlink_error(link, "io.systemd.NamespaceResource.UserNamespaceInterfaceNotSupported", NULL);
+
+ return 0;
+}
+
+static int validate_name(Varlink *link, const char *name, char **ret) {
+ _cleanup_free_ char *un = NULL;
+ int r;
+
+ assert(link);
+ assert(name);
+ assert(ret);
+
+ uid_t peer_uid;
+ r = varlink_get_peer_uid(link, &peer_uid);
+ if (r < 0)
+ return r;
+
+ if (peer_uid == 0) {
+ if (!userns_name_is_valid(name))
+ return varlink_error_invalid_parameter_name(link, "name");
+
+ un = strdup(name);
+ if (!un)
+ return -ENOMEM;
+ } else {
+ /* The the client is not root then prefix the name with the UID of the peer, so that they
+ * live in separate namespaces and cannot steal each other's names. */
+
+ if (asprintf(&un, UID_FMT "-%s", peer_uid, name) < 0)
+ return -ENOMEM;
+
+ if (!userns_name_is_valid(un))
+ return varlink_error_invalid_parameter_name(link, "name");
+ }
+
+ *ret = TAKE_PTR(un);
+ return 0;
+}
+
+static int validate_target_and_size(Varlink *link, unsigned target, unsigned size) {
+ assert(link);
+
+ if (!IN_SET(size, 1U, 0x10000))
+ return varlink_error_invalid_parameter_name(link, "size");
+
+ if (!uid_is_valid(target) || target > UINT32_MAX - size)
+ return varlink_error_invalid_parameter_name(link, "target");
+
+ return 0;
+}
+
+static int validate_userns(Varlink *link, int userns_fd) {
+ int r;
+
+ assert(link);
+ assert(userns_fd >= 0);
+
+ r = fd_verify_safe_flags(userns_fd);
+ if (r < 0)
+ return log_debug_errno(r, "User namespace file descriptor has unsafe flags set: %m");
+
+ /* Validate this is actually a valid user namespace fd */
+ r = fd_is_ns(userns_fd, CLONE_NEWUSER);
+ if (r < 0)
+ return log_debug_errno(r, "Failed to check if user namespace fd is actually a user namespace: %m");
+ if (r == 0)
+ return varlink_error_invalid_parameter_name(link, "userNamespaceFileDescriptor");
+
+ /* And refuse the thing if it is our own */
+ r = is_our_namespace(userns_fd, NAMESPACE_USER);
+ if (r < 0)
+ return log_debug_errno(r, "Failed to check if user namespace fd refers to our own user namespace: %m");
+ if (r > 0)
+ return varlink_error_invalid_parameter_name(link, "userNamespaceFileDescriptor");
+
+ uid_t peer_uid;
+ r = varlink_get_peer_uid(link, &peer_uid);
+ if (r < 0)
+ return log_debug_errno(r, "Failed to acquire peer UID: %m");
+
+ if (peer_uid != 0) {
+ /* Refuse if the userns is not actually owned by our client. */
+ uid_t owner_uid;
+ if (ioctl(userns_fd, NS_GET_OWNER_UID, &owner_uid) < 0)
+ return log_debug_errno(errno, "Failed to get owner UID of user namespace: %m");
+
+ if (owner_uid != peer_uid)
+ return varlink_error_invalid_parameter_name(link, "userNamespaceFileDescriptor");
+ }
+
+ return 0;
+}
+
+static int validate_userns_is_empty(Varlink *link, int userns_fd) {
+ int r;
+
+ assert(link);
+ assert(userns_fd >= 0);
+
+ _cleanup_(uid_range_freep) UIDRange *range = NULL;
+ r = uid_range_load_userns_by_fd(userns_fd, UID_RANGE_USERNS_OUTSIDE, &range);
+ if (r < 0)
+ return log_debug_errno(r, "Failed to read userns UID range: %m");
+
+ if (!uid_range_is_empty(range))
+ return varlink_error_invalid_parameter_name(link, "userNamespaceFileDescriptor");
+
+ range = uid_range_free(range);
+ r = uid_range_load_userns_by_fd(userns_fd, GID_RANGE_USERNS_OUTSIDE, &range);
+ if (r < 0)
+ return log_debug_errno(r, "Failed to read userns GID range: %m");
+
+ if (!uid_range_is_empty(range))
+ return varlink_error_invalid_parameter_name(link, "userNamespaceFileDescriptor");
+
+ return 0;
+}
+
+typedef struct AllocateParameters {
+ const char *name;
+ unsigned size;
+ unsigned target;
+ unsigned userns_fd_idx;
+} AllocateParameters;
+
+static int vl_method_allocate_user_range(Varlink *link, JsonVariant *parameters, VarlinkMethodFlags flags, void *userdata) {
+
+ static const JsonDispatch dispatch_table[] = {
+ { "name", JSON_VARIANT_STRING, json_dispatch_const_string, offsetof(AllocateParameters, name), JSON_MANDATORY },
+ { "size", _JSON_VARIANT_TYPE_INVALID, json_dispatch_uint, offsetof(AllocateParameters, size), JSON_MANDATORY },
+ { "target", _JSON_VARIANT_TYPE_INVALID, json_dispatch_uint, offsetof(AllocateParameters, target), 0 },
+ { "userNamespaceFileDescriptor", _JSON_VARIANT_TYPE_INVALID, json_dispatch_uint, offsetof(AllocateParameters, userns_fd_idx), JSON_MANDATORY },
+ {}
+ };
+
+ struct userns_restrict_bpf **bpf = ASSERT_PTR(userdata);
+ _cleanup_close_ int userns_fd = -EBADF, registry_dir_fd = -EBADF, lock_fd = -EBADF;
+ _cleanup_free_ char *userns_name = NULL;
+ uid_t peer_uid;
+ struct stat userns_st;
+ AllocateParameters p = {
+ .size = UINT_MAX,
+ .userns_fd_idx = UINT_MAX,
+ };
+ int r;
+
+ assert(link);
+ assert(parameters);
+
+ r = test_userns_api_support(link);
+ if (r != 0)
+ return r;
+
+ r = varlink_dispatch(link, parameters, dispatch_table, &p);
+ if (r != 0)
+ return r;
+
+ r = validate_name(link, p.name, &userns_name);
+ if (r != 0)
+ return r;
+
+ r = validate_target_and_size(link, p.target, p.size);
+ if (r != 0)
+ return r;
+
+ userns_fd = varlink_take_fd(link, p.userns_fd_idx);
+ if (userns_fd < 0)
+ return log_debug_errno(userns_fd, "Failed to take user namespace fd from Varlink connection: %m");
+
+ r = validate_userns(link, userns_fd);
+ if (r != 0)
+ return r;
+
+ r = validate_userns_is_empty(link, userns_fd);
+ if (r != 0)
+ return r;
+
+ if (fstat(userns_fd, &userns_st) < 0)
+ return log_debug_errno(errno, "Failed to fstat() user namespace fd: %m");
+
+ r = varlink_get_peer_uid(link, &peer_uid);
+ if (r < 0)
+ return r;
+
+ if (!*bpf) {
+ r = userns_restrict_install(/* pin= */ true, bpf);
+ if (r < 0)
+ return r;
+ }
+
+ registry_dir_fd = userns_registry_open_fd();
+ if (registry_dir_fd < 0)
+ return registry_dir_fd;
+
+ _cleanup_(userns_info_freep) UserNamespaceInfo *userns_info = userns_info_new();
+ if (!userns_info)
+ return -ENOMEM;
+
+ userns_info->name = TAKE_PTR(userns_name);
+ if (!userns_info->name)
+ return -ENOMEM;
+
+ userns_info->owner = peer_uid;
+ userns_info->userns_inode = userns_st.st_ino;
+ userns_info->size = p.size;
+ userns_info->target = p.target;
+
+ r = allocate_now(registry_dir_fd, userns_info, &lock_fd);
+ if (r == -EHOSTDOWN) /* The needed UID range is not delegated to us */
+ return varlink_error(link, "io.systemd.NamespaceResource.DynamicRangeUnavailable", NULL);
+ if (r == -EBUSY) /* All used up */
+ return varlink_error(link, "io.systemd.NamespaceResource.NoDynamicRange", NULL);
+ if (r == -EDEADLK)
+ return varlink_error(link, "io.systemd.NamespaceResource.UserNamespaceExists", NULL);
+ if (r == -EEXIST)
+ return varlink_error(link, "io.systemd.NamespaceResource.NameExists", NULL);
+ if (r < 0)
+ return r;
+
+ r = userns_registry_store(registry_dir_fd, userns_info);
+ if (r < 0)
+ return r;
+
+ /* Register the userns in the BPF map with an empty allowlist */
+ r = userns_restrict_put_by_fd(
+ *bpf,
+ userns_fd,
+ /* replace= */ true,
+ /* mount_fds= */ NULL,
+ /* n_mount_fds= */ 0);
+ if (r < 0)
+ goto fail;
+
+ r = write_userns(userns_fd, userns_info);
+ if (r < 0)
+ goto fail;
+
+ lock_fd = safe_close(lock_fd);
+
+ /* Send user namespace and process fd to our manager process, which will watch the process and user namespace */
+ r = sd_pid_notifyf_with_fds(
+ /* pid= */ 0,
+ /* unset_environment= */ false,
+ &userns_fd, 1,
+ "FDSTORE=1\n"
+ "FDNAME=userns-" INO_FMT "\n", userns_info->userns_inode);
+ if (r < 0)
+ goto fail;
+
+ /* Note, we'll not return UID values from the host, since the child might not run in the same
+ * user namespace as us. If they want to know the ranges they should read them off the userns fd, so
+ * that they are translated into their PoV */
+ return varlink_replyb(link, JSON_BUILD_EMPTY_OBJECT);
+
+fail:
+ /* Note: we don't have to clean-up the BPF maps in the error path: the bpf map type used will
+ * automatically do that once the userns inode goes away */
+ userns_registry_remove(registry_dir_fd, userns_info);
+ return r;
+}
+
+static int validate_userns_is_safe(Varlink *link, int userns_fd) {
+ int r;
+
+ assert(link);
+ assert(userns_fd >= 0);
+
+ /* Read the outside UID range and verify it isn't empty */
+ _cleanup_(uid_range_freep) UIDRange *outside_range = NULL;
+ r = uid_range_load_userns_by_fd(userns_fd, UID_RANGE_USERNS_OUTSIDE, &outside_range);
+ if (r < 0)
+ return log_debug_errno(r, "Failed to read userns UID range: %m");
+ if (uid_range_is_empty(outside_range))
+ return varlink_error_invalid_parameter_name(link, "userNamespaceFileDescriptor");
+
+ /* Read the outside GID range and check it is the same as the UID range */
+ _cleanup_(uid_range_freep) UIDRange *outside_range_gid = NULL;
+ r = uid_range_load_userns_by_fd(userns_fd, GID_RANGE_USERNS_OUTSIDE, &outside_range_gid);
+ if (r < 0)
+ return log_debug_errno(r, "Failed to read userns GID range: %m");
+ if (!uid_range_equal(outside_range, outside_range_gid))
+ return varlink_error_invalid_parameter_name(link, "userNamespaceFileDescriptor");
+
+ /* Read the inside UID range, and verify it matches the size of the outside UID range */
+ _cleanup_(uid_range_freep) UIDRange *inside_range = NULL;
+ r = uid_range_load_userns_by_fd(userns_fd, UID_RANGE_USERNS_INSIDE, &inside_range);
+ if (r < 0)
+ return log_debug_errno(r, "Failed to read userns contents: %m");
+ if (uid_range_size(outside_range) != uid_range_size(inside_range))
+ return log_debug_errno(SYNTHETIC_ERRNO(ENOTRECOVERABLE), "Uh, inside and outside UID range sizes don't match.");
+
+ /* Read the inside GID range, and verify it matches the inside UID range */
+ _cleanup_(uid_range_freep) UIDRange *inside_range_gid = NULL;
+ r = uid_range_load_userns_by_fd(userns_fd, GID_RANGE_USERNS_INSIDE, &inside_range_gid);
+ if (r < 0)
+ return log_debug_errno(r, "Failed to read userns contents: %m");
+ if (!uid_range_equal(inside_range, inside_range_gid))
+ return varlink_error_invalid_parameter_name(link, "userNamespaceFileDescriptor");
+
+ uid_t peer_uid;
+ r = varlink_get_peer_uid(link, &peer_uid);
+ if (r < 0)
+ return r;
+
+ uid_t peer_gid;
+ r = varlink_get_peer_gid(link, &peer_gid);
+ if (r < 0)
+ return r;
+
+ /* Insist that the first UID/GID in the range matches the client's UID/GID */
+ if (outside_range->entries[0].start != peer_uid ||
+ outside_range_gid->entries[0].start != peer_gid)
+ return varlink_error_invalid_parameter_name(link, "userNamespaceFileDescriptor");
+
+ /* If there are more than one UID in the range, then also insist that the first UID maps to root inside the userns */
+ if (uid_range_size(outside_range) > 1 && inside_range->entries[0].start != 0)
+ return varlink_error_invalid_parameter_name(link, "userNamespaceFileDescriptor");
+
+ return 0;
+}
+
+typedef struct RegisterParameters {
+ const char *name;
+ unsigned userns_fd_idx;
+} RegisterParameters;
+
+static int vl_method_register_user_namespace(Varlink *link, JsonVariant *parameters, VarlinkMethodFlags flags, void *userdata) {
+
+ static const JsonDispatch dispatch_table[] = {
+ { "name", JSON_VARIANT_STRING, json_dispatch_const_string, offsetof(RegisterParameters, name), JSON_MANDATORY },
+ { "userNamespaceFileDescriptor", _JSON_VARIANT_TYPE_INVALID, json_dispatch_uint, offsetof(RegisterParameters, userns_fd_idx), JSON_MANDATORY },
+ {}
+ };
+
+ struct userns_restrict_bpf **bpf = ASSERT_PTR(userdata);
+ _cleanup_close_ int userns_fd = -EBADF, registry_dir_fd = -EBADF;
+ _cleanup_free_ char *userns_name = NULL;
+ uid_t peer_uid;
+ struct stat userns_st;
+ RegisterParameters p = {
+ .userns_fd_idx = UINT_MAX,
+ };
+ int r;
+
+ assert(link);
+ assert(parameters);
+
+ r = test_userns_api_support(link);
+ if (r != 0)
+ return r;
+
+ r = varlink_dispatch(link, parameters, dispatch_table, &p);
+ if (r != 0)
+ return r;
+
+ r = validate_name(link, p.name, &userns_name);
+ if (r != 0)
+ return r;
+
+ userns_fd = varlink_take_fd(link, p.userns_fd_idx);
+ if (userns_fd < 0)
+ return userns_fd;
+
+ r = validate_userns(link, userns_fd);
+ if (r != 0)
+ return r;
+
+ r = validate_userns_is_safe(link, userns_fd);
+ if (r != 0)
+ return r;
+
+ if (fstat(userns_fd, &userns_st) < 0)
+ return log_debug_errno(errno, "Failed to fstat() user namespace fd: %m");
+
+ r = varlink_get_peer_uid(link, &peer_uid);
+ if (r < 0)
+ return r;
+
+ if (!*bpf) {
+ r = userns_restrict_install(/* pin= */ true, bpf);
+ if (r < 0)
+ return r;
+ }
+
+ registry_dir_fd = userns_registry_open_fd();
+ if (registry_dir_fd < 0)
+ return registry_dir_fd;
+
+ _cleanup_close_ int lock_fd = -EBADF;
+ lock_fd = userns_registry_lock(registry_dir_fd);
+ if (lock_fd < 0)
+ return log_debug_errno(lock_fd, "Failed to open nsresource registry lock file: %m");
+
+ r = userns_registry_inode_exists(registry_dir_fd, userns_st.st_ino);
+ if (r < 0)
+ return r;
+ if (r > 0)
+ return varlink_error(link, "io.systemd.NamespaceResource.UserNamespaceExists", NULL);
+
+ r = name_is_available(registry_dir_fd, userns_name);
+ if (r < 0)
+ return r;
+ if (r == 0)
+ return varlink_error(link, "io.systemd.NamespaceResource.NameExists", NULL);
+
+ _cleanup_(userns_info_freep) UserNamespaceInfo *userns_info = userns_info_new();
+ if (!userns_info)
+ return -ENOMEM;
+
+ userns_info->name = TAKE_PTR(userns_name);
+ if (!userns_info->name)
+ return -ENOMEM;
+
+ userns_info->owner = peer_uid;
+ userns_info->userns_inode = userns_st.st_ino;
+
+ r = userns_registry_store(registry_dir_fd, userns_info);
+ if (r < 0)
+ return log_debug_errno(r, "Failed to update userns registry: %m");
+
+ /* Register the userns in the BPF map with an empty allowlist */
+ r = userns_restrict_put_by_fd(
+ *bpf,
+ userns_fd,
+ /* replace= */ true,
+ /* mount_fds= */ NULL,
+ /* n_mount_fds= */ 0);
+ if (r < 0)
+ goto fail;
+
+ /* Send user namespace and process fd to our manager process, which will watch the process and user namespace */
+ r = sd_pid_notifyf_with_fds(
+ /* pid= */ 0,
+ /* unset_environment= */ false,
+ &userns_fd, 1,
+ "FDSTORE=1\n"
+ "FDNAME=userns-" INO_FMT "\n", userns_info->userns_inode);
+ if (r < 0)
+ goto fail;
+
+ return varlink_replyb(link, JSON_BUILD_EMPTY_OBJECT);
+
+fail:
+ userns_registry_remove(registry_dir_fd, userns_info);
+ return r;
+}
+
+typedef struct AddMountParameters {
+ unsigned userns_fd_idx;
+ unsigned mount_fd_idx;
+} AddMountParameters;
+
+static int vl_method_add_mount_to_user_namespace(Varlink *link, JsonVariant *parameters, VarlinkMethodFlags flags, void *userdata) {
+
+ static const JsonDispatch parameter_dispatch_table[] = {
+ { "userNamespaceFileDescriptor", _JSON_VARIANT_TYPE_INVALID, json_dispatch_uint, offsetof(AddMountParameters, userns_fd_idx), JSON_MANDATORY },
+ { "mountFileDescriptor", _JSON_VARIANT_TYPE_INVALID, json_dispatch_uint, offsetof(AddMountParameters, mount_fd_idx), JSON_MANDATORY },
+ {}
+ };
+
+ _cleanup_close_ int userns_fd = -EBADF, mount_fd = -EBADF, registry_dir_fd = -EBADF;
+ struct userns_restrict_bpf **bpf = ASSERT_PTR(userdata);
+ AddMountParameters p = {
+ .userns_fd_idx = UINT_MAX,
+ .mount_fd_idx = UINT_MAX,
+ };
+ int r, mnt_id = 0;
+ struct stat userns_st;
+ uid_t peer_uid;
+
+ assert(link);
+ assert(parameters);
+
+ r = test_userns_api_support(link);
+ if (r != 0)
+ return r;
+
+ /* Allowlisting arbitrary mounts is a privileged operation */
+ r = varlink_get_peer_uid(link, &peer_uid);
+ if (r < 0)
+ return r;
+ if (peer_uid != 0)
+ return varlink_error(link, VARLINK_ERROR_PERMISSION_DENIED, NULL);
+
+ r = varlink_dispatch(link, parameters, parameter_dispatch_table, &p);
+ if (r != 0)
+ return r;
+
+ userns_fd = varlink_take_fd(link, p.userns_fd_idx);
+ if (userns_fd < 0)
+ return userns_fd;
+
+ r = validate_userns(link, userns_fd);
+ if (r != 0)
+ return r;
+
+ if (fstat(userns_fd, &userns_st) < 0)
+ return -errno;
+
+ mount_fd = varlink_take_fd(link, p.mount_fd_idx);
+ if (mount_fd < 0)
+ return mount_fd;
+
+ r = fd_verify_safe_flags_full(mount_fd, O_PATH|O_DIRECTORY);
+ if (r < 0)
+ return log_debug_errno(r, "Mount file descriptor has unsafe flags set: %m");
+
+ r = fd_verify_directory(mount_fd);
+ if (r < 0)
+ return r;
+
+ r = path_get_mnt_id_at(mount_fd, NULL, &mnt_id);
+ if (r < 0)
+ return r;
+
+ registry_dir_fd = userns_registry_open_fd();
+ if (registry_dir_fd < 0)
+ return registry_dir_fd;
+
+ _cleanup_close_ int lock_fd = -EBADF;
+ lock_fd = userns_registry_lock(registry_dir_fd);
+ if (lock_fd < 0)
+ return log_debug_errno(lock_fd, "Failed to open nsresource registry lock file: %m");
+
+ _cleanup_(userns_info_freep) UserNamespaceInfo *userns_info = NULL;
+ r = userns_registry_load_by_userns_inode(
+ registry_dir_fd,
+ userns_st.st_ino,
+ &userns_info);
+ if (r == -ENOENT)
+ return varlink_error(link, "io.systemd.NamespaceResource.UserNamespaceNotRegistered", NULL);
+ if (r < 0)
+ return r;
+
+ if (!*bpf) {
+ r = userns_restrict_install(/* pin= */ true, bpf);
+ if (r < 0)
+ return r;
+ }
+
+ /* Pin the mount fd */
+ r = sd_pid_notifyf_with_fds(
+ /* pid= */ 0,
+ /* unset_environment= */ false,
+ &mount_fd, 1,
+ "FDSTORE=1\n"
+ "FDNAME=userns-" INO_FMT "\n", userns_st.st_ino);
+ if (r < 0)
+ return r;
+
+ /* Add this mount to the user namespace's BPF map allowlist entry. */
+ r = userns_restrict_put_by_fd(
+ *bpf,
+ userns_fd,
+ /* replace= */ false,
+ &mount_fd,
+ 1);
+ if (r < 0)
+ return r;
+
+ if (userns_info->size > 0)
+ log_debug("Granting access to mount %i to user namespace " INO_FMT " ('%s' @ UID " UID_FMT ")",
+ mnt_id, userns_st.st_ino, userns_info->name, userns_info->start);
+ else
+ log_debug("Granting access to mount %i to user namespace " INO_FMT " ('%s')",
+ mnt_id, userns_st.st_ino, userns_info->name);
+
+ return varlink_replyb(link, JSON_BUILD_EMPTY_OBJECT);
+}
+
+static int validate_cgroup(Varlink *link, int fd, uint64_t *ret_cgroup_id) {
+ int r;
+
+ assert(link);
+ assert(fd >= 0);
+ assert(ret_cgroup_id);
+
+ r = fd_verify_safe_flags_full(fd, O_DIRECTORY);
+ if (r < 0)
+ return log_debug_errno(r, "Control group file descriptor has unsafe flags set: %m");
+
+ r = fd_verify_directory(fd);
+ if (r < 0)
+ return log_debug_errno(r, "Verification that cgroup fd refers to directory failed: %m");
+
+ r = fd_is_fs_type(fd, CGROUP2_SUPER_MAGIC);
+ if (r < 0)
+ return log_debug_errno(r, "Failed to check if cgroup fd actually refers to cgroupfs: %m");
+ if (r == 0)
+ return varlink_error_invalid_parameter_name(link, "controlGroupFileDescriptor");
+
+ r = cg_fd_get_cgroupid(fd, ret_cgroup_id);
+ if (r < 0)
+ return log_debug_errno(r, "Failed to read cgroup ID from cgroupfs: %m");
+
+ return 0;
+}
+
+typedef struct AddCGroupParameters {
+ unsigned userns_fd_idx;
+ unsigned cgroup_fd_idx;
+} AddCGroupParameters;
+
+static int vl_method_add_cgroup_to_user_namespace(Varlink *link, JsonVariant *parameters, VarlinkMethodFlags flags, void *userdata) {
+ static const JsonDispatch parameter_dispatch_table[] = {
+ { "userNamespaceFileDescriptor", _JSON_VARIANT_TYPE_INVALID, json_dispatch_uint, offsetof(AddCGroupParameters, userns_fd_idx), JSON_MANDATORY },
+ { "controlGroupFileDescriptor", _JSON_VARIANT_TYPE_INVALID, json_dispatch_uint, offsetof(AddCGroupParameters, cgroup_fd_idx), JSON_MANDATORY },
+ {}
+ };
+
+ _cleanup_close_ int userns_fd = -EBADF, cgroup_fd = -EBADF, registry_dir_fd = -EBADF;
+ AddCGroupParameters p = {
+ .userns_fd_idx = UINT_MAX,
+ .cgroup_fd_idx = UINT_MAX,
+ };
+ _cleanup_(userns_info_freep) UserNamespaceInfo *userns_info = NULL;
+ struct stat userns_st, cgroup_st;
+ uid_t peer_uid;
+ int r;
+
+ assert(link);
+ assert(parameters);
+
+ r = test_userns_api_support(link);
+ if (r != 0)
+ return r;
+
+ r = varlink_dispatch(link, parameters, parameter_dispatch_table, &p);
+ if (r != 0)
+ return r;
+
+ userns_fd = varlink_take_fd(link, p.userns_fd_idx);
+ if (userns_fd < 0)
+ return log_debug_errno(userns_fd, "Failed to take user namespace fd from Varlink connection: %m");
+
+ r = validate_userns(link, userns_fd);
+ if (r != 0)
+ return r;
+
+ if (fstat(userns_fd, &userns_st) < 0)
+ return log_debug_errno(errno, "Failed to fstat() user namespace fd: %m");
+
+ cgroup_fd = varlink_take_fd(link, p.cgroup_fd_idx);
+ if (cgroup_fd < 0)
+ return log_debug_errno(cgroup_fd, "Failed to take cgroup fd from Varlink connection: %m");
+
+ uint64_t cgroup_id;
+ r = validate_cgroup(link, cgroup_fd, &cgroup_id);
+ if (r != 0)
+ return r;
+
+ if (fstat(cgroup_fd, &cgroup_st) < 0)
+ return log_debug_errno(errno, "Failed to fstat() cgroup fd: %m");
+
+ registry_dir_fd = userns_registry_open_fd();
+ if (registry_dir_fd < 0)
+ return registry_dir_fd;
+
+ _cleanup_close_ int lock_fd = -EBADF;
+ lock_fd = userns_registry_lock(registry_dir_fd);
+ if (lock_fd < 0)
+ return lock_fd;
+
+ r = userns_registry_load_by_userns_inode(
+ registry_dir_fd,
+ userns_st.st_ino,
+ &userns_info);
+ if (r == -ENOENT)
+ return varlink_error(link, "io.systemd.NamespaceResource.UserNamespaceNotRegistered", NULL);
+ if (r < 0)
+ return r;
+
+ /* The user namespace must have a user assigned */
+ if (userns_info->size == 0)
+ return varlink_error(link, "io.systemd.NamespaceResource.UserNamespaceWithoutUserRange", NULL);
+ if (userns_info_has_cgroup(userns_info, cgroup_id))
+ return varlink_error(link, "io.systemd.NamespaceResource.ControlGroupAlreadyAdded", NULL);
+ if (userns_info->n_cgroups > USER_NAMESPACE_CGROUPS_DELEGATE_MAX)
+ return varlink_error(link, "io.systemd.NamespaceResource.TooManyControlGroups", NULL);
+
+ /* Registering a cgroup for this client is only allowed for the root or the owner of a userns */
+ r = varlink_get_peer_uid(link, &peer_uid);
+ if (r < 0)
+ return log_debug_errno(r, "Failed to get connection peer: %m");
+ if (peer_uid != 0) {
+ if (peer_uid != userns_info->owner)
+ return varlink_error(link, VARLINK_ERROR_PERMISSION_DENIED, NULL);
+
+ /* The cgroup must be owned by the owner of the userns */
+ if (cgroup_st.st_uid != userns_info->owner)
+ return varlink_error(link, VARLINK_ERROR_PERMISSION_DENIED, NULL);
+ }
+
+ r = userns_info_add_cgroup(userns_info, cgroup_id);
+ if (r < 0)
+ return r;
+
+ r = userns_registry_store(registry_dir_fd, userns_info);
+ if (r < 0)
+ return r;
+
+ if (fchown(cgroup_fd, userns_info->start, userns_info->start) < 0)
+ return log_debug_errno(errno, "Failed to change ownership of cgroup: %m");
+
+ if (fchmod(cgroup_fd, 0755) < 0)
+ return log_debug_errno(errno, "Failed to change access mode of cgroup: %m");
+
+ FOREACH_STRING(attr, "cgroup.procs", "cgroup.subtree_control", "cgroup.threads") {
+ (void) fchmodat(cgroup_fd, attr, 0644, AT_SYMLINK_NOFOLLOW);
+ (void) fchownat(cgroup_fd, attr, userns_info->start, userns_info->start, AT_SYMLINK_NOFOLLOW);
+ }
+
+ log_debug("Granting ownership to cgroup %" PRIu64 " to userns " INO_FMT " ('%s' @ UID " UID_FMT ")",
+ cgroup_id, userns_st.st_ino, userns_info->name, userns_info->start);
+
+ return varlink_replyb(link, JSON_BUILD_EMPTY_OBJECT);
+}
+
+static uint64_t hash_ifname_id(UserNamespaceInfo *userns_info, const char *ifname) {
+ struct siphash state;
+
+ assert(userns_info);
+
+ siphash24_init(&state, (const uint8_t[]) { 0xc4, 0x6c, 0x96, 0xe8, 0xad, 0x37, 0x4d, 0x5f, 0xa1, 0xae, 0xfe, 0x70, 0x40, 0xed, 0x41, 0x5f });
+ siphash24_compress_string(userns_info->name, &state);
+ siphash24_compress_byte(0, &state); /* separator */
+ siphash24_compress_string(strempty(ifname), &state);
+
+ return siphash24_finalize(&state);
+}
+
+static void hash_ether_addr(UserNamespaceInfo *userns_info, const char *ifname, uint64_t n, struct ether_addr *ret) {
+ struct siphash state;
+ uint64_t h;
+
+ assert(userns_info);
+ assert(ret);
+
+ siphash24_init(&state, (const uint8_t[]) { 0x36, 0xaa, 0xd1, 0x69, 0xc7, 0xe5, 0x4c, 0xaa, 0x1e, 0xb2, 0x9e, 0xb3, 0x3a, 0x6b, 0xd4, 0x71 });
+ siphash24_compress_string(userns_info->name, &state);
+ siphash24_compress_byte(0, &state); /* separator */
+ siphash24_compress_string(strempty(ifname), &state);
+ siphash24_compress_byte(0, &state); /* separator */
+ n = htole64(n); /* add the 'index' to the mix in an endianess-independent fashion */
+ siphash24_compress(&n, sizeof(n), &state);
+
+ h = htole64(siphash24_finalize(&state));
+
+ assert(sizeof(h) >= sizeof_field(struct ether_addr, ether_addr_octet));
+
+ memcpy(ret->ether_addr_octet, &h, sizeof_field(struct ether_addr, ether_addr_octet));
+ ether_addr_mark_random(ret);
+}
+
+static int create_veth(
+ int netns_fd,
+ const char *ifname_host,
+ const char *altifname_host,
+ struct ether_addr *mac_host,
+ const char *ifname_namespace,
+ struct ether_addr *mac_namespace) {
+
+ int r;
+
+ assert(netns_fd >= 0);
+ assert(ifname_host);
+ assert(mac_host);
+ assert(ifname_namespace);
+ assert(mac_namespace);
+
+ log_debug("Creating veth link on host %s (%s) with address %s to container as %s with address %s",
+ ifname_host, strna(altifname_host), ETHER_ADDR_TO_STR(mac_host),
+ ifname_namespace, ETHER_ADDR_TO_STR(mac_namespace));
+
+ _cleanup_(sd_netlink_unrefp) sd_netlink *rtnl = NULL;
+ r = sd_netlink_open(&rtnl);
+ if (r < 0)
+ return r;
+
+ _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *m = NULL;
+ r = sd_rtnl_message_new_link(rtnl, &m, RTM_NEWLINK, 0);
+ if (r < 0)
+ return log_error_errno(r, "Failed to allocate netlink message: %m");
+
+ r = sd_netlink_message_append_string(m, IFLA_IFNAME, ifname_host);
+ if (r < 0)
+ return log_error_errno(r, "Failed to add netlink interface name: %m");
+
+ r = sd_netlink_message_append_ether_addr(m, IFLA_ADDRESS, mac_host);
+ if (r < 0)
+ return log_error_errno(r, "Failed to add netlink MAC address: %m");
+
+ r = sd_netlink_message_open_container(m, IFLA_LINKINFO);
+ if (r < 0)
+ return log_error_errno(r, "Failed to open netlink container: %m");
+
+ r = sd_netlink_message_open_container_union(m, IFLA_INFO_DATA, "veth");
+ if (r < 0)
+ return log_error_errno(r, "Failed to open netlink container: %m");
+
+ r = sd_netlink_message_open_container(m, VETH_INFO_PEER);
+ if (r < 0)
+ return log_error_errno(r, "Failed to open netlink container: %m");
+
+ r = sd_netlink_message_append_string(m, IFLA_IFNAME, ifname_namespace);
+ if (r < 0)
+ return log_error_errno(r, "Failed to add netlink interface name: %m");
+
+ r = sd_netlink_message_append_ether_addr(m, IFLA_ADDRESS, mac_namespace);
+ if (r < 0)
+ return log_error_errno(r, "Failed to add netlink MAC address: %m");
+
+ r = sd_netlink_message_append_u32(m, IFLA_NET_NS_FD, netns_fd);
+ if (r < 0)
+ return log_error_errno(r, "Failed to add netlink namespace field: %m");
+
+ r = sd_netlink_message_close_container(m);
+ if (r < 0)
+ return log_error_errno(r, "Failed to close netlink container: %m");
+
+ r = sd_netlink_message_close_container(m);
+ if (r < 0)
+ return log_error_errno(r, "Failed to close netlink container: %m");
+
+ r = sd_netlink_message_close_container(m);
+ if (r < 0)
+ return log_error_errno(r, "Failed to close netlink container: %m");
+
+ r = sd_netlink_call(rtnl, m, 0, NULL);
+ if (r < 0)
+ return log_error_errno(r, "Failed to add new veth interfaces (%s:%s): %m", ifname_host, ifname_namespace);
+
+ r = rtnl_set_link_alternative_names_by_ifname(&rtnl, ifname_host, STRV_MAKE(altifname_host));
+ if (r < 0)
+ log_warning_errno(r, "Failed to set alternative interface name to '%s', ignoring: %m", altifname_host);
+
+ return 0;
+}
+
+static int validate_netns(Varlink *link, int userns_fd, int netns_fd) {
+ int r;
+
+ assert(link);
+ assert(userns_fd >= 0);
+ assert(netns_fd >= 0);
+
+ r = fd_verify_safe_flags(netns_fd);
+ if (r < 0)
+ return log_debug_errno(r, "Network namespace file descriptor has unsafe flags set: %m");
+
+ /* Validate this is actually a valid network namespace fd */
+ r = fd_is_ns(netns_fd, CLONE_NEWNET);
+ if (r < 0)
+ return r;
+ if (r == 0)
+ return varlink_error_invalid_parameter_name(link, "networkNamespaceFileDescriptor");
+
+ /* And refuse the thing if it is our own */
+ r = is_our_namespace(netns_fd, NAMESPACE_NET);
+ if (r < 0)
+ return r;
+ if (r > 0)
+ return varlink_error_invalid_parameter_name(link, "networkNamespaceFileDescriptor");
+
+ /* Check if the netns actually belongs to the userns */
+ _cleanup_close_ int owner_userns_fd = -EBADF;
+ owner_userns_fd = ioctl(netns_fd, NS_GET_USERNS);
+ if (owner_userns_fd < 0)
+ return -errno;
+
+ r = inode_same_at(owner_userns_fd, /* path_a= */ NULL, userns_fd, /* path_b= */ NULL, AT_EMPTY_PATH);
+ if (r < 0)
+ return r;
+ if (r == 0)
+ return varlink_error_invalid_parameter_name(link, "networkNamespaceFileDescriptor");
+
+ uid_t peer_uid;
+ r = varlink_get_peer_uid(link, &peer_uid);
+ if (r < 0)
+ return r;
+
+ if (peer_uid != 0) {
+ /* Refuse if the netns is not actually owned by our client. */
+
+ uid_t owner_uid;
+ if (ioctl(owner_userns_fd, NS_GET_OWNER_UID, &owner_uid) < 0)
+ return -errno;
+
+ if (owner_uid != peer_uid)
+ return varlink_error_invalid_parameter_name(link, "networkNamespaceFileDescriptor");
+ }
+
+ return 0;
+}
+
+typedef struct AddNetworkParameters {
+ unsigned userns_fd_idx;
+ unsigned netns_fd_idx;
+ const char *ifname;
+ const char *mode;
+} AddNetworkParameters;
+
+static int vl_method_add_netif_to_user_namespace(Varlink *link, JsonVariant *parameters, VarlinkMethodFlags flags, void *userdata) {
+ static const JsonDispatch parameter_dispatch_table[] = {
+ { "userNamespaceFileDescriptor", _JSON_VARIANT_TYPE_INVALID, json_dispatch_uint, offsetof(AddNetworkParameters, userns_fd_idx), JSON_MANDATORY },
+ { "networkNamespaceFileDescriptor", _JSON_VARIANT_TYPE_INVALID, json_dispatch_uint, offsetof(AddNetworkParameters, netns_fd_idx), JSON_MANDATORY },
+ { "namespaceInterfaceName", JSON_VARIANT_STRING, json_dispatch_const_string, offsetof(AddNetworkParameters, ifname), 0 },
+ { "mode", JSON_VARIANT_STRING, json_dispatch_const_string, offsetof(AddNetworkParameters, mode), JSON_MANDATORY },
+ {}
+ };
+
+ _cleanup_close_ int userns_fd = -EBADF, netns_fd = -EBADF, registry_dir_fd = -EBADF;
+ AddNetworkParameters p = {
+ .userns_fd_idx = UINT_MAX,
+ };
+ _cleanup_(userns_info_freep) UserNamespaceInfo *userns_info = NULL;
+ struct stat userns_st;
+ uid_t peer_uid;
+ int r;
+
+ assert(link);
+ assert(parameters);
+
+ r = test_userns_api_support(link);
+ if (r != 0)
+ return r;
+
+ r = varlink_dispatch(link, parameters, parameter_dispatch_table, &p);
+ if (r != 0)
+ return r;
+
+ userns_fd = varlink_take_fd(link, p.userns_fd_idx);
+ if (userns_fd < 0)
+ return userns_fd;
+
+ r = validate_userns(link, userns_fd);
+ if (r != 0)
+ return r;
+
+ if (fstat(userns_fd, &userns_st) < 0)
+ return -errno;
+
+ netns_fd = varlink_take_fd(link, p.netns_fd_idx);
+ if (netns_fd < 0)
+ return netns_fd;
+
+ r = validate_netns(link, userns_fd, netns_fd);
+ if (r != 0)
+ return r;
+
+ if (!streq_ptr(p.mode, "veth"))
+ return varlink_error_invalid_parameter_name(link, "mode");
+
+ if (p.ifname && !ifname_valid(p.ifname))
+ return varlink_error_invalid_parameter_name(link, "interfaceName");
+
+ registry_dir_fd = userns_registry_open_fd();
+ if (registry_dir_fd < 0)
+ return registry_dir_fd;
+
+ _cleanup_close_ int lock_fd = -EBADF;
+ lock_fd = userns_registry_lock(registry_dir_fd);
+ if (lock_fd < 0)
+ return log_debug_errno(lock_fd, "Failed to open nsresource registry lock file: %m");
+
+ r = userns_registry_load_by_userns_inode(
+ registry_dir_fd,
+ userns_st.st_ino,
+ &userns_info);
+ if (r == -ENOENT)
+ return varlink_error(link, "io.systemd.NamespaceResource.UserNamespaceNotRegistered", NULL);
+ if (r < 0)
+ return r;
+
+ /* Registering a network interface for this client is only allowed for the root or the owner of a userns */
+ r = varlink_get_peer_uid(link, &peer_uid);
+ if (r < 0)
+ return r;
+ if (peer_uid != 0 && peer_uid != userns_info->owner)
+ return varlink_error(link, VARLINK_ERROR_PERMISSION_DENIED, NULL);
+
+ _cleanup_free_ char *ifname_host = NULL, *altifname_host = NULL;
+ const char *ifname_namespace = p.ifname ?: "host0";
+
+ /* The short ifname is just too short to generate readable and unique names where unprivileged users
+ * can't take each others names. Hence just hash it. The alternative name however contains more useful
+ * information. */
+ if (asprintf(&ifname_host, "ns-%08" PRIx64, hash_ifname_id(userns_info, p.ifname)) < 0)
+ return -ENOMEM;
+ strshorten(ifname_host, IFNAMSIZ-1);
+
+ if (p.ifname)
+ r = asprintf(&altifname_host, "ns-" UID_FMT "-%s-%s", userns_info->owner, userns_info->name, p.ifname);
+ else
+ r = asprintf(&altifname_host, "ns-" UID_FMT "-%s", userns_info->owner, userns_info->name);
+ if (r < 0)
+ return -ENOMEM;
+
+ struct ether_addr ether_addr_host, ether_addr_namespace;
+
+ hash_ether_addr(userns_info, p.ifname, 0, ðer_addr_host);
+ hash_ether_addr(userns_info, p.ifname, 1, ðer_addr_namespace);
+
+ r = create_veth(netns_fd,
+ ifname_host, altifname_host, ðer_addr_host,
+ ifname_namespace, ðer_addr_namespace);
+ if (r < 0)
+ return r;
+
+ log_debug("Adding veth tunnel %s from host to userns " INO_FMT " ('%s' @ UID " UID_FMT ", interface %s).",
+ ifname_host, userns_st.st_ino, userns_info->name, userns_info->start, ifname_namespace);
+
+ return varlink_replyb(link, JSON_BUILD_OBJECT(JSON_BUILD_PAIR("hostInterfaceName", JSON_BUILD_STRING(ifname_host)),
+ JSON_BUILD_PAIR("namespaceInterfaceName", JSON_BUILD_STRING(ifname_namespace))));
+}
+
+static int process_connection(VarlinkServer *server, int _fd) {
+ _cleanup_close_ int fd = TAKE_FD(_fd); /* always take possession */
+ _cleanup_(varlink_close_unrefp) Varlink *vl = NULL;
+ int r;
+
+ r = varlink_server_add_connection(server, fd, &vl);
+ if (r < 0)
+ return log_error_errno(r, "Failed to add connection: %m");
+
+ TAKE_FD(fd);
+ vl = varlink_ref(vl);
+
+ r = varlink_set_allow_fd_passing_input(vl, true);
+ if (r < 0)
+ return log_error_errno(r, "Failed to enable fd passing for read: %m");
+
+ r = varlink_set_allow_fd_passing_output(vl, true);
+ if (r < 0)
+ return log_error_errno(r, "Failed to enable fd passing for write: %m");
+
+ for (;;) {
+ r = varlink_process(vl);
+ if (r == -ENOTCONN) {
+ log_debug("Connection terminated.");
+ break;
+ }
+ if (r < 0)
+ return log_error_errno(r, "Failed to process connection: %m");
+ if (r > 0)
+ continue;
+
+ r = varlink_wait(vl, CONNECTION_IDLE_USEC);
+ if (r < 0)
+ return log_error_errno(r, "Failed to wait for connection events: %m");
+ if (r == 0)
+ break;
+ }
+
+ return 0;
+}
+
+static int run(int argc, char *argv[]) {
+ _cleanup_(userns_restrict_bpf_freep) struct userns_restrict_bpf *bpf = NULL;
+ usec_t start_time, listen_idle_usec, last_busy_usec = USEC_INFINITY;
+ _cleanup_(varlink_server_unrefp) VarlinkServer *server = NULL;
+ _cleanup_(pidref_done) PidRef parent = PIDREF_NULL;
+ unsigned n_iterations = 0;
+ int m, listen_fd, r;
+
+ log_setup();
+
+ m = sd_listen_fds(false);
+ if (m < 0)
+ return log_error_errno(m, "Failed to determine number of listening fds: %m");
+ if (m == 0)
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "No socket to listen on received.");
+ if (m > 1)
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Worker can only listen on a single socket at a time.");
+
+ listen_fd = SD_LISTEN_FDS_START;
+
+ r = fd_nonblock(listen_fd, false);
+ if (r < 0)
+ return log_error_errno(r, "Failed to turn off non-blocking mode for listening socket: %m");
+
+ r = varlink_server_new(&server, VARLINK_SERVER_INHERIT_USERDATA);
+ if (r < 0)
+ return log_error_errno(r, "Failed to allocate server: %m");
+
+ r = varlink_server_add_interface_many(
+ server,
+ &vl_interface_io_systemd_NamespaceResource,
+ &vl_interface_io_systemd_UserDatabase);
+ if (r < 0)
+ return log_error_errno(r, "Failed to add UserDatabase and NamespaceResource interface to varlink server: %m");
+
+ r = varlink_server_bind_method_many(
+ server,
+ "io.systemd.NamespaceResource.AllocateUserRange", vl_method_allocate_user_range,
+ "io.systemd.NamespaceResource.RegisterUserNamespace", vl_method_register_user_namespace,
+ "io.systemd.NamespaceResource.AddMountToUserNamespace", vl_method_add_mount_to_user_namespace,
+ "io.systemd.NamespaceResource.AddControlGroupToUserNamespace", vl_method_add_cgroup_to_user_namespace,
+ "io.systemd.NamespaceResource.AddNetworkToUserNamespace", vl_method_add_netif_to_user_namespace,
+ "io.systemd.UserDatabase.GetUserRecord", vl_method_get_user_record,
+ "io.systemd.UserDatabase.GetGroupRecord", vl_method_get_group_record,
+ "io.systemd.UserDatabase.GetMemberships", vl_method_get_memberships);
+ if (r < 0)
+ return log_error_errno(r, "Failed to bind methods: %m");
+
+ varlink_server_set_userdata(server, &bpf);
+
+ r = getenv_bool("NSRESOURCE_FIXED_WORKER");
+ if (r < 0)
+ return log_error_errno(r, "Failed to parse NSRESOURCE_FIXED_WORKER: %m");
+ listen_idle_usec = r ? USEC_INFINITY : LISTEN_IDLE_USEC;
+
+ r = pidref_set_parent(&parent);
+ if (r < 0)
+ return log_error_errno(r, "Failed to acquire pidfd of parent process: %m");
+
+ start_time = now(CLOCK_MONOTONIC);
+
+ for (;;) {
+ _cleanup_close_ int fd = -EBADF;
+ usec_t n;
+
+ /* Exit the worker in regular intervals, to flush out all memory use */
+ if (n_iterations++ > ITERATIONS_MAX) {
+ log_debug("Exiting worker, processed %u iterations, that's enough.", n_iterations);
+ break;
+ }
+
+ n = now(CLOCK_MONOTONIC);
+ if (n >= usec_add(start_time, RUNTIME_MAX_USEC)) {
+ log_debug("Exiting worker, ran for %s, that's enough.",
+ FORMAT_TIMESPAN(usec_sub_unsigned(n, start_time), 0));
+ break;
+ }
+
+ if (last_busy_usec == USEC_INFINITY)
+ last_busy_usec = n;
+ else if (listen_idle_usec != USEC_INFINITY && n >= usec_add(last_busy_usec, listen_idle_usec)) {
+ log_debug("Exiting worker, been idle for %s.",
+ FORMAT_TIMESPAN(usec_sub_unsigned(n, last_busy_usec), 0));
+ break;
+ }
+
+ (void) rename_process("systemd-nsresourcework: waiting...");
+ fd = RET_NERRNO(accept4(listen_fd, NULL, NULL, SOCK_NONBLOCK|SOCK_CLOEXEC));
+ (void) rename_process("systemd-nsresourcework: processing...");
+
+ if (fd == -EAGAIN)
+ continue; /* The listening socket has SO_RECVTIMEO set, hence a timeout is expected
+ * after a while, let's check if it's time to exit though. */
+ if (fd == -EINTR)
+ continue; /* Might be that somebody attached via strace, let's just continue in that
+ * case */
+ if (fd < 0)
+ return log_error_errno(fd, "Failed to accept() from listening socket: %m");
+
+ if (now(CLOCK_MONOTONIC) <= usec_add(n, PRESSURE_SLEEP_TIME_USEC)) {
+ /* We only slept a very short time? If so, let's see if there are more sockets
+ * pending, and if so, let's ask our parent for more workers */
+
+ r = fd_wait_for_event(listen_fd, POLLIN, 0);
+ if (r < 0)
+ return log_error_errno(r, "Failed to test for POLLIN on listening socket: %m");
+
+ if (FLAGS_SET(r, POLLIN)) {
+ r = pidref_kill(&parent, SIGUSR2);
+ if (r == -ESRCH)
+ return log_error_errno(r, "Parent already died?");
+ if (r < 0)
+ return log_error_errno(r, "Failed to send SIGUSR2 signal to parent. %m");
+ }
+ }
+
+ (void) process_connection(server, TAKE_FD(fd));
+ last_busy_usec = USEC_INFINITY;
+ }
+
+ return 0;
+}
+
+DEFINE_MAIN_FUNCTION(run);
--- /dev/null
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+
+#include <sys/eventfd.h>
+
+#include "fd-util.h"
+#include "main-func.h"
+#include "missing_mount.h"
+#include "missing_syscall.h"
+#include "namespace-util.h"
+#include "process-util.h"
+#include "rm-rf.h"
+#include "tmpfile-util.h"
+#include "userns-restrict.h"
+
+static int make_tmpfs_fsmount(void) {
+ _cleanup_close_ int fsfd = -EBADF, mntfd = -EBADF;
+
+ fsfd = fsopen("tmpfs", FSOPEN_CLOEXEC);
+ assert_se(fsfd >= 0);
+ assert_se(fsconfig(fsfd, FSCONFIG_CMD_CREATE, NULL, NULL, 0) >= 0);
+
+ mntfd = fsmount(fsfd, FSMOUNT_CLOEXEC, 0);
+ assert_se(mntfd >= 0);
+
+ return TAKE_FD(mntfd);
+}
+
+static void test_works_reg(int parent_fd, const char *fname) {
+ _cleanup_close_ int fd = -EBADF;
+
+ fd = openat(parent_fd, fname, O_RDWR|O_CREAT|O_CLOEXEC, 0666);
+ assert_se(fd >= 0);
+}
+
+static void test_fails_reg(int parent_fd, const char *fname) {
+ errno = 0;
+ assert_se(openat(parent_fd, fname, O_RDWR|O_CREAT|O_CLOEXEC, 0666) < 0);
+ assert_se(errno == EPERM);
+}
+
+static void test_works_dir(int parent_fd, const char *fname) {
+ assert_se(mkdirat(parent_fd, fname, 0666) >= 0);
+}
+
+static void test_fails_dir(int parent_fd, const char *fname) {
+ errno = 0;
+ assert_se(mkdirat(parent_fd, fname, 0666) < 0);
+ assert_se(errno == EPERM);
+}
+
+static int run(int argc, char *argv[]) {
+ _cleanup_(userns_restrict_bpf_freep) struct userns_restrict_bpf *obj = NULL;
+ _cleanup_close_ int userns_fd = -EBADF, host_fd1 = -EBADF, host_tmpfs = -EBADF, afd = -EBADF, bfd = -EBADF;
+ _cleanup_(rm_rf_physical_and_freep) char *t = NULL;
+ _cleanup_(sigkill_waitp) pid_t pid = 0;
+ int r;
+
+ log_set_max_level(LOG_DEBUG);
+ log_open();
+
+ r = userns_restrict_install(/* pin= */ false, &obj);
+ if (ERRNO_IS_NOT_SUPPORTED(r)) {
+ log_notice("Skipping test, LSM-BPF logic not supported.");
+ return EXIT_TEST_SKIP;
+ }
+ if (ERRNO_IS_PRIVILEGE(r)) {
+ log_notice("Skipping test, lacking privileges.");
+ return EXIT_TEST_SKIP;
+ }
+ if (r < 0)
+ return r;
+
+ assert_se(mkdtemp_malloc(NULL, &t) >= 0);
+
+ host_fd1 = open(t, O_DIRECTORY|O_CLOEXEC);
+ assert_se(host_fd1 >= 0);
+
+ host_tmpfs = make_tmpfs_fsmount();
+ assert_se(host_tmpfs >= 0);
+
+ userns_fd = userns_acquire("0 0 1", "0 0 1");
+ if (userns_fd < 0)
+ return log_error_errno(userns_fd, "Failed to make user namespace: %m");
+
+ r = userns_restrict_put_by_fd(
+ obj,
+ userns_fd,
+ /* replace= */ true,
+ /* mount_fds= */ NULL,
+ /* n_mount_fds= */ 0);
+ if (r < 0)
+ return log_error_errno(r, "Failed to restrict user namespace: %m");
+
+ afd = eventfd(0, EFD_CLOEXEC);
+ bfd = eventfd(0, EFD_CLOEXEC);
+
+ assert_se(afd >= 0 && bfd >= 0);
+
+ r = safe_fork("(test)", FORK_DEATHSIG_SIGKILL, &pid);
+ assert_se(r >= 0);
+ if (r == 0) {
+ _cleanup_close_ int private_tmpfs = -EBADF;
+
+ assert_se(setns(userns_fd, CLONE_NEWUSER) >= 0);
+ assert_se(unshare(CLONE_NEWNS) >= 0);
+
+ /* Allocate tmpfs locally */
+ private_tmpfs = make_tmpfs_fsmount();
+
+ /* These two host mounts should be inaccessible */
+ test_fails_reg(host_fd1, "test");
+ test_fails_reg(host_tmpfs, "xxx");
+ test_fails_dir(host_fd1, "test2");
+ test_fails_dir(host_tmpfs, "xxx2");
+
+ /* But this mount created locally should be fine */
+ test_works_reg(private_tmpfs, "yyy");
+ test_works_dir(private_tmpfs, "yyy2");
+
+ /* Let's sync with the parent, so that it allowlists more stuff for us */
+ assert_se(eventfd_write(afd, 1) >= 0);
+ uint64_t x;
+ assert_se(eventfd_read(bfd, &x) >= 0);
+
+ /* And now we should also have access to the host tmpfs */
+ test_works_reg(host_tmpfs, "zzz");
+ test_works_reg(private_tmpfs, "aaa");
+ test_works_dir(host_tmpfs, "zzz2");
+ test_works_dir(private_tmpfs, "aaa2");
+
+ /* But this one should still fail */
+ test_fails_reg(host_fd1, "bbb");
+ test_fails_dir(host_fd1, "bbb2");
+
+ /* Sync again, to get more stuff allowlisted */
+ assert_se(eventfd_write(afd, 1) >= 0);
+ assert_se(eventfd_read(bfd, &x) >= 0);
+
+ /* Everything should now be allowed */
+ test_works_reg(host_tmpfs, "ccc");
+ test_works_reg(host_fd1, "ddd");
+ test_works_reg(private_tmpfs, "eee");
+ test_works_dir(host_tmpfs, "ccc2");
+ test_works_reg(host_fd1, "ddd2");
+ test_works_dir(private_tmpfs, "eee2");
+
+ _exit(EXIT_SUCCESS);
+ }
+
+ uint64_t x;
+ assert_se(eventfd_read(afd, &x) >= 0);
+
+ r = userns_restrict_put_by_fd(
+ obj,
+ userns_fd,
+ /* replace= */ false,
+ &host_tmpfs,
+ 1);
+ if (r < 0)
+ return log_error_errno(r, "Failed to loosen user namespace: %m");
+
+ assert_se(eventfd_write(bfd, 1) >= 0);
+
+ assert_se(eventfd_read(afd, &x) >= 0);
+
+ r = userns_restrict_put_by_fd(
+ obj,
+ userns_fd,
+ /* replace= */ false,
+ &host_fd1,
+ 1);
+ if (r < 0)
+ return log_error_errno(r, "Failed to loosen user namespace: %m");
+
+ assert_se(eventfd_write(bfd, 1) >= 0);
+
+ assert_se(wait_for_terminate_and_check("(test)", pid, WAIT_LOG) >= 0);
+
+ return 0;
+}
+
+DEFINE_MAIN_FUNCTION(run);
--- /dev/null
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+
+#include "chase.h"
+#include "fd-util.h"
+#include "fileio.h"
+#include "format-util.h"
+#include "fs-util.h"
+#include "json.h"
+#include "missing_magic.h"
+#include "path-util.h"
+#include "recurse-dir.h"
+#include "rm-rf.h"
+#include "user-util.h"
+#include "userns-registry.h"
+
+int userns_registry_open_fd(void) {
+ int fd;
+
+ fd = chase_and_open(
+ "/run/systemd/nsresource/registry",
+ /* root= */ NULL,
+ CHASE_MKDIR_0755,
+ O_CLOEXEC|O_DIRECTORY|O_CREAT,
+ /* ret_path= */ NULL);
+ if (fd < 0)
+ return log_debug_errno(fd, "Failed to open registry dir: %m");
+
+ return fd;
+}
+
+int userns_registry_lock(int dir_fd) {
+ _cleanup_close_ int registry_fd = -EBADF, lock_fd = -EBADF;
+
+ if (dir_fd < 0) {
+ registry_fd = userns_registry_open_fd();
+ if (registry_fd < 0)
+ return registry_fd;
+
+ dir_fd = registry_fd;
+ }
+
+ lock_fd = xopenat_lock_full(dir_fd, "lock", O_CREAT|O_RDWR|O_CLOEXEC, /* xopen_flags= */ 0, 0600, LOCK_BSD, LOCK_EX);
+ if (lock_fd < 0)
+ return log_debug_errno(lock_fd, "Failed to open nsresource registry lock file: %m");
+
+ return TAKE_FD(lock_fd);
+}
+
+UserNamespaceInfo* userns_info_new(void) {
+ UserNamespaceInfo *info = new(UserNamespaceInfo, 1);
+ if (!info)
+ return NULL;
+
+ *info = (UserNamespaceInfo) {
+ .owner = UID_INVALID,
+ .start = UID_INVALID,
+ .target = UID_INVALID,
+ };
+
+ return info;
+}
+
+UserNamespaceInfo *userns_info_free(UserNamespaceInfo *userns) {
+ if (!userns)
+ return NULL;
+
+ free(userns->cgroups);
+ free(userns->name);
+
+ return mfree(userns);
+}
+
+static int dispatch_cgroups_array(const char *name, JsonVariant *variant, JsonDispatchFlags flags, void *userdata) {
+ UserNamespaceInfo *info = ASSERT_PTR(userdata);
+ _cleanup_free_ uint64_t *cgroups = NULL;
+ size_t n_cgroups = 0;
+
+ if (json_variant_is_null(variant)) {
+ info->cgroups = mfree(info->cgroups);
+ info->n_cgroups = 0;
+ return 0;
+ }
+
+ if (!json_variant_is_array(variant))
+ return json_log(variant, flags, SYNTHETIC_ERRNO(EINVAL), "JSON field '%s' is not an array.", strna(name));
+
+ cgroups = new(uint64_t, json_variant_elements(variant));
+ if (!cgroups)
+ return json_log_oom(variant, flags);
+
+ JsonVariant *e;
+ JSON_VARIANT_ARRAY_FOREACH(e, variant) {
+ bool found = false;
+
+ if (!json_variant_is_unsigned(e))
+ return json_log(e, flags, SYNTHETIC_ERRNO(EINVAL), "JSON array element is not a number.");
+
+ FOREACH_ARRAY(cg, cgroups, n_cgroups)
+ if (*cg == json_variant_unsigned(e)) {
+ found = true;
+ break;
+ }
+ if (found) /* suppress duplicate */
+ continue;
+
+ cgroups[n_cgroups++] = json_variant_unsigned(e);
+ }
+
+ assert(n_cgroups <= json_variant_elements(variant));
+
+ free_and_replace(info->cgroups, cgroups);
+ info->n_cgroups = n_cgroups;
+
+ return 0;
+}
+
+static int userns_registry_load(int dir_fd, const char *fn, UserNamespaceInfo **ret) {
+
+ static const JsonDispatch dispatch_table[] = {
+ { "owner", JSON_VARIANT_UNSIGNED, json_dispatch_uid_gid, offsetof(UserNamespaceInfo, owner), JSON_MANDATORY },
+ { "name", JSON_VARIANT_STRING, json_dispatch_string, offsetof(UserNamespaceInfo, name), JSON_MANDATORY },
+ { "userns", JSON_VARIANT_UNSIGNED, json_dispatch_uint64, offsetof(UserNamespaceInfo, userns_inode), JSON_MANDATORY },
+ { "start", JSON_VARIANT_UNSIGNED, json_dispatch_uid_gid, offsetof(UserNamespaceInfo, start), 0 },
+ { "size", JSON_VARIANT_UNSIGNED, json_dispatch_uint32, offsetof(UserNamespaceInfo, size), 0 },
+ { "target", JSON_VARIANT_UNSIGNED, json_dispatch_uid_gid, offsetof(UserNamespaceInfo, target), 0 },
+ { "cgroups", JSON_VARIANT_ARRAY, dispatch_cgroups_array, 0, 0 },
+ {}
+ };
+
+ _cleanup_(userns_info_freep) UserNamespaceInfo *userns_info = NULL;
+ _cleanup_(json_variant_unrefp) JsonVariant *v = NULL;
+ _cleanup_close_ int registry_fd = -EBADF;
+ int r;
+
+ if (dir_fd < 0) {
+ registry_fd = userns_registry_open_fd();
+ if (registry_fd < 0)
+ return registry_fd;
+
+ dir_fd = registry_fd;
+ }
+
+ r = json_parse_file_at(NULL, dir_fd, fn, 0, &v, NULL, NULL);
+ if (r < 0)
+ return r;
+
+ userns_info = userns_info_new();
+ if (!userns_info)
+ return -ENOMEM;
+
+ r = json_dispatch(v, dispatch_table, 0, userns_info);
+ if (r < 0)
+ return r;
+
+ if (userns_info->userns_inode == 0)
+ return -EBADMSG;
+ if (userns_info->start == 0)
+ return -EBADMSG;
+ if (userns_info->size == 0) {
+ if (uid_is_valid(userns_info->start) || uid_is_valid(userns_info->target))
+ return -EBADMSG;
+ } else {
+ if (!uid_is_valid(userns_info->start) || !uid_is_valid(userns_info->target))
+ return -EBADMSG;
+
+ if (userns_info->size > UINT32_MAX - userns_info->start ||
+ userns_info->size > UINT32_MAX - userns_info->target)
+ return -EBADMSG;
+ }
+
+ if (ret)
+ *ret = TAKE_PTR(userns_info);
+ return 0;
+}
+
+int userns_registry_uid_exists(int dir_fd, uid_t start) {
+ _cleanup_free_ char *fn = NULL;
+
+ assert(dir_fd >= 0);
+
+ if (!uid_is_valid(start))
+ return -ENOENT;
+
+ if (start == 0)
+ return true;
+
+ if (asprintf(&fn, "u" UID_FMT ".userns", start) < 0)
+ return -ENOMEM;
+
+ if (faccessat(dir_fd, fn, F_OK, AT_SYMLINK_NOFOLLOW) < 0)
+ return errno == ENOENT ? false : -errno;
+
+ return true;
+}
+
+int userns_registry_name_exists(int dir_fd, const char *name) {
+ _cleanup_free_ char *fn = NULL;
+
+ assert(dir_fd >= 0);
+
+ if (!userns_name_is_valid(name))
+ return -EINVAL;
+
+ fn = strjoin("n", name, ".userns");
+ if (!fn)
+ return -ENOMEM;
+
+ if (faccessat(dir_fd, fn, F_OK, AT_SYMLINK_NOFOLLOW) < 0)
+ return errno == ENOENT ? false : -errno;
+
+ return true;
+}
+
+int userns_registry_inode_exists(int dir_fd, uint64_t inode) {
+ _cleanup_free_ char *fn = NULL;
+
+ assert(dir_fd >= 0);
+
+ if (inode <= 0)
+ return -EINVAL;
+
+ if (asprintf(&fn, "i%" PRIu64 ".userns", inode) < 0)
+ return -ENOMEM;
+
+ if (faccessat(dir_fd, fn, F_OK, AT_SYMLINK_NOFOLLOW) < 0)
+ return errno == ENOENT ? false : -errno;
+
+ return true;
+}
+
+int userns_registry_load_by_start_uid(int dir_fd, uid_t start, UserNamespaceInfo **ret) {
+ _cleanup_(userns_info_freep) UserNamespaceInfo *userns_info = NULL;
+ _cleanup_close_ int registry_fd = -EBADF;
+ _cleanup_free_ char *fn = NULL;
+ int r;
+
+ if (!uid_is_valid(start))
+ return -ENOENT;
+
+ if (dir_fd < 0) {
+ registry_fd = userns_registry_open_fd();
+ if (registry_fd < 0)
+ return registry_fd;
+
+ dir_fd = registry_fd;
+ }
+
+ if (asprintf(&fn, "u" UID_FMT ".userns", start) < 0)
+ return -ENOMEM;
+
+ r = userns_registry_load(dir_fd, fn, &userns_info);
+ if (r < 0)
+ return r;
+
+ if (userns_info->start != start)
+ return -EBADMSG;
+
+ if (ret)
+ *ret = TAKE_PTR(userns_info);
+
+ return 0;
+}
+
+int userns_registry_load_by_userns_inode(int dir_fd, uint64_t inode, UserNamespaceInfo **ret) {
+ _cleanup_(userns_info_freep) UserNamespaceInfo *userns_info = NULL;
+ _cleanup_close_ int registry_fd = -EBADF;
+ _cleanup_free_ char *fn = NULL;
+ int r;
+
+ if (inode == 0)
+ return -ENOENT;
+
+ if (dir_fd < 0) {
+ registry_fd = userns_registry_open_fd();
+ if (registry_fd < 0)
+ return registry_fd;
+
+ dir_fd = registry_fd;
+ }
+
+ if (asprintf(&fn, "i%" PRIu64 ".userns", inode) < 0)
+ return -ENOMEM;
+
+ r = userns_registry_load(dir_fd, fn, &userns_info);
+ if (r < 0)
+ return r;
+
+ if (userns_info->userns_inode != inode)
+ return -EBADMSG;
+
+ if (ret)
+ *ret = TAKE_PTR(userns_info);
+
+ return 0;
+}
+
+int userns_registry_load_by_name(int dir_fd, const char *name, UserNamespaceInfo **ret) {
+ _cleanup_(userns_info_freep) UserNamespaceInfo *userns_info = NULL;
+ _cleanup_close_ int registry_fd = -EBADF;
+ _cleanup_free_ char *fn = NULL;
+ int r;
+
+ assert(name);
+
+ if (!userns_name_is_valid(name)) /* Invalid names never exist */
+ return -ENOENT;
+
+ if (dir_fd < 0) {
+ registry_fd = userns_registry_open_fd();
+ if (registry_fd < 0)
+ return registry_fd;
+
+ dir_fd = registry_fd;
+ }
+
+ fn = strjoin("n", name, ".userns");
+ if (!fn)
+ return -ENOMEM;
+
+ r = userns_registry_load(dir_fd, fn, &userns_info);
+ if (r < 0)
+ return r;
+
+ if (!streq_ptr(userns_info->name, name))
+ return -EBADMSG;
+
+ if (ret)
+ *ret = TAKE_PTR(userns_info);
+
+ return 0;
+}
+
+int userns_registry_store(int dir_fd, UserNamespaceInfo *info) {
+ _cleanup_close_ int registry_fd = -EBADF;
+ int r;
+
+ assert(info);
+
+ if (!uid_is_valid(info->owner) ||
+ !info->name ||
+ info->userns_inode == 0)
+ return -EINVAL;
+
+ if (dir_fd < 0) {
+ registry_fd = userns_registry_open_fd();
+ if (registry_fd < 0)
+ return registry_fd;
+
+ dir_fd = registry_fd;
+ }
+
+ _cleanup_(json_variant_unrefp) JsonVariant *cgroup_array = NULL;
+ FOREACH_ARRAY(cg, info->cgroups, info->n_cgroups) {
+ r = json_variant_append_arrayb(
+ &cgroup_array,
+ JSON_BUILD_UNSIGNED(*cg));
+ if (r < 0)
+ return r;
+ }
+
+ _cleanup_(json_variant_unrefp) JsonVariant *def = NULL;
+ r = json_build(&def, JSON_BUILD_OBJECT(
+ JSON_BUILD_PAIR("owner", JSON_BUILD_UNSIGNED(info->owner)),
+ JSON_BUILD_PAIR("name", JSON_BUILD_STRING(info->name)),
+ JSON_BUILD_PAIR("userns", JSON_BUILD_UNSIGNED(info->userns_inode)),
+ JSON_BUILD_PAIR_CONDITION(uid_is_valid(info->start), "start", JSON_BUILD_UNSIGNED(info->start)),
+ JSON_BUILD_PAIR_CONDITION(uid_is_valid(info->start), "size", JSON_BUILD_UNSIGNED(info->size)),
+ JSON_BUILD_PAIR_CONDITION(uid_is_valid(info->start), "target", JSON_BUILD_UNSIGNED(info->target)),
+ JSON_BUILD_PAIR_CONDITION(cgroup_array, "cgroups", JSON_BUILD_VARIANT(cgroup_array))));
+ if (r < 0)
+ return r;
+
+ _cleanup_free_ char *def_buf = NULL;
+ r = json_variant_format(def, 0, &def_buf);
+ if (r < 0)
+ return log_debug_errno(r, "Failed to format userns JSON object: %m");
+
+ _cleanup_free_ char *reg_fn = NULL, *link1_fn = NULL, *link2_fn = NULL, *owner_fn = NULL, *uid_fn = NULL;
+ if (asprintf(®_fn, "i%" PRIu64 ".userns", info->userns_inode) < 0)
+ return log_oom_debug();
+
+ r = write_string_file_at(dir_fd, reg_fn, def_buf, WRITE_STRING_FILE_CREATE|WRITE_STRING_FILE_ATOMIC);
+ if (r < 0)
+ return log_debug_errno(r, "Failed to write userns data to '%s' in registry: %m", reg_fn);
+
+ link1_fn = strjoin("n", info->name, ".userns");
+ if (!link1_fn) {
+ r = log_oom_debug();
+ goto fail;
+ }
+
+ r = linkat_replace(dir_fd, reg_fn, dir_fd, link1_fn);
+ if (r < 0) {
+ log_debug_errno(r, "Failed to link userns data to '%s' in registry: %m", link1_fn);
+ goto fail;
+ }
+
+ if (uid_is_valid(info->start)) {
+ if (asprintf(&link2_fn, "u" UID_FMT ".userns", info->start) < 0) {
+ r = log_oom_debug();
+ goto fail;
+ }
+
+ r = linkat_replace(dir_fd, reg_fn, dir_fd, link2_fn);
+ if (r < 0) {
+ log_debug_errno(r, "Failed to link userns data to '%s' in registry: %m", link2_fn);
+ goto fail;
+ }
+ }
+
+ if (asprintf(&uid_fn, "o" UID_FMT ".owns", info->owner) < 0) {
+ r = log_oom_debug();
+ goto fail;
+ }
+
+ if (mkdirat(dir_fd, uid_fn, 0755) < 0 && errno != EEXIST) {
+ r = log_debug_errno(errno, "Failed to create per-UID subdir '%s' of registry: %m", uid_fn);
+ goto fail;
+ }
+
+ if (asprintf(&owner_fn, "%s/i%" PRIu64 ".userns", uid_fn, info->userns_inode) < 0) {
+ r = log_oom_debug();
+ goto fail;
+ }
+
+ r = linkat_replace(dir_fd, reg_fn, dir_fd, owner_fn);
+ if (r < 0) {
+ log_debug_errno(r, "Failed to link userns data to '%s' in registry: %m", owner_fn);
+ goto fail;
+ }
+
+ return 0;
+
+fail:
+ if (reg_fn)
+ (void) unlinkat(dir_fd, reg_fn, /* flags= */ 0);
+ if (link1_fn)
+ (void) unlinkat(dir_fd, link1_fn, /* flags= */ 0);
+ if (link2_fn)
+ (void) unlinkat(dir_fd, link2_fn, /* flags= */ 0);
+ if (owner_fn)
+ (void) unlinkat(dir_fd, owner_fn, /* flags= */ 0);
+ if (uid_fn)
+ (void) unlinkat(dir_fd, uid_fn, AT_REMOVEDIR);
+
+ return r;
+}
+
+int userns_registry_remove(int dir_fd, UserNamespaceInfo *info) {
+ _cleanup_close_ int registry_fd = -EBADF;
+ int ret = 0, r;
+
+ assert(info);
+
+ if (dir_fd < 0) {
+ registry_fd = userns_registry_open_fd();
+ if (registry_fd < 0)
+ return registry_fd;
+
+ dir_fd = registry_fd;
+ }
+
+ _cleanup_free_ char *reg_fn = NULL;
+ if (asprintf(®_fn, "i%" PRIu64 ".userns", info->userns_inode) < 0)
+ return log_oom_debug();
+
+ ret = RET_NERRNO(unlinkat(dir_fd, reg_fn, 0));
+
+ _cleanup_free_ char *link1_fn = NULL;
+ link1_fn = strjoin("n", info->name, ".userns");
+ if (!link1_fn)
+ return log_oom_debug();
+
+ RET_GATHER(ret, RET_NERRNO(unlinkat(dir_fd, link1_fn, 0)));
+
+ if (uid_is_valid(info->start)) {
+ _cleanup_free_ char *link2_fn = NULL;
+
+ if (asprintf(&link2_fn, "u" UID_FMT ".userns", info->start) < 0)
+ return log_oom_debug();
+
+ RET_GATHER(ret, RET_NERRNO(unlinkat(dir_fd, link2_fn, 0)));
+ }
+
+ _cleanup_free_ char *uid_fn = NULL;
+ if (asprintf(&uid_fn, "o" UID_FMT ".owns", info->owner) < 0)
+ return log_oom_debug();
+
+ _cleanup_free_ char *owner_fn = NULL;
+ if (asprintf(&owner_fn, "%s/i%" PRIu64 ".userns", uid_fn, info->userns_inode) < 0)
+ return log_oom_debug();
+
+ RET_GATHER(ret, RET_NERRNO(unlinkat(dir_fd, owner_fn, 0)));
+
+ r = RET_NERRNO(unlinkat(dir_fd, uid_fn, AT_REMOVEDIR));
+ if (r != -ENOTEMPTY)
+ RET_GATHER(ret, r);
+
+ return ret;
+}
+
+bool userns_info_has_cgroup(UserNamespaceInfo *userns, uint64_t cgroup_id) {
+ assert(userns);
+
+ FOREACH_ARRAY(i, userns->cgroups, userns->n_cgroups)
+ if (*i == cgroup_id)
+ return true;
+
+ return false;
+}
+
+int userns_info_add_cgroup(UserNamespaceInfo *userns, uint64_t cgroup_id) {
+
+ if (userns_info_has_cgroup(userns, cgroup_id))
+ return 0;
+
+ if (!GREEDY_REALLOC(userns->cgroups, userns->n_cgroups+1))
+ return -ENOMEM;
+
+ userns->cgroups[userns->n_cgroups++] = cgroup_id;
+ return 1;
+}
+
+static int userns_destroy_cgroup(uint64_t cgroup_id) {
+ _cleanup_close_ int cgroup_fd = -EBADF, parent_fd = -EBADF;
+ int r;
+
+ cgroup_fd = cg_cgroupid_open(/* cgroupfsfd= */ -EBADF, cgroup_id);
+ if (cgroup_fd == -ESTALE) {
+ log_debug_errno(cgroup_fd, "Control group %" PRIu64 " already gone, ignoring: %m", cgroup_id);
+ return 0;
+ }
+ if (cgroup_fd < 0)
+ return log_debug_errno(errno, "Failed to open cgroup %" PRIu64 ", ignoring: %m", cgroup_id);
+
+ _cleanup_free_ char *path = NULL;
+ r = fd_get_path(cgroup_fd, &path);
+ if (r < 0)
+ return log_debug_errno(r, "Failed to get path of cgroup %" PRIu64 ", ignoring: %m", cgroup_id);
+
+ const char *e = path_startswith(path, "/sys/fs/cgroup/");
+ if (!e)
+ return log_debug_errno(SYNTHETIC_ERRNO(EPERM), "Got cgroup path that doesn't start with /sys/fs/cgroup/, refusing: %s", path);
+ if (isempty(e))
+ return log_debug_errno(SYNTHETIC_ERRNO(EPERM), "Got root cgroup path, which can't be right, refusing.");
+
+ log_debug("Path of cgroup %" PRIu64 " is: %s", cgroup_id, path);
+
+ _cleanup_free_ char *fname = NULL;
+ r = path_extract_filename(path, &fname);
+ if (r < 0)
+ return log_debug_errno(r, "Failed to extract name of cgroup %" PRIu64 ", ignoring: %m", cgroup_id);
+
+ parent_fd = openat(cgroup_fd, "..", O_CLOEXEC|O_DIRECTORY);
+ if (parent_fd < 0)
+ return log_debug_errno(errno, "Failed to open parent cgroup of %" PRIu64 ", ignoring: %m", cgroup_id);
+
+ /* Safety check, never leave cgroupfs */
+ r = fd_is_fs_type(parent_fd, CGROUP2_SUPER_MAGIC);
+ if (r < 0)
+ return log_debug_errno(r, "Failed to determine if parent directory of cgroup %" PRIu64 " is still a cgroup, ignoring: %m", cgroup_id);
+ if (!r)
+ return log_debug_errno(SYNTHETIC_ERRNO(EPERM), "Parent directory of cgroup %" PRIu64 " is not a cgroup, refusing.", cgroup_id);
+
+ cgroup_fd = safe_close(cgroup_fd);
+
+ r = rm_rf_child(parent_fd, fname, REMOVE_ONLY_DIRECTORIES|REMOVE_PHYSICAL|REMOVE_CHMOD);
+ if (r < 0)
+ log_debug_errno(r, "Failed to remove delegated cgroup %" PRIu64 ", ignoring: %m", cgroup_id);
+
+ return 0;
+}
+
+int userns_info_remove_cgroups(UserNamespaceInfo *userns) {
+ int ret = 0;
+
+ assert(userns);
+
+ FOREACH_ARRAY(c, userns->cgroups, userns->n_cgroups)
+ RET_GATHER(ret, userns_destroy_cgroup(*c));
+
+ userns->cgroups = mfree(userns->cgroups);
+ userns->n_cgroups = 0;
+
+ return ret;
+}
+
+bool userns_name_is_valid(const char *name) {
+
+ /* Checks if the specified string is suitable as user namespace name. */
+
+ if (strlen(name) > NAME_MAX) /* before we use alloca(), let's check for size */
+ return false;
+
+ const char *f = strjoina("n", name, ".userns"); /* Make sure we can name our lookup symlink with this name */
+ if (!filename_is_valid(f))
+ return false;
+
+ const char *u = strjoina("ns-", name, "-65535"); /* Make sure we can turn this into valid user names */
+ if (!valid_user_group_name(u, 0))
+ return false;
+
+ return true;
+}
+
+int userns_registry_per_uid(int dir_fd, uid_t owner) {
+ _cleanup_close_ int registry_fd = -EBADF;
+ int n = 0, r;
+
+ if (dir_fd < 0) {
+ registry_fd = userns_registry_open_fd();
+ if (registry_fd < 0)
+ return registry_fd;
+
+ dir_fd = registry_fd;
+ }
+
+ _cleanup_free_ char *uid_fn = NULL;
+ if (asprintf(&uid_fn, "o" UID_FMT ".owns", owner) < 0)
+ return log_oom_debug();
+
+ _cleanup_free_ DirectoryEntries *de = NULL;
+
+ r = readdir_all_at(dir_fd, uid_fn, RECURSE_DIR_IGNORE_DOT|RECURSE_DIR_ENSURE_TYPE, &de);
+ if (r == -ENOENT)
+ return 0;
+ if (r < 0)
+ return log_debug_errno(r, "Failed to enumerate contents of '%s' sub-directory: %m", uid_fn);
+
+ FOREACH_ARRAY(i, de->entries, de->n_entries) {
+ struct dirent *e = *i;
+
+ if (e->d_type != DT_REG)
+ continue;
+
+ if (!startswith(e->d_name, "i") || !endswith(e->d_name, ".userns"))
+ continue;
+
+ n++;
+
+ if (n == INT_MAX) /* overflow safety check, just in case */
+ break;
+ }
+
+ return n;
+}
--- /dev/null
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+#pragma once
+
+#define USER_NAMESPACE_CGROUPS_DELEGATE_MAX 16
+
+typedef struct UserNamespaceInfo {
+ uid_t owner;
+ char *name;
+ uint64_t userns_inode;
+ uid_t start;
+ uint32_t size;
+ uid_t target;
+ uint64_t *cgroups;
+ size_t n_cgroups;
+} UserNamespaceInfo;
+
+UserNamespaceInfo* userns_info_new(void);
+UserNamespaceInfo* userns_info_free(UserNamespaceInfo *userns);
+
+DEFINE_TRIVIAL_CLEANUP_FUNC(UserNamespaceInfo*, userns_info_free);
+
+bool userns_info_has_cgroup(UserNamespaceInfo *userns, uint64_t cgroup_id);
+int userns_info_add_cgroup(UserNamespaceInfo *userns, uint64_t cgroup_id);
+int userns_info_remove_cgroups(UserNamespaceInfo *userns);
+
+bool userns_name_is_valid(const char *name);
+
+int userns_registry_open_fd(void);
+int userns_registry_lock(int dir_fd);
+
+int userns_registry_load_by_start_uid(int dir_fd, uid_t start, UserNamespaceInfo **ret);
+int userns_registry_load_by_userns_inode(int dir_fd, uint64_t userns, UserNamespaceInfo **ret);
+int userns_registry_load_by_name(int dir_fd, const char *name, UserNamespaceInfo **ret);
+
+int userns_registry_store(int dir_fd, UserNamespaceInfo *info);
+int userns_registry_remove(int dir_fd, UserNamespaceInfo *info);
+
+int userns_registry_inode_exists(int dir_fd, uint64_t inode);
+int userns_registry_name_exists(int dir_fd, const char *name);
+int userns_registry_uid_exists(int dir_fd, uid_t start);
+
+int userns_registry_per_uid(int dir_fd, uid_t owner);
--- /dev/null
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+
+#include "userns-restrict.h"
+
+#if HAVE_VMLINUX_H
+
+#include <sched.h>
+
+#include "bpf-dlopen.h"
+#include "bpf-link.h"
+#include "fd-util.h"
+#include "fs-util.h"
+#include "lsm-util.h"
+#include "missing_mount.h"
+#include "mkdir.h"
+#include "mount-util.h"
+#include "mountpoint-util.h"
+#include "namespace-util.h"
+#include "path-util.h"
+
+#define USERNS_MAX (16U*1024U)
+#define MOUNTS_MAX 4096U
+
+#define PROGRAM_LINK_PREFIX "/sys/fs/bpf/systemd/userns-restrict/programs"
+#define MAP_LINK_PREFIX "/sys/fs/bpf/systemd/userns-restrict/maps"
+
+struct userns_restrict_bpf *userns_restrict_bpf_free(struct userns_restrict_bpf *obj) {
+ (void) userns_restrict_bpf__destroy(obj); /* this call is fine with NULL */
+ return NULL;
+}
+
+static int make_inner_hash_map(void) {
+ int fd;
+
+ fd = compat_bpf_map_create(
+ BPF_MAP_TYPE_HASH,
+ NULL,
+ sizeof(int),
+ sizeof(uint32_t),
+ MOUNTS_MAX,
+ NULL);
+ if (fd < 0)
+ return log_debug_errno(errno, "Failed allocate inner BPF map: %m");
+
+ return fd;
+}
+
+int userns_restrict_install(
+ bool pin,
+ struct userns_restrict_bpf **ret) {
+
+ _cleanup_(userns_restrict_bpf_freep) struct userns_restrict_bpf *obj = NULL;
+ _cleanup_close_ int dummy_mnt_id_hash_fd = -EBADF;
+ int r;
+
+ r = lsm_supported("bpf");
+ if (r < 0)
+ return r;
+ if (r == 0)
+ return log_error_errno(SYNTHETIC_ERRNO(EOPNOTSUPP), "bpf-lsm not supported, can't lock down user namespace.");
+
+ r = dlopen_bpf();
+ if (r < 0)
+ return r;
+
+ /* bpf_object__next_map() is not available in libbpf pre-0.7.0, and we want to use it. */
+ if (!sym_bpf_object__next_map)
+ return log_error_errno(SYNTHETIC_ERRNO(EOPNOTSUPP), "libbpf too old for locking down user namespace.");
+
+ obj = userns_restrict_bpf__open();
+ if (!obj)
+ return log_error_errno(errno, "Failed to open userns_restrict BPF object: %m");
+
+ if (pin) {
+ struct bpf_map *map;
+
+ /* libbpf will only create one level of dirs. Let's create the rest */
+ (void) mkdir_p(MAP_LINK_PREFIX, 0755);
+ (void) mkdir_p(PROGRAM_LINK_PREFIX, 0755);
+
+ map = sym_bpf_object__next_map(obj->obj, NULL);
+ while (map) {
+ _cleanup_free_ char *fn = NULL;
+
+ fn = path_join(MAP_LINK_PREFIX, sym_bpf_map__name(map));
+ if (!fn)
+ return log_oom();
+
+ r = sym_bpf_map__set_pin_path(map, fn);
+ if (r < 0)
+ return log_error_errno(r, "Failed to set pin path to '%s': %m", fn);
+
+ map = sym_bpf_object__next_map(obj->obj, map);
+ }
+ }
+
+ r = sym_bpf_map__set_max_entries(obj->maps.userns_mnt_id_hash, USERNS_MAX);
+ if (r < 0)
+ return log_error_errno(r, "Failed to size userns/mnt_id hash table: %m");
+
+ r = sym_bpf_map__set_max_entries(obj->maps.userns_ringbuf, USERNS_MAX * sizeof(unsigned int));
+ if (r < 0)
+ return log_error_errno(r, "Failed to size userns ring buffer: %m");
+
+ /* Dummy map to satisfy the verifier */
+ dummy_mnt_id_hash_fd = make_inner_hash_map();
+ if (dummy_mnt_id_hash_fd < 0)
+ return dummy_mnt_id_hash_fd;
+
+ r = sym_bpf_map__set_inner_map_fd(obj->maps.userns_mnt_id_hash, dummy_mnt_id_hash_fd);
+ if (r < 0)
+ return log_error_errno(r, "Failed to set inner BPF map: %m");
+
+ r = userns_restrict_bpf__load(obj);
+ if (r < 0)
+ return log_error_errno(r, "Failed to load BPF object: %m");
+
+ for (int i = 0; i < obj->skeleton->prog_cnt; i++) {
+ _cleanup_(bpf_link_freep) struct bpf_link *link = NULL;
+ struct bpf_prog_skeleton *ps = obj->skeleton->progs + i;
+ _cleanup_free_ char *fn = NULL;
+ bool linked = false;
+ const char *e;
+
+ e = startswith(ps->name, "userns_restrict_");
+ assert(e);
+
+ if (pin) {
+ fn = path_join(PROGRAM_LINK_PREFIX, e);
+ if (!fn)
+ return log_oom();
+
+ link = sym_bpf_link__open(fn);
+ r = sym_libbpf_get_error(link);
+ if (r < 0) {
+ if (r != -ENOENT)
+ return log_error_errno(r, "Unable to open pinned program link: %m");
+ link = NULL;
+ } else {
+ linked = true;
+ log_info("userns-restrict BPF-LSM program %s already attached.", ps->name);
+ }
+ }
+
+ if (!link) {
+ link = sym_bpf_program__attach(*ps->prog);
+ r = sym_libbpf_get_error(link);
+ if (r < 0)
+ return log_error_errno(r, "Failed to attach LSM BPF program: %m");
+
+ log_info("userns-restrict BPF-LSM program %s now attached.", ps->name);
+ }
+
+ if (pin && !linked) {
+ assert(fn);
+
+ r = sym_bpf_link__pin(link, fn);
+ if (r < 0)
+ return log_error_errno(r, "Failed to pin LSM attachment: %m");
+ }
+
+ *ps->link = TAKE_PTR(link);
+ }
+
+ if (pin) {
+ r = sym_bpf_object__pin_maps(obj->obj, NULL);
+ if (r < 0)
+ return log_error_errno(r, "Failed to pin BPF maps: %m");
+ }
+
+ if (ret)
+ *ret = TAKE_PTR(obj);
+
+ return 0;
+}
+
+int userns_restrict_put_by_inode(
+ struct userns_restrict_bpf *obj,
+ uint64_t userns_inode,
+ bool replace,
+ const int mount_fds[],
+ size_t n_mount_fds) {
+
+ _cleanup_close_ int inner_map_fd = -EBADF;
+ _cleanup_free_ int *mnt_ids = NULL;
+ uint64_t ino = userns_inode;
+ int r, outer_map_fd;
+
+ assert(obj);
+ assert(userns_inode != 0);
+ assert(n_mount_fds == 0 || mount_fds);
+
+ /* The BPF map type BPF_MAP_TYPE_HASH_OF_MAPS only supports 32bit keys, and user namespace inode
+ * numbers are 32bit too, even though ino_t is 64bit these days. Should we ever run into a 64bit
+ * inode let's refuse early, we can't support this with the current BPF code for now. */
+ if (userns_inode > UINT32_MAX)
+ return -EINVAL;
+
+ mnt_ids = new(int, n_mount_fds);
+ if (!mnt_ids)
+ return -ENOMEM;
+
+ for (size_t i = 0; i < n_mount_fds; i++) {
+ r = path_get_mnt_id_at(mount_fds[i], "", mnt_ids + i);
+ if (r < 0)
+ return log_debug_errno(r, "Failed to get mount ID: %m");
+ }
+
+ outer_map_fd = sym_bpf_map__fd(obj->maps.userns_mnt_id_hash);
+ if (outer_map_fd < 0)
+ return log_debug_errno(outer_map_fd, "Failed to get outer BPF map fd: %m");
+
+ if (replace) {
+ /* Add if missing, replace if already exists */
+ inner_map_fd = make_inner_hash_map();
+ if (inner_map_fd < 0)
+ return inner_map_fd;
+
+ r = sym_bpf_map_update_elem(outer_map_fd, &ino, &inner_map_fd, BPF_ANY);
+ if (r < 0)
+ return log_debug_errno(errno, "Failed to replace map in inode hash: %m");
+ } else {
+ /* Let's add an entry for this userns inode if missing. If it exists just extend the existing map. We
+ * might race against each other, hence we try a couple of times */
+ for (size_t n_try = 10;; n_try--) {
+ uint32_t innermap_id;
+
+ if (n_try == 0)
+ return log_debug_errno(SYNTHETIC_ERRNO(EEXIST),
+ "Stillcan't create inode entry in BPF map after 10 tries.");
+
+ r = sym_bpf_map_lookup_elem(outer_map_fd, &ino, &innermap_id);
+ if (r >= 0) {
+ inner_map_fd = sym_bpf_map_get_fd_by_id(innermap_id);
+ if (inner_map_fd < 0)
+ return log_debug_errno(inner_map_fd, "Failed to get file descriptor for inner map: %m");
+
+ break;
+ }
+ if (errno != ENOENT)
+ return log_debug_errno(errno, "Failed to look up inode hash entry: %m");
+
+ /* No entry for this user namespace yet. Let's create one */
+ inner_map_fd = make_inner_hash_map();
+ if (inner_map_fd < 0)
+ return inner_map_fd;
+
+ r = sym_bpf_map_update_elem(outer_map_fd, &ino, &inner_map_fd, BPF_NOEXIST);
+ if (r >= 0)
+ break;
+ if (errno != EEXIST)
+ return log_debug_errno(errno, "Failed to add mount ID list to inode hash: %m");
+ }
+ }
+
+ FOREACH_ARRAY(mntid, mnt_ids, n_mount_fds) {
+ uint32_t dummy_value = 1;
+
+ r = sym_bpf_map_update_elem(inner_map_fd, mntid, &dummy_value, BPF_ANY);
+ if (r < 0)
+ return log_debug_errno(errno, "Failed to add mount ID to map: %m");
+
+ log_debug("Allowing mount %i on userns inode %" PRIu64, *mntid, ino);
+ }
+
+ return 0;
+}
+
+int userns_restrict_put_by_fd(
+ struct userns_restrict_bpf *obj,
+ int userns_fd,
+ bool replace,
+ const int mount_fds[],
+ size_t n_mount_fds) {
+
+ struct stat st;
+ int r;
+
+ assert(obj);
+ assert(userns_fd >= 0);
+ assert(n_mount_fds == 0 || mount_fds);
+
+ r = fd_is_ns(userns_fd, CLONE_NEWUSER);
+ if (r < 0)
+ return log_debug_errno(r, "Failed to determine if file descriptor is user namespace: %m");
+ if (r == 0)
+ return log_debug_errno(SYNTHETIC_ERRNO(EBADF), "User namespace fd is not actually a user namespace fd.");
+
+ if (fstat(userns_fd, &st) < 0)
+ return log_debug_errno(errno, "Failed to fstat() user namespace: %m");
+
+ return userns_restrict_put_by_inode(
+ obj,
+ st.st_ino,
+ replace,
+ mount_fds,
+ n_mount_fds);
+}
+
+int userns_restrict_reset_by_inode(
+ struct userns_restrict_bpf *obj,
+ uint64_t ino) {
+
+ int r, outer_map_fd;
+ unsigned u;
+
+ assert(obj);
+ assert(ino != 0);
+
+ if (ino > UINT32_MAX) /* inodes larger than 32bit are definitely not included in our map, exit early */
+ return 0;
+
+ outer_map_fd = sym_bpf_map__fd(obj->maps.userns_mnt_id_hash);
+ if (outer_map_fd < 0)
+ return log_debug_errno(outer_map_fd, "Failed to get outer BPF map fd: %m");
+
+ u = (uint32_t) ino;
+
+ r = sym_bpf_map_delete_elem(outer_map_fd, &u);
+ if (r < 0)
+ return log_debug_errno(outer_map_fd, "Failed to remove entry for inode %" PRIu64 " from outer map: %m", ino);
+
+ return 0;
+}
+
+#else
+int userns_restrict_install(bool pin, struct userns_restrict_bpf **ret) {
+ return log_error_errno(SYNTHETIC_ERRNO(EOPNOTSUPP), "User Namespace Restriction BPF support disabled.");
+}
+
+struct userns_restrict_bpf *userns_restrict_bpf_free(struct userns_restrict_bpf *obj) {
+ return NULL;
+}
+
+int userns_restrict_put_by_fd(struct userns_restrict_bpf *obj, int userns_fd, bool replace, const int mount_fds[], size_t n_mount_fds) {
+ return log_error_errno(SYNTHETIC_ERRNO(EOPNOTSUPP), "User Namespace Restriction BPF support disabled.");
+}
+
+int userns_restrict_put_by_inode(struct userns_restrict_bpf *obj, uint64_t userns_inode, bool replace, const int mount_fds[], size_t n_mount_fds) {
+ return log_error_errno(SYNTHETIC_ERRNO(EOPNOTSUPP), "User Namespace Restriction BPF support disabled.");
+}
+
+int userns_restrict_reset_by_inode(struct userns_restrict_bpf *obj, uint64_t userns_inode) {
+ return log_error_errno(SYNTHETIC_ERRNO(EOPNOTSUPP), "User Namespace Restriction BPF support disabled.");
+}
+#endif
--- /dev/null
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+#pragma once
+
+#include <stdbool.h>
+
+#include "macro.h"
+
+#if HAVE_VMLINUX_H
+#include "bpf/userns_restrict/userns-restrict-skel.h"
+#else
+struct userns_restrict_bpf;
+#endif
+
+int userns_restrict_install(bool pin, struct userns_restrict_bpf **ret);
+struct userns_restrict_bpf *userns_restrict_bpf_free(struct userns_restrict_bpf *obj);
+
+int userns_restrict_put_by_fd(struct userns_restrict_bpf *obj, int userns_fd, bool replace, const int mount_fds[], size_t n_mount_fds);
+int userns_restrict_put_by_inode(struct userns_restrict_bpf *obj, uint64_t userns_inode, bool replace, const int mount_fds[], size_t n_mount_fds);
+
+int userns_restrict_reset_by_inode(struct userns_restrict_bpf *obj, uint64_t userns_inode);
+
+DEFINE_TRIVIAL_CLEANUP_FUNC(struct userns_restrict_bpf*, userns_restrict_bpf_free);
'c_args' : '-DSTANDALONE',
'link_with' : [
libbasic,
- libbasic_gcrypt,
libshared_fdisk,
libshared_static,
libsystemd_static,
char **exclude_files_target;
char **make_directories;
char **subvolumes;
+ char *default_subvolume;
EncryptMode encrypt;
VerityMode verity;
char *verity_match_key;
}
}
+static int config_parse_default_subvolume(
+ 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) {
+
+ char **subvol = ASSERT_PTR(data);
+ _cleanup_free_ char *p = NULL;
+ int r;
+
+ if (isempty(rvalue)) {
+ *subvol = mfree(*subvol);
+ return 0;
+ }
+
+ r = specifier_printf(rvalue, PATH_MAX-1, system_and_tmp_specifier_table, arg_root, NULL, &p);
+ if (r < 0) {
+ log_syntax(unit, LOG_WARNING, filename, line, r,
+ "Failed to expand specifiers in DefaultSubvolume= parameter, ignoring: %s", rvalue);
+ return 0;
+ }
+
+ r = path_simplify_and_warn(p, PATH_CHECK_ABSOLUTE, unit, filename, line, lvalue);
+ if (r < 0)
+ return 0;
+
+ return free_and_replace(*subvol, p);
+}
+
static DEFINE_CONFIG_PARSE_ENUM_WITH_DEFAULT(config_parse_encrypt, encrypt_mode, EncryptMode, ENCRYPT_OFF, "Invalid encryption mode");
static int config_parse_gpt_flags(
static int partition_read_definition(Partition *p, const char *path, const char *const *conf_file_dirs) {
ConfigTableItem table[] = {
- { "Partition", "Type", config_parse_type, 0, &p->type },
- { "Partition", "Label", config_parse_label, 0, &p->new_label },
- { "Partition", "UUID", config_parse_uuid, 0, p },
- { "Partition", "Priority", config_parse_int32, 0, &p->priority },
- { "Partition", "Weight", config_parse_weight, 0, &p->weight },
- { "Partition", "PaddingWeight", config_parse_weight, 0, &p->padding_weight },
- { "Partition", "SizeMinBytes", config_parse_size4096, -1, &p->size_min },
- { "Partition", "SizeMaxBytes", config_parse_size4096, 1, &p->size_max },
- { "Partition", "PaddingMinBytes", config_parse_size4096, -1, &p->padding_min },
- { "Partition", "PaddingMaxBytes", config_parse_size4096, 1, &p->padding_max },
- { "Partition", "FactoryReset", config_parse_bool, 0, &p->factory_reset },
- { "Partition", "CopyBlocks", config_parse_copy_blocks, 0, p },
- { "Partition", "Format", config_parse_fstype, 0, &p->format },
- { "Partition", "CopyFiles", config_parse_copy_files, 0, &p->copy_files },
- { "Partition", "ExcludeFiles", config_parse_exclude_files, 0, &p->exclude_files_source },
- { "Partition", "ExcludeFilesTarget", config_parse_exclude_files, 0, &p->exclude_files_target },
- { "Partition", "MakeDirectories", config_parse_make_dirs, 0, &p->make_directories },
- { "Partition", "Encrypt", config_parse_encrypt, 0, &p->encrypt },
- { "Partition", "Verity", config_parse_verity, 0, &p->verity },
- { "Partition", "VerityMatchKey", config_parse_string, 0, &p->verity_match_key },
- { "Partition", "Flags", config_parse_gpt_flags, 0, &p->gpt_flags },
- { "Partition", "ReadOnly", config_parse_tristate, 0, &p->read_only },
- { "Partition", "NoAuto", config_parse_tristate, 0, &p->no_auto },
- { "Partition", "GrowFileSystem", config_parse_tristate, 0, &p->growfs },
- { "Partition", "SplitName", config_parse_string, 0, &p->split_name_format },
- { "Partition", "Minimize", config_parse_minimize, 0, &p->minimize },
- { "Partition", "Subvolumes", config_parse_make_dirs, 0, &p->subvolumes },
- { "Partition", "VerityDataBlockSizeBytes", config_parse_block_size, 0, &p->verity_data_block_size },
- { "Partition", "VerityHashBlockSizeBytes", config_parse_block_size, 0, &p->verity_hash_block_size },
- { "Partition", "MountPoint", config_parse_mountpoint, 0, p },
- { "Partition", "EncryptedVolume", config_parse_encrypted_volume, 0, p },
+ { "Partition", "Type", config_parse_type, 0, &p->type },
+ { "Partition", "Label", config_parse_label, 0, &p->new_label },
+ { "Partition", "UUID", config_parse_uuid, 0, p },
+ { "Partition", "Priority", config_parse_int32, 0, &p->priority },
+ { "Partition", "Weight", config_parse_weight, 0, &p->weight },
+ { "Partition", "PaddingWeight", config_parse_weight, 0, &p->padding_weight },
+ { "Partition", "SizeMinBytes", config_parse_size4096, -1, &p->size_min },
+ { "Partition", "SizeMaxBytes", config_parse_size4096, 1, &p->size_max },
+ { "Partition", "PaddingMinBytes", config_parse_size4096, -1, &p->padding_min },
+ { "Partition", "PaddingMaxBytes", config_parse_size4096, 1, &p->padding_max },
+ { "Partition", "FactoryReset", config_parse_bool, 0, &p->factory_reset },
+ { "Partition", "CopyBlocks", config_parse_copy_blocks, 0, p },
+ { "Partition", "Format", config_parse_fstype, 0, &p->format },
+ { "Partition", "CopyFiles", config_parse_copy_files, 0, &p->copy_files },
+ { "Partition", "ExcludeFiles", config_parse_exclude_files, 0, &p->exclude_files_source },
+ { "Partition", "ExcludeFilesTarget", config_parse_exclude_files, 0, &p->exclude_files_target },
+ { "Partition", "MakeDirectories", config_parse_make_dirs, 0, &p->make_directories },
+ { "Partition", "Encrypt", config_parse_encrypt, 0, &p->encrypt },
+ { "Partition", "Verity", config_parse_verity, 0, &p->verity },
+ { "Partition", "VerityMatchKey", config_parse_string, 0, &p->verity_match_key },
+ { "Partition", "Flags", config_parse_gpt_flags, 0, &p->gpt_flags },
+ { "Partition", "ReadOnly", config_parse_tristate, 0, &p->read_only },
+ { "Partition", "NoAuto", config_parse_tristate, 0, &p->no_auto },
+ { "Partition", "GrowFileSystem", config_parse_tristate, 0, &p->growfs },
+ { "Partition", "SplitName", config_parse_string, 0, &p->split_name_format },
+ { "Partition", "Minimize", config_parse_minimize, 0, &p->minimize },
+ { "Partition", "Subvolumes", config_parse_make_dirs, 0, &p->subvolumes },
+ { "Partition", "DefaultSubvolume", config_parse_default_subvolume, 0, &p->default_subvolume },
+ { "Partition", "VerityDataBlockSizeBytes", config_parse_block_size, 0, &p->verity_data_block_size },
+ { "Partition", "VerityHashBlockSizeBytes", config_parse_block_size, 0, &p->verity_hash_block_size },
+ { "Partition", "MountPoint", config_parse_mountpoint, 0, p },
+ { "Partition", "EncryptedVolume", config_parse_encrypted_volume, 0, p },
{}
};
int r;
return log_syntax(NULL, LOG_ERR, path, 1, SYNTHETIC_ERRNO(EOPNOTSUPP),
"Subvolumes= cannot be used with --offline=yes");
+ if (p->default_subvolume && arg_offline > 0)
+ return log_syntax(NULL, LOG_ERR, path, 1, SYNTHETIC_ERRNO(EOPNOTSUPP),
+ "DefaultSubvolume= cannot be used with --offline=yes");
+
+ if (p->default_subvolume && !path_strv_contains(p->subvolumes, p->default_subvolume))
+ return log_syntax(NULL, LOG_ERR, path, 1, SYNTHETIC_ERRNO(EINVAL),
+ "DefaultSubvolume= must be one of the paths in Subvolumes=");
+
/* Verity partitions are read only, let's imply the RO flag hence, unless explicitly configured otherwise. */
if ((IN_SET(p->type.designator,
PARTITION_ROOT_VERITY,
return 0;
}
+static bool loop_device_error_is_fatal(const Partition *p, int r) {
+ assert(p);
+ return arg_offline == 0 || (r != -ENOENT && !ERRNO_IS_PRIVILEGE(r)) || !strv_isempty(p->subvolumes) || p->default_subvolume;
+}
+
static int partition_target_prepare(
Context *context,
Partition *p,
if (arg_offline <= 0) {
r = loop_device_make(whole_fd, O_RDWR, p->offset, size, context->sector_size, 0, LOCK_EX, &d);
- if (r < 0 && (arg_offline == 0 || (r != -ENOENT && !ERRNO_IS_PRIVILEGE(r)) || !strv_isempty(p->subvolumes)))
+ if (r < 0 && loop_device_error_is_fatal(p, r))
return log_error_errno(r, "Failed to make loopback device of future partition %" PRIu64 ": %m", p->partno);
if (r >= 0) {
t->loop = TAKE_PTR(d);
return 0;
}
+static int set_default_subvolume(Partition *p, const char *root) {
+ _cleanup_free_ char *path = NULL;
+ int r;
+
+ assert(p);
+ assert(root);
+
+ if (!p->default_subvolume)
+ return 0;
+
+ path = path_join(root, p->default_subvolume);
+ if (!path)
+ return log_oom();
+
+ r = btrfs_subvol_make_default(path);
+ if (r < 0)
+ return log_error_errno(r, "Failed to make '%s' the default subvolume: %m", p->default_subvolume);
+
+ return 0;
+}
+
static bool partition_needs_populate(Partition *p) {
assert(p);
return !strv_isempty(p->copy_files) || !strv_isempty(p->make_directories);
if (do_make_directories(p, fs) < 0)
_exit(EXIT_FAILURE);
+ if (set_default_subvolume(p, fs) < 0)
+ _exit(EXIT_FAILURE);
+
r = syncfs_path(AT_FDCWD, fs);
if (r < 0) {
log_error_errno(r, "Failed to synchronize written files: %m");
if (arg_offline <= 0) {
r = loop_device_make(fd, O_RDWR, 0, UINT64_MAX, context->sector_size, 0, LOCK_EX, &d);
- if (r < 0 && (arg_offline == 0 || (r != -ENOENT && !ERRNO_IS_PRIVILEGE(r)) || !strv_isempty(p->subvolumes)))
+ if (r < 0 && loop_device_error_is_fatal(p, r))
return log_error_errno(r, "Failed to make loopback device of %s: %m", temp);
}
return 0;
}
-static bool marker_matches_images(const char *marker, const char *name_or_path, char **extension_image_paths) {
+static bool marker_matches_images(const char *marker, const char *name_or_path, char **extension_image_paths, bool match_all) {
_cleanup_strv_free_ char **root_and_extensions = NULL;
- const char *a;
int r;
assert(marker);
* list of images/paths. We enforce strict 1:1 matching, so that we are sure
* we are detaching exactly what was attached.
* For each image, starting with the root, we look for a token in the marker,
- * and return a negative answer on any non-matching combination. */
+ * and return a negative answer on any non-matching combination.
+ * If a partial match is allowed, then return immediately once it is found, otherwise
+ * ensure that everything matches. */
root_and_extensions = strv_new(name_or_path);
if (!root_and_extensions)
if (r < 0)
return r;
- STRV_FOREACH(image_name_or_path, root_and_extensions) {
- _cleanup_free_ char *image = NULL;
+ /* Ensure the number of images passed matches the number of images listed in the marker */
+ while (!isempty(marker))
+ STRV_FOREACH(image_name_or_path, root_and_extensions) {
+ _cleanup_free_ char *image = NULL, *base_image = NULL, *base_image_name_or_path = NULL;
- r = extract_first_word(&marker, &image, ":", EXTRACT_UNQUOTE|EXTRACT_RETAIN_ESCAPE);
- if (r < 0)
- return log_debug_errno(r, "Failed to parse marker: %s", marker);
- if (r == 0)
- return false;
-
- a = last_path_component(image);
-
- if (image_name_is_valid(*image_name_or_path)) {
- const char *e, *underscore;
-
- /* We shall match against an image name. In that case let's compare the last component, and optionally
- * allow either a suffix of ".raw" or a series of "/".
- * But allow matching on a different version of the same image, when a "_" is used as a separator. */
- underscore = strchr(*image_name_or_path, '_');
- if (underscore) {
- if (strneq(a, *image_name_or_path, underscore - *image_name_or_path))
- continue;
- return false;
- }
-
- e = startswith(a, *image_name_or_path);
- if (!e)
- return false;
-
- if(!(e[strspn(e, "/")] == 0 || streq(e, ".raw")))
- return false;
- } else {
- const char *b, *underscore;
- size_t l;
-
- /* We shall match against a path. Let's ignore any prefix here though, as often there are many ways to
- * reach the same file. However, in this mode, let's validate any file suffix.
- * But also ensure that we don't fail if both components don't have a '/' at all
- * (strcspn returns the full length of the string in that case, which might not
- * match as the versions might differ). */
-
- l = strcspn(a, "/");
- b = last_path_component(*image_name_or_path);
-
- if ((a[l] != '/') != !strchr(b, '/')) /* One is a directory, the other is not */
+ r = extract_first_word(&marker, &image, ":", EXTRACT_UNQUOTE|EXTRACT_RETAIN_ESCAPE);
+ if (r < 0)
+ return log_debug_errno(r, "Failed to parse marker: %s", marker);
+ if (r == 0)
return false;
- if (a[l] != 0 && strcspn(b, "/") != l)
- return false;
+ r = path_extract_image_name(image, &base_image);
+ if (r < 0)
+ return log_debug_errno(r, "Failed to extract image name from %s, ignoring: %m", image);
- underscore = strchr(b, '_');
- if (underscore)
- l = underscore - b;
- else { /* Either component could be versioned */
- underscore = strchr(a, '_');
- if (underscore)
- l = underscore - a;
- }
+ r = path_extract_image_name(*image_name_or_path, &base_image_name_or_path);
+ if (r < 0)
+ return log_debug_errno(r, "Failed to extract image name from %s, ignoring: %m", *image_name_or_path);
- if (!strneq(a, b, l))
- return false;
+ if (!streq(base_image, base_image_name_or_path)) {
+ if (match_all)
+ return false;
+ } else if (!match_all)
+ return true;
}
- }
- return true;
+ return match_all;
}
static int test_chroot_dropin(
if (!name_or_path)
r = true;
else
- r = marker_matches_images(marker, name_or_path, extension_image_paths);
+ /* When detaching we want to match exactly on all images, but when inspecting we only need
+ * to get the state of one component */
+ r = marker_matches_images(marker, name_or_path, extension_image_paths, ret_marker != NULL);
if (ret_marker)
*ret_marker = TAKE_PTR(marker);
assert_se(f = memstream_init(&m));
(void) fprintf(f, "%s", strna(dns_resource_record_to_string(rr)));
- if (dns_resource_record_to_json(rr, &v) < 0)
- return 0;
-
- (void) json_variant_dump(v, JSON_FORMAT_PRETTY|JSON_FORMAT_COLOR|JSON_FORMAT_SOURCE, f, NULL);
- (void) dns_resource_record_to_wire_format(rr, false);
- (void) dns_resource_record_to_wire_format(rr, true);
+ assert_se(dns_resource_record_to_json(rr, &v) >= 0);
+ assert_se(json_variant_dump(v, JSON_FORMAT_PRETTY|JSON_FORMAT_COLOR|JSON_FORMAT_SOURCE, f, NULL) >= 0);
+ assert_se(dns_resource_record_to_wire_format(rr, false) >= 0);
+ assert_se(dns_resource_record_to_wire_format(rr, true) >= 0);
return 0;
}
endif
link_with = [
- libbasic_gcrypt,
libshared,
libsystemd_resolve_core,
]
_cleanup_(gcry_md_closep) gcry_md_hd_t md = NULL;
void *hash;
size_t hash_size;
+ int r;
- initialize_libgcrypt(false);
+ r = initialize_libgcrypt(false);
+ if (r < 0)
+ return r;
#endif
switch (rrsig->rrsig.algorithm) {
int dnssec_verify_dnskey_by_ds(DnsResourceRecord *dnskey, DnsResourceRecord *ds, bool mask_revoke) {
uint8_t wire_format[DNS_WIRE_FORMAT_HOSTNAME_MAX];
+ size_t encoded_length;
int r;
assert(dnskey);
r = dns_name_to_wire_format(dns_resource_key_name(dnskey->key), wire_format, sizeof wire_format, true);
if (r < 0)
return r;
+ encoded_length = r;
hash_md_t md_algorithm = digest_to_hash_md(ds->ds.digest_type);
if (EVP_DigestInit_ex(ctx, md_algorithm, NULL) <= 0)
return -EIO;
- if (EVP_DigestUpdate(ctx, wire_format, r) <= 0)
+ if (EVP_DigestUpdate(ctx, wire_format, encoded_length) <= 0)
return -EIO;
if (mask_revoke)
if (md_algorithm < 0)
return -EOPNOTSUPP;
- initialize_libgcrypt(false);
+ r = initialize_libgcrypt(false);
+ if (r < 0)
+ return r;
_cleanup_(gcry_md_closep) gcry_md_hd_t md = NULL;
if (gcry_err_code(err) != GPG_ERR_NO_ERROR || !md)
return -EIO;
- gcry_md_write(md, wire_format, r);
+ gcry_md_write(md, wire_format, encoded_length);
if (mask_revoke)
md_add_uint16(md, dnskey->dnskey.flags & ~DNSKEY_FLAG_REVOKE);
else
if (algorithm < 0)
return algorithm;
- initialize_libgcrypt(false);
+ r = initialize_libgcrypt(false);
+ if (r < 0)
+ return r;
+ size_t encoded_length;
unsigned hash_size = gcry_md_get_algo_dlen(algorithm);
assert(hash_size > 0);
r = dns_name_to_wire_format(name, wire_format, sizeof(wire_format), true);
if (r < 0)
return r;
+ encoded_length = r;
_cleanup_(gcry_md_closep) gcry_md_hd_t md = NULL;
gcry_error_t err = gcry_md_open(&md, algorithm, 0);
if (gcry_err_code(err) != GPG_ERR_NO_ERROR || !md)
return -EIO;
- gcry_md_write(md, wire_format, r);
+ gcry_md_write(md, wire_format, encoded_length);
gcry_md_write(md, nsec3->nsec3.salt, nsec3->nsec3.salt_size);
void *result = gcry_md_read(md, 0);
if (dns_name_parent(&name) <= 0)
return false;
- return dns_name_equal(name, "_tcp.local") || dns_name_equal(name, "_udp.local");
+ return dns_name_equal(name, "_tcp.local") > 0 || dns_name_equal(name, "_udp.local") > 0;
}
int dns_resource_key_equal(const DnsResourceKey *a, const DnsResourceKey *b) {
case DNS_TYPE_RRSIG:
/* do the fast comparisons first */
- return a->rrsig.type_covered == b->rrsig.type_covered &&
- a->rrsig.algorithm == b->rrsig.algorithm &&
- a->rrsig.labels == b->rrsig.labels &&
- a->rrsig.original_ttl == b->rrsig.original_ttl &&
- a->rrsig.expiration == b->rrsig.expiration &&
- a->rrsig.inception == b->rrsig.inception &&
- a->rrsig.key_tag == b->rrsig.key_tag &&
- FIELD_EQUAL(a->rrsig, b->rrsig, signature) &&
- dns_name_equal(a->rrsig.signer, b->rrsig.signer);
+ if (!(a->rrsig.type_covered == b->rrsig.type_covered &&
+ a->rrsig.algorithm == b->rrsig.algorithm &&
+ a->rrsig.labels == b->rrsig.labels &&
+ a->rrsig.original_ttl == b->rrsig.original_ttl &&
+ a->rrsig.expiration == b->rrsig.expiration &&
+ a->rrsig.inception == b->rrsig.inception &&
+ a->rrsig.key_tag == b->rrsig.key_tag &&
+ FIELD_EQUAL(a->rrsig, b->rrsig, signature)))
+ return false;
+
+ return dns_name_equal(a->rrsig.signer, b->rrsig.signer);
case DNS_TYPE_NSEC:
- return dns_name_equal(a->nsec.next_domain_name, b->nsec.next_domain_name) &&
- bitmap_equal(a->nsec.types, b->nsec.types);
+ r = dns_name_equal(a->nsec.next_domain_name, b->nsec.next_domain_name);
+ if (r <= 0)
+ return r;
+
+ return bitmap_equal(a->nsec.types, b->nsec.types);
case DNS_TYPE_NSEC3:
return a->nsec3.algorithm == b->nsec3.algorithm &&
case DNS_TYPE_SVCB:
case DNS_TYPE_HTTPS:
- return a->svcb.priority == b->svcb.priority &&
- dns_name_equal(a->svcb.target_name, b->svcb.target_name) &&
- dns_svc_params_equal(a->svcb.params, b->svcb.params);
+
+ if (!(a->svcb.priority == b->svcb.priority &&
+ dns_svc_params_equal(a->svcb.params, b->svcb.params)))
+ return false;
+
+ return dns_name_equal(a->svcb.target_name, b->svcb.target_name);
case DNS_TYPE_CAA:
return a->caa.flags == b->caa.flags &&
/* All good. */
break;
- case DNS_TRANSACTION_DNSSEC_FAILED: {
- DnsAnswer *empty;
-
+ case DNS_TRANSACTION_DNSSEC_FAILED:
/* We handle DNSSEC failures different from other errors, as we care about the DNSSEC
* validation result */
/* The answer would normally be replaced by the validated subset, but at this point
* we aren't going to bother validating the rest, so just drop it. */
- empty = dns_answer_new(0);
- if (!empty)
- return -ENOMEM;
- DNS_ANSWER_REPLACE(t->answer, empty);
+ t->answer = dns_answer_unref(t->answer);
dns_transaction_complete(t, DNS_TRANSACTION_DNSSEC_FAILED);
return 0;
- }
default:
log_debug("Auxiliary DNSSEC RR query failed with %s", dns_transaction_state_to_string(dt->state));
r = dns_transaction_go(aux);
if (r < 0)
return r;
- if (ret)
- *ret = aux;
}
+ if (ret)
+ *ret = aux;
return 1;
}
if (s->withdrawn)
continue;
- if (dns_name_equal(dns_resource_key_name(s->srv_rr->key), name)) {
+ if (dns_name_equal(dns_resource_key_name(s->srv_rr->key), name) > 0) {
_cleanup_free_ char *path = NULL;
s->withdrawn = true;
/* if the client didn't set the more flag, it is using us incorrectly */
if (!FLAGS_SET(flags, VARLINK_METHOD_MORE))
- return varlink_error_invalid_parameter(link, NULL);
+ return varlink_error(link, VARLINK_ERROR_EXPECTED_MORE, NULL);
if (json_variant_elements(parameters) > 0)
return varlink_error_invalid_parameter(link, parameters);
}
static int make_unit_name(sd_bus *bus, UnitType t, char **ret) {
+ unsigned soft_reboots_count = 0;
const char *unique, *id;
char *p;
int r;
assert(bus);
assert(t >= 0);
assert(t < _UNIT_TYPE_MAX);
+ assert(ret);
r = sd_bus_get_unique_name(bus, &unique);
if (r < 0) {
"Unique name %s has unexpected format.",
unique);
- p = strjoin("run-u", id, ".", unit_type_to_string(t));
- if (!p)
- return log_oom();
+ /* The unique D-Bus names are actually unique per D-Bus instance, so on soft-reboot they will wrap
+ * and start over since the D-Bus broker is restarted. If there's a failed unit left behind that
+ * hasn't been garbage collected, we'll conflict. Append the soft-reboot counter to avoid clashing. */
+ if (arg_runtime_scope == RUNTIME_SCOPE_SYSTEM) {
+ _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
+ r = bus_get_property_trivial(
+ bus, bus_systemd_mgr, "SoftRebootsCount", &error, 'u', &soft_reboots_count);
+ if (r < 0)
+ log_debug_errno(r,
+ "Failed to get SoftRebootsCount property, ignoring: %s",
+ bus_error_message(&error, r));
+ }
+
+ if (soft_reboots_count > 0) {
+ if (asprintf(&p, "run-u%s-s%u.%s", id, soft_reboots_count, unit_type_to_string(t)) < 0)
+ return log_oom();
+ } else {
+ p = strjoin("run-u", id, ".", unit_type_to_string(t));
+ if (!p)
+ return log_oom();
+ }
*ret = p;
return 0;
# else
# error "Unknown RISC-V ABI"
# endif
-#elif defined(__s390__)
- /* s390-linux-gnu */
#elif defined(__s390x__)
{ "lib64", 0, "usr/lib/"LIB_ARCH_TUPLE"\0"
"usr/lib64\0"
"usr/lib\0", "ld-lsb-s390x.so.3" },
# define KNOW_LIB64_DIRS 1
+#elif defined(__s390__)
+ /* s390-linux-gnu */
#elif defined(__sparc__)
#endif
/* gcc doesn't allow pragma to be used within constructs, hence log about this separately below */
* When removing this file move these back to bpf-dlopen.h */
extern int (*sym_bpf_map_create)(enum bpf_map_type, const char *, __u32, __u32, __u32, const struct bpf_map_create_opts *);
extern int (*sym_libbpf_probe_bpf_prog_type)(enum bpf_prog_type, const void *);
+extern struct bpf_map* (*sym_bpf_object__next_map)(const struct bpf_object *obj, const struct bpf_map *map);
/* compat symbols removed in libbpf 1.0 */
extern int (*sym_bpf_create_map)(enum bpf_map_type, int key_size, int value_size, int max_entries, __u32 map_flags);
#define MODERN_LIBBPF 0
#endif
-DLSYM_FUNCTION(bpf_program__attach_cgroup);
-DLSYM_FUNCTION(bpf_program__attach_lsm);
-DLSYM_FUNCTION(bpf_link__fd);
DLSYM_FUNCTION(bpf_link__destroy);
+DLSYM_FUNCTION(bpf_link__fd);
+DLSYM_FUNCTION(bpf_link__open);
+DLSYM_FUNCTION(bpf_link__pin);
DLSYM_FUNCTION(bpf_map__fd);
DLSYM_FUNCTION(bpf_map__name);
+DLSYM_FUNCTION(bpf_map__set_inner_map_fd);
DLSYM_FUNCTION(bpf_map__set_max_entries);
-DLSYM_FUNCTION(bpf_map_update_elem);
+DLSYM_FUNCTION(bpf_map__set_pin_path);
DLSYM_FUNCTION(bpf_map_delete_elem);
-DLSYM_FUNCTION(bpf_map__set_inner_map_fd);
-DLSYM_FUNCTION(bpf_object__open_skeleton);
-DLSYM_FUNCTION(bpf_object__load_skeleton);
+DLSYM_FUNCTION(bpf_map_get_fd_by_id);
+DLSYM_FUNCTION(bpf_map_lookup_elem);
+DLSYM_FUNCTION(bpf_map_update_elem);
DLSYM_FUNCTION(bpf_object__attach_skeleton);
-DLSYM_FUNCTION(bpf_object__detach_skeleton);
DLSYM_FUNCTION(bpf_object__destroy_skeleton);
+DLSYM_FUNCTION(bpf_object__detach_skeleton);
+DLSYM_FUNCTION(bpf_object__load_skeleton);
+DLSYM_FUNCTION(bpf_object__name);
+DLSYM_FUNCTION(bpf_object__open_skeleton);
+DLSYM_FUNCTION(bpf_object__pin_maps);
+DLSYM_FUNCTION(bpf_program__attach);
+DLSYM_FUNCTION(bpf_program__attach_cgroup);
+DLSYM_FUNCTION(bpf_program__attach_lsm);
DLSYM_FUNCTION(bpf_program__name);
-DLSYM_FUNCTION(libbpf_set_print);
DLSYM_FUNCTION(libbpf_get_error);
+DLSYM_FUNCTION(libbpf_set_print);
+DLSYM_FUNCTION(ring_buffer__epoll_fd);
+DLSYM_FUNCTION(ring_buffer__free);
+DLSYM_FUNCTION(ring_buffer__new);
+DLSYM_FUNCTION(ring_buffer__poll);
/* new symbols available from libbpf 0.7.0 */
int (*sym_bpf_map_create)(enum bpf_map_type, const char *, __u32, __u32, __u32, const struct bpf_map_create_opts *);
int (*sym_libbpf_probe_bpf_prog_type)(enum bpf_prog_type, const void *);
+struct bpf_map* (*sym_bpf_object__next_map)(const struct bpf_object *obj, const struct bpf_map *map);
/* compat symbols removed in libbpf 1.0 */
int (*sym_bpf_create_map)(enum bpf_map_type, int key_size, int value_size, int max_entries, __u32 map_flags);
return log_debug_errno(SYNTHETIC_ERRNO(EOPNOTSUPP),
"neither libbpf.so.1 nor libbpf.so.0 are installed: %s", dlerror());
+ log_debug("Loaded 'libbpf.so.0' via dlopen()");
+
/* symbols deprecated in 1.0 we use as compat */
r = dlsym_many_or_warn(
dl, LOG_DEBUG,
#if MODERN_LIBBPF
/* Don't exist anymore in new libbpf, hence cannot type check them */
DLSYM_ARG_FORCE(bpf_create_map),
- DLSYM_ARG_FORCE(bpf_probe_prog_type));
+ DLSYM_ARG_FORCE(bpf_probe_prog_type)
#else
DLSYM_ARG(bpf_create_map),
- DLSYM_ARG(bpf_probe_prog_type));
+ DLSYM_ARG(bpf_probe_prog_type)
#endif
+ );
+
+ /* NB: we don't try to load bpf_object__next_map() on old versions */
} else {
+ log_debug("Loaded 'libbpf.so.1' via dlopen()");
+
/* symbols available from 0.7.0 */
r = dlsym_many_or_warn(
dl, LOG_DEBUG,
#if MODERN_LIBBPF
DLSYM_ARG(bpf_map_create),
- DLSYM_ARG(libbpf_probe_bpf_prog_type)
+ DLSYM_ARG(libbpf_probe_bpf_prog_type),
+ DLSYM_ARG(bpf_object__next_map)
#else
/* These symbols did not exist in old libbpf, hence we cannot type check them */
DLSYM_ARG_FORCE(bpf_map_create),
- DLSYM_ARG_FORCE(libbpf_probe_bpf_prog_type)
+ DLSYM_ARG_FORCE(libbpf_probe_bpf_prog_type),
+ DLSYM_ARG_FORCE(bpf_object__next_map)
#endif
);
}
+ if (r < 0)
+ return r;
r = dlsym_many_or_warn(
dl, LOG_DEBUG,
DLSYM_ARG(bpf_link__destroy),
DLSYM_ARG(bpf_link__fd),
+ DLSYM_ARG(bpf_link__open),
+ DLSYM_ARG(bpf_link__pin),
DLSYM_ARG(bpf_map__fd),
DLSYM_ARG(bpf_map__name),
+ DLSYM_ARG(bpf_map__set_inner_map_fd),
DLSYM_ARG(bpf_map__set_max_entries),
- DLSYM_ARG(bpf_map_update_elem),
+ DLSYM_ARG(bpf_map__set_pin_path),
DLSYM_ARG(bpf_map_delete_elem),
- DLSYM_ARG(bpf_map__set_inner_map_fd),
- DLSYM_ARG(bpf_object__open_skeleton),
- DLSYM_ARG(bpf_object__load_skeleton),
+ DLSYM_ARG(bpf_map_get_fd_by_id),
+ DLSYM_ARG(bpf_map_lookup_elem),
+ DLSYM_ARG(bpf_map_update_elem),
DLSYM_ARG(bpf_object__attach_skeleton),
- DLSYM_ARG(bpf_object__detach_skeleton),
DLSYM_ARG(bpf_object__destroy_skeleton),
+ DLSYM_ARG(bpf_object__detach_skeleton),
+ DLSYM_ARG(bpf_object__load_skeleton),
+ DLSYM_ARG(bpf_object__name),
+ DLSYM_ARG(bpf_object__open_skeleton),
+ DLSYM_ARG(bpf_object__pin_maps),
#if MODERN_LIBBPF
+ DLSYM_ARG(bpf_program__attach),
DLSYM_ARG(bpf_program__attach_cgroup),
DLSYM_ARG(bpf_program__attach_lsm),
#else
/* libbpf added a "const" to function parameters where it should not have, ignore this type incompatibility */
+ DLSYM_ARG_FORCE(bpf_program__attach),
DLSYM_ARG_FORCE(bpf_program__attach_cgroup),
DLSYM_ARG_FORCE(bpf_program__attach_lsm),
#endif
DLSYM_ARG(bpf_program__name),
+ DLSYM_ARG(libbpf_get_error),
DLSYM_ARG(libbpf_set_print),
- DLSYM_ARG(libbpf_get_error));
+ DLSYM_ARG(ring_buffer__epoll_fd),
+ DLSYM_ARG(ring_buffer__free),
+ DLSYM_ARG(ring_buffer__new),
+ DLSYM_ARG(ring_buffer__poll));
if (r < 0)
return r;
#include "bpf-compat.h"
#include "dlfcn-util.h"
-DLSYM_PROTOTYPE(bpf_program__attach_cgroup);
-DLSYM_PROTOTYPE(bpf_program__attach_lsm);
-DLSYM_PROTOTYPE(bpf_link__fd);
DLSYM_PROTOTYPE(bpf_link__destroy);
+DLSYM_PROTOTYPE(bpf_link__fd);
+DLSYM_PROTOTYPE(bpf_link__open);
+DLSYM_PROTOTYPE(bpf_link__pin);
DLSYM_PROTOTYPE(bpf_map__fd);
DLSYM_PROTOTYPE(bpf_map__name);
+DLSYM_PROTOTYPE(bpf_map__set_inner_map_fd);
DLSYM_PROTOTYPE(bpf_map__set_max_entries);
-DLSYM_PROTOTYPE(bpf_map_update_elem);
+DLSYM_PROTOTYPE(bpf_map__set_pin_path);
DLSYM_PROTOTYPE(bpf_map_delete_elem);
-DLSYM_PROTOTYPE(bpf_map__set_inner_map_fd);
+DLSYM_PROTOTYPE(bpf_map_get_fd_by_id);
+DLSYM_PROTOTYPE(bpf_map_lookup_elem);
+DLSYM_PROTOTYPE(bpf_map_update_elem);
/* The *_skeleton APIs are autogenerated by bpftool, the targets can be found
* in ./build/src/core/bpf/socket_bind/socket-bind.skel.h */
-DLSYM_PROTOTYPE(bpf_object__open_skeleton);
-DLSYM_PROTOTYPE(bpf_object__load_skeleton);
DLSYM_PROTOTYPE(bpf_object__attach_skeleton);
-DLSYM_PROTOTYPE(bpf_object__detach_skeleton);
DLSYM_PROTOTYPE(bpf_object__destroy_skeleton);
+DLSYM_PROTOTYPE(bpf_object__detach_skeleton);
+DLSYM_PROTOTYPE(bpf_object__load_skeleton);
+DLSYM_PROTOTYPE(bpf_object__name);
+DLSYM_PROTOTYPE(bpf_object__open_skeleton);
+DLSYM_PROTOTYPE(bpf_object__pin_maps);
+DLSYM_PROTOTYPE(bpf_program__attach);
+DLSYM_PROTOTYPE(bpf_program__attach_cgroup);
+DLSYM_PROTOTYPE(bpf_program__attach_lsm);
DLSYM_PROTOTYPE(bpf_program__name);
-DLSYM_PROTOTYPE(libbpf_set_print);
DLSYM_PROTOTYPE(libbpf_get_error);
+DLSYM_PROTOTYPE(libbpf_set_print);
+DLSYM_PROTOTYPE(ring_buffer__epoll_fd);
+DLSYM_PROTOTYPE(ring_buffer__free);
+DLSYM_PROTOTYPE(ring_buffer__new);
+DLSYM_PROTOTYPE(ring_buffer__poll);
#endif
return btrfs_subvol_auto_qgroup_fd(fd, subvol_id, create_intermediary_qgroup);
}
+int btrfs_subvol_make_default(const char *path) {
+ _cleanup_close_ int fd = -EBADF;
+ uint64_t id;
+ int r;
+
+ assert(path);
+
+ fd = open(path, O_NOCTTY|O_CLOEXEC|O_DIRECTORY);
+ if (fd < 0)
+ return -errno;
+
+ r = btrfs_subvol_get_id_fd(fd, &id);
+ if (r < 0)
+ return r;
+
+ return RET_NERRNO(ioctl(fd, BTRFS_IOC_DEFAULT_SUBVOL, &id));
+}
+
int btrfs_subvol_get_parent(int fd, uint64_t subvol_id, uint64_t *ret) {
struct btrfs_ioctl_search_args args = {
int btrfs_subvol_auto_qgroup_fd(int fd, uint64_t subvol_id, bool new_qgroup);
int btrfs_subvol_auto_qgroup(const char *path, uint64_t subvol_id, bool create_intermediary_qgroup);
+int btrfs_subvol_make_default(const char *path);
+
int btrfs_qgroupid_make(uint64_t level, uint64_t id, uint64_t *ret);
int btrfs_qgroupid_split(uint64_t qgroupid, uint64_t *level, uint64_t *id);
* always under the same name. */
r = varlink_get_current_parameters(link, &v);
- if (r < 0)
- return r;
+ if (r < 0) {
+ log_debug_errno(r, "Unable to query current parameters: %m");
+ return false;
+ }
JsonVariant *b;
b = json_variant_by_key(v, "allowInteractiveAuthentication");
return 0;
}
+int cg_fd_attach(int fd, pid_t pid) {
+ char c[DECIMAL_STR_MAX(pid_t) + 2];
+
+ assert(fd >= 0);
+ assert(pid >= 0);
+
+ if (pid == 0)
+ pid = getpid_cached();
+
+ xsprintf(c, PID_FMT "\n", pid);
+
+ return write_string_file_at(fd, "cgroup.procs", c, WRITE_STRING_FILE_DISABLE_BUFFER);
+}
+
int cg_attach_fallback(const char *controller, const char *path, pid_t pid) {
int r;
bool done = false;
_cleanup_set_free_ Set *s = NULL;
int r, ret = 0;
- pid_t my_pid;
assert(cfrom);
assert(pfrom);
assert(cto);
assert(pto);
- s = set_new(NULL);
- if (!s)
- return -ENOMEM;
-
- my_pid = getpid_cached();
-
do {
_cleanup_fclose_ FILE *f = NULL;
- pid_t pid = 0;
+ pid_t pid;
+
done = true;
r = cg_enumerate_processes(cfrom, pfrom, &f);
- if (r < 0) {
- if (ret >= 0 && r != -ENOENT)
- return r;
-
- return ret;
- }
+ if (r < 0)
+ return RET_GATHER(ret, r);
while ((r = cg_read_pid(f, &pid)) > 0) {
-
- /* This might do weird stuff if we aren't a
- * single-threaded program. However, we
- * luckily know we are not */
- if ((flags & CGROUP_IGNORE_SELF) && pid == my_pid)
+ /* This might do weird stuff if we aren't a single-threaded program. However, we
+ * luckily know we are. */
+ if (FLAGS_SET(flags, CGROUP_IGNORE_SELF) && pid == getpid_cached())
continue;
- if (set_get(s, PID_TO_PTR(pid)) == PID_TO_PTR(pid))
+ if (set_contains(s, PID_TO_PTR(pid)))
continue;
- /* Ignore kernel threads. Since they can only
- * exist in the root cgroup, we only check for
- * them there. */
- if (cfrom &&
- empty_or_root(pfrom) &&
+ /* Ignore kernel threads. Since they can only exist in the root cgroup, we only
+ * check for them there. */
+ if (cfrom && empty_or_root(pfrom) &&
pid_is_kernel_thread(pid) > 0)
continue;
r = cg_attach(cto, pto, pid);
if (r < 0) {
- if (ret >= 0 && r != -ESRCH)
- ret = r;
+ if (r != -ESRCH)
+ RET_GATHER(ret, r);
} else if (ret == 0)
ret = 1;
done = false;
- r = set_put(s, PID_TO_PTR(pid));
- if (r < 0) {
- if (ret >= 0)
- return r;
-
- return ret;
- }
- }
-
- if (r < 0) {
- if (ret >= 0)
- return r;
-
- return ret;
+ r = set_ensure_put(&s, /* hash_ops = */ NULL, PID_TO_PTR(pid));
+ if (r < 0)
+ return RET_GATHER(ret, r);
}
+ if (r < 0)
+ return RET_GATHER(ret, r);
} while (!done);
return ret;
int cg_create(const char *controller, const char *path);
int cg_attach(const char *controller, const char *path, pid_t pid);
+int cg_fd_attach(int fd, pid_t pid);
int cg_attach_fallback(const char *controller, const char *path, pid_t pid);
int cg_create_and_attach(const char *controller, const char *path, pid_t pid);
delegate = r > 0;
if (FLAGS_SET(flags, OUTPUT_CGROUP_ID)) {
- cg_file_handle fh = CG_FILE_HANDLE_INIT;
- int mnt_id = -1;
-
- if (name_to_handle_at(
- fd,
- "",
- &fh.file_handle,
- &mnt_id,
- AT_EMPTY_PATH) < 0)
+ r = cg_fd_get_cgroupid(fd, &cgroupid);
+ if (r < 0)
log_debug_errno(errno, "Failed to determine cgroup ID of %s, ignoring: %m", path);
- else
- cgroupid = CG_FILE_HANDLE_CGROUPID(fh);
}
r = path_extract_filename(path, &b);
#include "capability-util.h"
#include "chattr-util.h"
#include "constants.h"
+#include "copy.h"
#include "creds-util.h"
#include "efi-api.h"
#include "env-util.h"
#include "fs-util.h"
#include "io-util.h"
#include "memory-util.h"
-#include "mkdir.h"
+#include "mkdir-label.h"
#include "openssl-util.h"
#include "parse-util.h"
#include "path-util.h"
#include "random-util.h"
+#include "recurse-dir.h"
#include "sparse-endian.h"
#include "stat-util.h"
#include "tmpfile-util.h"
return 0;
}
+
+static int pick_up_credential_one(
+ int credential_dir_fd,
+ const char *credential_name,
+ const PickUpCredential *table_entry) {
+
+ _cleanup_free_ char *fn = NULL, *target_path = NULL;
+ const char *e;
+ int r;
+
+ assert(credential_dir_fd >= 0);
+ assert(credential_name);
+ assert(table_entry);
+
+ e = startswith(credential_name, table_entry->credential_prefix);
+ if (!e)
+ return 0; /* unmatched */
+
+ fn = strjoin(e, table_entry->filename_suffix);
+ if (!fn)
+ return log_oom();
+
+ if (!filename_is_valid(fn))
+ return log_warning_errno(SYNTHETIC_ERRNO(EINVAL),
+ "Passed credential '%s' would result in invalid filename '%s'.",
+ credential_name, fn);
+
+ r = mkdir_p_label(table_entry->target_dir, 0755);
+ if (r < 0)
+ return log_warning_errno(r, "Failed to create '%s': %m", table_entry->target_dir);
+
+ target_path = path_join(table_entry->target_dir, fn);
+ if (!target_path)
+ return log_oom();
+
+ r = copy_file_at(
+ credential_dir_fd, credential_name,
+ AT_FDCWD, target_path,
+ /* open_flags= */ 0,
+ 0644,
+ /* flags= */ 0);
+ if (r < 0)
+ return log_warning_errno(r, "Failed to copy credential %s → file %s: %m",
+ credential_name, target_path);
+
+ log_info("Installed %s from credential.", target_path);
+ return 1; /* done */
+}
+
+int pick_up_credentials(const PickUpCredential *table, size_t n_table_entry) {
+ _cleanup_close_ int credential_dir_fd = -EBADF;
+ int r, ret = 0;
+
+ assert(table);
+ assert(n_table_entry > 0);
+
+ credential_dir_fd = open_credentials_dir();
+ if (IN_SET(credential_dir_fd, -ENXIO, -ENOENT)) {
+ /* Credential env var not set, or dir doesn't exist. */
+ log_debug("No credentials found.");
+ return 0;
+ }
+ if (credential_dir_fd < 0)
+ return log_error_errno(credential_dir_fd, "Failed to open credentials directory: %m");
+
+ _cleanup_free_ DirectoryEntries *des = NULL;
+ r = readdir_all(credential_dir_fd, RECURSE_DIR_SORT|RECURSE_DIR_IGNORE_DOT|RECURSE_DIR_ENSURE_TYPE, &des);
+ if (r < 0)
+ return log_error_errno(r, "Failed to enumerate credentials: %m");
+
+ FOREACH_ARRAY(i, des->entries, des->n_entries) {
+ struct dirent *de = *i;
+
+ if (de->d_type != DT_REG)
+ continue;
+
+ FOREACH_ARRAY(t, table, n_table_entry) {
+ r = pick_up_credential_one(credential_dir_fd, de->d_name, t);
+ if (r != 0) {
+ RET_GATHER(ret, r);
+ break; /* Done, or failed. Let's move to the next credential. */
+ }
+ }
+ }
+
+ return ret;
+}
int ipc_encrypt_credential(const char *name, usec_t timestamp, usec_t not_after, uid_t uid, const struct iovec *input, CredentialFlags flags, struct iovec *ret);
int ipc_decrypt_credential(const char *validate_name, usec_t validate_timestamp, uid_t uid, const struct iovec *input, CredentialFlags flags, struct iovec *ret);
+
+typedef struct PickUpCredential {
+ const char *credential_prefix;
+ const char *target_dir;
+ const char *filename_suffix;
+} PickUpCredential;
+
+int pick_up_credentials(const PickUpCredential *table, size_t n_table_entry);
#include "vpick.h"
#include "xattr-util.h"
-static const char* const image_search_path[_IMAGE_CLASS_MAX] = {
+const char* const image_search_path[_IMAGE_CLASS_MAX] = {
[IMAGE_MACHINE] = "/etc/machines\0" /* only place symlinks here */
"/run/machines\0" /* and here too */
"/var/lib/machines\0" /* the main place for images */
(void) mkdir("/run/systemd/nspawn/locks", 0700);
}
-int image_path_lock(const char *path, int operation, LockFile *global, LockFile *local) {
+int image_path_lock(
+ const char *path,
+ int operation,
+ LockFile *ret_global,
+ LockFile *ret_local) {
+
_cleanup_free_ char *p = NULL;
LockFile t = LOCK_FILE_INIT;
struct stat st;
int r;
assert(path);
- assert(global);
- assert(local);
+ assert(ret_local);
/* Locks an image path. This actually creates two locks: one "local" one, next to the image path
* itself, which might be shared via NFS. And another "global" one, in /run, that uses the
}
if (getenv_bool("SYSTEMD_NSPAWN_LOCK") == 0) {
- *local = *global = (LockFile) LOCK_FILE_INIT;
+ *ret_local = LOCK_FILE_INIT;
+ if (ret_global)
+ *ret_global = LOCK_FILE_INIT;
return 0;
}
if (exclusive)
return -EBUSY;
- *local = *global = (LockFile) LOCK_FILE_INIT;
+ *ret_local = LOCK_FILE_INIT;
+ if (ret_global)
+ *ret_global = LOCK_FILE_INIT;
return 0;
}
- if (stat(path, &st) >= 0) {
- if (S_ISBLK(st.st_mode))
- r = asprintf(&p, "/run/systemd/nspawn/locks/block-%u:%u", major(st.st_rdev), minor(st.st_rdev));
- else if (S_ISDIR(st.st_mode) || S_ISREG(st.st_mode))
- r = asprintf(&p, "/run/systemd/nspawn/locks/inode-%lu:%lu", (unsigned long) st.st_dev, (unsigned long) st.st_ino);
- else
- return -ENOTTY;
- if (r < 0)
- return -ENOMEM;
+ if (ret_global) {
+ if (stat(path, &st) >= 0) {
+ if (S_ISBLK(st.st_mode))
+ r = asprintf(&p, "/run/systemd/nspawn/locks/block-%u:%u", major(st.st_rdev), minor(st.st_rdev));
+ else if (S_ISDIR(st.st_mode) || S_ISREG(st.st_mode))
+ r = asprintf(&p, "/run/systemd/nspawn/locks/inode-%lu:%lu", (unsigned long) st.st_dev, (unsigned long) st.st_ino);
+ else
+ return -ENOTTY;
+ if (r < 0)
+ return -ENOMEM;
+ }
}
/* For block devices we don't need the "local" lock, as the major/minor lock above should be
if (p) {
make_lock_dir();
- r = make_lock_file(p, operation, global);
+ r = make_lock_file(p, operation, ret_global);
if (r < 0) {
release_lock_file(&t);
return r;
}
- } else
- *global = (LockFile) LOCK_FILE_INIT;
+ } else if (ret_global)
+ *ret_global = LOCK_FILE_INIT;
- *local = t;
+ *ret_local = t;
return 0;
}
if (r < 0)
return r;
- r = dissected_image_acquire_metadata(m, flags);
+ r = dissected_image_acquire_metadata(
+ m,
+ /* userns_fd= */ -EBADF,
+ flags);
if (r < 0)
return r;
const char *image_root_to_string(ImageClass c) _const_;
extern const struct hash_ops image_hash_ops;
+
+extern const char* const image_search_path[_IMAGE_CLASS_MAX];
#include "copy.h"
#include "cryptsetup-util.h"
#include "device-nodes.h"
+#include "device-private.h"
#include "device-util.h"
#include "devnum-util.h"
#include "discover-image.h"
#include "tmpfile-util.h"
#include "udev-util.h"
#include "user-util.h"
+#include "varlink.h"
#include "xattr-util.h"
/* how many times to wait for the device nodes to appear */
}
#if HAVE_BLKID
+static int diskseq_should_be_used(
+ const char *whole_devname,
+ uint64_t diskseq,
+ DissectImageFlags flags) {
+
+ int r;
+
+ assert(whole_devname);
+
+ /* No diskseq. We cannot use by-diskseq symlink. */
+ if (diskseq == 0)
+ return false;
+
+ /* Do not use by-diskseq link unless DISSECT_IMAGE_DISKSEQ_DEVNODE flag is explicitly set. */
+ if (!FLAGS_SET(flags, DISSECT_IMAGE_DISKSEQ_DEVNODE))
+ return false;
+
+ _cleanup_(sd_device_unrefp) sd_device *dev = NULL;
+ r = sd_device_new_from_devname(&dev, whole_devname);
+ if (r < 0)
+ return r;
+
+ /* When ID_IGNORE_DISKSEQ udev property is set, the by-diskseq symlink will not be created. */
+ r = device_get_property_bool(dev, "ID_IGNORE_DISKSEQ");
+ if (r >= 0)
+ return !r; /* If explicitly specified, use it. */
+ if (r != -ENOENT)
+ return r;
+
+ return true;
+}
+
static int make_partition_devname(
const char *whole_devname,
uint64_t diskseq,
assert(nr != 0); /* zero is not a valid partition nr */
assert(ret);
- if (!FLAGS_SET(flags, DISSECT_IMAGE_DISKSEQ_DEVNODE) || diskseq == 0) {
-
+ r = diskseq_should_be_used(whole_devname, diskseq, flags);
+ if (r < 0)
+ log_debug_errno(r, "Failed to determine if diskseq should be used for %s, assuming no, ignoring: %m", whole_devname);
+ if (r <= 0) {
/* Given a whole block device node name (e.g. /dev/sda or /dev/loop7) generate a partition
* device name (e.g. /dev/sda7 or /dev/loop7p5). The rule the kernel uses is simple: if whole
* block device node name ends in a digit, then suffix a 'p', followed by the partition
verity);
}
+void dissected_image_close(DissectedImage *m) {
+ if (!m)
+ return;
+
+ /* Closes all fds we keep open associated with this, but nothing else */
+
+ FOREACH_ARRAY(p, m->partitions, _PARTITION_DESIGNATOR_MAX) {
+ p->mount_node_fd = safe_close(p->mount_node_fd);
+ p->fsmount_fd = safe_close(p->fsmount_fd);
+ }
+
+ m->loop = loop_device_unref(m->loop);
+}
+
DissectedImage* dissected_image_unref(DissectedImage *m) {
if (!m)
return NULL;
* > 0 → Decrypted successfully
* -ENOKEY → There's something to decrypt but no key was supplied
* -EKEYREJECTED → Passed key was not correct
+ * -EBUSY → Generic Verity error (kernel is not very explanatory)
*/
if (verity && verity->root_hash && verity->root_hash_size < sizeof(sd_id128_t))
return 1;
}
-int dissected_image_acquire_metadata(DissectedImage *m, DissectImageFlags extra_flags) {
+int dissected_image_acquire_metadata(
+ DissectedImage *m,
+ int userns_fd,
+ DissectImageFlags extra_flags) {
enum {
META_HOSTNAME,
goto finish;
}
- r = safe_fork("(sd-dissect)", FORK_RESET_SIGNALS|FORK_DEATHSIG_SIGTERM|FORK_NEW_MOUNTNS|FORK_MOUNTNS_SLAVE, &child);
+ r = safe_fork("(sd-dissect)", FORK_RESET_SIGNALS|FORK_DEATHSIG_SIGTERM, &child);
if (r < 0)
goto finish;
if (r == 0) {
- /* Child in a new mount namespace */
+ /* Child */
error_pipe[0] = safe_close(error_pipe[0]);
+ if (userns_fd < 0)
+ r = detach_mount_namespace_harder(0, 0);
+ else
+ r = detach_mount_namespace_userns(userns_fd);
+ if (r < 0) {
+ log_debug_errno(r, "Failed to detach mount namespace: %m");
+ goto inner_fail;
+ }
+
r = dissected_image_mount(
m,
t,
return 0;
}
+
+#if HAVE_BLKID
+
+static JSON_DISPATCH_ENUM_DEFINE(dispatch_architecture, Architecture, architecture_from_string);
+static JSON_DISPATCH_ENUM_DEFINE(dispatch_partition_designator, PartitionDesignator, partition_designator_from_string);
+
+typedef struct PartitionFields {
+ PartitionDesignator designator;
+ bool rw;
+ bool growfs;
+ unsigned partno;
+ Architecture architecture;
+ sd_id128_t uuid;
+ char *fstype;
+ char *label;
+ uint64_t size;
+ uint64_t offset;
+ unsigned fsmount_fd_idx;
+} PartitionFields;
+
+static void partition_fields_done(PartitionFields *f) {
+ assert(f);
+
+ f->fstype = mfree(f->fstype);
+ f->label = mfree(f->label);
+}
+
+typedef struct ReplyParameters {
+ JsonVariant *partitions;
+ char *image_policy;
+ uint64_t image_size;
+ uint32_t sector_size;
+ sd_id128_t image_uuid;
+} ReplyParameters;
+
+static void reply_parameters_done(ReplyParameters *p) {
+ assert(p);
+
+ p->image_policy = mfree(p->image_policy);
+ p->partitions = json_variant_unref(p->partitions);
+}
+
+#endif
+
+int mountfsd_mount_image(
+ const char *path,
+ int userns_fd,
+ const ImagePolicy *image_policy,
+ DissectImageFlags flags,
+ DissectedImage **ret) {
+
+#if HAVE_BLKID
+ _cleanup_(reply_parameters_done) ReplyParameters p = {};
+
+ static const JsonDispatch dispatch_table[] = {
+ { "partitions", JSON_VARIANT_ARRAY, json_dispatch_variant, offsetof(struct ReplyParameters, partitions), JSON_MANDATORY },
+ { "imagePolicy", JSON_VARIANT_STRING, json_dispatch_string, offsetof(struct ReplyParameters, image_policy), 0 },
+ { "imageSize", _JSON_VARIANT_TYPE_INVALID, json_dispatch_uint64, offsetof(struct ReplyParameters, image_size), JSON_MANDATORY },
+ { "sectorSize", _JSON_VARIANT_TYPE_INVALID, json_dispatch_uint32, offsetof(struct ReplyParameters, sector_size), JSON_MANDATORY },
+ { "imageUuid", JSON_VARIANT_STRING, json_dispatch_id128, offsetof(struct ReplyParameters, image_uuid), 0 },
+ {}
+ };
+
+ _cleanup_(dissected_image_unrefp) DissectedImage *di = NULL;
+ _cleanup_close_ int image_fd = -EBADF;
+ _cleanup_(varlink_unrefp) Varlink *vl = NULL;
+ _cleanup_free_ char *ps = NULL;
+ unsigned max_fd = UINT_MAX;
+ const char *error_id;
+ int r;
+
+ assert(path);
+ assert(ret);
+
+ r = varlink_connect_address(&vl, "/run/systemd/io.systemd.MountFileSystem");
+ if (r < 0)
+ return log_error_errno(r, "Failed to connect to mountfsd: %m");
+
+ r = varlink_set_allow_fd_passing_input(vl, true);
+ if (r < 0)
+ return log_error_errno(r, "Failed to enable varlink fd passing for read: %m");
+
+ r = varlink_set_allow_fd_passing_output(vl, true);
+ if (r < 0)
+ return log_error_errno(r, "Failed to enable varlink fd passing for write: %m");
+
+ image_fd = open(path, O_RDONLY|O_CLOEXEC);
+ if (image_fd < 0)
+ return log_error_errno(errno, "Failed to open '%s': %m", path);
+
+ r = varlink_push_dup_fd(vl, image_fd);
+ if (r < 0)
+ return log_error_errno(r, "Failed to push image fd into varlink connection: %m");
+
+ if (userns_fd >= 0) {
+ r = varlink_push_dup_fd(vl, userns_fd);
+ if (r < 0)
+ return log_error_errno(r, "Failed to push image fd into varlink connection: %m");
+ }
+
+ if (image_policy) {
+ r = image_policy_to_string(image_policy, /* simplify= */ false, &ps);
+ if (r < 0)
+ return log_error_errno(r, "Failed format image policy to string: %m");
+ }
+
+ JsonVariant *reply = NULL;
+ r = varlink_callb(
+ vl,
+ "io.systemd.MountFileSystem.MountImage",
+ &reply,
+ &error_id,
+ JSON_BUILD_OBJECT(
+ JSON_BUILD_PAIR("imageFileDescriptor", JSON_BUILD_UNSIGNED(0)),
+ JSON_BUILD_PAIR_CONDITION(userns_fd >= 0, "userNamespaceFileDescriptor", JSON_BUILD_UNSIGNED(1)),
+ JSON_BUILD_PAIR("readOnly", JSON_BUILD_BOOLEAN(FLAGS_SET(flags, DISSECT_IMAGE_MOUNT_READ_ONLY))),
+ JSON_BUILD_PAIR("growFileSystems", JSON_BUILD_BOOLEAN(FLAGS_SET(flags, DISSECT_IMAGE_GROWFS))),
+ JSON_BUILD_PAIR_CONDITION(ps, "imagePolicy", JSON_BUILD_STRING(ps)),
+ JSON_BUILD_PAIR("allowInteractiveAuthentication", JSON_BUILD_BOOLEAN(FLAGS_SET(flags, DISSECT_IMAGE_ALLOW_INTERACTIVE_AUTH)))));
+ if (r < 0)
+ return log_error_errno(r, "Failed to call MountImage() varlink call: %m");
+ if (!isempty(error_id))
+ return log_error_errno(varlink_error_to_errno(error_id, reply), "Failed to call MountImage() varlink call: %s", error_id);
+
+ r = json_dispatch(reply, dispatch_table, JSON_ALLOW_EXTENSIONS, &p);
+ if (r < 0)
+ return log_error_errno(r, "Failed to parse MountImage() reply: %m");
+
+ log_debug("Effective image policy: %s", p.image_policy);
+
+ JsonVariant *i;
+ JSON_VARIANT_ARRAY_FOREACH(i, p.partitions) {
+ _cleanup_close_ int fsmount_fd = -EBADF;
+
+ _cleanup_(partition_fields_done) PartitionFields pp = {
+ .designator = _PARTITION_DESIGNATOR_INVALID,
+ .architecture = _ARCHITECTURE_INVALID,
+ .size = UINT64_MAX,
+ .offset = UINT64_MAX,
+ .fsmount_fd_idx = UINT_MAX,
+ };
+
+ static const JsonDispatch partition_dispatch_table[] = {
+ { "designator", JSON_VARIANT_STRING, dispatch_partition_designator, offsetof(struct PartitionFields, designator), JSON_MANDATORY },
+ { "writable", JSON_VARIANT_BOOLEAN, json_dispatch_boolean, offsetof(struct PartitionFields, rw), JSON_MANDATORY },
+ { "growFileSystem", JSON_VARIANT_BOOLEAN, json_dispatch_boolean, offsetof(struct PartitionFields, growfs), JSON_MANDATORY },
+ { "partitionNumber", _JSON_VARIANT_TYPE_INVALID, json_dispatch_uint, offsetof(struct PartitionFields, partno), 0 },
+ { "architecture", JSON_VARIANT_STRING, dispatch_architecture, offsetof(struct PartitionFields, architecture), 0 },
+ { "partitionUuid", JSON_VARIANT_STRING, json_dispatch_id128, offsetof(struct PartitionFields, uuid), 0 },
+ { "fileSystemType", JSON_VARIANT_STRING, json_dispatch_string, offsetof(struct PartitionFields, fstype), JSON_MANDATORY },
+ { "partitionLabel", JSON_VARIANT_STRING, json_dispatch_string, offsetof(struct PartitionFields, label), 0 },
+ { "size", _JSON_VARIANT_TYPE_INVALID, json_dispatch_uint64, offsetof(struct PartitionFields, size), JSON_MANDATORY },
+ { "offset", _JSON_VARIANT_TYPE_INVALID, json_dispatch_uint64, offsetof(struct PartitionFields, offset), JSON_MANDATORY },
+ { "mountFileDescriptor", _JSON_VARIANT_TYPE_INVALID, json_dispatch_uint, offsetof(struct PartitionFields, fsmount_fd_idx), JSON_MANDATORY },
+ {}
+ };
+
+ r = json_dispatch(i, partition_dispatch_table, JSON_ALLOW_EXTENSIONS, &pp);
+ if (r < 0)
+ return log_error_errno(r, "Failed to parse partition data: %m");
+
+ if (pp.fsmount_fd_idx != UINT_MAX) {
+ if (max_fd == UINT_MAX || pp.fsmount_fd_idx > max_fd)
+ max_fd = pp.fsmount_fd_idx;
+
+ fsmount_fd = varlink_take_fd(vl, pp.fsmount_fd_idx);
+ if (fsmount_fd < 0)
+ return fsmount_fd;
+ }
+
+ assert(pp.designator >= 0);
+
+ if (!di) {
+ r = dissected_image_new(path, &di);
+ if (r < 0)
+ return log_error_errno(r, "Failed to allocated new dissected image structure: %m");
+ }
+
+ if (di->partitions[pp.designator].found)
+ return log_error_errno(SYNTHETIC_ERRNO(EBADMSG), "Duplicate partition data for '%s'.", partition_designator_to_string(pp.designator));
+
+ di->partitions[pp.designator] = (DissectedPartition) {
+ .found = true,
+ .rw = pp.rw,
+ .growfs = pp.growfs,
+ .partno = pp.partno,
+ .architecture = pp.architecture,
+ .uuid = pp.uuid,
+ .fstype = TAKE_PTR(pp.fstype),
+ .label = TAKE_PTR(pp.label),
+ .mount_node_fd = -EBADF,
+ .size = pp.size,
+ .offset = pp.offset,
+ .fsmount_fd = TAKE_FD(fsmount_fd),
+ };
+ }
+
+ di->image_size = p.image_size;
+ di->sector_size = p.sector_size;
+ di->image_uuid = p.image_uuid;
+
+ *ret = TAKE_PTR(di);
+ return 0;
+#else
+ return -EOPNOTSUPP;
+#endif
+}
DISSECT_IMAGE_ALLOW_EMPTY = 1 << 24, /* Allow that no usable partitions is present */
DISSECT_IMAGE_TRY_ATOMIC_MOUNT_EXCHANGE = 1 << 25, /* Try to mount the image beneath the specified mountpoint, rather than on top of it, and then umount the top */
DISSECT_IMAGE_ALLOW_USERSPACE_VERITY = 1 << 26, /* Allow userspace verity keyring in /etc/verity.d/ and related dirs */
+ DISSECT_IMAGE_ALLOW_INTERACTIVE_AUTH = 1 << 27, /* Allow interactive authorization when going through mountfsd */
} DissectImageFlags;
struct DissectedImage {
int dissect_loop_device(LoopDevice *loop, const VeritySettings *verity, const MountOptions *mount_options, const ImagePolicy *image_policy, DissectImageFlags flags, DissectedImage **ret);
int dissect_loop_device_and_warn(LoopDevice *loop, const VeritySettings *verity, const MountOptions *mount_options, const ImagePolicy *image_policy, DissectImageFlags flags, DissectedImage **ret);
+void dissected_image_close(DissectedImage *m);
DissectedImage* dissected_image_unref(DissectedImage *m);
DEFINE_TRIVIAL_CLEANUP_FUNC(DissectedImage*, dissected_image_unref);
int dissected_image_mount(DissectedImage *m, const char *dest, uid_t uid_shift, uid_t uid_range, int userns_fd, DissectImageFlags flags);
int dissected_image_mount_and_warn(DissectedImage *m, const char *where, uid_t uid_shift, uid_t uid_range, int userns_fd, DissectImageFlags flags);
-int dissected_image_acquire_metadata(DissectedImage *m, DissectImageFlags extra_flags);
+int dissected_image_acquire_metadata(DissectedImage *m, int userns_fd, DissectImageFlags extra_flags);
Architecture dissected_image_architecture(DissectedImage *m);
int dissected_image_relinquish(DissectedImage *m);
int verity_settings_load(VeritySettings *verity, const char *image, const char *root_hash_path, const char *root_hash_sig_path);
+
+static inline bool verity_settings_set(const VeritySettings *settings) {
+ return settings &&
+ (settings->root_hash_size > 0 ||
+ (settings->root_hash_sig_size > 0 ||
+ settings->data_path));
+}
+
void verity_settings_done(VeritySettings *verity);
static inline bool verity_settings_data_covers(const VeritySettings *verity, PartitionDesignator d) {
}
int get_common_dissect_directory(char **ret);
+
+int mountfsd_mount_image(const char *path, int userns_fd, const ImagePolicy *image_policy, DissectImageFlags flags, DissectedImage **ret);
/* SPDX-License-Identifier: LGPL-2.1-or-later */
-/* Temporary work-around for broken glibc vs. linux kernel header definitions
- * This is already fixed upstream, remove this when distributions have updated.
- */
-#define _NET_IF_H 1
-
+/* Make sure the net/if.h header is included before any linux/ one */
+#include <net/if.h>
#include <arpa/inet.h>
#include <endian.h>
#include <errno.h>
#include <stddef.h>
#include <string.h>
-#include <net/if.h>
-#ifndef IFNAMSIZ
-#define IFNAMSIZ 16
-#endif
#include <linux/if.h>
#include <linux/netfilter_ipv4/ip_tables.h>
#include <linux/netfilter/nf_nat.h>
#include "log.h"
#include "parse-util.h"
#include "path-util.h"
+#include "proc-cmdline.h"
#include "stat-util.h"
#include "string-util.h"
#include "strv.h"
assert(ret_devno);
assert(ret_offset);
+ r = proc_cmdline_get_key("noresume", /* flags = */ 0, /* ret_value = */ NULL);
+ if (r < 0)
+ return log_debug_errno(r, "Failed to check if 'noresume' kernel command line option is set: %m");
+ if (r > 0)
+ return log_debug_errno(SYNTHETIC_ERRNO(ENOTRECOVERABLE),
+ "'noresume' kernel command line option is set, refusing hibernation device lookup.");
+
r = read_one_line_file("/sys/power/resume", &devno_str);
if (r < 0)
return log_debug_errno(r, "Failed to read /sys/power/resume: %m");
return 0;
}
-void clear_efi_hibernate_location_and_warn(void) {
+int clear_efi_hibernate_location_and_warn(void) {
int r;
if (!is_efi_boot())
- return;
+ return 0;
r = efi_set_variable(EFI_SYSTEMD_VARIABLE(HibernateLocation), NULL, 0);
+ if (r == -ENOENT)
+ return 0;
if (r < 0)
- log_warning_errno(r, "Failed to clear EFI variable HibernateLocation, ignoring: %m");
+ return log_warning_errno(r, "Failed to clear HibernateLocation EFI variable: %m");
+
+ return 1;
}
int write_resume_config(dev_t devno, uint64_t offset, const char *device);
-void clear_efi_hibernate_location_and_warn(void);
+int clear_efi_hibernate_location_and_warn(void);
/* Only for test-fiemap */
int read_fiemap(int fd, struct fiemap **ret);
if (!dl)
return log_debug_errno(SYNTHETIC_ERRNO(EOPNOTSUPP),
"libidn support is not installed: %s", dlerror());
- }
+ log_debug("Loaded 'libidn.so.11' via dlopen()");
+ } else
+ log_debug("Loaded 'libidn.so.12' via dlopen()");
+
r = dlsym_many_or_warn(
dl,
return flags;
}
+PartitionPolicyFlags partition_policy_flags_reduce(PartitionPolicyFlags flags) {
+ /* The reverse of partition_policy_flags_extend(): if some parts of the flags field allow all
+ * possible options, let's remove it from the flags to make them shorter */
+
+ if (FLAGS_SET(flags, _PARTITION_POLICY_USE_MASK))
+ flags &= ~_PARTITION_POLICY_USE_MASK;
+ if (FLAGS_SET(flags, _PARTITION_POLICY_READ_ONLY_MASK))
+ flags &= ~_PARTITION_POLICY_READ_ONLY_MASK;
+ if (FLAGS_SET(flags, _PARTITION_POLICY_GROWFS_MASK))
+ flags &= ~_PARTITION_POLICY_GROWFS_MASK;
+
+ return flags;
+}
+
static PartitionPolicyFlags partition_policy_normalized_flags(const PartitionPolicy *policy) {
PartitionPolicyFlags flags = ASSERT_PTR(policy)->flags;
return free_and_replace_full(*policy, np, image_policy_free);
}
+static bool partition_policy_flags_has_unspecified(PartitionPolicyFlags flags) {
+
+ if ((flags & _PARTITION_POLICY_USE_MASK) == 0)
+ return true;
+ if ((flags & _PARTITION_POLICY_READ_ONLY_MASK) == 0)
+ return true;
+ if ((flags & _PARTITION_POLICY_GROWFS_MASK) == 0)
+ return true;
+
+ return false;
+}
+
+int image_policy_intersect(const ImagePolicy *a, const ImagePolicy *b, ImagePolicy **ret) {
+ _cleanup_(image_policy_freep) ImagePolicy *p = NULL;
+
+ /* Calculates the intersection of the specified policies, i.e. only what is permitted in both. This
+ * might fail with -ENAVAIL if the intersection is an "impossible policy". For example, if a root
+ * partition my neither be used, nor be absent, nor be unused then this is considered
+ * "impossible". */
+
+ p = image_policy_new(_PARTITION_DESIGNATOR_MAX);
+ if (!p)
+ return -ENOMEM;
+
+ p->default_flags =
+ partition_policy_flags_extend(image_policy_default(a)) &
+ partition_policy_flags_extend(image_policy_default(b));
+
+ if (partition_policy_flags_has_unspecified(p->default_flags)) /* Intersection empty? */
+ return -ENAVAIL;
+
+ p->default_flags = partition_policy_flags_reduce(p->default_flags);
+
+ for (PartitionDesignator d = 0; d < _PARTITION_DESIGNATOR_MAX; d++) {
+ PartitionPolicyFlags x, y, z, df;
+
+ /* If this designator has no entry in either policy we don't need to include it in the intersection either. */
+ if (!image_policy_bsearch(a, d) && !image_policy_bsearch(b, d))
+ continue;
+
+ /* Expand this policy flags field to the "long" form, i.e. for each part of the flags that
+ * are left unspcified add in all possible options */
+ x = image_policy_get_exhaustively(a, d);
+ if (x < 0)
+ return x;
+
+ y = image_policy_get_exhaustively(b, d);
+ if (y < 0)
+ return y;
+
+ /* Mask it */
+ z = x & y;
+
+ /* Check if the intersection is empty for this partition. If so, generate a clear error */
+ if (partition_policy_flags_has_unspecified(z))
+ return -ENAVAIL;
+
+ df = partition_policy_normalized_flags(
+ &(const PartitionPolicy) {
+ .flags = image_policy_default(p),
+ .designator = d,
+ });
+ if (df < 0)
+ return df;
+ if (df == z) /* Same as default? then let's skip this */
+ continue;
+
+ /* image_policy_get_exhaustively() may have extended the flags mask to include all
+ * read-only/growfs flags if not set. Let's remove them again, if they are both set to
+ * minimize the policy again. */
+ z = partition_policy_flags_reduce(z);
+
+ p->policies[p->n_policies++] = (struct PartitionPolicy) {
+ .designator = d,
+ .flags = z,
+ };
+ }
+
+ if (ret)
+ *ret = TAKE_PTR(p);
+
+ return 0;
+}
+
const ImagePolicy image_policy_allow = {
/* Allow policy */
.n_policies = 0,
}
PartitionPolicyFlags partition_policy_flags_extend(PartitionPolicyFlags flags);
+PartitionPolicyFlags partition_policy_flags_reduce(PartitionPolicyFlags flags);
PartitionPolicyFlags partition_policy_flags_from_string(const char *s);
int partition_policy_flags_to_string(PartitionPolicyFlags flags, bool simplify, char **ret);
bool image_policy_equal(const ImagePolicy *a, const ImagePolicy *b); /* checks if defined the same way, i.e. has literally the same ruleset */
int image_policy_equivalent(const ImagePolicy *a, const ImagePolicy *b); /* checks if the outcome is the same, i.e. for all partitions results in the same decisions. */
+int image_policy_intersect(const ImagePolicy *a, const ImagePolicy *b, ImagePolicy **ret);
+
static inline ImagePolicy* image_policy_free(ImagePolicy *p) {
return mfree(p);
}
/* SPDX-License-Identifier: LGPL-2.0-or-later */
/*
- * initreq.h Interface to talk to init through /dev/initctl.
- *
- * Copyright (C) 1995-2004 Miquel van Smoorenburg
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 2 of the License, or (at your option) any later version.
- *
+ * Copyright (C) 1995-2004 Miquel van Smoorenburg
* Version: @(#)initreq.h 1.28 31-Mar-2004 MvS
*/
return true;
}
-static void log_children_no_yet_killed(Set *pids) {
+static void log_children_not_yet_killed(Set *pids) {
_cleanup_free_ char *lst_child = NULL;
- void *p;
int r;
+ void *p;
SET_FOREACH(p, pids) {
_cleanup_free_ char *s = NULL;
if (pid_get_comm(PTR_TO_PID(p), &s) >= 0)
- r = strextendf(&lst_child, ", " PID_FMT " (%s)", PTR_TO_PID(p), s);
+ r = strextendf_with_separator(&lst_child, ", ", PID_FMT " (%s)", PTR_TO_PID(p), s);
else
- r = strextendf(&lst_child, ", " PID_FMT, PTR_TO_PID(p));
+ r = strextendf_with_separator(&lst_child, ", ", PID_FMT, PTR_TO_PID(p));
if (r < 0)
return (void) log_oom_warning();
}
if (isempty(lst_child))
return;
- log_warning("Waiting for process: %s", lst_child + 2);
+ log_warning("Waiting for process: %s", lst_child);
}
static int wait_for_children(Set *pids, sigset_t *mask, usec_t timeout) {
n = now(CLOCK_MONOTONIC);
if (date_log_child > 0 && n >= date_log_child) {
- log_children_no_yet_killed(pids);
+ log_children_not_yet_killed(pids);
/* Log the children not yet killed only once */
date_log_child = 0;
}
r = proc_dir_open(&dir);
if (r < 0)
- return log_warning_errno(r, "opendir(/proc) failed: %m");
+ return log_warning_errno(r, "Failed to open /proc/: %m");
for (;;) {
_cleanup_(pidref_done) PidRef pidref = PIDREF_NULL;
r = proc_dir_read_pidref(dir, &pidref);
if (r < 0)
- return log_warning_errno(r, "Failed to enumerate /proc: %m");
+ return log_warning_errno(r, "Failed to enumerate /proc/: %m");
if (r == 0)
break;
DLSYM_FUNCTION(fido_cred_new);
DLSYM_FUNCTION(fido_cred_set_clientdata_hash);
DLSYM_FUNCTION(fido_cred_set_extensions);
+DLSYM_FUNCTION(fido_cred_set_prot);
DLSYM_FUNCTION(fido_cred_set_rk);
DLSYM_FUNCTION(fido_cred_set_rp);
DLSYM_FUNCTION(fido_cred_set_type);
DLSYM_ARG(fido_cred_new),
DLSYM_ARG(fido_cred_set_clientdata_hash),
DLSYM_ARG(fido_cred_set_extensions),
+ DLSYM_ARG(fido_cred_set_prot),
DLSYM_ARG(fido_cred_set_rk),
DLSYM_ARG(fido_cred_set_rp),
DLSYM_ARG(fido_cred_set_type),
if (!c)
return log_oom();
- r = sym_fido_cred_set_extensions(c, FIDO_EXT_HMAC_SECRET);
+ int extensions = FIDO_EXT_HMAC_SECRET;
+ if (FLAGS_SET(lock_with, FIDO2ENROLL_PIN) || FLAGS_SET(lock_with, FIDO2ENROLL_UV)) {
+ /* Attempt to use the "cred protect" extension, requiring user verification (UV) for this
+ * credential. If the authenticator doesn't support the extension, it will be ignored. */
+ extensions |= FIDO_EXT_CRED_PROTECT;
+
+ r = sym_fido_cred_set_prot(c, FIDO_CRED_PROT_UV_REQUIRED);
+ if (r != FIDO_OK)
+ log_warning("Failed to set protection level on FIDO2 credential, ignoring: %s", sym_fido_strerr(r));
+ }
+
+ r = sym_fido_cred_set_extensions(c, extensions);
if (r != FIDO_OK)
return log_error_errno(SYNTHETIC_ERRNO(EIO),
- "Failed to enable HMAC-SECRET extension on FIDO2 credential: %s", sym_fido_strerr(r));
+ "Failed to enable extensions on FIDO2 credential: %s", sym_fido_strerr(r));
r = sym_fido_cred_set_rp(c, rp_id, rp_name);
if (r != FIDO_OK)
emoji_enabled() ? special_glyph(SPECIAL_GLYPH_TOUCH) : "",
emoji_enabled() ? " " : "");
- r = sym_fido_dev_make_cred(d, c, NULL);
+ /* If we are using the user PIN, then we must pass that PIN to the get_assertion call below, or
+ * the authenticator will use the non-user-verification HMAC secret (which differs from the one when
+ * the PIN is passed).
+ *
+ * Rather than potentially trying and failing to create the credential, just collect the PIN first
+ * and then pass it to both the make_credential and the get_assertion operations. */
+ if (FLAGS_SET(lock_with, FIDO2ENROLL_PIN))
+ r = FIDO_ERR_PIN_REQUIRED;
+ else
+ r = sym_fido_dev_make_cred(d, c, NULL);
+
if (r == FIDO_ERR_PIN_REQUIRED) {
if (!has_client_pin)
DLSYM_PROTOTYPE(fido_cred_new);
DLSYM_PROTOTYPE(fido_cred_set_clientdata_hash);
DLSYM_PROTOTYPE(fido_cred_set_extensions);
+DLSYM_PROTOTYPE(fido_cred_set_prot);
DLSYM_PROTOTYPE(fido_cred_set_rk);
DLSYM_PROTOTYPE(fido_cred_set_rp);
DLSYM_PROTOTYPE(fido_cred_set_type);
#include <signal.h>
#include <stdint.h>
#include <stdlib.h>
-#include <sys/socket.h>
#include <syslog.h>
#include <unistd.h>
#include "log.h"
#include "logs-show.h"
#include "macro.h"
-#include "namespace-util.h"
#include "output-mode.h"
#include "parse-util.h"
#include "pretty-print.h"
-#include "process-util.h"
#include "sparse-endian.h"
#include "stdio-util.h"
#include "string-table.h"
/* Look for coredumps of the service */
(r = sd_journal_add_disjunction(j)) ||
- (r = sd_journal_add_match(j, "MESSAGE_ID=fc2e22bc6ee647b6b90729ab34a250b1", 0)) ||
- (r = sd_journal_add_match(j, "_UID=0", 0)) ||
+ (r = sd_journal_add_match(j, "MESSAGE_ID=fc2e22bc6ee647b6b90729ab34a250b1", SIZE_MAX)) ||
+ (r = sd_journal_add_match(j, "_UID=0", SIZE_MAX)) ||
(r = journal_add_match_pair(j, "COREDUMP_UNIT", unit)) ||
/* Look for messages from PID 1 about this service */
(r = sd_journal_add_disjunction(j)) ||
- (r = sd_journal_add_match(j, "_PID=1", 0)) ||
+ (r = sd_journal_add_match(j, "_PID=1", SIZE_MAX)) ||
(r = journal_add_match_pair(j, "UNIT", unit)) ||
/* Look for messages from authorized daemons about this service */
(r = sd_journal_add_disjunction(j)) ||
- (r = sd_journal_add_match(j, "_UID=0", 0)) ||
+ (r = sd_journal_add_match(j, "_UID=0", SIZE_MAX)) ||
(r = journal_add_match_pair(j, "OBJECT_SYSTEMD_UNIT", unit))
);
(r = sd_journal_add_disjunction(j)) ||
(r = journal_add_match_pair(j, "COREDUMP_USER_UNIT", unit)) ||
(r = journal_add_matchf(j, "_UID="UID_FMT, uid)) ||
- (r = sd_journal_add_match(j, "_UID=0", 0)) ||
+ (r = sd_journal_add_match(j, "_UID=0", SIZE_MAX)) ||
/* Look for messages from authorized daemons about this service */
(r = sd_journal_add_disjunction(j)) ||
(r = journal_add_match_pair(j, "OBJECT_SYSTEMD_USER_UNIT", unit)) ||
(r = journal_add_matchf(j, "_UID="UID_FMT, uid)) ||
- (r = sd_journal_add_match(j, "_UID=0", 0))
+ (r = sd_journal_add_match(j, "_UID=0", SIZE_MAX))
);
if (r == 0 && endswith(unit, ".slice"))
return r;
}
-static int get_boot_id_for_machine(const char *machine, sd_id128_t *boot_id) {
- _cleanup_close_pair_ int pair[2] = EBADF_PAIR;
- _cleanup_close_ int pidnsfd = -EBADF, mntnsfd = -EBADF, rootfd = -EBADF;
- char buf[SD_ID128_UUID_STRING_MAX];
- pid_t pid, child;
- ssize_t k;
+int add_match_boot_id(sd_journal *j, sd_id128_t id) {
int r;
- assert(machine);
- assert(boot_id);
-
- r = container_get_leader(machine, &pid);
- if (r < 0)
- return r;
-
- r = namespace_open(pid, &pidnsfd, &mntnsfd, /* ret_netns_fd = */ NULL, /* ret_userns_fd = */ NULL, &rootfd);
- if (r < 0)
- return r;
-
- if (socketpair(AF_UNIX, SOCK_DGRAM, 0, pair) < 0)
- return -errno;
-
- r = namespace_fork("(sd-bootidns)", "(sd-bootid)", NULL, 0, FORK_RESET_SIGNALS|FORK_DEATHSIG_SIGKILL,
- pidnsfd, mntnsfd, -1, -1, rootfd, &child);
- if (r < 0)
- return r;
- if (r == 0) {
- int fd;
-
- pair[0] = safe_close(pair[0]);
-
- fd = open("/proc/sys/kernel/random/boot_id", O_RDONLY|O_CLOEXEC|O_NOCTTY);
- if (fd < 0)
- _exit(EXIT_FAILURE);
+ assert(j);
- r = loop_read_exact(fd, buf, 36, false);
- safe_close(fd);
+ if (sd_id128_is_null(id)) {
+ r = sd_id128_get_boot(&id);
if (r < 0)
- _exit(EXIT_FAILURE);
-
- k = send(pair[1], buf, 36, MSG_NOSIGNAL);
- if (k != 36)
- _exit(EXIT_FAILURE);
-
- _exit(EXIT_SUCCESS);
+ return log_error_errno(r, "Failed to get boot ID: %m");
}
- pair[1] = safe_close(pair[1]);
-
- r = wait_for_terminate_and_check("(sd-bootidns)", child, 0);
- if (r < 0)
- return r;
- if (r != EXIT_SUCCESS)
- return -EIO;
-
- k = recv(pair[0], buf, 36, 0);
- if (k != 36)
- return -EIO;
-
- buf[36] = 0;
- r = sd_id128_from_string(buf, boot_id);
+ r = journal_add_match_pair(j, "_BOOT_ID", SD_ID128_TO_STRING(id));
if (r < 0)
- return r;
+ return log_error_errno(r, "Failed to add match: %m");
return 0;
}
-int add_match_boot_id(sd_journal *j, sd_id128_t id) {
- assert(j);
- assert(!sd_id128_is_null(id));
-
- return journal_add_match_pair(j, "_BOOT_ID", SD_ID128_TO_STRING(id));
-}
-
int add_match_this_boot(sd_journal *j, const char *machine) {
sd_id128_t boot_id;
int r;
assert(j);
- if (machine) {
- r = get_boot_id_for_machine(machine, &boot_id);
- if (r < 0)
- return log_error_errno(r, "Failed to get boot id of container %s: %m", machine);
- } else {
- r = sd_id128_get_boot(&boot_id);
- if (r < 0)
- return log_error_errno(r, "Failed to get boot id: %m");
- }
+ r = id128_get_boot_for_machine(machine, &boot_id);
+ if (r < 0)
+ return log_error_errno(r, "Failed to get boot ID%s%s: %m",
+ isempty(machine) ? "" : " of container ", machine);
r = add_match_boot_id(j, boot_id);
if (r < 0)
- return log_error_errno(r, "Failed to add match: %m");
+ return r;
r = sd_journal_add_conjunction(j);
if (r < 0)
'macvlan-util.c',
'mkdir-label.c',
'mkfs-util.c',
+ 'module-util.c',
'mount-setup.c',
'mount-util.c',
'net-condition.c',
'netif-sriov.c',
'netif-util.c',
'nsflags.c',
+ 'nsresource.c',
'numa-util.c',
'open-file.c',
'openssl-util.c',
'varlink-io.systemd.Hostname.c',
'varlink-io.systemd.Journal.c',
'varlink-io.systemd.ManagedOOM.c',
+ 'varlink-io.systemd.MountFileSystem.c',
+ 'varlink-io.systemd.NamespaceResource.c',
'varlink-io.systemd.Network.c',
'varlink-io.systemd.PCRExtend.c',
'varlink-io.systemd.PCRLock.c',
shared_sources += files('bpf-link.c')
endif
-if conf.get('HAVE_KMOD') == 1
- shared_sources += files('module-util.c')
-endif
-
if conf.get('HAVE_PAM') == 1
shared_sources += files('pam-util.c')
endif
libdl,
libgcrypt,
libiptc_cflags,
- libkmod,
+ libkmod_cflags,
liblz4_cflags,
libmount,
libopenssl,
link_depends : libshared_sym_path,
link_whole : [libshared_static,
libbasic,
- libbasic_gcrypt,
libsystemd_static],
dependencies : [libshared_deps,
userspace],
#include "proc-cmdline.h"
#include "strv.h"
+#if HAVE_KMOD
+
+static void *libkmod_dl = NULL;
+
+DLSYM_FUNCTION(kmod_list_next);
+DLSYM_FUNCTION(kmod_load_resources);
+DLSYM_FUNCTION(kmod_module_get_initstate);
+DLSYM_FUNCTION(kmod_module_get_module);
+DLSYM_FUNCTION(kmod_module_get_name);
+DLSYM_FUNCTION(kmod_module_new_from_lookup);
+DLSYM_FUNCTION(kmod_module_probe_insert_module);
+DLSYM_FUNCTION(kmod_module_unref);
+DLSYM_FUNCTION(kmod_module_unref_list);
+DLSYM_FUNCTION(kmod_new);
+DLSYM_FUNCTION(kmod_set_log_fn);
+DLSYM_FUNCTION(kmod_unref);
+DLSYM_FUNCTION(kmod_validate_resources);
+
+int dlopen_libkmod(void) {
+ return dlopen_many_sym_or_warn(
+ &libkmod_dl,
+ "libkmod.so.2",
+ LOG_DEBUG,
+ DLSYM_ARG(kmod_list_next),
+ DLSYM_ARG(kmod_load_resources),
+ DLSYM_ARG(kmod_module_get_initstate),
+ DLSYM_ARG(kmod_module_get_module),
+ DLSYM_ARG(kmod_module_get_name),
+ DLSYM_ARG(kmod_module_new_from_lookup),
+ DLSYM_ARG(kmod_module_probe_insert_module),
+ DLSYM_ARG(kmod_module_unref),
+ DLSYM_ARG(kmod_module_unref_list),
+ DLSYM_ARG(kmod_new),
+ DLSYM_ARG(kmod_set_log_fn),
+ DLSYM_ARG(kmod_unref),
+ DLSYM_ARG(kmod_validate_resources));
+}
+
static int denylist_modules(const char *p, char ***denylist) {
_cleanup_strv_free_ char **k = NULL;
int r;
}
int module_load_and_warn(struct kmod_ctx *ctx, const char *module, bool verbose) {
- const int probe_flags = KMOD_PROBE_APPLY_BLACKLIST;
- struct kmod_list *itr;
- _cleanup_(kmod_module_unref_listp) struct kmod_list *modlist = NULL;
+ _cleanup_(sym_kmod_module_unref_listp) struct kmod_list *modlist = NULL;
_cleanup_strv_free_ char **denylist = NULL;
bool denylist_parsed = false;
+ struct kmod_list *itr;
int r;
+ assert(ctx);
+ assert(module);
+
/* verbose==true means we should log at non-debug level if we
* fail to find or load the module. */
log_debug("Loading module: %s", module);
- r = kmod_module_new_from_lookup(ctx, module, &modlist);
+ r = sym_kmod_module_new_from_lookup(ctx, module, &modlist);
if (r < 0)
return log_full_errno(verbose ? LOG_ERR : LOG_DEBUG, r,
"Failed to look up module alias '%s': %m", module);
SYNTHETIC_ERRNO(ENOENT),
"Failed to find module '%s'", module);
- kmod_list_foreach(itr, modlist) {
- _cleanup_(kmod_module_unrefp) struct kmod_module *mod = NULL;
+ sym_kmod_list_foreach(itr, modlist) {
+ _cleanup_(sym_kmod_module_unrefp) struct kmod_module *mod = NULL;
int state, err;
- mod = kmod_module_get_module(itr);
- state = kmod_module_get_initstate(mod);
+ mod = sym_kmod_module_get_module(itr);
+ state = sym_kmod_module_get_initstate(mod);
switch (state) {
case KMOD_MODULE_BUILTIN:
log_full(verbose ? LOG_INFO : LOG_DEBUG,
- "Module '%s' is built in", kmod_module_get_name(mod));
+ "Module '%s' is built in", sym_kmod_module_get_name(mod));
break;
case KMOD_MODULE_LIVE:
- log_debug("Module '%s' is already loaded", kmod_module_get_name(mod));
+ log_debug("Module '%s' is already loaded", sym_kmod_module_get_name(mod));
break;
default:
- err = kmod_module_probe_insert_module(mod, probe_flags,
- NULL, NULL, NULL, NULL);
+ err = sym_kmod_module_probe_insert_module(
+ mod,
+ KMOD_PROBE_APPLY_BLACKLIST,
+ /* extra_options= */ NULL,
+ /* run_install= */ NULL,
+ /* data= */ NULL,
+ /* print_action= */ NULL);
if (err == 0)
log_full(verbose ? LOG_INFO : LOG_DEBUG,
- "Inserted module '%s'", kmod_module_get_name(mod));
+ "Inserted module '%s'", sym_kmod_module_get_name(mod));
else if (err == KMOD_PROBE_APPLY_BLACKLIST)
log_full(verbose ? LOG_INFO : LOG_DEBUG,
- "Module '%s' is deny-listed (by kmod)", kmod_module_get_name(mod));
+ "Module '%s' is deny-listed (by kmod)", sym_kmod_module_get_name(mod));
else {
assert(err < 0);
denylist_parsed = true;
}
- if (strv_contains(denylist, kmod_module_get_name(mod))) {
+ if (strv_contains(denylist, sym_kmod_module_get_name(mod))) {
log_full(verbose ? LOG_INFO : LOG_DEBUG,
- "Module '%s' is deny-listed (by kernel)", kmod_module_get_name(mod));
+ "Module '%s' is deny-listed (by kernel)", sym_kmod_module_get_name(mod));
continue;
}
}
LOG_ERR,
err,
"Failed to insert module '%s': %m",
- kmod_module_get_name(mod));
+ sym_kmod_module_get_name(mod));
if (!IN_SET(err, -ENODEV, -ENOENT))
r = err;
}
return r;
}
+
+_printf_(6,0) static void systemd_kmod_log(
+ void *data,
+ int priority,
+ const char *file,
+ int line,
+ const char *fn,
+ const char *format,
+ va_list args) {
+
+ log_internalv(priority, 0, file, line, fn, format, args);
+}
+
+int module_setup_context(struct kmod_ctx **ret) {
+ _cleanup_(sym_kmod_unrefp) struct kmod_ctx *ctx = NULL;
+ int r;
+
+ assert(ret);
+
+ r = dlopen_libkmod();
+ if (r < 0)
+ return r;
+
+ ctx = sym_kmod_new(NULL, NULL);
+ if (!ctx)
+ return -ENOMEM;
+
+ (void) sym_kmod_load_resources(ctx);
+ sym_kmod_set_log_fn(ctx, systemd_kmod_log, NULL);
+
+ *ret = TAKE_PTR(ctx);
+ return 0;
+}
+
+#endif
/* SPDX-License-Identifier: LGPL-2.1-or-later */
#pragma once
+#include "dlfcn-util.h"
+
+#if HAVE_KMOD
+
#include <libkmod.h>
#include "macro.h"
-DEFINE_TRIVIAL_CLEANUP_FUNC(struct kmod_ctx*, kmod_unref);
-DEFINE_TRIVIAL_CLEANUP_FUNC(struct kmod_module*, kmod_module_unref);
-DEFINE_TRIVIAL_CLEANUP_FUNC_FULL(struct kmod_list*, kmod_module_unref_list, NULL);
+DLSYM_PROTOTYPE(kmod_list_next);
+DLSYM_PROTOTYPE(kmod_load_resources);
+DLSYM_PROTOTYPE(kmod_module_get_initstate);
+DLSYM_PROTOTYPE(kmod_module_get_module);
+DLSYM_PROTOTYPE(kmod_module_get_name);
+DLSYM_PROTOTYPE(kmod_module_new_from_lookup);
+DLSYM_PROTOTYPE(kmod_module_probe_insert_module);
+DLSYM_PROTOTYPE(kmod_module_unref);
+DLSYM_PROTOTYPE(kmod_module_unref_list);
+DLSYM_PROTOTYPE(kmod_new);
+DLSYM_PROTOTYPE(kmod_set_log_fn);
+DLSYM_PROTOTYPE(kmod_unref);
+DLSYM_PROTOTYPE(kmod_validate_resources);
+
+int dlopen_libkmod(void);
+
+DEFINE_TRIVIAL_CLEANUP_FUNC(struct kmod_ctx*, sym_kmod_unref);
+DEFINE_TRIVIAL_CLEANUP_FUNC(struct kmod_module*, sym_kmod_module_unref);
+DEFINE_TRIVIAL_CLEANUP_FUNC_FULL(struct kmod_list*, sym_kmod_module_unref_list, NULL);
+
+#define sym_kmod_list_foreach(list_entry, first_entry) \
+ for (list_entry = first_entry; \
+ list_entry != NULL; \
+ list_entry = sym_kmod_list_next(first_entry, list_entry))
int module_load_and_warn(struct kmod_ctx *ctx, const char *module, bool verbose);
+int module_setup_context(struct kmod_ctx **ret);
+
+#else
+
+struct kmod_ctx;
+
+static inline int dlopen_libkmod(void) {
+ return -EOPNOTSUPP;
+}
+
+static inline int module_setup_context(struct kmod_ctx **ret) {
+ return -EOPNOTSUPP;
+}
+
+static inline int module_load_and_warn(struct kmod_ctx *ctx, const char *module, bool verbose) {
+ return -EOPNOTSUPP;
+}
+
+#endif
--- /dev/null
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+
+#include <sys/prctl.h>
+
+#include "fd-util.h"
+#include "format-util.h"
+#include "missing_sched.h"
+#include "namespace-util.h"
+#include "nsresource.h"
+#include "process-util.h"
+#include "varlink.h"
+
+static int make_pid_name(char **ret) {
+ char comm[TASK_COMM_LEN];
+
+ assert(ret);
+
+ if (prctl(PR_GET_NAME, comm) < 0)
+ return -errno;
+
+ /* So the namespace name should be 16 chars at max (because we want that it is usable in usernames,
+ * which have a limit of 31 chars effectively, and the nsresourced service wants to prefix/suffix
+ * some bits). But it also should be unique if we are called multiple times in a row. Hence we take
+ * the "comm" name (which is 15 chars), and suffix it with the UID, possibly overriding the end. */
+ assert_cc(TASK_COMM_LEN == 15 + 1);
+
+ char spid[DECIMAL_STR_MAX(pid_t)];
+ xsprintf(spid, PID_FMT, getpid_cached());
+
+ assert(strlen(spid) <= 16);
+ strshorten(comm, 16 - strlen(spid));
+
+ _cleanup_free_ char *s = strjoin(comm, spid);
+ if (!s)
+ return -ENOMEM;
+
+ *ret = TAKE_PTR(s);
+ return 0;
+}
+
+int nsresource_allocate_userns(const char *name, uint64_t size) {
+ _cleanup_(varlink_unrefp) Varlink *vl = NULL;
+ _cleanup_close_ int userns_fd = -EBADF;
+ _cleanup_free_ char *_name = NULL;
+ const char *error_id;
+ int r, userns_fd_idx;
+
+ /* Allocate a new dynamic user namespace via the userdb registry logic */
+
+ if (!name) {
+ r = make_pid_name(&_name);
+ if (r < 0)
+ return r;
+
+ name = _name;
+ }
+
+ if (size <= 0 || size > UINT64_C(0x100000000)) /* Note: the server actually only allows allocating 1 or 64K right now */
+ return -EINVAL;
+
+ r = varlink_connect_address(&vl, "/run/systemd/io.systemd.NamespaceResource");
+ if (r < 0)
+ return log_debug_errno(r, "Failed to connect to namespace resource manager: %m");
+
+ r = varlink_set_allow_fd_passing_output(vl, true);
+ if (r < 0)
+ return log_debug_errno(r, "Failed to enable varlink fd passing for write: %m");
+
+ userns_fd = userns_acquire_empty();
+ if (userns_fd < 0)
+ return log_debug_errno(userns_fd, "Failed to acquire empty user namespace: %m");
+
+ userns_fd_idx = varlink_push_dup_fd(vl, userns_fd);
+ if (userns_fd_idx < 0)
+ return log_debug_errno(userns_fd_idx, "Failed to push userns fd into varlink connection: %m");
+
+ JsonVariant *reply = NULL;
+ r = varlink_callb(vl,
+ "io.systemd.NamespaceResource.AllocateUserRange",
+ &reply,
+ &error_id,
+ JSON_BUILD_OBJECT(
+ JSON_BUILD_PAIR("name", JSON_BUILD_STRING(name)),
+ JSON_BUILD_PAIR("size", JSON_BUILD_UNSIGNED(size)),
+ JSON_BUILD_PAIR("userNamespaceFileDescriptor", JSON_BUILD_UNSIGNED(userns_fd_idx))));
+ if (r < 0)
+ return log_debug_errno(r, "Failed to call AllocateUserRange() varlink call: %m");
+ if (error_id)
+ return log_debug_errno(varlink_error_to_errno(error_id, reply), "Failed to allocate user namespace with %" PRIu64 " users: %s", size, error_id);
+
+ return TAKE_FD(userns_fd);
+}
+
+int nsresource_register_userns(const char *name, int userns_fd) {
+ _cleanup_(varlink_unrefp) Varlink *vl = NULL;
+ _cleanup_close_ int _userns_fd = -EBADF;
+ _cleanup_free_ char *_name = NULL;
+ const char *error_id;
+ int r, userns_fd_idx;
+
+ /* Register the specified user namespace with userbd. */
+
+ if (!name) {
+ r = make_pid_name(&_name);
+ if (r < 0)
+ return r;
+
+ name = _name;
+ }
+
+ if (userns_fd < 0) {
+ _userns_fd = namespace_open_by_type(NAMESPACE_USER);
+ if (_userns_fd < 0)
+ return -errno;
+
+ userns_fd = _userns_fd;
+ }
+
+ r = varlink_connect_address(&vl, "/run/systemd/io.systemd.NamespaceResource");
+ if (r < 0)
+ return log_debug_errno(r, "Failed to connect to namespace resource manager: %m");
+
+ r = varlink_set_allow_fd_passing_output(vl, true);
+ if (r < 0)
+ return log_debug_errno(r, "Failed to enable varlink fd passing for write: %m");
+
+ userns_fd_idx = varlink_push_dup_fd(vl, userns_fd);
+ if (userns_fd_idx < 0)
+ return log_debug_errno(userns_fd_idx, "Failed to push userns fd into varlink connection: %m");
+
+ JsonVariant *reply = NULL;
+ r = varlink_callb(vl,
+ "io.systemd.NamespaceResource.RegisterUserNamespace",
+ &reply,
+ &error_id,
+ JSON_BUILD_OBJECT(
+ JSON_BUILD_PAIR("name", JSON_BUILD_STRING(name)),
+ JSON_BUILD_PAIR("userNamespaceFileDescriptor", JSON_BUILD_UNSIGNED(userns_fd_idx))));
+ if (r < 0)
+ return log_debug_errno(r, "Failed to call RegisterUserNamespace() varlink call: %m");
+ if (error_id)
+ return log_debug_errno(varlink_error_to_errno(error_id, reply), "Failed to register user namespace: %s", error_id);
+
+ return 0;
+}
+
+int nsresource_add_mount(int userns_fd, int mount_fd) {
+ _cleanup_(varlink_unrefp) Varlink *vl = NULL;
+ _cleanup_close_ int _userns_fd = -EBADF;
+ int r, userns_fd_idx, mount_fd_idx;
+ const char *error_id;
+
+ assert(mount_fd >= 0);
+
+ if (userns_fd < 0) {
+ _userns_fd = namespace_open_by_type(NAMESPACE_USER);
+ if (_userns_fd < 0)
+ return _userns_fd;
+
+ userns_fd = _userns_fd;
+ }
+
+ r = varlink_connect_address(&vl, "/run/systemd/io.systemd.NamespaceResource");
+ if (r < 0)
+ return log_error_errno(r, "Failed to connect to namespace resource manager: %m");
+
+ r = varlink_set_allow_fd_passing_output(vl, true);
+ if (r < 0)
+ return log_error_errno(r, "Failed to enable varlink fd passing for write: %m");
+
+ userns_fd_idx = varlink_push_dup_fd(vl, userns_fd);
+ if (userns_fd_idx < 0)
+ return log_error_errno(userns_fd_idx, "Failed to push userns fd into varlink connection: %m");
+
+ mount_fd_idx = varlink_push_dup_fd(vl, mount_fd);
+ if (mount_fd_idx < 0)
+ return log_error_errno(mount_fd_idx, "Failed to push mount fd into varlink connection: %m");
+
+ JsonVariant *reply = NULL;
+ r = varlink_callb(vl,
+ "io.systemd.NamespaceResource.AddMountToUserNamespace",
+ &reply,
+ &error_id,
+ JSON_BUILD_OBJECT(
+ JSON_BUILD_PAIR("userNamespaceFileDescriptor", JSON_BUILD_UNSIGNED(userns_fd_idx)),
+ JSON_BUILD_PAIR("mountFileDescriptor", JSON_BUILD_UNSIGNED(mount_fd_idx))));
+ if (r < 0)
+ return log_error_errno(r, "Failed to call AddMountToUserNamespace() varlink call: %m");
+ if (streq_ptr(error_id, "io.systemd.NamespaceResource.UserNamespaceNotRegistered")) {
+ log_notice("User namespace has not been allocated via namespace resource registry, not adding mount to registration.");
+ return 0;
+ }
+ if (error_id)
+ return log_error_errno(varlink_error_to_errno(error_id, reply), "Failed to mount image: %s", error_id);
+
+ return 1;
+}
+
+int nsresource_add_cgroup(int userns_fd, int cgroup_fd) {
+ _cleanup_(varlink_unrefp) Varlink *vl = NULL;
+ _cleanup_close_ int _userns_fd = -EBADF;
+ int r, userns_fd_idx, cgroup_fd_idx;
+ const char *error_id;
+
+ assert(cgroup_fd >= 0);
+
+ if (userns_fd < 0) {
+ _userns_fd = namespace_open_by_type(NAMESPACE_USER);
+ if (_userns_fd < 0)
+ return -errno;
+
+ userns_fd = _userns_fd;
+ }
+
+ r = varlink_connect_address(&vl, "/run/systemd/io.systemd.NamespaceResource");
+ if (r < 0)
+ return log_debug_errno(r, "Failed to connect to namespace resource manager: %m");
+
+ r = varlink_set_allow_fd_passing_output(vl, true);
+ if (r < 0)
+ return log_debug_errno(r, "Failed to enable varlink fd passing for write: %m");
+
+ userns_fd_idx = varlink_push_dup_fd(vl, userns_fd);
+ if (userns_fd_idx < 0)
+ return log_debug_errno(userns_fd_idx, "Failed to push userns fd into varlink connection: %m");
+
+ cgroup_fd_idx = varlink_push_dup_fd(vl, cgroup_fd);
+ if (cgroup_fd_idx < 0)
+ return log_debug_errno(userns_fd_idx, "Failed to push cgroup fd into varlink connection: %m");
+
+ JsonVariant *reply = NULL;
+ r = varlink_callb(vl,
+ "io.systemd.NamespaceResource.AddControlGroupToUserNamespace",
+ &reply,
+ &error_id,
+ JSON_BUILD_OBJECT(
+ JSON_BUILD_PAIR("userNamespaceFileDescriptor", JSON_BUILD_UNSIGNED(userns_fd_idx)),
+ JSON_BUILD_PAIR("controlGroupFileDescriptor", JSON_BUILD_UNSIGNED(cgroup_fd_idx))));
+ if (r < 0)
+ return log_debug_errno(r, "Failed to call AddControlGroupToUserNamespace() varlink call: %m");
+ if (streq_ptr(error_id, "io.systemd.NamespaceResource.UserNamespaceNotRegistered")) {
+ log_notice("User namespace has not been allocated via namespace resource registry, not adding cgroup to registration.");
+ return 0;
+ }
+ if (error_id)
+ return log_debug_errno(varlink_error_to_errno(error_id, reply), "Failed to add cgroup to user namespace: %s", error_id);
+
+ return 1;
+}
+
+int nsresource_add_netif(
+ int userns_fd,
+ int netns_fd,
+ const char *namespace_ifname,
+ char **ret_host_ifname,
+ char **ret_namespace_ifname) {
+
+ _cleanup_close_ int _userns_fd = -EBADF, _netns_fd = -EBADF;
+ _cleanup_(varlink_unrefp) Varlink *vl = NULL;
+ int r, userns_fd_idx, netns_fd_idx;
+ const char *error_id;
+
+ if (userns_fd < 0) {
+ _userns_fd = namespace_open_by_type(NAMESPACE_USER);
+ if (_userns_fd < 0)
+ return -errno;
+
+ userns_fd = _userns_fd;
+ }
+
+ if (netns_fd < 0) {
+ _netns_fd = namespace_open_by_type(NAMESPACE_NET);
+ if (_netns_fd < 0)
+ return -errno;
+
+ netns_fd = _netns_fd;
+ }
+
+ r = varlink_connect_address(&vl, "/run/systemd/io.systemd.NamespaceResource");
+ if (r < 0)
+ return log_debug_errno(r, "Failed to connect to namespace resource manager: %m");
+
+ r = varlink_set_allow_fd_passing_output(vl, true);
+ if (r < 0)
+ return log_debug_errno(r, "Failed to enable varlink fd passing for write: %m");
+
+ userns_fd_idx = varlink_push_dup_fd(vl, userns_fd);
+ if (userns_fd_idx < 0)
+ return log_debug_errno(userns_fd_idx, "Failed to push userns fd into varlink connection: %m");
+
+ netns_fd_idx = varlink_push_dup_fd(vl, netns_fd);
+ if (netns_fd_idx < 0)
+ return log_debug_errno(netns_fd_idx, "Failed to push netns fd into varlink connection: %m");
+
+ JsonVariant *reply = NULL;
+ r = varlink_callb(vl,
+ "io.systemd.NamespaceResource.AddNetworkToUserNamespace",
+ &reply,
+ &error_id,
+ JSON_BUILD_OBJECT(
+ JSON_BUILD_PAIR("userNamespaceFileDescriptor", JSON_BUILD_UNSIGNED(userns_fd_idx)),
+ JSON_BUILD_PAIR("networkNamespaceFileDescriptor", JSON_BUILD_UNSIGNED(netns_fd_idx)),
+ JSON_BUILD_PAIR("mode", JSON_BUILD_CONST_STRING("veth")),
+ JSON_BUILD_PAIR_CONDITION(namespace_ifname, "namespaceInterfaceName", JSON_BUILD_STRING(namespace_ifname))));
+ if (r < 0)
+ return log_debug_errno(r, "Failed to call AddNetworkToUserNamespace() varlink call: %m");
+ if (streq_ptr(error_id, "io.systemd.NamespaceResource.UserNamespaceNotRegistered")) {
+ log_notice("User namespace has not been allocated via namespace resource registry, not adding network to registration.");
+ return 0;
+ }
+ if (error_id)
+ return log_debug_errno(varlink_error_to_errno(error_id, reply), "Failed to add network to user namespace: %s", error_id);
+
+ _cleanup_free_ char *host_interface_name = NULL, *namespace_interface_name = NULL;
+ r = json_dispatch(
+ reply,
+ (const JsonDispatch[]) {
+ { "hostInterfaceName", JSON_VARIANT_STRING, json_dispatch_string, PTR_TO_SIZE(&host_interface_name) },
+ { "namespaceInterfaceName", JSON_VARIANT_STRING, json_dispatch_string, PTR_TO_SIZE(&namespace_interface_name) },
+ },
+ JSON_ALLOW_EXTENSIONS,
+ /* userdata= */ NULL);
+
+ if (ret_host_ifname)
+ *ret_host_ifname = TAKE_PTR(host_interface_name);
+ if (ret_namespace_ifname)
+ *ret_namespace_ifname = TAKE_PTR(namespace_interface_name);
+
+ return 1;
+}
--- /dev/null
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+#pragma once
+
+#include <inttypes.h>
+
+int nsresource_allocate_userns(const char *name, uint64_t size);
+int nsresource_register_userns(const char *name, int userns_fd);
+int nsresource_add_mount(int userns_fd, int mount_fd);
+int nsresource_add_cgroup(int userns_fd, int cgroup_fd);
+int nsresource_add_netif(int userns_fd, int netns_fd, const char *namespace_ifname, char **ret_host_ifname, char **ret_namespace_ifname);
-
/* SPDX-License-Identifier: LGPL-2.1-or-later */
#include <fcntl.h>
assert(of);
assert(ret);
- s = shell_escape(of->path, ":");
+ s = xescape(of->path, ":");
if (!s)
return -ENOMEM;
return 0;
}
-OpenFile *open_file_free(OpenFile *of) {
+OpenFile* open_file_free(OpenFile *of) {
if (!of)
return NULL;
free(of->path);
free(of->fdname);
- return mfree(of);
-}
-void open_file_free_many(OpenFile **head) {
- assert(head);
-
- LIST_CLEAR(open_files, *head, open_file_free);
+ return mfree(of);
}
static const char * const open_file_flags_table[_OPENFILE_MAX] = {
-
/* SPDX-License-Identifier: LGPL-2.1-or-later */
#pragma once
#include "list.h"
+#include "macro.h"
typedef enum OpenFileFlag {
OPENFILE_READ_ONLY = 1 << 0,
int open_file_to_string(const OpenFile *of, char **ret);
-OpenFile *open_file_free(OpenFile *of);
+OpenFile* open_file_free(OpenFile *of);
DEFINE_TRIVIAL_CLEANUP_FUNC(OpenFile*, open_file_free);
-void open_file_free_many(OpenFile **head);
+static inline void open_file_free_many(OpenFile **head) {
+ LIST_CLEAR(open_files, *ASSERT_PTR(head), open_file_free);
+}
-const char *open_file_flags_to_string(OpenFileFlag t) _const_;
+const char* open_file_flags_to_string(OpenFileFlag t) _const_;
OpenFileFlag open_file_flags_from_string(const char *t) _pure_;
switch (attributes[i].type) {
case CKA_CLASS: {
CK_OBJECT_CLASS requested_class = *((CK_OBJECT_CLASS*) attributes[i].pValue);
- if (requested_class != CKO_PUBLIC_KEY && requested_class != CKO_CERTIFICATE)
+ if (!IN_SET(requested_class, CKO_PUBLIC_KEY, CKO_CERTIFICATE))
return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
"Selected PKCS#11 object is not a public key or certificate, refusing.");
break;
candidate_attributes[0].ulValueLen = sizeof(class);
candidate_attributes[1].ulValueLen = sizeof(type);
rv = m->C_GetAttributeValue(session, candidate, candidate_attributes, ELEMENTSOF(candidate_attributes));
- if (rv != CKR_OK && rv != CKR_ATTRIBUTE_TYPE_INVALID)
+ if (!IN_SET(rv, CKR_OK, CKR_ATTRIBUTE_TYPE_INVALID))
return log_error_errno(SYNTHETIC_ERRNO(EIO),
"Failed to get attributes of a selected candidate: %s", sym_p11_kit_strerror(rv));
* since we cannot lookahead to see if the Esc is followed by a \
* we cut a corner here and assume it will be \. */
- if (c == '\x07' || c == '\x1b') {
+ if (IN_SET(c, '\x07', '\x1b')) {
r = insert_window_title_fix(f, i+1);
if (r < 0)
return r;
if (ret_size)
*ret_size = sz;
- } else if (is_fs_type(&sfs, XFS_SB_MAGIC)) {
+ } else if (is_fs_type(&sfs, XFS_SUPER_MAGIC)) {
xfs_fsop_geom_t geo;
xfs_growfs_data_t d;
case (statfs_f_type_t) EXT4_SUPER_MAGIC:
return EXT4_MINIMAL_SIZE;
- case (statfs_f_type_t) XFS_SB_MAGIC:
+ case (statfs_f_type_t) XFS_SUPER_MAGIC:
return XFS_MINIMAL_SIZE;
case (statfs_f_type_t) BTRFS_SUPER_MAGIC:
}
int serialize_pidref(FILE *f, FDSet *fds, const char *key, PidRef *pidref) {
- int copy;
+ int r;
assert(f);
assert(fds);
if (!pidref_is_set(pidref))
return 0;
- /* If we have a pidfd we serialize the fd and encode the fd number prefixed by "@" in the
- * serialization. Otherwise we serialize the numeric PID as it is. */
+ /* We always serialize the pid separately, to keep downgrades mostly working (older versions will
+ * deserialize the pid and silently fail to deserialize the pidfd). If we also have a pidfd, we
+ * serialize both the pid and pidfd, so that we can construct the exact same pidref after
+ * deserialization (this doesn't work with only the pidfd, as we can't retrieve the original pid
+ * from the pidfd anymore if the process is reaped). */
- if (pidref->fd < 0)
- return serialize_item_format(f, key, PID_FMT, pidref->pid);
+ if (pidref->fd >= 0) {
+ int copy = fdset_put_dup(fds, pidref->fd);
+ if (copy < 0)
+ return log_error_errno(copy, "Failed to add file descriptor to serialization set: %m");
- copy = fdset_put_dup(fds, pidref->fd);
- if (copy < 0)
- return log_error_errno(copy, "Failed to add file descriptor to serialization set: %m");
+ r = serialize_item_format(f, key, "@%i:" PID_FMT, copy, pidref->pid);
+ if (r < 0)
+ return r;
+ }
- return serialize_item_format(f, key, "@%i", copy);
+ return serialize_item_format(f, key, PID_FMT, pidref->pid);
}
int serialize_ratelimit(FILE *f, const char *key, const RateLimit *rl) {
e = startswith(value, "@");
if (e) {
- int fd = deserialize_fd(fds, e);
+ _cleanup_free_ char *fdstr = NULL, *pidstr = NULL;
+ _cleanup_close_ int fd = -EBADF;
+
+ r = extract_many_words(&e, ":", /* flags = */ 0, &fdstr, &pidstr);
+ if (r < 0)
+ return log_debug_errno(r, "Failed to deserialize pidref '%s': %m", e);
+ if (r == 0)
+ return log_debug_errno(SYNTHETIC_ERRNO(EINVAL), "Cannot deserialize pidref from empty string.");
+
+ assert(r <= 2);
+ fd = deserialize_fd(fds, fdstr);
if (fd < 0)
return fd;
- r = pidref_set_pidfd_consume(ret, fd);
+ /* The serialization format changed after 255.4. In systemd <= 255.4 only pidfd is
+ * serialized, but that causes problems when reconstructing pidref (see serialize_pidref for
+ * details). After 255.4 the pid is serialized as well even if we have a pidfd, but we still
+ * need to support older format as we might be upgrading from a version that still uses the
+ * old format. */
+ if (pidstr) {
+ pid_t pid;
+
+ r = parse_pid(pidstr, &pid);
+ if (r < 0)
+ return log_debug_errno(r, "Failed to parse PID: %s", pidstr);
+
+ *ret = (PidRef) {
+ .pid = pid,
+ .fd = TAKE_FD(fd),
+ };
+ } else
+ r = pidref_set_pidfd_consume(ret, TAKE_FD(fd));
} else {
pid_t pid;
strv_free(sc->modes[i]);
}
+ strv_free(sc->mem_modes);
+
return mfree(sc);
}
void *data,
void *userdata) {
- _cleanup_strv_free_ char **modes = NULL;
char ***sv = ASSERT_PTR(data);
+ _cleanup_strv_free_ char **modes = NULL;
int r;
assert(filename);
return log_oom();
}
- return free_and_replace(*sv, modes);
+ return strv_free_and_replace(*sv, modes);
}
static void sleep_config_validate_state_and_mode(SleepConfig *sc) {
{ "Sleep", "HybridSleepState", config_parse_warn_compat, DISABLED_LEGACY, NULL },
{ "Sleep", "HybridSleepMode", config_parse_warn_compat, DISABLED_LEGACY, NULL },
+ { "Sleep", "MemorySleepMode", config_parse_sleep_mode, 0, &sc->mem_modes },
+
{ "Sleep", "HibernateDelaySec", config_parse_sec, 0, &sc->hibernate_delay_usec },
{ "Sleep", "SuspendEstimationSec", config_parse_sec, 0, &sc->suspend_estimation_usec },
{}
return 0;
}
-int sleep_state_supported(char **states) {
+int sleep_state_supported(char * const *states) {
_cleanup_free_ char *supported_sysfs = NULL;
const char *found;
int r;
return false;
}
-int sleep_mode_supported(char **modes) {
+int sleep_mode_supported(const char *path, char * const *modes) {
_cleanup_free_ char *supported_sysfs = NULL;
int r;
+ assert(path);
+
/* Unlike state, kernel has its own default choice if not configured */
if (strv_isempty(modes)) {
- log_debug("No sleep mode configured, using kernel default.");
+ log_debug("No sleep mode configured, using kernel default for %s.", path);
return true;
}
- if (access("/sys/power/disk", W_OK) < 0)
- return log_debug_errno(errno, "/sys/power/disk is not writable: %m");
+ if (access(path, W_OK) < 0)
+ return log_debug_errno(errno, "%s is not writable: %m", path);
- r = read_one_line_file("/sys/power/disk", &supported_sysfs);
+ r = read_one_line_file(path, &supported_sysfs);
if (r < 0)
- return log_debug_errno(r, "Failed to read /sys/power/disk: %m");
+ return log_debug_errno(r, "Failed to read %s: %m", path);
for (const char *p = supported_sysfs;;) {
_cleanup_free_ char *word = NULL;
r = extract_first_word(&p, &word, NULL, 0);
if (r < 0)
- return log_debug_errno(r, "Failed to parse /sys/power/disk: %m");
+ return log_debug_errno(r, "Failed to parse %s: %m", path);
if (r == 0)
break;
}
if (strv_contains(modes, mode)) {
- log_debug("Disk sleep mode '%s' is supported by kernel.", mode);
+ log_debug("Sleep mode '%s' is supported by kernel (%s).", mode, path);
return true;
}
}
if (DEBUG_LOGGING) {
_cleanup_free_ char *joined = strv_join(modes, " ");
- log_debug("None of the configured hibernation power modes are supported by kernel: %s", strnull(joined));
+ log_debug("None of the configured modes are supported by kernel (%s): %s",
+ path, strnull(joined));
}
return false;
}
return false;
}
- if (sleep_operation_is_hibernation(operation)) {
- r = sleep_mode_supported(sleep_config->modes[operation]);
+ if (SLEEP_NEEDS_MEM_SLEEP(sleep_config, operation)) {
+ r = sleep_mode_supported("/sys/power/mem_sleep", sleep_config->mem_modes);
+ if (r < 0)
+ return r;
+ if (r == 0) {
+ *ret_support = SLEEP_STATE_OR_MODE_NOT_SUPPORTED;
+ return false;
+ }
+ }
+
+ if (SLEEP_OPERATION_IS_HIBERNATION(operation)) {
+ r = sleep_mode_supported("/sys/power/disk", sleep_config->modes[operation]);
if (r < 0)
return r;
if (r == 0) {
/* SPDX-License-Identifier: LGPL-2.1-or-later */
#pragma once
+#include "strv.h"
#include "time-util.h"
typedef enum SleepOperation {
const char* sleep_operation_to_string(SleepOperation s) _const_;
SleepOperation sleep_operation_from_string(const char *s) _pure_;
-static inline bool sleep_operation_is_hibernation(SleepOperation operation) {
+static inline bool SLEEP_OPERATION_IS_HIBERNATION(SleepOperation operation) {
return IN_SET(operation, SLEEP_HIBERNATE, SLEEP_HYBRID_SLEEP);
}
bool allow[_SLEEP_OPERATION_MAX];
char **states[_SLEEP_OPERATION_CONFIG_MAX];
- char **modes[_SLEEP_OPERATION_CONFIG_MAX]; /* Power mode after writing hibernation image */
+ char **modes[_SLEEP_OPERATION_CONFIG_MAX]; /* Power mode after writing hibernation image (/sys/power/disk) */
+ char **mem_modes; /* /sys/power/mem_sleep */
usec_t hibernate_delay_usec;
usec_t suspend_estimation_usec;
int parse_sleep_config(SleepConfig **sleep_config);
+static inline bool SLEEP_NEEDS_MEM_SLEEP(const SleepConfig *sc, SleepOperation operation) {
+ assert(sc);
+ assert(operation >= 0 && operation < _SLEEP_OPERATION_CONFIG_MAX);
+
+ /* As per https://docs.kernel.org/admin-guide/pm/sleep-states.html#basic-sysfs-interfaces-for-system-suspend-and-hibernation,
+ * /sys/power/mem_sleep is honored if /sys/power/state is set to "mem" (common for suspend)
+ * or /sys/power/disk is set to "suspend" (hybrid-sleep). */
+
+ return strv_contains(sc->states[operation], "mem") ||
+ strv_contains(sc->modes[operation], "suspend");
+}
+
typedef enum SleepSupport {
SLEEP_SUPPORTED,
SLEEP_DISABLED, /* Disabled in SleepConfig.allow */
}
/* Only for test-sleep-config */
-int sleep_state_supported(char **states);
-int sleep_mode_supported(char **modes);
+int sleep_state_supported(char * const *states);
+int sleep_mode_supported(const char *path, char * const *modes);
/* SPDX-License-Identifier: LGPL-2.1-or-later */
+/* Make sure the net/if.h header is included before any linux/ one */
+#include <net/if.h>
#include <arpa/inet.h>
#include <errno.h>
#include <linux/net_namespace.h>
-#include <net/if.h>
#include <string.h>
#include "alloc-util.h"
#pragma once
#include <stdbool.h>
+#include <sys/prctl.h>
+#include <unistd.h>
#include "sd-daemon.h"
#include "argv-util.h"
#include "macro.h"
+#include "process-util.h"
+#include "rlimit-util.h"
+#include "signal-util.h"
#include "static-destruct.h"
#include "strv.h"
({ \
typeof(expr) _result = (expr); \
if (_result < 0) { \
- log_error_errno(_result, "%s:%i: Assertion failed: %s: %m", \
+ log_error_errno(_result, "%s:%i: Assertion failed: expected \"%s\" to succeed but got the following error: %m", \
PROJECT_FILE, __LINE__, #expr); \
abort(); \
} \
})
+#define ASSERT_OK_ERRNO(expr) \
+ ({ \
+ typeof(expr) _result = (expr); \
+ if (_result < 0) { \
+ log_error_errno(errno, "%s:%i: Assertion failed: expected \"%s\" to succeed but got the following error: %m", \
+ PROJECT_FILE, __LINE__, #expr); \
+ abort(); \
+ } \
+ })
+
#define ASSERT_TRUE(expr) \
({ \
if (!(expr)) { \
#define ASSERT_NULL(expr) \
({ \
- if ((expr) != NULL) { \
- log_error("%s:%i: Assertion failed: expected \"%s\" to be NULL", \
- PROJECT_FILE, __LINE__, #expr); \
+ typeof(expr) _result = (expr); \
+ if (_result != NULL) { \
+ log_error("%s:%i: Assertion failed: expected \"%s\" to be NULL, but \"%p\" != NULL", \
+ PROJECT_FILE, __LINE__, #expr, _result); \
abort(); \
} \
})
#define ASSERT_STREQ(expr1, expr2) \
({ \
- const char* _expr1 = (expr1); \
- const char* _expr2 = (expr2); \
- if (strcmp(_expr1, _expr2) != 0) { \
+ const char *_expr1 = (expr1), *_expr2 = (expr2); \
+ if (!streq_ptr(_expr1, _expr2)) { \
log_error("%s:%i: Assertion failed: expected \"%s == %s\", but \"%s != %s\"", \
- PROJECT_FILE, __LINE__, #expr1, #expr2, _expr1, _expr2); \
+ PROJECT_FILE, __LINE__, #expr1, #expr2, strnull(_expr1), strnull(_expr2)); \
abort(); \
} \
})
abort(); \
} \
})
+
+#define ASSERT_SIGNAL(expr, signal) \
+ ({ \
+ ASSERT_TRUE(SIGNAL_VALID(signal)); \
+ siginfo_t _siginfo = {}; \
+ int _pid = fork(); \
+ ASSERT_OK(_pid); \
+ if (_pid == 0) { \
+ /* Speed things up by never even attempting to generate a coredump */ \
+ (void) prctl(PR_SET_DUMPABLE, 0); \
+ /* But still set an rlimit just in case */ \
+ (void) setrlimit(RLIMIT_CORE, &RLIMIT_MAKE_CONST(0)); \
+ expr; \
+ _exit(EXIT_SUCCESS); \
+ } \
+ (void) wait_for_terminate(_pid, &_siginfo); \
+ if (_siginfo.si_status != signal) { \
+ log_error("%s:%i: Assertion failed: \"%s\" died with signal %s, but %s was expected", \
+ PROJECT_FILE, __LINE__, #expr, signal_to_string(_siginfo.si_status), \
+ signal_to_string(signal)); \
+ abort(); \
+ } \
+ })
if (!context->tcti_dl)
return log_debug_errno(SYNTHETIC_ERRNO(ENOTRECOVERABLE), "Failed to load %s: %s", fn, dlerror());
+ log_debug("Loaded '%s' via dlopen()", fn);
+
func = dlsym(context->tcti_dl, TSS2_TCTI_INFO_SYMBOL);
if (!func)
return log_debug_errno(SYNTHETIC_ERRNO(ENOTRECOVERABLE),
}
if (device) {
- if (sd_device_get_is_initialized(device) > 0) {
+ if (device_is_processed(device) > 0) {
if (ret)
*ret = sd_device_ref(device);
return 0;
if (r < 0 && !ERRNO_IS_DEVICE_ABSENT(r))
return log_error_errno(r, "Failed to create sd-device object from %s: %m", devlink);
}
- if (device && sd_device_get_is_initialized(device) > 0) {
+ if (device && device_is_processed(device) > 0) {
if (ret)
*ret = sd_device_ref(device);
return 0;
return r;
}
-int device_is_processing(sd_device *dev) {
+int device_is_processed(sd_device *dev) {
int r;
assert(dev);
+ /* sd_device_get_is_initialized() only checks if the udev database file exists. However, even if the
+ * database file exist, systemd-udevd may be still processing the device, e.g. when the udev rules
+ * for the device have RUN tokens. See issue #30056. Hence, to check if the device is really
+ * processed by systemd-udevd, we also need to read ID_PROCESSING property. */
+
+ r = sd_device_get_is_initialized(dev);
+ if (r <= 0)
+ return r;
+
r = device_get_property_bool(dev, "ID_PROCESSING");
if (r == -ENOENT)
- return false; /* defaults to false */
+ return true; /* If the property does not exist, then it means that the device is processed. */
+ if (r < 0)
+ return r;
- return r;
+ return !r;
}
bool device_for_action(sd_device *dev, sd_device_action_t a) {
int device_wait_for_initialization(sd_device *device, const char *subsystem, usec_t timeout_usec, sd_device **ret);
int device_wait_for_devlink(const char *path, const char *subsystem, usec_t timeout_usec, sd_device **ret);
int device_is_renaming(sd_device *dev);
-int device_is_processing(sd_device *dev);
+int device_is_processed(sd_device *dev);
bool device_for_action(sd_device *dev, sd_device_action_t action);
return 0;
}
+ log_debug("Loaded '%s' via dlopen()", LIBDIR "/libnss_systemd.so.2");
+
call = dlsym(dl, "_nss_systemd_block");
if (!call)
/* If the file is installed but lacks the symbol we expect, things are weird, let's complain */
--- /dev/null
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+
+#include "varlink-io.systemd.MountFileSystem.h"
+
+static VARLINK_DEFINE_ENUM_TYPE(
+ PartitionDesignator,
+ VARLINK_DEFINE_ENUM_VALUE(root),
+ VARLINK_DEFINE_ENUM_VALUE(usr),
+ VARLINK_DEFINE_ENUM_VALUE(home),
+ VARLINK_DEFINE_ENUM_VALUE(srv),
+ VARLINK_DEFINE_ENUM_VALUE(esp),
+ VARLINK_DEFINE_ENUM_VALUE(xbootldr),
+ VARLINK_DEFINE_ENUM_VALUE(swap),
+ VARLINK_DEFINE_ENUM_VALUE(root_verity),
+ VARLINK_DEFINE_ENUM_VALUE(usr_verity),
+ VARLINK_DEFINE_ENUM_VALUE(root_verity_sig),
+ VARLINK_DEFINE_ENUM_VALUE(usr_verity_sig),
+ VARLINK_DEFINE_ENUM_VALUE(tmp),
+ VARLINK_DEFINE_ENUM_VALUE(var));
+
+static VARLINK_DEFINE_STRUCT_TYPE(
+ PartitionInfo,
+ VARLINK_DEFINE_FIELD(designator, VARLINK_STRING, 0),
+ VARLINK_DEFINE_FIELD(writable, VARLINK_BOOL, 0),
+ VARLINK_DEFINE_FIELD(growFileSystem, VARLINK_BOOL, 0),
+ VARLINK_DEFINE_FIELD(partitionNumber, VARLINK_INT, VARLINK_NULLABLE),
+ VARLINK_DEFINE_FIELD(architecture, VARLINK_STRING, VARLINK_NULLABLE),
+ VARLINK_DEFINE_FIELD(partitionUuid, VARLINK_STRING, VARLINK_NULLABLE),
+ VARLINK_DEFINE_FIELD(fileSystemType, VARLINK_STRING, 0),
+ VARLINK_DEFINE_FIELD(partitionLabel, VARLINK_STRING, VARLINK_NULLABLE),
+ VARLINK_DEFINE_FIELD(size, VARLINK_INT, 0),
+ VARLINK_DEFINE_FIELD(offset, VARLINK_INT, 0),
+ VARLINK_DEFINE_FIELD(mountFileDescriptor, VARLINK_INT, 0));
+
+static VARLINK_DEFINE_METHOD(
+ MountImage,
+ VARLINK_DEFINE_INPUT(imageFileDescriptor, VARLINK_INT, 0),
+ VARLINK_DEFINE_INPUT(userNamespaceFileDescriptor, VARLINK_INT, VARLINK_NULLABLE),
+ VARLINK_DEFINE_INPUT(readOnly, VARLINK_BOOL, VARLINK_NULLABLE),
+ VARLINK_DEFINE_INPUT(growFileSystems, VARLINK_BOOL, VARLINK_NULLABLE),
+ VARLINK_DEFINE_INPUT(password, VARLINK_STRING, VARLINK_NULLABLE),
+ VARLINK_DEFINE_INPUT(imagePolicy, VARLINK_STRING, VARLINK_NULLABLE),
+ VARLINK_DEFINE_INPUT(allowInteractiveAuthentication, VARLINK_BOOL, VARLINK_NULLABLE),
+ VARLINK_DEFINE_OUTPUT_BY_TYPE(partitions, PartitionInfo, VARLINK_ARRAY),
+ VARLINK_DEFINE_OUTPUT(imagePolicy, VARLINK_STRING, 0),
+ VARLINK_DEFINE_OUTPUT(imageSize, VARLINK_INT, 0),
+ VARLINK_DEFINE_OUTPUT(sectorSize, VARLINK_INT, 0),
+ VARLINK_DEFINE_OUTPUT(imageName, VARLINK_STRING, VARLINK_NULLABLE),
+ VARLINK_DEFINE_OUTPUT(imageUuid, VARLINK_STRING, VARLINK_NULLABLE));
+
+static VARLINK_DEFINE_ERROR(IncompatibleImage);
+static VARLINK_DEFINE_ERROR(MultipleRootPartitionsFound);
+static VARLINK_DEFINE_ERROR(RootPartitionNotFound);
+static VARLINK_DEFINE_ERROR(DeniedByImagePolicy);
+static VARLINK_DEFINE_ERROR(KeyNotFound);
+static VARLINK_DEFINE_ERROR(VerityFailure);
+
+VARLINK_DEFINE_INTERFACE(
+ io_systemd_MountFileSystem,
+ "io.systemd.MountFileSystem",
+ &vl_type_PartitionDesignator,
+ &vl_type_PartitionInfo,
+ &vl_method_MountImage,
+ &vl_error_IncompatibleImage,
+ &vl_error_MultipleRootPartitionsFound,
+ &vl_error_RootPartitionNotFound,
+ &vl_error_DeniedByImagePolicy,
+ &vl_error_KeyNotFound,
+ &vl_error_VerityFailure);
--- /dev/null
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+#pragma once
+
+#include "varlink-idl.h"
+
+extern const VarlinkInterface vl_interface_io_systemd_MountFileSystem;
--- /dev/null
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+
+#include "varlink-io.systemd.NamespaceResource.h"
+
+static VARLINK_DEFINE_METHOD(
+ AllocateUserRange,
+ VARLINK_DEFINE_INPUT(name, VARLINK_STRING, 0),
+ VARLINK_DEFINE_INPUT(size, VARLINK_INT, 0),
+ VARLINK_DEFINE_INPUT(target, VARLINK_INT, VARLINK_NULLABLE),
+ VARLINK_DEFINE_INPUT(userNamespaceFileDescriptor, VARLINK_INT, 0));
+
+static VARLINK_DEFINE_METHOD(
+ RegisterUserNamespace,
+ VARLINK_DEFINE_INPUT(name, VARLINK_STRING, 0),
+ VARLINK_DEFINE_INPUT(userNamespaceFileDescriptor, VARLINK_INT, 0));
+
+static VARLINK_DEFINE_METHOD(
+ AddMountToUserNamespace,
+ VARLINK_DEFINE_INPUT(userNamespaceFileDescriptor, VARLINK_INT, 0),
+ VARLINK_DEFINE_INPUT(mountFileDescriptor, VARLINK_INT, 0));
+
+static VARLINK_DEFINE_METHOD(
+ AddControlGroupToUserNamespace,
+ VARLINK_DEFINE_INPUT(userNamespaceFileDescriptor, VARLINK_INT, 0),
+ VARLINK_DEFINE_INPUT(controlGroupFileDescriptor, VARLINK_INT, 0));
+
+static VARLINK_DEFINE_METHOD(
+ AddNetworkToUserNamespace,
+ VARLINK_DEFINE_INPUT(userNamespaceFileDescriptor, VARLINK_INT, 0),
+ VARLINK_DEFINE_INPUT(networkNamespaceFileDescriptor, VARLINK_INT, 0),
+ VARLINK_DEFINE_INPUT(namespaceInterfaceName, VARLINK_STRING, VARLINK_NULLABLE),
+ VARLINK_DEFINE_INPUT(mode, VARLINK_STRING, 0),
+ VARLINK_DEFINE_OUTPUT(hostInterfaceName, VARLINK_STRING, 0),
+ VARLINK_DEFINE_OUTPUT(namespaceInterfaceName, VARLINK_STRING, 0));
+
+static VARLINK_DEFINE_ERROR(UserNamespaceInterfaceNotSupported);
+static VARLINK_DEFINE_ERROR(NameExists);
+static VARLINK_DEFINE_ERROR(UserNamespaceExists);
+static VARLINK_DEFINE_ERROR(DynamicRangeUnavailable);
+static VARLINK_DEFINE_ERROR(NoDynamicRange);
+static VARLINK_DEFINE_ERROR(UserNamespaceNotRegistered);
+static VARLINK_DEFINE_ERROR(UserNamespaceWithoutUserRange);
+static VARLINK_DEFINE_ERROR(TooManyControlGroups);
+static VARLINK_DEFINE_ERROR(ControlGroupAlreadyAdded);
+
+VARLINK_DEFINE_INTERFACE(
+ io_systemd_NamespaceResource,
+ "io.systemd.NamespaceResource",
+ &vl_method_AllocateUserRange,
+ &vl_method_RegisterUserNamespace,
+ &vl_method_AddMountToUserNamespace,
+ &vl_method_AddControlGroupToUserNamespace,
+ &vl_method_AddNetworkToUserNamespace,
+ &vl_error_UserNamespaceInterfaceNotSupported,
+ &vl_error_NameExists,
+ &vl_error_UserNamespaceExists,
+ &vl_error_DynamicRangeUnavailable,
+ &vl_error_NoDynamicRange,
+ &vl_error_UserNamespaceNotRegistered,
+ &vl_error_UserNamespaceWithoutUserRange,
+ &vl_error_TooManyControlGroups,
+ &vl_error_ControlGroupAlreadyAdded);
--- /dev/null
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+#pragma once
+
+#include "varlink-idl.h"
+
+extern const VarlinkInterface vl_interface_io_systemd_NamespaceResource;
return varlink_log_errno(v, r, "Failed to acquire credentials: %m");
if (!uid_is_valid(v->ucred.uid))
- return varlink_log_errno(v, SYNTHETIC_ERRNO(ENODATA), "Peer uid is invalid.");
+ return varlink_log_errno(v, SYNTHETIC_ERRNO(ENODATA), "Peer UID is invalid.");
*ret = v->ucred.uid;
return 0;
}
+int varlink_get_peer_gid(Varlink *v, gid_t *ret) {
+ int r;
+
+ assert_return(v, -EINVAL);
+ assert_return(ret, -EINVAL);
+
+ r = varlink_acquire_ucred(v);
+ if (r < 0)
+ return varlink_log_errno(v, r, "Failed to acquire credentials: %m");
+
+ if (!gid_is_valid(v->ucred.gid))
+ return varlink_log_errno(v, SYNTHETIC_ERRNO(ENODATA), "Peer GID is invalid.");
+
+ *ret = v->ucred.gid;
+ return 0;
+}
+
int varlink_get_peer_pid(Varlink *v, pid_t *ret) {
int r;
return i;
}
-int varlink_dup_fd(Varlink *v, int fd) {
+int varlink_push_dup_fd(Varlink *v, int fd) {
_cleanup_close_ int dp = -1;
int r;
return v->input_fds[i];
}
+int varlink_peek_dup_fd(Varlink *v, size_t i) {
+ int fd;
+
+ fd = varlink_peek_fd(v, i);
+ if (fd < 0)
+ return fd;
+
+ return RET_NERRNO(fcntl(fd, F_DUPFD_CLOEXEC, 3));
+}
+
int varlink_take_fd(Varlink *v, size_t i) {
assert_return(v, -EINVAL);
/* Write outgoing fds into the socket (to be associated with the next enqueued message) */
int varlink_push_fd(Varlink *v, int fd);
-int varlink_dup_fd(Varlink *v, int fd);
+int varlink_push_dup_fd(Varlink *v, int fd);
int varlink_reset_fds(Varlink *v);
/* Read incoming fds from the socket (associated with the currently handled message) */
int varlink_peek_fd(Varlink *v, size_t i);
+int varlink_peek_dup_fd(Varlink *v, size_t i);
int varlink_take_fd(Varlink *v, size_t i);
int varlink_set_allow_fd_passing_input(Varlink *v, bool b);
void* varlink_get_userdata(Varlink *v);
int varlink_get_peer_uid(Varlink *v, uid_t *ret);
+int varlink_get_peer_gid(Varlink *v, gid_t *ret);
int varlink_get_peer_pid(Varlink *v, pid_t *ret);
int varlink_get_peer_pidref(Varlink *v, PidRef *ret);
assert(toplevel_fd >= 0 || toplevel_fd == AT_FDCWD);
assert(inode_path);
assert(filter);
-
- toplevel_path = strempty(toplevel_path);
+ assert(ret);
if (inode_fd < 0 || FLAGS_SET(flags, PICK_RESOLVE)) {
r = chaseat(toplevel_fd,
assert(toplevel_fd >= 0 || toplevel_fd == AT_FDCWD);
assert(inode_path);
assert(filter);
-
- toplevel_path = strempty(toplevel_path);
+ assert(ret);
if (inode_fd < 0) {
r = chaseat(toplevel_fd, inode_path, CHASE_AT_RESOLVE_IN_ROOT, NULL, &inode_fd);
ret);
}
-int path_pick(const char *toplevel_path,
- int toplevel_fd,
- const char *path,
- const PickFilter *filter,
- PickFlags flags,
- PickResult *ret) {
+int path_pick(
+ const char *toplevel_path,
+ int toplevel_fd,
+ const char *path,
+ const PickFilter *filter,
+ PickFlags flags,
+ PickResult *ret) {
_cleanup_free_ char *filter_bname = NULL, *dir = NULL, *parent = NULL, *fname = NULL;
const char *filter_suffix, *enumeration_path;
assert(toplevel_fd >= 0 || toplevel_fd == AT_FDCWD);
assert(path);
-
- toplevel_path = strempty(toplevel_path);
+ assert(filter);
+ assert(ret);
/* Given a path, resolve .v/ subdir logic (if used!), and returns the choice made. This supports
* three ways to be called:
assert(path);
assert(*path);
+ assert(filter);
/* This updates the first argument if needed! */
&result);
if (r == -ENOENT) {
log_debug("Path '%s' doesn't exist, leaving as is.", *path);
- *ret_result = PICK_RESULT_NULL;
+
+ if (ret_result)
+ *ret_result = PICK_RESULT_NULL;
return 0;
}
if (r < 0)
void pick_result_done(PickResult *p);
-int path_pick(const char *toplevel_path,
- int toplevel_fd,
- const char *path,
- const PickFilter *filter,
- PickFlags flags,
- PickResult *ret);
+int path_pick(
+ const char *toplevel_path,
+ int toplevel_fd,
+ const char *path,
+ const PickFilter *filter,
+ PickFlags flags,
+ PickResult *ret);
int path_pick_update_warn(
char **path,
governor,
WRITE_STRING_FILE_DISABLE_BUFFER | WRITE_STRING_FILE_VERIFY_ON_FAILURE | WRITE_STRING_FILE_VERIFY_IGNORE_NEWLINE);
if (r < 0)
- return log_error_errno(r, "Failed to set pretimeout_governor to '%s': %m", governor);
+ return log_error_errno(r, "Failed to set watchdog pretimeout_governor to '%s': %m", governor);
return r;
}
if (ioctl(watchdog_fd, WDIOC_GETPRETIMEOUT, &sec) < 0) {
watchdog_pretimeout = 0;
- return log_full_errno(ERRNO_IS_NOT_SUPPORTED(errno) ? LOG_DEBUG : LOG_WARNING, errno, "Failed to get pretimeout value, ignoring: %m");
+ return log_full_errno(ERRNO_IS_NOT_SUPPORTED(errno) ? LOG_DEBUG : LOG_WARNING, errno, "Failed to get watchdog pretimeout value, ignoring: %m");
}
watchdog_pretimeout = sec * USEC_PER_SEC;
return 0;
}
- return log_error_errno(errno, "Failed to set pretimeout to %s: %m", FORMAT_TIMESPAN(sec, USEC_PER_SEC));
+ return log_error_errno(errno, "Failed to set watchdog pretimeout to %s: %m", FORMAT_TIMESPAN(sec, USEC_PER_SEC));
}
/* The set ioctl does not return the actual value set so get it now. */
r = watchdog_set_timeout();
if (r < 0) {
if (!ERRNO_IS_NOT_SUPPORTED(r))
- return log_error_errno(r, "Failed to set timeout to %s: %m",
+ return log_error_errno(r, "Failed to set watchdog hardware timeout to %s: %m",
FORMAT_TIMESPAN(watchdog_timeout, 0));
- log_info("Modifying watchdog timeout is not supported, reusing the programmed timeout.");
+ log_info("Modifying watchdog hardware timeout is not supported, reusing the programmed timeout.");
watchdog_timeout = USEC_INFINITY;
}
}
r = watchdog_read_timeout();
if (r < 0) {
if (!ERRNO_IS_NOT_SUPPORTED(r))
- return log_error_errno(r, "Failed to query watchdog HW timeout: %m");
- log_info("Reading watchdog timeout is not supported, reusing the configured timeout.");
+ return log_error_errno(r, "Failed to query watchdog hardware timeout: %m");
+ log_info("Reading watchdog hardware timeout is not supported, reusing the configured timeout.");
watchdog_timeout = previous_timeout;
}
}
if (r < 0)
return r;
- log_info("Watchdog running with a timeout of %s.", FORMAT_TIMESPAN(watchdog_timeout, 0));
+ log_info("Watchdog running with a hardware timeout of %s.", FORMAT_TIMESPAN(watchdog_timeout, 0));
return watchdog_ping_now();
}
return r;
}
-static int write_mode(char * const *modes) {
- int r = 0;
+static int write_mode(const char *path, char * const *modes) {
+ int r, ret = 0;
- STRV_FOREACH(mode, modes) {
- int k;
+ assert(path);
- k = write_string_file("/sys/power/disk", *mode, WRITE_STRING_FILE_DISABLE_BUFFER);
- if (k >= 0) {
- log_debug("Using sleep disk mode '%s'.", *mode);
+ STRV_FOREACH(mode, modes) {
+ r = write_string_file(path, *mode, WRITE_STRING_FILE_DISABLE_BUFFER);
+ if (r >= 0) {
+ log_debug("Using sleep mode '%s' for %s.", *mode, path);
return 0;
}
- RET_GATHER(r, log_debug_errno(k, "Failed to write '%s' to /sys/power/disk: %m", *mode));
+ RET_GATHER(ret, log_debug_errno(r, "Failed to write '%s' to %s: %m", *mode, path));
}
- return r;
+ return ret;
}
static int lock_all_homes(void) {
if (state_fd < 0)
return log_error_errno(errno, "Failed to open /sys/power/state: %m");
+ if (SLEEP_NEEDS_MEM_SLEEP(sleep_config, operation)) {
+ r = write_mode("/sys/power/mem_sleep", sleep_config->mem_modes);
+ if (r < 0)
+ return log_error_errno(r, "Failed to write mode to /sys/power/mem_sleep: %m");
+ }
+
/* Configure hibernation settings if we are supposed to hibernate */
- if (sleep_operation_is_hibernation(operation)) {
+ if (SLEEP_OPERATION_IS_HIBERNATION(operation)) {
_cleanup_(hibernation_device_done) HibernationDevice hibernation_device = {};
bool resume_set;
goto fail;
}
- r = write_mode(sleep_config->modes[operation]);
+ r = write_mode("/sys/power/disk", sleep_config->modes[operation]);
if (r < 0) {
log_error_errno(r, "Failed to write mode to /sys/power/disk: %m");
goto fail;
return 0;
fail:
- if (sleep_operation_is_hibernation(operation))
- clear_efi_hibernate_location_and_warn();
+ if (SLEEP_OPERATION_IS_HIBERNATION(operation))
+ (void) clear_efi_hibernate_location_and_warn();
return r;
}
#AllowHybridSleep=yes
#SuspendState=mem standby freeze
#HibernateMode=platform shutdown
+#MemorySleepMode=
#HibernateDelaySec=
#SuspendEstimationSec=60min
int r;
r = read_credential_with_decryption("ssh.listen", (void*) &b, &sz);
- if (r < 0)
+ if (r <= 0)
return r;
- if (r == 0)
- return 0;
_cleanup_fclose_ FILE *f = NULL;
f = fmemopen_unlocked(b, sz, "r");
if (r < 0)
return log_error_errno(r, "Failed to exclude loop devices: %m");
- FOREACH_DEVICE(enumerator, device)
+ FOREACH_DEVICE(enumerator, device) {
+ if (device_is_processed(device) <= 0)
+ continue;
device_added(&context, device);
+ }
}
_cleanup_(sd_netlink_unrefp) sd_netlink *rtnl = NULL;
free(op->work_dir);
strv_free(op->lower_dirs);
- free(op);
- return NULL;
+ return mfree(op);
}
DEFINE_TRIVIAL_CLEANUP_FUNC(OverlayFSPaths *, overlayfs_paths_free);
systemctl_link_with = [libshared]
else
systemctl_link_with = [libsystemd_static,
- libshared_static,
- libbasic_gcrypt]
+ libshared_static]
endif
executables += [
#include "bus-error.h"
#include "bus-locator.h"
#include "login-util.h"
+#include "mountpoint-util.h"
#include "process-util.h"
#include "systemctl-logind.h"
#include "systemctl-start-unit.h"
SET_FLAG(flags,
SD_LOGIND_REBOOT_VIA_KEXEC,
a == ACTION_KEXEC || (a == ACTION_REBOOT && getenv_bool("SYSTEMCTL_SKIP_AUTO_KEXEC") <= 0));
+ /* Try to soft-reboot if /run/nextroot/ is a valid OS tree, but only if it's also a mount point.
+ * Otherwise, if people store new rootfs directly on /run/ tmpfs, 'systemctl reboot' would always
+ * soft-reboot, as /run/nextroot/ can never go away. */
SET_FLAG(flags,
SD_LOGIND_SOFT_REBOOT_IF_NEXTROOT_SET_UP,
- a == ACTION_REBOOT && getenv_bool("SYSTEMCTL_SKIP_AUTO_SOFT_REBOOT") <= 0);
+ a == ACTION_REBOOT && getenv_bool("SYSTEMCTL_SKIP_AUTO_SOFT_REBOOT") <= 0 && path_is_mount_point("/run/nextroot") > 0);
SET_FLAG(flags, SD_LOGIND_SOFT_REBOOT, a == ACTION_SOFT_REBOOT);
r = bus_call_method(bus, bus_login_mgr, method_with_flags, &error, NULL, "t", flags);
'sd-lldp-tx.h',
'sd-lldp.h',
'sd-ndisc.h',
+ 'sd-ndisc-neighbor.h',
'sd-ndisc-protocol.h',
+ 'sd-ndisc-redirect.h',
'sd-ndisc-router.h',
+ 'sd-ndisc-router-solicit.h',
'sd-netlink.h',
'sd-network.h',
'sd-radv.h',
_41, _42, _43, _44, _45, _46, _47, _48, _49, _50, \
NAME, ...) NAME
+#define _SD_VARARGS_FOREACH_EVEN_00(FN)
#define _SD_VARARGS_FOREACH_EVEN_01(FN, X) FN(X)
#define _SD_VARARGS_FOREACH_EVEN_02(FN, X, Y) FN(X)
#define _SD_VARARGS_FOREACH_EVEN_04(FN, X, Y, ...) FN(X) _SD_VARARGS_FOREACH_EVEN_02(FN, __VA_ARGS__)
_SD_VARARGS_FOREACH_EVEN_08, _SD_VARARGS_FOREACH_EVEN_07, \
_SD_VARARGS_FOREACH_EVEN_06, _SD_VARARGS_FOREACH_EVEN_05, \
_SD_VARARGS_FOREACH_EVEN_04, _SD_VARARGS_FOREACH_EVEN_03, \
- _SD_VARARGS_FOREACH_EVEN_02, _SD_VARARGS_FOREACH_EVEN_01) \
+ _SD_VARARGS_FOREACH_EVEN_02, _SD_VARARGS_FOREACH_EVEN_01, \
+ _SD_VARARGS_FOREACH_EVEN_00) \
(FN, __VA_ARGS__)
+#define _SD_VARARGS_FOREACH_ODD_00(FN)
#define _SD_VARARGS_FOREACH_ODD_01(FN, X)
#define _SD_VARARGS_FOREACH_ODD_02(FN, X, Y) FN(Y)
#define _SD_VARARGS_FOREACH_ODD_04(FN, X, Y, ...) FN(Y) _SD_VARARGS_FOREACH_ODD_02(FN, __VA_ARGS__)
_SD_VARARGS_FOREACH_ODD_08, _SD_VARARGS_FOREACH_ODD_07, \
_SD_VARARGS_FOREACH_ODD_06, _SD_VARARGS_FOREACH_ODD_05, \
_SD_VARARGS_FOREACH_ODD_04, _SD_VARARGS_FOREACH_ODD_03, \
- _SD_VARARGS_FOREACH_ODD_02, _SD_VARARGS_FOREACH_ODD_01) \
+ _SD_VARARGS_FOREACH_ODD_02, _SD_VARARGS_FOREACH_ODD_01, \
+ _SD_VARARGS_FOREACH_ODD_00) \
(FN, __VA_ARGS__)
#define SD_BUS_ARGS(...) __VA_ARGS__
--- /dev/null
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+#ifndef foosdndiscneighborfoo
+#define foosdndiscneighborfoo
+
+/***
+ systemd is free software; you can redistribute it and/or modify it
+ under the terms of the GNU Lesser General Public License as published by
+ the Free Software Foundation; either version 2.1 of the License, or
+ (at your option) any later version.
+
+ systemd is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with systemd; If not, see <https://www.gnu.org/licenses/>.
+***/
+
+#include <inttypes.h>
+#include <net/ethernet.h>
+#include <netinet/in.h>
+#include <sys/types.h>
+#include <time.h>
+
+#include "_sd-common.h"
+
+_SD_BEGIN_DECLARATIONS;
+
+typedef struct sd_ndisc_neighbor sd_ndisc_neighbor;
+
+sd_ndisc_neighbor *sd_ndisc_neighbor_ref(sd_ndisc_neighbor *na);
+sd_ndisc_neighbor *sd_ndisc_neighbor_unref(sd_ndisc_neighbor *na);
+_SD_DEFINE_POINTER_CLEANUP_FUNC(sd_ndisc_neighbor, sd_ndisc_neighbor_unref);
+
+int sd_ndisc_neighbor_get_sender_address(sd_ndisc_neighbor *na, struct in6_addr *ret);
+/* RFC 4861 section 4.4:
+ * For solicited advertisements, the Target Address field in the Neighbor Solicitation message that prompted
+ * this advertisement. For an unsolicited advertisement, the address whose link-layer address has changed.
+ * The Target Address MUST NOT be a multicast address. */
+int sd_ndisc_neighbor_get_target_address(sd_ndisc_neighbor *na, struct in6_addr *ret);
+int sd_ndisc_neighbor_get_target_mac(sd_ndisc_neighbor *na, struct ether_addr *ret);
+int sd_ndisc_neighbor_get_flags(sd_ndisc_neighbor *na, uint32_t *ret);
+int sd_ndisc_neighbor_is_router(sd_ndisc_neighbor *na);
+int sd_ndisc_neighbor_is_solicited(sd_ndisc_neighbor *na);
+int sd_ndisc_neighbor_is_override(sd_ndisc_neighbor *na);
+
+_SD_END_DECLARATIONS;
+
+#endif
--- /dev/null
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+#ifndef foosdndiscredirectfoo
+#define foosdndiscredirectfoo
+
+/***
+ systemd is free software; you can redistribute it and/or modify it
+ under the terms of the GNU Lesser General Public License as published by
+ the Free Software Foundation; either version 2.1 of the License, or
+ (at your option) any later version.
+
+ systemd is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with systemd; If not, see <https://www.gnu.org/licenses/>.
+***/
+
+#include <inttypes.h>
+#include <net/ethernet.h>
+#include <netinet/in.h>
+#include <netinet/ip6.h>
+#include <sys/types.h>
+#include <time.h>
+
+#include "_sd-common.h"
+
+_SD_BEGIN_DECLARATIONS;
+
+typedef struct sd_ndisc_redirect sd_ndisc_redirect;
+
+sd_ndisc_redirect* sd_ndisc_redirect_ref(sd_ndisc_redirect *na);
+sd_ndisc_redirect* sd_ndisc_redirect_unref(sd_ndisc_redirect *na);
+_SD_DEFINE_POINTER_CLEANUP_FUNC(sd_ndisc_redirect, sd_ndisc_redirect_unref);
+
+int sd_ndisc_redirect_set_sender_address(sd_ndisc_redirect *rd, const struct in6_addr *addr);
+int sd_ndisc_redirect_get_sender_address(sd_ndisc_redirect *na, struct in6_addr *ret);
+int sd_ndisc_redirect_get_target_address(sd_ndisc_redirect *na, struct in6_addr *ret);
+int sd_ndisc_redirect_get_destination_address(sd_ndisc_redirect *na, struct in6_addr *ret);
+int sd_ndisc_redirect_get_target_mac(sd_ndisc_redirect *na, struct ether_addr *ret);
+int sd_ndisc_redirect_get_redirected_header(sd_ndisc_redirect *na, struct ip6_hdr *ret);
+
+_SD_END_DECLARATIONS;
+
+#endif
--- /dev/null
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+#ifndef foosdndiscroutersolicitfoo
+#define foosdndiscroutersolicitfoo
+
+/***
+ systemd is free software; you can redistribute it and/or modify it
+ under the terms of the GNU Lesser General Public License as published by
+ the Free Software Foundation; either version 2.1 of the License, or
+ (at your option) any later version.
+
+ systemd is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with systemd; If not, see <https://www.gnu.org/licenses/>.
+***/
+
+#include <inttypes.h>
+#include <net/ethernet.h>
+#include <netinet/in.h>
+#include <sys/types.h>
+
+#include "_sd-common.h"
+
+_SD_BEGIN_DECLARATIONS;
+
+typedef struct sd_ndisc_router_solicit sd_ndisc_router_solicit;
+
+sd_ndisc_router_solicit *sd_ndisc_router_solicit_ref(sd_ndisc_router_solicit *rs);
+sd_ndisc_router_solicit *sd_ndisc_router_solicit_unref(sd_ndisc_router_solicit *rs);
+_SD_DEFINE_POINTER_CLEANUP_FUNC(sd_ndisc_router_solicit, sd_ndisc_router_solicit_unref);
+
+int sd_ndisc_router_solicit_get_sender_address(sd_ndisc_router_solicit *rs, struct in6_addr *ret);
+int sd_ndisc_router_solicit_get_sender_mac(sd_ndisc_router_solicit *rs, struct ether_addr *ret);
+
+_SD_END_DECLARATIONS;
+
+#endif
sd_ndisc_router *sd_ndisc_router_unref(sd_ndisc_router *rt);
_SD_DEFINE_POINTER_CLEANUP_FUNC(sd_ndisc_router, sd_ndisc_router_unref);
+int sd_ndisc_router_set_sender_address(sd_ndisc_router *rt, const struct in6_addr *addr);
int sd_ndisc_router_get_sender_address(sd_ndisc_router *rt, struct in6_addr *ret);
int sd_ndisc_router_get_timestamp(sd_ndisc_router *rt, clockid_t clock, uint64_t *ret);
#include <sys/types.h>
#include "sd-event.h"
+#include "sd-ndisc-neighbor.h"
#include "sd-ndisc-protocol.h"
+#include "sd-ndisc-redirect.h"
#include "sd-ndisc-router.h"
#include "_sd-common.h"
__extension__ typedef enum sd_ndisc_event_t {
SD_NDISC_EVENT_TIMEOUT,
SD_NDISC_EVENT_ROUTER,
+ SD_NDISC_EVENT_NEIGHBOR,
+ SD_NDISC_EVENT_REDIRECT,
_SD_NDISC_EVENT_MAX,
_SD_NDISC_EVENT_INVALID = -EINVAL,
_SD_ENUM_FORCE_S64(NDISC_EVENT)
#include "_sd-common.h"
#include "sd-event.h"
-#include "sd-ndisc.h"
+#include "sd-ndisc-protocol.h"
+#include "sd-ndisc-router-solicit.h"
_SD_BEGIN_DECLARATIONS;
"Target path is not a normalized, absolute path: %s", t->target.path);
if (strv_isempty(t->target.patterns)) {
+ log_syntax(NULL, LOG_INFO, path, 1, 0, "Target specification lacks MatchPattern= expression. Assuming same value as in source specification.");
strv_free(t->target.patterns);
t->target.patterns = strv_copy(t->source.patterns);
if (!t->target.patterns)
if (space == 0)
log_info("Making room%s", special_glyph(SPECIAL_GLYPH_ELLIPSIS));
else
- log_info("Making room for %" PRIu64 " updates%s", space,special_glyph(SPECIAL_GLYPH_ELLIPSIS));
+ log_info("Making room for %" PRIu64 " updates%s", space, special_glyph(SPECIAL_GLYPH_ELLIPSIS));
for (size_t i = 0; i < c->n_transfers; i++) {
r = transfer_vacuum(c->transfers[i], space, extra_protected_version);
'c_args' : '-DSTANDALONE',
'link_with' : [
libbasic,
- libbasic_gcrypt,
libshared_static,
libsystemd_static,
],
},
test_template + {
'sources' : files('test-dlopen-so.c'),
- 'dependencies' : libp11kit_cflags
+ 'dependencies' : [
+ libp11kit_cflags,
+ libkmod_cflags,
+ ],
},
test_template + {
# only static linking apart from libdl, to make sure that the
},
test_template + {
'sources' : files('test-netlink-manual.c'),
- 'dependencies' : libkmod,
+ 'dependencies' : libkmod_cflags,
'conditions' : ['HAVE_KMOD'],
'type' : 'manual',
},
#include "fd-util.h"
#include "format-util.h"
#include "fs-util.h"
+#include "path-util.h"
#include "string-util.h"
#include "tests.h"
#include "tmpfile-util.h"
uid_t uid;
int r;
+ FOREACH_STRING(s, "capsh", "getfacl", "ls") {
+ r = find_executable(s, NULL);
+ if (r < 0)
+ return log_tests_skipped_errno(r, "Could not find %s binary: %m", s);
+ }
+
fd = mkostemp_safe(fn);
assert_se(fd >= 0);
return 0;
}
-TEST(fd_acl_make_read_only) {
+TEST_RET(fd_acl_make_read_only) {
_cleanup_(unlink_tempfilep) char fn[] = "/tmp/test-empty.XXXXXX";
_cleanup_close_ int fd = -EBADF;
const char *cmd;
struct stat st;
+ int r;
+
+ FOREACH_STRING(s, "capsh", "getfacl", "ls", "stat") {
+ r = find_executable(s, NULL);
+ if (r < 0)
+ return log_tests_skipped_errno(r, "Could not find %s binary: %m", s);
+ }
fd = mkostemp_safe(fn);
assert_se(fd >= 0);
/* make it more exciting */
(void) fd_add_uid_acl_permission(fd, 1, ACL_READ|ACL_WRITE|ACL_EXECUTE);
- assert_se(fstat(fd, &st) >= 0);
+ ASSERT_OK_ERRNO(fstat(fd, &st));
assert_se(FLAGS_SET(st.st_mode, 0200));
cmd = strjoina("getfacl -p ", fn);
log_info("read-only");
assert_se(fd_acl_make_read_only(fd));
- assert_se(fstat(fd, &st) >= 0);
+ ASSERT_OK_ERRNO(fstat(fd, &st));
assert_se((st.st_mode & 0222) == 0000);
cmd = strjoina("getfacl -p ", fn);
log_info("writable");
assert_se(fd_acl_make_writable(fd));
- assert_se(fstat(fd, &st) >= 0);
+ ASSERT_OK_ERRNO(fstat(fd, &st));
assert_se((st.st_mode & 0222) == 0200);
cmd = strjoina("getfacl -p ", fn);
log_info("read-only");
assert_se(fd_acl_make_read_only(fd));
- assert_se(fstat(fd, &st) >= 0);
+ ASSERT_OK_ERRNO(fstat(fd, &st));
assert_se((st.st_mode & 0222) == 0000);
cmd = strjoina("getfacl -p ", fn);
cmd = strjoina("stat ", fn);
assert_se(system(cmd) == 0);
+
+ return 0;
}
DEFINE_TEST_MAIN(LOG_INFO);
TEST(af_list) {
for (unsigned i = 0; i < ELEMENTSOF(af_names); i++) {
if (af_names[i]) {
- assert_se(streq(af_to_name(i), af_names[i]));
+ ASSERT_STREQ(af_to_name(i), af_names[i]);
assert_se(af_from_name(af_names[i]) == (int) i);
}
}
- assert_se(af_to_name(af_max()) == NULL);
- assert_se(af_to_name(0) == NULL);
- assert_se(af_to_name(-1) == NULL);
+ ASSERT_NULL(af_to_name(af_max()));
+ ASSERT_NULL(af_to_name(0));
+ ASSERT_NULL(af_to_name(-1));
assert_se(af_from_name("huddlduddl") == -EINVAL);
assert_se(af_from_name("") == -EINVAL);
}
test_setup_logging(LOG_INFO);
- assert_se(architecture_from_string("") < 0);
- assert_se(architecture_from_string(NULL) < 0);
- assert_se(architecture_from_string("hoge") < 0);
- assert_se(architecture_to_string(-1) == NULL);
- assert_se(architecture_from_string(architecture_to_string(0)) == 0);
- assert_se(architecture_from_string(architecture_to_string(1)) == 1);
+ ASSERT_LT(architecture_from_string(""), 0);
+ ASSERT_LT(architecture_from_string(NULL), 0);
+ ASSERT_LT(architecture_from_string("hoge"), 0);
+ ASSERT_NULL(architecture_to_string(-1));
+ ASSERT_EQ(architecture_from_string(architecture_to_string(0)), 0);
+ ASSERT_EQ(architecture_from_string(architecture_to_string(1)), 1);
v = detect_virtualization();
if (ERRNO_IS_NEG_PRIVILEGE(v))
return log_tests_skipped("Cannot detect virtualization");
- assert_se(v >= 0);
+ ASSERT_OK(v);
log_info("virtualization=%s id=%s",
VIRTUALIZATION_IS_CONTAINER(v) ? "container" :
virtualization_to_string(v));
a = uname_architecture();
- assert_se(a >= 0);
+ ASSERT_OK(a);
p = architecture_to_string(a);
assert_se(p);
log_info("uname architecture=%s", p);
- assert_se(architecture_from_string(p) == a);
+ ASSERT_EQ(architecture_from_string(p), a);
a = native_architecture();
- assert_se(a >= 0);
+ ASSERT_OK(a);
p = architecture_to_string(a);
assert_se(p);
log_info("native architecture=%s", p);
- assert_se(architecture_from_string(p) == a);
+ ASSERT_EQ(architecture_from_string(p), a);
log_info("primary library architecture=" LIB_ARCH_TUPLE);
/* SPDX-License-Identifier: LGPL-2.1-or-later */
+/* Make sure the net/if.h header is included before any linux/ one */
+#include <net/if.h>
#include <linux/if_arp.h>
#include "arphrd-util.h"
if (name) {
log_info("%i: %s", i, name);
- assert_se(arphrd_from_name(name) == i);
+ ASSERT_EQ(arphrd_from_name(name), i);
}
}
- assert_se(arphrd_to_name(ARPHRD_VOID + 1) == NULL);
+ ASSERT_NULL(arphrd_to_name(ARPHRD_VOID + 1));
assert_se(arphrd_from_name("huddlduddl") == -EINVAL);
assert_se(arphrd_from_name("") == -EINVAL);
}
r = ask_password_tty(-EBADF, &req, /* until= */ 0, /* flags= */ ASK_PASSWORD_CONSOLE_COLOR, /* flag_file= */ NULL, &ret);
if (r == -ECANCELED)
- assert_se(ret == NULL);
+ ASSERT_NULL(ret);
else {
- assert_se(r >= 0);
+ ASSERT_OK(r);
assert_se(strv_length(ret) == 1);
log_info("Got \"%s\"", *ret);
}
#include "tmpfile-util.h"
TEST(asynchronous_sync) {
- assert_se(asynchronous_sync(NULL) >= 0);
+ ASSERT_OK(asynchronous_sync(NULL));
}
TEST(asynchronous_close) {
int fd, r;
fd = mkostemp_safe(name);
- assert_se(fd >= 0);
+ ASSERT_OK(fd);
asynchronous_close(fd);
sleep(1);
- assert_se(fcntl(fd, F_GETFD) == -1);
+ ASSERT_EQ(fcntl(fd, F_GETFD), -1);
assert_se(errno == EBADF);
r = safe_fork("(subreaper)", FORK_RESET_SIGNALS|FORK_CLOSE_ALL_FDS|FORK_DEATHSIG_SIGKILL|FORK_LOG|FORK_WAIT, NULL);
- assert(r >= 0);
+ ASSERT_OK(r);
if (r == 0) {
/* child */
- assert(make_reaper_process(true) >= 0);
+ ASSERT_OK(make_reaper_process(true));
fd = open("/dev/null", O_RDONLY|O_CLOEXEC);
- assert_se(fd >= 0);
+ ASSERT_OK(fd);
asynchronous_close(fd);
sleep(1);
- assert_se(fcntl(fd, F_GETFD) == -1);
+ ASSERT_EQ(fcntl(fd, F_GETFD), -1);
assert_se(errno == EBADF);
_exit(EXIT_SUCCESS);
_cleanup_free_ char *t = NULL, *k = NULL;
int r;
- assert_se(mkdtemp_malloc(NULL, &t) >= 0);
+ ASSERT_OK(mkdtemp_malloc(NULL, &t));
assert_se(k = path_join(t, "somefile"));
- assert_se(touch(k) >= 0);
- assert_se(asynchronous_rm_rf(t, REMOVE_ROOT|REMOVE_PHYSICAL) >= 0);
+ ASSERT_OK(touch(k));
+ ASSERT_OK(asynchronous_rm_rf(t, REMOVE_ROOT|REMOVE_PHYSICAL));
/* Do this once more, form a subreaper. Which is nice, because we can watch the async child even
* though detached */
r = safe_fork("(subreaper)", FORK_RESET_SIGNALS|FORK_CLOSE_ALL_FDS|FORK_DEATHSIG_SIGTERM|FORK_LOG|FORK_WAIT, NULL);
- assert_se(r >= 0);
+ ASSERT_OK(r);
if (r == 0) {
_cleanup_free_ char *tt = NULL, *kk = NULL;
/* child */
- assert_se(sigprocmask_many(SIG_BLOCK, NULL, SIGCHLD) >= 0);
- assert_se(make_reaper_process(true) >= 0);
+ ASSERT_OK(sigprocmask_many(SIG_BLOCK, NULL, SIGCHLD));
+ ASSERT_OK(make_reaper_process(true));
- assert_se(mkdtemp_malloc(NULL, &tt) >= 0);
+ ASSERT_OK(mkdtemp_malloc(NULL, &tt));
assert_se(kk = path_join(tt, "somefile"));
- assert_se(touch(kk) >= 0);
- assert_se(asynchronous_rm_rf(tt, REMOVE_ROOT|REMOVE_PHYSICAL) >= 0);
+ ASSERT_OK(touch(kk));
+ ASSERT_OK(asynchronous_rm_rf(tt, REMOVE_ROOT|REMOVE_PHYSICAL));
for (;;) {
siginfo_t si = {};
- assert_se(waitid(P_ALL, 0, &si, WEXITED) >= 0);
+ ASSERT_OK(waitid(P_ALL, 0, &si, WEXITED));
if (access(tt, F_OK) < 0) {
assert_se(errno == ENOENT);
struct itimerval v = { };
timeval_store(&v.it_value, usecs);
- assert_se(setitimer(ITIMER_REAL, &v, NULL) >= 0);
+ ASSERT_OK(setitimer(ITIMER_REAL, &v, NULL));
}
#define TEST_BARRIER(_FUNCTION, _CHILD_CODE, _WAIT_CHILD, _PARENT_CODE, _WAIT_PARENT) \
Barrier b = BARRIER_NULL; \
pid_t pid1, pid2; \
\
- assert_se(barrier_create(&b) >= 0); \
- assert_se(b.me > 0); \
- assert_se(b.them > 0); \
- assert_se(b.pipe[0] > 0); \
- assert_se(b.pipe[1] > 0); \
+ ASSERT_OK(barrier_create(&b)); \
+ ASSERT_GT(b.me, 0); \
+ ASSERT_GT(b.them, 0); \
+ ASSERT_GT(b.pipe[0], 0); \
+ ASSERT_GT(b.pipe[1], 0); \
\
pid1 = fork(); \
- assert_se(pid1 >= 0); \
+ ASSERT_OK(pid1); \
if (pid1 == 0) { \
barrier_set_role(&b, BARRIER_CHILD); \
{ _CHILD_CODE; } \
} \
\
pid2 = fork(); \
- assert_se(pid2 >= 0); \
+ ASSERT_OK(pid2); \
if (pid2 == 0) { \
barrier_set_role(&b, BARRIER_PARENT); \
{ _PARENT_CODE; } \
({ \
int pidr, status; \
pidr = waitpid(_pid, &status, 0); \
- assert_se(pidr == _pid); \
+ ASSERT_EQ(pidr, _pid); \
assert_se(WIFEXITED(status)); \
- assert_se(WEXITSTATUS(status) == 42); \
+ ASSERT_EQ(WEXITSTATUS(status), 42); \
})
#define TEST_BARRIER_WAIT_ALARM(_pid) \
({ \
int pidr, status; \
pidr = waitpid(_pid, &status, 0); \
- assert_se(pidr == _pid); \
+ ASSERT_EQ(pidr, _pid); \
assert_se(WIFSIGNALED(status)); \
assert_se(WTERMSIG(status) == SIGALRM); \
})
b = bitmap_new();
assert_se(b);
- assert_se(bitmap_ensure_allocated(&b) == 0);
+ ASSERT_EQ(bitmap_ensure_allocated(&b), 0);
b = bitmap_free(b);
- assert_se(bitmap_ensure_allocated(&b) == 0);
+ ASSERT_EQ(bitmap_ensure_allocated(&b), 0);
- assert_se(bitmap_isset(b, 0) == false);
- assert_se(bitmap_isset(b, 1) == false);
- assert_se(bitmap_isset(b, 256) == false);
- assert_se(bitmap_isclear(b) == true);
+ ASSERT_FALSE(bitmap_isset(b, 0));
+ ASSERT_FALSE(bitmap_isset(b, 1));
+ ASSERT_FALSE(bitmap_isset(b, 256));
+ ASSERT_TRUE(bitmap_isclear(b));
- assert_se(bitmap_set(b, 0) == 0);
- assert_se(bitmap_isset(b, 0) == true);
- assert_se(bitmap_isclear(b) == false);
+ ASSERT_EQ(bitmap_set(b, 0), 0);
+ ASSERT_TRUE(bitmap_isset(b, 0));
+ ASSERT_FALSE(bitmap_isclear(b));
bitmap_unset(b, 0);
- assert_se(bitmap_isset(b, 0) == false);
- assert_se(bitmap_isclear(b) == true);
+ ASSERT_FALSE(bitmap_isset(b, 0));
+ ASSERT_TRUE(bitmap_isclear(b));
- assert_se(bitmap_set(b, 1) == 0);
- assert_se(bitmap_isset(b, 1) == true);
- assert_se(bitmap_isclear(b) == false);
+ ASSERT_EQ(bitmap_set(b, 1), 0);
+ ASSERT_TRUE(bitmap_isset(b, 1));
+ ASSERT_FALSE(bitmap_isclear(b));
bitmap_unset(b, 1);
- assert_se(bitmap_isset(b, 1) == false);
- assert_se(bitmap_isclear(b) == true);
+ ASSERT_FALSE(bitmap_isset(b, 1));
+ ASSERT_TRUE(bitmap_isclear(b));
- assert_se(bitmap_set(b, 256) == 0);
- assert_se(bitmap_isset(b, 256) == true);
- assert_se(bitmap_isclear(b) == false);
+ ASSERT_EQ(bitmap_set(b, 256), 0);
+ ASSERT_TRUE(bitmap_isset(b, 256));
+ ASSERT_FALSE(bitmap_isclear(b));
bitmap_unset(b, 256);
- assert_se(bitmap_isset(b, 256) == false);
- assert_se(bitmap_isclear(b) == true);
+ ASSERT_FALSE(bitmap_isset(b, 256));
+ ASSERT_TRUE(bitmap_isclear(b));
- assert_se(bitmap_set(b, 32) == 0);
+ ASSERT_EQ(bitmap_set(b, 32), 0);
bitmap_unset(b, 0);
- assert_se(bitmap_isset(b, 32) == true);
+ ASSERT_TRUE(bitmap_isset(b, 32));
bitmap_unset(b, 32);
BITMAP_FOREACH(n, NULL)
assert_not_reached();
- assert_se(bitmap_set(b, 0) == 0);
- assert_se(bitmap_set(b, 1) == 0);
- assert_se(bitmap_set(b, 256) == 0);
+ ASSERT_EQ(bitmap_set(b, 0), 0);
+ ASSERT_EQ(bitmap_set(b, 1), 0);
+ ASSERT_EQ(bitmap_set(b, 256), 0);
BITMAP_FOREACH(n, b) {
- assert_se(n == i);
+ ASSERT_EQ(n, i);
if (i == 0)
i = 1;
else if (i == 1)
i = UINT_MAX;
}
- assert_se(i == UINT_MAX);
+ ASSERT_EQ(i, UINT_MAX);
i = 0;
BITMAP_FOREACH(n, b) {
- assert_se(n == i);
+ ASSERT_EQ(n, i);
if (i == 0)
i = 1;
else if (i == 1)
i = UINT_MAX;
}
- assert_se(i == UINT_MAX);
+ ASSERT_EQ(i, UINT_MAX);
b2 = bitmap_copy(b);
assert_se(b2);
- assert_se(bitmap_equal(b, b2) == true);
- assert_se(bitmap_equal(b, b) == true);
- assert_se(bitmap_equal(b, NULL) == false);
- assert_se(bitmap_equal(NULL, b) == false);
- assert_se(bitmap_equal(NULL, NULL) == true);
+ ASSERT_TRUE(bitmap_equal(b, b2));
+ ASSERT_TRUE(bitmap_equal(b, b));
+ ASSERT_FALSE(bitmap_equal(b, NULL));
+ ASSERT_FALSE(bitmap_equal(NULL, b));
+ ASSERT_TRUE(bitmap_equal(NULL, NULL));
bitmap_clear(b);
- assert_se(bitmap_isclear(b) == true);
- assert_se(bitmap_equal(b, b2) == false);
+ ASSERT_TRUE(bitmap_isclear(b));
+ ASSERT_FALSE(bitmap_equal(b, b2));
b2 = bitmap_free(b2);
assert_se(bitmap_set(b, UINT_MAX) == -ERANGE);
b = bitmap_free(b);
- assert_se(bitmap_ensure_allocated(&b) == 0);
- assert_se(bitmap_ensure_allocated(&b2) == 0);
+ ASSERT_EQ(bitmap_ensure_allocated(&b), 0);
+ ASSERT_EQ(bitmap_ensure_allocated(&b2), 0);
assert_se(bitmap_equal(b, b2));
- assert_se(bitmap_set(b, 0) == 0);
+ ASSERT_EQ(bitmap_set(b, 0), 0);
bitmap_unset(b, 0);
assert_se(bitmap_equal(b, b2));
- assert_se(bitmap_set(b, 1) == 0);
+ ASSERT_EQ(bitmap_set(b, 1), 0);
bitmap_clear(b);
assert_se(bitmap_equal(b, b2));
- assert_se(bitmap_set(b, 0) == 0);
- assert_se(bitmap_set(b2, 0) == 0);
+ ASSERT_EQ(bitmap_set(b, 0), 0);
+ ASSERT_EQ(bitmap_set(b2, 0), 0);
assert_se(bitmap_equal(b, b2));
return 0;
test_setup_logging(LOG_DEBUG);
p = test_acpi_fpdt();
- assert_se(p >= 0);
+ ASSERT_OK(p);
q = test_efi_loader();
- assert_se(q >= 0);
+ ASSERT_OK(q);
r = test_boot_timestamps();
- assert_se(r >= 0);
+ ASSERT_OK(r);
if (p == 0 && q == 0 && r == 0)
return log_tests_skipped("access to firmware variables not possible");
_cleanup_(rm_rf_physical_and_freep) char *d = NULL;
_cleanup_(boot_config_free) BootConfig config = BOOT_CONFIG_NULL;
- assert_se(mkdtemp_malloc("/tmp/bootspec-testXXXXXX", &d) >= 0);
+ ASSERT_OK(mkdtemp_malloc("/tmp/bootspec-testXXXXXX", &d));
for (size_t i = 0; i < ELEMENTSOF(entries); i++) {
_cleanup_free_ char *j = NULL;
j = path_join(d, "/loader/entries/", entries[i].fname);
assert_se(j);
- assert_se(write_string_file(j, entries[i].contents, WRITE_STRING_FILE_CREATE|WRITE_STRING_FILE_MKDIR_0755) >= 0);
+ ASSERT_OK(write_string_file(j, entries[i].contents, WRITE_STRING_FILE_CREATE|WRITE_STRING_FILE_MKDIR_0755));
}
- assert_se(boot_config_load(&config, d, NULL) >= 0);
+ ASSERT_OK(boot_config_load(&config, d, NULL));
assert_se(config.n_entries == 6);
/* First, because has sort key, and its the lowest one */
- assert_se(streq(config.entries[0].id, "d.conf"));
+ ASSERT_STREQ(config.entries[0].id, "d.conf");
/* These two have a sort key, and newest must be first */
- assert_se(streq(config.entries[1].id, "cx.conf"));
- assert_se(streq(config.entries[2].id, "c.conf"));
+ ASSERT_STREQ(config.entries[1].id, "cx.conf");
+ ASSERT_STREQ(config.entries[2].id, "c.conf");
/* The following ones have no sort key, hence order by version compared ids, lowest first */
- assert_se(streq(config.entries[3].id, "b.conf"));
- assert_se(streq(config.entries[4].id, "a-10.conf"));
- assert_se(streq(config.entries[5].id, "a-5.conf"));
+ ASSERT_STREQ(config.entries[3].id, "b.conf");
+ ASSERT_STREQ(config.entries[4].id, "a-10.conf");
+ ASSERT_STREQ(config.entries[5].id, "a-5.conf");
return 0;
}
if (ret < 0)
return;
- assert_se(streq_ptr(p, stripped));
+ ASSERT_STREQ(p, stripped);
assert_se(l == tries_left);
assert_se(d == tries_done);
}
/* Test finding a non-existent entry */
entry = boot_config_find_entry(&config, "nonexistent.conf");
- assert_se(entry == NULL);
+ ASSERT_NULL(entry);
/* Test case-insensitivity */
entry = boot_config_find_entry(&config, "A-10.CONF");
log_info("/* %s */", __func__);
r = bpf_devices_cgroup_init(&prog, CGROUP_DEVICE_POLICY_CLOSED, true);
- assert_se(r >= 0);
+ ASSERT_OK(r);
r = bpf_devices_allow_list_static(prog, cgroup_path);
- assert_se(r >= 0);
+ ASSERT_OK(r);
r = bpf_devices_apply_policy(&prog, CGROUP_DEVICE_POLICY_CLOSED, true, cgroup_path, installed_prog);
- assert_se(r >= 0);
+ ASSERT_OK(r);
FOREACH_STRING(s, "/dev/null",
"/dev/zero",
log_info("/* %s */", __func__);
r = bpf_devices_cgroup_init(&prog, CGROUP_DEVICE_POLICY_STRICT, true);
- assert_se(r >= 0);
+ ASSERT_OK(r);
r = bpf_devices_allow_list_device(prog, cgroup_path, "/dev/null", CGROUP_DEVICE_READ|CGROUP_DEVICE_WRITE);
- assert_se(r >= 0);
+ ASSERT_OK(r);
r = bpf_devices_allow_list_device(prog, cgroup_path, "/dev/random", CGROUP_DEVICE_READ);
- assert_se(r >= 0);
+ ASSERT_OK(r);
r = bpf_devices_allow_list_device(prog, cgroup_path, "/dev/zero", CGROUP_DEVICE_WRITE);
- assert_se(r >= 0);
+ ASSERT_OK(r);
r = bpf_devices_apply_policy(&prog, CGROUP_DEVICE_POLICY_STRICT, true, cgroup_path, installed_prog);
- assert_se(r >= 0);
+ ASSERT_OK(r);
{
_cleanup_close_ int fd = -EBADF, fd2 = -EBADF;
log_info("/* %s(%s) */", __func__, pattern);
r = bpf_devices_cgroup_init(&prog, CGROUP_DEVICE_POLICY_STRICT, true);
- assert_se(r >= 0);
+ ASSERT_OK(r);
r = bpf_devices_allow_list_major(prog, cgroup_path, pattern, 'c', CGROUP_DEVICE_READ|CGROUP_DEVICE_WRITE);
- assert_se(r >= 0);
+ ASSERT_OK(r);
r = bpf_devices_apply_policy(&prog, CGROUP_DEVICE_POLICY_STRICT, true, cgroup_path, installed_prog);
- assert_se(r >= 0);
+ ASSERT_OK(r);
/* /dev/null, /dev/full have major==1, /dev/tty has major==5 */
{
log_info("/* %s(type=%c) */", __func__, type);
r = bpf_devices_cgroup_init(&prog, CGROUP_DEVICE_POLICY_STRICT, true);
- assert_se(r >= 0);
+ ASSERT_OK(r);
r = bpf_devices_allow_list_major(prog, cgroup_path, "*", type, CGROUP_DEVICE_READ|CGROUP_DEVICE_WRITE);
- assert_se(r >= 0);
+ ASSERT_OK(r);
r = bpf_devices_apply_policy(&prog, CGROUP_DEVICE_POLICY_STRICT, true, cgroup_path, installed_prog);
- assert_se(r >= 0);
+ ASSERT_OK(r);
{
_cleanup_close_ int fd = -EBADF;
log_info("/* %s(add_mismatched=%s) */", __func__, yes_no(add_mismatched));
r = bpf_devices_cgroup_init(&prog, CGROUP_DEVICE_POLICY_STRICT, add_mismatched);
- assert_se(r >= 0);
+ ASSERT_OK(r);
if (add_mismatched) {
r = bpf_devices_allow_list_major(prog, cgroup_path, "foobarxxx", 'c', CGROUP_DEVICE_READ|CGROUP_DEVICE_WRITE);
}
r = bpf_devices_apply_policy(&prog, CGROUP_DEVICE_POLICY_STRICT, false, cgroup_path, installed_prog);
- assert_se(r >= 0);
+ ASSERT_OK(r);
{
_cleanup_close_ int fd = -EBADF;
test_setup_logging(LOG_DEBUG);
- assert_se(getrlimit(RLIMIT_MEMLOCK, &rl) >= 0);
+ ASSERT_OK(getrlimit(RLIMIT_MEMLOCK, &rl));
rl.rlim_cur = rl.rlim_max = MAX(rl.rlim_max, CAN_MEMLOCK_SIZE);
(void) setrlimit(RLIMIT_MEMLOCK, &rl);
r = bpf_devices_supported();
if (r == 0)
return log_tests_skipped("BPF device filter not supported");
- assert_se(r == 1);
+ ASSERT_EQ(r, 1);
r = cg_get_path(SYSTEMD_CGROUP_CONTROLLER, cgroup, NULL, &controller_path);
- assert_se(r >= 0);
+ ASSERT_OK(r);
_cleanup_(bpf_program_freep) BPFProgram *prog = NULL;
test_policy_empty(false, cgroup, &prog);
test_policy_empty(true, cgroup, &prog);
- assert_se(path_extract_directory(cgroup, &parent) >= 0);
+ ASSERT_OK(path_extract_directory(cgroup, &parent));
- assert_se(cg_mask_supported(&supported) >= 0);
+ ASSERT_OK(cg_mask_supported(&supported));
r = cg_attach_everywhere(supported, parent, 0, NULL, NULL);
- assert_se(r >= 0);
+ ASSERT_OK(r);
return 0;
}
if (detect_container() > 0)
return log_tests_skipped("test-bpf-firewall fails inside LXC and Docker containers: https://github.com/systemd/systemd/issues/9666");
- assert_se(getrlimit(RLIMIT_MEMLOCK, &rl) >= 0);
+ ASSERT_OK(getrlimit(RLIMIT_MEMLOCK, &rl));
rl.rlim_cur = rl.rlim_max = MAX(rl.rlim_max, CAN_MEMLOCK_SIZE);
(void) setrlimit(RLIMIT_MEMLOCK, &rl);
return log_tests_skipped("cgroupfs not available");
_cleanup_free_ char *unit_dir = NULL;
- assert_se(get_testdata_dir("units", &unit_dir) >= 0);
- assert_se(set_unit_path(unit_dir) >= 0);
+ ASSERT_OK(get_testdata_dir("units", &unit_dir));
+ ASSERT_OK(set_unit_path(unit_dir));
assert_se(runtime_dir = setup_fake_runtime_dir());
r = bpf_program_new(BPF_PROG_TYPE_CGROUP_SKB, "sd_trivial", &p);
- assert_se(r == 0);
+ ASSERT_EQ(r, 0);
r = bpf_program_add_instructions(p, exit_insn, ELEMENTSOF(exit_insn));
- assert_se(r == 0);
+ ASSERT_EQ(r, 0);
r = bpf_firewall_supported();
if (r == BPF_FIREWALL_UNSUPPORTED)
return log_tests_skipped("BPF firewalling not supported");
- assert_se(r > 0);
+ ASSERT_GT(r, 0);
if (r == BPF_FIREWALL_SUPPORTED_WITH_MULTI) {
log_notice("BPF firewalling with BPF_F_ALLOW_MULTI supported. Yay!");
log_notice("BPF firewalling (though without BPF_F_ALLOW_MULTI) supported. Good.");
r = bpf_program_load_kernel(p, log_buf, ELEMENTSOF(log_buf));
- assert_se(r >= 0);
+ ASSERT_OK(r);
if (test_custom_filter) {
zero(attr);
/* The simple tests succeeded. Now let's try full unit-based use-case. */
- assert_se(manager_new(RUNTIME_SCOPE_USER, MANAGER_TEST_RUN_BASIC, &m) >= 0);
- assert_se(manager_startup(m, NULL, NULL, NULL) >= 0);
+ ASSERT_OK(manager_new(RUNTIME_SCOPE_USER, MANAGER_TEST_RUN_BASIC, &m));
+ ASSERT_OK(manager_startup(m, NULL, NULL, NULL));
assert_se(u = unit_new(m, sizeof(Service)));
- assert_se(unit_add_name(u, "foo.service") == 0);
+ ASSERT_EQ(unit_add_name(u, "foo.service"), 0);
assert_se(cc = unit_get_cgroup_context(u));
u->perpetual = true;
cc->ip_accounting = true;
- assert_se(config_parse_in_addr_prefixes(u->id, "filename", 1, "Service", 1, "IPAddressAllow", 0, "10.0.1.0/24", &cc->ip_address_allow, NULL) == 0);
- assert_se(config_parse_in_addr_prefixes(u->id, "filename", 1, "Service", 1, "IPAddressAllow", 0, "127.0.0.2", &cc->ip_address_allow, NULL) == 0);
- assert_se(config_parse_in_addr_prefixes(u->id, "filename", 1, "Service", 1, "IPAddressDeny", 0, "127.0.0.3", &cc->ip_address_deny, NULL) == 0);
- assert_se(config_parse_in_addr_prefixes(u->id, "filename", 1, "Service", 1, "IPAddressDeny", 0, "10.0.3.2/24", &cc->ip_address_deny, NULL) == 0);
- assert_se(config_parse_in_addr_prefixes(u->id, "filename", 1, "Service", 1, "IPAddressDeny", 0, "127.0.0.1/25", &cc->ip_address_deny, NULL) == 0);
- assert_se(config_parse_in_addr_prefixes(u->id, "filename", 1, "Service", 1, "IPAddressDeny", 0, "127.0.0.4", &cc->ip_address_deny, NULL) == 0);
+ ASSERT_EQ(config_parse_in_addr_prefixes(u->id, "filename", 1, "Service", 1, "IPAddressAllow", 0, "10.0.1.0/24", &cc->ip_address_allow, NULL), 0);
+ ASSERT_EQ(config_parse_in_addr_prefixes(u->id, "filename", 1, "Service", 1, "IPAddressAllow", 0, "127.0.0.2", &cc->ip_address_allow, NULL), 0);
+ ASSERT_EQ(config_parse_in_addr_prefixes(u->id, "filename", 1, "Service", 1, "IPAddressDeny", 0, "127.0.0.3", &cc->ip_address_deny, NULL), 0);
+ ASSERT_EQ(config_parse_in_addr_prefixes(u->id, "filename", 1, "Service", 1, "IPAddressDeny", 0, "10.0.3.2/24", &cc->ip_address_deny, NULL), 0);
+ ASSERT_EQ(config_parse_in_addr_prefixes(u->id, "filename", 1, "Service", 1, "IPAddressDeny", 0, "127.0.0.1/25", &cc->ip_address_deny, NULL), 0);
+ ASSERT_EQ(config_parse_in_addr_prefixes(u->id, "filename", 1, "Service", 1, "IPAddressDeny", 0, "127.0.0.4", &cc->ip_address_deny, NULL), 0);
assert_se(set_size(cc->ip_address_allow) == 2);
assert_se(set_size(cc->ip_address_deny) == 4);
/* The deny list is defined redundantly, let's ensure it will be properly reduced */
- assert_se(in_addr_prefixes_reduce(cc->ip_address_allow) >= 0);
- assert_se(in_addr_prefixes_reduce(cc->ip_address_deny) >= 0);
+ ASSERT_OK(in_addr_prefixes_reduce(cc->ip_address_allow));
+ ASSERT_OK(in_addr_prefixes_reduce(cc->ip_address_deny));
assert_se(set_size(cc->ip_address_allow) == 2);
assert_se(set_size(cc->ip_address_deny) == 2);
log_notice("%s", log_buf);
log_notice("-------");
- assert_se(r >= 0);
+ ASSERT_OK(r);
r = bpf_program_load_kernel(crt->ip_bpf_egress, log_buf, ELEMENTSOF(log_buf));
log_notice("%s", log_buf);
log_notice("-------");
- assert_se(r >= 0);
+ ASSERT_OK(r);
- assert_se(unit_start(u, NULL) >= 0);
+ ASSERT_OK(unit_start(u, NULL));
while (!IN_SET(SERVICE(u)->state, SERVICE_DEAD, SERVICE_FAILED))
assert_se(sd_event_run(m->event, UINT64_MAX) >= 0);
SERVICE(u)->type = SERVICE_ONESHOT;
u->load_state = UNIT_LOADED;
- assert_se(unit_start(u, NULL) >= 0);
+ ASSERT_OK(unit_start(u, NULL));
while (!IN_SET(SERVICE(u)->state, SERVICE_DEAD, SERVICE_FAILED))
assert_se(sd_event_run(m->event, UINT64_MAX) >= 0);
if (getuid() != 0)
return log_tests_skipped("not running as root");
- assert_se(getrlimit(RLIMIT_MEMLOCK, &rl) >= 0);
+ ASSERT_OK(getrlimit(RLIMIT_MEMLOCK, &rl));
rl.rlim_cur = rl.rlim_max = MAX(rl.rlim_max, CAN_MEMLOCK_SIZE);
(void) setrlimit_closest(RLIMIT_MEMLOCK, &rl);
if (r == -ENOMEDIUM)
return log_tests_skipped("cgroupfs not available");
- assert_se(get_testdata_dir("units", &unit_dir) >= 0);
- assert_se(set_unit_path(unit_dir) >= 0);
+ ASSERT_OK(get_testdata_dir("units", &unit_dir));
+ ASSERT_OK(set_unit_path(unit_dir));
assert_se(runtime_dir = setup_fake_runtime_dir());
- assert_se(manager_new(RUNTIME_SCOPE_USER, MANAGER_TEST_RUN_BASIC, &m) >= 0);
- assert_se(manager_startup(m, NULL, NULL, NULL) >= 0);
+ ASSERT_OK(manager_new(RUNTIME_SCOPE_USER, MANAGER_TEST_RUN_BASIC, &m));
+ ASSERT_OK(manager_startup(m, NULL, NULL, NULL));
- assert_se(test_bpf_cgroup_programs(m,
- "single_prog.service", single_prog, ELEMENTSOF(single_prog)) >= 0);
- assert_se(test_bpf_cgroup_programs(m,
+ ASSERT_OK(test_bpf_cgroup_programs(m,
+ "single_prog.service", single_prog, ELEMENTSOF(single_prog)));
+ ASSERT_OK(test_bpf_cgroup_programs(m,
"multi_prog_same_hook.service",
- multi_prog_same_hook, ELEMENTSOF(multi_prog_same_hook)) >= 0);
- assert_se(test_bpf_cgroup_programs(m,
+ multi_prog_same_hook, ELEMENTSOF(multi_prog_same_hook)));
+ ASSERT_OK(test_bpf_cgroup_programs(m,
"same_prog_multi_hook.service",
- same_prog_multi_hook, ELEMENTSOF(same_prog_multi_hook)) >= 0);
- assert_se(test_bpf_cgroup_programs(m,
+ same_prog_multi_hook, ELEMENTSOF(same_prog_multi_hook)));
+ ASSERT_OK(test_bpf_cgroup_programs(m,
"same_prog_multi_option_0.service",
- same_prog_multi_option_0, ELEMENTSOF(same_prog_multi_option_0)) >= 0);
- assert_se(test_bpf_cgroup_programs(m,
+ same_prog_multi_option_0, ELEMENTSOF(same_prog_multi_option_0)));
+ ASSERT_OK(test_bpf_cgroup_programs(m,
"same_prog_multi_option_1.service",
- same_prog_multi_option_1, ELEMENTSOF(same_prog_multi_option_1)) >= 0);
- assert_se(test_bpf_cgroup_programs(m,
+ same_prog_multi_option_1, ELEMENTSOF(same_prog_multi_option_1)));
+ ASSERT_OK(test_bpf_cgroup_programs(m,
"same_prog_same_hook.service",
same_prog_same_hook,
- ELEMENTSOF(same_prog_same_hook)) >= 0);
- assert_se(test_bpf_cgroup_programs(m,
+ ELEMENTSOF(same_prog_same_hook)));
+ ASSERT_OK(test_bpf_cgroup_programs(m,
"path_split_test.service",
path_split_test,
- ELEMENTSOF(path_split_test)) >= 0);
+ ELEMENTSOF(path_split_test)));
return 0;
}
test_setup_logging(LOG_DEBUG);
- assert_se(getrlimit(RLIMIT_MEMLOCK, &rl) >= 0);
+ ASSERT_OK(getrlimit(RLIMIT_MEMLOCK, &rl));
rl.rlim_cur = rl.rlim_max = MAX(rl.rlim_max, CAN_MEMLOCK_SIZE);
(void) setrlimit_closest(RLIMIT_MEMLOCK, &rl);
if (r == -ENOMEDIUM)
return log_tests_skipped("cgroupfs not available");
- assert_se(get_testdata_dir("units", &unit_dir) >= 0);
- assert_se(set_unit_path(unit_dir) >= 0);
+ ASSERT_OK(get_testdata_dir("units", &unit_dir));
+ ASSERT_OK(set_unit_path(unit_dir));
assert_se(runtime_dir = setup_fake_runtime_dir());
- assert_se(manager_new(RUNTIME_SCOPE_SYSTEM, MANAGER_TEST_RUN_BASIC, &m) >= 0);
- assert_se(manager_startup(m, NULL, NULL, NULL) >= 0);
+ ASSERT_OK(manager_new(RUNTIME_SCOPE_SYSTEM, MANAGER_TEST_RUN_BASIC, &m));
+ ASSERT_OK(manager_startup(m, NULL, NULL, NULL));
/* We need to enable access to the filesystem where the binary is so we
* add @common-block */
- assert_se(test_restrict_filesystems(m, "restrict_filesystems_test.service", "/sys/kernel/tracing/printk_formats", STRV_MAKE("@common-block")) < 0);
- assert_se(test_restrict_filesystems(m, "restrict_filesystems_test.service", "/sys/kernel/tracing/printk_formats", STRV_MAKE("tracefs", "@common-block")) >= 0);
- assert_se(test_restrict_filesystems(m, "restrict_filesystems_test.service", "/sys/kernel/tracing/printk_formats", STRV_MAKE("tracefs", "@common-block", "~tracefs")) < 0);
- assert_se(test_restrict_filesystems(m, "restrict_filesystems_test.service", "/sys/kernel/debug/sleep_time", STRV_MAKE("@common-block")) < 0);
- assert_se(test_restrict_filesystems(m, "restrict_filesystems_test.service", "/sys/kernel/debug/sleep_time", STRV_MAKE("debugfs", "@common-block")) >= 0);
- assert_se(test_restrict_filesystems(m, "restrict_filesystems_test.service", "/sys/kernel/debug/sleep_time", STRV_MAKE("~debugfs")) < 0);
+ ASSERT_LT(test_restrict_filesystems(m, "restrict_filesystems_test.service", "/sys/kernel/tracing/printk_formats", STRV_MAKE("@common-block")), 0);
+ ASSERT_OK(test_restrict_filesystems(m, "restrict_filesystems_test.service", "/sys/kernel/tracing/printk_formats", STRV_MAKE("tracefs", "@common-block")));
+ ASSERT_LT(test_restrict_filesystems(m, "restrict_filesystems_test.service", "/sys/kernel/tracing/printk_formats", STRV_MAKE("tracefs", "@common-block", "~tracefs")), 0);
+ ASSERT_LT(test_restrict_filesystems(m, "restrict_filesystems_test.service", "/sys/kernel/debug/sleep_time", STRV_MAKE("@common-block")), 0);
+ ASSERT_OK(test_restrict_filesystems(m, "restrict_filesystems_test.service", "/sys/kernel/debug/sleep_time", STRV_MAKE("debugfs", "@common-block")));
+ ASSERT_LT(test_restrict_filesystems(m, "restrict_filesystems_test.service", "/sys/kernel/debug/sleep_time", STRV_MAKE("~debugfs")), 0);
return 0;
}
r = calendar_spec_from_string(input, &c);
if (r < 0)
log_error_errno(r, "Failed to parse \"%s\": %m", input);
- assert_se(r >= 0);
+ ASSERT_OK(r);
- assert_se(calendar_spec_to_string(c, &p) >= 0);
+ ASSERT_OK(calendar_spec_to_string(c, &p));
log_info("line %d: \"%s\" → \"%s\"%s%s", line, input, p,
!streq(p, output) ? " expected:" : "",
!streq(p, output) ? output : "");
- assert_se(streq(p, output));
+ ASSERT_STREQ(p, output);
u = now(CLOCK_REALTIME);
r = calendar_spec_next_usec(c, u, &u);
log_info("Next: %s", r < 0 ? STRERROR(r) : FORMAT_TIMESTAMP(u));
c = calendar_spec_free(c);
- assert_se(calendar_spec_from_string(p, &c) >= 0);
- assert_se(calendar_spec_to_string(c, &q) >= 0);
+ ASSERT_OK(calendar_spec_from_string(p, &c));
+ ASSERT_OK(calendar_spec_to_string(c, &q));
- assert_se(streq(q, p));
+ ASSERT_STREQ(q, p);
}
#define test_one(input, output) _test_one(__LINE__, input, output)
assert_se(set_unset_env("TZ", new_tz, true) == 0);
tzset();
- assert_se(calendar_spec_from_string(input, &c) >= 0);
+ ASSERT_OK(calendar_spec_from_string(input, &c));
log_info("line %d: \"%s\" new_tz=%s", line, input, strnull(new_tz));
assert_se(format_timestamp_style(buf, sizeof buf, x, TIMESTAMP_US));
log_info("%s", buf);
- assert_se(calendar_spec_from_string(buf, &c) >= 0);
- assert_se(calendar_spec_to_string(c, &t) >= 0);
+ ASSERT_OK(calendar_spec_from_string(buf, &c));
+ ASSERT_OK(calendar_spec_to_string(c, &t));
log_info("%s", t);
- assert_se(parse_timestamp(t, &y) >= 0);
+ ASSERT_OK(parse_timestamp(t, &y));
assert_se(y == x);
}
usec_t n, u, w;
int r;
- assert_se(calendar_spec_from_string("hourly", &c) >= 0);
+ ASSERT_OK(calendar_spec_from_string("hourly", &c));
n = now(CLOCK_REALTIME);
- assert_se((r = calendar_spec_next_usec(c, n, &u)) >= 0);
+ ASSERT_OK((r = calendar_spec_next_usec(c, n, &u)));
log_info("Now: %s (%"PRIu64")", FORMAT_TIMESTAMP_STYLE(n, TIMESTAMP_US), n);
log_info("Next hourly: %s (%"PRIu64")", r < 0 ? STRERROR(r) : FORMAT_TIMESTAMP_STYLE(u, TIMESTAMP_US), u);
static int intro(void) {
/* Tests have hard-coded results that do not expect a specific timezone to be set by the caller */
- assert_se(unsetenv("TZ") >= 0);
+ ASSERT_OK(unsetenv("TZ"));
return EXIT_SUCCESS;
}
assert_se(!CAPABILITY_TO_STRING(-1));
if (capability_list_length() <= 62)
- assert_se(streq(CAPABILITY_TO_STRING(62), "0x3e"));
+ ASSERT_STREQ(CAPABILITY_TO_STRING(62), "0x3e");
assert_se(!CAPABILITY_TO_STRING(64));
for (int i = 0; i < capability_list_length(); i++) {
assert_se(capability_from_name(n) == i);
printf("%s = %i\n", n, i);
- assert_se(streq(CAPABILITY_TO_STRING(i), n));
+ ASSERT_STREQ(CAPABILITY_TO_STRING(i), n);
}
assert_se(capability_from_name("asdfbsd") == -EINVAL);
uint64_t c1, c_masked = c & all_capabilities();
assert_se(capability_set_to_string(c, &t1) == 0);
- assert_se(streq(t1, t));
+ ASSERT_STREQ(t1, t);
assert_se(capability_set_from_string(t1, &c1) > 0);
assert_se(c1 == c_masked);
uint64_t m =
random_u64() % (UINT64_C(1) << (cap_last_cap() + 1));
- assert_se(capability_set_to_string(m, &a) >= 0);
- assert_se(capability_set_to_string_negative(m, &b) >= 0);
+ ASSERT_OK(capability_set_to_string(m, &a));
+ ASSERT_OK(capability_set_to_string_negative(m, &b));
printf("%s (%zu) → ", a, strlen(a));
else
printf("%s (%zu)\n", b, strlen(b));
- assert_se(strlen(b) <= strlen(a));
+ ASSERT_LE(strlen(b), strlen(a));
}
}
r = read_one_line_file("/proc/sys/kernel/cap_last_cap", &content);
if (r == -ENOENT || ERRNO_IS_NEG_PRIVILEGE(r)) /* kernel pre 3.2 or no access */
return;
- assert_se(r >= 0);
+ ASSERT_OK(r);
r = safe_atolu(content, &val);
- assert_se(r >= 0);
+ ASSERT_OK(r);
assert_se(val != 0);
- assert_se(val == cap_last_cap());
+ ASSERT_EQ(val, cap_last_cap());
}
/* verify cap_last_cap() against syscall probing */
}
assert_se(p != 0);
- assert_se(p == cap_last_cap());
+ ASSERT_EQ(p, cap_last_cap());
}
static void fork_test(void (*test_func)(void)) {
show_capabilities();
sock = socket(AF_INET, SOCK_RAW, IPPROTO_UDP);
- assert_se(sock >= 0);
+ ASSERT_OK(sock);
safe_close(sock);
}
int sock;
sock = socket(AF_INET, SOCK_RAW, IPPROTO_UDP);
- assert_se(sock >= 0);
+ ASSERT_OK(sock);
safe_close(sock);
assert_se(drop_privileges(test_uid, test_gid, test_flags) >= 0);
show_capabilities();
sock = socket(AF_INET, SOCK_RAW, IPPROTO_UDP);
- assert_se(sock < 0);
+ ASSERT_LT(sock, 0);
}
static void test_drop_privileges_fail(void) {
assert_se(getuid() == test_uid);
assert_se(getgid() == test_gid);
- assert_se(drop_privileges(test_uid, test_gid, test_flags) < 0);
- assert_se(drop_privileges(0, 0, test_flags) < 0);
+ ASSERT_LT(drop_privileges(test_uid, test_gid, test_flags), 0);
+ ASSERT_LT(drop_privileges(0, 0, test_flags), 0);
}
static void test_drop_privileges(void) {
}
static void test_have_effective_cap(void) {
- assert_se(have_effective_cap(CAP_KILL) > 0);
- assert_se(have_effective_cap(CAP_CHOWN) > 0);
+ ASSERT_GT(have_effective_cap(CAP_KILL), 0);
+ ASSERT_GT(have_effective_cap(CAP_CHOWN), 0);
- assert_se(drop_privileges(test_uid, test_gid, test_flags | (1ULL << CAP_KILL)) >= 0);
+ ASSERT_OK(drop_privileges(test_uid, test_gid, test_flags | (1ULL << CAP_KILL)));
assert_se(getuid() == test_uid);
assert_se(getgid() == test_gid);
- assert_se(have_effective_cap(CAP_KILL) > 0);
+ ASSERT_GT(have_effective_cap(CAP_KILL), 0);
assert_se(have_effective_cap(CAP_CHOWN) == 0);
}
r = read_one_line_file("/proc/sys/kernel/cap_last_cap", &content);
if (r == -ENOENT || ERRNO_IS_NEG_PRIVILEGE(r)) /* kernel pre 3.2 or no access */
return;
- assert_se(r >= 0);
+ ASSERT_OK(r);
- assert_se(safe_atolu(content, &p) >= 0);
+ ASSERT_OK(safe_atolu(content, &p));
/* If caps don't fit into 64-bit anymore, we have a problem, fail the test. */
assert_se(p <= 63);
uint64_t c;
int r;
- assert_se(capability_get_ambient(&c) >= 0);
+ ASSERT_OK(capability_get_ambient(&c));
r = safe_fork("(getambient)", FORK_RESET_SIGNALS|FORK_DEATHSIG_SIGTERM|FORK_WAIT|FORK_LOG, NULL);
- assert_se(r >= 0);
+ ASSERT_OK(r);
if (r == 0) {
int x, y;
static void log_cgroup_mask(CGroupMask got, CGroupMask expected) {
_cleanup_free_ char *e_store = NULL, *g_store = NULL;
- assert_se(cg_mask_to_string(expected, &e_store) >= 0);
+ ASSERT_OK(cg_mask_to_string(expected, &e_store));
log_info("Expected mask: %s", e_store);
- assert_se(cg_mask_to_string(got, &g_store) >= 0);
+ ASSERT_OK(cg_mask_to_string(got, &g_store));
log_info("Got mask: %s", g_store);
}
/* Prepare the manager. */
_cleanup_free_ char *unit_dir = NULL;
- assert_se(get_testdata_dir("units", &unit_dir) >= 0);
- assert_se(set_unit_path(unit_dir) >= 0);
+ ASSERT_OK(get_testdata_dir("units", &unit_dir));
+ ASSERT_OK(set_unit_path(unit_dir));
assert_se(runtime_dir = setup_fake_runtime_dir());
r = manager_new(RUNTIME_SCOPE_USER, MANAGER_TEST_RUN_BASIC, &m);
if (IN_SET(r, -EPERM, -EACCES)) {
assert_se(manager_startup(m, NULL, NULL, NULL) >= 0);
/* Load units and verify hierarchy. */
- assert_se(manager_load_startable_unit_or_warn(m, "parent.slice", NULL, &parent) >= 0);
- assert_se(manager_load_startable_unit_or_warn(m, "son.service", NULL, &son) >= 0);
- assert_se(manager_load_startable_unit_or_warn(m, "daughter.service", NULL, &daughter) >= 0);
- assert_se(manager_load_startable_unit_or_warn(m, "grandchild.service", NULL, &grandchild) >= 0);
- assert_se(manager_load_startable_unit_or_warn(m, "parent-deep.slice", NULL, &parent_deep) >= 0);
- assert_se(manager_load_startable_unit_or_warn(m, "nomem.slice", NULL, &nomem_parent) >= 0);
- assert_se(manager_load_startable_unit_or_warn(m, "nomemleaf.service", NULL, &nomem_leaf) >= 0);
+ ASSERT_OK(manager_load_startable_unit_or_warn(m, "parent.slice", NULL, &parent));
+ ASSERT_OK(manager_load_startable_unit_or_warn(m, "son.service", NULL, &son));
+ ASSERT_OK(manager_load_startable_unit_or_warn(m, "daughter.service", NULL, &daughter));
+ ASSERT_OK(manager_load_startable_unit_or_warn(m, "grandchild.service", NULL, &grandchild));
+ ASSERT_OK(manager_load_startable_unit_or_warn(m, "parent-deep.slice", NULL, &parent_deep));
+ ASSERT_OK(manager_load_startable_unit_or_warn(m, "nomem.slice", NULL, &nomem_parent));
+ ASSERT_OK(manager_load_startable_unit_or_warn(m, "nomemleaf.service", NULL, &nomem_leaf));
assert_se(UNIT_GET_SLICE(son) == parent);
assert_se(UNIT_GET_SLICE(daughter) == parent);
assert_se(UNIT_GET_SLICE(parent_deep) == parent);
_cleanup_free_ char *b = NULL;
assert_se(cg_mask_to_string(mask, &b) >= 0);
- assert_se(streq_ptr(b, t));
+ ASSERT_STREQ(b, t);
}
TEST(cg_mask_to_string) {
}
static void cgroup_device_permissions_test_normalize(const char *a, const char *b) {
- assert_se(streq_ptr(cgroup_device_permissions_to_string(cgroup_device_permissions_from_string(a)), b));
+ ASSERT_STREQ(cgroup_device_permissions_to_string(cgroup_device_permissions_from_string(a)), b);
}
TEST(cgroup_device_permissions) {
_cleanup_free_ char *cmdline = NULL;
log_info("-- %s --", __func__);
- assert_se(proc_cmdline(&cmdline) >= 0);
+ ASSERT_OK(proc_cmdline(&cmdline));
log_info("cmdline: %s", cmdline);
if (header)
(void) system("findmnt -n /sys/fs/cgroup");
}
TEST(is_wanted) {
- assert_se(setenv("SYSTEMD_PROC_CMDLINE",
- "systemd.unified_cgroup_hierarchy", 1) >= 0);
+ ASSERT_OK(setenv("SYSTEMD_PROC_CMDLINE",
+ "systemd.unified_cgroup_hierarchy", 1));
test_is_wanted_print_one(false);
- assert_se(setenv("SYSTEMD_PROC_CMDLINE",
- "systemd.unified_cgroup_hierarchy=0", 1) >= 0);
+ ASSERT_OK(setenv("SYSTEMD_PROC_CMDLINE",
+ "systemd.unified_cgroup_hierarchy=0", 1));
test_is_wanted_print_one(false);
- assert_se(setenv("SYSTEMD_PROC_CMDLINE",
+ ASSERT_OK(setenv("SYSTEMD_PROC_CMDLINE",
"systemd.unified_cgroup_hierarchy=0 "
- "systemd.legacy_systemd_cgroup_controller", 1) >= 0);
+ "systemd.legacy_systemd_cgroup_controller", 1));
test_is_wanted_print_one(false);
- assert_se(setenv("SYSTEMD_PROC_CMDLINE",
+ ASSERT_OK(setenv("SYSTEMD_PROC_CMDLINE",
"systemd.unified_cgroup_hierarchy=0 "
- "systemd.legacy_systemd_cgroup_controller=0", 1) >= 0);
+ "systemd.legacy_systemd_cgroup_controller=0", 1));
test_is_wanted_print_one(false);
/* cgroup_no_v1=all implies unified cgroup hierarchy, unless otherwise
* explicitly specified. */
- assert_se(setenv("SYSTEMD_PROC_CMDLINE",
- "cgroup_no_v1=all", 1) >= 0);
+ ASSERT_OK(setenv("SYSTEMD_PROC_CMDLINE",
+ "cgroup_no_v1=all", 1));
test_is_wanted_print_one(false);
- assert_se(setenv("SYSTEMD_PROC_CMDLINE",
+ ASSERT_OK(setenv("SYSTEMD_PROC_CMDLINE",
"cgroup_no_v1=all "
- "systemd.unified_cgroup_hierarchy=0", 1) >= 0);
+ "systemd.unified_cgroup_hierarchy=0", 1));
test_is_wanted_print_one(false);
}
return log_tests_skipped("cgroupfs not available");
_cleanup_free_ char *unit_dir = NULL;
- assert_se(get_testdata_dir("units", &unit_dir) >= 0);
- assert_se(set_unit_path(unit_dir) >= 0);
+ ASSERT_OK(get_testdata_dir("units", &unit_dir));
+ ASSERT_OK(set_unit_path(unit_dir));
assert_se(runtime_dir = setup_fake_runtime_dir());
r = manager_new(RUNTIME_SCOPE_USER, MANAGER_TEST_RUN_BASIC, &m);
if (IN_SET(r, -EPERM, -EACCES)) {
return log_tests_skipped("cannot create manager");
}
- assert_se(r >= 0);
- assert_se(manager_startup(m, NULL, NULL, NULL) >= 0);
+ ASSERT_OK(r);
+ ASSERT_OK(manager_startup(m, NULL, NULL, NULL));
/* dml.slice has DefaultMemoryLow=50. Beyond that, individual subhierarchies look like this:
*
* │ dml-discard-empty.service │ │ dml-discard-set-ml.service │
* └───────────────────────────┘ └────────────────────────────┘
*/
- assert_se(manager_load_startable_unit_or_warn(m, "dml.slice", NULL, &dml) >= 0);
+ ASSERT_OK(manager_load_startable_unit_or_warn(m, "dml.slice", NULL, &dml));
- assert_se(manager_load_startable_unit_or_warn(m, "dml-passthrough.slice", NULL, &dml_passthrough) >= 0);
+ ASSERT_OK(manager_load_startable_unit_or_warn(m, "dml-passthrough.slice", NULL, &dml_passthrough));
assert_se(UNIT_GET_SLICE(dml_passthrough) == dml);
- assert_se(manager_load_startable_unit_or_warn(m, "dml-passthrough-empty.service", NULL, &dml_passthrough_empty) >= 0);
+ ASSERT_OK(manager_load_startable_unit_or_warn(m, "dml-passthrough-empty.service", NULL, &dml_passthrough_empty));
assert_se(UNIT_GET_SLICE(dml_passthrough_empty) == dml_passthrough);
- assert_se(manager_load_startable_unit_or_warn(m, "dml-passthrough-set-dml.service", NULL, &dml_passthrough_set_dml) >= 0);
+ ASSERT_OK(manager_load_startable_unit_or_warn(m, "dml-passthrough-set-dml.service", NULL, &dml_passthrough_set_dml));
assert_se(UNIT_GET_SLICE(dml_passthrough_set_dml) == dml_passthrough);
- assert_se(manager_load_startable_unit_or_warn(m, "dml-passthrough-set-ml.service", NULL, &dml_passthrough_set_ml) >= 0);
+ ASSERT_OK(manager_load_startable_unit_or_warn(m, "dml-passthrough-set-ml.service", NULL, &dml_passthrough_set_ml));
assert_se(UNIT_GET_SLICE(dml_passthrough_set_ml) == dml_passthrough);
- assert_se(manager_load_startable_unit_or_warn(m, "dml-override.slice", NULL, &dml_override) >= 0);
+ ASSERT_OK(manager_load_startable_unit_or_warn(m, "dml-override.slice", NULL, &dml_override));
assert_se(UNIT_GET_SLICE(dml_override) == dml);
- assert_se(manager_load_startable_unit_or_warn(m, "dml-override-empty.service", NULL, &dml_override_empty) >= 0);
+ ASSERT_OK(manager_load_startable_unit_or_warn(m, "dml-override-empty.service", NULL, &dml_override_empty));
assert_se(UNIT_GET_SLICE(dml_override_empty) == dml_override);
- assert_se(manager_load_startable_unit_or_warn(m, "dml-discard.slice", NULL, &dml_discard) >= 0);
+ ASSERT_OK(manager_load_startable_unit_or_warn(m, "dml-discard.slice", NULL, &dml_discard));
assert_se(UNIT_GET_SLICE(dml_discard) == dml);
- assert_se(manager_load_startable_unit_or_warn(m, "dml-discard-empty.service", NULL, &dml_discard_empty) >= 0);
+ ASSERT_OK(manager_load_startable_unit_or_warn(m, "dml-discard-empty.service", NULL, &dml_discard_empty));
assert_se(UNIT_GET_SLICE(dml_discard_empty) == dml_discard);
- assert_se(manager_load_startable_unit_or_warn(m, "dml-discard-set-ml.service", NULL, &dml_discard_set_ml) >= 0);
+ ASSERT_OK(manager_load_startable_unit_or_warn(m, "dml-discard-set-ml.service", NULL, &dml_discard_set_ml));
assert_se(UNIT_GET_SLICE(dml_discard_set_ml) == dml_discard);
assert_se(root = UNIT_GET_SLICE(dml));
r = cg_path_decode_unit(path, &unit);
printf("%s: %s → %s %d expected %s %d\n", __func__, path, unit, r, strnull(result), code);
assert_se(r == code);
- assert_se(streq_ptr(unit, result));
+ ASSERT_STREQ(unit, result);
}
TEST(path_decode_unit) {
r = cg_path_get_unit(path, &unit);
printf("%s: %s → %s %d expected %s %d\n", __func__, path, unit, r, strnull(result), code);
assert_se(r == code);
- assert_se(streq_ptr(unit, result));
+ ASSERT_STREQ(unit, result);
}
TEST(path_get_unit) {
r = cg_path_get_unit_path(path, &unit_path);
printf("%s: %s → %s %d expected %s %d\n", __func__, path, unit_path, r, strnull(result), code);
assert_se(r == code);
- assert_se(streq_ptr(unit_path, result));
+ ASSERT_STREQ(unit_path, result);
}
TEST(path_get_unit_path) {
r = cg_path_get_user_unit(path, &unit);
printf("%s: %s → %s %d expected %s %d\n", __func__, path, unit, r, strnull(result), code);
assert_se(r == code);
- assert_se(streq_ptr(unit, result));
+ ASSERT_STREQ(unit, result);
}
TEST(path_get_user_unit) {
_cleanup_free_ char *s = NULL;
assert_se(cg_path_get_session(path, &s) == code);
- assert_se(streq_ptr(s, result));
+ ASSERT_STREQ(s, result);
}
TEST(path_get_session) {
_cleanup_free_ char *s = NULL;
assert_se(cg_path_get_slice(path, &s) == code);
- assert_se(streq_ptr(s, result));
+ ASSERT_STREQ(s, result);
}
TEST(path_get_slice) {
_cleanup_free_ char *s = NULL;
assert_se(cg_path_get_user_slice(path, &s) == code);
- assert_se(streq_ptr(s, result));
+ ASSERT_STREQ(s, result);
}
TEST(path_get_user_slice) {
_cleanup_closedir_ DIR *d = NULL;
int r;
- assert_se(proc_dir_open(&d) >= 0);
+ ASSERT_OK(proc_dir_open(&d));
for (;;) {
_cleanup_free_ char *path = NULL, *path_shifted = NULL, *session = NULL, *unit = NULL, *user_unit = NULL, *machine = NULL, *slice = NULL;
assert_se(s);
assert_se(expected);
- assert_se(cg_escape(s, &b) >= 0);
- assert_se(streq(b, expected));
+ ASSERT_OK(cg_escape(s, &b));
+ ASSERT_STREQ(b, expected);
- assert_se(streq(cg_unescape(b), s));
+ ASSERT_STREQ(cg_unescape(b), s);
assert_se(filename_is_valid(b));
assert_se(!cg_needs_escape(s) || b[0] == '_');
log_info("actual: %s / %d", strnull(ret), r);
log_info("expect: %s / %d", strnull(path), error);
assert_se(r == error);
- assert_se(streq_ptr(ret, path));
+ ASSERT_STREQ(ret, path);
}
TEST(slice_to_path) {
static void test_shift_path_one(const char *raw, const char *root, const char *shifted) {
const char *s = NULL;
- assert_se(cg_shift_path(raw, root, &s) >= 0);
- assert_se(streq(s, shifted));
+ ASSERT_OK(cg_shift_path(raw, root, &s));
+ ASSERT_STREQ(s, shifted);
}
TEST(shift_path) {
TEST(mask_supported, .sd_booted = true) {
CGroupMask m;
- assert_se(cg_mask_supported(&m) >= 0);
+ ASSERT_OK(cg_mask_supported(&m));
for (CGroupController c = 0; c < _CGROUP_CONTROLLER_MAX; c++)
printf("'%s' is supported: %s\n",
}
assert_se(r == -ENOENT);
- assert_se(val == NULL);
+ ASSERT_NULL(val);
if (access("/sys/fs/cgroup/init.scope/cpu.stat", R_OK) < 0) {
log_info_errno(errno, "Skipping most of %s, /init.scope/cpu.stat not accessible: %m", __func__);
assert_se(cg_get_keyed_attribute("cpu", "/init.scope", "cpu.stat", STRV_MAKE("no_such_attr"), &val) == -ENXIO);
assert_se(cg_get_keyed_attribute_graceful("cpu", "/init.scope", "cpu.stat", STRV_MAKE("no_such_attr"), &val) == 0);
- assert_se(val == NULL);
+ ASSERT_NULL(val);
assert_se(cg_get_keyed_attribute("cpu", "/init.scope", "cpu.stat", STRV_MAKE("usage_usec"), &val) == 0);
val = mfree(val);
#include "cgroup-setup.h"
#include "cgroup-util.h"
#include "errno-util.h"
+#include "fd-util.h"
#include "path-util.h"
#include "process-util.h"
#include "string-util.h"
char *c, *p;
assert_se(cg_split_spec("foobar:/", &c, &p) == 0);
- assert_se(streq(c, "foobar"));
- assert_se(streq(p, "/"));
+ ASSERT_STREQ(c, "foobar");
+ ASSERT_STREQ(p, "/");
c = mfree(c);
p = mfree(p);
assert_se(cg_split_spec("fo/obar:/", &c, &p) < 0);
assert_se(cg_split_spec("/", &c, &p) >= 0);
- assert_se(c == NULL);
- assert_se(streq(p, "/"));
+ ASSERT_NULL(c);
+ ASSERT_STREQ(p, "/");
p = mfree(p);
assert_se(cg_split_spec("foo", &c, &p) >= 0);
- assert_se(streq(c, "foo"));
- assert_se(p == NULL);
+ ASSERT_STREQ(c, "foo");
+ ASSERT_NULL(p);
c = mfree(c);
}
assert_se(cg_create_and_attach(SYSTEMD_CGROUP_CONTROLLER, test_b, 0) == 0);
assert_se(cg_pid_get_path(SYSTEMD_CGROUP_CONTROLLER, getpid_cached(), &path) == 0);
- assert_se(streq(path, test_b));
+ ASSERT_STREQ(path, test_b);
free(path);
assert_se(cg_attach(SYSTEMD_CGROUP_CONTROLLER, test_a, 0) == 0);
assert_se(cg_rmdir(SYSTEMD_CGROUP_CONTROLLER, test_a) == 0);
}
+TEST(id) {
+ _cleanup_free_ char *p = NULL, *p2 = NULL;
+ _cleanup_close_ int fd = -EBADF, fd2 = -EBADF;
+ uint64_t id, id2;
+ int r;
+
+ r = cg_all_unified();
+ if (r == 0) {
+ log_tests_skipped("skipping cgroupid test, not running in unified mode");
+ return;
+ }
+ if (IN_SET(r, -ENOMEDIUM, -ENOENT)) {
+ log_tests_skipped("cgroupfs is not mounted");
+ return;
+ }
+ assert_se(r > 0);
+
+ fd = cg_path_open(SYSTEMD_CGROUP_CONTROLLER, "/");
+ assert_se(fd >= 0);
+
+ assert_se(fd_get_path(fd, &p) >= 0);
+ assert_se(path_equal(p, "/sys/fs/cgroup"));
+
+ assert_se(cg_fd_get_cgroupid(fd, &id) >= 0);
+
+ fd2 = cg_cgroupid_open(fd, id);
+
+ if (ERRNO_IS_NEG_PRIVILEGE(fd2))
+ log_notice("Skipping open-by-cgroup-id test because lacking privs.");
+ else {
+ assert_se(fd2 >= 0);
+
+ assert_se(fd_get_path(fd2, &p2) >= 0);
+ assert_se(path_equal(p2, "/sys/fs/cgroup"));
+
+ assert_se(cg_fd_get_cgroupid(fd2, &id2) >= 0);
+
+ assert_se(id == id2);
+
+ assert_se(inode_same_at(fd, NULL, fd2, NULL, AT_EMPTY_PATH) > 0);
+ }
+}
+
DEFINE_TEST_MAIN(LOG_DEBUG);
log_debug("/* %s(path=%s, root=%s) */", __func__, path, strnull(root));
assert_se(chase(path, root, CHASE_EXTRACT_FILENAME, &ret1, NULL) > 0);
- assert_se(streq(ret1, expected));
+ ASSERT_STREQ(ret1, expected);
assert_se(chase(path, root, 0, &ret2, NULL) > 0);
- assert_se(chase_extract_filename(ret2, root, &fname) >= 0);
- assert_se(streq(fname, expected));
+ ASSERT_OK(chase_extract_filename(ret2, root, &fname));
+ ASSERT_STREQ(fname, expected);
}
TEST(chase) {
assert_se(mkdtemp(temp));
top = strjoina(temp, "/top");
- assert_se(mkdir(top, 0700) >= 0);
+ ASSERT_OK(mkdir(top, 0700));
p = strjoina(top, "/dot");
if (symlink(".", p) < 0) {
};
p = strjoina(top, "/dotdot");
- assert_se(symlink("..", p) >= 0);
+ ASSERT_OK(symlink("..", p));
p = strjoina(top, "/dotdota");
- assert_se(symlink("../a", p) >= 0);
+ ASSERT_OK(symlink("../a", p));
p = strjoina(temp, "/a");
- assert_se(symlink("b", p) >= 0);
+ ASSERT_OK(symlink("b", p));
p = strjoina(temp, "/b");
- assert_se(symlink("/usr", p) >= 0);
+ ASSERT_OK(symlink("/usr", p));
p = strjoina(temp, "/start");
- assert_se(symlink("top/dot/dotdota", p) >= 0);
+ ASSERT_OK(symlink("top/dot/dotdota", p));
/* Paths that use symlinks underneath the "root" */
assert_se(path_equal(result, qslash));
result = mfree(result);
- assert_se(mkdir(q, 0700) >= 0);
+ ASSERT_OK(mkdir(q, 0700));
r = chase(p, temp, 0, &result, NULL);
assert_se(r > 0);
/* Paths that would "escape" outside of the "root" */
p = strjoina(temp, "/6dots");
- assert_se(symlink("../../..", p) >= 0);
+ ASSERT_OK(symlink("../../..", p));
r = chase(p, temp, 0, &result, NULL);
assert_se(r > 0 && path_equal(result, temp));
result = mfree(result);
p = strjoina(temp, "/6dotsusr");
- assert_se(symlink("../../../usr", p) >= 0);
+ ASSERT_OK(symlink("../../../usr", p));
r = chase(p, temp, 0, &result, NULL);
assert_se(r > 0 && path_equal(result, q));
result = mfree(result);
p = strjoina(temp, "/top/8dotsusr");
- assert_se(symlink("../../../../usr", p) >= 0);
+ ASSERT_OK(symlink("../../../../usr", p));
r = chase(p, temp, 0, &result, NULL);
assert_se(r > 0 && path_equal(result, q));
/* Paths that contain repeated slashes */
p = strjoina(temp, "/slashslash");
- assert_se(symlink("///usr///", p) >= 0);
+ ASSERT_OK(symlink("///usr///", p));
r = chase(p, NULL, 0, &result, NULL);
assert_se(r > 0);
assert_se(path_equal(result, "/usr"));
- assert_se(streq(result, "/usr")); /* we guarantee that we drop redundant slashes */
+ ASSERT_STREQ(result, "/usr"); /* we guarantee that we drop redundant slashes */
result = mfree(result);
r = chase(p, temp, 0, &result, NULL);
if (geteuid() == 0) {
p = strjoina(temp, "/user");
- assert_se(mkdir(p, 0755) >= 0);
- assert_se(chown(p, UID_NOBODY, GID_NOBODY) >= 0);
+ ASSERT_OK(mkdir(p, 0755));
+ ASSERT_OK(chown(p, UID_NOBODY, GID_NOBODY));
q = strjoina(temp, "/user/root");
- assert_se(mkdir(q, 0755) >= 0);
+ ASSERT_OK(mkdir(q, 0755));
p = strjoina(q, "/link");
- assert_se(symlink("/", p) >= 0);
+ ASSERT_OK(symlink("/", p));
/* Fail when user-owned directories contain root-owned subdirectories. */
r = chase(p, temp, CHASE_SAFE, &result, NULL);
r = chase("/../.././//../../etc", NULL, 0, &result, NULL);
assert_se(r > 0);
- assert_se(streq(result, "/etc"));
+ ASSERT_STREQ(result, "/etc");
result = mfree(result);
r = chase("/../.././//../../test-chase.fsldajfl", NULL, CHASE_NONEXISTENT, &result, NULL);
assert_se(r == 0);
- assert_se(streq(result, "/test-chase.fsldajfl"));
+ ASSERT_STREQ(result, "/test-chase.fsldajfl");
result = mfree(result);
r = chase("/../.././//../../etc", "/", CHASE_PREFIX_ROOT, &result, NULL);
assert_se(r > 0);
- assert_se(streq(result, "/etc"));
+ ASSERT_STREQ(result, "/etc");
result = mfree(result);
r = chase("/../.././//../../test-chase.fsldajfl", "/", CHASE_PREFIX_ROOT|CHASE_NONEXISTENT, &result, NULL);
assert_se(r == 0);
- assert_se(streq(result, "/test-chase.fsldajfl"));
+ ASSERT_STREQ(result, "/test-chase.fsldajfl");
result = mfree(result);
r = chase("/etc/machine-id/foo", NULL, 0, &result, NULL);
/* Path that loops back to self */
p = strjoina(temp, "/recursive-symlink");
- assert_se(symlink("recursive-symlink", p) >= 0);
+ ASSERT_OK(symlink("recursive-symlink", p));
r = chase(p, NULL, 0, &result, NULL);
assert_se(r == -ELOOP);
/* Relative paths */
- assert_se(safe_getcwd(&pwd) >= 0);
+ ASSERT_OK(safe_getcwd(&pwd));
- assert_se(chdir(temp) >= 0);
+ ASSERT_OK(chdir(temp));
p = "this/is/a/relative/path";
r = chase(p, NULL, CHASE_NONEXISTENT, &result, NULL);
if (geteuid() == 0) {
p = strjoina(temp, "/priv1");
- assert_se(mkdir(p, 0755) >= 0);
+ ASSERT_OK(mkdir(p, 0755));
q = strjoina(p, "/priv2");
- assert_se(mkdir(q, 0755) >= 0);
+ ASSERT_OK(mkdir(q, 0755));
- assert_se(chase(q, NULL, CHASE_SAFE, NULL, NULL) >= 0);
+ ASSERT_OK(chase(q, NULL, CHASE_SAFE, NULL, NULL));
- assert_se(chown(q, UID_NOBODY, GID_NOBODY) >= 0);
- assert_se(chase(q, NULL, CHASE_SAFE, NULL, NULL) >= 0);
+ ASSERT_OK(chown(q, UID_NOBODY, GID_NOBODY));
+ ASSERT_OK(chase(q, NULL, CHASE_SAFE, NULL, NULL));
- assert_se(chown(p, UID_NOBODY, GID_NOBODY) >= 0);
- assert_se(chase(q, NULL, CHASE_SAFE, NULL, NULL) >= 0);
+ ASSERT_OK(chown(p, UID_NOBODY, GID_NOBODY));
+ ASSERT_OK(chase(q, NULL, CHASE_SAFE, NULL, NULL));
assert_se(chown(q, 0, 0) >= 0);
assert_se(chase(q, NULL, CHASE_SAFE, NULL, NULL) == -ENOLINK);
- assert_se(rmdir(q) >= 0);
- assert_se(symlink("/etc/passwd", q) >= 0);
+ ASSERT_OK(rmdir(q));
+ ASSERT_OK(symlink("/etc/passwd", q));
assert_se(chase(q, NULL, CHASE_SAFE, NULL, NULL) == -ENOLINK);
assert_se(chown(p, 0, 0) >= 0);
- assert_se(chase(q, NULL, CHASE_SAFE, NULL, NULL) >= 0);
+ ASSERT_OK(chase(q, NULL, CHASE_SAFE, NULL, NULL));
}
p = strjoina(temp, "/machine-id-test");
- assert_se(symlink("/usr/../etc/./machine-id", p) >= 0);
+ ASSERT_OK(symlink("/usr/../etc/./machine-id", p));
r = chase(p, NULL, 0, NULL, &pfd);
if (r != -ENOENT && sd_id128_get_machine(NULL) >= 0) {
_cleanup_close_ int fd = -EBADF;
sd_id128_t a, b;
- assert_se(pfd >= 0);
+ ASSERT_OK(pfd);
fd = fd_reopen(pfd, O_RDONLY|O_CLOEXEC);
- assert_se(fd >= 0);
+ ASSERT_OK(fd);
safe_close(pfd);
- assert_se(id128_read_fd(fd, ID128_FORMAT_PLAIN, &a) >= 0);
- assert_se(sd_id128_get_machine(&b) >= 0);
+ ASSERT_OK(id128_read_fd(fd, ID128_FORMAT_PLAIN, &a));
+ ASSERT_OK(sd_id128_get_machine(&b));
assert_se(sd_id128_equal(a, b));
}
q = strjoina(temp, "/symlink");
assert_se(symlink(p, q) >= 0);
r = chase(q, NULL, CHASE_NOFOLLOW, &result, &pfd);
- assert_se(r >= 0);
- assert_se(pfd >= 0);
+ ASSERT_OK(r);
+ ASSERT_OK(pfd);
assert_se(path_equal(result, q));
- assert_se(fstat(pfd, &st) >= 0);
+ ASSERT_OK(fstat(pfd, &st));
assert_se(S_ISLNK(st.st_mode));
result = mfree(result);
pfd = safe_close(pfd);
/* s1 -> s2 -> nonexistent */
q = strjoina(temp, "/s1");
- assert_se(symlink("s2", q) >= 0);
+ ASSERT_OK(symlink("s2", q));
p = strjoina(temp, "/s2");
- assert_se(symlink("nonexistent", p) >= 0);
+ ASSERT_OK(symlink("nonexistent", p));
r = chase(q, NULL, CHASE_NOFOLLOW, &result, &pfd);
- assert_se(r >= 0);
- assert_se(pfd >= 0);
+ ASSERT_OK(r);
+ ASSERT_OK(pfd);
assert_se(path_equal(result, q));
- assert_se(fstat(pfd, &st) >= 0);
+ ASSERT_OK(fstat(pfd, &st));
assert_se(S_ISLNK(st.st_mode));
result = mfree(result);
pfd = safe_close(pfd);
r = chase(p, NULL, CHASE_STEP, &result, NULL);
assert_se(r == 0);
p = strjoina(temp, "/top/dot/dotdota");
- assert_se(streq(p, result));
+ ASSERT_STREQ(p, result);
result = mfree(result);
r = chase(p, NULL, CHASE_STEP, &result, NULL);
assert_se(r == 0);
p = strjoina(temp, "/top/dotdota");
- assert_se(streq(p, result));
+ ASSERT_STREQ(p, result);
result = mfree(result);
r = chase(p, NULL, CHASE_STEP, &result, NULL);
assert_se(r == 0);
p = strjoina(temp, "/top/../a");
- assert_se(streq(p, result));
+ ASSERT_STREQ(p, result);
result = mfree(result);
r = chase(p, NULL, CHASE_STEP, &result, NULL);
assert_se(r == 0);
p = strjoina(temp, "/a");
- assert_se(streq(p, result));
+ ASSERT_STREQ(p, result);
result = mfree(result);
r = chase(p, NULL, CHASE_STEP, &result, NULL);
assert_se(r == 0);
p = strjoina(temp, "/b");
- assert_se(streq(p, result));
+ ASSERT_STREQ(p, result);
result = mfree(result);
r = chase(p, NULL, CHASE_STEP, &result, NULL);
assert_se(r == 0);
- assert_se(streq("/usr", result));
+ ASSERT_STREQ("/usr", result);
result = mfree(result);
r = chase("/usr", NULL, CHASE_STEP, &result, NULL);
assert_se(r > 0);
- assert_se(streq("/usr", result));
+ ASSERT_STREQ("/usr", result);
result = mfree(result);
/* Make sure that symlinks in the "root" path are not resolved, but those below are */
assert_se(chase("top/dot/dot", temp, CHASE_PREFIX_ROOT|CHASE_PROHIBIT_SYMLINKS|CHASE_WARN, NULL, NULL) == -EREMCHG);
cleanup:
- assert_se(rm_rf(temp, REMOVE_ROOT|REMOVE_PHYSICAL) >= 0);
+ ASSERT_OK(rm_rf(temp, REMOVE_ROOT|REMOVE_PHYSICAL));
}
TEST(chaseat) {
struct stat st;
const char *p;
- assert_se((tfd = mkdtemp_open(NULL, 0, &t)) >= 0);
+ ASSERT_OK((tfd = mkdtemp_open(NULL, 0, &t)));
/* Test that AT_FDCWD with CHASE_AT_RESOLVE_IN_ROOT resolves against / and not the current working
* directory. */
- assert_se(symlinkat("/usr", tfd, "abc") >= 0);
+ ASSERT_OK(symlinkat("/usr", tfd, "abc"));
p = strjoina(t, "/abc");
- assert_se(chaseat(AT_FDCWD, p, CHASE_AT_RESOLVE_IN_ROOT, &result, NULL) >= 0);
- assert_se(streq(result, "/usr"));
+ ASSERT_OK(chaseat(AT_FDCWD, p, CHASE_AT_RESOLVE_IN_ROOT, &result, NULL));
+ ASSERT_STREQ(result, "/usr");
result = mfree(result);
/* If the file descriptor points to the root directory, the result will be absolute. */
fd = open("/", O_CLOEXEC | O_DIRECTORY | O_PATH);
- assert_se(fd >= 0);
+ ASSERT_OK(fd);
- assert_se(chaseat(fd, p, 0, &result, NULL) >= 0);
- assert_se(streq(result, "/usr"));
+ ASSERT_OK(chaseat(fd, p, 0, &result, NULL));
+ ASSERT_STREQ(result, "/usr");
result = mfree(result);
- assert_se(chaseat(fd, p, CHASE_AT_RESOLVE_IN_ROOT, &result, NULL) >= 0);
- assert_se(streq(result, "/usr"));
+ ASSERT_OK(chaseat(fd, p, CHASE_AT_RESOLVE_IN_ROOT, &result, NULL));
+ ASSERT_STREQ(result, "/usr");
result = mfree(result);
fd = safe_close(fd);
/* If the file descriptor does not point to the root directory, the result will be relative
* unless the result is outside of the specified file descriptor. */
- assert_se(chaseat(tfd, "abc", 0, &result, NULL) >= 0);
- assert_se(streq(result, "/usr"));
+ ASSERT_OK(chaseat(tfd, "abc", 0, &result, NULL));
+ ASSERT_STREQ(result, "/usr");
result = mfree(result);
- assert_se(chaseat(tfd, "/abc", 0, &result, NULL) >= 0);
- assert_se(streq(result, "/usr"));
+ ASSERT_OK(chaseat(tfd, "/abc", 0, &result, NULL));
+ ASSERT_STREQ(result, "/usr");
result = mfree(result);
assert_se(chaseat(tfd, "abc", CHASE_AT_RESOLVE_IN_ROOT, NULL, NULL) == -ENOENT);
assert_se(chaseat(tfd, "/abc", CHASE_AT_RESOLVE_IN_ROOT, NULL, NULL) == -ENOENT);
- assert_se(chaseat(tfd, "abc", CHASE_AT_RESOLVE_IN_ROOT | CHASE_NONEXISTENT, &result, NULL) >= 0);
- assert_se(streq(result, "usr"));
+ ASSERT_OK(chaseat(tfd, "abc", CHASE_AT_RESOLVE_IN_ROOT | CHASE_NONEXISTENT, &result, NULL));
+ ASSERT_STREQ(result, "usr");
result = mfree(result);
- assert_se(chaseat(tfd, "/abc", CHASE_AT_RESOLVE_IN_ROOT | CHASE_NONEXISTENT, &result, NULL) >= 0);
- assert_se(streq(result, "usr"));
+ ASSERT_OK(chaseat(tfd, "/abc", CHASE_AT_RESOLVE_IN_ROOT | CHASE_NONEXISTENT, &result, NULL));
+ ASSERT_STREQ(result, "usr");
result = mfree(result);
/* Test that absolute path or not are the same when resolving relative to a directory file
* descriptor and that we always get a relative path back. */
- assert_se(fd = openat(tfd, "def", O_CREAT|O_CLOEXEC, 0700) >= 0);
+ ASSERT_OK(fd = openat(tfd, "def", O_CREAT|O_CLOEXEC, 0700));
fd = safe_close(fd);
- assert_se(symlinkat("/def", tfd, "qed") >= 0);
- assert_se(chaseat(tfd, "qed", CHASE_AT_RESOLVE_IN_ROOT, &result, NULL) >= 0);
- assert_se(streq(result, "def"));
+ ASSERT_OK(symlinkat("/def", tfd, "qed"));
+ ASSERT_OK(chaseat(tfd, "qed", CHASE_AT_RESOLVE_IN_ROOT, &result, NULL));
+ ASSERT_STREQ(result, "def");
result = mfree(result);
- assert_se(chaseat(tfd, "/qed", CHASE_AT_RESOLVE_IN_ROOT, &result, NULL) >= 0);
- assert_se(streq(result, "def"));
+ ASSERT_OK(chaseat(tfd, "/qed", CHASE_AT_RESOLVE_IN_ROOT, &result, NULL));
+ ASSERT_STREQ(result, "def");
result = mfree(result);
/* Valid directory file descriptor without CHASE_AT_RESOLVE_IN_ROOT should resolve symlinks against
/* Test CHASE_PARENT */
- assert_se((fd = open_mkdir_at(tfd, "chase", O_CLOEXEC, 0755)) >= 0);
- assert_se(symlinkat("/def", fd, "parent") >= 0);
+ ASSERT_OK((fd = open_mkdir_at(tfd, "chase", O_CLOEXEC, 0755)));
+ ASSERT_OK(symlinkat("/def", fd, "parent"));
fd = safe_close(fd);
/* Make sure that when we chase a symlink parent directory, that we chase the parent directory of the
* symlink target and not the symlink itself. But if we add CHASE_NOFOLLOW, we get the parent
* directory of the symlink itself. */
- assert_se(chaseat(tfd, "chase/parent", CHASE_PARENT|CHASE_AT_RESOLVE_IN_ROOT, &result, &fd) >= 0);
- assert_se(faccessat(fd, "def", F_OK, 0) >= 0);
- assert_se(streq(result, "def"));
+ ASSERT_OK(chaseat(tfd, "chase/parent", CHASE_PARENT|CHASE_AT_RESOLVE_IN_ROOT, &result, &fd));
+ ASSERT_OK(faccessat(fd, "def", F_OK, 0));
+ ASSERT_STREQ(result, "def");
fd = safe_close(fd);
result = mfree(result);
- assert_se(chaseat(tfd, "chase/parent", CHASE_AT_RESOLVE_IN_ROOT|CHASE_PARENT|CHASE_NOFOLLOW, &result, &fd) >= 0);
- assert_se(faccessat(fd, "parent", F_OK, AT_SYMLINK_NOFOLLOW) >= 0);
- assert_se(streq(result, "chase/parent"));
+ ASSERT_OK(chaseat(tfd, "chase/parent", CHASE_AT_RESOLVE_IN_ROOT|CHASE_PARENT|CHASE_NOFOLLOW, &result, &fd));
+ ASSERT_OK(faccessat(fd, "parent", F_OK, AT_SYMLINK_NOFOLLOW));
+ ASSERT_STREQ(result, "chase/parent");
fd = safe_close(fd);
result = mfree(result);
- assert_se(chaseat(tfd, "chase", CHASE_PARENT|CHASE_AT_RESOLVE_IN_ROOT, &result, &fd) >= 0);
- assert_se(faccessat(fd, "chase", F_OK, 0) >= 0);
- assert_se(streq(result, "chase"));
+ ASSERT_OK(chaseat(tfd, "chase", CHASE_PARENT|CHASE_AT_RESOLVE_IN_ROOT, &result, &fd));
+ ASSERT_OK(faccessat(fd, "chase", F_OK, 0));
+ ASSERT_STREQ(result, "chase");
fd = safe_close(fd);
result = mfree(result);
- assert_se(chaseat(tfd, "/", CHASE_PARENT|CHASE_AT_RESOLVE_IN_ROOT, &result, NULL) >= 0);
- assert_se(streq(result, "."));
+ ASSERT_OK(chaseat(tfd, "/", CHASE_PARENT|CHASE_AT_RESOLVE_IN_ROOT, &result, NULL));
+ ASSERT_STREQ(result, ".");
result = mfree(result);
- assert_se(chaseat(tfd, ".", CHASE_PARENT|CHASE_AT_RESOLVE_IN_ROOT, &result, NULL) >= 0);
- assert_se(streq(result, "."));
+ assert_se(chaseat(tfd, ".", CHASE_PARENT|CHASE_AT_RESOLVE_IN_ROOT, &result, NULL));
+ ASSERT_STREQ(result, ".");
result = mfree(result);
/* Test CHASE_MKDIR_0755 */
- assert_se(chaseat(tfd, "m/k/d/i/r", CHASE_MKDIR_0755|CHASE_NONEXISTENT, &result, NULL) >= 0);
- assert_se(faccessat(tfd, "m/k/d/i", F_OK, 0) >= 0);
+ ASSERT_OK(chaseat(tfd, "m/k/d/i/r", CHASE_MKDIR_0755|CHASE_NONEXISTENT, &result, NULL));
+ ASSERT_OK(faccessat(tfd, "m/k/d/i", F_OK, 0));
assert_se(RET_NERRNO(faccessat(tfd, "m/k/d/i/r", F_OK, 0)) == -ENOENT);
- assert_se(streq(result, "m/k/d/i/r"));
+ ASSERT_STREQ(result, "m/k/d/i/r");
result = mfree(result);
- assert_se(chaseat(tfd, "m/../q", CHASE_MKDIR_0755|CHASE_NONEXISTENT, &result, NULL) >= 0);
- assert_se(faccessat(tfd, "m", F_OK, 0) >= 0);
+ ASSERT_OK(chaseat(tfd, "m/../q", CHASE_MKDIR_0755|CHASE_NONEXISTENT, &result, NULL));
+ ASSERT_OK(faccessat(tfd, "m", F_OK, 0));
assert_se(RET_NERRNO(faccessat(tfd, "q", F_OK, 0)) == -ENOENT);
- assert_se(streq(result, "q"));
+ ASSERT_STREQ(result, "q");
result = mfree(result);
assert_se(chaseat(tfd, "i/../p", CHASE_MKDIR_0755|CHASE_NONEXISTENT, NULL, NULL) == -ENOENT);
/* Test CHASE_EXTRACT_FILENAME */
- assert_se(chaseat(tfd, "chase/parent", CHASE_AT_RESOLVE_IN_ROOT|CHASE_PARENT|CHASE_NOFOLLOW|CHASE_EXTRACT_FILENAME, &result, &fd) >= 0);
- assert_se(faccessat(fd, result, F_OK, AT_SYMLINK_NOFOLLOW) >= 0);
- assert_se(streq(result, "parent"));
+ ASSERT_OK(chaseat(tfd, "chase/parent", CHASE_AT_RESOLVE_IN_ROOT|CHASE_PARENT|CHASE_NOFOLLOW|CHASE_EXTRACT_FILENAME, &result, &fd));
+ ASSERT_OK(faccessat(fd, result, F_OK, AT_SYMLINK_NOFOLLOW));
+ ASSERT_STREQ(result, "parent");
fd = safe_close(fd);
result = mfree(result);
- assert_se(chaseat(tfd, "chase", CHASE_PARENT|CHASE_AT_RESOLVE_IN_ROOT|CHASE_EXTRACT_FILENAME, &result, &fd) >= 0);
- assert_se(faccessat(fd, result, F_OK, 0) >= 0);
- assert_se(streq(result, "chase"));
+ ASSERT_OK(chaseat(tfd, "chase", CHASE_PARENT|CHASE_AT_RESOLVE_IN_ROOT|CHASE_EXTRACT_FILENAME, &result, &fd));
+ ASSERT_OK(faccessat(fd, result, F_OK, 0));
+ ASSERT_STREQ(result, "chase");
fd = safe_close(fd);
result = mfree(result);
- assert_se(chaseat(tfd, "/", CHASE_PARENT|CHASE_AT_RESOLVE_IN_ROOT|CHASE_EXTRACT_FILENAME, &result, NULL) >= 0);
- assert_se(streq(result, "."));
+ ASSERT_OK(chaseat(tfd, "/", CHASE_PARENT|CHASE_AT_RESOLVE_IN_ROOT|CHASE_EXTRACT_FILENAME, &result, NULL));
+ ASSERT_STREQ(result, ".");
result = mfree(result);
- assert_se(chaseat(tfd, ".", CHASE_PARENT|CHASE_AT_RESOLVE_IN_ROOT|CHASE_EXTRACT_FILENAME, &result, NULL) >= 0);
- assert_se(streq(result, "."));
+ ASSERT_OK(chaseat(tfd, ".", CHASE_PARENT|CHASE_AT_RESOLVE_IN_ROOT|CHASE_EXTRACT_FILENAME, &result, NULL));
+ ASSERT_STREQ(result, ".");
result = mfree(result);
- assert_se(chaseat(tfd, NULL, CHASE_PARENT|CHASE_AT_RESOLVE_IN_ROOT|CHASE_EXTRACT_FILENAME, &result, NULL) >= 0);
- assert_se(streq(result, "."));
+ ASSERT_OK(chaseat(tfd, NULL, CHASE_PARENT|CHASE_AT_RESOLVE_IN_ROOT|CHASE_EXTRACT_FILENAME, &result, NULL));
+ ASSERT_STREQ(result, ".");
result = mfree(result);
/* Test chase_and_openat() */
fd = chase_and_openat(tfd, "o/p/e/n/f/i/l/e", CHASE_MKDIR_0755, O_CREAT|O_EXCL|O_CLOEXEC, NULL);
- assert_se(fd >= 0);
- assert_se(fd_verify_regular(fd) >= 0);
+ ASSERT_OK(fd);
+ ASSERT_OK(fd_verify_regular(fd));
fd = safe_close(fd);
fd = chase_and_openat(tfd, "o/p/e/n/d/i/r", CHASE_MKDIR_0755, O_DIRECTORY|O_CREAT|O_EXCL|O_CLOEXEC, NULL);
- assert_se(fd >= 0);
- assert_se(fd_verify_directory(fd) >= 0);
+ ASSERT_OK(fd);
+ ASSERT_OK(fd_verify_directory(fd));
fd = safe_close(fd);
fd = chase_and_openat(tfd, NULL, CHASE_PARENT|CHASE_EXTRACT_FILENAME, O_PATH|O_DIRECTORY|O_CLOEXEC, &result);
- assert_se(fd >= 0);
- assert_se(streq(result, "."));
+ ASSERT_OK(fd);
+ ASSERT_STREQ(result, ".");
fd = safe_close(fd);
result = mfree(result);
/* Test chase_and_openatdir() */
- assert_se(chase_and_opendirat(tfd, "o/p/e/n/d/i", 0, &result, &dir) >= 0);
+ ASSERT_OK(chase_and_opendirat(tfd, "o/p/e/n/d/i", 0, &result, &dir));
FOREACH_DIRENT(de, dir, assert_not_reached())
- assert_se(streq(de->d_name, "r"));
- assert_se(streq(result, "o/p/e/n/d/i"));
+ ASSERT_STREQ(de->d_name, "r");
+ ASSERT_STREQ(result, "o/p/e/n/d/i");
result = mfree(result);
/* Test chase_and_statat() */
- assert_se(chase_and_statat(tfd, "o/p", 0, &result, &st) >= 0);
- assert_se(stat_verify_directory(&st) >= 0);
- assert_se(streq(result, "o/p"));
+ ASSERT_OK(chase_and_statat(tfd, "o/p", 0, &result, &st));
+ ASSERT_OK(stat_verify_directory(&st));
+ ASSERT_STREQ(result, "o/p");
result = mfree(result);
/* Test chase_and_accessat() */
- assert_se(chase_and_accessat(tfd, "o/p/e", 0, F_OK, &result) >= 0);
- assert_se(streq(result, "o/p/e"));
+ ASSERT_OK(chase_and_accessat(tfd, "o/p/e", 0, F_OK, &result));
+ ASSERT_STREQ(result, "o/p/e");
result = mfree(result);
/* Test chase_and_fopenat_unlocked() */
- assert_se(chase_and_fopenat_unlocked(tfd, "o/p/e/n/f/i/l/e", 0, "re", &result, &f) >= 0);
+ ASSERT_OK(chase_and_fopenat_unlocked(tfd, "o/p/e/n/f/i/l/e", 0, "re", &result, &f));
assert_se(fread(&(char[1]) {}, 1, 1, f) == 0);
assert_se(feof(f));
f = safe_fclose(f);
- assert_se(streq(result, "o/p/e/n/f/i/l/e"));
+ ASSERT_STREQ(result, "o/p/e/n/f/i/l/e");
result = mfree(result);
/* Test chase_and_unlinkat() */
- assert_se(chase_and_unlinkat(tfd, "o/p/e/n/f/i/l/e", 0, 0, &result) >= 0);
- assert_se(streq(result, "o/p/e/n/f/i/l/e"));
+ ASSERT_OK(chase_and_unlinkat(tfd, "o/p/e/n/f/i/l/e", 0, 0, &result));
+ ASSERT_STREQ(result, "o/p/e/n/f/i/l/e");
result = mfree(result);
/* Test chase_and_open_parent_at() */
- assert_se((fd = chase_and_open_parent_at(tfd, "chase/parent", CHASE_AT_RESOLVE_IN_ROOT|CHASE_NOFOLLOW, &result)) >= 0);
- assert_se(faccessat(fd, result, F_OK, AT_SYMLINK_NOFOLLOW) >= 0);
- assert_se(streq(result, "parent"));
+ ASSERT_OK((fd = chase_and_open_parent_at(tfd, "chase/parent", CHASE_AT_RESOLVE_IN_ROOT|CHASE_NOFOLLOW, &result)));
+ ASSERT_OK(faccessat(fd, result, F_OK, AT_SYMLINK_NOFOLLOW));
+ ASSERT_STREQ(result, "parent");
fd = safe_close(fd);
result = mfree(result);
- assert_se((fd = chase_and_open_parent_at(tfd, "chase", CHASE_AT_RESOLVE_IN_ROOT, &result)) >= 0);
- assert_se(faccessat(fd, result, F_OK, 0) >= 0);
- assert_se(streq(result, "chase"));
+ ASSERT_OK((fd = chase_and_open_parent_at(tfd, "chase", CHASE_AT_RESOLVE_IN_ROOT, &result)));
+ ASSERT_OK(faccessat(fd, result, F_OK, 0));
+ ASSERT_STREQ(result, "chase");
fd = safe_close(fd);
result = mfree(result);
- assert_se((fd = chase_and_open_parent_at(tfd, "/", CHASE_AT_RESOLVE_IN_ROOT, &result)) >= 0);
- assert_se(streq(result, "."));
+ ASSERT_OK((fd = chase_and_open_parent_at(tfd, "/", CHASE_AT_RESOLVE_IN_ROOT, &result)));
+ ASSERT_STREQ(result, ".");
fd = safe_close(fd);
result = mfree(result);
- assert_se((fd = chase_and_open_parent_at(tfd, ".", CHASE_AT_RESOLVE_IN_ROOT, &result)) >= 0);
- assert_se(streq(result, "."));
+ ASSERT_OK((fd = chase_and_open_parent_at(tfd, ".", CHASE_AT_RESOLVE_IN_ROOT, &result)));
+ ASSERT_STREQ(result, ".");
fd = safe_close(fd);
result = mfree(result);
}
TEST(chaseat_prefix_root) {
_cleanup_free_ char *cwd = NULL, *ret = NULL, *expected = NULL;
- assert_se(safe_getcwd(&cwd) >= 0);
+ ASSERT_OK(safe_getcwd(&cwd));
- assert_se(chaseat_prefix_root("/hoge", NULL, &ret) >= 0);
- assert_se(streq(ret, "/hoge"));
+ ASSERT_OK(chaseat_prefix_root("/hoge", NULL, &ret));
+ ASSERT_STREQ(ret, "/hoge");
ret = mfree(ret);
- assert_se(chaseat_prefix_root("/hoge", "a/b/c", &ret) >= 0);
- assert_se(streq(ret, "/hoge"));
+ ASSERT_OK(chaseat_prefix_root("/hoge", "a/b/c", &ret));
+ ASSERT_STREQ(ret, "/hoge");
ret = mfree(ret);
- assert_se(chaseat_prefix_root("hoge", "/a/b//./c///", &ret) >= 0);
- assert_se(streq(ret, "/a/b/c/hoge"));
+ ASSERT_OK(chaseat_prefix_root("hoge", "/a/b//./c///", &ret));
+ ASSERT_STREQ(ret, "/a/b/c/hoge");
ret = mfree(ret);
- assert_se(chaseat_prefix_root("hoge", "a/b//./c///", &ret) >= 0);
+ ASSERT_OK(chaseat_prefix_root("hoge", "a/b//./c///", &ret));
assert_se(expected = path_join(cwd, "a/b/c/hoge"));
- assert_se(streq(ret, expected));
+ ASSERT_STREQ(ret, expected);
ret = mfree(ret);
expected = mfree(expected);
- assert_se(chaseat_prefix_root("./hoge/aaa/../././b", "/a/b//./c///", &ret) >= 0);
- assert_se(streq(ret, "/a/b/c/hoge/aaa/../././b"));
+ ASSERT_OK(chaseat_prefix_root("./hoge/aaa/../././b", "/a/b//./c///", &ret));
+ ASSERT_STREQ(ret, "/a/b/c/hoge/aaa/../././b");
ret = mfree(ret);
assert_se(chaseat_prefix_root("./hoge/aaa/../././b", "a/b//./c///", &ret) >= 0);
assert_se(expected = path_join(cwd, "a/b/c/hoge/aaa/../././b"));
- assert_se(streq(ret, expected));
+ ASSERT_STREQ(ret, expected);
}
TEST(trailing_dot_dot) {
_cleanup_free_ char *path = NULL, *fdpath = NULL;
_cleanup_close_ int fd = -EBADF;
- assert_se(chase("/usr/..", NULL, CHASE_PARENT, &path, &fd) >= 0);
+ ASSERT_OK(chase("/usr/..", NULL, CHASE_PARENT, &path, &fd));
assert_se(path_equal(path, "/"));
- assert_se(fd_get_path(fd, &fdpath) >= 0);
+ ASSERT_OK(fd_get_path(fd, &fdpath));
assert_se(path_equal(fdpath, "/"));
path = mfree(path);
fd = safe_close(fd);
_cleanup_(rm_rf_physical_and_freep) char *t = NULL;
- assert_se(mkdtemp_malloc(NULL, &t) >= 0);
+ ASSERT_OK(mkdtemp_malloc(NULL, &t));
_cleanup_free_ char *sub = ASSERT_PTR(path_join(t, "a/b/c/d"));
- assert_se(mkdir_p(sub, 0700) >= 0);
+ ASSERT_OK(mkdir_p(sub, 0700));
_cleanup_free_ char *suffixed = ASSERT_PTR(path_join(sub, ".."));
- assert_se(chase(suffixed, NULL, CHASE_PARENT, &path, &fd) >= 0);
+ ASSERT_OK(chase(suffixed, NULL, CHASE_PARENT, &path, &fd));
_cleanup_free_ char *expected1 = ASSERT_PTR(path_join(t, "a/b/c"));
_cleanup_free_ char *expected2 = ASSERT_PTR(path_join(t, "a/b"));
assert_se(path_equal(path, expected1));
- assert_se(fd_get_path(fd, &fdpath) >= 0);
+ ASSERT_OK(fd_get_path(fd, &fdpath));
assert_se(path_equal(fdpath, expected2));
}
_cleanup_free_ char *path = NULL;
assert_se(config_parse_path("unit", "filename", 1, "section", 1, "lvalue", 0, rvalue, &path, NULL) >= 0);
- assert_se(streq_ptr(expected, path));
+ ASSERT_STREQ(expected, path);
}
static void test_config_parse_log_level_one(const char *rvalue, int expected) {
switch (i) {
case 0 ... 4:
assert_se(r == 1);
- assert_se(streq(setting1, "1"));
+ ASSERT_STREQ(setting1, "1");
break;
case 5 ... 10:
assert_se(r == 1);
- assert_se(streq(setting1, "1 2 3"));
+ ASSERT_STREQ(setting1, "1 2 3");
break;
case 11:
assert_se(r == 1);
- assert_se(streq(setting1, "1\\\\ \\\\2"));
+ ASSERT_STREQ(setting1, "1\\\\ \\\\2");
break;
case 12:
assert_se(r == 1);
- assert_se(streq(setting1, x1000("ABCD")));
+ ASSERT_STREQ(setting1, x1000("ABCD"));
break;
case 13 ... 14:
assert_se(r == 1);
- assert_se(streq(setting1, x1000("ABCD") " foobar"));
+ ASSERT_STREQ(setting1, x1000("ABCD") " foobar");
break;
case 15 ... 16:
assert_se(r == -ENOBUFS);
- assert_se(setting1 == NULL);
+ ASSERT_NULL(setting1);
break;
case 17:
assert_se(r == 1);
- assert_se(streq(setting1, "2"));
+ ASSERT_STREQ(setting1, "2");
break;
}
}
/* ret_stats_by_path= */ NULL,
/* ret_dropin_files= */ &dropins);
assert_se(r >= 0);
- assert_se(streq_ptr(A, "aaa"));
- assert_se(streq_ptr(B, "bbb"));
- assert_se(streq_ptr(C, "c1"));
- assert_se(streq_ptr(D, "ddd"));
- assert_se(streq_ptr(E, "eee"));
- assert_se(streq_ptr(F, NULL));
+ ASSERT_STREQ(A, "aaa");
+ ASSERT_STREQ(B, "bbb");
+ ASSERT_STREQ(C, "c1");
+ ASSERT_STREQ(D, "ddd");
+ ASSERT_STREQ(E, "eee");
+ ASSERT_STREQ(F, NULL);
A = mfree(A);
B = mfree(B);
/* ret_stats_by_path= */ NULL,
/* ret_dropin_files= */ NULL);
assert_se(r >= 0);
- assert_se(streq_ptr(A, "aaa"));
- assert_se(streq_ptr(B, "bbb"));
- assert_se(streq_ptr(C, "c1"));
- assert_se(streq_ptr(D, "ddd"));
- assert_se(streq_ptr(E, "eee"));
- assert_se(streq_ptr(F, NULL));
+ ASSERT_STREQ(A, "aaa");
+ ASSERT_STREQ(B, "bbb");
+ ASSERT_STREQ(C, "c1");
+ ASSERT_STREQ(D, "ddd");
+ ASSERT_STREQ(E, "eee");
+ ASSERT_STREQ(F, NULL);
}
DEFINE_TEST_MAIN(LOG_INFO);
assert_se(copy_file(fn, fn_copy, 0, 0644, COPY_REFLINK) == 0);
assert_se(read_full_file(fn_copy, &buf, &sz) == 0);
- assert_se(streq(buf, "foo bar bar bar foo\n"));
+ ASSERT_STREQ(buf, "foo bar bar bar foo\n");
assert_se(sz == 20);
}
assert_se(lseek(out_fd, SEEK_SET, 0) == 0);
assert_se(read(out_fd, buf, sizeof buf) == (ssize_t) strlen(text));
- assert_se(streq(buf, text));
+ ASSERT_STREQ(buf, text);
}
TEST(copy_tree) {
assert_se(access(f, F_OK) == 0);
assert_se(read_full_file(f, &buf, &sz) == 0);
- assert_se(streq(buf, "file\n"));
+ ASSERT_STREQ(buf, "file\n");
k = lgetxattr_malloc(f, "user.testxattr", &c);
assert_se(xattr_worked < 0 || ((k >= 0) == !!xattr_worked));
_cleanup_free_ char *d = NULL;
assert_se(base64mem(*p, strlen(*p), &d) >= 0);
- assert_se(streq(d, c));
+ ASSERT_STREQ(d, c);
}
}
assert_se(copy_tree_at(tfd, "from", tfd, "to_1", UID_INVALID, GID_INVALID, 0, NULL, NULL) >= 0);
assert_se(readlinkat_malloc(tfd, "to_1", &p) >= 0);
- assert_se(streq(p, expect));
+ ASSERT_STREQ(p, expect);
p = mfree(p);
assert_se(q = path_join(t, "from"));
assert_se(copy_tree_at(AT_FDCWD, q, tfd, "to_2", UID_INVALID, GID_INVALID, 0, NULL, NULL) >= 0);
assert_se(readlinkat_malloc(tfd, "to_2", &p) >= 0);
- assert_se(streq(p, expect));
+ ASSERT_STREQ(p, expect);
p = mfree(p);
q = mfree(q);
assert_se(fd >= 0);
assert_se(copy_tree_at(fd, NULL, tfd, "to_3", UID_INVALID, GID_INVALID, 0, NULL, NULL) >= 0);
assert_se(readlinkat_malloc(tfd, "to_3", &p) >= 0);
- assert_se(streq(p, expect));
+ ASSERT_STREQ(p, expect);
p = mfree(p);
assert_se(copy_tree_at(fd, "", tfd, "to_4", UID_INVALID, GID_INVALID, 0, NULL, NULL) >= 0);
assert_se(readlinkat_malloc(tfd, "to_4", &p) >= 0);
- assert_se(streq(p, expect));
+ ASSERT_STREQ(p, expect);
p = mfree(p);
fd = safe_close(fd);
}
-TEST(copy_bytes) {
+TEST_RET(copy_bytes) {
_cleanup_close_pair_ int pipefd[2] = EBADF_PAIR;
_cleanup_close_ int infd = -EBADF;
int r, r2;
infd = open("/usr/lib/os-release", O_RDONLY|O_CLOEXEC);
if (infd < 0)
infd = open("/etc/os-release", O_RDONLY|O_CLOEXEC);
- assert_se(infd >= 0);
+ if (infd < 0)
+ return log_tests_skipped_errno(errno, "Could not open /usr/lib/os-release or /etc/os-release: %m");
assert_se(pipe2(pipefd, O_CLOEXEC) == 0);
r = copy_bytes(pipefd[1], infd, 1, 0);
assert_se(r == -EBADF);
+
+ return 0;
}
static void test_copy_bytes_regular_file_one(const char *src, bool try_reflink, uint64_t max_bytes) {
assert_se(read_one_line_file("/proc/version", &a) >= 0);
assert_se(read_one_line_file(f, &b) >= 0);
- assert_se(streq(a, b));
+ ASSERT_STREQ(a, b);
assert_se(!isempty(a));
}
return log_tests_skipped("Filesystem doesn't support hole punching");
assert_se(r >= 0);
- assert_se(fstat(fd, &stat) >= 0);
+ ASSERT_OK_ERRNO(fstat(fd, &stat));
blksz = stat.st_blksize;
buf = alloca_safe(blksz);
memset(buf, 1, blksz);
assert_se(lseek(fd_copy, 2 * blksz, SEEK_DATA) < 0 && errno == ENXIO);
/* Test that the copied file has the correct size. */
- assert_se(fstat(fd_copy, &stat) >= 0);
+ ASSERT_OK_ERRNO(fstat(fd_copy, &stat));
assert_se(stat.st_size == 3 * blksz);
close(fd);
assert_se((fd = openat(tfd, "src", O_CREAT | O_RDWR, 0600)) >= 0);
assert_se((fd_copy = openat(tfd, "dst", O_CREAT | O_WRONLY, 0600)) >= 0);
- assert_se(fstat(fd, &st) >= 0);
+ ASSERT_OK_ERRNO(fstat(fd, &st));
blksz = st.st_blksize;
buf = alloca_safe(blksz);
memset(buf, 1, blksz);
/* Copy to the start of the second hole */
assert_se(copy_bytes(fd, fd_copy, 3 * blksz, COPY_HOLES) >= 0);
- assert_se(fstat(fd_copy, &st) >= 0);
+ ASSERT_OK_ERRNO(fstat(fd_copy, &st));
assert_se(st.st_size == 3 * blksz);
/* Copy to the middle of the second hole */
assert_se(lseek(fd_copy, 0, SEEK_SET) >= 0);
assert_se(ftruncate(fd_copy, 0) >= 0);
assert_se(copy_bytes(fd, fd_copy, 4 * blksz, COPY_HOLES) >= 0);
- assert_se(fstat(fd_copy, &st) >= 0);
+ ASSERT_OK_ERRNO(fstat(fd_copy, &st));
assert_se(st.st_size == 4 * blksz);
/* Copy to the end of the second hole */
assert_se(lseek(fd_copy, 0, SEEK_SET) >= 0);
assert_se(ftruncate(fd_copy, 0) >= 0);
assert_se(copy_bytes(fd, fd_copy, 5 * blksz, COPY_HOLES) >= 0);
- assert_se(fstat(fd_copy, &st) >= 0);
+ ASSERT_OK_ERRNO(fstat(fd_copy, &st));
assert_se(st.st_size == 5 * blksz);
/* Copy everything */
assert_se(lseek(fd_copy, 0, SEEK_SET) >= 0);
assert_se(ftruncate(fd_copy, 0) >= 0);
assert_se(copy_bytes(fd, fd_copy, UINT64_MAX, COPY_HOLES) >= 0);
- assert_se(fstat(fd_copy, &st) >= 0);
+ ASSERT_OK_ERRNO(fstat(fd_copy, &st));
assert_se(st.st_size == 6 * blksz);
return 0;
assert_se(t = unit_escape_setting(s, 0, &a));
assert_se(a_esc = cescape(t));
log_debug("%s: [%s] → [%s]", __func__, s_esc, a_esc);
- assert_se(a == NULL);
+ ASSERT_NULL(a);
assert_se(t == s);
assert_se(t = unit_escape_setting(s, UNIT_ESCAPE_EXEC_SYNTAX_ENV, &b));
assert_se(b_esc = cescape(t));
log_debug("%s: [%s] → [%s]", __func__, s_esc, b_esc);
assert_se(b == NULL || streq(b, t));
- assert_se(streq(t, expected_exec_env));
+ ASSERT_STREQ(t, expected_exec_env);
assert_se(t = unit_escape_setting(s, UNIT_ESCAPE_EXEC_SYNTAX, &c));
assert_se(c_esc = cescape(t));
log_debug("%s: [%s] → [%s]", __func__, s_esc, c_esc);
assert_se(c == NULL || streq(c, t));
- assert_se(streq(t, expected_exec));
+ ASSERT_STREQ(t, expected_exec);
assert_se(t = unit_escape_setting(s, UNIT_ESCAPE_C, &d));
assert_se(d_esc = cescape(t));
log_debug("%s: [%s] → [%s]", __func__, s_esc, d_esc);
assert_se(d == NULL || streq(d, t));
- assert_se(streq(t, expected_c));
+ ASSERT_STREQ(t, expected_c);
}
TEST(unit_escape_setting) {
assert_se(a = unit_concat_strv(s, 0));
assert_se(a_esc = cescape(a));
log_debug("%s: [%s] → [%s]", __func__, s_esc, a_esc);
- assert_se(streq(a, expected_none));
+ ASSERT_STREQ(a, expected_none);
assert_se(b = unit_concat_strv(s, UNIT_ESCAPE_EXEC_SYNTAX_ENV));
assert_se(b_esc = cescape(b));
log_debug("%s: [%s] → [%s]", __func__, s_esc, b_esc);
- assert_se(streq(b, expected_exec_env));
+ ASSERT_STREQ(b, expected_exec_env);
assert_se(c = unit_concat_strv(s, UNIT_ESCAPE_EXEC_SYNTAX));
assert_se(c_esc = cescape(c));
log_debug("%s: [%s] → [%s]", __func__, s_esc, c_esc);
- assert_se(streq(c, expected_exec));
+ ASSERT_STREQ(c, expected_exec);
assert_se(d = unit_concat_strv(s, UNIT_ESCAPE_C));
assert_se(d_esc = cescape(d));
log_debug("%s: [%s] → [%s]", __func__, s_esc, d_esc);
- assert_se(streq(d, expected_c));
+ ASSERT_STREQ(d, expected_c);
}
TEST(unit_concat_strv) {
str = mfree(str);
assert_se(str = cpu_set_to_range_string(&c));
log_info("cpu_set_to_range_string: %s", str);
- assert_se(streq(str, "0"));
+ ASSERT_STREQ(str, "0");
str = mfree(str);
assert_se(str = cpu_set_to_mask_string(&c));
log_info("cpu_set_to_mask_string: %s", str);
- assert_se(streq(str, "1"));
+ ASSERT_STREQ(str, "1");
str = mfree(str);
cpu_set_reset(&c);
str = mfree(str);
assert_se(str = cpu_set_to_range_string(&c));
log_info("cpu_set_to_range_string: %s", str);
- assert_se(streq(str, "1-2 4"));
+ ASSERT_STREQ(str, "1-2 4");
str = mfree(str);
assert_se(str = cpu_set_to_mask_string(&c));
log_info("cpu_set_to_mask_string: %s", str);
- assert_se(streq(str, "16"));
+ ASSERT_STREQ(str, "16");
str = mfree(str);
cpu_set_reset(&c);
str = mfree(str);
assert_se(str = cpu_set_to_range_string(&c));
log_info("cpu_set_to_range_string: %s", str);
- assert_se(streq(str, "0-3 8-11"));
+ ASSERT_STREQ(str, "0-3 8-11");
str = mfree(str);
assert_se(str = cpu_set_to_mask_string(&c));
log_info("cpu_set_to_mask_string: %s", str);
- assert_se(streq(str, "f0f"));
+ ASSERT_STREQ(str, "f0f");
str = mfree(str);
cpu_set_reset(&c);
str = mfree(str);
assert_se(str = cpu_set_to_range_string(&c));
log_info("cpu_set_to_range_string: %s", str);
- assert_se(streq(str, "8-11"));
+ ASSERT_STREQ(str, "8-11");
str = mfree(str);
assert_se(str = cpu_set_to_mask_string(&c));
log_info("cpu_set_to_mask_string: %s", str);
- assert_se(streq(str, "f00"));
+ ASSERT_STREQ(str, "f00");
str = mfree(str);
cpu_set_reset(&c);
str = mfree(str);
assert_se(str = cpu_set_to_range_string(&c));
log_info("cpu_set_to_range_string: %s", str);
- assert_se(streq(str, "0-7 63"));
+ ASSERT_STREQ(str, "0-7 63");
str = mfree(str);
assert_se(str = cpu_set_to_mask_string(&c));
log_info("cpu_set_to_mask_string: %s", str);
- assert_se(streq(str, "80000000,000000ff"));
+ ASSERT_STREQ(str, "80000000,000000ff");
str = mfree(str);
cpu_set_reset(&c);
assert_se(CPU_ISSET_S(cpu, c.allocated, c.set));
assert_se(str = cpu_set_to_mask_string(&c));
log_info("cpu_set_to_mask_string: %s", str);
- assert_se(streq(str, "f0f0,00000000"));
+ ASSERT_STREQ(str, "f0f0,00000000");
str = mfree(str);
cpu_set_reset(&c);
assert_se(parse_cpu_set_full("64-71", &c, true, NULL, "fake", 1, "CPUAffinity") >= 0);
assert_se(CPU_ISSET_S(cpu, c.allocated, c.set));
assert_se(str = cpu_set_to_mask_string(&c));
log_info("cpu_set_to_mask_string: %s", str);
- assert_se(streq(str, "ff,00000000,00000000"));
+ ASSERT_STREQ(str, "ff,00000000,00000000");
str = mfree(str);
cpu_set_reset(&c);
str = mfree(str);
assert_se(str = cpu_set_to_range_string(&c));
log_info("cpu_set_to_range_string: %s", str);
- assert_se(streq(str, "0-3 8-11"));
+ ASSERT_STREQ(str, "0-3 8-11");
str = mfree(str);
assert_se(str = cpu_set_to_mask_string(&c));
log_info("cpu_set_to_mask_string: %s", str);
- assert_se(streq(str, "f0f"));
+ ASSERT_STREQ(str, "f0f");
str = mfree(str);
cpu_set_reset(&c);
assert_se(CPU_COUNT_S(c.allocated, c.set) == 0);
assert_se(str = cpu_set_to_mask_string(&c));
log_info("cpu_set_to_mask_string: %s", str);
- assert_se(streq(str, "0"));
+ ASSERT_STREQ(str, "0");
str = mfree(str);
cpu_set_reset(&c);
str = mfree(str);
assert_se(str = cpu_set_to_range_string(&c));
log_info("cpu_set_to_range_string: %s", str);
- assert_se(streq(str, "0-11"));
+ ASSERT_STREQ(str, "0-11");
str = mfree(str);
assert_se(str = cpu_set_to_mask_string(&c));
log_info("cpu_set_to_mask_string: %s", str);
- assert_se(streq(str, "fff"));
+ ASSERT_STREQ(str, "fff");
str = mfree(str);
cpu_set_reset(&c);
str = mfree(str);
assert_se(str = cpu_set_to_range_string(&c));
log_info("cpu_set_to_range_string: %s", str);
- assert_se(streq(str, "0 2 4-11"));
+ ASSERT_STREQ(str, "0 2 4-11");
str = mfree(str);
assert_se(str = cpu_set_to_mask_string(&c));
log_info("cpu_set_to_mask_string: %s", str);
- assert_se(streq(str, "ff5"));
+ ASSERT_STREQ(str, "ff5");
str = mfree(str);
cpu_set_reset(&c);
assert_se(c.allocated == 0);
assert_se(str = cpu_set_to_mask_string(&c));
log_info("cpu_set_to_mask_string: %s", str);
- assert_se(streq(str, "0"));
+ ASSERT_STREQ(str, "0");
str = mfree(str);
/* Runaway quoted string */
str = mfree(str);
assert_se(str = cpu_set_to_range_string(&c));
log_info("cpu_set_to_range_string: %s", str);
- assert_se(streq(str, "8000-8191"));
+ ASSERT_STREQ(str, "8000-8191");
str = mfree(str);
assert_se(str = cpu_set_to_mask_string(&c));
log_info("cpu_set_to_mask_string: %s", str);
assert_se(saved = strdup(e));
assert_se(read_credential_strings_many("foo", &x, "bar", &y) == 0);
- assert_se(x == NULL);
- assert_se(y == NULL);
+ ASSERT_NULL(x);
+ ASSERT_NULL(y);
assert_se(mkdtemp_malloc(NULL, &tmp) >= 0);
assert_se(setenv("CREDENTIALS_DIRECTORY", tmp, /* override= */ true) >= 0);
assert_se(read_credential_strings_many("foo", &x, "bar", &y) == 0);
- assert_se(x == NULL);
- assert_se(y == NULL);
+ ASSERT_NULL(x);
+ ASSERT_NULL(y);
assert_se(p = path_join(tmp, "bar"));
assert_se(write_string_file(p, "piff", WRITE_STRING_FILE_CREATE|WRITE_STRING_FILE_AVOID_NEWLINE) >= 0);
assert_se(read_credential_strings_many("foo", &x, "bar", &y) == 0);
- assert_se(x == NULL);
- assert_se(streq(y, "piff"));
+ ASSERT_NULL(x);
+ ASSERT_STREQ(y, "piff");
assert_se(write_string_file(p, "paff", WRITE_STRING_FILE_TRUNCATE|WRITE_STRING_FILE_AVOID_NEWLINE) >= 0);
assert_se(read_credential_strings_many("foo", &x, "bar", &y) == 0);
- assert_se(x == NULL);
- assert_se(streq(y, "paff"));
+ ASSERT_NULL(x);
+ ASSERT_STREQ(y, "paff");
p = mfree(p);
assert_se(p = path_join(tmp, "foo"));
assert_se(write_string_file(p, "knurz", WRITE_STRING_FILE_CREATE|WRITE_STRING_FILE_AVOID_NEWLINE) >= 0);
assert_se(read_credential_strings_many("foo", &x, "bar", &y) >= 0);
- assert_se(streq(x, "knurz"));
- assert_se(streq(y, "paff"));
+ ASSERT_STREQ(x, "knurz");
+ ASSERT_STREQ(y, "paff");
p = mfree(p);
assert_se(p = path_join(tmp, "bazz"));
y = mfree(y);
assert_se(read_credential_strings_many("bazz", &x, "bar", &y) == -EBADMSG);
- assert_se(streq(x, "knurz"));
- assert_se(streq(y, "paff"));
+ ASSERT_STREQ(x, "knurz");
+ ASSERT_STREQ(y, "paff");
if (saved)
assert_se(setenv("CREDENTIALS_DIRECTORY", saved, /* override= */ 1) >= 0);
OPENSSL_OR_GCRYPT("SHA224", GCRY_MD_SHA224),
&out1) == 0);
/* echo -n 'asdf' | sha224sum - */
- assert_se(streq(out1, "7872a74bcbf298a1e77d507cd95d4f8d96131cbbd4cdfc571e776c8a"));
+ ASSERT_STREQ(out1, "7872a74bcbf298a1e77d507cd95d4f8d96131cbbd4cdfc571e776c8a");
assert_se(string_hashsum("asdf", 4,
OPENSSL_OR_GCRYPT("SHA256", GCRY_MD_SHA256),
&out2) == 0);
/* echo -n 'asdf' | sha256sum - */
- assert_se(streq(out2, "f0e4c2f76c58916ec258f246851bea091d14d4247a2fc3e18694461b1816e13b"));
+ ASSERT_STREQ(out2, "f0e4c2f76c58916ec258f246851bea091d14d4247a2fc3e18694461b1816e13b");
assert_se(string_hashsum("", 0,
OPENSSL_OR_GCRYPT("SHA224", GCRY_MD_SHA224),
&out3) == 0);
/* echo -n '' | sha224sum - */
- assert_se(streq(out3, "d14a028c2a3a2bc9476102bb288234c415a2b01f828ea62ac5b3e42f"));
+ ASSERT_STREQ(out3, "d14a028c2a3a2bc9476102bb288234c415a2b01f828ea62ac5b3e42f");
assert_se(string_hashsum("", 0,
OPENSSL_OR_GCRYPT("SHA256", GCRY_MD_SHA256),
&out4) == 0);
/* echo -n '' | sha256sum - */
- assert_se(streq(out4, "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855"));
+ ASSERT_STREQ(out4, "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855");
}
DEFINE_TEST_MAIN(LOG_INFO);
zero(rbuffer);
assert_se(read(fd, rbuffer, sizeof(rbuffer)) == 3);
- assert_se(streq(rbuffer, "foo"));
+ ASSERT_STREQ(rbuffer, "foo");
fd = safe_close(fd);
zero(rbuffer);
assert_se(read(fd, rbuffer, sizeof(rbuffer)) == 0);
- assert_se(streq(rbuffer, ""));
+ ASSERT_STREQ(rbuffer, "");
fd = safe_close(fd);
/* SPDX-License-Identifier: LGPL-2.1-or-later */
+#include <unistd.h>
+
#include "alloc-util.h"
#include "string-util.h"
#include "tests.h"
test_one("today");
test_one("tomorrow");
test_one_noutc("16:20 UTC");
- test_one_noutc("16:20 Asia/Seoul");
- test_one_noutc("tomorrow Asia/Seoul");
- test_one_noutc("2012-12-30 18:42 Asia/Seoul");
+ if (access("/usr/share/zoneinfo/Asia/Seoul", F_OK) >= 0) {
+ test_one_noutc("16:20 Asia/Seoul");
+ test_one_noutc("tomorrow Asia/Seoul");
+ test_one_noutc("2012-12-30 18:42 Asia/Seoul");
+ }
test_one_noutc("now");
test_one_noutc("+2d");
test_one_noutc("+2y 4d");
test_should_fail("1969-12-31 UTC");
test_should_fail("-1000y");
test_should_fail("today UTC UTC");
- test_should_fail("now Asia/Seoul");
- test_should_fail("+2d Asia/Seoul");
- test_should_fail("@1395716396 Asia/Seoul");
+ if (access("/usr/share/zoneinfo/Asia/Seoul", F_OK) >= 0) {
+ test_should_fail("now Asia/Seoul");
+ test_should_fail("+2d Asia/Seoul");
+ test_should_fail("@1395716396 Asia/Seoul");
+ }
#if SIZEOF_TIME_T == 8
test_should_pass("9999-12-30 23:59:59 UTC");
test_should_fail("9999-12-31 00:00:00 UTC");
static void test_devnum_format_str_one(dev_t devnum, const char *s) {
dev_t x;
- assert_se(streq(FORMAT_DEVNUM(devnum), s));
+ ASSERT_STREQ(FORMAT_DEVNUM(devnum), s);
assert_se(parse_devnum(s, &x) >= 0);
assert_se(x == devnum);
}
}
dir = opendir(t);
- if (dir == NULL) {
+ if (!dir) {
log_error_errno(errno, "Failed to open directory '%s': %m", t);
exit(EXIT_FAILURE);
}
}
dir = opendir(t);
- if (dir == NULL) {
+ if (!dir) {
log_error_errno(errno, "Failed to open directory '%s': %m", t);
exit(EXIT_FAILURE);
}
#include "compress.h"
#include "cryptsetup-util.h"
#include "elf-util.h"
+#include "gcrypt-util.h"
#include "idn-util.h"
#include "libarchive-util.h"
#include "libfido2-util.h"
#include "macro.h"
#include "main-func.h"
+#include "module-util.h"
#include "password-quality-util-passwdqc.h"
#include "password-quality-util-pwquality.h"
#include "pcre2-util.h"
assert_se(dlopen_lzma() >= 0);
#endif
+#if HAVE_GCRYPT
+ assert_se(initialize_libgcrypt(/* secmem= */ false) >= 0);
+#endif
+
+#if HAVE_KMOD
+ assert_se(dlopen_libkmod() >= 0);
+#endif
+
return 0;
}
r = dns_label_unescape(&w, buffer, buffer_sz, 0);
assert_se(r == ret);
if (r >= 0)
- assert_se(streq(buffer, expect));
+ ASSERT_STREQ(buffer, expect);
w = what;
r = dns_label_unescape(&w, buffer, buffer_sz, DNS_LABEL_LDH);
assert_se(r == ret_ldh);
if (r >= 0)
- assert_se(streq(buffer, expect));
+ ASSERT_STREQ(buffer, expect);
w = what;
r = dns_label_unescape(&w, buffer, buffer_sz, DNS_LABEL_NO_ESCAPES);
const int ret_noe = strchr(what, '\\') ? -EINVAL : ret;
assert_se(r == ret_noe);
if (r >= 0)
- assert_se(streq(buffer, expect));
+ ASSERT_STREQ(buffer, expect);
}
TEST(dns_label_unescape) {
r = dns_label_unescape_suffix(what, &label, buffer, buffer_sz);
assert_se(r == ret1);
if (r >= 0)
- assert_se(streq(buffer, expect1));
+ ASSERT_STREQ(buffer, expect1);
r = dns_label_unescape_suffix(what, &label, buffer, buffer_sz);
assert_se(r == ret2);
if (r >= 0)
- assert_se(streq(buffer, expect2));
+ ASSERT_STREQ(buffer, expect2);
}
TEST(dns_label_unescape_suffix) {
if (r < 0)
return;
- assert_se(streq_ptr(expect, t));
+ ASSERT_STREQ(expect, t);
}
TEST(dns_label_escape) {
if (r < 0)
return;
- assert_se(streq_ptr(expect, t));
+ ASSERT_STREQ(expect, t);
}
TEST(dns_name_normalize) {
assert_se(in_addr_from_string_auto(address, &familya, &a) >= 0);
assert_se(dns_name_reverse(familya, &a, &p) >= 0);
- assert_se(streq(p, name));
+ ASSERT_STREQ(p, name);
assert_se(dns_name_address(p, &familyb, &b) > 0);
assert_se(familya == familyb);
assert_se(in_addr_equal(familya, &a, &b));
_cleanup_free_ char *p = NULL;
assert_se(dns_name_concat(a, b, 0, &p) == r);
- assert_se(streq_ptr(p, result));
+ ASSERT_STREQ(p, result);
}
TEST(dns_name_concat) {
log_info("%s, %s, %s, →%d, %s", strnull(a), strnull(b), strnull(c), r, strnull(d));
assert_se(dns_service_join(a, b, c, &t) == r);
- assert_se(streq_ptr(t, d));
+ ASSERT_STREQ(t, d);
if (r < 0)
return;
assert_se(dns_service_split(t, &x, &y, &z) >= 0);
- assert_se(streq_ptr(a, x));
- assert_se(streq_ptr(b, y));
+ ASSERT_STREQ(a, x);
+ ASSERT_STREQ(b, y);
assert_se(dns_name_equal(c, z) > 0);
}
log_info("%s, %s, %s, %s, →%d", joined, strnull(a), strnull(b), strnull(c), r);
assert_se(dns_service_split(joined, &x, &y, &z) == r);
- assert_se(streq_ptr(x, a));
- assert_se(streq_ptr(y, b));
- assert_se(streq_ptr(z, c));
+ ASSERT_STREQ(x, a);
+ ASSERT_STREQ(y, b);
+ ASSERT_STREQ(z, c);
if (r < 0)
return;
log_info("%s, %s, %s, →%s", name, old_suffix, new_suffix, strnull(result));
assert_se(dns_name_change_suffix(name, old_suffix, new_suffix, &s) == r);
- assert_se(streq_ptr(s, result));
+ ASSERT_STREQ(s, result);
}
TEST(dns_name_change_suffix) {
log_info("%s, %u, → %s, %d", name, n_labels, strnull(result), ret);
assert_se(ret == dns_name_suffix(name, n_labels, &p));
- assert_se(streq_ptr(p, result));
+ ASSERT_STREQ(p, result);
}
TEST(dns_name_suffix) {
log_info("%s, %s, →%s", a, b, result);
assert_se(dns_name_common_suffix(a, b, &c) >= 0);
- assert_se(streq(c, result));
+ ASSERT_STREQ(c, result);
}
TEST(dns_name_common_suffix) {
assert_se(utf8_console_width(t3) <= max_width);
if (new_length >= old_length) {
- assert_se(streq(t1, n));
- assert_se(streq(t2, n));
- assert_se(streq(t3, n));
+ ASSERT_STREQ(t1, n);
+ ASSERT_STREQ(t2, n);
+ ASSERT_STREQ(t3, n);
}
}
e = ellipsize("01" ANSI_NORMAL "23", 4, 0);
puts(e);
- assert_se(streq(e, "01" ANSI_NORMAL "23"));
+ ASSERT_STREQ(e, "01" ANSI_NORMAL "23");
f = ellipsize("ab" ANSI_NORMAL "cd", 4, 90);
puts(f);
- assert_se(streq(f, "ab" ANSI_NORMAL "cd"));
+ ASSERT_STREQ(f, "ab" ANSI_NORMAL "cd");
g = ellipsize("🐱🐱" ANSI_NORMAL "🐱🐱" ANSI_NORMAL, 5, 0);
puts(g);
- assert_se(streq(g, "…" ANSI_NORMAL "🐱🐱" ANSI_NORMAL));
+ ASSERT_STREQ(g, "…" ANSI_NORMAL "🐱🐱" ANSI_NORMAL);
h = ellipsize("🐱🐱" ANSI_NORMAL "🐱🐱" ANSI_NORMAL, 5, 90);
puts(h);
- assert_se(streq(h, "🐱…" ANSI_NORMAL "🐱" ANSI_NORMAL));
+ ASSERT_STREQ(h, "🐱…" ANSI_NORMAL "🐱" ANSI_NORMAL);
}
DEFINE_TEST_MAIN(LOG_INFO);
assert_se(manager_load_unit(m, "unit-with-multiple-dashes.service", NULL, NULL, &unit_with_multiple_dashes) >= 0);
assert_se(strv_equal(unit_with_multiple_dashes->documentation, STRV_MAKE("man:test", "man:override2", "man:override3")));
- assert_se(streq_ptr(unit_with_multiple_dashes->description, "override4"));
+ ASSERT_STREQ(unit_with_multiple_dashes->description, "override4");
/* Now merge a synthetic unit into the existing one */
assert_se(unit_new_for_name(m, sizeof(Service), "merged.service", &stub) >= 0);
_cleanup_strv_free_ char **data = NULL;
assert_se(load_env_file(NULL, name, &data) == 0);
- assert_se(streq(data[0], "a=a"));
- assert_se(streq(data[1], "b=bc"));
- assert_se(streq(data[2], "d=de f"));
- assert_se(streq(data[3], "g=g "));
- assert_se(streq(data[4], "h=ąęół śćńźżμ"));
- assert_se(streq(data[5], "i=i"));
- assert_se(data[6] == NULL);
+ ASSERT_STREQ(data[0], "a=a");
+ ASSERT_STREQ(data[1], "b=bc");
+ ASSERT_STREQ(data[2], "d=de f");
+ ASSERT_STREQ(data[3], "g=g ");
+ ASSERT_STREQ(data[4], "h=ąęół śćńźżμ");
+ ASSERT_STREQ(data[5], "i=i");
+ ASSERT_NULL(data[6]);
}
TEST(load_env_file_2) {
_cleanup_strv_free_ char **data = NULL;
assert_se(load_env_file(NULL, name, &data) == 0);
- assert_se(streq(data[0], "a=a"));
- assert_se(data[1] == NULL);
+ ASSERT_STREQ(data[0], "a=a");
+ ASSERT_NULL(data[1]);
}
TEST(load_env_file_3) {
_cleanup_strv_free_ char **data = NULL;
assert_se(load_env_file(NULL, name, &data) == 0);
- assert_se(streq(data[0], "normal1=line111"));
- assert_se(streq(data[1], "normal2=line222"));
- assert_se(data[2] == NULL);
+ ASSERT_STREQ(data[0], "normal1=line111");
+ ASSERT_STREQ(data[1], "normal2=line222");
+ ASSERT_NULL(data[2]);
}
TEST(load_env_file_4) {
_cleanup_strv_free_ char **data = NULL;
assert_se(load_env_file(NULL, name, &data) == 0);
- assert_se(streq(data[0], "HWMON_MODULES=coretemp f71882fg"));
- assert_se(streq(data[1], "MODULE_0=coretemp"));
- assert_se(streq(data[2], "MODULE_1=f71882fg"));
- assert_se(data[3] == NULL);
+ ASSERT_STREQ(data[0], "HWMON_MODULES=coretemp f71882fg");
+ ASSERT_STREQ(data[1], "MODULE_0=coretemp");
+ ASSERT_STREQ(data[2], "MODULE_1=f71882fg");
+ ASSERT_NULL(data[3]);
}
TEST(load_env_file_5) {
_cleanup_strv_free_ char **data = NULL;
assert_se(load_env_file(NULL, name, &data) == 0);
- assert_se(streq(data[0], "a="));
- assert_se(streq(data[1], "b="));
- assert_se(data[2] == NULL);
+ ASSERT_STREQ(data[0], "a=");
+ ASSERT_STREQ(data[1], "b=");
+ ASSERT_NULL(data[2]);
}
TEST(load_env_file_6) {
_cleanup_strv_free_ char **data = NULL;
assert_se(load_env_file(NULL, name, &data) == 0);
- assert_se(streq(data[0], "a= n t x y '"));
- assert_se(streq(data[1], "b=$'"));
- assert_se(streq(data[2], "c= \\n\\t\\$\\`\\\\\n"));
- assert_se(streq(data[3], "d= \\n\\t$`\\\n"));
- assert_se(data[4] == NULL);
+ ASSERT_STREQ(data[0], "a= n t x y '");
+ ASSERT_STREQ(data[1], "b=$'");
+ ASSERT_STREQ(data[2], "c= \\n\\t\\$\\`\\\\\n");
+ ASSERT_STREQ(data[3], "d= \\n\\t$`\\\n");
+ ASSERT_NULL(data[4]);
}
TEST(load_env_file_invalid_utf8) {
assert_se(f = popen(cmd, "re"));
assert_se(read_full_stream(f, &from_shell, &sz) >= 0);
assert_se(sz == strlen(v));
- assert_se(streq(from_shell, v));
+ ASSERT_STREQ(from_shell, v);
assert_se(load_env_file(NULL, p, &l) >= 0);
assert_se(strv_equal(l, STRV_MAKE(j)));
assert_se(parse_env_file(NULL, p, "TEST", &w) >= 0);
- assert_se(streq_ptr(w, v));
+ ASSERT_STREQ(w, v);
}
}
d = strv_env_delete(a, 2, b, c);
assert_se(d);
- assert_se(streq(d[0], "WALDO=WALDO"));
- assert_se(streq(d[1], "WALDO="));
+ ASSERT_STREQ(d[0], "WALDO=WALDO");
+ ASSERT_STREQ(d[1], "WALDO=");
assert_se(strv_length(d) == 2);
}
TEST(strv_env_get) {
char **l = STRV_MAKE("ONE_OR_TWO=1", "THREE=3", "ONE_OR_TWO=2", "FOUR=4");
- assert_se(streq(strv_env_get(l, "ONE_OR_TWO"), "2"));
- assert_se(streq(strv_env_get(l, "THREE"), "3"));
- assert_se(streq(strv_env_get(l, "FOUR"), "4"));
+ ASSERT_STREQ(strv_env_get(l, "ONE_OR_TWO"), "2");
+ ASSERT_STREQ(strv_env_get(l, "THREE"), "3");
+ ASSERT_STREQ(strv_env_get(l, "FOUR"), "4");
}
TEST(strv_env_pairs_get) {
char **l = STRV_MAKE("ONE_OR_TWO", "1", "THREE", "3", "ONE_OR_TWO", "2", "FOUR", "4", "FIVE", "5", "SIX", "FIVE", "SEVEN", "7");
- assert_se(streq(strv_env_pairs_get(l, "ONE_OR_TWO"), "2"));
- assert_se(streq(strv_env_pairs_get(l, "THREE"), "3"));
- assert_se(streq(strv_env_pairs_get(l, "FOUR"), "4"));
- assert_se(streq(strv_env_pairs_get(l, "FIVE"), "5"));
+ ASSERT_STREQ(strv_env_pairs_get(l, "ONE_OR_TWO"), "2");
+ ASSERT_STREQ(strv_env_pairs_get(l, "THREE"), "3");
+ ASSERT_STREQ(strv_env_pairs_get(l, "FOUR"), "4");
+ ASSERT_STREQ(strv_env_pairs_get(l, "FIVE"), "5");
}
TEST(strv_env_unset) {
assert_se(strv_env_unset(l, "SCHLUMPF") == l);
- assert_se(streq(l[0], "PIEP"));
- assert_se(streq(l[1], "NANANANA=YES"));
+ ASSERT_STREQ(l[0], "PIEP");
+ ASSERT_STREQ(l[1], "NANANANA=YES");
assert_se(strv_length(l) == 2);
}
_cleanup_strv_free_ char **r = strv_env_merge(NULL, a, NULL, b, NULL, a, b, b, NULL);
assert_se(r);
- assert_se(streq(r[0], "FOO="));
- assert_se(streq(r[1], "WALDO="));
- assert_se(streq(r[2], "PIEP"));
- assert_se(streq(r[3], "SCHLUMPF=SMURFF"));
- assert_se(streq(r[4], "EQ==="));
- assert_se(streq(r[5], "PIEP="));
- assert_se(streq(r[6], "NANANANA=YES"));
+ ASSERT_STREQ(r[0], "FOO=");
+ ASSERT_STREQ(r[1], "WALDO=");
+ ASSERT_STREQ(r[2], "PIEP");
+ ASSERT_STREQ(r[3], "SCHLUMPF=SMURFF");
+ ASSERT_STREQ(r[4], "EQ===");
+ ASSERT_STREQ(r[5], "PIEP=");
+ ASSERT_STREQ(r[6], "NANANANA=YES");
assert_se(strv_length(r) == 7);
assert_se(strv_env_clean(r) == r);
- assert_se(streq(r[0], "FOO="));
- assert_se(streq(r[1], "WALDO="));
- assert_se(streq(r[2], "SCHLUMPF=SMURFF"));
- assert_se(streq(r[3], "EQ==="));
- assert_se(streq(r[4], "PIEP="));
- assert_se(streq(r[5], "NANANANA=YES"));
+ ASSERT_STREQ(r[0], "FOO=");
+ ASSERT_STREQ(r[1], "WALDO=");
+ ASSERT_STREQ(r[2], "SCHLUMPF=SMURFF");
+ ASSERT_STREQ(r[3], "EQ===");
+ ASSERT_STREQ(r[4], "PIEP=");
+ ASSERT_STREQ(r[5], "NANANANA=YES");
assert_se(strv_length(r) == 6);
}
assert_se(strv_length(a) == 2);
strv_sort(a);
- assert_se(streq(a[0], "a=A"));
- assert_se(streq(a[1], "b=b"));
+ ASSERT_STREQ(a[0], "a=A");
+ ASSERT_STREQ(a[1], "b=b");
}
TEST(strv_env_replace_strdup_passthrough) {
assert_se(strv_env_replace_strdup_passthrough(&a, "$a") == -EINVAL);
assert_se(strv_length(a) == 3);
- assert_se(streq(a[0], "a=a"));
- assert_se(streq(a[1], "b="));
- assert_se(streq(a[2], "c="));
+ ASSERT_STREQ(a[0], "a=a");
+ ASSERT_STREQ(a[1], "b=");
+ ASSERT_STREQ(a[2], "c=");
}
TEST(strv_env_assign) {
assert_se(strv_env_assign(&a, "a=", "B") == -EINVAL);
assert_se(strv_length(a) == 1);
- assert_se(streq(a[0], "a=A"));
+ ASSERT_STREQ(a[0], "a=A");
}
TEST(strv_env_assignf) {
assert_se(strv_env_assignf(&a, "a=", "B") == -EINVAL);
assert_se(strv_length(a) == 1);
- assert_se(streq(a[0], "a=A"));
+ ASSERT_STREQ(a[0], "a=A");
}
TEST(strv_env_assign_many) {
};
char **env = (char**) _env;
- assert_se(streq(strv_env_get_n(env, "FOO__", 3, 0), "BAR BAR"));
- assert_se(streq(strv_env_get_n(env, "FOO__", 3, REPLACE_ENV_USE_ENVIRONMENT), "BAR BAR"));
- assert_se(streq(strv_env_get_n(env, "FOO", 3, 0), "BAR BAR"));
- assert_se(streq(strv_env_get_n(env, "FOO", 3, REPLACE_ENV_USE_ENVIRONMENT), "BAR BAR"));
+ ASSERT_STREQ(strv_env_get_n(env, "FOO__", 3, 0), "BAR BAR");
+ ASSERT_STREQ(strv_env_get_n(env, "FOO__", 3, REPLACE_ENV_USE_ENVIRONMENT), "BAR BAR");
+ ASSERT_STREQ(strv_env_get_n(env, "FOO", 3, 0), "BAR BAR");
+ ASSERT_STREQ(strv_env_get_n(env, "FOO", 3, REPLACE_ENV_USE_ENVIRONMENT), "BAR BAR");
- assert_se(streq(strv_env_get_n(env, "PATH__", 4, 0), "unset"));
- assert_se(streq(strv_env_get_n(env, "PATH", 4, 0), "unset"));
- assert_se(streq(strv_env_get_n(env, "PATH__", 4, REPLACE_ENV_USE_ENVIRONMENT), "unset"));
- assert_se(streq(strv_env_get_n(env, "PATH", 4, REPLACE_ENV_USE_ENVIRONMENT), "unset"));
+ ASSERT_STREQ(strv_env_get_n(env, "PATH__", 4, 0), "unset");
+ ASSERT_STREQ(strv_env_get_n(env, "PATH", 4, 0), "unset");
+ ASSERT_STREQ(strv_env_get_n(env, "PATH__", 4, REPLACE_ENV_USE_ENVIRONMENT), "unset");
+ ASSERT_STREQ(strv_env_get_n(env, "PATH", 4, REPLACE_ENV_USE_ENVIRONMENT), "unset");
env[3] = NULL; /* kill our $PATH */
unsigned flags = REPLACE_ENV_ALLOW_BRACELESS*braceless;
assert_se(replace_env("FOO=$FOO=${FOO}", (char**) env, flags, &t) >= 0);
- assert_se(streq(t, braceless ? "FOO=BAR BAR=BAR BAR" : "FOO=$FOO=BAR BAR"));
+ ASSERT_STREQ(t, braceless ? "FOO=BAR BAR=BAR BAR" : "FOO=$FOO=BAR BAR");
assert_se(replace_env("BAR=$BAR=${BAR}", (char**) env, flags, &s) >= 0);
- assert_se(streq(s, braceless ? "BAR=waldo=waldo" : "BAR=$BAR=waldo"));
+ ASSERT_STREQ(s, braceless ? "BAR=waldo=waldo" : "BAR=$BAR=waldo");
assert_se(replace_env("BARBAR=$BARBAR=${BARBAR}", (char**) env, flags, &q) >= 0);
- assert_se(streq(q, braceless ? "BARBAR==" : "BARBAR=$BARBAR="));
+ ASSERT_STREQ(q, braceless ? "BARBAR==" : "BARBAR=$BARBAR=");
assert_se(replace_env("BAR=$BAR$BAR${BAR}${BAR}", (char**) env, flags, &r) >= 0);
- assert_se(streq(r, braceless ? "BAR=waldowaldowaldowaldo" : "BAR=$BAR$BARwaldowaldo"));
+ ASSERT_STREQ(r, braceless ? "BAR=waldowaldowaldowaldo" : "BAR=$BAR$BARwaldowaldo");
assert_se(replace_env("${BAR}$BAR$BAR", (char**) env, flags, &p) >= 0);
- assert_se(streq(p, braceless ? "waldowaldowaldo" : "waldo$BAR$BAR"));
+ ASSERT_STREQ(p, braceless ? "waldowaldowaldo" : "waldo$BAR$BAR");
}
static void test_replace_env2(bool extended) {
unsigned flags = REPLACE_ENV_ALLOW_EXTENDED*extended;
assert_se(replace_env("FOO=${FOO:-${BAR}}", (char**) env, flags, &t) >= 0);
- assert_se(streq(t, extended ? "FOO=foo" : "FOO=${FOO:-bar}"));
+ ASSERT_STREQ(t, extended ? "FOO=foo" : "FOO=${FOO:-bar}");
assert_se(replace_env("BAR=${XXX:-${BAR}}", (char**) env, flags, &s) >= 0);
- assert_se(streq(s, extended ? "BAR=bar" : "BAR=${XXX:-bar}"));
+ ASSERT_STREQ(s, extended ? "BAR=bar" : "BAR=${XXX:-bar}");
assert_se(replace_env("XXX=${XXX:+${BAR}}", (char**) env, flags, &q) >= 0);
- assert_se(streq(q, extended ? "XXX=" : "XXX=${XXX:+bar}"));
+ ASSERT_STREQ(q, extended ? "XXX=" : "XXX=${XXX:+bar}");
assert_se(replace_env("FOO=${FOO:+${BAR}}", (char**) env, flags, &r) >= 0);
- assert_se(streq(r, extended ? "FOO=bar" : "FOO=${FOO:+bar}"));
+ ASSERT_STREQ(r, extended ? "FOO=bar" : "FOO=${FOO:+bar}");
assert_se(replace_env("FOO=${FOO:-${BAR}post}", (char**) env, flags, &p) >= 0);
- assert_se(streq(p, extended ? "FOO=foo" : "FOO=${FOO:-barpost}"));
+ ASSERT_STREQ(p, extended ? "FOO=foo" : "FOO=${FOO:-barpost}");
assert_se(replace_env("XXX=${XXX:+${BAR}post}", (char**) env, flags, &x) >= 0);
- assert_se(streq(x, extended ? "XXX=" : "XXX=${XXX:+barpost}"));
+ ASSERT_STREQ(x, extended ? "XXX=" : "XXX=${XXX:+barpost}");
assert_se(replace_env("FOO=${FOO}between${BAR:-baz}", (char**) env, flags, &y) >= 0);
- assert_se(streq(y, extended ? "FOO=foobetweenbar" : "FOO=foobetween${BAR:-baz}"));
+ ASSERT_STREQ(y, extended ? "FOO=foobetweenbar" : "FOO=foobetween${BAR:-baz}");
}
TEST(replace_env) {
assert_se(replace_env_argv((char**) line, (char**) env, &r, NULL, NULL) >= 0);
assert_se(r);
- assert_se(streq(r[0], "FOO$FOO"));
- assert_se(streq(r[1], "FOO$FOOFOO"));
- assert_se(streq(r[2], "FOOBAR BAR$FOO"));
- assert_se(streq(r[3], "FOOBAR BAR"));
- assert_se(streq(r[4], "BAR BAR"));
- assert_se(streq(r[5], "BAR"));
- assert_se(streq(r[6], "BAR"));
- assert_se(streq(r[7], "BAR BARwaldo"));
- assert_se(streq(r[8], "${FOO"));
- assert_se(streq(r[9], "FOO$BAR BAR"));
- assert_se(streq(r[10], "$FOOBAR BAR"));
- assert_se(streq(r[11], "${FOO:-waldo}"));
- assert_se(streq(r[12], "${QUUX:-BAR BAR}"));
- assert_se(streq(r[13], "${FOO:+waldo}"));
- assert_se(streq(r[14], "${QUUX:+waldo}"));
- assert_se(streq(r[15], "${FOO:+|waldo|}}"));
- assert_se(streq(r[16], "${FOO:+|waldo{|}"));
+ ASSERT_STREQ(r[0], "FOO$FOO");
+ ASSERT_STREQ(r[1], "FOO$FOOFOO");
+ ASSERT_STREQ(r[2], "FOOBAR BAR$FOO");
+ ASSERT_STREQ(r[3], "FOOBAR BAR");
+ ASSERT_STREQ(r[4], "BAR BAR");
+ ASSERT_STREQ(r[5], "BAR");
+ ASSERT_STREQ(r[6], "BAR");
+ ASSERT_STREQ(r[7], "BAR BARwaldo");
+ ASSERT_STREQ(r[8], "${FOO");
+ ASSERT_STREQ(r[9], "FOO$BAR BAR");
+ ASSERT_STREQ(r[10], "$FOOBAR BAR");
+ ASSERT_STREQ(r[11], "${FOO:-waldo}");
+ ASSERT_STREQ(r[12], "${QUUX:-BAR BAR}");
+ ASSERT_STREQ(r[13], "${FOO:+waldo}");
+ ASSERT_STREQ(r[14], "${QUUX:+waldo}");
+ ASSERT_STREQ(r[15], "${FOO:+|waldo|}}");
+ ASSERT_STREQ(r[16], "${FOO:+|waldo{|}");
assert_se(strv_length(r) == 17);
}
assert_se(strv_env_clean(e) == e);
assert_se(strv_env_is_valid(e));
- assert_se(streq(e[0], "FOOBAR=WALDO"));
- assert_se(streq(e[1], "X="));
- assert_se(streq(e[2], "F=F"));
- assert_se(streq(e[3], "abcd=äöüß"));
- assert_se(streq(e[4], "xyz=xyz\n"));
- assert_se(streq(e[5], "another=final one"));
- assert_se(streq(e[6], "CRLF=\r\n"));
- assert_se(streq(e[7], "LESS_TERMCAP_mb=\x1b[01;31m"));
- assert_se(e[8] == NULL);
+ ASSERT_STREQ(e[0], "FOOBAR=WALDO");
+ ASSERT_STREQ(e[1], "X=");
+ ASSERT_STREQ(e[2], "F=F");
+ ASSERT_STREQ(e[3], "abcd=äöüß");
+ ASSERT_STREQ(e[4], "xyz=xyz\n");
+ ASSERT_STREQ(e[5], "another=final one");
+ ASSERT_STREQ(e[6], "CRLF=\r\n");
+ ASSERT_STREQ(e[7], "LESS_TERMCAP_mb=\x1b[01;31m");
+ ASSERT_NULL(e[8]);
}
TEST(env_name_is_valid) {
TEST(putenv_dup) {
assert_se(putenv_dup("A=a1", true) == 0);
- assert_se(streq_ptr(getenv("A"), "a1"));
+ ASSERT_STREQ(getenv("A"), "a1");
assert_se(putenv_dup("A=a1", true) == 0);
- assert_se(streq_ptr(getenv("A"), "a1"));
+ ASSERT_STREQ(getenv("A"), "a1");
assert_se(putenv_dup("A=a2", false) == 0);
- assert_se(streq_ptr(getenv("A"), "a1"));
+ ASSERT_STREQ(getenv("A"), "a1");
assert_se(putenv_dup("A=a2", true) == 0);
- assert_se(streq_ptr(getenv("A"), "a2"));
+ ASSERT_STREQ(getenv("A"), "a2");
}
TEST(setenv_systemd_exec_pid) {
assert_se(setenv("SYSTEMD_EXEC_PID", "*", 1) >= 0);
assert_se(setenv_systemd_exec_pid(true) == 0);
assert_se(e = getenv("SYSTEMD_EXEC_PID"));
- assert_se(streq(e, "*"));
+ ASSERT_STREQ(e, "*");
assert_se(setenv("SYSTEMD_EXEC_PID", "123abc", 1) >= 0);
assert_se(setenv_systemd_exec_pid(true) == 1);
copy1 = strdup(eq + 1);
assert_se(copy1);
- assert_se(streq_ptr(getenv(n), copy1));
+ ASSERT_STREQ(getenv(n), copy1);
assert_se(getenv(n) == eq + 1);
assert_se(getenv_steal_erase(n, ©2) > 0);
- assert_se(streq_ptr(copy1, copy2));
+ ASSERT_STREQ(copy1, copy2);
assert_se(isempty(eq + 1));
assert_se(!getenv(n));
}
/* Finally some valid paths */
assert_se(setenv("TEST_GETENV_PATH_LIST", "/foo:/bar/baz:/hello/world:/path with spaces:/final", 1) >= 0);
assert_se(getenv_path_list("TEST_GETENV_PATH_LIST", &path_list) >= 0);
- assert_se(streq(path_list[0], "/foo"));
- assert_se(streq(path_list[1], "/bar/baz"));
- assert_se(streq(path_list[2], "/hello/world"));
- assert_se(streq(path_list[3], "/path with spaces"));
- assert_se(streq(path_list[4], "/final"));
- assert_se(path_list[5] == NULL);
+ ASSERT_STREQ(path_list[0], "/foo");
+ ASSERT_STREQ(path_list[1], "/bar/baz");
+ ASSERT_STREQ(path_list[2], "/hello/world");
+ ASSERT_STREQ(path_list[3], "/path with spaces");
+ ASSERT_STREQ(path_list[4], "/final");
+ ASSERT_NULL(path_list[5]);
assert_se(unsetenv("TEST_GETENV_PATH_LIST") >= 0);
}
TEST(errno_list) {
for (size_t i = 0; i < ELEMENTSOF(errno_names); i++) {
if (errno_names[i]) {
- assert_se(streq(errno_to_name(i), errno_names[i]));
+ ASSERT_STREQ(errno_to_name(i), errno_names[i]);
assert_se(errno_from_name(errno_names[i]) == (int) i);
}
}
#ifdef ECANCELLED
/* ECANCELLED is an alias of ECANCELED. */
- assert_se(streq(errno_to_name(ECANCELLED), "ECANCELED"));
+ ASSERT_STREQ(errno_to_name(ECANCELLED), "ECANCELED");
#endif
- assert_se(streq(errno_to_name(ECANCELED), "ECANCELED"));
+ ASSERT_STREQ(errno_to_name(ECANCELED), "ECANCELED");
#ifdef EREFUSED
/* EREFUSED is an alias of ECONNREFUSED. */
- assert_se(streq(errno_to_name(EREFUSED), "ECONNREFUSED"));
+ ASSERT_STREQ(errno_to_name(EREFUSED), "ECONNREFUSED");
#endif
- assert_se(streq(errno_to_name(ECONNREFUSED), "ECONNREFUSED"));
+ ASSERT_STREQ(errno_to_name(ECONNREFUSED), "ECONNREFUSED");
}
DEFINE_TEST_MAIN(LOG_INFO);
assert_se(strstr(b, "201"));
/* Check with negative values */
- assert_se(streq(a, STRERROR(-200)));
- assert_se(streq(b, STRERROR(-201)));
+ ASSERT_STREQ(a, STRERROR(-200));
+ ASSERT_STREQ(b, STRERROR(-201));
const char *c = STRERROR(INT_MAX);
char buf[DECIMAL_STR_MAX(int)];
_cleanup_free_ char *t = NULL;
assert_se(t = cescape("abc\\\"\b\f\n\r\t\v\a\003\177\234\313"));
- assert_se(streq(t, "abc\\\\\\\"\\b\\f\\n\\r\\t\\v\\a\\003\\177\\234\\313"));
+ ASSERT_STREQ(t, "abc\\\\\\\"\\b\\f\\n\\r\\t\\v\\a\\003\\177\\234\\313");
}
TEST(xescape) {
_cleanup_free_ char *t = NULL;
assert_se(t = xescape("abc\\\"\b\f\n\r\t\v\a\003\177\234\313", ""));
- assert_se(streq(t, "abc\\x5c\"\\x08\\x0c\\x0a\\x0d\\x09\\x0b\\x07\\x03\\x7f\\x9c\\xcb"));
+ ASSERT_STREQ(t, "abc\\x5c\"\\x08\\x0c\\x0a\\x0d\\x09\\x0b\\x07\\x03\\x7f\\x9c\\xcb");
}
static void test_xescape_full_one(bool eight_bits) {
log_info("%02u: <%s>", i, t);
if (i >= full_fit)
- assert_se(streq(t, escaped));
+ ASSERT_STREQ(t, escaped);
else if (i >= 3) {
/* We need up to four columns, so up to three columns may be wasted */
assert_se(strlen(t) == i || strlen(t) == i - 1 || strlen(t) == i - 2 || strlen(t) == i - 3);
assert_se(cunescape("abc\\\\\\\"\\b\\f\\a\\n\\r\\t\\v\\003\\177\\234\\313\\000\\x00", 0, &unescaped) < 0);
assert_se(cunescape("abc\\\\\\\"\\b\\f\\a\\n\\r\\t\\v\\003\\177\\234\\313\\000\\x00", UNESCAPE_RELAX, &unescaped) >= 0);
- assert_se(streq_ptr(unescaped, "abc\\\"\b\f\a\n\r\t\v\003\177\234\313\\000\\x00"));
+ ASSERT_STREQ(unescaped, "abc\\\"\b\f\a\n\r\t\v\003\177\234\313\\000\\x00");
unescaped = mfree(unescaped);
/* incomplete sequences */
assert_se(cunescape("\\x0", 0, &unescaped) < 0);
assert_se(cunescape("\\x0", UNESCAPE_RELAX, &unescaped) >= 0);
- assert_se(streq_ptr(unescaped, "\\x0"));
+ ASSERT_STREQ(unescaped, "\\x0");
unescaped = mfree(unescaped);
assert_se(cunescape("\\x", 0, &unescaped) < 0);
assert_se(cunescape("\\x", UNESCAPE_RELAX, &unescaped) >= 0);
- assert_se(streq_ptr(unescaped, "\\x"));
+ ASSERT_STREQ(unescaped, "\\x");
unescaped = mfree(unescaped);
assert_se(cunescape("\\", 0, &unescaped) < 0);
assert_se(cunescape("\\", UNESCAPE_RELAX, &unescaped) >= 0);
- assert_se(streq_ptr(unescaped, "\\"));
+ ASSERT_STREQ(unescaped, "\\");
unescaped = mfree(unescaped);
assert_se(cunescape("\\11", 0, &unescaped) < 0);
assert_se(cunescape("\\11", UNESCAPE_RELAX, &unescaped) >= 0);
- assert_se(streq_ptr(unescaped, "\\11"));
+ ASSERT_STREQ(unescaped, "\\11");
unescaped = mfree(unescaped);
assert_se(cunescape("\\1", 0, &unescaped) < 0);
assert_se(cunescape("\\1", UNESCAPE_RELAX, &unescaped) >= 0);
- assert_se(streq_ptr(unescaped, "\\1"));
+ ASSERT_STREQ(unescaped, "\\1");
unescaped = mfree(unescaped);
assert_se(cunescape("\\u0000", 0, &unescaped) < 0);
assert_se(cunescape("\\u00DF\\U000000df\\u03a0\\U00000041", UNESCAPE_RELAX, &unescaped) >= 0);
- assert_se(streq_ptr(unescaped, "ßßΠA"));
+ ASSERT_STREQ(unescaped, "ßßΠA");
unescaped = mfree(unescaped);
assert_se(cunescape("\\073", 0, &unescaped) >= 0);
- assert_se(streq_ptr(unescaped, ";"));
+ ASSERT_STREQ(unescaped, ";");
unescaped = mfree(unescaped);
assert_se(cunescape("A=A\\\\x0aB", 0, &unescaped) >= 0);
- assert_se(streq_ptr(unescaped, "A=A\\x0aB"));
+ ASSERT_STREQ(unescaped, "A=A\\x0aB");
unescaped = mfree(unescaped);
assert_se(cunescape("A=A\\\\x0aB", UNESCAPE_RELAX, &unescaped) >= 0);
- assert_se(streq_ptr(unescaped, "A=A\\x0aB"));
+ ASSERT_STREQ(unescaped, "A=A\\x0aB");
unescaped = mfree(unescaped);
assert_se(cunescape("\\x00\\x00\\x00", UNESCAPE_ACCEPT_NUL, &unescaped) == 3);
assert_se(r = shell_escape(s, bad));
log_debug("%s → %s (expected %s)", s, r, expected);
- assert_se(streq_ptr(r, expected));
+ ASSERT_STREQ(r, expected);
}
TEST(shell_escape) {
assert_se(ret = shell_maybe_quote(s, flags));
log_debug("[%s] → [%s] (%s)", s, ret, expected);
- assert_se(streq(ret, expected));
+ ASSERT_STREQ(ret, expected);
}
TEST(shell_maybe_quote) {
assert_se(s = quote_command_line(argv, SHELL_ESCAPE_EMPTY));
log_info("%s", s);
- assert_se(streq(s, expected));
+ ASSERT_STREQ(s, expected);
}
TEST(quote_command_line) {
assert_se(ret = octescape(s, strlen_ptr(s)));
log_debug("octescape(\"%s\") → \"%s\" (expected: \"%s\")", strnull(s), ret, expected);
- assert_se(streq(ret, expected));
+ ASSERT_STREQ(ret, expected);
}
TEST(octescape) {
assert_se(ret = decescape(s, bad, strlen_ptr(s)));
log_debug("decescape(\"%s\") → \"%s\" (expected: \"%s\")", strnull(s), ret, expected);
- assert_se(streq(ret, expected));
+ ASSERT_STREQ(ret, expected);
}
TEST(decescape) {
if (r >= 0) {
if (!IN_SET(expected_len, 0, SIZE_MAX))
assert_se(h.length == expected_len);
- assert_se(streq(HW_ADDR_TO_STR(&h), expected));
+ ASSERT_STREQ(HW_ADDR_TO_STR(&h), expected);
}
}
execute_directories(dirs, DEFAULT_TIMEOUT_USEC, ignore_stdout, ignore_stdout_args, NULL, NULL, EXEC_DIR_PARALLEL | EXEC_DIR_IGNORE_ERRORS);
assert_se(read_full_file(output, &contents, NULL) >= 0);
- assert_se(streq(contents, "30-override\n80-foo\n90-bar\nlast\n"));
+ ASSERT_STREQ(contents, "30-override\n80-foo\n90-bar\nlast\n");
}
static int gather_stdout_one(int fd, void *arg) {
log_info("got: %s", output);
- assert_se(streq(output, "a\nb\nc\nd\n"));
+ ASSERT_STREQ(output, "a\nb\nc\nd\n");
}
TEST(environment_gathering) {
STRV_FOREACH(p, env)
log_info("got env: \"%s\"", *p);
- assert_se(streq(strv_env_get(env, "A"), "22:23:24"));
- assert_se(streq(strv_env_get(env, "B"), "12"));
- assert_se(streq(strv_env_get(env, "C"), "001"));
- assert_se(streq(strv_env_get(env, "PATH"), "no-sh-built-in-path:/no/such/file"));
+ ASSERT_STREQ(strv_env_get(env, "A"), "22:23:24");
+ ASSERT_STREQ(strv_env_get(env, "B"), "12");
+ ASSERT_STREQ(strv_env_get(env, "C"), "001");
+ ASSERT_STREQ(strv_env_get(env, "PATH"), "no-sh-built-in-path:/no/such/file");
/* now retest with "default" path passed in, as created by
* manager_default_environment */
STRV_FOREACH(p, env)
log_info("got env: \"%s\"", *p);
- assert_se(streq(strv_env_get(env, "A"), "22:23:24"));
- assert_se(streq(strv_env_get(env, "B"), "12"));
- assert_se(streq(strv_env_get(env, "C"), "001"));
- assert_se(streq(strv_env_get(env, "PATH"), DEFAULT_PATH ":/no/such/file"));
+ ASSERT_STREQ(strv_env_get(env, "A"), "22:23:24");
+ ASSERT_STREQ(strv_env_get(env, "B"), "12");
+ ASSERT_STREQ(strv_env_get(env, "C"), "001");
+ ASSERT_STREQ(strv_env_get(env, "PATH"), DEFAULT_PATH ":/no/such/file");
/* reset environ PATH */
assert_se(set_unset_env("PATH", old, true) == 0);
#include "sd-event.h"
+#include "build-path.h"
#include "capability-util.h"
#include "cpu-set-util.h"
#include "copy.h"
#if defined(__x86_64__)
test(m, "exec-personality-x86-64.service", 0, CLD_EXITED);
+#elif defined(__s390x__)
+ test(m, "exec-personality-s390x.service", 0, CLD_EXITED);
+
#elif defined(__s390__)
test(m, "exec-personality-s390.service", 0, CLD_EXITED);
static void test_exec_standardinput(Manager *m) {
test(m, "exec-standardinput-data.service", 0, CLD_EXITED);
test(m, "exec-standardinput-file.service", 0, CLD_EXITED);
+
+ ExecOutput saved = m->defaults.std_output;
+ m->defaults.std_output = EXEC_OUTPUT_NULL;
test(m, "exec-standardinput-file-cat.service", 0, CLD_EXITED);
+ m->defaults.std_output = saved;
}
static void test_exec_standardoutput(Manager *m) {
return (void) log_tests_skipped_errno(r, "manager_new");
assert_se(r >= 0);
- m->defaults.std_output = EXEC_OUTPUT_NULL; /* don't rely on host journald */
+ m->defaults.std_output = EXEC_OUTPUT_INHERIT; /* don't rely on host journald */
assert_se(manager_startup(m, NULL, NULL, NULL) >= 0);
/* Uncomment below if you want to make debugging logs stored to journal. */
NULL);
assert_se(r >= 0);
if (r == 0) {
- _cleanup_free_ char *unit_dir = NULL;
+ _cleanup_free_ char *unit_dir = NULL, *build_dir = NULL, *build_dir_mount = NULL;
+ int ret;
/* Make "/" read-only. */
assert_se(mount_nofollow_verbose(LOG_DEBUG, NULL, "/", NULL, MS_BIND|MS_REMOUNT|MS_RDONLY, NULL) >= 0);
assert_se(mkdir_p(PRIVATE_UNIT_DIR, 0755) >= 0);
assert_se(mount_nofollow_verbose(LOG_DEBUG, "tmpfs", PRIVATE_UNIT_DIR, "tmpfs", MS_NOSUID|MS_NODEV, NULL) >= 0);
+ /* Mark our test "playground" as MS_SLAVE, so we can MS_MOVE mounts underneath it. */
+ assert_se(mount_nofollow_verbose(LOG_DEBUG, NULL, PRIVATE_UNIT_DIR, NULL, MS_SLAVE, NULL) >= 0);
/* Copy unit files to make them accessible even when unprivileged. */
assert_se(get_testdata_dir("test-execute/", &unit_dir) >= 0);
assert_se(copy_directory_at(AT_FDCWD, unit_dir, AT_FDCWD, PRIVATE_UNIT_DIR, COPY_MERGE_EMPTY) >= 0);
/* Mount tmpfs on the following directories to make not StateDirectory= or friends disturb the host. */
+ ret = get_build_exec_dir(&build_dir);
+ assert_se(ret >= 0 || ret == -ENOEXEC);
+
+ if (build_dir) {
+ /* Account for a build directory being in one of the soon-to-be-tmpfs directories. If we
+ * overmount it with an empty tmpfs, manager_new() will pin the wrong systemd-executor binary,
+ * which can then lead to unexpected (and painful to debug) test fails. */
+ assert_se(access(build_dir, F_OK) >= 0);
+ assert_se(build_dir_mount = path_join(PRIVATE_UNIT_DIR, "build_dir"));
+ assert_se(mkdir_p(build_dir_mount, 0755) >= 0);
+ assert_se(mount_nofollow_verbose(LOG_DEBUG, build_dir, build_dir_mount, NULL, MS_BIND, NULL) >= 0);
+ }
+
FOREACH_STRING(p, "/dev/shm", "/root", "/tmp", "/var/tmp", "/var/lib")
assert_se(mount_nofollow_verbose(LOG_DEBUG, "tmpfs", p, "tmpfs", MS_NOSUID|MS_NODEV, NULL) >= 0);
+ if (build_dir_mount) {
+ ret = RET_NERRNO(access(build_dir, F_OK));
+ assert_se(ret >= 0 || ret == -ENOENT);
+
+ if (ret == -ENOENT) {
+ /* The build directory got overmounted by tmpfs, so let's use the "backup" bind mount to
+ * bring it back. */
+ assert_se(mkdir_p(build_dir, 0755) >= 0);
+ assert_se(mount_nofollow_verbose(LOG_DEBUG, build_dir_mount, build_dir, NULL, MS_MOVE, NULL) >= 0);
+ }
+ }
+
/* Prepare credstore like tmpfiles.d/credstore.conf for LoadCredential= tests. */
FOREACH_STRING(p, "/run/credstore", "/run/credstore.encrypted") {
assert_se(mkdir_p(p, 0) >= 0);
}
TEST(exit_status_NUMA_POLICY) {
- assert_se(streq(exit_status_to_string(EXIT_NUMA_POLICY, EXIT_STATUS_FULL), "NUMA_POLICY"));
- assert_se(streq(exit_status_to_string(EXIT_NUMA_POLICY, EXIT_STATUS_SYSTEMD), "NUMA_POLICY"));
+ ASSERT_STREQ(exit_status_to_string(EXIT_NUMA_POLICY, EXIT_STATUS_FULL), "NUMA_POLICY");
+ ASSERT_STREQ(exit_status_to_string(EXIT_NUMA_POLICY, EXIT_STATUS_SYSTEMD), "NUMA_POLICY");
assert_se(!exit_status_to_string(EXIT_NUMA_POLICY, EXIT_STATUS_BSD));
assert_se(!exit_status_to_string(EXIT_NUMA_POLICY, EXIT_STATUS_LSB));
}
p = original = "foobar waldo";
assert_se(extract_first_word(&p, &t, NULL, 0) > 0);
- assert_se(streq(t, "foobar"));
+ ASSERT_STREQ(t, "foobar");
free(t);
assert_se(p == original + 7);
assert_se(extract_first_word(&p, &t, NULL, 0) > 0);
- assert_se(streq(t, "waldo"));
+ ASSERT_STREQ(t, "waldo");
free(t);
assert_se(isempty(p));
p = original = "\"foobar\" \'waldo\'";
assert_se(extract_first_word(&p, &t, NULL, 0) > 0);
- assert_se(streq(t, "\"foobar\""));
+ ASSERT_STREQ(t, "\"foobar\"");
free(t);
assert_se(p == original + 9);
assert_se(extract_first_word(&p, &t, NULL, 0) > 0);
- assert_se(streq(t, "\'waldo\'"));
+ ASSERT_STREQ(t, "\'waldo\'");
free(t);
assert_se(isempty(p));
p = original = "\"foobar\" \'waldo\'";
assert_se(extract_first_word(&p, &t, NULL, EXTRACT_UNQUOTE) > 0);
- assert_se(streq(t, "foobar"));
+ ASSERT_STREQ(t, "foobar");
free(t);
assert_se(p == original + 9);
assert_se(extract_first_word(&p, &t, NULL, EXTRACT_UNQUOTE) > 0);
- assert_se(streq(t, "waldo"));
+ ASSERT_STREQ(t, "waldo");
free(t);
assert_se(isempty(p));
p = original = "\"";
assert_se(extract_first_word(&p, &t, NULL, 0) == 1);
- assert_se(streq(t, "\""));
+ ASSERT_STREQ(t, "\"");
free(t);
assert_se(isempty(p));
p = original = "\'";
assert_se(extract_first_word(&p, &t, NULL, 0) == 1);
- assert_se(streq(t, "\'"));
+ ASSERT_STREQ(t, "\'");
free(t);
assert_se(isempty(p));
p = original = "\'fooo";
assert_se(extract_first_word(&p, &t, NULL, 0) == 1);
- assert_se(streq(t, "\'fooo"));
+ ASSERT_STREQ(t, "\'fooo");
free(t);
assert_se(isempty(p));
p = original = "KEY=val \"KEY2=val with space\" \"KEY3=val with \\\"quotation\\\"\"";
assert_se(extract_first_word(&p, &t, NULL, EXTRACT_UNQUOTE) == 1);
- assert_se(streq(t, "KEY=val"));
+ ASSERT_STREQ(t, "KEY=val");
free(t);
assert_se(extract_first_word(&p, &t, NULL, EXTRACT_UNQUOTE) == 1);
- assert_se(streq(t, "KEY2=val with space"));
+ ASSERT_STREQ(t, "KEY2=val with space");
free(t);
assert_se(extract_first_word(&p, &t, NULL, EXTRACT_UNQUOTE) == 1);
- assert_se(streq(t, "KEY3=val with \"quotation\""));
+ ASSERT_STREQ(t, "KEY3=val with \"quotation\"");
free(t);
assert_se(isempty(p));
p = original = "KEY=val \"KEY2=val space\" \"KEY3=val with \\\"quotation\\\"\"";
assert_se(extract_first_word(&p, &t, NULL, EXTRACT_RETAIN_ESCAPE) == 1);
- assert_se(streq(t, "KEY=val"));
+ ASSERT_STREQ(t, "KEY=val");
free(t);
assert_se(extract_first_word(&p, &t, NULL, EXTRACT_RETAIN_ESCAPE) == 1);
- assert_se(streq(t, "\"KEY2=val"));
+ ASSERT_STREQ(t, "\"KEY2=val");
free(t);
assert_se(extract_first_word(&p, &t, NULL, EXTRACT_RETAIN_ESCAPE) == 1);
- assert_se(streq(t, "space\""));
+ ASSERT_STREQ(t, "space\"");
free(t);
assert_se(startswith(p, "\"KEY3="));
p = original = "\'fooo";
assert_se(extract_first_word(&p, &t, NULL, EXTRACT_UNQUOTE|EXTRACT_RELAX) > 0);
- assert_se(streq(t, "fooo"));
+ ASSERT_STREQ(t, "fooo");
free(t);
assert_se(isempty(p));
p = original = "\"fooo";
assert_se(extract_first_word(&p, &t, NULL, EXTRACT_UNQUOTE|EXTRACT_RELAX) > 0);
- assert_se(streq(t, "fooo"));
+ ASSERT_STREQ(t, "fooo");
free(t);
assert_se(isempty(p));
p = original = "yay\'foo\'bar";
assert_se(extract_first_word(&p, &t, NULL, 0) > 0);
- assert_se(streq(t, "yay\'foo\'bar"));
+ ASSERT_STREQ(t, "yay\'foo\'bar");
free(t);
assert_se(isempty(p));
p = original = "yay\'foo\'bar";
assert_se(extract_first_word(&p, &t, NULL, EXTRACT_UNQUOTE) > 0);
- assert_se(streq(t, "yayfoobar"));
+ ASSERT_STREQ(t, "yayfoobar");
free(t);
assert_se(isempty(p));
p = original = " foobar ";
assert_se(extract_first_word(&p, &t, NULL, 0) > 0);
- assert_se(streq(t, "foobar"));
+ ASSERT_STREQ(t, "foobar");
free(t);
assert_se(isempty(p));
p = original = " foo\\ba\\x6ar ";
assert_se(extract_first_word(&p, &t, NULL, EXTRACT_CUNESCAPE) > 0);
- assert_se(streq(t, "foo\ba\x6ar"));
+ ASSERT_STREQ(t, "foo\ba\x6ar");
free(t);
assert_se(isempty(p));
p = original = " foo\\ba\\x6ar ";
assert_se(extract_first_word(&p, &t, NULL, 0) > 0);
- assert_se(streq(t, "foobax6ar"));
+ ASSERT_STREQ(t, "foobax6ar");
free(t);
assert_se(isempty(p));
p = original = " f\\u00f6o \"pi\\U0001F4A9le\" ";
assert_se(extract_first_word(&p, &t, NULL, EXTRACT_CUNESCAPE) > 0);
- assert_se(streq(t, "föo"));
+ ASSERT_STREQ(t, "föo");
free(t);
assert_se(p == original + 13);
assert_se(extract_first_word(&p, &t, NULL, EXTRACT_UNQUOTE|EXTRACT_CUNESCAPE) > 0);
- assert_se(streq(t, "pi\360\237\222\251le"));
+ ASSERT_STREQ(t, "pi\360\237\222\251le");
free(t);
assert_se(isempty(p));
p = original = "fooo\\";
assert_se(extract_first_word(&p, &t, NULL, EXTRACT_RELAX) > 0);
- assert_se(streq(t, "fooo"));
+ ASSERT_STREQ(t, "fooo");
free(t);
assert_se(isempty(p));
p = original = "fooo\\";
assert_se(extract_first_word(&p, &t, NULL, EXTRACT_UNESCAPE_RELAX) > 0);
- assert_se(streq(t, "fooo\\"));
+ ASSERT_STREQ(t, "fooo\\");
free(t);
assert_se(isempty(p));
p = original = "fooo\\";
assert_se(extract_first_word(&p, &t, NULL, EXTRACT_UNESCAPE_RELAX|EXTRACT_RELAX) > 0);
- assert_se(streq(t, "fooo\\"));
+ ASSERT_STREQ(t, "fooo\\");
free(t);
assert_se(isempty(p));
p = original = "fooo\\";
assert_se(extract_first_word(&p, &t, NULL, EXTRACT_CUNESCAPE|EXTRACT_UNESCAPE_RELAX) > 0);
- assert_se(streq(t, "fooo\\"));
+ ASSERT_STREQ(t, "fooo\\");
free(t);
assert_se(isempty(p));
p = original = "\"foo\\";
assert_se(extract_first_word(&p, &t, NULL, EXTRACT_UNQUOTE|EXTRACT_RELAX) > 0);
- assert_se(streq(t, "foo"));
+ ASSERT_STREQ(t, "foo");
free(t);
assert_se(isempty(p));
p = original = "foo::bar";
assert_se(extract_first_word(&p, &t, ":", 0) == 1);
- assert_se(streq(t, "foo"));
+ ASSERT_STREQ(t, "foo");
free(t);
assert_se(p == original + 5);
assert_se(extract_first_word(&p, &t, ":", 0) == 1);
- assert_se(streq(t, "bar"));
+ ASSERT_STREQ(t, "bar");
free(t);
assert_se(isempty(p));
p = original = "foo\\:bar::waldo";
assert_se(extract_first_word(&p, &t, ":", 0) == 1);
- assert_se(streq(t, "foo:bar"));
+ ASSERT_STREQ(t, "foo:bar");
free(t);
assert_se(p == original + 10);
assert_se(extract_first_word(&p, &t, ":", 0) == 1);
- assert_se(streq(t, "waldo"));
+ ASSERT_STREQ(t, "waldo");
free(t);
assert_se(isempty(p));
p = original = "\"foo\\";
assert_se(extract_first_word(&p, &t, NULL, EXTRACT_UNQUOTE|EXTRACT_UNESCAPE_RELAX|EXTRACT_RELAX) > 0);
- assert_se(streq(t, "foo\\"));
+ ASSERT_STREQ(t, "foo\\");
free(t);
assert_se(isempty(p));
p = original = "\"foo\\";
assert_se(extract_first_word(&p, &t, NULL, EXTRACT_UNQUOTE|EXTRACT_CUNESCAPE|EXTRACT_UNESCAPE_RELAX|EXTRACT_RELAX) > 0);
- assert_se(streq(t, "foo\\"));
+ ASSERT_STREQ(t, "foo\\");
free(t);
assert_se(isempty(p));
p = original = "fooo\\ bar quux";
assert_se(extract_first_word(&p, &t, NULL, EXTRACT_RELAX) > 0);
- assert_se(streq(t, "fooo bar"));
+ ASSERT_STREQ(t, "fooo bar");
free(t);
assert_se(p == original + 10);
p = original = "fooo\\ bar quux";
assert_se(extract_first_word(&p, &t, NULL, EXTRACT_UNESCAPE_RELAX) > 0);
- assert_se(streq(t, "fooo bar"));
+ ASSERT_STREQ(t, "fooo bar");
free(t);
assert_se(p == original + 10);
p = original = "fooo\\ bar quux";
assert_se(extract_first_word(&p, &t, NULL, EXTRACT_UNESCAPE_RELAX|EXTRACT_RELAX) > 0);
- assert_se(streq(t, "fooo bar"));
+ ASSERT_STREQ(t, "fooo bar");
free(t);
assert_se(p == original + 10);
p = original = "fooo\\ bar quux";
assert_se(extract_first_word(&p, &t, NULL, EXTRACT_CUNESCAPE|EXTRACT_UNESCAPE_RELAX) > 0);
- assert_se(streq(t, "fooo\\ bar"));
+ ASSERT_STREQ(t, "fooo\\ bar");
free(t);
assert_se(p == original + 10);
p = original = "\\w+@\\K[\\d.]+";
assert_se(extract_first_word(&p, &t, NULL, EXTRACT_CUNESCAPE|EXTRACT_UNESCAPE_RELAX) > 0);
- assert_se(streq(t, "\\w+@\\K[\\d.]+"));
+ ASSERT_STREQ(t, "\\w+@\\K[\\d.]+");
free(t);
assert_se(isempty(p));
p = original = "\\w+\\b";
assert_se(extract_first_word(&p, &t, NULL, EXTRACT_CUNESCAPE|EXTRACT_UNESCAPE_RELAX) > 0);
- assert_se(streq(t, "\\w+\b"));
+ ASSERT_STREQ(t, "\\w+\b");
free(t);
assert_se(isempty(p));
p = original = "-N ''";
assert_se(extract_first_word(&p, &t, NULL, EXTRACT_UNQUOTE) > 0);
- assert_se(streq(t, "-N"));
+ ASSERT_STREQ(t, "-N");
free(t);
assert_se(p == original + 3);
assert_se(extract_first_word(&p, &t, NULL, EXTRACT_UNQUOTE) > 0);
- assert_se(streq(t, ""));
+ ASSERT_STREQ(t, "");
free(t);
assert_se(isempty(p));
p = original = ":foo\\:bar::waldo:";
assert_se(extract_first_word(&p, &t, ":", EXTRACT_DONT_COALESCE_SEPARATORS) == 1);
assert_se(t);
- assert_se(streq(t, ""));
+ ASSERT_STREQ(t, "");
free(t);
assert_se(p == original + 1);
assert_se(extract_first_word(&p, &t, ":", EXTRACT_DONT_COALESCE_SEPARATORS) == 1);
- assert_se(streq(t, "foo:bar"));
+ ASSERT_STREQ(t, "foo:bar");
free(t);
assert_se(p == original + 10);
assert_se(extract_first_word(&p, &t, ":", EXTRACT_DONT_COALESCE_SEPARATORS) == 1);
assert_se(t);
- assert_se(streq(t, ""));
+ ASSERT_STREQ(t, "");
free(t);
assert_se(p == original + 11);
assert_se(extract_first_word(&p, &t, ":", EXTRACT_DONT_COALESCE_SEPARATORS) == 1);
- assert_se(streq(t, "waldo"));
+ ASSERT_STREQ(t, "waldo");
free(t);
assert_se(p == original + 17);
assert_se(extract_first_word(&p, &t, ":", EXTRACT_DONT_COALESCE_SEPARATORS) == 1);
- assert_se(streq(t, ""));
+ ASSERT_STREQ(t, "");
free(t);
- assert_se(p == NULL);
+ ASSERT_NULL(p);
assert_se(extract_first_word(&p, &t, ":", EXTRACT_DONT_COALESCE_SEPARATORS) == 0);
assert_se(!t);
p = "foo\\xbar";
assert_se(extract_first_word(&p, &t, NULL, 0) > 0);
- assert_se(streq(t, "fooxbar"));
+ ASSERT_STREQ(t, "fooxbar");
free(t);
- assert_se(p == NULL);
+ ASSERT_NULL(p);
p = "foo\\xbar";
assert_se(extract_first_word(&p, &t, NULL, EXTRACT_RETAIN_ESCAPE) > 0);
- assert_se(streq(t, "foo\\xbar"));
+ ASSERT_STREQ(t, "foo\\xbar");
free(t);
- assert_se(p == NULL);
+ ASSERT_NULL(p);
p = "\\:";
assert_se(extract_first_word(&p, &t, ":", EXTRACT_UNESCAPE_SEPARATORS) == 1);
- assert_se(streq(t, ":"));
+ ASSERT_STREQ(t, ":");
free(t);
- assert_se(p == NULL);
+ ASSERT_NULL(p);
p = "a\\:b";
assert_se(extract_first_word(&p, &t, ":", EXTRACT_UNESCAPE_SEPARATORS) == 1);
- assert_se(streq(t, "a:b"));
+ ASSERT_STREQ(t, "a:b");
free(t);
- assert_se(p == NULL);
+ ASSERT_NULL(p);
p = "a\\ b:c";
assert_se(extract_first_word(&p, &t, WHITESPACE ":", EXTRACT_UNESCAPE_SEPARATORS) == 1);
- assert_se(streq(t, "a b"));
+ ASSERT_STREQ(t, "a b");
free(t);
assert_se(extract_first_word(&p, &t, WHITESPACE ":", EXTRACT_UNESCAPE_SEPARATORS) == 1);
- assert_se(streq(t, "c"));
+ ASSERT_STREQ(t, "c");
free(t);
- assert_se(p == NULL);
+ ASSERT_NULL(p);
p = "a\\ b:c\\x";
assert_se(extract_first_word(&p, &t, ":", EXTRACT_UNESCAPE_SEPARATORS) == -EINVAL);
p = "a\\\\ b:c\\\\x";
assert_se(extract_first_word(&p, &t, ":", EXTRACT_UNESCAPE_SEPARATORS) == 1);
- assert_se(streq(t, "a\\ b"));
+ ASSERT_STREQ(t, "a\\ b");
free(t);
assert_se(extract_first_word(&p, &t, ":", EXTRACT_UNESCAPE_SEPARATORS) == 1);
- assert_se(streq(t, "c\\x"));
+ ASSERT_STREQ(t, "c\\x");
free(t);
- assert_se(p == NULL);
+ ASSERT_NULL(p);
p = "\\:";
assert_se(extract_first_word(&p, &t, ":", EXTRACT_CUNESCAPE|EXTRACT_UNESCAPE_SEPARATORS) == 1);
- assert_se(streq(t, ":"));
+ ASSERT_STREQ(t, ":");
free(t);
- assert_se(p == NULL);
+ ASSERT_NULL(p);
p = "a\\:b";
assert_se(extract_first_word(&p, &t, ":", EXTRACT_CUNESCAPE|EXTRACT_UNESCAPE_SEPARATORS) == 1);
- assert_se(streq(t, "a:b"));
+ ASSERT_STREQ(t, "a:b");
free(t);
- assert_se(p == NULL);
+ ASSERT_NULL(p);
p = "a\\ b:c";
assert_se(extract_first_word(&p, &t, WHITESPACE ":", EXTRACT_CUNESCAPE|EXTRACT_UNESCAPE_SEPARATORS) == 1);
- assert_se(streq(t, "a b"));
+ ASSERT_STREQ(t, "a b");
free(t);
assert_se(extract_first_word(&p, &t, WHITESPACE ":", EXTRACT_CUNESCAPE|EXTRACT_UNESCAPE_SEPARATORS) == 1);
- assert_se(streq(t, "c"));
+ ASSERT_STREQ(t, "c");
free(t);
- assert_se(p == NULL);
+ ASSERT_NULL(p);
p = "a\\ b:c\\x";
assert_se(extract_first_word(&p, &t, ":", EXTRACT_CUNESCAPE|EXTRACT_UNESCAPE_SEPARATORS) == -EINVAL);
p = "a\\\\ b:c\\\\x";
assert_se(extract_first_word(&p, &t, ":", EXTRACT_CUNESCAPE|EXTRACT_UNESCAPE_SEPARATORS) == 1);
- assert_se(streq(t, "a\\ b"));
+ ASSERT_STREQ(t, "a\\ b");
free(t);
assert_se(extract_first_word(&p, &t, ":", EXTRACT_CUNESCAPE|EXTRACT_UNESCAPE_SEPARATORS) == 1);
- assert_se(streq(t, "c\\x"));
+ ASSERT_STREQ(t, "c\\x");
free(t);
- assert_se(p == NULL);
+ ASSERT_NULL(p);
p = "\\:";
assert_se(extract_first_word(&p, &t, ":", EXTRACT_CUNESCAPE) == -EINVAL);
p = "a\\:b";
assert_se(extract_first_word(&p, &t, ":", EXTRACT_CUNESCAPE) == -EINVAL);
assert_se(extract_first_word(&p, &t, ":", EXTRACT_CUNESCAPE) == 1);
- assert_se(streq(t, "b"));
+ ASSERT_STREQ(t, "b");
free(t);
p = "a\\ b:c";
assert_se(extract_first_word(&p, &t, WHITESPACE ":", EXTRACT_CUNESCAPE) == -EINVAL);
assert_se(extract_first_word(&p, &t, WHITESPACE ":", EXTRACT_CUNESCAPE) == 1);
- assert_se(streq(t, "b"));
+ ASSERT_STREQ(t, "b");
free(t);
assert_se(extract_first_word(&p, &t, WHITESPACE ":", EXTRACT_CUNESCAPE) == 1);
- assert_se(streq(t, "c"));
+ ASSERT_STREQ(t, "c");
free(t);
- assert_se(p == NULL);
+ ASSERT_NULL(p);
p = original = "foobar=\"waldo\"maldo, baldo";
assert_se(extract_first_word(&p, &t, "=\", ", 0) > 0);
- assert_se(streq(t, "foobar"));
+ ASSERT_STREQ(t, "foobar");
free(t);
assert_se(extract_first_word(&p, &t, "=\", ", 0) > 0);
- assert_se(streq(t, "waldo"));
+ ASSERT_STREQ(t, "waldo");
free(t);
assert_se(extract_first_word(&p, &t, "=\", ", 0) > 0);
- assert_se(streq(t, "maldo"));
+ ASSERT_STREQ(t, "maldo");
free(t);
assert_se(extract_first_word(&p, &t, "=\", ", 0) > 0);
- assert_se(streq(t, "baldo"));
+ ASSERT_STREQ(t, "baldo");
free(t);
p = original = "mode=\"1777\",size=\"10%\",nr_inodes=\"400\"k,uid=\"496,,107\"520,gi\"\"'d=49610,'\"\"7520,context=\"system_u:object_r:svirt_sandbox_file_t:s0:c0,c1\"";
assert_se(extract_first_word(&p, &t, ",", EXTRACT_KEEP_QUOTE) > 0);
- assert_se(streq(t, "mode=\"1777\""));
+ ASSERT_STREQ(t, "mode=\"1777\"");
free(t);
assert_se(extract_first_word(&p, &t, ",", EXTRACT_KEEP_QUOTE) > 0);
- assert_se(streq(t, "size=\"10%\""));
+ ASSERT_STREQ(t, "size=\"10%\"");
free(t);
assert_se(extract_first_word(&p, &t, ",", EXTRACT_KEEP_QUOTE) > 0);
- assert_se(streq(t, "nr_inodes=\"400\"k"));
+ ASSERT_STREQ(t, "nr_inodes=\"400\"k");
free(t);
assert_se(extract_first_word(&p, &t, ",", EXTRACT_KEEP_QUOTE) > 0);
- assert_se(streq(t, "uid=\"496,,107\"520"));
+ ASSERT_STREQ(t, "uid=\"496,,107\"520");
free(t);
assert_se(extract_first_word(&p, &t, ",", EXTRACT_KEEP_QUOTE) > 0);
- assert_se(streq(t, "gi\"\"'d=49610,'\"\"7520"));
+ ASSERT_STREQ(t, "gi\"\"'d=49610,'\"\"7520");
free(t);
assert_se(extract_first_word(&p, &t, ",", EXTRACT_KEEP_QUOTE) > 0);
- assert_se(streq(t, "context=\"system_u:object_r:svirt_sandbox_file_t:s0:c0,c1\""));
+ ASSERT_STREQ(t, "context=\"system_u:object_r:svirt_sandbox_file_t:s0:c0,c1\"");
free(t);
p = original = "mode=\"1777\",size=\"10%\",nr_inodes=\"400\"k,uid=\"496,,107\"520,gi\"\"'d=49610,'\"\"7520,context=\"system_u:object_r:svirt_sandbox_file_t:s0:c0,c1\"";
assert_se(extract_first_word(&p, &t, ",", EXTRACT_UNQUOTE) > 0);
- assert_se(streq(t, "mode=1777"));
+ ASSERT_STREQ(t, "mode=1777");
free(t);
assert_se(extract_first_word(&p, &t, ",", EXTRACT_UNQUOTE) > 0);
- assert_se(streq(t, "size=10%"));
+ ASSERT_STREQ(t, "size=10%");
free(t);
assert_se(extract_first_word(&p, &t, ",", EXTRACT_UNQUOTE) > 0);
- assert_se(streq(t, "nr_inodes=400k"));
+ ASSERT_STREQ(t, "nr_inodes=400k");
free(t);
assert_se(extract_first_word(&p, &t, ",", EXTRACT_UNQUOTE) > 0);
- assert_se(streq(t, "uid=496,,107520"));
+ ASSERT_STREQ(t, "uid=496,,107520");
free(t);
assert_se(extract_first_word(&p, &t, ",", EXTRACT_UNQUOTE) > 0);
- assert_se(streq(t, "gid=49610,7520"));
+ ASSERT_STREQ(t, "gid=49610,7520");
free(t);
assert_se(extract_first_word(&p, &t, ",", EXTRACT_UNQUOTE) > 0);
- assert_se(streq(t, "context=system_u:object_r:svirt_sandbox_file_t:s0:c0,c1"));
+ ASSERT_STREQ(t, "context=system_u:object_r:svirt_sandbox_file_t:s0:c0,c1");
free(t);
p = "a:b";
assert_se(extract_first_word(&p, &t, ":", EXTRACT_RETAIN_SEPARATORS) == 1);
- assert_se(streq(t, "a"));
- assert_se(streq(p, ":b"));
+ ASSERT_STREQ(t, "a");
+ ASSERT_STREQ(p, ":b");
free(t);
assert_se(extract_first_word(&p, &t, ":", EXTRACT_RETAIN_SEPARATORS) == 1);
- assert_se(streq(t, "b"));
+ ASSERT_STREQ(t, "b");
free(t);
p = "a>:b";
assert_se(extract_first_word(&p, &t, ">:", EXTRACT_RETAIN_SEPARATORS) == 1);
- assert_se(streq(t, "a"));
- assert_se(streq(p, ">:b"));
+ ASSERT_STREQ(t, "a");
+ ASSERT_STREQ(p, ">:b");
free(t);
assert_se(extract_first_word(&p, &t, ">:", EXTRACT_RETAIN_SEPARATORS) == 1);
- assert_se(streq(t, "b"));
+ ASSERT_STREQ(t, "b");
free(t);
p = "a>:b";
assert_se(extract_first_word(&p, &t, ">:", EXTRACT_RETAIN_SEPARATORS|EXTRACT_DONT_COALESCE_SEPARATORS) == 1);
- assert_se(streq(t, "a"));
- assert_se(streq(p, ">:b"));
+ ASSERT_STREQ(t, "a");
+ ASSERT_STREQ(p, ">:b");
free(t);
assert_se(extract_first_word(&p, &t, ">:", EXTRACT_RETAIN_SEPARATORS|EXTRACT_DONT_COALESCE_SEPARATORS) == 1);
- assert_se(streq(t, ""));
- assert_se(streq(p, ">:b"));
+ ASSERT_STREQ(t, "");
+ ASSERT_STREQ(p, ">:b");
free(t);
p = "a\\:b";
assert_se(extract_first_word(&p, &t, ":", EXTRACT_RETAIN_SEPARATORS|EXTRACT_RETAIN_ESCAPE) == 1);
- assert_se(streq(t, "a\\"));
- assert_se(streq(p, ":b"));
+ ASSERT_STREQ(t, "a\\");
+ ASSERT_STREQ(p, ":b");
free(t);
p = "a\\:b";
assert_se(extract_first_word(&p, &t, ":", EXTRACT_RETAIN_SEPARATORS) == 1);
- assert_se(streq(t, "a:b"));
+ ASSERT_STREQ(t, "a:b");
assert_se(!p);
free(t);
p = "a\\:b";
assert_se(extract_first_word(&p, &t, ":", EXTRACT_RETAIN_SEPARATORS|EXTRACT_UNESCAPE_SEPARATORS) == 1);
- assert_se(streq(t, "a:b"));
+ ASSERT_STREQ(t, "a:b");
assert_se(!p);
free(t);
p = "a\\:a:b";
assert_se(extract_first_word(&p, &t, ":", EXTRACT_RETAIN_SEPARATORS|EXTRACT_UNESCAPE_SEPARATORS) == 1);
- assert_se(streq(t, "a:a"));
- assert_se(streq(p, ":b"));
+ ASSERT_STREQ(t, "a:a");
+ ASSERT_STREQ(p, ":b");
free(t);
p = original = "zażółcić 👊🔪💐 가너도루";
assert_se(extract_first_word(&p, &t, NULL, 0) > 0);
- assert_se(streq(t, "zażółcić"));
+ ASSERT_STREQ(t, "zażółcić");
free(t);
assert_se(p == original + 13);
assert_se(extract_first_word(&p, &t, NULL, 0) > 0);
- assert_se(streq(t, "👊🔪💐"));
+ ASSERT_STREQ(t, "👊🔪💐");
free(t);
assert_se(extract_first_word(&p, &t, NULL, 0) > 0);
- assert_se(streq(t, "가너도루"));
+ ASSERT_STREQ(t, "가너도루");
free(t);
assert_se(isempty(p));
}
p = original = "foobar waldo";
assert_se(extract_first_word_and_warn(&p, &t, NULL, 0, NULL, "fake", 1, original) > 0);
- assert_se(streq(t, "foobar"));
+ ASSERT_STREQ(t, "foobar");
free(t);
assert_se(p == original + 7);
assert_se(extract_first_word_and_warn(&p, &t, NULL, 0, NULL, "fake", 1, original) > 0);
- assert_se(streq(t, "waldo"));
+ ASSERT_STREQ(t, "waldo");
free(t);
assert_se(isempty(p));
p = original = "\"foobar\" \'waldo\'";
assert_se(extract_first_word_and_warn(&p, &t, NULL, EXTRACT_UNQUOTE, NULL, "fake", 1, original) > 0);
- assert_se(streq(t, "foobar"));
+ ASSERT_STREQ(t, "foobar");
free(t);
assert_se(p == original + 9);
assert_se(extract_first_word_and_warn(&p, &t, NULL, EXTRACT_UNQUOTE, NULL, "fake", 1, original) > 0);
- assert_se(streq(t, "waldo"));
+ ASSERT_STREQ(t, "waldo");
free(t);
assert_se(isempty(p));
p = original = "\'fooo";
assert_se(extract_first_word_and_warn(&p, &t, NULL, EXTRACT_UNQUOTE|EXTRACT_RELAX, NULL, "fake", 1, original) > 0);
- assert_se(streq(t, "fooo"));
+ ASSERT_STREQ(t, "fooo");
free(t);
assert_se(isempty(p));
p = original = " foo\\ba\\x6ar ";
assert_se(extract_first_word_and_warn(&p, &t, NULL, EXTRACT_CUNESCAPE, NULL, "fake", 1, original) > 0);
- assert_se(streq(t, "foo\ba\x6ar"));
+ ASSERT_STREQ(t, "foo\ba\x6ar");
free(t);
assert_se(isempty(p));
p = original = " foo\\ba\\x6ar ";
assert_se(extract_first_word_and_warn(&p, &t, NULL, 0, NULL, "fake", 1, original) > 0);
- assert_se(streq(t, "foobax6ar"));
+ ASSERT_STREQ(t, "foobax6ar");
free(t);
assert_se(isempty(p));
p = original = " f\\u00f6o \"pi\\U0001F4A9le\" ";
assert_se(extract_first_word_and_warn(&p, &t, NULL, EXTRACT_CUNESCAPE, NULL, "fake", 1, original) > 0);
- assert_se(streq(t, "föo"));
+ ASSERT_STREQ(t, "föo");
free(t);
assert_se(p == original + 13);
assert_se(extract_first_word_and_warn(&p, &t, NULL, EXTRACT_UNQUOTE|EXTRACT_CUNESCAPE, NULL, "fake", 1, original) > 0);
- assert_se(streq(t, "pi\360\237\222\251le"));
+ ASSERT_STREQ(t, "pi\360\237\222\251le");
free(t);
assert_se(isempty(p));
p = original = "fooo\\";
assert_se(extract_first_word_and_warn(&p, &t, NULL, EXTRACT_RELAX, NULL, "fake", 1, original) > 0);
- assert_se(streq(t, "fooo"));
+ ASSERT_STREQ(t, "fooo");
free(t);
assert_se(isempty(p));
p = original = "fooo\\";
assert_se(extract_first_word_and_warn(&p, &t, NULL, 0, NULL, "fake", 1, original) > 0);
- assert_se(streq(t, "fooo\\"));
+ ASSERT_STREQ(t, "fooo\\");
free(t);
assert_se(isempty(p));
p = original = "fooo\\";
assert_se(extract_first_word_and_warn(&p, &t, NULL, EXTRACT_CUNESCAPE, NULL, "fake", 1, original) > 0);
- assert_se(streq(t, "fooo\\"));
+ ASSERT_STREQ(t, "fooo\\");
free(t);
assert_se(isempty(p));
p = original = "\"foo\\";
assert_se(extract_first_word_and_warn(&p, &t, NULL, EXTRACT_UNQUOTE|EXTRACT_RELAX, NULL, "fake", 1, original) > 0);
- assert_se(streq(t, "foo"));
+ ASSERT_STREQ(t, "foo");
free(t);
assert_se(isempty(p));
p = original = "\"foo\\";
assert_se(extract_first_word_and_warn(&p, &t, NULL, EXTRACT_UNQUOTE|EXTRACT_CUNESCAPE|EXTRACT_RELAX, NULL, "fake", 1, original) > 0);
- assert_se(streq(t, "foo"));
+ ASSERT_STREQ(t, "foo");
free(t);
assert_se(isempty(p));
p = original = "fooo\\ bar quux";
assert_se(extract_first_word_and_warn(&p, &t, NULL, EXTRACT_RELAX, NULL, "fake", 1, original) > 0);
- assert_se(streq(t, "fooo bar"));
+ ASSERT_STREQ(t, "fooo bar");
free(t);
assert_se(p == original + 10);
p = original = "fooo\\ bar quux";
assert_se(extract_first_word_and_warn(&p, &t, NULL, 0, NULL, "fake", 1, original) > 0);
- assert_se(streq(t, "fooo bar"));
+ ASSERT_STREQ(t, "fooo bar");
free(t);
assert_se(p == original + 10);
p = original = "fooo\\ bar quux";
assert_se(extract_first_word_and_warn(&p, &t, NULL, EXTRACT_CUNESCAPE, NULL, "fake", 1, original) > 0);
- assert_se(streq(t, "fooo\\ bar"));
+ ASSERT_STREQ(t, "fooo\\ bar");
free(t);
assert_se(p == original + 10);
p = original = "\\w+@\\K[\\d.]+";
assert_se(extract_first_word_and_warn(&p, &t, NULL, EXTRACT_CUNESCAPE, NULL, "fake", 1, original) > 0);
- assert_se(streq(t, "\\w+@\\K[\\d.]+"));
+ ASSERT_STREQ(t, "\\w+@\\K[\\d.]+");
free(t);
assert_se(isempty(p));
p = original = "\\w+\\b";
assert_se(extract_first_word_and_warn(&p, &t, NULL, EXTRACT_CUNESCAPE, NULL, "fake", 1, original) > 0);
- assert_se(streq(t, "\\w+\b"));
+ ASSERT_STREQ(t, "\\w+\b");
free(t);
assert_se(isempty(p));
}
p = original = "foobar waldi piep";
assert_se(extract_many_words(&p, NULL, 0, &a, &b, &c) == 3);
assert_se(isempty(p));
- assert_se(streq_ptr(a, "foobar"));
- assert_se(streq_ptr(b, "waldi"));
- assert_se(streq_ptr(c, "piep"));
+ ASSERT_STREQ(a, "foobar");
+ ASSERT_STREQ(b, "waldi");
+ ASSERT_STREQ(c, "piep");
free(a);
free(b);
free(c);
p = original = "foobar:waldi:piep ba1:ba2";
assert_se(extract_many_words(&p, ":" WHITESPACE, 0, &a, &b, &c) == 3);
assert_se(!isempty(p));
- assert_se(streq_ptr(a, "foobar"));
- assert_se(streq_ptr(b, "waldi"));
- assert_se(streq_ptr(c, "piep"));
+ ASSERT_STREQ(a, "foobar");
+ ASSERT_STREQ(b, "waldi");
+ ASSERT_STREQ(c, "piep");
assert_se(extract_many_words(&p, ":" WHITESPACE, 0, &d, &e, &f) == 2);
assert_se(isempty(p));
- assert_se(streq_ptr(d, "ba1"));
- assert_se(streq_ptr(e, "ba2"));
+ ASSERT_STREQ(d, "ba1");
+ ASSERT_STREQ(e, "ba2");
assert_se(isempty(f));
free(a);
free(b);
p = original = "'foobar' wa\"ld\"i ";
assert_se(extract_many_words(&p, NULL, 0, &a, &b, &c) == 2);
assert_se(isempty(p));
- assert_se(streq_ptr(a, "'foobar'"));
- assert_se(streq_ptr(b, "wa\"ld\"i"));
- assert_se(streq_ptr(c, NULL));
+ ASSERT_STREQ(a, "'foobar'");
+ ASSERT_STREQ(b, "wa\"ld\"i");
+ ASSERT_STREQ(c, NULL);
free(a);
free(b);
p = original = "'foobar' wa\"ld\"i ";
assert_se(extract_many_words(&p, NULL, EXTRACT_UNQUOTE, &a, &b, &c) == 2);
assert_se(isempty(p));
- assert_se(streq_ptr(a, "foobar"));
- assert_se(streq_ptr(b, "waldi"));
- assert_se(streq_ptr(c, NULL));
+ ASSERT_STREQ(a, "foobar");
+ ASSERT_STREQ(b, "waldi");
+ ASSERT_STREQ(c, NULL);
free(a);
free(b);
p = original = "";
assert_se(extract_many_words(&p, NULL, 0, &a, &b, &c) == 0);
assert_se(isempty(p));
- assert_se(streq_ptr(a, NULL));
- assert_se(streq_ptr(b, NULL));
- assert_se(streq_ptr(c, NULL));
+ ASSERT_STREQ(a, NULL);
+ ASSERT_STREQ(b, NULL);
+ ASSERT_STREQ(c, NULL);
p = original = " ";
assert_se(extract_many_words(&p, NULL, 0, &a, &b, &c) == 0);
assert_se(isempty(p));
- assert_se(streq_ptr(a, NULL));
- assert_se(streq_ptr(b, NULL));
- assert_se(streq_ptr(c, NULL));
+ ASSERT_STREQ(a, NULL);
+ ASSERT_STREQ(b, NULL);
+ ASSERT_STREQ(c, NULL);
p = original = "foobar";
assert_se(extract_many_words(&p, NULL, 0) == 0);
p = original = "foobar waldi";
assert_se(extract_many_words(&p, NULL, 0, &a) == 1);
assert_se(p == original+7);
- assert_se(streq_ptr(a, "foobar"));
+ ASSERT_STREQ(a, "foobar");
free(a);
p = original = " foobar ";
assert_se(extract_many_words(&p, NULL, 0, &a) == 1);
assert_se(isempty(p));
- assert_se(streq_ptr(a, "foobar"));
+ ASSERT_STREQ(a, "foobar");
free(a);
p = original = "gęślą:👊🔪💐 가너도루";
assert_se(extract_many_words(&p, ":" WHITESPACE, 0, &a, &b, &c) == 3);
assert_se(isempty(p));
- assert_se(streq(a, "gęślą"));
- assert_se(streq(b, "👊🔪💐"));
- assert_se(streq(c, "가너도루"));
+ ASSERT_STREQ(a, "gęślą");
+ ASSERT_STREQ(b, "👊🔪💐");
+ ASSERT_STREQ(c, "가너도루");
free(a);
free(b);
free(c);
if (r == 0) {
_cleanup_free_ char *path = NULL;
+ int pipe_read_fd, pair[2];
char buffer[10];
/* Child */
safe_close(STDERR_FILENO); /* Let's close an fd < 2, to make it more interesting */
assert_se(rearrange_stdio(-EBADF, -EBADF, -EBADF) >= 0);
+ /* Reconfigure logging after rearranging stdout/stderr, so we still log to somewhere if the
+ * following tests fail, making it slightly less annoying to debug */
+ log_set_target(LOG_TARGET_KMSG);
+ log_open();
assert_se(fd_get_path(STDIN_FILENO, &path) >= 0);
assert_se(path_equal(path, "/dev/null"));
safe_close(STDOUT_FILENO);
safe_close(STDERR_FILENO);
- {
- int pair[2];
- assert_se(pipe(pair) >= 0);
- assert_se(pair[0] == 0);
- assert_se(pair[1] == 1);
- assert_se(fd_move_above_stdio(0) == 3);
- }
+ assert_se(pipe(pair) >= 0);
+ assert_se(pair[0] == 0);
+ assert_se(pair[1] == 1);
+ pipe_read_fd = fd_move_above_stdio(0);
+ assert_se(pipe_read_fd >= 3);
+
assert_se(open("/dev/full", O_WRONLY|O_CLOEXEC) == 0);
assert_se(acquire_data_fd("foobar") == 2);
assert_se(write(1, "x", 1) < 0 && errno == ENOSPC);
assert_se(write(2, "z", 1) == 1);
- assert_se(read(3, buffer, sizeof(buffer)) == 1);
+ assert_se(read(pipe_read_fd, buffer, sizeof(buffer)) == 1);
assert_se(buffer[0] == 'z');
assert_se(read(0, buffer, sizeof(buffer)) == 6);
assert_se(memcmp(buffer, "foobar", 6) == 0);
assert_se(rearrange_stdio(-EBADF, 1, 2) >= 0);
assert_se(write(1, "a", 1) < 0 && errno == ENOSPC);
assert_se(write(2, "y", 1) == 1);
- assert_se(read(3, buffer, sizeof(buffer)) == 1);
+ assert_se(read(pipe_read_fd, buffer, sizeof(buffer)) == 1);
assert_se(buffer[0] == 'y');
assert_se(fd_get_path(0, &path) >= 0);
}
TEST(format_proc_fd_path) {
- assert_se(streq_ptr(FORMAT_PROC_FD_PATH(0), "/proc/self/fd/0"));
- assert_se(streq_ptr(FORMAT_PROC_FD_PATH(1), "/proc/self/fd/1"));
- assert_se(streq_ptr(FORMAT_PROC_FD_PATH(2), "/proc/self/fd/2"));
- assert_se(streq_ptr(FORMAT_PROC_FD_PATH(3), "/proc/self/fd/3"));
- assert_se(streq_ptr(FORMAT_PROC_FD_PATH(2147483647), "/proc/self/fd/2147483647"));
+ ASSERT_STREQ(FORMAT_PROC_FD_PATH(0), "/proc/self/fd/0");
+ ASSERT_STREQ(FORMAT_PROC_FD_PATH(1), "/proc/self/fd/1");
+ ASSERT_STREQ(FORMAT_PROC_FD_PATH(2), "/proc/self/fd/2");
+ ASSERT_STREQ(FORMAT_PROC_FD_PATH(3), "/proc/self/fd/3");
+ ASSERT_STREQ(FORMAT_PROC_FD_PATH(2147483647), "/proc/self/fd/2147483647");
}
TEST(fd_reopen) {
fd1 = open("/proc", O_DIRECTORY|O_PATH|O_CLOEXEC);
assert_se(fd1 >= 0);
- assert_se(fstat(fd1, &st1) >= 0);
+ ASSERT_OK_ERRNO(fstat(fd1, &st1));
assert_se(S_ISDIR(st1.st_mode));
fl = fcntl(fd1, F_GETFL);
fd2 = fd_reopen(fd1, O_RDONLY|O_DIRECTORY|O_CLOEXEC); /* drop the O_PATH */
assert_se(fd2 >= 0);
- assert_se(fstat(fd2, &st2) >= 0);
+ ASSERT_OK_ERRNO(fstat(fd2, &st2));
assert_se(S_ISDIR(st2.st_mode));
assert_se(stat_inode_same(&st1, &st2));
fd1 = fd_reopen(fd2, O_DIRECTORY|O_PATH|O_CLOEXEC); /* reacquire the O_PATH */
assert_se(fd1 >= 0);
- assert_se(fstat(fd1, &st1) >= 0);
+ ASSERT_OK_ERRNO(fstat(fd1, &st1));
assert_se(S_ISDIR(st1.st_mode));
assert_se(stat_inode_same(&st1, &st2));
fd1 = open("/proc/version", O_PATH|O_CLOEXEC);
assert_se(fd1 >= 0);
- assert_se(fstat(fd1, &st1) >= 0);
+ ASSERT_OK_ERRNO(fstat(fd1, &st1));
assert_se(S_ISREG(st1.st_mode));
fl = fcntl(fd1, F_GETFL);
fd2 = fd_reopen(fd1, O_RDONLY|O_CLOEXEC); /* drop the O_PATH */
assert_se(fd2 >= 0);
- assert_se(fstat(fd2, &st2) >= 0);
+ ASSERT_OK_ERRNO(fstat(fd2, &st2));
assert_se(S_ISREG(st2.st_mode));
assert_se(stat_inode_same(&st1, &st2));
fd1 = fd_reopen(fd2, O_PATH|O_CLOEXEC); /* reacquire the O_PATH */
assert_se(fd1 >= 0);
- assert_se(fstat(fd1, &st1) >= 0);
+ ASSERT_OK_ERRNO(fstat(fd1, &st1));
assert_se(S_ISREG(st1.st_mode));
assert_se(stat_inode_same(&st1, &st2));
/* Validate what happens if we reopen a symlink */
fd1 = open("/proc/self", O_PATH|O_CLOEXEC|O_NOFOLLOW);
assert_se(fd1 >= 0);
- assert_se(fstat(fd1, &st1) >= 0);
+ ASSERT_OK_ERRNO(fstat(fd1, &st1));
assert_se(S_ISLNK(st1.st_mode));
fd2 = fd_reopen(fd1, O_PATH|O_CLOEXEC);
assert_se(fd2 >= 0);
- assert_se(fstat(fd2, &st2) >= 0);
+ ASSERT_OK_ERRNO(fstat(fd2, &st2));
assert_se(S_ISLNK(st2.st_mode));
assert_se(stat_inode_same(&st1, &st2));
fd2 = safe_close(fd2);
tfd = mkdtemp_open(NULL, O_PATH, &t);
assert_se(tfd >= 0);
assert_se(fd_get_path(tfd, &p) >= 0);
- assert_se(streq(p, t));
+ ASSERT_STREQ(p, t);
p = mfree(p);
assert_se(chdir(t) >= 0);
assert_se(fd_get_path(AT_FDCWD, &p) >= 0);
- assert_se(streq(p, t));
+ ASSERT_STREQ(p, t);
p = mfree(p);
fd = openat(tfd, "regular", O_CLOEXEC|O_PATH);
assert_se(fd >= 0);
assert_se(fd_get_path(fd, &p) >= 0);
- assert_se(streq(p, q));
+ ASSERT_STREQ(p, q);
p = mfree(p);
fd = safe_close(fd);
fd = openat(AT_FDCWD, "regular", O_CLOEXEC|O_PATH);
assert_se(fd >= 0);
assert_se(fd_get_path(fd, &p) >= 0);
- assert_se(streq(p, q));
+ ASSERT_STREQ(p, q);
p = mfree(p);
fd = safe_close(fd);
assert_se(fd >= 0);
assert_se(fd_verify_regular(fd) >= 0);
assert_se(fd_get_path(fd, &p) >= 0);
- assert_se(streq(p, q));
+ ASSERT_STREQ(p, q);
p = mfree(p);
fd = safe_close(fd);
assert_se(fd >= 0);
assert_se(fd_verify_regular(fd) >= 0);
assert_se(fd_get_path(fd, &p) >= 0);
- assert_se(streq(p, q));
+ ASSERT_STREQ(p, q);
p = mfree(p);
fd = safe_close(fd);
assert_se(fd >= 0);
assert_se(fd_verify_regular(fd) >= 0);
assert_se(fd_get_path(fd, &p) >= 0);
- assert_se(streq(p, q));
+ ASSERT_STREQ(p, q);
p = mfree(p);
fd = safe_close(fd);
assert_se(fd >= 0);
assert_se(fd_verify_regular(fd) >= 0);
assert_se(fd_get_path(fd, &p) >= 0);
- assert_se(streq(p, q));
+ ASSERT_STREQ(p, q);
p = mfree(p);
q = mfree(q);
assert_se(fd >= 0);
assert_se(fd_verify_regular(fd) == -ELOOP);
assert_se(fd_get_path(fd, &p) >= 0);
- assert_se(streq(p, q));
+ ASSERT_STREQ(p, q);
p = mfree(p);
fd = safe_close(fd);
assert_se(fd >= 0);
assert_se(fd_verify_regular(fd) == -ELOOP);
assert_se(fd_get_path(fd, &p) >= 0);
- assert_se(streq(p, q));
+ ASSERT_STREQ(p, q);
p = mfree(p);
fd = safe_close(fd);
assert_se(fd >= 0);
assert_se(fd_verify_regular(fd) == -ELOOP);
assert_se(fd_get_path(fd, &p) >= 0);
- assert_se(streq(p, q));
+ ASSERT_STREQ(p, q);
p = mfree(p);
fd = safe_close(fd);
assert_se(fd >= 0);
assert_se(fd_verify_regular(fd) == -ELOOP);
assert_se(fd_get_path(fd, &p) >= 0);
- assert_se(streq(p, q));
+ ASSERT_STREQ(p, q);
assert_se(chdir(saved_cwd) >= 0);
}
STRV_FOREACH(i, a)
log_info("Got: <%s>", *i);
- assert_se(streq_ptr(a[0], "one=BAR"));
- assert_se(streq_ptr(a[1], "two=bar"));
- assert_se(streq_ptr(a[2], "three=333\nxxxx"));
- assert_se(streq_ptr(a[3], "four=44\\\"44"));
- assert_se(streq_ptr(a[4], "five=55\"55FIVEcinco"));
- assert_se(streq_ptr(a[5], "six=seis sechs sis"));
- assert_se(streq_ptr(a[6], "seven=sevenval#nocomment"));
- assert_se(streq_ptr(a[7], "eight=eightval #nocomment"));
- assert_se(streq_ptr(a[8], "export nine=nineval"));
- assert_se(streq_ptr(a[9], "ten="));
- assert_se(streq_ptr(a[10], "eleven=value"));
- assert_se(streq_ptr(a[11], "twelve=\\value"));
- assert_se(streq_ptr(a[12], "thirteen=\\value"));
- assert_se(a[13] == NULL);
+ ASSERT_STREQ(a[0], "one=BAR");
+ ASSERT_STREQ(a[1], "two=bar");
+ ASSERT_STREQ(a[2], "three=333\nxxxx");
+ ASSERT_STREQ(a[3], "four=44\\\"44");
+ ASSERT_STREQ(a[4], "five=55\"55FIVEcinco");
+ ASSERT_STREQ(a[5], "six=seis sechs sis");
+ ASSERT_STREQ(a[6], "seven=sevenval#nocomment");
+ ASSERT_STREQ(a[7], "eight=eightval #nocomment");
+ ASSERT_STREQ(a[8], "export nine=nineval");
+ ASSERT_STREQ(a[9], "ten=");
+ ASSERT_STREQ(a[10], "eleven=value");
+ ASSERT_STREQ(a[11], "twelve=\\value");
+ ASSERT_STREQ(a[12], "thirteen=\\value");
+ ASSERT_NULL(a[13]);
strv_env_clean(a);
k = 0;
STRV_FOREACH(i, b) {
log_info("Got2: <%s>", *i);
- assert_se(streq(*i, a[k++]));
+ ASSERT_STREQ(*i, a[k++]);
}
r = parse_env_file(
log_info("twelve=[%s]", strna(twelve));
log_info("thirteen=[%s]", strna(thirteen));
- assert_se(streq(one, "BAR"));
- assert_se(streq(two, "bar"));
- assert_se(streq(three, "333\nxxxx"));
- assert_se(streq(four, "44\\\"44"));
- assert_se(streq(five, "55\"55FIVEcinco"));
- assert_se(streq(six, "seis sechs sis"));
- assert_se(streq(seven, "sevenval#nocomment"));
- assert_se(streq(eight, "eightval #nocomment"));
- assert_se(streq(nine, "nineval"));
- assert_se(ten == NULL);
- assert_se(streq(eleven, "value"));
- assert_se(streq(twelve, "\\value"));
- assert_se(streq(thirteen, "\\value"));
+ ASSERT_STREQ(one, "BAR");
+ ASSERT_STREQ(two, "bar");
+ ASSERT_STREQ(three, "333\nxxxx");
+ ASSERT_STREQ(four, "44\\\"44");
+ ASSERT_STREQ(five, "55\"55FIVEcinco");
+ ASSERT_STREQ(six, "seis sechs sis");
+ ASSERT_STREQ(seven, "sevenval#nocomment");
+ ASSERT_STREQ(eight, "eightval #nocomment");
+ ASSERT_STREQ(nine, "nineval");
+ ASSERT_NULL(ten);
+ ASSERT_STREQ(eleven, "value");
+ ASSERT_STREQ(twelve, "\\value");
+ ASSERT_STREQ(thirteen, "\\value");
{
/* prepare a temporary file to write the environment to */
assert_se(f = popen(cmd, "re"));
assert_se(read_full_stream(f, &from_shell, &sz) >= 0);
assert_se(sz == strlen(value));
- assert_se(streq(from_shell, value));
+ ASSERT_STREQ(from_shell, value);
}
TEST(parse_multiline_env_file) {
STRV_FOREACH(i, a)
log_info("Got: <%s>", *i);
- assert_se(streq_ptr(a[0], "one=BAR VAR\tGAR"));
- assert_se(streq_ptr(a[1], "two=bar var\tgar"));
- assert_se(streq_ptr(a[2], "tri=bar var \tgar "));
- assert_se(a[3] == NULL);
+ ASSERT_STREQ(a[0], "one=BAR VAR\tGAR");
+ ASSERT_STREQ(a[1], "two=bar var\tgar");
+ ASSERT_STREQ(a[2], "tri=bar var \tgar ");
+ ASSERT_NULL(a[3]);
{
_cleanup_close_ int fd = mkostemp_safe(p);
STRV_FOREACH(i, a)
log_info("Got: <%s>", *i);
- assert_se(streq(a[0], "one=2"));
- assert_se(streq(a[1], "twelve=12"));
- assert_se(streq(a[2], "twentyone=21"));
- assert_se(streq(a[3], "twentytwo=22"));
- assert_se(streq(a[4], "xxx=0x222"));
- assert_se(streq(a[5], "xxx_minus_three= - 3"));
- assert_se(streq(a[6], "yyy=2"));
- assert_se(streq(a[7], "zzz=replacement"));
- assert_se(streq(a[8], "zzzz="));
- assert_se(streq(a[9], "zzzzz="));
- assert_se(a[10] == NULL);
+ ASSERT_STREQ(a[0], "one=2");
+ ASSERT_STREQ(a[1], "twelve=12");
+ ASSERT_STREQ(a[2], "twentyone=21");
+ ASSERT_STREQ(a[3], "twentytwo=22");
+ ASSERT_STREQ(a[4], "xxx=0x222");
+ ASSERT_STREQ(a[5], "xxx_minus_three= - 3");
+ ASSERT_STREQ(a[6], "yyy=2");
+ ASSERT_STREQ(a[7], "zzz=replacement");
+ ASSERT_STREQ(a[8], "zzzz=");
+ ASSERT_STREQ(a[9], "zzzzz=");
+ ASSERT_NULL(a[10]);
r = merge_env_file(&a, NULL, t);
assert_se(r >= 0);
STRV_FOREACH(i, a)
log_info("Got2: <%s>", *i);
- assert_se(streq(a[0], "one=2"));
- assert_se(streq(a[1], "twelve=12"));
- assert_se(streq(a[2], "twentyone=21"));
- assert_se(streq(a[3], "twentytwo=22"));
- assert_se(streq(a[4], "xxx=0x222"));
- assert_se(streq(a[5], "xxx_minus_three=0x222 - 3"));
- assert_se(streq(a[6], "yyy=2"));
- assert_se(streq(a[7], "zzz=replacement"));
- assert_se(streq(a[8], "zzzz="));
- assert_se(streq(a[9], "zzzzz="));
- assert_se(a[10] == NULL);
+ ASSERT_STREQ(a[0], "one=2");
+ ASSERT_STREQ(a[1], "twelve=12");
+ ASSERT_STREQ(a[2], "twentyone=21");
+ ASSERT_STREQ(a[3], "twentytwo=22");
+ ASSERT_STREQ(a[4], "xxx=0x222");
+ ASSERT_STREQ(a[5], "xxx_minus_three=0x222 - 3");
+ ASSERT_STREQ(a[6], "yyy=2");
+ ASSERT_STREQ(a[7], "zzz=replacement");
+ ASSERT_STREQ(a[8], "zzzz=");
+ ASSERT_STREQ(a[9], "zzzzz=");
+ ASSERT_NULL(a[10]);
}
TEST(merge_env_file_invalid) {
r = executable_is_script(t, &command);
assert_se(r > 0);
- assert_se(streq(command, "/bin/script"));
+ ASSERT_STREQ(command, "/bin/script");
free(command);
r = executable_is_script("/bin/sh", &command);
assert_se(f);
assert_se(read_one_line_file(fn, &buf) == 0);
- assert_se(streq_ptr(buf, ""));
+ ASSERT_STREQ(buf, "");
assert_se(read_one_line_file(fn, &buf2) == 0);
- assert_se(streq_ptr(buf2, ""));
+ ASSERT_STREQ(buf2, "");
assert_se(write_string_stream(f, "x", WRITE_STRING_FILE_AVOID_NEWLINE) >= 0);
fflush(f);
assert_se(read_one_line_file(fn, &buf3) == 1);
- assert_se(streq_ptr(buf3, "x"));
+ ASSERT_STREQ(buf3, "x");
assert_se(write_string_stream(f, "\n", WRITE_STRING_FILE_AVOID_NEWLINE) >= 0);
fflush(f);
assert_se(read_one_line_file(fn, &buf4) == 2);
- assert_se(streq_ptr(buf4, "x"));
+ ASSERT_STREQ(buf4, "x");
assert_se(write_string_stream(f, "\n", WRITE_STRING_FILE_AVOID_NEWLINE) >= 0);
fflush(f);
assert_se(read_one_line_file(fn, &buf5) == 2);
- assert_se(streq_ptr(buf5, "x"));
+ ASSERT_STREQ(buf5, "x");
}
TEST(write_string_stream) {
rewind(f);
assert_se(fgets(buf, sizeof(buf), f));
- assert_se(streq(buf, "boohoo\n"));
+ ASSERT_STREQ(buf, "boohoo\n");
f = safe_fclose(f);
f = fopen(fn, "w+");
assert_se(fgets(buf, sizeof(buf), f));
printf(">%s<", buf);
- assert_se(streq(buf, "boohoo"));
+ ASSERT_STREQ(buf, "boohoo");
}
TEST(write_string_file) {
assert_se(write_string_file(fn, "boohoo", WRITE_STRING_FILE_CREATE) == 0);
assert_se(read(fd, buf, sizeof(buf)) == 7);
- assert_se(streq(buf, "boohoo\n"));
+ ASSERT_STREQ(buf, "boohoo\n");
}
TEST(write_string_file_no_create) {
assert_se(write_string_file(fn, "boohoo", 0) == 0);
assert_se(read(fd, buf, sizeof buf) == (ssize_t) strlen("boohoo\n"));
- assert_se(streq(buf, "boohoo\n"));
+ ASSERT_STREQ(buf, "boohoo\n");
}
TEST(write_string_file_verify) {
r = search_and_fopen(basename(name), "re", NULL, (const char**) dirs, &f, &p);
assert_se(r >= 0);
assert_se(e = path_startswith(p, "/tmp/"));
- assert_se(streq(basename(name), e));
+ ASSERT_STREQ(basename(name), e);
f = safe_fclose(f);
p = mfree(p);
r = search_and_fopen(basename(name), NULL, NULL, (const char**) dirs, NULL, &p);
assert_se(r >= 0);
assert_se(e = path_startswith(p, "/tmp/"));
- assert_se(streq(basename(name), e));
+ ASSERT_STREQ(basename(name), e);
p = mfree(p);
r = search_and_fopen(name, "re", NULL, (const char**) dirs, &f, &p);
r = search_and_fopen(basename(name), "re", "/", (const char**) dirs, &f, &p);
assert_se(r >= 0);
assert_se(e = path_startswith(p, "/tmp/"));
- assert_se(streq(basename(name), e));
+ ASSERT_STREQ(basename(name), e);
f = safe_fclose(f);
p = mfree(p);
r = search_and_fopen(basename(name), NULL, "/", (const char**) dirs, NULL, &p);
assert_se(r >= 0);
assert_se(e = path_startswith(p, "/tmp/"));
- assert_se(streq(basename(name), e));
+ ASSERT_STREQ(basename(name), e);
p = mfree(p);
r = search_and_fopen("/a/file/which/does/not/exist/i/guess", "re", NULL, (const char**) dirs, &f, &p);
r = search_and_fopen_nulstr(basename(name), "re", NULL, dirs, &f, &p);
assert_se(r >= 0);
assert_se(e = path_startswith(p, "/tmp/"));
- assert_se(streq(basename(name), e));
+ ASSERT_STREQ(basename(name), e);
f = safe_fclose(f);
p = mfree(p);
r = read_full_file(name, &contents, &size);
assert_se(r == 0);
printf("contents: %s", contents);
- assert_se(streq(contents, "abc\n" ALPHANUMERICAL "\n"));
+ ASSERT_STREQ(contents, "abc\n" ALPHANUMERICAL "\n");
}
TEST(tempfn) {
char *ret = NULL, *p;
assert_se(tempfn_xxxxxx("/foo/bar/waldo", NULL, &ret) >= 0);
- assert_se(streq_ptr(ret, "/foo/bar/.#waldoXXXXXX"));
+ ASSERT_STREQ(ret, "/foo/bar/.#waldoXXXXXX");
free(ret);
assert_se(tempfn_xxxxxx("/foo/bar/waldo", "[miau]", &ret) >= 0);
- assert_se(streq_ptr(ret, "/foo/bar/.#[miau]waldoXXXXXX"));
+ ASSERT_STREQ(ret, "/foo/bar/.#[miau]waldoXXXXXX");
free(ret);
assert_se(tempfn_random("/foo/bar/waldo", NULL, &ret) >= 0);
r = read_line(f, SIZE_MAX, &s);
assert_se((size_t) r == eof_endings[i].length);
- assert_se(streq_ptr(s, "foo"));
+ ASSERT_STREQ(s, "foo");
assert_se(read_line(f, SIZE_MAX, NULL) == 0); /* Ensure we hit EOF */
}
assert_se(peer.un.sun_family == AF_UNIX);
assert_se(peerlen > offsetof(struct sockaddr_un, sun_path));
assert_se(peer.un.sun_path[0] == 0);
- assert_se(streq(peer.un.sun_path + 1, clientname + 1));
+ ASSERT_STREQ(peer.un.sun_path + 1, clientname + 1);
#define TEST_STR "This is a test\nreally."
assert_se(read_full_file_full(AT_FDCWD, jj, UINT64_MAX, SIZE_MAX, 0, NULL, &data, &size) == -ENXIO);
assert_se(read_full_file_full(AT_FDCWD, jj, UINT64_MAX, SIZE_MAX, READ_FULL_FILE_CONNECT_SOCKET, clientname, &data, &size) >= 0);
assert_se(size == strlen(TEST_STR));
- assert_se(streq(data, TEST_STR));
+ ASSERT_STREQ(data, TEST_STR);
assert_se(wait_for_terminate_and_check("(server)", pid, WAIT_LOG) >= 0);
#undef TEST_STR
assert_se(fdopen_independent(fd, "re", &f) >= 0);
zero(buf);
assert_se(fread(buf, 1, sizeof(buf), f) == strlen(TEST_TEXT));
- assert_se(streq(buf, TEST_TEXT));
+ ASSERT_STREQ(buf, TEST_TEXT);
assert_se((fcntl(fileno(f), F_GETFL) & O_ACCMODE) == O_RDONLY);
assert_se(FLAGS_SET(fcntl(fileno(f), F_GETFD), FD_CLOEXEC));
f = safe_fclose(f);
assert_se(fdopen_independent(fd, "r", &f) >= 0);
zero(buf);
assert_se(fread(buf, 1, sizeof(buf), f) == strlen(TEST_TEXT));
- assert_se(streq(buf, TEST_TEXT));
+ ASSERT_STREQ(buf, TEST_TEXT);
assert_se((fcntl(fileno(f), F_GETFL) & O_ACCMODE) == O_RDONLY);
assert_se(!FLAGS_SET(fcntl(fileno(f), F_GETFD), FD_CLOEXEC));
f = safe_fclose(f);
assert_se(fdopen_independent(fd, "r+e", &f) >= 0);
zero(buf);
assert_se(fread(buf, 1, sizeof(buf), f) == strlen(TEST_TEXT));
- assert_se(streq(buf, TEST_TEXT));
+ ASSERT_STREQ(buf, TEST_TEXT);
assert_se((fcntl(fileno(f), F_GETFL) & O_ACCMODE) == O_RDWR);
assert_se(FLAGS_SET(fcntl(fileno(f), F_GETFD), FD_CLOEXEC));
f = safe_fclose(f);
assert_se(table_format(t, &formatted) >= 0);
- assert_se(streq(formatted, "bar\nbar\nbaz\n"));
+ ASSERT_STREQ(formatted, "bar\nbar\nbaz\n");
}
TEST(dup_cell) {
const char *si_with_p, const char *si_without_p) {
char buf[FORMAT_BYTES_MAX];
- assert_se(streq_ptr(format_bytes_full(buf, sizeof buf, val, FORMAT_BYTES_USE_IEC | FORMAT_BYTES_BELOW_POINT | (trailing_B ? FORMAT_BYTES_TRAILING_B : 0)), iec_with_p));
- assert_se(streq_ptr(format_bytes_full(buf, sizeof buf, val, FORMAT_BYTES_USE_IEC | (trailing_B ? FORMAT_BYTES_TRAILING_B : 0)), iec_without_p));
- assert_se(streq_ptr(format_bytes_full(buf, sizeof buf, val, FORMAT_BYTES_BELOW_POINT | (trailing_B ? FORMAT_BYTES_TRAILING_B : 0)), si_with_p));
- assert_se(streq_ptr(format_bytes_full(buf, sizeof buf, val, trailing_B ? FORMAT_BYTES_TRAILING_B : 0), si_without_p));
+ ASSERT_STREQ(format_bytes_full(buf, sizeof buf, val, FORMAT_BYTES_USE_IEC | FORMAT_BYTES_BELOW_POINT | (trailing_B ? FORMAT_BYTES_TRAILING_B : 0)), iec_with_p);
+ ASSERT_STREQ(format_bytes_full(buf, sizeof buf, val, FORMAT_BYTES_USE_IEC | (trailing_B ? FORMAT_BYTES_TRAILING_B : 0)), iec_without_p);
+ ASSERT_STREQ(format_bytes_full(buf, sizeof buf, val, FORMAT_BYTES_BELOW_POINT | (trailing_B ? FORMAT_BYTES_TRAILING_B : 0)), si_with_p);
+ ASSERT_STREQ(format_bytes_full(buf, sizeof buf, val, trailing_B ? FORMAT_BYTES_TRAILING_B : 0), si_without_p);
}
TEST(format_bytes) {
log_tests_skipped_errno(errno, "symlink() not possible");
} else {
assert_se(readlink_and_make_absolute(name_alias, &r1) >= 0);
- assert_se(streq(r1, name));
+ ASSERT_STREQ(r1, name);
assert_se(unlink(name_alias) >= 0);
assert_se(safe_getcwd(&pwd) >= 0);
assert_se(chdir(tempdir) >= 0);
assert_se(symlink(name2, name_alias) >= 0);
assert_se(readlink_and_make_absolute(name_alias, &r2) >= 0);
- assert_se(streq(r2, name));
+ ASSERT_STREQ(r2, name);
assert_se(unlink(name_alias) >= 0);
assert_se(chdir(pwd) >= 0);
assert_se(unsetenv("TMP") >= 0);
assert_se(var_tmp_dir(&tmp_dir) >= 0);
- assert_se(streq(tmp_dir, "/var/tmp"));
+ ASSERT_STREQ(tmp_dir, "/var/tmp");
assert_se(setenv("TMPDIR", "/tmp", true) >= 0);
- assert_se(streq(getenv("TMPDIR"), "/tmp"));
+ ASSERT_STREQ(getenv("TMPDIR"), "/tmp");
assert_se(var_tmp_dir(&tmp_dir) >= 0);
- assert_se(streq(tmp_dir, "/tmp"));
+ ASSERT_STREQ(tmp_dir, "/tmp");
assert_se(setenv("TMPDIR", "/88_does_not_exist_88", true) >= 0);
- assert_se(streq(getenv("TMPDIR"), "/88_does_not_exist_88"));
+ ASSERT_STREQ(getenv("TMPDIR"), "/88_does_not_exist_88");
assert_se(var_tmp_dir(&tmp_dir) >= 0);
- assert_se(streq(tmp_dir, "/var/tmp"));
+ ASSERT_STREQ(tmp_dir, "/var/tmp");
if (tmpdir_backup) {
assert_se(setenv("TMPDIR", tmpdir_backup, true) >= 0);
- assert_se(streq(getenv("TMPDIR"), tmpdir_backup));
+ ASSERT_STREQ(getenv("TMPDIR"), tmpdir_backup);
}
if (temp_backup) {
assert_se(setenv("TEMP", temp_backup, true) >= 0);
- assert_se(streq(getenv("TEMP"), temp_backup));
+ ASSERT_STREQ(getenv("TEMP"), temp_backup);
}
if (tmp_backup) {
assert_se(setenv("TMP", tmp_backup, true) >= 0);
- assert_se(streq(getenv("TMP"), tmp_backup));
+ ASSERT_STREQ(getenv("TMP"), tmp_backup);
}
}
assert_se(write(fd, "hallo\n", 6) == 6);
- assert_se(fstat(fd, &st) >= 0);
+ ASSERT_OK_ERRNO(fstat(fd, &st));
assert_se(st.st_size == 6);
assert_se(st.st_blocks > 0);
assert_se(st.st_nlink == 1);
assert_se(unlinkat_deallocate(AT_FDCWD, p, UNLINK_ERASE) >= 0);
- assert_se(fstat(fd, &st) >= 0);
+ ASSERT_OK_ERRNO(fstat(fd, &st));
assert_se(IN_SET(st.st_size, 0, 6)); /* depending on whether hole punching worked the size will be 6
(it worked) or 0 (we had to resort to truncation) */
assert_se(st.st_blocks == 0);
_cleanup_free_ char *a = NULL, *b = NULL, *c = NULL;
assert_se(parse_cifs_service(f, &a, &b, &c) == ret);
- assert_se(streq_ptr(a, h));
- assert_se(streq_ptr(b, s));
- assert_se(streq_ptr(c, d));
+ ASSERT_STREQ(a, h);
+ ASSERT_STREQ(b, s);
+ ASSERT_STREQ(c, d);
}
TEST(parse_cifs_service) {
fd = open_mkdir_at(AT_FDCWD, "/", O_CLOEXEC, 0);
assert_se(fd >= 0);
assert_se(stat("/", &sta) >= 0);
- assert_se(fstat(fd, &stb) >= 0);
+ ASSERT_OK_ERRNO(fstat(fd, &stb));
assert_se(stat_inode_same(&sta, &stb));
fd = safe_close(fd);
fd = open_mkdir_at(AT_FDCWD, ".", O_CLOEXEC, 0);
assert_se(stat(".", &sta) >= 0);
- assert_se(fstat(fd, &stb) >= 0);
+ ASSERT_OK_ERRNO(fstat(fd, &stb));
assert_se(stat_inode_same(&sta, &stb));
fd = safe_close(fd);
assert_se(symlinkat(expect, tfd, "linkname") >= 0);
assert_se(readlinkat_malloc(tfd, "linkname", &p) >= 0);
- assert_se(streq(p, expect));
+ ASSERT_STREQ(p, expect);
p = mfree(p);
fd = openat(tfd, "linkname", O_PATH | O_NOFOLLOW | O_CLOEXEC);
assert_se(fd >= 0);
assert_se(readlinkat_malloc(fd, NULL, &p) >= 0);
- assert_se(streq(p, expect));
+ ASSERT_STREQ(p, expect);
p = mfree(p);
assert_se(readlinkat_malloc(fd, "", &p) >= 0);
- assert_se(streq(p, expect));
+ ASSERT_STREQ(p, expect);
p = mfree(p);
fd = safe_close(fd);
assert_se(q = path_join(t, "linkname"));
assert_se(readlinkat_malloc(AT_FDCWD, q, &p) >= 0);
- assert_se(streq(p, expect));
+ ASSERT_STREQ(p, expect);
p = mfree(p);
assert_se(readlinkat_malloc(INT_MAX, q, &p) >= 0);
- assert_se(streq(p, expect));
+ ASSERT_STREQ(p, expect);
p = mfree(p);
q = mfree(q);
}
opts, r, strnull(name), value, filtered,
r_expected, strnull(name_expected), strnull(value_expected), filtered_expected ?: opts);
assert_se(r == r_expected);
- assert_se(streq_ptr(name, name_expected));
- assert_se(streq_ptr(value, value_expected));
- assert_se(streq_ptr(filtered, filtered_expected ?: opts));
+ ASSERT_STREQ(name, name_expected);
+ ASSERT_STREQ(value, value_expected);
+ ASSERT_STREQ(filtered, filtered_expected ?: opts);
/* test mode which returns all the values */
opts, r, strnull(name), joined,
r_values_expected, strnull(name_expected), strnull(values_expected));
assert_se(r == r_values_expected);
- assert_se(streq_ptr(name, r_values_expected > 0 ? name_expected : NULL));
- assert_se(streq_ptr(joined, values_expected));
+ ASSERT_STREQ(name, r_values_expected > 0 ? name_expected : NULL);
+ ASSERT_STREQ(joined, values_expected);
/* also test the malloc-less mode */
r = fstab_filter_options(opts, remove, &name, NULL, NULL, NULL);
opts, r, strnull(name),
r_expected, strnull(name_expected));
assert_se(r == r_expected);
- assert_se(streq_ptr(name, name_expected));
+ ASSERT_STREQ(name, name_expected);
}
TEST(fstab_filter_options) {
n = fstab_node_to_udev_node("LABEL=applé/jack");
puts(n);
- assert_se(streq(n, "/dev/disk/by-label/applé\\x2fjack"));
+ ASSERT_STREQ(n, "/dev/disk/by-label/applé\\x2fjack");
free(n);
n = fstab_node_to_udev_node("PARTLABEL=pinkié pie");
puts(n);
- assert_se(streq(n, "/dev/disk/by-partlabel/pinkié\\x20pie"));
+ ASSERT_STREQ(n, "/dev/disk/by-partlabel/pinkié\\x20pie");
free(n);
n = fstab_node_to_udev_node("UUID=037b9d94-148e-4ee4-8d38-67bfe15bb535");
puts(n);
- assert_se(streq(n, "/dev/disk/by-uuid/037b9d94-148e-4ee4-8d38-67bfe15bb535"));
+ ASSERT_STREQ(n, "/dev/disk/by-uuid/037b9d94-148e-4ee4-8d38-67bfe15bb535");
free(n);
n = fstab_node_to_udev_node("PARTUUID=037b9d94-148e-4ee4-8d38-67bfe15bb535");
puts(n);
- assert_se(streq(n, "/dev/disk/by-partuuid/037b9d94-148e-4ee4-8d38-67bfe15bb535"));
+ ASSERT_STREQ(n, "/dev/disk/by-partuuid/037b9d94-148e-4ee4-8d38-67bfe15bb535");
free(n);
n = fstab_node_to_udev_node("PONIES=awesome");
puts(n);
- assert_se(streq(n, "PONIES=awesome"));
+ ASSERT_STREQ(n, "PONIES=awesome");
free(n);
n = fstab_node_to_udev_node("/dev/xda1");
puts(n);
- assert_se(streq(n, "/dev/xda1"));
+ ASSERT_STREQ(n, "/dev/xda1");
free(n);
}
r = glob_first("/tmp/test-glob_first*", &first);
assert_se(r == 1);
- assert_se(streq(name, first));
+ ASSERT_STREQ(name, first);
first = mfree(first);
r = unlink(name);
assert_se(r == 0);
r = glob_first("/tmp/test-glob_first*", &first);
assert_se(r == 0);
- assert_se(first == NULL);
+ ASSERT_NULL(first);
}
TEST(glob_exists) {
r = safe_glob(fn2, GLOB_NOSORT|GLOB_BRACE, &g);
assert_se(r == 0);
assert_se(g.gl_pathc == 1);
- assert_se(streq(g.gl_pathv[0], fname));
- assert_se(g.gl_pathv[1] == NULL);
+ ASSERT_STREQ(g.gl_pathv[0], fname);
+ ASSERT_NULL(g.gl_pathv[1]);
(void) rm_rf(template, REMOVE_ROOT|REMOVE_PHYSICAL);
}
_cleanup_free_ char *t;
assert_se(glob_non_glob_prefix(path, &t) == 0);
- assert_se(streq(t, expected));
+ ASSERT_STREQ(t, expected);
}
TEST(glob_non_glob) {
ASSERT_EQ(x.arch, y.arch);
ASSERT_EQ(x.designator, y.designator);
assert_se(sd_id128_equal(x.uuid, y.uuid));
- assert_se(streq(x.name, y.name));
+ ASSERT_STREQ(x.name, y.name);
/* If the partition type does not have an architecture, nothing should change. */
ASSERT_EQ(x.arch, y.arch);
ASSERT_EQ(x.designator, y.designator);
assert_se(sd_id128_equal(x.uuid, y.uuid));
- assert_se(streq(x.name, y.name));
+ ASSERT_STREQ(x.name, y.name);
}
DEFINE_TEST_MAIN(LOG_INFO);
#include "tests.h"
#include "time-util.h"
+/* PROJECT_FILE, which is used by ASSERT_XYZ(), cannot be used in generated files, as the build directory
+ * may be outside of the source directory. */
+#ifdef ORDERED
+# undef PROJECT_FILE
+# define PROJECT_FILE __FILE__
+#endif
+
TEST(hashmap_replace) {
_cleanup_hashmap_free_ Hashmap *m = NULL;
_cleanup_free_ char *val1 = NULL, *val2 = NULL, *val3 = NULL, *val4 = NULL, *val5 = NULL;
hashmap_replace(m, "key 3", val1);
r = hashmap_get(m, "key 3");
- assert_se(streq(r, "val1"));
+ ASSERT_STREQ(r, "val1");
hashmap_replace(m, "key 5", val5);
r = hashmap_get(m, "key 5");
- assert_se(streq(r, "val5"));
+ ASSERT_STREQ(r, "val5");
}
TEST(hashmap_copy) {
copy = hashmap_copy(m);
r = hashmap_get(copy, "key 1");
- assert_se(streq(r, "val1"));
+ ASSERT_STREQ(r, "val1");
r = hashmap_get(copy, "key 2");
- assert_se(streq(r, "val2"));
+ ASSERT_STREQ(r, "val2");
r = hashmap_get(copy, "key 3");
- assert_se(streq(r, "val3"));
+ ASSERT_STREQ(r, "val3");
r = hashmap_get(copy, "key 4");
- assert_se(streq(r, "val4"));
+ ASSERT_STREQ(r, "val4");
}
TEST(hashmap_get_strv) {
strv = strv_sort(strv);
#endif
- assert_se(streq(strv[0], "val1"));
- assert_se(streq(strv[1], "val2"));
- assert_se(streq(strv[2], "val3"));
- assert_se(streq(strv[3], "val4"));
+ ASSERT_STREQ(strv[0], "val1");
+ ASSERT_STREQ(strv[1], "val2");
+ ASSERT_STREQ(strv[2], "val3");
+ ASSERT_STREQ(strv[3], "val4");
}
TEST(hashmap_move_one) {
hashmap_put(m, "key 1", val1);
r = hashmap_get(m, "key 1");
- assert_se(streq(r, "old_value"));
+ ASSERT_STREQ(r, "old_value");
assert_se(hashmap_update(m, "key 2", val2) == -ENOENT);
r = hashmap_get(m, "key 1");
- assert_se(streq(r, "old_value"));
+ ASSERT_STREQ(r, "old_value");
assert_se(hashmap_update(m, "key 1", val2) == 0);
r = hashmap_get(m, "key 1");
- assert_se(streq(r, "new_value"));
+ ASSERT_STREQ(r, "new_value");
}
TEST(hashmap_put) {
char *r;
r = hashmap_remove(NULL, "key 1");
- assert_se(r == NULL);
+ ASSERT_NULL(r);
m = hashmap_new(&string_hash_ops);
assert_se(m);
r = hashmap_remove(m, "no such key");
- assert_se(r == NULL);
+ ASSERT_NULL(r);
hashmap_put(m, "key 1", (void*) "val 1");
hashmap_put(m, "key 2", (void*) "val 2");
r = hashmap_remove(m, "key 1");
- assert_se(streq(r, "val 1"));
+ ASSERT_STREQ(r, "val 1");
r = hashmap_get(m, "key 2");
- assert_se(streq(r, "val 2"));
+ ASSERT_STREQ(r, "val 2");
assert_se(!hashmap_get(m, "key 1"));
}
void *r, *r2;
r = hashmap_remove2(NULL, "key 1", &r2);
- assert_se(r == NULL);
+ ASSERT_NULL(r);
m = hashmap_new(&string_hash_ops);
assert_se(m);
r = hashmap_remove2(m, "no such key", &r2);
- assert_se(r == NULL);
+ ASSERT_NULL(r);
hashmap_put(m, strdup(key1), strdup(val1));
hashmap_put(m, strdup(key2), strdup(val2));
r = hashmap_remove2(m, key1, &r2);
- assert_se(streq(r, val1));
- assert_se(streq(r2, key1));
+ ASSERT_STREQ(r, val1);
+ ASSERT_STREQ(r2, key1);
free(r);
free(r2);
r = hashmap_get(m, key2);
- assert_se(streq(r, val2));
+ ASSERT_STREQ(r, val2);
assert_se(!hashmap_get(m, key1));
}
char val2[] = "val 2";
r = hashmap_remove_value(NULL, "key 1", val1);
- assert_se(r == NULL);
+ ASSERT_NULL(r);
m = hashmap_new(&string_hash_ops);
assert_se(m);
r = hashmap_remove_value(m, "key 1", val1);
- assert_se(r == NULL);
+ ASSERT_NULL(r);
hashmap_put(m, "key 1", val1);
hashmap_put(m, "key 2", val2);
r = hashmap_remove_value(m, "key 1", val1);
- assert_se(streq(r, "val 1"));
+ ASSERT_STREQ(r, "val 1");
r = hashmap_get(m, "key 2");
- assert_se(streq(r, "val 2"));
+ ASSERT_STREQ(r, "val 2");
assert_se(!hashmap_get(m, "key 1"));
r = hashmap_remove_value(m, "key 2", val1);
- assert_se(r == NULL);
+ ASSERT_NULL(r);
r = hashmap_get(m, "key 2");
- assert_se(streq(r, "val 2"));
+ ASSERT_STREQ(r, "val 2");
assert_se(!hashmap_get(m, "key 1"));
}
assert_se(valid == 0);
r = hashmap_get(m, "key 2");
- assert_se(streq(r, "val 2"));
+ ASSERT_STREQ(r, "val 2");
assert_se(!hashmap_get(m, "key 1"));
valid = hashmap_put(m, "key 3", (void*) (const char *) "val 3");
assert_se(val);
r = hashmap_get(NULL, "Key 1");
- assert_se(r == NULL);
+ ASSERT_NULL(r);
m = hashmap_new(&string_hash_ops);
hashmap_put(m, "Key 1", val);
r = hashmap_get(m, "Key 1");
- assert_se(streq(r, val));
+ ASSERT_STREQ(r, val);
r = hashmap_get(m, "no such key");
- assert_se(r == NULL);
+ ASSERT_NULL(r);
assert_se(m);
}
assert_se(key_copy);
r = hashmap_get2(NULL, key_orig, &key_copy);
- assert_se(r == NULL);
+ ASSERT_NULL(r);
m = hashmap_new(&string_hash_ops);
key_copy = NULL;
r = hashmap_get2(m, key_orig, &key_copy);
- assert_se(streq(r, val));
+ ASSERT_STREQ(r, val);
assert_se(key_orig != key_copy);
- assert_se(streq(key_orig, key_copy));
+ ASSERT_STREQ(key_orig, key_copy);
r = hashmap_get2(m, "no such key", NULL);
- assert_se(r == NULL);
+ ASSERT_NULL(r);
assert_se(m);
}
assert_se(!hashmap_first(m));
assert_se(hashmap_put(m, "key 1", (void*) "val 1") == 1);
- assert_se(streq(hashmap_first(m), "val 1"));
+ ASSERT_STREQ(hashmap_first(m), "val 1");
assert_se(hashmap_put(m, "key 2", (void*) "val 2") == 1);
#ifdef ORDERED
- assert_se(streq(hashmap_first(m), "val 1"));
+ ASSERT_STREQ(hashmap_first(m), "val 1");
assert_se(hashmap_remove(m, "key 1"));
- assert_se(streq(hashmap_first(m), "val 2"));
+ ASSERT_STREQ(hashmap_first(m), "val 2");
#endif
}
assert_se(!hashmap_first_key(m));
assert_se(hashmap_put(m, "key 1", NULL) == 1);
- assert_se(streq(hashmap_first_key(m), "key 1"));
+ ASSERT_STREQ(hashmap_first_key(m), "key 1");
assert_se(hashmap_put(m, "key 2", NULL) == 1);
#ifdef ORDERED
- assert_se(streq(hashmap_first_key(m), "key 1"));
- assert_se(hashmap_remove(m, "key 1") == NULL);
- assert_se(streq(hashmap_first_key(m), "key 2"));
+ ASSERT_STREQ(hashmap_first_key(m), "key 1");
+ ASSERT_NULL(hashmap_remove(m, "key 1"));
+ ASSERT_STREQ(hashmap_first_key(m), "key 2");
#endif
}
assert_se(!hashmap_steal_first_key(m));
assert_se(hashmap_put(m, "key 1", NULL) == 1);
- assert_se(streq(hashmap_steal_first_key(m), "key 1"));
+ ASSERT_STREQ(hashmap_steal_first_key(m), "key 1");
assert_se(hashmap_isempty(m));
}
assert_se(hashmap_dump_keys_sorted(m, &vals, &n) >= 0);
assert_se(n == ELEMENTSOF(expected_keys));
for (size_t i = 0; i < n; i++)
- assert_se(streq(vals[i], expected_keys[i]));
+ ASSERT_STREQ(vals[i], expected_keys[i]);
vals = mfree(vals);
m = hashmap_free(m);
hashmap_clear(m);
compare_cache(m, c);
- assert_se(hashmap_free(m) == NULL);
- assert_se(iterated_cache_free(c) == NULL);
+ ASSERT_NULL(hashmap_free(m));
+ ASSERT_NULL(iterated_cache_free(c));
}
TEST(hashmap_put_strdup) {
assert_se(hashmap_contains(m, "foo"));
s = hashmap_get(m, "foo");
- assert_se(streq(s, "bar"));
+ ASSERT_STREQ(s, "bar");
assert_se(hashmap_put_strdup(&m, "xxx", "bar") == 1);
assert_se(hashmap_put_strdup(&m, "xxx", "bar") == 0);
assert_se(hashmap_contains(m, "xxx"));
s = hashmap_get(m, "xxx");
- assert_se(streq(s, "bar"));
+ ASSERT_STREQ(s, "bar");
}
TEST(hashmap_put_strdup_null) {
assert_se(hashmap_contains(m, "foo"));
s = hashmap_get(m, "foo");
- assert_se(streq(s, "bar"));
+ ASSERT_STREQ(s, "bar");
assert_se(hashmap_put_strdup(&m, "xxx", NULL) == 1);
assert_se(hashmap_put_strdup(&m, "xxx", "bar") == -EEXIST);
assert_se(hashmap_contains(m, "xxx"));
s = hashmap_get(m, "xxx");
- assert_se(s == NULL);
+ ASSERT_NULL(s);
}
/* This file tests in test-hashmap-plain.c, and tests in test-hashmap-ordered.c, which is generated
assert_se(result = hexmem(in, strlen_ptr(in)));
log_debug("hexmem(\"%s\") → \"%s\" (expected: \"%s\")", strnull(in), result, expected);
- assert_se(streq(result, expected));
+ ASSERT_STREQ(result, expected);
assert_se(unhexmem(result, &mem, &len) >= 0);
assert_se(memcmp_safe(mem, in, len) == 0);
assert_se(hex = hexmem(mem, len));
answer = strndupa_safe(strempty(s), l);
- assert_se(streq(delete_chars(answer, WHITESPACE), hex));
+ ASSERT_STREQ(delete_chars(answer, WHITESPACE), hex);
}
}
b32 = base32hexmem("", STRLEN(""), true);
assert_se(b32);
- assert_se(streq(b32, ""));
+ ASSERT_STREQ(b32, "");
free(b32);
b32 = base32hexmem("f", STRLEN("f"), true);
assert_se(b32);
- assert_se(streq(b32, "CO======"));
+ ASSERT_STREQ(b32, "CO======");
free(b32);
b32 = base32hexmem("fo", STRLEN("fo"), true);
assert_se(b32);
- assert_se(streq(b32, "CPNG===="));
+ ASSERT_STREQ(b32, "CPNG====");
free(b32);
b32 = base32hexmem("foo", STRLEN("foo"), true);
assert_se(b32);
- assert_se(streq(b32, "CPNMU==="));
+ ASSERT_STREQ(b32, "CPNMU===");
free(b32);
b32 = base32hexmem("foob", STRLEN("foob"), true);
assert_se(b32);
- assert_se(streq(b32, "CPNMUOG="));
+ ASSERT_STREQ(b32, "CPNMUOG=");
free(b32);
b32 = base32hexmem("fooba", STRLEN("fooba"), true);
assert_se(b32);
- assert_se(streq(b32, "CPNMUOJ1"));
+ ASSERT_STREQ(b32, "CPNMUOJ1");
free(b32);
b32 = base32hexmem("foobar", STRLEN("foobar"), true);
assert_se(b32);
- assert_se(streq(b32, "CPNMUOJ1E8======"));
+ ASSERT_STREQ(b32, "CPNMUOJ1E8======");
free(b32);
b32 = base32hexmem("", STRLEN(""), false);
assert_se(b32);
- assert_se(streq(b32, ""));
+ ASSERT_STREQ(b32, "");
free(b32);
b32 = base32hexmem("f", STRLEN("f"), false);
assert_se(b32);
- assert_se(streq(b32, "CO"));
+ ASSERT_STREQ(b32, "CO");
free(b32);
b32 = base32hexmem("fo", STRLEN("fo"), false);
assert_se(b32);
- assert_se(streq(b32, "CPNG"));
+ ASSERT_STREQ(b32, "CPNG");
free(b32);
b32 = base32hexmem("foo", STRLEN("foo"), false);
assert_se(b32);
- assert_se(streq(b32, "CPNMU"));
+ ASSERT_STREQ(b32, "CPNMU");
free(b32);
b32 = base32hexmem("foob", STRLEN("foob"), false);
assert_se(b32);
- assert_se(streq(b32, "CPNMUOG"));
+ ASSERT_STREQ(b32, "CPNMUOG");
free(b32);
b32 = base32hexmem("fooba", STRLEN("fooba"), false);
assert_se(b32);
- assert_se(streq(b32, "CPNMUOJ1"));
+ ASSERT_STREQ(b32, "CPNMUOJ1");
free(b32);
b32 = base32hexmem("foobar", STRLEN("foobar"), false);
assert_se(b32);
- assert_se(streq(b32, "CPNMUOJ1E8"));
+ ASSERT_STREQ(b32, "CPNMUOJ1E8");
free(b32);
}
char *str;
str = strndupa_safe(mem, len);
- assert_se(streq(str, ans));
+ ASSERT_STREQ(str, ans);
}
}
char *b64;
assert_se(base64mem("", STRLEN(""), &b64) == 0);
- assert_se(streq(b64, ""));
+ ASSERT_STREQ(b64, "");
free(b64);
assert_se(base64mem("f", STRLEN("f"), &b64) == 4);
- assert_se(streq(b64, "Zg=="));
+ ASSERT_STREQ(b64, "Zg==");
free(b64);
assert_se(base64mem("fo", STRLEN("fo"), &b64) == 4);
- assert_se(streq(b64, "Zm8="));
+ ASSERT_STREQ(b64, "Zm8=");
free(b64);
assert_se(base64mem("foo", STRLEN("foo"), &b64) == 4);
- assert_se(streq(b64, "Zm9v"));
+ ASSERT_STREQ(b64, "Zm9v");
free(b64);
assert_se(base64mem("foob", STRLEN("foob"), &b64) == 8);
- assert_se(streq(b64, "Zm9vYg=="));
+ ASSERT_STREQ(b64, "Zm9vYg==");
free(b64);
assert_se(base64mem("fooba", STRLEN("fooba"), &b64) == 8);
- assert_se(streq(b64, "Zm9vYmE="));
+ ASSERT_STREQ(b64, "Zm9vYmE=");
free(b64);
assert_se(base64mem("foobar", STRLEN("foobar"), &b64) == 8);
- assert_se(streq(b64, "Zm9vYmFy"));
+ ASSERT_STREQ(b64, "Zm9vYmFy");
free(b64);
}
assert_se(new_len >= 0);
log_debug("base64_append_one(\"%s\")\nresult:\n%s\nexpected:\n%s", in, strnull(*buf), strnull(expected));
assert_se((size_t) new_len == strlen_ptr(*buf));
- assert_se(streq_ptr(*buf, expected));
+ ASSERT_STREQ(*buf, expected);
*len = new_len;
}
"baldohaldo",
result);
hex_result = hexmem(result, sizeof(result));
- assert_se(streq_ptr(hex_result, "c47ad5031ba21605e52c6ca68090d66a2dd5ccf84efa4bace15361a8cba63cda"));
+ ASSERT_STREQ(hex_result, "c47ad5031ba21605e52c6ca68090d66a2dd5ccf84efa4bace15361a8cba63cda");
hex_result = mfree(hex_result);
hmac_sha256_by_string("waldo",
"baldo haldo",
result);
hex_result = hexmem(result, sizeof(result));
- assert_se(streq_ptr(hex_result, "4e8974ad6c08b98cc2519cd1e27aa7195769fcf86db1dd7ceaab4d44c490ad69"));
+ ASSERT_STREQ(hex_result, "4e8974ad6c08b98cc2519cd1e27aa7195769fcf86db1dd7ceaab4d44c490ad69");
hex_result = mfree(hex_result);
hmac_sha256_by_string("waldo",
"baldo 4e8974ad6c08b98cc2519cd1e27aa7195769fcf86db1dd7ceaab4d44c490ad69 haldo",
result);
hex_result = hexmem(result, sizeof(result));
- assert_se(streq_ptr(hex_result, "039f3df430b19753ffb493e5b90708f75c5210b63c6bcbef3374eb3f0a3f97f7"));
+ ASSERT_STREQ(hex_result, "039f3df430b19753ffb493e5b90708f75c5210b63c6bcbef3374eb3f0a3f97f7");
hex_result = mfree(hex_result);
hmac_sha256_by_string("4e8974ad6c08b98cc2519cd1e27aa7195769fcf86db1dd7ceaab4d44c490ad69",
"baldo haldo",
result);
hex_result = hexmem(result, sizeof(result));
- assert_se(streq_ptr(hex_result, "c4cfaf48077cbb0bbd177a09e59ec4c248f4ca771503410f5b54b98d88d2f47b"));
+ ASSERT_STREQ(hex_result, "c4cfaf48077cbb0bbd177a09e59ec4c248f4ca771503410f5b54b98d88d2f47b");
hex_result = mfree(hex_result);
hmac_sha256_by_string("4e8974ad6c08b98cc2519cd1e27aa7195769fcf86db1dd7ceaab4d44c490ad69",
"supercalifragilisticexpialidocious",
result);
hex_result = hexmem(result, sizeof(result));
- assert_se(streq_ptr(hex_result, "2c059e7a63c4c3b23f47966a65fd2f8a2f5d7161e2e90d78ff68866b5c375cb7"));
+ ASSERT_STREQ(hex_result, "2c059e7a63c4c3b23f47966a65fd2f8a2f5d7161e2e90d78ff68866b5c375cb7");
hex_result = mfree(hex_result);
hmac_sha256_by_string("4e8974ad6c08b98cc2519cd1e27aa7195769fcf86db1dd7ceaab4d44c490ad69c47ad5031ba21605e52c6ca68090d66a2dd5ccf84efa4bace15361a8cba63cda",
"supercalifragilisticexpialidocious",
result);
hex_result = hexmem(result, sizeof(result));
- assert_se(streq_ptr(hex_result, "1dd1d1d45b9d9f9673dc9983c968c46ff3168e03cfeb4156a219eba1af4cff5f"));
+ ASSERT_STREQ(hex_result, "1dd1d1d45b9d9f9673dc9983c968c46ff3168e03cfeb4156a219eba1af4cff5f");
hex_result = mfree(hex_result);
}
/* simple hostname */
assert_se(write_string_file(path, "foo", WRITE_STRING_FILE_CREATE) == 0);
assert_se(read_etc_hostname(path, &hostname) == 0);
- assert_se(streq(hostname, "foo"));
+ ASSERT_STREQ(hostname, "foo");
hostname = mfree(hostname);
/* with comment */
assert_se(write_string_file(path, "# comment\nfoo", WRITE_STRING_FILE_CREATE) == 0);
assert_se(read_etc_hostname(path, &hostname) == 0);
assert_se(hostname);
- assert_se(streq(hostname, "foo"));
+ ASSERT_STREQ(hostname, "foo");
hostname = mfree(hostname);
/* with comment and extra whitespace */
assert_se(write_string_file(path, "# comment\n\n foo ", WRITE_STRING_FILE_CREATE) == 0);
assert_se(read_etc_hostname(path, &hostname) == 0);
assert_se(hostname);
- assert_se(streq(hostname, "foo"));
+ ASSERT_STREQ(hostname, "foo");
hostname = mfree(hostname);
/* cleans up name */
assert_se(write_string_file(path, "!foo/bar.com", WRITE_STRING_FILE_CREATE) == 0);
assert_se(read_etc_hostname(path, &hostname) == 0);
assert_se(hostname);
- assert_se(streq(hostname, "foobar.com"));
+ ASSERT_STREQ(hostname, "foobar.com");
hostname = mfree(hostname);
/* no value set */
char *s;
s = strdupa_safe("foobar");
- assert_se(streq(hostname_cleanup(s), "foobar"));
+ ASSERT_STREQ(hostname_cleanup(s), "foobar");
s = strdupa_safe("foobar.com");
- assert_se(streq(hostname_cleanup(s), "foobar.com"));
+ ASSERT_STREQ(hostname_cleanup(s), "foobar.com");
s = strdupa_safe("foobar.com.");
- assert_se(streq(hostname_cleanup(s), "foobar.com"));
+ ASSERT_STREQ(hostname_cleanup(s), "foobar.com");
s = strdupa_safe("foo-bar.-com-.");
- assert_se(streq(hostname_cleanup(s), "foo-bar.com"));
+ ASSERT_STREQ(hostname_cleanup(s), "foo-bar.com");
s = strdupa_safe("foo-bar-.-com-.");
- assert_se(streq(hostname_cleanup(s), "foo-bar--com"));
+ ASSERT_STREQ(hostname_cleanup(s), "foo-bar--com");
s = strdupa_safe("--foo-bar.-com");
- assert_se(streq(hostname_cleanup(s), "foo-bar.com"));
+ ASSERT_STREQ(hostname_cleanup(s), "foo-bar.com");
s = strdupa_safe("fooBAR");
- assert_se(streq(hostname_cleanup(s), "fooBAR"));
+ ASSERT_STREQ(hostname_cleanup(s), "fooBAR");
s = strdupa_safe("fooBAR.com");
- assert_se(streq(hostname_cleanup(s), "fooBAR.com"));
+ ASSERT_STREQ(hostname_cleanup(s), "fooBAR.com");
s = strdupa_safe("fooBAR.");
- assert_se(streq(hostname_cleanup(s), "fooBAR"));
+ ASSERT_STREQ(hostname_cleanup(s), "fooBAR");
s = strdupa_safe("fooBAR.com.");
- assert_se(streq(hostname_cleanup(s), "fooBAR.com"));
+ ASSERT_STREQ(hostname_cleanup(s), "fooBAR.com");
s = strdupa_safe("fööbar");
- assert_se(streq(hostname_cleanup(s), "fbar"));
+ ASSERT_STREQ(hostname_cleanup(s), "fbar");
s = strdupa_safe("");
assert_se(isempty(hostname_cleanup(s)));
s = strdupa_safe(".");
s = strdupa_safe("..");
assert_se(isempty(hostname_cleanup(s)));
s = strdupa_safe("foobar.");
- assert_se(streq(hostname_cleanup(s), "foobar"));
+ ASSERT_STREQ(hostname_cleanup(s), "foobar");
s = strdupa_safe(".foobar");
- assert_se(streq(hostname_cleanup(s), "foobar"));
+ ASSERT_STREQ(hostname_cleanup(s), "foobar");
s = strdupa_safe("foo..bar");
- assert_se(streq(hostname_cleanup(s), "foo.bar"));
+ ASSERT_STREQ(hostname_cleanup(s), "foo.bar");
s = strdupa_safe("foo.bar..");
- assert_se(streq(hostname_cleanup(s), "foo.bar"));
+ ASSERT_STREQ(hostname_cleanup(s), "foo.bar");
s = strdupa_safe("xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx");
- assert_se(streq(hostname_cleanup(s), "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"));
+ ASSERT_STREQ(hostname_cleanup(s), "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx");
s = strdupa_safe("xxxx........xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx");
- assert_se(streq(hostname_cleanup(s), "xxxx.xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"));
+ ASSERT_STREQ(hostname_cleanup(s), "xxxx.xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx");
}
TEST(hostname_malloc) {
}
printf("waldi: %s\n", sd_id128_to_string(ID128_WALDI, t));
- assert_se(streq(t, STR_WALDI));
+ ASSERT_STREQ(t, STR_WALDI);
assert_se(asprintf(&b, SD_ID128_FORMAT_STR, SD_ID128_FORMAT_VAL(ID128_WALDI)) == 32);
printf("waldi2: %s\n", b);
- assert_se(streq(t, b));
+ ASSERT_STREQ(t, b);
printf("waldi3: %s\n", sd_id128_to_uuid_string(ID128_WALDI, q));
- assert_se(streq(q, UUID_WALDI));
+ ASSERT_STREQ(q, UUID_WALDI);
b = mfree(b);
assert_se(asprintf(&b, SD_ID128_UUID_FORMAT_STR, SD_ID128_FORMAT_VAL(ID128_WALDI)) == 36);
printf("waldi4: %s\n", b);
- assert_se(streq(q, b));
+ ASSERT_STREQ(q, b);
assert_se(sd_id128_from_string(STR_WALDI, &id) >= 0);
assert_se(sd_id128_equal(id, ID128_WALDI));
assert_se(partition_policy_flags_extend(PARTITION_POLICY_GROWFS_ON) == (PARTITION_POLICY_GROWFS_ON|_PARTITION_POLICY_USE_MASK|_PARTITION_POLICY_READ_ONLY_MASK));
}
+static void test_policy_intersect_one(const char *a, const char *b, const char *c) {
+ _cleanup_(image_policy_freep) ImagePolicy *x = NULL, *y = NULL, *z = NULL, *t = NULL;
+
+ assert_se(image_policy_from_string(a, &x) >= 0);
+ assert_se(image_policy_from_string(b, &y) >= 0);
+ assert_se(image_policy_from_string(c, &z) >= 0);
+
+ assert_se(image_policy_intersect(x, y, &t) >= 0);
+
+ _cleanup_free_ char *s1 = NULL, *s2 = NULL, *s3 = NULL, *s4 = NULL;
+ assert_se(image_policy_to_string(x, false, &s1) >= 0);
+ assert_se(image_policy_to_string(y, false, &s2) >= 0);
+ assert_se(image_policy_to_string(z, false, &s3) >= 0);
+ assert_se(image_policy_to_string(t, false, &s4) >= 0);
+
+ log_info("%s ^ %s → %s vs. %s", s1, s2, s3, s4);
+
+ assert_se(image_policy_equivalent(z, t) > 0);
+}
+
+TEST(image_policy_intersect) {
+ test_policy_intersect_one("", "", "");
+ test_policy_intersect_one("-", "-", "-");
+ test_policy_intersect_one("*", "*", "*");
+ test_policy_intersect_one("~", "~", "~");
+ test_policy_intersect_one("root=verity+signed", "root=signed+verity", "root=verity+signed");
+ test_policy_intersect_one("root=verity+signed", "root=signed", "root=signed");
+ test_policy_intersect_one("root=verity+signed", "root=verity", "root=verity");
+ test_policy_intersect_one("root=open", "root=verity", "root=verity");
+ test_policy_intersect_one("root=open", "=verity+ignore", "root=verity+ignore:=ignore");
+}
+
DEFINE_TEST_MAIN(LOG_INFO);
_cleanup_free_ char *s = NULL;
assert_se(import_url_last_component(input, &s) == ret);
- assert_se(streq_ptr(output, s));
+ ASSERT_STREQ(output, s);
}
TEST(import_url_last_component) {
_cleanup_free_ char *s = NULL;
assert_se(import_url_change_suffix(input, n, suffix, &s) == ret);
- assert_se(streq_ptr(output, s));
+ ASSERT_STREQ(output, s);
}
TEST(import_url_change_suffix) {
printf("%s: %s/%u == %s\n", __func__, addr, prefixlen, r);
assert_se(startswith(r, addr));
- assert_se(streq(r, IN_ADDR_PREFIX_TO_STRING(f, &ua, prefixlen)));
- assert_se(streq(IN_ADDR_PREFIX_TO_STRING(f, &ua, prefixlen), r));
+ ASSERT_STREQ(r, IN_ADDR_PREFIX_TO_STRING(f, &ua, prefixlen));
+ ASSERT_STREQ(IN_ADDR_PREFIX_TO_STRING(f, &ua, prefixlen), r);
}
TEST(in_addr_to_string_prefix) {
assert_se(config_parse_in_addr_prefixes("unit", "filename", 1, "Service", 1, "IPAddressAllow", 0, str, prefixes, NULL) >= 0);
- assert_se(streq(str, IN_ADDR_PREFIX_TO_STRING(family, addr, prefixlen)));
- assert_se(streq(IN_ADDR_PREFIX_TO_STRING(family, addr, prefixlen), str));
+ ASSERT_STREQ(str, IN_ADDR_PREFIX_TO_STRING(family, addr, prefixlen));
+ ASSERT_STREQ(IN_ADDR_PREFIX_TO_STRING(family, addr, prefixlen), str);
}
static void test_config_parse_in_addr_prefixes(Set **ret) {
log_info("%s: %s", __func__, p);
assert_se(in_addr_prefix_from_string(p, family, &u, &l) >= 0);
- assert_se(streq(p, IN_ADDR_PREFIX_TO_STRING(family, &u, l)));
+ ASSERT_STREQ(p, IN_ADDR_PREFIX_TO_STRING(family, &u, l));
}
static void test_in_addr_prefix_to_string_unoptimized(int family, const char *p) {
const char *str2 = IN_ADDR_PREFIX_TO_STRING(family, &u2, len2);
assert_se(str2);
- assert_se(streq(str1, str2));
+ ASSERT_STREQ(str1, str2);
assert_se(len1 == len2);
assert_se(in_addr_equal(family, &u1, &u2) > 0);
}
assert_se(in_addr_from_string(f, addr, &ua) >= 0);
assert_se(in_addr_to_string(f, &ua, &r) >= 0);
printf("%s: %s == %s\n", __func__, addr, r);
- assert_se(streq(addr, r));
+ ASSERT_STREQ(addr, r);
- assert_se(streq(r, IN_ADDR_TO_STRING(f, &ua)));
+ ASSERT_STREQ(r, IN_ADDR_TO_STRING(f, &ua));
}
TEST(in_addr_to_string) {
assert_se(in_addr_prefixlen_to_netmask(AF_INET, &addr, prefixlen) >= 0);
assert_se(in_addr_to_string(AF_INET, &addr, &result) >= 0);
printf("test_in_addr_prefixlen_to_netmask: %s == %s\n", ipv4_netmasks[prefixlen], result);
- assert_se(streq(ipv4_netmasks[prefixlen], result));
+ ASSERT_STREQ(ipv4_netmasks[prefixlen], result);
}
for (unsigned char prefixlen = 0; prefixlen <= 128; prefixlen++) {
assert_se(in_addr_to_string(AF_INET6, &addr, &result) >= 0);
printf("test_in_addr_prefixlen_to_netmask: %s\n", result);
if (ipv6_netmasks[prefixlen])
- assert_se(streq(ipv6_netmasks[prefixlen], result));
+ ASSERT_STREQ(ipv6_netmasks[prefixlen], result);
}
}
assert_se(unit_file_mask(RUNTIME_SCOPE_SYSTEM, 0, root, STRV_MAKE("a.service"), &changes, &n_changes) >= 0);
assert_se(n_changes == 1);
assert_se(changes[0].type == INSTALL_CHANGE_SYMLINK);
- assert_se(streq(changes[0].source, "/dev/null"));
+ ASSERT_STREQ(changes[0].source, "/dev/null");
p = strjoina(root, SYSTEM_CONFIG_UNIT_DIR"/a.service");
- assert_se(streq(changes[0].path, p));
+ ASSERT_STREQ(changes[0].path, p);
install_changes_free(changes, n_changes);
changes = NULL; n_changes = 0;
assert_se(n_changes == 1);
assert_se(changes[0].type == INSTALL_CHANGE_UNLINK);
p = strjoina(root, SYSTEM_CONFIG_UNIT_DIR"/a.service");
- assert_se(streq(changes[0].path, p));
+ ASSERT_STREQ(changes[0].path, p);
install_changes_free(changes, n_changes);
changes = NULL; n_changes = 0;
assert_se(unit_file_enable(RUNTIME_SCOPE_SYSTEM, 0, root, STRV_MAKE("a.service"), &changes, &n_changes) == 1);
assert_se(n_changes == 1);
assert_se(changes[0].type == INSTALL_CHANGE_SYMLINK);
- assert_se(streq(changes[0].source, "/usr/lib/systemd/system/a.service"));
+ ASSERT_STREQ(changes[0].source, "/usr/lib/systemd/system/a.service");
p = strjoina(root, SYSTEM_CONFIG_UNIT_DIR"/multi-user.target.wants/a.service");
- assert_se(streq(changes[0].path, p));
+ ASSERT_STREQ(changes[0].path, p);
install_changes_free(changes, n_changes);
changes = NULL; n_changes = 0;
assert_se(n_changes == 1);
assert_se(changes[0].type == INSTALL_CHANGE_UNLINK);
p = strjoina(root, SYSTEM_CONFIG_UNIT_DIR"/multi-user.target.wants/a.service");
- assert_se(streq(changes[0].path, p));
+ ASSERT_STREQ(changes[0].path, p);
install_changes_free(changes, n_changes);
changes = NULL; n_changes = 0;
assert_se(unit_file_enable(RUNTIME_SCOPE_SYSTEM, 0, root, STRV_MAKE("d.service"), &changes, &n_changes) >= 0);
assert_se(n_changes == 1);
assert_se(changes[0].type == INSTALL_CHANGE_SYMLINK);
- assert_se(streq(changes[0].source, "/usr/lib/systemd/system/a.service"));
+ ASSERT_STREQ(changes[0].source, "/usr/lib/systemd/system/a.service");
p = strjoina(root, SYSTEM_CONFIG_UNIT_DIR"/multi-user.target.wants/a.service");
- assert_se(streq(changes[0].path, p));
+ ASSERT_STREQ(changes[0].path, p);
install_changes_free(changes, n_changes);
changes = NULL; n_changes = 0;
assert_se(n_changes == 2);
assert_se(changes[0].type == INSTALL_CHANGE_UNLINK);
p = strjoina(root, SYSTEM_CONFIG_UNIT_DIR"/multi-user.target.wants/a.service");
- assert_se(streq(changes[0].path, p));
+ ASSERT_STREQ(changes[0].path, p);
assert_se(changes[1].type == INSTALL_CHANGE_SYMLINK);
- assert_se(streq(changes[1].source, "/usr/lib/systemd/system/a.service"));
- assert_se(streq(changes[1].path, p));
+ ASSERT_STREQ(changes[1].source, "/usr/lib/systemd/system/a.service");
+ ASSERT_STREQ(changes[1].path, p);
install_changes_free(changes, n_changes);
changes = NULL; n_changes = 0;
assert_se(unit_file_enable(RUNTIME_SCOPE_SYSTEM, 0, root, STRV_MAKE("f.service"), &changes, &n_changes) == 1);
assert_se(n_changes == 2);
assert_se(changes[0].type == INSTALL_CHANGE_SYMLINK);
- assert_se(streq(changes[0].source, "/usr/lib/systemd/system/f.service"));
+ ASSERT_STREQ(changes[0].source, "/usr/lib/systemd/system/f.service");
p = strjoina(root, SYSTEM_CONFIG_UNIT_DIR"/x.target.wants/f.service");
- assert_se(streq(changes[0].path, p));
+ ASSERT_STREQ(changes[0].path, p);
assert_se(changes[1].type == INSTALL_CHANGE_DESTINATION_NOT_PRESENT);
p = strjoina(root, "/usr/lib/systemd/system/f.service");
- assert_se(streq(changes[1].source, p));
- assert_se(streq(changes[1].path, "x.target"));
+ ASSERT_STREQ(changes[1].source, p);
+ ASSERT_STREQ(changes[1].path, "x.target");
install_changes_free(changes, n_changes);
changes = NULL; n_changes = 0;
assert_se(unit_file_link(RUNTIME_SCOPE_SYSTEM, 0, root, STRV_MAKE("/opt/linked.service"), &changes, &n_changes) >= 0);
assert_se(n_changes == 1);
assert_se(changes[0].type == INSTALL_CHANGE_SYMLINK);
- assert_se(streq(changes[0].source, "/opt/linked.service"));
+ ASSERT_STREQ(changes[0].source, "/opt/linked.service");
p = strjoina(root, SYSTEM_CONFIG_UNIT_DIR"/linked.service");
- assert_se(streq(changes[0].path, p));
+ ASSERT_STREQ(changes[0].path, p);
install_changes_free(changes, n_changes);
changes = NULL; n_changes = 0;
assert_se(n_changes == 1);
assert_se(changes[0].type == INSTALL_CHANGE_UNLINK);
p = strjoina(root, SYSTEM_CONFIG_UNIT_DIR"/linked.service");
- assert_se(streq(changes[0].path, p));
+ ASSERT_STREQ(changes[0].path, p);
install_changes_free(changes, n_changes);
changes = NULL; n_changes = 0;
if (s && streq(changes[i].path, s))
/* The alias symlink should point within the search path. */
- assert_se(streq(changes[i].source, SYSTEM_CONFIG_UNIT_DIR"/linked.service"));
+ ASSERT_STREQ(changes[i].source, SYSTEM_CONFIG_UNIT_DIR"/linked.service");
else
- assert_se(streq(changes[i].source, "/opt/linked.service"));
+ ASSERT_STREQ(changes[i].source, "/opt/linked.service");
if (p && streq(changes[i].path, p))
p = NULL;
q = strjoina(root, SYSTEM_CONFIG_UNIT_DIR"/linked2.service");
for (i = 0 ; i < n_changes; i++) {
assert_se(changes[i].type == INSTALL_CHANGE_SYMLINK);
- assert_se(streq(changes[i].source, "/opt/linked2.service"));
+ ASSERT_STREQ(changes[i].source, "/opt/linked2.service");
if (p && streq(changes[i].path, p))
p = NULL;
assert_se(changes[0].type == INSTALL_CHANGE_SYMLINK);
assert_se(startswith(changes[0].path, root));
assert_se(endswith(changes[0].path, "linked3.service"));
- assert_se(streq(changes[0].source, "/opt/linked3.service"));
+ ASSERT_STREQ(changes[0].source, "/opt/linked3.service");
install_changes_free(changes, n_changes);
changes = NULL; n_changes = 0;
}
assert_se(unit_file_set_default(RUNTIME_SCOPE_SYSTEM, 0, root, "idontexist.target", &changes, &n_changes) == -ENOENT);
assert_se(n_changes == 1);
assert_se(changes[0].type == -ENOENT);
- assert_se(streq_ptr(changes[0].path, "idontexist.target"));
+ ASSERT_STREQ(changes[0].path, "idontexist.target");
install_changes_free(changes, n_changes);
changes = NULL; n_changes = 0;
assert_se(unit_file_set_default(RUNTIME_SCOPE_SYSTEM, 0, root, "test-default.target", &changes, &n_changes) >= 0);
assert_se(n_changes == 1);
assert_se(changes[0].type == INSTALL_CHANGE_SYMLINK);
- assert_se(streq(changes[0].source, "/usr/lib/systemd/system/test-default-real.target"));
+ ASSERT_STREQ(changes[0].source, "/usr/lib/systemd/system/test-default-real.target");
p = strjoina(root, SYSTEM_CONFIG_UNIT_DIR "/" SPECIAL_DEFAULT_TARGET);
- assert_se(streq(changes[0].path, p));
+ ASSERT_STREQ(changes[0].path, p);
install_changes_free(changes, n_changes);
changes = NULL; n_changes = 0;
assert_se(unit_file_get_default(RUNTIME_SCOPE_SYSTEM, root, &def) >= 0);
- assert_se(streq_ptr(def, "test-default-real.target"));
+ ASSERT_STREQ(def, "test-default-real.target");
}
TEST(add_dependency) {
assert_se(unit_file_add_dependency(RUNTIME_SCOPE_SYSTEM, 0, root, STRV_MAKE("add-dependency-test-service.service"), "add-dependency-test-target.target", UNIT_WANTS, &changes, &n_changes) >= 0);
assert_se(n_changes == 1);
assert_se(changes[0].type == INSTALL_CHANGE_SYMLINK);
- assert_se(streq(changes[0].source, "/usr/lib/systemd/system/real-add-dependency-test-service.service"));
+ ASSERT_STREQ(changes[0].source, "/usr/lib/systemd/system/real-add-dependency-test-service.service");
p = strjoina(root, SYSTEM_CONFIG_UNIT_DIR"/real-add-dependency-test-target.target.wants/real-add-dependency-test-service.service");
- assert_se(streq(changes[0].path, p));
+ ASSERT_STREQ(changes[0].path, p);
install_changes_free(changes, n_changes);
changes = NULL; n_changes = 0;
}
assert_se(unit_file_enable(RUNTIME_SCOPE_SYSTEM, 0, root, STRV_MAKE("template@.service"), &changes, &n_changes) >= 0);
assert_se(n_changes == 1);
assert_se(changes[0].type == INSTALL_CHANGE_SYMLINK);
- assert_se(streq(changes[0].source, "/usr/lib/systemd/system/template@.service"));
+ ASSERT_STREQ(changes[0].source, "/usr/lib/systemd/system/template@.service");
p = strjoina(root, SYSTEM_CONFIG_UNIT_DIR"/multi-user.target.wants/template@def.service");
- assert_se(streq(changes[0].path, p));
+ ASSERT_STREQ(changes[0].path, p);
install_changes_free(changes, n_changes);
changes = NULL; n_changes = 0;
assert_se(unit_file_disable(RUNTIME_SCOPE_SYSTEM, 0, root, STRV_MAKE("template@.service"), &changes, &n_changes) >= 0);
assert_se(n_changes == 1);
assert_se(changes[0].type == INSTALL_CHANGE_UNLINK);
- assert_se(streq(changes[0].path, p));
+ ASSERT_STREQ(changes[0].path, p);
install_changes_free(changes, n_changes);
changes = NULL; n_changes = 0;
assert_se(unit_file_enable(RUNTIME_SCOPE_SYSTEM, 0, root, STRV_MAKE("template@foo.service"), &changes, &n_changes) >= 0);
assert_se(changes[0].type == INSTALL_CHANGE_SYMLINK);
- assert_se(streq(changes[0].source, "/usr/lib/systemd/system/template@.service"));
+ ASSERT_STREQ(changes[0].source, "/usr/lib/systemd/system/template@.service");
p = strjoina(root, SYSTEM_CONFIG_UNIT_DIR"/multi-user.target.wants/template@foo.service");
- assert_se(streq(changes[0].path, p));
+ ASSERT_STREQ(changes[0].path, p);
install_changes_free(changes, n_changes);
changes = NULL; n_changes = 0;
assert_se(unit_file_disable(RUNTIME_SCOPE_SYSTEM, 0, root, STRV_MAKE("template@foo.service"), &changes, &n_changes) >= 0);
assert_se(n_changes == 1);
assert_se(changes[0].type == INSTALL_CHANGE_UNLINK);
- assert_se(streq(changes[0].path, p));
+ ASSERT_STREQ(changes[0].path, p);
install_changes_free(changes, n_changes);
changes = NULL; n_changes = 0;
assert_se(unit_file_enable(RUNTIME_SCOPE_SYSTEM, 0, root, STRV_MAKE("template-symlink@quux.service"), &changes, &n_changes) >= 0);
assert_se(changes[0].type == INSTALL_CHANGE_SYMLINK);
- assert_se(streq(changes[0].source, "/usr/lib/systemd/system/template@.service"));
+ ASSERT_STREQ(changes[0].source, "/usr/lib/systemd/system/template@.service");
p = strjoina(root, SYSTEM_CONFIG_UNIT_DIR"/multi-user.target.wants/template@quux.service");
- assert_se(streq(changes[0].path, p));
+ ASSERT_STREQ(changes[0].path, p);
install_changes_free(changes, n_changes);
changes = NULL; n_changes = 0;
assert_se(unit_file_enable(RUNTIME_SCOPE_SYSTEM, 0, root, STRV_MAKE("indirectc.service"), &changes, &n_changes) >= 0);
assert_se(n_changes == 1);
assert_se(changes[0].type == INSTALL_CHANGE_SYMLINK);
- assert_se(streq(changes[0].source, "/usr/lib/systemd/system/indirectb.service"));
+ ASSERT_STREQ(changes[0].source, "/usr/lib/systemd/system/indirectb.service");
p = strjoina(root, SYSTEM_CONFIG_UNIT_DIR"/multi-user.target.wants/indirectb.service");
- assert_se(streq(changes[0].path, p));
+ ASSERT_STREQ(changes[0].path, p);
install_changes_free(changes, n_changes);
changes = NULL; n_changes = 0;
assert_se(n_changes == 1);
assert_se(changes[0].type == INSTALL_CHANGE_UNLINK);
p = strjoina(root, SYSTEM_CONFIG_UNIT_DIR"/multi-user.target.wants/indirectb.service");
- assert_se(streq(changes[0].path, p));
+ ASSERT_STREQ(changes[0].path, p);
install_changes_free(changes, n_changes);
changes = NULL; n_changes = 0;
}
assert_se(unit_file_preset(RUNTIME_SCOPE_SYSTEM, 0, root, STRV_MAKE("preset-yes.service"), UNIT_FILE_PRESET_FULL, &changes, &n_changes) >= 0);
assert_se(n_changes == 1);
assert_se(changes[0].type == INSTALL_CHANGE_SYMLINK);
- assert_se(streq(changes[0].source, "/usr/lib/systemd/system/preset-yes.service"));
+ ASSERT_STREQ(changes[0].source, "/usr/lib/systemd/system/preset-yes.service");
p = strjoina(root, SYSTEM_CONFIG_UNIT_DIR"/multi-user.target.wants/preset-yes.service");
- assert_se(streq(changes[0].path, p));
+ ASSERT_STREQ(changes[0].path, p);
install_changes_free(changes, n_changes);
changes = NULL; n_changes = 0;
assert_se(n_changes == 1);
assert_se(changes[0].type == INSTALL_CHANGE_UNLINK);
p = strjoina(root, SYSTEM_CONFIG_UNIT_DIR"/multi-user.target.wants/preset-yes.service");
- assert_se(streq(changes[0].path, p));
+ ASSERT_STREQ(changes[0].path, p);
install_changes_free(changes, n_changes);
changes = NULL; n_changes = 0;
for (i = 0; i < n_changes; i++) {
if (changes[i].type == INSTALL_CHANGE_SYMLINK) {
- assert_se(streq(changes[i].source, "/usr/lib/systemd/system/preset-yes.service"));
- assert_se(streq(changes[i].path, p));
+ ASSERT_STREQ(changes[i].source, "/usr/lib/systemd/system/preset-yes.service");
+ ASSERT_STREQ(changes[i].path, p);
} else
assert_se(changes[i].type == INSTALL_CHANGE_UNLINK);
}
assert_se(unit_file_revert(RUNTIME_SCOPE_SYSTEM, root, STRV_MAKE("xx.service"), &changes, &n_changes) >= 0);
assert_se(n_changes == 1);
assert_se(changes[0].type == INSTALL_CHANGE_UNLINK);
- assert_se(streq(changes[0].path, p));
+ ASSERT_STREQ(changes[0].path, p);
install_changes_free(changes, n_changes);
changes = NULL; n_changes = 0;
assert_se(unit_file_revert(RUNTIME_SCOPE_SYSTEM, root, STRV_MAKE("xx.service"), &changes, &n_changes) >= 0);
assert_se(n_changes == 2);
assert_se(changes[0].type == INSTALL_CHANGE_UNLINK);
- assert_se(streq(changes[0].path, p));
+ ASSERT_STREQ(changes[0].path, p);
p = strjoina(root, SYSTEM_CONFIG_UNIT_DIR"/xx.service.d");
assert_se(changes[1].type == INSTALL_CHANGE_UNLINK);
- assert_se(streq(changes[1].path, p));
+ ASSERT_STREQ(changes[1].path, p);
install_changes_free(changes, n_changes);
changes = NULL; n_changes = 0;
}
assert_se(unit_file_preset(RUNTIME_SCOPE_SYSTEM, 0, root, STRV_MAKE("prefix-1.service"), UNIT_FILE_PRESET_FULL, &changes, &n_changes) >= 0);
assert_se(n_changes == 1);
assert_se(changes[0].type == INSTALL_CHANGE_SYMLINK);
- assert_se(streq(changes[0].source, "/usr/lib/systemd/system/prefix-1.service"));
+ ASSERT_STREQ(changes[0].source, "/usr/lib/systemd/system/prefix-1.service");
p = strjoina(root, SYSTEM_CONFIG_UNIT_DIR"/multi-user.target.wants/prefix-1.service");
- assert_se(streq(changes[0].path, p));
+ ASSERT_STREQ(changes[0].path, p);
install_changes_free(changes, n_changes);
changes = NULL; n_changes = 0;
assert_se(n_changes == 2);
assert_se(changes[0].type == INSTALL_CHANGE_SYMLINK);
assert_se(changes[1].type == INSTALL_CHANGE_SYMLINK);
- assert_se(streq(changes[0].source, "/usr/lib/systemd/system/with-dropin-1.service"));
- assert_se(streq(changes[1].source, "/usr/lib/systemd/system/with-dropin-1.service"));
+ ASSERT_STREQ(changes[0].source, "/usr/lib/systemd/system/with-dropin-1.service");
+ ASSERT_STREQ(changes[1].source, "/usr/lib/systemd/system/with-dropin-1.service");
p = strjoina(root, SYSTEM_CONFIG_UNIT_DIR"/multi-user.target.wants/with-dropin-1.service");
- assert_se(streq(changes[0].path, p));
+ ASSERT_STREQ(changes[0].path, p);
p = strjoina(root, SYSTEM_CONFIG_UNIT_DIR"/graphical.target.wants/with-dropin-1.service");
- assert_se(streq(changes[1].path, p));
+ ASSERT_STREQ(changes[1].path, p);
install_changes_free(changes, n_changes);
changes = NULL; n_changes = 0;
assert_se(n_changes == 2);
assert_se(changes[0].type == INSTALL_CHANGE_SYMLINK);
assert_se(changes[1].type == INSTALL_CHANGE_SYMLINK);
- assert_se(streq(changes[0].source, SYSTEM_CONFIG_UNIT_DIR"/with-dropin-2.service"));
- assert_se(streq(changes[1].source, SYSTEM_CONFIG_UNIT_DIR"/with-dropin-2.service"));
+ ASSERT_STREQ(changes[0].source, SYSTEM_CONFIG_UNIT_DIR"/with-dropin-2.service");
+ ASSERT_STREQ(changes[1].source, SYSTEM_CONFIG_UNIT_DIR"/with-dropin-2.service");
p = strjoina(root, SYSTEM_CONFIG_UNIT_DIR"/multi-user.target.wants/with-dropin-2.service");
- assert_se(streq(changes[0].path, p));
+ ASSERT_STREQ(changes[0].path, p);
p = strjoina(root, SYSTEM_CONFIG_UNIT_DIR"/graphical.target.wants/with-dropin-2.service");
- assert_se(streq(changes[1].path, p));
+ ASSERT_STREQ(changes[1].path, p);
install_changes_free(changes, n_changes);
changes = NULL; n_changes = 0;
assert_se(n_changes == 2);
assert_se(changes[0].type == INSTALL_CHANGE_SYMLINK);
assert_se(changes[1].type == INSTALL_CHANGE_SYMLINK);
- assert_se(streq(changes[0].source, "/usr/lib/systemd/system/with-dropin-3.service"));
- assert_se(streq(changes[1].source, "/usr/lib/systemd/system/with-dropin-3.service"));
+ ASSERT_STREQ(changes[0].source, "/usr/lib/systemd/system/with-dropin-3.service");
+ ASSERT_STREQ(changes[1].source, "/usr/lib/systemd/system/with-dropin-3.service");
p = strjoina(root, SYSTEM_CONFIG_UNIT_DIR"/multi-user.target.wants/with-dropin-3.service");
- assert_se(streq(changes[0].path, p));
+ ASSERT_STREQ(changes[0].path, p);
p = strjoina(root, SYSTEM_CONFIG_UNIT_DIR"/graphical.target.wants/with-dropin-3.service");
- assert_se(streq(changes[1].path, p));
+ ASSERT_STREQ(changes[1].path, p);
install_changes_free(changes, n_changes);
changes = NULL; n_changes = 0;
assert_se(n_changes == 2);
assert_se(changes[0].type == INSTALL_CHANGE_SYMLINK);
assert_se(changes[1].type == INSTALL_CHANGE_SYMLINK);
- assert_se(streq(changes[0].source, "/usr/lib/systemd/system/with-dropin-4a.service"));
- assert_se(streq(changes[1].source, "/usr/lib/systemd/system/with-dropin-4b.service"));
+ ASSERT_STREQ(changes[0].source, "/usr/lib/systemd/system/with-dropin-4a.service");
+ ASSERT_STREQ(changes[1].source, "/usr/lib/systemd/system/with-dropin-4b.service");
p = strjoina(root, SYSTEM_CONFIG_UNIT_DIR"/multi-user.target.wants/with-dropin-4a.service");
- assert_se(streq(changes[0].path, p));
+ ASSERT_STREQ(changes[0].path, p);
p = strjoina(root, SYSTEM_CONFIG_UNIT_DIR"/multi-user.target.wants/with-dropin-4b.service");
- assert_se(streq(changes[1].path, p));
+ ASSERT_STREQ(changes[1].path, p);
install_changes_free(changes, n_changes);
changes = NULL; n_changes = 0;
assert_se(n_changes == 2);
assert_se(changes[0].type == INSTALL_CHANGE_SYMLINK);
assert_se(changes[1].type == INSTALL_CHANGE_SYMLINK);
- assert_se(streq(changes[0].source, "/usr/lib/systemd/system/with-dropin-1@.service"));
- assert_se(streq(changes[1].source, "/usr/lib/systemd/system/with-dropin-1@.service"));
+ ASSERT_STREQ(changes[0].source, "/usr/lib/systemd/system/with-dropin-1@.service");
+ ASSERT_STREQ(changes[1].source, "/usr/lib/systemd/system/with-dropin-1@.service");
p = strjoina(root, SYSTEM_CONFIG_UNIT_DIR"/multi-user.target.wants/with-dropin-1@instance-1.service");
- assert_se(streq(changes[0].path, p));
+ ASSERT_STREQ(changes[0].path, p);
p = strjoina(root, SYSTEM_CONFIG_UNIT_DIR"/graphical.target.wants/with-dropin-1@instance-1.service");
- assert_se(streq(changes[1].path, p));
+ ASSERT_STREQ(changes[1].path, p);
install_changes_free(changes, n_changes);
changes = NULL; n_changes = 0;
assert_se(n_changes == 2);
assert_se(changes[0].type == INSTALL_CHANGE_SYMLINK);
assert_se(changes[1].type == INSTALL_CHANGE_SYMLINK);
- assert_se(streq(changes[0].source, "/usr/lib/systemd/system/with-dropin-2@.service"));
- assert_se(streq(changes[1].source, "/usr/lib/systemd/system/with-dropin-2@.service"));
+ ASSERT_STREQ(changes[0].source, "/usr/lib/systemd/system/with-dropin-2@.service");
+ ASSERT_STREQ(changes[1].source, "/usr/lib/systemd/system/with-dropin-2@.service");
p = strjoina(root, SYSTEM_CONFIG_UNIT_DIR"/multi-user.target.wants/with-dropin-2@instance-1.service");
- assert_se(streq(changes[0].path, p));
+ ASSERT_STREQ(changes[0].path, p);
p = strjoina(root, SYSTEM_CONFIG_UNIT_DIR"/graphical.target.wants/with-dropin-2@instance-1.service");
- assert_se(streq(changes[1].path, p));
+ ASSERT_STREQ(changes[1].path, p);
install_changes_free(changes, n_changes);
changes = NULL; n_changes = 0;
assert_se(unit_file_enable(RUNTIME_SCOPE_SYSTEM, 0, root, STRV_MAKE("with-dropin-2@instance-2.service"), &changes, &n_changes) == 1);
assert_se(n_changes == 1);
assert_se(changes[0].type == INSTALL_CHANGE_SYMLINK);
- assert_se(streq(changes[0].source, "/usr/lib/systemd/system/with-dropin-2@.service"));
+ ASSERT_STREQ(changes[0].source, "/usr/lib/systemd/system/with-dropin-2@.service");
p = strjoina(root, SYSTEM_CONFIG_UNIT_DIR"/multi-user.target.wants/with-dropin-2@instance-2.service");
- assert_se(streq(changes[0].path, p));
+ ASSERT_STREQ(changes[0].path, p);
install_changes_free(changes, n_changes);
changes = NULL; n_changes = 0;
assert_se(unit_file_enable(RUNTIME_SCOPE_SYSTEM, 0, root, STRV_MAKE("with-dropin-3@.service"), &changes, &n_changes) == 1);
assert_se(n_changes == 1);
assert_se(changes[0].type == INSTALL_CHANGE_SYMLINK);
- assert_se(streq(changes[0].source, "/usr/lib/systemd/system/with-dropin-3@.service"));
+ ASSERT_STREQ(changes[0].source, "/usr/lib/systemd/system/with-dropin-3@.service");
p = strjoina(root, SYSTEM_CONFIG_UNIT_DIR"/multi-user.target.wants/with-dropin-3@instance-2.service");
- assert_se(streq(changes[0].path, p));
+ ASSERT_STREQ(changes[0].path, p);
install_changes_free(changes, n_changes);
changes = NULL; n_changes = 0;
assert_se(n_changes == 1);
assert_se(changes[0].type == INSTALL_CHANGE_SYMLINK);
p = strjoina(root, SYSTEM_CONFIG_UNIT_DIR"/multi-user.target.wants/foo@bar0.service");
- assert_se(streq(changes[0].path, p));
+ ASSERT_STREQ(changes[0].path, p);
install_changes_free(changes, n_changes);
changes = NULL; n_changes = 0;
assert_se(n_changes == 1);
assert_se(changes[0].type == INSTALL_CHANGE_UNLINK);
p = strjoina(root, SYSTEM_CONFIG_UNIT_DIR"/multi-user.target.wants/foo@bar0.service");
- assert_se(streq(changes[0].path, p));
+ ASSERT_STREQ(changes[0].path, p);
install_changes_free(changes, n_changes);
changes = NULL; n_changes = 0;
* RequiredBy= settings, and less so for Alias=. The only case where it should happen is when we have
* an Alias=alias@.service an instantiated template template@instance. In that case the instance name
* should be propagated into the alias as alias@instance. */
- assert_se(streq_ptr(alias2, updated_name));
+ ASSERT_STREQ(alias2, updated_name);
}
TEST(verify_alias) {
}
static void test_str(const char *s) {
- assert_se(streq(ip_protocol_to_name(ip_protocol_from_name(s)), s));
- assert_se(streq(ip_protocol_to_name(parse_ip_protocol(s)), s));
+ ASSERT_STREQ(ip_protocol_to_name(ip_protocol_from_name(s)), s);
+ ASSERT_STREQ(ip_protocol_to_name(parse_ip_protocol(s)), s);
}
static void test_str_fail(const char *s, int error) {
const char *nn;
nn = va_arg(ap, const char *);
- assert_se(streq_ptr(nn, str));
+ ASSERT_STREQ(nn, str);
} else if (t == JSON_TOKEN_REAL) {
double d;
s = mfree(s);
r = json_variant_format(w, JSON_FORMAT_CENSOR_SENSITIVE, &s);
assert_se(s);
- assert_se(streq_ptr(s, "\"<sensitive data>\""));
+ ASSERT_STREQ(s, "\"<sensitive data>\"");
s = mfree(s);
r = json_variant_format(w, JSON_FORMAT_PRETTY, &s);
assert_se(p && json_variant_type(p) == JSON_VARIANT_STRING);
/* k equals v */
- assert_se(streq(json_variant_string(p), "v"));
+ ASSERT_STREQ(json_variant_string(p), "v");
/* has foo */
p = json_variant_by_key(v, "foo");
assert_se(json_variant_format(b, 0, &t) >= 0);
log_info("GOT: %s", t);
- assert_se(streq(s, t));
+ ASSERT_STREQ(s, t);
a = json_variant_unref(a);
b = json_variant_unref(b);
assert_se(fopen_unlocked("/dev/null", "re", &f) >= 0);
assert_se(json_parse_file(f, "waldo", 0, &v, NULL, NULL) == -ENODATA);
- assert_se(v == NULL);
+ ASSERT_NULL(v);
}
TEST(json_parse_file_invalid) {
assert_se(f = fmemopen_unlocked((void*) "kookoo", 6, "r"));
assert_se(json_parse_file(f, "waldo", 0, &v, NULL, NULL) == -EINVAL);
- assert_se(v == NULL);
+ ASSERT_NULL(v);
}
TEST(source) {
assert_se(!json_variant_is_normalized(v));
assert_se(json_variant_format(v, 0, &t) >= 0);
- assert_se(streq(t, "{\"b\":\"x\",\"c\":\"y\",\"a\":\"z\"}"));
+ ASSERT_STREQ(t, "{\"b\":\"x\",\"c\":\"y\",\"a\":\"z\"}");
t = mfree(t);
assert_se(json_build(&w, JSON_BUILD_OBJECT(
assert_se(!json_variant_is_normalized(w));
assert_se(json_variant_format(w, 0, &t) >= 0);
- assert_se(streq(t, "{\"bar\":\"zzz\",\"foo\":{\"b\":\"x\",\"c\":\"y\",\"a\":\"z\"}}"));
+ ASSERT_STREQ(t, "{\"bar\":\"zzz\",\"foo\":{\"b\":\"x\",\"c\":\"y\",\"a\":\"z\"}}");
t = mfree(t);
assert_se(json_variant_sort(&v) >= 0);
assert_se(json_variant_is_normalized(v));
assert_se(json_variant_format(v, 0, &t) >= 0);
- assert_se(streq(t, "{\"a\":\"z\",\"b\":\"x\",\"c\":\"y\"}"));
+ ASSERT_STREQ(t, "{\"a\":\"z\",\"b\":\"x\",\"c\":\"y\"}");
t = mfree(t);
assert_se(json_variant_normalize(&w) >= 0);
assert_se(json_variant_is_normalized(w));
assert_se(json_variant_format(w, 0, &t) >= 0);
- assert_se(streq(t, "{\"bar\":\"zzz\",\"foo\":{\"a\":\"z\",\"b\":\"x\",\"c\":\"y\"}}"));
+ ASSERT_STREQ(t, "{\"bar\":\"zzz\",\"foo\":{\"a\":\"z\",\"b\":\"x\",\"c\":\"y\"}}");
t = mfree(t);
}
assert_se(json_variant_is_string(k));
z = (char[5]){ '<', c, c, '>', 0};
- assert_se(streq(json_variant_string(k), z));
+ ASSERT_STREQ(json_variant_string(k), z);
}
}
assert_se(json_variant_get_source(a, &s1, &line1, &col1) >= 0);
assert_se(json_variant_get_source(b, &s2, &line2, &col2) >= 0);
- assert_se(streq_ptr(s1, source ? "string 1" : NULL));
- assert_se(streq_ptr(s2, source ? "string 2" : NULL));
+ ASSERT_STREQ(s1, source ? "string 1" : NULL);
+ ASSERT_STREQ(s2, source ? "string 2" : NULL);
assert_se(line1 == 1);
assert_se(col1 == 2);
assert_se(line2 == 3);
assert_se(elem = json_variant_by_index(a, 1));
assert_se(json_variant_get_source(elem, &s2, &line2, &col2) >= 0);
- assert_se(streq_ptr(s1, source ? "string 2" : NULL));
- assert_se(streq_ptr(s2, source ? "string 2" : NULL));
+ ASSERT_STREQ(s1, source ? "string 2" : NULL);
+ ASSERT_STREQ(s2, source ? "string 2" : NULL);
assert_se(line1 == 3);
assert_se(col1 == 5);
assert_se(line2 == 3);
json_variant_sensitive(a);
assert_se(json_variant_format(a, JSON_FORMAT_CENSOR_SENSITIVE, &s) >= 0);
- assert_se(streq_ptr(s, "\"<sensitive data>\""));
+ ASSERT_STREQ(s, "\"<sensitive data>\"");
s = mfree(s);
r = json_variant_format(b, JSON_FORMAT_CENSOR_SENSITIVE, &s);
json_variant_dump(v, JSON_FORMAT_COLOR|JSON_FORMAT_PRETTY, NULL, NULL);
assert_se(json_variant_format(v, JSON_FORMAT_CENSOR_SENSITIVE, &s) >= 0);
- assert_se(streq_ptr(s, "{\"b\":[\"foo\",\"bar\",\"baz\",\"qux\"],\"a\":\"<sensitive data>\",\"c\":-9223372036854775808,\"d\":\"-9223372036854775808\",\"e\":{}}"));
+ ASSERT_STREQ(s, "{\"b\":[\"foo\",\"bar\",\"baz\",\"qux\"],\"a\":\"<sensitive data>\",\"c\":-9223372036854775808,\"d\":\"-9223372036854775808\",\"e\":{}}");
s = mfree(s);
v = json_variant_unref(v);
json_variant_dump(v, JSON_FORMAT_COLOR|JSON_FORMAT_PRETTY, NULL, NULL);
assert_se(json_variant_format(v, JSON_FORMAT_CENSOR_SENSITIVE, &s) >= 0);
- assert_se(streq_ptr(s, "{\"b\":[\"foo\",\"bar\",\"baz\",\"qux\"],\"c\":-9223372036854775808,\"a\":\"<sensitive data>\",\"d\":\"-9223372036854775808\",\"e\":{}}"));
+ ASSERT_STREQ(s, "{\"b\":[\"foo\",\"bar\",\"baz\",\"qux\"],\"c\":-9223372036854775808,\"a\":\"<sensitive data>\",\"d\":\"-9223372036854775808\",\"e\":{}}");
s = mfree(s);
v = json_variant_unref(v);
json_variant_dump(v, JSON_FORMAT_COLOR|JSON_FORMAT_PRETTY, NULL, NULL);
assert_se(json_variant_format(v, JSON_FORMAT_CENSOR_SENSITIVE, &s) >= 0);
- assert_se(streq_ptr(s, "{\"b\":[\"foo\",\"bar\",\"baz\",\"qux\"],\"c\":-9223372036854775808,\"d\":\"-9223372036854775808\",\"a\":\"<sensitive data>\",\"e\":{}}"));
+ ASSERT_STREQ(s, "{\"b\":[\"foo\",\"bar\",\"baz\",\"qux\"],\"c\":-9223372036854775808,\"d\":\"-9223372036854775808\",\"a\":\"<sensitive data>\",\"e\":{}}");
s = mfree(s);
v = json_variant_unref(v);
json_variant_dump(v, JSON_FORMAT_COLOR|JSON_FORMAT_PRETTY, NULL, NULL);
assert_se(json_variant_format(v, JSON_FORMAT_CENSOR_SENSITIVE, &s) >= 0);
- assert_se(streq_ptr(s, "{\"b\":[\"foo\",\"bar\",\"baz\",\"qux\"],\"c\":-9223372036854775808,\"d\":\"-9223372036854775808\",\"e\":{},\"a\":\"<sensitive data>\"}"));
+ ASSERT_STREQ(s, "{\"b\":[\"foo\",\"bar\",\"baz\",\"qux\"],\"c\":-9223372036854775808,\"d\":\"-9223372036854775808\",\"e\":{},\"a\":\"<sensitive data>\"}");
}
TEST(json_iovec) {
LIST_HEAD_INIT(head);
LIST_HEAD_INIT(head2);
- assert_se(head == NULL);
- assert_se(head2 == NULL);
+ ASSERT_NULL(head);
+ ASSERT_NULL(head2);
for (i = 0; i < ELEMENTSOF(items); i++) {
LIST_INIT(item_list, &items[i]);
assert_se(!LIST_JUST_US(item_list, head));
- assert_se(items[0].item_list_next == NULL);
+ ASSERT_NULL(items[0].item_list_next);
assert_se(items[1].item_list_next == &items[0]);
assert_se(items[2].item_list_next == &items[1]);
assert_se(items[3].item_list_next == &items[2]);
assert_se(items[0].item_list_prev == &items[1]);
assert_se(items[1].item_list_prev == &items[2]);
assert_se(items[2].item_list_prev == &items[3]);
- assert_se(items[3].item_list_prev == NULL);
+ ASSERT_NULL(items[3].item_list_prev);
list_item *cursor = LIST_FIND_HEAD(item_list, &items[0]);
assert_se(cursor == &items[3]);
assert_se(LIST_REMOVE(item_list, head, &items[1]) == &items[1]);
assert_se(LIST_JUST_US(item_list, &items[1]));
- assert_se(items[0].item_list_next == NULL);
+ ASSERT_NULL(items[0].item_list_next);
assert_se(items[2].item_list_next == &items[0]);
assert_se(items[3].item_list_next == &items[2]);
assert_se(items[0].item_list_prev == &items[2]);
assert_se(items[2].item_list_prev == &items[3]);
- assert_se(items[3].item_list_prev == NULL);
+ ASSERT_NULL(items[3].item_list_prev);
assert_se(LIST_INSERT_AFTER(item_list, head, &items[3], &items[1]) == &items[1]);
- assert_se(items[0].item_list_next == NULL);
+ ASSERT_NULL(items[0].item_list_next);
assert_se(items[2].item_list_next == &items[0]);
assert_se(items[1].item_list_next == &items[2]);
assert_se(items[3].item_list_next == &items[1]);
assert_se(items[0].item_list_prev == &items[2]);
assert_se(items[2].item_list_prev == &items[1]);
assert_se(items[1].item_list_prev == &items[3]);
- assert_se(items[3].item_list_prev == NULL);
+ ASSERT_NULL(items[3].item_list_prev);
assert_se(LIST_REMOVE(item_list, head, &items[1]) == &items[1]);
assert_se(LIST_JUST_US(item_list, &items[1]));
- assert_se(items[0].item_list_next == NULL);
+ ASSERT_NULL(items[0].item_list_next);
assert_se(items[2].item_list_next == &items[0]);
assert_se(items[3].item_list_next == &items[2]);
assert_se(items[0].item_list_prev == &items[2]);
assert_se(items[2].item_list_prev == &items[3]);
- assert_se(items[3].item_list_prev == NULL);
+ ASSERT_NULL(items[3].item_list_prev);
assert_se(LIST_INSERT_BEFORE(item_list, head, &items[2], &items[1]) == &items[1]);
- assert_se(items[0].item_list_next == NULL);
+ ASSERT_NULL(items[0].item_list_next);
assert_se(items[2].item_list_next == &items[0]);
assert_se(items[1].item_list_next == &items[2]);
assert_se(items[3].item_list_next == &items[1]);
assert_se(items[0].item_list_prev == &items[2]);
assert_se(items[2].item_list_prev == &items[1]);
assert_se(items[1].item_list_prev == &items[3]);
- assert_se(items[3].item_list_prev == NULL);
+ ASSERT_NULL(items[3].item_list_prev);
assert_se(LIST_REMOVE(item_list, head, &items[0]) == &items[0]);
assert_se(LIST_JUST_US(item_list, &items[0]));
- assert_se(items[2].item_list_next == NULL);
+ ASSERT_NULL(items[2].item_list_next);
assert_se(items[1].item_list_next == &items[2]);
assert_se(items[3].item_list_next == &items[1]);
assert_se(items[2].item_list_prev == &items[1]);
assert_se(items[1].item_list_prev == &items[3]);
- assert_se(items[3].item_list_prev == NULL);
+ ASSERT_NULL(items[3].item_list_prev);
assert_se(LIST_INSERT_BEFORE(item_list, head, &items[3], &items[0]) == &items[0]);
- assert_se(items[2].item_list_next == NULL);
+ ASSERT_NULL(items[2].item_list_next);
assert_se(items[1].item_list_next == &items[2]);
assert_se(items[3].item_list_next == &items[1]);
assert_se(items[0].item_list_next == &items[3]);
assert_se(items[2].item_list_prev == &items[1]);
assert_se(items[1].item_list_prev == &items[3]);
assert_se(items[3].item_list_prev == &items[0]);
- assert_se(items[0].item_list_prev == NULL);
+ ASSERT_NULL(items[0].item_list_prev);
assert_se(head == &items[0]);
assert_se(LIST_REMOVE(item_list, head, &items[0]) == &items[0]);
assert_se(LIST_JUST_US(item_list, &items[0]));
- assert_se(items[2].item_list_next == NULL);
+ ASSERT_NULL(items[2].item_list_next);
assert_se(items[1].item_list_next == &items[2]);
assert_se(items[3].item_list_next == &items[1]);
assert_se(items[2].item_list_prev == &items[1]);
assert_se(items[1].item_list_prev == &items[3]);
- assert_se(items[3].item_list_prev == NULL);
+ ASSERT_NULL(items[3].item_list_prev);
assert_se(LIST_INSERT_BEFORE(item_list, head, NULL, &items[0]) == &items[0]);
- assert_se(items[0].item_list_next == NULL);
+ ASSERT_NULL(items[0].item_list_next);
assert_se(items[2].item_list_next == &items[0]);
assert_se(items[1].item_list_next == &items[2]);
assert_se(items[3].item_list_next == &items[1]);
assert_se(items[0].item_list_prev == &items[2]);
assert_se(items[2].item_list_prev == &items[1]);
assert_se(items[1].item_list_prev == &items[3]);
- assert_se(items[3].item_list_prev == NULL);
+ ASSERT_NULL(items[3].item_list_prev);
assert_se(LIST_REMOVE(item_list, head, &items[0]) == &items[0]);
assert_se(LIST_JUST_US(item_list, &items[0]));
- assert_se(items[2].item_list_next == NULL);
+ ASSERT_NULL(items[2].item_list_next);
assert_se(items[1].item_list_next == &items[2]);
assert_se(items[3].item_list_next == &items[1]);
assert_se(items[2].item_list_prev == &items[1]);
assert_se(items[1].item_list_prev == &items[3]);
- assert_se(items[3].item_list_prev == NULL);
+ ASSERT_NULL(items[3].item_list_prev);
assert_se(LIST_REMOVE(item_list, head, &items[1]) == &items[1]);
assert_se(LIST_JUST_US(item_list, &items[1]));
- assert_se(items[2].item_list_next == NULL);
+ ASSERT_NULL(items[2].item_list_next);
assert_se(items[3].item_list_next == &items[2]);
assert_se(items[2].item_list_prev == &items[3]);
- assert_se(items[3].item_list_prev == NULL);
+ ASSERT_NULL(items[3].item_list_prev);
assert_se(LIST_REMOVE(item_list, head, &items[2]) == &items[2]);
assert_se(LIST_JUST_US(item_list, &items[2]));
assert_se(LIST_REMOVE(item_list, head, &items[3]) == &items[3]);
assert_se(LIST_JUST_US(item_list, &items[3]));
- assert_se(head == NULL);
+ ASSERT_NULL(head);
for (i = 0; i < ELEMENTSOF(items); i++) {
assert_se(LIST_JUST_US(item_list, &items[i]));
assert_se(items[0].item_list_next == &items[1]);
assert_se(items[1].item_list_next == &items[2]);
assert_se(items[2].item_list_next == &items[3]);
- assert_se(items[3].item_list_next == NULL);
+ ASSERT_NULL(items[3].item_list_next);
- assert_se(items[0].item_list_prev == NULL);
+ ASSERT_NULL(items[0].item_list_prev);
assert_se(items[1].item_list_prev == &items[0]);
assert_se(items[2].item_list_prev == &items[1]);
assert_se(items[3].item_list_prev == &items[2]);
for (i = 0; i < ELEMENTSOF(items); i++)
assert_se(LIST_REMOVE(item_list, head, &items[i]) == &items[i]);
- assert_se(head == NULL);
+ ASSERT_NULL(head);
for (i = 0; i < ELEMENTSOF(items) / 2; i++) {
LIST_INIT(item_list, &items[i]);
assert_se(LIST_PREPEND(item_list, head2, &items[i]) == &items[i]);
}
- assert_se(items[0].item_list_next == NULL);
+ ASSERT_NULL(items[0].item_list_next);
assert_se(items[1].item_list_next == &items[0]);
- assert_se(items[2].item_list_next == NULL);
+ ASSERT_NULL(items[2].item_list_next);
assert_se(items[3].item_list_next == &items[2]);
assert_se(items[0].item_list_prev == &items[1]);
- assert_se(items[1].item_list_prev == NULL);
+ ASSERT_NULL(items[1].item_list_prev);
assert_se(items[2].item_list_prev == &items[3]);
- assert_se(items[3].item_list_prev == NULL);
+ ASSERT_NULL(items[3].item_list_prev);
assert_se(LIST_JOIN(item_list, head2, head) == head2);
- assert_se(head == NULL);
+ ASSERT_NULL(head);
- assert_se(items[0].item_list_next == NULL);
+ ASSERT_NULL(items[0].item_list_next);
assert_se(items[1].item_list_next == &items[0]);
assert_se(items[2].item_list_next == &items[1]);
assert_se(items[3].item_list_next == &items[2]);
assert_se(items[0].item_list_prev == &items[1]);
assert_se(items[1].item_list_prev == &items[2]);
assert_se(items[2].item_list_prev == &items[3]);
- assert_se(items[3].item_list_prev == NULL);
+ ASSERT_NULL(items[3].item_list_prev);
assert_se(LIST_JOIN(item_list, head, head2) == head);
- assert_se(head2 == NULL);
+ ASSERT_NULL(head2);
assert_se(head);
for (i = 0; i < ELEMENTSOF(items); i++)
assert_se(LIST_REMOVE(item_list, head, &items[i]) == &items[i]);
- assert_se(head == NULL);
+ ASSERT_NULL(head);
assert_se(LIST_PREPEND(item_list, head, items + 0) == items + 0);
assert_se(LIST_PREPEND(item_list, head, items + 1) == items + 1);
assert_se(LIST_POP(item_list, head) == items + 2);
assert_se(LIST_POP(item_list, head) == items + 1);
assert_se(LIST_POP(item_list, head) == items + 0);
- assert_se(LIST_POP(item_list, head) == NULL);
+ ASSERT_NULL(LIST_POP(item_list, head));
/* No-op on an empty list */
LIST_CLEAR(item_list, head, free);
- assert_se(head == NULL);
+ ASSERT_NULL(head);
/* A list can be cleared partially */
LIST_CLEAR(item_list, head->item_list_next, free);
assert_se(head == items + 0);
- assert_se(head->item_list_next == NULL);
+ ASSERT_NULL(head->item_list_next);
return 0;
}
n = strv_length(c->argv);
log_info("actual: \"%s\" [\"%s\" \"%s\" \"%s\"]",
c->path, c->argv[0], n > 0 ? c->argv[1] : "(null)", n > 1 ? c->argv[2] : "(null)");
- assert_se(streq(c->path, path));
- assert_se(streq(c->argv[0], argv0 ?: path));
+ ASSERT_STREQ(c->path, path);
+ ASSERT_STREQ(c->argv[0], argv0 ?: path);
if (n > 0)
- assert_se(streq_ptr(c->argv[1], argv1));
+ ASSERT_STREQ(c->argv[1], argv1);
if (n > 1)
- assert_se(streq_ptr(c->argv[2], argv2));
+ ASSERT_STREQ(c->argv[2], argv2);
assert_se(!!(c->flags & EXEC_COMMAND_IGNORE_FAILURE) == ignore);
}
"LValue", 0, "/RValue/ argv0 r1",
&c, u);
assert_se(r == -ENOEXEC);
- assert_se(c1->command_next == NULL);
+ ASSERT_NULL(c1->command_next);
log_info("/* honour_argv0 */");
r = config_parse_exec(NULL, "fake", 3, "section", 1,
"LValue", 0, "@/RValue",
&c, u);
assert_se(r == -ENOEXEC);
- assert_se(c1->command_next == NULL);
+ ASSERT_NULL(c1->command_next);
log_info("/* no command, whitespace only, reset */");
r = config_parse_exec(NULL, "fake", 3, "section", 1,
"LValue", 0, "",
&c, u);
assert_se(r == 0);
- assert_se(c == NULL);
+ ASSERT_NULL(c);
log_info("/* ignore && honour_argv0 */");
r = config_parse_exec(NULL, "fake", 4, "section", 1,
"LValue", 0, "--/RValue argv0 r1",
&c, u);
assert_se(r == 0);
- assert_se(c1->command_next == NULL);
+ ASSERT_NULL(c1->command_next);
log_info("/* ignore && ignore (2) */");
r = config_parse_exec(NULL, "fake", 4, "section", 1,
"LValue", 0, "-@-/RValue argv0 r1",
&c, u);
assert_se(r == 0);
- assert_se(c1->command_next == NULL);
+ ASSERT_NULL(c1->command_next);
log_info("/* semicolon */");
r = config_parse_exec(NULL, "fake", 5, "section", 1,
c1 = c1->command_next;
check_execcommand(c1, "/RValue", "argv0", "r1", NULL, true);
- assert_se(c1->command_next == NULL);
+ ASSERT_NULL(c1->command_next);
log_info("/* trailing semicolon, no whitespace */");
r = config_parse_exec(NULL, "fake", 5, "section", 1,
c1 = c1->command_next;
check_execcommand(c1, "/RValue", "argv0", "r1", NULL, true);
- assert_se(c1->command_next == NULL);
+ ASSERT_NULL(c1->command_next);
log_info("/* trailing semicolon in single quotes */");
r = config_parse_exec(NULL, "fake", 5, "section", 1,
"LValue", 0, path,
&c, u);
assert_se(r == -ENOEXEC);
- assert_se(c1->command_next == NULL);
+ ASSERT_NULL(c1->command_next);
}
log_info("/* valid character: \\s */");
"LValue", 0, "/path\\",
&c, u);
assert_se(r == -ENOEXEC);
- assert_se(c1->command_next == NULL);
+ ASSERT_NULL(c1->command_next);
log_info("/* missing ending ' */");
r = config_parse_exec(NULL, "fake", 4, "section", 1,
"LValue", 0, "/path 'foo",
&c, u);
assert_se(r == -ENOEXEC);
- assert_se(c1->command_next == NULL);
+ ASSERT_NULL(c1->command_next);
log_info("/* missing ending ' with trailing backslash */");
r = config_parse_exec(NULL, "fake", 4, "section", 1,
"LValue", 0, "/path 'foo\\",
&c, u);
assert_se(r == -ENOEXEC);
- assert_se(c1->command_next == NULL);
+ ASSERT_NULL(c1->command_next);
log_info("/* invalid space between modifiers */");
r = config_parse_exec(NULL, "fake", 4, "section", 1,
"LValue", 0, "- /path",
&c, u);
assert_se(r == 0);
- assert_se(c1->command_next == NULL);
+ ASSERT_NULL(c1->command_next);
log_info("/* only modifiers, no path */");
r = config_parse_exec(NULL, "fake", 4, "section", 1,
"LValue", 0, "-",
&c, u);
assert_se(r == 0);
- assert_se(c1->command_next == NULL);
+ ASSERT_NULL(c1->command_next);
log_info("/* long arg */"); /* See issue #22957. */
"LValue", 0, "",
&c, u);
assert_se(r == 0);
- assert_se(c == NULL);
+ ASSERT_NULL(c);
exec_command_free_list(c);
}
memzero(i.path, strlen(i.path)); \
if (result) { \
printf("%s\n", t); \
- assert_se(streq(t, result)); \
+ ASSERT_STREQ(t, result); \
} else \
assert_se(!t); \
strcpy(i.name, d1); \
&passenv, NULL);
assert_se(r >= 0);
assert_se(strv_length(passenv) == 2);
- assert_se(streq(passenv[0], "A"));
- assert_se(streq(passenv[1], "B"));
+ ASSERT_STREQ(passenv[0], "A");
+ ASSERT_STREQ(passenv[1], "B");
r = config_parse_pass_environ(NULL, "fake", 1, "section", 1,
"PassEnvironment", 0, "",
&passenv, NULL);
assert_se(r >= 0);
assert_se(strv_length(passenv) == 1);
- assert_se(streq(passenv[0], "normal_name"));
+ ASSERT_STREQ(passenv[0], "normal_name");
}
TEST(config_parse_unit_env_file) {
&files, u);
assert_se(r == 0);
assert_se(strv_length(files) == 2);
- assert_se(streq(files[0], "/absolute1"));
- assert_se(streq(files[1], "/absolute2"));
+ ASSERT_STREQ(files[0], "/absolute1");
+ ASSERT_STREQ(files[1], "/absolute2");
r = config_parse_unit_env_file(u->id, "fake", 1, "section", 1,
"EnvironmentFile", 0, "",
&files, u);
assert_se(r == 0);
assert_se(strv_length(files) == 1);
- assert_se(streq(files[0], "/path/foobar.service.conf"));
+ ASSERT_STREQ(files[0], "/path/foobar.service.conf");
}
TEST(unit_dump_config_items) {
&of, u);
assert_se(r >= 0);
assert_se(of);
- assert_se(streq(of->path, "/proc/1/ns/mnt"));
- assert_se(streq(of->fdname, "host-mount-namespace"));
+ ASSERT_STREQ(of->path, "/proc/1/ns/mnt");
+ ASSERT_STREQ(of->fdname, "host-mount-namespace");
assert_se(of->flags == OPENFILE_READ_ONLY);
of = open_file_free(of);
&of, u);
assert_se(r >= 0);
assert_se(of);
- assert_se(streq(of->path, "/proc/1/ns/mnt"));
- assert_se(streq(of->fdname, "mnt"));
+ ASSERT_STREQ(of->path, "/proc/1/ns/mnt");
+ ASSERT_STREQ(of->fdname, "mnt");
assert_se(of->flags == OPENFILE_READ_ONLY);
r = config_parse_open_file(NULL, "fake", 1, "section", 1,
/* SPDX-License-Identifier: LGPL-2.1-or-later */
+/* Make sure the net/if.h header is included before any linux/ one */
+#include <net/if.h>
#include <linux/if.h>
#include <stdio.h>
static void verify_dissected_image_harder(DissectedImage *dissected) {
verify_dissected_image(dissected);
- assert_se(streq(dissected->partitions[PARTITION_ESP].fstype, "vfat"));
- assert_se(streq(dissected->partitions[PARTITION_XBOOTLDR].fstype, "vfat"));
- assert_se(streq(dissected->partitions[PARTITION_ROOT].fstype, "ext4"));
- assert_se(streq(dissected->partitions[PARTITION_HOME].fstype, "ext4"));
+ ASSERT_STREQ(dissected->partitions[PARTITION_ESP].fstype, "vfat");
+ ASSERT_STREQ(dissected->partitions[PARTITION_XBOOTLDR].fstype, "vfat");
+ ASSERT_STREQ(dissected->partitions[PARTITION_ROOT].fstype, "ext4");
+ ASSERT_STREQ(dissected->partitions[PARTITION_HOME].fstype, "ext4");
}
static void* thread_func(void *ptr) {
log_notice("All threads started now.");
if (arg_n_threads == 1)
- assert_se(thread_func(FD_TO_PTR(fd)) == NULL);
+ ASSERT_NULL(thread_func(FD_TO_PTR(fd)));
else
for (unsigned i = 0; i < arg_n_threads; i++) {
log_notice("Joining thread #%u.", i);
FOREACH_ARGUMENT(p, p_1, NULL, p_2, p_3, NULL, p_4, NULL) {
switch (i++) {
case 0: assert_se(p == p_1); break;
- case 1: assert_se(p == NULL); break;
+ case 1: ASSERT_NULL(p); break;
case 2: assert_se(p == p_2); break;
case 3: assert_se(p == p_3); break;
- case 4: assert_se(p == NULL); break;
+ case 4: ASSERT_NULL(p); break;
case 5: assert_se(p == p_4); break;
- case 6: assert_se(p == NULL); break;
+ case 6: ASSERT_NULL(p); break;
default: assert_se(false);
}
}
FOREACH_ARGUMENT(v, v_1, NULL, u32p, v_3, p_2, p_4, v_2, NULL) {
switch (i++) {
case 0: assert_se(v == v_1); break;
- case 1: assert_se(v == NULL); break;
+ case 1: ASSERT_NULL(v); break;
case 2: assert_se(v == u32p); break;
case 3: assert_se(v == v_3); break;
case 4: assert_se(v == p_2); break;
case 5: assert_se(v == p_4); break;
case 6: assert_se(v == v_2); break;
- case 7: assert_se(v == NULL); break;
+ case 7: ASSERT_NULL(v); break;
default: assert_se(false);
}
}
assert_se(i == 8);
i = 0;
FOREACH_ARGUMENT(v, NULL) {
- assert_se(v == NULL);
+ ASSERT_NULL(v);
assert_se(i++ == 0);
}
assert_se(i == 1);
assert_se(u64_multiply_safe(UINT64_MAX, UINT64_MAX) == 0);
}
+TEST(ASSERT) {
+ char *null = NULL;
+
+ ASSERT_OK(0);
+ ASSERT_OK(255);
+ ASSERT_OK(printf("Hello world\n"));
+ ASSERT_SIGNAL(ASSERT_OK(-1), SIGABRT);
+ ASSERT_SIGNAL(ASSERT_OK(-ENOANO), SIGABRT);
+
+ ASSERT_OK_ERRNO(0 >= 0);
+ ASSERT_OK_ERRNO(255 >= 0);
+ ASSERT_OK_ERRNO(printf("Hello world\n"));
+ ASSERT_SIGNAL(ASSERT_OK_ERRNO(-1), SIGABRT);
+ ASSERT_SIGNAL(ASSERT_OK_ERRNO(-ENOANO), SIGABRT);
+
+ ASSERT_TRUE(true);
+ ASSERT_TRUE(255);
+ ASSERT_TRUE(getpid());
+ ASSERT_SIGNAL(ASSERT_TRUE(1 == 0), SIGABRT);
+
+ ASSERT_FALSE(false);
+ ASSERT_FALSE(1 == 0);
+ ASSERT_SIGNAL(ASSERT_FALSE(1 > 0), SIGABRT);
+
+ ASSERT_NULL(NULL);
+ ASSERT_SIGNAL(ASSERT_NULL(signal_to_string(SIGINT)), SIGABRT);
+
+ ASSERT_NOT_NULL(signal_to_string(SIGTERM));
+ ASSERT_SIGNAL(ASSERT_NOT_NULL(NULL), SIGABRT);
+
+ ASSERT_STREQ(NULL, null);
+ ASSERT_STREQ("foo", "foo");
+ ASSERT_SIGNAL(ASSERT_STREQ(null, "bar"), SIGABRT);
+ ASSERT_SIGNAL(ASSERT_STREQ("foo", "bar"), SIGABRT);
+
+ ASSERT_EQ(0, 0);
+ ASSERT_EQ(-1, -1);
+ ASSERT_SIGNAL(ASSERT_EQ(255, -1), SIGABRT);
+
+ ASSERT_GE(0, 0);
+ ASSERT_GE(1, -1);
+ ASSERT_SIGNAL(ASSERT_GE(-1, 1), SIGABRT);
+
+ ASSERT_LE(0, 0);
+ ASSERT_LE(-1, 1);
+ ASSERT_SIGNAL(ASSERT_LE(1, -1), SIGABRT);
+
+ ASSERT_NE(0, (int64_t) UINT_MAX);
+ ASSERT_NE(-1, 1);
+ ASSERT_SIGNAL(ASSERT_NE(0, 0), SIGABRT);
+ ASSERT_SIGNAL(ASSERT_NE(-1, -1), SIGABRT);
+
+ ASSERT_GT(1, 0);
+ ASSERT_GT(1, -1);
+ ASSERT_SIGNAL(ASSERT_GT(0, 0), SIGABRT);
+ ASSERT_SIGNAL(ASSERT_GT(-1, 1), SIGABRT);
+
+ ASSERT_LT(0, 1);
+ ASSERT_LT(-1, 1);
+ ASSERT_SIGNAL(ASSERT_LT(0, 0), SIGABRT);
+ ASSERT_SIGNAL(ASSERT_LT(1, -1), SIGABRT);
+}
+
DEFINE_TEST_MAIN(LOG_INFO);
assert_se(cfd >= 0);
char buf[STRLEN("hello")+1] = {};
assert_se(read(cfd, buf, sizeof(buf)-1) == sizeof(buf)-1);
- assert_se(streq(buf, "hello"));
+ ASSERT_STREQ(buf, "hello");
assert_se(write(cfd, &(const char) { 'z' }, 1) == 1);
return 0;
assert_se(memstream_init(&m));
assert_se(memstream_finalize(&m, &buf, &sz) >= 0);
- assert_se(streq(buf, ""));
+ ASSERT_STREQ(buf, "");
assert_se(sz == 0);
}
fputs("おはよう!", f);
fputs(u8"😀😀😀", f);
assert_se(memstream_finalize(&m, &buf, &sz) >= 0);
- assert_se(streq(buf, u8"hogeおはよう!😀😀😀"));
+ ASSERT_STREQ(buf, u8"hogeおはよう!😀😀😀");
assert_se(sz == strlen(u8"hogeおはよう!😀😀😀"));
buf = mfree(buf);
assert_se(f = memstream_init(&m));
fputs("second", f);
assert_se(memstream_finalize(&m, &buf, &sz) >= 0);
- assert_se(streq(buf, "second"));
+ ASSERT_STREQ(buf, "second");
assert_se(sz == strlen("second"));
}
assert_se(mount_option_mangle(NULL, MS_RDONLY|MS_NOSUID, &f, &opts) == 0);
assert_se(f == (MS_RDONLY|MS_NOSUID));
- assert_se(opts == NULL);
+ ASSERT_NULL(opts);
assert_se(mount_option_mangle("", MS_RDONLY|MS_NOSUID, &f, &opts) == 0);
assert_se(f == (MS_RDONLY|MS_NOSUID));
- assert_se(opts == NULL);
+ ASSERT_NULL(opts);
assert_se(mount_option_mangle("ro,nosuid,nodev,noexec", 0, &f, &opts) == 0);
assert_se(f == (MS_RDONLY|MS_NOSUID|MS_NODEV|MS_NOEXEC));
- assert_se(opts == NULL);
+ ASSERT_NULL(opts);
assert_se(mount_option_mangle("ro,nosuid,nodev,noexec,mode=0755", 0, &f, &opts) == 0);
assert_se(f == (MS_RDONLY|MS_NOSUID|MS_NODEV|MS_NOEXEC));
- assert_se(streq(opts, "mode=0755"));
+ ASSERT_STREQ(opts, "mode=0755");
opts = mfree(opts);
assert_se(mount_option_mangle("rw,nosuid,foo,hogehoge,nodev,mode=0755", 0, &f, &opts) == 0);
assert_se(f == (MS_NOSUID|MS_NODEV));
- assert_se(streq(opts, "foo,hogehoge,mode=0755"));
+ ASSERT_STREQ(opts, "foo,hogehoge,mode=0755");
opts = mfree(opts);
assert_se(mount_option_mangle("rw,nosuid,nodev,noexec,relatime,net_cls,net_prio", MS_RDONLY, &f, &opts) == 0);
assert_se(f == (MS_NOSUID|MS_NODEV|MS_NOEXEC|MS_RELATIME));
- assert_se(streq(opts, "net_cls,net_prio"));
+ ASSERT_STREQ(opts, "net_cls,net_prio");
opts = mfree(opts);
assert_se(mount_option_mangle("rw,nosuid,nodev,relatime,size=1630748k,mode=0700,uid=1000,gid=1000", MS_RDONLY, &f, &opts) == 0);
assert_se(f == (MS_NOSUID|MS_NODEV|MS_RELATIME));
- assert_se(streq(opts, "size=1630748k,mode=0700,uid=1000,gid=1000"));
+ ASSERT_STREQ(opts, "size=1630748k,mode=0700,uid=1000,gid=1000");
opts = mfree(opts);
assert_se(mount_option_mangle("size=1630748k,rw,gid=1000,,,nodev,relatime,,mode=0700,nosuid,uid=1000", MS_RDONLY, &f, &opts) == 0);
assert_se(f == (MS_NOSUID|MS_NODEV|MS_RELATIME));
- assert_se(streq(opts, "size=1630748k,gid=1000,mode=0700,uid=1000"));
+ ASSERT_STREQ(opts, "size=1630748k,gid=1000,mode=0700,uid=1000");
opts = mfree(opts);
assert_se(mount_option_mangle("rw,exec,size=8143984k,nr_inodes=2035996,mode=0755", MS_RDONLY|MS_NOSUID|MS_NOEXEC|MS_NODEV, &f, &opts) == 0);
assert_se(f == (MS_NOSUID|MS_NODEV));
- assert_se(streq(opts, "size=8143984k,nr_inodes=2035996,mode=0755"));
+ ASSERT_STREQ(opts, "size=8143984k,nr_inodes=2035996,mode=0755");
opts = mfree(opts);
assert_se(mount_option_mangle("rw,relatime,fmask=0022,,,dmask=0022", MS_RDONLY, &f, &opts) == 0);
assert_se(f == MS_RELATIME);
- assert_se(streq(opts, "fmask=0022,dmask=0022"));
+ ASSERT_STREQ(opts, "fmask=0022,dmask=0022");
opts = mfree(opts);
assert_se(mount_option_mangle("rw,relatime,fmask=0022,dmask=0022,\"hogehoge", MS_RDONLY, &f, &opts) < 0);
assert_se(mount_option_mangle("mode=01777,size=10%,nr_inodes=400k,uid=496107520,gid=496107520,context=\"system_u:object_r:svirt_sandbox_file_t:s0:c0,c1\"", 0, &f, &opts) == 0);
assert_se(f == 0);
- assert_se(streq(opts, "mode=01777,size=10%,nr_inodes=400k,uid=496107520,gid=496107520,context=\"system_u:object_r:svirt_sandbox_file_t:s0:c0,c1\""));
+ ASSERT_STREQ(opts, "mode=01777,size=10%,nr_inodes=400k,uid=496107520,gid=496107520,context=\"system_u:object_r:svirt_sandbox_file_t:s0:c0,c1\"");
opts = mfree(opts);
}
r = mount_flags_to_string(flags, &x);
log_info("flags: %#lX → %d/\"%s\"", flags, r, strnull(x));
assert_se(r >= 0);
- assert_se(streq(x, expected));
+ ASSERT_STREQ(x, expected);
}
TEST(mount_flags_to_string) {
if (isempty(name))
assert_se(isempty(c));
else
- assert_se(streq(c, name));
+ ASSERT_STREQ(c, name);
}
}
log_info("latest → %s", n->name);
assert_se(n = naming_scheme_from_name("v238"));
- assert_se(streq(n->name, "v238"));
+ ASSERT_STREQ(n->name, "v238");
}
DEFINE_TEST_MAIN(LOG_INFO);
/* SPDX-License-Identifier: LGPL-2.1-or-later */
+/* Make sure the net/if.h header is included before any linux/ one */
+#include <net/if.h>
#include <arpa/inet.h>
#include <linux/if_tunnel.h>
#include <linux/ip.h>
#include "tests.h"
static int load_module(const char *mod_name) {
- _cleanup_(kmod_unrefp) struct kmod_ctx *ctx = NULL;
- _cleanup_(kmod_module_unref_listp) struct kmod_list *list = NULL;
+ _cleanup_(sym_kmod_unrefp) struct kmod_ctx *ctx = NULL;
+ _cleanup_(sym_kmod_module_unref_listp) struct kmod_list *list = NULL;
struct kmod_list *l;
int r;
- ctx = kmod_new(NULL, NULL);
+ r = dlopen_libkmod();
+ if (r < 0)
+ return log_error_errno(r, "Failed to load libkmod: %m");
+
+ ctx = sym_kmod_new(NULL, NULL);
if (!ctx)
return log_oom();
- r = kmod_module_new_from_lookup(ctx, mod_name, &list);
+ r = sym_kmod_module_new_from_lookup(ctx, mod_name, &list);
if (r < 0)
return r;
- kmod_list_foreach(l, list) {
- _cleanup_(kmod_module_unrefp) struct kmod_module *mod = NULL;
+ sym_kmod_list_foreach(l, list) {
+ _cleanup_(sym_kmod_module_unrefp) struct kmod_module *mod = NULL;
- mod = kmod_module_get_module(l);
+ mod = sym_kmod_module_get_module(l);
- r = kmod_module_probe_insert_module(mod, 0, NULL, NULL, NULL, NULL);
+ r = sym_kmod_module_probe_insert_module(mod, 0, NULL, NULL, NULL, NULL);
if (r > 0)
r = -EINVAL;
}
assert_se(sd_netlink_call(rtnl, m, -1, 0) == 1);
- assert_se((m = sd_netlink_message_unref(m)) == NULL);
+ ASSERT_NULL((m = sd_netlink_message_unref(m)));
/* sit */
assert_se(sd_rtnl_message_new_link(rtnl, &n, RTM_NEWLINK, 0) >= 0);
assert_se(sd_netlink_call(rtnl, n, -1, 0) == 1);
- assert_se((n = sd_netlink_message_unref(n)) == NULL);
+ ASSERT_NULL((n = sd_netlink_message_unref(n)));
return EXIT_SUCCESS;
}
r = test_tunnel_configure(rtnl);
- assert_se((rtnl = sd_netlink_unref(rtnl)) == NULL);
+ ASSERT_NULL((rtnl = sd_netlink_unref(rtnl)));
return r;
}
l = strv_split_nulstr(nulstr);
assert_se(l);
- assert_se(streq(l[0], "str0"));
- assert_se(streq(l[1], "str1"));
- assert_se(streq(l[2], "str2"));
- assert_se(streq(l[3], "str3"));
+ ASSERT_STREQ(l[0], "str0");
+ ASSERT_STREQ(l[1], "str1");
+ ASSERT_STREQ(l[2], "str2");
+ ASSERT_STREQ(l[3], "str3");
}
#define strv_parse_nulstr_full_one(s, n, e0, e1) \
assert_se(memcmp_nn(b, n, c, m) == 0);
NULSTR_FOREACH(s, b)
- assert_se(streq(s, l[i++]));
+ ASSERT_STREQ(s, l[i++]);
assert_se(i == strv_length(l));
}
r = open_file_parse("/proc/1/ns/mnt:host-mount-namespace:read-only", &of);
assert_se(r >= 0);
- assert_se(streq(of->path, "/proc/1/ns/mnt"));
- assert_se(streq(of->fdname, "host-mount-namespace"));
+ ASSERT_STREQ(of->path, "/proc/1/ns/mnt");
+ ASSERT_STREQ(of->fdname, "host-mount-namespace");
assert_se(of->flags == OPENFILE_READ_ONLY);
of = open_file_free(of);
r = open_file_parse("/proc/1/ns/mnt", &of);
assert_se(r >= 0);
- assert_se(streq(of->path, "/proc/1/ns/mnt"));
- assert_se(streq(of->fdname, "mnt"));
+ ASSERT_STREQ(of->path, "/proc/1/ns/mnt");
+ ASSERT_STREQ(of->fdname, "mnt");
assert_se(of->flags == 0);
of = open_file_free(of);
r = open_file_parse("/proc/1/ns/mnt:host-mount-namespace", &of);
assert_se(r >= 0);
- assert_se(streq(of->path, "/proc/1/ns/mnt"));
- assert_se(streq(of->fdname, "host-mount-namespace"));
+ ASSERT_STREQ(of->path, "/proc/1/ns/mnt");
+ ASSERT_STREQ(of->fdname, "host-mount-namespace");
assert_se(of->flags == 0);
of = open_file_free(of);
r = open_file_parse("/proc/1/ns/mnt::read-only", &of);
assert_se(r >= 0);
- assert_se(streq(of->path, "/proc/1/ns/mnt"));
- assert_se(streq(of->fdname, "mnt"));
+ ASSERT_STREQ(of->path, "/proc/1/ns/mnt");
+ ASSERT_STREQ(of->fdname, "mnt");
assert_se(of->flags == OPENFILE_READ_ONLY);
of = open_file_free(of);
r = open_file_parse("/proc/1/ns/mnt:host-mount-namespace:append", &of);
assert_se(r >= 0);
- assert_se(streq(of->path, "/proc/1/ns/mnt"));
- assert_se(streq(of->fdname, "host-mount-namespace"));
+ ASSERT_STREQ(of->path, "/proc/1/ns/mnt");
+ ASSERT_STREQ(of->fdname, "host-mount-namespace");
assert_se(of->flags == OPENFILE_APPEND);
of = open_file_free(of);
r = open_file_parse("/proc/1/ns/mnt:host-mount-namespace:truncate", &of);
assert_se(r >= 0);
- assert_se(streq(of->path, "/proc/1/ns/mnt"));
- assert_se(streq(of->fdname, "host-mount-namespace"));
+ ASSERT_STREQ(of->path, "/proc/1/ns/mnt");
+ ASSERT_STREQ(of->fdname, "host-mount-namespace");
assert_se(of->flags == OPENFILE_TRUNCATE);
of = open_file_free(of);
r = open_file_parse("/proc/1/ns/mnt:host-mount-namespace:graceful", &of);
assert_se(r >= 0);
- assert_se(streq(of->path, "/proc/1/ns/mnt"));
- assert_se(streq(of->fdname, "host-mount-namespace"));
+ ASSERT_STREQ(of->path, "/proc/1/ns/mnt");
+ ASSERT_STREQ(of->fdname, "host-mount-namespace");
assert_se(of->flags == OPENFILE_GRACEFUL);
of = open_file_free(of);
r = open_file_parse("/proc/1/ns/mnt:host-mount-namespace:read-only,graceful", &of);
assert_se(r >= 0);
- assert_se(streq(of->path, "/proc/1/ns/mnt"));
- assert_se(streq(of->fdname, "host-mount-namespace"));
+ ASSERT_STREQ(of->path, "/proc/1/ns/mnt");
+ ASSERT_STREQ(of->fdname, "host-mount-namespace");
assert_se(of->flags == (OPENFILE_READ_ONLY | OPENFILE_GRACEFUL));
of = open_file_free(of);
r = open_file_to_string(of, &s);
assert_se(r >= 0);
- assert_se(streq(s, "/proc/1/ns/mnt:host-mount-namespace:read-only"));
+ ASSERT_STREQ(s, "/proc/1/ns/mnt:host-mount-namespace:read-only");
s = mfree(s);
of->flags = OPENFILE_APPEND;
r = open_file_to_string(of, &s);
assert_se(r >= 0);
- assert_se(streq(s, "/proc/1/ns/mnt:host-mount-namespace:append"));
+ ASSERT_STREQ(s, "/proc/1/ns/mnt:host-mount-namespace:append");
s = mfree(s);
of->flags = OPENFILE_TRUNCATE;
r = open_file_to_string(of, &s);
assert_se(r >= 0);
- assert_se(streq(s, "/proc/1/ns/mnt:host-mount-namespace:truncate"));
+ ASSERT_STREQ(s, "/proc/1/ns/mnt:host-mount-namespace:truncate");
s = mfree(s);
of->flags = OPENFILE_GRACEFUL;
r = open_file_to_string(of, &s);
assert_se(r >= 0);
- assert_se(streq(s, "/proc/1/ns/mnt:host-mount-namespace:graceful"));
+ ASSERT_STREQ(s, "/proc/1/ns/mnt:host-mount-namespace:graceful");
s = mfree(s);
of->flags = OPENFILE_READ_ONLY | OPENFILE_GRACEFUL;
r = open_file_to_string(of, &s);
assert_se(r >= 0);
- assert_se(streq(s, "/proc/1/ns/mnt:host-mount-namespace:read-only,graceful"));
+ ASSERT_STREQ(s, "/proc/1/ns/mnt:host-mount-namespace:read-only,graceful");
s = mfree(s);
of->flags = 0;
r = open_file_to_string(of, &s);
assert_se(r >= 0);
- assert_se(streq(s, "/proc/1/ns/mnt:host-mount-namespace"));
+ ASSERT_STREQ(s, "/proc/1/ns/mnt:host-mount-namespace");
s = mfree(s);
assert_se(free_and_strdup(&of->fdname, "mnt"));
r = open_file_to_string(of, &s);
assert_se(r >= 0);
- assert_se(streq(s, "/proc/1/ns/mnt::read-only"));
+ ASSERT_STREQ(s, "/proc/1/ns/mnt::read-only");
s = mfree(s);
- assert_se(free_and_strdup(&of->path, "/path:with:colon"));
- assert_se(free_and_strdup(&of->fdname, "path:with:colon"));
+ ASSERT_OK(free_and_strdup(&of->path, "/path:with:colon"));
+ ASSERT_OK(free_and_strdup(&of->fdname, "path:with:colon"));
of->flags = 0;
- r = open_file_to_string(of, &s);
-
- assert_se(r >= 0);
- assert_se(streq(s, "/path\\:with\\:colon"));
+ ASSERT_OK(open_file_to_string(of, &s));
+ ASSERT_STREQ(s, "/path\\x3awith\\x3acolon");
}
DEFINE_TEST_MAIN(LOG_INFO);
DEFINE_HEX_PTR(key, "2d2d2d2d2d424547494e205055424c4943204b45592d2d2d2d2d0a4d466b7b");
assert_se(openssl_pkey_from_pem(key, key_len, &pkey) == -EIO);
- assert_se(pkey == NULL);
+ ASSERT_NULL(pkey);
}
static const struct {
* non-trivial hash ops. */
assert_se(t = ordered_set_get_strv(m));
- assert_se(streq(t[0], "1"));
- assert_se(streq(t[1], "22"));
- assert_se(streq(t[2], "333"));
+ ASSERT_STREQ(t[0], "1");
+ ASSERT_STREQ(t[1], "22");
+ ASSERT_STREQ(t[2], "333");
assert_se(!t[3]);
ordered_set_print(stdout, "FOO=", m);
}
TEST(parse_os_release) {
- /* Let's assume that we're running in a valid system, so os-release is available */
_cleanup_free_ char *id = NULL, *id2 = NULL, *name = NULL, *foobar = NULL;
- ASSERT_EQ(parse_os_release(NULL, "ID", &id), 0);
- log_info("ID: %s", id);
+
+ if (access("/etc/os-release", F_OK) >= 0 || access("/usr/lib/os-release", F_OK) >= 0) {
+ ASSERT_EQ(parse_os_release(NULL, "ID", &id), 0);
+ log_info("ID: %s", id);
+ }
ASSERT_EQ(setenv("SYSTEMD_OS_RELEASE", "/dev/null", 1), 0);
ASSERT_EQ(parse_os_release(NULL, "ID", &id2), 0);
ASSERT_EQ(setenv("SYSTEMD_OS_RELEASE", tmpfile, 1), 0);
ASSERT_EQ(parse_os_release(NULL, "ID", &id, "NAME", &name), 0);
log_info("ID: %s NAME: %s", id, name);
- assert_se(streq(id, "the-id"));
- assert_se(streq(name, "the-name"));
+ ASSERT_STREQ(id, "the-id");
+ ASSERT_STREQ(name, "the-name");
_cleanup_(unlink_tempfilep) char tmpfile2[] = "/tmp/test-os-util.XXXXXX";
ASSERT_EQ(write_tmpfile(tmpfile2,
ASSERT_EQ(setenv("SYSTEMD_OS_RELEASE", tmpfile2, 1), 0);
ASSERT_EQ(parse_os_release(NULL, "ID", &id, "NAME", &name), 0);
log_info("ID: %s NAME: %s", id, name);
- assert_se(streq(id, "the-id"));
- assert_se(streq(name, "the-name"));
+ ASSERT_STREQ(id, "the-id");
+ ASSERT_STREQ(name, "the-name");
ASSERT_EQ(parse_os_release(NULL, "FOOBAR", &foobar), 0);
log_info("FOOBAR: %s", strnull(foobar));
assert_se(parse_extension_release(tempdir, IMAGE_SYSEXT, "test", false, "ID", &id, "VERSION_ID", &version_id) == 0);
log_info("ID: %s VERSION_ID: %s", id, version_id);
- assert_se(streq(id, "the-id"));
- assert_se(streq(version_id, "the-version-id"));
+ ASSERT_STREQ(id, "the-id");
+ ASSERT_STREQ(version_id, "the-version-id");
assert_se(b = path_join(tempdir, "/etc/extension-release.d/extension-release.tester"));
assert_se(mkdir_parents(b, 0777) >= 0);
ASSERT_EQ(parse_extension_release(tempdir, IMAGE_CONFEXT, "tester", false, "ID", &id, "VERSION_ID", &version_id), 0);
log_info("ID: %s VERSION_ID: %s", id, version_id);
- assert_se(streq(id, "the-id"));
- assert_se(streq(version_id, "the-version-id"));
+ ASSERT_STREQ(id, "the-id");
+ ASSERT_STREQ(version_id, "the-version-id");
assert_se(parse_extension_release(tempdir, IMAGE_CONFEXT, "tester", false, "FOOBAR", &foobar) == 0);
log_info("FOOBAR: %s", strnull(foobar));
_cleanup_free_ char *path = NULL;
assert_se(parse_path_argument("help", false, &path) == 0);
- assert_se(streq(basename(path), "help"));
+ ASSERT_STREQ(basename(path), "help");
assert_se(parse_path_argument("/", false, &path) == 0);
- assert_se(streq(path, "/"));
+ ASSERT_STREQ(path, "/");
assert_se(parse_path_argument("/", true, &path) == 0);
- assert_se(path == NULL);
+ ASSERT_NULL(path);
}
TEST(parse_signal_argument) {
assert_se(setenv("SYSTEMD_UNIT_PATH", systemd_unit_path, 1) == 0);
assert_se(lookup_paths_init(&lp_with_env, scope, 0, NULL) == 0);
assert_se(strv_length(lp_with_env.search_path) == 1);
- assert_se(streq(lp_with_env.search_path[0], systemd_unit_path));
+ ASSERT_STREQ(lp_with_env.search_path[0], systemd_unit_path);
lookup_paths_log(&lp_with_env);
assert_se(strv_equal(lp_with_env.search_path, STRV_MAKE(systemd_unit_path)));
}
assert_se( path_is_absolute("/"));
assert_se(!path_is_absolute("./"));
- assert_se(streq(basename("./aa/bb/../file.da."), "file.da."));
- assert_se(streq(basename("/aa///.file"), ".file"));
- assert_se(streq(basename("/aa///file..."), "file..."));
- assert_se(streq(basename("file.../"), ""));
+ ASSERT_STREQ(basename("./aa/bb/../file.da."), "file.da.");
+ ASSERT_STREQ(basename("/aa///.file"), ".file");
+ ASSERT_STREQ(basename("/aa///file..."), "file...");
+ ASSERT_STREQ(basename("file.../"), "");
assert_se( PATH_IN_SET("/bin", "/", "/bin", "/foo"));
assert_se( PATH_IN_SET("/bin", "/bin"));
p = strdupa_safe(in);
path_simplify_full(p, flags);
log_debug("/* test_path_simplify(%s) → %s (expected: %s) */", in, p, out);
- assert_se(streq(p, out));
+ ASSERT_STREQ(p, out);
}
TEST(path_simplify) {
assert_se(find_executable_full("sh", NULL, NULL, true, &p, NULL) == 0);
puts(p);
- assert_se(streq(basename(p), "sh"));
+ ASSERT_STREQ(basename(p), "sh");
free(p);
assert_se(find_executable_full("sh", NULL, NULL, false, &p, NULL) == 0);
puts(p);
- assert_se(streq(basename(p), "sh"));
+ ASSERT_STREQ(basename(p), "sh");
free(p);
_cleanup_free_ char *oldpath = NULL;
assert_se(find_executable_full("sh", NULL, NULL, true, &p, NULL) == 0);
puts(p);
- assert_se(streq(basename(p), "sh"));
+ ASSERT_STREQ(basename(p), "sh");
free(p);
assert_se(find_executable_full("sh", NULL, NULL, false, &p, NULL) == 0);
puts(p);
- assert_se(streq(basename(p), "sh"));
+ ASSERT_STREQ(basename(p), "sh");
free(p);
if (oldpath)
assert_se(find_executable_full(test_file_name, NULL, STRV_MAKE("/doesnotexist", "/tmp", "/bin"), false, &p, NULL) == 0);
puts(p);
- assert_se(streq(p, fn));
+ ASSERT_STREQ(p, fn);
free(p);
(void) unlink(fn);
free(p);
assert_se(find_executable("/bin/touch", &p) == 0);
- assert_se(streq(p, "/bin/touch"));
+ ASSERT_STREQ(p, "/bin/touch");
free(p);
assert_se(find_executable("touch", &p) == 0);
assert_se(path_is_absolute(p));
- assert_se(streq(basename(p), "touch"));
+ ASSERT_STREQ(basename(p), "touch");
free(p);
assert_se(find_executable("xxxx-xxxx", &p) == -ENOENT);
assert_se(fd > STDERR_FILENO);
assert_se(path_is_absolute(t));
if (path_is_absolute(path))
- assert_se(streq(t, path));
+ ASSERT_STREQ(t, path);
pid = fork();
assert_se(pid >= 0);
i = 0;
PATH_FOREACH_PREFIX_MORE(s, "/a/b/c/d") {
log_error("---%s---", s);
- assert_se(streq(s, values[i++]));
+ ASSERT_STREQ(s, values[i++]);
}
ASSERT_NULL(values[i]);
i = 1;
PATH_FOREACH_PREFIX(s, "/a/b/c/d") {
log_error("---%s---", s);
- assert_se(streq(s, values[i++]));
+ ASSERT_STREQ(s, values[i++]);
}
ASSERT_NULL(values[i]);
i = 0;
PATH_FOREACH_PREFIX_MORE(s, "////a////b////c///d///////")
- assert_se(streq(s, values[i++]));
+ ASSERT_STREQ(s, values[i++]);
ASSERT_NULL(values[i]);
i = 1;
PATH_FOREACH_PREFIX(s, "////a////b////c///d///////")
- assert_se(streq(s, values[i++]));
+ ASSERT_STREQ(s, values[i++]);
ASSERT_NULL(values[i]);
PATH_FOREACH_PREFIX(s, "////")
b = false;
PATH_FOREACH_PREFIX_MORE(s, "////") {
assert_se(!b);
- assert_se(streq(s, ""));
+ ASSERT_STREQ(s, "");
b = true;
}
assert_se(b);
b = false;
PATH_FOREACH_PREFIX_MORE(s, "") {
assert_se(!b);
- assert_se(streq(s, ""));
+ ASSERT_STREQ(s, "");
b = true;
}
}
_cleanup_free_ char *z = NULL; \
z = path_join(__VA_ARGS__); \
log_debug("got \"%s\", expected \"%s\"", z, expected); \
- assert_se(streq(z, expected)); \
+ ASSERT_STREQ(z, expected); \
}
test_join("/root/a/b/c", "/root", "/a/b", "/c");
_cleanup_free_ char *p = NULL;
assert_se(path_extend(&p, "foo", "bar", "baz") == p);
- assert_se(streq(p, "foo/bar/baz"));
+ ASSERT_STREQ(p, "foo/bar/baz");
assert_se(path_extend(&p, "foo", "bar", "baz") == p);
- assert_se(streq(p, "foo/bar/baz/foo/bar/baz"));
+ ASSERT_STREQ(p, "foo/bar/baz/foo/bar/baz");
p = mfree(p);
assert_se(path_extend(&p, "foo") == p);
- assert_se(streq(p, "foo"));
+ ASSERT_STREQ(p, "foo");
assert_se(path_extend(&p, "/foo") == p);
- assert_se(streq(p, "foo/foo"));
+ ASSERT_STREQ(p, "foo/foo");
assert_se(path_extend(&p, "/waaaah/wahhh//") == p);
- assert_se(streq(p, "foo/foo/waaaah/wahhh//")); /* path_extend() does not drop redundant slashes */
+ ASSERT_STREQ(p, "foo/foo/waaaah/wahhh//"); /* path_extend() does not drop redundant slashes */
assert_se(path_extend(&p, "/aaa/bbb/") == p);
- assert_se(streq(p, "foo/foo/waaaah/wahhh///aaa/bbb/")); /* but not add an extra slash */
+ ASSERT_STREQ(p, "foo/foo/waaaah/wahhh///aaa/bbb/"); /* but not add an extra slash */
assert_se(free_and_strdup(&p, "/") >= 0);
assert_se(path_extend(&p, "foo") == p);
- assert_se(streq(p, "/foo"));
+ ASSERT_STREQ(p, "/foo");
}
TEST(fsck_exists) {
r = path_make_relative(from, to, &z);
assert_se((r >= 0) == !!expected);
- assert_se(streq_ptr(z, expected));
+ ASSERT_STREQ(z, expected);
}
TEST(path_make_relative) {
r = path_make_relative_parent(from, to, &z);
assert_se((r >= 0) == !!expected);
- assert_se(streq_ptr(z, expected));
+ ASSERT_STREQ(z, expected);
}
TEST(path_make_relative_parent) {
_cleanup_strv_free_ char **search_dirs = NULL;
_cleanup_strv_free_ char **absolute_dirs = NULL;
- assert_se(mkdtemp(tmp_dir) != NULL);
+ ASSERT_NOT_NULL(mkdtemp(tmp_dir));
search_dirs = strv_new("/dir1", "/dir2", "/dir3");
assert_se(search_dirs);
assert_se(symlink("dir2", absolute_dirs[2]) == 0);
path_strv_resolve(search_dirs, tmp_dir);
- assert_se(streq(search_dirs[0], "/dir1"));
- assert_se(streq(search_dirs[1], "/dir2"));
- assert_se(streq(search_dirs[2], "/dir2"));
+ ASSERT_STREQ(search_dirs[0], "/dir1");
+ ASSERT_STREQ(search_dirs[1], "/dir2");
+ ASSERT_STREQ(search_dirs[2], "/dir2");
assert_se(rm_rf(tmp_dir, REMOVE_ROOT|REMOVE_PHYSICAL) == 0);
}
log_debug("/* %s(%s, %s) */", __func__, path, prefix);
p = path_startswith(path, prefix);
- assert_se(streq_ptr(p, expected));
+ ASSERT_STREQ(p, expected);
if (p) {
q = strjoina(skipped, p);
- assert_se(streq(q, path));
+ ASSERT_STREQ(q, path);
assert_se(p == path + strlen(skipped));
}
}
assert_se(file_in_same_dir("/", "a", &t) == -EADDRNOTAVAIL);
assert_se(file_in_same_dir("/", "/a", &t) >= 0);
- assert_se(streq(t, "/a"));
+ ASSERT_STREQ(t, "/a");
free(t);
assert_se(file_in_same_dir("", "a", &t) == -EINVAL);
assert_se(file_in_same_dir("a/", "x", &t) >= 0);
- assert_se(streq(t, "x"));
+ ASSERT_STREQ(t, "x");
free(t);
assert_se(file_in_same_dir("bar/foo", "bar", &t) >= 0);
- assert_se(streq(t, "bar/bar"));
+ ASSERT_STREQ(t, "bar/bar");
free(t);
}
}
TEST(last_path_component) {
- assert_se(last_path_component(NULL) == NULL);
- assert_se(streq(last_path_component("a/b/c"), "c"));
- assert_se(streq(last_path_component("a/b/c/"), "c/"));
- assert_se(streq(last_path_component("/"), "/"));
- assert_se(streq(last_path_component("//"), "/"));
- assert_se(streq(last_path_component("///"), "/"));
- assert_se(streq(last_path_component("."), "."));
- assert_se(streq(last_path_component("./."), "."));
- assert_se(streq(last_path_component("././"), "./"));
- assert_se(streq(last_path_component("././/"), ".//"));
- assert_se(streq(last_path_component("/foo/a"), "a"));
- assert_se(streq(last_path_component("/foo/a/"), "a/"));
- assert_se(streq(last_path_component(""), ""));
- assert_se(streq(last_path_component("a"), "a"));
- assert_se(streq(last_path_component("a/"), "a/"));
- assert_se(streq(last_path_component("/a"), "a"));
- assert_se(streq(last_path_component("/a/"), "a/"));
+ ASSERT_NULL(last_path_component(NULL));
+ ASSERT_STREQ(last_path_component("a/b/c"), "c");
+ ASSERT_STREQ(last_path_component("a/b/c/"), "c/");
+ ASSERT_STREQ(last_path_component("/"), "/");
+ ASSERT_STREQ(last_path_component("//"), "/");
+ ASSERT_STREQ(last_path_component("///"), "/");
+ ASSERT_STREQ(last_path_component("."), ".");
+ ASSERT_STREQ(last_path_component("./."), ".");
+ ASSERT_STREQ(last_path_component("././"), "./");
+ ASSERT_STREQ(last_path_component("././/"), ".//");
+ ASSERT_STREQ(last_path_component("/foo/a"), "a");
+ ASSERT_STREQ(last_path_component("/foo/a/"), "a/");
+ ASSERT_STREQ(last_path_component(""), "");
+ ASSERT_STREQ(last_path_component("a"), "a");
+ ASSERT_STREQ(last_path_component("a/"), "a/");
+ ASSERT_STREQ(last_path_component("/a"), "a");
+ ASSERT_STREQ(last_path_component("/a/"), "a/");
}
static void test_path_extract_filename_one(const char *input, const char *output, int ret) {
strnull(input),
strnull(k), r < 0 ? STRERROR(r) : "-",
strnull(output), ret < 0 ? STRERROR(ret) : "-");
- assert_se(streq_ptr(k, output));
+ ASSERT_STREQ(k, output);
assert_se(r == ret);
}
strnull(input),
strnull(k), r < 0 ? STRERROR(r) : "-",
strnull(output), STRERROR(ret));
- assert_se(streq_ptr(k, output));
+ ASSERT_STREQ(k, output);
assert_se(r == ret);
/* Extra safety check: let's make sure that if we split out the filename too (and it works) the
}
TEST(skip_dev_prefix) {
- assert_se(streq(skip_dev_prefix("/"), "/"));
- assert_se(streq(skip_dev_prefix("/dev"), ""));
- assert_se(streq(skip_dev_prefix("/dev/"), ""));
- assert_se(streq(skip_dev_prefix("/dev/foo"), "foo"));
- assert_se(streq(skip_dev_prefix("/dev/foo/bar"), "foo/bar"));
- assert_se(streq(skip_dev_prefix("//dev"), ""));
- assert_se(streq(skip_dev_prefix("//dev//"), ""));
- assert_se(streq(skip_dev_prefix("/dev///foo"), "foo"));
- assert_se(streq(skip_dev_prefix("///dev///foo///bar"), "foo///bar"));
- assert_se(streq(skip_dev_prefix("//foo"), "//foo"));
- assert_se(streq(skip_dev_prefix("foo"), "foo"));
+ ASSERT_STREQ(skip_dev_prefix("/"), "/");
+ ASSERT_STREQ(skip_dev_prefix("/dev"), "");
+ ASSERT_STREQ(skip_dev_prefix("/dev/"), "");
+ ASSERT_STREQ(skip_dev_prefix("/dev/foo"), "foo");
+ ASSERT_STREQ(skip_dev_prefix("/dev/foo/bar"), "foo/bar");
+ ASSERT_STREQ(skip_dev_prefix("//dev"), "");
+ ASSERT_STREQ(skip_dev_prefix("//dev//"), "");
+ ASSERT_STREQ(skip_dev_prefix("/dev///foo"), "foo");
+ ASSERT_STREQ(skip_dev_prefix("///dev///foo///bar"), "foo///bar");
+ ASSERT_STREQ(skip_dev_prefix("//foo"), "//foo");
+ ASSERT_STREQ(skip_dev_prefix("foo"), "foo");
}
TEST(empty_or_root) {
}
TEST(path_startswith_set) {
- assert_se(streq_ptr(PATH_STARTSWITH_SET("/foo/bar", "/foo/quux", "/foo/bar", "/zzz"), ""));
- assert_se(streq_ptr(PATH_STARTSWITH_SET("/foo/bar", "/foo/quux", "/foo/", "/zzz"), "bar"));
- assert_se(streq_ptr(PATH_STARTSWITH_SET("/foo/bar", "/foo/quux", "/foo", "/zzz"), "bar"));
- assert_se(streq_ptr(PATH_STARTSWITH_SET("/foo/bar", "/foo/quux", "/", "/zzz"), "foo/bar"));
- assert_se(streq_ptr(PATH_STARTSWITH_SET("/foo/bar", "/foo/quux", "", "/zzz"), NULL));
-
- assert_se(streq_ptr(PATH_STARTSWITH_SET("/foo/bar2", "/foo/quux", "/foo/bar", "/zzz"), NULL));
- assert_se(streq_ptr(PATH_STARTSWITH_SET("/foo/bar2", "/foo/quux", "/foo/", "/zzz"), "bar2"));
- assert_se(streq_ptr(PATH_STARTSWITH_SET("/foo/bar2", "/foo/quux", "/foo", "/zzz"), "bar2"));
- assert_se(streq_ptr(PATH_STARTSWITH_SET("/foo/bar2", "/foo/quux", "/", "/zzz"), "foo/bar2"));
- assert_se(streq_ptr(PATH_STARTSWITH_SET("/foo/bar2", "/foo/quux", "", "/zzz"), NULL));
-
- assert_se(streq_ptr(PATH_STARTSWITH_SET("/foo2/bar", "/foo/quux", "/foo/bar", "/zzz"), NULL));
- assert_se(streq_ptr(PATH_STARTSWITH_SET("/foo2/bar", "/foo/quux", "/foo/", "/zzz"), NULL));
- assert_se(streq_ptr(PATH_STARTSWITH_SET("/foo2/bar", "/foo/quux", "/foo", "/zzz"), NULL));
- assert_se(streq_ptr(PATH_STARTSWITH_SET("/foo2/bar", "/foo/quux", "/", "/zzz"), "foo2/bar"));
- assert_se(streq_ptr(PATH_STARTSWITH_SET("/foo2/bar", "/foo/quux", "", "/zzz"), NULL));
+ ASSERT_STREQ(PATH_STARTSWITH_SET("/foo/bar", "/foo/quux", "/foo/bar", "/zzz"), "");
+ ASSERT_STREQ(PATH_STARTSWITH_SET("/foo/bar", "/foo/quux", "/foo/", "/zzz"), "bar");
+ ASSERT_STREQ(PATH_STARTSWITH_SET("/foo/bar", "/foo/quux", "/foo", "/zzz"), "bar");
+ ASSERT_STREQ(PATH_STARTSWITH_SET("/foo/bar", "/foo/quux", "/", "/zzz"), "foo/bar");
+ ASSERT_STREQ(PATH_STARTSWITH_SET("/foo/bar", "/foo/quux", "", "/zzz"), NULL);
+
+ ASSERT_STREQ(PATH_STARTSWITH_SET("/foo/bar2", "/foo/quux", "/foo/bar", "/zzz"), NULL);
+ ASSERT_STREQ(PATH_STARTSWITH_SET("/foo/bar2", "/foo/quux", "/foo/", "/zzz"), "bar2");
+ ASSERT_STREQ(PATH_STARTSWITH_SET("/foo/bar2", "/foo/quux", "/foo", "/zzz"), "bar2");
+ ASSERT_STREQ(PATH_STARTSWITH_SET("/foo/bar2", "/foo/quux", "/", "/zzz"), "foo/bar2");
+ ASSERT_STREQ(PATH_STARTSWITH_SET("/foo/bar2", "/foo/quux", "", "/zzz"), NULL);
+
+ ASSERT_STREQ(PATH_STARTSWITH_SET("/foo2/bar", "/foo/quux", "/foo/bar", "/zzz"), NULL);
+ ASSERT_STREQ(PATH_STARTSWITH_SET("/foo2/bar", "/foo/quux", "/foo/", "/zzz"), NULL);
+ ASSERT_STREQ(PATH_STARTSWITH_SET("/foo2/bar", "/foo/quux", "/foo", "/zzz"), NULL);
+ ASSERT_STREQ(PATH_STARTSWITH_SET("/foo2/bar", "/foo/quux", "/", "/zzz"), "foo2/bar");
+ ASSERT_STREQ(PATH_STARTSWITH_SET("/foo2/bar", "/foo/quux", "", "/zzz"), NULL);
}
TEST(path_startswith_strv) {
- assert_se(streq_ptr(path_startswith_strv("/foo/bar", STRV_MAKE("/foo/quux", "/foo/bar", "/zzz")), ""));
- assert_se(streq_ptr(path_startswith_strv("/foo/bar", STRV_MAKE("/foo/quux", "/foo/", "/zzz")), "bar"));
- assert_se(streq_ptr(path_startswith_strv("/foo/bar", STRV_MAKE("/foo/quux", "/foo", "/zzz")), "bar"));
- assert_se(streq_ptr(path_startswith_strv("/foo/bar", STRV_MAKE("/foo/quux", "/", "/zzz")), "foo/bar"));
- assert_se(streq_ptr(path_startswith_strv("/foo/bar", STRV_MAKE("/foo/quux", "", "/zzz")), NULL));
-
- assert_se(streq_ptr(path_startswith_strv("/foo/bar2", STRV_MAKE("/foo/quux", "/foo/bar", "/zzz")), NULL));
- assert_se(streq_ptr(path_startswith_strv("/foo/bar2", STRV_MAKE("/foo/quux", "/foo/", "/zzz")), "bar2"));
- assert_se(streq_ptr(path_startswith_strv("/foo/bar2", STRV_MAKE("/foo/quux", "/foo", "/zzz")), "bar2"));
- assert_se(streq_ptr(path_startswith_strv("/foo/bar2", STRV_MAKE("/foo/quux", "/", "/zzz")), "foo/bar2"));
- assert_se(streq_ptr(path_startswith_strv("/foo/bar2", STRV_MAKE("/foo/quux", "", "/zzz")), NULL));
-
- assert_se(streq_ptr(path_startswith_strv("/foo2/bar", STRV_MAKE("/foo/quux", "/foo/bar", "/zzz")), NULL));
- assert_se(streq_ptr(path_startswith_strv("/foo2/bar", STRV_MAKE("/foo/quux", "/foo/", "/zzz")), NULL));
- assert_se(streq_ptr(path_startswith_strv("/foo2/bar", STRV_MAKE("/foo/quux", "/foo", "/zzz")), NULL));
- assert_se(streq_ptr(path_startswith_strv("/foo2/bar", STRV_MAKE("/foo/quux", "/", "/zzz")), "foo2/bar"));
- assert_se(streq_ptr(path_startswith_strv("/foo2/bar", STRV_MAKE("/foo/quux", "", "/zzz")), NULL));
+ ASSERT_STREQ(path_startswith_strv("/foo/bar", STRV_MAKE("/foo/quux", "/foo/bar", "/zzz")), "");
+ ASSERT_STREQ(path_startswith_strv("/foo/bar", STRV_MAKE("/foo/quux", "/foo/", "/zzz")), "bar");
+ ASSERT_STREQ(path_startswith_strv("/foo/bar", STRV_MAKE("/foo/quux", "/foo", "/zzz")), "bar");
+ ASSERT_STREQ(path_startswith_strv("/foo/bar", STRV_MAKE("/foo/quux", "/", "/zzz")), "foo/bar");
+ ASSERT_STREQ(path_startswith_strv("/foo/bar", STRV_MAKE("/foo/quux", "", "/zzz")), NULL);
+
+ ASSERT_STREQ(path_startswith_strv("/foo/bar2", STRV_MAKE("/foo/quux", "/foo/bar", "/zzz")), NULL);
+ ASSERT_STREQ(path_startswith_strv("/foo/bar2", STRV_MAKE("/foo/quux", "/foo/", "/zzz")), "bar2");
+ ASSERT_STREQ(path_startswith_strv("/foo/bar2", STRV_MAKE("/foo/quux", "/foo", "/zzz")), "bar2");
+ ASSERT_STREQ(path_startswith_strv("/foo/bar2", STRV_MAKE("/foo/quux", "/", "/zzz")), "foo/bar2");
+ ASSERT_STREQ(path_startswith_strv("/foo/bar2", STRV_MAKE("/foo/quux", "", "/zzz")), NULL);
+
+ ASSERT_STREQ(path_startswith_strv("/foo2/bar", STRV_MAKE("/foo/quux", "/foo/bar", "/zzz")), NULL);
+ ASSERT_STREQ(path_startswith_strv("/foo2/bar", STRV_MAKE("/foo/quux", "/foo/", "/zzz")), NULL);
+ ASSERT_STREQ(path_startswith_strv("/foo2/bar", STRV_MAKE("/foo/quux", "/foo", "/zzz")), NULL);
+ ASSERT_STREQ(path_startswith_strv("/foo2/bar", STRV_MAKE("/foo/quux", "/", "/zzz")), "foo2/bar");
+ ASSERT_STREQ(path_startswith_strv("/foo2/bar", STRV_MAKE("/foo/quux", "", "/zzz")), NULL);
}
static void test_path_glob_can_match_one(const char *pattern, const char *prefix, const char *expected) {
log_debug("%s(%s, %s, %s)", __func__, pattern, prefix, strnull(expected));
assert_se(path_glob_can_match(pattern, prefix, &result) == !!expected);
- assert_se(streq_ptr(result, expected));
+ ASSERT_STREQ(result, expected);
}
TEST(path_glob_can_match) {
assert_se(q = prioq_new((compare_func_t) test_compare));
assert_se(s = set_new(&test_hash_ops));
- assert_se(prioq_peek(q) == NULL);
- assert_se(prioq_peek_by_index(q, 0) == NULL);
- assert_se(prioq_peek_by_index(q, 1) == NULL);
- assert_se(prioq_peek_by_index(q, UINT_MAX) == NULL);
+ ASSERT_NULL(prioq_peek(q));
+ ASSERT_NULL(prioq_peek_by_index(q, 0));
+ ASSERT_NULL(prioq_peek_by_index(q, 1));
+ ASSERT_NULL(prioq_peek_by_index(q, UINT_MAX));
for (i = 0; i < SET_SIZE; i++) {
assert_se(t = new0(struct test, 1));
for (i = 0; i < SET_SIZE; i++)
assert_se(prioq_peek_by_index(q, i));
- assert_se(prioq_peek_by_index(q, SET_SIZE) == NULL);
+ ASSERT_NULL(prioq_peek_by_index(q, SET_SIZE));
unsigned count = 0;
PRIOQ_FOREACH_ITEM(q, t) {
/* First test if the overrides for /proc/cmdline still work */
assert_se(proc_cmdline(&line) >= 0);
- assert_se(streq(line, "foo_bar=quux wuff-piep=tuet zumm some_arg_with_space='foo bar' and_one_more=\"zzz aaa\""));
+ ASSERT_STREQ(line, "foo_bar=quux wuff-piep=tuet zumm some_arg_with_space='foo bar' and_one_more=\"zzz aaa\"");
line = mfree(line);
assert_se(proc_cmdline_strv(&args) >= 0);
assert_se(strv_equal(args, STRV_MAKE("foo_bar=quux", "wuff-piep=tuet", "zumm", "some_arg_with_space=foo bar", "and_one_more=zzz aaa")));
assert_se(putenv((char*) "SYSTEMD_EFI_OPTIONS=foo_bar=quux wuff-piep=tuet zumm some_arg_with_space='foo bar' and_one_more=\"zzz aaa\"") == 0);
assert_se(proc_cmdline(&line) >= 0);
- assert_se(streq(line, "hoge"));
+ ASSERT_STREQ(line, "hoge");
line = mfree(line);
assert_se(proc_cmdline_strv(&args) >= 0);
assert_se(strv_equal(args, STRV_MAKE("hoge")));
log_info("%s: option <%s> = <%s>", __func__, key, strna(value));
if (proc_cmdline_key_streq(key, "foo_bar"))
- assert_se(streq(value, "quux"));
+ ASSERT_STREQ(value, "quux");
else if (proc_cmdline_key_streq(key, "wuff-piep"))
- assert_se(streq(value, "tuet "));
+ ASSERT_STREQ(value, "tuet ");
else if (proc_cmdline_key_streq(key, "space"))
- assert_se(streq(value, "x y z"));
+ ASSERT_STREQ(value, "x y z");
else if (proc_cmdline_key_streq(key, "miepf"))
- assert_se(streq(value, "uuu"));
+ ASSERT_STREQ(value, "uuu");
else if (in_initrd() && *strip && proc_cmdline_key_streq(key, "zumm"))
assert_se(!value);
else if (in_initrd() && !*strip && proc_cmdline_key_streq(key, "rd.zumm"))
"doubleticks", &value6,
"zummm", &value7) == 5);
- assert_se(streq_ptr(value1, "quux"));
+ ASSERT_STREQ(value1, "quux");
assert_se(!value2);
- assert_se(streq_ptr(value3, "tuet"));
+ ASSERT_STREQ(value3, "tuet");
assert_se(!value4);
- assert_se(streq_ptr(value5, "one two"));
- assert_se(streq_ptr(value6, " aaa aaa "));
- assert_se(streq_ptr(value7, "\n"));
+ ASSERT_STREQ(value5, "one two");
+ ASSERT_STREQ(value6, " aaa aaa ");
+ ASSERT_STREQ(value7, "\n");
}
TEST(proc_cmdline_key_streq) {
log_debug("got: <%s>", n);
- assert_se(streq_ptr(n, output));
+ ASSERT_STREQ(n, output);
}
TEST(pid_get_comm_escape) {
assert_se(personality_to_string(PER_LINUX));
assert_se(!personality_to_string(PERSONALITY_INVALID));
- assert_se(streq(personality_to_string(PER_LINUX), architecture_to_string(native_architecture())));
+ ASSERT_STREQ(personality_to_string(PER_LINUX), architecture_to_string(native_architecture()));
assert_se(personality_from_string(personality_to_string(PER_LINUX)) == PER_LINUX);
assert_se(personality_from_string(architecture_to_string(native_architecture())) == PER_LINUX);
#ifdef __x86_64__
- assert_se(streq_ptr(personality_to_string(PER_LINUX), "x86-64"));
- assert_se(streq_ptr(personality_to_string(PER_LINUX32), "x86"));
+ ASSERT_STREQ(personality_to_string(PER_LINUX), "x86-64");
+ ASSERT_STREQ(personality_to_string(PER_LINUX32), "x86");
assert_se(personality_from_string("x86-64") == PER_LINUX);
assert_se(personality_from_string("x86") == PER_LINUX32);
assert_se(pid_get_cmdline(0, SIZE_MAX, PROCESS_CMDLINE_COMM_FALLBACK, &line) >= 0);
log_debug("'%s'", line);
- assert_se(streq(line, "[testa]"));
+ ASSERT_STREQ(line, "[testa]");
line = mfree(line);
assert_se(pid_get_cmdline(0, SIZE_MAX, PROCESS_CMDLINE_COMM_FALLBACK | PROCESS_CMDLINE_QUOTE, &line) >= 0);
log_debug("'%s'", line);
- assert_se(streq(line, "\"[testa]\"")); /* quoting is enabled here */
+ ASSERT_STREQ(line, "\"[testa]\""); /* quoting is enabled here */
line = mfree(line);
assert_se(pid_get_cmdline(0, 0, PROCESS_CMDLINE_COMM_FALLBACK, &line) >= 0);
log_debug("'%s'", line);
- assert_se(streq(line, ""));
+ ASSERT_STREQ(line, "");
line = mfree(line);
assert_se(pid_get_cmdline(0, 1, PROCESS_CMDLINE_COMM_FALLBACK, &line) >= 0);
- assert_se(streq(line, "…"));
+ ASSERT_STREQ(line, "…");
line = mfree(line);
assert_se(pid_get_cmdline(0, 2, PROCESS_CMDLINE_COMM_FALLBACK, &line) >= 0);
- assert_se(streq(line, "[…"));
+ ASSERT_STREQ(line, "[…");
line = mfree(line);
assert_se(pid_get_cmdline(0, 3, PROCESS_CMDLINE_COMM_FALLBACK, &line) >= 0);
- assert_se(streq(line, "[t…"));
+ ASSERT_STREQ(line, "[t…");
line = mfree(line);
assert_se(pid_get_cmdline(0, 4, PROCESS_CMDLINE_COMM_FALLBACK, &line) >= 0);
- assert_se(streq(line, "[te…"));
+ ASSERT_STREQ(line, "[te…");
line = mfree(line);
assert_se(pid_get_cmdline(0, 5, PROCESS_CMDLINE_COMM_FALLBACK, &line) >= 0);
- assert_se(streq(line, "[tes…"));
+ ASSERT_STREQ(line, "[tes…");
line = mfree(line);
assert_se(pid_get_cmdline(0, 6, PROCESS_CMDLINE_COMM_FALLBACK, &line) >= 0);
- assert_se(streq(line, "[test…"));
+ ASSERT_STREQ(line, "[test…");
line = mfree(line);
assert_se(pid_get_cmdline(0, 7, PROCESS_CMDLINE_COMM_FALLBACK, &line) >= 0);
- assert_se(streq(line, "[testa]"));
+ ASSERT_STREQ(line, "[testa]");
line = mfree(line);
assert_se(pid_get_cmdline(0, 8, PROCESS_CMDLINE_COMM_FALLBACK, &line) >= 0);
- assert_se(streq(line, "[testa]"));
+ ASSERT_STREQ(line, "[testa]");
line = mfree(line);
assert_se(pid_get_cmdline_strv(0, PROCESS_CMDLINE_COMM_FALLBACK, &args) >= 0);
assert_se(pid_get_cmdline(0, SIZE_MAX, 0, &line) >= 0);
log_debug("'%s'", line);
- assert_se(streq(line, "foo bar"));
+ ASSERT_STREQ(line, "foo bar");
line = mfree(line);
assert_se(pid_get_cmdline(0, SIZE_MAX, PROCESS_CMDLINE_COMM_FALLBACK, &line) >= 0);
- assert_se(streq(line, "foo bar"));
+ ASSERT_STREQ(line, "foo bar");
line = mfree(line);
assert_se(pid_get_cmdline_strv(0, PROCESS_CMDLINE_COMM_FALLBACK, &args) >= 0);
assert_se(write(fd, "quux", 4) == 4);
assert_se(pid_get_cmdline(0, SIZE_MAX, 0, &line) >= 0);
log_debug("'%s'", line);
- assert_se(streq(line, "foo bar quux"));
+ ASSERT_STREQ(line, "foo bar quux");
line = mfree(line);
assert_se(pid_get_cmdline(0, SIZE_MAX, PROCESS_CMDLINE_COMM_FALLBACK, &line) >= 0);
log_debug("'%s'", line);
- assert_se(streq(line, "foo bar quux"));
+ ASSERT_STREQ(line, "foo bar quux");
line = mfree(line);
assert_se(pid_get_cmdline(0, 1, PROCESS_CMDLINE_COMM_FALLBACK, &line) >= 0);
log_debug("'%s'", line);
- assert_se(streq(line, "…"));
+ ASSERT_STREQ(line, "…");
line = mfree(line);
assert_se(pid_get_cmdline(0, 2, PROCESS_CMDLINE_COMM_FALLBACK, &line) >= 0);
log_debug("'%s'", line);
- assert_se(streq(line, "f…"));
+ ASSERT_STREQ(line, "f…");
line = mfree(line);
assert_se(pid_get_cmdline(0, 3, PROCESS_CMDLINE_COMM_FALLBACK, &line) >= 0);
log_debug("'%s'", line);
- assert_se(streq(line, "fo…"));
+ ASSERT_STREQ(line, "fo…");
line = mfree(line);
assert_se(pid_get_cmdline(0, 4, PROCESS_CMDLINE_COMM_FALLBACK, &line) >= 0);
log_debug("'%s'", line);
- assert_se(streq(line, "foo…"));
+ ASSERT_STREQ(line, "foo…");
line = mfree(line);
assert_se(pid_get_cmdline(0, 5, PROCESS_CMDLINE_COMM_FALLBACK, &line) >= 0);
log_debug("'%s'", line);
- assert_se(streq(line, "foo …"));
+ ASSERT_STREQ(line, "foo …");
line = mfree(line);
assert_se(pid_get_cmdline(0, 6, PROCESS_CMDLINE_COMM_FALLBACK, &line) >= 0);
log_debug("'%s'", line);
- assert_se(streq(line, "foo b…"));
+ ASSERT_STREQ(line, "foo b…");
line = mfree(line);
assert_se(pid_get_cmdline(0, 7, PROCESS_CMDLINE_COMM_FALLBACK, &line) >= 0);
log_debug("'%s'", line);
- assert_se(streq(line, "foo ba…"));
+ ASSERT_STREQ(line, "foo ba…");
line = mfree(line);
assert_se(pid_get_cmdline(0, 8, PROCESS_CMDLINE_COMM_FALLBACK, &line) >= 0);
log_debug("'%s'", line);
- assert_se(streq(line, "foo bar…"));
+ ASSERT_STREQ(line, "foo bar…");
line = mfree(line);
assert_se(pid_get_cmdline(0, 9, PROCESS_CMDLINE_COMM_FALLBACK, &line) >= 0);
log_debug("'%s'", line);
- assert_se(streq(line, "foo bar …"));
+ ASSERT_STREQ(line, "foo bar …");
line = mfree(line);
assert_se(pid_get_cmdline(0, 10, PROCESS_CMDLINE_COMM_FALLBACK, &line) >= 0);
log_debug("'%s'", line);
- assert_se(streq(line, "foo bar q…"));
+ ASSERT_STREQ(line, "foo bar q…");
line = mfree(line);
assert_se(pid_get_cmdline(0, 11, PROCESS_CMDLINE_COMM_FALLBACK, &line) >= 0);
log_debug("'%s'", line);
- assert_se(streq(line, "foo bar qu…"));
+ ASSERT_STREQ(line, "foo bar qu…");
line = mfree(line);
assert_se(pid_get_cmdline(0, 12, PROCESS_CMDLINE_COMM_FALLBACK, &line) >= 0);
log_debug("'%s'", line);
- assert_se(streq(line, "foo bar quux"));
+ ASSERT_STREQ(line, "foo bar quux");
line = mfree(line);
assert_se(pid_get_cmdline(0, 13, PROCESS_CMDLINE_COMM_FALLBACK, &line) >= 0);
log_debug("'%s'", line);
- assert_se(streq(line, "foo bar quux"));
+ ASSERT_STREQ(line, "foo bar quux");
line = mfree(line);
assert_se(pid_get_cmdline(0, 14, PROCESS_CMDLINE_COMM_FALLBACK, &line) >= 0);
log_debug("'%s'", line);
- assert_se(streq(line, "foo bar quux"));
+ ASSERT_STREQ(line, "foo bar quux");
line = mfree(line);
assert_se(pid_get_cmdline(0, 1000, PROCESS_CMDLINE_COMM_FALLBACK, &line) >= 0);
log_debug("'%s'", line);
- assert_se(streq(line, "foo bar quux"));
+ ASSERT_STREQ(line, "foo bar quux");
line = mfree(line);
assert_se(pid_get_cmdline_strv(0, PROCESS_CMDLINE_COMM_FALLBACK, &args) >= 0);
assert_se(pid_get_cmdline(0, SIZE_MAX, PROCESS_CMDLINE_COMM_FALLBACK, &line) >= 0);
log_debug("'%s'", line);
- assert_se(streq(line, "[aaaa bbbb cccc]"));
+ ASSERT_STREQ(line, "[aaaa bbbb cccc]");
line = mfree(line);
assert_se(pid_get_cmdline(0, 10, PROCESS_CMDLINE_COMM_FALLBACK, &line) >= 0);
log_debug("'%s'", line);
- assert_se(streq(line, "[aaaa bbb…"));
+ ASSERT_STREQ(line, "[aaaa bbb…");
line = mfree(line);
assert_se(pid_get_cmdline(0, 11, PROCESS_CMDLINE_COMM_FALLBACK, &line) >= 0);
log_debug("'%s'", line);
- assert_se(streq(line, "[aaaa bbbb…"));
+ ASSERT_STREQ(line, "[aaaa bbbb…");
line = mfree(line);
assert_se(pid_get_cmdline(0, 12, PROCESS_CMDLINE_COMM_FALLBACK, &line) >= 0);
log_debug("'%s'", line);
- assert_se(streq(line, "[aaaa bbbb …"));
+ ASSERT_STREQ(line, "[aaaa bbbb …");
line = mfree(line);
assert_se(pid_get_cmdline_strv(0, PROCESS_CMDLINE_COMM_FALLBACK, &args) >= 0);
assert_se(pid_get_cmdline(0, SIZE_MAX, PROCESS_CMDLINE_QUOTE, &line) >= 0);
log_debug("got: ==%s==", line);
log_debug("exp: ==%s==", EXPECT1);
- assert_se(streq(line, EXPECT1));
+ ASSERT_STREQ(line, EXPECT1);
line = mfree(line);
assert_se(pid_get_cmdline(0, SIZE_MAX, PROCESS_CMDLINE_QUOTE_POSIX, &line) >= 0);
log_debug("got: ==%s==", line);
log_debug("exp: ==%s==", EXPECT1p);
- assert_se(streq(line, EXPECT1p));
+ ASSERT_STREQ(line, EXPECT1p);
line = mfree(line);
assert_se(pid_get_cmdline_strv(0, 0, &args) >= 0);
assert_se(pid_get_cmdline(0, SIZE_MAX, PROCESS_CMDLINE_QUOTE, &line) >= 0);
log_debug("got: ==%s==", line);
log_debug("exp: ==%s==", EXPECT2);
- assert_se(streq(line, EXPECT2));
+ ASSERT_STREQ(line, EXPECT2);
line = mfree(line);
assert_se(pid_get_cmdline(0, SIZE_MAX, PROCESS_CMDLINE_QUOTE_POSIX, &line) >= 0);
log_debug("got: ==%s==", line);
log_debug("exp: ==%s==", EXPECT2p);
- assert_se(streq(line, EXPECT2p));
+ ASSERT_STREQ(line, EXPECT2p);
line = mfree(line);
assert_se(pid_get_cmdline_strv(0, 0, &args) >= 0);
TEST(pid_to_ptr) {
assert_se(PTR_TO_PID(NULL) == 0);
- assert_se(PID_TO_PTR(0) == NULL);
+ ASSERT_NULL(PID_TO_PTR(0));
assert_se(PTR_TO_PID(PID_TO_PTR(1)) == 1);
assert_se(PTR_TO_PID(PID_TO_PTR(2)) == 2);
r = normalize_recovery_key("cdefVhij-cDefghij-cdefkhij-cdufghij-cdefgdij-cidefIhj-cdefNijR-cdVfguij",
&normalized_key2);
assert_se(r == 0);
- assert_se(streq(normalized_key2, "cdefvhij-cdefghij-cdefkhij-cdufghij-cdefgdij-cidefihj-cdefnijr-cdvfguij"));
+ ASSERT_STREQ(normalized_key2, "cdefvhij-cdefghij-cdefkhij-cdufghij-cdefgdij-cidefihj-cdefnijr-cdvfguij");
/* Case 3: Invalid password length */
r = normalize_recovery_key("1234-5678-90AB-CDEF-1234-5678-90AB-CDEF", &normalized_key1);
r = normalize_recovery_key("BFGHICEHHIUVLKJIHFHEDlntruvcdefjiTUVKLNIJVTUTKJIHDFBCBGHIJHHFDBC",
&normalized_key3);
assert(r == 0);
- assert_se(streq(normalized_key3, "bfghiceh-hiuvlkji-hfhedlnt-ruvcdefj-ituvklni-jvtutkji-hdfbcbgh-ijhhfdbc"));
+ ASSERT_STREQ(normalized_key3, "bfghiceh-hiuvlkji-hfhedlnt-ruvcdefj-ituvklni-jvtutkji-hdfbcbgh-ijhhfdbc");
/* Case 6: Minimum password length */
r = normalize_recovery_key("", &normalized_key1);
assert_se(r = replace_var("@@@foobar@xyz@HALLO@foobar@test@@testtest@TEST@...@@@", lookup, NULL));
puts(r);
- assert_se(streq(r, "@@@foobar@xyz<<<HALLO>>>foobar@test@@testtest<<<TEST>>>...@@@"));
+ ASSERT_STREQ(r, "@@@foobar@xyz<<<HALLO>>>foobar@test@@testtest<<<TEST>>>...@@@");
free(r);
}
assert_se(r = strreplace("XYZFFFFXYZFFFFXYZ", "XYZ", "ABC"));
puts(r);
- assert_se(streq(r, "ABCFFFFABCFFFFABC"));
+ ASSERT_STREQ(r, "ABCFFFFABCFFFFABC");
free(r);
}
assert_se(rl.rlim_max == hard);
assert_se(rlimit_format(&rl, &f) >= 0);
- assert_se(streq(formatted, f));
+ ASSERT_STREQ(formatted, f);
assert_se(rlimit_parse(resource, formatted, &rl2) >= 0);
assert_se(memcmp(&rl, &rl2, sizeof(struct rlimit)) == 0);
new.rlim_max = old.rlim_max;
assert_se(setrlimit(RLIMIT_NOFILE, &new) >= 0);
- assert_se(streq_ptr(rlimit_to_string(RLIMIT_NOFILE), "NOFILE"));
- assert_se(rlimit_to_string(-1) == NULL);
+ ASSERT_STREQ(rlimit_to_string(RLIMIT_NOFILE), "NOFILE");
+ ASSERT_NULL(rlimit_to_string(-1));
assert_se(getrlimit(RLIMIT_NOFILE, &old) == 0);
assert_se(setrlimit_closest(RLIMIT_NOFILE, &old) == 0);
int e;
assert_se(parse_syscall_and_errno("uname:EILSEQ", &n, &e) >= 0);
- assert_se(streq(n, "uname"));
+ ASSERT_STREQ(n, "uname");
assert_se(e == errno_from_name("EILSEQ") && e >= 0);
n = mfree(n);
assert_se(parse_syscall_and_errno("uname:EINVAL", &n, &e) >= 0);
- assert_se(streq(n, "uname"));
+ ASSERT_STREQ(n, "uname");
assert_se(e == errno_from_name("EINVAL") && e >= 0);
n = mfree(n);
assert_se(parse_syscall_and_errno("@sync:4095", &n, &e) >= 0);
- assert_se(streq(n, "@sync"));
+ ASSERT_STREQ(n, "@sync");
assert_se(e == 4095);
n = mfree(n);
/* If errno is omitted, then e is set to -1 */
assert_se(parse_syscall_and_errno("mount", &n, &e) >= 0);
- assert_se(streq(n, "mount"));
+ ASSERT_STREQ(n, "mount");
assert_se(e == -1);
n = mfree(n);
/* parse_syscall_and_errno() does not check the syscall name is valid or not. */
assert_se(parse_syscall_and_errno("hoge:255", &n, &e) >= 0);
- assert_se(streq(n, "hoge"));
+ ASSERT_STREQ(n, "hoge");
assert_se(e == 255);
n = mfree(n);
/* 0 is also a valid errno. */
assert_se(parse_syscall_and_errno("hoge:0", &n, &e) >= 0);
- assert_se(streq(n, "hoge"));
+ ASSERT_STREQ(n, "hoge");
assert_se(e == 0);
n = mfree(n);
assert_se(parse_syscall_and_errno("hoge:kill", &n, &e) >= 0);
- assert_se(streq(n, "hoge"));
+ ASSERT_STREQ(n, "hoge");
assert_se(e == SECCOMP_ERROR_NUMBER_KILL);
n = mfree(n);
assert_se(seccomp_arch_from_string(n, &c) >= 0);
n2 = seccomp_arch_to_string(c);
log_info("seccomp-arch: %s → 0x%"PRIx32" → %s", n, c, n2);
- assert_se(streq_ptr(n, n2));
+ ASSERT_STREQ(n, n2);
}
}
TEST(filter_sets_ordered) {
/* Ensure "@default" always remains at the beginning of the list */
assert_se(SYSCALL_FILTER_SET_DEFAULT == 0);
- assert_se(streq(syscall_filter_sets[0].name, "@default"));
+ ASSERT_STREQ(syscall_filter_sets[0].name, "@default");
/* Ensure "@known" always remains at the end of the list */
assert_se(SYSCALL_FILTER_SET_KNOWN == _SYSCALL_FILTER_SET_MAX - 1);
- assert_se(streq(syscall_filter_sets[SYSCALL_FILTER_SET_KNOWN].name, "@known"));
+ ASSERT_STREQ(syscall_filter_sets[SYSCALL_FILTER_SET_KNOWN].name, "@known");
for (size_t i = 0; i < _SYSCALL_FILTER_SET_MAX; i++) {
const char *p = NULL;
s = mfree(s);
assert_se(namespace_flags_to_string(NAMESPACE_FLAGS_ALL, &s) == 0);
- assert_se(streq(s, "cgroup ipc net mnt pid user uts"));
+ ASSERT_STREQ(s, "cgroup ipc net mnt pid user uts");
assert_se(namespace_flags_from_string(s, &ul) == 0 && ul == NAMESPACE_FLAGS_ALL);
s = mfree(s);
if (pid == 0) {
void *p;
- p = mmap(NULL, page_size(), PROT_WRITE|PROT_EXEC, MAP_PRIVATE|MAP_ANONYMOUS, -1,0);
+ p = mmap(NULL, page_size(), PROT_WRITE|PROT_EXEC, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0);
assert_se(p != MAP_FAILED);
assert_se(munmap(p, page_size()) >= 0);
- p = mmap(NULL, page_size(), PROT_WRITE|PROT_READ, MAP_PRIVATE|MAP_ANONYMOUS, -1,0);
+ p = mmap(NULL, page_size(), PROT_WRITE|PROT_READ, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0);
assert_se(p != MAP_FAILED);
assert_se(munmap(p, page_size()) >= 0);
assert_se(seccomp_memory_deny_write_execute() >= 0);
- p = mmap(NULL, page_size(), PROT_WRITE|PROT_EXEC, MAP_PRIVATE|MAP_ANONYMOUS, -1,0);
+ p = mmap(NULL, page_size(), PROT_WRITE|PROT_EXEC, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0);
#if defined(__x86_64__) || defined(__i386__) || defined(__powerpc64__) || defined(__arm__) || defined(__aarch64__) || defined(__loongarch_lp64)
assert_se(p == MAP_FAILED);
assert_se(errno == EPERM);
if (p != MAP_FAILED)
assert_se(munmap(p, page_size()) == 0);
- p = mmap(NULL, page_size(), PROT_WRITE|PROT_READ, MAP_PRIVATE|MAP_ANONYMOUS, -1,0);
+ p = mmap(NULL, page_size(), PROT_WRITE|PROT_READ, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0);
assert_se(p != MAP_FAILED);
assert_se(munmap(p, page_size()) >= 0);
assert_se(secure_bits_is_valid(r));
assert_se(secure_bits_to_string_alloc(r, &s) >= 0);
printf("%s = 0x%x = %s\n", *bit, (unsigned)r, s);
- assert_se(streq(*bit, s));
+ ASSERT_STREQ(*bit, s);
}
/* Ditto, but with all bits at once */
assert_se(secure_bits_is_valid(r));
assert_se(secure_bits_to_string_alloc(r, &str) >= 0);
printf("%s = 0x%x = %s\n", joined, (unsigned)r, str);
- assert_se(streq(joined, str));
+ ASSERT_STREQ(joined, str);
str = mfree(str);
/* Bits to string with check */
assert_se(secure_bits_to_string_alloc_with_check(INT_MAX, &str) == -EINVAL);
- assert_se(str == NULL);
+ ASSERT_NULL(str);
assert_se(secure_bits_to_string_alloc_with_check(
(1 << SECURE_KEEP_CAPS) | (1 << SECURE_KEEP_CAPS_LOCKED),
&str) >= 0);
- assert_se(streq(str, "keep-caps keep-caps-locked"));
+ ASSERT_STREQ(str, "keep-caps keep-caps-locked");
}
TEST(secure_bits_mix) {
assert_se(secure_bits_is_valid(r));
assert_se(secure_bits_to_string_alloc(r, &str) >= 0);
printf("%s = 0x%x = %s\n", s->input, (unsigned)r, str);
- assert_se(streq(s->expected, str));
+ ASSERT_STREQ(s->expected, str);
}
}
_cleanup_free_ char *line1 = NULL, *line2 = NULL, *line3 = NULL, *line4 = NULL;
assert_se(read_line(f, LONG_LINE_MAX, &line1) > 0);
- assert_se(streq(line1, "a=bbb"));
+ ASSERT_STREQ(line1, "a=bbb");
assert_se(read_line(f, LONG_LINE_MAX, &line2) > 0);
- assert_se(streq(line2, "a=bbb"));
+ ASSERT_STREQ(line2, "a=bbb");
assert_se(read_line(f, LONG_LINE_MAX, &line3) > 0);
- assert_se(streq(line3, "c=yes"));
+ ASSERT_STREQ(line3, "c=yes");
assert_se(read_line(f, LONG_LINE_MAX, &line4) == 0);
- assert_se(streq(line4, ""));
+ ASSERT_STREQ(line4, "");
}
TEST(serialize_item_escaped) {
_cleanup_free_ char *line1 = NULL, *line2 = NULL, *line3 = NULL;
assert_se(read_line(f, LONG_LINE_MAX, &line1) > 0);
- assert_se(streq(line1, "a=bbb"));
+ ASSERT_STREQ(line1, "a=bbb");
assert_se(read_line(f, LONG_LINE_MAX, &line2) > 0);
- assert_se(streq(line2, "a=bbb"));
+ ASSERT_STREQ(line2, "a=bbb");
assert_se(read_line(f, LONG_LINE_MAX, &line3) == 0);
- assert_se(streq(line3, ""));
+ ASSERT_STREQ(line3, "");
}
TEST(serialize_usec) {
usec_t x;
assert_se(read_line(f, LONG_LINE_MAX, &line1) > 0);
- assert_se(streq(line1, "usec2=0"));
+ ASSERT_STREQ(line1, "usec2=0");
assert_se(deserialize_usec(line1 + 6, &x) == 0);
assert_se(x == 0);
_cleanup_free_ char *line = NULL;
assert_se(read_line(f, LONG_LINE_MAX, &line) > 0);
- assert_se(streq(line, "a=ffffff"));
+ ASSERT_STREQ(line, "a=ffffff");
}
_cleanup_free_ char *line = NULL;
assert_se(read_line(f, LONG_LINE_MAX, &line) > 0);
- assert_se(streq(line, "a=////"));
+ ASSERT_STREQ(line, "a=////");
}
TEST(serialize_string_set) {
/* Single entry */
assert_se(set_put_strdup(&m, "aaa") == 1);
assert_se(set_strjoin(m, NULL, false, &joined) >= 0);
- assert_se(streq(joined, "aaa"));
+ ASSERT_STREQ(joined, "aaa");
joined = mfree(joined);
assert_se(set_strjoin(m, "", false, &joined) >= 0);
- assert_se(streq(joined, "aaa"));
+ ASSERT_STREQ(joined, "aaa");
joined = mfree(joined);
assert_se(set_strjoin(m, " ", false, &joined) >= 0);
- assert_se(streq(joined, "aaa"));
+ ASSERT_STREQ(joined, "aaa");
joined = mfree(joined);
assert_se(set_strjoin(m, "xxx", false, &joined) >= 0);
- assert_se(streq(joined, "aaa"));
+ ASSERT_STREQ(joined, "aaa");
joined = mfree(joined);
assert_se(set_strjoin(m, NULL, true, &joined) >= 0);
- assert_se(streq(joined, "aaa"));
+ ASSERT_STREQ(joined, "aaa");
joined = mfree(joined);
assert_se(set_strjoin(m, "", true, &joined) >= 0);
- assert_se(streq(joined, "aaa"));
+ ASSERT_STREQ(joined, "aaa");
joined = mfree(joined);
assert_se(set_strjoin(m, " ", true, &joined) >= 0);
- assert_se(streq(joined, " aaa "));
+ ASSERT_STREQ(joined, " aaa ");
joined = mfree(joined);
assert_se(set_strjoin(m, "xxx", true, &joined) >= 0);
- assert_se(streq(joined, "xxxaaaxxx"));
+ ASSERT_STREQ(joined, "xxxaaaxxx");
/* Two entries */
assert_se(set_put_strdup(&m, "bbb") == 1);
sha256_finish_ctx(&ctx, result + j);
hex_result = hexmem(result + j, SHA256_DIGEST_SIZE);
- assert_se(streq_ptr(hex_result, expect));
+ ASSERT_STREQ(hex_result, expect);
}
}
}
log_info("Standby configured: %s", yes_no(sleep_state_supported(standby) > 0));
log_info("Suspend configured: %s", yes_no(sleep_state_supported(mem) > 0));
log_info("Hibernate configured: %s", yes_no(sleep_state_supported(disk) > 0));
- log_info("Hibernate+Suspend (Hybrid-Sleep) configured: %s", yes_no(sleep_mode_supported(suspend) > 0));
- log_info("Hibernate+Reboot configured: %s", yes_no(sleep_mode_supported(reboot) > 0));
- log_info("Hibernate+Platform configured: %s", yes_no(sleep_mode_supported(platform) > 0));
- log_info("Hibernate+Shutdown configured: %s", yes_no(sleep_mode_supported(shutdown) > 0));
+ log_info("Hibernate+Suspend (Hybrid-Sleep) configured: %s", yes_no(sleep_mode_supported("/sys/power/disk", suspend) > 0));
+ log_info("Hibernate+Reboot configured: %s", yes_no(sleep_mode_supported("/sys/power/disk", reboot) > 0));
+ log_info("Hibernate+Platform configured: %s", yes_no(sleep_mode_supported("/sys/power/disk", platform) > 0));
+ log_info("Hibernate+Shutdown configured: %s", yes_no(sleep_mode_supported("/sys/power/disk", shutdown) > 0));
log_info("Freeze configured: %s", yes_no(sleep_state_supported(freeze) > 0));
log_info("/= high-level sleep verbs =/");
fputc('\n', stderr);
exec_start = strjoin("-timeout --preserve-status -sSIGTERM 1s ", netcat_path, " -l ", port, " -vv");
- assert_se(exec_start != NULL);
+ ASSERT_NOT_NULL(exec_start);
r = config_parse_exec(u->id, "filename", 1, "Service", 1, "ExecStart",
SERVICE_EXEC_START, exec_start, SERVICE(u)->exec_command, u);
assert_se(r == ret);
if (r >= 0) {
assert_se(a.sockaddr.sa.sa_family == family);
- assert_se(streq(out, expected ?: in));
+ ASSERT_STREQ(out, expected ?: in);
}
}
assert_se(!socket_address_get_path(&a));
assert_se(socket_address_parse(&a, "/foo/bar") >= 0);
- assert_se(streq(socket_address_get_path(&a), "/foo/bar"));
+ ASSERT_STREQ(socket_address_get_path(&a), "/foo/bar");
assert_se(socket_address_parse(&a, "vsock:2:1234") >= 0);
assert_se(!socket_address_get_path(&a));
assert_se(in_addr_from_string(f, a, &ua) >= 0);
assert_se(in_addr_ifindex_to_string(f, &ua, ifindex, &r) >= 0);
printf("test_in_addr_ifindex_to_string_one: %s == %s\n", b, r);
- assert_se(streq(b, r));
+ ASSERT_STREQ(b, r);
assert_se(in_addr_ifindex_from_string_auto(b, &ff, &uuaa, &ifindex2) >= 0);
assert_se(ff == f);
_cleanup_free_ char *server_name = NULL;
assert_se(in_addr_ifindex_name_from_string_auto(a, &family, &ua, &ifindex, &server_name) >= 0);
- assert_se(streq_ptr(server_name, expected));
+ ASSERT_STREQ(server_name, expected);
}
TEST(in_addr_ifindex_name_from_string_auto) {
assert_se(family == f);
assert_se(port == p);
assert_se(ifindex == i);
- assert_se(streq_ptr(server_name, name));
+ ASSERT_STREQ(server_name, name);
assert_se(in_addr_port_ifindex_name_to_string(f, &a, p, i, name, &x) >= 0);
- assert_se(streq(str_repr ?: str, x));
+ ASSERT_STREQ(str_repr ?: str, x);
}
if (port > 0)
assert_se(in_addr_port_ifindex_name_from_string_auto(str, &f, &a, NULL, &i, &name) == 0);
assert_se(family == f);
assert_se(ifindex == i);
- assert_se(streq_ptr(server_name, name));
+ ASSERT_STREQ(server_name, name);
assert_se(in_addr_port_ifindex_name_to_string(f, &a, 0, i, name, &x) >= 0);
- assert_se(streq(str_repr ?: str, x));
+ ASSERT_STREQ(str_repr ?: str, x);
}
if (ifindex > 0)
assert_se(in_addr_port_ifindex_name_from_string_auto(str, &f, &a, &p, NULL, &name) == 0);
assert_se(family == f);
assert_se(port == p);
- assert_se(streq_ptr(server_name, name));
+ ASSERT_STREQ(server_name, name);
assert_se(in_addr_port_ifindex_name_to_string(f, &a, p, 0, name, &x) >= 0);
- assert_se(streq(str_repr ?: str, x));
+ ASSERT_STREQ(str_repr ?: str, x);
}
if (server_name)
assert_se(port == p);
assert_se(ifindex == i);
assert_se(in_addr_port_ifindex_name_to_string(f, &a, p, i, NULL, &x) >= 0);
- assert_se(streq(str_repr ?: str, x));
+ ASSERT_STREQ(str_repr ?: str, x);
}
}
assert_se(socket_address_print(&a, &out) >= 0);
assert_se(c = cescape(in));
log_info("\"%s\" → \"%s\" (expect \"%s\")", in, out, expected);
- assert_se(streq(out, expected));
+ ASSERT_STREQ(out, expected);
}
TEST(socket_print_unix) {
uid_t test_uid;
gid_t test_gid;
struct ucred ucred;
- int pair[2];
+ int pair[2] = EBADF_PAIR;
if (geteuid() == 0) {
test_uid = 1;
TEST(passfd_read) {
static const char file_contents[] = "test contents for passfd";
- _cleanup_close_pair_ int pair[2];
+ _cleanup_close_pair_ int pair[2] = EBADF_PAIR;
int r;
assert_se(socketpair(AF_UNIX, SOCK_DGRAM, 0, pair) >= 0);
/* Parent */
char buf[64];
struct iovec iov = IOVEC_MAKE(buf, sizeof(buf)-1);
- _cleanup_close_ int fd;
+ _cleanup_close_ int fd = -EBADF;
pair[1] = safe_close(pair[1]);
r = read(fd, buf, sizeof(buf)-1);
assert_se(r >= 0);
buf[r] = 0;
- assert_se(streq(buf, file_contents));
+ ASSERT_STREQ(buf, file_contents);
}
TEST(passfd_contents_read) {
- _cleanup_close_pair_ int pair[2];
+ _cleanup_close_pair_ int pair[2] = EBADF_PAIR;
static const char file_contents[] = "test contents in the file";
static const char wire_contents[] = "test contents on the wire";
int r;
k = receive_one_fd_iov(pair[0], &iov, 1, MSG_DONTWAIT, &fd);
assert_se(k > 0);
buf[k] = 0;
- assert_se(streq(buf, wire_contents));
+ ASSERT_STREQ(buf, wire_contents);
assert_se(fd >= 0);
r = read(fd, buf, sizeof(buf)-1);
assert_se(r >= 0);
buf[r] = 0;
- assert_se(streq(buf, file_contents));
+ ASSERT_STREQ(buf, file_contents);
}
TEST(pass_many_fds_contents_read) {
- _cleanup_close_pair_ int pair[2];
+ _cleanup_close_pair_ int pair[2] = EBADF_PAIR;
static const char file_contents[][STRLEN("test contents in the fileX") + 1] = {
"test contents in the file0",
"test contents in the file1",
k = receive_many_fds_iov(pair[0], &iov, 1, &fds, &n_fds, MSG_DONTWAIT);
assert_se(k > 0);
buf[k] = 0;
- assert_se(streq(buf, wire_contents));
+ ASSERT_STREQ(buf, wire_contents);
assert_se(n_fds == 3);
r = read(fds[i], buf, sizeof(buf)-1);
assert_se(r >= 0);
buf[r] = 0;
- assert_se(streq(buf, file_contents[i]));
+ ASSERT_STREQ(buf, file_contents[i]);
safe_close(fds[i]);
}
}
TEST(receive_nopassfd) {
- _cleanup_close_pair_ int pair[2];
+ _cleanup_close_pair_ int pair[2] = EBADF_PAIR;
static const char wire_contents[] = "no fd passed here";
int r;
k = receive_one_fd_iov(pair[0], &iov, 1, MSG_DONTWAIT, &fd);
assert_se(k > 0);
buf[k] = 0;
- assert_se(streq(buf, wire_contents));
+ ASSERT_STREQ(buf, wire_contents);
/* no fd passed here, confirm it was reset */
assert_se(fd == -EBADF);
}
TEST(send_nodata_nofd) {
- _cleanup_close_pair_ int pair[2];
+ _cleanup_close_pair_ int pair[2] = EBADF_PAIR;
int r;
assert_se(socketpair(AF_UNIX, SOCK_DGRAM, 0, pair) >= 0);
}
TEST(send_emptydata) {
- _cleanup_close_pair_ int pair[2];
+ _cleanup_close_pair_ int pair[2] = EBADF_PAIR;
int r;
assert_se(socketpair(AF_UNIX, SOCK_DGRAM, 0, pair) >= 0);
_cleanup_free_ char *x = NULL;
x = specifier_escape(a);
- assert_se(streq_ptr(x, b));
+ ASSERT_STREQ(x, b);
}
TEST(specifier_escape) {
assert_se(w);
puts(w);
- assert_se(streq(w, "xxx a=AAAA b=BBBB e= yyy"));
+ ASSERT_STREQ(w, "xxx a=AAAA b=BBBB e= yyy");
free(w);
r = specifier_printf("boot=%b, host=%H, pretty=%q, version=%v, arch=%a, empty=%e", SIZE_MAX, table, NULL, NULL, &w);
/* /dev/initctl should normally be a symlink to /run/initctl */
if (inode_same("/dev/initctl", "/run/initctl", 0) > 0)
- assert_se(streq(w, "p=/dev/initctl y=/run/initctl Y=/run w=/dev/tty W=/dev"));
+ ASSERT_STREQ(w, "p=/dev/initctl y=/run/initctl Y=/run w=/dev/tty W=/dev");
}
TEST(specifier_real_path_missing_file) {
xsprintf(spec, "%%%c", s->specifier);
r = specifier_printf(spec, SIZE_MAX, specifier_table, NULL, NULL, &resolved);
+ if (s->specifier == 'A' && r == -EUNATCH) /* os-release might be missing in build chroots */
+ continue;
if (s->specifier == 'm' && IN_SET(r, -EUNATCH, -ENOMEDIUM, -ENOPKG)) /* machine-id might be missing in build chroots */
continue;
assert_se(r >= 0);
assert_se(setenv("SYSTEMD_OS_RELEASE", "/dev/null", 1) == 0);
assert_se(specifier_printf("%A-%B-%M-%o-%w-%W", SIZE_MAX, specifier_table, NULL, NULL, &resolved) >= 0);
- assert_se(streq(resolved, "-----"));
+ ASSERT_STREQ(resolved, "-----");
assert_se(setenv("SYSTEMD_OS_RELEASE", "/nosuchfileordirectory", 1) == 0);
assert_se(specifier_printf("%A-%B-%M-%o-%w-%W", SIZE_MAX, specifier_table, NULL, NULL, &resolved) == -EUNATCH);
- assert_se(streq(resolved, "-----"));
+ ASSERT_STREQ(resolved, "-----");
assert_se(unsetenv("SYSTEMD_OS_RELEASE") == 0);
}
/* Verify that we handle anonymous inodes correctly, i.e. those which have no file type */
struct stat st;
- assert_se(fstat(fd, &st) >= 0);
+ ASSERT_OK_ERRNO(fstat(fd, &st));
assert_se((st.st_mode & S_IFMT) == 0);
assert_se(!inode_type_to_string(st.st_mode));
l = strv_parse_nulstr(sb->buf, sb->len);
assert_se(l);
- assert_se(streq(l[0], "")); /* root */
- assert_se(streq(l[1], "waldo"));
- assert_se(streq(l[2], "foo"));
- assert_se(streq(l[3], "bar"));
- assert_se(streq(l[4], "waldorf"));
- assert_se(l[5] == NULL);
+ ASSERT_STREQ(l[0], ""); /* root */
+ ASSERT_STREQ(l[1], "waldo");
+ ASSERT_STREQ(l[2], "foo");
+ ASSERT_STREQ(l[3], "bar");
+ ASSERT_STREQ(l[4], "waldorf");
+ ASSERT_NULL(l[5]);
assert_se(sb->nodes_count == 5); /* root + 4 non-duplicates */
assert_se(sb->dedup_count == 4);
assert_se(g == 15);
assert_se(h == 0);
- assert_se(streq(sb->buf + a, "waldo"));
- assert_se(streq(sb->buf + b, "foo"));
- assert_se(streq(sb->buf + c, "bar"));
- assert_se(streq(sb->buf + d, "waldo"));
- assert_se(streq(sb->buf + e, "aldo"));
- assert_se(streq(sb->buf + f, "do"));
- assert_se(streq(sb->buf + g, "waldorf"));
- assert_se(streq(sb->buf + h, ""));
+ ASSERT_STREQ(sb->buf + a, "waldo");
+ ASSERT_STREQ(sb->buf + b, "foo");
+ ASSERT_STREQ(sb->buf + c, "bar");
+ ASSERT_STREQ(sb->buf + d, "waldo");
+ ASSERT_STREQ(sb->buf + e, "aldo");
+ ASSERT_STREQ(sb->buf + f, "do");
+ ASSERT_STREQ(sb->buf + g, "waldorf");
+ ASSERT_STREQ(sb->buf + h, "");
strbuf_complete(sb);
- assert_se(sb->root == NULL);
+ ASSERT_NULL(sb->root);
}
DEFINE_TEST_MAIN(LOG_INFO);
TEST(string_erase) {
char *x;
x = strdupa_safe("");
- assert_se(streq(string_erase(x), ""));
+ ASSERT_STREQ(string_erase(x), "");
x = strdupa_safe("1");
- assert_se(streq(string_erase(x), ""));
+ ASSERT_STREQ(string_erase(x), "");
x = strdupa_safe("123456789");
- assert_se(streq(string_erase(x), ""));
+ ASSERT_STREQ(string_erase(x), "");
assert_se(x[1] == '\0');
assert_se(x[2] == '\0');
__func__, strnull(*t), strnull(src), l, strnull(expected), yes_no(change));
int r = free_and_strndup(t, src, l);
- assert_se(streq_ptr(*t, expected));
+ ASSERT_STREQ(*t, expected);
assert_se(r == change); /* check that change occurs only when necessary */
}
assert_se(strdup_to_full(NULL, "") == 1);
assert_se(strdup_to_full(&dst, "") == 1);
- assert_se(streq_ptr(dst, ""));
+ ASSERT_STREQ(dst, "");
dst = mfree(dst);
assert_se(strdup_to_full(NULL, "x") == 1);
assert_se(strdup_to_full(&dst, "x") == 1);
- assert_se(streq_ptr(dst, "x"));
+ ASSERT_STREQ(dst, "x");
}
TEST(strdup_to) {
assert_se(strdup_to(&dst, NULL) == 0);
assert_se(strdup_to(&dst, "") == 0);
- assert_se(streq_ptr(dst, ""));
+ ASSERT_STREQ(dst, "");
dst = mfree(dst);
assert_se(strdup_to(&dst, "x") == 0);
- assert_se(streq_ptr(dst, "x"));
+ ASSERT_STREQ(dst, "x");
}
TEST(ascii_strcasecmp_n) {
TEST(cellescape) {
char buf[40];
- assert_se(streq(cellescape(buf, 1, ""), ""));
- assert_se(streq(cellescape(buf, 1, "1"), ""));
- assert_se(streq(cellescape(buf, 1, "12"), ""));
-
- assert_se(streq(cellescape(buf, 2, ""), ""));
- assert_se(streq(cellescape(buf, 2, "1"), "1"));
- assert_se(streq(cellescape(buf, 2, "12"), "."));
- assert_se(streq(cellescape(buf, 2, "123"), "."));
-
- assert_se(streq(cellescape(buf, 3, ""), ""));
- assert_se(streq(cellescape(buf, 3, "1"), "1"));
- assert_se(streq(cellescape(buf, 3, "12"), "12"));
- assert_se(streq(cellescape(buf, 3, "123"), ".."));
- assert_se(streq(cellescape(buf, 3, "1234"), ".."));
-
- assert_se(streq(cellescape(buf, 4, ""), ""));
- assert_se(streq(cellescape(buf, 4, "1"), "1"));
- assert_se(streq(cellescape(buf, 4, "12"), "12"));
- assert_se(streq(cellescape(buf, 4, "123"), "123"));
- assert_se(streq(cellescape(buf, 4, "1234"), is_locale_utf8() ? "…" : "..."));
- assert_se(streq(cellescape(buf, 4, "12345"), is_locale_utf8() ? "…" : "..."));
-
- assert_se(streq(cellescape(buf, 5, ""), ""));
- assert_se(streq(cellescape(buf, 5, "1"), "1"));
- assert_se(streq(cellescape(buf, 5, "12"), "12"));
- assert_se(streq(cellescape(buf, 5, "123"), "123"));
- assert_se(streq(cellescape(buf, 5, "1234"), "1234"));
- assert_se(streq(cellescape(buf, 5, "12345"), is_locale_utf8() ? "1…" : "1..."));
- assert_se(streq(cellescape(buf, 5, "123456"), is_locale_utf8() ? "1…" : "1..."));
-
- assert_se(streq(cellescape(buf, 1, "\020"), ""));
- assert_se(streq(cellescape(buf, 2, "\020"), "."));
- assert_se(streq(cellescape(buf, 3, "\020"), ".."));
- assert_se(streq(cellescape(buf, 4, "\020"), is_locale_utf8() ? "…" : "..."));
- assert_se(streq(cellescape(buf, 5, "\020"), "\\020"));
-
- assert_se(streq(cellescape(buf, 5, "1234\020"), is_locale_utf8() ? "1…" : "1..."));
- assert_se(streq(cellescape(buf, 6, "1234\020"), is_locale_utf8() ? "12…" : "12..."));
- assert_se(streq(cellescape(buf, 7, "1234\020"), is_locale_utf8() ? "123…" : "123..."));
- assert_se(streq(cellescape(buf, 8, "1234\020"), is_locale_utf8() ? "1234…" : "1234..."));
- assert_se(streq(cellescape(buf, 9, "1234\020"), "1234\\020"));
-
- assert_se(streq(cellescape(buf, 1, "\t\n"), ""));
- assert_se(streq(cellescape(buf, 2, "\t\n"), "."));
- assert_se(streq(cellescape(buf, 3, "\t\n"), ".."));
- assert_se(streq(cellescape(buf, 4, "\t\n"), is_locale_utf8() ? "…" : "..."));
- assert_se(streq(cellescape(buf, 5, "\t\n"), "\\t\\n"));
-
- assert_se(streq(cellescape(buf, 5, "1234\t\n"), is_locale_utf8() ? "1…" : "1..."));
- assert_se(streq(cellescape(buf, 6, "1234\t\n"), is_locale_utf8() ? "12…" : "12..."));
- assert_se(streq(cellescape(buf, 7, "1234\t\n"), is_locale_utf8() ? "123…" : "123..."));
- assert_se(streq(cellescape(buf, 8, "1234\t\n"), is_locale_utf8() ? "1234…" : "1234..."));
- assert_se(streq(cellescape(buf, 9, "1234\t\n"), "1234\\t\\n"));
-
- assert_se(streq(cellescape(buf, 4, "x\t\020\n"), is_locale_utf8() ? "…" : "..."));
- assert_se(streq(cellescape(buf, 5, "x\t\020\n"), is_locale_utf8() ? "x…" : "x..."));
- assert_se(streq(cellescape(buf, 6, "x\t\020\n"), is_locale_utf8() ? "x…" : "x..."));
- assert_se(streq(cellescape(buf, 7, "x\t\020\n"), is_locale_utf8() ? "x\\t…" : "x\\t..."));
- assert_se(streq(cellescape(buf, 8, "x\t\020\n"), is_locale_utf8() ? "x\\t…" : "x\\t..."));
- assert_se(streq(cellescape(buf, 9, "x\t\020\n"), is_locale_utf8() ? "x\\t…" : "x\\t..."));
- assert_se(streq(cellescape(buf, 10, "x\t\020\n"), "x\\t\\020\\n"));
-
- assert_se(streq(cellescape(buf, 6, "1\011"), "1\\t"));
- assert_se(streq(cellescape(buf, 6, "1\020"), "1\\020"));
- assert_se(streq(cellescape(buf, 6, "1\020x"), is_locale_utf8() ? "1…" : "1..."));
-
- assert_se(streq(cellescape(buf, 40, "1\020"), "1\\020"));
- assert_se(streq(cellescape(buf, 40, "1\020x"), "1\\020x"));
-
- assert_se(streq(cellescape(buf, 40, "\a\b\f\n\r\t\v\\\"'"), "\\a\\b\\f\\n\\r\\t\\v\\\\\\\"\\'"));
- assert_se(streq(cellescape(buf, 6, "\a\b\f\n\r\t\v\\\"'"), is_locale_utf8() ? "\\a…" : "\\a..."));
- assert_se(streq(cellescape(buf, 7, "\a\b\f\n\r\t\v\\\"'"), is_locale_utf8() ? "\\a…" : "\\a..."));
- assert_se(streq(cellescape(buf, 8, "\a\b\f\n\r\t\v\\\"'"), is_locale_utf8() ? "\\a\\b…" : "\\a\\b..."));
-
- assert_se(streq(cellescape(buf, sizeof buf, "1\020"), "1\\020"));
- assert_se(streq(cellescape(buf, sizeof buf, "1\020x"), "1\\020x"));
+ ASSERT_STREQ(cellescape(buf, 1, ""), "");
+ ASSERT_STREQ(cellescape(buf, 1, "1"), "");
+ ASSERT_STREQ(cellescape(buf, 1, "12"), "");
+
+ ASSERT_STREQ(cellescape(buf, 2, ""), "");
+ ASSERT_STREQ(cellescape(buf, 2, "1"), "1");
+ ASSERT_STREQ(cellescape(buf, 2, "12"), ".");
+ ASSERT_STREQ(cellescape(buf, 2, "123"), ".");
+
+ ASSERT_STREQ(cellescape(buf, 3, ""), "");
+ ASSERT_STREQ(cellescape(buf, 3, "1"), "1");
+ ASSERT_STREQ(cellescape(buf, 3, "12"), "12");
+ ASSERT_STREQ(cellescape(buf, 3, "123"), "..");
+ ASSERT_STREQ(cellescape(buf, 3, "1234"), "..");
+
+ ASSERT_STREQ(cellescape(buf, 4, ""), "");
+ ASSERT_STREQ(cellescape(buf, 4, "1"), "1");
+ ASSERT_STREQ(cellescape(buf, 4, "12"), "12");
+ ASSERT_STREQ(cellescape(buf, 4, "123"), "123");
+ ASSERT_STREQ(cellescape(buf, 4, "1234"), is_locale_utf8() ? "…" : "...");
+ ASSERT_STREQ(cellescape(buf, 4, "12345"), is_locale_utf8() ? "…" : "...");
+
+ ASSERT_STREQ(cellescape(buf, 5, ""), "");
+ ASSERT_STREQ(cellescape(buf, 5, "1"), "1");
+ ASSERT_STREQ(cellescape(buf, 5, "12"), "12");
+ ASSERT_STREQ(cellescape(buf, 5, "123"), "123");
+ ASSERT_STREQ(cellescape(buf, 5, "1234"), "1234");
+ ASSERT_STREQ(cellescape(buf, 5, "12345"), is_locale_utf8() ? "1…" : "1...");
+ ASSERT_STREQ(cellescape(buf, 5, "123456"), is_locale_utf8() ? "1…" : "1...");
+
+ ASSERT_STREQ(cellescape(buf, 1, "\020"), "");
+ ASSERT_STREQ(cellescape(buf, 2, "\020"), ".");
+ ASSERT_STREQ(cellescape(buf, 3, "\020"), "..");
+ ASSERT_STREQ(cellescape(buf, 4, "\020"), is_locale_utf8() ? "…" : "...");
+ ASSERT_STREQ(cellescape(buf, 5, "\020"), "\\020");
+
+ ASSERT_STREQ(cellescape(buf, 5, "1234\020"), is_locale_utf8() ? "1…" : "1...");
+ ASSERT_STREQ(cellescape(buf, 6, "1234\020"), is_locale_utf8() ? "12…" : "12...");
+ ASSERT_STREQ(cellescape(buf, 7, "1234\020"), is_locale_utf8() ? "123…" : "123...");
+ ASSERT_STREQ(cellescape(buf, 8, "1234\020"), is_locale_utf8() ? "1234…" : "1234...");
+ ASSERT_STREQ(cellescape(buf, 9, "1234\020"), "1234\\020");
+
+ ASSERT_STREQ(cellescape(buf, 1, "\t\n"), "");
+ ASSERT_STREQ(cellescape(buf, 2, "\t\n"), ".");
+ ASSERT_STREQ(cellescape(buf, 3, "\t\n"), "..");
+ ASSERT_STREQ(cellescape(buf, 4, "\t\n"), is_locale_utf8() ? "…" : "...");
+ ASSERT_STREQ(cellescape(buf, 5, "\t\n"), "\\t\\n");
+
+ ASSERT_STREQ(cellescape(buf, 5, "1234\t\n"), is_locale_utf8() ? "1…" : "1...");
+ ASSERT_STREQ(cellescape(buf, 6, "1234\t\n"), is_locale_utf8() ? "12…" : "12...");
+ ASSERT_STREQ(cellescape(buf, 7, "1234\t\n"), is_locale_utf8() ? "123…" : "123...");
+ ASSERT_STREQ(cellescape(buf, 8, "1234\t\n"), is_locale_utf8() ? "1234…" : "1234...");
+ ASSERT_STREQ(cellescape(buf, 9, "1234\t\n"), "1234\\t\\n");
+
+ ASSERT_STREQ(cellescape(buf, 4, "x\t\020\n"), is_locale_utf8() ? "…" : "...");
+ ASSERT_STREQ(cellescape(buf, 5, "x\t\020\n"), is_locale_utf8() ? "x…" : "x...");
+ ASSERT_STREQ(cellescape(buf, 6, "x\t\020\n"), is_locale_utf8() ? "x…" : "x...");
+ ASSERT_STREQ(cellescape(buf, 7, "x\t\020\n"), is_locale_utf8() ? "x\\t…" : "x\\t...");
+ ASSERT_STREQ(cellescape(buf, 8, "x\t\020\n"), is_locale_utf8() ? "x\\t…" : "x\\t...");
+ ASSERT_STREQ(cellescape(buf, 9, "x\t\020\n"), is_locale_utf8() ? "x\\t…" : "x\\t...");
+ ASSERT_STREQ(cellescape(buf, 10, "x\t\020\n"), "x\\t\\020\\n");
+
+ ASSERT_STREQ(cellescape(buf, 6, "1\011"), "1\\t");
+ ASSERT_STREQ(cellescape(buf, 6, "1\020"), "1\\020");
+ ASSERT_STREQ(cellescape(buf, 6, "1\020x"), is_locale_utf8() ? "1…" : "1...");
+
+ ASSERT_STREQ(cellescape(buf, 40, "1\020"), "1\\020");
+ ASSERT_STREQ(cellescape(buf, 40, "1\020x"), "1\\020x");
+
+ ASSERT_STREQ(cellescape(buf, 40, "\a\b\f\n\r\t\v\\\"'"), "\\a\\b\\f\\n\\r\\t\\v\\\\\\\"\\'");
+ ASSERT_STREQ(cellescape(buf, 6, "\a\b\f\n\r\t\v\\\"'"), is_locale_utf8() ? "\\a…" : "\\a...");
+ ASSERT_STREQ(cellescape(buf, 7, "\a\b\f\n\r\t\v\\\"'"), is_locale_utf8() ? "\\a…" : "\\a...");
+ ASSERT_STREQ(cellescape(buf, 8, "\a\b\f\n\r\t\v\\\"'"), is_locale_utf8() ? "\\a\\b…" : "\\a\\b...");
+
+ ASSERT_STREQ(cellescape(buf, sizeof buf, "1\020"), "1\\020");
+ ASSERT_STREQ(cellescape(buf, sizeof buf, "1\020x"), "1\\020x");
}
TEST(streq_ptr) {
char *ret, input[] = " hello, waldo. ";
ret = strstrip(input);
- assert_se(streq(ret, "hello, waldo."));
+ ASSERT_STREQ(ret, "hello, waldo.");
}
TEST(strextend) {
_cleanup_free_ char *str = NULL;
assert_se(strextend(&str, NULL));
- assert_se(streq_ptr(str, ""));
+ ASSERT_STREQ(str, "");
assert_se(strextend(&str, "", "0", "", "", "123"));
- assert_se(streq_ptr(str, "0123"));
+ ASSERT_STREQ(str, "0123");
assert_se(strextend(&str, "456", "78", "9"));
- assert_se(streq_ptr(str, "0123456789"));
+ ASSERT_STREQ(str, "0123456789");
}
TEST(strextend_with_separator) {
_cleanup_free_ char *str = NULL;
assert_se(strextend_with_separator(&str, NULL, NULL));
- assert_se(streq_ptr(str, ""));
+ ASSERT_STREQ(str, "");
str = mfree(str);
assert_se(strextend_with_separator(&str, "...", NULL));
- assert_se(streq_ptr(str, ""));
+ ASSERT_STREQ(str, "");
assert_se(strextend_with_separator(&str, "...", NULL));
- assert_se(streq_ptr(str, ""));
+ ASSERT_STREQ(str, "");
str = mfree(str);
assert_se(strextend_with_separator(&str, "xyz", "a", "bb", "ccc"));
- assert_se(streq_ptr(str, "axyzbbxyzccc"));
+ ASSERT_STREQ(str, "axyzbbxyzccc");
str = mfree(str);
assert_se(strextend_with_separator(&str, ",", "start", "", "1", "234"));
- assert_se(streq_ptr(str, "start,,1,234"));
+ ASSERT_STREQ(str, "start,,1,234");
assert_se(strextend_with_separator(&str, ";", "more", "5", "678"));
- assert_se(streq_ptr(str, "start,,1,234;more;5;678"));
+ ASSERT_STREQ(str, "start,,1,234;more;5;678");
}
TEST(strrep) {
three = strrep("waldo", 3);
zero = strrep("waldo", 0);
- assert_se(streq(one, "waldo"));
- assert_se(streq(three, "waldowaldowaldo"));
- assert_se(streq(zero, ""));
+ ASSERT_STREQ(one, "waldo");
+ ASSERT_STREQ(three, "waldowaldowaldo");
+ ASSERT_STREQ(zero, "");
onea = strrepa("waldo", 1);
threea = strrepa("waldo", 3);
- assert_se(streq(onea, "waldo"));
- assert_se(streq(threea, "waldowaldowaldo"));
+ ASSERT_STREQ(onea, "waldo");
+ ASSERT_STREQ(threea, "waldowaldowaldo");
}
TEST(string_has_cc) {
TEST(ascii_strlower) {
char a[] = "AabBcC Jk Ii Od LKJJJ kkd LK";
- assert_se(streq(ascii_strlower(a), "aabbcc jk ii od lkjjj kkd lk"));
+ ASSERT_STREQ(ascii_strlower(a), "aabbcc jk ii od lkjjj kkd lk");
}
TEST(strshorten) {
char *actual;
actual = strjoina("", "foo", "bar");
- assert_se(streq(actual, "foobar"));
+ ASSERT_STREQ(actual, "foobar");
actual = strjoina("foo", "bar", "baz");
- assert_se(streq(actual, "foobarbaz"));
+ ASSERT_STREQ(actual, "foobarbaz");
actual = strjoina("foo", "", "bar", "baz");
- assert_se(streq(actual, "foobarbaz"));
+ ASSERT_STREQ(actual, "foobarbaz");
actual = strjoina("foo");
- assert_se(streq(actual, "foo"));
+ ASSERT_STREQ(actual, "foo");
actual = strjoina(NULL);
- assert_se(streq(actual, ""));
+ ASSERT_STREQ(actual, "");
actual = strjoina(NULL, "foo");
- assert_se(streq(actual, ""));
+ ASSERT_STREQ(actual, "");
actual = strjoina("foo", NULL, "bar");
- assert_se(streq(actual, "foo"));
+ ASSERT_STREQ(actual, "foo");
actual = strjoina("/sys/fs/cgroup/", "dn", "/a/b/c", "/cgroup.procs");
- assert_se(streq(actual, "/sys/fs/cgroup/dn/a/b/c/cgroup.procs"));
+ ASSERT_STREQ(actual, "/sys/fs/cgroup/dn/a/b/c/cgroup.procs");
actual = strjoina("/sys/fs/cgroup/", "dn", NULL, NULL);
- assert_se(streq(actual, "/sys/fs/cgroup/dn"));
+ ASSERT_STREQ(actual, "/sys/fs/cgroup/dn");
}
TEST(strjoin) {
char *actual;
actual = strjoin("", "foo", "bar");
- assert_se(streq(actual, "foobar"));
- mfree(actual);
+ ASSERT_STREQ(actual, "foobar");
+ free(actual);
actual = strjoin("foo", "bar", "baz");
- assert_se(streq(actual, "foobarbaz"));
- mfree(actual);
+ ASSERT_STREQ(actual, "foobarbaz");
+ free(actual);
actual = strjoin("foo", "", "bar", "baz");
- assert_se(streq(actual, "foobarbaz"));
- mfree(actual);
+ ASSERT_STREQ(actual, "foobarbaz");
+ free(actual);
actual = strjoin("foo", NULL);
- assert_se(streq(actual, "foo"));
- mfree(actual);
+ ASSERT_STREQ(actual, "foo");
+ free(actual);
actual = strjoin(NULL, NULL);
- assert_se(streq(actual, ""));
- mfree(actual);
+ ASSERT_STREQ(actual, "");
+ free(actual);
actual = strjoin(NULL, "foo");
- assert_se(streq(actual, ""));
- mfree(actual);
+ ASSERT_STREQ(actual, "");
+ free(actual);
actual = strjoin("foo", NULL, "bar");
- assert_se(streq(actual, "foo"));
- mfree(actual);
+ ASSERT_STREQ(actual, "foo");
+ free(actual);
}
TEST(strcmp_ptr) {
}
assert_se(r > 0);
- assert_se(streq(expected[i++], word));
+ ASSERT_STREQ(expected[i++], word);
}
}
break;
}
- assert_se(streq(word, expected[i++]));
+ ASSERT_STREQ(word, expected[i++]);
printf("<%s>\n", word);
}
- assert_se(expected[i] == NULL);
+ ASSERT_NULL(expected[i]);
}
TEST(foreach_word_quoted) {
char *s, input[] = " hello, waldo. abc";
s = delete_chars(input, WHITESPACE);
- assert_se(streq(s, "hello,waldo.abc"));
+ ASSERT_STREQ(s, "hello,waldo.abc");
assert_se(s == input);
}
input3[] = "abcdef";
s = delete_trailing_chars(input1, WHITESPACE);
- assert_se(streq(s, " \n \r k"));
+ ASSERT_STREQ(s, " \n \r k");
assert_se(s == input1);
s = delete_trailing_chars(input2, "kt");
- assert_se(streq(s, "kkkkthiskkkiskkkaktes"));
+ ASSERT_STREQ(s, "kkkkthiskkkiskkkaktes");
assert_se(s == input2);
s = delete_trailing_chars(input3, WHITESPACE);
- assert_se(streq(s, "abcdef"));
+ ASSERT_STREQ(s, "abcdef");
assert_se(s == input3);
s = delete_trailing_chars(input3, "fe");
- assert_se(streq(s, "abcd"));
+ ASSERT_STREQ(s, "abcd");
assert_se(s == input3);
}
s3[] = "foobar",
s4[] = "";
- assert_se(streq(delete_trailing_chars(s1, "_"), "foobar//"));
- assert_se(streq(delete_trailing_chars(s1, "/"), "foobar"));
- assert_se(streq(delete_trailing_chars(s2, "/"), "foobar"));
- assert_se(streq(delete_trailing_chars(s3, "/"), "foobar"));
- assert_se(streq(delete_trailing_chars(s4, "/"), ""));
+ ASSERT_STREQ(delete_trailing_chars(s1, "_"), "foobar//");
+ ASSERT_STREQ(delete_trailing_chars(s1, "/"), "foobar");
+ ASSERT_STREQ(delete_trailing_chars(s2, "/"), "foobar");
+ ASSERT_STREQ(delete_trailing_chars(s3, "/"), "foobar");
+ ASSERT_STREQ(delete_trailing_chars(s4, "/"), "");
}
TEST(skip_leading_chars) {
input2[] = "kkkkthiskkkiskkkaktestkkk",
input3[] = "abcdef";
- assert_se(streq(skip_leading_chars(input1, WHITESPACE), "k \n \r "));
- assert_se(streq(skip_leading_chars(input2, "k"), "thiskkkiskkkaktestkkk"));
- assert_se(streq(skip_leading_chars(input2, "tk"), "hiskkkiskkkaktestkkk"));
- assert_se(streq(skip_leading_chars(input3, WHITESPACE), "abcdef"));
- assert_se(streq(skip_leading_chars(input3, "bcaef"), "def"));
+ ASSERT_STREQ(skip_leading_chars(input1, WHITESPACE), "k \n \r ");
+ ASSERT_STREQ(skip_leading_chars(input2, "k"), "thiskkkiskkkaktestkkk");
+ ASSERT_STREQ(skip_leading_chars(input2, "tk"), "hiskkkiskkkaktestkkk");
+ ASSERT_STREQ(skip_leading_chars(input3, WHITESPACE), "abcdef");
+ ASSERT_STREQ(skip_leading_chars(input3, "bcaef"), "def");
}
TEST(in_charset) {
assert_se(split_pair("foo=bar", "", &a, &b) == -EINVAL);
assert_se(split_pair("", "=", &a, &b) == -EINVAL);
assert_se(split_pair("foo=bar", "=", &a, &b) >= 0);
- assert_se(streq(a, "foo"));
- assert_se(streq(b, "bar"));
+ ASSERT_STREQ(a, "foo");
+ ASSERT_STREQ(b, "bar");
free(a);
free(b);
assert_se(split_pair("==", "==", &a, &b) >= 0);
- assert_se(streq(a, ""));
- assert_se(streq(b, ""));
+ ASSERT_STREQ(a, "");
+ ASSERT_STREQ(b, "");
free(a);
free(b);
assert_se(split_pair("===", "==", &a, &b) >= 0);
- assert_se(streq(a, ""));
- assert_se(streq(b, "="));
+ ASSERT_STREQ(a, "");
+ ASSERT_STREQ(b, "=");
}
TEST(first_word) {
}
TEST(memory_startswith) {
- assert_se(streq(memory_startswith("", 0, ""), ""));
- assert_se(streq(memory_startswith("", 1, ""), ""));
- assert_se(streq(memory_startswith("x", 2, ""), "x"));
+ ASSERT_STREQ(memory_startswith("", 0, ""), "");
+ ASSERT_STREQ(memory_startswith("", 1, ""), "");
+ ASSERT_STREQ(memory_startswith("x", 2, ""), "x");
assert_se(!memory_startswith("", 1, "x"));
assert_se(!memory_startswith("", 1, "xxxxxxxx"));
- assert_se(streq(memory_startswith("xxx", 4, "x"), "xx"));
- assert_se(streq(memory_startswith("xxx", 4, "xx"), "x"));
- assert_se(streq(memory_startswith("xxx", 4, "xxx"), ""));
+ ASSERT_STREQ(memory_startswith("xxx", 4, "x"), "xx");
+ ASSERT_STREQ(memory_startswith("xxx", 4, "xx"), "x");
+ ASSERT_STREQ(memory_startswith("xxx", 4, "xxx"), "");
assert_se(!memory_startswith("xxx", 4, "xxxx"));
}
TEST(memory_startswith_no_case) {
- assert_se(streq(memory_startswith_no_case("", 0, ""), ""));
- assert_se(streq(memory_startswith_no_case("", 1, ""), ""));
- assert_se(streq(memory_startswith_no_case("x", 2, ""), "x"));
- assert_se(streq(memory_startswith_no_case("X", 2, ""), "X"));
+ ASSERT_STREQ(memory_startswith_no_case("", 0, ""), "");
+ ASSERT_STREQ(memory_startswith_no_case("", 1, ""), "");
+ ASSERT_STREQ(memory_startswith_no_case("x", 2, ""), "x");
+ ASSERT_STREQ(memory_startswith_no_case("X", 2, ""), "X");
assert_se(!memory_startswith_no_case("", 1, "X"));
assert_se(!memory_startswith_no_case("", 1, "xxxxXXXX"));
- assert_se(streq(memory_startswith_no_case("xxx", 4, "X"), "xx"));
- assert_se(streq(memory_startswith_no_case("XXX", 4, "x"), "XX"));
- assert_se(streq(memory_startswith_no_case("XXX", 4, "X"), "XX"));
- assert_se(streq(memory_startswith_no_case("xxx", 4, "XX"), "x"));
- assert_se(streq(memory_startswith_no_case("XXX", 4, "xx"), "X"));
- assert_se(streq(memory_startswith_no_case("XXX", 4, "XX"), "X"));
- assert_se(streq(memory_startswith_no_case("xxx", 4, "XXX"), ""));
- assert_se(streq(memory_startswith_no_case("XXX", 4, "xxx"), ""));
- assert_se(streq(memory_startswith_no_case("XXX", 4, "XXX"), ""));
+ ASSERT_STREQ(memory_startswith_no_case("xxx", 4, "X"), "xx");
+ ASSERT_STREQ(memory_startswith_no_case("XXX", 4, "x"), "XX");
+ ASSERT_STREQ(memory_startswith_no_case("XXX", 4, "X"), "XX");
+ ASSERT_STREQ(memory_startswith_no_case("xxx", 4, "XX"), "x");
+ ASSERT_STREQ(memory_startswith_no_case("XXX", 4, "xx"), "X");
+ ASSERT_STREQ(memory_startswith_no_case("XXX", 4, "XX"), "X");
+ ASSERT_STREQ(memory_startswith_no_case("xxx", 4, "XXX"), "");
+ ASSERT_STREQ(memory_startswith_no_case("XXX", 4, "xxx"), "");
+ ASSERT_STREQ(memory_startswith_no_case("XXX", 4, "XXX"), "");
assert_se(memory_startswith_no_case((char[2]){'x', 'x'}, 2, "xx"));
assert_se(memory_startswith_no_case((char[2]){'x', 'X'}, 2, "xX"));
int k;
assert_se((k = string_truncate_lines(input, n_lines, &b)) >= 0);
- assert_se(streq(b, output));
+ ASSERT_STREQ(b, output);
assert_se(!!k == truncation);
}
int k;
assert_se((k = string_extract_line(input, i, &b)) >= 0);
- assert_se(streq(b ?: input, output));
+ ASSERT_STREQ(b ?: input, output);
assert_se(!!k == more);
}
assert_se(string_contains_word_strv("a b cc", NULL, STRV_MAKE("a", "b"), NULL));
assert_se(string_contains_word_strv("a b cc", NULL, STRV_MAKE("a", "b"), &w));
- assert_se(streq(w, "a"));
+ ASSERT_STREQ(w, "a");
assert_se(!string_contains_word_strv("a b cc", NULL, STRV_MAKE("d"), &w));
- assert_se(w == NULL);
+ ASSERT_NULL(w);
assert_se(string_contains_word_strv("a b cc", NULL, STRV_MAKE("b", "a"), &w));
- assert_se(streq(w, "a"));
+ ASSERT_STREQ(w, "a");
assert_se(string_contains_word_strv("b a b cc", NULL, STRV_MAKE("b", "a", "b"), &w));
- assert_se(streq(w, "b"));
+ ASSERT_STREQ(w, "b");
assert_se(string_contains_word_strv("a b cc", NULL, STRV_MAKE("b", ""), &w));
- assert_se(streq(w, "b"));
+ ASSERT_STREQ(w, "b");
assert_se(!string_contains_word_strv("a b cc", NULL, STRV_MAKE(""), &w));
- assert_se(w == NULL);
+ ASSERT_NULL(w);
assert_se(string_contains_word_strv("a b cc", " ", STRV_MAKE(""), &w));
- assert_se(streq(w, ""));
+ ASSERT_STREQ(w, "");
}
TEST(string_contains_word) {
_cleanup_free_ char *p = NULL;
assert_se(strextendf(&p, "<%i>", 77) >= 0);
- assert_se(streq(p, "<77>"));
+ ASSERT_STREQ(p, "<77>");
assert_se(strextendf(&p, "<%i>", 99) >= 0);
- assert_se(streq(p, "<77><99>"));
+ ASSERT_STREQ(p, "<77><99>");
assert_se(strextendf(&p, "<%80i>", 88) >= 0);
- assert_se(streq(p, "<77><99>< 88>"));
+ ASSERT_STREQ(p, "<77><99>< 88>");
assert_se(strextendf(&p, "<%08x>", 0x1234u) >= 0);
- assert_se(streq(p, "<77><99>< 88><00001234>"));
+ ASSERT_STREQ(p, "<77><99>< 88><00001234>");
p = mfree(p);
assert_se(strextendf_with_separator(&p, ",", "<%i>", 77) >= 0);
- assert_se(streq(p, "<77>"));
+ ASSERT_STREQ(p, "<77>");
assert_se(strextendf_with_separator(&p, ",", "<%i>", 99) >= 0);
- assert_se(streq(p, "<77>,<99>"));
+ ASSERT_STREQ(p, "<77>,<99>");
assert_se(strextendf_with_separator(&p, ",", "<%80i>", 88) >= 0);
- assert_se(streq(p, "<77>,<99>,< 88>"));
+ ASSERT_STREQ(p, "<77>,<99>,< 88>");
assert_se(strextendf_with_separator(&p, ",", "<%08x>", 0x1234u) >= 0);
- assert_se(streq(p, "<77>,<99>,< 88>,<00001234>"));
+ ASSERT_STREQ(p, "<77>,<99>,< 88>,<00001234>");
}
TEST(string_replace_char) {
- assert_se(streq(string_replace_char(strdupa_safe(""), 'a', 'b'), ""));
- assert_se(streq(string_replace_char(strdupa_safe("abc"), 'a', 'b'), "bbc"));
- assert_se(streq(string_replace_char(strdupa_safe("hoge"), 'a', 'b'), "hoge"));
- assert_se(streq(string_replace_char(strdupa_safe("aaaa"), 'a', 'b'), "bbbb"));
- assert_se(streq(string_replace_char(strdupa_safe("aaaa"), 'a', '\t'), "\t\t\t\t"));
+ ASSERT_STREQ(string_replace_char(strdupa_safe(""), 'a', 'b'), "");
+ ASSERT_STREQ(string_replace_char(strdupa_safe("abc"), 'a', 'b'), "bbc");
+ ASSERT_STREQ(string_replace_char(strdupa_safe("hoge"), 'a', 'b'), "hoge");
+ ASSERT_STREQ(string_replace_char(strdupa_safe("aaaa"), 'a', 'b'), "bbbb");
+ ASSERT_STREQ(string_replace_char(strdupa_safe("aaaa"), 'a', '\t'), "\t\t\t\t");
}
TEST(strspn_from_end) {
do { \
_cleanup_free_ char *b = NULL; \
assert_se(make_cstring((x), ELEMENTSOF(x), (mode), &b) == (ret)); \
- assert_se(streq_ptr(b, (expect))); \
+ ASSERT_STREQ(b, (expect)); \
} while(false)
TEST(make_cstring) {
assert_se(!strstrafter(NULL, NULL));
assert_se(!strstrafter("", NULL));
assert_se(!strstrafter(NULL, ""));
- assert_se(streq_ptr(strstrafter("", ""), ""));
+ ASSERT_STREQ(strstrafter("", ""), "");
assert_se(strstrafter(buffer, "a") == buffer + 1);
assert_se(strstrafter(buffer, "") == buffer);
TEST(strextendn) {
_cleanup_free_ char *x = NULL;
- assert_se(streq_ptr(strextendn(&x, NULL, 0), ""));
+ ASSERT_STREQ(strextendn(&x, NULL, 0), "");
x = mfree(x);
- assert_se(streq_ptr(strextendn(&x, "", 0), ""));
+ ASSERT_STREQ(strextendn(&x, "", 0), "");
x = mfree(x);
- assert_se(streq_ptr(strextendn(&x, "xxx", 3), "xxx"));
- assert_se(streq_ptr(strextendn(&x, "xxx", 3), "xxxxxx"));
- assert_se(streq_ptr(strextendn(&x, "...", 1), "xxxxxx."));
- assert_se(streq_ptr(strextendn(&x, "...", 2), "xxxxxx..."));
- assert_se(streq_ptr(strextendn(&x, "...", 3), "xxxxxx......"));
- assert_se(streq_ptr(strextendn(&x, "...", 4), "xxxxxx........."));
+ ASSERT_STREQ(strextendn(&x, "xxx", 3), "xxx");
+ ASSERT_STREQ(strextendn(&x, "xxx", 3), "xxxxxx");
+ ASSERT_STREQ(strextendn(&x, "...", 1), "xxxxxx.");
+ ASSERT_STREQ(strextendn(&x, "...", 2), "xxxxxx...");
+ ASSERT_STREQ(strextendn(&x, "...", 3), "xxxxxx......");
+ ASSERT_STREQ(strextendn(&x, "...", 4), "xxxxxx.........");
x = mfree(x);
}
assert_se(p = strdup("\tFoobar\tbar\twaldo\t"));
assert_se(strip_tab_ansi(&p, NULL, NULL));
fprintf(stdout, "<%s>\n", p);
- assert_se(streq(p, " Foobar bar waldo "));
+ ASSERT_STREQ(p, " Foobar bar waldo ");
free(p);
assert_se(p = strdup(ANSI_HIGHLIGHT "Hello" ANSI_NORMAL ANSI_HIGHLIGHT_RED " world!" ANSI_NORMAL));
assert_se(strip_tab_ansi(&p, NULL, NULL));
fprintf(stdout, "<%s>\n", p);
- assert_se(streq(p, "Hello world!"));
+ ASSERT_STREQ(p, "Hello world!");
free(p);
assert_se(p = strdup("\x1B[\x1B[\t\x1B[" ANSI_HIGHLIGHT "\x1B[" "Hello" ANSI_NORMAL ANSI_HIGHLIGHT_RED " world!" ANSI_NORMAL));
assert_se(strip_tab_ansi(&p, NULL, NULL));
- assert_se(streq(p, "\x1B[\x1B[ \x1B[\x1B[Hello world!"));
+ ASSERT_STREQ(p, "\x1B[\x1B[ \x1B[\x1B[Hello world!");
free(p);
assert_se(p = strdup("\x1B[waldo"));
assert_se(strip_tab_ansi(&p, NULL, NULL));
- assert_se(streq(p, "\x1B[waldo"));
+ ASSERT_STREQ(p, "\x1B[waldo");
free(p);
assert_se(p = strdup("\r\rwaldo"));
assert_se(strip_tab_ansi(&p, NULL, NULL));
- assert_se(streq(p, "\r\rwaldo"));
+ ASSERT_STREQ(p, "\r\rwaldo");
free(p);
assert_se(p = strdup("waldo\r\r"));
assert_se(strip_tab_ansi(&p, NULL, NULL));
- assert_se(streq(p, "waldo"));
+ ASSERT_STREQ(p, "waldo");
free(p);
assert_se(p = strdup("waldo\r\r\n\r\n"));
assert_se(strip_tab_ansi(&p, NULL, NULL));
- assert_se(streq(p, "waldo\n\n"));
+ ASSERT_STREQ(p, "waldo\n\n");
free(p);
assert_se(terminal_urlify_path("/etc/fstab", "i am a fabulous link", &urlified) >= 0);
printf("<%s>\n", p);
assert_se(strip_tab_ansi(&p, NULL, NULL));
printf("<%s>\n", p);
- assert_se(streq(p, "something i am a fabulous link something-else"));
+ ASSERT_STREQ(p, "something i am a fabulous link something-else");
p = mfree(p);
/* Truncate the formatted string in the middle of an ANSI sequence (in which case we shouldn't touch the
*z = 0;
assert_se(qq = strdup(q));
assert_se(strip_tab_ansi(&q, NULL, NULL));
- assert_se(streq(q, qq));
+ ASSERT_STREQ(q, qq);
}
}
assert_se(STARTSWITH_SET("abc", "ax", "abx", "abc"));
assert_se(!STARTSWITH_SET("abc", "ax", "abx", "abcx"));
- assert_se(streq_ptr(STARTSWITH_SET("foobar", "hhh", "kkk", "foo", "zzz"), "bar"));
- assert_se(streq_ptr(STARTSWITH_SET("foobar", "hhh", "kkk", "", "zzz"), "foobar"));
- assert_se(streq_ptr(STARTSWITH_SET("", "hhh", "kkk", "zzz", ""), ""));
+ ASSERT_STREQ(STARTSWITH_SET("foobar", "hhh", "kkk", "foo", "zzz"), "bar");
+ ASSERT_STREQ(STARTSWITH_SET("foobar", "hhh", "kkk", "", "zzz"), "foobar");
+ ASSERT_STREQ(STARTSWITH_SET("", "hhh", "kkk", "zzz", ""), "");
}
static const char* const input_table_multiple[] = {
TEST(strv_join) {
_cleanup_free_ char *p = strv_join((char **)input_table_multiple, ", ");
assert_se(p);
- assert_se(streq(p, "one, two, three"));
+ ASSERT_STREQ(p, "one, two, three");
_cleanup_free_ char *q = strv_join((char **)input_table_multiple, ";");
assert_se(q);
- assert_se(streq(q, "one;two;three"));
+ ASSERT_STREQ(q, "one;two;three");
_cleanup_free_ char *r = strv_join((char **)input_table_multiple, NULL);
assert_se(r);
- assert_se(streq(r, "one two three"));
+ ASSERT_STREQ(r, "one two three");
_cleanup_free_ char *s = strv_join(STRV_MAKE("1", "2", "3,3"), ",");
assert_se(s);
- assert_se(streq(s, "1,2,3,3"));
+ ASSERT_STREQ(s, "1,2,3,3");
_cleanup_free_ char *t = strv_join((char **)input_table_one, ", ");
assert_se(t);
- assert_se(streq(t, "one"));
+ ASSERT_STREQ(t, "one");
_cleanup_free_ char *u = strv_join((char **)input_table_none, ", ");
assert_se(u);
- assert_se(streq(u, ""));
+ ASSERT_STREQ(u, "");
_cleanup_free_ char *v = strv_join((char **)input_table_two_empties, ", ");
assert_se(v);
- assert_se(streq(v, ", "));
+ ASSERT_STREQ(v, ", ");
_cleanup_free_ char *w = strv_join((char **)input_table_one_empty, ", ");
assert_se(w);
- assert_se(streq(w, ""));
+ ASSERT_STREQ(w, "");
}
TEST(strv_join_full) {
_cleanup_free_ char *p = strv_join_full((char **)input_table_multiple, ", ", "foo", false);
assert_se(p);
- assert_se(streq(p, "fooone, footwo, foothree"));
+ ASSERT_STREQ(p, "fooone, footwo, foothree");
_cleanup_free_ char *q = strv_join_full((char **)input_table_multiple, ";", "foo", false);
assert_se(q);
- assert_se(streq(q, "fooone;footwo;foothree"));
+ ASSERT_STREQ(q, "fooone;footwo;foothree");
_cleanup_free_ char *r = strv_join_full(STRV_MAKE("a", "a;b", "a:c"), ";", NULL, true);
assert_se(r);
- assert_se(streq(r, "a;a\\;b;a:c"));
+ ASSERT_STREQ(r, "a;a\\;b;a:c");
_cleanup_free_ char *s = strv_join_full(STRV_MAKE("a", "a;b", "a;;c", ";", ";x"), ";", NULL, true);
assert_se(s);
- assert_se(streq(s, "a;a\\;b;a\\;\\;c;\\;;\\;x"));
+ ASSERT_STREQ(s, "a;a\\;b;a\\;\\;c;\\;;\\;x");
_cleanup_free_ char *t = strv_join_full(STRV_MAKE("a", "a;b", "a:c", ";"), ";", "=", true);
assert_se(t);
- assert_se(streq(t, "=a;=a\\;b;=a:c;=\\;"));
+ ASSERT_STREQ(t, "=a;=a\\;b;=a:c;=\\;");
t = mfree(t);
_cleanup_free_ char *u = strv_join_full((char **)input_table_multiple, NULL, "foo", false);
assert_se(u);
- assert_se(streq(u, "fooone footwo foothree"));
+ ASSERT_STREQ(u, "fooone footwo foothree");
_cleanup_free_ char *v = strv_join_full((char **)input_table_one, ", ", "foo", false);
assert_se(v);
- assert_se(streq(v, "fooone"));
+ ASSERT_STREQ(v, "fooone");
_cleanup_free_ char *w = strv_join_full((char **)input_table_none, ", ", "foo", false);
assert_se(w);
- assert_se(streq(w, ""));
+ ASSERT_STREQ(w, "");
_cleanup_free_ char *x = strv_join_full((char **)input_table_two_empties, ", ", "foo", false);
assert_se(x);
- assert_se(streq(x, "foo, foo"));
+ ASSERT_STREQ(x, "foo, foo");
_cleanup_free_ char *y = strv_join_full((char **)input_table_one_empty, ", ", "foo", false);
assert_se(y);
- assert_se(streq(y, "foo"));
+ ASSERT_STREQ(y, "foo");
}
static void test_strv_unquote_one(const char *quoted, char **list) {
puts(j);
STRV_FOREACH(t, s)
- assert_se(streq(list[i++], *t));
+ ASSERT_STREQ(list[i++], *t);
- assert_se(list[i] == NULL);
+ ASSERT_NULL(list[i]);
}
TEST(strv_unquote) {
log_info("/* %s */", __func__);
r = strv_split_full(&s, quoted, WHITESPACE, EXTRACT_UNQUOTE);
- assert_se(s == NULL);
+ ASSERT_NULL(s);
assert_se(r == -EINVAL);
}
r = strv_split_full(&l, str, ":", EXTRACT_DONT_COALESCE_SEPARATORS);
assert_se(r == (int) strv_length(l));
- assert_se(streq_ptr(l[0], ""));
- assert_se(streq_ptr(l[1], "foo:bar"));
- assert_se(streq_ptr(l[2], ""));
- assert_se(streq_ptr(l[3], "waldo"));
- assert_se(streq_ptr(l[4], ""));
- assert_se(streq_ptr(l[5], NULL));
+ ASSERT_STREQ(l[0], "");
+ ASSERT_STREQ(l[1], "foo:bar");
+ ASSERT_STREQ(l[2], "");
+ ASSERT_STREQ(l[3], "waldo");
+ ASSERT_STREQ(l[4], "");
+ ASSERT_STREQ(l[5], NULL);
}
TEST(strv_split_and_extend_full) {
assert_se(r == (int) strv_length(l));
r = strv_split_and_extend_full(&l, str1, ":", false, EXTRACT_DONT_COALESCE_SEPARATORS);
assert_se(r == (int) strv_length(l));
- assert_se(streq_ptr(l[0], ""));
- assert_se(streq_ptr(l[1], "foo:bar"));
- assert_se(streq_ptr(l[2], ""));
+ ASSERT_STREQ(l[0], "");
+ ASSERT_STREQ(l[1], "foo:bar");
+ ASSERT_STREQ(l[2], "");
r = strv_split_and_extend_full(&l, str2, ":", false, 0);
assert_se(r == (int) strv_length(l));
- assert_se(streq_ptr(l[3], "waldo"));
- assert_se(streq_ptr(l[4], "baz"));
- assert_se(streq_ptr(l[5], NULL));
+ ASSERT_STREQ(l[3], "waldo");
+ ASSERT_STREQ(l[4], "baz");
+ ASSERT_STREQ(l[5], NULL);
}
TEST(strv_split_colon_pairs) {
r = strv_split_colon_pairs(&l, str);
assert_se(r == (int) strv_length(l));
assert_se(r == 12);
- assert_se(streq_ptr(l[0], "one"));
- assert_se(streq_ptr(l[1], "two"));
- assert_se(streq_ptr(l[2], "three"));
- assert_se(streq_ptr(l[3], ""));
- assert_se(streq_ptr(l[4], "four"));
- assert_se(streq_ptr(l[5], "five"));
- assert_se(streq_ptr(l[6], "six"));
- assert_se(streq_ptr(l[7], ""));
- assert_se(streq_ptr(l[8], "seven"));
- assert_se(streq_ptr(l[9], "eight:nine"));
- assert_se(streq_ptr(l[10], "ten:eleven\\"));
- assert_se(streq_ptr(l[11], ""));
- assert_se(streq_ptr(l[12], NULL));
+ ASSERT_STREQ(l[0], "one");
+ ASSERT_STREQ(l[1], "two");
+ ASSERT_STREQ(l[2], "three");
+ ASSERT_STREQ(l[3], "");
+ ASSERT_STREQ(l[4], "four");
+ ASSERT_STREQ(l[5], "five");
+ ASSERT_STREQ(l[6], "six");
+ ASSERT_STREQ(l[7], "");
+ ASSERT_STREQ(l[8], "seven");
+ ASSERT_STREQ(l[9], "eight:nine");
+ ASSERT_STREQ(l[10], "ten:eleven\\");
+ ASSERT_STREQ(l[11], "");
+ ASSERT_STREQ(l[12], NULL);
r = strv_split_colon_pairs(&l, str_inval);
assert_se(r == -EINVAL);
assert_se(l);
STRV_FOREACH(s, l)
- assert_se(streq(*s, input_table_multiple[i++]));
+ ASSERT_STREQ(*s, input_table_multiple[i++]);
}
TEST(strv_split_newlines_full) {
strv_sort((char **)input_table);
- assert_se(streq(input_table[0], "CAPITAL LETTERS FIRST"));
- assert_se(streq(input_table[1], "apple"));
- assert_se(streq(input_table[2], "banana"));
- assert_se(streq(input_table[3], "citrus"));
- assert_se(streq(input_table[4], "durian"));
+ ASSERT_STREQ(input_table[0], "CAPITAL LETTERS FIRST");
+ ASSERT_STREQ(input_table[1], "apple");
+ ASSERT_STREQ(input_table[2], "banana");
+ ASSERT_STREQ(input_table[3], "citrus");
+ ASSERT_STREQ(input_table[4], "durian");
}
TEST(strv_extend_strv_biconcat) {
assert_se(strv_extend_strv_biconcat(&a, "prefix_", (const char* const*) b, "_suffix") >= 0);
- assert_se(streq(a[0], "without"));
- assert_se(streq(a[1], "suffix"));
- assert_se(streq(a[2], "prefix_with_suffix"));
- assert_se(streq(a[3], "prefix_suffix_suffix"));
+ ASSERT_STREQ(a[0], "without");
+ ASSERT_STREQ(a[1], "suffix");
+ ASSERT_STREQ(a[2], "prefix_with_suffix");
+ ASSERT_STREQ(a[3], "prefix_suffix_suffix");
}
TEST(strv_extend_strv_concat) {
assert_se(strv_extend_strv_concat(&a, (const char* const*) b, "_suffix") >= 0);
- assert_se(streq(a[0], "without"));
- assert_se(streq(a[1], "suffix"));
- assert_se(streq(a[2], "with_suffix"));
- assert_se(streq(a[3], "suffix_suffix"));
+ ASSERT_STREQ(a[0], "without");
+ ASSERT_STREQ(a[1], "suffix");
+ ASSERT_STREQ(a[2], "with_suffix");
+ ASSERT_STREQ(a[3], "suffix_suffix");
}
TEST(strv_extend_strv) {
assert_se(strv_extend_strv(&a, b, true) == 3);
- assert_se(streq(a[0], "abc"));
- assert_se(streq(a[1], "def"));
- assert_se(streq(a[2], "ghi"));
- assert_se(streq(a[3], "jkl"));
- assert_se(streq(a[4], "mno"));
- assert_se(streq(a[5], "pqr"));
+ ASSERT_STREQ(a[0], "abc");
+ ASSERT_STREQ(a[1], "def");
+ ASSERT_STREQ(a[2], "ghi");
+ ASSERT_STREQ(a[3], "jkl");
+ ASSERT_STREQ(a[4], "mno");
+ ASSERT_STREQ(a[5], "pqr");
assert_se(strv_length(a) == 6);
assert_se(strv_extend_strv(&n, b, false) >= 0);
- assert_se(streq(n[0], "jkl"));
- assert_se(streq(n[1], "mno"));
- assert_se(streq(n[2], "abc"));
- assert_se(streq(n[3], "pqr"));
+ ASSERT_STREQ(n[0], "jkl");
+ ASSERT_STREQ(n[1], "mno");
+ ASSERT_STREQ(n[2], "abc");
+ ASSERT_STREQ(n[3], "pqr");
assert_se(strv_length(n) == 4);
}
assert_se(strv_extend_with_size(&a, &n, "test3") >= 0);
assert_se(n == 4);
- assert_se(streq(a[0], "test"));
- assert_se(streq(a[1], "test1"));
- assert_se(streq(a[2], "test2"));
- assert_se(streq(a[3], "test3"));
- assert_se(a[4] == NULL);
+ ASSERT_STREQ(a[0], "test");
+ ASSERT_STREQ(a[1], "test1");
+ ASSERT_STREQ(a[2], "test2");
+ ASSERT_STREQ(a[3], "test3");
+ ASSERT_NULL(a[4]);
}
TEST(strv_extend) {
assert_se(strv_extend(&a, "test2") >= 0);
assert_se(strv_extend(&b, "test3") >= 0);
- assert_se(streq(a[0], "test"));
- assert_se(streq(a[1], "test1"));
- assert_se(streq(a[2], "test2"));
- assert_se(streq(b[0], "test3"));
+ ASSERT_STREQ(a[0], "test");
+ ASSERT_STREQ(a[1], "test1");
+ ASSERT_STREQ(a[2], "test2");
+ ASSERT_STREQ(b[0], "test3");
}
TEST(strv_extendf) {
assert_se(strv_extendf(&a, "test2 %s %d %s", "foo", 128, "bar") >= 0);
assert_se(strv_extendf(&b, "test3 %s %s %d", "bar", "foo", 128) >= 0);
- assert_se(streq(a[0], "test"));
- assert_se(streq(a[1], "test1"));
- assert_se(streq(a[2], "test2 foo 128 bar"));
- assert_se(streq(b[0], "test3 bar foo 128"));
+ ASSERT_STREQ(a[0], "test");
+ ASSERT_STREQ(a[1], "test1");
+ ASSERT_STREQ(a[2], "test2 foo 128 bar");
+ ASSERT_STREQ(b[0], "test3 bar foo 128");
}
TEST(strv_foreach) {
assert_se(a);
STRV_FOREACH(check, a)
- assert_se(streq(*check, input_table_multiple[i++]));
+ ASSERT_STREQ(*check, input_table_multiple[i++]);
}
TEST(strv_foreach_backwards) {
assert_se(a);
STRV_FOREACH_BACKWARDS(check, a)
- assert_se(streq_ptr(*check, input_table_multiple[i--]));
+ ASSERT_STREQ(*check, input_table_multiple[i--]);
STRV_FOREACH_BACKWARDS(check, (char**) NULL)
assert_not_reached();
"pair_two", "pair_two",
"pair_three", "pair_three");
STRV_FOREACH_PAIR(x, y, a)
- assert_se(streq(*x, *y));
+ ASSERT_STREQ(*x, *y);
}
static void test_strv_from_stdarg_alloca_one(char **l, const char *first, ...) {
j = strv_from_stdarg_alloca(first);
for (i = 0;; i++) {
- assert_se(streq_ptr(l[i], j[i]));
+ ASSERT_STREQ(l[i], j[i]);
if (!l[i])
break;
_cleanup_strv_free_ char **a = NULL;
assert_se(strv_insert(&a, 0, strdup("first")) == 0);
- assert_se(streq(a[0], "first"));
+ ASSERT_STREQ(a[0], "first");
assert_se(!a[1]);
assert_se(strv_insert(&a, 0, NULL) == 0);
- assert_se(streq(a[0], "first"));
+ ASSERT_STREQ(a[0], "first");
assert_se(!a[1]);
assert_se(strv_insert(&a, 1, strdup("two")) == 0);
- assert_se(streq(a[0], "first"));
- assert_se(streq(a[1], "two"));
+ ASSERT_STREQ(a[0], "first");
+ ASSERT_STREQ(a[1], "two");
assert_se(!a[2]);
assert_se(strv_insert(&a, 4, strdup("tri")) == 0);
- assert_se(streq(a[0], "first"));
- assert_se(streq(a[1], "two"));
- assert_se(streq(a[2], "tri"));
+ ASSERT_STREQ(a[0], "first");
+ ASSERT_STREQ(a[1], "two");
+ ASSERT_STREQ(a[2], "tri");
assert_se(!a[3]);
assert_se(strv_insert(&a, 1, strdup("duo")) == 0);
- assert_se(streq(a[0], "first"));
- assert_se(streq(a[1], "duo"));
- assert_se(streq(a[2], "two"));
- assert_se(streq(a[3], "tri"));
+ ASSERT_STREQ(a[0], "first");
+ ASSERT_STREQ(a[1], "duo");
+ ASSERT_STREQ(a[2], "two");
+ ASSERT_STREQ(a[3], "tri");
assert_se(!a[4]);
}
assert_se(a = strv_new("foo", "bar", "three"));
assert_se(strv_push_prepend(&a, strdup("first")) >= 0);
- assert_se(streq(a[0], "first"));
- assert_se(streq(a[1], "foo"));
- assert_se(streq(a[2], "bar"));
- assert_se(streq(a[3], "three"));
+ ASSERT_STREQ(a[0], "first");
+ ASSERT_STREQ(a[1], "foo");
+ ASSERT_STREQ(a[2], "bar");
+ ASSERT_STREQ(a[3], "three");
assert_se(!a[4]);
assert_se(strv_consume_prepend(&a, strdup("first2")) >= 0);
- assert_se(streq(a[0], "first2"));
- assert_se(streq(a[1], "first"));
- assert_se(streq(a[2], "foo"));
- assert_se(streq(a[3], "bar"));
- assert_se(streq(a[4], "three"));
+ ASSERT_STREQ(a[0], "first2");
+ ASSERT_STREQ(a[1], "first");
+ ASSERT_STREQ(a[2], "foo");
+ ASSERT_STREQ(a[3], "bar");
+ ASSERT_STREQ(a[4], "three");
assert_se(!a[5]);
}
assert_se(strv_push_with_size(&a, &n, j) >= 0);
assert_se(n == 3);
- assert_se(streq_ptr(a[0], "foo"));
- assert_se(streq_ptr(a[1], "a"));
- assert_se(streq_ptr(a[2], "b"));
- assert_se(streq_ptr(a[3], NULL));
+ ASSERT_STREQ(a[0], "foo");
+ ASSERT_STREQ(a[1], "a");
+ ASSERT_STREQ(a[2], "b");
+ ASSERT_STREQ(a[3], NULL);
assert_se(n = strv_length(a));
}
assert_se(j = strdup("b"));
assert_se(strv_push_pair(&a, i, j) >= 0);
- assert_se(streq_ptr(a[0], "foo"));
- assert_se(streq_ptr(a[1], "a"));
- assert_se(streq_ptr(a[2], "b"));
- assert_se(streq_ptr(a[3], NULL));
+ ASSERT_STREQ(a[0], "foo");
+ ASSERT_STREQ(a[1], "a");
+ ASSERT_STREQ(a[2], "b");
+ ASSERT_STREQ(a[3], NULL);
}
TEST(strv_compare) {
b = strv_new("foo");
assert_se(b);
strv_reverse(b);
- assert_se(streq_ptr(b[0], "foo"));
- assert_se(streq_ptr(b[1], NULL));
+ ASSERT_STREQ(b[0], "foo");
+ ASSERT_STREQ(b[1], NULL);
c = strv_new("foo", "bar");
assert_se(c);
strv_reverse(c);
- assert_se(streq_ptr(c[0], "bar"));
- assert_se(streq_ptr(c[1], "foo"));
- assert_se(streq_ptr(c[2], NULL));
+ ASSERT_STREQ(c[0], "bar");
+ ASSERT_STREQ(c[1], "foo");
+ ASSERT_STREQ(c[2], NULL);
d = strv_new("foo", "bar", "waldo");
assert_se(d);
strv_reverse(d);
- assert_se(streq_ptr(d[0], "waldo"));
- assert_se(streq_ptr(d[1], "bar"));
- assert_se(streq_ptr(d[2], "foo"));
- assert_se(streq_ptr(d[3], NULL));
+ ASSERT_STREQ(d[0], "waldo");
+ ASSERT_STREQ(d[1], "bar");
+ ASSERT_STREQ(d[2], "foo");
+ ASSERT_STREQ(d[3], NULL);
}
TEST(strv_shell_escape) {
v = strv_new("foo:bar", "bar,baz", "wal\\do");
assert_se(v);
assert_se(strv_shell_escape(v, ",:"));
- assert_se(streq_ptr(v[0], "foo\\:bar"));
- assert_se(streq_ptr(v[1], "bar\\,baz"));
- assert_se(streq_ptr(v[2], "wal\\\\do"));
- assert_se(streq_ptr(v[3], NULL));
+ ASSERT_STREQ(v[0], "foo\\:bar");
+ ASSERT_STREQ(v[1], "bar\\,baz");
+ ASSERT_STREQ(v[2], "wal\\\\do");
+ ASSERT_STREQ(v[3], NULL);
}
static void test_strv_skip_one(char **a, size_t n, char **b) {
assert_se(strv_extend_n(&v, "waldo", 3) >= 0);
assert_se(strv_extend_n(&v, "piep", 2) >= 0);
- assert_se(streq(v[0], "foo"));
- assert_se(streq(v[1], "bar"));
- assert_se(streq(v[2], "waldo"));
- assert_se(streq(v[3], "waldo"));
- assert_se(streq(v[4], "waldo"));
- assert_se(streq(v[5], "piep"));
- assert_se(streq(v[6], "piep"));
- assert_se(v[7] == NULL);
+ ASSERT_STREQ(v[0], "foo");
+ ASSERT_STREQ(v[1], "bar");
+ ASSERT_STREQ(v[2], "waldo");
+ ASSERT_STREQ(v[3], "waldo");
+ ASSERT_STREQ(v[4], "waldo");
+ ASSERT_STREQ(v[5], "piep");
+ ASSERT_STREQ(v[6], "piep");
+ ASSERT_NULL(v[7]);
v = strv_free(v);
assert_se(strv_extend_n(&v, "foo", 1) >= 0);
assert_se(strv_extend_n(&v, "bar", 0) >= 0);
- assert_se(streq(v[0], "foo"));
- assert_se(v[1] == NULL);
+ ASSERT_STREQ(v[0], "foo");
+ ASSERT_NULL(v[1]);
}
TEST(foreach_string) {
unsigned i = 0;
FOREACH_STRING(x, "foo", "bar", "waldo")
- assert_se(streq_ptr(t[i++], x));
+ ASSERT_STREQ(t[i++], x);
assert_se(i == 3);
FOREACH_STRING(x, "zzz")
- assert_se(streq(x, "zzz"));
+ ASSERT_STREQ(x, "zzz");
}
TEST(strv_fnmatch) {
assert_se(strv_extend_assignment(&v, "MISSING", NULL) >= 0);
assert_se(strv_length(v) == 2);
- assert_se(streq(v[0], "MESSAGE=ABC"));
- assert_se(streq(v[1], "ABC=QER"));
+ ASSERT_STREQ(v[0], "MESSAGE=ABC");
+ ASSERT_STREQ(v[1], "ABC=QER");
}
TEST(strv_copy_n) {
TEST(strv_find_first_field) {
char **haystack = STRV_MAKE("a", "b", "c", "d", "e", "f", "g", "h", "i", "j");
- assert_se(strv_find_first_field(NULL, NULL) == NULL);
- assert_se(strv_find_first_field(NULL, haystack) == NULL);
- assert_se(strv_find_first_field(STRV_MAKE("k", "l", "m", "d", "b"), NULL) == NULL);
- assert_se(strv_find_first_field(STRV_MAKE("k", "l", "m", "d", "b"), haystack) == NULL);
- assert_se(streq_ptr(strv_find_first_field(STRV_MAKE("k", "l", "m", "d", "a", "c"), haystack), "b"));
- assert_se(streq_ptr(strv_find_first_field(STRV_MAKE("k", "l", "m", "d", "c", "a"), haystack), "d"));
- assert_se(streq_ptr(strv_find_first_field(STRV_MAKE("i", "k", "l", "m", "d", "c", "a", "b"), haystack), "j"));
+ ASSERT_NULL(strv_find_first_field(NULL, NULL));
+ ASSERT_NULL(strv_find_first_field(NULL, haystack));
+ ASSERT_NULL(strv_find_first_field(STRV_MAKE("k", "l", "m", "d", "b"), NULL));
+ ASSERT_NULL(strv_find_first_field(STRV_MAKE("k", "l", "m", "d", "b"), haystack));
+ ASSERT_STREQ(strv_find_first_field(STRV_MAKE("k", "l", "m", "d", "a", "c"), haystack), "b");
+ ASSERT_STREQ(strv_find_first_field(STRV_MAKE("k", "l", "m", "d", "c", "a"), haystack), "d");
+ ASSERT_STREQ(strv_find_first_field(STRV_MAKE("i", "k", "l", "m", "d", "c", "a", "b"), haystack), "j");
}
TEST(endswith_strv) {
- assert_se(streq_ptr(endswith_strv("waldo", STRV_MAKE("xxx", "yyy", "ldo", "zzz")), "ldo"));
- assert_se(streq_ptr(endswith_strv("waldo", STRV_MAKE("xxx", "yyy", "zzz")), NULL));
- assert_se(streq_ptr(endswith_strv("waldo", STRV_MAKE("waldo")), "waldo"));
- assert_se(streq_ptr(endswith_strv("waldo", STRV_MAKE("w", "o", "ldo")), "o"));
- assert_se(streq_ptr(endswith_strv("waldo", STRV_MAKE("knurz", "", "waldo")), ""));
+ ASSERT_STREQ(endswith_strv("waldo", STRV_MAKE("xxx", "yyy", "ldo", "zzz")), "ldo");
+ ASSERT_STREQ(endswith_strv("waldo", STRV_MAKE("xxx", "yyy", "zzz")), NULL);
+ ASSERT_STREQ(endswith_strv("waldo", STRV_MAKE("waldo")), "waldo");
+ ASSERT_STREQ(endswith_strv("waldo", STRV_MAKE("w", "o", "ldo")), "o");
+ ASSERT_STREQ(endswith_strv("waldo", STRV_MAKE("knurz", "", "waldo")), "");
}
TEST(strv_extend_many) {
space_left = strpcpy_full(&s, space_left, "r", &truncated);
assert_se(!truncated);
assert_se(space_left == 1);
- assert_se(streq(target, "12345hey hey heywaldobar"));
+ ASSERT_STREQ(target, "12345hey hey heywaldobar");
space_left = strpcpy_full(&s, space_left, "", &truncated);
assert_se(!truncated);
assert_se(space_left == 1);
- assert_se(streq(target, "12345hey hey heywaldobar"));
+ ASSERT_STREQ(target, "12345hey hey heywaldobar");
space_left = strpcpy_full(&s, space_left, "f", &truncated);
assert_se(truncated);
assert_se(space_left == 0);
- assert_se(streq(target, "12345hey hey heywaldobar"));
+ ASSERT_STREQ(target, "12345hey hey heywaldobar");
space_left = strpcpy_full(&s, space_left, "", &truncated);
assert_se(!truncated);
assert_se(space_left == 0);
- assert_se(streq(target, "12345hey hey heywaldobar"));
+ ASSERT_STREQ(target, "12345hey hey heywaldobar");
space_left = strpcpy_full(&s, space_left, "foo", &truncated);
assert_se(truncated);
assert_se(space_left == 0);
- assert_se(streq(target, "12345hey hey heywaldobar"));
+ ASSERT_STREQ(target, "12345hey hey heywaldobar");
}
TEST(strpcpyf) {
space_left = strpcpyf_full(&s, space_left, &truncated, "foo%s", "bar");
assert_se(!truncated);
assert_se(space_left == 3);
- assert_se(streq(target, "space left: 25. foobar"));
+ ASSERT_STREQ(target, "space left: 25. foobar");
space_left = strpcpyf_full(&s, space_left, &truncated, "%i", 42);
assert_se(!truncated);
assert_se(space_left == 1);
- assert_se(streq(target, "space left: 25. foobar42"));
+ ASSERT_STREQ(target, "space left: 25. foobar42");
space_left = strpcpyf_full(&s, space_left, &truncated, "%s", "");
assert_se(!truncated);
assert_se(space_left == 1);
- assert_se(streq(target, "space left: 25. foobar42"));
+ ASSERT_STREQ(target, "space left: 25. foobar42");
space_left = strpcpyf_full(&s, space_left, &truncated, "%c", 'x');
assert_se(truncated);
assert_se(space_left == 0);
- assert_se(streq(target, "space left: 25. foobar42"));
+ ASSERT_STREQ(target, "space left: 25. foobar42");
space_left = strpcpyf_full(&s, space_left, &truncated, "%s", "");
assert_se(!truncated);
assert_se(space_left == 0);
- assert_se(streq(target, "space left: 25. foobar42"));
+ ASSERT_STREQ(target, "space left: 25. foobar42");
space_left = strpcpyf_full(&s, space_left, &truncated, "abc%s", "hoge");
assert_se(truncated);
assert_se(space_left == 0);
- assert_se(streq(target, "space left: 25. foobar42"));
+ ASSERT_STREQ(target, "space left: 25. foobar42");
/* test overflow */
s = target;
space_left = strpcpyf_full(&s, 12, &truncated, "00 left: %i. ", 999);
assert_se(truncated);
- assert_se(streq(target, "00 left: 99"));
+ ASSERT_STREQ(target, "00 left: 99");
assert_se(space_left == 0);
assert_se(target[12] == '2');
}
space_left = strpcpyl_full(&s, space_left, &truncated, "Banana", NULL);
assert_se(!truncated);
assert_se(space_left == 1);
- assert_se(streq(target, "waldo test waldo. Banana"));
+ ASSERT_STREQ(target, "waldo test waldo. Banana");
space_left = strpcpyl_full(&s, space_left, &truncated, "", "", "", NULL);
assert_se(!truncated);
assert_se(space_left == 1);
- assert_se(streq(target, "waldo test waldo. Banana"));
+ ASSERT_STREQ(target, "waldo test waldo. Banana");
space_left = strpcpyl_full(&s, space_left, &truncated, "", "x", "", NULL);
assert_se(truncated);
assert_se(space_left == 0);
- assert_se(streq(target, "waldo test waldo. Banana"));
+ ASSERT_STREQ(target, "waldo test waldo. Banana");
space_left = strpcpyl_full(&s, space_left, &truncated, "hoge", NULL);
assert_se(truncated);
assert_se(space_left == 0);
- assert_se(streq(target, "waldo test waldo. Banana"));
+ ASSERT_STREQ(target, "waldo test waldo. Banana");
}
TEST(strscpy) {
space_left = strscpy_full(target, space_left, "12345", &truncated);
assert_se(!truncated);
- assert_se(streq(target, "12345"));
+ ASSERT_STREQ(target, "12345");
assert_se(space_left == 20);
}
space_left = strscpyl_full(target, space_left, &truncated, "12345", "waldo", "waldo", NULL);
assert_se(!truncated);
- assert_se(streq(target, "12345waldowaldo"));
+ ASSERT_STREQ(target, "12345waldowaldo");
assert_se(space_left == 10);
}
for (i = 0; i < 100; i++)
l = strpcpyf(&p, l, "%u ", i);
- assert_se(streq(b, c));
+ ASSERT_STREQ(b, c);
}
DEFINE_TEST_MAIN(LOG_INFO);
assert_se(sysctl_normalize(t) == t);
log_info("\"%s\" → \"%s\", expected \"%s\"", *s, t, *expected);
- assert_se(streq(t, *expected));
+ ASSERT_STREQ(t, *expected);
}
}
assert_se(sysctl_read("kernel/hostname", &s) >= 0);
assert_se(uname(&u) >= 0);
- assert_se(streq_ptr(s, u.nodename));
+ ASSERT_STREQ(s, u.nodename);
r = sysctl_write("kernel/hostname", s);
assert_se(r >= 0 || ERRNO_IS_PRIVILEGE(r) || r == -EROFS);
}
/* In almost all cases STDIN will match our controlling TTY. Let's verify that and then compare paths */
- assert_se(fstat(STDIN_FILENO, &st) >= 0);
+ ASSERT_OK_ERRNO(fstat(STDIN_FILENO, &st));
if (S_ISCHR(st.st_mode) && st.st_rdev == devnr) {
_cleanup_free_ char *stdin_name = NULL;
override ? ", ignoring." : "");
if (!override) {
assert_se(x / USEC_PER_SEC == y / USEC_PER_SEC);
- assert_se(streq(xx, yy));
+ ASSERT_STREQ(xx, yy);
}
}
x = now(CLOCK_REALTIME) - (1*USEC_PER_YEAR + 1*USEC_PER_MONTH);
assert_se(format_timestamp_relative_full(buf, sizeof(buf), x, CLOCK_REALTIME, true));
log_debug("%s", buf);
- assert_se(streq(buf, "1 year 1 month ago"));
+ ASSERT_STREQ(buf, "1 year 1 month ago");
x = now(CLOCK_MONOTONIC) + (1*USEC_PER_YEAR + 1.5*USEC_PER_MONTH);
assert_se(format_timestamp_relative_full(buf, sizeof(buf), x, CLOCK_MONOTONIC, false));
log_debug("%s", buf);
- assert_se(streq(buf, "1 year 1 month left"));
+ ASSERT_STREQ(buf, "1 year 1 month left");
x = now(CLOCK_REALTIME) - (1*USEC_PER_YEAR + 2*USEC_PER_MONTH);
assert_se(format_timestamp_relative_full(buf, sizeof(buf), x, CLOCK_REALTIME, true));
log_debug("%s", buf);
- assert_se(streq(buf, "1 year 2 months ago"));
+ ASSERT_STREQ(buf, "1 year 2 months ago");
x = now(CLOCK_REALTIME) - (2*USEC_PER_YEAR + 1*USEC_PER_MONTH);
assert_se(format_timestamp_relative_full(buf, sizeof(buf), x, CLOCK_REALTIME, true));
log_debug("%s", buf);
- assert_se(streq(buf, "2 years 1 month ago"));
+ ASSERT_STREQ(buf, "2 years 1 month ago");
x = now(CLOCK_REALTIME) - (2*USEC_PER_YEAR + 2*USEC_PER_MONTH);
assert_se(format_timestamp_relative_full(buf, sizeof(buf), x, CLOCK_REALTIME, true));
log_debug("%s", buf);
- assert_se(streq(buf, "2 years 2 months ago"));
+ ASSERT_STREQ(buf, "2 years 2 months ago");
/* Months and days */
x = now(CLOCK_REALTIME) - (1*USEC_PER_MONTH + 1*USEC_PER_DAY);
assert_se(format_timestamp_relative_full(buf, sizeof(buf), x, CLOCK_REALTIME, true));
log_debug("%s", buf);
- assert_se(streq(buf, "1 month 1 day ago"));
+ ASSERT_STREQ(buf, "1 month 1 day ago");
x = now(CLOCK_REALTIME) - (1*USEC_PER_MONTH + 2*USEC_PER_DAY);
assert_se(format_timestamp_relative_full(buf, sizeof(buf), x, CLOCK_REALTIME, true));
log_debug("%s", buf);
- assert_se(streq(buf, "1 month 2 days ago"));
+ ASSERT_STREQ(buf, "1 month 2 days ago");
x = now(CLOCK_REALTIME) - (2*USEC_PER_MONTH + 1*USEC_PER_DAY);
assert_se(format_timestamp_relative_full(buf, sizeof(buf), x, CLOCK_REALTIME, true));
log_debug("%s", buf);
- assert_se(streq(buf, "2 months 1 day ago"));
+ ASSERT_STREQ(buf, "2 months 1 day ago");
x = now(CLOCK_REALTIME) - (2*USEC_PER_MONTH + 2*USEC_PER_DAY);
assert_se(format_timestamp_relative_full(buf, sizeof(buf), x, CLOCK_REALTIME, true));
log_debug("%s", buf);
- assert_se(streq(buf, "2 months 2 days ago"));
+ ASSERT_STREQ(buf, "2 months 2 days ago");
/* Weeks and days */
x = now(CLOCK_REALTIME) - (1*USEC_PER_WEEK + 1*USEC_PER_DAY);
assert_se(format_timestamp_relative_full(buf, sizeof(buf), x, CLOCK_REALTIME, true));
log_debug("%s", buf);
- assert_se(streq(buf, "1 week 1 day ago"));
+ ASSERT_STREQ(buf, "1 week 1 day ago");
x = now(CLOCK_REALTIME) - (1*USEC_PER_WEEK + 2*USEC_PER_DAY);
assert_se(format_timestamp_relative_full(buf, sizeof(buf), x, CLOCK_REALTIME, true));
log_debug("%s", buf);
- assert_se(streq(buf, "1 week 2 days ago"));
+ ASSERT_STREQ(buf, "1 week 2 days ago");
x = now(CLOCK_REALTIME) - (2*USEC_PER_WEEK + 1*USEC_PER_DAY);
assert_se(format_timestamp_relative_full(buf, sizeof(buf), x, CLOCK_REALTIME, true));
log_debug("%s", buf);
- assert_se(streq(buf, "2 weeks 1 day ago"));
+ ASSERT_STREQ(buf, "2 weeks 1 day ago");
x = now(CLOCK_REALTIME) - (2*USEC_PER_WEEK + 2*USEC_PER_DAY);
assert_se(format_timestamp_relative_full(buf, sizeof(buf), x, CLOCK_REALTIME, true));
log_debug("%s", buf);
- assert_se(streq(buf, "2 weeks 2 days ago"));
+ ASSERT_STREQ(buf, "2 weeks 2 days ago");
}
TEST(format_timestamp_relative) {
x = now(CLOCK_REALTIME) - (1*USEC_PER_YEAR + 1*USEC_PER_MONTH);
assert_se(format_timestamp_relative(buf, sizeof(buf), x));
log_debug("%s", buf);
- assert_se(streq(buf, "1 year 1 month ago"));
+ ASSERT_STREQ(buf, "1 year 1 month ago");
x = now(CLOCK_REALTIME) - (1*USEC_PER_YEAR + 2*USEC_PER_MONTH);
assert_se(format_timestamp_relative(buf, sizeof(buf), x));
log_debug("%s", buf);
- assert_se(streq(buf, "1 year 2 months ago"));
+ ASSERT_STREQ(buf, "1 year 2 months ago");
x = now(CLOCK_REALTIME) - (2*USEC_PER_YEAR + 1*USEC_PER_MONTH);
assert_se(format_timestamp_relative(buf, sizeof(buf), x));
log_debug("%s", buf);
- assert_se(streq(buf, "2 years 1 month ago"));
+ ASSERT_STREQ(buf, "2 years 1 month ago");
x = now(CLOCK_REALTIME) - (2*USEC_PER_YEAR + 2*USEC_PER_MONTH);
assert_se(format_timestamp_relative(buf, sizeof(buf), x));
log_debug("%s", buf);
- assert_se(streq(buf, "2 years 2 months ago"));
+ ASSERT_STREQ(buf, "2 years 2 months ago");
/* Months and days */
x = now(CLOCK_REALTIME) - (1*USEC_PER_MONTH + 1*USEC_PER_DAY);
assert_se(format_timestamp_relative(buf, sizeof(buf), x));
log_debug("%s", buf);
- assert_se(streq(buf, "1 month 1 day ago"));
+ ASSERT_STREQ(buf, "1 month 1 day ago");
x = now(CLOCK_REALTIME) - (1*USEC_PER_MONTH + 2*USEC_PER_DAY);
assert_se(format_timestamp_relative(buf, sizeof(buf), x));
log_debug("%s", buf);
- assert_se(streq(buf, "1 month 2 days ago"));
+ ASSERT_STREQ(buf, "1 month 2 days ago");
x = now(CLOCK_REALTIME) - (2*USEC_PER_MONTH + 1*USEC_PER_DAY);
assert_se(format_timestamp_relative(buf, sizeof(buf), x));
log_debug("%s", buf);
- assert_se(streq(buf, "2 months 1 day ago"));
+ ASSERT_STREQ(buf, "2 months 1 day ago");
x = now(CLOCK_REALTIME) - (2*USEC_PER_MONTH + 2*USEC_PER_DAY);
assert_se(format_timestamp_relative(buf, sizeof(buf), x));
log_debug("%s", buf);
- assert_se(streq(buf, "2 months 2 days ago"));
+ ASSERT_STREQ(buf, "2 months 2 days ago");
/* Weeks and days */
x = now(CLOCK_REALTIME) - (1*USEC_PER_WEEK + 1*USEC_PER_DAY);
assert_se(format_timestamp_relative(buf, sizeof(buf), x));
log_debug("%s", buf);
- assert_se(streq(buf, "1 week 1 day ago"));
+ ASSERT_STREQ(buf, "1 week 1 day ago");
x = now(CLOCK_REALTIME) - (1*USEC_PER_WEEK + 2*USEC_PER_DAY);
assert_se(format_timestamp_relative(buf, sizeof(buf), x));
log_debug("%s", buf);
- assert_se(streq(buf, "1 week 2 days ago"));
+ ASSERT_STREQ(buf, "1 week 2 days ago");
x = now(CLOCK_REALTIME) - (2*USEC_PER_WEEK + 1*USEC_PER_DAY);
assert_se(format_timestamp_relative(buf, sizeof(buf), x));
log_debug("%s", buf);
- assert_se(streq(buf, "2 weeks 1 day ago"));
+ ASSERT_STREQ(buf, "2 weeks 1 day ago");
x = now(CLOCK_REALTIME) - (2*USEC_PER_WEEK + 2*USEC_PER_DAY);
assert_se(format_timestamp_relative(buf, sizeof(buf), x));
log_debug("%s", buf);
- assert_se(streq(buf, "2 weeks 2 days ago"));
+ ASSERT_STREQ(buf, "2 weeks 2 days ago");
}
static void test_format_timestamp_one(usec_t val, TimestampStyle style, const char *result) {
const char *t;
t = format_timestamp_style(buf, sizeof(buf), val, style);
- assert_se(streq_ptr(t, result));
+ ASSERT_STREQ(t, result);
}
TEST(format_timestamp_range) {
assert_se(setenv("TZ", ":UTC", 1) >= 0);
assert_se(in_utc_timezone());
- assert_se(streq(tzname[0], "UTC"));
- assert_se(streq(tzname[1], "UTC"));
+ ASSERT_STREQ(tzname[0], "UTC");
+ ASSERT_STREQ(tzname[1], "UTC");
assert_se(timezone == 0);
assert_se(daylight == 0);
assert_se(setenv("TZ", ":Europe/Berlin", 1) >= 0);
assert_se(!in_utc_timezone());
- assert_se(streq(tzname[0], "CET"));
- assert_se(streq(tzname[1], "CEST"));
+ ASSERT_STREQ(tzname[0], "CET");
+ ASSERT_STREQ(tzname[1], "CEST");
assert_se(set_unset_env("TZ", tz, true) == 0);
tzset();
s = FORMAT_TIMESTAMP_STYLE(x, TIMESTAMP_UTC);
assert_se(parse_timestamp(s, &y) >= 0);
log_debug("%s -> " USEC_FMT " -> %s -> " USEC_FMT, utc, x, s, y);
- assert_se(streq(s, utc));
+ ASSERT_STREQ(s, utc);
assert_se(x == y);
assert_se(parse_timestamp(pretty, &y) >= 0);
s = FORMAT_TIMESTAMP_STYLE(y, TIMESTAMP_PRETTY);
assert_se(parse_timestamp(s, &z) >= 0);
log_debug("%s -> " USEC_FMT " -> %s -> " USEC_FMT, pretty, y, s, z);
- assert_se(streq(s, pretty));
+ ASSERT_STREQ(s, pretty);
assert_se(x == y);
assert_se(x == z);
}
const char *suffix;
assert_se(suffix = startswith(s, expect));
- assert_se(streq(suffix, "XXXXXX"));
+ ASSERT_STREQ(suffix, "XXXXXX");
}
assert_se(ret == r);
}
assert_se(link_tmpfile(fd, tmp, d, /* flags= */ 0) >= 0);
assert_se(read_one_line_file(d, &line) >= 0);
- assert_se(streq(line, "foobar"));
+ ASSERT_STREQ(line, "foobar");
fd = safe_close(fd);
tmp = mfree(tmp);
line = mfree(line);
assert_se(read_one_line_file(d, &line) >= 0);
- assert_se(streq(line, "waumiau"));
+ ASSERT_STREQ(line, "waumiau");
assert_se(unlink(d) >= 0);
}
tpm2_tpms_pcr_selection_from_mask(mask, hash, &s);
_cleanup_free_ char *tpms_str = tpm2_tpms_pcr_selection_to_string(&s);
- assert_se(streq(tpms_str, expected_str));
+ ASSERT_STREQ(tpms_str, expected_str);
assert_se(tpm2_tpms_pcr_selection_weight(&s) == expected_weight);
assert_se(tpm2_tpms_pcr_selection_is_empty(&s) == (expected_weight == 0));
assert_se(l.count == expected_count);
_cleanup_free_ char *tpml_str = tpm2_tpml_pcr_selection_to_string(&l);
- assert_se(streq(tpml_str, expected_str));
+ ASSERT_STREQ(tpml_str, expected_str);
assert_se(tpm2_tpml_pcr_selection_weight(&l) == expected_weight);
assert_se(tpm2_tpml_pcr_selection_is_empty(&l) == (expected_weight == 0));
assert_se(tpm2_parse_pcr_argument("1,2=123456abc", &v, &n_v) < 0);
assert_se(tpm2_parse_pcr_argument("1,2:invalid", &v, &n_v) < 0);
assert_se(tpm2_parse_pcr_argument("1:sha1=invalid", &v, &n_v) < 0);
- assert_se(v == NULL);
+ ASSERT_NULL(v);
assert_se(n_v == 0);
check_parse_pcr_argument_to_mask("", 0x0);
assert_se(result);
r = udev_replace_whitespace(str, result, len);
assert_se((size_t) r == strlen(expected));
- assert_se(streq(result, expected));
+ ASSERT_STREQ(result, expected);
}
static void test_udev_replace_whitespace_one(const char *str, const char *expected) {
assert_se(!uid_range_covers(p, UINT32_MAX, 1));
assert_se(!uid_range_covers(p, UINT32_MAX - 10, 11));
+ assert_se(uid_range_entries(p) == 0);
+ assert_se(uid_range_size(p) == 0);
+ assert_se(uid_range_is_empty(p));
+
assert_se(uid_range_add_str(&p, "500-999") >= 0);
assert_se(p);
- assert_se(p->n_entries == 1);
+ assert_se(uid_range_entries(p) == 1);
+ assert_se(uid_range_size(p) == 500);
+ assert_se(!uid_range_is_empty(p));
assert_se(p->entries[0].start == 500);
assert_se(p->entries[0].nr == 500);
assert_se(uid_range_next_lower(p, &search) == -EBUSY);
assert_se(uid_range_add_str(&p, "1000") >= 0);
- assert_se(p->n_entries == 1);
+ assert_se(uid_range_entries(p) == 1);
assert_se(p->entries[0].start == 500);
assert_se(p->entries[0].nr == 501);
assert_se(uid_range_add_str(&p, "30-40") >= 0);
- assert_se(p->n_entries == 2);
+ assert_se(uid_range_entries(p) == 2);
+ assert_se(uid_range_size(p) == 500 + 1 + 11);
+ assert_se(!uid_range_is_empty(p));
assert_se(p->entries[0].start == 30);
assert_se(p->entries[0].nr == 11);
assert_se(p->entries[1].start == 500);
assert_se(p->entries[1].nr == 501);
assert_se(uid_range_add_str(&p, "60-70") >= 0);
- assert_se(p->n_entries == 3);
+ assert_se(uid_range_entries(p) == 3);
+ assert_se(uid_range_size(p) == 500 + 1 + 11 + 11);
+ assert_se(!uid_range_is_empty(p));
assert_se(p->entries[0].start == 30);
assert_se(p->entries[0].nr == 11);
assert_se(p->entries[1].start == 60);
assert_se(p->entries[2].nr == 501);
assert_se(uid_range_add_str(&p, "20-2000") >= 0);
- assert_se(p->n_entries == 1);
+ assert_se(uid_range_entries(p) == 1);
+ assert_se(uid_range_size(p) == 1981);
assert_se(p->entries[0].start == 20);
assert_se(p->entries[0].nr == 1981);
assert_se(uid_range_add_str(&p, "2002") >= 0);
- assert_se(p->n_entries == 2);
+ assert_se(uid_range_entries(p) == 2);
+ assert_se(uid_range_size(p) == 1982);
assert_se(p->entries[0].start == 20);
assert_se(p->entries[0].nr == 1981);
assert_se(p->entries[1].start == 2002);
assert_se(p->entries[1].nr == 1);
+ _cleanup_(uid_range_freep) UIDRange *q = NULL;
+ assert_se(!uid_range_equal(p, q));
+ assert_se(uid_range_add_str(&q, "20-2000") >= 0);
+ assert_se(!uid_range_equal(p, q));
+ assert_se(uid_range_add_str(&q, "2002") >= 0);
+ assert_se(uid_range_equal(p, q));
+
assert_se(uid_range_add_str(&p, "2001") >= 0);
- assert_se(p->n_entries == 1);
+ assert_se(uid_range_entries(p) == 1);
+ assert_se(uid_range_size(p) == 1983);
assert_se(p->entries[0].start == 20);
assert_se(p->entries[0].nr == 1983);
+
+ assert_se(uid_range_add_str(&q, "2001") >= 0);
+ assert_se(uid_range_equal(p, q));
}
TEST(load_userns) {
_cleanup_fclose_ FILE *f = NULL;
int r;
- r = uid_range_load_userns(&p, NULL);
+ r = uid_range_load_userns(NULL, UID_RANGE_USERNS_INSIDE, &p);
if (ERRNO_IS_NEG_NOT_SUPPORTED(r))
return;
p = uid_range_free(p);
- assert_se(uid_range_load_userns(&p, fn) >= 0);
+ assert_se(uid_range_load_userns(fn, UID_RANGE_USERNS_INSIDE, &p) >= 0);
assert_se(uid_range_contains(p, 0));
assert_se(uid_range_contains(p, 19));
TEST(runlevel_to_target) {
in_initrd_force(false);
- assert_se(streq_ptr(runlevel_to_target(NULL), NULL));
- assert_se(streq_ptr(runlevel_to_target("unknown-runlevel"), NULL));
- assert_se(streq_ptr(runlevel_to_target("rd.unknown-runlevel"), NULL));
- assert_se(streq_ptr(runlevel_to_target("3"), SPECIAL_MULTI_USER_TARGET));
- assert_se(streq_ptr(runlevel_to_target("rd.rescue"), NULL));
+ ASSERT_STREQ(runlevel_to_target(NULL), NULL);
+ ASSERT_STREQ(runlevel_to_target("unknown-runlevel"), NULL);
+ ASSERT_STREQ(runlevel_to_target("rd.unknown-runlevel"), NULL);
+ ASSERT_STREQ(runlevel_to_target("3"), SPECIAL_MULTI_USER_TARGET);
+ ASSERT_STREQ(runlevel_to_target("rd.rescue"), NULL);
in_initrd_force(true);
- assert_se(streq_ptr(runlevel_to_target(NULL), NULL));
- assert_se(streq_ptr(runlevel_to_target("unknown-runlevel"), NULL));
- assert_se(streq_ptr(runlevel_to_target("rd.unknown-runlevel"), NULL));
- assert_se(streq_ptr(runlevel_to_target("3"), NULL));
- assert_se(streq_ptr(runlevel_to_target("rd.rescue"), SPECIAL_RESCUE_TARGET));
+ ASSERT_STREQ(runlevel_to_target(NULL), NULL);
+ ASSERT_STREQ(runlevel_to_target("unknown-runlevel"), NULL);
+ ASSERT_STREQ(runlevel_to_target("rd.unknown-runlevel"), NULL);
+ ASSERT_STREQ(runlevel_to_target("3"), NULL);
+ ASSERT_STREQ(runlevel_to_target("rd.rescue"), SPECIAL_RESCUE_TARGET);
}
static int intro(void) {
_cleanup_free_ char *t = NULL;
assert_se(unit_name_replace_instance(pattern, repl, &t) == ret);
puts(strna(t));
- assert_se(streq_ptr(t, expected));
+ ASSERT_STREQ(t, expected);
}
TEST(unit_name_replace_instance) {
assert_se(unit_name_from_path(path, suffix, &t) == ret);
puts(strna(t));
- assert_se(streq_ptr(t, expected));
+ ASSERT_STREQ(t, expected);
if (t) {
_cleanup_free_ char *k = NULL;
assert_se(unit_name_from_path_instance(pattern, path, suffix, &t) == ret);
puts(strna(t));
- assert_se(streq_ptr(t, expected));
+ ASSERT_STREQ(t, expected);
if (t) {
_cleanup_free_ char *k = NULL, *v = NULL;
_cleanup_free_ char *p = NULL;
assert_se(unit_name_to_path(unit, &p) == ret);
- assert_se(streq_ptr(path, p));
+ ASSERT_STREQ(path, p);
}
TEST(unit_name_to_path) {
assert_se(r == ret);
puts(strna(t));
- assert_se(streq_ptr(t, expect));
+ ASSERT_STREQ(t, expect);
if (t) {
_cleanup_free_ char *k = NULL;
(allow_globs && string_is_glob(t)));
assert_se(unit_name_mangle(t, (allow_globs * UNIT_NAME_MANGLE_GLOB) | UNIT_NAME_MANGLE_WARN, &k) == 0);
- assert_se(streq_ptr(t, k));
+ ASSERT_STREQ(t, k);
}
}
log_debug("%s: %s -> %d, %s", __func__, arg, r, strnull(s));
assert_se(r == expected);
- assert_se(streq_ptr(s, expected_name));
+ ASSERT_STREQ(s, expected_name);
}
TEST(unit_name_mangle_with_suffix) {
char *t;
assert_se(unit_name_change_suffix("foo.mount", ".service", &t) == 0);
- assert_se(streq(t, "foo.service"));
+ ASSERT_STREQ(t, "foo.service");
free(t);
assert_se(unit_name_change_suffix("foo@stuff.service", ".socket", &t) == 0);
- assert_se(streq(t, "foo@stuff.socket"));
+ ASSERT_STREQ(t, "foo@stuff.socket");
free(t);
}
char *t;
assert_se(unit_name_build("foo", "bar", ".service", &t) == 0);
- assert_se(streq(t, "foo@bar.service"));
+ ASSERT_STREQ(t, "foo@bar.service");
free(t);
assert_se(unit_name_build("fo0-stUff_b", "bar", ".mount", &t) == 0);
- assert_se(streq(t, "fo0-stUff_b@bar.mount"));
+ ASSERT_STREQ(t, "fo0-stUff_b@bar.mount");
free(t);
assert_se(unit_name_build("foo", NULL, ".service", &t) == 0);
- assert_se(streq(t, "foo.service"));
+ ASSERT_STREQ(t, "foo.service");
free(t);
}
free(b);
assert_se(slice_build_subslice(a, "foobar", &b) >= 0);
free(a);
- assert_se(streq(b, "foo-bar-barfoo-foobar.slice"));
+ ASSERT_STREQ(b, "foo-bar-barfoo-foobar.slice");
free(b);
assert_se(slice_build_subslice("foo.service", "bar", &a) < 0);
_cleanup_free_ char *s = NULL;
assert_se(slice_build_parent_slice(name, &s) == ret);
- assert_se(streq_ptr(s, expect));
+ ASSERT_STREQ(s, expect);
}
TEST(build_parent_slice) {
r = unit_name_to_instance("foo@bar.service", &instance);
assert_se(r == UNIT_NAME_INSTANCE);
- assert_se(streq(instance, "bar"));
+ ASSERT_STREQ(instance, "bar");
free(instance);
r = unit_name_to_instance("foo@.service", &instance);
assert_se(r == UNIT_NAME_TEMPLATE);
- assert_se(streq(instance, ""));
+ ASSERT_STREQ(instance, "");
free(instance);
r = unit_name_to_instance("fo0-stUff_b@b.service", &instance);
assert_se(r == UNIT_NAME_INSTANCE);
- assert_se(streq(instance, "b"));
+ ASSERT_STREQ(instance, "b");
free(instance);
r = unit_name_to_instance("foo.service", &instance);
r = unit_name_escape("ab+-c.a/bc@foo.service");
assert_se(r);
- assert_se(streq(r, "ab\\x2b\\x2dc.a-bc\\x40foo.service"));
+ ASSERT_STREQ(r, "ab\\x2b\\x2dc.a-bc\\x40foo.service");
}
static void test_u_n_t_one(const char *name, const char *expected, int ret) {
assert_se(unit_name_template(name, &f) == ret);
printf("got: %s, expected: %s\n", strna(f), strna(expected));
- assert_se(streq_ptr(f, expected));
+ ASSERT_STREQ(f, expected);
}
TEST(unit_name_template) {
_cleanup_free_ char *p = NULL;
assert_se(unit_name_path_unescape(name, &p) == ret);
- assert_se(streq_ptr(path, p));
+ ASSERT_STREQ(path, p);
}
TEST(unit_name_path_unescape) {
_cleanup_free_ char *k = NULL;
assert_se(unit_name_to_prefix(input, &k) == ret);
- assert_se(streq_ptr(k, output));
+ ASSERT_STREQ(k, output);
}
TEST(unit_name_to_prefix) {
_cleanup_free_ char *k = NULL;
assert_se(unit_name_from_dbus_path(input, &k) == ret);
- assert_se(streq_ptr(k, output));
+ ASSERT_STREQ(k, output);
}
TEST(unit_name_from_dbus_path) {
log_info("(skipping detailed tests because nobody is not synthesized)");
return;
}
- assert_se(streq_ptr(t, name));
+ ASSERT_STREQ(t, name);
}
TEST(uid_to_name) {
log_info("(skipping detailed tests because nobody is not synthesized)");
return;
}
- assert_se(streq_ptr(t, name));
+ ASSERT_STREQ(t, name);
}
TEST(gid_to_name) {
return;
}
assert_se(r == 0);
- assert_se(streq_ptr(id, name));
+ ASSERT_STREQ(id, name);
assert_se(ruid == uid);
assert_se(rgid == gid);
assert_se(path_equal(rhome, home));
return;
}
assert_se(r == 0);
- assert_se(streq_ptr(id, name));
+ ASSERT_STREQ(id, name);
assert_se(rgid == gid);
}
_cleanup_free_ char *p = NULL;
assert_se(p = mangle_gecos(input));
- assert_se(streq(p, expected));
+ ASSERT_STREQ(p, expected);
assert_se(valid_gecos(p));
}
r = utf8_to_ascii(s, '*', &ans);
log_debug("\"%s\" → %d/\"%s\" (expected %d/\"%s\")", s, r, strnull(ans), r_expected, strnull(expected));
assert_se(r == r_expected);
- assert_se(streq_ptr(ans, expected));
+ ASSERT_STREQ(ans, expected);
}
TEST(utf8_to_ascii) {
b = utf16_to_utf8(a, SIZE_MAX);
assert_se(b);
- assert_se(streq(p, b));
+ ASSERT_STREQ(p, b);
}
}
#include "varlink-io.systemd.Credentials.h"
#include "varlink-io.systemd.Journal.h"
#include "varlink-io.systemd.ManagedOOM.h"
+#include "varlink-io.systemd.MountFileSystem.h"
+#include "varlink-io.systemd.NamespaceResource.h"
#include "varlink-io.systemd.Network.h"
#include "varlink-io.systemd.PCRExtend.h"
#include "varlink-io.systemd.PCRLock.h"
-#include "varlink-io.systemd.Resolve.Monitor.h"
#include "varlink-io.systemd.Resolve.h"
+#include "varlink-io.systemd.Resolve.Monitor.h"
#include "varlink-io.systemd.UserDatabase.h"
#include "varlink-io.systemd.oom.h"
#include "varlink-io.systemd.service.h"
assert_se(varlink_idl_parse(text, NULL, NULL, &parsed) >= 0);
assert_se(varlink_idl_consistent(parsed, LOG_ERR) >= 0);
assert_se(varlink_idl_format(parsed, &text2) >= 0);
- assert_se(streq(text, text2));
+ ASSERT_STREQ(text, text2);
}
TEST(parse_format) {
print_separator();
test_parse_format_one(&vl_interface_io_systemd_UserDatabase);
print_separator();
+ test_parse_format_one(&vl_interface_io_systemd_NamespaceResource);
+ print_separator();
test_parse_format_one(&vl_interface_io_systemd_Journal);
print_separator();
test_parse_format_one(&vl_interface_io_systemd_Resolve);
print_separator();
test_parse_format_one(&vl_interface_io_systemd_ManagedOOM);
print_separator();
+ test_parse_format_one(&vl_interface_io_systemd_MountFileSystem);
+ print_separator();
test_parse_format_one(&vl_interface_io_systemd_Network);
print_separator();
test_parse_format_one(&vl_interface_io_systemd_oom);
JSON_BUILD_PAIR_UNSIGNED("foo", 8),
JSON_BUILD_PAIR_UNSIGNED("bar", 9),
JSON_BUILD_PAIR_STRING("zzz", "pfft"))) >= 0);
- assert_se(streq_ptr(error_id, VARLINK_ERROR_INVALID_PARAMETER));
+ ASSERT_STREQ(error_id, VARLINK_ERROR_INVALID_PARAMETER);
assert_se(varlink_callb(v, "xyz.TestMethod", &reply, &error_id,
JSON_BUILD_OBJECT(
JSON_BUILD_PAIR_BOOLEAN("foo", true),
JSON_BUILD_PAIR_UNSIGNED("bar", 9))) >= 0);
- assert_se(streq_ptr(error_id, VARLINK_ERROR_INVALID_PARAMETER));
+ ASSERT_STREQ(error_id, VARLINK_ERROR_INVALID_PARAMETER);
assert_se(varlink_send(v, "xyz.Done", NULL) >= 0);
assert_se(varlink_flush(v) >= 0);
if (!a)
return varlink_error(link, "io.test.BadParameters", NULL);
- assert_se(streq_ptr(json_variant_string(a), "whoop"));
+ ASSERT_STREQ(json_variant_string(a), "whoop");
int xx = varlink_peek_fd(link, 0),
yy = varlink_peek_fd(link, 1),
* be talking to an overloaded server */
log_debug("Over reply triggered with error: %s", strna(error_id));
- assert_se(streq(error_id, VARLINK_ERROR_DISCONNECTED));
+ ASSERT_STREQ(error_id, VARLINK_ERROR_DISCONNECTED);
sd_event_exit(varlink_get_event(link), 0);
return 0;
test_fd(fd5, "wuff", 4);
assert_se(varlink_callb(c, "io.test.IDontExist", &o, &e, JSON_BUILD_OBJECT(JSON_BUILD_PAIR("x", JSON_BUILD_REAL(5.5)))) >= 0);
- assert_se(streq_ptr(json_variant_string(json_variant_by_key(o, "method")), "io.test.IDontExist"));
- assert_se(streq(e, VARLINK_ERROR_METHOD_NOT_FOUND));
+ ASSERT_STREQ(json_variant_string(json_variant_by_key(o, "method")), "io.test.IDontExist");
+ ASSERT_STREQ(e, VARLINK_ERROR_METHOD_NOT_FOUND);
flood_test(arg);
if (IN_SET(native_architecture(), ARCHITECTURE_X86, ARCHITECTURE_X86_64)) {
assert_se(path_pick(NULL, AT_FDCWD, pp, &filter, PICK_ARCHITECTURE|PICK_TRIES, &result) > 0);
assert_se(S_ISREG(result.st.st_mode));
- assert_se(streq_ptr(result.version, "99"));
+ ASSERT_STREQ(result.version, "99");
assert_se(result.architecture == ARCHITECTURE_X86);
assert_se(endswith(result.path, "/foo_99_x86.raw"));
filter.architecture = ARCHITECTURE_X86_64;
assert_se(path_pick(NULL, AT_FDCWD, pp, &filter, PICK_ARCHITECTURE|PICK_TRIES, &result) > 0);
assert_se(S_ISREG(result.st.st_mode));
- assert_se(streq_ptr(result.version, "55"));
+ ASSERT_STREQ(result.version, "55");
assert_se(result.architecture == ARCHITECTURE_X86_64);
assert_se(endswith(result.path, "/foo_55_x86-64.raw"));
pick_result_done(&result);
filter.architecture = ARCHITECTURE_IA64;
assert_se(path_pick(NULL, AT_FDCWD, pp, &filter, PICK_ARCHITECTURE|PICK_TRIES, &result) > 0);
assert_se(S_ISREG(result.st.st_mode));
- assert_se(streq_ptr(result.version, "5"));
+ ASSERT_STREQ(result.version, "5");
assert_se(result.architecture == ARCHITECTURE_IA64);
assert_se(endswith(result.path, "/foo_5_ia64.raw"));
pick_result_done(&result);
filter.version = "5";
assert_se(path_pick(NULL, AT_FDCWD, pp, &filter, PICK_ARCHITECTURE|PICK_TRIES, &result) > 0);
assert_se(S_ISREG(result.st.st_mode));
- assert_se(streq_ptr(result.version, "5"));
+ ASSERT_STREQ(result.version, "5");
if (native_architecture() != ARCHITECTURE_IA64) {
assert_se(result.architecture == _ARCHITECTURE_INVALID);
assert_se(endswith(result.path, "/foo_5.raw"));
filter.architecture = ARCHITECTURE_IA64;
assert_se(path_pick(NULL, AT_FDCWD, pp, &filter, PICK_ARCHITECTURE|PICK_TRIES, &result) > 0);
assert_se(S_ISREG(result.st.st_mode));
- assert_se(streq_ptr(result.version, "5"));
+ ASSERT_STREQ(result.version, "5");
assert_se(result.architecture == ARCHITECTURE_IA64);
assert_se(endswith(result.path, "/foo_5_ia64.raw"));
pick_result_done(&result);
if (IN_SET(native_architecture(), ARCHITECTURE_X86_64, ARCHITECTURE_X86)) {
assert_se(path_pick(NULL, AT_FDCWD, pp, &filter, PICK_ARCHITECTURE|PICK_TRIES, &result) > 0);
assert_se(S_ISREG(result.st.st_mode));
- assert_se(streq_ptr(result.version, "55"));
+ ASSERT_STREQ(result.version, "55");
if (native_architecture() == ARCHITECTURE_X86_64) {
assert_se(result.architecture == ARCHITECTURE_X86_64);
if (IN_SET(native_architecture(), ARCHITECTURE_X86, ARCHITECTURE_X86_64)) {
assert_se(path_pick(NULL, AT_FDCWD, pp, &filter, PICK_ARCHITECTURE|PICK_TRIES, &result) > 0);
assert_se(S_ISREG(result.st.st_mode));
- assert_se(streq_ptr(result.version, "55"));
+ ASSERT_STREQ(result.version, "55");
assert_se(result.architecture == native_architecture());
assert_se(endswith(result.path, ".raw"));
assert_se(strrstr(result.path, "/foo_55_x86"));
assert_se(path_pick(NULL, AT_FDCWD, pp, &filter, PICK_ARCHITECTURE|PICK_TRIES, &result) > 0);
assert_se(S_ISREG(result.st.st_mode));
- assert_se(streq_ptr(result.version, "2"));
+ ASSERT_STREQ(result.version, "2");
assert_se(result.tries_left == 4);
assert_se(result.tries_done == 6);
assert_se(endswith(result.path, "quux_2_s390+4-6.raw"));
assert_se(pid >= 0);
assert_se(hashmap_isempty(m->watch_pids));
- assert_se(manager_get_unit_by_pid(m, pid) == NULL);
+ ASSERT_NULL(manager_get_unit_by_pid(m, pid));
assert_se(unit_watch_pid(a, pid, false) >= 0);
assert_se(manager_get_unit_by_pid(m, pid) == a);
assert_se(manager_get_unit_by_pid(m, pid) == c);
unit_unwatch_pid(c, pid);
- assert_se(manager_get_unit_by_pid(m, pid) == NULL);
+ ASSERT_NULL(manager_get_unit_by_pid(m, pid));
unit_unwatch_pid(c, pid);
- assert_se(manager_get_unit_by_pid(m, pid) == NULL);
+ ASSERT_NULL(manager_get_unit_by_pid(m, pid));
return 0;
}
fd = open(x, O_PATH|O_CLOEXEC);
assert_se(fd >= 0);
assert_se(getxattr_at_malloc(fd, NULL, "user.foo", 0, &value) == 3);
- assert_se(streq(value, "bar"));
+ ASSERT_STREQ(value, "bar");
}
TEST(getcrtime) {
_cleanup_free_ char *value = NULL;
assert_se(getxattr_at_malloc(dfd, "test", "user.foo", 0, &value) == (int) strlen(expected));
- assert_se(streq(value, expected));
+ ASSERT_STREQ(value, expected);
}
TEST(xsetxattr) {
break;
nn = va_arg(ap, const char *);
- assert_se(streq_ptr(nn, name));
+ ASSERT_STREQ(nn, name);
}
va_end(ap);
#define NULL_ADJTIME_LOCAL "0.0 0 0\n0\nLOCAL\n"
#define UNIT_LIST_DIRS (const char* const*) CONF_PATHS_STRV("systemd/ntp-units.d")
+#define SET_NTP_IN_FLIGHT_MAX 16
typedef struct UnitStatusInfo {
char *name;
bool local_rtc;
Hashmap *polkit_registry;
sd_bus_message *cache;
+ Set *set_ntp_calls;
sd_bus_slot *slot_job_removed;
free(c->zone);
hashmap_free(c->polkit_registry);
sd_bus_message_unref(c->cache);
+ set_free(c->set_ntp_calls);
sd_bus_slot_unref(c->slot_job_removed);
n += !!u->path;
if (n == 0) {
+ sd_bus_message *cm;
+
c->slot_job_removed = sd_bus_slot_unref(c->slot_job_removed);
(void) sd_bus_emit_properties_changed(sd_bus_message_get_bus(m),
"/org/freedesktop/timedate1", "org.freedesktop.timedate1", "NTP",
NULL);
+ while ((cm = set_steal_first(c->set_ntp_calls))) {
+ r = sd_bus_reply_method_return(cm, NULL);
+ if (r < 0)
+ log_debug_errno(r, "Failed to reply to SetNTP method call, ignoring: %m");
+ sd_bus_message_unref(cm);
+ }
}
return 0;
LIST_FOREACH(units, u, c->units)
u->path = mfree(u->path);
+ if (set_size(c->set_ntp_calls) >= SET_NTP_IN_FLIGHT_MAX)
+ return sd_bus_error_set_errnof(error, EAGAIN, "Too many calls in flight.");
+
if (!c->slot_job_removed) {
r = bus_match_signal_async(
bus,
c->slot_job_removed = TAKE_PTR(slot);
if (selected)
- log_info("Set NTP to enabled (%s).", selected->name);
+ log_info("Set NTP to be enabled (%s).", selected->name);
else
- log_info("Set NTP to disabled.");
+ log_info("Set NTP to be disabled.");
- return sd_bus_reply_method_return(m, NULL);
+ /* Asynchronous reply to m in match_job_removed() */
+ return set_ensure_consume(&c->set_ntp_calls, &bus_message_hash_ops, sd_bus_message_ref(m));
}
static int method_list_timezones(sd_bus_message *m, void *userdata, sd_bus_error *error) {
timesyncd_link_with = [libshared]
else
timesyncd_link_with = [libsystemd_static,
- libshared_static,
- libbasic_gcrypt]
+ libshared_static]
endif
libtimesyncd_core = static_library(
'c_args' : '-DSTANDALONE',
'link_with' : [
libbasic,
- libbasic_gcrypt,
libshared_static,
libsystemd_static,
],
include_directories : includes + include_directories('net'),
link_with : udev_link_with,
dependencies : [libblkid,
- libkmod,
+ libkmod_cflags,
userspace],
build_by_default : false)
},
udev_test_template + {
'sources' : files('net/test-link-config-tables.c'),
+ 'include_directories' : includes + include_directories('.'),
'suite' : 'udev',
},
udev_test_template + {
},
udev_fuzz_template + {
'sources' : files('net/fuzz-link-parser.c'),
+ 'include_directories' : includes + include_directories('.'),
},
udev_fuzz_template + {
'sources' : files('fuzz-udev-rule-parse-value.c'),
return -ENOENT;
}
-static int link_apply_ethtool_settings(Link *link, int *ethtool_fd) {
+static int link_apply_ethtool_settings(Link *link, int *ethtool_fd, EventMode mode) {
LinkConfig *config;
const char *name;
int r;
assert(link->config);
assert(ethtool_fd);
+ if (mode != EVENT_UDEV_WORKER) {
+ log_link_debug(link, "Running in test mode, skipping application of ethtool settings.");
+ return 0;
+ }
+
config = link->config;
name = link->ifname;
return 0;
}
-static int link_apply_rtnl_settings(Link *link, sd_netlink **rtnl) {
+static int link_apply_rtnl_settings(Link *link, sd_netlink **rtnl, EventMode mode) {
struct hw_addr_data hw_addr = {};
LinkConfig *config;
int r;
assert(link->config);
assert(rtnl);
+ if (mode != EVENT_UDEV_WORKER) {
+ log_link_debug(link, "Running in test mode, skipping application of rtnl settings.");
+ return 0;
+ }
+
config = link->config;
(void) link_generate_new_hw_addr(link, &hw_addr);
return 0;
}
-static int link_apply_sr_iov_config(Link *link, sd_netlink **rtnl) {
+static int link_apply_sr_iov_config(Link *link, sd_netlink **rtnl, EventMode mode) {
SRIOV *sr_iov;
uint32_t n;
int r;
assert(link->config);
assert(link->device);
+ if (mode != EVENT_UDEV_WORKER) {
+ log_link_debug(link, "Running in test mode, skipping application of SR-IOV settings.");
+ return 0;
+ }
+
r = sr_iov_set_num_vfs(link->device, link->config->sr_iov_num_vfs, link->config->sr_iov_by_section);
if (r < 0)
log_link_warning_errno(link, r, "Failed to set the number of SR-IOV virtual functions, ignoring: %m");
return 0;
}
-static int link_apply_rps_cpu_mask(Link *link) {
+static int link_apply_rps_cpu_mask(Link *link, EventMode mode) {
_cleanup_free_ char *mask_str = NULL;
LinkConfig *config;
int r;
assert(link);
config = ASSERT_PTR(link->config);
+ if (mode != EVENT_UDEV_WORKER) {
+ log_link_debug(link, "Running in test mode, skipping application of RPS setting.");
+ return 0;
+ }
+
/* Skip if the config is not specified. */
if (!config->rps_cpu_mask)
return 0;
return 0;
}
-static int link_apply_udev_properties(Link *link, bool test) {
+static int link_apply_udev_properties(Link *link, EventMode mode) {
LinkConfig *config;
sd_device *device;
/* 1. apply ImportProperty=. */
STRV_FOREACH(p, config->import_properties)
- (void) udev_builtin_import_property(device, link->device_db_clone, test, *p);
+ (void) udev_builtin_import_property(device, link->device_db_clone, mode, *p);
/* 2. apply Property=. */
STRV_FOREACH(p, config->properties) {
if (!key)
return log_oom();
- (void) udev_builtin_add_property(device, test, key, eq + 1);
+ (void) udev_builtin_add_property(device, mode, key, eq + 1);
}
/* 3. apply UnsetProperty=. */
STRV_FOREACH(p, config->unset_properties)
- (void) udev_builtin_add_property(device, test, *p, NULL);
+ (void) udev_builtin_add_property(device, mode, *p, NULL);
/* 4. set the default properties. */
- (void) udev_builtin_add_property(device, test, "ID_NET_LINK_FILE", config->filename);
+ (void) udev_builtin_add_property(device, mode, "ID_NET_LINK_FILE", config->filename);
_cleanup_free_ char *joined = NULL;
STRV_FOREACH(d, config->dropins) {
return log_oom();
}
- (void) udev_builtin_add_property(device, test, "ID_NET_LINK_FILE_DROPINS", joined);
+ (void) udev_builtin_add_property(device, mode, "ID_NET_LINK_FILE_DROPINS", joined);
if (link->new_name)
- (void) udev_builtin_add_property(device, test, "ID_NET_NAME", link->new_name);
+ (void) udev_builtin_add_property(device, mode, "ID_NET_NAME", link->new_name);
return 0;
}
-int link_apply_config(LinkConfigContext *ctx, sd_netlink **rtnl, Link *link, bool test) {
+int link_apply_config(LinkConfigContext *ctx, sd_netlink **rtnl, Link *link, EventMode mode) {
int r;
assert(ctx);
assert(rtnl);
assert(link);
- r = link_apply_ethtool_settings(link, &ctx->ethtool_fd);
+ r = link_apply_ethtool_settings(link, &ctx->ethtool_fd, mode);
if (r < 0)
return r;
- r = link_apply_rtnl_settings(link, rtnl);
+ r = link_apply_rtnl_settings(link, rtnl, mode);
if (r < 0)
return r;
if (r < 0)
return r;
- r = link_apply_sr_iov_config(link, rtnl);
+ r = link_apply_sr_iov_config(link, rtnl, mode);
if (r < 0)
return r;
- r = link_apply_udev_properties(link, test);
+ r = link_apply_rps_cpu_mask(link, mode);
if (r < 0)
return r;
- r = link_apply_rps_cpu_mask(link);
- if (r < 0)
- return r;
-
- return 0;
+ return link_apply_udev_properties(link, mode);
}
int config_parse_udev_property(
#include "list.h"
#include "net-condition.h"
#include "netif-naming-scheme.h"
+#include "udev-event.h"
typedef struct LinkConfigContext LinkConfigContext;
typedef struct LinkConfig LinkConfig;
DEFINE_TRIVIAL_CLEANUP_FUNC(Link*, link_free);
int link_get_config(LinkConfigContext *ctx, Link *link);
-int link_apply_config(LinkConfigContext *ctx, sd_netlink **rtnl, Link *link, bool test);
+int link_apply_config(LinkConfigContext *ctx, sd_netlink **rtnl, Link *link, EventMode mode);
const char *mac_address_policy_to_string(MACAddressPolicy p) _const_;
MACAddressPolicy mac_address_policy_from_string(const char *p) _pure_;
/* SPDX-License-Identifier: GPL-2.0-or-later */
#pragma once
-/*
- * scsi.h
- *
- * General scsi and linux scsi specific defines and structs.
- *
- * Copyright (C) IBM Corp. 2003
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License as published by the
- * Free Software Foundation version 2 of the License.
- */
+/* Copyright (C) IBM Corp. 2003 */
#include <scsi/scsi.h>
if (r < 0)
return log_debug_errno(r, "Failed to open device '%s'", devpath);
- assert_se(event = udev_event_new(dev, NULL));
+ assert_se(event = udev_event_new(dev, NULL, EVENT_TEST_RULE_RUNNER));
assert_se(sigprocmask_many(SIG_BLOCK, NULL, SIGTERM, SIGINT, SIGHUP, SIGCHLD) >= 0);
assert_se(setenv("SYSTEMD_PIDFD", yes_no(with_pidfd), 1) >= 0);
assert_se(sd_device_new_from_syspath(&dev, "/sys/class/net/lo") >= 0);
- assert_se(event = udev_event_new(dev, NULL));
+ assert_se(event = udev_event_new(dev, NULL, EVENT_TEST_SPAWN));
assert_se(udev_event_spawn(event, false, cmd, result_buf, buf_size, NULL) == 0);
assert_se(unsetenv("SYSTEMD_PIDFD") >= 0);
#include "strxcpyx.h"
#include "udev-builtin.h"
-static void print_property(sd_device *dev, bool test, const char *name, const char *value) {
+static void print_property(sd_device *dev, EventMode mode, const char *name, const char *value) {
char s[256];
s[0] = '\0';
if (streq(name, "TYPE")) {
- udev_builtin_add_property(dev, test, "ID_FS_TYPE", value);
+ udev_builtin_add_property(dev, mode, "ID_FS_TYPE", value);
} else if (streq(name, "USAGE")) {
- udev_builtin_add_property(dev, test, "ID_FS_USAGE", value);
+ udev_builtin_add_property(dev, mode, "ID_FS_USAGE", value);
} else if (streq(name, "VERSION")) {
- udev_builtin_add_property(dev, test, "ID_FS_VERSION", value);
+ udev_builtin_add_property(dev, mode, "ID_FS_VERSION", value);
} else if (streq(name, "UUID")) {
blkid_safe_string(value, s, sizeof(s));
- udev_builtin_add_property(dev, test, "ID_FS_UUID", s);
+ udev_builtin_add_property(dev, mode, "ID_FS_UUID", s);
blkid_encode_string(value, s, sizeof(s));
- udev_builtin_add_property(dev, test, "ID_FS_UUID_ENC", s);
+ udev_builtin_add_property(dev, mode, "ID_FS_UUID_ENC", s);
} else if (streq(name, "UUID_SUB")) {
blkid_safe_string(value, s, sizeof(s));
- udev_builtin_add_property(dev, test, "ID_FS_UUID_SUB", s);
+ udev_builtin_add_property(dev, mode, "ID_FS_UUID_SUB", s);
blkid_encode_string(value, s, sizeof(s));
- udev_builtin_add_property(dev, test, "ID_FS_UUID_SUB_ENC", s);
+ udev_builtin_add_property(dev, mode, "ID_FS_UUID_SUB_ENC", s);
} else if (streq(name, "LABEL")) {
blkid_safe_string(value, s, sizeof(s));
- udev_builtin_add_property(dev, test, "ID_FS_LABEL", s);
+ udev_builtin_add_property(dev, mode, "ID_FS_LABEL", s);
blkid_encode_string(value, s, sizeof(s));
- udev_builtin_add_property(dev, test, "ID_FS_LABEL_ENC", s);
+ udev_builtin_add_property(dev, mode, "ID_FS_LABEL_ENC", s);
} else if (STR_IN_SET(name, "FSSIZE", "FSLASTBLOCK", "FSBLOCKSIZE")) {
strscpyl(s, sizeof(s), "ID_FS_", name + 2, NULL);
- udev_builtin_add_property(dev, test, s, value);
+ udev_builtin_add_property(dev, mode, s, value);
} else if (streq(name, "PTTYPE")) {
- udev_builtin_add_property(dev, test, "ID_PART_TABLE_TYPE", value);
+ udev_builtin_add_property(dev, mode, "ID_PART_TABLE_TYPE", value);
} else if (streq(name, "PTUUID")) {
- udev_builtin_add_property(dev, test, "ID_PART_TABLE_UUID", value);
+ udev_builtin_add_property(dev, mode, "ID_PART_TABLE_UUID", value);
} else if (streq(name, "PART_ENTRY_NAME")) {
blkid_encode_string(value, s, sizeof(s));
- udev_builtin_add_property(dev, test, "ID_PART_ENTRY_NAME", s);
+ udev_builtin_add_property(dev, mode, "ID_PART_ENTRY_NAME", s);
} else if (streq(name, "PART_ENTRY_TYPE")) {
blkid_encode_string(value, s, sizeof(s));
- udev_builtin_add_property(dev, test, "ID_PART_ENTRY_TYPE", s);
+ udev_builtin_add_property(dev, mode, "ID_PART_ENTRY_TYPE", s);
} else if (startswith(name, "PART_ENTRY_")) {
strscpyl(s, sizeof(s), "ID_", name, NULL);
- udev_builtin_add_property(dev, test, s, value);
+ udev_builtin_add_property(dev, mode, s, value);
} else if (streq(name, "SYSTEM_ID")) {
blkid_encode_string(value, s, sizeof(s));
- udev_builtin_add_property(dev, test, "ID_FS_SYSTEM_ID", s);
+ udev_builtin_add_property(dev, mode, "ID_FS_SYSTEM_ID", s);
} else if (streq(name, "PUBLISHER_ID")) {
blkid_encode_string(value, s, sizeof(s));
- udev_builtin_add_property(dev, test, "ID_FS_PUBLISHER_ID", s);
+ udev_builtin_add_property(dev, mode, "ID_FS_PUBLISHER_ID", s);
} else if (streq(name, "APPLICATION_ID")) {
blkid_encode_string(value, s, sizeof(s));
- udev_builtin_add_property(dev, test, "ID_FS_APPLICATION_ID", s);
+ udev_builtin_add_property(dev, mode, "ID_FS_APPLICATION_ID", s);
} else if (streq(name, "BOOT_SYSTEM_ID")) {
blkid_encode_string(value, s, sizeof(s));
- udev_builtin_add_property(dev, test, "ID_FS_BOOT_SYSTEM_ID", s);
+ udev_builtin_add_property(dev, mode, "ID_FS_BOOT_SYSTEM_ID", s);
} else if (streq(name, "VOLUME_ID")) {
blkid_encode_string(value, s, sizeof(s));
- udev_builtin_add_property(dev, test, "ID_FS_VOLUME_ID", s);
+ udev_builtin_add_property(dev, mode, "ID_FS_VOLUME_ID", s);
} else if (streq(name, "LOGICAL_VOLUME_ID")) {
blkid_encode_string(value, s, sizeof(s));
- udev_builtin_add_property(dev, test, "ID_FS_LOGICAL_VOLUME_ID", s);
+ udev_builtin_add_property(dev, mode, "ID_FS_LOGICAL_VOLUME_ID", s);
} else if (streq(name, "VOLUME_SET_ID")) {
blkid_encode_string(value, s, sizeof(s));
- udev_builtin_add_property(dev, test, "ID_FS_VOLUME_SET_ID", s);
+ udev_builtin_add_property(dev, mode, "ID_FS_VOLUME_SET_ID", s);
} else if (streq(name, "DATA_PREPARER_ID")) {
blkid_encode_string(value, s, sizeof(s));
- udev_builtin_add_property(dev, test, "ID_FS_DATA_PREPARER_ID", s);
+ udev_builtin_add_property(dev, mode, "ID_FS_DATA_PREPARER_ID", s);
}
}
-static int find_gpt_root(sd_device *dev, blkid_probe pr, bool test) {
+static int find_gpt_root(sd_device *dev, blkid_probe pr, EventMode mode) {
#if defined(SD_GPT_ROOT_NATIVE) && ENABLE_EFI
/* We found the ESP/XBOOTLDR on this disk, and also found a root partition, nice! Let's export its
* UUID */
if (found_esp_or_xbootldr && !sd_id128_is_null(root_id))
- udev_builtin_add_property(dev, test, "ID_PART_GPT_AUTO_ROOT_UUID", SD_ID128_TO_UUID_STRING(root_id));
+ udev_builtin_add_property(dev, mode, "ID_PART_GPT_AUTO_ROOT_UUID", SD_ID128_TO_UUID_STRING(root_id));
#endif
return 0;
return 0;
}
-static int builtin_blkid(UdevEvent *event, int argc, char *argv[], bool test) {
+static int builtin_blkid(UdevEvent *event, int argc, char *argv[]) {
sd_device *dev = ASSERT_PTR(ASSERT_PTR(event)->dev);
const char *devnode, *root_partition = NULL, *data, *name;
_cleanup_(blkid_free_probep) blkid_probe pr = NULL;
if (blkid_probe_get_value(pr, i, &name, &data, NULL) < 0)
continue;
- print_property(dev, test, name, data);
+ print_property(dev, event->event_mode, name, data);
/* Is this a disk with GPT partition table? */
if (streq(name, "PTTYPE") && streq(data, "gpt"))
/* Is this a partition that matches the root partition
* property inherited from the parent? */
if (root_partition && streq(name, "PART_ENTRY_UUID") && streq(data, root_partition))
- udev_builtin_add_property(dev, test, "ID_PART_GPT_AUTO_ROOT", "1");
+ udev_builtin_add_property(dev, event->event_mode, "ID_PART_GPT_AUTO_ROOT", "1");
}
if (is_gpt)
- find_gpt_root(dev, pr, test);
+ find_gpt_root(dev, pr, event->event_mode);
r = read_loopback_backing_inode(
dev,
if (r < 0)
log_device_debug_errno(dev, r, "Failed to read loopback backing inode, ignoring: %m");
else if (r > 0) {
- udev_builtin_add_propertyf(dev, test, "ID_LOOP_BACKING_DEVICE", DEVNUM_FORMAT_STR, DEVNUM_FORMAT_VAL(backing_devno));
- udev_builtin_add_propertyf(dev, test, "ID_LOOP_BACKING_INODE", "%" PRIu64, (uint64_t) backing_inode);
+ udev_builtin_add_propertyf(dev, event->event_mode, "ID_LOOP_BACKING_DEVICE", DEVNUM_FORMAT_STR, DEVNUM_FORMAT_VAL(backing_devno));
+ udev_builtin_add_propertyf(dev, event->event_mode, "ID_LOOP_BACKING_INODE", "%" PRIu64, (uint64_t) backing_inode);
if (backing_fname) {
/* In the worst case blkid_encode_string() will blow up to 4x the string
assert(strlen(backing_fname) < ELEMENTSOF(encoded) / 4);
blkid_encode_string(backing_fname, encoded, ELEMENTSOF(encoded));
- udev_builtin_add_property(dev, test, "ID_LOOP_BACKING_FILENAME", backing_fname);
- udev_builtin_add_property(dev, test, "ID_LOOP_BACKING_FILENAME_ENC", encoded);
+ udev_builtin_add_property(dev, event->event_mode, "ID_LOOP_BACKING_FILENAME", backing_fname);
+ udev_builtin_add_property(dev, event->event_mode, "ID_LOOP_BACKING_FILENAME_ENC", encoded);
}
}
#include "strxcpyx.h"
#include "udev-builtin.h"
-static int builtin_btrfs(UdevEvent *event, int argc, char *argv[], bool test) {
+static int builtin_btrfs(UdevEvent *event, int argc, char *argv[]) {
sd_device *dev = ASSERT_PTR(ASSERT_PTR(event)->dev);
struct btrfs_ioctl_vol_args args = {};
_cleanup_close_ int fd = -EBADF;
/* Driver not installed? Then we aren't ready. This is useful in initrds that lack
* btrfs.ko. After the host transition (where btrfs.ko will hopefully become
* available) the device can be retriggered and will then be considered ready. */
- udev_builtin_add_property(dev, test, "ID_BTRFS_READY", "0");
+ udev_builtin_add_property(dev, event->event_mode, "ID_BTRFS_READY", "0");
return 0;
}
if (r < 0)
return log_device_debug_errno(dev, errno, "Failed to call BTRFS_IOC_DEVICES_READY: %m");
- udev_builtin_add_property(dev, test, "ID_BTRFS_READY", one_zero(r == 0));
+ udev_builtin_add_property(dev, event->event_mode, "ID_BTRFS_READY", one_zero(r == 0));
return 0;
}
static sd_hwdb *hwdb;
-int udev_builtin_hwdb_lookup(sd_device *dev,
- const char *prefix, const char *modalias,
- const char *filter, bool test) {
+int udev_builtin_hwdb_lookup(
+ sd_device *dev,
+ const char *prefix,
+ const char *modalias,
+ const char *filter,
+ EventMode mode) {
+
_cleanup_free_ char *lookup = NULL;
const char *key, *value;
int n = 0, r;
if (filter && fnmatch(filter, key, FNM_NOESCAPE) != 0)
continue;
- r = udev_builtin_add_property(dev, test, key, value);
+ r = udev_builtin_add_property(dev, mode, key, value);
if (r < 0)
return r;
n++;
return s;
}
-static int udev_builtin_hwdb_search(sd_device *dev, sd_device *srcdev,
- const char *subsystem, const char *prefix,
- const char *filter, bool test) {
+static int udev_builtin_hwdb_search(
+ sd_device *dev,
+ sd_device *srcdev,
+ const char *subsystem,
+ const char *prefix,
+ const char *filter,
+ EventMode mode) {
+
char s[LINE_MAX];
bool last = false;
int r = 0;
log_device_debug(dev, "hwdb modalias key: \"%s\"", modalias);
- r = udev_builtin_hwdb_lookup(dev, prefix, modalias, filter, test);
+ r = udev_builtin_hwdb_lookup(dev, prefix, modalias, filter, mode);
if (r > 0)
break;
return r;
}
-static int builtin_hwdb(UdevEvent *event, int argc, char *argv[], bool test) {
+static int builtin_hwdb(UdevEvent *event, int argc, char *argv[]) {
static const struct option options[] = {
{ "filter", required_argument, NULL, 'f' },
{ "device", required_argument, NULL, 'd' },
/* query a specific key given as argument */
if (argv[optind]) {
- r = udev_builtin_hwdb_lookup(dev, prefix, argv[optind], filter, test);
+ r = udev_builtin_hwdb_lookup(dev, prefix, argv[optind], filter, event->event_mode);
if (r < 0)
return log_device_debug_errno(dev, r, "Failed to look up hwdb: %m");
if (r == 0)
return log_device_debug_errno(dev, r, "Failed to create sd_device object '%s': %m", device);
}
- r = udev_builtin_hwdb_search(dev, srcdev, subsystem, prefix, filter, test);
+ r = udev_builtin_hwdb_search(dev, srcdev, subsystem, prefix, filter, event->event_mode);
if (r < 0)
return log_device_debug_errno(dev, r, "Failed to look up hwdb: %m");
if (r == 0)
return (absinfo->maximum - absinfo->minimum) / absinfo->resolution;
}
-static void extract_info(sd_device *dev, bool test) {
+static void extract_info(sd_device *dev, EventMode mode) {
char width[DECIMAL_STR_MAX(int)], height[DECIMAL_STR_MAX(int)];
struct input_absinfo xabsinfo = {}, yabsinfo = {};
_cleanup_close_ int fd = -EBADF;
xsprintf(width, "%d", abs_size_mm(&xabsinfo));
xsprintf(height, "%d", abs_size_mm(&yabsinfo));
- udev_builtin_add_property(dev, test, "ID_INPUT_WIDTH_MM", width);
- udev_builtin_add_property(dev, test, "ID_INPUT_HEIGHT_MM", height);
+ udev_builtin_add_property(dev, mode, "ID_INPUT_WIDTH_MM", width);
+ udev_builtin_add_property(dev, mode, "ID_INPUT_HEIGHT_MM", height);
}
/*
* @param attr sysfs attribute name (e. g. "capabilities/key")
* @param bitmask: Output array which has a sizeof of bitmask_size
*/
-static void get_cap_mask(sd_device *pdev, const char* attr,
- unsigned long *bitmask, size_t bitmask_size,
- bool test) {
+static void get_cap_mask(
+ sd_device *pdev,
+ const char* attr,
+ unsigned long *bitmask,
+ size_t bitmask_size,
+ EventMode mode) {
+
const char *v;
char text[4096];
unsigned i;
else
log_device_debug(pdev, "Ignoring %s block %lX which is larger than maximum size", attr, val);
- if (test && DEBUG_LOGGING) {
+ if (mode == EVENT_UDEVADM_TEST_BUILTIN && DEBUG_LOGGING) {
log_device_debug(pdev, "%s decoded bit map:", attr);
val = bitmask_size / sizeof (unsigned long);
}
/* pointer devices */
-static bool test_pointers(sd_device *dev,
- const struct input_id *id,
- const unsigned long* bitmask_ev,
- const unsigned long* bitmask_abs,
- const unsigned long* bitmask_key,
- const unsigned long* bitmask_rel,
- const unsigned long* bitmask_props,
- bool test) {
+static bool test_pointers(
+ sd_device *dev,
+ const struct input_id *id,
+ const unsigned long* bitmask_ev,
+ const unsigned long* bitmask_abs,
+ const unsigned long* bitmask_key,
+ const unsigned long* bitmask_rel,
+ const unsigned long* bitmask_props,
+ EventMode mode) {
+
bool has_abs_coordinates = false;
bool has_rel_coordinates = false;
bool has_mt_coordinates = false;
is_accelerometer = true;
if (is_accelerometer) {
- udev_builtin_add_property(dev, test, "ID_INPUT_ACCELEROMETER", "1");
+ udev_builtin_add_property(dev, mode, "ID_INPUT_ACCELEROMETER", "1");
return true;
}
}
if (is_pointing_stick)
- udev_builtin_add_property(dev, test, "ID_INPUT_POINTINGSTICK", "1");
+ udev_builtin_add_property(dev, mode, "ID_INPUT_POINTINGSTICK", "1");
if (is_mouse || is_abs_mouse)
- udev_builtin_add_property(dev, test, "ID_INPUT_MOUSE", "1");
+ udev_builtin_add_property(dev, mode, "ID_INPUT_MOUSE", "1");
if (is_touchpad)
- udev_builtin_add_property(dev, test, "ID_INPUT_TOUCHPAD", "1");
+ udev_builtin_add_property(dev, mode, "ID_INPUT_TOUCHPAD", "1");
if (is_touchscreen)
- udev_builtin_add_property(dev, test, "ID_INPUT_TOUCHSCREEN", "1");
+ udev_builtin_add_property(dev, mode, "ID_INPUT_TOUCHSCREEN", "1");
if (is_joystick)
- udev_builtin_add_property(dev, test, "ID_INPUT_JOYSTICK", "1");
+ udev_builtin_add_property(dev, mode, "ID_INPUT_JOYSTICK", "1");
if (is_tablet)
- udev_builtin_add_property(dev, test, "ID_INPUT_TABLET", "1");
+ udev_builtin_add_property(dev, mode, "ID_INPUT_TABLET", "1");
if (is_tablet_pad)
- udev_builtin_add_property(dev, test, "ID_INPUT_TABLET_PAD", "1");
+ udev_builtin_add_property(dev, mode, "ID_INPUT_TABLET_PAD", "1");
return is_tablet || is_mouse || is_abs_mouse || is_touchpad || is_touchscreen || is_joystick || is_pointing_stick;
}
/* key like devices */
-static bool test_key(sd_device *dev,
- const unsigned long* bitmask_ev,
- const unsigned long* bitmask_key,
- bool test) {
+static bool test_key(
+ sd_device *dev,
+ const unsigned long* bitmask_ev,
+ const unsigned long* bitmask_key,
+ EventMode mode) {
bool found = false;
}
if (found)
- udev_builtin_add_property(dev, test, "ID_INPUT_KEY", "1");
+ udev_builtin_add_property(dev, mode, "ID_INPUT_KEY", "1");
/* the first 32 bits are ESC, numbers, and Q to D; if we have all of
* those, consider it a full keyboard; do not test KEY_RESERVED, though */
if (FLAGS_SET(bitmask_key[0], 0xFFFFFFFE)) {
- udev_builtin_add_property(dev, test, "ID_INPUT_KEYBOARD", "1");
+ udev_builtin_add_property(dev, mode, "ID_INPUT_KEYBOARD", "1");
return true;
}
return found;
}
-static int builtin_input_id(UdevEvent *event, int argc, char *argv[], bool test) {
+static int builtin_input_id(UdevEvent *event, int argc, char *argv[]) {
sd_device *pdev, *dev = ASSERT_PTR(ASSERT_PTR(event)->dev);
unsigned long bitmask_ev[NBITS(EV_MAX)];
unsigned long bitmask_abs[NBITS(ABS_MAX)];
/* Use this as a flag that input devices were detected, so that this
* program doesn't need to be called more than once per device */
- udev_builtin_add_property(dev, test, "ID_INPUT", "1");
- get_cap_mask(pdev, "capabilities/ev", bitmask_ev, sizeof(bitmask_ev), test);
- get_cap_mask(pdev, "capabilities/abs", bitmask_abs, sizeof(bitmask_abs), test);
- get_cap_mask(pdev, "capabilities/rel", bitmask_rel, sizeof(bitmask_rel), test);
- get_cap_mask(pdev, "capabilities/key", bitmask_key, sizeof(bitmask_key), test);
- get_cap_mask(pdev, "properties", bitmask_props, sizeof(bitmask_props), test);
+ udev_builtin_add_property(dev, event->event_mode, "ID_INPUT", "1");
+ get_cap_mask(pdev, "capabilities/ev", bitmask_ev, sizeof(bitmask_ev), event->event_mode);
+ get_cap_mask(pdev, "capabilities/abs", bitmask_abs, sizeof(bitmask_abs), event->event_mode);
+ get_cap_mask(pdev, "capabilities/rel", bitmask_rel, sizeof(bitmask_rel), event->event_mode);
+ get_cap_mask(pdev, "capabilities/key", bitmask_key, sizeof(bitmask_key), event->event_mode);
+ get_cap_mask(pdev, "properties", bitmask_props, sizeof(bitmask_props), event->event_mode);
is_pointer = test_pointers(dev, &id, bitmask_ev, bitmask_abs,
bitmask_key, bitmask_rel,
- bitmask_props, test);
- is_key = test_key(dev, bitmask_ev, bitmask_key, test);
+ bitmask_props, event->event_mode);
+ is_key = test_key(dev, bitmask_ev, bitmask_key, event->event_mode);
/* Some evdev nodes have only a scrollwheel */
if (!is_pointer && !is_key && test_bit(EV_REL, bitmask_ev) &&
(test_bit(REL_WHEEL, bitmask_rel) || test_bit(REL_HWHEEL, bitmask_rel)))
- udev_builtin_add_property(dev, test, "ID_INPUT_KEY", "1");
+ udev_builtin_add_property(dev, event->event_mode, "ID_INPUT_KEY", "1");
if (test_bit(EV_SW, bitmask_ev))
- udev_builtin_add_property(dev, test, "ID_INPUT_SWITCH", "1");
+ udev_builtin_add_property(dev, event->event_mode, "ID_INPUT_SWITCH", "1");
}
if (sd_device_get_sysname(dev, &sysname) >= 0 &&
startswith(sysname, "event"))
- extract_info(dev, test);
+ extract_info(dev, event->event_mode);
return 0;
}
return 0;
}
-static int builtin_keyboard(UdevEvent *event, int argc, char *argv[], bool test) {
+static int builtin_keyboard(UdevEvent *event, int argc, char *argv[]) {
sd_device *dev = ASSERT_PTR(ASSERT_PTR(event)->dev);
unsigned release[1024];
unsigned release_count = 0;
const char *node;
int has_abs = -1, r;
+ if (event->event_mode != EVENT_UDEV_WORKER) {
+ log_device_debug(dev, "Running in test mode, skipping execution of 'keyboard' builtin command.");
+ return 0;
+ }
+
r = sd_device_get_devname(dev, &node);
if (r < 0)
return log_device_error_errno(dev, r, "Failed to get device name: %m");
static struct kmod_ctx *ctx = NULL;
-_printf_(6,0) static void udev_kmod_log(void *data, int priority, const char *file, int line, const char *fn, const char *format, va_list args) {
- log_internalv(priority, 0, file, line, fn, format, args);
-}
-
-static int builtin_kmod(UdevEvent *event, int argc, char *argv[], bool test) {
+static int builtin_kmod(UdevEvent *event, int argc, char *argv[]) {
sd_device *dev = ASSERT_PTR(ASSERT_PTR(event)->dev);
int r;
+ if (event->event_mode != EVENT_UDEV_WORKER) {
+ log_device_debug(dev, "Running in test mode, skipping execution of 'kmod' builtin command.");
+ return 0;
+ }
+
if (!ctx)
return 0;
r = sd_device_get_property_value(dev, "MODALIAS", &modalias);
if (r < 0)
- return log_device_warning_errno(dev, r, "Failed to read property \"MODALIAS\".");
+ return log_device_warning_errno(dev, r, "Failed to read property \"MODALIAS\": %m");
(void) module_load_and_warn(ctx, modalias, /* verbose = */ false);
} else
/* called at udev startup and reload */
static int builtin_kmod_init(void) {
+ int r;
+
if (ctx)
return 0;
- ctx = kmod_new(NULL, NULL);
- if (!ctx)
- return -ENOMEM;
-
log_debug("Loading kernel module index.");
- kmod_set_log_fn(ctx, udev_kmod_log, NULL);
- kmod_load_resources(ctx);
+
+ r = module_setup_context(&ctx);
+ if (r < 0)
+ return log_error_errno(r, "Failed to initialize libkmod context: %m");
+
return 0;
}
/* called on udev shutdown and reload request */
static void builtin_kmod_exit(void) {
log_debug("Unload kernel module index.");
- ctx = kmod_unref(ctx);
+
+ if (!ctx)
+ return;
+
+ ctx = sym_kmod_unref(ctx);
}
/* called every couple of seconds during event activity; 'true' if config has changed */
if (!ctx)
return false;
- if (kmod_validate_resources(ctx) != KMOD_RESOURCES_OK) {
+ if (sym_kmod_validate_resources(ctx) != KMOD_RESOURCES_OK) {
log_debug("Kernel module index needs reloading.");
return true;
}
#include "string-util.h"
#include "udev-builtin.h"
-static int builtin_net_driver_set_driver(UdevEvent *event, int argc, char **argv, bool test) {
+static int builtin_net_driver_set_driver(UdevEvent *event, int argc, char **argv) {
sd_device *dev = ASSERT_PTR(ASSERT_PTR(event)->dev);
_cleanup_close_ int ethtool_fd = -EBADF;
_cleanup_free_ char *driver = NULL;
if (r < 0)
return log_device_warning_errno(dev, r, "Failed to get driver for '%s': %m", sysname);
- return udev_builtin_add_property(event->dev, test, "ID_NET_DRIVER", driver);
+ return udev_builtin_add_property(event->dev, event->event_mode, "ID_NET_DRIVER", driver);
}
const UdevBuiltin udev_builtin_net_driver = {
* When the code here is changed, man/systemd.net-naming-scheme.xml must be updated too.
*/
+/* Make sure the net/if.h header is included before any linux/ one */
+#include <net/if.h>
#include <errno.h>
#include <fcntl.h>
-#include <net/if.h>
#include <stdarg.h>
#include <unistd.h>
#include <linux/if.h>
return 0;
}
-static int names_pci_onboard(sd_device *dev, sd_device *pci_dev, const char *prefix, const char *suffix, bool test) {
+static int names_pci_onboard(sd_device *dev, sd_device *pci_dev, const char *prefix, const char *suffix, EventMode mode) {
_cleanup_free_ char *port = NULL;
unsigned idx = 0; /* avoid false maybe-uninitialized warning */
int r;
char str[ALTIFNAMSIZ];
if (snprintf_ok(str, sizeof str, "%so%u%s%s", prefix, idx, strempty(port), strempty(suffix)))
- udev_builtin_add_property(dev, test, "ID_NET_NAME_ONBOARD", str);
+ udev_builtin_add_property(dev, mode, "ID_NET_NAME_ONBOARD", str);
log_device_debug(dev, "Onboard index identifier: index=%u port=%s %s %s",
idx, strna(port),
return 0;
}
-static int names_pci_onboard_label(sd_device *dev, sd_device *pci_dev, const char *prefix, bool test) {
+static int names_pci_onboard_label(sd_device *dev, sd_device *pci_dev, const char *prefix, EventMode mode) {
const char *label;
int r;
if (snprintf_ok(str, sizeof str, "%s%s",
naming_scheme_has(NAMING_LABEL_NOPREFIX) ? "" : prefix,
label))
- udev_builtin_add_property(dev, test, "ID_NET_LABEL_ONBOARD", str);
+ udev_builtin_add_property(dev, mode, "ID_NET_LABEL_ONBOARD", str);
log_device_debug(dev, "Onboard label from PCI device: %s", label);
return 0;
return 0;
}
-static int names_pci_slot(sd_device *dev, sd_device *pci_dev, const char *prefix, const char *suffix, bool test) {
+static int names_pci_slot(sd_device *dev, sd_device *pci_dev, const char *prefix, const char *suffix, EventMode mode) {
_cleanup_free_ char *domain = NULL, *bus_and_slot = NULL, *func = NULL, *port = NULL;
uint32_t hotplug_slot = 0; /* avoid false maybe-uninitialized warning */
char str[ALTIFNAMSIZ];
/* compose a name based on the raw kernel's PCI bus, slot numbers */
if (snprintf_ok(str, sizeof str, "%s%s%s%s%s%s",
prefix, strempty(domain), bus_and_slot, strempty(func), strempty(port), strempty(suffix)))
- udev_builtin_add_property(dev, test, "ID_NET_NAME_PATH", str);
+ udev_builtin_add_property(dev, mode, "ID_NET_NAME_PATH", str);
log_device_debug(dev, "PCI path identifier: domain=%s bus_and_slot=%s func=%s port=%s %s %s",
strna(domain), bus_and_slot, strna(func), strna(port),
if (snprintf_ok(str, sizeof str, "%s%ss%"PRIu32"%s%s%s",
prefix, strempty(domain), hotplug_slot, strempty(func), strempty(port), strempty(suffix)))
- udev_builtin_add_property(dev, test, "ID_NET_NAME_SLOT", str);
+ udev_builtin_add_property(dev, mode, "ID_NET_NAME_SLOT", str);
log_device_debug(dev, "Slot identifier: domain=%s slot=%"PRIu32" func=%s port=%s %s %s",
strna(domain), hotplug_slot, strna(func), strna(port),
return 0;
}
-static int names_vio(sd_device *dev, const char *prefix, bool test) {
+static int names_vio(sd_device *dev, const char *prefix, EventMode mode) {
_cleanup_free_ char *s = NULL;
unsigned slotid;
int r;
char str[ALTIFNAMSIZ];
if (snprintf_ok(str, sizeof str, "%sv%u", prefix, slotid))
- udev_builtin_add_property(dev, test, "ID_NET_NAME_SLOT", str);
+ udev_builtin_add_property(dev, mode, "ID_NET_NAME_SLOT", str);
log_device_debug(dev, "Vio slot identifier: slotid=%u %s %s",
slotid, special_glyph(SPECIAL_GLYPH_ARROW_RIGHT), str + strlen(prefix));
return 0;
}
-static int names_platform(sd_device *dev, const char *prefix, bool test) {
+static int names_platform(sd_device *dev, const char *prefix, EventMode mode) {
_cleanup_free_ char *p = NULL;
const char *validchars;
char *vendor, *model_str, *instance_str;
char str[ALTIFNAMSIZ];
if (snprintf_ok(str, sizeof str, "%sa%s%xi%u", prefix, vendor, model, instance))
- udev_builtin_add_property(dev, test, "ID_NET_NAME_PATH", str);
+ udev_builtin_add_property(dev, mode, "ID_NET_NAME_PATH", str);
log_device_debug(dev, "Platform identifier: vendor=%s model=%x instance=%u %s %s",
vendor, model, instance, special_glyph(SPECIAL_GLYPH_ARROW_RIGHT), str + strlen(prefix));
return 0;
}
-static int names_devicetree(sd_device *dev, const char *prefix, bool test) {
+static int names_devicetree(sd_device *dev, const char *prefix, EventMode mode) {
_cleanup_(sd_device_unrefp) sd_device *aliases_dev = NULL, *ofnode_dev = NULL, *devicetree_dev = NULL;
const char *ofnode_path, *ofnode_syspath, *devicetree_syspath;
sd_device *parent;
char str[ALTIFNAMSIZ];
if (snprintf_ok(str, sizeof str, "%sd%u", prefix, i))
- udev_builtin_add_property(dev, test, "ID_NET_NAME_ONBOARD", str);
+ udev_builtin_add_property(dev, mode, "ID_NET_NAME_ONBOARD", str);
log_device_debug(dev, "devicetree identifier: alias_index=%u %s \"%s\"",
i, special_glyph(SPECIAL_GLYPH_ARROW_RIGHT), str + strlen(prefix));
return 0;
return -ENOENT;
}
-static int names_pci(sd_device *dev, const char *prefix, bool test) {
+static int names_pci(sd_device *dev, const char *prefix, EventMode mode) {
_cleanup_(sd_device_unrefp) sd_device *physfn_pcidev = NULL;
_cleanup_free_ char *virtfn_suffix = NULL;
sd_device *parent;
get_virtfn_info(parent, &physfn_pcidev, &virtfn_suffix) >= 0)
parent = physfn_pcidev;
else
- (void) names_pci_onboard_label(dev, parent, prefix, test);
+ (void) names_pci_onboard_label(dev, parent, prefix, mode);
- (void) names_pci_onboard(dev, parent, prefix, virtfn_suffix, test);
- (void) names_pci_slot(dev, parent, prefix, virtfn_suffix, test);
+ (void) names_pci_onboard(dev, parent, prefix, virtfn_suffix, mode);
+ (void) names_pci_slot(dev, parent, prefix, virtfn_suffix, mode);
return 0;
}
return 0;
}
-static int names_usb(sd_device *dev, const char *prefix, bool test) {
+static int names_usb(sd_device *dev, const char *prefix, EventMode mode) {
_cleanup_free_ char *suffix = NULL;
sd_device *usbdev, *pcidev;
int r;
/* If the USB bus is on PCI bus, then suffix the USB specifier to the name based on the PCI bus. */
r = sd_device_get_parent_with_subsystem_devtype(usbdev, "pci", NULL, &pcidev);
if (r >= 0)
- return names_pci_slot(dev, pcidev, prefix, suffix, test);
+ return names_pci_slot(dev, pcidev, prefix, suffix, mode);
if (r != -ENOENT || !naming_scheme_has(NAMING_USB_HOST))
return log_device_debug_errno(usbdev, r, "Failed to get parent PCI bus: %m");
/* Otherwise, e.g. on-chip asics that have USB ports, use the USB specifier as is. */
char str[ALTIFNAMSIZ];
if (snprintf_ok(str, sizeof str, "%s%s", prefix, suffix))
- udev_builtin_add_property(dev, test, "ID_NET_NAME_PATH", str);
+ udev_builtin_add_property(dev, mode, "ID_NET_NAME_PATH", str);
return 0;
}
return 0;
}
-static int names_bcma(sd_device *dev, const char *prefix, bool test) {
+static int names_bcma(sd_device *dev, const char *prefix, EventMode mode) {
_cleanup_free_ char *suffix = NULL;
sd_device *bcmadev, *pcidev;
int r;
if (r < 0)
return r;
- return names_pci_slot(dev, pcidev, prefix, suffix, test);
+ return names_pci_slot(dev, pcidev, prefix, suffix, mode);
}
-static int names_ccw(sd_device *dev, const char *prefix, bool test) {
+static int names_ccw(sd_device *dev, const char *prefix, EventMode mode) {
sd_device *cdev;
const char *bus_id;
size_t bus_id_start, bus_id_len;
/* Use the CCW bus-ID as network device name */
char str[ALTIFNAMSIZ];
if (snprintf_ok(str, sizeof str, "%sc%s", prefix, bus_id))
- udev_builtin_add_property(dev, test, "ID_NET_NAME_PATH", str);
+ udev_builtin_add_property(dev, mode, "ID_NET_NAME_PATH", str);
log_device_debug(dev, "CCW identifier: ccw_busid=%s %s \"%s\"",
bus_id, special_glyph(SPECIAL_GLYPH_ARROW_RIGHT), str + strlen(prefix));
return 0;
}
/* IEEE Organizationally Unique Identifier vendor string */
-static int ieee_oui(sd_device *dev, const struct hw_addr_data *hw_addr, bool test) {
+static int ieee_oui(sd_device *dev, const struct hw_addr_data *hw_addr, EventMode mode) {
char str[32];
assert(dev);
hw_addr->bytes[4],
hw_addr->bytes[5]);
- return udev_builtin_hwdb_lookup(dev, NULL, str, NULL, test);
+ return udev_builtin_hwdb_lookup(dev, NULL, str, NULL, mode);
}
-static int names_mac(sd_device *dev, const char *prefix, bool test) {
+static int names_mac(sd_device *dev, const char *prefix, EventMode mode) {
unsigned iftype, assign_type;
struct hw_addr_data hw_addr;
const char *s;
char str[ALTIFNAMSIZ];
xsprintf(str, "%sx%s", prefix, HW_ADDR_TO_STR_FULL(&hw_addr, HW_ADDR_TO_STRING_NO_COLON));
- udev_builtin_add_property(dev, test, "ID_NET_NAME_MAC", str);
+ udev_builtin_add_property(dev, mode, "ID_NET_NAME_MAC", str);
log_device_debug(dev, "MAC address identifier: hw_addr=%s %s %s",
HW_ADDR_TO_STR(&hw_addr),
special_glyph(SPECIAL_GLYPH_ARROW_RIGHT), str + strlen(prefix));
- (void) ieee_oui(dev, &hw_addr, test);
+ (void) ieee_oui(dev, &hw_addr, mode);
return 0;
}
-static int names_netdevsim(sd_device *dev, const char *prefix, bool test) {
+static int names_netdevsim(sd_device *dev, const char *prefix, EventMode mode) {
sd_device *netdevsimdev;
const char *sysnum, *phys_port_name;
unsigned addr;
char str[ALTIFNAMSIZ];
if (snprintf_ok(str, sizeof str, "%si%un%s", prefix, addr, phys_port_name))
- udev_builtin_add_property(dev, test, "ID_NET_NAME_PATH", str);
+ udev_builtin_add_property(dev, mode, "ID_NET_NAME_PATH", str);
log_device_debug(dev, "Netdevsim identifier: address=%u, port_name=%s %s %s",
addr, phys_port_name, special_glyph(SPECIAL_GLYPH_ARROW_RIGHT), str + strlen(prefix));
return 0;
}
-static int names_xen(sd_device *dev, const char *prefix, bool test) {
+static int names_xen(sd_device *dev, const char *prefix, EventMode mode) {
_cleanup_free_ char *vif = NULL;
const char *p;
unsigned id;
char str[ALTIFNAMSIZ];
if (snprintf_ok(str, sizeof str, "%sX%u", prefix, id))
- udev_builtin_add_property(dev, test, "ID_NET_NAME_SLOT", str);
+ udev_builtin_add_property(dev, mode, "ID_NET_NAME_SLOT", str);
log_device_debug(dev, "Xen identifier: id=%u %s %s",
id, special_glyph(SPECIAL_GLYPH_ARROW_RIGHT), str + strlen(prefix));
return 0;
return ifindex != iflink;
}
-static int builtin_net_id(UdevEvent *event, int argc, char *argv[], bool test) {
+static int builtin_net_id(UdevEvent *event, int argc, char *argv[]) {
sd_device *dev = ASSERT_PTR(ASSERT_PTR(event)->dev);
const char *prefix;
int r;
return 0;
}
- udev_builtin_add_property(dev, test, "ID_NET_NAMING_SCHEME", naming_scheme()->name);
-
- (void) names_mac(dev, prefix, test);
- (void) names_devicetree(dev, prefix, test);
- (void) names_ccw(dev, prefix, test);
- (void) names_vio(dev, prefix, test);
- (void) names_platform(dev, prefix, test);
- (void) names_netdevsim(dev, prefix, test);
- (void) names_xen(dev, prefix, test);
- (void) names_pci(dev, prefix, test);
- (void) names_usb(dev, prefix, test);
- (void) names_bcma(dev, prefix, test);
+ udev_builtin_add_property(dev, event->event_mode, "ID_NET_NAMING_SCHEME", naming_scheme()->name);
+
+ (void) names_mac(dev, prefix, event->event_mode);
+ (void) names_devicetree(dev, prefix, event->event_mode);
+ (void) names_ccw(dev, prefix, event->event_mode);
+ (void) names_vio(dev, prefix, event->event_mode);
+ (void) names_platform(dev, prefix, event->event_mode);
+ (void) names_netdevsim(dev, prefix, event->event_mode);
+ (void) names_xen(dev, prefix, event->event_mode);
+ (void) names_pci(dev, prefix, event->event_mode);
+ (void) names_usb(dev, prefix, event->event_mode);
+ (void) names_bcma(dev, prefix, event->event_mode);
return 0;
}
static LinkConfigContext *ctx = NULL;
-static int builtin_net_setup_link(UdevEvent *event, int argc, char **argv, bool test) {
+static int builtin_net_setup_link(UdevEvent *event, int argc, char **argv) {
sd_device *dev = ASSERT_PTR(ASSERT_PTR(event)->dev);
_cleanup_(link_freep) Link *link = NULL;
int r;
device_action_to_string(action));
/* Import previously assigned .link file name. */
- (void) udev_builtin_import_property(dev, event->dev_db_clone, test, "ID_NET_LINK_FILE");
- (void) udev_builtin_import_property(dev, event->dev_db_clone, test, "ID_NET_LINK_FILE_DROPINS");
+ (void) udev_builtin_import_property(dev, event->dev_db_clone, event->event_mode, "ID_NET_LINK_FILE");
+ (void) udev_builtin_import_property(dev, event->dev_db_clone, event->event_mode, "ID_NET_LINK_FILE_DROPINS");
/* Set ID_NET_NAME= with the current interface name. */
const char *value;
if (sd_device_get_sysname(dev, &value) >= 0)
- (void) udev_builtin_add_property(dev, test, "ID_NET_NAME", value);
+ (void) udev_builtin_add_property(dev, event->event_mode, "ID_NET_NAME", value);
return 0;
}
return log_device_error_errno(dev, r, "Failed to get link config: %m");
}
- r = link_apply_config(ctx, &event->rtnl, link, test);
+ r = link_apply_config(ctx, &event->rtnl, link, event->event_mode);
if (r == -ENODEV)
log_device_debug_errno(dev, r, "Link vanished while applying configuration, ignoring.");
else if (r < 0)
return 0;
}
-static void add_id_with_usb_revision(sd_device *dev, bool test, char *path) {
+static void add_id_with_usb_revision(sd_device *dev, EventMode mode, char *path) {
char *p;
assert(dev);
if (p[1] != '-')
return;
- (void) udev_builtin_add_property(dev, test, "ID_PATH_WITH_USB_REVISION", path);
+ (void) udev_builtin_add_property(dev, mode, "ID_PATH_WITH_USB_REVISION", path);
/* Drop the USB revision specifier for backward compatibility. */
memmove(p - 1, p + 1, strlen(p + 1) + 1);
}
-static void add_id_tag(sd_device *dev, bool test, const char *path) {
+static void add_id_tag(sd_device *dev, EventMode mode, const char *path) {
char tag[UDEV_NAME_SIZE];
size_t i = 0;
i--;
tag[i] = '\0';
- (void) udev_builtin_add_property(dev, test, "ID_PATH_TAG", tag);
+ (void) udev_builtin_add_property(dev, mode, "ID_PATH_TAG", tag);
}
-static int builtin_path_id(UdevEvent *event, int argc, char *argv[], bool test) {
+static int builtin_path_id(UdevEvent *event, int argc, char *argv[]) {
sd_device *dev = ASSERT_PTR(ASSERT_PTR(event)->dev);
_cleanup_(sd_device_unrefp) sd_device *dev_other_branch = NULL;
_cleanup_free_ char *path = NULL, *compat_path = NULL;
if (device_in_subsystem(dev, "block") && !supported_transport)
return -ENOENT;
- add_id_with_usb_revision(dev, test, path);
+ add_id_with_usb_revision(dev, event->event_mode, path);
- (void) udev_builtin_add_property(dev, test, "ID_PATH", path);
+ (void) udev_builtin_add_property(dev, event->event_mode, "ID_PATH", path);
- add_id_tag(dev, test, path);
+ add_id_tag(dev, event->event_mode, path);
/*
* Compatible link generation for ATA devices
* ID_PATH_ATA_COMPAT
*/
if (compat_path)
- (void) udev_builtin_add_property(dev, test, "ID_PATH_ATA_COMPAT", compat_path);
+ (void) udev_builtin_add_property(dev, event->event_mode, "ID_PATH_ATA_COMPAT", compat_path);
return 0;
}
#include "log.h"
#include "udev-builtin.h"
-static int builtin_uaccess(UdevEvent *event, int argc, char *argv[], bool test) {
+static int builtin_uaccess(UdevEvent *event, int argc, char *argv[]) {
sd_device *dev = ASSERT_PTR(ASSERT_PTR(event)->dev);
const char *path = NULL, *seat;
bool changed_acl = false;
uid_t uid;
int r;
+ if (event->event_mode != EVENT_UDEV_WORKER) {
+ log_device_debug(dev, "Running in test mode, skipping execution of 'uaccess' builtin command.");
+ return 0;
+ }
+
umask(0022);
/* don't muck around with ACLs when the system is not running systemd */
* 6.) If the device supplies a serial number, this number
* is concatenated with the identification with an underscore '_'.
*/
-static int builtin_usb_id(UdevEvent *event, int argc, char *argv[], bool test) {
+static int builtin_usb_id(UdevEvent *event, int argc, char *argv[]) {
sd_device *dev = ASSERT_PTR(ASSERT_PTR(event)->dev);
char vendor_str[64] = "";
char vendor_str_enc[256];
if (sd_device_get_property_value(dev, "ID_BUS", NULL) >= 0)
log_device_debug(dev, "ID_BUS property is already set, setting only properties prefixed with \"ID_USB_\".");
else {
- udev_builtin_add_property(dev, test, "ID_BUS", "usb");
+ udev_builtin_add_property(dev, event->event_mode, "ID_BUS", "usb");
- udev_builtin_add_property(dev, test, "ID_MODEL", model_str);
- udev_builtin_add_property(dev, test, "ID_MODEL_ENC", model_str_enc);
- udev_builtin_add_property(dev, test, "ID_MODEL_ID", product_id);
+ udev_builtin_add_property(dev, event->event_mode, "ID_MODEL", model_str);
+ udev_builtin_add_property(dev, event->event_mode, "ID_MODEL_ENC", model_str_enc);
+ udev_builtin_add_property(dev, event->event_mode, "ID_MODEL_ID", product_id);
- udev_builtin_add_property(dev, test, "ID_SERIAL", serial);
+ udev_builtin_add_property(dev, event->event_mode, "ID_SERIAL", serial);
if (!isempty(serial_str))
- udev_builtin_add_property(dev, test, "ID_SERIAL_SHORT", serial_str);
+ udev_builtin_add_property(dev, event->event_mode, "ID_SERIAL_SHORT", serial_str);
- udev_builtin_add_property(dev, test, "ID_VENDOR", vendor_str);
- udev_builtin_add_property(dev, test, "ID_VENDOR_ENC", vendor_str_enc);
- udev_builtin_add_property(dev, test, "ID_VENDOR_ID", vendor_id);
+ udev_builtin_add_property(dev, event->event_mode, "ID_VENDOR", vendor_str);
+ udev_builtin_add_property(dev, event->event_mode, "ID_VENDOR_ENC", vendor_str_enc);
+ udev_builtin_add_property(dev, event->event_mode, "ID_VENDOR_ID", vendor_id);
- udev_builtin_add_property(dev, test, "ID_REVISION", revision_str);
+ udev_builtin_add_property(dev, event->event_mode, "ID_REVISION", revision_str);
if (!isempty(type_str))
- udev_builtin_add_property(dev, test, "ID_TYPE", type_str);
+ udev_builtin_add_property(dev, event->event_mode, "ID_TYPE", type_str);
if (!isempty(instance_str))
- udev_builtin_add_property(dev, test, "ID_INSTANCE", instance_str);
+ udev_builtin_add_property(dev, event->event_mode, "ID_INSTANCE", instance_str);
}
/* Also export the same values in the above by prefixing ID_USB_. */
- udev_builtin_add_property(dev, test, "ID_USB_MODEL", model_str);
- udev_builtin_add_property(dev, test, "ID_USB_MODEL_ENC", model_str_enc);
- udev_builtin_add_property(dev, test, "ID_USB_MODEL_ID", product_id);
- udev_builtin_add_property(dev, test, "ID_USB_SERIAL", serial);
+ udev_builtin_add_property(dev, event->event_mode, "ID_USB_MODEL", model_str);
+ udev_builtin_add_property(dev, event->event_mode, "ID_USB_MODEL_ENC", model_str_enc);
+ udev_builtin_add_property(dev, event->event_mode, "ID_USB_MODEL_ID", product_id);
+ udev_builtin_add_property(dev, event->event_mode, "ID_USB_SERIAL", serial);
if (!isempty(serial_str))
- udev_builtin_add_property(dev, test, "ID_USB_SERIAL_SHORT", serial_str);
+ udev_builtin_add_property(dev, event->event_mode, "ID_USB_SERIAL_SHORT", serial_str);
- udev_builtin_add_property(dev, test, "ID_USB_VENDOR", vendor_str);
- udev_builtin_add_property(dev, test, "ID_USB_VENDOR_ENC", vendor_str_enc);
- udev_builtin_add_property(dev, test, "ID_USB_VENDOR_ID", vendor_id);
+ udev_builtin_add_property(dev, event->event_mode, "ID_USB_VENDOR", vendor_str);
+ udev_builtin_add_property(dev, event->event_mode, "ID_USB_VENDOR_ENC", vendor_str_enc);
+ udev_builtin_add_property(dev, event->event_mode, "ID_USB_VENDOR_ID", vendor_id);
- udev_builtin_add_property(dev, test, "ID_USB_REVISION", revision_str);
+ udev_builtin_add_property(dev, event->event_mode, "ID_USB_REVISION", revision_str);
if (!isempty(type_str))
- udev_builtin_add_property(dev, test, "ID_USB_TYPE", type_str);
+ udev_builtin_add_property(dev, event->event_mode, "ID_USB_TYPE", type_str);
if (!isempty(instance_str))
- udev_builtin_add_property(dev, test, "ID_USB_INSTANCE", instance_str);
+ udev_builtin_add_property(dev, event->event_mode, "ID_USB_INSTANCE", instance_str);
if (!isempty(packed_if_str))
- udev_builtin_add_property(dev, test, "ID_USB_INTERFACES", packed_if_str);
+ udev_builtin_add_property(dev, event->event_mode, "ID_USB_INTERFACES", packed_if_str);
if (ifnum)
- udev_builtin_add_property(dev, test, "ID_USB_INTERFACE_NUM", ifnum);
+ udev_builtin_add_property(dev, event->event_mode, "ID_USB_INTERFACE_NUM", ifnum);
if (driver)
- udev_builtin_add_property(dev, test, "ID_USB_DRIVER", driver);
+ udev_builtin_add_property(dev, event->event_mode, "ID_USB_DRIVER", driver);
return 0;
}
return _UDEV_BUILTIN_INVALID;
}
-int udev_builtin_run(UdevEvent *event, UdevBuiltinCommand cmd, const char *command, bool test) {
+int udev_builtin_run(UdevEvent *event, UdevBuiltinCommand cmd, const char *command) {
_cleanup_strv_free_ char **argv = NULL;
int r;
/* we need '0' here to reset the internal state */
optind = 0;
- return builtins[cmd]->cmd(event, strv_length(argv), argv, test);
+ return builtins[cmd]->cmd(event, strv_length(argv), argv);
}
-int udev_builtin_add_property(sd_device *dev, bool test, const char *key, const char *val) {
+int udev_builtin_add_property(sd_device *dev, EventMode mode, const char *key, const char *val) {
int r;
assert(dev);
return log_device_debug_errno(dev, r, "Failed to add property '%s%s%s'",
key, val ? "=" : "", strempty(val));
- if (test)
+ if (mode == EVENT_UDEVADM_TEST_BUILTIN)
printf("%s=%s\n", key, strempty(val));
return 0;
}
-int udev_builtin_add_propertyf(sd_device *dev, bool test, const char *key, const char *valf, ...) {
+int udev_builtin_add_propertyf(sd_device *dev, EventMode mode, const char *key, const char *valf, ...) {
_cleanup_free_ char *val = NULL;
va_list ap;
int r;
if (r < 0)
return log_oom_debug();
- return udev_builtin_add_property(dev, test, key, val);
+ return udev_builtin_add_property(dev, mode, key, val);
}
-int udev_builtin_import_property(sd_device *dev, sd_device *src, bool test, const char *key) {
+int udev_builtin_import_property(sd_device *dev, sd_device *src, EventMode mode, const char *key) {
const char *val;
int r;
if (r < 0)
return log_device_debug_errno(src, r, "Failed to get property \"%s\", ignoring: %m", key);
- r = udev_builtin_add_property(dev, test, key, val);
+ r = udev_builtin_add_property(dev, mode, key, val);
if (r < 0)
return r;
typedef struct UdevBuiltin {
const char *name;
- int (*cmd)(UdevEvent *event, int argc, char *argv[], bool test);
+ int (*cmd)(UdevEvent *event, int argc, char *argv[]);
const char *help;
int (*init)(void);
void (*exit)(void);
UdevBuiltinCommand udev_builtin_lookup(const char *command);
const char *udev_builtin_name(UdevBuiltinCommand cmd);
bool udev_builtin_run_once(UdevBuiltinCommand cmd);
-int udev_builtin_run(UdevEvent *event, UdevBuiltinCommand cmd, const char *command, bool test);
+int udev_builtin_run(UdevEvent *event, UdevBuiltinCommand cmd, const char *command);
void udev_builtin_list(void);
bool udev_builtin_should_reload(void);
-int udev_builtin_add_property(sd_device *dev, bool test, const char *key, const char *val);
-int udev_builtin_add_propertyf(sd_device *dev, bool test, const char *key, const char *valf, ...) _printf_(4, 5);
-int udev_builtin_import_property(sd_device *dev, sd_device *src, bool test, const char *key);
+int udev_builtin_add_property(sd_device *dev, EventMode mode, const char *key, const char *val);
+int udev_builtin_add_propertyf(sd_device *dev, EventMode mode, const char *key, const char *valf, ...) _printf_(4, 5);
+int udev_builtin_import_property(sd_device *dev, sd_device *src, EventMode mode, const char *key);
int udev_builtin_hwdb_lookup(sd_device *dev, const char *prefix, const char *modalias,
- const char *filter, bool test);
+ const char *filter, EventMode mode);
#include "udev-util.h"
#include "user-util.h"
-UdevEvent *udev_event_new(sd_device *dev, UdevWorker *worker) {
+UdevEvent *udev_event_new(sd_device *dev, UdevWorker *worker, EventMode mode) {
int log_level = worker ? worker->log_level : log_get_max_level();
UdevEvent *event;
.mode = MODE_INVALID,
.log_level_was_debug = log_level == LOG_DEBUG,
.default_log_level = log_level,
+ .event_mode = mode,
};
return event;
assert(event);
+ if (!EVENT_MODE_DESTRUCTIVE(event))
+ return 0;
+
if (!event->name)
return 0; /* No new name is requested. */
int ifindex, r;
const char *s;
+ if (!EVENT_MODE_DESTRUCTIVE(event))
+ return 0;
+
if (strv_isempty(event->altnames))
return 0;
sd_device *dev = ASSERT_PTR(ASSERT_PTR(event)->dev);
int r;
+ if (!EVENT_MODE_DESTRUCTIVE(event))
+ return 0;
+
r = sd_device_get_devnum(dev, NULL);
if (r == -ENOENT)
return 0;
if (r < 0)
log_device_debug_errno(dev, r, "Failed to read database under /run/udev/data/: %m");
- r = device_tag_index(dev, NULL, false);
- if (r < 0)
- log_device_debug_errno(dev, r, "Failed to remove corresponding tag files under /run/udev/tag/, ignoring: %m");
+ if (EVENT_MODE_DESTRUCTIVE(event)) {
+ r = device_tag_index(dev, NULL, false);
+ if (r < 0)
+ log_device_debug_errno(dev, r, "Failed to remove corresponding tag files under /run/udev/tag/, ignoring: %m");
- r = device_delete_db(dev);
- if (r < 0)
- log_device_debug_errno(dev, r, "Failed to delete database under /run/udev/data/, ignoring: %m");
+ r = device_delete_db(dev);
+ if (r < 0)
+ log_device_debug_errno(dev, r, "Failed to delete database under /run/udev/data/, ignoring: %m");
+ }
r = udev_rules_apply_to_event(rules, event);
- if (sd_device_get_devnum(dev, NULL) >= 0)
- (void) udev_node_remove(dev);
+ if (EVENT_MODE_DESTRUCTIVE(event)) {
+ if (sd_device_get_devnum(dev, NULL) >= 0)
+ (void) udev_node_remove(dev);
+ }
return r;
}
return 0;
}
+static int update_clone(UdevEvent *event) {
+ sd_device *dev = ASSERT_PTR(ASSERT_PTR(event)->dev_db_clone);
+ int r;
+
+ if (!EVENT_MODE_DESTRUCTIVE(event))
+ return 0;
+
+ /* Drop previously added property for safety to make IMPORT{db}="ID_RENAMING" not work. This is
+ * mostly for 'move' uevent, but let's do unconditionally. Why? If a network interface is renamed in
+ * initrd, then udevd may lose the 'move' uevent during switching root. Usually, we do not set the
+ * persistent flag for network interfaces, but user may set it. Just for safety. */
+
+ r = device_add_property(dev, "ID_RENAMING", NULL);
+ if (r < 0)
+ return log_device_debug_errno(dev, r, "Failed to remove 'ID_RENAMING' property: %m");
+
+ /* If the database file already exists, append ID_PROCESSING property to the existing database,
+ * to indicate that the device is being processed by udevd. */
+ if (device_has_db(dev) > 0) {
+ r = device_add_property(dev, "ID_PROCESSING", "1");
+ if (r < 0)
+ return log_device_warning_errno(dev, r, "Failed to add 'ID_PROCESSING' property: %m");
+
+ r = device_update_db(dev);
+ if (r < 0)
+ return log_device_warning_errno(dev, r, "Failed to update database under /run/udev/data/: %m");
+ }
+
+ return 0;
+}
+
int udev_event_execute_rules(UdevEvent *event, UdevRules *rules) {
sd_device_action_t action;
sd_device *dev;
int r;
- dev = ASSERT_PTR(ASSERT_PTR(event)->dev);
+ assert(event);
+ assert(IN_SET(event->event_mode, EVENT_UDEV_WORKER, EVENT_UDEVADM_TEST, EVENT_TEST_RULE_RUNNER));
+ dev = ASSERT_PTR(event->dev);
assert(rules);
r = sd_device_get_action(dev, &action);
if (r < 0)
log_device_warning_errno(dev, r, "Failed to copy all tags from old database entry, ignoring: %m");
- /* Drop previously added property for safety to make IMPORT{db}="ID_RENAMING" not work. This is
- * mostly for 'move' uevent, but let's do unconditionally. Why? If a network interface is renamed in
- * initrd, then udevd may lose the 'move' uevent during switching root. Usually, we do not set the
- * persistent flag for network interfaces, but user may set it. Just for safety. */
- r = device_add_property(event->dev_db_clone, "ID_RENAMING", NULL);
+ r = update_clone(event);
if (r < 0)
- return log_device_debug_errno(dev, r, "Failed to remove 'ID_RENAMING' property: %m");
-
- /* If the database file already exists, append ID_PROCESSING property to the existing database,
- * to indicate that the device is being processed by udevd. */
- if (device_has_db(event->dev_db_clone) > 0) {
- r = device_add_property(event->dev_db_clone, "ID_PROCESSING", "1");
- if (r < 0)
- return log_device_warning_errno(event->dev_db_clone, r, "Failed to add 'ID_PROCESSING' property: %m");
-
- r = device_update_db(event->dev_db_clone);
- if (r < 0)
- return log_device_warning_errno(event->dev_db_clone, r, "Failed to update database under /run/udev/data/: %m");
- }
+ return r;
DEVICE_TRACE_POINT(rules_start, dev);
if (r < 0)
return log_device_debug_errno(dev, r, "Failed to set initialization timestamp: %m");
- /* (re)write database file */
- r = device_tag_index(dev, event->dev_db_clone, true);
- if (r < 0)
- return log_device_debug_errno(dev, r, "Failed to update tags under /run/udev/tag/: %m");
+ if (EVENT_MODE_DESTRUCTIVE(event)) {
+ /* (re)write database file */
+ r = device_tag_index(dev, event->dev_db_clone, true);
+ if (r < 0)
+ return log_device_debug_errno(dev, r, "Failed to update tags under /run/udev/tag/: %m");
+ }
/* If the database file for the device will be created below, add ID_PROCESSING=1 to indicate that
* the device is still being processed by udevd, as commands specified in RUN are invoked after
return log_device_warning_errno(dev, r, "Failed to add 'ID_PROCESSING' property: %m");
}
- r = device_update_db(dev);
- if (r < 0)
- return log_device_debug_errno(dev, r, "Failed to update database under /run/udev/data/: %m");
+ if (EVENT_MODE_DESTRUCTIVE(event)) {
+ r = device_update_db(dev);
+ if (r < 0)
+ return log_device_debug_errno(dev, r, "Failed to update database under /run/udev/data/: %m");
+ }
device_set_is_initialized(dev);
#include "udev-worker.h"
#include "user-util.h"
+typedef enum EventMode {
+ EVENT_UDEV_WORKER,
+ EVENT_UDEVADM_TEST,
+ EVENT_UDEVADM_TEST_BUILTIN,
+ EVENT_TEST_RULE_RUNNER,
+ EVENT_TEST_SPAWN,
+} EventMode;
+
typedef struct UdevEvent {
UdevWorker *worker;
sd_netlink *rtnl;
bool run_final;
bool log_level_was_debug;
int default_log_level;
+ EventMode event_mode;
} UdevEvent;
-UdevEvent *udev_event_new(sd_device *dev, UdevWorker *worker);
+UdevEvent *udev_event_new(sd_device *dev, UdevWorker *worker, EventMode mode);
UdevEvent *udev_event_free(UdevEvent *event);
DEFINE_TRIVIAL_CLEANUP_FUNC(UdevEvent*, udev_event_free);
int udev_event_execute_rules(UdevEvent *event, UdevRules *rules);
+
+static inline bool EVENT_MODE_DESTRUCTIVE(UdevEvent *event) {
+ assert(event);
+ return IN_SET(event->event_mode, EVENT_UDEV_WORKER, EVENT_TEST_RULE_RUNNER);
+}
return false;
}
- log_event_debug(dev, token, "Running PROGRAM '%s'", buf);
+ log_event_debug(dev, token, "Running PROGRAM=\"%s\"", buf);
r = udev_event_spawn(event, /* accept_failure = */ true, buf, result, sizeof(result), NULL);
if (r != 0) {
log_event_debug(dev, token, "Importing properties from results of builtin command '%s'", buf);
- r = udev_builtin_run(event, cmd, buf, false);
+ r = udev_builtin_run(event, cmd, buf);
if (r < 0) {
/* remember failure */
log_event_debug_errno(dev, token, r, "Failed to run builtin '%s': %m", buf);
break;
}
- log_event_debug(dev, token, "ATTR '%s' writing '%s'", buf, value);
- r = write_string_file(buf, value,
- WRITE_STRING_FILE_VERIFY_ON_FAILURE |
- WRITE_STRING_FILE_DISABLE_BUFFER |
- WRITE_STRING_FILE_AVOID_NEWLINE |
- WRITE_STRING_FILE_VERIFY_IGNORE_NEWLINE);
- if (r < 0)
- log_event_error_errno(dev, token, r, "Failed to write ATTR{%s}, ignoring: %m", buf);
+ if (EVENT_MODE_DESTRUCTIVE(event)) {
+ log_event_debug(dev, token, "Writing ATTR{'%s'}=\"%s\".", buf, value);
+ r = write_string_file(buf, value,
+ WRITE_STRING_FILE_VERIFY_ON_FAILURE |
+ WRITE_STRING_FILE_DISABLE_BUFFER |
+ WRITE_STRING_FILE_AVOID_NEWLINE |
+ WRITE_STRING_FILE_VERIFY_IGNORE_NEWLINE);
+ if (r < 0)
+ log_event_error_errno(dev, token, r, "Failed to write ATTR{%s}=\"%s\", ignoring: %m", buf, value);
+ } else
+ log_event_debug(dev, token, "Running in test mode, skipping writing ATTR{%s}=\"%s\".", buf, value);
+
break;
}
case TK_A_SYSCTL: {
}
sysctl_normalize(buf);
- log_event_debug(dev, token, "SYSCTL '%s' writing '%s'", buf, value);
- r = sysctl_write(buf, value);
- if (r < 0)
- log_event_error_errno(dev, token, r, "Failed to write SYSCTL{%s}='%s', ignoring: %m", buf, value);
+
+ if (EVENT_MODE_DESTRUCTIVE(event)) {
+ log_event_debug(dev, token, "Writing SYSCTL{%s}=\"%s\".", buf, value);
+ r = sysctl_write(buf, value);
+ if (r < 0)
+ log_event_error_errno(dev, token, r, "Failed to write SYSCTL{%s}=\"%s\", ignoring: %m", buf, value);
+ } else
+ log_event_debug(dev, token, "Running in test mode, skipping writing SYSCTL{%s}=\"%s\".", buf, value);
+
break;
}
case TK_A_RUN_BUILTIN:
int r;
assert(event);
+ assert(IN_SET(event->event_mode, EVENT_UDEV_WORKER, EVENT_UDEVADM_TEST, EVENT_TEST_RULE_RUNNER, EVENT_TEST_SPAWN));
assert(event->dev);
+ assert(cmd);
assert(result || result_size == 0);
+ if (event->event_mode == EVENT_UDEVADM_TEST &&
+ !STARTSWITH_SET(cmd, "ata_id", "cdrom_id", "dmi_memory_id", "fido_id", "mtd_probe", "scsi_id")) {
+ log_device_debug(event->dev, "Running in test mode, skipping execution of '%s'.", cmd);
+ result[0] = '\0';
+ ret_truncated = false;
+ return 0;
+ }
+
int timeout_signal = event->worker ? event->worker->timeout_signal : SIGKILL;
usec_t timeout_usec = event->worker ? event->worker->timeout_usec : DEFAULT_WORKER_TIMEOUT_USEC;
usec_t now_usec = now(CLOCK_MONOTONIC);
if (builtin_cmd != _UDEV_BUILTIN_INVALID) {
log_device_debug(event->dev, "Running built-in command \"%s\"", command);
- r = udev_builtin_run(event, builtin_cmd, command, false);
+ r = udev_builtin_run(event, builtin_cmd, command);
if (r < 0)
log_device_debug_errno(event->dev, r, "Failed to run built-in command \"%s\", ignoring: %m", command);
} else {
log_device_uevent(dev, "Processing device");
- udev_event = udev_event_new(dev, worker);
+ udev_event = udev_event_new(dev, worker, EVENT_UDEV_WORKER);
if (!udev_event)
return -ENOMEM;
/* SPDX-License-Identifier: GPL-2.0-or-later */
-/*
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- */
#include <errno.h>
#include <getopt.h>
#include <string.h>
#include <unistd.h>
+#include "creds-util.h"
#include "parse-util.h"
#include "process-util.h"
#include "static-destruct.h"
static int arg_max_children = -1;
static int arg_log_level = -1;
static int arg_start_exec_queue = -1;
+static bool arg_load_credentials = false;
STATIC_DESTRUCTOR_REGISTER(arg_env, strv_freep);
+static bool arg_has_control_commands(void) {
+ return
+ arg_exit ||
+ arg_log_level >= 0 ||
+ arg_start_exec_queue >= 0 ||
+ arg_reload ||
+ !strv_isempty(arg_env) ||
+ arg_max_children >= 0 ||
+ arg_ping;
+}
+
static int help(void) {
printf("%s control OPTION\n\n"
"Control the udev daemon.\n\n"
" -p --property=KEY=VALUE Set a global property for all events\n"
" -m --children-max=N Maximum number of children\n"
" --ping Wait for udev to respond to a ping message\n"
- " -t --timeout=SECONDS Maximum time to block for a reply\n",
+ " -t --timeout=SECONDS Maximum time to block for a reply\n"
+ " --load-credentials Load udev rules from credentials\n",
program_invocation_short_name);
return 0;
static int parse_argv(int argc, char *argv[]) {
enum {
ARG_PING = 0x100,
+ ARG_LOAD_CREDENTIALS,
};
static const struct option options[] = {
- { "exit", no_argument, NULL, 'e' },
- { "log-level", required_argument, NULL, 'l' },
- { "log-priority", required_argument, NULL, 'l' }, /* for backward compatibility */
- { "stop-exec-queue", no_argument, NULL, 's' },
- { "start-exec-queue", no_argument, NULL, 'S' },
- { "reload", no_argument, NULL, 'R' },
- { "reload-rules", no_argument, NULL, 'R' }, /* alias for -R */
- { "property", required_argument, NULL, 'p' },
- { "env", required_argument, NULL, 'p' }, /* alias for -p */
- { "children-max", required_argument, NULL, 'm' },
- { "ping", no_argument, NULL, ARG_PING },
- { "timeout", required_argument, NULL, 't' },
- { "version", no_argument, NULL, 'V' },
- { "help", no_argument, NULL, 'h' },
+ { "exit", no_argument, NULL, 'e' },
+ { "log-level", required_argument, NULL, 'l' },
+ { "log-priority", required_argument, NULL, 'l' }, /* for backward compatibility */
+ { "stop-exec-queue", no_argument, NULL, 's' },
+ { "start-exec-queue", no_argument, NULL, 'S' },
+ { "reload", no_argument, NULL, 'R' },
+ { "reload-rules", no_argument, NULL, 'R' }, /* alias for -R */
+ { "property", required_argument, NULL, 'p' },
+ { "env", required_argument, NULL, 'p' }, /* alias for -p */
+ { "children-max", required_argument, NULL, 'm' },
+ { "ping", no_argument, NULL, ARG_PING },
+ { "timeout", required_argument, NULL, 't' },
+ { "load-credentials", no_argument, NULL, ARG_LOAD_CREDENTIALS },
+ { "version", no_argument, NULL, 'V' },
+ { "help", no_argument, NULL, 'h' },
{}
};
assert(argc >= 0);
assert(argv);
- if (argc <= 1)
- return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
- "This command expects one or more options.");
-
while ((c = getopt_long(argc, argv, "el:sSRp:m:t:Vh", options, NULL)) >= 0)
switch (c) {
return log_error_errno(r, "Failed to parse timeout value '%s': %m", optarg);
break;
+ case ARG_LOAD_CREDENTIALS:
+ arg_load_credentials = true;
+ break;
+
case 'V':
return print_version();
assert_not_reached();
}
+ if (!arg_has_control_commands() && !arg_load_credentials)
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
+ "No control command option is specified.");
+
if (optind < argc)
return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
"Extraneous argument: %s", argv[optind]);
return 1;
}
-int control_main(int argc, char *argv[], void *userdata) {
+static int send_control_commands(void) {
_cleanup_(udev_ctrl_unrefp) UdevCtrl *uctrl = NULL;
int r;
- if (running_in_chroot() > 0) {
- log_info("Running in chroot, ignoring request.");
- return 0;
- }
-
- r = parse_argv(argc, argv);
- if (r <= 0)
- return r;
-
r = udev_ctrl_new(&uctrl);
if (r < 0)
return log_error_errno(r, "Failed to initialize udev control: %m");
return 0;
}
+
+int control_main(int argc, char *argv[], void *userdata) {
+ int r;
+
+ if (running_in_chroot() > 0) {
+ log_info("Running in chroot, ignoring request.");
+ return 0;
+ }
+
+ r = parse_argv(argc, argv);
+ if (r <= 0)
+ return r;
+
+ if (arg_load_credentials) {
+ static const PickUpCredential table[] = {
+ { "udev.conf.", "/run/udev/udev.conf.d/", ".conf" },
+ { "udev.rules.", "/run/udev/rules.d/", ".rules" },
+ };
+ r = pick_up_credentials(table, ELEMENTSOF(table));
+ if (r < 0)
+ return r;
+ }
+
+ if (arg_has_control_commands()) {
+ r = send_control_commands();
+ if (r < 0)
+ return r;
+ }
+
+ return 0;
+}
goto finish;
}
- event = udev_event_new(dev, NULL);
+ event = udev_event_new(dev, NULL, EVENT_UDEVADM_TEST_BUILTIN);
if (!event) {
r = log_oom();
goto finish;
}
}
- r = udev_builtin_run(event, cmd, arg_command, true);
+ r = udev_builtin_run(event, cmd, arg_command);
if (r < 0) {
log_debug_errno(r, "Builtin command '%s' fails: %m", arg_command);
goto finish;
#include "device-private.h"
#include "device-util.h"
+#include "format-util.h"
#include "path-util.h"
#include "string-util.h"
+#include "strv.h"
#include "strxcpyx.h"
+#include "terminal-util.h"
#include "udev-builtin.h"
#include "udev-event.h"
#include "udev-format.h"
#include "udevadm-util.h"
#include "udevadm.h"
+#include "user-util.h"
static sd_device_action_t arg_action = SD_DEVICE_ADD;
static ResolveNameTiming arg_resolve_name_timing = RESOLVE_NAME_EARLY;
_cleanup_(udev_rules_freep) UdevRules *rules = NULL;
_cleanup_(udev_event_freep) UdevEvent *event = NULL;
_cleanup_(sd_device_unrefp) sd_device *dev = NULL;
- const char *cmd;
sigset_t mask, sigmask_orig;
- void *val;
int r;
log_set_max_level(LOG_DEBUG);
/* don't read info from the db */
device_seal(dev);
- event = udev_event_new(dev, NULL);
+ event = udev_event_new(dev, NULL, EVENT_UDEVADM_TEST);
assert_se(sigfillset(&mask) >= 0);
assert_se(sigprocmask(SIG_SETMASK, &mask, &sigmask_orig) >= 0);
udev_event_execute_rules(event, rules);
+ printf("%sProperties:%s\n", ansi_highlight(), ansi_normal());
FOREACH_DEVICE_PROPERTY(dev, key, value)
- printf("%s=%s\n", key, value);
+ printf(" %s=%s\n", key, value);
- ORDERED_HASHMAP_FOREACH_KEY(val, cmd, event->run_list) {
- char program[UDEV_PATH_SIZE];
- bool truncated;
+ if (sd_device_get_tag_first(dev)) {
+ printf("%sTags:%s\n", ansi_highlight(), ansi_normal());
+ FOREACH_DEVICE_TAG(dev, tag)
+ printf(" %s\n", tag);
+ }
+
+ if (sd_device_get_devnum(dev, NULL) >= 0) {
+
+ if (sd_device_get_devlink_first(dev)) {
+ int prio;
+ device_get_devlink_priority(dev, &prio);
+ printf("%sDevice node symlinks:%s (priority=%i)\n", ansi_highlight(), ansi_normal(), prio);
+ FOREACH_DEVICE_DEVLINK(dev, devlink)
+ printf(" %s\n", devlink);
+ }
+
+ printf("%sInotify watch:%s\n %s\n", ansi_highlight(), ansi_normal(), enabled_disabled(event->inotify_watch));
+
+ uid_t uid = event->uid;
+ if (!uid_is_valid(uid))
+ (void) device_get_devnode_uid(dev, &uid);
+ if (uid_is_valid(uid)) {
+ _cleanup_free_ char *user = uid_to_name(uid);
+ printf("%sDevice node owner:%s\n %s (uid="UID_FMT")\n", ansi_highlight(), ansi_normal(), strna(user), uid);
+ }
+
+ gid_t gid = event->gid;
+ if (!gid_is_valid(uid))
+ (void) device_get_devnode_gid(dev, &gid);
+ if (gid_is_valid(gid)) {
+ _cleanup_free_ char *group = gid_to_name(gid);
+ printf("%sDevice node group:%s\n %s (gid="GID_FMT")\n", ansi_highlight(), ansi_normal(), strna(group), gid);
+ }
- (void) udev_event_apply_format(event, cmd, program, sizeof(program), false, &truncated);
- if (truncated)
- log_warning("The command '%s' is truncated while substituting into '%s'.", program, cmd);
- printf("run: '%s'\n", program);
+ mode_t mode = event->mode;
+ if (mode == MODE_INVALID)
+ (void) device_get_devnode_mode(dev, &mode);
+ if (mode != MODE_INVALID)
+ printf("%sDevice node permission:%s\n %04o\n", ansi_highlight(), ansi_normal(), mode);
+
+ if (!ordered_hashmap_isempty(event->seclabel_list)) {
+ const char *name, *label;
+ printf("%sDevice node security label:%s\n", ansi_highlight(), ansi_normal());
+ ORDERED_HASHMAP_FOREACH_KEY(label, name, event->seclabel_list)
+ printf(" %s : %s\n", name, label);
+ }
+ }
+
+ if (sd_device_get_ifindex(dev, NULL) >= 0) {
+ if (!isempty(event->name))
+ printf("%sNetwork interface name:%s\n %s\n", ansi_highlight(), ansi_normal(), event->name);
+
+ if (!strv_isempty(event->altnames)) {
+ bool space = true;
+ printf("%sAlternative interface names:%s", ansi_highlight(), ansi_normal());
+ fputstrv(stdout, event->altnames, "\n ", &space);
+ puts("");
+ }
+ }
+
+ if (!ordered_hashmap_isempty(event->run_list)) {
+ void *val;
+ const char *command;
+ printf("%sQueued commands:%s\n", ansi_highlight(), ansi_normal());
+ ORDERED_HASHMAP_FOREACH_KEY(val, command, event->run_list) {
+ UdevBuiltinCommand builtin_cmd = PTR_TO_UDEV_BUILTIN_CMD(val);
+
+ if (builtin_cmd != _UDEV_BUILTIN_INVALID)
+ printf(" RUN{builtin} : %s\n", command);
+ else
+ printf(" RUN{program} : %s\n", command);
+ }
}
r = 0;
return r;
if (arg_wait_until == WAIT_UNTIL_INITIALIZED)
- return sd_device_get_is_initialized(dev);
+ return device_is_processed(dev);
return true;
}
/* SPDX-License-Identifier: GPL-2.0-or-later */
/*
* Copyright (c) 2009 Filippo Argiolas <filippo.argiolas@gmail.com>
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License as
- * published by the Free Software Foundation; either version 2 of the
- * License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * General Public License for more details:
*/
#include <ctype.h>
assert(table);
- for (size_t i = 0; i < ELEMENTSOF(uid_range_table); i++) {
+ FOREACH_ARRAY(i, uid_range_table, ELEMENTSOF(uid_range_table)) {
_cleanup_free_ char *name = NULL, *comment = NULL;
- if (!uid_range_covers(p, uid_range_table[i].first, uid_range_table[i].last - uid_range_table[i].first + 1))
+ if (!uid_range_covers(p, i->first, i->last - i->first + 1))
continue;
name = strjoin(special_glyph(SPECIAL_GLYPH_ARROW_DOWN),
- " begin ", uid_range_table[i].name, " users ",
+ " begin ", i->name, " users ",
special_glyph(SPECIAL_GLYPH_ARROW_DOWN));
if (!name)
return log_oom();
- comment = strjoin("First ", uid_range_table[i].name, " user");
+ comment = strjoin("First ", i->name, " user");
if (!comment)
return log_oom();
TABLE_STRING, special_glyph(SPECIAL_GLYPH_TREE_TOP),
TABLE_STRING, name,
TABLE_SET_COLOR, ansi_grey(),
- TABLE_STRING, user_disposition_to_string(uid_range_table[i].disposition),
+ TABLE_STRING, user_disposition_to_string(i->disposition),
TABLE_SET_COLOR, ansi_grey(),
- TABLE_UID, uid_range_table[i].first,
+ TABLE_UID, i->first,
TABLE_SET_COLOR, ansi_grey(),
TABLE_EMPTY,
TABLE_STRING, comment,
free(name);
name = strjoin(special_glyph(SPECIAL_GLYPH_ARROW_UP),
- " end ", uid_range_table[i].name, " users ",
+ " end ", i->name, " users ",
special_glyph(SPECIAL_GLYPH_ARROW_UP));
if (!name)
return log_oom();
free(comment);
- comment = strjoin("Last ", uid_range_table[i].name, " user");
+ comment = strjoin("Last ", i->name, " user");
if (!comment)
return log_oom();
TABLE_STRING, special_glyph(SPECIAL_GLYPH_TREE_RIGHT),
TABLE_STRING, name,
TABLE_SET_COLOR, ansi_grey(),
- TABLE_STRING, user_disposition_to_string(uid_range_table[i].disposition),
+ TABLE_STRING, user_disposition_to_string(i->disposition),
TABLE_SET_COLOR, ansi_grey(),
- TABLE_UID, uid_range_table[i].last,
+ TABLE_UID, i->last,
TABLE_SET_COLOR, ansi_grey(),
TABLE_EMPTY,
TABLE_STRING, comment,
assert(table);
assert(add_unavailable);
- for (size_t i = 0; p && i < p->n_entries; i++) {
- UIDRangeEntry *x = p->entries + i;
+ if (!p)
+ return 0;
+ FOREACH_ARRAY(x, p->entries, p->n_entries) {
if (focus < x->start) {
r = add_unavailable(table, focus, x->start-1);
if (r < 0)
_cleanup_(uid_range_freep) UIDRange *uid_range = NULL;
int boundary_lines, uid_map_lines;
- r = uid_range_load_userns(&uid_range, "/proc/self/uid_map");
+ r = uid_range_load_userns(/* path = */ NULL, UID_RANGE_USERNS_INSIDE, &uid_range);
if (r < 0)
log_debug_errno(r, "Failed to load /proc/self/uid_map, ignoring: %m");
assert(table);
- for (size_t i = 0; i < ELEMENTSOF(uid_range_table); i++) {
+ FOREACH_ARRAY(i, uid_range_table, ELEMENTSOF(uid_range_table)) {
_cleanup_free_ char *name = NULL, *comment = NULL;
- if (!uid_range_covers(p, uid_range_table[i].first, uid_range_table[i].last))
+ if (!uid_range_covers(p, i->first, i->last - i->first + 1))
continue;
name = strjoin(special_glyph(SPECIAL_GLYPH_ARROW_DOWN),
- " begin ", uid_range_table[i].name, " groups ",
+ " begin ", i->name, " groups ",
special_glyph(SPECIAL_GLYPH_ARROW_DOWN));
if (!name)
return log_oom();
- comment = strjoin("First ", uid_range_table[i].name, " group");
+ comment = strjoin("First ", i->name, " group");
if (!comment)
return log_oom();
TABLE_STRING, special_glyph(SPECIAL_GLYPH_TREE_TOP),
TABLE_STRING, name,
TABLE_SET_COLOR, ansi_grey(),
- TABLE_STRING, user_disposition_to_string(uid_range_table[i].disposition),
+ TABLE_STRING, user_disposition_to_string(i->disposition),
TABLE_SET_COLOR, ansi_grey(),
- TABLE_GID, uid_range_table[i].first,
+ TABLE_GID, i->first,
TABLE_SET_COLOR, ansi_grey(),
TABLE_STRING, comment,
TABLE_SET_COLOR, ansi_grey(),
free(name);
name = strjoin(special_glyph(SPECIAL_GLYPH_ARROW_UP),
- " end ", uid_range_table[i].name, " groups ",
+ " end ", i->name, " groups ",
special_glyph(SPECIAL_GLYPH_ARROW_UP));
if (!name)
return log_oom();
free(comment);
- comment = strjoin("Last ", uid_range_table[i].name, " group");
+ comment = strjoin("Last ", i->name, " group");
if (!comment)
return log_oom();
TABLE_STRING, special_glyph(SPECIAL_GLYPH_TREE_RIGHT),
TABLE_STRING, name,
TABLE_SET_COLOR, ansi_grey(),
- TABLE_STRING, user_disposition_to_string(uid_range_table[i].disposition),
+ TABLE_STRING, user_disposition_to_string(i->disposition),
TABLE_SET_COLOR, ansi_grey(),
- TABLE_GID, uid_range_table[i].last,
+ TABLE_GID, i->last,
TABLE_SET_COLOR, ansi_grey(),
TABLE_STRING, comment,
TABLE_SET_COLOR, ansi_grey(),
_cleanup_(uid_range_freep) UIDRange *gid_range = NULL;
int boundary_lines, gid_map_lines;
- r = uid_range_load_userns(&gid_range, "/proc/self/gid_map");
+ r = uid_range_load_userns(/* path = */ NULL, GID_RANGE_USERNS_INSIDE, &gid_range);
if (r < 0)
log_debug_errno(r, "Failed to load /proc/self/gid_map, ignoring: %m");
if (r < 0)
return bus_log_create_error(r);
- r = sd_bus_message_append(m, "(sa)(sa)(sa)",
+ r = sd_bus_message_append(m, "(sv)(sv)(sv)",
"Description", "s", description,
"AddRef", "b", 1,
"CollectMode", "s", "inactive-or-failed");
+ if (r < 0)
+ return bus_log_create_error(r);
_cleanup_(pidref_done) PidRef pidref = PIDREF_NULL;
r = pidref_set_self(&pidref);
#include "strv.h"
#include "vmspawn-util.h"
+static const char* const architecture_to_qemu_table[_ARCHITECTURE_MAX] = {
+ [ARCHITECTURE_ARM64] = "aarch64", /* differs from our name */
+ [ARCHITECTURE_ARM] = "arm",
+ [ARCHITECTURE_ALPHA] = "alpha",
+ [ARCHITECTURE_X86_64] = "x86_64", /* differs from our name */
+ [ARCHITECTURE_X86] = "i386", /* differs from our name */
+ [ARCHITECTURE_LOONGARCH64] = "loongarch64",
+ [ARCHITECTURE_MIPS64_LE] = "mips", /* differs from our name */
+ [ARCHITECTURE_MIPS_LE] = "mips", /* differs from our name */
+ [ARCHITECTURE_PARISC] = "hppa", /* differs from our name */
+ [ARCHITECTURE_PPC64_LE] = "ppc", /* differs from our name */
+ [ARCHITECTURE_PPC64] = "ppc", /* differs from our name */
+ [ARCHITECTURE_PPC] = "ppc",
+ [ARCHITECTURE_RISCV32] = "riscv32",
+ [ARCHITECTURE_RISCV64] = "riscv64",
+ [ARCHITECTURE_S390X] = "s390x",
+};
+
+static int native_arch_as_qemu(const char **ret) {
+ const char *s = architecture_to_qemu_table[native_architecture()];
+ if (!s)
+ return log_debug_errno(SYNTHETIC_ERRNO(EOPNOTSUPP), "Architecture %s not supported by qemu", architecture_to_string(native_architecture()));
+
+ if (ret)
+ *ret = s;
+
+ return 0;
+}
+
OvmfConfig* ovmf_config_free(OvmfConfig *config) {
if (!config)
return NULL;
char *firmware_format;
char *vars;
char *vars_format;
+ char **architectures;
} FirmwareData;
static bool firmware_data_supports_sb(const FirmwareData *fwd) {
free(fwd->firmware_format);
free(fwd->vars);
free(fwd->vars_format);
+ strv_free(fwd->architectures);
return mfree(fwd);
}
return json_dispatch(v, table, flags, userdata);
}
+static int target_architecture(const char *name, JsonVariant *v, JsonDispatchFlags flags, void *userdata) {
+ int r;
+ JsonVariant *e;
+ char ***supported_architectures = ASSERT_PTR(userdata);
+
+ static const JsonDispatch table[] = {
+ { "architecture", JSON_VARIANT_STRING, json_dispatch_string, 0, JSON_MANDATORY },
+ { "machines", JSON_VARIANT_ARRAY, NULL, 0, JSON_MANDATORY },
+ {}
+ };
+
+ JSON_VARIANT_ARRAY_FOREACH(e, v) {
+ _cleanup_free_ char *arch = NULL;
+
+ r = json_dispatch(e, table, flags, &arch);
+ if (r < 0)
+ return r;
+
+ r = strv_consume(supported_architectures, TAKE_PTR(arch));
+ if (r < 0)
+ return r;
+ }
+
+ return 0;
+}
+
static int get_firmware_search_dirs(char ***ret) {
int r;
return r;
static const JsonDispatch table[] = {
- { "description", JSON_VARIANT_STRING, NULL, 0, JSON_MANDATORY },
- { "interface-types", JSON_VARIANT_ARRAY, NULL, 0, JSON_MANDATORY },
- { "mapping", JSON_VARIANT_OBJECT, firmware_mapping, 0, JSON_MANDATORY },
- { "targets", JSON_VARIANT_ARRAY, NULL, 0, JSON_MANDATORY },
- { "features", JSON_VARIANT_ARRAY, json_dispatch_strv, offsetof(FirmwareData, features), JSON_MANDATORY },
- { "tags", JSON_VARIANT_ARRAY, NULL, 0, JSON_MANDATORY },
+ { "description", JSON_VARIANT_STRING, NULL, 0, JSON_MANDATORY },
+ { "interface-types", JSON_VARIANT_ARRAY, NULL, 0, JSON_MANDATORY },
+ { "mapping", JSON_VARIANT_OBJECT, firmware_mapping, 0, JSON_MANDATORY },
+ { "targets", JSON_VARIANT_ARRAY, target_architecture, offsetof(FirmwareData, architectures), JSON_MANDATORY },
+ { "features", JSON_VARIANT_ARRAY, json_dispatch_strv, offsetof(FirmwareData, features), JSON_MANDATORY },
+ { "tags", JSON_VARIANT_ARRAY, NULL, 0, JSON_MANDATORY },
{}
};
int find_ovmf_config(int search_sb, OvmfConfig **ret) {
_cleanup_(ovmf_config_freep) OvmfConfig *config = NULL;
_cleanup_strv_free_ char **conf_files = NULL;
+ const char* native_arch_qemu;
int r;
assert(ret);
+ r = native_arch_as_qemu(&native_arch_qemu);
+ if (r < 0)
+ return r;
+
/* Search in:
* - $XDG_CONFIG_HOME/qemu/firmware
* - /etc/qemu/firmware
continue;
}
+ if (!strv_contains(fwd->architectures, native_arch_qemu)) {
+ log_debug("Skipping %s, firmware doesn't support the native architecture.", *file);
+ continue;
+ }
+
/* exclude firmware which doesn't match our Secure Boot requirements */
if (search_sb >= 0 && !!search_sb != firmware_data_supports_sb(fwd)) {
log_debug("Skipping %s, firmware doesn't fit required Secure Boot configuration.", *file);
}
int find_qemu_binary(char **ret_qemu_binary) {
+ const char *native_arch_qemu;
int r;
/*
* If the native architecture is not supported by qemu -EOPNOTSUPP will be returned;
*/
- static const char *architecture_to_qemu_table[_ARCHITECTURE_MAX] = {
- [ARCHITECTURE_ARM64] = "aarch64", /* differs from our name */
- [ARCHITECTURE_ARM] = "arm",
- [ARCHITECTURE_ALPHA] = "alpha",
- [ARCHITECTURE_X86_64] = "x86_64", /* differs from our name */
- [ARCHITECTURE_X86] = "i386", /* differs from our name */
- [ARCHITECTURE_LOONGARCH64] = "loongarch64",
- [ARCHITECTURE_MIPS64_LE] = "mips", /* differs from our name */
- [ARCHITECTURE_MIPS_LE] = "mips", /* differs from our name */
- [ARCHITECTURE_PARISC] = "hppa", /* differs from our name */
- [ARCHITECTURE_PPC64_LE] = "ppc", /* differs from our name */
- [ARCHITECTURE_PPC64] = "ppc", /* differs from our name */
- [ARCHITECTURE_PPC] = "ppc",
- [ARCHITECTURE_RISCV32] = "riscv32",
- [ARCHITECTURE_RISCV64] = "riscv64",
- [ARCHITECTURE_S390X] = "s390x",
- };
-
FOREACH_STRING(s, "qemu", "qemu-kvm") {
r = find_executable(s, ret_qemu_binary);
if (r == 0)
return r;
}
- const char *arch_qemu = architecture_to_qemu_table[native_architecture()];
- if (!arch_qemu)
- return log_debug_errno(SYNTHETIC_ERRNO(EOPNOTSUPP), "Architecture %s not supported by qemu", architecture_to_string(native_architecture()));
+ r = native_arch_as_qemu(&native_arch_qemu);
+ if (r < 0)
+ return r;
_cleanup_free_ char *qemu_arch_specific = NULL;
- qemu_arch_specific = strjoin("qemu-system-", arch_qemu);
+ qemu_arch_specific = strjoin("qemu-system-", native_arch_qemu);
if (!qemu_arch_specific)
return -ENOMEM;
static char *arg_background = NULL;
static bool arg_pass_ssh_key = true;
static char *arg_ssh_key_type = NULL;
+static bool arg_discard_disk = true;
STATIC_DESTRUCTOR_REGISTER(arg_directory, freep);
STATIC_DESTRUCTOR_REGISTER(arg_image, freep);
" --network-user-mode Use user mode networking\n"
" --secure-boot=BOOL Enable searching for firmware supporting SecureBoot\n"
" --firmware=PATH|list Select firmware definition file (or list available)\n"
+ " --discard-disk=BOOL Control processing of discard requests\n"
"\n%3$sSystem Identity:%4$s\n"
" -M --machine=NAME Set the machine name for the VM\n"
" --uuid=UUID Set a specific machine UUID for the VM\n"
ARG_SET_CREDENTIAL,
ARG_LOAD_CREDENTIAL,
ARG_FIRMWARE,
+ ARG_DISCARD_DISK,
ARG_CONSOLE,
ARG_BACKGROUND,
};
{ "set-credential", required_argument, NULL, ARG_SET_CREDENTIAL },
{ "load-credential", required_argument, NULL, ARG_LOAD_CREDENTIAL },
{ "firmware", required_argument, NULL, ARG_FIRMWARE },
+ { "discard-disk", required_argument, NULL, ARG_DISCARD_DISK },
{ "background", required_argument, NULL, ARG_BACKGROUND },
{}
};
break;
case ARG_REGISTER:
- r = parse_boolean(optarg);
- if (r < 0) {
- log_error("Failed to parse --register= argument: %s", optarg);
+ r = parse_boolean_argument("--register=", optarg, &arg_register);
+ if (r < 0)
return r;
- }
-
- arg_register = r;
break;
case ARG_BIND:
break;
case ARG_PASS_SSH_KEY:
- r = parse_boolean(optarg);
+ r = parse_boolean_argument("--pass-ssh-key=", optarg, &arg_pass_ssh_key);
if (r < 0)
- return log_error_errno(r, "Failed to parse --pass-ssh-key= argument: %s", optarg);
-
- arg_pass_ssh_key = r;
+ return r;
break;
case ARG_SSH_KEY_TYPE:
}
if (!isempty(optarg) && !path_is_absolute(optarg) && !startswith(optarg, "./"))
- return log_error_errno(SYNTHETIC_ERRNO(errno), "Absolute path or path starting with './' required.");
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Absolute path or path starting with './' required.");
r = parse_path_argument(optarg, /* suppress_root= */ false, &arg_firmware);
if (r < 0)
break;
+ case ARG_DISCARD_DISK:
+ r = parse_boolean_argument("--discard-disk=", optarg, &arg_discard_disk);
+ if (r < 0)
+ return r;
+ break;
+
case ARG_BACKGROUND:
r = free_and_strdup_warn(&arg_background, optarg);
if (r < 0)
"-smp", arg_cpus ?: "1",
"-m", mem,
"-object", "rng-random,filename=/dev/urandom,id=rng0",
- "-device", "virtio-rng-pci,rng=rng0,id=rng-device0"
+ "-device", "virtio-rng-pci,rng=rng0,id=rng-device0",
+ "-device", "virtio-balloon,free-page-reporting=on"
);
if (!cmdline)
return log_oom();
if (!escaped_image)
log_oom();
- r = strv_extendf(&cmdline, "if=none,id=mkosi,file=%s,format=raw", escaped_image);
+ r = strv_extendf(&cmdline, "if=none,id=mkosi,file=%s,format=raw,discard=%s", escaped_image, on_off(arg_discard_disk));
if (r < 0)
return log_oom();
values list (obviously), which is the most human-friendly output format the
CodeQL utility provides (so far).
+Running Coverity locally
+========================
+
+Note: this requires a Coverity license, as the public tool tarball (from [0])
+doesn't contain cov-analyze and friends, so the usefulness of this guide is
+somewhat limited.
+
+Debugging certain pesky Coverity defects can be painful, especially since the
+OSS Coverity instance has a very strict limit on how many builds we can send it
+per day/week, so if you have an access to a non-OSS Coverity license, knowing
+how to debug defects locally might come in handy.
+
+After installing the necessary tooling we need to populate the emit DB first:
+
+$ rm -rf build cov
+$ meson setup build -Dman=false
+$ cov-build --dir=./cov ninja -C build
+
+From there it depends if you're interested in a specific defect or all of them.
+For the latter run:
+
+$ cov-analyze --dir=./cov --wait-for-license
+
+If you want to debug a specific defect, telling that to cov-analyze speeds
+things up a bit:
+
+$ cov-analyze --dir=./cov --wait-for-license --disable-default --enable ASSERT_SIDE_EFFECT
+
+The final step is getting the actual report which can be generated in multiple
+formats, for example:
+
+$ cov-format-errors --dir ./cov --text-output-style multiline
+$ cov-format-errors --dir=./cov --emacs-style
+$ cov-format-errors --dir=./cov --html-output html-out
+
+Which generate a text report, an emacs-compatible text report, and an HTML
+report respectively.
+
+Other useful options for cov-format-error include --file <file> to filter out
+defects for a specific file, --checker-regex DEFECT_TYPE to filter our only a
+specific defect (if this wasn't done already by cov-analyze), and many others,
+see --help for an exhaustive list.
+
+[0] https://scan.coverity.com/download
+
Code coverage
=============
"luks.name=$PART_UUID=$DM_NAME"
"luks.key=$PART_UUID=/keyfile:LABEL=varcrypt_keydev"
"luks.options=$PART_UUID=x-initrd.attach"
+ # Forward journal to console to make debugging easier (or possible at all) if we fail to bring the
+ # encrypted /var up during boot
+ "systemd.journald.forward_to_console=1"
)
KERNEL_APPEND+=" ${KERNEL_OPTIONS[*]}"
QEMU_OPTIONS+=" -drive format=raw,cache=unsafe,file=${STATEDIR:?}/keydev.img"
# shellcheck source=test/test-functions
. "${TEST_BASE_DIR:?}/test-functions"
+# On Ubuntu the BPF LSM is not enabled by default, so we need to do it via the
+# kernel command line on boot
+if [ "$LOOKS_LIKE_UBUNTU" = "yes" ]; then
+ KERNEL_OPTIONS=(
+ "lsm=lockdown,capability,landlock,yama,apparmor,bpf"
+ )
+ KERNEL_APPEND+=" ${KERNEL_OPTIONS[*]}"
+fi
+
test_require_bin mksquashfs veritysetup sfdisk
test_append_files() {
)
NSPAWN_ARGUMENTS="${NSPAWN_ARGUMENTS:-} ${NSPAWN_CREDS[*]}"
+UNIT_CRED=$(base64 -w 0 <<EOF
+[Service]
+Type=oneshot
+ExecStart=touch /tmp/unit-cred
+EOF
+)
+DROPIN_CRED=$(base64 -w 0 <<EOF
+[Service]
+ExecStart=
+ExecStart=touch /tmp/unit-dropin
+EOF
+)
+
QEMU_CREDS=(
"-fw_cfg name=opt/io.systemd.credentials/myqemucredential,string=othervalue"
"-smbios type=11,value=io.systemd.credential:smbioscredential=magicdata"
"-smbios type=11,value=io.systemd.credential.binary:tmpfiles.extra=ZiAvdG1wL3NvdXJjZWRmcm9tY3JlZGVudGlhbCAtIC0gLSAtIHRtcGZpbGVzc2VjcmV0Cg=="
"-smbios type=11,value=io.systemd.credential.binary:fstab.extra=aW5qZWN0ZWQgL2luamVjdGVkIHRtcGZzIFgtbW91bnQubWtkaXIgMCAwCg=="
"-smbios type=11,value=io.systemd.credential:getty.ttys.container=idontexist"
+ "-smbios type=11,value=io.systemd.credential.binary:systemd.extra-unit.my-service.service=$UNIT_CRED"
+ "-smbios type=11,value=io.systemd.credential.binary:systemd.unit-dropin.my-service.service=$DROPIN_CRED"
)
QEMU_OPTIONS="${QEMU_OPTIONS:-} ${QEMU_CREDS[*]}"
# and attach them to a virtio-scsi controller
local qemu_opts=("-device virtio-scsi-pci,id=scsi0,num_queues=4")
local diskpath="${TESTDIR:?}/namedpart0.img"
- local i lodev num_disk num_part qemu_timeout
+ local i num_disk qemu_timeout
if get_bool "${IS_BUILT_WITH_ASAN:=}" || ! get_bool "$QEMU_KVM"; then
num_disk=4
- num_part=4
else
num_disk=16
- num_part=8
fi
dd if=/dev/zero of="$diskpath" bs=1M count=18
- lodev="$(losetup --show -f -P "$diskpath")"
- sfdisk "${lodev:?}" <<EOF
-label: gpt
-
-$(for ((i = 1; i <= num_part; i++)); do echo 'name="Hello world", size=2M'; done)
-EOF
- losetup -d "$lodev"
for ((i = 0; i < num_disk; i++)); do
diskpath="${TESTDIR:?}/namedpart$i.img"
local qemu_opts=("-device virtio-scsi-pci,id=scsi")
local partdisk="${TESTDIR:?}/multipathpartitioned.img"
- local image lodev nback ndisk wwn
+ local image nback ndisk wwn
dd if=/dev/zero of="$partdisk" bs=1M count=16
- lodev="$(losetup --show -f -P "$partdisk")"
- sfdisk "${lodev:?}" <<EOF
-label: gpt
-
-name="first_partition", size=5M
-uuid="deadbeef-dead-dead-beef-000000000000", name="failover_part", size=5M
-EOF
- udevadm settle
- mkfs.ext4 -U "deadbeef-dead-dead-beef-111111111111" -L "failover_vol" "${lodev}p2"
- losetup -d "$lodev"
# Add 16 multipath devices, each backed by 4 paths
for ndisk in {0..15}; do
)
dd if=/dev/zero of="$testdisk" bs=1M count=64
- lodev="$(losetup --show -f -P "$testdisk")"
- sfdisk "${lodev:?}" <<EOF
-label: gpt
-
-name="test_swap", size=32M
-uuid="deadbeef-dead-dead-beef-000000000000", name="test_part", size=5M
-EOF
- udevadm settle
- mkswap -U "deadbeef-dead-dead-beef-111111111111" -L "swap_vol" "${lodev}p1"
- mkfs.ext4 -U "deadbeef-dead-dead-beef-222222222222" -L "data_vol" "${lodev}p2"
- losetup -d "$lodev"
-
# Create 25 additional PCI bridges, each one connected to the previous one
# (basically a really long extension cable), and attach a virtio drive to
# the last one. This should force udev into attempting to create a device
setup_nspawn_root_hook() {
cat >"${STATEDIR:?}/run-nspawn" <<EOF
#!/bin/bash
-exec "${TEST_BASE_DIR:?}/test-shutdown.py" -v -- "$_ORIG_NSPAWN" "\$@"
+exec "${TEST_BASE_DIR:?}/test-shutdown.py" -v -- "$_ORIG_NSPAWN" --background= "\$@"
exit 1
EOF
chmod 755 "${STATEDIR:?}"/run-nspawn
+++ /dev/null
-../TEST-01-BASIC/Makefile
\ No newline at end of file
+++ /dev/null
-#!/usr/bin/env bash
-# SPDX-License-Identifier: LGPL-2.1-or-later
-set -e
-
-TEST_DESCRIPTION="Openfile tests"
-
-# shellcheck source=test/test-functions
-. "${TEST_BASE_DIR:?}/test-functions"
-
-test_append_files() {
- local workspace="${1:?}"
- echo "Open" >"$workspace/test-77-open.dat"
- echo "File" >"$workspace/test-77-file.dat"
-}
-
-do_test "$@"
--- /dev/null
+# SPDX-License-Identifier: LGPL-2.1-or-later
+[Unit]
+Description=Test for Personality=s390x
+
+[Service]
+ExecStart=sh -x -c 'c=$$(uname -m); test "$$c" = "s390x"'
+Type=oneshot
+Personality=s390x
# shellcheck source=/dev/null
source "$os_release"
[[ "$ID" == "debian" || " $ID_LIKE " == *" debian "* ]] && LOOKS_LIKE_DEBIAN=yes || LOOKS_LIKE_DEBIAN=no
+# shellcheck disable=SC2034
+[[ "$ID" == "ubuntu" ]] && LOOKS_LIKE_UBUNTU=yes || LOOKS_LIKE_UBUNTU=no
[[ "$ID" == "arch" || " $ID_LIKE " == *" arch "* ]] && LOOKS_LIKE_ARCH=yes || LOOKS_LIKE_ARCH=no
[[ "$ID" == "fedora" ]] && LOOKS_LIKE_FEDORA=yes || LOOKS_LIKE_FEDORA=no
[[ " $ID_LIKE " == *" suse "* ]] && LOOKS_LIKE_SUSE=yes || LOOKS_LIKE_SUSE=no
BASICTOOLS=(
bash
cat
+ echo
grep
mount
sleep
# we use mkfs in system-repart tests
image_install /sbin/mkfs.ext4
image_install /sbin/mkfs.vfat
+
+ # we use mkswap in udev-storage tests
+ image_install /sbin/mkswap
}
install_modules() {
# units using DynamicUser=yes. Do this only for services with test- prefix and a couple of
# known-to-use DynamicUser=yes services, as setting this system-wide has many undesirable
# side-effects, as it creates its own namespace.
- for service in test- systemd-journal-{gatewayd,upload}; do
+ for service in capsule@ test- systemd-journal-{gatewayd,upload}; do
mkdir -p "$initdir/etc/systemd/system/$service.service.d/"
echo -ne "[Service]\nReadWritePaths=${BUILD_DIR:?}\n" >"$initdir/etc/systemd/system/$service.service.d/99-gcov-rwpaths-override.conf"
done
local lib path
# A number of dependencies is now optional via dlopen, so the install
# script will not pick them up, since it looks at linkage.
- for lib in libcryptsetup libidn libidn2 pwquality libqrencode tss2-esys tss2-rc tss2-mu tss2-tcti-device libfido2 libbpf libelf libdw xkbcommon p11-kit-1 libarchive; do
+ for lib in libcryptsetup libidn libidn2 pwquality libqrencode tss2-esys tss2-rc tss2-mu tss2-tcti-device libfido2 libbpf libelf libdw xkbcommon p11-kit-1 libarchive libgcrypt libkmod; do
ddebug "Searching for $lib via pkg-config"
if pkg-config --exists "$lib"; then
path="$(pkg-config --variable=libdir "$lib")"
rm -rf "${WORK_DIR:?}"/*
stderr="$WORK_DIR/stderr"
- if ! "$GENERATOR_BIN" --root "$WORK_DIR" 2>"$stderr"; then
+ if ! SYSTEMD_LOG_LEVEL="info" "$GENERATOR_BIN" --root "$WORK_DIR" 2>"$stderr"; then
echo >&2 "Generator failed when parsing $SYSTEMD_PROC_CMDLINE"
cat >&2 "$stderr"
return 1
IPv6AcceptRA=no
Address=2600::1/0
Address=192.168.5.1/24
+# To make the kernel send NA with IsRouter flag.
+IPv6Forwarding=yes
IPv6AcceptRA=true
[IPv6AcceptRA]
-Token=prefixstable:2002:da8:1::
-Token=prefixstable:2002:da8:1::,86b123b969ba4b7eb8b3d8605123525a
# invalid tokens
Token=prefixstable:2002:da8:1::,00000000000000000000000000000000
Token=prefixstable:2002:da8:1::,
Token=static
Token=static:
Token=static:::
+# valid token
+Token=prefixstable:2002:da8:1::
+Token=prefixstable:2002:da8:1::,86b123b969ba4b7eb8b3d8605123525a
+# reset token
+Token=
+# set token again
+Token=prefixstable:2002:da8:1::,86b123b969ba4b7eb8b3d8605123525a
--- /dev/null
+# SPDX-License-Identifier: LGPL-2.1-or-later
+[Match]
+Name=veth-peer
+
+[Network]
+IPv6AcceptRA=no
--- /dev/null
+# SPDX-License-Identifier: LGPL-2.1-or-later
+[DHCPServer]
+PersistLeases=no
resolvectl_bin = shutil.which('resolvectl', path=which_paths)
timedatectl_bin = shutil.which('timedatectl', path=which_paths)
udevadm_bin = shutil.which('udevadm', path=which_paths)
+test_ndisc_send = None
build_dir = None
source_dir = None
self.assertRegex(output, route_regex)
+ def wait_route_dropped(self, link, route_regex, table='main', ipv='', timeout_sec=100):
+ for i in range(timeout_sec):
+ if i > 0:
+ time.sleep(1)
+ output = check_output(f'ip {ipv} route show dev {link} table {table}')
+ if not re.search(route_regex, output):
+ break
+
+ self.assertNotRegex(output, route_regex)
+
def check_netlabel(self, interface, address, label='system_u:object_r:root_t:s0'):
if not shutil.which('selinuxenabled'):
print('## Checking NetLabel skipped: selinuxenabled command not found.')
print(output)
self.assertRegex(output, 'qdisc teql1 31: root')
+ @expectedFailureIfModuleIsNotAvailable('sch_fq', 'sch_sfq', 'sch_tbf')
+ def test_qdisc_drop(self):
+ copy_network_unit('12-dummy.netdev', '12-dummy.network')
+ start_networkd()
+ self.wait_online('dummy98:routable')
+
+ # Test case for issue #32247 and #32254.
+ for _ in range(20):
+ check_output('tc qdisc replace dev dummy98 root fq')
+ self.assertFalse(networkd_is_failed())
+ check_output('tc qdisc replace dev dummy98 root fq pacing')
+ self.assertFalse(networkd_is_failed())
+ check_output('tc qdisc replace dev dummy98 handle 10: root tbf rate 0.5mbit burst 5kb latency 70ms peakrate 1mbit minburst 1540')
+ self.assertFalse(networkd_is_failed())
+ check_output('tc qdisc add dev dummy98 parent 10:1 handle 100: sfq')
+ self.assertFalse(networkd_is_failed())
+
class NetworkdStateFileTests(unittest.TestCase, Utilities):
def setUp(self):
self.check_ipv6_token_static()
+ def test_ndisc_redirect(self):
+ if not os.path.exists(test_ndisc_send):
+ self.skipTest(f"{test_ndisc_send} does not exist.")
+
+ copy_network_unit('25-veth.netdev', '25-ipv6-prefix.network', '25-ipv6-prefix-veth-token-static.network')
+ start_networkd()
+
+ self.check_ipv6_token_static()
+
+ # Introduce two redirect routes.
+ check_output(f'{test_ndisc_send} --interface veth-peer --type redirect --target-address 2002:da8:1:1:1a:2b:3c:4d --redirect-destination 2002:da8:1:1:1a:2b:3c:4d')
+ check_output(f'{test_ndisc_send} --interface veth-peer --type redirect --target-address 2002:da8:1:2:1a:2b:3c:4d --redirect-destination 2002:da8:1:2:1a:2b:3c:4d')
+ self.wait_route('veth99', '2002:da8:1:1:1a:2b:3c:4d proto redirect', ipv='-6', timeout_sec=10)
+ self.wait_route('veth99', '2002:da8:1:2:1a:2b:3c:4d proto redirect', ipv='-6', timeout_sec=10)
+
+ # Change the target address of the redirects.
+ check_output(f'{test_ndisc_send} --interface veth-peer --type redirect --target-address fe80::1 --redirect-destination 2002:da8:1:1:1a:2b:3c:4d')
+ check_output(f'{test_ndisc_send} --interface veth-peer --type redirect --target-address fe80::2 --redirect-destination 2002:da8:1:2:1a:2b:3c:4d')
+ self.wait_route_dropped('veth99', '2002:da8:1:1:1a:2b:3c:4d proto redirect', ipv='-6', timeout_sec=10)
+ self.wait_route_dropped('veth99', '2002:da8:1:2:1a:2b:3c:4d proto redirect', ipv='-6', timeout_sec=10)
+ self.wait_route('veth99', '2002:da8:1:1:1a:2b:3c:4d via fe80::1 proto redirect', ipv='-6', timeout_sec=10)
+ self.wait_route('veth99', '2002:da8:1:2:1a:2b:3c:4d via fe80::2 proto redirect', ipv='-6', timeout_sec=10)
+
+ # Send Neighbor Advertisement without the router flag to announce the default router is not available anymore.
+ # Then, verify that all redirect routes and the default route are dropped.
+ output = check_output('ip -6 address show dev veth-peer scope link')
+ veth_peer_ipv6ll = re.search('fe80:[:0-9a-f]*', output).group()
+ print(f'veth-peer IPv6LL address: {veth_peer_ipv6ll}')
+ check_output(f'{test_ndisc_send} --interface veth-peer --type neighbor-advertisement --target-address {veth_peer_ipv6ll} --is-router no')
+ self.wait_route_dropped('veth99', 'proto ra', ipv='-6', timeout_sec=10)
+
+ output = check_output('ip -6 route show dev veth99')
+ print(output)
+ self.assertIn('2002:da8:1:1:1a:2b:3c:4d via fe80::1 proto redirect', output)
+ self.assertIn('2002:da8:1:2:1a:2b:3c:4d via fe80::2 proto redirect', output)
+
+ def check_ndisc_mtu(self, mtu):
+ for _ in range(20):
+ output = read_ipv6_sysctl_attr('veth99', 'mtu')
+ if output == f'{mtu}':
+ break
+ time.sleep(0.5)
+ else:
+ self.fail(f'IPv6 MTU does not matches: value={output}, expected={mtu}')
+
+ def test_ndisc_mtu(self):
+ if not os.path.exists(test_ndisc_send):
+ self.skipTest(f"{test_ndisc_send} does not exist.")
+
+ copy_network_unit('25-veth.netdev',
+ '25-veth-peer-no-address.network',
+ '25-ipv6-prefix-veth-token-static.network')
+ start_networkd()
+ self.wait_online('veth-peer:degraded')
+
+ for _ in range(20):
+ output = read_networkd_log()
+ if 'veth99: NDISC: Started IPv6 Router Solicitation client' in output:
+ break
+ time.sleep(0.5)
+ else:
+ self.fail('sd-ndisc does not started on veth99.')
+
+ check_output(f'{test_ndisc_send} --interface veth-peer --type ra --lifetime 1hour --mtu 1400')
+ self.check_ndisc_mtu(1400)
+
+ check_output(f'{test_ndisc_send} --interface veth-peer --type ra --lifetime 1hour --mtu 1410')
+ self.check_ndisc_mtu(1410)
+
+ check_output('ip link set dev veth99 mtu 1600')
+ self.check_ndisc_mtu(1410)
+
+ check_output(f'{test_ndisc_send} --interface veth-peer --type ra --lifetime 1hour --mtu 1700')
+ self.check_ndisc_mtu(1600)
+
+ check_output('ip link set dev veth99 mtu 1800')
+ self.check_ndisc_mtu(1700)
+
def test_ipv6_token_prefixstable(self):
copy_network_unit('25-veth.netdev', '25-ipv6-prefix.network', '25-ipv6-prefix-veth-token-prefixstable.network')
start_networkd()
self.wait_online('veth99:routable', 'veth-peer:degraded')
- output = networkctl_status('veth99')
+ output = check_output('ip -6 address show dev veth99')
print(output)
- self.assertIn('2002:da8:1:0:b47e:7975:fc7a:7d6e', output)
- self.assertIn('2002:da8:2:0:1034:56ff:fe78:9abc', output) # EUI64
+ self.assertIn('2002:da8:1:0:b47e:7975:fc7a:7d6e/64', output) # the 1st prefixstable
+ self.assertIn('2002:da8:2:0:1034:56ff:fe78:9abc/64', output) # EUI64
+
+ with open(os.path.join(network_unit_dir, '25-ipv6-prefix-veth-token-prefixstable.network'), mode='a', encoding='utf-8') as f:
+ f.write('\n[IPv6AcceptRA]\nPrefixAllowList=2002:da8:1:0::/64\n')
+
+ networkctl_reload()
+ self.wait_online('veth99:routable')
+
+ output = check_output('ip -6 address show dev veth99')
+ print(output)
+ self.assertIn('2002:da8:1:0:b47e:7975:fc7a:7d6e/64', output) # the 1st prefixstable
+ self.assertNotIn('2002:da8:2:0:1034:56ff:fe78:9abc/64', output) # EUI64
+
+ check_output('ip address del 2002:da8:1:0:b47e:7975:fc7a:7d6e/64 dev veth99')
+ check_output('ip address add 2002:da8:1:0:b47e:7975:fc7a:7d6e/64 dev veth-peer nodad')
+
+ networkctl_reconfigure('veth99')
+ self.wait_online('veth99:routable')
+
+ output = check_output('ip -6 address show dev veth99')
+ print(output)
+ self.assertNotIn('2002:da8:1:0:b47e:7975:fc7a:7d6e/64', output) # the 1st prefixstable
+ self.assertIn('2002:da8:1:0:da5d:e50a:43fd:5d0f/64', output) # the 2nd prefixstable
+
+ check_output('ip address del 2002:da8:1:0:da5d:e50a:43fd:5d0f/64 dev veth99')
+ check_output('ip address add 2002:da8:1:0:da5d:e50a:43fd:5d0f/64 dev veth-peer nodad')
+
+ networkctl_reconfigure('veth99')
+ self.wait_online('veth99:routable')
+
+ output = check_output('ip -6 address show dev veth99')
+ print(output)
+ self.assertNotIn('2002:da8:1:0:b47e:7975:fc7a:7d6e/64', output) # the 1st prefixstable
+ self.assertNotIn('2002:da8:1:0:da5d:e50a:43fd:5d0f/64', output) # the 2nd prefixstable
+ self.assertIn('2002:da8:1:0:c7e4:77ec:eb31:1b0d/64', output) # the 3rd prefixstable
def test_ipv6_token_prefixstable_without_address(self):
copy_network_unit('25-veth.netdev', '25-ipv6-prefix.network', '25-ipv6-prefix-veth-token-prefixstable-without-address.network')
self.assertIn('2002:da8:1:0:b47e:7975:fc7a:7d6e', output)
self.assertIn('2002:da8:2:0:f689:561a:8eda:7443', output)
- def check_router_hop_limit(self, hop_limit):
- self.wait_route('client', rf'default via fe80::1034:56ff:fe78:9a99 proto ra .* hoplimit {hop_limit}', ipv='-6', timeout_sec=10)
-
- output = check_output('ip -6 route show dev client default via fe80::1034:56ff:fe78:9a99')
- print(output)
- self.assertIn(f'hoplimit {hop_limit}', output)
-
- self.check_ipv6_sysctl_attr('client', 'hop_limit', f'{hop_limit}')
-
def test_router_hop_limit(self):
copy_network_unit('25-veth-client.netdev',
'25-veth-router.netdev',
'25-veth-router-hop-limit.network',
'25-bridge99.network')
start_networkd()
- self.wait_online('client-p:enslaved',
+ self.wait_online('client:routable', 'client-p:enslaved',
'router:degraded', 'router-p:enslaved',
'bridge99:routable')
- self.check_router_hop_limit(42)
+ self.check_ipv6_sysctl_attr('client', 'hop_limit', '42')
with open(os.path.join(network_unit_dir, '25-veth-router-hop-limit.network'), mode='a', encoding='utf-8') as f:
f.write('\n[IPv6SendRA]\nHopLimit=43\n')
networkctl_reload()
- self.check_router_hop_limit(43)
+ for _ in range(20):
+ output = read_ipv6_sysctl_attr('client', 'hop_limit')
+ if output == '43':
+ break
+ time.sleep(0.5)
+
+ self.check_ipv6_sysctl_attr('client', 'hop_limit', '43')
def test_router_preference(self):
copy_network_unit('25-veth-client.netdev',
def tearDown(self):
tear_down_common()
- def test_dhcp_server(self):
- copy_network_unit('25-veth.netdev', '25-dhcp-client.network', '25-dhcp-server.network')
- start_networkd()
- self.wait_online('veth99:routable', 'veth-peer:routable')
-
+ def check_dhcp_server(self, persist_leases=True):
output = networkctl_status('veth99')
print(output)
self.assertRegex(output, r'Address: 192.168.5.[0-9]* \(DHCP4 via 192.168.5.1\)')
print(output)
self.assertRegex(output, "Offered DHCP leases: 192.168.5.[0-9]*")
+ if persist_leases:
+ with open('/var/lib/systemd/network/dhcp-server-lease/veth-peer', encoding='utf-8') as f:
+ check_json(f.read())
+ else:
+ self.assertFalse(os.path.exists('/var/lib/systemd/network/dhcp-server-lease/veth-peer'))
+
+ def test_dhcp_server(self):
+ copy_network_unit('25-veth.netdev', '25-dhcp-client.network', '25-dhcp-server.network')
+ start_networkd()
+ self.wait_online('veth99:routable', 'veth-peer:routable')
+
+ self.check_dhcp_server()
+
networkctl_reconfigure('veth-peer')
self.wait_online('veth-peer:routable')
else:
self.fail()
+ def test_dhcp_server_persist_leases_no(self):
+ copy_networkd_conf_dropin('persist-leases-no.conf')
+ copy_network_unit('25-veth.netdev', '25-dhcp-client.network', '25-dhcp-server.network')
+ start_networkd()
+ self.wait_online('veth99:routable', 'veth-peer:routable')
+
+ self.check_dhcp_server(persist_leases=False)
+
+ remove_networkd_conf_dropin('persist-leases-no.conf')
+ with open(os.path.join(network_unit_dir, '25-dhcp-server.network'), mode='a', encoding='utf-8') as f:
+ f.write('[DHCPServer]\nPersistLeases=no')
+ restart_networkd()
+ self.wait_online('veth99:routable', 'veth-peer:routable')
+
+ self.check_dhcp_server(persist_leases=False)
+
def test_dhcp_server_null_server_address(self):
copy_network_unit('25-veth.netdev', '25-dhcp-client.network', '25-dhcp-server-null-server-address.network')
start_networkd()
check(self, True, False)
check(self, False, True)
check(self, False, False)
+
+ def test_dhcp_client_default_use_domains(self):
+ def check(self, ipv4, ipv6):
+ mkdir_p(networkd_conf_dropin_dir)
+ with open(os.path.join(networkd_conf_dropin_dir, 'default_use_domains.conf'), mode='w', encoding='utf-8') as f:
+ f.write('[DHCPv4]\nUseDomains=')
+ f.write('yes\n' if ipv4 else 'no\n')
+ f.write('[DHCPv6]\nUseDomains=')
+ f.write('yes\n' if ipv6 else 'no\n')
+
+ restart_networkd()
+ self.wait_online('veth-peer:carrier')
+ start_dnsmasq('--dhcp-option=option:dns-server,192.168.5.1',
+ '--dhcp-option=option6:dns-server,[2600::1]',
+ '--dhcp-option=option:domain-search,example.com',
+ '--dhcp-option=option6:domain-search,example.com')
+
+ self.wait_online('veth99:routable')
+
+ # link becomes 'routable' when at least one protocol provide an valid address. Hence, we need to explicitly wait for both addresses.
+ self.wait_address('veth99', r'inet 192.168.5.[0-9]*/24 metric 1024 brd 192.168.5.255 scope global dynamic', ipv='-4')
+ self.wait_address('veth99', r'inet6 2600::[0-9a-f]*/128 scope global (dynamic noprefixroute|noprefixroute dynamic)', ipv='-6')
+
+ for _ in range(20):
+ output = resolvectl('domain', 'veth99')
+ if ipv4 or ipv6:
+ if 'example.com' in output:
+ break
+ else:
+ if 'example.com' not in output:
+ break
+ time.sleep(0.5)
+ else:
+ print(output)
+ self.fail('unexpected domain setting in resolved...')
+
+ stop_dnsmasq()
+ remove_networkd_conf_dropin('default_use_domains.conf')
+
+ copy_network_unit('25-veth.netdev', '25-dhcp-server-veth-peer.network', '25-dhcp-client.network', copy_dropins=False)
+ check(self, True, True)
+ check(self, True, False)
+ check(self, False, True)
+ check(self, False, False)
def test_dhcp_client_use_captive_portal(self):
def check(self, ipv4, ipv6):
udevadm_cmd = valgrind_cmd.split() + [udevadm_bin]
wait_online_cmd = valgrind_cmd.split() + [wait_online_bin]
+ if build_dir:
+ test_ndisc_send = os.path.normpath(os.path.join(build_dir, 'test-ndisc-send'))
+ else:
+ test_ndisc_send = '/usr/lib/tests/test-ndisc-send'
+
if asan_options:
env.update({'ASAN_OPTIONS': asan_options})
if lsan_options:
def run(args):
-
ret = 1
logger = logging.getLogger("test-shutdown")
+ logfile = None
+
+ if args.logfile:
+ logger.debug("Logging pexpect IOs to %s", args.logfile)
+ logfile = open(args.logfile, 'w')
+ elif args.verbose:
+ logfile = sys.stdout
logger.info("spawning test")
- console = pexpect.spawn(args.command, args.arg, env={
- "TERM": "linux",
+ console = pexpect.spawn(args.command, args.arg, logfile=logfile, env={
+ "TERM": "dumb",
}, encoding='utf-8', timeout=60)
logger.debug("child pid %d", console.pid)
logger.info("waiting for login prompt")
console.expect('H login: ', 10)
- if args.logfile:
- logger.debug("Logging pexpect IOs to %s", args.logfile)
- console.logfile = open(args.logfile, 'w')
- elif args.verbose:
- console.logfile = sys.stdout
-
logger.info("log in and start screen")
console.sendline('root')
console.expect('bash.*# ', 10)
console.send('c')
console.expect('screen1 ', 10)
+ logger.info('wait for the machine to fully boot')
+ console.sendline('systemctl is-system-running --wait')
+ console.expect(r'\b(running|degraded)\b', 60)
+
# console.interact()
console.sendline('tty')
[Unit]
-Description=TEST-77-OPENFILE server socket
+Description=OpenFile= test server socket
[Socket]
ListenStream=/tmp/test.sock
[Unit]
-Description=TEST-77-OPENFILE server
+Description=OpenFile= test server service
[Service]
ExecStart=echo "Socket"
# Here, the redundant '[ ]' in the pattern is required in order not to match the logged command itself.
(! journalctl -q -o short-monotonic --grep 'Warning: cannot close sd-bus connection[ ].*after fork' >>/failed)
+# Check if sd-executor doesn't complain about not being able to (de)serialize stuff
+(! journalctl -q -o short-monotonic --grep "[F]ailed to parse serialized line" >>/failed)
+(! journalctl -q -o short-monotonic --grep "[F]ailed to (de)?serialize \w+" >>/failed)
+
systemctl poweroff --no-block
exit 0
#!/usr/bin/env bash
# SPDX-License-Identifier: LGPL-2.1-or-later
set -eux
-# pipefail is disabled intentionally, as `curl | grep -q` is very SIGPIPE happy
+set -o pipefail
if [[ ! -x /usr/lib/systemd/systemd-journal-gatewayd ]]; then
echo "Built without systemd-journal-gatewayd support, skipping the test"
exit 0
fi
+LOG_FILE="$(mktemp)"
+
+at_exit() {
+ if [[ $? -ne 0 ]]; then
+ # The $LOG_FILE is potentially huge (as it might be a full copy of the current journal), so let's
+ # dump it at debug level under a specific syslog tag, so it's clearly separated from the actual test
+ # journal; things get very confusing otherwise.
+ systemd-cat -t log-file-dump -p debug cat "$LOG_FILE"
+ fi
+
+ rm -f "$LOG_FILE"
+}
+
+trap at_exit EXIT
+
TEST_MESSAGE="-= This is a test message $RANDOM =-"
TEST_TAG="$(systemd-id128 new)"
# /browse
# We should get redirected to /browse by default
-curl -Lfs http://localhost:19531 | grep -qF "<title>Journal</title>"
-curl -Lfs http://localhost:19531/browse | grep -qF "<title>Journal</title>"
-(! curl -Lfs http://localhost:19531/foo/bar/baz)
-(! curl -Lfs http://localhost:19531/foo/../../../bar/../baz)
+curl -LSfs http://localhost:19531 >"$LOG_FILE"
+grep -qF "<title>Journal</title>" "$LOG_FILE"
+curl -LSfs http://localhost:19531/browse >"$LOG_FILE"
+grep -qF "<title>Journal</title>" "$LOG_FILE"
+(! curl -LSfs http://localhost:19531/foo/bar/baz)
+(! curl -LSfs http://localhost:19531/foo/../../../bar/../baz)
# /entries
# Accept: text/plain should be the default
-curl -Lfs http://localhost:19531/entries | \
- grep -qE " $TEST_TAG\[[0-9]+\]: $TEST_MESSAGE"
-curl -Lfs --header "Accept: text/plain" http://localhost:19531/entries | \
- grep -qE " $TEST_TAG\[[0-9]+\]: $TEST_MESSAGE"
-curl -Lfs --header "Accept: application/json" http://localhost:19531/entries | \
- jq -se ".[] | select(.MESSAGE == \"$TEST_MESSAGE\")"
-curl -Lfs --header "Accept: application/json" http://localhost:19531/entries?boot | \
- jq -se ".[] | select(.MESSAGE == \"$TEST_MESSAGE\")"
-curl -Lfs --header "Accept: application/json" http://localhost:19531/entries?SYSLOG_IDENTIFIER="$TEST_TAG" | \
- jq -se "length == 1 and select(.[].MESSAGE == \"$TEST_MESSAGE\")"
+curl -LSfs http://localhost:19531/entries >"$LOG_FILE"
+grep -qE " $TEST_TAG\[[0-9]+\]: $TEST_MESSAGE" "$LOG_FILE"
+curl -LSfs --header "Accept: text/plain" http://localhost:19531/entries >"$LOG_FILE"
+grep -qE " $TEST_TAG\[[0-9]+\]: $TEST_MESSAGE" "$LOG_FILE"
+curl -LSfs --header "Accept: application/json" http://localhost:19531/entries >"$LOG_FILE"
+jq -se ".[] | select(.MESSAGE == \"$TEST_MESSAGE\")" "$LOG_FILE"
+curl -LSfs --header "Accept: application/json" http://localhost:19531/entries?boot >"$LOG_FILE"
+jq -se ".[] | select(.MESSAGE == \"$TEST_MESSAGE\")" "$LOG_FILE"
+curl -LSfs --header "Accept: application/json" http://localhost:19531/entries?SYSLOG_IDENTIFIER="$TEST_TAG" >"$LOG_FILE"
+jq -se "length == 1 and select(.[].MESSAGE == \"$TEST_MESSAGE\")" "$LOG_FILE"
# Show 10 entries starting from $BOOT_CURSOR, skip the first 5
-curl -Lfs --header "Accept: application/json" --header "Range: entries=$BOOT_CURSOR:5:10" http://localhost:19531/entries | \
- jq -se "length == 10"
+curl -LSfs \
+ --header "Accept: application/json" \
+ --header "Range: entries=$BOOT_CURSOR:5:10" \
+ http://localhost:19531/entries >"$LOG_FILE"
+jq -se "length == 10" "$LOG_FILE"
# Check if the specified cursor refers to an existing entry and return just that entry
-curl -Lfs --header "Accept: application/json" --header "Range: entries=$TEST_CURSOR" http://localhost:19531/entries?discrete | \
- jq -se "length == 1 and select(.[].MESSAGE == \"$TEST_MESSAGE\")"
+curl -LSfs \
+ --header "Accept: application/json" \
+ --header "Range: entries=$TEST_CURSOR" \
+ http://localhost:19531/entries?discrete >"$LOG_FILE"
+jq -se "length == 1 and select(.[].MESSAGE == \"$TEST_MESSAGE\")" "$LOG_FILE"
# Check entry is present (resp. absent) when filtering by timestamp
-curl -Lfs --header "Range: realtime=$BEFORE_TIMESTAMP:" http://localhost:19531/entries?SYSLOG_IDENTIFIER="$TEST_TAG" | \
- grep -qE " $TEST_TAG\[[0-9]+\]: $TEST_MESSAGE"
-curl -Lfs --header "Range: realtime=:$AFTER_TIMESTAMP" http://localhost:19531/entries?SYSLOG_IDENTIFIER="$TEST_TAG" | \
- grep -qE " $TEST_TAG\[[0-9]+\]: $TEST_MESSAGE"
-curl -Lfs --header "Accept: application/json" --header "Range: realtime=:$BEFORE_TIMESTAMP" http://localhost:19531/entries?SYSLOG_IDENTIFIER="$TEST_TAG" | \
- jq -se "length == 0"
-curl -Lfs --header "Accept: application/json" --header "Range: realtime=$AFTER_TIMESTAMP:" http://localhost:19531/entries?SYSLOG_IDENTIFIER="$TEST_TAG" | \
- jq -se "length == 0"
+curl -LSfs \
+ --header "Range: realtime=$BEFORE_TIMESTAMP:" \
+ http://localhost:19531/entries?SYSLOG_IDENTIFIER="$TEST_TAG" >"$LOG_FILE"
+grep -qE " $TEST_TAG\[[0-9]+\]: $TEST_MESSAGE" "$LOG_FILE"
+curl -LSfs \
+ --header "Range: realtime=:$AFTER_TIMESTAMP" \
+ http://localhost:19531/entries?SYSLOG_IDENTIFIER="$TEST_TAG" >"$LOG_FILE"
+grep -qE " $TEST_TAG\[[0-9]+\]: $TEST_MESSAGE" "$LOG_FILE"
+curl -LSfs \
+ --header "Accept: application/json" \
+ --header "Range: realtime=:$BEFORE_TIMESTAMP" \
+ http://localhost:19531/entries?SYSLOG_IDENTIFIER="$TEST_TAG" >"$LOG_FILE"
+jq -se "length == 0" "$LOG_FILE"
+curl -LSfs \
+ --header "Accept: application/json" \
+ --header "Range: realtime=$AFTER_TIMESTAMP:" \
+ http://localhost:19531/entries?SYSLOG_IDENTIFIER="$TEST_TAG" >"$LOG_FILE"
+jq -se "length == 0" "$LOG_FILE"
# Check positive and negative skip when filtering by timestamp
echo "-= This is a second test message =-" | systemd-cat -t "$TEST_TAG"
journalctl --sync
journalctl --sync
sleep 1
END_TIMESTAMP="$(date +%s)"
-curl -Lfs --header "Accept: application/json" --header "Range: realtime=$BEFORE_TIMESTAMP::1:1" http://localhost:19531/entries?SYSLOG_IDENTIFIER="$TEST_TAG" | \
- jq -se "length == 1 and select(.[].__CURSOR == \"$TEST2_CURSOR\")"
-curl -Lfs --header "Accept: application/json" --header "Range: realtime=$END_TIMESTAMP::-1:1" http://localhost:19531/entries?SYSLOG_IDENTIFIER="$TEST_TAG" | \
- jq -se "length == 1 and select(.[].__CURSOR == \"$TEST2_CURSOR\")"
+curl -LSfs \
+ --header "Accept: application/json" \
+ --header "Range: realtime=$BEFORE_TIMESTAMP::1:1" \
+ http://localhost:19531/entries?SYSLOG_IDENTIFIER="$TEST_TAG" >"$LOG_FILE"
+jq -se "length == 1 and select(.[].__CURSOR == \"$TEST2_CURSOR\")" "$LOG_FILE"
+curl -LSfs \
+ --header "Accept: application/json" \
+ --header "Range: realtime=$END_TIMESTAMP::-1:1" \
+ http://localhost:19531/entries?SYSLOG_IDENTIFIER="$TEST_TAG" >"$LOG_FILE"
+jq -se "length == 1 and select(.[].__CURSOR == \"$TEST2_CURSOR\")" "$LOG_FILE"
# No idea how to properly parse this (jq won't cut it), so let's at least do some sanity checks that every
# line is either empty or begins with data:
-curl -Lfs --header "Accept: text/event-stream" http://localhost:19531/entries | \
- awk '!/^(data: \{.+\}|)$/ { exit 1; }'
+curl -LSfs --header "Accept: text/event-stream" http://localhost:19531/entries >"$LOG_FILE"
+awk '!/^(data: \{.+\}|)$/ { exit 1; }' "$LOG_FILE"
# Same thing as journalctl --output=export
mkdir /tmp/remote-journal
-curl -Lfs --header "Accept: application/vnd.fdo.journal" http://localhost:19531/entries | \
- /usr/lib/systemd/systemd-journal-remote --output=/tmp/remote-journal/system.journal --split-mode=none -
+curl -LSfs --header "Accept: application/vnd.fdo.journal" http://localhost:19531/entries >"$LOG_FILE"
+/usr/lib/systemd/systemd-journal-remote --output=/tmp/remote-journal/system.journal --split-mode=none "$LOG_FILE"
journalctl --directory=/tmp/remote-journal -t "$TEST_TAG" --grep "$TEST_MESSAGE"
rm -rf /tmp/remote-journal/*
# Let's do the same thing again, but let systemd-journal-remote spawn curl itself
rm -rf /tmp/remote-journal
# /machine
-curl -Lfs http://localhost:19531/machine | jq
+curl -LSfs http://localhost:19531/machine >"$LOG_FILE"
+jq . "$LOG_FILE"
# /fields
-curl -Lfs http://localhost:19531/fields/MESSAGE | grep -qE -- "$TEST_MESSAGE"
-curl -Lfs http://localhost:19531/fields/_TRANSPORT
-(! curl -Lfs http://localhost:19531/fields)
-(! curl -Lfs http://localhost:19531/fields/foo-bar-baz)
+curl -LSfs http://localhost:19531/fields/MESSAGE >"$LOG_FILE"
+grep -qE -- "$TEST_MESSAGE" "$LOG_FILE"
+curl -LSfs http://localhost:19531/fields/_TRANSPORT
+(! curl -LSfs http://localhost:19531/fields)
+(! curl -LSfs http://localhost:19531/fields/foo-bar-baz)
systemctl stop systemd-journal-gatewayd.{socket,service}
sleep 1
# Do a limited set of tests, since the underlying code should be the same past the HTTPS transport
-curl -Lfsk https://localhost:19531 | grep -qF "<title>Journal</title>"
-curl -Lfsk https://localhost:19531/entries | \
- grep -qE " $TEST_TAG\[[0-9]+\]: $TEST_MESSAGE"
-curl -Lfsk --header "Accept: application/json" https://localhost:19531/entries | \
- jq -se ".[] | select(.MESSAGE == \"$TEST_MESSAGE\")"
-curl -Lfsk https://localhost:19531/machine | jq
-curl -Lfsk https://localhost:19531/fields/_TRANSPORT
+curl -LSfsk https://localhost:19531 >"$LOG_FILE"
+grep -qF "<title>Journal</title>" "$LOG_FILE"
+curl -LSfsk https://localhost:19531/entries >"$LOG_FILE"
+grep -qE " $TEST_TAG\[[0-9]+\]: $TEST_MESSAGE" "$LOG_FILE"
+curl -LSfsk --header "Accept: application/json" https://localhost:19531/entries >"$LOG_FILE"
+jq -se ".[] | select(.MESSAGE == \"$TEST_MESSAGE\")" "$LOG_FILE"
+curl -LSfsk https://localhost:19531/machine >"$LOG_FILE"
+jq . "$LOG_FILE"
+curl -LSfsk https://localhost:19531/fields/_TRANSPORT
kill "$GATEWAYD_PID"
# See: https://github.com/systemd/systemd/issues/9858
OUT="$(mktemp)"
for _ in {0..4}; do
- curl --fail-with-body -d "please process this🐱 $RANDOM" -L http://localhost:19531/upload | tee "$OUT"
+ (! curl --fail-with-body -d "please process this🐱 $RANDOM" -L http://localhost:19531/upload | tee "$OUT")
(! grep '[^[:print:]]' "$OUT")
done
-curl --fail-with-body --upload-file "$GATEWAYD_FILE" -L http://localhost:19531/upload | tee "$OUT"
+(! curl --fail-with-body --upload-file "$GATEWAYD_FILE" -L http://localhost:19531/upload | tee "$OUT")
(! grep '[^[:print:]]' "$OUT")
rm -rf "$OUT"
-curl -Lfs http://localhost:19531/browse | grep -qF "<title>Journal</title>"
+curl -LSfs http://localhost:19531/browse >"$LOG_FILE"
+grep -qF "<title>Journal</title>" "$LOG_FILE"
# Nuke the file behind the /browse endpoint
mv /usr/share/systemd/gatewayd/browse.html /usr/share/systemd/gatewayd/browse.html.bak
(! curl --fail-with-body -L http://localhost:19531/browse)
mv /usr/share/systemd/gatewayd/browse.html.bak /usr/share/systemd/gatewayd/browse.html
-curl -Lfs http://localhost:19531/browse | grep -qF "<title>Journal</title>"
+curl -LSfs http://localhost:19531/browse >"$LOG_FILE"
+grep -qF "<title>Journal</title>" "$LOG_FILE"
# Nuke the journal file
mv "$GATEWAYD_FILE" "$GATEWAYD_FILE.bak"
(! curl --fail-with-body -L http://localhost:19531/fields/_PID)
mv "$GATEWAYD_FILE.bak" "$GATEWAYD_FILE"
-curl -Lfs http://localhost:19531/fields/_PID
+curl -LSfs http://localhost:19531/fields/_PID
systemctl stop test-gatewayd.{socket,service}
rm -f "$GATEWAYD_FILE"
if [[ "$(systemctl show -P InitRDTimestampMonotonic)" -eq 0 ]]; then
echo "systemd didn't run in the initrd, skipping the test"
touch /skipped
- exit 0
+ exit 77
fi
# We should've created a mount under /run in initrd (see the other half of the test)
udevadm control -R
udevadm control -p HELLO=world
udevadm control -m 42
-udevadm control --ping
-udevadm control -t 5
+udevadm control --ping -t 5
+udevadm control --load-credentials
udevadm control -h
udevadm info /dev/null
--- /dev/null
+#!/usr/bin/env bash
+# SPDX-License-Identifier: LGPL-2.1-or-later
+# shellcheck disable=SC2016
+set -eux
+set -o pipefail
+
+at_exit() {
+ rm -f /run/credstore/udev.*
+ rm -f /run/udev/udev.conf.d/*
+ rm -f /run/udev/rules.d/*
+ rm -rf /run/systemd/system/systemd-udev-load-credentials.service.d
+}
+
+trap at_exit EXIT
+
+mkdir -p /run/credstore
+cat > /run/credstore/udev.conf.50-testme <<EOF
+udev_log=debug
+EOF
+cat > /run/credstore/udev.rules.50-testme <<EOF
+SUBSYSTEM=="net", OPTIONS="log_level=debug"
+EOF
+
+systemctl edit systemd-udev-load-credentials.service --stdin --drop-in=50-testme.conf <<EOF
+[Service]
+LoadCredential=udev.conf.50-testme
+LoadCredential=udev.rules.50-testme
+EOF
+
+systemctl restart systemd-udev-load-credentials.service
+
+diff /run/credstore/udev.conf.50-testme /run/udev/udev.conf.d/50-testme.conf
+diff /run/credstore/udev.rules.50-testme /run/udev/rules.d/50-testme.rules
assert_in "ID_NET_LINK_FILE_DROPINS=/run/systemd/network/10-test.link.d/10-override.conf:/run/systemd/network/10-test.link.d/11-override.conf" "$output"
assert_in "ID_NET_NAME=test1" "$output"
-# check that test command _does_ update udev database.
+# check that test command does not update udev database.
output=$(udevadm info --query property /sys/class/net/test1)
assert_not_in "HOGE=" "$output"
-assert_in "HOGE2=foo2" "$output"
+assert_not_in "HOGE2=" "$output"
assert_not_in "BAR=" "$output"
-assert_in "BAR2=baz2" "$output"
+assert_not_in "BAR2=" "$output"
assert_not_in "SHOULD_BE_UNSET=" "$output"
assert_in "ID_NET_LINK_FILE=/run/systemd/network/10-test.link" "$output"
assert_in "ID_NET_LINK_FILE_DROPINS=/run/systemd/network/10-test.link.d/10-override.conf:/run/systemd/network/10-test.link.d/11-override.conf" "$output"
output=$(udevadm test --action add /sys/class/net/test1)
assert_in "LINK_VERSION=$(uname -r)" "$output"
+udevadm trigger --settle --action add /sys/class/net/test1
output=$(udevadm info --query property /sys/class/net/test1)
assert_in "LINK_VERSION=$(uname -r)" "$output"
assert_not_in "IFINDEX=bar" "$output"
assert_in "DEVPATH=" "$output"
+udevadm trigger --settle --action add /sys/class/net/test1
output=$(udevadm info --query property /sys/class/net/test1)
assert_not_in "ACTION=foo" "$output"
assert_in "IFINDEX=" "$output"
--- /dev/null
+#!/usr/bin/env bash
+# SPDX-License-Identifier: LGPL-2.1-or-later
+set -eux
+set -o pipefail
+
+# shellcheck source=test/units/util.sh
+. "$(dirname "$0")"/util.sh
+
+assert_eq "$LISTEN_FDS" "$1"
+assert_eq "$LISTEN_FDNAMES" "$2"
+
+for ((i = 3; i < 3 + LISTEN_FDS; i++)); do
+ read -r -u "$i" text
+ assert_eq "$text" "${!i}" # Dereference $i to get i'th arg
+done
--- /dev/null
+#!/usr/bin/env bash
+# SPDX-License-Identifier: LGPL-2.1-or-later
+set -eux
+set -o pipefail
+
+# shellcheck source=test/units/util.sh
+. "$(dirname "$0")"/util.sh
+
+at_exit() {
+ set +e
+
+ rm -rf /tmp/test-open-file/
+}
+
+trap at_exit EXIT
+
+systemctl log-level debug
+
+# Existing files
+
+mkdir /tmp/test-open-file
+echo "Open" >'/tmp/test-open-file/open.txt'
+echo "File" >'/tmp/test-open-file/file:colon.txt'
+
+systemd-run -p DynamicUser=yes -p EnvironmentFile=-/usr/lib/systemd/systemd-asan-env \
+ -p OpenFile='/tmp/test-open-file/open.txt::read-only' \
+ -p OpenFile='/tmp/test-open-file/file\x3Acolon.txt:colon' \
+ -p RemainAfterExit=yes \
+ --unit=test-23-openfile-existing.service \
+ --service-type=oneshot \
+ /usr/lib/systemd/tests/testdata/units/testsuite-23-openfile-child.sh 2 "open.txt:colon" "Open" "File"
+
+cmp <(systemctl show -p OpenFile test-23-openfile-existing.service) <<EOF
+OpenFile=/tmp/test-open-file/open.txt::read-only
+OpenFile=/tmp/test-open-file/file\\x3acolon.txt:colon
+EOF
+
+systemctl stop test-23-openfile-existing.service
+
+# Sockets
+
+systemctl start testsuite-23-openfile-server.socket
+
+systemd-run -p OpenFile=/tmp/test.sock:socket:read-only \
+ --wait \
+ /usr/lib/systemd/tests/testdata/units/testsuite-23-openfile-child.sh 1 "socket" "Socket"
+
+systemctl stop testsuite-23-openfile-server.socket
+
+# Ignore when missing
+
+assert_rc 202 systemd-run -p OpenFile=/run/missing/foo:missing-file:read-only --wait true
+assert_rc 0 systemd-run -p OpenFile=/run/missing/foo:missing-file:read-only,graceful --wait true
+
+systemctl log-level info
portablectl detach --now --runtime --extension /usr/share/app0.raw /usr/share/minimal_1.raw app0
+# Ensure versioned images are accepted without needing to use --force to override the extension-release
+# matching
+
+cp /usr/share/app0.raw /tmp/app0_1.0.raw
+portablectl "${ARGS[@]}" attach --now --runtime --extension /tmp/app0_1.0.raw /usr/share/minimal_0.raw app0
+
+systemctl is-active app0.service
+status="$(portablectl is-attached --extension app0_1 minimal_0)"
+[[ "${status}" == "running-runtime" ]]
+
+portablectl detach --now --runtime --extension /tmp/app0_1.0.raw /usr/share/minimal_1.raw app0
+rm -f /tmp/app0_1.0.raw
+
portablectl "${ARGS[@]}" attach --now --runtime --extension /usr/share/app1.raw /usr/share/minimal_0.raw app1
systemctl is-active app1.service
test -L /run/systemd/system.attached/app0.service.d/10-profile.conf
portablectl detach --runtime --extension /usr/share/app0.raw /usr/share/minimal_0.raw app0
+# Ensure that when two portables share the same base image, removing one doesn't remove the other too
+
+portablectl "${ARGS[@]}" attach --runtime --extension /usr/share/app0.raw /usr/share/minimal_0.raw app0
+portablectl "${ARGS[@]}" attach --runtime --extension /usr/share/app1.raw /usr/share/minimal_0.raw app1
+
+status="$(portablectl is-attached --extension app0 minimal_0)"
+[[ "${status}" == "attached-runtime" ]]
+status="$(portablectl is-attached --extension app1 minimal_0)"
+[[ "${status}" == "attached-runtime" ]]
+
+(! portablectl detach --runtime /usr/share/minimal_0.raw app)
+
+status="$(portablectl is-attached --extension app0 minimal_0)"
+[[ "${status}" == "attached-runtime" ]]
+status="$(portablectl is-attached --extension app1 minimal_0)"
+[[ "${status}" == "attached-runtime" ]]
+
+# Ensure 'portablectl list' shows the correct status for both images
+portablectl list
+portablectl list | grep -F "minimal_0" | grep -q -F "attached-runtime"
+portablectl list | grep -F "app0" | grep -q -F "attached-runtime"
+portablectl list | grep -F "app1" | grep -q -F "attached-runtime"
+
+portablectl detach --runtime --extension /usr/share/app0.raw /usr/share/minimal_0.raw app
+
+status="$(portablectl is-attached --extension app1 minimal_0)"
+[[ "${status}" == "attached-runtime" ]]
+
+portablectl detach --runtime --extension /usr/share/app1.raw /usr/share/minimal_0.raw app
+
# portablectl also works with directory paths rather than images
mkdir /tmp/rootdir /tmp/app0 /tmp/app1 /tmp/overlay /tmp/os-release-fix /tmp/os-release-fix/etc
if [[ "$(sysctl -ne kernel.apparmor_restrict_unprivileged_userns)" -eq 1 ]]; then
echo "Cannot create unprivileged user namespaces" >/skipped
- exit 0
+ exit 77
fi
systemd-analyze log-level debug
# Check if homectl is installed, and if it isn't bail out early instead of failing
if ! test -x /usr/bin/homectl ; then
echo "no homed" >/skipped
- exit 0
+ exit 77
fi
inspect() {
PASSWORD=xEhErW0ndafV4s homectl with test-user -- test ! -f /home/test-user/xyz
(! PASSWORD=xEhErW0ndafV4s homectl with test-user -- test -f /home/test-user/xyz)
+# Regression tests
+wait_for_state test-user inactive
+/usr/lib/systemd/tests/unit-tests/manual/test-homed-regression-31896 test-user
+
wait_for_state test-user inactive
homectl remove test-user
--- /dev/null
+#!/usr/bin/env bash
+# SPDX-License-Identifier: LGPL-2.1-or-later
+set -eux
+set -o pipefail
+
+# Check that the /sbin/mount.ddi helper works
+dir="/tmp/mounthelper.$RANDOM"
+mount -t ddi "$MINIMAL_IMAGE.gpt" "$dir" -o ro,X-mount.mkdir,discard
+umount -R "$dir"
+
+# Test systemd-repart --make-ddi=:
+if [[ -z "${OPENSSL_CONFIG:?}" ]] || ! command -v mksquashfs &>/dev/null; then
+ echo "Skipping --make-ddi= tests"
+ exit 0
+fi
+
+openssl req -config "$OPENSSL_CONFIG" -subj="/CN=waldo" \
+ -x509 -sha256 -nodes -days 365 -newkey rsa:4096 \
+ -keyout /tmp/test-50-privkey.key -out /tmp/test-50-cert.crt
+mkdir -p /tmp/test-50-confext/etc/extension-release.d/
+echo "foobar50" >/tmp/test-50-confext/etc/waldo
+{
+ grep -e '^\(ID\|VERSION_ID\)=' /etc/os-release
+ echo IMAGE_ID=waldo
+ echo IMAGE_VERSION=7
+} >/tmp/test-50-confext/etc/extension-release.d/extension-release.waldo
+mkdir -p /run/confexts
+
+SYSTEMD_REPART_OVERRIDE_FSTYPE=squashfs \
+ systemd-repart -C \
+ -s /tmp/test-50-confext \
+ --certificate=/tmp/test-50-cert.crt \
+ --private-key=/tmp/test-50-privkey.key \
+ /run/confexts/waldo.confext.raw
+rm -rf /tmp/test-50-confext
+
+mkdir -p /run/verity.d
+cp /tmp/test-50-cert.crt /run/verity.d/
+systemd-dissect --mtree /run/confexts/waldo.confext.raw
+
+systemd-confext refresh
+test "$(</etc/waldo)" = foobar50
+rm /run/confexts/waldo.confext.raw
+systemd-confext refresh
+test ! -f /etc/waldo
+
+mkdir -p /tmp/test-50-sysext/usr/lib/extension-release.d/
+# Make sure the sysext is big enough to not fit in the minimum partition size of repart so we know the
+# Minimize= logic is working.
+truncate --size=50M /tmp/test-50-sysext/usr/waldo
+{
+ grep -e '^\(ID\|VERSION_ID\)=' /etc/os-release
+ echo IMAGE_ID=waldo
+ echo IMAGE_VERSION=7
+} >/tmp/test-50-sysext/usr/lib/extension-release.d/extension-release.waldo
+mkdir -p /run/extensions
+
+SYSTEMD_REPART_OVERRIDE_FSTYPE=squashfs \
+ systemd-repart -S \
+ -s /tmp/test-50-sysext \
+ --certificate=/tmp/test-50-cert.crt \
+ --private-key=/tmp/test-50-privkey.key \
+ /run/extensions/waldo.sysext.raw
+
+systemd-dissect --mtree /run/extensions/waldo.sysext.raw
+systemd-sysext refresh
+test -f /usr/waldo
+rm /run/verity.d/test-50-cert.crt /run/extensions/waldo.sysext.raw /tmp/test-50-cert.crt /tmp/test-50-privkey.key
+systemd-sysext refresh
+test ! -f /usr/waldo
--- /dev/null
+#!/usr/bin/env bash
+# SPDX-License-Identifier: LGPL-2.1-or-later
+# -*- mode: shell-script; indent-tabs-mode: nil; sh-basic-offset: 4; -*-
+# ex: ts=8 sw=4 sts=4 et filetype=sh
+# shellcheck disable=SC2233,SC2235
+set -eux
+set -o pipefail
+
+# shellcheck source=test/units/util.sh
+. "$(dirname "$0")"/util.sh
+
+systemd-dissect --json=short "$MINIMAL_IMAGE.raw" | \
+ grep -q -F '{"rw":"ro","designator":"root","partition_uuid":null,"partition_label":null,"fstype":"squashfs","architecture":null,"verity":"external"'
+systemd-dissect "$MINIMAL_IMAGE.raw" | grep -q -F "MARKER=1"
+systemd-dissect "$MINIMAL_IMAGE.raw" | grep -q -F -f <(sed 's/"//g' "$OS_RELEASE")
+
+systemd-dissect --list "$MINIMAL_IMAGE.raw" | grep -q '^etc/os-release$'
+systemd-dissect --mtree "$MINIMAL_IMAGE.raw" --mtree-hash yes | \
+ grep -qe "^./usr/bin/cat type=file mode=0755 uid=0 gid=0 size=[0-9]* sha256sum=[a-z0-9]*$"
+systemd-dissect --mtree "$MINIMAL_IMAGE.raw" --mtree-hash no | \
+ grep -qe "^./usr/bin/cat type=file mode=0755 uid=0 gid=0 size=[0-9]*$"
+
+read -r SHA256SUM1 _ < <(systemd-dissect --copy-from "$MINIMAL_IMAGE.raw" etc/os-release | sha256sum)
+test "$SHA256SUM1" != ""
+read -r SHA256SUM2 _ < <(systemd-dissect --read-only --with "$MINIMAL_IMAGE.raw" sha256sum etc/os-release)
+test "$SHA256SUM2" != ""
+test "$SHA256SUM1" = "$SHA256SUM2"
+
+if systemctl --version | grep -qF -- "+LIBARCHIVE" ; then
+ # Make sure tarballs are reproducible
+ read -r SHA256SUM1 _ < <(systemd-dissect --make-archive "$MINIMAL_IMAGE.raw" | sha256sum)
+ test "$SHA256SUM1" != ""
+ read -r SHA256SUM2 _ < <(systemd-dissect --make-archive "$MINIMAL_IMAGE.raw" | sha256sum)
+ test "$SHA256SUM2" != ""
+ test "$SHA256SUM1" = "$SHA256SUM2"
+ # Also check that a file we expect to be there is there
+ systemd-dissect --make-archive "$MINIMAL_IMAGE.raw" | tar t | grep etc/os-release
+fi
+
+mv "$MINIMAL_IMAGE.verity" "$MINIMAL_IMAGE.fooverity"
+mv "$MINIMAL_IMAGE.roothash" "$MINIMAL_IMAGE.foohash"
+systemd-dissect "$MINIMAL_IMAGE.raw" \
+ --json=short \
+ --root-hash="$MINIMAL_IMAGE_ROOTHASH" \
+ --verity-data="$MINIMAL_IMAGE.fooverity" | \
+ grep -q -F '{"rw":"ro","designator":"root","partition_uuid":null,"partition_label":null,"fstype":"squashfs","architecture":null,"verity":"external"'
+systemd-dissect "$MINIMAL_IMAGE.raw" \
+ --root-hash="$MINIMAL_IMAGE_ROOTHASH" \
+ --verity-data="$MINIMAL_IMAGE.fooverity" | \
+ grep -q -F "MARKER=1"
+systemd-dissect "$MINIMAL_IMAGE.raw" \
+ --root-hash="$MINIMAL_IMAGE_ROOTHASH" \
+ --verity-data="$MINIMAL_IMAGE.fooverity" | \
+ grep -q -F -f <(sed 's/"//g' "$OS_RELEASE")
+mv "$MINIMAL_IMAGE.fooverity" "$MINIMAL_IMAGE.verity"
+mv "$MINIMAL_IMAGE.foohash" "$MINIMAL_IMAGE.roothash"
+
+mkdir -p "$IMAGE_DIR/mount" "$IMAGE_DIR/mount2"
+systemd-dissect --mount "$MINIMAL_IMAGE.raw" "$IMAGE_DIR/mount"
+grep -q -F -f "$OS_RELEASE" "$IMAGE_DIR/mount/usr/lib/os-release"
+grep -q -F -f "$OS_RELEASE" "$IMAGE_DIR/mount/etc/os-release"
+grep -q -F "MARKER=1" "$IMAGE_DIR/mount/usr/lib/os-release"
+# Verity volume should be shared (opened only once)
+systemd-dissect --mount "$MINIMAL_IMAGE.raw" "$IMAGE_DIR/mount2"
+verity_count=$(find /dev/mapper/ -name "*verity*" | wc -l)
+# In theory we should check that count is exactly one. In practice, libdevmapper
+# randomly and unpredictably fails with an unhelpful EINVAL when a device is open
+# (and even mounted and in use), so best-effort is the most we can do for now
+if [[ "$verity_count" -lt 1 ]]; then
+ echo "Verity device $MINIMAL_IMAGE.raw not found in /dev/mapper/"
+ exit 1
+fi
+systemd-dissect --umount "$IMAGE_DIR/mount"
+systemd-dissect --umount "$IMAGE_DIR/mount2"
+
+systemd-run -P -p RootImage="$MINIMAL_IMAGE.raw" cat /usr/lib/os-release | grep -q -F "MARKER=1"
+mv "$MINIMAL_IMAGE.verity" "$MINIMAL_IMAGE.fooverity"
+mv "$MINIMAL_IMAGE.roothash" "$MINIMAL_IMAGE.foohash"
+systemd-run -P \
+ -p RootImage="$MINIMAL_IMAGE.raw" \
+ -p RootHash="$MINIMAL_IMAGE.foohash" \
+ -p RootVerity="$MINIMAL_IMAGE.fooverity" \
+ cat /usr/lib/os-release | grep -q -F "MARKER=1"
+# Let's use the long option name just here as a test
+systemd-run -P \
+ --property RootImage="$MINIMAL_IMAGE.raw" \
+ --property RootHash="$MINIMAL_IMAGE_ROOTHASH" \
+ --property RootVerity="$MINIMAL_IMAGE.fooverity" \
+ cat /usr/lib/os-release | grep -q -F "MARKER=1"
+mv "$MINIMAL_IMAGE.fooverity" "$MINIMAL_IMAGE.verity"
+mv "$MINIMAL_IMAGE.foohash" "$MINIMAL_IMAGE.roothash"
+
+# Derive partition UUIDs from root hash, in UUID syntax
+ROOT_UUID="$(systemd-id128 -u show "$(head -c 32 "$MINIMAL_IMAGE.roothash")" -u | tail -n 1 | cut -b 6-)"
+VERITY_UUID="$(systemd-id128 -u show "$(tail -c 32 "$MINIMAL_IMAGE.roothash")" -u | tail -n 1 | cut -b 6-)"
+
+systemd-dissect --json=short \
+ --root-hash "$MINIMAL_IMAGE_ROOTHASH" \
+ "$MINIMAL_IMAGE.gpt" | \
+ grep -q '{"rw":"ro","designator":"root","partition_uuid":"'"$ROOT_UUID"'","partition_label":"Root Partition","fstype":"squashfs","architecture":"'"$ARCHITECTURE"'","verity":"signed",'
+systemd-dissect --json=short \
+ --root-hash "$MINIMAL_IMAGE_ROOTHASH" \
+ "$MINIMAL_IMAGE.gpt" | \
+ grep -q '{"rw":"ro","designator":"root-verity","partition_uuid":"'"$VERITY_UUID"'","partition_label":"Verity Partition","fstype":"DM_verity_hash","architecture":"'"$ARCHITECTURE"'","verity":null,'
+if [[ -n "${OPENSSL_CONFIG:-}" ]]; then
+ systemd-dissect --json=short \
+ --root-hash "$MINIMAL_IMAGE_ROOTHASH" \
+ "$MINIMAL_IMAGE.gpt" | \
+ grep -qE '{"rw":"ro","designator":"root-verity-sig","partition_uuid":"'".*"'","partition_label":"Signature Partition","fstype":"verity_hash_signature","architecture":"'"$ARCHITECTURE"'","verity":null,'
+fi
+systemd-dissect --root-hash "$MINIMAL_IMAGE_ROOTHASH" "$MINIMAL_IMAGE.gpt" | grep -q -F "MARKER=1"
+systemd-dissect --root-hash "$MINIMAL_IMAGE_ROOTHASH" "$MINIMAL_IMAGE.gpt" | grep -q -F -f <(sed 's/"//g' "$OS_RELEASE")
+
+# Test image policies
+systemd-dissect --validate "$MINIMAL_IMAGE.gpt"
+systemd-dissect --validate "$MINIMAL_IMAGE.gpt" --image-policy='*'
+(! systemd-dissect --validate "$MINIMAL_IMAGE.gpt" --image-policy='~')
+(! systemd-dissect --validate "$MINIMAL_IMAGE.gpt" --image-policy='-')
+(! systemd-dissect --validate "$MINIMAL_IMAGE.gpt" --image-policy=root=absent)
+(! systemd-dissect --validate "$MINIMAL_IMAGE.gpt" --image-policy=swap=unprotected+encrypted+verity)
+systemd-dissect --validate "$MINIMAL_IMAGE.gpt" --image-policy=root=unprotected
+systemd-dissect --validate "$MINIMAL_IMAGE.gpt" --image-policy=root=verity
+systemd-dissect --validate "$MINIMAL_IMAGE.gpt" --image-policy=root=verity:root-verity-sig=unused+absent
+systemd-dissect --validate "$MINIMAL_IMAGE.gpt" --image-policy=root=verity:swap=absent
+systemd-dissect --validate "$MINIMAL_IMAGE.gpt" --image-policy=root=verity:swap=absent+unprotected
+(! systemd-dissect --validate "$MINIMAL_IMAGE.gpt" --image-policy=root=verity:root-verity=unused+absent)
+systemd-dissect --validate "$MINIMAL_IMAGE.gpt" --image-policy=root=signed
+(! systemd-dissect --validate "$MINIMAL_IMAGE.gpt" --image-policy=root=signed:root-verity-sig=unused+absent)
+(! systemd-dissect --validate "$MINIMAL_IMAGE.gpt" --image-policy=root=signed:root-verity=unused+absent)
+
+# Test RootImagePolicy= unit file setting
+systemd-run --wait -P \
+ -p RootImage="$MINIMAL_IMAGE.gpt" \
+ -p RootHash="$MINIMAL_IMAGE_ROOTHASH" \
+ -p MountAPIVFS=yes \
+ cat /usr/lib/os-release | grep -q -F "MARKER=1"
+systemd-run --wait -P \
+ -p RootImage="$MINIMAL_IMAGE.gpt" \
+ -p RootHash="$MINIMAL_IMAGE_ROOTHASH" \
+ -p RootImagePolicy='*' \
+ -p MountAPIVFS=yes \
+ cat /usr/lib/os-release | grep -q -F "MARKER=1"
+(! systemd-run --wait -P \
+ -p RootImage="$MINIMAL_IMAGE.gpt" \
+ -p RootHash="$MINIMAL_IMAGE_ROOTHASH" \
+ -p RootImagePolicy='~' \
+ -p MountAPIVFS=yes \
+ cat /usr/lib/os-release | grep -q -F "MARKER=1")
+(! systemd-run --wait -P \
+ -p RootImage="$MINIMAL_IMAGE.gpt" \
+ -p RootHash="$MINIMAL_IMAGE_ROOTHASH" \
+ -p RootImagePolicy='-' \
+ -p MountAPIVFS=yes \
+ cat /usr/lib/os-release | grep -q -F "MARKER=1")
+(! systemd-run --wait -P \
+ -p RootImage="$MINIMAL_IMAGE.gpt" \
+ -p RootHash="$MINIMAL_IMAGE_ROOTHASH" \
+ -p RootImagePolicy='root=absent' \
+ -p MountAPIVFS=yes \
+ cat /usr/lib/os-release | grep -q -F "MARKER=1")
+systemd-run --wait -P \
+ -p RootImage="$MINIMAL_IMAGE.gpt" \
+ -p RootHash="$MINIMAL_IMAGE_ROOTHASH" \
+ -p RootImagePolicy='root=verity' \
+ -p MountAPIVFS=yes \
+ cat /usr/lib/os-release | grep -q -F "MARKER=1"
+systemd-run --wait -P \
+ -p RootImage="$MINIMAL_IMAGE.gpt" \
+ -p RootHash="$MINIMAL_IMAGE_ROOTHASH" \
+ -p RootImagePolicy='root=signed' \
+ -p MountAPIVFS=yes \
+ cat /usr/lib/os-release | grep -q -F "MARKER=1"
+(! systemd-run --wait -P \
+ -p RootImage="$MINIMAL_IMAGE.gpt" \
+ -p RootHash="$MINIMAL_IMAGE_ROOTHASH" \
+ -p RootImagePolicy='root=encrypted' \
+ -p MountAPIVFS=yes \
+ cat /usr/lib/os-release | grep -q -F "MARKER=1")
+
+systemd-dissect --root-hash "$MINIMAL_IMAGE_ROOTHASH" --mount "$MINIMAL_IMAGE.gpt" "$IMAGE_DIR/mount"
+grep -q -F -f "$OS_RELEASE" "$IMAGE_DIR/mount/usr/lib/os-release"
+grep -q -F -f "$OS_RELEASE" "$IMAGE_DIR/mount/etc/os-release"
+grep -q -F "MARKER=1" "$IMAGE_DIR/mount/usr/lib/os-release"
+systemd-dissect --umount "$IMAGE_DIR/mount"
+
+systemd-dissect --root-hash "$MINIMAL_IMAGE_ROOTHASH" --mount "$MINIMAL_IMAGE.gpt" --in-memory "$IMAGE_DIR/mount"
+grep -q -F -f "$OS_RELEASE" "$IMAGE_DIR/mount/usr/lib/os-release"
+grep -q -F -f "$OS_RELEASE" "$IMAGE_DIR/mount/etc/os-release"
+grep -q -F "MARKER=1" "$IMAGE_DIR/mount/usr/lib/os-release"
+systemd-dissect --umount "$IMAGE_DIR/mount"
+
+# add explicit -p MountAPIVFS=yes once to test the parser
+systemd-run -P \
+ -p RootImage="$MINIMAL_IMAGE.gpt" \
+ -p RootHash="$MINIMAL_IMAGE_ROOTHASH" \
+ -p MountAPIVFS=yes \
+ cat /usr/lib/os-release | grep -q -F "MARKER=1"
+systemd-run -P \
+ -p RootImage="$MINIMAL_IMAGE.raw" \
+ -p RootImageOptions="root:nosuid,dev home:ro,dev ro,noatime" \
+ mount | grep -F "squashfs" | grep -q -F "nosuid"
+systemd-run -P \
+ -p RootImage="$MINIMAL_IMAGE.gpt" \
+ -p RootImageOptions="root:ro,noatime root:ro,dev" \
+ mount | grep -F "squashfs" | grep -q -F "noatime"
+
+mkdir -p "$IMAGE_DIR/result"
+cat >/run/systemd/system/testservice-50a.service <<EOF
+[Service]
+Type=oneshot
+ExecStart=bash -c "mount >/run/result/a"
+BindPaths=$IMAGE_DIR/result:/run/result
+TemporaryFileSystem=/run
+RootImage=$MINIMAL_IMAGE.raw
+RootImageOptions=root:ro,noatime home:ro,dev relatime,dev
+RootImageOptions=nosuid,dev
+EOF
+systemctl start testservice-50a.service
+grep -F "squashfs" "$IMAGE_DIR/result/a" | grep -q -F "noatime"
+grep -F "squashfs" "$IMAGE_DIR/result/a" | grep -q -F -v "nosuid"
+
+cat >/run/systemd/system/testservice-50b.service <<EOF
+[Service]
+Type=oneshot
+ExecStart=bash -c "mount >/run/result/b"
+BindPaths=$IMAGE_DIR/result:/run/result
+TemporaryFileSystem=/run
+RootImage=$MINIMAL_IMAGE.gpt
+RootImageOptions=root:ro,noatime,nosuid home:ro,dev nosuid,dev
+RootImageOptions=home:ro,dev nosuid,dev,%%foo
+# this is the default, but let's specify once to test the parser
+MountAPIVFS=yes
+EOF
+systemctl start testservice-50b.service
+grep -F "squashfs" "$IMAGE_DIR/result/b" | grep -q -F "noatime"
+
+# Check that specifier escape is applied %%foo → %foo
+busctl get-property org.freedesktop.systemd1 \
+ /org/freedesktop/systemd1/unit/testservice_2d50b_2eservice \
+ org.freedesktop.systemd1.Service RootImageOptions | grep -F "nosuid,dev,%foo"
+
+# Now do some checks with MountImages, both by itself, with options and in combination with RootImage, and as single FS or GPT image
+systemd-run -P \
+ -p MountImages="$MINIMAL_IMAGE.gpt:/run/img1 $MINIMAL_IMAGE.raw:/run/img2" \
+ cat /run/img1/usr/lib/os-release | grep -q -F "MARKER=1"
+systemd-run -P \
+ -p MountImages="$MINIMAL_IMAGE.gpt:/run/img1 $MINIMAL_IMAGE.raw:/run/img2" \
+ cat /run/img2/usr/lib/os-release | grep -q -F "MARKER=1"
+systemd-run -P \
+ -p MountImages="$MINIMAL_IMAGE.gpt:/run/img1 $MINIMAL_IMAGE.raw:/run/img2:nosuid,dev" \
+ mount | grep -F "squashfs" | grep -q -F "nosuid"
+systemd-run -P \
+ -p MountImages="$MINIMAL_IMAGE.gpt:/run/img1:root:nosuid $MINIMAL_IMAGE.raw:/run/img2:home:suid" \
+ mount | grep -F "squashfs" | grep -q -F "nosuid"
+systemd-run -P \
+ -p MountImages="$MINIMAL_IMAGE.raw:/run/img2\:3" \
+ cat /run/img2:3/usr/lib/os-release | grep -q -F "MARKER=1"
+systemd-run -P \
+ -p MountImages="$MINIMAL_IMAGE.raw:/run/img2\:3:nosuid" \
+ mount | grep -F "squashfs" | grep -q -F "nosuid"
+systemd-run -P \
+ -p TemporaryFileSystem=/run \
+ -p RootImage="$MINIMAL_IMAGE.raw" \
+ -p MountImages="$MINIMAL_IMAGE.gpt:/run/img1 $MINIMAL_IMAGE.raw:/run/img2" \
+ cat /usr/lib/os-release | grep -q -F "MARKER=1"
+systemd-run -P \
+ -p TemporaryFileSystem=/run \
+ -p RootImage="$MINIMAL_IMAGE.raw" \
+ -p MountImages="$MINIMAL_IMAGE.gpt:/run/img1 $MINIMAL_IMAGE.raw:/run/img2" \
+ cat /run/img1/usr/lib/os-release | grep -q -F "MARKER=1"
+systemd-run -P \
+ -p TemporaryFileSystem=/run \
+ -p RootImage="$MINIMAL_IMAGE.gpt" \
+ -p RootHash="$MINIMAL_IMAGE_ROOTHASH" \
+ -p MountImages="$MINIMAL_IMAGE.gpt:/run/img1 $MINIMAL_IMAGE.raw:/run/img2" \
+ cat /run/img2/usr/lib/os-release | grep -q -F "MARKER=1"
+cat >/run/systemd/system/testservice-50c.service <<EOF
+[Service]
+MountAPIVFS=yes
+TemporaryFileSystem=/run
+RootImage=$MINIMAL_IMAGE.raw
+MountImages=$MINIMAL_IMAGE.gpt:/run/img1:root:noatime:home:relatime
+MountImages=$MINIMAL_IMAGE.raw:/run/img2\:3:nosuid
+ExecStart=bash -c "cat /run/img1/usr/lib/os-release >/run/result/c"
+ExecStart=bash -c "cat /run/img2:3/usr/lib/os-release >>/run/result/c"
+ExecStart=bash -c "mount >>/run/result/c"
+BindPaths=$IMAGE_DIR/result:/run/result
+Type=oneshot
+EOF
+systemctl start testservice-50c.service
+grep -q -F "MARKER=1" "$IMAGE_DIR/result/c"
+grep -F "squashfs" "$IMAGE_DIR/result/c" | grep -q -F "noatime"
+grep -F "squashfs" "$IMAGE_DIR/result/c" | grep -q -F -v "nosuid"
+
+# Adding a new mounts at runtime works if the unit is in the active state,
+# so use Type=notify to make sure there's no race condition in the test
+cat >/run/systemd/system/testservice-50d.service <<EOF
+[Service]
+RuntimeMaxSec=300
+Type=notify
+RemainAfterExit=yes
+MountAPIVFS=yes
+PrivateTmp=yes
+ExecStart=sh -c ' \\
+ systemd-notify --ready; \\
+ while [ ! -f /tmp/img/usr/lib/os-release ] || ! grep -q -F MARKER /tmp/img/usr/lib/os-release; do \\
+ sleep 0.1; \\
+ done; \\
+ mount; \\
+ mount | grep -F "on /tmp/img type squashfs" | grep -q -F "nosuid"; \\
+'
+EOF
+systemctl start testservice-50d.service
+
+# Mount twice to exercise mount-beneath (on kernel 6.5+, on older kernels it will just overmount)
+mkdir -p /tmp/wrong/foo
+mksquashfs /tmp/wrong/foo /tmp/wrong.raw
+systemctl mount-image --mkdir testservice-50d.service /tmp/wrong.raw /tmp/img
+test "$(systemctl show -P SubState testservice-50d.service)" = "running"
+systemctl mount-image --mkdir testservice-50d.service "$MINIMAL_IMAGE.raw" /tmp/img root:nosuid
+# shellcheck disable=SC2016
+timeout 30s bash -xec 'while [[ $(systemctl show -P SubState testservice-50d.service) == running ]]; do sleep .2; done'
+systemctl is-active testservice-50d.service
+
+# ExtensionImages will set up an overlay
+systemd-run -P \
+ --property ExtensionImages=/usr/share/app0.raw \
+ --property RootImage="$MINIMAL_IMAGE.raw" \
+ cat /opt/script0.sh | grep -q -F "extension-release.app0"
+systemd-run -P \
+ --property ExtensionImages=/usr/share/app0.raw \
+ --property RootImage="$MINIMAL_IMAGE.raw" \
+ cat /usr/lib/systemd/system/some_file | grep -q -F "MARKER=1"
+systemd-run -P \
+ --property ExtensionImages="/usr/share/app0.raw /usr/share/app1.raw" \
+ --property RootImage="$MINIMAL_IMAGE.raw" \
+ cat /opt/script0.sh | grep -q -F "extension-release.app0"
+systemd-run -P \
+ --property ExtensionImages="/usr/share/app0.raw /usr/share/app1.raw" \
+ --property RootImage="$MINIMAL_IMAGE.raw" \
+ cat /usr/lib/systemd/system/some_file | grep -q -F "MARKER=1"
+systemd-run -P \
+ --property ExtensionImages="/usr/share/app0.raw /usr/share/app1.raw" \
+ --property RootImage="$MINIMAL_IMAGE.raw" \
+ cat /opt/script1.sh | grep -q -F "extension-release.app2"
+systemd-run -P \
+ --property ExtensionImages="/usr/share/app0.raw /usr/share/app1.raw" \
+ --property RootImage="$MINIMAL_IMAGE.raw" \
+ cat /usr/lib/systemd/system/other_file | grep -q -F "MARKER=1"
+systemd-run -P \
+ --property ExtensionImages=/usr/share/app-nodistro.raw \
+ --property RootImage="$MINIMAL_IMAGE.raw" \
+ cat /usr/lib/systemd/system/some_file | grep -q -F "MARKER=1"
+systemd-run -P \
+ --property ExtensionImages=/etc/service-scoped-test.raw \
+ --property RootImage="$MINIMAL_IMAGE.raw" \
+ cat /etc/systemd/system/some_file | grep -q -F "MARKER_CONFEXT_123"
+# Check that using a symlink to NAME-VERSION.raw works as long as the symlink has the correct name NAME.raw
+mkdir -p /usr/share/symlink-test/
+cp /usr/share/app-nodistro.raw /usr/share/symlink-test/app-nodistro-v1.raw
+ln -fs /usr/share/symlink-test/app-nodistro-v1.raw /usr/share/symlink-test/app-nodistro.raw
+systemd-run -P \
+ --property ExtensionImages=/usr/share/symlink-test/app-nodistro.raw \
+ --property RootImage="$MINIMAL_IMAGE.raw" \
+ cat /usr/lib/systemd/system/some_file | grep -q -F "MARKER=1"
+
+# Symlink check again but for confext
+mkdir -p /etc/symlink-test/
+cp /etc/service-scoped-test.raw /etc/symlink-test/service-scoped-test-v1.raw
+ln -fs /etc/symlink-test/service-scoped-test-v1.raw /etc/symlink-test/service-scoped-test.raw
+systemd-run -P \
+ --property ExtensionImages=/etc/symlink-test/service-scoped-test.raw \
+ --property RootImage="$MINIMAL_IMAGE.raw" \
+ cat /etc/systemd/system/some_file | grep -q -F "MARKER_CONFEXT_123"
+# And again mixing sysext and confext
+systemd-run -P \
+ --property ExtensionImages=/usr/share/symlink-test/app-nodistro.raw \
+ --property ExtensionImages=/etc/symlink-test/service-scoped-test.raw \
+ --property RootImage="$MINIMAL_IMAGE.raw" \
+ cat /etc/systemd/system/some_file | grep -q -F "MARKER_CONFEXT_123"
+systemd-run -P \
+ --property ExtensionImages=/usr/share/symlink-test/app-nodistro.raw \
+ --property ExtensionImages=/etc/symlink-test/service-scoped-test.raw \
+ --property RootImage="$MINIMAL_IMAGE.raw" \
+ cat /usr/lib/systemd/system/some_file | grep -q -F "MARKER=1"
+
+cat >/run/systemd/system/testservice-50e.service <<EOF
+[Service]
+MountAPIVFS=yes
+TemporaryFileSystem=/run /var/lib
+StateDirectory=app0
+RootImage=$MINIMAL_IMAGE.raw
+ExtensionImages=/usr/share/app0.raw /usr/share/app1.raw:nosuid
+# Relevant only for sanitizer runs
+UnsetEnvironment=LD_PRELOAD
+ExecStart=bash -c '/opt/script0.sh | grep ID'
+ExecStart=bash -c '/opt/script1.sh | grep ID'
+Type=oneshot
+RemainAfterExit=yes
+EOF
+systemctl start testservice-50e.service
+systemctl is-active testservice-50e.service
+
+# Check vpick support in ExtensionImages=
+VBASE="vtest$RANDOM"
+VDIR="/tmp/$VBASE.v"
+mkdir "$VDIR"
+
+ln -s /usr/share/app0.raw "$VDIR/${VBASE}_0.raw"
+ln -s /usr/share/app1.raw "$VDIR/${VBASE}_1.raw"
+
+systemd-run -P -p ExtensionImages="$VDIR" bash -c '/opt/script1.sh | grep ID'
+
+rm -rf "$VDIR"
+
+# ExtensionDirectories will set up an overlay
+mkdir -p "$IMAGE_DIR/app0" "$IMAGE_DIR/app1" "$IMAGE_DIR/app-nodistro" "$IMAGE_DIR/service-scoped-test"
+(! systemd-run -P \
+ --property ExtensionDirectories="$IMAGE_DIR/nonexistent" \
+ --property RootImage="$MINIMAL_IMAGE.raw" \
+ cat /opt/script0.sh)
+(! systemd-run -P \
+ --property ExtensionDirectories="$IMAGE_DIR/app0" \
+ --property RootImage="$MINIMAL_IMAGE.raw" \
+ cat /opt/script0.sh)
+systemd-dissect --mount /usr/share/app0.raw "$IMAGE_DIR/app0"
+systemd-dissect --mount /usr/share/app1.raw "$IMAGE_DIR/app1"
+systemd-dissect --mount /usr/share/app-nodistro.raw "$IMAGE_DIR/app-nodistro"
+systemd-dissect --mount /etc/service-scoped-test.raw "$IMAGE_DIR/service-scoped-test"
+systemd-run -P \
+ --property ExtensionDirectories="$IMAGE_DIR/app0" \
+ --property RootImage="$MINIMAL_IMAGE.raw" \
+ cat /opt/script0.sh | grep -q -F "extension-release.app0"
+systemd-run -P \
+ --property ExtensionDirectories="$IMAGE_DIR/app0" \
+ --property RootImage="$MINIMAL_IMAGE.raw" \
+ cat /usr/lib/systemd/system/some_file | grep -q -F "MARKER=1"
+systemd-run -P \
+ --property ExtensionDirectories="$IMAGE_DIR/app0 $IMAGE_DIR/app1" \
+ --property RootImage="$MINIMAL_IMAGE.raw" \
+ cat /opt/script0.sh | grep -q -F "extension-release.app0"
+systemd-run -P \
+ --property ExtensionDirectories="$IMAGE_DIR/app0 $IMAGE_DIR/app1" \
+ --property RootImage="$MINIMAL_IMAGE.raw" \
+ cat /usr/lib/systemd/system/some_file | grep -q -F "MARKER=1"
+systemd-run -P \
+ --property ExtensionDirectories="$IMAGE_DIR/app0 $IMAGE_DIR/app1" \
+ --property RootImage="$MINIMAL_IMAGE.raw" \
+ cat /opt/script1.sh | grep -q -F "extension-release.app2"
+systemd-run -P \
+ --property ExtensionDirectories="$IMAGE_DIR/app0 $IMAGE_DIR/app1" \
+ --property RootImage="$MINIMAL_IMAGE.raw" \
+ cat /usr/lib/systemd/system/other_file | grep -q -F "MARKER=1"
+systemd-run -P \
+ --property ExtensionDirectories="$IMAGE_DIR/app-nodistro" \
+ --property RootImage="$MINIMAL_IMAGE.raw" \
+ cat /usr/lib/systemd/system/some_file | grep -q -F "MARKER=1"
+systemd-run -P \
+ --property ExtensionDirectories="$IMAGE_DIR/service-scoped-test" \
+ --property RootImage="$MINIMAL_IMAGE.raw" \
+ cat /etc/systemd/system/some_file | grep -q -F "MARKER_CONFEXT_123"
+cat >/run/systemd/system/testservice-50f.service <<EOF
+[Service]
+MountAPIVFS=yes
+TemporaryFileSystem=/run /var/lib
+StateDirectory=app0
+RootImage=$MINIMAL_IMAGE.raw
+ExtensionDirectories=$IMAGE_DIR/app0 $IMAGE_DIR/app1
+# Relevant only for sanitizer runs
+UnsetEnvironment=LD_PRELOAD
+ExecStart=bash -c '/opt/script0.sh | grep ID'
+ExecStart=bash -c '/opt/script1.sh | grep ID'
+Type=oneshot
+RemainAfterExit=yes
+EOF
+systemctl start testservice-50f.service
+systemctl is-active testservice-50f.service
+
+# Check vpick support in ExtensionDirectories=
+VBASE="vtest$RANDOM"
+VDIR="/tmp/$VBASE.v"
+mkdir "$VDIR"
+
+ln -s "$IMAGE_DIR/app0" "$VDIR/${VBASE}_0"
+ln -s "$IMAGE_DIR/app1" "$VDIR/${VBASE}_1"
+
+systemd-run -P --property ExtensionDirectories="$VDIR" cat /opt/script1.sh | grep -q -F "extension-release.app2"
+
+rm -rf "$VDIR"
+
+systemd-dissect --umount "$IMAGE_DIR/app0"
+systemd-dissect --umount "$IMAGE_DIR/app1"
+
+# Test that an extension consisting of an empty directory under /etc/extensions/ takes precedence
+mkdir -p /var/lib/extensions/
+ln -s /usr/share/app-nodistro.raw /var/lib/extensions/app-nodistro.raw
+systemd-sysext merge
+grep -q -F "MARKER=1" /usr/lib/systemd/system/some_file
+systemd-sysext unmerge
+mkdir -p /etc/extensions/app-nodistro
+systemd-sysext merge
+test ! -e /usr/lib/systemd/system/some_file
+systemd-sysext unmerge
+rmdir /etc/extensions/app-nodistro
+
+# Similar, but go via varlink
+varlinkctl call /run/systemd/io.systemd.sysext io.systemd.sysext.List '{}'
+(! grep -q -F "MARKER=1" /usr/lib/systemd/system/some_file )
+varlinkctl call /run/systemd/io.systemd.sysext io.systemd.sysext.Merge '{}'
+grep -q -F "MARKER=1" /usr/lib/systemd/system/some_file
+varlinkctl call /run/systemd/io.systemd.sysext io.systemd.sysext.Refresh '{}'
+grep -q -F "MARKER=1" /usr/lib/systemd/system/some_file
+varlinkctl call /run/systemd/io.systemd.sysext io.systemd.sysext.Unmerge '{}'
+(! grep -q -F "MARKER=1" /usr/lib/systemd/system/some_file )
+
+# Check that extensions cannot contain os-release
+mkdir -p /run/extensions/app-reject/usr/lib/{extension-release.d/,systemd/system}
+echo "ID=_any" >/run/extensions/app-reject/usr/lib/extension-release.d/extension-release.app-reject
+echo "ID=_any" >/run/extensions/app-reject/usr/lib/os-release
+touch /run/extensions/app-reject/usr/lib/systemd/system/other_file
+(! systemd-sysext merge)
+test ! -e /usr/lib/systemd/system/some_file
+test ! -e /usr/lib/systemd/system/other_file
+systemd-sysext unmerge
+rm -rf /run/extensions/app-reject
+rm /var/lib/extensions/app-nodistro.raw
+
+# Some super basic test that RootImage= works with .v/ dirs
+VBASE="vtest$RANDOM"
+VDIR="/tmp/$VBASE.v"
+mkdir "$VDIR"
+
+ln -s "$MINIMAL_IMAGE.raw" "$VDIR/${VBASE}_33.raw"
+ln -s "$MINIMAL_IMAGE.raw" "$VDIR/${VBASE}_34.raw"
+ln -s "$MINIMAL_IMAGE.raw" "$VDIR/${VBASE}_35.raw"
+
+systemd-run -P -p RootImage="$VDIR" cat /usr/lib/os-release | grep -q -F "MARKER=1"
+
+rm "$VDIR/${VBASE}_33.raw" "$VDIR/${VBASE}_34.raw" "$VDIR/${VBASE}_35.raw"
+rmdir "$VDIR"
+
+mkdir -p /run/machines /run/portables /run/extensions
+touch /run/machines/a.raw /run/portables/b.raw /run/extensions/c.raw
+
+systemd-dissect --discover --json=short >/tmp/discover.json
+grep -q -F '{"name":"a","type":"raw","class":"machine","ro":false,"path":"/run/machines/a.raw"' /tmp/discover.json
+grep -q -F '{"name":"b","type":"raw","class":"portable","ro":false,"path":"/run/portables/b.raw"' /tmp/discover.json
+grep -q -F '{"name":"c","type":"raw","class":"sysext","ro":false,"path":"/run/extensions/c.raw"' /tmp/discover.json
+rm /tmp/discover.json /run/machines/a.raw /run/portables/b.raw /run/extensions/c.raw
+
+LOOP="$(systemd-dissect --attach --loop-ref=waldo "$MINIMAL_IMAGE.raw")"
+
+# Wait until the symlinks we want to test are established
+udevadm trigger -w "$LOOP"
+
+# Check if the /dev/loop/* symlinks really reference the right device
+test /dev/disk/by-loop-ref/waldo -ef "$LOOP"
+
+if [ "$(stat -c '%Hd:%Ld' "$MINIMAL_IMAGE.raw")" != '?d:?d' ] ; then
+ # Old stat didn't know the %Hd and %Ld specifiers and turned them into ?d
+ # instead. Let's simply skip the test on such old systems.
+ test "$(stat -c '/dev/disk/by-loop-inode/%Hd:%Ld-%i' "$MINIMAL_IMAGE.raw")" -ef "$LOOP"
+fi
+
+# Detach by loopback device
+systemd-dissect --detach "$LOOP"
+
+# Test long reference name.
+# Note, sizeof_field(struct loop_info64, lo_file_name) == 64,
+# and --loop-ref accepts upto 63 characters, and udev creates symlink
+# based on the name when it has upto _62_ characters.
+name="$(for _ in {1..62}; do echo -n 'x'; done)"
+LOOP="$(systemd-dissect --attach --loop-ref="$name" "$MINIMAL_IMAGE.raw")"
+udevadm trigger -w "$LOOP"
+
+# Check if the /dev/disk/by-loop-ref/$name symlink really references the right device
+test "/dev/disk/by-loop-ref/$name" -ef "$LOOP"
+
+# Detach by the /dev/disk/by-loop-ref symlink
+systemd-dissect --detach "/dev/disk/by-loop-ref/$name"
+
+name="$(for _ in {1..63}; do echo -n 'x'; done)"
+LOOP="$(systemd-dissect --attach --loop-ref="$name" "$MINIMAL_IMAGE.raw")"
+udevadm trigger -w "$LOOP"
+
+# Check if the /dev/disk/by-loop-ref/$name symlink does not exist
+test ! -e "/dev/disk/by-loop-ref/$name"
+
+# Detach by backing inode
+systemd-dissect --detach "$MINIMAL_IMAGE.raw"
+(! systemd-dissect --detach "$MINIMAL_IMAGE.raw")
+
+# check for confext functionality
+mkdir -p /run/confexts/test/etc/extension-release.d
+echo "ID=_any" >/run/confexts/test/etc/extension-release.d/extension-release.test
+echo "ARCHITECTURE=_any" >>/run/confexts/test/etc/extension-release.d/extension-release.test
+echo "MARKER_CONFEXT_123" >/run/confexts/test/etc/testfile
+cat <<EOF >/run/confexts/test/etc/testscript
+#!/bin/bash
+echo "This should not happen"
+EOF
+chmod +x /run/confexts/test/etc/testscript
+systemd-confext merge
+grep -q -F "MARKER_CONFEXT_123" /etc/testfile
+(! /etc/testscript)
+systemd-confext status
+systemd-confext unmerge
+rm -rf /run/confexts/
+
+unsquashfs -no-xattrs -d /tmp/img "$MINIMAL_IMAGE.raw"
+systemd-run --unit=test-root-ephemeral \
+ -p RootDirectory=/tmp/img \
+ -p RootEphemeral=yes \
+ -p Type=exec \
+ bash -c "touch /abc && sleep infinity"
+test -n "$(ls -A /var/lib/systemd/ephemeral-trees)"
+systemctl stop test-root-ephemeral
+# shellcheck disable=SC2016
+timeout 10 bash -c 'until test -z "$(ls -A /var/lib/systemd/ephemeral-trees)"; do sleep .5; done'
+test ! -f /tmp/img/abc
+
+systemd-dissect --mtree /tmp/img
+systemd-dissect --list /tmp/img
+
+read -r SHA256SUM1 _ < <(systemd-dissect --copy-from /tmp/img etc/os-release | sha256sum)
+test "$SHA256SUM1" != ""
+
+echo abc > abc
+systemd-dissect --copy-to /tmp/img abc /abc
+test -f /tmp/img/abc
+
+# Test for dissect tool support with systemd-sysext
+mkdir -p /run/extensions/ testkit/usr/lib/extension-release.d/
+echo "ID=_any" >testkit/usr/lib/extension-release.d/extension-release.testkit
+echo "ARCHITECTURE=_any" >>testkit/usr/lib/extension-release.d/extension-release.testkit
+echo "MARKER_SYSEXT_123" >testkit/usr/lib/testfile
+mksquashfs testkit/ testkit.raw
+cp testkit.raw /run/extensions/
+unsquashfs -l /run/extensions/testkit.raw
+systemd-dissect --no-pager /run/extensions/testkit.raw | grep -q '✓ sysext for portable service'
+systemd-dissect --no-pager /run/extensions/testkit.raw | grep -q '✓ sysext for system'
+systemd-sysext merge
+systemd-sysext status
+grep -q -F "MARKER_SYSEXT_123" /usr/lib/testfile
+systemd-sysext unmerge
+rm -rf /run/extensions/ testkit/
+
+# Test for dissect tool support with systemd-confext
+mkdir -p /run/confexts/ testjob/etc/extension-release.d/
+echo "ID=_any" >testjob/etc/extension-release.d/extension-release.testjob
+echo "ARCHITECTURE=_any" >>testjob/etc/extension-release.d/extension-release.testjob
+echo "MARKER_CONFEXT_123" >testjob/etc/testfile
+mksquashfs testjob/ testjob.raw
+cp testjob.raw /run/confexts/
+unsquashfs -l /run/confexts/testjob.raw
+systemd-dissect --no-pager /run/confexts/testjob.raw | grep -q '✓ confext for system'
+systemd-dissect --no-pager /run/confexts/testjob.raw | grep -q '✓ confext for portable service'
+systemd-confext merge
+systemd-confext status
+grep -q -F "MARKER_CONFEXT_123" /etc/testfile
+systemd-confext unmerge
+rm -rf /run/confexts/ testjob/
+
+systemd-run -P -p RootImage="$MINIMAL_IMAGE.raw" cat /run/host/os-release | cmp "$OS_RELEASE"
+
+# Test that systemd-sysext reloads the daemon.
+mkdir -p /var/lib/extensions/
+ln -s /usr/share/app-reload.raw /var/lib/extensions/app-reload.raw
+systemd-sysext merge --no-reload
+# the service should not be running
+(! systemctl --quiet is-active foo.service)
+systemd-sysext unmerge --no-reload
+systemd-sysext merge
+# shellcheck disable=SC2016
+timeout 30s bash -xec 'until [[ $(journalctl -b -u foo.service _TRANSPORT=stdout -o cat) == foo ]]; do sleep .5; done'
+systemd-sysext unmerge --no-reload
+# Grep on the Warning to find the warning helper mentioning the daemon reload.
+systemctl status foo.service 2>&1 | grep -q -F "Warning"
+systemd-sysext merge
+systemd-sysext unmerge
+systemctl status foo.service 2>&1 | grep -v -q -F "Warning"
+rm /var/lib/extensions/app-reload.raw
+
+# Sneak in a couple of expected-to-fail invocations to cover
+# https://github.com/systemd/systemd/issues/29610
+(! systemd-run -P -p MountImages="/this/should/definitely/not/exist.img:/run/img2\:3:nosuid" false)
+(! systemd-run -P -p ExtensionImages="/this/should/definitely/not/exist.img" false)
+(! systemd-run -P -p RootImage="/this/should/definitely/not/exist.img" false)
+(! systemd-run -P -p ExtensionDirectories="/foo/bar /foo/baz" false)
--- /dev/null
+#!/usr/bin/env bash
+# SPDX-License-Identifier: LGPL-2.1-or-later
+set -eux
+set -o pipefail
+
+# shellcheck source=test/units/util.sh
+. "$(dirname "$0")"/util.sh
+
+if [[ ! -f /usr/lib/systemd/system/systemd-mountfsd.socket ]] || \
+ [[ ! -f /usr/lib/systemd/system/systemd-nsresourced.socket ]] || \
+ ! command -v mksquashfs || \
+ ! grep -q bpf /sys/kernel/security/lsm ||
+ ! find /usr/lib* -name libbpf.so.1 2>/dev/null | grep .; then
+ echo "Skipping mountnfsd/nsresourced tests"
+ exit 0
+fi
+
+at_exit() {
+ set +e
+
+ umount -R /tmp/unpriv/mount
+ rmdir /tmp/unpriv
+ rm -f /tmp/test-50-unpriv-privkey.key /tmp/test-50-unpriv-cert.crt /run/verity.d/test-50-unpriv-cert.crt
+ rm -f /var/tmp/unpriv.raw /tmp/unpriv.raw.mtree /tmp/unpriv2.raw.mtree
+ rm -f /tmp/unpriv.out /tmp/unpriv.out2 /tmp/unpriv.out3
+}
+
+trap at_exit EXIT
+
+systemctl start systemd-mountfsd.socket systemd-nsresourced.socket
+
+openssl req -config "$OPENSSL_CONFIG" -subj="/CN=waldo" \
+ -x509 -sha256 -nodes -days 365 -newkey rsa:4096 \
+ -keyout /tmp/test-50-unpriv-privkey.key -out /tmp/test-50-unpriv-cert.crt
+
+systemd-dissect --mkdir --mount "$MINIMAL_IMAGE.raw" /tmp/unpriv/mount
+SYSTEMD_REPART_OVERRIDE_FSTYPE=squashfs \
+ systemd-repart -P \
+ -s /tmp/unpriv/mount \
+ --certificate=/tmp/test-50-unpriv-cert.crt \
+ --private-key=/tmp/test-50-unpriv-privkey.key \
+ /var/tmp/unpriv.raw
+systemd-dissect --rmdir --umount /tmp/unpriv/mount
+
+systemd-dissect --image-policy='root=unprotected:=absent+unused' /var/tmp/unpriv.raw
+systemd-dissect --image-policy='root=unprotected:=absent+unused' --mtree /var/tmp/unpriv.raw | tee /tmp/unpriv.raw.mtree
+
+# Run unpriv, should fail due to lack of privs
+(! runas testuser systemd-dissect /var/tmp/unpriv.raw)
+(! runas testuser systemd-dissect --mtree /var/tmp/unpriv.raw)
+
+# Install key in keychain
+cp /tmp/test-50-unpriv-cert.crt /run/verity.d
+
+# Now run unpriv again, should be OK now.
+runas testuser systemd-dissect /var/tmp/unpriv.raw
+runas testuser systemd-dissect --mtree /var/tmp/unpriv.raw | tee /tmp/unpriv2.raw.mtree
+
+# Check that unpriv and priv run yielded same results
+cmp /tmp/unpriv.raw.mtree /tmp/unpriv2.raw.mtree
+
+# Make sure nspawn works unpriv, too (for now do not nest)
+if ! systemd-detect-virt -c; then
+ systemd-nspawn --pipe -i /var/tmp/unpriv.raw --read-only echo thisisatest > /tmp/unpriv.out
+ echo thisisatest | cmp /tmp/unpriv.out -
+
+ # The unpriv user has no rights to lock the image or write to it. Let's
+ # turn off both for this test, so that we don't have to copy the image
+ # around.
+ systemd-run -M testuser@ --user --pipe \
+ -p Environment=SYSTEMD_NSPAWN_LOCK=0 \
+ -p Delegate=1 \
+ -p DelegateSubgroup=supervisor \
+ -p Environment=SYSTEMD_LOG_LEVEL=debug \
+ --wait -- \
+ systemd-nspawn --keep-unit -i /var/tmp/unpriv.raw --read-only --pipe echo thisisatest >/tmp/unpriv.out2
+ echo thisisatest | cmp /tmp/unpriv.out2 -
+fi
+
+systemd-run -M testuser@ --user --pipe -p RootImage=/var/tmp/unpriv.raw -p PrivateUsers=1 --wait echo thisisatest >/tmp/unpriv.out3
+echo thisisatest | cmp /tmp/unpriv.out3 -
#!/usr/bin/env bash
# SPDX-License-Identifier: LGPL-2.1-or-later
-# -*- mode: shell-script; indent-tabs-mode: nil; sh-basic-offset: 4; -*-
-# ex: ts=8 sw=4 sts=4 et filetype=sh
-# shellcheck disable=SC2233,SC2235
set -eux
set -o pipefail
-export SYSTEMD_LOG_LEVEL=debug
+# shellcheck source=test/units/test-control.sh
+. "$(dirname "$0")"/test-control.sh
-cleanup_image_dir() {
- if [ -z "${image_dir}" ]; then
- return
- fi
- umount "${image_dir}/app0"
- umount "${image_dir}/app1"
- umount "${image_dir}/app-nodistro"
- umount "${image_dir}/service-scoped-test"
- rm -rf "${image_dir}"
-}
+# Setup shared stuff & run all subtests
-fake_roots_dir=/fake-roots
+at_exit() {
+ set +e
-cleanup_fake_rootfses() {
- local tries=10 e
- local -a lines fake_roots_mounts
+ if [[ -z "${IMAGE_DIR:-}" ]]; then
+ return
+ fi
- while [[ ${tries} -gt 0 ]]; do
- tries=$((tries - 1))
- mapfile -t lines < <(mount | awk '{ print $3 }')
- fake_roots_mounts=()
- for e in "${lines[@]}"; do
- if [[ ${e} = "${fake_roots_dir}"/* ]]; then
- fake_roots_mounts+=( "${e}" )
- fi
- done
- if [[ ${#fake_roots_mounts[@]} -eq 0 ]]; then
- break
+ while read -r dir; do
+ if mountpoint -q "$dir"; then
+ umount -Rv "$dir"
fi
- for e in "${fake_roots_mounts[@]}"; do
- umount "${e}"
- done
- done
- rm -rf "${fake_roots_dir}"
-}
+ done < <(find "${IMAGE_DIR}" -mindepth 1 -maxdepth 1 -type d)
-# shellcheck disable=SC2317
-cleanup() {(
- set +ex
+ rm -rf "$IMAGE_DIR"
+}
- cleanup_image_dir
- cleanup_fake_rootfses
-)}
+trap at_exit EXIT
-udevadm control --log-level=debug
+: "Setup base images"
-cd /tmp
+export SYSTEMD_LOG_LEVEL=debug
+export ARCHITECTURE
+export IMAGE_DIR
+export MACHINE
+export MINIMAL_IMAGE
+export MINIMAL_IMAGE_ROOTHASH
+export OPENSSL_CONFIG
+export OS_RELEASE
+export ROOT_GUID
+export SIGNATURE_GUID
+export VERITY_GUID
-image_dir="$(mktemp -d -t -p /tmp tmp.XXXXXX)"
-if [ -z "${image_dir}" ] || [ ! -d "${image_dir}" ]; then
- echo "mktemp under /tmp failed"
+machine="$(uname -m)"
+if [[ "$machine" == "x86_64" ]]; then
+ ROOT_GUID=4f68bce3-e8cd-4db1-96e7-fbcaf984b709
+ VERITY_GUID=2c7357ed-ebd2-46d9-aec1-23d437ec2bf5
+ SIGNATURE_GUID=41092b05-9fc8-4523-994f-2def0408b176
+ ARCHITECTURE="x86-64"
+elif [[ "$machine" =~ ^(i386|i686|x86)$ ]]; then
+ ROOT_GUID=44479540-f297-41b2-9af7-d131d5f0458a
+ VERITY_GUID=d13c5d3b-b5d1-422a-b29f-9454fdc89d76
+ SIGNATURE_GUID=5996fc05-109c-48de-808b-23fa0830b676
+ ARCHITECTURE="x86"
+elif [[ "$machine" =~ ^(aarch64|aarch64_be|armv8b|armv8l)$ ]]; then
+ ROOT_GUID=b921b045-1df0-41c3-af44-4c6f280d3fae
+ VERITY_GUID=df3300ce-d69f-4c92-978c-9bfb0f38d820
+ SIGNATURE_GUID=6db69de6-29f4-4758-a7a5-962190f00ce3
+ ARCHITECTURE="arm64"
+elif [[ "$machine" == "arm" ]]; then
+ ROOT_GUID=69dad710-2ce4-4e3c-b16c-21a1d49abed3
+ VERITY_GUID=7386cdf2-203c-47a9-a498-f2ecce45a2d6
+ SIGNATURE_GUID=42b0455f-eb11-491d-98d3-56145ba9d037
+ ARCHITECTURE="arm"
+elif [[ "$machine" == "ia64" ]]; then
+ ROOT_GUID=993d8d3d-f80e-4225-855a-9daf8ed7ea97
+ VERITY_GUID=86ed10d5-b607-45bb-8957-d350f23d0571
+ SIGNATURE_GUID=e98b36ee-32ba-4882-9b12-0ce14655f46a
+ ARCHITECTURE="ia64"
+elif [[ "$machine" == "loongarch64" ]]; then
+ ROOT_GUID=77055800-792c-4f94-b39a-98c91b762bb6
+ VERITY_GUID=f3393b22-e9af-4613-a948-9d3bfbd0c535
+ SIGNATURE_GUID=5afb67eb-ecc8-4f85-ae8e-ac1e7c50e7d0
+ ARCHITECTURE="loongarch64"
+elif [[ "$machine" == "s390x" ]]; then
+ ROOT_GUID=5eead9a9-fe09-4a1e-a1d7-520d00531306
+ VERITY_GUID=b325bfbe-c7be-4ab8-8357-139e652d2f6b
+ SIGNATURE_GUID=c80187a5-73a3-491a-901a-017c3fa953e9
+ ARCHITECTURE="s390x"
+elif [[ "$machine" == "ppc64le" ]]; then
+ ROOT_GUID=c31c45e6-3f39-412e-80fb-4809c4980599
+ VERITY_GUID=906bd944-4589-4aae-a4e4-dd983917446a
+ SIGNATURE_GUID=d4a236e7-e873-4c07-bf1d-bf6cf7f1c3c6
+ ARCHITECTURE="ppc64-le"
+elif [[ "$machine" == "riscv64" ]]; then
+ ROOT_GUID=72ec70a6-cf74-40e6-bd49-4bda08e8f224
+ VERITY_GUID=b6ed5582-440b-4209-b8da-5ff7c419ea3d
+ SIGNATURE_GUID=efe0f087-ea8d-4469-821a-4c2a96a8386a
+ ARCHITECTURE="riscv64"
+elif [[ "$machine" == "riscv32" ]]; then
+ ROOT_GUID=60d5a7fe-8e7d-435c-b714-3dd8162144e1
+ VERITY_GUID=ae0253be-1167-4007-ac68-43926c14c5de
+ SIGNATURE_GUID=3a112a75-8729-4380-b4cf-764d79934448
+ ARCHITECTURE="riscv32"
+else
+ echo "Unexpected uname -m: $machine in testsuite-50.sh, please fix me"
exit 1
fi
-trap cleanup EXIT
-
-cp /usr/share/minimal* "${image_dir}/"
-image="${image_dir}/minimal_0"
-roothash="$(cat "${image}.roothash")"
-
-os_release="$(test -e /etc/os-release && echo /etc/os-release || echo /usr/lib/os-release)"
-
-systemd-dissect --json=short "${image}.raw" | grep -q -F '{"rw":"ro","designator":"root","partition_uuid":null,"partition_label":null,"fstype":"squashfs","architecture":null,"verity":"external"'
-systemd-dissect "${image}.raw" | grep -q -F "MARKER=1"
-systemd-dissect "${image}.raw" | grep -q -F -f <(sed 's/"//g' "$os_release")
-
-systemd-dissect --list "${image}.raw" | grep -q '^etc/os-release$'
-systemd-dissect --mtree "${image}.raw" --mtree-hash yes | grep -qe "^./usr/bin/cat type=file mode=0755 uid=0 gid=0 size=[0-9]* sha256sum=[a-z0-9]*$"
-systemd-dissect --mtree "${image}.raw" --mtree-hash no | grep -qe "^./usr/bin/cat type=file mode=0755 uid=0 gid=0 size=[0-9]*$"
-
-read -r SHA256SUM1 _ < <(systemd-dissect --copy-from "${image}.raw" etc/os-release | sha256sum)
-test "$SHA256SUM1" != ""
-read -r SHA256SUM2 _ < <(systemd-dissect --read-only --with "${image}.raw" sha256sum etc/os-release)
-test "$SHA256SUM2" != ""
-test "$SHA256SUM1" = "$SHA256SUM2"
-
-if systemctl --version | grep -qF -- "+LIBARCHIVE" ; then
- # Make sure tarballs are reproducible
- read -r SHA256SUM1 _ < <(systemd-dissect --make-archive "${image}.raw" | sha256sum)
- test "$SHA256SUM1" != ""
- read -r SHA256SUM2 _ < <(systemd-dissect --make-archive "${image}.raw" | sha256sum)
- test "$SHA256SUM2" != ""
- test "$SHA256SUM1" = "$SHA256SUM2"
- # Also check that a file we expect to be there is there
- systemd-dissect --make-archive "${image}.raw" | tar t | grep etc/os-release
-fi
-
-mv "${image}.verity" "${image}.fooverity"
-mv "${image}.roothash" "${image}.foohash"
-systemd-dissect --json=short "${image}.raw" --root-hash="${roothash}" --verity-data="${image}.fooverity" | grep -q -F '{"rw":"ro","designator":"root","partition_uuid":null,"partition_label":null,"fstype":"squashfs","architecture":null,"verity":"external"'
-systemd-dissect "${image}.raw" --root-hash="${roothash}" --verity-data="${image}.fooverity" | grep -q -F "MARKER=1"
-systemd-dissect "${image}.raw" --root-hash="${roothash}" --verity-data="${image}.fooverity" | grep -q -F -f <(sed 's/"//g' "$os_release")
-mv "${image}.fooverity" "${image}.verity"
-mv "${image}.foohash" "${image}.roothash"
-
-mkdir -p "${image_dir}/mount" "${image_dir}/mount2"
-systemd-dissect --mount "${image}.raw" "${image_dir}/mount"
-grep -q -F -f "$os_release" "${image_dir}/mount/usr/lib/os-release"
-grep -q -F -f "$os_release" "${image_dir}/mount/etc/os-release"
-grep -q -F "MARKER=1" "${image_dir}/mount/usr/lib/os-release"
-# Verity volume should be shared (opened only once)
-systemd-dissect --mount "${image}.raw" "${image_dir}/mount2"
-verity_count=$(find /dev/mapper/ -name "*verity*" | wc -l)
-# In theory we should check that count is exactly one. In practice, libdevmapper
-# randomly and unpredictably fails with an unhelpful EINVAL when a device is open
-# (and even mounted and in use), so best-effort is the most we can do for now
-if [ "${verity_count}" -lt 1 ]; then
- echo "Verity device ${image}.raw not found in /dev/mapper/"
- exit 1
-fi
-systemd-dissect --umount "${image_dir}/mount"
-systemd-dissect --umount "${image_dir}/mount2"
+udevadm control --log-level=debug
-systemd-run -P -p RootImage="${image}.raw" cat /usr/lib/os-release | grep -q -F "MARKER=1"
-mv "${image}.verity" "${image}.fooverity"
-mv "${image}.roothash" "${image}.foohash"
-systemd-run -P -p RootImage="${image}.raw" -p RootHash="${image}.foohash" -p RootVerity="${image}.fooverity" cat /usr/lib/os-release | grep -q -F "MARKER=1"
-# Let's use the long option name just here as a test
-systemd-run -P --property RootImage="${image}.raw" --property RootHash="${roothash}" --property RootVerity="${image}.fooverity" cat /usr/lib/os-release | grep -q -F "MARKER=1"
-mv "${image}.fooverity" "${image}.verity"
-mv "${image}.foohash" "${image}.roothash"
+IMAGE_DIR="$(mktemp -d --tmpdir="" TEST-50-IMAGES.XXX)"
+cp -v /usr/share/minimal* "$IMAGE_DIR/"
+MINIMAL_IMAGE="$IMAGE_DIR/minimal_0"
+MINIMAL_IMAGE_ROOTHASH="$(<"$MINIMAL_IMAGE.roothash")"
-# Make a GPT disk on the fly, with the squashfs as partition 1 and the verity hash tree as partition 2
-machine="$(uname -m)"
-if [ "${machine}" = "x86_64" ]; then
- root_guid=4f68bce3-e8cd-4db1-96e7-fbcaf984b709
- verity_guid=2c7357ed-ebd2-46d9-aec1-23d437ec2bf5
- signature_guid=41092b05-9fc8-4523-994f-2def0408b176
- architecture="x86-64"
-elif [ "${machine}" = "i386" ] || [ "${machine}" = "i686" ] || [ "${machine}" = "x86" ]; then
- root_guid=44479540-f297-41b2-9af7-d131d5f0458a
- verity_guid=d13c5d3b-b5d1-422a-b29f-9454fdc89d76
- signature_guid=5996fc05-109c-48de-808b-23fa0830b676
- architecture="x86"
-elif [ "${machine}" = "aarch64" ] || [ "${machine}" = "aarch64_be" ] || [ "${machine}" = "armv8b" ] || [ "${machine}" = "armv8l" ]; then
- root_guid=b921b045-1df0-41c3-af44-4c6f280d3fae
- verity_guid=df3300ce-d69f-4c92-978c-9bfb0f38d820
- signature_guid=6db69de6-29f4-4758-a7a5-962190f00ce3
- architecture="arm64"
-elif [ "${machine}" = "arm" ]; then
- root_guid=69dad710-2ce4-4e3c-b16c-21a1d49abed3
- verity_guid=7386cdf2-203c-47a9-a498-f2ecce45a2d6
- signature_guid=42b0455f-eb11-491d-98d3-56145ba9d037
- architecture="arm"
-elif [ "${machine}" = "loongarch64" ]; then
- root_guid=77055800-792c-4f94-b39a-98c91b762bb6
- verity_guid=f3393b22-e9af-4613-a948-9d3bfbd0c535
- signature_guid=5afb67eb-ecc8-4f85-ae8e-ac1e7c50e7d0
- architecture="loongarch64"
-elif [ "${machine}" = "ia64" ]; then
- root_guid=993d8d3d-f80e-4225-855a-9daf8ed7ea97
- verity_guid=86ed10d5-b607-45bb-8957-d350f23d0571
- signature_guid=e98b36ee-32ba-4882-9b12-0ce14655f46a
- architecture="ia64"
-elif [ "${machine}" = "s390x" ]; then
- root_guid=5eead9a9-fe09-4a1e-a1d7-520d00531306
- verity_guid=b325bfbe-c7be-4ab8-8357-139e652d2f6b
- signature_guid=c80187a5-73a3-491a-901a-017c3fa953e9
- architecture="s390x"
-elif [ "${machine}" = "ppc64le" ]; then
- root_guid=c31c45e6-3f39-412e-80fb-4809c4980599
- verity_guid=906bd944-4589-4aae-a4e4-dd983917446a
- signature_guid=d4a236e7-e873-4c07-bf1d-bf6cf7f1c3c6
- architecture="ppc64-le"
-else
- echo "Unexpected uname -m: ${machine} in testsuite-50.sh, please fix me"
- exit 1
-fi
-# du rounds up to block size, which is more helpful for partitioning
-root_size="$(du -k "${image}.raw" | cut -f1)"
-verity_size="$(du -k "${image}.verity" | cut -f1)"
-signature_size=4
-# 4MB seems to be the minimum size blkid will accept, below that probing fails
-dd if=/dev/zero of="${image}.gpt" bs=512 count=$((8192+root_size*2+verity_size*2+signature_size*2))
-# sfdisk seems unhappy if the size overflows into the next unit, eg: 1580KiB will be interpreted as 1MiB
-# so do some basic rounding up if the minimal image is more than 1 MB
-if [ "${root_size}" -ge 1024 ]; then
- root_size="$((root_size/1024 + 1))MiB"
-else
- root_size="${root_size}KiB"
-fi
-verity_size="$((verity_size * 2))KiB"
-signature_size="$((signature_size * 2))KiB"
+OS_RELEASE="$(test -e /etc/os-release && echo /etc/os-release || echo /usr/lib/os-release)"
-HAVE_OPENSSL=0
if systemctl --version | grep -q -- +OPENSSL ; then
- # The openssl binary is installed conditionally.
- # If we have OpenSSL support enabled and openssl is missing, fail early
- # with a proper error message.
- if ! command -v openssl >/dev/null 2>&1; then
- echo "openssl missing" >/failed
+ # The openssl binary is installed conditionally. If we have OpenSSL support enabled and openssl is
+ # missing, fail early with a proper error message.
+ if ! command -v openssl &>/dev/null; then
+ echo "openssl binary is missing" >/failed
exit 1
fi
- HAVE_OPENSSL=1
OPENSSL_CONFIG="$(mktemp)"
# Unfortunately OpenSSL insists on reading some config file, hence provide one with mostly placeholder contents
cat >"${OPENSSL_CONFIG:?}" <<EOF
CN = Common Name
emailAddress = test@email.com
EOF
+fi
+
+# Make a GPT disk on the fly, with the squashfs as partition 1 and the verity hash tree as partition 2
+#
+# du rounds up to block size, which is more helpful for partitioning
+root_size="$(du -k "$MINIMAL_IMAGE.raw" | cut -f1)"
+verity_size="$(du -k "$MINIMAL_IMAGE.verity" | cut -f1)"
+signature_size=4
+# 4MB seems to be the minimum size blkid will accept, below that probing fails
+dd if=/dev/zero of="$MINIMAL_IMAGE.gpt" bs=512 count=$((8192+root_size*2+verity_size*2+signature_size*2))
+# sfdisk seems unhappy if the size overflows into the next unit, eg: 1580KiB will be interpreted as 1MiB
+# so do some basic rounding up if the minimal image is more than 1 MB
+if [[ "$root_size" -ge 1024 ]]; then
+ root_size="$((root_size/1024 + 1))MiB"
+else
+ root_size="${root_size}KiB"
+fi
+verity_size="$((verity_size * 2))KiB"
+signature_size="$((signature_size * 2))KiB"
+if [[ -n "${OPENSSL_CONFIG:-}" ]]; then
# Create key pair
- openssl req -config "$OPENSSL_CONFIG" -new -x509 -newkey rsa:1024 -keyout "${image}.key" -out "${image}.crt" -days 365 -nodes
+ openssl req -config "$OPENSSL_CONFIG" -new -x509 -newkey rsa:1024 \
+ -keyout "$MINIMAL_IMAGE.key" -out "$MINIMAL_IMAGE.crt" -days 365 -nodes
# Sign Verity root hash with it
- openssl smime -sign -nocerts -noattr -binary -in "${image}.roothash" -inkey "${image}.key" -signer "${image}.crt" -outform der -out "${image}.roothash.p7s"
+ openssl smime -sign -nocerts -noattr -binary \
+ -in "$MINIMAL_IMAGE.roothash" \
+ -inkey "$MINIMAL_IMAGE.key" \
+ -signer "$MINIMAL_IMAGE.crt" \
+ -outform der \
+ -out "$MINIMAL_IMAGE.roothash.p7s"
# Generate signature partition JSON data
- echo '{"rootHash":"'"${roothash}"'","signature":"'"$(base64 -w 0 <"${image}.roothash.p7s")"'"}' >"${image}.verity-sig"
+ echo '{"rootHash":"'"$MINIMAL_IMAGE_ROOTHASH"'","signature":"'"$(base64 -w 0 <"$MINIMAL_IMAGE.roothash.p7s")"'"}' >"$MINIMAL_IMAGE.verity-sig"
# Pad it
- truncate -s "${signature_size}" "${image}.verity-sig"
+ truncate -s "$signature_size" "$MINIMAL_IMAGE.verity-sig"
# Register certificate in the (userspace) verity key ring
mkdir -p /run/verity.d
- ln -s "${image}.crt" /run/verity.d/ok.crt
+ ln -s "$MINIMAL_IMAGE.crt" /run/verity.d/ok.crt
fi
# Construct a UUID from hash
# input: 11111111222233334444555566667777
# output: 11111111-2222-3333-4444-555566667777
-uuid="$(head -c 32 "${image}.roothash" | sed -r 's/(.{8})(.{4})(.{4})(.{4})(.+)/\1-\2-\3-\4-\5/')"
-echo -e "label: gpt\nsize=${root_size}, type=${root_guid}, uuid=${uuid}" | sfdisk "${image}.gpt"
-uuid="$(tail -c 32 "${image}.roothash" | sed -r 's/(.{8})(.{4})(.{4})(.{4})(.+)/\1-\2-\3-\4-\5/')"
-echo -e "size=${verity_size}, type=${verity_guid}, uuid=${uuid}" | sfdisk "${image}.gpt" --append
-if [ "${HAVE_OPENSSL}" -eq 1 ]; then
- echo -e "size=${signature_size}, type=${signature_guid}" | sfdisk "${image}.gpt" --append
+uuid="$(head -c 32 "$MINIMAL_IMAGE.roothash" | sed -r 's/(.{8})(.{4})(.{4})(.{4})(.+)/\1-\2-\3-\4-\5/')"
+echo -e "label: gpt\nsize=$root_size, type=$ROOT_GUID, uuid=$uuid" | sfdisk "$MINIMAL_IMAGE.gpt"
+uuid="$(tail -c 32 "$MINIMAL_IMAGE.roothash" | sed -r 's/(.{8})(.{4})(.{4})(.{4})(.+)/\1-\2-\3-\4-\5/')"
+echo -e "size=$verity_size, type=$VERITY_GUID, uuid=$uuid" | sfdisk "$MINIMAL_IMAGE.gpt" --append
+if [[ -n "${OPENSSL_CONFIG:-}" ]]; then
+ echo -e "size=$signature_size, type=$SIGNATURE_GUID" | sfdisk "$MINIMAL_IMAGE.gpt" --append
fi
-sfdisk --part-label "${image}.gpt" 1 "Root Partition"
-sfdisk --part-label "${image}.gpt" 2 "Verity Partition"
-if [ "${HAVE_OPENSSL}" -eq 1 ]; then
- sfdisk --part-label "${image}.gpt" 3 "Signature Partition"
+sfdisk --part-label "$MINIMAL_IMAGE.gpt" 1 "Root Partition"
+sfdisk --part-label "$MINIMAL_IMAGE.gpt" 2 "Verity Partition"
+if [[ -n "${OPENSSL_CONFIG:-}" ]]; then
+ sfdisk --part-label "$MINIMAL_IMAGE.gpt" 3 "Signature Partition"
fi
-loop="$(losetup --show -P -f "${image}.gpt")"
+loop="$(losetup --show -P -f "$MINIMAL_IMAGE.gpt")"
partitions=(
"${loop:?}p1"
"${loop:?}p2"
)
-if [ "${HAVE_OPENSSL}" -eq 1 ]; then
- partitions+=( "${loop:?}p3" )
+if [[ -n "${OPENSSL_CONFIG:-}" ]]; then
+ partitions+=("${loop:?}p3")
fi
# The kernel sometimes(?) does not emit "add" uevent for loop block partition devices.
# Let's not expect the devices to be initialized.
udevadm wait --timeout 60 --settle --initialized=no "${partitions[@]}"
-udevadm lock --device="${loop}p1" dd if="${image}.raw" of="${loop}p1"
-udevadm lock --device="${loop}p2" dd if="${image}.verity" of="${loop}p2"
-if [ "${HAVE_OPENSSL}" -eq 1 ]; then
- udevadm lock --device="${loop}p3" dd if="${image}.verity-sig" of="${loop}p3"
-fi
-losetup -d "${loop}"
-
-# Derive partition UUIDs from root hash, in UUID syntax
-ROOT_UUID="$(systemd-id128 -u show "$(head -c 32 "${image}.roothash")" -u | tail -n 1 | cut -b 6-)"
-VERITY_UUID="$(systemd-id128 -u show "$(tail -c 32 "${image}.roothash")" -u | tail -n 1 | cut -b 6-)"
-
-systemd-dissect --json=short --root-hash "${roothash}" "${image}.gpt" | grep -q '{"rw":"ro","designator":"root","partition_uuid":"'"$ROOT_UUID"'","partition_label":"Root Partition","fstype":"squashfs","architecture":"'"$architecture"'","verity":"signed",'
-systemd-dissect --json=short --root-hash "${roothash}" "${image}.gpt" | grep -q '{"rw":"ro","designator":"root-verity","partition_uuid":"'"$VERITY_UUID"'","partition_label":"Verity Partition","fstype":"DM_verity_hash","architecture":"'"$architecture"'","verity":null,'
-if [ "${HAVE_OPENSSL}" -eq 1 ]; then
- systemd-dissect --json=short --root-hash "${roothash}" "${image}.gpt" | grep -q -E '{"rw":"ro","designator":"root-verity-sig","partition_uuid":"'".*"'","partition_label":"Signature Partition","fstype":"verity_hash_signature","architecture":"'"$architecture"'","verity":null,'
-fi
-systemd-dissect --root-hash "${roothash}" "${image}.gpt" | grep -q -F "MARKER=1"
-systemd-dissect --root-hash "${roothash}" "${image}.gpt" | grep -q -F -f <(sed 's/"//g' "$os_release")
-
-# Test image policies
-systemd-dissect --validate "${image}.gpt"
-systemd-dissect --validate "${image}.gpt" --image-policy='*'
-(! systemd-dissect --validate "${image}.gpt" --image-policy='~')
-(! systemd-dissect --validate "${image}.gpt" --image-policy='-')
-(! systemd-dissect --validate "${image}.gpt" --image-policy=root=absent)
-(! systemd-dissect --validate "${image}.gpt" --image-policy=swap=unprotected+encrypted+verity)
-systemd-dissect --validate "${image}.gpt" --image-policy=root=unprotected
-systemd-dissect --validate "${image}.gpt" --image-policy=root=verity
-systemd-dissect --validate "${image}.gpt" --image-policy=root=verity:root-verity-sig=unused+absent
-systemd-dissect --validate "${image}.gpt" --image-policy=root=verity:swap=absent
-systemd-dissect --validate "${image}.gpt" --image-policy=root=verity:swap=absent+unprotected
-(! systemd-dissect --validate "${image}.gpt" --image-policy=root=verity:root-verity=unused+absent)
-systemd-dissect --validate "${image}.gpt" --image-policy=root=signed
-(! systemd-dissect --validate "${image}.gpt" --image-policy=root=signed:root-verity-sig=unused+absent)
-(! systemd-dissect --validate "${image}.gpt" --image-policy=root=signed:root-verity=unused+absent)
-
-# Test RootImagePolicy= unit file setting
-systemd-run --wait -P -p RootImage="${image}.gpt" -p RootHash="${roothash}" -p MountAPIVFS=yes cat /usr/lib/os-release | grep -q -F "MARKER=1"
-systemd-run --wait -P -p RootImage="${image}.gpt" -p RootHash="${roothash}" -p RootImagePolicy='*' -p MountAPIVFS=yes cat /usr/lib/os-release | grep -q -F "MARKER=1"
-(! systemd-run --wait -P -p RootImage="${image}.gpt" -p RootHash="${roothash}" -p RootImagePolicy='~' -p MountAPIVFS=yes cat /usr/lib/os-release | grep -q -F "MARKER=1")
-(! systemd-run --wait -P -p RootImage="${image}.gpt" -p RootHash="${roothash}" -p RootImagePolicy='-' -p MountAPIVFS=yes cat /usr/lib/os-release | grep -q -F "MARKER=1")
-(! systemd-run --wait -P -p RootImage="${image}.gpt" -p RootHash="${roothash}" -p RootImagePolicy='root=absent' -p MountAPIVFS=yes cat /usr/lib/os-release | grep -q -F "MARKER=1")
-systemd-run --wait -P -p RootImage="${image}.gpt" -p RootHash="${roothash}" -p RootImagePolicy='root=verity' -p MountAPIVFS=yes cat /usr/lib/os-release | grep -q -F "MARKER=1"
-systemd-run --wait -P -p RootImage="${image}.gpt" -p RootHash="${roothash}" -p RootImagePolicy='root=signed' -p MountAPIVFS=yes cat /usr/lib/os-release | grep -q -F "MARKER=1"
-(! systemd-run --wait -P -p RootImage="${image}.gpt" -p RootHash="${roothash}" -p RootImagePolicy='root=encrypted' -p MountAPIVFS=yes cat /usr/lib/os-release | grep -q -F "MARKER=1")
-
-systemd-dissect --root-hash "${roothash}" --mount "${image}.gpt" "${image_dir}/mount"
-grep -q -F -f "$os_release" "${image_dir}/mount/usr/lib/os-release"
-grep -q -F -f "$os_release" "${image_dir}/mount/etc/os-release"
-grep -q -F "MARKER=1" "${image_dir}/mount/usr/lib/os-release"
-systemd-dissect --umount "${image_dir}/mount"
-
-systemd-dissect --root-hash "${roothash}" --mount "${image}.gpt" --in-memory "${image_dir}/mount"
-grep -q -F -f "$os_release" "${image_dir}/mount/usr/lib/os-release"
-grep -q -F -f "$os_release" "${image_dir}/mount/etc/os-release"
-grep -q -F "MARKER=1" "${image_dir}/mount/usr/lib/os-release"
-systemd-dissect --umount "${image_dir}/mount"
-
-# add explicit -p MountAPIVFS=yes once to test the parser
-systemd-run -P -p RootImage="${image}.gpt" -p RootHash="${roothash}" -p MountAPIVFS=yes cat /usr/lib/os-release | grep -q -F "MARKER=1"
-
-systemd-run -P -p RootImage="${image}.raw" -p RootImageOptions="root:nosuid,dev home:ro,dev ro,noatime" mount | grep -F "squashfs" | grep -q -F "nosuid"
-systemd-run -P -p RootImage="${image}.gpt" -p RootImageOptions="root:ro,noatime root:ro,dev" mount | grep -F "squashfs" | grep -q -F "noatime"
-
-mkdir -p "${image_dir}/result"
-cat >/run/systemd/system/testservice-50a.service <<EOF
-[Service]
-Type=oneshot
-ExecStart=bash -c "mount >/run/result/a"
-BindPaths=${image_dir}/result:/run/result
-TemporaryFileSystem=/run
-RootImage=${image}.raw
-RootImageOptions=root:ro,noatime home:ro,dev relatime,dev
-RootImageOptions=nosuid,dev
-EOF
-systemctl start testservice-50a.service
-grep -F "squashfs" "${image_dir}/result/a" | grep -q -F "noatime"
-grep -F "squashfs" "${image_dir}/result/a" | grep -q -F -v "nosuid"
-
-cat >/run/systemd/system/testservice-50b.service <<EOF
-[Service]
-Type=oneshot
-ExecStart=bash -c "mount >/run/result/b"
-BindPaths=${image_dir}/result:/run/result
-TemporaryFileSystem=/run
-RootImage=${image}.gpt
-RootImageOptions=root:ro,noatime,nosuid home:ro,dev nosuid,dev
-RootImageOptions=home:ro,dev nosuid,dev,%%foo
-# this is the default, but let's specify once to test the parser
-MountAPIVFS=yes
-EOF
-systemctl start testservice-50b.service
-grep -F "squashfs" "${image_dir}/result/b" | grep -q -F "noatime"
-
-# Check that specifier escape is applied %%foo → %foo
-busctl get-property org.freedesktop.systemd1 /org/freedesktop/systemd1/unit/testservice_2d50b_2eservice org.freedesktop.systemd1.Service RootImageOptions | grep -F "nosuid,dev,%foo"
-
-# Now do some checks with MountImages, both by itself, with options and in combination with RootImage, and as single FS or GPT image
-systemd-run -P -p MountImages="${image}.gpt:/run/img1 ${image}.raw:/run/img2" cat /run/img1/usr/lib/os-release | grep -q -F "MARKER=1"
-systemd-run -P -p MountImages="${image}.gpt:/run/img1 ${image}.raw:/run/img2" cat /run/img2/usr/lib/os-release | grep -q -F "MARKER=1"
-systemd-run -P -p MountImages="${image}.gpt:/run/img1 ${image}.raw:/run/img2:nosuid,dev" mount | grep -F "squashfs" | grep -q -F "nosuid"
-systemd-run -P -p MountImages="${image}.gpt:/run/img1:root:nosuid ${image}.raw:/run/img2:home:suid" mount | grep -F "squashfs" | grep -q -F "nosuid"
-systemd-run -P -p MountImages="${image}.raw:/run/img2\:3" cat /run/img2:3/usr/lib/os-release | grep -q -F "MARKER=1"
-systemd-run -P -p MountImages="${image}.raw:/run/img2\:3:nosuid" mount | grep -F "squashfs" | grep -q -F "nosuid"
-systemd-run -P -p TemporaryFileSystem=/run -p RootImage="${image}.raw" -p MountImages="${image}.gpt:/run/img1 ${image}.raw:/run/img2" cat /usr/lib/os-release | grep -q -F "MARKER=1"
-systemd-run -P -p TemporaryFileSystem=/run -p RootImage="${image}.raw" -p MountImages="${image}.gpt:/run/img1 ${image}.raw:/run/img2" cat /run/img1/usr/lib/os-release | grep -q -F "MARKER=1"
-systemd-run -P -p TemporaryFileSystem=/run -p RootImage="${image}.gpt" -p RootHash="${roothash}" -p MountImages="${image}.gpt:/run/img1 ${image}.raw:/run/img2" cat /run/img2/usr/lib/os-release | grep -q -F "MARKER=1"
-cat >/run/systemd/system/testservice-50c.service <<EOF
-[Service]
-MountAPIVFS=yes
-TemporaryFileSystem=/run
-RootImage=${image}.raw
-MountImages=${image}.gpt:/run/img1:root:noatime:home:relatime
-MountImages=${image}.raw:/run/img2\:3:nosuid
-ExecStart=bash -c "cat /run/img1/usr/lib/os-release >/run/result/c"
-ExecStart=bash -c "cat /run/img2:3/usr/lib/os-release >>/run/result/c"
-ExecStart=bash -c "mount >>/run/result/c"
-BindPaths=${image_dir}/result:/run/result
-Type=oneshot
-EOF
-systemctl start testservice-50c.service
-grep -q -F "MARKER=1" "${image_dir}/result/c"
-grep -F "squashfs" "${image_dir}/result/c" | grep -q -F "noatime"
-grep -F "squashfs" "${image_dir}/result/c" | grep -q -F -v "nosuid"
-
-# Adding a new mounts at runtime works if the unit is in the active state,
-# so use Type=notify to make sure there's no race condition in the test
-cat >/run/systemd/system/testservice-50d.service <<EOF
-[Service]
-RuntimeMaxSec=300
-Type=notify
-RemainAfterExit=yes
-MountAPIVFS=yes
-PrivateTmp=yes
-ExecStart=sh -c ' \\
- systemd-notify --ready; \\
- while [ ! -f /tmp/img/usr/lib/os-release ] || ! grep -q -F MARKER /tmp/img/usr/lib/os-release; do \\
- sleep 0.1; \\
- done; \\
- mount; \\
- mount | grep -F "on /tmp/img type squashfs" | grep -q -F "nosuid"; \\
-'
-EOF
-systemctl start testservice-50d.service
-
-# Mount twice to exercise mount-beneath (on kernel 6.5+, on older kernels it will just overmount)
-mkdir -p /tmp/wrong/foo
-mksquashfs /tmp/wrong/foo /tmp/wrong.raw
-systemctl mount-image --mkdir testservice-50d.service /tmp/wrong.raw /tmp/img
-test "$(systemctl show -P SubState testservice-50d.service)" = "running"
-systemctl mount-image --mkdir testservice-50d.service "${image}.raw" /tmp/img root:nosuid
-
-while systemctl show -P SubState testservice-50d.service | grep -q running
-do
- sleep 0.1
-done
-
-systemctl is-active testservice-50d.service
-
-# ExtensionImages will set up an overlay
-systemd-run -P --property ExtensionImages=/usr/share/app0.raw --property RootImage="${image}.raw" cat /opt/script0.sh | grep -q -F "extension-release.app0"
-systemd-run -P --property ExtensionImages=/usr/share/app0.raw --property RootImage="${image}.raw" cat /usr/lib/systemd/system/some_file | grep -q -F "MARKER=1"
-systemd-run -P --property ExtensionImages="/usr/share/app0.raw /usr/share/app1.raw" --property RootImage="${image}.raw" cat /opt/script0.sh | grep -q -F "extension-release.app0"
-systemd-run -P --property ExtensionImages="/usr/share/app0.raw /usr/share/app1.raw" --property RootImage="${image}.raw" cat /usr/lib/systemd/system/some_file | grep -q -F "MARKER=1"
-systemd-run -P --property ExtensionImages="/usr/share/app0.raw /usr/share/app1.raw" --property RootImage="${image}.raw" cat /opt/script1.sh | grep -q -F "extension-release.app2"
-systemd-run -P --property ExtensionImages="/usr/share/app0.raw /usr/share/app1.raw" --property RootImage="${image}.raw" cat /usr/lib/systemd/system/other_file | grep -q -F "MARKER=1"
-systemd-run -P --property ExtensionImages=/usr/share/app-nodistro.raw --property RootImage="${image}.raw" cat /usr/lib/systemd/system/some_file | grep -q -F "MARKER=1"
-systemd-run -P --property ExtensionImages=/etc/service-scoped-test.raw --property RootImage="${image}.raw" cat /etc/systemd/system/some_file | grep -q -F "MARKER_CONFEXT_123"
-# Check that using a symlink to NAME-VERSION.raw works as long as the symlink has the correct name NAME.raw
-mkdir -p /usr/share/symlink-test/
-cp /usr/share/app-nodistro.raw /usr/share/symlink-test/app-nodistro-v1.raw
-ln -fs /usr/share/symlink-test/app-nodistro-v1.raw /usr/share/symlink-test/app-nodistro.raw
-systemd-run -P --property ExtensionImages=/usr/share/symlink-test/app-nodistro.raw --property RootImage="${image}.raw" cat /usr/lib/systemd/system/some_file | grep -q -F "MARKER=1"
-
-# Symlink check again but for confext
-mkdir -p /etc/symlink-test/
-cp /etc/service-scoped-test.raw /etc/symlink-test/service-scoped-test-v1.raw
-ln -fs /etc/symlink-test/service-scoped-test-v1.raw /etc/symlink-test/service-scoped-test.raw
-systemd-run -P --property ExtensionImages=/etc/symlink-test/service-scoped-test.raw --property RootImage="${image}.raw" cat /etc/systemd/system/some_file | grep -q -F "MARKER_CONFEXT_123"
-# And again mixing sysext and confext
-systemd-run -P \
- --property ExtensionImages=/usr/share/symlink-test/app-nodistro.raw \
- --property ExtensionImages=/etc/symlink-test/service-scoped-test.raw \
- --property RootImage="${image}.raw" cat /etc/systemd/system/some_file | grep -q -F "MARKER_CONFEXT_123"
-systemd-run -P \
- --property ExtensionImages=/usr/share/symlink-test/app-nodistro.raw \
- --property ExtensionImages=/etc/symlink-test/service-scoped-test.raw \
- --property RootImage="${image}.raw" cat /usr/lib/systemd/system/some_file | grep -q -F "MARKER=1"
-
-cat >/run/systemd/system/testservice-50e.service <<EOF
-[Service]
-MountAPIVFS=yes
-TemporaryFileSystem=/run /var/lib
-StateDirectory=app0
-RootImage=${image}.raw
-ExtensionImages=/usr/share/app0.raw /usr/share/app1.raw:nosuid
-# Relevant only for sanitizer runs
-UnsetEnvironment=LD_PRELOAD
-ExecStart=bash -c '/opt/script0.sh | grep ID'
-ExecStart=bash -c '/opt/script1.sh | grep ID'
-Type=oneshot
-RemainAfterExit=yes
-EOF
-systemctl start testservice-50e.service
-systemctl is-active testservice-50e.service
-
-# Check vpick support in ExtensionImages=
-VBASE="vtest$RANDOM"
-VDIR="/tmp/${VBASE}.v"
-mkdir "$VDIR"
-
-ln -s /usr/share/app0.raw "$VDIR/${VBASE}_0.raw"
-ln -s /usr/share/app1.raw "$VDIR/${VBASE}_1.raw"
-
-systemd-run -P -p ExtensionImages="$VDIR" bash -c '/opt/script1.sh | grep ID'
-
-rm -rf "$VDIR"
-
-# ExtensionDirectories will set up an overlay
-mkdir -p "${image_dir}/app0" "${image_dir}/app1" "${image_dir}/app-nodistro" "${image_dir}/service-scoped-test"
-(! systemd-run -P --property ExtensionDirectories="${image_dir}/nonexistent" --property RootImage="${image}.raw" cat /opt/script0.sh)
-(! systemd-run -P --property ExtensionDirectories="${image_dir}/app0" --property RootImage="${image}.raw" cat /opt/script0.sh)
-systemd-dissect --mount /usr/share/app0.raw "${image_dir}/app0"
-systemd-dissect --mount /usr/share/app1.raw "${image_dir}/app1"
-systemd-dissect --mount /usr/share/app-nodistro.raw "${image_dir}/app-nodistro"
-systemd-dissect --mount /etc/service-scoped-test.raw "${image_dir}/service-scoped-test"
-systemd-run -P --property ExtensionDirectories="${image_dir}/app0" --property RootImage="${image}.raw" cat /opt/script0.sh | grep -q -F "extension-release.app0"
-systemd-run -P --property ExtensionDirectories="${image_dir}/app0" --property RootImage="${image}.raw" cat /usr/lib/systemd/system/some_file | grep -q -F "MARKER=1"
-systemd-run -P --property ExtensionDirectories="${image_dir}/app0 ${image_dir}/app1" --property RootImage="${image}.raw" cat /opt/script0.sh | grep -q -F "extension-release.app0"
-systemd-run -P --property ExtensionDirectories="${image_dir}/app0 ${image_dir}/app1" --property RootImage="${image}.raw" cat /usr/lib/systemd/system/some_file | grep -q -F "MARKER=1"
-systemd-run -P --property ExtensionDirectories="${image_dir}/app0 ${image_dir}/app1" --property RootImage="${image}.raw" cat /opt/script1.sh | grep -q -F "extension-release.app2"
-systemd-run -P --property ExtensionDirectories="${image_dir}/app0 ${image_dir}/app1" --property RootImage="${image}.raw" cat /usr/lib/systemd/system/other_file | grep -q -F "MARKER=1"
-systemd-run -P --property ExtensionDirectories="${image_dir}/app-nodistro" --property RootImage="${image}.raw" cat /usr/lib/systemd/system/some_file | grep -q -F "MARKER=1"
-systemd-run -P --property ExtensionDirectories="${image_dir}/service-scoped-test" --property RootImage="${image}.raw" cat /etc/systemd/system/some_file | grep -q -F "MARKER_CONFEXT_123"
-cat >/run/systemd/system/testservice-50f.service <<EOF
-[Service]
-MountAPIVFS=yes
-TemporaryFileSystem=/run /var/lib
-StateDirectory=app0
-RootImage=${image}.raw
-ExtensionDirectories=${image_dir}/app0 ${image_dir}/app1
-# Relevant only for sanitizer runs
-UnsetEnvironment=LD_PRELOAD
-ExecStart=bash -c '/opt/script0.sh | grep ID'
-ExecStart=bash -c '/opt/script1.sh | grep ID'
-Type=oneshot
-RemainAfterExit=yes
-EOF
-systemctl start testservice-50f.service
-systemctl is-active testservice-50f.service
-
-# Check vpick support in ExtensionDirectories=
-VBASE="vtest$RANDOM"
-VDIR="/tmp/${VBASE}.v"
-mkdir "$VDIR"
-
-ln -s "${image_dir}/app0" "$VDIR/${VBASE}_0"
-ln -s "${image_dir}/app1" "$VDIR/${VBASE}_1"
-
-systemd-run -P --property ExtensionDirectories="$VDIR" cat /opt/script1.sh | grep -q -F "extension-release.app2"
-
-rm -rf "$VDIR"
-
-systemd-dissect --umount "${image_dir}/app0"
-systemd-dissect --umount "${image_dir}/app1"
-
-# Test that an extension consisting of an empty directory under /etc/extensions/ takes precedence
-mkdir -p /var/lib/extensions/
-ln -s /usr/share/app-nodistro.raw /var/lib/extensions/app-nodistro.raw
-systemd-sysext merge
-grep -q -F "MARKER=1" /usr/lib/systemd/system/some_file
-systemd-sysext unmerge
-mkdir -p /etc/extensions/app-nodistro
-systemd-sysext merge
-test ! -e /usr/lib/systemd/system/some_file
-systemd-sysext unmerge
-rmdir /etc/extensions/app-nodistro
-
-# Similar, but go via varlink
-varlinkctl call /run/systemd/io.systemd.sysext io.systemd.sysext.List '{}'
-(! grep -q -F "MARKER=1" /usr/lib/systemd/system/some_file )
-varlinkctl call /run/systemd/io.systemd.sysext io.systemd.sysext.Merge '{}'
-grep -q -F "MARKER=1" /usr/lib/systemd/system/some_file
-varlinkctl call /run/systemd/io.systemd.sysext io.systemd.sysext.Refresh '{}'
-grep -q -F "MARKER=1" /usr/lib/systemd/system/some_file
-varlinkctl call /run/systemd/io.systemd.sysext io.systemd.sysext.Unmerge '{}'
-(! grep -q -F "MARKER=1" /usr/lib/systemd/system/some_file )
-
-# Check that extensions cannot contain os-release
-mkdir -p /run/extensions/app-reject/usr/lib/{extension-release.d/,systemd/system}
-echo "ID=_any" >/run/extensions/app-reject/usr/lib/extension-release.d/extension-release.app-reject
-echo "ID=_any" >/run/extensions/app-reject/usr/lib/os-release
-touch /run/extensions/app-reject/usr/lib/systemd/system/other_file
-(! systemd-sysext merge)
-test ! -e /usr/lib/systemd/system/some_file
-test ! -e /usr/lib/systemd/system/other_file
-systemd-sysext unmerge
-rm -rf /run/extensions/app-reject
-rm /var/lib/extensions/app-nodistro.raw
-
-# Some super basic test that RootImage= works with .v/ dirs
-VBASE="vtest$RANDOM"
-VDIR="/tmp/${VBASE}.v"
-mkdir "$VDIR"
-
-ln -s "${image}.raw" "$VDIR/${VBASE}_33.raw"
-ln -s "${image}.raw" "$VDIR/${VBASE}_34.raw"
-ln -s "${image}.raw" "$VDIR/${VBASE}_35.raw"
-
-systemd-run -P -p RootImage="$VDIR" cat /usr/lib/os-release | grep -q -F "MARKER=1"
-
-rm "$VDIR/${VBASE}_33.raw" "$VDIR/${VBASE}_34.raw" "$VDIR/${VBASE}_35.raw"
-rmdir "$VDIR"
-
-mkdir -p /run/machines /run/portables /run/extensions
-touch /run/machines/a.raw /run/portables/b.raw /run/extensions/c.raw
-
-systemd-dissect --discover --json=short >/tmp/discover.json
-grep -q -F '{"name":"a","type":"raw","class":"machine","ro":false,"path":"/run/machines/a.raw"' /tmp/discover.json
-grep -q -F '{"name":"b","type":"raw","class":"portable","ro":false,"path":"/run/portables/b.raw"' /tmp/discover.json
-grep -q -F '{"name":"c","type":"raw","class":"sysext","ro":false,"path":"/run/extensions/c.raw"' /tmp/discover.json
-rm /tmp/discover.json /run/machines/a.raw /run/portables/b.raw /run/extensions/c.raw
-
-# Check that the /sbin/mount.ddi helper works
-T="/tmp/mounthelper.$RANDOM"
-mount -t ddi "${image}.gpt" "$T" -o ro,X-mount.mkdir,discard
-umount -R "$T"
-rmdir "$T"
-
-LOOP="$(systemd-dissect --attach --loop-ref=waldo "${image}.raw")"
-
-# Wait until the symlinks we want to test are established
-udevadm trigger -w "$LOOP"
-
-# Check if the /dev/loop/* symlinks really reference the right device
-test /dev/disk/by-loop-ref/waldo -ef "$LOOP"
-
-if [ "$(stat -c '%Hd:%Ld' "${image}.raw")" != '?d:?d' ] ; then
- # Old stat didn't know the %Hd and %Ld specifiers and turned them into ?d
- # instead. Let's simply skip the test on such old systems.
- test "$(stat -c '/dev/disk/by-loop-inode/%Hd:%Ld-%i' "${image}.raw")" -ef "$LOOP"
-fi
-
-# Detach by loopback device
-systemd-dissect --detach "$LOOP"
-
-# Test long reference name.
-# Note, sizeof_field(struct loop_info64, lo_file_name) == 64,
-# and --loop-ref accepts upto 63 characters, and udev creates symlink
-# based on the name when it has upto _62_ characters.
-name="$(for _ in {1..62}; do echo -n 'x'; done)"
-LOOP="$(systemd-dissect --attach --loop-ref="$name" "${image}.raw")"
-udevadm trigger -w "$LOOP"
-
-# Check if the /dev/disk/by-loop-ref/$name symlink really references the right device
-test "/dev/disk/by-loop-ref/$name" -ef "$LOOP"
-
-# Detach by the /dev/disk/by-loop-ref symlink
-systemd-dissect --detach "/dev/disk/by-loop-ref/$name"
-
-name="$(for _ in {1..63}; do echo -n 'x'; done)"
-LOOP="$(systemd-dissect --attach --loop-ref="$name" "${image}.raw")"
-udevadm trigger -w "$LOOP"
-
-# Check if the /dev/disk/by-loop-ref/$name symlink does not exist
-test ! -e "/dev/disk/by-loop-ref/$name"
-
-# Detach by backing inode
-systemd-dissect --detach "${image}.raw"
-(! systemd-dissect --detach "${image}.raw")
-
-# check for confext functionality
-mkdir -p /run/confexts/test/etc/extension-release.d
-echo "ID=_any" >/run/confexts/test/etc/extension-release.d/extension-release.test
-echo "ARCHITECTURE=_any" >>/run/confexts/test/etc/extension-release.d/extension-release.test
-echo "MARKER_CONFEXT_123" >/run/confexts/test/etc/testfile
-cat <<EOF >/run/confexts/test/etc/testscript
-#!/bin/bash
-echo "This should not happen"
-EOF
-chmod +x /run/confexts/test/etc/testscript
-systemd-confext merge
-grep -q -F "MARKER_CONFEXT_123" /etc/testfile
-(! /etc/testscript)
-systemd-confext status
-systemd-confext unmerge
-rm -rf /run/confexts/
-
-unsquashfs -no-xattrs -d /tmp/img "${image}.raw"
-systemd-run --unit=test-root-ephemeral \
- -p RootDirectory=/tmp/img \
- -p RootEphemeral=yes \
- -p Type=exec \
- bash -c "touch /abc && sleep infinity"
-test -n "$(ls -A /var/lib/systemd/ephemeral-trees)"
-systemctl stop test-root-ephemeral
-# shellcheck disable=SC2016
-timeout 10 bash -c 'until test -z "$(ls -A /var/lib/systemd/ephemeral-trees)"; do sleep .5; done'
-test ! -f /tmp/img/abc
-
-systemd-dissect --mtree /tmp/img
-systemd-dissect --list /tmp/img
-
-read -r SHA256SUM1 _ < <(systemd-dissect --copy-from /tmp/img etc/os-release | sha256sum)
-test "$SHA256SUM1" != ""
-
-echo abc > abc
-systemd-dissect --copy-to /tmp/img abc /abc
-test -f /tmp/img/abc
-
-# Test for dissect tool support with systemd-sysext
-mkdir -p /run/extensions/ testkit/usr/lib/extension-release.d/
-echo "ID=_any" >testkit/usr/lib/extension-release.d/extension-release.testkit
-echo "ARCHITECTURE=_any" >>testkit/usr/lib/extension-release.d/extension-release.testkit
-echo "MARKER_SYSEXT_123" >testkit/usr/lib/testfile
-mksquashfs testkit/ testkit.raw
-cp testkit.raw /run/extensions/
-unsquashfs -l /run/extensions/testkit.raw
-systemd-dissect --no-pager /run/extensions/testkit.raw | grep -q '✓ sysext for portable service'
-systemd-dissect --no-pager /run/extensions/testkit.raw | grep -q '✓ sysext for system'
-systemd-sysext merge
-systemd-sysext status
-grep -q -F "MARKER_SYSEXT_123" /usr/lib/testfile
-systemd-sysext unmerge
-rm -rf /run/extensions/ testkit/
-
-# Test for dissect tool support with systemd-confext
-mkdir -p /run/confexts/ testjob/etc/extension-release.d/
-echo "ID=_any" >testjob/etc/extension-release.d/extension-release.testjob
-echo "ARCHITECTURE=_any" >>testjob/etc/extension-release.d/extension-release.testjob
-echo "MARKER_CONFEXT_123" >testjob/etc/testfile
-mksquashfs testjob/ testjob.raw
-cp testjob.raw /run/confexts/
-unsquashfs -l /run/confexts/testjob.raw
-systemd-dissect --no-pager /run/confexts/testjob.raw | grep -q '✓ confext for system'
-systemd-dissect --no-pager /run/confexts/testjob.raw | grep -q '✓ confext for portable service'
-systemd-confext merge
-systemd-confext status
-grep -q -F "MARKER_CONFEXT_123" /etc/testfile
-systemd-confext unmerge
-rm -rf /run/confexts/ testjob/
-
-systemd-run -P -p RootImage="${image}.raw" cat /run/host/os-release | cmp "${os_release}"
-
-# Test that systemd-sysext reloads the daemon.
-mkdir -p /var/lib/extensions/
-ln -s /usr/share/app-reload.raw /var/lib/extensions/app-reload.raw
-systemd-sysext merge --no-reload
-# the service should not be running
-if systemctl --quiet is-active foo.service; then
- echo "foo.service should not be active"
- exit 1
-fi
-systemd-sysext unmerge --no-reload
-systemd-sysext merge
-for RETRY in $(seq 60) LAST; do
- if journalctl --boot --unit foo.service | grep -q -P 'echo\[[0-9]+\]: foo'; then
- break
- fi
- if [ "${RETRY}" = LAST ]; then
- echo "Output of foo.service not found"
- exit 1
- fi
- sleep 0.5
-done
-systemd-sysext unmerge --no-reload
-# Grep on the Warning to find the warning helper mentioning the daemon reload.
-systemctl status foo.service 2>&1 | grep -q -F "Warning"
-systemd-sysext merge
-systemd-sysext unmerge
-systemctl status foo.service 2>&1 | grep -v -q -F "Warning"
-rm /var/lib/extensions/app-reload.raw
-
-# Test systemd-repart --make-ddi=:
-if command -v mksquashfs >/dev/null 2>&1; then
-
- openssl req -config "$OPENSSL_CONFIG" -subj="/CN=waldo" -x509 -sha256 -nodes -days 365 -newkey rsa:4096 -keyout /tmp/test-50-privkey.key -out /tmp/test-50-cert.crt
-
- mkdir -p /tmp/test-50-confext/etc/extension-release.d/
-
- echo "foobar50" > /tmp/test-50-confext/etc/waldo
-
- ( grep -e '^\(ID\|VERSION_ID\)=' /etc/os-release ; echo IMAGE_ID=waldo ; echo IMAGE_VERSION=7 ) > /tmp/test-50-confext/etc/extension-release.d/extension-release.waldo
-
- mkdir -p /run/confexts
-
- SYSTEMD_REPART_OVERRIDE_FSTYPE=squashfs systemd-repart -C -s /tmp/test-50-confext --certificate=/tmp/test-50-cert.crt --private-key=/tmp/test-50-privkey.key /run/confexts/waldo.confext.raw
- rm -rf /tmp/test-50-confext
-
- mkdir -p /run/verity.d
- cp /tmp/test-50-cert.crt /run/verity.d/
- systemd-dissect --mtree /run/confexts/waldo.confext.raw
-
- systemd-confext refresh
-
- read -r X < /etc/waldo
- test "$X" = foobar50
-
- rm /run/confexts/waldo.confext.raw
-
- systemd-confext refresh
-
- (! test -f /etc/waldo )
-
- mkdir -p /tmp/test-50-sysext/usr/lib/extension-release.d/
-
- # Make sure the sysext is big enough to not fit in the minimum partition size of repart so we know the
- # Minimize= logic is working.
- truncate --size=50M /tmp/test-50-sysext/usr/waldo
-
- ( grep -e '^\(ID\|VERSION_ID\)=' /etc/os-release ; echo IMAGE_ID=waldo ; echo IMAGE_VERSION=7 ) > /tmp/test-50-sysext/usr/lib/extension-release.d/extension-release.waldo
-
- mkdir -p /run/extensions
-
- SYSTEMD_REPART_OVERRIDE_FSTYPE=squashfs systemd-repart -S -s /tmp/test-50-sysext --certificate=/tmp/test-50-cert.crt --private-key=/tmp/test-50-privkey.key /run/extensions/waldo.sysext.raw
-
- systemd-dissect --mtree /run/extensions/waldo.sysext.raw
-
- systemd-sysext refresh
-
- test -f /usr/waldo
-
- rm /run/verity.d/test-50-cert.crt /run/extensions/waldo.sysext.raw /tmp/test-50-cert.crt /tmp/test-50-privkey.key
-
- systemd-sysext refresh
-
- (! test -f /usr/waldo)
+udevadm lock --device="${loop}p1" dd if="$MINIMAL_IMAGE.raw" of="${loop}p1"
+udevadm lock --device="${loop}p2" dd if="$MINIMAL_IMAGE.verity" of="${loop}p2"
+if [[ -n "${OPENSSL_CONFIG:-}" ]]; then
+ udevadm lock --device="${loop}p3" dd if="$MINIMAL_IMAGE.verity-sig" of="${loop}p3"
fi
+losetup -d "$loop"
-# Sneak in a couple of expected-to-fail invocations to cover
-# https://github.com/systemd/systemd/issues/29610
-(! systemd-run -P -p MountImages="/this/should/definitely/not/exist.img:/run/img2\:3:nosuid" false)
-(! systemd-run -P -p ExtensionImages="/this/should/definitely/not/exist.img" false)
-(! systemd-run -P -p RootImage="/this/should/definitely/not/exist.img" false)
-(! systemd-run -P -p ExtensionDirectories="/foo/bar /foo/baz" false)
-
-# general systemd-sysext tests
-
-shopt -s extglob
-
-die() {
- echo "${*}"
- exit 1
-}
-
-prep_root() {
- local r=${1}; shift
- local h=${1}; shift
-
- if [[ -d ${r} ]]; then
- die "${r@Q} is being reused as a root, possibly a result of copy-pasting some test case and forgetting to rename the root directory name"
- fi
-
- mkdir -p "${r}${h}" "${r}/usr/lib" "${r}/var/lib/extensions" "${r}/var/lib/extensions.mutable"
-}
-
-prep_env() {
- local mode=${1}; shift
-
- export SYSTEMD_SYSEXT_MUTABLE_MODE=${mode}
-}
-
-drop_env() {
- unset -v SYSTEMD_SYSEXT_MUTABLE_MODE
-}
-
-gen_os_release() {
- local r=${1}; shift
-
- {
- echo "ID=testtest"
- echo "VERSION=1.2.3"
- } >"${r}/usr/lib/os-release"
-}
-
-gen_test_ext_image() {
- local r=${1}; shift
- local h=${1}; shift
-
- local n d f
-
- n='test-extension'
- d="${r}/var/lib/extensions/${n}"
- f="${d}/usr/lib/extension-release.d/extension-release.${n}"
- mkdir -p "$(dirname "${f}")"
- echo "ID=_any" >"${f}"
- mkdir -p "${d}/${h}"
- touch "${d}${h}/preexisting-file-in-extension-image"
-}
-
-hierarchy_ext_mut_path() {
- local r=${1}; shift
- local h=${1}; shift
-
- # /a/b/c -> a.b.c
- local n=${h}
- n="${n##+(/)}"
- n="${n%%+(/)}"
- n="${n//\//.}"
-
- printf '%s' "${r}/var/lib/extensions.mutable/${n}"
-}
-
-prep_ext_mut() {
- local p=${1}; shift
-
- mkdir -p "${p}"
- touch "${p}/preexisting-file-in-extensions-mutable"
-}
-
-make_ro() {
- local r=${1}; shift
- local h=${1}; shift
-
- mount -o bind "${r}${h}" "${r}${h}"
- mount -o bind,remount,ro "${r}${h}"
-}
-
-prep_hierarchy() {
- local r=${1}; shift
- local h=${1}; shift
-
- touch "${r}${h}/preexisting-file-in-hierarchy"
-}
-
-prep_ro_hierarchy() {
- local r=${1}; shift
- local h=${1}; shift
-
- prep_hierarchy "${r}" "${h}"
- make_ro "${r}" "${h}"
-}
-
-# extra args:
-# "e" for checking for the preexisting file in extension
-# "h" for checking for the preexisting file in hierarchy
-# "u" for checking for the preexisting file in upperdir
-check_usual_suspects() {
- local root=${1}; shift
- local hierarchy=${1}; shift
- local message=${1}; shift
-
- local arg
- # shellcheck disable=SC2034 # the variables below are used indirectly
- local e='' h='' u=''
-
- for arg; do
- case ${arg} in
- e|h|u)
- local -n v=${arg}
- v=x
- unset -n v
- ;;
- *)
- die "invalid arg to ${0}: ${arg@Q}"
- ;;
- esac
- done
-
- # var name, file name
- local pairs=(
- e:preexisting-file-in-extension-image
- h:preexisting-file-in-hierarchy
- u:preexisting-file-in-extensions-mutable
- )
- local pair name file desc full_path
- for pair in "${pairs[@]}"; do
- name=${pair%%:*}
- file=${pair#*:}
- desc=${file//-/ }
- full_path="${root}${hierarchy}/${file}"
- local -n v=${name}
- if [[ -n ${v} ]]; then
- test -f "${full_path}" || {
- ls -la "$(dirname "${full_path}")"
- die "${desc} is missing ${message}"
- }
- else
- test ! -f "${full_path}" || {
- ls -la "$(dirname "${full_path}")"
- die "${desc} unexpectedly exists ${message}"
- }
- fi
- unset -n v
- done
-}
-
-check_usual_suspects_after_merge() {
- local r=${1}; shift
- local h=${1}; shift
-
- check_usual_suspects "${r}" "${h}" "after merge" "${@}"
-}
-
-check_usual_suspects_after_unmerge() {
- local r=${1}; shift
- local h=${1}; shift
-
- check_usual_suspects "${r}" "${h}" "after unmerge" "${@}"
-}
-
-drop_env
-
-#
-# no extension data in /var/lib/extensions.mutable/…, read-only hierarchy,
-# mutability disabled by default
-#
-# read-only merged
-#
-
-
-fake_root=${fake_roots_dir}/simple-read-only-with-read-only-hierarchy
-hierarchy=/usr
-
-prep_root "${fake_root}" "${hierarchy}"
-gen_os_release "${fake_root}"
-gen_test_ext_image "${fake_root}" "${hierarchy}"
-
-prep_ro_hierarchy "${fake_root}" "${hierarchy}"
-
-touch "${fake_root}${hierarchy}/should-fail-on-read-only-fs" && die "${fake_root}${hierarchy} is not read-only"
-
-# run systemd-sysext
-SYSTEMD_SYSEXT_HIERARCHIES="${hierarchy}" systemd-sysext --root="${fake_root}" merge
-
-touch "${fake_root}${hierarchy}/should-still-fail-on-read-only-fs" && die "${fake_root}${hierarchy} is not read-only"
-check_usual_suspects_after_merge "${fake_root}" "${hierarchy}" e h
-
-SYSTEMD_SYSEXT_HIERARCHIES="${hierarchy}" systemd-sysext --root="${fake_root}" unmerge
-
-check_usual_suspects_after_unmerge "${fake_root}" "${hierarchy}" h
-
-touch "${fake_root}${hierarchy}/should-still-fail-on-read-only-fs" && die "${fake_root}${hierarchy} is not read-only after unmerge"
-
-
-#
-# no extension data in /var/lib/extensions.mutable/…, mutable hierarchy,
-# mutability disabled by default
-#
-# read-only merged
-#
-
-
-fake_root=${fake_roots_dir}/simple-read-only-with-mutable-hierarchy
-hierarchy=/usr
-
-prep_root "${fake_root}" "${hierarchy}"
-gen_os_release "${fake_root}"
-gen_test_ext_image "${fake_root}" "${hierarchy}"
-
-prep_hierarchy "${fake_root}" "${hierarchy}"
-
-touch "${fake_root}${hierarchy}/should-succeed-on-mutable-fs" || die "${fake_root}${hierarchy} is not mutable"
-
-# run systemd-sysext
-SYSTEMD_SYSEXT_HIERARCHIES="${hierarchy}" systemd-sysext --root="${fake_root}" merge
-
-touch "${fake_root}${hierarchy}/should-fail-on-read-only-fs" && die "${fake_root}${hierarchy} is not read-only"
-check_usual_suspects_after_merge "${fake_root}" "${hierarchy}" e h
-
-SYSTEMD_SYSEXT_HIERARCHIES="${hierarchy}" systemd-sysext --root="${fake_root}" unmerge
-
-check_usual_suspects_after_unmerge "${fake_root}" "${hierarchy}" h
-
-touch "${fake_root}${hierarchy}/should-succeed-on-mutable-fs-again" || die "${fake_root}${hierarchy} is not mutable after unmerge"
-
-
-#
-# no extension data in /var/lib/extensions.mutable/…, no hierarchy either,
-# mutability disabled by default
-#
-# read-only merged
-#
-
-
-fake_root=${fake_roots_dir}/simple-read-only-with-missing-hierarchy
-hierarchy=/opt
-
-prep_root "${fake_root}" "${hierarchy}"
-rmdir "${fake_root}/${hierarchy}"
-gen_os_release "${fake_root}"
-gen_test_ext_image "${fake_root}" "${hierarchy}"
-
-# run systemd-sysext
-SYSTEMD_SYSEXT_HIERARCHIES="${hierarchy}" systemd-sysext --root="${fake_root}" merge
-
-touch "${fake_root}${hierarchy}/should-still-fail-on-read-only-fs" && die "${fake_root}${hierarchy} is not read-only"
-check_usual_suspects_after_merge "${fake_root}" "${hierarchy}" e
-
-SYSTEMD_SYSEXT_HIERARCHIES="${hierarchy}" systemd-sysext --root="${fake_root}" unmerge
-
-check_usual_suspects_after_unmerge "${fake_root}" "${hierarchy}"
-
-
-#
-# no extension data in /var/lib/extensions.mutable/…, an empty hierarchy,
-# mutability disabled by default
-#
-# read-only merged
-#
-
-
-fake_root=${fake_roots_dir}/simple-read-only-with-empty-hierarchy
-hierarchy=/opt
-
-prep_root "${fake_root}" "${hierarchy}"
-gen_os_release "${fake_root}"
-gen_test_ext_image "${fake_root}" "${hierarchy}"
-
-make_ro "${fake_root}" "${hierarchy}"
-
-touch "${fake_root}${hierarchy}/should-fail-on-read-only-fs" && die "${fake_root}${hierarchy} is not read-only"
-
-# run systemd-sysext
-SYSTEMD_SYSEXT_HIERARCHIES="${hierarchy}" systemd-sysext --root="${fake_root}" merge
-
-touch "${fake_root}${hierarchy}/should-still-fail-on-read-only-fs" && die "${fake_root}${hierarchy} is not read-only"
-check_usual_suspects_after_merge "${fake_root}" "${hierarchy}" e
-
-SYSTEMD_SYSEXT_HIERARCHIES="${hierarchy}" systemd-sysext --root="${fake_root}" unmerge
-
-check_usual_suspects_after_unmerge "${fake_root}" "${hierarchy}"
-
-
-#
-# extension data in /var/lib/extensions.mutable/…, read-only hierarchy, mutability disabled-by-default
-#
-# read-only merged
-#
-
-
-fake_root=${fake_roots_dir}/simple-mutable-with-read-only-hierarchy-disabled
-hierarchy=/usr
-
-prep_root "${fake_root}" "${hierarchy}"
-gen_os_release "${fake_root}"
-gen_test_ext_image "${fake_root}" "${hierarchy}"
-
-ext_data_path=$(hierarchy_ext_mut_path "${fake_root}" "${hierarchy}")
-prep_ext_mut "${ext_data_path}"
-
-prep_ro_hierarchy "${fake_root}" "${hierarchy}"
-
-touch "${fake_root}${hierarchy}/should-fail-on-read-only-fs" && die "${fake_root}${hierarchy} is not read-only"
-
-# run systemd-sysext
-SYSTEMD_SYSEXT_HIERARCHIES="${hierarchy}" systemd-sysext --root="${fake_root}" merge
-
-touch "${fake_root}${hierarchy}/should-be-read-only" && die "${fake_root}${hierarchy} is not read-only"
-check_usual_suspects_after_merge "${fake_root}" "${hierarchy}" e h
-
-SYSTEMD_SYSEXT_HIERARCHIES="${hierarchy}" systemd-sysext --root="${fake_root}" unmerge
-
-check_usual_suspects_after_unmerge "${fake_root}" "${hierarchy}" h
-
-
-#
-# extension data in /var/lib/extensions.mutable/…, read-only hierarchy, auto-mutability
-#
-# mutable merged
-#
-
-
-fake_root=${fake_roots_dir}/simple-mutable-with-read-only-hierarchy
-hierarchy=/usr
-
-prep_root "${fake_root}" "${hierarchy}"
-gen_os_release "${fake_root}"
-gen_test_ext_image "${fake_root}" "${hierarchy}"
-
-ext_data_path=$(hierarchy_ext_mut_path "${fake_root}" "${hierarchy}")
-prep_ext_mut "${ext_data_path}"
-
-prep_ro_hierarchy "${fake_root}" "${hierarchy}"
-
-touch "${fake_root}${hierarchy}/should-fail-on-read-only-fs" && die "${fake_root}${hierarchy} is not read-only"
-
-# run systemd-sysext
-SYSTEMD_SYSEXT_HIERARCHIES="${hierarchy}" systemd-sysext --root="${fake_root}" --mutable=auto merge
-
-touch "${fake_root}${hierarchy}/now-is-mutable" || die "${fake_root}${hierarchy} is not mutable"
-check_usual_suspects_after_merge "${fake_root}" "${hierarchy}" e h u
-test -f "${ext_data_path}/now-is-mutable" || die "now-is-mutable is not stored in expected location"
-
-SYSTEMD_SYSEXT_HIERARCHIES="${hierarchy}" systemd-sysext --root="${fake_root}" unmerge
-
-check_usual_suspects_after_unmerge "${fake_root}" "${hierarchy}" h
-test -f "${ext_data_path}/now-is-mutable" || die "now-is-mutable disappeared from writable storage after unmerge"
-test ! -f "${fake_root}${hierarchy}/now-is-mutable" || die "now-is-mutable did not disappear from hierarchy after unmerge"
-
-
-#
-# extension data in /var/lib/extensions.mutable/…, missing hierarchy,
-# auto-mutability
-#
-# mutable merged
-#
-
-
-fake_root=${fake_roots_dir}/simple-mutable-with-missing-hierarchy
-hierarchy=/opt
-
-prep_root "${fake_root}" "${hierarchy}"
-rmdir "${fake_root}/${hierarchy}"
-gen_os_release "${fake_root}"
-gen_test_ext_image "${fake_root}" "${hierarchy}"
-
-ext_data_path=$(hierarchy_ext_mut_path "${fake_root}" "${hierarchy}")
-prep_ext_mut "${ext_data_path}"
-
-# run systemd-sysext
-SYSTEMD_SYSEXT_HIERARCHIES="${hierarchy}" systemd-sysext --root="${fake_root}" --mutable=auto merge
-
-touch "${fake_root}${hierarchy}/now-is-mutable" || die "${fake_root}${hierarchy} is not mutable"
-check_usual_suspects_after_merge "${fake_root}" "${hierarchy}" e u
-test -f "${ext_data_path}/now-is-mutable" || die "now-is-mutable is not stored in expected location"
-
-SYSTEMD_SYSEXT_HIERARCHIES="${hierarchy}" systemd-sysext --root="${fake_root}" unmerge
-
-check_usual_suspects_after_unmerge "${fake_root}" "${hierarchy}"
-test -f "${ext_data_path}/now-is-mutable" || die "now-is-mutable disappeared from writable storage after unmerge"
-test ! -f "${fake_root}${hierarchy}/now-is-mutable" || die "now-is-mutable did not disappear from hierarchy after unmerge"
-
-
-#
-# extension data in /var/lib/extensions.mutable/…, empty hierarchy, auto-mutability
-#
-# mutable merged
-#
-
-
-fake_root=${fake_roots_dir}/simple-mutable-with-empty-hierarchy
-hierarchy=/opt
-
-prep_root "${fake_root}" "${hierarchy}"
-gen_os_release "${fake_root}"
-gen_test_ext_image "${fake_root}" "${hierarchy}"
-
-ext_data_path=$(hierarchy_ext_mut_path "${fake_root}" "${hierarchy}")
-prep_ext_mut "${ext_data_path}"
-
-make_ro "${fake_root}" "${hierarchy}"
-
-touch "${fake_root}${hierarchy}/should-fail-on-read-only-fs" && die "${fake_root}${hierarchy} is not read-only"
-
-# run systemd-sysext
-SYSTEMD_SYSEXT_HIERARCHIES="${hierarchy}" systemd-sysext --root="${fake_root}" --mutable=auto merge
-
-touch "${fake_root}${hierarchy}/now-is-mutable" || die "${fake_root}${hierarchy} is not mutable"
-check_usual_suspects_after_merge "${fake_root}" "${hierarchy}" e u
-test -f "${ext_data_path}/now-is-mutable" || die "now-is-mutable is not stored in expected location"
-
-SYSTEMD_SYSEXT_HIERARCHIES="${hierarchy}" systemd-sysext --root="${fake_root}" unmerge
-
-check_usual_suspects_after_unmerge "${fake_root}" "${hierarchy}"
-test -f "${ext_data_path}/now-is-mutable" || die "now-is-mutable disappeared from writable storage after unmerge"
-test ! -f "${fake_root}${hierarchy}/now-is-mutable" || die "now-is-mutable did not disappear from hierarchy after unmerge"
-
-
-#
-# /var/lib/extensions.mutable/… is a symlink to /some/other/dir, read-only
-# hierarchy, auto-mutability
-#
-# mutable merged
-#
-
-
-fake_root=${fake_roots_dir}/mutable-symlink-with-read-only-hierarchy
-hierarchy=/usr
-
-prep_root "${fake_root}" "${hierarchy}"
-gen_os_release "${fake_root}"
-gen_test_ext_image "${fake_root}" "${hierarchy}"
-
-# generate extension writable data
-ext_data_path=$(hierarchy_ext_mut_path "${fake_root}" "${hierarchy}")
-real_ext_dir="${fake_root}/upperdir"
-prep_ext_mut "${real_ext_dir}"
-ln -sfTr "${real_ext_dir}" "${ext_data_path}"
-
-prep_ro_hierarchy "${fake_root}" "${hierarchy}"
-
-touch "${fake_root}${hierarchy}/should-fail-on-read-only-fs" && die "${fake_root}${hierarchy} is not read-only"
-
-# run systemd-sysext
-SYSTEMD_SYSEXT_HIERARCHIES="${hierarchy}" systemd-sysext --root="${fake_root}" --mutable=auto merge
-
-touch "${fake_root}${hierarchy}/now-is-mutable" || die "${fake_root}${hierarchy} is not mutable"
-check_usual_suspects_after_merge "${fake_root}" "${hierarchy}" e h u
-test -f "${ext_data_path}/now-is-mutable" || die "now-is-mutable is not stored in expected location"
-test -f "${real_ext_dir}/now-is-mutable" || die "now-is-mutable is not stored in expected location"
-
-SYSTEMD_SYSEXT_HIERARCHIES="${hierarchy}" systemd-sysext --root="${fake_root}" unmerge
-
-check_usual_suspects_after_unmerge "${fake_root}" "${hierarchy}" h
-test -f "${ext_data_path}/now-is-mutable" || die "now-is-mutable disappeared from writable storage after unmerge"
-test -f "${real_ext_dir}/now-is-mutable" || die "now-is-mutable disappeared from writable storage after unmerge"
-test ! -f "${fake_root}${hierarchy}/now-is-mutable" || die "now-is-mutable did not disappear from hierarchy after unmerge"
-
-
-#
-# /var/lib/extensions.mutable/… is a symlink to the hierarchy itself, auto-mutability
-#
-# for this to work, hierarchy must be mutable
-#
-# mutable merged
-#
-
-
-fake_root=${fake_roots_dir}/mutable-self-upper
-hierarchy=/usr
-
-prep_root "${fake_root}" "${hierarchy}"
-gen_os_release "${fake_root}"
-gen_test_ext_image "${fake_root}" "${hierarchy}"
-
-# generate extension writable data
-ext_data_path=$(hierarchy_ext_mut_path "${fake_root}" "${hierarchy}")
-real_ext_dir="${fake_root}${hierarchy}"
-prep_ext_mut "${real_ext_dir}"
-ln -sfTr "${real_ext_dir}" "${ext_data_path}"
-
-# prepare writable hierarchy
-touch "${fake_root}${hierarchy}/preexisting-file-in-hierarchy"
-
-# run systemd-sysext
-SYSTEMD_SYSEXT_HIERARCHIES="${hierarchy}" systemd-sysext --root="${fake_root}" --mutable=auto merge
-
-touch "${fake_root}${hierarchy}/now-is-mutable" || die "${fake_root}${hierarchy} is not mutable"
-check_usual_suspects_after_merge "${fake_root}" "${hierarchy}" e h u
-test -f "${ext_data_path}/now-is-mutable" || die "now-is-mutable is not stored in expected location"
-test -f "${real_ext_dir}/now-is-mutable" || die "now-is-mutable is not stored in expected location"
-
-SYSTEMD_SYSEXT_HIERARCHIES="${hierarchy}" systemd-sysext --root="${fake_root}" unmerge
-
-check_usual_suspects_after_unmerge "${fake_root}" "${hierarchy}" h u
-test -f "${ext_data_path}/now-is-mutable" || die "now-is-mutable disappeared from writable storage after unmerge"
-test -f "${real_ext_dir}/now-is-mutable" || die "now-is-mutable disappeared from writable storage after unmerge"
-
-
-#
-# /var/lib/extensions.mutable/… is a symlink to the hierarchy itself, which is
-# read-only, auto-mutability
-#
-# expecting a failure here
-#
-
-
-fake_root=${fake_roots_dir}/failure-self-upper-ro
-hierarchy=/usr
-
-prep_root "${fake_root}" "${hierarchy}"
-gen_os_release "${fake_root}"
-gen_test_ext_image "${fake_root}" "${hierarchy}"
-
-# generate extension writable data
-ext_data_path=$(hierarchy_ext_mut_path "${fake_root}" "${hierarchy}")
-real_ext_dir="${fake_root}${hierarchy}"
-prep_ext_mut "${real_ext_dir}"
-ln -sfTr "${real_ext_dir}" "${ext_data_path}"
-
-prep_ro_hierarchy "${fake_root}" "${hierarchy}"
-
-# run systemd-sysext
-SYSTEMD_SYSEXT_HIERARCHIES="${hierarchy}" systemd-sysext --root="${fake_root}" --mutable=auto merge && die "expected merge to fail"
-
-
-#
-# /var/lib/extensions.mutable/… is a dangling symlink, auto-mutability
-#
-# read-only merged
-#
-
-
-fake_root=${fake_roots_dir}/read-only-mutable-dangling-symlink
-hierarchy=/usr
-
-prep_root "${fake_root}" "${hierarchy}"
-gen_os_release "${fake_root}"
-gen_test_ext_image "${fake_root}" "${hierarchy}"
-
-ext_data_path=$(hierarchy_ext_mut_path "${fake_root}" "${hierarchy}")
-ln -sfTr "/should/not/exist/" "${ext_data_path}"
-
-prep_ro_hierarchy "${fake_root}" "${hierarchy}"
-
-touch "${fake_root}${hierarchy}/should-fail-on-read-only-fs" && die "${fake_root}${hierarchy} is not read-only"
-
-# run systemd-sysext
-SYSTEMD_SYSEXT_HIERARCHIES="${hierarchy}" systemd-sysext --root="${fake_root}" --mutable=auto merge
-
-touch "${fake_root}${hierarchy}/should-still-fail-on-read-only-fs" && die "${fake_root}${hierarchy} is not read-only"
-
-check_usual_suspects_after_merge "${fake_root}" "${hierarchy}" e h
-
-SYSTEMD_SYSEXT_HIERARCHIES="${hierarchy}" systemd-sysext --root="${fake_root}" unmerge
-
-check_usual_suspects_after_unmerge "${fake_root}" "${hierarchy}" h
-
-
-#
-# /var/lib/extensions.mutable/… exists, but it's ignored, mutability disabled explicitly
-#
-# read-only merged
-#
-
-
-fake_root=${fake_roots_dir}/disabled
-hierarchy=/usr
-
-prep_root "${fake_root}" "${hierarchy}"
-gen_os_release "${fake_root}"
-gen_test_ext_image "${fake_root}" "${hierarchy}"
-
-ext_data_path=$(hierarchy_ext_mut_path "${fake_root}" "${hierarchy}")
-prep_ext_mut "${ext_data_path}"
-
-prep_ro_hierarchy "${fake_root}" "${hierarchy}"
-
-touch "${fake_root}${hierarchy}/should-fail-on-read-only-fs" && die "${fake_root}${hierarchy} is not read-only"
-
-# run systemd-sysext
-SYSTEMD_SYSEXT_HIERARCHIES="${hierarchy}" systemd-sysext --root="${fake_root}" --mutable=no merge
-
-touch "${fake_root}${hierarchy}/should-still-fail-on-read-only-fs" && die "${fake_root}${hierarchy} is not read-only"
-
-check_usual_suspects_after_merge "${fake_root}" "${hierarchy}" e h
-
-SYSTEMD_SYSEXT_HIERARCHIES="${hierarchy}" systemd-sysext --root="${fake_root}" unmerge
-
-check_usual_suspects_after_unmerge "${fake_root}" "${hierarchy}" h
-
-
-#
-# /var/lib/extensions.mutable/… exists, but it's imported instead
-#
-# read-only merged
-#
-
-
-fake_root=${fake_roots_dir}/imported
-hierarchy=/usr
-
-prep_root "${fake_root}" "${hierarchy}"
-gen_os_release "${fake_root}"
-gen_test_ext_image "${fake_root}" "${hierarchy}"
-
-ext_data_path=$(hierarchy_ext_mut_path "${fake_root}" "${hierarchy}")
-prep_ext_mut "${ext_data_path}"
-
-prep_ro_hierarchy "${fake_root}" "${hierarchy}"
-
-touch "${fake_root}${hierarchy}/should-fail-on-read-only-fs" && die "${fake_root}${hierarchy} is not read-only"
-
-# run systemd-sysext
-SYSTEMD_SYSEXT_HIERARCHIES="${hierarchy}" systemd-sysext --root="${fake_root}" --mutable=import merge
-
-touch "${fake_root}${hierarchy}/should-still-fail-on-read-only-fs" && die "${fake_root}${hierarchy} is not read-only"
-
-check_usual_suspects_after_merge "${fake_root}" "${hierarchy}" e h u
-
-SYSTEMD_SYSEXT_HIERARCHIES="${hierarchy}" systemd-sysext --root="${fake_root}" unmerge
-
-check_usual_suspects_after_unmerge "${fake_root}" "${hierarchy}" h
-
-
-#
-# /var/lib/extensions.mutable/… does not exist, but mutability is enabled
-# explicitly
-#
-# mutable merged
-#
-
-
-fake_root=${fake_roots_dir}/enabled
-hierarchy=/usr
-
-prep_root "${fake_root}" "${hierarchy}"
-gen_os_release "${fake_root}"
-gen_test_ext_image "${fake_root}" "${hierarchy}"
-
-ext_data_path=$(hierarchy_ext_mut_path "${fake_root}" "${hierarchy}")
-
-prep_ro_hierarchy "${fake_root}" "${hierarchy}"
-
-touch "${fake_root}${hierarchy}/should-fail-on-read-only-fs" && die "${fake_root}${hierarchy} is not read-only"
-
-test ! -d "${ext_data_path}" || die "extensions.mutable should not exist"
-
-# run systemd-sysext
-SYSTEMD_SYSEXT_HIERARCHIES="${hierarchy}" systemd-sysext --root="${fake_root}" --mutable=yes merge
-
-test -d "${ext_data_path}" || die "extensions.mutable should exist now"
-touch "${fake_root}${hierarchy}/now-is-mutable" || die "${fake_root}${hierarchy} is not mutable"
-check_usual_suspects_after_merge "${fake_root}" "${hierarchy}" e h
-test -f "${ext_data_path}/now-is-mutable" || die "now-is-mutable is not stored in expected location"
-
-SYSTEMD_SYSEXT_HIERARCHIES="${hierarchy}" systemd-sysext --root="${fake_root}" unmerge
-
-check_usual_suspects_after_unmerge "${fake_root}" "${hierarchy}" h
-test -f "${ext_data_path}/now-is-mutable" || die "now-is-mutable disappeared from writable storage after unmerge"
-test ! -f "${fake_root}${hierarchy}/now-is-mutable" || die "now-is-mutable did not disappear from hierarchy after unmerge"
-
-
-#
-# /var/lib/extensions.mutable/… does not exist, auto-mutability
-#
-# read-only merged
-#
-
-
-fake_root=${fake_roots_dir}/simple-read-only-explicit
-hierarchy=/usr
-
-prep_root "${fake_root}" "${hierarchy}"
-gen_os_release "${fake_root}"
-gen_test_ext_image "${fake_root}" "${hierarchy}"
-
-prep_ro_hierarchy "${fake_root}" "${hierarchy}"
-
-touch "${fake_root}${hierarchy}/should-fail-on-read-only-fs" && die "${fake_root}${hierarchy} is not read-only"
-
-# run systemd-sysext
-SYSTEMD_SYSEXT_HIERARCHIES="${hierarchy}" systemd-sysext --root="${fake_root}" --mutable=auto merge
-
-touch "${fake_root}${hierarchy}/should-still-fail-on-read-only-fs" && die "${fake_root}${hierarchy} is not read-only"
-check_usual_suspects_after_merge "${fake_root}" "${hierarchy}" e h
-
-SYSTEMD_SYSEXT_HIERARCHIES="${hierarchy}" systemd-sysext --root="${fake_root}" unmerge
-
-check_usual_suspects_after_unmerge "${fake_root}" "${hierarchy}" h
-
-
-#
-# /var/lib/extensions.mutable/… does not exist, but mutability is enabled
-# through an env var
-#
-# mutable merged
-#
-
-
-fake_root=${fake_roots_dir}/enabled-env-var
-hierarchy=/usr
-
-prep_env "yes"
-prep_root "${fake_root}" "${hierarchy}"
-gen_os_release "${fake_root}"
-gen_test_ext_image "${fake_root}" "${hierarchy}"
-
-ext_data_path=$(hierarchy_ext_mut_path "${fake_root}" "${hierarchy}")
-
-prep_ro_hierarchy "${fake_root}" "${hierarchy}"
-
-touch "${fake_root}${hierarchy}/should-fail-on-read-only-fs" && die "${fake_root}${hierarchy} is not read-only"
-
-test ! -d "${ext_data_path}" || die "extensions.mutable should not exist"
-
-# run systemd-sysext
-SYSTEMD_SYSEXT_HIERARCHIES="${hierarchy}" systemd-sysext --root="${fake_root}" merge
-
-test -d "${ext_data_path}" || die "extensions.mutable should exist now"
-touch "${fake_root}${hierarchy}/now-is-mutable" || die "${fake_root}${hierarchy} is not mutable"
-check_usual_suspects_after_merge "${fake_root}" "${hierarchy}" e h
-test -f "${ext_data_path}/now-is-mutable" || die "now-is-mutable is not stored in expected location"
-
-SYSTEMD_SYSEXT_HIERARCHIES="${hierarchy}" systemd-sysext --root="${fake_root}" unmerge
-
-check_usual_suspects_after_unmerge "${fake_root}" "${hierarchy}" h
-test -f "${ext_data_path}/now-is-mutable" || die "now-is-mutable disappeared from writable storage after unmerge"
-test ! -f "${fake_root}${hierarchy}/now-is-mutable" || die "now-is-mutable did not disappear from hierarchy after unmerge"
-drop_env
-
-
-#
-# /var/lib/extensions.mutable/… does not exist, auto-mutability through an env
-# var
-#
-# read-only merged
-#
-
-
-fake_root=${fake_roots_dir}/read-only-auto-env-var
-hierarchy=/usr
-
-prep_env "auto"
-prep_root "${fake_root}" "${hierarchy}"
-gen_os_release "${fake_root}"
-gen_test_ext_image "${fake_root}" "${hierarchy}"
-
-prep_ro_hierarchy "${fake_root}" "${hierarchy}"
-
-touch "${fake_root}${hierarchy}/should-fail-on-read-only-fs" && die "${fake_root}${hierarchy} is not read-only"
-
-# run systemd-sysext
-SYSTEMD_SYSEXT_HIERARCHIES="${hierarchy}" systemd-sysext --root="${fake_root}" --mutable=auto merge
-
-touch "${fake_root}${hierarchy}/should-still-fail-on-read-only-fs" && die "${fake_root}${hierarchy} is not read-only"
-check_usual_suspects_after_merge "${fake_root}" "${hierarchy}" e h
-
-SYSTEMD_SYSEXT_HIERARCHIES="${hierarchy}" systemd-sysext --root="${fake_root}" unmerge
-
-check_usual_suspects_after_unmerge "${fake_root}" "${hierarchy}" h
-drop_env
-
-
-#
-# extension data in /var/lib/extensions.mutable/…, read-only hierarchy,
-# auto-mutability through an env var
-#
-# mutable merged
-#
-
-
-fake_root=${fake_roots_dir}/auto-mutable-env-var
-hierarchy=/usr
-
-prep_env "auto"
-prep_root "${fake_root}" "${hierarchy}"
-gen_os_release "${fake_root}"
-gen_test_ext_image "${fake_root}" "${hierarchy}"
-
-ext_data_path=$(hierarchy_ext_mut_path "${fake_root}" "${hierarchy}")
-prep_ext_mut "${ext_data_path}"
-
-prep_ro_hierarchy "${fake_root}" "${hierarchy}"
-
-touch "${fake_root}${hierarchy}/should-fail-on-read-only-fs" && die "${fake_root}${hierarchy} is not read-only"
-
-# run systemd-sysext
-SYSTEMD_SYSEXT_HIERARCHIES="${hierarchy}" systemd-sysext --root="${fake_root}" merge
-
-touch "${fake_root}${hierarchy}/now-is-mutable" || die "${fake_root}${hierarchy} is not mutable"
-check_usual_suspects_after_merge "${fake_root}" "${hierarchy}" e h u
-test -f "${ext_data_path}/now-is-mutable" || die "now-is-mutable is not stored in expected location"
-
-SYSTEMD_SYSEXT_HIERARCHIES="${hierarchy}" systemd-sysext --root="${fake_root}" unmerge
-
-check_usual_suspects_after_unmerge "${fake_root}" "${hierarchy}" h
-test -f "${ext_data_path}/now-is-mutable" || die "now-is-mutable disappeared from writable storage after unmerge"
-test ! -f "${fake_root}${hierarchy}/now-is-mutable" || die "now-is-mutable did not disappear from hierarchy after unmerge"
-drop_env
-
-
-#
-# extension data in /var/lib/extensions.mutable/…, read-only hierarchy,
-# mutability disabled through an env var
-#
-# read-only merged
-#
-
-
-fake_root=${fake_roots_dir}/env-var-disabled
-hierarchy=/usr
-
-prep_env "no"
-prep_root "${fake_root}" "${hierarchy}"
-gen_os_release "${fake_root}"
-gen_test_ext_image "${fake_root}" "${hierarchy}"
-
-ext_data_path=$(hierarchy_ext_mut_path "${fake_root}" "${hierarchy}")
-prep_ext_mut "${ext_data_path}"
-
-prep_ro_hierarchy "${fake_root}" "${hierarchy}"
-
-touch "${fake_root}${hierarchy}/should-fail-on-read-only-fs" && die "${fake_root}${hierarchy} is not read-only"
-
-# run systemd-sysext
-SYSTEMD_SYSEXT_HIERARCHIES="${hierarchy}" systemd-sysext --root="${fake_root}" merge
-
-touch "${fake_root}${hierarchy}/should-be-read-only" && die "${fake_root}${hierarchy} is not read-only"
-check_usual_suspects_after_merge "${fake_root}" "${hierarchy}" e h
-
-SYSTEMD_SYSEXT_HIERARCHIES="${hierarchy}" systemd-sysext --root="${fake_root}" unmerge
-
-check_usual_suspects_after_unmerge "${fake_root}" "${hierarchy}" h
-drop_env
-
-
-#
-# /var/lib/extensions.mutable/… exists, but it's imported instead through an
-# env var
-#
-# read-only merged
-#
-
-
-fake_root=${fake_roots_dir}/imported-env-var
-hierarchy=/usr
-
-prep_env "import"
-prep_root "${fake_root}" "${hierarchy}"
-gen_os_release "${fake_root}"
-gen_test_ext_image "${fake_root}" "${hierarchy}"
-
-ext_data_path=$(hierarchy_ext_mut_path "${fake_root}" "${hierarchy}")
-prep_ext_mut "${ext_data_path}"
-
-prep_ro_hierarchy "${fake_root}" "${hierarchy}"
-
-touch "${fake_root}${hierarchy}/should-fail-on-read-only-fs" && die "${fake_root}${hierarchy} is not read-only"
-
-# run systemd-sysext
-SYSTEMD_SYSEXT_HIERARCHIES="${hierarchy}" systemd-sysext --root="${fake_root}" merge
-
-touch "${fake_root}${hierarchy}/should-still-fail-on-read-only-fs" && die "${fake_root}${hierarchy} is not read-only"
-
-check_usual_suspects_after_merge "${fake_root}" "${hierarchy}" e h u
-
-SYSTEMD_SYSEXT_HIERARCHIES="${hierarchy}" systemd-sysext --root="${fake_root}" unmerge
-
-check_usual_suspects_after_unmerge "${fake_root}" "${hierarchy}" h
-drop_env
-
-
-#
-# extension data in /var/lib/extensions.mutable/…, read-only hierarchy,
-# mutability enabled through an env var, but overridden with a command-line
-# option
-#
-# read-only merged
-#
-
-
-fake_root=${fake_roots_dir}/env-var-overridden
-hierarchy=/usr
-
-prep_env "yes"
-prep_root "${fake_root}" "${hierarchy}"
-gen_os_release "${fake_root}"
-gen_test_ext_image "${fake_root}" "${hierarchy}"
-
-ext_data_path=$(hierarchy_ext_mut_path "${fake_root}" "${hierarchy}")
-prep_ext_mut "${ext_data_path}"
-
-prep_ro_hierarchy "${fake_root}" "${hierarchy}"
-
-touch "${fake_root}${hierarchy}/should-fail-on-read-only-fs" && die "${fake_root}${hierarchy} is not read-only"
-
-# run systemd-sysext
-SYSTEMD_SYSEXT_HIERARCHIES="${hierarchy}" systemd-sysext --root="${fake_root}" --mutable=no merge
-
-touch "${fake_root}${hierarchy}/should-be-read-only" && die "${fake_root}${hierarchy} is not read-only"
-check_usual_suspects_after_merge "${fake_root}" "${hierarchy}" e h
-
-SYSTEMD_SYSEXT_HIERARCHIES="${hierarchy}" systemd-sysext --root="${fake_root}" unmerge
-
-check_usual_suspects_after_unmerge "${fake_root}" "${hierarchy}" h
-drop_env
-
-
-#
-# extension data in /var/lib/extensions.mutable/…, read-only hierarchy,
-# ephemeral mutability, so extension data contents are ignored
-#
-# mutable merged
-#
-
-
-fake_root=${fake_roots_dir}/ephemeral
-hierarchy=/usr
-
-prep_root "${fake_root}" "${hierarchy}"
-gen_os_release "${fake_root}"
-gen_test_ext_image "${fake_root}" "${hierarchy}"
-
-ext_data_path=$(hierarchy_ext_mut_path "${fake_root}" "${hierarchy}")
-prep_ext_mut "${ext_data_path}"
-
-prep_ro_hierarchy "${fake_root}" "${hierarchy}"
-
-touch "${fake_root}${hierarchy}/should-fail-on-read-only-fs" && die "${fake_root}${hierarchy} is not read-only"
-
-# run systemd-sysext
-SYSTEMD_SYSEXT_HIERARCHIES="${hierarchy}" systemd-sysext --root="${fake_root}" --mutable=ephemeral merge
-
-touch "${fake_root}${hierarchy}/now-is-mutable" || die "${fake_root}${hierarchy} is not mutable"
-check_usual_suspects_after_merge "${fake_root}" "${hierarchy}" e h
-test ! -f "${ext_data_path}/now-is-mutable" || die "now-is-mutable should not be stored in extension data"
-
-SYSTEMD_SYSEXT_HIERARCHIES="${hierarchy}" systemd-sysext --root="${fake_root}" unmerge
-
-check_usual_suspects_after_unmerge "${fake_root}" "${hierarchy}" h
-test ! -f "${ext_data_path}/now-is-mutable" || die "now-is-mutable should not appear in writable storage after unmerge"
-test ! -f "${fake_root}${hierarchy}/now-is-mutable" || die "now-is-mutable did not disappear from hierarchy after unmerge"
-
-
-#
-# extension data in /var/lib/extensions.mutable/…, read-only hierarchy,
-# ephemeral mutability through an env var, so extension data contents are
-# ignored
-#
-# mutable merged
-#
-
-
-fake_root=${fake_roots_dir}/ephemeral-env-var
-hierarchy=/usr
-
-prep_env "ephemeral"
-prep_root "${fake_root}" "${hierarchy}"
-gen_os_release "${fake_root}"
-gen_test_ext_image "${fake_root}" "${hierarchy}"
-
-ext_data_path=$(hierarchy_ext_mut_path "${fake_root}" "${hierarchy}")
-prep_ext_mut "${ext_data_path}"
-
-prep_ro_hierarchy "${fake_root}" "${hierarchy}"
-
-touch "${fake_root}${hierarchy}/should-fail-on-read-only-fs" && die "${fake_root}${hierarchy} is not read-only"
-
-# run systemd-sysext
-SYSTEMD_SYSEXT_HIERARCHIES="${hierarchy}" systemd-sysext --root="${fake_root}" merge
-
-touch "${fake_root}${hierarchy}/now-is-mutable" || die "${fake_root}${hierarchy} is not mutable"
-check_usual_suspects_after_merge "${fake_root}" "${hierarchy}" e h
-test ! -f "${ext_data_path}/now-is-mutable" || die "now-is-mutable should not be stored in extension data"
-
-SYSTEMD_SYSEXT_HIERARCHIES="${hierarchy}" systemd-sysext --root="${fake_root}" unmerge
-
-check_usual_suspects_after_unmerge "${fake_root}" "${hierarchy}" h
-test ! -f "${ext_data_path}/now-is-mutable" || die "now-is-mutable should not appear in writable storage after unmerge"
-test ! -f "${fake_root}${hierarchy}/now-is-mutable" || die "now-is-mutable did not disappear from hierarchy after unmerge"
-drop_env
-
-
-#
-# extension data in /var/lib/extensions.mutable/…, read-only hierarchy,
-# ephemeral import mutability, so extension data contents are imported too
-#
-# mutable merged
-#
-
-
-fake_root=${fake_roots_dir}/ephemeral-import
-hierarchy=/usr
-
-prep_root "${fake_root}" "${hierarchy}"
-gen_os_release "${fake_root}"
-gen_test_ext_image "${fake_root}" "${hierarchy}"
-
-ext_data_path=$(hierarchy_ext_mut_path "${fake_root}" "${hierarchy}")
-prep_ext_mut "${ext_data_path}"
-
-prep_ro_hierarchy "${fake_root}" "${hierarchy}"
-
-touch "${fake_root}${hierarchy}/should-fail-on-read-only-fs" && die "${fake_root}${hierarchy} is not read-only"
-
-# run systemd-sysext
-SYSTEMD_SYSEXT_HIERARCHIES="${hierarchy}" systemd-sysext --root="${fake_root}" --mutable=ephemeral-import merge
-
-touch "${fake_root}${hierarchy}/now-is-mutable" || die "${fake_root}${hierarchy} is not mutable"
-check_usual_suspects_after_merge "${fake_root}" "${hierarchy}" e h u
-test ! -f "${ext_data_path}/now-is-mutable" || die "now-is-mutable should not be stored in extension data"
-
-SYSTEMD_SYSEXT_HIERARCHIES="${hierarchy}" systemd-sysext --root="${fake_root}" unmerge
-
-check_usual_suspects_after_unmerge "${fake_root}" "${hierarchy}" h
-test ! -f "${ext_data_path}/now-is-mutable" || die "now-is-mutable should not appear in writable storage after unmerge"
-test ! -f "${fake_root}${hierarchy}/now-is-mutable" || die "now-is-mutable did not disappear from hierarchy after unmerge"
-
-
-#
-# extension data in /var/lib/extensions.mutable/…, read-only hierarchy,
-# ephemeral mutability through an env var, so extension data contents are
-# imported too
-#
-# mutable merged
-#
-
-
-fake_root=${fake_roots_dir}/ephemeral-import-env-var
-hierarchy=/usr
-
-prep_env "ephemeral-import"
-prep_root "${fake_root}" "${hierarchy}"
-gen_os_release "${fake_root}"
-gen_test_ext_image "${fake_root}" "${hierarchy}"
-
-ext_data_path=$(hierarchy_ext_mut_path "${fake_root}" "${hierarchy}")
-prep_ext_mut "${ext_data_path}"
-
-prep_ro_hierarchy "${fake_root}" "${hierarchy}"
-
-touch "${fake_root}${hierarchy}/should-fail-on-read-only-fs" && die "${fake_root}${hierarchy} is not read-only"
-
-# run systemd-sysext
-SYSTEMD_SYSEXT_HIERARCHIES="${hierarchy}" systemd-sysext --root="${fake_root}" merge
-
-touch "${fake_root}${hierarchy}/now-is-mutable" || die "${fake_root}${hierarchy} is not mutable"
-check_usual_suspects_after_merge "${fake_root}" "${hierarchy}" e h u
-test ! -f "${ext_data_path}/now-is-mutable" || die "now-is-mutable should not be stored in extension data"
-
-SYSTEMD_SYSEXT_HIERARCHIES="${hierarchy}" systemd-sysext --root="${fake_root}" unmerge
-
-check_usual_suspects_after_unmerge "${fake_root}" "${hierarchy}" h
-test ! -f "${ext_data_path}/now-is-mutable" || die "now-is-mutable should not appear in writable storage after unmerge"
-test ! -f "${fake_root}${hierarchy}/now-is-mutable" || die "now-is-mutable did not disappear from hierarchy after unmerge"
-drop_env
-
-
-#
-# extension data pointing to mutable hierarchy, ephemeral import mutability
-#
-# expecting a failure here
-#
-
-
-fake_root=${fake_roots_dir}/ephemeral-import-self
-hierarchy=/usr
-
-prep_root "${fake_root}" "${hierarchy}"
-gen_os_release "${fake_root}"
-gen_test_ext_image "${fake_root}" "${hierarchy}"
-
-ext_data_path=$(hierarchy_ext_mut_path "${fake_root}" "${hierarchy}")
-real_ext_dir="${fake_root}${hierarchy}"
-prep_ext_mut "${real_ext_dir}"
-ln -sfTr "${real_ext_dir}" "${ext_data_path}"
-
-prep_hierarchy "${fake_root}" "${hierarchy}"
-
-touch "${fake_root}${hierarchy}/should-succeed-on-read-only-fs" || die "${fake_root}${hierarchy} is not mutable"
-
-# run systemd-sysext
-SYSTEMD_SYSEXT_HIERARCHIES="${hierarchy}" systemd-sysext --root="${fake_root}" --mutable=ephemeral-import merge && die 'expected merge to fail'
-
-
-#
-# extension data pointing to mutable hierarchy, import mutability
-#
-# expecting a failure here
-#
-
-
-fake_root=${fake_roots_dir}/import-self
-hierarchy=/usr
-
-prep_root "${fake_root}" "${hierarchy}"
-gen_os_release "${fake_root}"
-gen_test_ext_image "${fake_root}" "${hierarchy}"
-
-ext_data_path=$(hierarchy_ext_mut_path "${fake_root}" "${hierarchy}")
-real_ext_dir="${fake_root}${hierarchy}"
-prep_ext_mut "${real_ext_dir}"
-ln -sfTr "${real_ext_dir}" "${ext_data_path}"
-
-prep_hierarchy "${fake_root}" "${hierarchy}"
-
-touch "${fake_root}${hierarchy}/should-succeed-on-read-only-fs" || die "${fake_root}${hierarchy} is not mutable"
-
-# run systemd-sysext
-SYSTEMD_SYSEXT_HIERARCHIES="${hierarchy}" systemd-sysext --root="${fake_root}" --mutable=import merge && die 'expected merge to fail'
-
-
-#
-# done
-#
+: "Run subtests"
+run_subtests
touch /testok
--- /dev/null
+#!/usr/bin/env bash
+# SPDX-License-Identifier: LGPL-2.1-or-later
+set -eux
+set -o pipefail
+
+FAKE_ROOTS_DIR="$(mktemp -d --tmpdir="" fake-roots-XXX)"
+
+# shellcheck disable=SC2317
+at_exit() {
+ set +ex
+
+ local target
+
+ # Note: `cat` here is used intentionally, so we iterate over our own copy of /proc/mounts. Otherwise
+ # things get very confusing once we start unmounting things, due to changing file offsets.
+ # shellcheck disable=SC2002
+ cat /proc/mounts | while read -r _ target _ _ _ _; do
+ if [[ "$target" =~ ^"$FAKE_ROOTS_DIR" ]]; then
+ umount -Rv "$target"
+ fi
+ done
+
+ rm -rf "${FAKE_ROOTS_DIR}"
+}
+
+trap at_exit EXIT
+
+prepare_root() {
+ local root=${1:?}
+ local hierarchy=${2:?}
+ local dir
+
+ if [[ -d $root ]]; then
+ echo >&2 "Directory $root already exists, possible copy-paste error?"
+ exit 1
+ fi
+
+ for dir in "$hierarchy" "/usr/lib" "/var/lib/extensions/" "/var/lib/extensions.mutable"; do
+ mkdir -p "$root$dir"
+ done
+
+ {
+ echo "ID=testtest"
+ echo "VERSION=1.2.3"
+ } >"$root/usr/lib/os-release"
+}
+
+prepare_extension_image() {
+ local root="${1:?}"
+ local hierarchy="${2:?}"
+ local ext_dir ext_release name
+
+ name="test-extension"
+ ext_dir="$root/var/lib/extensions/$name"
+ ext_release="$ext_dir/usr/lib/extension-release.d/extension-release.$name"
+ mkdir -p "${ext_release%/*}"
+ echo "ID=_any" >"$ext_release"
+ mkdir -p "$ext_dir/$hierarchy"
+ touch "$ext_dir$hierarchy/preexisting-file-in-extension-image"
+}
+
+prepare_extension_mutable_dir() {
+ local dir=${1:?}
+
+ mkdir -p "$dir"
+ touch "$dir/preexisting-file-in-extensions-mutable"
+}
+
+make_read_only() {
+ local root="${1:?}"
+ local hierarchy="${2:?}"
+
+ mount -o bind,ro "$root$hierarchy" "$root$hierarchy"
+}
+
+prepare_hierarchy() {
+ local root="${1:?}"
+ local hierarchy="${2:?}"
+
+ touch "$root$hierarchy/preexisting-file-in-hierarchy"
+}
+
+prepare_read_only_hierarchy() {
+ local root="${1:?}"
+ local hierarchy="${2:?}"
+
+ prepare_hierarchy "$root" "$hierarchy"
+ make_read_only "$root" "$hierarchy"
+}
+
+# Extra arguments:
+# -e: check for a preexisting file in extension
+# -h: check for a preexisting file in hierarchy
+# -u: check for a preexisting file in upperdir
+extension_verify() {
+ local root="${1:?}"
+ local hierarchy="${2:?}"
+ local message="${3:?}"
+ shift 3
+ # Map each option to a pre-defined file name
+ local -A option_files_map=(
+ [e]="preexisting-file-in-extension-image"
+ [h]="preexisting-file-in-hierarchy"
+ [u]="preexisting-file-in-extensions-mutable"
+ )
+ local -A args=(
+ [e]=0
+ [h]=0
+ [u]=0
+ )
+ local file full_path option
+
+ while getopts "ehu" opt; do
+ case "$opt" in
+ e|h|u)
+ args[$opt]=1
+ ;;
+ *)
+ echo >&2 "Unxexpected option: $opt"
+ exit 1
+ esac
+ done
+
+ echo "${args[@]}"
+
+ for option in "${!option_files_map[@]}"; do
+ file="${option_files_map[$option]}"
+ full_path="$root$hierarchy/$file"
+
+ if [[ ${args[$option]} -ne 0 ]]; then
+ if [[ ! -f "$full_path" ]]; then
+ ls -la "$root$hierarchy"
+ echo >&2 "Expected file '$file' to exist under $root$hierarchy $message"
+ exit 1
+ fi
+ else
+ if [[ -f "$full_path" ]]; then
+ ls -la "$root$hierarchy"
+ echo >&2 "Expected file '$file' to not exist under $root$hierarchy $message"
+ exit 1
+ fi
+ fi
+ done
+}
+
+extension_verify_after_merge() (
+ set +x
+
+ local root="${1:?}"
+ local hierarchy="${2:?}"
+ shift 2
+
+ extension_verify "$root" "$hierarchy" "after merge" "$@"
+)
+
+extension_verify_after_unmerge() (
+ set +x
+
+ local root="${1:?}"
+ local hierarchy="${2:?}"
+ shift 2
+
+ extension_verify "$root" "$hierarchy" "after unmerge" "$@"
+)
+
+# General systemd-sysext tests
+
+: "No extension data in /var/lib/extensions.mutable/…, R/O hierarchy, mutability disabled by default, read-only merged"
+fake_root="$FAKE_ROOTS_DIR/simple-read-only-with-read-only-hierarchy"
+hierarchy=/usr
+
+prepare_root "$fake_root" "$hierarchy"
+prepare_extension_image "$fake_root" "$hierarchy"
+prepare_read_only_hierarchy "$fake_root" "$hierarchy"
+(! touch "$fake_root$hierarchy/should-fail-on-read-only-fs")
+
+SYSTEMD_SYSEXT_HIERARCHIES="$hierarchy" systemd-sysext --root="$fake_root" merge
+(! touch "$fake_root$hierarchy/should-still-fail-on-read-only-fs")
+extension_verify_after_merge "$fake_root" "$hierarchy" -e -h
+
+SYSTEMD_SYSEXT_HIERARCHIES="$hierarchy" systemd-sysext --root="$fake_root" unmerge
+extension_verify_after_unmerge "$fake_root" "$hierarchy" -h
+(! touch "$fake_root$hierarchy/should-still-fail-on-read-only-fs")
+
+
+: "No extension data in /var/lib/extensions.mutable/…, mutable hierarchy, mutability disabled by default, read-only merged"
+fake_root="$FAKE_ROOTS_DIR/simple-read-only-with-mutable-hierarchy"
+hierarchy=/usr
+
+prepare_root "$fake_root" "$hierarchy"
+prepare_extension_image "$fake_root" "$hierarchy"
+prepare_hierarchy "$fake_root" "$hierarchy"
+touch "$fake_root$hierarchy/should-succeed-on-mutable-fs"
+
+SYSTEMD_SYSEXT_HIERARCHIEe="$hierarchy" systemd-sysext --root="$fake_root" merge
+(! touch "$fake_root$hierarchy/should-fail-on-read-only-fs")
+extension_verify_after_merge "$fake_root" "$hierarchy" -e -h
+
+SYSTEMD_SYSEXT_HIERARCHIES="$hierarchy" systemd-sysext --root="$fake_root" unmerge
+extension_verify_after_unmerge "$fake_root" "$hierarchy" -h
+touch "$fake_root$hierarchy/should-succeed-on-mutable-fs-again"
+
+
+: "No extension data in /var/lib/extensions.mutable/…, no hierarchy either, mutability disabled by default, read-only merged"
+fake_root="$FAKE_ROOTS_DIR/simple-read-only-with-missing-hierarchy"
+hierarchy=/opt
+
+prepare_root "$fake_root" "$hierarchy"
+rmdir "$fake_root/$hierarchy"
+prepare_extension_image "$fake_root" "$hierarchy"
+
+SYSTEMD_SYSEXT_HIERARCHIES="$hierarchy" systemd-sysext --root="$fake_root" merge
+(! touch "$fake_root$hierarchy/should-still-fail-on-read-only-fs")
+extension_verify_after_merge "$fake_root" "$hierarchy" -e
+
+SYSTEMD_SYSEXT_HIERARCHIES="$hierarchy" systemd-sysext --root="$fake_root" unmerge
+extension_verify_after_unmerge "$fake_root" "$hierarchy"
+
+
+: "No extension data in /var/lib/extensions.mutable/…, empty hierarchy, mutability disabled by default, read-only merged"
+fake_root="$FAKE_ROOTS_DIR/simple-read-only-with-empty-hierarchy"
+hierarchy=/opt
+
+prepare_root "$fake_root" "$hierarchy"
+prepare_extension_image "$fake_root" "$hierarchy"
+make_read_only "$fake_root" "$hierarchy"
+(! touch "$fake_root$hierarchy/should-fail-on-read-only-fs")
+
+SYSTEMD_SYSEXT_HIERARCHIES="$hierarchy" systemd-sysext --root="$fake_root" merge
+(! touch "$fake_root$hierarchy/should-still-fail-on-read-only-fs")
+extension_verify_after_merge "$fake_root" "$hierarchy" -e
+
+SYSTEMD_SYSEXT_HIERARCHIES="$hierarchy" systemd-sysext --root="$fake_root" unmerge
+extension_verify_after_unmerge "$fake_root" "$hierarchy"
+
+
+: "Extension data in /var/lib/extensions.mutable/…, R/O hierarchy, mutability disabled by default, read-only merged"
+fake_root="$FAKE_ROOTS_DIR/simple-mutable-with-read-only-hierarchy-disabled"
+hierarchy=/usr
+extension_data_dir="$fake_root/var/lib/extensions.mutable$hierarchy"
+
+prepare_root "$fake_root" "$hierarchy"
+prepare_extension_image "$fake_root" "$hierarchy"
+prepare_extension_mutable_dir "$extension_data_dir"
+prepare_read_only_hierarchy "$fake_root" "$hierarchy"
+(! touch "$fake_root$hierarchy/should-fail-on-read-only-fs")
+
+SYSTEMD_SYSEXT_HIERARCHIES="$hierarchy" systemd-sysext --root="$fake_root" merge
+(! touch "$fake_root$hierarchy/should-be-read-only")
+extension_verify_after_merge "$fake_root" "$hierarchy" -e -h
+
+SYSTEMD_SYSEXT_HIERARCHIES="$hierarchy" systemd-sysext --root="$fake_root" unmerge
+extension_verify_after_unmerge "$fake_root" "$hierarchy" -h
+
+: "Extension data in /var/lib/extensions.mutable/…, R/O hierarchy, auto-mutability, mutable merged"
+fake_root="$FAKE_ROOTS_DIR/simple-mutable-with-read-only-hierarchy"
+hierarchy=/usr
+extension_data_dir="$fake_root/var/lib/extensions.mutable$hierarchy"
+
+prepare_root "$fake_root" "$hierarchy"
+prepare_extension_image "$fake_root" "$hierarchy"
+prepare_extension_mutable_dir "$extension_data_dir"
+prepare_read_only_hierarchy "$fake_root" "$hierarchy"
+(! touch "$fake_root$hierarchy/should-fail-on-read-only-fs")
+
+SYSTEMD_SYSEXT_HIERARCHIES="$hierarchy" systemd-sysext --root="$fake_root" --mutable=auto merge
+touch "$fake_root$hierarchy/now-is-mutable"
+extension_verify_after_merge "$fake_root" "$hierarchy" -e -h -u
+test -f "$extension_data_dir/now-is-mutable"
+
+SYSTEMD_SYSEXT_HIERARCHIES="$hierarchy" systemd-sysext --root="$fake_root" unmerge
+extension_verify_after_unmerge "$fake_root" "$hierarchy" -h
+test -f "$extension_data_dir/now-is-mutable"
+test ! -f "$fake_root$hierarchy/now-is-mutable"
+
+
+: "Extension data in /var/lib/extensions.mutable/…, missing hierarchy, auto-mutability, mutable merged"
+fake_root="$FAKE_ROOTS_DIR/simple-mutable-with-missing-hierarchy"
+hierarchy=/opt
+extension_data_dir="$fake_root/var/lib/extensions.mutable$hierarchy"
+
+prepare_root "$fake_root" "$hierarchy"
+rmdir "$fake_root/$hierarchy"
+prepare_extension_image "$fake_root" "$hierarchy"
+prepare_extension_mutable_dir "$extension_data_dir"
+
+SYSTEMD_SYSEXT_HIERARCHIES="$hierarchy" systemd-sysext --root="$fake_root" --mutable=auto merge
+touch "$fake_root$hierarchy/now-is-mutable"
+extension_verify_after_merge "$fake_root" "$hierarchy" -e -u
+test -f "$extension_data_dir/now-is-mutable"
+
+SYSTEMD_SYSEXT_HIERARCHIES="$hierarchy" systemd-sysext --root="$fake_root" unmerge
+extension_verify_after_unmerge "$fake_root" "$hierarchy"
+test -f "$extension_data_dir/now-is-mutable"
+test ! -f "$fake_root$hierarchy/now-is-mutable"
+
+
+: "Extension data in /var/lib/extensions.mutable/…, empty hierarchy, auto-mutability, mutable merged"
+fake_root="$FAKE_ROOTS_DIR/simple-mutable-with-empty-hierarchy"
+hierarchy=/opt
+extension_data_dir="$fake_root/var/lib/extensions.mutable$hierarchy"
+
+prepare_root "$fake_root" "$hierarchy"
+prepare_extension_image "$fake_root" "$hierarchy"
+prepare_extension_mutable_dir "$extension_data_dir"
+make_read_only "$fake_root" "$hierarchy"
+(! touch "$fake_root$hierarchy/should-fail-on-read-only-fs")
+
+SYSTEMD_SYSEXT_HIERARCHIES="$hierarchy" systemd-sysext --root="$fake_root" --mutable=auto merge
+touch "$fake_root$hierarchy/now-is-mutable"
+extension_verify_after_merge "$fake_root" "$hierarchy" -e -u
+test -f "$extension_data_dir/now-is-mutable"
+
+SYSTEMD_SYSEXT_HIERARCHIES="$hierarchy" systemd-sysext --root="$fake_root" unmerge
+extension_verify_after_unmerge "$fake_root" "$hierarchy"
+test -f "$extension_data_dir/now-is-mutable"
+test ! -f "$fake_root$hierarchy/now-is-mutable"
+
+
+: "/var/lib/extensions.mutable/… is a symlink to other dir, R/O hierarchy, auto-mutability, mutable merged"
+fake_root="$FAKE_ROOTS_DIR/mutable-symlink-with-read-only-hierarchy"
+hierarchy=/usr
+extension_data_dir="$fake_root/var/lib/extensions.mutable$hierarchy"
+extension_real_dir="$fake_root/upperdir"
+
+prepare_root "$fake_root" "$hierarchy"
+prepare_extension_image "$fake_root" "$hierarchy"
+prepare_extension_mutable_dir "$extension_real_dir"
+ln -sfTr "$extension_real_dir" "$extension_data_dir"
+prepare_read_only_hierarchy "$fake_root" "$hierarchy"
+(! touch "$fake_root$hierarchy/should-fail-on-read-only-fs")
+
+SYSTEMD_SYSEXT_HIERARCHIES="$hierarchy" systemd-sysext --root="$fake_root" --mutable=auto merge
+touch "$fake_root$hierarchy/now-is-mutable"
+extension_verify_after_merge "$fake_root" "$hierarchy" -e -h -u
+test -f "$extension_data_dir/now-is-mutable"
+test -f "$extension_real_dir/now-is-mutable"
+
+SYSTEMD_SYSEXT_HIERARCHIES="$hierarchy" systemd-sysext --root="$fake_root" unmerge
+extension_verify_after_unmerge "$fake_root" "$hierarchy" -h
+test -f "$extension_data_dir/now-is-mutable"
+test -f "$extension_real_dir/now-is-mutable"
+test ! -f "$fake_root$hierarchy/now-is-mutable"
+
+
+: "/var/lib/extensions.mutable/… is a symlink to the hierarchy itself, mutable hierarchy, auto-mutability, mutable merged"
+fake_root="$FAKE_ROOTS_DIR/mutable-self-upper"
+hierarchy=/usr
+extension_data_dir="$fake_root/var/lib/extensions.mutable$hierarchy"
+extension_real_dir="$fake_root$hierarchy"
+
+prepare_root "$fake_root" "$hierarchy"
+prepare_extension_image "$fake_root" "$hierarchy"
+prepare_extension_mutable_dir "$extension_real_dir"
+ln -sfTr "$extension_real_dir" "$extension_data_dir"
+touch "$fake_root$hierarchy/preexisting-file-in-hierarchy"
+
+SYSTEMD_SYSEXT_HIERARCHIES="$hierarchy" systemd-sysext --root="$fake_root" --mutable=auto merge
+touch "$fake_root$hierarchy/now-is-mutable"
+extension_verify_after_merge "$fake_root" "$hierarchy" -e -h -u
+test -f "$extension_data_dir/now-is-mutable"
+test -f "$extension_real_dir/now-is-mutable"
+
+SYSTEMD_SYSEXT_HIERARCHIES="$hierarchy" systemd-sysext --root="$fake_root" unmerge
+extension_verify_after_unmerge "$fake_root" "$hierarchy" -h -u
+test -f "$extension_data_dir/now-is-mutable"
+test -f "$extension_real_dir/now-is-mutable"
+
+
+: "/var/lib/extensions.mutable/… is a symlink to the hierarchy itself, R/O hierarchy, auto-mutability, expected fail"
+fake_root="$FAKE_ROOTS_DIR/failure-self-upper-ro"
+hierarchy=/usr
+extension_data_dir="$fake_root/var/lib/extensions.mutable$hierarchy"
+extension_real_dir="$fake_root$hierarchy"
+
+prepare_root "$fake_root" "$hierarchy"
+prepare_extension_image "$fake_root" "$hierarchy"
+prepare_extension_mutable_dir "$extension_real_dir"
+ln -sfTr "$extension_real_dir" "$extension_data_dir"
+prepare_read_only_hierarchy "$fake_root" "$hierarchy"
+
+(! SYSTEMD_SYSEXT_HIERARCHIES="$hierarchy" systemd-sysext --root="$fake_root" --mutable=auto merge)
+
+
+: "/var/lib/extensions.mutable/… is a dangling symlink, auto-mutability, read-only merged"
+fake_root="$FAKE_ROOTS_DIR/read-only-mutable-dangling-symlink"
+hierarchy=/usr
+extension_data_dir="$fake_root/var/lib/extensions.mutable$hierarchy"
+
+prepare_root "$fake_root" "$hierarchy"
+prepare_extension_image "$fake_root" "$hierarchy"
+ln -sfTr "/should/not/exist/" "$extension_data_dir"
+prepare_read_only_hierarchy "$fake_root" "$hierarchy"
+(! touch "$fake_root$hierarchy/should-fail-on-read-only-fs")
+
+# run systemd-sysext
+SYSTEMD_SYSEXT_HIERARCHIES="$hierarchy" systemd-sysext --root="$fake_root" --mutable=auto merge
+(! touch "$fake_root$hierarchy/should-still-fail-on-read-only-fs")
+extension_verify_after_merge "$fake_root" "$hierarchy" -e -h
+
+SYSTEMD_SYSEXT_HIERARCHIES="$hierarchy" systemd-sysext --root="$fake_root" unmerge
+extension_verify_after_unmerge "$fake_root" "$hierarchy" -h
+
+
+: "/var/lib/extensions.mutable/… exists but ignored, mutability disabled explicitly, read-only merged"
+fake_root="$FAKE_ROOTS_DIR/disabled"
+hierarchy=/usr
+extension_data_dir="$fake_root/var/lib/extensions.mutable$hierarchy"
+
+prepare_root "$fake_root" "$hierarchy"
+prepare_extension_image "$fake_root" "$hierarchy"
+prepare_extension_mutable_dir "$extension_data_dir"
+prepare_read_only_hierarchy "$fake_root" "$hierarchy"
+(! touch "$fake_root$hierarchy/should-fail-on-read-only-fs")
+
+# run systemd-sysext
+SYSTEMD_SYSEXT_HIERARCHIES="$hierarchy" systemd-sysext --root="$fake_root" --mutable=no merge
+(! touch "$fake_root$hierarchy/should-still-fail-on-read-only-fs")
+extension_verify_after_merge "$fake_root" "$hierarchy" -e -h
+
+SYSTEMD_SYSEXT_HIERARCHIES="$hierarchy" systemd-sysext --root="$fake_root" unmerge
+extension_verify_after_unmerge "$fake_root" "$hierarchy" -h
+
+
+: "/var/lib/extensions.mutable/… exists but is imported instead, read-only merged"
+fake_root="$FAKE_ROOTS_DIR/imported"
+hierarchy=/usr
+extension_data_dir="$fake_root/var/lib/extensions.mutable$hierarchy"
+
+prepare_root "$fake_root" "$hierarchy"
+prepare_extension_image "$fake_root" "$hierarchy"
+prepare_extension_mutable_dir "$extension_data_dir"
+prepare_read_only_hierarchy "$fake_root" "$hierarchy"
+(! touch "$fake_root$hierarchy/should-fail-on-read-only-fs")
+
+# run systemd-sysext
+SYSTEMD_SYSEXT_HIERARCHIES="$hierarchy" systemd-sysext --root="$fake_root" --mutable=import merge
+(! touch "$fake_root$hierarchy/should-still-fail-on-read-only-fs")
+extension_verify_after_merge "$fake_root" "$hierarchy" -e -h -u
+
+SYSTEMD_SYSEXT_HIERARCHIES="$hierarchy" systemd-sysext --root="$fake_root" unmerge
+extension_verify_after_unmerge "$fake_root" "$hierarchy" -h
+
+
+: "/var/lib/extensions.mutable/… doesn't exists, mutability enabled, mutable merged"
+fake_root="$FAKE_ROOTS_DIR/enabled"
+hierarchy=/usr
+extension_data_dir="$fake_root/var/lib/extensions.mutable$hierarchy"
+
+prepare_root "$fake_root" "$hierarchy"
+prepare_extension_image "$fake_root" "$hierarchy"
+prepare_read_only_hierarchy "$fake_root" "$hierarchy"
+(! touch "$fake_root$hierarchy/should-fail-on-read-only-fs")
+test ! -d "$extension_data_dir"
+
+SYSTEMD_SYSEXT_HIERARCHIES="$hierarchy" systemd-sysext --root="$fake_root" --mutable=yes merge
+test -d "$extension_data_dir"
+touch "$fake_root$hierarchy/now-is-mutable"
+extension_verify_after_merge "$fake_root" "$hierarchy" -e -h
+test -f "$extension_data_dir/now-is-mutable"
+
+SYSTEMD_SYSEXT_HIERARCHIES="$hierarchy" systemd-sysext --root="$fake_root" unmerge
+extension_verify_after_unmerge "$fake_root" "$hierarchy" -h
+test -f "$extension_data_dir/now-is-mutable"
+test ! -f "$fake_root$hierarchy/now-is-mutable"
+
+
+: "/var/lib/extensions.mutable/… doesn't exists, auto-mutability, read-only merged"
+fake_root="$FAKE_ROOTS_DIR/simple-read-only-explicit"
+hierarchy=/usr
+
+prepare_root "$fake_root" "$hierarchy"
+prepare_extension_image "$fake_root" "$hierarchy"
+prepare_read_only_hierarchy "$fake_root" "$hierarchy"
+(! touch "$fake_root$hierarchy/should-fail-on-read-only-fs")
+
+SYSTEMD_SYSEXT_HIERARCHIES="$hierarchy" systemd-sysext --root="$fake_root" --mutable=auto merge
+(! touch "$fake_root$hierarchy/should-still-fail-on-read-only-fs")
+extension_verify_after_merge "$fake_root" "$hierarchy" -e -h
+
+SYSTEMD_SYSEXT_HIERARCHIES="$hierarchy" systemd-sysext --root="$fake_root" unmerge
+extension_verify_after_unmerge "$fake_root" "$hierarchy" -h
+
+
+: "/var/lib/extensions.mutable/… doesn't exists, mutability enabled through env var, mutable merged"
+fake_root="$FAKE_ROOTS_DIR/enabled-env-var"
+hierarchy=/usr
+extension_data_dir="$fake_root/var/lib/extensions.mutable$hierarchy"
+
+prepare_root "$fake_root" "$hierarchy"
+prepare_extension_image "$fake_root" "$hierarchy"
+prepare_read_only_hierarchy "$fake_root" "$hierarchy"
+(! touch "$fake_root$hierarchy/should-fail-on-read-only-fs")
+test ! -d "$extension_data_dir"
+
+SYSTEMD_SYSEXT_HIERARCHIES="$hierarchy" SYSTEMD_SYSEXT_MUTABLE_MODE=yes systemd-sysext --root="$fake_root" merge
+test -d "$extension_data_dir"
+touch "$fake_root$hierarchy/now-is-mutable"
+extension_verify_after_merge "$fake_root" "$hierarchy" -e -h
+test -f "$extension_data_dir/now-is-mutable"
+
+SYSTEMD_SYSEXT_HIERARCHIES="$hierarchy" SYSTEMD_SYSEXT_MUTABLE_MODE=yes systemd-sysext --root="$fake_root" unmerge
+extension_verify_after_unmerge "$fake_root" "$hierarchy" -h
+test -f "$extension_data_dir/now-is-mutable"
+test ! -f "$fake_root$hierarchy/now-is-mutable"
+
+
+: "/var/lib/extensions.mutable/… doesn't exists, auto-mutability enabled through env var, read-only merged"
+fake_root="$FAKE_ROOTS_DIR/read-only-auto-env-var"
+hierarchy=/usr
+
+prepare_root "$fake_root" "$hierarchy"
+prepare_extension_image "$fake_root" "$hierarchy"
+prepare_read_only_hierarchy "$fake_root" "$hierarchy"
+(! touch "$fake_root$hierarchy/should-fail-on-read-only-fs")
+
+SYSTEMD_SYSEXT_HIERARCHIES="$hierarchy" SYSTEMD_SYSEXT_MUTABLE_MODE=auto systemd-sysext --root="$fake_root" --mutable=auto merge
+(! touch "$fake_root$hierarchy/should-still-fail-on-read-only-fs")
+extension_verify_after_merge "$fake_root" "$hierarchy" -e -h
+
+SYSTEMD_SYSEXT_HIERARCHIES="$hierarchy" SYSTEMD_SYSEXT_MUTABLE_MODE=auto systemd-sysext --root="$fake_root" unmerge
+extension_verify_after_unmerge "$fake_root" "$hierarchy" -h
+
+
+: "Extension data in /var/lib/extensions.mutable/…, R/O hierarchy, auto-mutability enabled through env var, mutable merged"
+fake_root="$FAKE_ROOTS_DIR/auto-mutable-env-var"
+hierarchy=/usr
+extension_data_dir="$fake_root/var/lib/extensions.mutable$hierarchy"
+
+prepare_root "$fake_root" "$hierarchy"
+prepare_extension_image "$fake_root" "$hierarchy"
+prepare_extension_mutable_dir "$extension_data_dir"
+prepare_read_only_hierarchy "$fake_root" "$hierarchy"
+(! touch "$fake_root$hierarchy/should-fail-on-read-only-fs")
+
+SYSTEMD_SYSEXT_HIERARCHIES="$hierarchy" SYSTEMD_SYSEXT_MUTABLE_MODE=auto systemd-sysext --root="$fake_root" merge
+touch "$fake_root$hierarchy/now-is-mutable"
+extension_verify_after_merge "$fake_root" "$hierarchy" -e -h -u
+test -f "$extension_data_dir/now-is-mutable"
+
+SYSTEMD_SYSEXT_HIERARCHIES="$hierarchy" SYSTEMD_SYSEXT_MUTABLE_MODE=auto systemd-sysext --root="$fake_root" unmerge
+extension_verify_after_unmerge "$fake_root" "$hierarchy" -h
+test -f "$extension_data_dir/now-is-mutable"
+test ! -f "$fake_root$hierarchy/now-is-mutable"
+
+
+: "Extension data in /var/lib/extensions.mutable/…, R/O hierarchy, mutability disabled through env var, read-only merged"
+fake_root="$FAKE_ROOTS_DIR/env-var-disabled"
+hierarchy=/usr
+extension_data_dir="$fake_root/var/lib/extensions.mutable$hierarchy"
+
+prepare_root "$fake_root" "$hierarchy"
+prepare_extension_image "$fake_root" "$hierarchy"
+prepare_extension_mutable_dir "$extension_data_dir"
+prepare_read_only_hierarchy "$fake_root" "$hierarchy"
+(! touch "$fake_root$hierarchy/should-fail-on-read-only-fs")
+
+SYSTEMD_SYSEXT_HIERARCHIES="$hierarchy" SYSTEMD_SYSEXT_MUTABLE_MODE=no systemd-sysext --root="$fake_root" merge
+(! touch "$fake_root$hierarchy/should-be-read-only")
+extension_verify_after_merge "$fake_root" "$hierarchy" -e -h
+
+SYSTEMD_SYSEXT_HIERARCHIES="$hierarchy" SYSTEMD_SYSEXT_MUTABLE_MODE=no systemd-sysext --root="$fake_root" unmerge
+extension_verify_after_unmerge "$fake_root" "$hierarchy" -h
+
+
+: "/var/lib/extensions.mutable/… exists but is imported through env var, read-only merged"
+fake_root="$FAKE_ROOTS_DIR/imported-env-var"
+hierarchy=/usr
+extension_data_dir="$fake_root/var/lib/extensions.mutable$hierarchy"
+
+prepare_root "$fake_root" "$hierarchy"
+prepare_extension_image "$fake_root" "$hierarchy"
+prepare_extension_mutable_dir "$extension_data_dir"
+prepare_read_only_hierarchy "$fake_root" "$hierarchy"
+(! touch "$fake_root$hierarchy/should-fail-on-read-only-fs")
+
+SYSTEMD_SYSEXT_HIERARCHIES="$hierarchy" SYSTEMD_SYSEXT_MUTABLE_MODE=import systemd-sysext --root="$fake_root" merge
+(! touch "$fake_root$hierarchy/should-still-fail-on-read-only-fs")
+extension_verify_after_merge "$fake_root" "$hierarchy" -e -h -u
+
+SYSTEMD_SYSEXT_HIERARCHIES="$hierarchy" SYSTEMD_SYSEXT_MUTABLE_MODE=import systemd-sysext --root="$fake_root" unmerge
+extension_verify_after_unmerge "$fake_root" "$hierarchy" -h
+
+
+: "Extension data in /var/lib/extensions.mutable/…, R/O hierarchy, mutability enabled through env var but overridden via CLI option, read-only merged"
+fake_root="$FAKE_ROOTS_DIR/env-var-overridden"
+hierarchy=/usr
+extension_data_dir="$fake_root/var/lib/extensions.mutable$hierarchy"
+
+prepare_root "$fake_root" "$hierarchy"
+prepare_extension_image "$fake_root" "$hierarchy"
+prepare_extension_mutable_dir "$extension_data_dir"
+prepare_read_only_hierarchy "$fake_root" "$hierarchy"
+(! touch "$fake_root$hierarchy/should-fail-on-read-only-fs")
+
+SYSTEMD_SYSEXT_HIERARCHIES="$hierarchy" SYSTEMD_SYSEXT_MUTABLE_MODE=yes systemd-sysext --root="$fake_root" --mutable=no merge
+(! touch "$fake_root$hierarchy/should-be-read-only")
+extension_verify_after_merge "$fake_root" "$hierarchy" -e -h
+
+SYSTEMD_SYSEXT_HIERARCHIES="$hierarchy" SYSTEMD_SYSEXT_MUTABLE_MODE=yes systemd-sysext --root="$fake_root" unmerge
+extension_verify_after_unmerge "$fake_root" "$hierarchy" -h
+
+
+: "Extension data in /var/lib/extensions.mutable/…, R/O hierarchy, ephemeral mutability, mutable merged"
+fake_root="$FAKE_ROOTS_DIR/ephemeral"
+hierarchy=/usr
+extension_data_dir="$fake_root/var/lib/extensions.mutable$hierarchy"
+
+prepare_root "$fake_root" "$hierarchy"
+prepare_extension_image "$fake_root" "$hierarchy"
+prepare_extension_mutable_dir "$extension_data_dir"
+prepare_read_only_hierarchy "$fake_root" "$hierarchy"
+(! touch "$fake_root$hierarchy/should-fail-on-read-only-fs")
+
+SYSTEMD_SYSEXT_HIERARCHIES="$hierarchy" systemd-sysext --root="$fake_root" --mutable=ephemeral merge
+touch "$fake_root$hierarchy/now-is-mutable"
+extension_verify_after_merge "$fake_root" "$hierarchy" -e -h
+test ! -f "$extension_data_dir/now-is-mutable"
+
+SYSTEMD_SYSEXT_HIERARCHIES="$hierarchy" systemd-sysext --root="$fake_root" unmerge
+extension_verify_after_unmerge "$fake_root" "$hierarchy" -h
+test ! -f "$extension_data_dir/now-is-mutable"
+test ! -f "$fake_root$hierarchy/now-is-mutable"
+
+
+: "Extension data in /var/lib/extensions.mutable/…, R/O hierarchy, ephemeral mutability through env var, mutable merged"
+fake_root="$FAKE_ROOTS_DIR/ephemeral-env-var"
+hierarchy=/usr
+extension_data_dir="$fake_root/var/lib/extensions.mutable$hierarchy"
+
+prepare_root "$fake_root" "$hierarchy"
+prepare_extension_image "$fake_root" "$hierarchy"
+prepare_extension_mutable_dir "$extension_data_dir"
+prepare_read_only_hierarchy "$fake_root" "$hierarchy"
+(! touch "$fake_root$hierarchy/should-fail-on-read-only-fs")
+
+SYSTEMD_SYSEXT_HIERARCHIES="$hierarchy" SYSTEMD_SYSEXT_MUTABLE_MODE=ephemeral systemd-sysext --root="$fake_root" merge
+touch "$fake_root$hierarchy/now-is-mutable"
+extension_verify_after_merge "$fake_root" "$hierarchy" -e -h
+test ! -f "$extension_data_dir/now-is-mutable"
+
+SYSTEMD_SYSEXT_HIERARCHIES="$hierarchy" SYSTEMD_SYSEXT_MUTABLE_MODE=ephemeral systemd-sysext --root="$fake_root" unmerge
+extension_verify_after_unmerge "$fake_root" "$hierarchy" -h
+test ! -f "$extension_data_dir/now-is-mutable"
+test ! -f "$fake_root$hierarchy/now-is-mutable"
+
+
+: "Extension data in /var/lib/extensions.mutable/…, R/O hierarchy, ephemeral import mutability, mutable merged"
+fake_root="$FAKE_ROOTS_DIR/ephemeral-import"
+hierarchy=/usr
+extension_data_dir="$fake_root/var/lib/extensions.mutable$hierarchy"
+
+prepare_root "$fake_root" "$hierarchy"
+prepare_extension_image "$fake_root" "$hierarchy"
+prepare_extension_mutable_dir "$extension_data_dir"
+prepare_read_only_hierarchy "$fake_root" "$hierarchy"
+(! touch "$fake_root$hierarchy/should-fail-on-read-only-fs")
+
+# run systemd-sysext
+SYSTEMD_SYSEXT_HIERARCHIES="$hierarchy" systemd-sysext --root="$fake_root" --mutable=ephemeral-import merge
+touch "$fake_root$hierarchy/now-is-mutable"
+extension_verify_after_merge "$fake_root" "$hierarchy" -e -h -u
+test ! -f "$extension_data_dir/now-is-mutable"
+
+SYSTEMD_SYSEXT_HIERARCHIES="$hierarchy" systemd-sysext --root="$fake_root" unmerge
+extension_verify_after_unmerge "$fake_root" "$hierarchy" -h
+test ! -f "$extension_data_dir/now-is-mutable"
+test ! -f "$fake_root$hierarchy/now-is-mutable"
+
+
+: "Extension data in /var/lib/extensions.mutable/…, R/O hierarchy, ephemeral import mutability through env var, mutable merged"
+fake_root="$FAKE_ROOTS_DIR/ephemeral-import-env-var"
+hierarchy=/usr
+extension_data_dir="$fake_root/var/lib/extensions.mutable$hierarchy"
+
+prepare_root "$fake_root" "$hierarchy"
+prepare_extension_image "$fake_root" "$hierarchy"
+prepare_extension_mutable_dir "$extension_data_dir"
+prepare_read_only_hierarchy "$fake_root" "$hierarchy"
+(! touch "$fake_root$hierarchy/should-fail-on-read-only-fs")
+
+SYSTEMD_SYSEXT_HIERARCHIES="$hierarchy" SYSTEMD_SYSEXT_MUTABLE_MODE=ephemeral-import systemd-sysext --root="$fake_root" merge
+touch "$fake_root$hierarchy/now-is-mutable"
+extension_verify_after_merge "$fake_root" "$hierarchy" -e -h -u
+test ! -f "$extension_data_dir/now-is-mutable"
+
+SYSTEMD_SYSEXT_HIERARCHIES="$hierarchy" SYSTEMD_SYSEXT_MUTABLE_MODE=ephemeral-import systemd-sysext --root="$fake_root" unmerge
+extension_verify_after_unmerge "$fake_root" "$hierarchy" -h
+test ! -f "$extension_data_dir/now-is-mutable"
+test ! -f "$fake_root$hierarchy/now-is-mutable"
+
+
+: "Extension data pointing to mutable hierarchy, ephemeral import mutability, expected fail"
+fake_root="$FAKE_ROOTS_DIR/ephemeral-import-self"
+hierarchy=/usr
+extension_data_dir="$fake_root/var/lib/extensions.mutable$hierarchy"
+extension_real_dir="$fake_root$hierarchy"
+
+prepare_root "$fake_root" "$hierarchy"
+prepare_extension_image "$fake_root" "$hierarchy"
+prepare_extension_mutable_dir "$extension_real_dir"
+ln -sfTr "$extension_real_dir" "$extension_data_dir"
+prepare_hierarchy "$fake_root" "$hierarchy"
+touch "$fake_root$hierarchy/should-succeed-on-read-only-fs"
+
+(! SYSTEMD_SYSEXT_HIERARCHIES="$hierarchy" systemd-sysext --root="$fake_root" --mutable=ephemeral-import merge)
+
+
+: "Extension data pointing to mutable hierarchy, import mutability, expected fail"
+fake_root="$FAKE_ROOTS_DIR/import-self"
+hierarchy=/usr
+extension_data_dir="$fake_root/var/lib/extensions.mutable$hierarchy"
+extension_real_dir="$fake_root$hierarchy"
+
+prepare_root "$fake_root" "$hierarchy"
+prepare_extension_image "$fake_root" "$hierarchy"
+prepare_extension_mutable_dir "$extension_real_dir"
+ln -sfTr "$extension_real_dir" "$extension_data_dir"
+prepare_hierarchy "$fake_root" "$hierarchy"
+touch "$fake_root$hierarchy/should-succeed-on-read-only-fs"
+
+(! SYSTEMD_SYSEXT_HIERARCHIES="$hierarchy" systemd-sysext --root="$fake_root" --mutable=import merge)
+
+exit 0
systemd-analyze log-level debug
-run_with_cred_compare() {
+run_with_cred_compare() (
local cred="${1:?}"
local exp="${2?}"
+ local log_file
shift 2
- diff <(systemd-run -p SetCredential="$cred" --wait --pipe -- systemd-creds "$@") <(echo -ne "$exp")
-}
+ log_file="$(mktemp)"
+ # shellcheck disable=SC2064
+ trap "rm -f '$log_file'" RETURN
+
+ set -o pipefail
+ systemd-run -p SetCredential="$cred" --wait --pipe -- systemd-creds "$@" | tee "$log_file"
+ diff "$log_file" <(echo -ne "$exp")
+)
# Sanity checks
#
[ "$(cat /tmp/sourcedfromcredential)" = "tmpfilessecret" ]
[ "$(cat /etc/motd.d/50-provision.conf)" = "hello" ]
[ "$(cat /etc/issue.d/50-provision.conf)" = "welcome" ]
+
+ # Verify that adding a unit and drop-in via credentials worked
+ systemctl start my-service
+ test -f /tmp/unit-dropin
else
echo "qemu_fw_cfg support missing in kernel. Sniff!"
expected_credential=""
[[ "$(get_cgroup_hierarchy)" == "unified" ]] || echo "no cgroupsv2" >>/skipped
[[ -x /usr/lib/systemd/systemd-oomd ]] || echo "no oomd" >>/skipped
if [[ -s /skipped ]]; then
- exit 0
+ exit 77
fi
rm -rf /run/systemd/system/testsuite-55-testbloat.service.d
if ! command -v systemd-repart >/dev/null; then
echo "no systemd-repart" >/skipped
- exit 0
+ exit 77
fi
# shellcheck source=test/units/test-control.sh
systemctl daemon-reload
+# Same test for reexec, but we wait here
+timeout 15 bash -c 'while systemctl daemon-reexec; do true; done'
+
+# Rate limit should reset after 9s
+sleep 10
+
+systemctl daemon-reexec
+
# Let's now test the notify-reload logic
cat >/run/notify-reload-test.sh <<EOF
if [[ "$KERNEL_MAJOR" -lt $MAJOR_REQUIRED || ("$KERNEL_MAJOR" -eq $MAJOR_REQUIRED && "$KERNEL_MINOR" -lt $MINOR_REQUIRED) ]]; then
echo "kernel is not 5.7+" >>/skipped
- exit 0
+ exit 77
fi
if systemctl --version | grep -q -F -- "-BPF_FRAMEWORK"; then
echo "bpf-framework is disabled" >>/skipped
- exit 0
+ exit 77
fi
trap teardown EXIT
}
testcase_virtio_scsi_identically_named_partitions() {
- local num
+ local num_part num_disk i j
+ local alphabet='abcdefghijklmnopqrstuvwxyz'
if [[ -v ASAN_OPTIONS || "$(systemd-detect-virt -v)" == "qemu" ]]; then
- num=$((4 * 4))
+ num_part=4
+ num_disk=4
else
- num=$((16 * 8))
+ num_part=8
+ num_disk=16
fi
+ for ((i = 0; i < num_disk; i++)); do
+ sfdisk "/dev/sd${alphabet:$i:1}" <<EOF
+label: gpt
+
+$(for ((j = 1; j <= num_part; j++)); do echo 'name="Hello world", size=2M'; done)
+EOF
+ done
+
lsblk --noheadings -a -o NAME,PARTLABEL
- [[ "$(lsblk --noheadings -a -o NAME,PARTLABEL | grep -c "Hello world")" -eq "$num" ]]
+ [[ "$(lsblk --noheadings -a -o NAME,PARTLABEL | grep -c "Hello world")" -eq "$((num_part * num_disk))" ]]
}
testcase_multipath_basic_failover() {
blacklist {
}
EOF
+
+ sfdisk /dev/sda <<EOF
+label: gpt
+
+name="first_partition", size=5M
+uuid="deadbeef-dead-dead-beef-000000000000", name="failover_part", size=5M
+EOF
+ udevadm settle
+ mkfs.ext4 -U "deadbeef-dead-dead-beef-111111111111" -L "failover_vol" "/dev/sda2"
+
modprobe -v dm_multipath
systemctl start multipathd.service
systemctl status multipathd.service
stat /sys/block/vda
readlink -f /sys/block/vda/dev
+ dev="/dev/vda"
+ sfdisk "${dev:?}" <<EOF
+label: gpt
+
+name="test_swap", size=32M
+uuid="deadbeef-dead-dead-beef-000000000000", name="test_part", size=5M
+EOF
+ udevadm settle
+ mkswap -U "deadbeef-dead-dead-beef-111111111111" -L "swap_vol" "${dev}1"
+ mkfs.ext4 -U "deadbeef-dead-dead-beef-222222222222" -L "data_vol" "${dev}2"
udevadm wait --settle --timeout=30 "${expected_symlinks[@]}"
# Try to mount the data partition manually (using its label)
set -o pipefail
SYSUPDATE=/lib/systemd/systemd-sysupdate
-SECTOR_SIZES="512 4096"
-BACKING_FILE=/var/tmp/72-joined.raw
-export SYSTEMD_ESP_PATH=/var/tmp/72-esp
-export SYSTEMD_XBOOTLDR_PATH=/var/tmp/72-xbootldr
+SECTOR_SIZES=(512 4096)
+WORKDIR="$(mktemp -d /var/tmp/test-72-XXXXXX)"
+BACKING_FILE="$WORKDIR/joined.raw"
+export SYSTEMD_ESP_PATH="$WORKDIR/esp"
+export SYSTEMD_XBOOTLDR_PATH="$WORKDIR/xbootldr"
export SYSTEMD_PAGER=cat
export SYSTEMD_LOG_LEVEL=debug
-if ! test -x "$SYSUPDATE"; then
+if [[ ! -x "$SYSUPDATE" ]]; then
echo "no systemd-sysupdate" >/skipped
- exit 0
+ exit 77
fi
# Loopback devices may not be supported. They are used because sfdisk cannot
# change the sector size of a file, and we want to test both 512 and 4096 byte
# sectors. If loopback devices are not supported, we can only test one sector
# size, and the underlying device is likely to have a sector size of 512 bytes.
-if ! losetup --find >/dev/null 2>&1; then
+if [[ ! -e /dev/loop-control ]]; then
echo "No loopback device support"
- SECTOR_SIZES="512"
+ SECTOR_SIZES=(512)
fi
-trap cleanup ERR
-cleanup() {
- set +o pipefail
- blockdev="$( losetup --list --output NAME,BACK-FILE | grep $BACKING_FILE | cut -d' ' -f1)"
- [ -n "$blockdev" ] && losetup --detach "$blockdev"
- rm -f "$BACKING_FILE"
- rm -rf /var/tmp/72-{dirs,defs,source,xbootldr,esp}
- rm -f /testok
+at_exit() {
+ set +e
+
+ losetup -n --output NAME --associated "$BACKING_FILE" | while read -r loop_dev; do
+ losetup --detach "$loop_dev"
+ done
+
+ rm -rf "$WORKDIR"
}
+trap at_exit EXIT
+
new_version() {
- # Inputs:
- # $1: sector size
- # $2: version
+ local sector_size="${1:?}"
+ local version="${2:?}"
# Create a pair of random partition payloads, and compress one
- dd if=/dev/urandom of="/var/tmp/72-source/part1-$2.raw" bs="$1" count=2048
- dd if=/dev/urandom of="/var/tmp/72-source/part2-$2.raw" bs="$1" count=2048
- gzip -k -f "/var/tmp/72-source/part2-$2.raw"
+ dd if=/dev/urandom of="$WORKDIR/source/part1-$version.raw" bs="$sector_size" count=2048
+ dd if=/dev/urandom of="$WORKDIR/source/part2-$version.raw" bs="$sector_size" count=2048
+ gzip -k -f "$WORKDIR/source/part2-$version.raw"
# Create a random "UKI" payload
- echo $RANDOM >"/var/tmp/72-source/uki-$2.efi"
+ echo $RANDOM >"$WORKDIR/source/uki-$version.efi"
# Create a random extra payload
- echo $RANDOM >"/var/tmp/72-source/uki-extra-$2.efi"
+ echo $RANDOM >"$WORKDIR/source/uki-extra-$version.efi"
# Create tarball of a directory
- mkdir -p "/var/tmp/72-source/dir-$2"
- echo $RANDOM >"/var/tmp/72-source/dir-$2/foo.txt"
- echo $RANDOM >"/var/tmp/72-source/dir-$2/bar.txt"
- tar --numeric-owner -C "/var/tmp/72-source/dir-$2/" -czf "/var/tmp/72-source/dir-$2.tar.gz" .
+ mkdir -p "$WORKDIR/source/dir-$version"
+ echo $RANDOM >"$WORKDIR/source/dir-$version/foo.txt"
+ echo $RANDOM >"$WORKDIR/source/dir-$version/bar.txt"
+ tar --numeric-owner -C "$WORKDIR/source/dir-$version/" -czf "$WORKDIR/source/dir-$version.tar.gz" .
- ( cd /var/tmp/72-source/ && sha256sum uki* part* dir-*.tar.gz >SHA256SUMS )
+ (cd "$WORKDIR/source" && sha256sum uki* part* dir-*.tar.gz >SHA256SUMS)
}
update_now() {
# Update to newest version. First there should be an update ready, then we
# do the update, and then there should not be any ready anymore
- "$SYSUPDATE" --definitions=/var/tmp/72-defs --verify=no check-new
- "$SYSUPDATE" --definitions=/var/tmp/72-defs --verify=no update
- ( ! "$SYSUPDATE" --definitions=/var/tmp/72-defs --verify=no check-new )
+ "$SYSUPDATE" --definitions="$WORKDIR/defs" --verify=no check-new
+ "$SYSUPDATE" --definitions="$WORKDIR/defs" --verify=no update
+ (! "$SYSUPDATE" --definitions="$WORKDIR/defs" --verify=no check-new)
}
verify_version() {
- # Inputs:
- # $1: block device
- # $2: sector size
- # $3: version
- # $4: partition number of part1
- # $5: partition number of part2
+ local block_device="${1:?}"
+ local sector_size="${2:?}"
+ local version="${3:?}"
+ local part1_number="${4:?}"
+ local part2_number="${5:?}"
+ local gpt_reserved_sectors part1_offset part2_offset
- gpt_reserved_sectors=$(( 1024 * 1024 / $2 ))
- part1_offset=$(( ( $4 - 1 ) * 2048 + gpt_reserved_sectors ))
- part2_offset=$(( ( $5 - 1 ) * 2048 + gpt_reserved_sectors ))
+ gpt_reserved_sectors=$((1024 * 1024 / sector_size))
+ part1_offset=$(((part1_number - 1) * 2048 + gpt_reserved_sectors))
+ part2_offset=$(((part2_number - 1) * 2048 + gpt_reserved_sectors))
# Check the partitions
- dd if="$1" bs="$2" skip="$part1_offset" count=2048 | cmp "/var/tmp/72-source/part1-$3.raw"
- dd if="$1" bs="$2" skip="$part2_offset" count=2048 | cmp "/var/tmp/72-source/part2-$3.raw"
+ dd if="$block_device" bs="$sector_size" skip="$part1_offset" count=2048 | cmp "$WORKDIR/source/part1-$version.raw"
+ dd if="$block_device" bs="$sector_size" skip="$part2_offset" count=2048 | cmp "$WORKDIR/source/part2-$version.raw"
# Check the UKI
- cmp "/var/tmp/72-source/uki-$3.efi" "/var/tmp/72-xbootldr/EFI/Linux/uki_$3+3-0.efi"
- test -z "$(ls -A /var/tmp/72-esp/EFI/Linux)"
+ cmp "$WORKDIR/source/uki-$version.efi" "$WORKDIR/xbootldr/EFI/Linux/uki_$version+3-0.efi"
+ test -z "$(ls -A "$WORKDIR/esp/EFI/Linux")"
# Check the extra efi
- cmp "/var/tmp/72-source/uki-extra-$3.efi" "/var/tmp/72-xbootldr/EFI/Linux/uki_$3.efi.extra.d/extra.addon.efi"
+ cmp "$WORKDIR/source/uki-extra-$version.efi" "$WORKDIR/xbootldr/EFI/Linux/uki_$version.efi.extra.d/extra.addon.efi"
# Check the directories
- cmp "/var/tmp/72-source/dir-$3/foo.txt" /var/tmp/72-dirs/current/foo.txt
- cmp "/var/tmp/72-source/dir-$3/bar.txt" /var/tmp/72-dirs/current/bar.txt
+ cmp "$WORKDIR/source/dir-$version/foo.txt" "$WORKDIR/dirs/current/foo.txt"
+ cmp "$WORKDIR/source/dir-$version/bar.txt" "$WORKDIR/dirs/current/bar.txt"
}
-for sector_size in $SECTOR_SIZES ; do
+for sector_size in "${SECTOR_SIZES[@]}"; do
# Disk size of:
# - 1MB for GPT
# - 4 partitions of 2048 sectors each
# - 1MB for backup GPT
- disk_size=$(( sector_size * 2048 * 4 + 1024 * 1024 * 2 ))
+ disk_size=$((sector_size * 2048 * 4 + 1024 * 1024 * 2))
rm -f "$BACKING_FILE"
truncate -s "$disk_size" "$BACKING_FILE"
- if losetup --find >/dev/null 2>&1; then
- # shellcheck disable=SC2086
- blockdev="$(losetup --find --show --sector-size $sector_size $BACKING_FILE)"
+ if [[ -e /dev/loop-control ]]; then
+ blockdev="$(losetup --find --show --sector-size "$sector_size" "$BACKING_FILE")"
else
blockdev="$BACKING_FILE"
fi
size=2048, type=2c7357ed-ebd2-46d9-aec1-23d437ec2bf5, name=_empty
EOF
- rm -rf /var/tmp/72-dirs
- mkdir -p /var/tmp/72-dirs
+ for d in "dirs" "defs"; do
+ rm -rf "${WORKDIR:?}/$d"
+ mkdir -p "$WORKDIR/$d"
+ done
- rm -rf /var/tmp/72-defs
- mkdir -p /var/tmp/72-defs
-
- cat >/var/tmp/72-defs/01-first.conf <<EOF
+ cat >"$WORKDIR/defs/01-first.conf" <<EOF
[Source]
Type=regular-file
-Path=/var/tmp/72-source
+Path=$WORKDIR/source
MatchPattern=part1-@v.raw
[Target]
MatchPartitionType=root-x86-64
EOF
- cat >/var/tmp/72-defs/02-second.conf <<EOF
+ cat >"$WORKDIR/defs/02-second.conf" <<EOF
[Source]
Type=regular-file
-Path=/var/tmp/72-source
+Path=$WORKDIR/source
MatchPattern=part2-@v.raw.gz
[Target]
MatchPartitionType=root-x86-64-verity
EOF
- cat >/var/tmp/72-defs/03-third.conf <<EOF
+ cat >"$WORKDIR/defs/03-third.conf" <<EOF
[Source]
Type=directory
-Path=/var/tmp/72-source
+Path=$WORKDIR/source
MatchPattern=dir-@v
[Target]
Type=directory
-Path=/var/tmp/72-dirs
-CurrentSymlink=/var/tmp/72-dirs/current
+Path=$WORKDIR/dirs
+CurrentSymlink=$WORKDIR/dirs/current
MatchPattern=dir-@v
InstancesMax=3
EOF
- cat >/var/tmp/72-defs/04-fourth.conf <<EOF
+ cat >"$WORKDIR/defs/04-fourth.conf" <<EOF
[Source]
Type=regular-file
-Path=/var/tmp/72-source
+Path=$WORKDIR/source
MatchPattern=uki-@v.efi
[Target]
InstancesMax=2
EOF
- cat >/var/tmp/72-defs/05-fifth.conf <<EOF
+ cat >"$WORKDIR/defs/05-fifth.conf" <<EOF
[Source]
Type=regular-file
-Path=/var/tmp/72-source
+Path=$WORKDIR/source
MatchPattern=uki-extra-@v.efi
[Target]
InstancesMax=2
EOF
- rm -rf /var/tmp/72-esp /var/tmp/72-xbootldr
- mkdir -p /var/tmp/72-esp/EFI/Linux /var/tmp/72-xbootldr/EFI/Linux
-
- rm -rf /var/tmp/72-source
- mkdir -p /var/tmp/72-source
+ rm -rf "${WORKDIR:?}"/{esp,xbootldr,source}
+ mkdir -p "$WORKDIR"/{source,esp/EFI/Linux,xbootldr/EFI/Linux}
# Install initial version and verify
new_version "$sector_size" v1
new_version "$sector_size" v3
update_now
verify_version "$blockdev" "$sector_size" v3 1 3
- test ! -f "/var/tmp/72-xbootldr/EFI/Linux/uki_v1+3-0.efi"
- test ! -f "/var/tmp/72-xbootldr/EFI/Linux/uki_v1.efi.extra.d/extra.addon.efi"
- test ! -d "/var/tmp/72-xbootldr/EFI/Linux/uki_v1.efi.extra.d"
+ test ! -f "$WORKDIR/xbootldr/EFI/Linux/uki_v1+3-0.efi"
+ test ! -f "$WORKDIR/xbootldr/EFI/Linux/uki_v1.efi.extra.d/extra.addon.efi"
+ test ! -d "$WORKDIR/xbootldr/EFI/Linux/uki_v1.efi.extra.d"
# Create fourth version, and update through a file:// URL. This should be
# almost as good as testing HTTP, but is simpler for us to set up. file:// is
# see above)
new_version "$sector_size" v4
- cat >/var/tmp/72-defs/02-second.conf <<EOF
+ cat >"$WORKDIR/defs/02-second.conf" <<EOF
[Source]
Type=url-file
-Path=file:///var/tmp/72-source
+Path=file://$WORKDIR/source
MatchPattern=part2-@v.raw.gz
[Target]
MatchPartitionType=root-x86-64-verity
EOF
- cat >/var/tmp/72-defs/03-third.conf <<EOF
+ cat >"$WORKDIR/defs/03-third.conf" <<EOF
[Source]
Type=url-tar
-Path=file:///var/tmp/72-source
+Path=file://$WORKDIR/source
MatchPattern=dir-@v.tar.gz
[Target]
Type=directory
-Path=/var/tmp/72-dirs
-CurrentSymlink=/var/tmp/72-dirs/current
+Path=$WORKDIR/dirs
+CurrentSymlink=$WORKDIR/dirs/current
MatchPattern=dir-@v
InstancesMax=3
EOF
verify_version "$blockdev" "$sector_size" v4 2 4
# Cleanup
- [ -b "$blockdev" ] && losetup --detach "$blockdev"
+ [[ -b "$blockdev" ]] && losetup --detach "$blockdev"
rm "$BACKING_FILE"
done
-rm -r /var/tmp/72-{dirs,defs,source,xbootldr,esp}
-
touch /testok
# Wait a bit for the coredumps to get processed
timeout 30 bash -c "while [[ \$(coredumpctl list -q --no-legend $CORE_TEST_BIN | wc -l) -lt 4 ]]; do sleep 1; done"
-# Make sure we can forward crashes back to containers
-CONTAINER="testsuite-74-container"
-
-mkdir -p "/var/lib/machines/$CONTAINER"
-mkdir -p "/run/systemd/system/systemd-nspawn@$CONTAINER.service.d"
-# Bind-mounting /etc into the container kinda defeats the purpose of --volatile=,
-# but we need the ASan-related overrides scattered across /etc
-cat > "/run/systemd/system/systemd-nspawn@$CONTAINER.service.d/override.conf" << EOF
+if cgroupfs_supports_user_xattrs; then
+ # Make sure we can forward crashes back to containers
+ CONTAINER="testsuite-74-container"
+
+ mkdir -p "/var/lib/machines/$CONTAINER"
+ mkdir -p "/run/systemd/system/systemd-nspawn@$CONTAINER.service.d"
+ # Bind-mounting /etc into the container kinda defeats the purpose of --volatile=,
+ # but we need the ASan-related overrides scattered across /etc
+ cat > "/run/systemd/system/systemd-nspawn@$CONTAINER.service.d/override.conf" <<EOF
[Service]
ExecStart=
ExecStart=systemd-nspawn --quiet --link-journal=try-guest --keep-unit --machine=%i --boot \
--volatile=yes --directory=/ --bind-ro=/etc --inaccessible=/etc/machine-id
EOF
-systemctl daemon-reload
+ systemctl daemon-reload
+
+ [[ "$(systemd-detect-virt)" == "qemu" ]] && TIMEOUT=120 || TIMEOUT=60
-if cgroupfs_supports_user_xattrs; then
machinectl start "$CONTAINER"
- timeout 60 bash -xec "until systemd-run -M '$CONTAINER' -q --wait --pipe true; do sleep .5; done"
+ timeout "$TIMEOUT" bash -xec "until systemd-run -M '$CONTAINER' -q --wait --pipe true; do sleep .5; done"
[[ "$(systemd-run -M "$CONTAINER" -q --wait --pipe coredumpctl list -q --no-legend /usr/bin/sleep | wc -l)" -eq 0 ]]
machinectl copy-to "$CONTAINER" "$MAKE_DUMP_SCRIPT"
systemd-run -M "$CONTAINER" -q --wait --pipe "$MAKE_DUMP_SCRIPT" "/usr/bin/sleep" "SIGTRAP"
# Wait a bit for the coredumps to get processed
timeout 30 bash -c "while [[ \$(systemd-run -M $CONTAINER -q --wait --pipe coredumpctl list -q --no-legend /usr/bin/sleep | wc -l) -lt 2 ]]; do sleep 1; done"
+
+ rm -rf "/var/lib/machines/$CONTAINER"
fi
coredumpctl
set -o pipefail
at_exit() {
+ rm -f /run/credstore/network.conf.50-testme
rm -f /run/credstore/network.network.50-testme
+ rm -f /run/systemd/networkd.conf.d/50-testme.conf
+ rm -f /run/systemd/network/50-testme.network
rm -f /run/systemd/system/systemd-network-generator.service.d/50-testme.conf
}
trap at_exit EXIT
mkdir -p /run/credstore
+cat > /run/credstore/network.conf.50-testme <<EOF
+[Network]
+SpeedMeter=yes
+EOF
+
cat > /run/credstore/network.network.50-testme <<EOF
[Match]
Property=IDONTEXIST
systemctl edit systemd-network-generator.service --stdin --drop-in=50-testme.conf <<EOF
[Service]
+LoadCredential=network.conf.50-testme
LoadCredential=network.network.50-testme
EOF
systemctl restart systemd-network-generator
-test -f /run/systemd/network/50-testme.network
+diff /run/credstore/network.conf.50-testme /run/systemd/networkd.conf.d/50-testme.conf
+diff /run/credstore/network.network.50-testme /run/systemd/network/50-testme.network
{/usr/lib,/etc}/systemd/network/"$LINK_NAME" "/etc/systemd/network/${NETWORK_NAME}.d" \
"new" "+4"
fi
+
+ rm -f /run/systemd/networkd.conf.d/10-hoge.conf
}
trap at_exit EXIT
if systemctl --quiet is-active systemd-udevd; then
assert_in 'alias test_alias' "$ip_link"
fi
+
+mkdir -p /run/systemd/networkd.conf.d
+cat >/run/systemd/networkd.conf.d/10-hoge.conf <<EOF
+# TEST DROP-IN FILE
+[Network]
+SpeedMeter=yes
+EOF
+
+assert_in '# TEST DROP-IN FILE' "$(networkctl cat)"
Name=dns0
[Network]
+IPv6AcceptRA=no
Address=10.0.0.1/24
Address=fd00:dead:beef:cafe::1/64
DNSSEC=allow-downgrade
Name=dns1
[Network]
+IPv6AcceptRA=no
Address=10.99.0.1/24
DNSSEC=no
EOF
sleep 2
drop_dns_outbound_traffic
set +e
- run dig stale1.unsigned.test -t A
+ # Make sure we give sd-resolved enough time to timeout (5-10s) before giving up
+ # See: https://github.com/systemd/systemd/issues/31639#issuecomment-2009152617
+ run dig +tries=1 +timeout=15 stale1.unsigned.test -t A
set -eux
grep -qE "no servers could be reached" "$RUN_OUT"
nft flush ruleset
grep -qE "NOERROR" "$RUN_OUT"
sleep 2
drop_dns_outbound_traffic
- run dig stale1.unsigned.test -t A
+ # Make sure we give sd-resolved enough time to timeout (5-10s) and serve the stale data (see above)
+ run dig +tries=1 +timeout=15 stale1.unsigned.test -t A
grep -qE "NOERROR" "$RUN_OUT"
grep -qE "10.0.0.112" "$RUN_OUT"
+++ /dev/null
-#!/usr/bin/env bash
-# SPDX-License-Identifier: LGPL-2.1-or-later
-set -eux
-set -o pipefail
-
-# shellcheck source=test/units/util.sh
-. "$(dirname "$0")"/util.sh
-
-export SYSTEMD_LOG_LEVEL=debug
-
-assert_eq "$LISTEN_FDS" "1"
-assert_eq "$LISTEN_FDNAMES" "socket"
-read -r -u 3 text
-assert_eq "$text" "Socket"
+++ /dev/null
-#!/usr/bin/env bash
-# SPDX-License-Identifier: LGPL-2.1-or-later
-set -eux
-set -o pipefail
-
-# shellcheck source=test/units/util.sh
-. "$(dirname "$0")"/util.sh
-
-export SYSTEMD_LOG_LEVEL=debug
-
-assert_eq "$LISTEN_FDS" "1"
-assert_eq "$LISTEN_FDNAMES" "new-file"
-read -r -u 3 text
-assert_eq "$text" "New"
+++ /dev/null
-# SPDX-License-Identifier: LGPL-2.1-or-later
-[Unit]
-Description=TEST-77-OPENFILE
-
-[Service]
-OpenFile=/test-77-open.dat:open:read-only
-OpenFile=/test-77-file.dat
-ExecStartPre=rm -f /failed /testok
-ExecStart=/usr/lib/systemd/tests/testdata/units/%N.sh
-Type=oneshot
+++ /dev/null
-#!/usr/bin/env bash
-# SPDX-License-Identifier: LGPL-2.1-or-later
-set -eux
-set -o pipefail
-
-# shellcheck source=test/units/util.sh
-. "$(dirname "$0")"/util.sh
-
-export SYSTEMD_LOG_LEVEL=debug
-
-assert_eq "$LISTEN_FDS" "2"
-assert_eq "$LISTEN_FDNAMES" "open:test-77-file.dat"
-read -r -u 3 text
-assert_eq "$text" "Open"
-read -r -u 4 text
-assert_eq "$text" "File"
-
-# Test for socket
-systemctl start testsuite-77-server.socket
-systemd-run -p OpenFile=/tmp/test.sock:socket:read-only \
- --wait \
- --pipe \
- /usr/lib/systemd/tests/testdata/units/testsuite-77-client.sh
-
-# Tests for D-Bus
-diff <(systemctl show -p OpenFile testsuite-77) - <<EOF
-OpenFile=/test-77-open.dat:open:read-only
-OpenFile=/test-77-file.dat
-EOF
-echo "New" >/test-77-new-file.dat
-systemd-run --wait -p OpenFile=/test-77-new-file.dat:new-file:read-only "$(dirname "$0")"/testsuite-77-run.sh
-
-assert_rc 202 systemd-run --wait -p OpenFile=/test-77-new-file.dat:new-file:read-only -p OpenFile=/test-77-mssing-file.dat:missing-file:read-only "$(dirname "$0")"/testsuite-77-run.sh
-
-assert_rc 0 systemd-run --wait -p OpenFile=/test-77-new-file.dat:new-file:read-only -p OpenFile=/test-77-mssing-file.dat:missing-file:read-only,graceful "$(dirname "$0")"/testsuite-77-run.sh
-
-# End
-touch /testok
echo "This is the fourth boot!"
systemd-notify --status="Fourth Boot"
+ test "$(busctl -j get-property org.freedesktop.systemd1 /org/freedesktop/systemd1 org.freedesktop.systemd1.Manager SoftRebootsCount | jq -r '.data')" -eq 3
+
rm /run/testsuite82.touch3
mount
rmdir /original-root /run/nextroot
echo "This is the third boot!"
systemd-notify --status="Third Boot"
+ test "$(busctl -j get-property org.freedesktop.systemd1 /org/freedesktop/systemd1 org.freedesktop.systemd1.Manager SoftRebootsCount | jq -r '.data')" -eq 2
+
rm /run/testsuite82.touch2
# Check that the fdstore entry still exists
echo "This is the second boot!"
systemd-notify --status="Second Boot"
+ test "$(busctl -j get-property org.freedesktop.systemd1 /org/freedesktop/systemd1 org.freedesktop.systemd1.Manager SoftRebootsCount | jq -r '.data')" -eq 1
+
# Clean up what we created earlier
rm /run/testsuite82.touch
# Restart the unit that is not supposed to survive
systemd-run --collect --service-type=exec --unit=testsuite-82-nosurvive.service sleep infinity
+ # Now ensure there are no naming clashes and a bunch of transient units all succeed
+ for _ in $(seq 1 25); do
+ systemd-run --wait true
+ done
+
# Now issue the soft reboot. We should be right back soon. Given /run/nextroot exists, we should
# automatically do a softreboot instead of normal reboot.
touch /run/testsuite82.touch2
# This is the first boot
systemd-notify --status="First Boot"
+ test "$(busctl -j get-property org.freedesktop.systemd1 /org/freedesktop/systemd1 org.freedesktop.systemd1.Manager SoftRebootsCount | jq -r '.data')" -eq 0
+
# Let's upload an fd to the fdstore, so that we can verify fdstore passing works correctly
T="/dev/shm/fdstore.$RANDOM"
echo "wuffwuff" >"$T"
systemd-inhibit --what=shutdown --who=test --why=test --mode=delay \
sleep infinity
+ # Enqueue a bunch of failing units to try and trigger the transient name clash that happens due to D-Bus
+ # being restarted and the "unique" bus IDs not being unique across restarts
+ for _ in $(seq 1 25); do
+ # Use --wait to ensure we connect to the system bus instead of the private bus (otherwise a UUID is
+ # used instead of the bus ID)
+ systemd-run --wait false || true
+ done
+
# Now issue the soft reboot. We should be right back soon.
touch /run/testsuite82.touch
systemctl --no-block --check-inhibitors=yes soft-reboot
# See tmpfiles.d(5) for details
L {{SSHCONFDIR}}/20-systemd-ssh-proxy.conf - - - - {{LIBEXECDIR}}/ssh_config.d/20-systemd-ssh-proxy.conf
+{% if CREATE_SSHDPRIVSEPDIR %}
+d {{SSHDPRIVSEPDIR}} 0755
+{% endif %}
import os
import sys
-import lxml.etree as tree
+
+try:
+ import lxml.etree as tree
+except ImportError as e:
+ print(str(e), file=sys.stderr)
+ sys.exit(77)
_parser = tree.XMLParser(resolve_entities=False)
tree.set_default_parser(_parser)
set -eu
tag="$(git describe --abbrev=0 --match 'v[0-9][0-9][0-9]')"
-(
- # authors
- git log --pretty=tformat:%aN -s "${tag}.."
- # Co-authors (drop empty line and mail addresses)
- git log --pretty='tformat:%(trailers:key=Co-authored-by,valueonly)' -s "${tag}.." | sed -e '/^[[:space:]]*$/ d' | sed -e 's/ <.*@.*>$//'
-) |
- grep -v noreply@weblate.org |
- sed 's/ / /g; s/--/-/g; s/.*/\0,/' |
- sort -u | tr '\n' ' ' | sed -e "s/^/Contributions from: /g" -e "s/,\s*$/\n/g" | fold -w 72 -s |
+git shortlog -s --group=author --group=trailer:Co-authored-by "${tag}.." |
+ sed -e 's/^[[:space:]]*[0-9]*[[:space:]]*//; /Weblate/ d; /dependabot\[bot\]/ d; s/ / /g; s/--/-/g; s/.*/\0,/' |
+ tr '\n' ' ' | sed -e "s/^/Contributions from: /g" -e "s/,\s*$/\n/g" | fold -w 72 -s |
sed -e "s/^/ /g" -e "s/\s*$//g"
-#!/bin/bash
+#!/bin/sh
# SPDX-License-Identifier: LGPL-2.1-or-later
-set -e
-mkosi-install systemd udev
+exec git submodule update
git config push.recurseSubmodules no
fi
-if [ ! -f .git/hooks/pre-commit.sample ] || [ -f .git/hooks/pre-commit ]; then
- exit 2 # not needed
+ret=2
+
+if [ -f .git/hooks/pre-commit.sample ] && [ ! -f .git/hooks/pre-commit ]; then
+ cp -p .git/hooks/pre-commit.sample .git/hooks/pre-commit
+ chmod +x .git/hooks/pre-commit
+ echo 'Activated pre-commit hook'
+ ret=0
+fi
+
+if [ ! -f .git/hooks/post-rewrite ]; then
+ cp -p tools/git-post-rewrite-hook.sh .git/hooks/post-rewrite
+ echo 'Activated post-rewrite hook'
+ ret=0
fi
-cp -p .git/hooks/pre-commit.sample .git/hooks/pre-commit
-chmod +x .git/hooks/pre-commit
-echo 'Activated pre-commit hook'
+exit $ret
{ 'file' : 'systemd-growfs-root.service.in' },
{ 'file' : 'systemd-growfs@.service.in' },
{ 'file' : 'systemd-halt.service' },
+ {
+ 'file' : 'systemd-hibernate-clear.service.in',
+ 'conditions' : ['ENABLE_HIBERNATE', 'ENABLE_EFI'],
+ 'symlinks' : ['sysinit.target.wants/'],
+ },
{
'file' : 'systemd-hibernate-resume.service.in',
'conditions' : ['ENABLE_HIBERNATE'],
'conditions' : ['ENABLE_TMPFILES'],
'symlinks' : ['sysinit.target.wants/'],
},
+ { 'file' : 'systemd-udev-load-credentials.service' },
{ 'file' : 'systemd-udev-settle.service' },
{
'file' : 'systemd-udev-trigger.service',
'file' : 'systemd-userdbd.socket',
'conditions' : ['ENABLE_USERDB'],
},
+ {
+ 'file' : 'systemd-mountfsd.service.in',
+ 'conditions' : ['ENABLE_MOUNTFSD'],
+ },
+ {
+ 'file' : 'systemd-mountfsd.socket',
+ 'conditions' : ['ENABLE_MOUNTFSD'],
+ },
+ {
+ 'file' : 'systemd-nsresourced.service.in',
+ 'conditions' : ['ENABLE_NSRESOURCED'],
+ },
+ {
+ 'file' : 'systemd-nsresourced.socket',
+ 'conditions' : ['ENABLE_NSRESOURCED'],
+ },
{
'file' : 'systemd-vconsole-setup.service.in',
'conditions' : ['ENABLE_VCONSOLE'],
Documentation=man:systemd-boot-check-no-failures.service(8)
After=default.target graphical.target multi-user.target
Before=boot-complete.target
-Conflicts=shutdown.target
-Before=shutdown.target
[Service]
Type=oneshot
--- /dev/null
+# SPDX-License-Identifier: LGPL-2.1-or-later
+#
+# This file is part of systemd.
+#
+# systemd is free software; you can redistribute it and/or modify it
+# under the terms of the GNU Lesser General Public License as published by
+# the Free Software Foundation; either version 2.1 of the License, or
+# (at your option) any later version.
+
+[Unit]
+Description=Clear Stale Hibernate Storage Info
+Documentation=man:systemd-hibernate-clear.service(8)
+
+ConditionPathExists=/sys/firmware/efi/efivars/HibernateLocation-8cf2644b-4b0b-428f-9387-6d876050dc67
+ConditionPathExists=!/etc/initrd-release
+
+DefaultDependencies=no
+Before=sysinit.target shutdown.target
+Conflicts=shutdown.target
+
+[Service]
+Type=oneshot
+RemainAfterExit=yes
+ExecStart={{LIBEXECDIR}}/systemd-hibernate-resume --clear
--- /dev/null
+# SPDX-License-Identifier: LGPL-2.1-or-later
+#
+# This file is part of systemd.
+#
+# systemd is free software; you can redistribute it and/or modify it
+# under the terms of the GNU Lesser General Public License as published by
+# the Free Software Foundation; either version 2.1 of the License, or
+# (at your option) any later version.
+
+[Unit]
+Description=DDI File System Mounter
+Documentation=man:systemd-mountfsd.service(8)
+Requires=systemd-mountfsd.socket
+After=systemd-mountfsd.socket
+Conflicts=shutdown.target
+Before=sysinit.target shutdown.target
+DefaultDependencies=no
+
+[Service]
+#CapabilityBoundingSet=CAP_DAC_READ_SEARCH CAP_SYS_RESOURCE CAP_BPF CAP_PERFMON CAP_SETGID CAP_SETUID
+ExecStart={{LIBEXECDIR}}/systemd-mountfsd
+IPAddressDeny=any
+LimitNOFILE={{HIGH_RLIMIT_NOFILE}}
+LockPersonality=yes
+MemoryDenyWriteExecute=yes
+NoNewPrivileges=yes
+ProtectProc=invisible
+ProtectControlGroups=yes
+ProtectHome=yes
+ProtectHostname=yes
+ProtectKernelLogs=yes
+ProtectKernelModules=yes
+ProtectSystem=strict
+RestrictAddressFamilies=AF_UNIX AF_NETLINK AF_INET AF_INET6
+RestrictRealtime=yes
+RestrictSUIDSGID=yes
+SystemCallArchitectures=native
+SystemCallErrorNumber=EPERM
+SystemCallFilter=@system-service @mount
+Type=notify
+NotifyAccess=all
+FileDescriptorStoreMax=4096
+{{SERVICE_WATCHDOG}}
+
+[Install]
+Also=systemd-mountfsd.socket
--- /dev/null
+# SPDX-License-Identifier: LGPL-2.1-or-later
+#
+# This file is part of systemd.
+#
+# systemd is free software; you can redistribute it and/or modify it
+# under the terms of the GNU Lesser General Public License as published by
+# the Free Software Foundation; either version 2.1 of the License, or
+# (at your option) any later version.
+
+[Unit]
+Description=DDI File System Mounter Socket
+Documentation=man:systemd-mountfsd.service(8)
+DefaultDependencies=no
+Conflicts=shutdown.target
+Before=sockets.target shutdown.target
+
+[Socket]
+ListenStream=/run/systemd/io.systemd.MountFileSystem
+SocketMode=0666
+
+[Install]
+WantedBy=sockets.target
After=systemd-remount-fs.service systemd-networkd.service
BindsTo=systemd-networkd.service
Conflicts=shutdown.target
-RequiresMountsFor=/var/lib/systemd/network
ConditionPathExists=!/etc/initrd-release
[Service]
--- /dev/null
+# SPDX-License-Identifier: LGPL-2.1-or-later
+#
+# This file is part of systemd.
+#
+# systemd is free software; you can redistribute it and/or modify it
+# under the terms of the GNU Lesser General Public License as published by
+# the Free Software Foundation; either version 2.1 of the License, or
+# (at your option) any later version.
+
+[Unit]
+Description=Namespace Resource Manager
+Documentation=man:systemd-nsresourced.service(8)
+Requires=systemd-nsresourced.socket
+After=systemd-nsresourced.socket
+Conflicts=shutdown.target
+Before=sysinit.target shutdown.target
+DefaultDependencies=no
+
+[Service]
+CapabilityBoundingSet=CAP_DAC_READ_SEARCH CAP_SYS_RESOURCE CAP_BPF CAP_PERFMON CAP_SETGID CAP_SETUID CAP_SYS_ADMIN CAP_CHOWN CAP_FOWNER
+ExecStart={{LIBEXECDIR}}/systemd-nsresourced
+IPAddressDeny=any
+LimitNOFILE={{HIGH_RLIMIT_NOFILE}}
+LockPersonality=yes
+MemoryDenyWriteExecute=yes
+NoNewPrivileges=yes
+PrivateDevices=yes
+ProtectProc=invisible
+ProtectControlGroups=yes
+ProtectHome=yes
+ProtectHostname=yes
+ProtectKernelLogs=yes
+ProtectKernelModules=yes
+ProtectSystem=strict
+RestrictAddressFamilies=AF_UNIX AF_NETLINK
+RestrictRealtime=yes
+RestrictSUIDSGID=yes
+SystemCallArchitectures=native
+SystemCallErrorNumber=EPERM
+SystemCallFilter=@system-service bpf perf_event_open open_by_handle_at
+Type=notify
+NotifyAccess=all
+FileDescriptorStoreMax=4096
+{{SERVICE_WATCHDOG}}
+
+[Install]
+Also=systemd-nsresourced.socket
--- /dev/null
+# SPDX-License-Identifier: LGPL-2.1-or-later
+#
+# This file is part of systemd.
+#
+# systemd is free software; you can redistribute it and/or modify it
+# under the terms of the GNU Lesser General Public License as published by
+# the Free Software Foundation; either version 2.1 of the License, or
+# (at your option) any later version.
+
+[Unit]
+Description=Namespace Resource Manager Socket
+Documentation=man:systemd-nsresourced.service(8)
+DefaultDependencies=no
+Conflicts=shutdown.target
+Before=sockets.target shutdown.target
+
+[Socket]
+ListenStream=/run/systemd/io.systemd.NamespaceResource
+Symlinks=/run/systemd/userdb/io.systemd.NamespaceResource
+SocketMode=0666
+
+[Install]
+WantedBy=sockets.target
DefaultDependencies=no
Wants=modprobe@loop.service modprobe@dm_mod.service
-After=initrd-usr-fs.target modprobe@loop.service modprobe@dm_mod.service
+After=initrd-usr-fs.target modprobe@loop.service modprobe@dm_mod.service systemd-tpm2-setup-early.service
Before=initrd-root-fs.target
Conflicts=shutdown.target initrd-switch-root.target
Before=shutdown.target initrd-switch-root.target
--- /dev/null
+# SPDX-License-Identifier: LGPL-2.1-or-later
+#
+# This file is part of systemd.
+#
+# systemd is free software; you can redistribute it and/or modify it
+# under the terms of the GNU Lesser General Public License as published by
+# the Free Software Foundation; either version 2.1 of the License, or
+# (at your option) any later version.
+
+[Unit]
+Description=Load udev Rules from Credentials
+Documentation=man:udevadm(8)
+Documentation=man:udev(7)
+Documentation=man:systemd.system-credentials(7)
+
+DefaultDependencies=no
+Before=systemd-udevd.service
+Conflicts=shutdown.target initrd-switch-root.target
+Before=shutdown.target initrd-switch-root.target
+
+[Service]
+Type=oneshot
+RemainAfterExit=yes
+ExecStart=udevadm control --load-credentials
+ImportCredential=udev.conf.*
+ImportCredential=udev.rules.*
+
+[Install]
+WantedBy=sysinit.target
DefaultDependencies=no
After=systemd-sysusers.service systemd-hwdb-update.service
Before=sysinit.target
+Wants=systemd-udev-load-credentials.service
ConditionPathIsReadWrite=/sys
[Service]