1
0
mirror of https://github.com/systemd/systemd synced 2026-03-18 02:54:47 +01:00

Compare commits

..

No commits in common. "efe76b273a40d96ecd51f88ab22aa25fb8e8d358" and "5f94ac543529cd118b5091b9b567400a2ae4444c" have entirely different histories.

22 changed files with 298 additions and 509 deletions

272
NEWS
View File

@ -5,9 +5,9 @@ CHANGES WITH 249 in spe:
* When operating on disk images via the --image= switch of various * When operating on disk images via the --image= switch of various
tools (such as systemd-nspawn or systemd-dissect), and multiple tools (such as systemd-nspawn or systemd-dissect), and multiple
suitable root or /usr/ partitions exist in the image, then a simple suitable root or /usr/ partitions exist in the image, then a simple
comparison inspired by strverscmp() is done on the GPT partition strverscmp() inspired comparison is done on the GPT partition label,
label, and the newest partition is picked. This permits a simple and and the newest partition picked. This permits a simple and generic
generic whole-file-system A/B update logic where new operating system whole-file-system A/B update logic where new operating system
versions are dropped into partitions whose label is then updated with versions are dropped into partitions whose label is then updated with
a matching version identifier. a matching version identifier.
@ -34,6 +34,9 @@ CHANGES WITH 249 in spe:
previously unprovisioned images (i.e. images with a mostly empty previously unprovisioned images (i.e. images with a mostly empty
/etc/). /etc/).
* The systemd-ask-password now also supports reading passwords from the
credentials subsystem, via the new --credential= switch.
* Services gained a new ExitType= setting which can configure how to * Services gained a new ExitType= setting which can configure how to
determine when a service exited: the default is "main" which defines determine when a service exited: the default is "main" which defines
the runtime by the service's main process lifetime (this matches the the runtime by the service's main process lifetime (this matches the
@ -44,31 +47,30 @@ CHANGES WITH 249 in spe:
* The systemd-machine-id-setup tool now supports a --image= switch for * The systemd-machine-id-setup tool now supports a --image= switch for
provisioning a machine ID file into an OS disk image, similar to how provisioning a machine ID file into an OS disk image, similar to how
--root= operates on an OS file tree. This matches the existing switch --root= operates on an OS file tree. This matches the existing switch
of the same name for systemd-tmpfiles, systemd-firstboot, and of the same name to the systemd-tmpfiles, systemd-firstboot or
systemd-sysusers tools. systemd-sysusers tools.
* Similarly, systemd-repart gained support for the --image= switch too. * Similar, systemd-repart gained support for a --image= switch, too. In
In combination with the existing --size= option, this makes the tool combination with the existing --size= makes the tool particularly
particularly useful for easily growing disk images in a single useful for easily growing disk images in a single command invocation,
invocation, following the declarative rules included in the image following the declarative rules included in the image itself.
itself.
* systemd-repart's partition configuration files gained support for a * systemd-repart's partition configuration files gained support for a
new switch MakeDirectories= which may be used to create arbitrary new switch MakeDirectories= which may be used to create arbitrary
directories inside file systems that are created, before registering directories inside file systems that are created, before registering
them in the partition table. This is useful in particular for root them in the partition table. This is useful in particular if root
partitions to create mount point directories for other partitions partitions are created that way to create mount point directories for
included in the image. For example, a disk image that contains a other partitions included in the image. For example, a disk image
root, /home/, and /var/ partitions, may set MakeDirectories=yes to that is created to contain a /home/ and a /var/ partition in addition
create /home/ and /var/ as empty directories in the root file system to a root partition may MakeDirectories= to create /home/ and /var/
on its creation, so that the resulting image can mounted immediately, as empty directories in the root file system on creation so that the
even in read-only mode. resulting image can mounted immediately, even in read-only mode.
* systemd-repart's CopyBlocks= setting gained support for the special * systemd-repart's CopyBlocks= setting gained support for a special
value "auto". If used, a suitable matching partition on the booted OS value "auto". If used a suitable, matching partition on the booted OS
is found as source to copy blocks from. This is useful when is found as source to copy blocks from. This is useful for
implementing replicating installers, that are booted from one medium implementing replicating installers, that are booted from one medium
and then stream their own root partition onto the target medium. and then stream their own root partition on the target medium.
* systemd-repart's partition configuration files gained support for a * systemd-repart's partition configuration files gained support for a
Flags= and a ReadOnly= setting, allowing control of the GPT partition Flags= and a ReadOnly= setting, allowing control of the GPT partition
@ -76,54 +78,36 @@ CHANGES WITH 249 in spe:
created partitions as read-only from the start. created partitions as read-only from the start.
* The /etc/os-release file has been extended with two new (optional) * The /etc/os-release file has been extended with two new (optional)
variables IMAGE_VERSION= and IMAGE_ID=, carrying identity and version variables IMAGE_VERSION= and IMAGE_ID=, for carrying identification
information for OS images that are updated comprehensively and and versioning information for OS images that are updated
atomically as one image. Two new specifiers %M, %A now resolve to consistently, comprehensively and atomically as one image. The two
these two fields in the various configuration options that resolve new specifiers %M, %A now resolve to these two fields in the various
specifiers. configuration options that resolve specifiers.
* portablectl gained a new switch --extension= for enabling portable * portablectl gained a new switch --extension= for enabling portable
service images with extensions that follow the extension image service images with extensions that follow the extension image
concept introduced with v248. concept introduced with v248.
* systemd-coredump will now extract ELF build-id information from * systemd-coredump will now extract ELF build-id information from
processes dumping core and include it in the coredump report. processes dumping core and include it in the coredump
Moreover, it will look for ELF .note.package sections with report. Moreover, if will look for ELF .note.package sections that
distribution packaging meta-information about the crashing process. may carry distribution packaging meta-information about the crashing
This is useful to directly embed the rpm or deb (or any other) process. This is useful to directly embed the RPM or Debian (or any
package name and version in ELF files, making it easy to match other) package name and version in the ELF files where they originate
coredump reports with the specific package for which the software was from, making it easy to match up coredump reports with the software
compiled. This is particularly useful on environments with ELF files versions they were found it. This is particular useful on
from multiple vendors, different distributions and versions, as is environments with ELF files form multiple vendors, different
common today in our containerized and sand-boxed world. For further distributions and versions, as it is common today, in particular in a
information, see: containerized and sand-boxed world. For further information, see:
https://systemd.io/COREDUMP_PACKAGE_METADATA https://systemd.io/COREDUMP_PACKAGE_METADATA
* A new udev hardware database has been added for FireWire devices * A new udev hwdb has been added for Firewire audio devices
(IEEE 1394).
* sd-bus' sd_bus_is_ready() and sd_bus_is_open() calls now accept a * sd-bus' sd_bus_is_ready() and sd_bus_is_open() calls now accept a
NULL bus object, for which they will return false. Or in other words, NULL bus object, for which they will return false. Or in other words,
an unallocated bus connection is neither ready nor open. an unallocated bus connection is neither ready nor open.
* The sd-device API acquired a new API function
sd_device_get_usec_initialized() that returns the monotonic time when
the udev device first appeared in the database.
* sd-device gained a new APIs sd_device_trigger_with_uuid() and
sd_device_get_trigger_uuid(). The former is similar to
sd_device_trigger() but returns a randomly generated UUID that is
associated with the synthetic uevent generated by the call. This UUID
may be read from the sd_device object a monitor eventually receives,
via the sd_device_get_trigger_uuid(). This interface requires kernel
4.13 or above to work, and allows tracking a synthetic uevent through
the entire device management stack. The "udevadm trigger --settle"
logic has been updated to make use of this concept if available to
wait precisely for the uevents it generates. "udevadm trigger" also
gained a new parameter --uuid that prints the UUID for each generated
uevent.
* The native Journal protocol has been documented. Clients may talk * The native Journal protocol has been documented. Clients may talk
this as alternative to the classic BSD syslog protocol for locally this as alternative to the classic BSD syslog protocol for locally
delivering log records to the Journal. The protocol has been stable delivering log records to the Journal. The protocol has been stable
@ -158,90 +142,81 @@ CHANGES WITH 249 in spe:
fine-tune whether to require an IPv4 or IPv6 address in order to fine-tune whether to require an IPv4 or IPv6 address in order to
consider an interface "online". consider an interface "online".
* networkctl will now show an over-all "online" state in the per-link
information.
* In .network files a new OutgoingInterface= setting has been added to
specify the output interface in bridge FDB setups.
* In ,network files the Multipath group ID may now be configured for
[NextHop] entries, via the new Group= setting.
* The DHCP server logic configured in .network files gained a new
setting RelayTarget= that turns the server into a DHCP server relay.
The RelayAgentCircuitId= and RelayAgentRemoteId= settings may be used
to further tweak the DHCP relay behaviour.
* The DHCP server logic also gained a new ServerAddress= setting in
.network files that explicitly specifies the server IP address to
use. If not specified, the address is determined automatically, as
before.
* The DHCP server logic in systemd-networkd gained support for static
DHCP leases, configurable via the [DHCPServerStaticLease]
section. This allows explicitly mapping specific MAC addresses to
fixed IP addresses and vice versa.
* The RestrictAddressFamilies= setting in service files now supports a * The RestrictAddressFamilies= setting in service files now supports a
new special value "none". If specified sockets of all address new special value "none". If specified sockets of all address
families will be made unavailable to services configured that way. families will be made unavailable to services configured that way.
* The DHCP server logic configured in .network files gained a new
setting RelayTarget= that turns the server into a DHCP server
relay. The RelayAgentCircuitId= and RelayAgentRemoteId= settings may
be used to further tweak the DHCP relay behaviour.
* The DHCP server logic also gained a new setting ServerAddress= in
.network files for explicitly specifying the server IP address to
use. If not used, the address is determined automatically, as before.
* The sd-device API acquired a new API function
sd_device_get_usec_initialized() that returns the monotonic timestamp
when a udev device appeared in the database first.
* systemd-fstab-generator and systemd-repart have been updated to * systemd-fstab-generator and systemd-repart have been updated to
support booting from disks that carry only a /usr/ partition but no support booting from disks that carry only a /usr/ partition but no
root partition yet, and where systemd-repart can add it in on the root partition yet, but where systemd-repart can add them in on first
first boot. This is useful for implementing systems that ship with a boot. This is useful for implementing systems that ship with a single
single /usr/ file system, and whose root file system shall be set up /usr/ file system and whose root file system shall be set up on a
and formatted on a LUKS-encrypted volume whose key is generated LUKS encrypted volume whose key is generated locally (and possibly
locally (and possibly enrolled in the TPM) during the first boot. enrolled in TPM), and which is set up and formatted during first
boot.
* The [Address] section of .network files now accepts a new * The [Address] section of .network files now accepts a new
RouteMetric= setting that configures the routing metric to use for RouteMetric= setting that configures the routing metric to use for
the prefix route created as effect of the address configuration. the prefix route created as effect of the address
Similarly, the [DHCPv6PrefixDelegation] and [IPv6Prefix] sections configuration. Similar, the [DHCPv6PrefixDelegation] and [IPv6Prefix]
gained matching settings for their prefix routes. (The option of the sections gained matching settings for their prefix routes. (The
same name in the [DHCPv6] section is moved to [IPv6AcceptRA], since option of the same name of the [DHCPv6] section is moved to
it conceptually belongs there; the old option is still understood for [IPv6AcceptRA], since it conceptually belongs there; the old option
compatibility.) is still understood for compatibility.)
* The DHCPv6 IAID and DUID are now explicitly configurable in .network * The DHCPv6 IAID and DUID are now explicitly configurable in .network
files. files.
* A new udev property ID_NET_DHCP_BROADCAST on network interface * A new udev property ID_NET_DHCP_BROADCAST on network interface
devices is now honoured by systemd-networkd, controlling whether to devices is not honoured by systemd-networkd, that controls whether to
issue DHCP offers via broadcasting. This is used to ensure that s390 issue DHCP offers via broadcasting. This is used to ensure that s390
layer 3 network interfaces work out-of-the-box with systemd-networkd. layer 3 network interfaces work out-of-the-box with systemd-networkd.
* nss-myhostname and systemd-resolved will now synthesize address * nss-myhostname and systemd-resolved will now synthesize address
records for a new special hostname "_outbound". The name will always records for a new special hostname "_outbound". The name will always
resolve to the local IP addresses most likely used for outbound resolve to the local IP addresses most likely used for outbound
connections towards the default routes. On multi-homed hosts this is connections towards the default route. On multi-homed hosts this is
useful to have a stable handle referring to "the" local IP address useful to have a stable handle referring to "the" local IP address
that matters most, to the point where this is defined. that matters most, to the point where this is defined.
* The Discoverable Partition Specification has been updated with a new * The Discoverable Partition Specification has been updated with a new
GPT partition flag "growsfs" defined for its partition types. GPT partition flag defined its partition types: a "growfs"
Whenever partitions with this flag set are automatically mounted flag. Whenever partitions with this flag set are automatically
(i.e. via systemd-gpt-auto-generator or the --image= switch of mounted (i.e. via systemd-gpt-auto-generator or the --image= switch
systemd-nspawn or other tools; and as opposed to explicit mounting of systemd-nspawn or other tools; and as opposed to explicit mounting
via /etc/fstab), the file system within the partition is via /etc/fstab), the file system within the partition is
automatically grown to the full size of the partition. If the file automatically grown to the full size of the partition should it be
system size already matches the partition size this flag has no smaller. If the file system size already matches the partition size
effect. Previously, this functionality has been available via the this flag has no effect. Previously, this functionality has been
explicit x-systemd.growfs mount option, and this new flag extends available via the explicit x-systemd.growfs mount option, and this
this to automatically discovered mounts. A new GrowFileSystem= new flag extends this to automatically discovered mounts. A new
setting has been added to systemd-repart drop-in files that allows GrowFileSystem= setting has been added to systemd-repart drop-in
configuring this partition flag. This new flag defaults to on for files that allows configuring this partition flag. This new flag
partitions automatically created by systemd-repart, except if they defaults to on for partitions automatically created by
are marked read-only. See the specification for further details: systemd-repart, except if they are marked read-only. See the
specification for further details:
https://systemd.io/DISCOVERABLE_PARTITIONS https://systemd.io/DISCOVERABLE_PARTITIONS
* .network files gained a new setting RoutesToNTP= in the [DHCPv4] * .network files gained a new setting RoutToNTP= in the [DHCPv4]
section. If enabled (which is the default), and an NTP server address section. If enabled (which is the default), and an NTP server address
is acquired through a DHCP lease on this interface an explicit route is acquired through a DHCP lease on this interface an explicit route
to this address is created on this interface to ensure that NTP to this address is created on this interface to ensure that NTP
traffic to the NTP server acquired on an interface is also routed traffic to the NTP server acquired on an interface is also routed
through that interface. The pre-existing RoutesToDNS= setting that trough that interface. The pre-existing RoutesToDNS= setting that
implements the same for DNS servers is now enabled by default. implements the same for DNS servers is now enabled by default.
* A pair of service settings SocketBindAllow= + SocketBindDeny= have * A pair of service settings SocketBindAllow= + SocketBindDeny= have
@ -249,27 +224,27 @@ CHANGES WITH 249 in spe:
sockets created by the service may be bound to. This is implemented sockets created by the service may be bound to. This is implemented
via BPF. via BPF.
* A new ConditionFirmware= condition type has been added to unit files. * A new ConditionFirmware= condition type has been added to unit
It may be used to detect certain firmware features. At the moment it files. It may be used to detect certain firmware features. At the
may check whether running on an UEFI system, a device.tree system, or moment it may check whether running on an UEFI system, a device.tree
if the system is compatible with some specified device-tree feature. system or if the system is compatible with some specified device-tree
feature.
* hostnamed gained a new Describe() D-Bus method that returns a JSON * hostnamed gained a new Describe() D-Bus method that returns a JSON
serialization of the host data it exposes. This is exposed via serialization of the host data it exposes. This is exposed via
"hostnamectl --json=" to acquire a host identity description in JSON. "hostnamectl --json=" to acquire a host identity description in
It's our intention to add a similar features to most services and JSON. It's our intention to add a similar concept to most services
objects systemd manages, in order to simplify integration with and objects systemd manages, in order to simplify integration with
program code that can consume JSON. program code the consumes JSON.
* Similarly, networkd gained a Describe() method on its Manager and * Similar, networkd gained a Describe() method on its Manager and Link
Link bus objects. This is exposed via "networkctl --json=". bus objects. This is exposed via "networkctl --json=".
* hostnamectl's various "get-xyz"/"set-xyz" verb pairs * hostnamectl's various set-xyz verbs (e.g. "hostnamectl set-hostname")
(e.g. "hostnamectl get-hostname", "hostnamectl "set-hostname") have have been renamed to just xyz (e.g. "hostnamectl hostname") and may
been replaced by a single "xyz" verb (e.g. "hostnamectl hostname") now be used to print the indicated data in terse form, instead of
that is used both to get the value (when no argument is given), and only setting it. The old names continue to be supported for
to set the value (when an argument is specified). The old names compatibility.
continue to be supported for compatibility.
* systemd-detect-virt and ConditionVirtualization= are now able to * systemd-detect-virt and ConditionVirtualization= are now able to
correctly identify Amazon EC2 environments. correctly identify Amazon EC2 environments.
@ -282,8 +257,8 @@ CHANGES WITH 249 in spe:
* bootctl gained support for a new --make-machine-id-directory= switch * bootctl gained support for a new --make-machine-id-directory= switch
that allows precise control on whether to create the top-level that allows precise control on whether to create the top-level
per-machine directory in the boot partition that typically contains per-machine directory in the boot partition that typically contain
Type 1 boot loader entries. Type #1 boot loader entries.
* During build SBAT data to include in the systemd-boot EFI PE binaries * During build SBAT data to include in the systemd-boot EFI PE binaries
may be specified now. may be specified now.
@ -369,6 +344,15 @@ CHANGES WITH 249 in spe:
journalctl, and permit exposing only the specified subset of the journalctl, and permit exposing only the specified subset of the
Journal records. Journal records.
* networkctl will now show an over-all "online" state in the per-link
information.
* In .network files a new OutgoingInterface= setting has been added to
specify the output interface in bridge FDB setups.
* In ,network files the Multipath group ID may now be configured for
[NextHop] entries, via the new Group= setting.
* The OnFailure= dependency between units is now augmented with a * The OnFailure= dependency between units is now augmented with a
implicit reverse dependency OnFailureOf= (this new dependency cannot implicit reverse dependency OnFailureOf= (this new dependency cannot
be configured directly it's only created as effect of an OnFailure= be configured directly it's only created as effect of an OnFailure=
@ -382,46 +366,6 @@ CHANGES WITH 249 in spe:
stop events between two units. It operates similar to the existing stop events between two units. It operates similar to the existing
PropagatesReloadTo= + ReloadPropagatedFrom= dependencies. PropagatesReloadTo= + ReloadPropagatedFrom= dependencies.
* A new dependency type OnSuccess= has been added (plus the reverse
dependency OnSuccessOf=, which cannot be configured directly, but
exists only as effect of the reverse OnSuccess=). It is similar to
OnFailure=, but triggers in the opposite case: when a service exits
cleanly. This allows "chaining up" of services where one or more
services are started once another service has successfully completed.
* A new dependency type Upholds= has been added (plus the reverse
dependency UpheldBy=, which cannot be configured directly, but exists
only as effect of Upholds=). This dependency type is a stronger form
of Wants=: if a unit has an UpHolds= dependency on some other unit
and the former is active then the latter is started whenever it is
found inactive (and no job is queued for it). This is an alternative
to Restart= inside service units, but less configurable, and the
request to uphold a unit is not encoded in the unit itself but in
another unit that intends to uphold it.
* The systemd-ask-password tool now also supports reading passwords
from the credentials subsystem, via the new --credential= switch.
* The systemd-ask-password tool learnt a new switch --emoji= which may
be used to explicit control whether the lock and key emoji (🔐) is
shown in the password prompt on suitable TTYs.
* The --echo switch of systemd-ask-password now optionally takes a
parameter that controls character echo. It may either show asterisks
(default, as before), turn echo off entirely, or echo the typed
characters literally.
* New documentation has been added that describes the organization of
the systemd source code tree:
https://systemd.io/ARCHITECTURE
* …
Contributions from: …
— Berlin, 2021-06-XX
CHANGES WITH 248: CHANGES WITH 248:
* A concept of system extension images is introduced. Such images may * A concept of system extension images is introduced. Such images may

View File

@ -1,5 +1,5 @@
--- ---
title: systemd Repository Architecture title: systemd repository architecture
category: Contributing category: Contributing
layout: default layout: default
--- ---
@ -13,19 +13,19 @@ components of the systemd repository.
Directories in `src/` provide the implementation of all daemons, libraries and Directories in `src/` provide the implementation of all daemons, libraries and
command-line tools shipped by the project. There are many, and more are command-line tools shipped by the project. There are many, and more are
constantly added, so we will not enumerate them all here the directory constantly added, so we will not enumerate them all here - the directory
names are self-explanatory. names are self-explanatory.
## Shared Code ## Shared Code
You might wonder what kind of common code belongs in `src/shared/` and what You might wonder what kind of common code belongs in `src/shared/` and what
belongs in `src/basic/`. The split is like this: anything that is used to belongs in `src/basic/`. The split is like this: anything that is used to
implement the public shared objects we provide (`sd-bus`, `sd-login`, implement the public shared object we provide (sd-bus, sd-login, sd-id128,
`sd-id128`, `nss-systemd`, `nss-mymachines`, `nss-resolve`, `nss-myhostname`, nss-systemd, nss-mymachines, nss-resolve, nss-myhostname, pam_systemd), must
`pam_systemd`), must be located in `src/basic` (those objects are not allowed be located in `src/basic` (those objects are not allowed to link to
to link to `libsystemd-shared.so`). Conversely, anything which is shared libsystemd-shared.so). Conversely, anything which is shared between multiple
between multiple components and does not need to be in `src/basic/`, should be components and does not need to be in `src/basic/`, should be in
in `src/shared/`. `src/shared/`.
To summarize: To summarize:
@ -63,10 +63,10 @@ file is compiled in a standalone binary that can be run to exercise the
corresponding module. While most of the tests can be ran by any user, some corresponding module. While most of the tests can be ran by any user, some
require privileges, and will attempt to clearly log about what they need require privileges, and will attempt to clearly log about what they need
(mostly in the form of effective capabilities). These tests are self-contained, (mostly in the form of effective capabilities). These tests are self-contained,
and generally safe to run on the host without side effects. and generally safe to run on a host without side effects.
Ideally, every module in `src/basic/` and `src/shared/` should have a Ideally, every module in `src/basic/` and `src/shared/` should have a corresponding
corresponding unit test under `src/test/`, exercising every helper function. unit test under `src/test/`, which exercises every helper function.
# Integration Tests # Integration Tests

View File

@ -625,9 +625,9 @@ evdev:atkbd:dmi:bvn*:bvr*:bd*:svnHewlett-Packard*:pnHPEliteBook*:*
evdev:atkbd:dmi:bvn*:bvr*:bd*:svnHP*:pnHPEliteBook*:* evdev:atkbd:dmi:bvn*:bvr*:bd*:svnHP*:pnHPEliteBook*:*
# HP ProBook 440 G2 # HP ProBook 440 G2
evdev:atkbd:dmi:bvn*:bvr*:bd*:svnHewlett-Packard*:pnHP440G2:* evdev:atkbd:dmi:bvn*:bvr*:bd*:svnHewlett-Packard*:pnHP440G2:*
# HP ProBook # several HP ProBooks 4xx
evdev:atkbd:dmi:bvn*:bvr*:bd*:svnHewlett-Packard*:pnHP*ProBook*:* evdev:atkbd:dmi:bvn*:bvr*:bd*:svnHewlett-Packard*:pnHP*ProBook4*:*
evdev:atkbd:dmi:bvn*:bvr*:bd*:svnHP*:pnHP*ProBook*:* evdev:atkbd:dmi:bvn*:bvr*:bd*:svnHP*:pnHP*ProBook*4*:*
# HP ZBook # HP ZBook
evdev:atkbd:dmi:bvn*:bvr*:bd*:svnHewlett-Packard*:pnHPZBook*:* evdev:atkbd:dmi:bvn*:bvr*:bd*:svnHewlett-Packard*:pnHPZBook*:*
evdev:atkbd:dmi:bvn*:bvr*:bd*:svnHP*:pnHPZBook*:* evdev:atkbd:dmi:bvn*:bvr*:bd*:svnHP*:pnHPZBook*:*

View File

@ -51,7 +51,7 @@ _udevadm() {
[INFO_STANDALONE]='-r --root -a --attribute-walk -x --export -e --export-db -c --cleanup-db [INFO_STANDALONE]='-r --root -a --attribute-walk -x --export -e --export-db -c --cleanup-db
-w --wait-for-initialization' -w --wait-for-initialization'
[INFO_ARG]='-q --query -p --path -n --name -P --export-prefix -d --device-id-of-file' [INFO_ARG]='-q --query -p --path -n --name -P --export-prefix -d --device-id-of-file'
[TRIGGER_STANDALONE]='-v --verbose -n --dry-run -q --quiet -w --settle --wait-daemon --uuid' [TRIGGER_STANDALONE]='-v --verbose -n --dry-run -q --quiet -w --settle --wait-daemon'
[TRIGGER_ARG]='-t --type -c --action -s --subsystem-match -S --subsystem-nomatch [TRIGGER_ARG]='-t --type -c --action -s --subsystem-match -S --subsystem-nomatch
-a --attr-match -A --attr-nomatch -p --property-match -a --attr-match -A --attr-nomatch -p --property-match
-g --tag-match -y --sysname-match --name-match -b --parent-match' -g --tag-match -y --sysname-match --name-match -b --parent-match'

View File

@ -31,8 +31,7 @@ _udevadm_trigger(){
'--property-match=[Trigger events for devices with a matching property value.]' \ '--property-match=[Trigger events for devices with a matching property value.]' \
'--tag-match=property[Trigger events for devices with a matching tag.]' \ '--tag-match=property[Trigger events for devices with a matching tag.]' \
'--sysname-match=[Trigger events for devices with a matching sys device name.]' \ '--sysname-match=[Trigger events for devices with a matching sys device name.]' \
'--parent-match=[Trigger events for all children of a given device.]' \ '--parent-match=[Trigger events for all children of a given device.]'
'--uuid[Print synthetic uevent UUID.]'
} }
(( $+functions[_udevadm_settle] )) || (( $+functions[_udevadm_settle] )) ||

View File

@ -19,6 +19,7 @@
#include "btrfs-util.h" #include "btrfs-util.h"
#include "chattr-util.h" #include "chattr-util.h"
#include "copy.h" #include "copy.h"
#include "device-nodes.h"
#include "fd-util.h" #include "fd-util.h"
#include "fileio.h" #include "fileio.h"
#include "fs-util.h" #include "fs-util.h"

16
src/basic/device-nodes.h Normal file
View File

@ -0,0 +1,16 @@
/* SPDX-License-Identifier: LGPL-2.1-or-later */
#pragma once
#include <stddef.h>
#include <sys/types.h>
#include "macro.h"
#include "stdio-util.h"
int encode_devnode_name(const char *str, char *str_enc, size_t len);
int allow_listed_char_for_devnode(char c, const char *additional);
#define DEV_NUM_PATH_MAX \
(STRLEN("/dev/block/") + DECIMAL_STR_MAX(dev_t) + 1 + DECIMAL_STR_MAX(dev_t))
#define xsprintf_dev_num_path(buf, type, devno) \
xsprintf(buf, "/dev/%s/%u:%u", type, major(devno), minor(devno))

View File

@ -526,16 +526,6 @@ char base64char(int x) {
return table[x & 63]; return table[x & 63];
} }
/* This is almost base64char(), but not entirely, as it uses the "url and filename safe" alphabet,
* since we don't want "/" appear in interface names (since interfaces appear in sysfs as filenames).
* See section #5 of RFC 4648. */
char urlsafe_base64char(int x) {
static const char table[64] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
"abcdefghijklmnopqrstuvwxyz"
"0123456789-_";
return table[x & 63];
}
int unbase64char(char c) { int unbase64char(char c) {
unsigned offset; unsigned offset;

View File

@ -27,7 +27,6 @@ char base32hexchar(int x) _const_;
int unbase32hexchar(char c) _const_; int unbase32hexchar(char c) _const_;
char base64char(int x) _const_; char base64char(int x) _const_;
char urlsafe_base64char(int x) _const_;
int unbase64char(char c) _const_; int unbase64char(char c) _const_;
char *base32hexmem(const void *p, size_t l, bool padding); char *base32hexmem(const void *p, size_t l, bool padding);

View File

@ -38,6 +38,8 @@ basic_sources = files('''
creds-util.c creds-util.c
creds-util.h creds-util.h
def.h def.h
device-nodes.c
device-nodes.h
dirent-util.c dirent-util.c
dirent-util.h dirent-util.h
dlfcn-util.c dlfcn-util.c

View File

@ -1475,7 +1475,7 @@ static int unit_add_mount_dependencies(Unit *u) {
r = unit_name_from_path(prefix, ".mount", &p); r = unit_name_from_path(prefix, ".mount", &p);
if (IN_SET(r, -EINVAL, -ENAMETOOLONG)) if (IN_SET(r, -EINVAL, -ENAMETOOLONG))
continue; /* If the path cannot be converted to a mount unit name, then it's continue; /* If the path cannot be converted to a mount unit name, then it's
* not manageable as a unit by systemd, and hence we don't need a * not managable as a unit by systemd, and hence we don't need a
* dependency on it. Let's thus silently ignore the issue. */ * dependency on it. Let's thus silently ignore the issue. */
if (r < 0) if (r < 0)
return r; return r;

View File

@ -11,7 +11,6 @@
#include "alloc-util.h" #include "alloc-util.h"
#include "ether-addr-util.h" #include "ether-addr-util.h"
#include "hexdecoct.h"
#include "lockfile-util.h" #include "lockfile-util.h"
#include "missing_network.h" #include "missing_network.h"
#include "netif-naming-scheme.h" #include "netif-naming-scheme.h"
@ -201,6 +200,16 @@ static int add_veth(
return 0; return 0;
} }
/* This is almost base64char(), but not entirely, as it uses the "url and filename safe" alphabet, since we
* don't want "/" appear in interface names (since interfaces appear in sysfs as filenames). See section #5
* of RFC 4648. */
static char urlsafe_base64char(int x) {
static const char table[64] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
"abcdefghijklmnopqrstuvwxyz"
"0123456789-_";
return table[x & 63];
}
static int shorten_ifname(char *ifname) { static int shorten_ifname(char *ifname) {
char new_ifname[IFNAMSIZ]; char new_ifname[IFNAMSIZ];

View File

@ -1,7 +0,0 @@
/* SPDX-License-Identifier: LGPL-2.1-or-later */
#pragma once
#include <stddef.h>
int encode_devnode_name(const char *str, char *str_enc, size_t len);
int allow_listed_char_for_devnode(char c, const char *additional);

View File

@ -80,8 +80,6 @@ shared_sources = files('''
daemon-util.h daemon-util.h
dev-setup.c dev-setup.c
dev-setup.h dev-setup.h
device-nodes.c
device-nodes.h
devnode-acl.h devnode-acl.h
discover-image.c discover-image.c
discover-image.h discover-image.h

View File

@ -1,11 +1,11 @@
/* SPDX-License-Identifier: LGPL-2.1-or-later */ /* SPDX-License-Identifier: LGPL-2.1-or-later */
#include <stdio.h>
#include <sys/types.h> #include <sys/types.h>
#include "alloc-util.h" #include "alloc-util.h"
#include "device-nodes.h" #include "device-nodes.h"
#include "string-util.h" #include "string-util.h"
#include "util.h"
/* helpers for test_encode_devnode_name */ /* helpers for test_encode_devnode_name */
static char *do_encode_string(const char *in) { static char *do_encode_string(const char *in) {

View File

@ -316,7 +316,7 @@ static void test_raw_clone(void) {
errno = 0; errno = 0;
assert_se(raw_clone(CLONE_FS|CLONE_NEWNS) == -1); assert_se(raw_clone(CLONE_FS|CLONE_NEWNS) == -1);
assert_se(errno == EINVAL || ERRNO_IS_PRIVILEGE(errno)); /* Certain container environments prohibit namespaces to us, don't fail in that case */ assert_se(errno == EINVAL);
} }
static void test_physical_memory(void) { static void test_physical_memory(void) {

View File

@ -200,12 +200,6 @@ tests += [
[threads, [threads,
libacl]], libacl]],
[['src/udev/test-udev-node.c'],
[libudevd_core,
libshared],
[threads,
libacl]],
[['src/udev/test-udev-builtin.c'], [['src/udev/test-udev-builtin.c'],
[libudevd_core, [libudevd_core,
libshared], libshared],

View File

@ -1,44 +0,0 @@
/* SPDX-License-Identifier: LGPL-2.1-or-later */
#include "tests.h"
#include "udev-node.h"
static void test_udev_node_escape_path_one(const char *path, const char *expected) {
char buf[NAME_MAX+1];
size_t r;
r = udev_node_escape_path(path, buf, sizeof buf);
log_debug("udev_node_escape_path(%s) -> %s (expected: %s)", path, buf, expected);
assert_se(r == strlen(expected));
assert_se(streq(buf, expected));
}
static void test_udev_node_escape_path(void) {
char a[NAME_MAX+1], b[NAME_MAX+1];
test_udev_node_escape_path_one("/disk/by-id/nvme-eui.1922908022470001001b448b44ccb9d6", "\\x2fdisk\\x2fby-id\\x2fnvme-eui.1922908022470001001b448b44ccb9d6");
test_udev_node_escape_path_one("/disk/by-id/nvme-eui.1922908022470001001b448b44ccb9d6-part1", "\\x2fdisk\\x2fby-id\\x2fnvme-eui.1922908022470001001b448b44ccb9d6-part1");
test_udev_node_escape_path_one("/disk/by-id/nvme-eui.1922908022470001001b448b44ccb9d6-part2", "\\x2fdisk\\x2fby-id\\x2fnvme-eui.1922908022470001001b448b44ccb9d6-part2");
test_udev_node_escape_path_one("/disk/by-id/nvme-WDC_PC_SN720_SDAQNTW-512G-1001_192290802247", "\\x2fdisk\\x2fby-id\\x2fnvme-WDC_PC_SN720_SDAQNTW-512G-1001_192290802247");
test_udev_node_escape_path_one("/disk/by-id/nvme-WDC_PC_SN720_SDAQNTW-512G-1001_192290802247-part1", "\\x2fdisk\\x2fby-id\\x2fnvme-WDC_PC_SN720_SDAQNTW-512G-1001_192290802247-part1");
test_udev_node_escape_path_one("/disk/by-id/nvme-WDC_PC_SN720_SDAQNTW-512G-1001_192290802247-part2", "\\x2fdisk\\x2fby-id\\x2fnvme-WDC_PC_SN720_SDAQNTW-512G-1001_192290802247-part2");
test_udev_node_escape_path_one("/disk/by-id/usb-Generic-_SD_MMC_20120501030900000-0:0", "\\x2fdisk\\x2fby-id\\x2fusb-Generic-_SD_MMC_20120501030900000-0:0");
memset(a, 'a', sizeof(a) - 1);
memcpy(a, "/disk/by-id/", strlen("/disk/by-id/"));
char_array_0(a);
memset(b, 'a', sizeof(b) - 1);
memcpy(b, "\\x2fdisk\\x2fby-id\\x2f", strlen("\\x2fdisk\\x2fby-id\\x2f"));
strcpy(b + sizeof(b) - 12, "N3YhcCqFeID");
test_udev_node_escape_path_one(a, b);
}
int main(int argc, char *argv[]) {
test_setup_logging(LOG_INFO);
test_udev_node_escape_path();
return 0;
}

View File

@ -7,16 +7,14 @@
#include <sys/stat.h> #include <sys/stat.h>
#include <unistd.h> #include <unistd.h>
#include "sd-id128.h"
#include "alloc-util.h" #include "alloc-util.h"
#include "device-nodes.h"
#include "device-private.h" #include "device-private.h"
#include "device-util.h" #include "device-util.h"
#include "dirent-util.h" #include "dirent-util.h"
#include "fd-util.h" #include "fd-util.h"
#include "format-util.h" #include "format-util.h"
#include "fs-util.h" #include "fs-util.h"
#include "hexdecoct.h"
#include "mkdir.h" #include "mkdir.h"
#include "path-util.h" #include "path-util.h"
#include "selinux-util.h" #include "selinux-util.h"
@ -28,36 +26,7 @@
#include "udev-node.h" #include "udev-node.h"
#include "user-util.h" #include "user-util.h"
#define CREATE_LINK_MAX_RETRIES 128
#define LINK_UPDATE_MAX_RETRIES 128 #define LINK_UPDATE_MAX_RETRIES 128
#define TOUCH_FILE_MAX_RETRIES 128
#define UDEV_NODE_HASH_KEY SD_ID128_MAKE(b9,6a,f1,ce,40,31,44,1a,9e,19,ec,8b,ae,f3,e3,2f)
static int create_symlink(const char *target, const char *slink) {
int r;
assert(target);
assert(slink);
for (unsigned i = 0; i < CREATE_LINK_MAX_RETRIES; i++) {
r = mkdir_parents_label(slink, 0755);
if (r == -ENOENT)
continue;
if (r < 0)
return r;
mac_selinux_create_file_prepare(slink, S_IFLNK);
if (symlink(target, slink) < 0)
r = -errno;
else
r = 0;
mac_selinux_create_file_clear();
if (r != -ENOENT)
return r;
}
return r;
}
static int node_symlink(sd_device *dev, const char *node, const char *slink) { static int node_symlink(sd_device *dev, const char *node, const char *slink) {
_cleanup_free_ char *slink_dirname = NULL, *target = NULL; _cleanup_free_ char *slink_dirname = NULL, *target = NULL;
@ -69,80 +38,87 @@ static int node_symlink(sd_device *dev, const char *node, const char *slink) {
assert(node); assert(node);
assert(slink); assert(slink);
r = path_extract_directory(slink, &slink_dirname); slink_dirname = dirname_malloc(slink);
if (r < 0) if (!slink_dirname)
return log_device_debug_errno(dev, r, "Failed to get parent directory of '%s': %m", slink); return log_oom();
/* use relative link */ /* use relative link */
r = path_make_relative(slink_dirname, node, &target); r = path_make_relative(slink_dirname, node, &target);
if (r < 0) if (r < 0)
return log_device_debug_errno(dev, r, "Failed to get relative path from '%s' to '%s': %m", slink, node); return log_device_error_errno(dev, r, "Failed to get relative path from '%s' to '%s': %m", slink, node);
if (lstat(slink, &stats) >= 0) { /* preserve link with correct target, do not replace node of other device */
if (lstat(slink, &stats) == 0) {
if (S_ISBLK(stats.st_mode) || S_ISCHR(stats.st_mode))
return log_device_error_errno(dev, SYNTHETIC_ERRNO(EOPNOTSUPP),
"Conflicting device node '%s' found, link to '%s' will not be created.", slink, node);
else if (S_ISLNK(stats.st_mode)) {
_cleanup_free_ char *buf = NULL; _cleanup_free_ char *buf = NULL;
if (!S_ISLNK(stats.st_mode))
return log_device_debug_errno(dev, SYNTHETIC_ERRNO(EEXIST),
"Conflicting inode '%s' found, link to '%s' will not be created.", slink, node);
if (readlink_malloc(slink, &buf) >= 0 && if (readlink_malloc(slink, &buf) >= 0 &&
path_equal(target, buf)) { streq(target, buf)) {
/* preserve link with correct target, do not replace node of other device */
log_device_debug(dev, "Preserve already existing symlink '%s' to '%s'", slink, target); log_device_debug(dev, "Preserve already existing symlink '%s' to '%s'", slink, target);
(void) label_fix(slink, LABEL_IGNORE_ENOENT); (void) label_fix(slink, LABEL_IGNORE_ENOENT);
(void) utimensat(AT_FDCWD, slink, NULL, AT_SYMLINK_NOFOLLOW); (void) utimensat(AT_FDCWD, slink, NULL, AT_SYMLINK_NOFOLLOW);
return 0; return 0;
} }
} else if (errno == ENOENT) { }
} else {
log_device_debug(dev, "Creating symlink '%s' to '%s'", slink, target); log_device_debug(dev, "Creating symlink '%s' to '%s'", slink, target);
do {
r = create_symlink(target, slink); r = mkdir_parents_label(slink, 0755);
if (r >= 0) if (!IN_SET(r, 0, -ENOENT))
break;
mac_selinux_create_file_prepare(slink, S_IFLNK);
if (symlink(target, slink) < 0)
r = -errno;
mac_selinux_create_file_clear();
} while (r == -ENOENT);
if (r == 0)
return 0; return 0;
if (r < 0)
log_device_debug_errno(dev, r, "Failed to create symlink '%s' to '%s', trying to replace '%s': %m", slink, target, slink); log_device_debug_errno(dev, r, "Failed to create symlink '%s' to '%s', trying to replace '%s': %m", slink, target, slink);
} else }
return log_device_debug_errno(dev, errno, "Failed to lstat() '%s': %m", slink);
log_device_debug(dev, "Atomically replace '%s'", slink); log_device_debug(dev, "Atomically replace '%s'", slink);
r = device_get_device_id(dev, &id); r = device_get_device_id(dev, &id);
if (r < 0) if (r < 0)
return log_device_debug_errno(dev, r, "Failed to get device id: %m"); return log_device_error_errno(dev, r, "Failed to get device id: %m");
slink_tmp = strjoina(slink, ".tmp-", id); slink_tmp = strjoina(slink, ".tmp-", id);
(void) unlink(slink_tmp); (void) unlink(slink_tmp);
do {
r = create_symlink(target, slink_tmp); r = mkdir_parents_label(slink_tmp, 0755);
if (!IN_SET(r, 0, -ENOENT))
break;
mac_selinux_create_file_prepare(slink_tmp, S_IFLNK);
if (symlink(target, slink_tmp) < 0)
r = -errno;
mac_selinux_create_file_clear();
} while (r == -ENOENT);
if (r < 0) if (r < 0)
return log_device_debug_errno(dev, r, "Failed to create symlink '%s' to '%s': %m", slink_tmp, target); return log_device_error_errno(dev, r, "Failed to create symlink '%s' to '%s': %m", slink_tmp, target);
if (rename(slink_tmp, slink) < 0) { if (rename(slink_tmp, slink) < 0) {
r = log_device_debug_errno(dev, errno, "Failed to rename '%s' to '%s': %m", slink_tmp, slink); r = log_device_error_errno(dev, errno, "Failed to rename '%s' to '%s': %m", slink_tmp, slink);
(void) unlink(slink_tmp); (void) unlink(slink_tmp);
return r; } else
}
/* Tell caller that we replaced already existing symlink. */ /* Tell caller that we replaced already existing symlink. */
return 1; r = 1;
return r;
} }
/* find device node of device with highest priority */
static int link_find_prioritized(sd_device *dev, bool add, const char *stackdir, char **ret) { static int link_find_prioritized(sd_device *dev, bool add, const char *stackdir, char **ret) {
_cleanup_closedir_ DIR *dir = NULL; _cleanup_closedir_ DIR *dir = NULL;
_cleanup_free_ char *target = NULL; _cleanup_free_ char *target = NULL;
struct dirent *dent; struct dirent *dent;
int r, priority = 0; int r, priority = 0;
const char *id;
assert(dev); assert(!add || dev);
assert(stackdir); assert(stackdir);
assert(ret); assert(ret);
/* Find device node of device with highest priority. This returns 1 if a device found, 0 if no
* device found, or a negative errno. */
if (add) { if (add) {
const char *devnode; const char *devnode;
@ -161,21 +137,17 @@ static int link_find_prioritized(sd_device *dev, bool add, const char *stackdir,
dir = opendir(stackdir); dir = opendir(stackdir);
if (!dir) { if (!dir) {
if (errno == ENOENT) { if (target) {
*ret = TAKE_PTR(target); *ret = TAKE_PTR(target);
return !!*ret; return 0;
} }
return -errno; return -errno;
} }
r = device_get_device_id(dev, &id);
if (r < 0)
return r;
FOREACH_DIRENT_ALL(dent, dir, break) { FOREACH_DIRENT_ALL(dent, dir, break) {
_cleanup_(sd_device_unrefp) sd_device *dev_db = NULL; _cleanup_(sd_device_unrefp) sd_device *dev_db = NULL;
const char *devnode; const char *devnode, *id;
int db_prio = 0; int db_prio = 0;
if (dent->d_name[0] == '\0') if (dent->d_name[0] == '\0')
@ -185,6 +157,9 @@ static int link_find_prioritized(sd_device *dev, bool add, const char *stackdir,
log_device_debug(dev, "Found '%s' claiming '%s'", dent->d_name, stackdir); log_device_debug(dev, "Found '%s' claiming '%s'", dent->d_name, stackdir);
if (device_get_device_id(dev, &id) < 0)
continue;
/* did we find ourself? */ /* did we find ourself? */
if (streq(dent->d_name, id)) if (streq(dent->d_name, id))
continue; continue;
@ -209,73 +184,59 @@ static int link_find_prioritized(sd_device *dev, bool add, const char *stackdir,
priority = db_prio; priority = db_prio;
} }
if (!target)
return -ENOENT;
*ret = TAKE_PTR(target); *ret = TAKE_PTR(target);
return !!*ret; return 0;
} }
size_t udev_node_escape_path(const char *src, char *dest, size_t size) { static size_t escape_path(const char *src, char *dest, size_t size) {
size_t i, j; size_t i, j;
uint64_t h;
assert(src); assert(src);
assert(dest); assert(dest);
for (i = 0, j = 0; src[i] != '\0'; i++) { for (i = 0, j = 0; src[i] != '\0'; i++) {
if (src[i] == '/') { if (src[i] == '/') {
if (j+4 >= size) if (j+4 >= size) {
goto toolong; j = 0;
break;
}
memcpy(&dest[j], "\\x2f", 4); memcpy(&dest[j], "\\x2f", 4);
j += 4; j += 4;
} else if (src[i] == '\\') { } else if (src[i] == '\\') {
if (j+4 >= size) if (j+4 >= size) {
goto toolong; j = 0;
break;
}
memcpy(&dest[j], "\\x5c", 4); memcpy(&dest[j], "\\x5c", 4);
j += 4; j += 4;
} else { } else {
if (j+1 >= size) if (j+1 >= size) {
goto toolong; j = 0;
break;
}
dest[j] = src[i]; dest[j] = src[i];
j++; j++;
} }
} }
dest[j] = '\0'; dest[j] = '\0';
return j; return j;
toolong:
/* If the input path is too long to encode as a filename, then let's suffix with a string
* generated from the hash of the path. */
h = siphash24_string(src, UDEV_NODE_HASH_KEY.bytes);
assert(size >= 12);
for (unsigned k = 0; k <= 10; k++)
dest[size - k - 2] = urlsafe_base64char((h >> (k * 6)) & 63);
dest[size - 1] = '\0';
return size - 1;
} }
/* manage "stack of names" with possibly specified device priorities */ /* manage "stack of names" with possibly specified device priorities */
static int link_update(sd_device *dev, const char *slink_in, bool add) { static int link_update(sd_device *dev, const char *slink, bool add) {
_cleanup_free_ char *slink = NULL, *filename = NULL, *dirname = NULL; _cleanup_free_ char *filename = NULL, *dirname = NULL;
const char *slink_name, *id; const char *slink_name, *id;
char name_enc[NAME_MAX+1]; char name_enc[PATH_MAX];
int i, r, retries; int i, r, retries;
assert(dev); assert(dev);
assert(slink_in); assert(slink);
slink = strdup(slink_in);
if (!slink)
return log_oom_debug();
path_simplify(slink);
slink_name = path_startswith(slink, "/dev"); slink_name = path_startswith(slink, "/dev");
if (!slink_name || if (!slink_name)
empty_or_root(slink_name) ||
!path_is_normalized(slink_name))
return log_device_debug_errno(dev, SYNTHETIC_ERRNO(EINVAL), return log_device_debug_errno(dev, SYNTHETIC_ERRNO(EINVAL),
"Invalid symbolic link of device node: %s", slink); "Invalid symbolic link of device node: %s", slink);
@ -283,30 +244,30 @@ static int link_update(sd_device *dev, const char *slink_in, bool add) {
if (r < 0) if (r < 0)
return log_device_debug_errno(dev, r, "Failed to get device id: %m"); return log_device_debug_errno(dev, r, "Failed to get device id: %m");
(void) udev_node_escape_path(slink_name, name_enc, sizeof(name_enc)); escape_path(slink_name, name_enc, sizeof(name_enc));
dirname = path_join("/run/udev/links/", name_enc); dirname = path_join("/run/udev/links/", name_enc);
if (!dirname) if (!dirname)
return log_oom_debug(); return log_oom();
filename = path_join(dirname, id); filename = path_join(dirname, id);
if (!filename) if (!filename)
return log_oom_debug(); return log_oom();
if (!add) { if (!add) {
if (unlink(filename) < 0 && errno != ENOENT) if (unlink(filename) == 0)
log_device_debug_errno(dev, errno, "Failed to remove %s, ignoring: %m", filename);
(void) rmdir(dirname); (void) rmdir(dirname);
} else { } else
for (unsigned j = 0; j < TOUCH_FILE_MAX_RETRIES; j++) { for (;;) {
/* This may fail with -ENOENT when the parent directory is removed during _cleanup_close_ int fd = -1;
* creating the file by another udevd worker. */
r = touch_file(filename, /* parents= */ true, USEC_INFINITY, UID_INVALID, GID_INVALID, 0444); r = mkdir_parents(filename, 0755);
if (r != -ENOENT) if (!IN_SET(r, 0, -ENOENT))
return r;
fd = open(filename, O_WRONLY|O_CREAT|O_CLOEXEC|O_TRUNC|O_NOFOLLOW, 0444);
if (fd >= 0)
break; break;
} if (errno != ENOENT)
if (r < 0) return -errno;
return log_device_debug_errno(dev, r, "Failed to create %s: %m", filename);
} }
/* If the database entry is not written yet we will just do one iteration and possibly wrong symlink /* If the database entry is not written yet we will just do one iteration and possibly wrong symlink
@ -319,25 +280,23 @@ static int link_update(sd_device *dev, const char *slink_in, bool add) {
r = stat(dirname, &st1); r = stat(dirname, &st1);
if (r < 0 && errno != ENOENT) if (r < 0 && errno != ENOENT)
return log_device_debug_errno(dev, errno, "Failed to stat %s: %m", dirname); return -errno;
r = link_find_prioritized(dev, add, dirname, &target); r = link_find_prioritized(dev, add, dirname, &target);
if (r < 0) if (r == -ENOENT) {
return log_device_debug_errno(dev, r, "Failed to determine highest priority for symlink '%s': %m", slink); log_device_debug(dev, "No reference left, removing '%s'", slink);
if (r == 0) { if (unlink(slink) == 0)
log_device_debug(dev, "No reference left for '%s', removing", slink); (void) rmdir_parents(slink, "/");
if (unlink(slink) < 0 && errno != ENOENT)
log_device_debug_errno(dev, errno, "Failed to remove '%s', ignoring: %m", slink);
(void) rmdir_parents(slink, "/dev");
break; break;
} } else if (r < 0)
return log_device_error_errno(dev, r, "Failed to determine highest priority symlink: %m");
r = node_symlink(dev, target, slink); r = node_symlink(dev, target, slink);
if (r < 0) if (r < 0) {
return r; (void) unlink(filename);
if (r == 1) break;
} else if (r == 1)
/* We have replaced already existing symlink, possibly there is some other device trying /* We have replaced already existing symlink, possibly there is some other device trying
* to claim the same symlink. Let's do one more iteration to give us a chance to fix * to claim the same symlink. Let's do one more iteration to give us a chance to fix
* the error if other device actually claims the symlink with higher priority. */ * the error if other device actually claims the symlink with higher priority. */
@ -347,7 +306,7 @@ static int link_update(sd_device *dev, const char *slink_in, bool add) {
if ((st1.st_mode & S_IFMT) != 0) { if ((st1.st_mode & S_IFMT) != 0) {
r = stat(dirname, &st2); r = stat(dirname, &st2);
if (r < 0 && errno != ENOENT) if (r < 0 && errno != ENOENT)
return log_device_debug_errno(dev, errno, "Failed to stat %s: %m", dirname); return -errno;
if (stat_inode_unmodified(&st1, &st2)) if (stat_inode_unmodified(&st1, &st2))
break; break;
@ -358,12 +317,16 @@ static int link_update(sd_device *dev, const char *slink_in, bool add) {
} }
int udev_node_update_old_links(sd_device *dev, sd_device *dev_old) { int udev_node_update_old_links(sd_device *dev, sd_device *dev_old) {
const char *name; const char *name, *devpath;
int r; int r;
assert(dev); assert(dev);
assert(dev_old); assert(dev_old);
r = sd_device_get_devpath(dev, &devpath);
if (r < 0)
return log_device_debug_errno(dev, r, "Failed to get devpath: %m");
/* update possible left-over symlinks */ /* update possible left-over symlinks */
FOREACH_DEVICE_DEVLINK(dev_old, name) { FOREACH_DEVICE_DEVLINK(dev_old, name) {
const char *name_current; const char *name_current;
@ -379,10 +342,8 @@ int udev_node_update_old_links(sd_device *dev, sd_device *dev_old) {
if (found) if (found)
continue; continue;
log_device_debug(dev, log_device_debug(dev, "Updating old name, '%s' no longer belonging to '%s'",
"Updating old device symlink '%s', which is no longer belonging to this device.", name, devpath);
name);
r = link_update(dev, name, false); r = link_update(dev, name, false);
if (r < 0) if (r < 0)
log_device_warning_errno(dev, r, log_device_warning_errno(dev, r,
@ -516,6 +477,7 @@ static int node_permissions_apply(sd_device *dev, bool apply_mac,
} }
static int xsprintf_dev_num_path_from_sd_device(sd_device *dev, char **ret) { static int xsprintf_dev_num_path_from_sd_device(sd_device *dev, char **ret) {
char filename[DEV_NUM_PATH_MAX], *s;
const char *subsystem; const char *subsystem;
dev_t devnum; dev_t devnum;
int r; int r;
@ -530,7 +492,16 @@ static int xsprintf_dev_num_path_from_sd_device(sd_device *dev, char **ret) {
if (r < 0) if (r < 0)
return r; return r;
return device_path_make_major_minor(streq(subsystem, "block") ? S_IFBLK : S_IFCHR, devnum, ret); xsprintf_dev_num_path(filename,
streq(subsystem, "block") ? "block" : "char",
devnum);
s = strdup(filename);
if (!s)
return -ENOMEM;
*ret = s;
return 0;
} }
int udev_node_add(sd_device *dev, bool apply, int udev_node_add(sd_device *dev, bool apply,
@ -557,6 +528,13 @@ int udev_node_add(sd_device *dev, bool apply,
if (r < 0) if (r < 0)
return r; return r;
r = xsprintf_dev_num_path_from_sd_device(dev, &filename);
if (r < 0)
return log_device_debug_errno(dev, r, "Failed to get device path: %m");
/* always add /dev/{block,char}/$major:$minor */
(void) node_symlink(dev, devnode, filename);
/* create/update symlinks, add symlinks to name index */ /* create/update symlinks, add symlinks to name index */
FOREACH_DEVICE_DEVLINK(dev, devlink) { FOREACH_DEVICE_DEVLINK(dev, devlink) {
r = link_update(dev, devlink, true); r = link_update(dev, devlink, true);
@ -566,15 +544,6 @@ int udev_node_add(sd_device *dev, bool apply,
devlink); devlink);
} }
r = xsprintf_dev_num_path_from_sd_device(dev, &filename);
if (r < 0)
return log_device_debug_errno(dev, r, "Failed to get device path: %m");
/* always add /dev/{block,char}/$major:$minor */
r = node_symlink(dev, devnode, filename);
if (r < 0)
return log_device_warning_errno(dev, r, "Failed to create device symlink '%s': %m", filename);
return 0; return 0;
} }
@ -599,8 +568,7 @@ int udev_node_remove(sd_device *dev) {
return log_device_debug_errno(dev, r, "Failed to get device path: %m"); return log_device_debug_errno(dev, r, "Failed to get device path: %m");
/* remove /dev/{block,char}/$major:$minor */ /* remove /dev/{block,char}/$major:$minor */
if (unlink(filename) < 0 && errno != ENOENT) (void) unlink(filename);
return log_device_debug_errno(dev, errno, "Failed to remove '%s': %m", filename);
return 0; return 0;
} }

View File

@ -13,5 +13,3 @@ int udev_node_add(sd_device *dev, bool apply,
OrderedHashmap *seclabel_list); OrderedHashmap *seclabel_list);
int udev_node_remove(sd_device *dev); int udev_node_remove(sd_device *dev);
int udev_node_update_old_links(sd_device *dev, sd_device *dev_old); int udev_node_update_old_links(sd_device *dev, sd_device *dev_old);
size_t udev_node_escape_path(const char *src, char *dest, size_t size);

View File

@ -1,78 +0,0 @@
#!/usr/bin/python
# SPDX-License-Identifier: LGPL-2.1-or-later
"""
A helper to compare 'systemd-analyze dump' outputs.
systemd-analyze dump >/var/tmp/dump1
(reboot)
tools/analyze-dump-sort.py /var/tmp/dump1 this does a diff from dump1 to current
systemd-analyze dump >/var/tmp/dump2
tools/analyze-dump-sort.py /var/tmp/{dump1,dump2} this does a diff from dump1 to dump2
"""
import argparse
import tempfile
import subprocess
def sort_dump(sourcefile, destfile=None):
if destfile is None:
destfile = tempfile.NamedTemporaryFile('wt')
units = {}
unit = []
same = []
for line in sourcefile:
line = line.rstrip()
header = line.split(':')[0]
if 'Timestamp' in header or 'Invocation ID' in header or 'PID' in header:
line = header + ': …'
if line.startswith('->'):
if unit:
units[unit[0]] = unit
unit = [line]
elif line.startswith('\t'):
assert unit
if same and same[0].startswith(header):
same.append(line)
else:
unit.extend(sorted(same, key=str.lower))
same = [line]
else:
print(line, file=destfile)
if unit:
units[unit[0]] = unit
for unit in sorted(units.values()):
print('\n'.join(unit), file=destfile)
destfile.flush()
return destfile
def parse_args():
p = argparse.ArgumentParser(description=__doc__)
p.add_argument('one')
p.add_argument('two', nargs='?')
p.add_argument('--user', action='store_true')
return p.parse_args()
if __name__ == '__main__':
opts = parse_args()
one = sort_dump(open(opts.one))
if opts.two:
two = sort_dump(open(opts.two))
else:
user = ['--user'] if opts.user else []
two = subprocess.run(['systemd-analyze', 'dump', *user],
capture_output=True, text=True, check=True)
two = sort_dump(two.stdout.splitlines())
with subprocess.Popen(['diff', '-U10', one.name, two.name], stdout=subprocess.PIPE) as diff:
subprocess.Popen(['less'], stdin=diff.stdout)