matrix:
sanitizer: [address, undefined, memory]
steps:
+ - run: sudo sysctl -w vm.mmap_rnd_bits=28
- name: Build Fuzzers
id: build
uses: google/clusterfuzzlite/actions/build_fuzzers@v1
security-events: write
steps:
+ - run: sudo sysctl -w vm.mmap_rnd_bits=28
- name: Build Fuzzers
id: build
uses: google/oss-fuzz/infra/cifuzz/actions/build_fuzzers@master
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 it.
- To forcibly reenable cgroup v1 support, SYSTEMD_CGROUP_ENABLE_LEGACY_FORCE=1
- must be set on kernel command line. The meson option 'default-hierarchy='
- is also deprecated, i.e. only cgroup v2 ('unified' hierarchy) can be
- selected as build-time default.
-
- * Previously, systemd-networkd did not explicitly remove any bridge VLAN
- IDs assigned on bridge master and ports. Since v256, if a .network
- file for an interface has at least one valid settings in [BridgeVLAN]
- section, then all assigned VLAN IDs on the interface that are not
- configured in the .network file are removed.
+ considered obsolete and systemd by default will refuse to boot under
+ it. To forcibly reenable cgroup v1 support,
+ SYSTEMD_CGROUP_ENABLE_LEGACY_FORCE=1 must be set on kernel command
+ line. The meson option 'default-hierarchy=' is also deprecated, i.e.
+ only cgroup v2 ('unified' hierarchy) can be selected as build-time
+ default.
+
+ * Previously, systemd-networkd did not explicitly remove any bridge
+ VLAN IDs assigned on bridge master and ports. Since version 256, if a
+ .network file for an interface has at least one valid setting in the
+ [BridgeVLAN] section, then all assigned VLAN IDs on the interface
+ 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 is still commonly found).
+ 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 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.
+
+ 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.
+
+ ukify will look for the config files in /usr/lib/kernel/ and the
+ other 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 embedded in the name, and they may be ordered by version
+ and the newest one can be reliably 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 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.
+
+ systemd-homed, userdbctl, and homectl gained support for blob
+ directories.
+
+ * New command-line tool 'importctl' is added with the following verbs:
+ pull-tar, pull-raw, import-tar, import-raw, import-fs, export-tar,
+ export-raw, list-transfers, cancel-transfer.
+
+ Service Manager:
+
+ * 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 unit setting WantsMountsFor= has been added. It is analogous to
+ RequiresMountsFor=, but with a Wants= dependency instead of
+ Requires=. This new logic is used in various places where mounts were
+ added as dependencies for other settings (WorkingDirectory=-…,
+ PrivateTmp=yes, cryptsetup lines with 'nofail').
+
+ * New unit setting MemoryZSwapWriteback= can be used to control the new
+ 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.
+
+ * 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.
+
+ * AllowedCPUs= now supports specifier expansion.
+
+ * What= setting in .mount and .swap units now accepts fstab-style
+ identifiers, for example UUID=… or LABEL=….
+
+ * RestrictNetworkInterfaces= now supports alternative network interface
+ names.
+
+ * 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.
+
+ * Systemd hostname can be configured via the systemd.hostname
+ credential.
+
+ The 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).
+
+ * systemd-vmspawn gained a new --forward-journal= option to forward the
+ virtual machine's journal entries to the host. This is done over a
+ AF_VSOCK socket, i.e. it does not require networking in the guest.
+
+ * journalctl gained option '-i' as a shortcut for --file=.
+
+ * journalctl gained a new -T/--exclude-identifier= option to filter
+ out certain syslog identifiers.
+
+ * journalctl gained a new --list-namespaces option.
+
+ * systemd-journal-gatewayd allows restricting the time range of
+ retrieved entries with realtime=[<since>]:[<until>].
+
+ Device Management:
+
+ * Udev now creates symlinks that combine by-path and by-{label,uuid}
+ information:
+ /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
+ /dev/media/by-path/pci-0000:04:00.3-usb-0:1:1.0-media-controller.
+
+ * 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
+ 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.
+
+ * 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
+ 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.
+
+ '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.
+
+ * systemd-hostnamed now provides a Varlink interface.
+
+ * systemd-hostnamed exports the data in os-release(5) and
+ machine-info(5) via D-Bus and Varlink.
+
Network Management:
+ * systemd-networkd now provides a 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=.
- * 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 cryptographic algorithms it only supports — does not
- actually provide the security benefits it's supposed to
- provide. Given that the rest of systemd's codebase never supported
- TPM 1.2 the support has now been removed from systemd-stub as well.
+ * systemd-networkd now exports the NamespaceId and NamespaceNSID
+ properties via D-Bus and Varlink.
+
+ * 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 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-networkd will now pick up wireguard configuration from
+ credentials.
+
+ * systemd-ssh-proxy is a new SSH client plugin that allows connecting
+ to AF_SOCK or AF_UNIX sockets.
+
+ * 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 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.
+
+ * 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 now implements RFC 8914 EDE error codes.
+
+ * systemd-resolved and resolvectl now support RFC 9460 SVCB and HTTPS
+ 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:
+
+ * 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
+ cryptographic algorithms it only supports — does not actually provide
+ the security benefits it's supposed to provide. Given that the rest
+ 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.
+
+ * The pcrlock policy is saved in an unencrypted credential file
+ "pcrlock.<entry-token>.cred" under XBOOTLDR/ESP in the
+ /loader/credentials/ directory. It will be picked up at boot by
+ systemd-stub and passed to the initrd, where it can be used to unlock
+ the root file system.
+
+ * kernel-install gained support for --root= for the 'list' verb.
+
+ * 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.
+
+ * bootctl now provides a Varlink interface and can be run as a daemon
+ via a template unit.
+
+ * ukify gained support for signing of PCR signatures via OpenSSL's
+ engines and providers.
+
+ * ukify now supports zboot kernels.
+
+ Command-line tools:
+
+ * systemd-run is now a multi-call binary. When invoked as 'uid0', 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
+ 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.
+
+ * systemd-run gained a new option '--ignore-failure' to suppress
+ command failures.
+
+ * systemd-creds gained new options --user/--uid=.
+
+ * 'systemctl edit --stdin' allows creation of unit files and drop-ins
+ with contents fed via standard input. This is useful when creating
+ configuration programmatically; the tool takes care of figuring out
+ the file name, creating any directories, and reloading the manager
+ afterwards.
+
+ * 'systemctl disable --now' and 'systemctl mask --now' now work
+ correctly with template units.
+
+ * 'systemd-analyze architectures' lists known CPU architectures.
+
+ * 'systemd-analyze --json=…' is supported for 'architectures',
+ 'capability', 'exit-status'.
+
+ * 'systemd-tmpfiles --purge' will purge (remove) all files and
+ directories created via tmpfiles.d configuration.
+
+ * systemd-id128 gained new options --no-pager, --no-legend, and
+ -j/--json=.
+
+ * hostnamectl gained '-j' as shortcut for '--json=pretty' or
+ '--json=short'.
+
+ * loginctl now supports -j/--json=.
+
+ * resolvectl now supports -j/--json= for --type=.
+
+ * 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
+ kernel binary for direct kernel boot, a new --initrd= option to
+ specify an initrd for direct kernel boot, a new -D/--directory option
+ to use a plain directory as the root file system, a new
+ --private-users option similar to the one in systemd-nspawn, new
+ options --bind= and --bind-ro= to bind part of the host's file system
+ hierarchy into the guest, a new --extra-drive= option to attach
+ additional storage, and -n/--network-tap/--network-user-mode to
+ configure networking.
+
+ * 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.
+
+ * 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-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.
+
+ * systemd-dissect gained a new --make-archive-option to generate an
+ archive file from a disk image.
+
+ * systemd-repart gained new options --generate-fstab= and
+ --generate-crypttab= to write the fstab and crypttab files.
+
+ * systemd-repart gained 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.
+
+ Libraries:
+
+ * 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.
+
+ * RPM macro %_kernel_install_dir has been added with the path
+ to the directory for kernel-install plugins.
+
+ Other:
+
+ * 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.
+
+ 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.
+
+ 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.
+
+ * 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).
+
+ * Core dumps are now retained for two weeks by default.
+
+ * systemd-cryptsetup gained support for crypttab option
+ link-volume-key= to enter the volume key into the kernel keyring when
+ the volume is opened.
+
+ * The remaining documentation that was on
+ https://freedesktop.org/wiki/Software/systemd/ has been moved to
+ https://systemd.io.
+
CHANGES WITH 255:
Features:
+* insert the new pidfs inode number as a third field into PidRef, so that
+ PidRef are reasonably serializable without having to pass around fds.
+
+* systemd-analyze smbios11 to dump smbios type 11 vendor strings
+
* move documentation about our common env vars (SYSTEMD_LOG_LEVEL,
SYSTEMD_PAGER, …) into a man page of its own, and just link it from our
various man pages that so far embed the whole list again and again, in an
speed than SHA256 on 64bit archs (since based on 64bit words unlike SHA256
which uses 32bit words).
-* send out sd_notify() from PID 1 when we determined hostname and machine ID
-
-* send out sd_notify() from PID 1 whenever we reach a target unit. Then
- introduce ssh.target or so. And in vmspawn/nspawn wait for that as indication
- whether/when SSH is available. Similar for D-Bus (but just use sockets.target for that)
+* In vmspawn/nspawn/machined wait for X_SYSTEMD_UNIT_ACTIVE=ssh-active.target
+ and X_SYSTEMD_SIGNAL_LEVEL=2 as indication whether/when SSH and the POSIX
+ signals are available. Similar for D-Bus (but just use sockets.target for
+ that). Report as property for the machine.
* teach nspawn/machined a new bus call/verb that gets you a
shell in containers that have no sensible pid1, via joining the container,
SOURCE_DATE_EPOCH (maybe even under that name?). Would then be used to
initialize the timestamp logic of ConditionNeedsUpdate=.
-* ptyfwd: look for window title ANSI sequences and insert colored dot in front
- of it while passing it through, to indicate whether we are in privileged, VM,
- container terminal sessions.
-
* nspawn/vmspawn/pid1: add ability to easily insert fully booted VMs/FOSC into
shell pipelines, i.e. add easy to use switch that turns off console status
output, and generates the right credentials for systemd-run-generator so that
systemd.import_encrypted_creds=foobar.waldo,tmpfiles.extra to protect locked
down kernels from credentials generated on the host with a weak kernel
+* Merge systemd-creds options --uid= (which accepts user names) and --user.
+
* Add support for extra verity configuration options to systemd-repart (FEC,
hash type, etc)
* systemd-analyze netif that explains predictable interface (or networkctl)
+* Figure out naming of verbs in systemd-analyze: we have (singular) capability,
+ exit-status, but (plural) filesystems, architectures.
+
* Add service setting to run a service within the specified VRF. i.e. do the
equivalent of "ip vrf exec".
- allow Name= to be specified repeatedly in the [Match] section. Maybe also
support Name=foo*|bar*|baz ?
- whenever uplink info changes, make DHCP server send out FORCERENEW
+ - figure out spelling: NamespaceId vs. NamespaceNSID
* in networkd, when matching device types, fix up DEVTYPE rubbish the kernel passes to us
# The Container Interface
Also consult [Writing Virtual Machine or Container
-Managers](https://www.freedesktop.org/wiki/Software/systemd/writing-vm-managers).
+Managers](https://systemd.io/WRITING_VM_AND_CONTAINER_MANAGERS).
systemd has a number of interfaces for interacting with container managers,
when systemd is used inside of an OS container. If you work on a container
variable's name you may only specify ptys, and not other types of ttys. Also
you need to specify the pty itself, a symlink will not suffice. This is
implemented in
- [systemd-getty-generator(8)](https://www.freedesktop.org/software/systemd/man/systemd-getty-generator.html).
+ [systemd-getty-generator(8)](https://www.freedesktop.org/software/systemd/man/latest/systemd-getty-generator.html).
Note that this variable should not include the pty that `/dev/console` maps
to if it maps to one (see below). Example: if the container receives
`container_ttys=pts/7 pts/8 pts/14` it will spawn three additional login
running the container manager, if this is considered desirable, please parse
the host's `/etc/os-release` and set a `$container_host_<key>=<VALUE>`
environment variable for the ID fields described by the [os-release
- interface](https://www.freedesktop.org/software/systemd/man/os-release.html), eg:
+ interface](https://www.freedesktop.org/software/systemd/man/latest/os-release.html), eg:
`$container_host_id=debian`
`$container_host_build_id=2020-06-15`
`$container_host_variant_id=server`
issuing `journalctl -m`. The container machine ID can be determined from
`/etc/machine-id` in the container.
-3. If the container manager wants to cleanly shutdown the container, it might
+3. If the container manager wants to cleanly shut down the container, it might
be a good idea to send `SIGRTMIN+3` to its init process. systemd will then
do a clean shutdown. Note however, that since only systemd understands
- `SIGRTMIN+3` like this, this might confuse other init systems.
+ `SIGRTMIN+3` like this, this might confuse other init systems. A container
+ manager may implement the `$NOTIFY_SOCKET` protocol mentioned below in which
+ case it will receive a notification message `X_SYSTEMD_SIGNALS_LEVEL=2` that
+ indicates if and when these additional signal handlers are installed. If
+ these signals are sent to the container's PID 1 before this notification
+ message is sent they might not be handled correctly yet.
4. To support [Socket Activated
Containers](https://0pointer.de/blog/projects/socket-activated-containers.html)
unit they created for their container. That's private property of systemd,
and no other code should modify it.
-6. systemd running inside the container can report when boot-up is complete
- using the usual `sd_notify()` protocol that is also used when a service
- wants to tell the service manager about readiness. A container manager can
- set the `$NOTIFY_SOCKET` environment variable to a suitable socket path to
- make use of this functionality. (Also see information about
- `/run/host/notify` below.)
+6. systemd running inside the container can report when boot-up is complete,
+ boot progress and functionality as well as various other bits of system
+ information using the `sd_notify()` protocol that is also used when a
+ service wants to tell the service manager about readiness. A container
+ manager can set the `$NOTIFY_SOCKET` environment variable to a suitable
+ socket path to make use of this functionality. (Also see information about
+ `/run/host/notify` below, as well as the Readiness Protocol section on
+ [systemd(1)](https://www.freedesktop.org/software/systemd/man/latest/systemd.html)
## Networking
--- /dev/null
+---
+title: VM Interface
+category: Interfaces
+layout: default
+SPDX-License-Identifier: LGPL-2.1-or-later
+---
+
+# The VM Interface
+
+Also consult [Writing Virtual Machine or Container
+Managers](https://systemd.io/WRITING_VM_AND_CONTAINER_MANAGERS).
+
+systemd has a number of interfaces for interacting with virtual machine
+managers, when systemd is used inside of a VM. If you work on a VM manager,
+please consider supporting the following interfaces.
+
+1. systemd supports passing immutable binary data blobs with limited size and
+ restricted access to services via the `ImportCredential=`, `LoadCredential=`
+ and `SetCredential=` settings. These credentials may be passed into a system
+ via SMBIOS Type 11 vendor strings, see
+ [systemd(1)](https://www.freedesktop.org/software/systemd/man/latest/systemd.html)
+ for details. This concept may be used to flexibly configure various facets
+ ot the guest system. See
+ [systemd.system-credentials(7)](https://www.freedesktop.org/software/systemd/man/latest/systemd.system-credentials.html)
+ for a list of system credentials implemented by various systemd components.
+
+2. Readiness, information about various system properties and functionality, as
+ well as progress of boot may be reported by systemd to a machine manager via
+ the `sd_notify()` protocol via `AF_VSOCK` sockets. The address of this
+ socket may be configured via the `vmm.notify_socket` system credential. See
+ [systemd(1)](https://www.freedesktop.org/software/systemd/man/latest/systemd.html).
+
+3. The
+ [systemd-ssh-generator(8)](https://www.freedesktop.org/software/systemd/man/latest/systemd-ssh-generator.html)
+ functionality will automatically bind SSH login functionality to `AF_VSOCK`
+ port 22, if the system runs in a VM.
+
+4. If not initialized yet the system's
+ [machine-id(5)](https://www.freedesktop.org/software/systemd/man/latest/machine-id.html)
+ is automatically set to the SMBIOS product UUID if available and invocation
+ in an VM environment is detected.
+
+5. The
+ [`systemd-boot(7)`](https://www.freedesktop.org/software/systemd/man/latest/systemd-boot.html)
+ and
+ [`systemd-stub(7)`](https://www.freedesktop.org/software/systemd/man/latest/systemd-stub.html)
+ components support two SMBIOS Type 11 vendor strings that may be used to
+ extend the kernel command line of booted Linux environments:
+ `io.systemd.stub.kernel-cmdline-extra=` and
+ `io.systemd.boot.kernel-cmdline-extra=`.
+
+Also see
+[smbios-type-11(7)](https://www.freedesktop.org/software/systemd/man/latest/smbios-type-11.html)
+for a list of supported SMBIOS Type 11 vendor strings.
SPDX-License-Identifier: LGPL-2.1-or-later
---
-
# Writing VM and Container Managers
_Or: How to hook up your favorite VM or container manager with systemd_
-Nomenclature: a _Virtual Machine_ shall refer to a system running on virtualized hardware consisting of a full OS with its own kernel. A _Container_ shall refer to a system running on the same shared kernel of the host, but running a mostly complete OS with its own init system. Both kinds of virtualized systems shall collectively be called "machines".
+Nomenclature: a _Virtual Machine_ shall refer to a system running on
+virtualized hardware consisting of a full OS with its own kernel. A _Container_
+shall refer to a system running on the same shared kernel of the host, but
+running a mostly complete OS with its own init system. Both kinds of
+virtualized systems shall collectively be called "machines".
-systemd provides a number of integration points with virtual machine and container managers, such as libvirt, LXC or systemd-nspawn. On one hand there are integration points of the VM/container manager towards the host OS it is running on, and on the other there integration points for container managers towards the guest OS it is managing.
+systemd provides a number of integration points with virtual machine and
+container managers, such as libvirt, LXC or systemd-nspawn. On one hand there
+are integration points of the VM/container manager towards the host OS it is
+running on, and on the other there integration points for container managers
+towards the guest OS it is managing.
-Note that this document does not cover lightweight containers for the purpose of application sandboxes, i.e. containers that do _not_ run a init system of their own.
+Note that this document does not cover lightweight containers for the purpose
+of application sandboxes, i.e. containers that do _not_ run a init system of
+their own.
## Host OS Integration
-All virtual machines and containers should be registered with the [machined](http://www.freedesktop.org/wiki/Software/systemd/machined) mini service that is part of systemd. This provides integration into the core OS at various points. For example, tools like ps, cgls, gnome-system-manager use this registration information to show machine information for running processes, as each of the VM's/container's processes can reliably attributed to a registered machine. The various systemd tools (like systemctl, journalctl, loginctl, systemd-run, ...) all support a -M switch that operates on machines registered with machined. "machinectl" may be used to execute operations on any such machine. When a machine is registered via machined its processes will automatically be placed in a systemd scope unit (that is located in the machines.slice slice) and thus appear in "systemctl" and similar commands. The scope unit name is based on the machine meta information passed to machined at registration.
-
-For more details on the APIs provided by machine consult [the bus API interface documentation](http://www.freedesktop.org/wiki/Software/systemd/machined).
+All virtual machines and containers should be registered with the
+[systemd-machined(8)](https://www.freedesktop.org/software/systemd/man/latest/systemd-machined.service.html)
+mini service that is part of systemd. This provides integration into the core
+OS at various points. For example, tools like ps, cgls, gnome-system-manager
+use this registration information to show machine information for running
+processes, as each of the VM's/container's processes can reliably attributed to
+a registered machine. The various systemd tools (like systemctl, journalctl,
+loginctl, systemd-run, ...) all support a -M switch that operates on machines
+registered with machined. "machinectl" may be used to execute operations on any
+such machine. When a machine is registered via machined its processes will
+automatically be placed in a systemd scope unit (that is located in the
+machines.slice slice) and thus appear in "systemctl" and similar commands. The
+scope unit name is based on the machine meta information passed to machined at
+registration.
+
+For more details on the APIs provided by machine consult [the bus API interface
+documentation](https://www.freedesktop.org/software/systemd/man/latest/org.freedesktop.machine1.html).
## Guest OS Integration
-As container virtualization is much less comprehensive, and the guest is less isolated from the host, there are a number of interfaces defined how the container manager can set up the environment for systemd running inside a container. These Interfaces are documented in [Container Interface of systemd](http://www.freedesktop.org/wiki/Software/systemd/ContainerInterface).
-
-VM virtualization is more comprehensive and fewer integration APIs are available. In fact there's only one: a VM manager may initialize the SMBIOS DMI field "Product UUUID" to a UUID uniquely identifying this virtual machine instance. This is read in the guest via /sys/class/dmi/id/product_uuid, and used as configuration source for /etc/machine-id if in the guest, if that file is not initialized yet. Note that this is currently only supported for kvm hosts, but may be extended to other managers as well.
+A number of interfaces are defined that permit a machine or container manager
+to set provide integration points with the payload/guest system. These
+interfaces are documented in [Container Interface of
+systemd](https://systemd.io/CONTAINER_INTERFACE) and [VM Interface of
+systemd](https://systemd.io/VM_INTERFACE).
'sd_journal_seek_realtime_usec',
'sd_journal_seek_tail'],
''],
- ['sd_journal_stream_fd', '3', [], ''],
+ ['sd_journal_stream_fd', '3', ['sd_journal_stream_fd_with_namespace'], ''],
['sd_listen_fds',
'3',
['SD_LISTEN_FDS_START', 'sd_listen_fds_with_names'],
<refnamediv>
<refname>sd_journal_stream_fd</refname>
+ <refname>sd_journal_stream_fd_with_namespace</refname>
<refpurpose>Create log stream file descriptor to the journal</refpurpose>
</refnamediv>
<paramdef>int <parameter>level_prefix</parameter></paramdef>
</funcprototype>
+ <funcprototype>
+ <funcdef>int <function>sd_journal_stream_fd_with_namespace</function></funcdef>
+ <paramdef>const char *<parameter>name_space</parameter></paramdef>
+ <paramdef>const char *<parameter>identifier</parameter></paramdef>
+ <paramdef>int <parameter>priority</parameter></paramdef>
+ <paramdef>int <parameter>level_prefix</parameter></paramdef>
+ </funcprototype>
+
</funcsynopsis>
</refsynopsisdiv>
<refsect1>
<title>Description</title>
- <para><function>sd_journal_stream_fd()</function> may be used to
- create a log stream file descriptor. Log messages written to this
- file descriptor as simple newline-separated text strings are
- written to the journal. This file descriptor can be used
- internally by applications or be made standard output or standard
- error of other processes executed.</para>
+ <para><function>sd_journal_stream_fd()</function> may be used to create a log stream file descriptor.
+ Log messages written to this file descriptor as simple newline-separated text strings are written
+ to the journal. This file descriptor can be used internally by applications or be made standard output
+ or standard error of other processes executed.</para>
- <para><function>sd_journal_stream_fd()</function> takes a short
- program identifier string as first argument, which will be written
- to the journal as SYSLOG_IDENTIFIER= field for each log entry
+ <para><function>sd_journal_stream_fd()</function> takes a short program identifier string as
+ first argument, which will be written to the journal as SYSLOG_IDENTIFIER= field for each log entry
(see
<citerefentry><refentrytitle>systemd.journal-fields</refentrytitle><manvolnum>7</manvolnum></citerefentry>
- for more information). The second argument shall be the default
- priority level for all messages. The priority level is one of
+ for more information). The second argument shall be the default priority level for all messages.
+ The priority level is one of
<constant>LOG_EMERG</constant>, <constant>LOG_ALERT</constant>,
<constant>LOG_CRIT</constant>, <constant>LOG_ERR</constant>,
<constant>LOG_WARNING</constant>, <constant>LOG_NOTICE</constant>,
<citerefentry><refentrytitle>sd-daemon</refentrytitle><manvolnum>3</manvolnum></citerefentry>
for more information.</para>
- <para>It is recommended that applications log UTF-8 messages only
- with this API, but this is not enforced.</para>
+ <para><function>sd_journal_stream_fd_with_namespace()</function> is similar to
+ <function>sd_journal_stream_fd()</function>, but takes an additional <parameter>name_space</parameter> parameter
+ that specifies which journal namespace to operate on. If specified as <constant>NULL</constant> the call
+ is identical to <function>sd_journal_stream_fd()</function>. For details about journal namespaces, see
+ <citerefentry><refentrytitle>systemd-journald.service</refentrytitle><manvolnum>8</manvolnum></citerefentry>.</para>
+
+ <para>It is recommended that applications log UTF-8 messages only with this API, but this is not enforced.</para>
- <para>Each invocation of <function>sd_journal_stream_fd()</function> allocates a new log stream file descriptor,
- that is not shared with prior or later invocations. The file descriptor is write-only (its reading direction is
- shut down), and <constant>O_NONBLOCK</constant> is turned off initially.</para>
+ <para>Each invocation of these functions allocates a new log stream file descriptor,
+ that is not shared with prior or later invocations. The file descriptor is write-only (its reading direction
+ is shut down), and <constant>O_NONBLOCK</constant> is turned off initially.</para>
</refsect1>
<refsect1>
<title>Return Value</title>
- <para>The call returns a valid write-only file descriptor on
- success or a negative errno-style error code.</para>
+ <para>The call returns a valid write-only file descriptor on success or a negative errno-style error code.</para>
</refsect1>
<refsect1>
<title>Signal safety</title>
- <para><function>sd_journal_stream_fd()</function> is "async signal safe" in the meaning of <citerefentry
+ <para><function>sd_journal_stream_fd()</function> and <function>sd_journal_stream_fd_with_namespace()</function>
+ are "async signal safe" in the meaning of <citerefentry
project='man-pages'><refentrytitle>signal-safety</refentrytitle><manvolnum>7</manvolnum></citerefentry>.
</para>
</refsect1>
<refsect1>
<title>History</title>
<para><function>sd_journal_stream_fd()</function> was added in version 187.</para>
+ <para><function>sd_journal_stream_fd_with_namespace()</function> was added in version 256.</para>
</refsect1>
<refsect1>
</variablelist>
<para>The notification messages sent by services are interpreted by the service manager. Unknown
- assignments may be logged, but are otherwise ignored. Thus, it is not useful to send assignments which
- are not in this list. The service manager also sends some messages to <emphasis>its</emphasis>
- notification socket, which are then consumed by the machine or container manager.</para>
+ assignments are ignored. Thus, it is is safe (but often without effect) to send assignments which are not
+ in this list. The protocol is extensible, but care should be taken to ensure private extensions are
+ recognizable as such. Specifically, it is recommend to prefix them with <literal>X_</literal> followed by
+ some namespace identifier. The service manager also sends some messages to <emphasis>its</emphasis>
+ notification socket, which may then consumed by a supervising machine or container manager further up the
+ stack. The service manager sends a number of extension fields, for example
+ <varname>X_SYSTEMD_UNIT_ACTIVE=</varname>, for details see
+ <citerefentry><refentrytitle>systemd</refentrytitle><manvolnum>1</manvolnum></citerefentry>.</para>
</refsect1>
<refsect1>
boot counting is used.</para>
<para>Internally, the service operates based on the <varname>LoaderBootCountPath</varname> EFI variable (of the
- vendor UUID <constant>4a67b082-0a4c-41cf-b6c7-440b29bb8c4</constant>), which is passed from the boot loader to the
+ vendor UUID <constant>4a67b082-0a4c-41cf-b6c7-440b29bb8c4f</constant>), which is passed from the boot loader to the
OS. It contains a file system path (relative to the EFI system partition) of the <ulink
url="https://uapi-group.org/specifications/specs/boot_loader_specification">Boot Loader Specification</ulink> compliant boot loader entry
file or unified kernel image file that was used to boot up the
boolean argument.</para></listitem>
</varlistentry>
+ <varlistentry>
+ <term><option>--namespace=</option></term>
+
+ <listitem><para>Specifies the journal namespace to which the standard IO should be connected.
+ For details about journal namespaces, see
+ <citerefentry><refentrytitle>systemd-journald.service</refentrytitle><manvolnum>8</manvolnum></citerefentry>.
+ </para>
+
+ <xi:include href="version-info.xml" xpointer="v256"/></listitem>
+ </varlistentry>
+
</variablelist>
</refsect1>
<refsect1>
<title>Exit status</title>
- <para>On success, 0 is returned, a non-zero failure code
- otherwise.</para>
+ <para>On success, 0 is returned, a non-zero failure code otherwise.</para>
</refsect1>
<refsect1>
also have to be configured manually for correct ordering and conflicting. For example:</para>
<programlisting>[Unit]
-Description=My surviving service
+Description=My Surviving Service
SurviveFinalKillSignal=yes
IgnoreOnIsolate=yes
DefaultDependencies=no
After=basic.target
-Conflicts=reboot.target
-Before=reboot.target
-Conflicts=kexec.target
-Before=kexec.target
-Conflicts=poweroff.target
-Before=poweroff.target
-Conflicts=halt.target
-Before=halt.target
-Conflicts=rescue.target
-Before=rescue.target
-Conflicts=emergency.target
-Before=emergency.target
+Conflicts=reboot.target kexec.target poweroff.target halt.target rescue.target emergency.target
+Before=reboot.target kexec.target poweroff.target halt.target rescue.target emergency.target
[Service]
Type=oneshot
-ExecStart=sleep infinity
- </programlisting>
+ExecStart=sleep infinity</programlisting>
</listitem>
<listitem><para>File system mounts may remain mounted during the transition, and complex storage
<row>
<entry><literal>:</literal></entry>
- <entry>If the executable path is prefixed with <literal>:</literal>, environment variable substitution (as described by the "Command Lines" section below) is not applied.</entry>
+ <entry>If the executable path is prefixed with <literal>:</literal>, environment variable substitution (as described below this table) is not applied.</entry>
</row>
<row>
<filename>sockets.target</filename>,
<filename>soft-reboot.target</filename>,
<filename>sound.target</filename>,
+ <filename>ssh-access.target</filename>,
<filename>storage-target-mode.target</filename>,
<filename>suspend.target</filename>,
<filename>swap.target</filename>,
the <literal>$portmap</literal> facility.</para>
</listitem>
</varlistentry>
+ <varlistentry>
+ <term><filename>ssh-access.target</filename></term>
+ <listitem>
+ <para>Service and socket units that provide remote SSH secure shell access to the local system
+ should pull in this unit and order themselves before this unit. It's supposed to act as a
+ milestone indicating if and when SSH access into the system is available. It should only become
+ active when an SSH port is bound for remote clients (i.e. if SSH is used as a local privilege
+ escalation mechanism, it should <emphasis>not</emphasis> involve this target unit), regardless of
+ the protocol choices, i.e. regardless if IPv4, IPv6 or <constant>AF_VSOCK</constant> is
+ used.</para>
+ <xi:include href="version-info.xml" xpointer="v256"/>
+ </listitem>
+ </varlistentry>
<varlistentry>
<term><filename>time-set.target</filename></term>
<listitem>
<refsect1>
<title>Signals</title>
+ <para>The service listens to various UNIX process signals that can be used to request various actions
+ asynchronously. The signal handling is enabled very early during boot, before any further processes are
+ invoked. However, a supervising container manager or similar that intends to request these operations via
+ this mechanism must take into consideration that this functionality is not available during the earliest
+ initialization phase. An <function>sd_notify()</function> notification message carrying the
+ <varname>X_SYSTEMD_SIGNALS_LEVEL=2</varname> field is emitted once the signal handlers are enabled, see
+ below. This may be used to schedule submission of these signals correctly.</para>
+
<variablelist>
<varlistentry>
<term><constant>SIGTERM</constant></term>
<varlistentry>
<term><varname>$NOTIFY_SOCKET</varname></term>
- <listitem><para>Set by systemd for supervised processes for
- status and start-up completion notification. See
- <citerefentry><refentrytitle>sd_notify</refentrytitle><manvolnum>3</manvolnum></citerefentry>
- for more information.</para></listitem>
+ <listitem><para>Set by service manager for its services for status and readiness notifications. Also
+ consumed by service manager for notifying supervising container managers or service managers up the
+ stack about its own progress. See
+ <citerefentry><refentrytitle>sd_notify</refentrytitle><manvolnum>3</manvolnum></citerefentry> and the
+ relevant section below for more information.</para></listitem>
</varlistentry>
</variablelist>
</refsect1>
<refsect1>
- <title>System credentials</title>
+ <title>System Credentials</title>
<para>During initialization the service manager will import credentials from various sources into the
system's set of credentials, which can then be propagated into services and consumed by
<term><varname>vmm.notify_socket</varname></term>
<listitem>
<para>Contains a <constant>AF_VSOCK</constant> or <constant>AF_UNIX</constant> address where to
- send a <constant>READY=1</constant> notification datagram when the system has finished booting. See
- <citerefentry><refentrytitle>sd_notify</refentrytitle><manvolnum>3</manvolnum></citerefentry> for
- more information. Note that in case the hypervisor does not support <constant>SOCK_DGRAM</constant>
- over <constant>AF_VSOCK</constant>, <constant>SOCK_SEQPACKET</constant> will be tried instead. The
- credential payload for <constant>AF_VSOCK</constant> should be in the form
+ send a <constant>READY=1</constant> notification message when the service manager has completed
+ booting. See
+ <citerefentry><refentrytitle>sd_notify</refentrytitle><manvolnum>3</manvolnum></citerefentry> and
+ the next section for more information. Note that in case the hypervisor does not support
+ <constant>SOCK_DGRAM</constant> over <constant>AF_VSOCK</constant>,
+ <constant>SOCK_SEQPACKET</constant> will be tried instead. The credential payload for
+ <constant>AF_VSOCK</constant> should be a string in the form
<literal>vsock:CID:PORT</literal>.</para>
- <para>This feature is useful for hypervisors/VMMs or other processes on the host to receive a
+ <para>This feature is useful for machine managers or other processes on the host to receive a
notification via VSOCK when a virtual machine has finished booting.</para>
<xi:include href="version-info.xml" xpointer="v254"/>
</listitem>
</varlistentry>
</variablelist>
+
+ <para>For a list of system credentials various other components of systemd consume, see
+ <citerefentry><refentrytitle>systemd.system-credentials</refentrytitle><manvolnum>7</manvolnum></citerefentry>.</para>
+ </refsect1>
+
+ <refsect1>
+ <title>Readiness Protocol</title>
+
+ <para>The service manager implements a readiness notification protocol both between the manager and its
+ services (i.e. down the stack), and between the manager and a potential supervisor further up the stack
+ (the latter could be a machine or container manager, or in case of a per-user service manager the system
+ service manager instance). The basic protocol (and the suggested API for it) is described in
+ <citerefentry><refentrytitle>sd_notify</refentrytitle><manvolnum>3</manvolnum></citerefentry>.</para>
+
+ <para>The notification socket the service manager (including PID 1) uses for reporting readiness to its
+ own supervisor is set via the usual <varname>$NOTIFY_SOCKET</varname> environment variable (see
+ above). Since this is directly settable only for container managers and for the per-user instance of the
+ service manager, an additional mechanism to configure this is available, in particular intended for use
+ in VM environments: the <varname>vmm.notify_socket</varname> system credential (see above) may be set to
+ a suitable socket (typically an <constant>AF_VSOCK</constant> one) via SMBIOS Type 11 vendor strings. For
+ details see above.</para>
+
+ <para>The notification protocol from the service manager up the stack towards a supervisor supports a
+ number of extension fields that allow a supervisor to learn about specific properties of the system and
+ track its boot progress. Specifically the following fields are sent:</para>
+
+ <itemizedlist>
+ <listitem><para>An <varname>X_SYSTEMD_HOSTNAME=…</varname> message will be sent out once the initial
+ hostname for the system has been determined. Note that during later runtime the hostname might be
+ changed again programmatically, and (currently) no further notifications are sent out in that case.</para>
+
+ <xi:include href="version-info.xml" xpointer="v256"/></listitem>
+
+ <listitem><para>An <varname>X_SYSTEMD_MACHINE_ID=…</varname> message will be sent out once the machine
+ ID of the system has been determined. See
+ <citerefentry><refentrytitle>machine-id</refentrytitle><manvolnum>5</manvolnum></citerefentry> for
+ details.</para>
+
+ <xi:include href="version-info.xml" xpointer="v256"/></listitem>
+
+ <listitem><para>An <varname>X_SYSTEMD_SIGNALS_LEVEL=…</varname> message will be sent out once the
+ service manager installed the various UNIX process signal handlers described above. The field's value
+ is an unsigned integer formatted as decimal string, and indicates the supported UNIX process signal
+ feature level of the service manager. Currently, only a single feature level is defined:</para>
+
+ <itemizedlist>
+ <listitem><para><varname>X_SYSTEMD_SIGNALS_LEVEL=2</varname> covers the various UNIX process signals
+ documented above – which are a superset of those supported by the historical SysV init
+ system.</para></listitem>
+ </itemizedlist>
+
+ <para>Signals sent to PID 1 before this message is sent might not be handled correctly yet. A consumer
+ of these messages should parse the value as an unsigned integer indication the level of support. For
+ now only the mentioned level 2 is defined, but later on additional levels might be defined with higher
+ integers, that will implement a superset of the currently defined behaviour.</para>
+
+ <xi:include href="version-info.xml" xpointer="v256"/></listitem>
+
+ <listitem><para><varname>X_SYSTEMD_UNIT_ACTIVE=…</varname> and
+ <varname>X_SYSTEMD_UNIT_INACTIVE=…</varname> messages will be sent out for each target unit as it
+ becomes active or stops being active. This is useful to track boot progress and functionality. For
+ example, once the <filename>ssh-access.target</filename> unit is reported started SSH access is
+ typically available, see
+ <citerefentry><refentrytitle>systemd.special</refentrytitle><manvolnum>7</manvolnum></citerefentry> for
+ details.</para>
+
+ <xi:include href="version-info.xml" xpointer="v256"/></listitem>
+ </itemizedlist>
+
+ <para>Note that these extension fields are sent in addition to the regular <literal>READY=1</literal> and
+ <literal>RELOADING=1</literal> notifications.</para>
</refsect1>
<refsect1>
+pkg/debian
src/basic/parse-util.c
src/boot/efi/addon.c
src/boot/efi/boot.c
local -A OPTS=(
[STANDALONE]='-h --help --version'
- [ARG]='-t --identifier -p --priority --stderr-priority --level-prefix'
+ [ARG]='-t --identifier -p --priority --stderr-priority --level-prefix --namespace'
)
_init_completion || return
--level-prefix)
comps='yes no'
;;
+ --namespace)
+ comps=$(journalctl --list-namespaces --output=cat 2>/dev/null)
+ ;;
esac
COMPREPLY=( $(compgen -W '$comps' -- "$cur") )
return 0
{-t+,--identifier=}'[Set syslog identifier.]:syslog identifier:' \
{-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)
return TAKE_FD(fd);
}
+
+int link_fd(int fd, int newdirfd, const char *newpath) {
+ int r;
+
+ assert(fd >= 0);
+ assert(newdirfd >= 0 || newdirfd == AT_FDCWD);
+ assert(newpath);
+
+ /* Try linking via /proc/self/fd/ first. */
+ r = RET_NERRNO(linkat(AT_FDCWD, FORMAT_PROC_FD_PATH(fd), newdirfd, newpath, AT_SYMLINK_FOLLOW));
+ if (r != -ENOENT)
+ return r;
+
+ /* Fall back to symlinking via AT_EMPTY_PATH as fallback (this requires CAP_DAC_READ_SEARCH and a
+ * more recent kernel, but does not require /proc/ mounted) */
+ if (proc_mounted() != 0)
+ return r;
+
+ return RET_NERRNO(linkat(fd, "", newdirfd, newpath, AT_EMPTY_PATH));
+}
+
+int linkat_replace(int olddirfd, const char *oldpath, int newdirfd, const char *newpath) {
+ _cleanup_close_ int old_fd = -EBADF;
+ int r;
+
+ assert(olddirfd >= 0 || olddirfd == AT_FDCWD);
+ assert(newdirfd >= 0 || newdirfd == AT_FDCWD);
+ assert(!isempty(newpath)); /* source path is optional, but the target path is not */
+
+ /* Like linkat() but replaces the target if needed. Is a NOP if source and target already share the
+ * same inode. */
+
+ if (olddirfd == AT_FDCWD && isempty(oldpath)) /* Refuse operating on the cwd (which is a dir, and dirs can't be hardlinked) */
+ return -EISDIR;
+
+ if (path_implies_directory(oldpath)) /* Refuse these definite directories early */
+ return -EISDIR;
+
+ if (path_implies_directory(newpath))
+ return -EISDIR;
+
+ /* First, try to link this directly */
+ if (oldpath)
+ r = RET_NERRNO(linkat(olddirfd, oldpath, newdirfd, newpath, 0));
+ else
+ r = link_fd(olddirfd, newdirfd, newpath);
+ if (r >= 0)
+ return 0;
+ if (r != -EEXIST)
+ return r;
+
+ old_fd = xopenat(olddirfd, oldpath, O_PATH|O_CLOEXEC);
+ if (old_fd < 0)
+ return old_fd;
+
+ struct stat old_st;
+ if (fstat(old_fd, &old_st) < 0)
+ return -errno;
+
+ if (S_ISDIR(old_st.st_mode)) /* Don't bother if we are operating on a directory */
+ return -EISDIR;
+
+ struct stat new_st;
+ if (fstatat(newdirfd, newpath, &new_st, AT_SYMLINK_NOFOLLOW) < 0)
+ return -errno;
+
+ if (S_ISDIR(new_st.st_mode)) /* Refuse replacing directories */
+ return -EEXIST;
+
+ if (stat_inode_same(&old_st, &new_st)) /* Already the same inode? Then shortcut this */
+ return 0;
+
+ _cleanup_free_ char *tmp_path = NULL;
+ r = tempfn_random(newpath, /* extra= */ NULL, &tmp_path);
+ if (r < 0)
+ return r;
+
+ r = link_fd(old_fd, newdirfd, tmp_path);
+ if (r < 0) {
+ if (!ERRNO_IS_PRIVILEGE(r))
+ return r;
+
+ /* If that didn't work due to permissions then go via the path of the dentry */
+ r = RET_NERRNO(linkat(olddirfd, oldpath, newdirfd, tmp_path, 0));
+ if (r < 0)
+ return r;
+ }
+
+ r = RET_NERRNO(renameat(newdirfd, tmp_path, newdirfd, newpath));
+ if (r < 0) {
+ (void) unlinkat(newdirfd, tmp_path, /* flags= */ 0);
+ return r;
+ }
+
+ return 0;
+}
static inline int xopenat_lock(int dir_fd, const char *path, int open_flags, LockType locktype, int operation) {
return xopenat_lock_full(dir_fd, path, open_flags, 0, 0, locktype, operation);
}
+
+int link_fd(int fd, int newdirfd, const char *newpath);
+
+int linkat_replace(int olddirfd, const char *oldpath, int newdirfd, const char *newpath);
return path[2] == 0;
}
+bool path_implies_directory(const char *path) {
+
+ /* Sometimes, if we look at a path we already know it must refer to a directory, because it is
+ * suffixed with a slash, or its last component is "." or ".." */
+
+ if (!path)
+ return false;
+
+ if (dot_or_dot_dot(path))
+ return true;
+
+ return ENDSWITH_SET(path, "/", "/.", "/..");
+}
+
bool empty_or_root(const char *path) {
/* For operations relative to some root directory, returns true if the specified root directory is
#define path_extend(x, ...) path_extend_internal(x, __VA_ARGS__, POINTER_MAX)
#define path_join(...) path_extend_internal(NULL, __VA_ARGS__, POINTER_MAX)
+static inline char* skip_leading_slash(const char *p) {
+ return skip_leading_chars(p, "/");
+}
+
typedef enum PathSimplifyFlags {
PATH_SIMPLIFY_KEEP_TRAILING_SLASH = 1 << 0,
} PathSimplifyFlags;
bool dot_or_dot_dot(const char *path);
+bool path_implies_directory(const char *path);
+
static inline const char *skip_dev_prefix(const char *p) {
const char *e;
return null_or_empty(&st);
}
-static int fd_is_read_only_fs(int fd) {
+int fd_is_read_only_fs(int fd) {
struct statvfs st;
assert(fd >= 0);
return null_or_empty_path_with_root(fn, NULL);
}
+int fd_is_read_only_fs(int fd);
int path_is_read_only_fs(const char *path);
int inode_same_at(int fda, const char *filea, int fdb, const char *fileb, int flags);
return 0;
}
-static int link_fd(int fd, int newdirfd, const char *newpath) {
- int r;
-
- assert(fd >= 0);
- assert(newdirfd >= 0 || newdirfd == AT_FDCWD);
- assert(newpath);
-
- /* Try symlinking via /proc/fd/ first. */
- r = RET_NERRNO(linkat(AT_FDCWD, FORMAT_PROC_FD_PATH(fd), newdirfd, newpath, AT_SYMLINK_FOLLOW));
- if (r != -ENOENT)
- return r;
-
- /* Fall back to symlinking via AT_EMPTY_PATH as fallback (this requires CAP_DAC_READ_SEARCH and a
- * more recent kernel, but does not require /proc/ mounted) */
- if (proc_mounted() != 0)
- return r;
-
- return RET_NERRNO(linkat(fd, "", newdirfd, newpath, AT_EMPTY_PATH));
-}
-
int link_tmpfile_at(int fd, int dir_fd, const char *path, const char *target, LinkTmpfileFlags flags) {
- _cleanup_free_ char *tmp = NULL;
int r;
assert(fd >= 0);
r = RET_NERRNO(renameat(dir_fd, path, dir_fd, target));
else
r = rename_noreplace(dir_fd, path, dir_fd, target);
- if (r < 0)
- return r;
} else {
-
- r = link_fd(fd, dir_fd, target);
- if (r != -EEXIST || !FLAGS_SET(flags, LINK_TMPFILE_REPLACE))
- return r;
-
- /* So the target already exists and we were asked to replace it. That sucks a bit, since the kernel's
- * linkat() logic does not allow that. We work-around this by linking the file to a random name
- * first, and then renaming that to the final name. This reintroduces the race O_TMPFILE kinda is
- * trying to fix, but at least the vulnerability window (i.e. where the file is linked into the file
- * system under a temporary name) is very short. */
-
- r = tempfn_random(target, NULL, &tmp);
- if (r < 0)
- return r;
-
- if (link_fd(fd, dir_fd, tmp) < 0)
- return -EEXIST; /* propagate original error */
-
- r = RET_NERRNO(renameat(dir_fd, tmp, dir_fd, target));
- if (r < 0) {
- (void) unlinkat(dir_fd, tmp, 0);
- return r;
- }
+ if (FLAGS_SET(flags, LINK_TMPFILE_REPLACE))
+ r = linkat_replace(fd, /* oldpath= */ NULL, dir_fd, target);
+ else
+ r = link_fd(fd, dir_fd, target);
}
+ if (r < 0)
+ return r;
if (FLAGS_SET(flags, LINK_TMPFILE_SYNC)) {
r = fsync_full(fd);
return 0;
}
-static const char *skip_slash(const char *path) {
- assert(path);
- assert(path[0] == '/');
-
- return path + 1;
-}
-
static int verb_status(int argc, char *argv[], void *userdata) {
_cleanup_free_ char *path = NULL, *prefix = NULL, *suffix = NULL, *good = NULL, *bad = NULL;
uint64_t left, done;
return log_error_errno(errno, "Failed to open $BOOT partition '%s': %m", *p);
}
- if (faccessat(fd, skip_slash(path), F_OK, 0) >= 0) {
+ if (faccessat(fd, skip_leading_slash(path), F_OK, 0) >= 0) {
puts("indeterminate");
return 0;
}
if (errno != ENOENT)
return log_error_errno(errno, "Failed to check if '%s' exists: %m", path);
- if (faccessat(fd, skip_slash(good), F_OK, 0) >= 0) {
+ if (faccessat(fd, skip_leading_slash(good), F_OK, 0) >= 0) {
puts("good");
return 0;
}
if (errno != ENOENT)
return log_error_errno(errno, "Failed to check if '%s' exists: %m", good);
- if (faccessat(fd, skip_slash(bad), F_OK, 0) >= 0) {
+ if (faccessat(fd, skip_leading_slash(bad), F_OK, 0) >= 0) {
puts("bad");
return 0;
}
if (fd < 0)
return log_error_errno(errno, "Failed to open $BOOT partition '%s': %m", *p);
- r = rename_noreplace(fd, skip_slash(source1), fd, skip_slash(target));
+ r = rename_noreplace(fd, skip_leading_slash(source1), fd, skip_leading_slash(target));
if (r == -EEXIST)
goto exists;
if (r == -ENOENT) {
- r = rename_noreplace(fd, skip_slash(source2), fd, skip_slash(target));
+ r = rename_noreplace(fd, skip_leading_slash(source2), fd, skip_leading_slash(target));
if (r == -EEXIST)
goto exists;
if (r == -ENOENT) {
- if (faccessat(fd, skip_slash(target), F_OK, 0) >= 0) /* Hmm, if we can't find either source file, maybe the destination already exists? */
+ if (faccessat(fd, skip_leading_slash(target), F_OK, 0) >= 0) /* Hmm, if we can't find either source file, maybe the destination already exists? */
goto exists;
if (errno != ENOENT)
log_debug("Successfully renamed '%s' to '%s'.", source1, target);
/* First, fsync() the directory these files are located in */
- r = fsync_parent_at(fd, skip_slash(target));
+ r = fsync_parent_at(fd, skip_leading_slash(target));
if (r < 0)
log_debug_errno(errno, "Failed to synchronize image directory, ignoring: %m");
#include "hexdecoct.h"
#include "io-util.h"
#include "iovec-util.h"
+#include "journal-send.h"
#include "missing_ioprio.h"
#include "missing_prctl.h"
#include "missing_securebits.h"
const char *j;
int r;
- j = log_namespace ?
- strjoina("/run/systemd/journal.", log_namespace, "/stdout") :
- "/run/systemd/journal/stdout";
+ assert(fd >= 0);
+
+ j = journal_stream_path(log_namespace);
+ if (!j)
+ return -EINVAL;
if (gid_is_valid(gid)) {
oldgid = getgid();
#endif
if (r < 0)
- (void) sd_notifyf(0, "ERRNO=%i", -r);
+ (void) sd_notifyf(/* unset_environment= */ false,
+ "ERRNO=%i", -r);
/* Try to invoke the shutdown binary unless we already failed.
* If we failed above, we want to freeze after finishing cleanup. */
/* This is primarily useful when running systemd in a VM, as it provides the user running the VM with
* a mechanism to pick up systemd's exit status in the VM. */
- (void) sd_notifyf(0, "EXIT_STATUS=%i", retval);
+ (void) sd_notifyf(/* unset_environment= */ false,
+ "EXIT_STATUS=%i", retval);
watchdog_free_device();
arg_watchdog_device = mfree(arg_watchdog_device);
strempty(status_text));
}
- sd_notifyf(false,
- "STATUS=%sUser job %s/%s running (%s / %s)...",
- job_of_n,
- ident,
- job_type_to_string(j->type),
- time, limit);
+ (void) sd_notifyf(/* unset_environment= */ false,
+ "STATUS=%sUser job %s/%s running (%s / %s)...",
+ job_of_n,
+ ident, job_type_to_string(j->type),
+ time, limit);
m->status_ready = false;
}
if (MANAGER_IS_TEST_RUN(m))
return 0;
- /* Enable that we get SIGINT on control-alt-del. In containers
- * this will fail with EPERM (older) or EINVAL (newer), so
- * ignore that. */
+ /* Enable that we get SIGINT on control-alt-del. In containers this will fail with EPERM (older) or
+ * EINVAL (newer), so ignore that. */
if (reboot(RB_DISABLE_CAD) < 0 && !IN_SET(errno, EPERM, EINVAL))
- log_warning_errno(errno, "Failed to enable ctrl-alt-del handling: %m");
+ log_warning_errno(errno, "Failed to enable ctrl-alt-del handling, ignoring: %m");
fd = open_terminal("/dev/tty0", O_RDWR|O_NOCTTY|O_CLOEXEC);
- if (fd < 0) {
- /* Support systems without virtual console */
- if (fd != -ENOENT)
- log_warning_errno(errno, "Failed to open /dev/tty0: %m");
- } else {
+ if (fd < 0)
+ /* Support systems without virtual console (ENOENT) gracefully */
+ log_full_errno(fd == -ENOENT ? LOG_DEBUG : LOG_WARNING, fd, "Failed to open /dev/tty0, ignoring: %m");
+ else {
/* Enable that we get SIGWINCH on kbrequest */
if (ioctl(fd, KDSIGACCEPT, SIGWINCH) < 0)
- log_warning_errno(errno, "Failed to enable kbrequest handling: %m");
+ log_warning_errno(errno, "Failed to enable kbrequest handling, ignoring: %m");
}
return 0;
if (r < 0)
return r;
+ /* Report to supervisor that we now process the above signals. We report this as level "2", to
+ * indicate that we support more than sysvinit's signals (of course, sysvinit never sent this
+ * message, but conceptually it makes sense to consider level "1" to be equivalent to sysvinit's
+ * signal handling). Also, by setting this to "2" people looking for this hopefully won't
+ * misunderstand this as a boolean concept. Signal level 2 shall refer to the signals PID 1
+ * understands at the time of release of systemd v256, i.e. including basic SIGRTMIN+18 handling for
+ * memory pressure and stuff. When more signals are hooked up (or more SIGRTMIN+18 multiplex
+ * operations added, this level should be increased). */
+ (void) sd_notify(/* unset_environment= */ false,
+ "X_SYSTEMD_SIGNALS_LEVEL=2");
+
if (MANAGER_IS_SYSTEM(m))
return enable_special_signals(m);
log_info("Activating special unit %s...", s);
- sd_notifyf(false,
- "STATUS=Activating special unit %s...", s);
+ (void) sd_notifyf(/* unset_environment= */ false,
+ "STATUS=Activating special unit %s...", s);
m->status_ready = false;
}
const char *msg;
int audit_fd, r;
+ assert(m);
+ assert(u);
+
if (!MANAGER_IS_SYSTEM(m))
return;
- audit_fd = get_audit_fd();
- if (audit_fd < 0)
+ /* Don't generate audit events if the service was already started and we're just deserializing */
+ if (MANAGER_IS_RELOADING(m))
return;
- /* Don't generate audit events if the service was already
- * started and we're just deserializing */
- if (MANAGER_IS_RELOADING(m))
+ audit_fd = get_audit_fd();
+ if (audit_fd < 0)
return;
r = unit_name_to_prefix_and_instance(u->id, &p);
log_warning_errno(errno, "Failed to send audit message, ignoring: %m");
}
#endif
-
}
void manager_send_unit_plymouth(Manager *m, Unit *u) {
_cleanup_free_ char *message = NULL;
int c, r;
- /* Don't generate plymouth events if the service was already
- * started and we're just deserializing */
- if (MANAGER_IS_RELOADING(m))
- return;
+ assert(m);
+ assert(u);
if (!MANAGER_IS_SYSTEM(m))
return;
+ /* Don't generate plymouth events if the service was already started and we're just deserializing */
+ if (MANAGER_IS_RELOADING(m))
+ return;
+
if (detect_container() > 0)
return;
"Failed to communicate with plymouth: %m");
}
+void manager_send_unit_supervisor(Manager *m, Unit *u, bool active) {
+ assert(m);
+ assert(u);
+
+ /* Notify a "supervisor" process about our progress, i.e. a container manager, hypervisor, or
+ * surrounding service manager. */
+
+ if (MANAGER_IS_RELOADING(m))
+ return;
+
+ if (!UNIT_VTABLE(u)->notify_supervisor)
+ return;
+
+ if (in_initrd()) /* Only send these once we left the initrd */
+ return;
+
+ (void) sd_notifyf(/* unset_environment= */ false,
+ active ? "X_SYSTEMD_UNIT_ACTIVE=%s" : "X_SYSTEMD_UNIT_INACTIVE=%s",
+ u->id);
+}
+
usec_t manager_get_watchdog(Manager *m, WatchdogType t) {
assert(m);
log_taint_string(m);
}
-static void user_manager_send_ready(Manager *m) {
+static void manager_send_ready_user_scope(Manager *m) {
int r;
assert(m);
if (!MANAGER_IS_USER(m) || m->ready_sent)
return;
- r = sd_notify(false,
+ r = sd_notify(/* unset_environment= */ false,
"READY=1\n"
"STATUS=Reached " SPECIAL_BASIC_TARGET ".");
if (r < 0)
m->status_ready = false;
}
-static void manager_send_ready(Manager *m) {
+static void manager_send_ready_system_scope(Manager *m) {
int r;
+ assert(m);
+
+ if (!MANAGER_IS_SYSTEM(m))
+ return;
+
+ /* Skip the notification if nothing changed. */
if (m->ready_sent && m->status_ready)
- /* Skip the notification if nothing changed. */
return;
- r = sd_notify(false,
+ r = sd_notify(/* unset_environment= */ false,
"READY=1\n"
"STATUS=Ready.");
if (r < 0)
return;
/* For user managers, send out READY=1 as soon as we reach basic.target */
- user_manager_send_ready(m);
+ manager_send_ready_user_scope(m);
/* Log the taint string as soon as we reach basic.target */
log_taint_string(m);
if (hashmap_buckets(m->jobs) > hashmap_size(m->units) / 10)
m->jobs = hashmap_free(m->jobs);
- manager_send_ready(m);
+ manager_send_ready_system_scope(m);
/* Notify Type=idle units that we are done now */
manager_close_idle_pipe(m);
assert(m);
/* Let whoever invoked us know that we are now reloading */
- (void) sd_notifyf(/* unset= */ false,
+ (void) sd_notifyf(/* unset_environment= */ false,
"RELOADING=1\n"
"MONOTONIC_USEC=" USEC_FMT "\n", now(CLOCK_MONOTONIC));
void manager_send_unit_audit(Manager *m, Unit *u, int type, bool success);
void manager_send_unit_plymouth(Manager *m, Unit *u);
+void manager_send_unit_supervisor(Manager *m, Unit *u, bool active);
bool manager_unit_inactive_or_pending(Manager *m, const char *name);
[JOB_DONE] = "Stopped target %s.",
},
},
+
+ .notify_supervisor = true,
};
unit_emit_audit_start(u);
manager_send_unit_plymouth(m, u);
+ manager_send_unit_supervisor(m, u, /* active= */ true);
}
if (UNIT_IS_INACTIVE_OR_FAILED(ns) && !UNIT_IS_INACTIVE_OR_FAILED(os)) {
/* This unit just stopped/failed. */
unit_emit_audit_stop(u, ns);
+ manager_send_unit_supervisor(m, u, /* active= */ false);
unit_log_resources(u);
}
/* If true, we'll notify plymouth about this unit */
bool notify_plymouth;
+ /* If true, we'll notify a surrounding VMM/container manager about this unit becoming available */
+ bool notify_supervisor;
+
/* The audit events to generate on start + stop (or 0 if none shall be generated) */
int audit_start_message_type;
int audit_stop_message_type;
#include "terminal-util.h"
static const char *arg_identifier = NULL;
+static const char *arg_namespace = NULL;
static int arg_priority = LOG_INFO;
static int arg_stderr_priority = -1;
static bool arg_level_prefix = true;
" -p --priority=PRIORITY Set priority value (0..7)\n"
" --stderr-priority=PRIORITY Set priority value (0..7) used for stderr\n"
" --level-prefix=BOOL Control whether level prefix shall be parsed\n"
+ " --namespace=NAMESPACE Connect to specified journal namespace\n"
"\nSee the %s for details.\n",
program_invocation_short_name,
ansi_highlight(),
enum {
ARG_VERSION = 0x100,
ARG_STDERR_PRIORITY,
- ARG_LEVEL_PREFIX
+ ARG_LEVEL_PREFIX,
+ ARG_NAMESPACE,
};
static const struct option options[] = {
{ "priority", required_argument, NULL, 'p' },
{ "stderr-priority", required_argument, NULL, ARG_STDERR_PRIORITY },
{ "level-prefix", required_argument, NULL, ARG_LEVEL_PREFIX },
+ { "namespace", required_argument, NULL, ARG_NAMESPACE },
{}
};
return r;
break;
+ case ARG_NAMESPACE:
+ if (isempty(optarg))
+ arg_namespace = NULL;
+ else
+ arg_namespace = optarg;
+ break;
+
case '?':
return -EINVAL;
if (r <= 0)
return r;
- outfd = sd_journal_stream_fd(arg_identifier, arg_priority, arg_level_prefix);
+ outfd = sd_journal_stream_fd_with_namespace(arg_namespace, arg_identifier, arg_priority, arg_level_prefix);
if (outfd < 0)
return log_error_errno(outfd, "Failed to create stream fd: %m");
if (arg_stderr_priority >= 0 && arg_stderr_priority != arg_priority) {
- errfd = sd_journal_stream_fd(arg_identifier, arg_stderr_priority, arg_level_prefix);
+ errfd = sd_journal_stream_fd_with_namespace(arg_namespace, arg_identifier, arg_stderr_priority, arg_level_prefix);
if (errfd < 0)
return log_error_errno(errfd, "Failed to create stream fd: %m");
}
global:
sd_bus_creds_get_pidfd_dup;
sd_bus_creds_new_from_pidfd;
+ sd_journal_stream_fd_with_namespace;
} LIBSYSTEMD_255;
return fill_iovec_perror_and_send(message, 0, iovec);
}
-_public_ int sd_journal_stream_fd(const char *identifier, int priority, int level_prefix) {
+_public_ int sd_journal_stream_fd_with_namespace(
+ const char *name_space,
+ const char *identifier,
+ int priority,
+ int level_prefix) {
+
_cleanup_close_ int fd = -EBADF;
- char *header;
- size_t l;
+ const char *path;
int r;
assert_return(priority >= 0, -EINVAL);
assert_return(priority <= 7, -EINVAL);
+ path = journal_stream_path(name_space);
+ if (!path)
+ return -EINVAL;
+
fd = socket(AF_UNIX, SOCK_STREAM|SOCK_CLOEXEC, 0);
if (fd < 0)
return -errno;
- r = connect_unix_path(fd, AT_FDCWD, "/run/systemd/journal/stdout");
+ r = connect_unix_path(fd, AT_FDCWD, path);
if (r < 0)
return r;
identifier = strempty(identifier);
+ char *header;
+ size_t l;
+
l = strlen(identifier);
header = newa(char, l + 1 + 1 + 2 + 2 + 2 + 2 + 2);
return TAKE_FD(fd);
}
+_public_ int sd_journal_stream_fd(const char *identifier, int priority, int level_prefix) {
+ return sd_journal_stream_fd_with_namespace(NULL, identifier, priority, level_prefix);
+}
+
_public_ int sd_journal_print_with_location(int priority, const char *file, const char *line, const char *func, const char *format, ...) {
int r;
va_list ap;
#pragma once
#include <stdbool.h>
+#include <stddef.h>
+
+#include "syslog-util.h"
int journal_fd_nonblock(bool nonblock);
void close_journal_fd(void);
+
+/* We declare sd_journal_stream_fd() as async-signal-safe. So instead of strjoin(), which calls malloc()
+ * internally, use a macro + alloca(). */
+#define journal_stream_path(log_namespace) \
+ ({ \
+ const char *_ns = (log_namespace), *_ret; \
+ if (!_ns) \
+ _ret = "/run/systemd/journal/stdout"; \
+ else if (log_namespace_name_valid(_ns)) \
+ _ret = strjoina("/run/systemd/journal.", _ns, "/stdout"); \
+ else \
+ _ret = NULL; \
+ _ret; \
+ })
j->has_persistent_files = true;
}
-static const char *skip_slash(const char *p) {
-
- if (!p)
- return NULL;
-
- while (*p == '/')
- p++;
-
- return p;
-}
-
static int add_any_file(
sd_journal *j,
int fd,
/* If there's a top-level fd defined make the path relative, explicitly, since otherwise
* openat() ignores the first argument. */
- fd = our_fd = openat(j->toplevel_fd, skip_slash(path), O_RDONLY|O_CLOEXEC|O_NONBLOCK);
+ fd = our_fd = openat(j->toplevel_fd, skip_leading_slash(path), O_RDONLY|O_CLOEXEC|O_NONBLOCK);
else
fd = our_fd = open(path, O_RDONLY|O_CLOEXEC|O_NONBLOCK);
if (fd < 0) {
else
/* Open the specified directory relative to the toplevel fd. Enforce that the path specified is
* relative, by dropping the initial slash */
- d = xopendirat(j->toplevel_fd, skip_slash(path), 0);
+ d = xopendirat(j->toplevel_fd, skip_leading_slash(path), 0);
if (!d)
return -errno;
address->source = NETWORK_CONFIG_SOURCE_FOREIGN;
}
-static int address_acquire(Link *link, const Address *original, Address **ret) {
- _cleanup_(address_unrefp) Address *na = NULL;
- union in_addr_union in_addr;
- int r;
-
- assert(link);
- assert(original);
- assert(ret);
-
- /* Something useful was configured? just use it */
- if (in_addr_is_set(original->family, &original->in_addr))
- return address_dup(original, ret);
-
- /* The address is configured to be 0.0.0.0 or [::] by the user?
- * Then let's acquire something more useful from the pool. */
- r = address_pool_acquire(link->manager, original->family, original->prefixlen, &in_addr);
- if (r < 0)
- return r;
- if (r == 0)
- return -EBUSY;
-
- /* Pick first address in range for ourselves. */
- if (original->family == AF_INET)
- in_addr.in.s_addr = in_addr.in.s_addr | htobe32(1);
- else if (original->family == AF_INET6)
- in_addr.in6.s6_addr[15] |= 1;
-
- r = address_dup(original, &na);
- if (r < 0)
- return r;
-
- na->in_addr = in_addr;
-
- *ret = TAKE_PTR(na);
- return 0;
-}
-
int address_configure_handler_internal(sd_netlink *rtnl, sd_netlink_message *m, Link *link, const char *error_msg) {
int r;
return request_call_netlink_async(link->manager->rtnl, m, req);
}
-static bool address_is_ready_to_configure(Link *link, const Address *address) {
+static int address_acquire(Link *link, const Address *address, union in_addr_union *ret) {
+ union in_addr_union a;
+ int r;
+
assert(link);
assert(address);
+ assert(ret);
- if (!link_is_ready_to_configure(link, false))
- return false;
+ r = address_pool_acquire(link->manager, address->family, address->prefixlen, &a);
+ if (r < 0)
+ return r;
+ if (r == 0)
+ return -EBUSY;
- if (!ipv4acd_bound(link, address))
- return false;
+ /* Pick first address in range for ourselves. */
+ if (address->family == AF_INET)
+ a.in.s_addr |= htobe32(1);
+ else if (address->family == AF_INET6)
+ a.in6.s6_addr[15] |= 1;
+ else
+ assert_not_reached();
- /* Refuse adding more than the limit */
- if (set_size(link->addresses) >= ADDRESSES_PER_LINK_MAX)
- return false;
+ *ret = a;
+ return 0;
+}
- return true;
+static int address_requeue_request(Request *req, Link *link, const Address *address) {
+ int r;
+
+ assert(req);
+ assert(link);
+ assert(link->manager);
+ assert(link->network);
+ assert(address);
+
+ /* Something useful was configured? just use it */
+ if (in_addr_is_set(address->family, &address->in_addr))
+ return 0;
+
+ /* The address is configured to be 0.0.0.0 or [::] by the user?
+ * Then let's acquire something more useful. */
+ union in_addr_union a;
+ r = address_acquire(link, address, &a);
+ if (r < 0)
+ return r;
+
+ _cleanup_(address_unrefp) Address *tmp = NULL;
+ r = address_dup(address, &tmp);
+ if (r < 0)
+ return r;
+
+ tmp->in_addr = a;
+
+ r = link_requeue_request(link, req, tmp, NULL);
+ if (r < 0)
+ return r;
+ if (r == 0)
+ return -EEXIST; /* Already queued?? Strange... */
+
+ TAKE_PTR(tmp);
+ return 1; /* A new request is queued. it is not necessary to process this request anymore. */
}
static int address_process_request(Request *req, Link *link, Address *address) {
assert(link);
assert(address);
- if (!address_is_ready_to_configure(link, address))
+ if (!link_is_ready_to_configure(link, false))
+ return 0;
+
+ /* Refuse adding more than the limit */
+ if (set_size(link->addresses) >= ADDRESSES_PER_LINK_MAX)
+ return 0;
+
+ r = address_requeue_request(req, link, address);
+ if (r == -EBUSY)
+ return 0;
+ if (r != 0)
+ return r;
+
+ address_set_broadcast(address, link);
+
+ r = ipv4acd_configure(link, address);
+ if (r < 0)
+ return r;
+
+ if (!ipv4acd_bound(link, address))
return 0;
address_set_cinfo(link->manager, address, &c);
/* The requested address is outdated. Let's ignore the request. */
return 0;
- if (address_get(link, address, &existing) < 0) {
- if (address_get_request(link, address, NULL) >= 0)
- return 0; /* already requested, skipping. */
+ if (address_get_request(link, address, NULL) >= 0)
+ return 0; /* already requested, skipping. */
- r = address_acquire(link, address, &tmp);
- if (r < 0)
- return log_link_warning_errno(link, r, "Failed to acquire an address from pool: %m");
-
- } else {
- r = address_dup(address, &tmp);
- if (r < 0)
- return log_oom();
+ r = address_dup(address, &tmp);
+ if (r < 0)
+ return log_oom();
+ if (address_get(link, address, &existing) >= 0) {
/* Copy already assigned address when it is requested as a null address. */
if (address_is_static_null(address))
tmp->in_addr = existing->in_addr;
tmp->state = existing->state;
}
- address_set_broadcast(tmp, link);
-
- r = ipv4acd_configure(link, tmp);
- if (r < 0)
- return r;
-
log_address_debug(tmp, "Requesting", link);
r = link_queue_request_safe(link, REQUEST_TYPE_ADDRESS,
tmp,
return 0;
}
-static int link_start_dhcp4_server(Link *link) {
+int link_start_dhcp4_server(Link *link) {
int r;
assert(link);
int link_request_dhcp_server(Link *link);
+int link_start_dhcp4_server(Link *link);
void manager_toggle_dhcp4_server_state(Manager *manager, bool start);
CONFIG_PARSER_PROTOTYPE(config_parse_dhcp_server_relay_agent_suboption);
log_link_debug(link, "Acquiring IPv4 link-local address.");
}
- if (link->dhcp_server) {
- r = sd_dhcp_server_start(link->dhcp_server);
- if (r < 0)
- return log_link_warning_errno(link, r, "Could not start DHCP server: %m");
- }
+ r = link_start_dhcp4_server(link);
+ if (r < 0)
+ return log_link_warning_errno(link, r, "Could not start DHCP server: %m");
r = ipv4acd_start(link);
if (r < 0)
process, counter, netlink_handler, ret);
}
+int link_requeue_request(Link *link, Request *req, void *userdata, Request **ret) {
+ assert(link);
+ assert(req);
+
+ return link_queue_request_full(
+ link,
+ req->type,
+ userdata,
+ req->free_func,
+ req->hash_func,
+ req->compare_func,
+ req->process,
+ req->counter,
+ req->netlink_handler,
+ ret);
+}
+
int manager_process_requests(Manager *manager) {
Request *req;
int r;
request_netlink_handler_t netlink_handler,
Request **ret);
+int link_requeue_request(Link *link, Request *req, void *userdata, Request **ret);
+
static inline int link_queue_request(
Link *link,
RequestType type,
request_detach(req);
/* Request the route with the adjusted Route object combined with the same other parameters. */
- r = link_queue_request_full(link,
- req->type,
- tmp,
- req->free_func,
- req->hash_func,
- req->compare_func,
- req->process,
- req->counter,
- req->netlink_handler,
- NULL);
+ r = link_requeue_request(link, req, tmp, NULL);
if (r < 0)
return r;
if (r == 0)
if (!tags)
return log_oom();
+ if (DEBUG_LOGGING) {
+ _cleanup_free_ char *joined = strv_join(tags, " ");
+
+ if (joined) {
+ _cleanup_free_ char *j = cescape(joined);
+ free_and_replace(joined, j);
+ }
+
+ log_debug("Got sd_notify() message: %s", strnull(joined));
+ }
+
if (strv_contains(tags, "READY=1")) {
r = sd_notify(false, "READY=1\n");
if (r < 0)
#include "random-util.h"
#include "sparse-endian.h"
#include "stat-util.h"
+#include "tmpfile-util.h"
#include "tpm2-util.h"
#include "user-util.h"
#include "varlink.h"
assert(dfd >= 0);
assert(fn);
- /* For non-root users creating a temporary file using the openat(2) over "." will fail later, in the
- * linkat(2) step at the end. The reason is that linkat(2) requires the CAP_DAC_READ_SEARCH
- * capability when it uses the AT_EMPTY_PATH flag. */
- if (have_effective_cap(CAP_DAC_READ_SEARCH) > 0) {
- fd = openat(dfd, ".", O_CLOEXEC|O_WRONLY|O_TMPFILE, 0400);
- if (fd < 0)
- log_debug_errno(errno, "Failed to create temporary credential file with O_TMPFILE, proceeding without: %m");
- }
- if (fd < 0) {
- if (asprintf(&t, "credential.secret.%016" PRIx64, random_u64()) < 0)
- return -ENOMEM;
-
- fd = openat(dfd, t, O_CLOEXEC|O_WRONLY|O_CREAT|O_EXCL|O_NOFOLLOW, 0400);
- if (fd < 0)
- return -errno;
- }
+ fd = open_tmpfile_linkable_at(dfd, fn, O_CLOEXEC|O_WRONLY, &t);
+ if (fd < 0)
+ return log_debug_errno(fd, "Failed to create temporary file for credential host secret: %m");
r = chattr_secret(fd, 0);
if (r < 0)
if (r < 0)
goto fail;
- if (fsync(fd) < 0) {
+ if (fchmod(fd, 0400) < 0) {
r = -errno;
goto fail;
}
- warn_not_encrypted(fd, flags, dirname, fn);
-
- if (t) {
- r = rename_noreplace(dfd, t, dfd, fn);
- if (r < 0)
- goto fail;
-
- t = mfree(t);
- } else if (linkat(fd, "", dfd, fn, AT_EMPTY_PATH) < 0) {
+ if (fsync(fd) < 0) {
r = -errno;
goto fail;
}
- if (fsync(dfd) < 0) {
- r = -errno;
+ warn_not_encrypted(fd, flags, dirname, fn);
+
+ r = link_tmpfile_at(fd, dfd, t, fn, LINK_TMPFILE_SYNC);
+ if (r < 0) {
+ log_debug_errno(r, "Failed to link host key into place: %m");
goto fail;
}
void *copy;
copy = memdup(buf.data, sizeof(buf.data));
- if (!copy) {
- r = -ENOMEM;
- goto fail;
- }
+ if (!copy)
+ return -ENOMEM;
*ret = IOVEC_MAKE(copy, sizeof(buf.data));
}
#include <sys/utsname.h>
#include <unistd.h>
+#include "sd-daemon.h"
+
#include "alloc-util.h"
#include "creds-util.h"
#include "fd-util.h"
#include "fs-util.h"
#include "hostname-setup.h"
#include "hostname-util.h"
+#include "initrd-util.h"
#include "log.h"
#include "macro.h"
#include "proc-cmdline.h"
assert(s);
- assert_se(uname(&u) >= 0);
+ if (uname(&u) < 0)
+ return -errno;
if (streq_ptr(s, u.nodename))
return 0;
}
int shorten_overlong(const char *s, char **ret) {
- char *h, *p;
+ _cleanup_free_ char *h = NULL;
/* Shorten an overlong name to HOST_NAME_MAX or to the first dot,
* whatever comes earlier. */
assert(s);
+ assert(ret);
h = strdup(s);
if (!h)
return -ENOMEM;
if (hostname_is_valid(h, 0)) {
- *ret = h;
+ *ret = TAKE_PTR(h);
return 0;
}
- p = strchr(h, '.');
+ char *p = strchr(h, '.');
if (p)
*p = 0;
strshorten(h, HOST_NAME_MAX);
- if (!hostname_is_valid(h, 0)) {
- free(h);
+ if (!hostname_is_valid(h, /* flags= */ 0))
return -EDOM;
- }
- *ret = h;
+ *ret = TAKE_PTR(h);
return 1;
}
r = write_string_file("/run/systemd/default-hostname", hostname,
WRITE_STRING_FILE_CREATE | WRITE_STRING_FILE_ATOMIC);
if (r < 0)
- log_warning_errno(r, "Failed to create \"/run/systemd/default-hostname\": %m");
+ log_warning_errno(r, "Failed to create \"/run/systemd/default-hostname\", ignoring: %m");
} else
unlink_or_warn("/run/systemd/default-hostname");
}
int hostname_setup(bool really) {
- _cleanup_free_ char *b = NULL;
- const char *hn = NULL;
+ _cleanup_free_ char *hn = NULL;
HostnameSource source;
bool enoent = false;
int r;
- r = proc_cmdline_get_key("systemd.hostname", 0, &b);
+ r = proc_cmdline_get_key("systemd.hostname", 0, &hn);
if (r < 0)
log_warning_errno(r, "Failed to retrieve system hostname from kernel command line, ignoring: %m");
else if (r > 0) {
- if (hostname_is_valid(b, VALID_HOSTNAME_TRAILING_DOT)) {
- hn = b;
+ if (hostname_is_valid(hn, VALID_HOSTNAME_TRAILING_DOT))
source = HOSTNAME_TRANSIENT;
- } else {
- log_warning("Hostname specified on kernel command line is invalid, ignoring: %s", b);
- b = mfree(b);
+ else {
+ log_warning("Hostname specified on kernel command line is invalid, ignoring: %s", hn);
+ hn = mfree(hn);
}
}
if (!hn) {
- r = read_etc_hostname(NULL, &b);
- if (r < 0) {
- if (r == -ENOENT)
- enoent = true;
- else
- log_warning_errno(r, "Failed to read configured hostname: %m");
- } else {
- hn = b;
+ r = read_etc_hostname(NULL, &hn);
+ if (r == -ENOENT)
+ enoent = true;
+ else if (r < 0)
+ log_warning_errno(r, "Failed to read configured hostname, ignoring: %m");
+ else
source = HOSTNAME_STATIC;
- }
}
if (!hn) {
- r = acquire_hostname_from_credential(&b);
- if (r >= 0) {
- hn = b;
+ r = acquire_hostname_from_credential(&hn);
+ if (r >= 0)
source = HOSTNAME_TRANSIENT;
- }
}
if (!hn) {
- _cleanup_free_ char *buf = NULL;
-
/* Don't override the hostname if it is already set and not explicitly configured */
- r = gethostname_full(GET_HOSTNAME_ALLOW_LOCALHOST, &buf);
+ r = gethostname_full(GET_HOSTNAME_ALLOW_LOCALHOST, &hn);
if (r == -ENOMEM)
return log_oom();
if (r >= 0) {
- log_debug("No hostname configured, leaving existing hostname <%s> in place.", buf);
- return 0;
+ log_debug("No hostname configured, leaving existing hostname <%s> in place.", hn);
+ goto finish;
}
if (enoent)
log_info("No hostname configured, using default hostname.");
- hn = b = get_default_hostname();
+ hn = get_default_hostname();
if (!hn)
return log_oom();
source = HOSTNAME_DEFAULT;
-
}
r = sethostname_idempotent_full(hn, really);
if (really)
hostname_update_source_hint(hn, source);
- return r;
+finish:
+ if (!in_initrd())
+ (void) sd_notifyf(/* unset_environment= */ false, "X_SYSTEMD_HOSTNAME=%s", hn);
+
+ return 0;
}
static const char* const hostname_source_table[] = {
return 0;
}
+int json_dispatch_int8(const char *name, JsonVariant *variant, JsonDispatchFlags flags, void *userdata) {
+ int8_t *i = ASSERT_PTR(userdata);
+ int64_t i64;
+ int r;
+
+ assert(variant);
+
+ r = json_dispatch_int64(name, variant, flags, &i64);
+ if (r < 0)
+ return r;
+
+ if (i64 < INT8_MIN || i64 > INT8_MAX)
+ return json_log(variant, flags, SYNTHETIC_ERRNO(ERANGE), "JSON field '%s' out of bounds.", strna(name));
+
+ *i = (int8_t) i64;
+ return 0;
+}
+
+int json_dispatch_uint8(const char *name, JsonVariant *variant, JsonDispatchFlags flags, void *userdata) {
+ uint8_t *u = ASSERT_PTR(userdata);
+ uint64_t u64;
+ int r;
+
+ assert(variant);
+
+ r = json_dispatch_uint64(name, variant, flags, &u64);
+ if (r < 0)
+ return r;
+
+ if (u64 > UINT8_MAX)
+ return json_log(variant, flags, SYNTHETIC_ERRNO(ERANGE), "JSON field '%s' out of bounds.", strna(name));
+
+ *u = (uint8_t) u64;
+ return 0;
+}
+
int json_dispatch_string(const char *name, JsonVariant *variant, JsonDispatchFlags flags, void *userdata) {
char **s = ASSERT_PTR(userdata);
int r;
int json_dispatch_int32(const char *name, JsonVariant *variant, JsonDispatchFlags flags, void *userdata);
int json_dispatch_uint16(const char *name, JsonVariant *variant, JsonDispatchFlags flags, void *userdata);
int json_dispatch_int16(const char *name, JsonVariant *variant, JsonDispatchFlags flags, void *userdata);
+int json_dispatch_int8(const char *name, JsonVariant *variant, JsonDispatchFlags flags, void *userdata);
+int json_dispatch_uint8(const char *name, JsonVariant *variant, JsonDispatchFlags flags, void *userdata);
int json_dispatch_uid_gid(const char *name, JsonVariant *variant, JsonDispatchFlags flags, void *userdata);
int json_dispatch_user_group_name(const char *name, JsonVariant *variant, JsonDispatchFlags flags, void *userdata);
int json_dispatch_id128(const char *name, JsonVariant *variant, JsonDispatchFlags flags, void *userdata);
#include <sys/mount.h>
#include <unistd.h>
+#include "sd-daemon.h"
#include "sd-id128.h"
#include "alloc-util.h"
#include "creds-util.h"
#include "fd-util.h"
#include "id128-util.h"
+#include "initrd-util.h"
#include "io-util.h"
#include "log.h"
#include "machine-id-setup.h"
if (sd_id128_is_null(machine_id)) {
/* Try to read any existing machine ID */
- if (id128_read_fd(fd, ID128_FORMAT_PLAIN, ret) >= 0)
- return 0;
+ if (id128_read_fd(fd, ID128_FORMAT_PLAIN, &machine_id) >= 0)
+ goto finish;
/* Hmm, so, the id currently stored is not useful, then let's generate one */
r = generate_machine_id(root, &machine_id);
return r;
finish:
+ if (!in_initrd())
+ (void) sd_notifyf(/* unset_environment= */ false, "X_SYSTEMD_MACHINE_ID=" SD_ID128_FORMAT_STR, SD_ID128_FORMAT_VAL(machine_id));
+
if (ret)
*ret = machine_id;
const char *dest,
const char *unit,
const char *listen_stream,
- const char *comment) {
+ const char *comment,
+ bool with_ssh_access_target_dependency) {
int r;
fprintf(f,
"[Unit]\n"
"Description=OpenSSH Server Socket (systemd-ssh-generator, %s)\n"
- "Documentation=man:systemd-ssh-generator(8)\n"
+ "Documentation=man:systemd-ssh-generator(8)\n",
+ comment);
+
+ /* When this is a remotely accessible socket let's mark this with a milestone: ssh-access.target */
+ if (with_ssh_access_target_dependency)
+ fputs("Wants=ssh-access.target\n"
+ "Before=ssh-access.target\n",
+ f);
+
+ fprintf(f,
"\n[Socket]\n"
"ListenStream=%s\n"
"Accept=yes\n"
"PollLimitIntervalSec=30s\n"
"PollLimitBurst=50\n",
- comment,
listen_stream);
r = fflush_and_check(f);
dest,
"sshd-vsock.socket",
"vsock::22",
- "AF_VSOCK");
+ "AF_VSOCK",
+ /* with_ssh_access_target_dependency= */ true);
if (r < 0)
return r;
dest,
"sshd-unix-local.socket",
"/run/ssh-unix-local/socket",
- "AF_UNIX Local");
+ "AF_UNIX Local",
+ /* with_ssh_access_target_dependency= */ false);
if (r < 0)
return r;
dest,
"sshd-unix-export.socket",
"/run/host/unix-export/ssh",
- "AF_UNIX Export");
+ "AF_UNIX Export",
+ /* with_ssh_access_target_dependency= */ true);
if (r < 0)
return r;
dest,
socket ?: "sshd-extra.socket",
*i,
- *i);
+ *i,
+ /* with_ssh_access_target_dependency= */ true);
if (r < 0)
return r;
#endif
int sd_journal_stream_fd(const char *identifier, int priority, int level_prefix);
+int sd_journal_stream_fd_with_namespace(const char *name_space, const char *identifier, int priority, int level_prefix);
/* Browse journal stream */
'test-dev-setup.c',
'test-device-nodes.c',
'test-devnum-util.c',
+ 'test-dirent-util.c',
'test-dns-domain.c',
'test-ellipsize.c',
'test-env-file.c',
--- /dev/null
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+
+#include <dirent.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <sys/stat.h>
+
+#include "alloc-util.h"
+#include "dirent-util.h"
+#include "fs-util.h"
+#include "mkdir.h"
+#include "path-util.h"
+#include "rm-rf.h"
+#include "stat-util.h"
+#include "string-util.h"
+#include "tmpfile-util.h"
+#include "tests.h"
+
+TEST (test_dirent_ensure_type) {
+ int r, dir_fd;
+ static struct dirent de = {
+ .d_type = DT_UNKNOWN,
+ .d_name = "test",
+ };
+
+ assert_se(de.d_type == DT_UNKNOWN);
+
+ dir_fd = 0;
+ dirent_ensure_type(dir_fd, &de);
+
+ /* Test when d_name is "." or ".." */
+ strcpy(de.d_name, ".");
+ r = dirent_ensure_type(dir_fd, &de);
+ assert_se(r == 0);
+ assert_se(de.d_type == DT_DIR);
+
+ strcpy(de.d_name, "..");
+ r = dirent_ensure_type(dir_fd, &de);
+ assert_se(r == 0);
+ assert_se(de.d_type == DT_DIR);
+}
+
+TEST (test_dirent_is_file) {
+ _cleanup_(rm_rf_physical_and_freep) char *t = NULL;
+ const char *name, *dotfile, *name_alias, *bakfile, *tilda;
+ const struct dirent *de_reg, *de_lnk, *de_dot, *de_bak, *de_tilda;
+ DIR *dir;
+
+ static const struct dirent de_unknown = {
+ .d_type = DT_UNKNOWN,
+ .d_name = "test_unknown",
+ };
+
+ assert_se(mkdtemp_malloc(NULL, &t) >= 0);
+
+ name = strjoina(t, "/test.txt");
+ dotfile = strjoina(t, "/.hidden_file");
+ bakfile = strjoina(t, "/test.bak");
+ tilda = strjoina(t, "/test~");
+ name_alias = strjoina(t, "/test_link");
+
+ assert_se(touch(name) >= 0);
+ assert_se(touch(dotfile) >= 0);
+ assert_se(touch(bakfile) >= 0);
+ assert_se(touch(tilda) >= 0);
+
+ if (symlink(name, name_alias) < 0) {
+ assert_se(IN_SET(errno, EINVAL, ENOSYS, ENOTTY, EPERM));
+ log_tests_skipped_errno(errno, "symlink() not possible");
+ }
+
+ dir = opendir(t);
+ if (dir == NULL) {
+ log_error_errno(errno, "Failed to open directory '%s': %m", t);
+ exit(EXIT_FAILURE);
+ }
+
+ rewinddir(dir);
+ while ((de_reg = readdir_ensure_type(dir)) != NULL)
+ if (strcmp(de_reg->d_name, "test.txt") == 0)
+ break;
+
+ rewinddir(dir);
+ while ((de_lnk = readdir_ensure_type(dir)) != NULL)
+ if (strcmp(de_lnk->d_name, "test_link") == 0)
+ break;
+
+ rewinddir(dir);
+ while ((de_dot = readdir_ensure_type(dir)) != NULL)
+ if (strcmp(de_dot->d_name, ".hidden_file") == 0)
+ break;
+
+ rewinddir(dir);
+ while ((de_bak = readdir(dir)) != NULL)
+ if (strcmp(de_bak->d_name, "test.bak") == 0)
+ break;
+
+ rewinddir(dir);
+ while ((de_tilda = readdir(dir)) != NULL)
+ if (strcmp(de_tilda->d_name, "test~") == 0)
+ break;
+
+ /* Test when d_type is DT_REG, DT_LNK, or DT_UNKNOWN */
+ assert_se(dirent_is_file(de_reg) == true);
+ if (de_lnk)
+ assert_se(dirent_is_file(de_lnk) == true);
+ else
+ log_tests_skipped("de_lnk is NULL, skipping test");
+ assert_se(dirent_is_file(&de_unknown) == true);
+
+ /* Test for hidden files */
+ assert_se(dirent_is_file(de_dot) == false);
+ assert_se(dirent_is_file(de_bak) == false);
+ assert_se(dirent_is_file(de_tilda) == false);
+
+ closedir(dir);
+}
+
+TEST (test_dirent_is_file_with_suffix) {
+ _cleanup_(rm_rf_physical_and_freep) char *t = NULL;
+ const char *name, *dotfile, *name_alias, *dotdot, *chr;
+ const struct dirent *de_reg, *de_lnk, *de_dot, *de_dotdot, *de_chr;
+ DIR *dir;
+
+ static const struct dirent de_unknown = {
+ .d_type = DT_UNKNOWN,
+ .d_name = "test_unknown",
+ };
+
+ assert_se(mkdtemp_malloc(NULL, &t) >= 0);
+
+ name = strjoina(t, "/test.txt");
+ dotfile = strjoina(t, "/.hidden_file");
+ dotdot = strjoina(t, "/..dotdot");
+ chr = strjoina(t, "/test_chr");
+ name_alias = strjoina(t, "/test_link");
+
+ assert_se(touch(name) >= 0);
+ assert_se(touch(dotfile) >= 0);
+ assert_se(touch(dotdot) >= 0);
+ assert_se(mknod(chr, 0775 | S_IFCHR, makedev(0, 0)) >= 0);
+
+ if (symlink(name, name_alias) < 0) {
+ assert_se(IN_SET(errno, EINVAL, ENOSYS, ENOTTY, EPERM));
+ log_tests_skipped_errno(errno, "symlink() not possible");
+ }
+
+ dir = opendir(t);
+ if (dir == NULL) {
+ log_error_errno(errno, "Failed to open directory '%s': %m", t);
+ exit(EXIT_FAILURE);
+ }
+
+ rewinddir(dir);
+ while ((de_reg = readdir_ensure_type(dir)) != NULL)
+ if (strcmp(de_reg->d_name, "test.txt") == 0)
+ break;
+
+ rewinddir(dir);
+ while ((de_lnk = readdir_ensure_type(dir)) != NULL)
+ if (strcmp(de_lnk->d_name, "test_link") == 0)
+ break;
+
+ rewinddir(dir);
+ while ((de_dot = readdir_ensure_type(dir)) != NULL)
+ if (strcmp(de_dot->d_name, ".hidden_file") == 0)
+ break;
+
+ rewinddir(dir);
+ while ((de_dotdot = readdir(dir)) != NULL)
+ if (strcmp(de_dotdot->d_name, "..dotdot") == 0)
+ break;
+
+ rewinddir(dir);
+ while ((de_chr = readdir(dir)) != NULL)
+ if (strcmp(de_chr->d_name, "test_chr") == 0)
+ break;
+
+ /* Test when d_type is not DT_REG, DT_LNK, or DT_UNKNOWN */
+ assert_se(!dirent_is_file_with_suffix(de_chr, NULL));
+
+ /* Test when suffix is NULL */
+ assert_se(dirent_is_file_with_suffix(de_reg, NULL) == true);
+ if (de_lnk)
+ assert_se(dirent_is_file_with_suffix(de_lnk, NULL) == true);
+ else
+ log_tests_skipped("de_lnk is NULL, skipping test");
+ assert_se(dirent_is_file_with_suffix(&de_unknown, NULL) == true);
+
+ /* Test for present suffix */
+ assert_se(dirent_is_file_with_suffix(de_reg, "txt") == true);
+ if (de_lnk)
+ assert_se(dirent_is_file_with_suffix(de_lnk, "link") == true);
+ else
+ log_tests_skipped("de_lnk is NULL, skipping test");
+ assert_se(dirent_is_file_with_suffix(&de_unknown, "unknown") == true);
+
+ /* Test for absent suffix */
+ assert_se(dirent_is_file_with_suffix(de_reg, "svg") == false);
+ if (de_lnk)
+ assert_se(dirent_is_file_with_suffix(de_lnk, "pdf") == false);
+ else
+ log_tests_skipped("de_lnk is NULL, skipping test");
+ assert_se(dirent_is_file_with_suffix(&de_unknown, "yes") == false);
+
+ /* Test for dot and dot-dot */
+ assert_se(dirent_is_file_with_suffix(de_dot, NULL) == false);
+ assert_se(dirent_is_file_with_suffix(de_dotdot, NULL) == false);
+
+ closedir(dir);
+}
+
+DEFINE_TEST_MAIN(LOG_INFO);
assert_se(xopenat_lock_full(tfd, "def", O_DIRECTORY, 0, 0755, LOCK_POSIX, LOCK_EX) == -EBADF);
}
+TEST(linkat_replace) {
+ _cleanup_(rm_rf_physical_and_freep) char *t = NULL;
+ _cleanup_close_ int tfd = -EBADF;
+
+ assert_se((tfd = mkdtemp_open(NULL, 0, &t)) >= 0);
+
+ _cleanup_close_ int fd1 = openat(tfd, "foo", O_CREAT|O_RDWR|O_CLOEXEC, 0600);
+ assert_se(fd1 >= 0);
+
+ assert_se(linkat_replace(tfd, "foo", tfd, "bar") >= 0);
+ assert_se(linkat_replace(tfd, "foo", tfd, "bar") >= 0);
+
+ _cleanup_close_ int fd1_check = openat(tfd, "bar", O_RDWR|O_CLOEXEC);
+ assert_se(fd1_check >= 0);
+
+ assert_se(inode_same_at(fd1, NULL, fd1_check, NULL, AT_EMPTY_PATH) > 0);
+
+ _cleanup_close_ int fd2 = openat(tfd, "baz", O_CREAT|O_RDWR|O_CLOEXEC, 0600);
+ assert_se(fd2 >= 0);
+
+ assert_se(inode_same_at(fd1, NULL, fd2, NULL, AT_EMPTY_PATH) == 0);
+
+ assert_se(linkat_replace(tfd, "foo", tfd, "baz") >= 0);
+
+ _cleanup_close_ int fd2_check = openat(tfd, "baz", O_RDWR|O_CLOEXEC);
+
+ assert_se(inode_same_at(fd2, NULL, fd2_check, NULL, AT_EMPTY_PATH) == 0);
+ assert_se(inode_same_at(fd1, NULL, fd2_check, NULL, AT_EMPTY_PATH) > 0);
+}
+
static int intro(void) {
arg_test_dir = saved_argv[1];
return EXIT_SUCCESS;
assert_cc(FILENAME_MAX == PATH_MAX);
}
+TEST(path_implies_directory) {
+ assert_se(!path_implies_directory(NULL));
+ assert_se(!path_implies_directory(""));
+ assert_se(path_implies_directory("/"));
+ assert_se(path_implies_directory("////"));
+ assert_se(path_implies_directory("////.///"));
+ assert_se(path_implies_directory("////./"));
+ assert_se(path_implies_directory("////."));
+ assert_se(path_implies_directory("."));
+ assert_se(path_implies_directory("./"));
+ assert_se(path_implies_directory("/."));
+ assert_se(path_implies_directory(".."));
+ assert_se(path_implies_directory("../"));
+ assert_se(path_implies_directory("/.."));
+ assert_se(!path_implies_directory("a"));
+ assert_se(!path_implies_directory("ab"));
+ assert_se(path_implies_directory("ab/"));
+ assert_se(!path_implies_directory("ab/a"));
+ assert_se(path_implies_directory("ab/a/"));
+ assert_se(path_implies_directory("ab/a/.."));
+ assert_se(path_implies_directory("ab/a/."));
+ assert_se(path_implies_directory("ab/a//"));
+}
+
DEFINE_TEST_MAIN(LOG_DEBUG);
--- /dev/null
+#!/usr/bin/env bash
+# SPDX-License-Identifier: LGPL-2.1-or-later
+set -eux
+set -o pipefail
+
+systemctl enable --now systemd-journald@cat-test.socket
+
+systemd-cat --namespace cat-test env CAT_TEST_RESULT=1
+
+timeout 30 bash -c "until systemctl -q is-active systemd-journald@cat-test.service; do sleep .5; done"
+
+journalctl --namespace cat-test --grep "JOURNAL_STREAM="
+journalctl --namespace cat-test --grep "CAT_TEST_RESULT=1"
+
+systemctl disable --now systemd-journald@cat-test.socket
{ 'file' : 'sockets.target' },
{ 'file' : 'soft-reboot.target' },
{ 'file' : 'sound.target' },
+ { 'file' : 'ssh-access.target' },
{
'file' : 'suspend-then-hibernate.target',
'conditions' : ['ENABLE_HIBERNATE'],
--- /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=SSH Access Available
+Documentation=man:systemd.special(7)
PassSecurity=yes
ReceiveBuffer=8M
SendBuffer=8M
+
+[Install]
+WantedBy=sockets.target