1
0
mirror of https://github.com/systemd/systemd synced 2026-03-17 18:44:46 +01:00

Compare commits

...

27 Commits

Author SHA1 Message Date
Yu Watanabe
efe76b273a shell-completion: udevadm: support --uuid option
Follow-up for 730b9c1e1469dfd6d6850e9ea06da3cac469eba0.
2021-06-04 15:00:11 +02:00
Yu Watanabe
16f9036471
Merge pull request #19796 from yuwata/udev-node-cleanups
udev: several cleanups about creating device symlink
2021-06-04 21:46:43 +09:00
Kai-Heng Feng
a7161e0288 hwdb: Add ProBook to use micmute hotkey
Like HP EliteBooks and ZBooks, all ProBooks use the same micmute
scancode.
2021-06-04 14:00:25 +02:00
Lennart Poettering
d414f41535 test: don't expect that clone() always makes it to the kernel
We might get EPERM on certain clone() flag combinations. Apparently in
Docker for example.

Prompted by: https://github.com/systemd/systemd/pull/19800#issuecomment-854196491
2021-06-04 13:38:00 +02:00
Zbigniew Jędrzejewski-Szmek
19c79550b4 tools/analyze-dump-sort: a helper to compare two 'systemd-analyze dump' outputs
Lines in the dumps are ordered by some pseudo-random hashmap entry order, which
makes it hard to diff two outputs. This sort the entries alphabetically, and
also sorts items within the entries, and supresses timestamps and other fields
which always vary.

We could sort the output inside of systemd itself, but it'd make things more
complex, and we probably don't need output to be sorted in most cases. It also
wouldn't be enough, because timestamps and such would still need to be ignored
to do a nice diff. So I think doing the sorting and suppression in a python
helper is a better approach.
2021-06-04 12:09:58 +02:00
Zbigniew Jędrzejewski-Szmek
771bdb6aae docs/ARCHITECTURE: more markup and unicode 2021-06-04 10:51:19 +02:00
Zbigniew Jędrzejewski-Szmek
287079696c NEWS: adjust grammar and other small changes 2021-06-04 10:51:19 +02:00
Yu Watanabe
4076ad9daf fix typo 2021-06-04 10:37:40 +02:00
Lennart Poettering
00d06c996d docs: use uppercase letters in title
We usually do it that way, do so here, too.
2021-06-04 10:34:56 +02:00
Lennart Poettering
99c2a9552b more 249 NEWS work 2021-06-04 10:34:02 +02:00
Yu Watanabe
78d8eae9a5 util: drop DEV_NUM_PATH_MAX and xsprintf_dev_num_path() 2021-06-04 15:51:39 +09:00
Yu Watanabe
902b4c677e util: move device-node.[ch] to shared 2021-06-04 15:51:39 +09:00
Yu Watanabe
ebb0a0155d udev: warn and propagate error in creating device symlink
Also, this makes the file in /run/udev/links/ is kept on failure, as the
target of the symbolic link may be belonging to another device.
2021-06-04 15:51:39 +09:00
Yu Watanabe
c891389a16 udev: try to create device symlink directly only when the link does not exist yet 2021-06-04 15:51:39 +09:00
Yu Watanabe
5802d4ea03 udev: extract same logic of creating device symlink
This also limits the number of trial.
2021-06-04 15:51:39 +09:00
Yu Watanabe
1ddfb6cf29 udev: use path_extract_directory() and path_equal() 2021-06-04 15:51:39 +09:00
Yu Watanabe
f3b393e951 udev: refuse to create device symlink when a non-symlink file already exists 2021-06-04 15:51:39 +09:00
Yu Watanabe
d2b50631fb udev: make link_find_prioritized() return 0, 1, or negative errno 2021-06-04 15:51:39 +09:00
Yu Watanabe
e7f3b33e70 udev: slightly update log message and adjust log level 2021-06-04 15:51:39 +09:00
Yu Watanabe
a33dc87e42 udev: logs if failed to remove devlink 2021-06-04 15:51:39 +09:00
Yu Watanabe
e91454231b udev: do not try to remove /dev 2021-06-04 15:51:39 +09:00
Yu Watanabe
5733bd4862 udev: use touch_file() and limit the number of trial 2021-06-04 15:51:39 +09:00
Yu Watanabe
286bedd7a4 udev: logs when failed to remove saved info about devlink 2021-06-04 15:51:39 +09:00
Yu Watanabe
be322ecafb udev: refuse unsafe device symbolic link 2021-06-04 15:51:39 +09:00
Yu Watanabe
52fde28014 test: add tests for udev_node_escape_path() 2021-06-04 15:51:39 +09:00
Yu Watanabe
e64943363a udev: use hashed path as a filename to save devlink 2021-06-04 15:51:31 +09:00
Yu Watanabe
130298ba10 util: expose urlsafe_base64char() 2021-06-04 01:48:50 +09:00
22 changed files with 502 additions and 291 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
tools (such as systemd-nspawn or systemd-dissect), and multiple
suitable root or /usr/ partitions exist in the image, then a simple
strverscmp() inspired comparison is done on the GPT partition label,
and the newest partition picked. This permits a simple and generic
whole-file-system A/B update logic where new operating system
comparison inspired by strverscmp() is done on the GPT partition
label, and the newest partition is picked. This permits a simple and
generic whole-file-system A/B update logic where new operating system
versions are dropped into partitions whose label is then updated with
a matching version identifier.
@ -34,9 +34,6 @@ CHANGES WITH 249 in spe:
previously unprovisioned images (i.e. images with a mostly empty
/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
determine when a service exited: the default is "main" which defines
the runtime by the service's main process lifetime (this matches the
@ -47,30 +44,31 @@ CHANGES WITH 249 in spe:
* 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
--root= operates on an OS file tree. This matches the existing switch
of the same name to the systemd-tmpfiles, systemd-firstboot or
of the same name for systemd-tmpfiles, systemd-firstboot, and
systemd-sysusers tools.
* Similar, systemd-repart gained support for a --image= switch, too. In
combination with the existing --size= makes the tool particularly
useful for easily growing disk images in a single command invocation,
following the declarative rules included in the image itself.
* Similarly, systemd-repart gained support for the --image= switch too.
In combination with the existing --size= option, this makes the tool
particularly useful for easily growing disk images in a single
invocation, following the declarative rules included in the image
itself.
* systemd-repart's partition configuration files gained support for a
new switch MakeDirectories= which may be used to create arbitrary
directories inside file systems that are created, before registering
them in the partition table. This is useful in particular if root
partitions are created that way to create mount point directories for
other partitions included in the image. For example, a disk image
that is created to contain a /home/ and a /var/ partition in addition
to a root partition may MakeDirectories= to create /home/ and /var/
as empty directories in the root file system on creation so that the
resulting image can mounted immediately, even in read-only mode.
them in the partition table. This is useful in particular for root
partitions to create mount point directories for other partitions
included in the image. For example, a disk image that contains a
root, /home/, and /var/ partitions, may set MakeDirectories=yes to
create /home/ and /var/ as empty directories in the root file system
on its creation, so that the resulting image can mounted immediately,
even in read-only mode.
* systemd-repart's CopyBlocks= setting gained support for a special
value "auto". If used a suitable, matching partition on the booted OS
is found as source to copy blocks from. This is useful for
* systemd-repart's CopyBlocks= setting gained support for the special
value "auto". If used, a suitable matching partition on the booted OS
is found as source to copy blocks from. This is useful when
implementing replicating installers, that are booted from one medium
and then stream their own root partition on the target medium.
and then stream their own root partition onto the target medium.
* systemd-repart's partition configuration files gained support for a
Flags= and a ReadOnly= setting, allowing control of the GPT partition
@ -78,36 +76,54 @@ CHANGES WITH 249 in spe:
created partitions as read-only from the start.
* The /etc/os-release file has been extended with two new (optional)
variables IMAGE_VERSION= and IMAGE_ID=, for carrying identification
and versioning information for OS images that are updated
consistently, comprehensively and atomically as one image. The two
new specifiers %M, %A now resolve to these two fields in the various
configuration options that resolve specifiers.
variables IMAGE_VERSION= and IMAGE_ID=, carrying identity and version
information for OS images that are updated comprehensively and
atomically as one image. Two new specifiers %M, %A now resolve to
these two fields in the various configuration options that resolve
specifiers.
* portablectl gained a new switch --extension= for enabling portable
service images with extensions that follow the extension image
concept introduced with v248.
* systemd-coredump will now extract ELF build-id information from
processes dumping core and include it in the coredump
report. Moreover, if will look for ELF .note.package sections that
may carry distribution packaging meta-information about the crashing
process. This is useful to directly embed the RPM or Debian (or any
other) package name and version in the ELF files where they originate
from, making it easy to match up coredump reports with the software
versions they were found it. This is particular useful on
environments with ELF files form multiple vendors, different
distributions and versions, as it is common today, in particular in a
containerized and sand-boxed world. For further information, see:
processes dumping core and include it in the coredump report.
Moreover, it will look for ELF .note.package sections with
distribution packaging meta-information about the crashing process.
This is useful to directly embed the rpm or deb (or any other)
package name and version in ELF files, making it easy to match
coredump reports with the specific package for which the software was
compiled. This is particularly useful on environments with ELF files
from multiple vendors, different distributions and versions, as is
common today in our containerized and sand-boxed world. For further
information, see:
https://systemd.io/COREDUMP_PACKAGE_METADATA
* A new udev hwdb has been added for Firewire audio devices
* A new udev hardware database has been added for FireWire devices
(IEEE 1394).
* 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,
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
this as alternative to the classic BSD syslog protocol for locally
delivering log records to the Journal. The protocol has been stable
@ -142,81 +158,90 @@ CHANGES WITH 249 in spe:
fine-tune whether to require an IPv4 or IPv6 address in order to
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
new special value "none". If specified sockets of all address
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
support booting from disks that carry only a /usr/ partition but no
root partition yet, but where systemd-repart can add them in on first
boot. This is useful for implementing systems that ship with a single
/usr/ file system and whose root file system shall be set up on a
LUKS encrypted volume whose key is generated locally (and possibly
enrolled in TPM), and which is set up and formatted during first
boot.
root partition yet, and where systemd-repart can add it in on the
first boot. This is useful for implementing systems that ship with a
single /usr/ file system, and whose root file system shall be set up
and formatted on a LUKS-encrypted volume whose key is generated
locally (and possibly enrolled in the TPM) during the first boot.
* The [Address] section of .network files now accepts a new
RouteMetric= setting that configures the routing metric to use for
the prefix route created as effect of the address
configuration. Similar, the [DHCPv6PrefixDelegation] and [IPv6Prefix]
sections gained matching settings for their prefix routes. (The
option of the same name of the [DHCPv6] section is moved to
[IPv6AcceptRA], since it conceptually belongs there; the old option
is still understood for compatibility.)
the prefix route created as effect of the address configuration.
Similarly, the [DHCPv6PrefixDelegation] and [IPv6Prefix] sections
gained matching settings for their prefix routes. (The option of the
same name in the [DHCPv6] section is moved to [IPv6AcceptRA], since
it conceptually belongs there; the old option is still understood for
compatibility.)
* The DHCPv6 IAID and DUID are now explicitly configurable in .network
files.
* A new udev property ID_NET_DHCP_BROADCAST on network interface
devices is not honoured by systemd-networkd, that controls whether to
devices is now honoured by systemd-networkd, controlling whether to
issue DHCP offers via broadcasting. This is used to ensure that s390
layer 3 network interfaces work out-of-the-box with systemd-networkd.
* nss-myhostname and systemd-resolved will now synthesize address
records for a new special hostname "_outbound". The name will always
resolve to the local IP addresses most likely used for outbound
connections towards the default route. On multi-homed hosts this is
connections towards the default routes. On multi-homed hosts this is
useful to have a stable handle referring to "the" local IP address
that matters most, to the point where this is defined.
* The Discoverable Partition Specification has been updated with a new
GPT partition flag defined its partition types: a "growfs"
flag. Whenever partitions with this flag set are automatically
mounted (i.e. via systemd-gpt-auto-generator or the --image= switch
of systemd-nspawn or other tools; and as opposed to explicit mounting
GPT partition flag "growsfs" defined for its partition types.
Whenever partitions with this flag set are automatically mounted
(i.e. via systemd-gpt-auto-generator or the --image= switch of
systemd-nspawn or other tools; and as opposed to explicit mounting
via /etc/fstab), the file system within the partition is
automatically grown to the full size of the partition should it be
smaller. If the file system size already matches the partition size
this flag has no effect. Previously, this functionality has been
available via the explicit x-systemd.growfs mount option, and this
new flag extends this to automatically discovered mounts. A new
GrowFileSystem= setting has been added to systemd-repart drop-in
files that allows configuring this partition flag. This new flag
defaults to on for partitions automatically created by
systemd-repart, except if they are marked read-only. See the
specification for further details:
automatically grown to the full size of the partition. If the file
system size already matches the partition size this flag has no
effect. Previously, this functionality has been available via the
explicit x-systemd.growfs mount option, and this new flag extends
this to automatically discovered mounts. A new GrowFileSystem=
setting has been added to systemd-repart drop-in files that allows
configuring this partition flag. This new flag defaults to on for
partitions automatically created by systemd-repart, except if they
are marked read-only. See the specification for further details:
https://systemd.io/DISCOVERABLE_PARTITIONS
* .network files gained a new setting RoutToNTP= in the [DHCPv4]
* .network files gained a new setting RoutesToNTP= in the [DHCPv4]
section. If enabled (which is the default), and an NTP server address
is acquired through a DHCP lease on this interface an explicit route
to this address is created on this interface to ensure that NTP
traffic to the NTP server acquired on an interface is also routed
trough that interface. The pre-existing RoutesToDNS= setting that
through that interface. The pre-existing RoutesToDNS= setting that
implements the same for DNS servers is now enabled by default.
* A pair of service settings SocketBindAllow= + SocketBindDeny= have
@ -224,27 +249,27 @@ CHANGES WITH 249 in spe:
sockets created by the service may be bound to. This is implemented
via BPF.
* A new ConditionFirmware= condition type has been added to unit
files. It may be used to detect certain firmware features. At the
moment it may check whether running on an UEFI system, a device.tree
system or if the system is compatible with some specified device-tree
feature.
* A new ConditionFirmware= condition type has been added to unit files.
It may be used to detect certain firmware features. At the moment it
may check whether running on an UEFI system, a device.tree 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
serialization of the host data it exposes. This is exposed via
"hostnamectl --json=" to acquire a host identity description in
JSON. It's our intention to add a similar concept to most services
and objects systemd manages, in order to simplify integration with
program code the consumes JSON.
"hostnamectl --json=" to acquire a host identity description in JSON.
It's our intention to add a similar features to most services and
objects systemd manages, in order to simplify integration with
program code that can consume JSON.
* Similar, networkd gained a Describe() method on its Manager and Link
bus objects. This is exposed via "networkctl --json=".
* Similarly, networkd gained a Describe() method on its Manager and
Link bus objects. This is exposed via "networkctl --json=".
* hostnamectl's various set-xyz verbs (e.g. "hostnamectl set-hostname")
have been renamed to just xyz (e.g. "hostnamectl hostname") and may
now be used to print the indicated data in terse form, instead of
only setting it. The old names continue to be supported for
compatibility.
* hostnamectl's various "get-xyz"/"set-xyz" verb pairs
(e.g. "hostnamectl get-hostname", "hostnamectl "set-hostname") have
been replaced by a single "xyz" verb (e.g. "hostnamectl hostname")
that is used both to get the value (when no argument is given), and
to set the value (when an argument is specified). The old names
continue to be supported for compatibility.
* systemd-detect-virt and ConditionVirtualization= are now able to
correctly identify Amazon EC2 environments.
@ -257,8 +282,8 @@ CHANGES WITH 249 in spe:
* bootctl gained support for a new --make-machine-id-directory= switch
that allows precise control on whether to create the top-level
per-machine directory in the boot partition that typically contain
Type #1 boot loader entries.
per-machine directory in the boot partition that typically contains
Type 1 boot loader entries.
* During build SBAT data to include in the systemd-boot EFI PE binaries
may be specified now.
@ -344,15 +369,6 @@ CHANGES WITH 249 in spe:
journalctl, and permit exposing only the specified subset of the
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
implicit reverse dependency OnFailureOf= (this new dependency cannot
be configured directly it's only created as effect of an OnFailure=
@ -366,6 +382,46 @@ CHANGES WITH 249 in spe:
stop events between two units. It operates similar to the existing
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:
* 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
layout: default
---
@ -13,19 +13,19 @@ components of the systemd repository.
Directories in `src/` provide the implementation of all daemons, libraries and
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.
## Shared Code
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
implement the public shared object we provide (sd-bus, sd-login, sd-id128,
nss-systemd, nss-mymachines, nss-resolve, nss-myhostname, pam_systemd), must
be located in `src/basic` (those objects are not allowed to link to
libsystemd-shared.so). Conversely, anything which is shared between multiple
components and does not need to be in `src/basic/`, should be in
`src/shared/`.
implement the public shared objects we provide (`sd-bus`, `sd-login`,
`sd-id128`, `nss-systemd`, `nss-mymachines`, `nss-resolve`, `nss-myhostname`,
`pam_systemd`), must be located in `src/basic` (those objects are not allowed
to link to `libsystemd-shared.so`). Conversely, anything which is shared
between multiple components and does not need to be in `src/basic/`, should be
in `src/shared/`.
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
require privileges, and will attempt to clearly log about what they need
(mostly in the form of effective capabilities). These tests are self-contained,
and generally safe to run on a host without side effects.
and generally safe to run on the host without side effects.
Ideally, every module in `src/basic/` and `src/shared/` should have a corresponding
unit test under `src/test/`, which exercises every helper function.
Ideally, every module in `src/basic/` and `src/shared/` should have a
corresponding unit test under `src/test/`, exercising every helper function.
# 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*:*
# HP ProBook 440 G2
evdev:atkbd:dmi:bvn*:bvr*:bd*:svnHewlett-Packard*:pnHP440G2:*
# several HP ProBooks 4xx
evdev:atkbd:dmi:bvn*:bvr*:bd*:svnHewlett-Packard*:pnHP*ProBook4*:*
evdev:atkbd:dmi:bvn*:bvr*:bd*:svnHP*:pnHP*ProBook*4*:*
# HP ProBook
evdev:atkbd:dmi:bvn*:bvr*:bd*:svnHewlett-Packard*:pnHP*ProBook*:*
evdev:atkbd:dmi:bvn*:bvr*:bd*:svnHP*:pnHP*ProBook*:*
# HP ZBook
evdev:atkbd:dmi:bvn*:bvr*:bd*:svnHewlett-Packard*: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
-w --wait-for-initialization'
[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'
[TRIGGER_STANDALONE]='-v --verbose -n --dry-run -q --quiet -w --settle --wait-daemon --uuid'
[TRIGGER_ARG]='-t --type -c --action -s --subsystem-match -S --subsystem-nomatch
-a --attr-match -A --attr-nomatch -p --property-match
-g --tag-match -y --sysname-match --name-match -b --parent-match'

View File

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

View File

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

View File

@ -1,16 +0,0 @@
/* 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,6 +526,16 @@ char base64char(int x) {
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) {
unsigned offset;

View File

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

View File

@ -38,8 +38,6 @@ basic_sources = files('''
creds-util.c
creds-util.h
def.h
device-nodes.c
device-nodes.h
dirent-util.c
dirent-util.h
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);
if (IN_SET(r, -EINVAL, -ENAMETOOLONG))
continue; /* If the path cannot be converted to a mount unit name, then it's
* not managable as a unit by systemd, and hence we don't need a
* not manageable as a unit by systemd, and hence we don't need a
* dependency on it. Let's thus silently ignore the issue. */
if (r < 0)
return r;

View File

@ -11,6 +11,7 @@
#include "alloc-util.h"
#include "ether-addr-util.h"
#include "hexdecoct.h"
#include "lockfile-util.h"
#include "missing_network.h"
#include "netif-naming-scheme.h"
@ -200,16 +201,6 @@ static int add_veth(
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) {
char new_ifname[IFNAMSIZ];

View File

@ -0,0 +1,7 @@
/* 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,6 +80,8 @@ shared_sources = files('''
daemon-util.h
dev-setup.c
dev-setup.h
device-nodes.c
device-nodes.h
devnode-acl.h
discover-image.c
discover-image.h

View File

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

View File

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

View File

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

44
src/udev/test-udev-node.c Normal file
View File

@ -0,0 +1,44 @@
/* 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,14 +7,16 @@
#include <sys/stat.h>
#include <unistd.h>
#include "sd-id128.h"
#include "alloc-util.h"
#include "device-nodes.h"
#include "device-private.h"
#include "device-util.h"
#include "dirent-util.h"
#include "fd-util.h"
#include "format-util.h"
#include "fs-util.h"
#include "hexdecoct.h"
#include "mkdir.h"
#include "path-util.h"
#include "selinux-util.h"
@ -26,7 +28,36 @@
#include "udev-node.h"
#include "user-util.h"
#define CREATE_LINK_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) {
_cleanup_free_ char *slink_dirname = NULL, *target = NULL;
@ -38,87 +69,80 @@ static int node_symlink(sd_device *dev, const char *node, const char *slink) {
assert(node);
assert(slink);
slink_dirname = dirname_malloc(slink);
if (!slink_dirname)
return log_oom();
r = path_extract_directory(slink, &slink_dirname);
if (r < 0)
return log_device_debug_errno(dev, r, "Failed to get parent directory of '%s': %m", slink);
/* use relative link */
r = path_make_relative(slink_dirname, node, &target);
if (r < 0)
return log_device_error_errno(dev, r, "Failed to get relative path from '%s' to '%s': %m", slink, node);
return log_device_debug_errno(dev, r, "Failed to get relative path from '%s' to '%s': %m", slink, node);
/* 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;
if (lstat(slink, &stats) >= 0) {
_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 &&
path_equal(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);
(void) label_fix(slink, LABEL_IGNORE_ENOENT);
(void) utimensat(AT_FDCWD, slink, NULL, AT_SYMLINK_NOFOLLOW);
if (readlink_malloc(slink, &buf) >= 0 &&
streq(target, buf)) {
log_device_debug(dev, "Preserve already existing symlink '%s' to '%s'", slink, target);
(void) label_fix(slink, LABEL_IGNORE_ENOENT);
(void) utimensat(AT_FDCWD, slink, NULL, AT_SYMLINK_NOFOLLOW);
return 0;
}
}
} else {
log_device_debug(dev, "Creating symlink '%s' to '%s'", slink, target);
do {
r = mkdir_parents_label(slink, 0755);
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;
if (r < 0)
log_device_debug_errno(dev, r, "Failed to create symlink '%s' to '%s', trying to replace '%s': %m", slink, target, slink);
}
}
} else if (errno == ENOENT) {
log_device_debug(dev, "Creating symlink '%s' to '%s'", slink, target);
r = create_symlink(target, slink);
if (r >= 0)
return 0;
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);
r = device_get_device_id(dev, &id);
if (r < 0)
return log_device_error_errno(dev, r, "Failed to get device id: %m");
return log_device_debug_errno(dev, r, "Failed to get device id: %m");
slink_tmp = strjoina(slink, ".tmp-", id);
(void) unlink(slink_tmp);
do {
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);
r = create_symlink(target, slink_tmp);
if (r < 0)
return log_device_error_errno(dev, r, "Failed to create symlink '%s' to '%s': %m", slink_tmp, target);
return log_device_debug_errno(dev, r, "Failed to create symlink '%s' to '%s': %m", slink_tmp, target);
if (rename(slink_tmp, slink) < 0) {
r = log_device_error_errno(dev, errno, "Failed to rename '%s' to '%s': %m", slink_tmp, slink);
r = log_device_debug_errno(dev, errno, "Failed to rename '%s' to '%s': %m", slink_tmp, slink);
(void) unlink(slink_tmp);
} else
/* Tell caller that we replaced already existing symlink. */
r = 1;
return r;
}
return r;
/* Tell caller that we replaced already existing symlink. */
return 1;
}
/* find device node of device with highest priority */
static int link_find_prioritized(sd_device *dev, bool add, const char *stackdir, char **ret) {
_cleanup_closedir_ DIR *dir = NULL;
_cleanup_free_ char *target = NULL;
struct dirent *dent;
int r, priority = 0;
const char *id;
assert(!add || dev);
assert(dev);
assert(stackdir);
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) {
const char *devnode;
@ -137,17 +161,21 @@ static int link_find_prioritized(sd_device *dev, bool add, const char *stackdir,
dir = opendir(stackdir);
if (!dir) {
if (target) {
if (errno == ENOENT) {
*ret = TAKE_PTR(target);
return 0;
return !!*ret;
}
return -errno;
}
r = device_get_device_id(dev, &id);
if (r < 0)
return r;
FOREACH_DIRENT_ALL(dent, dir, break) {
_cleanup_(sd_device_unrefp) sd_device *dev_db = NULL;
const char *devnode, *id;
const char *devnode;
int db_prio = 0;
if (dent->d_name[0] == '\0')
@ -157,9 +185,6 @@ 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);
if (device_get_device_id(dev, &id) < 0)
continue;
/* did we find ourself? */
if (streq(dent->d_name, id))
continue;
@ -184,59 +209,73 @@ static int link_find_prioritized(sd_device *dev, bool add, const char *stackdir,
priority = db_prio;
}
if (!target)
return -ENOENT;
*ret = TAKE_PTR(target);
return 0;
return !!*ret;
}
static size_t escape_path(const char *src, char *dest, size_t size) {
size_t udev_node_escape_path(const char *src, char *dest, size_t size) {
size_t i, j;
uint64_t h;
assert(src);
assert(dest);
for (i = 0, j = 0; src[i] != '\0'; i++) {
if (src[i] == '/') {
if (j+4 >= size) {
j = 0;
break;
}
if (j+4 >= size)
goto toolong;
memcpy(&dest[j], "\\x2f", 4);
j += 4;
} else if (src[i] == '\\') {
if (j+4 >= size) {
j = 0;
break;
}
if (j+4 >= size)
goto toolong;
memcpy(&dest[j], "\\x5c", 4);
j += 4;
} else {
if (j+1 >= size) {
j = 0;
break;
}
if (j+1 >= size)
goto toolong;
dest[j] = src[i];
j++;
}
}
dest[j] = '\0';
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 */
static int link_update(sd_device *dev, const char *slink, bool add) {
_cleanup_free_ char *filename = NULL, *dirname = NULL;
static int link_update(sd_device *dev, const char *slink_in, bool add) {
_cleanup_free_ char *slink = NULL, *filename = NULL, *dirname = NULL;
const char *slink_name, *id;
char name_enc[PATH_MAX];
char name_enc[NAME_MAX+1];
int i, r, retries;
assert(dev);
assert(slink);
assert(slink_in);
slink = strdup(slink_in);
if (!slink)
return log_oom_debug();
path_simplify(slink);
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),
"Invalid symbolic link of device node: %s", slink);
@ -244,31 +283,31 @@ static int link_update(sd_device *dev, const char *slink, bool add) {
if (r < 0)
return log_device_debug_errno(dev, r, "Failed to get device id: %m");
escape_path(slink_name, name_enc, sizeof(name_enc));
(void) udev_node_escape_path(slink_name, name_enc, sizeof(name_enc));
dirname = path_join("/run/udev/links/", name_enc);
if (!dirname)
return log_oom();
return log_oom_debug();
filename = path_join(dirname, id);
if (!filename)
return log_oom();
return log_oom_debug();
if (!add) {
if (unlink(filename) == 0)
(void) rmdir(dirname);
} else
for (;;) {
_cleanup_close_ int fd = -1;
if (unlink(filename) < 0 && errno != ENOENT)
log_device_debug_errno(dev, errno, "Failed to remove %s, ignoring: %m", filename);
r = mkdir_parents(filename, 0755);
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)
(void) rmdir(dirname);
} else {
for (unsigned j = 0; j < TOUCH_FILE_MAX_RETRIES; j++) {
/* This may fail with -ENOENT when the parent directory is removed during
* creating the file by another udevd worker. */
r = touch_file(filename, /* parents= */ true, USEC_INFINITY, UID_INVALID, GID_INVALID, 0444);
if (r != -ENOENT)
break;
if (errno != ENOENT)
return -errno;
}
if (r < 0)
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
* will be fixed in the second invocation. */
@ -280,23 +319,25 @@ static int link_update(sd_device *dev, const char *slink, bool add) {
r = stat(dirname, &st1);
if (r < 0 && errno != ENOENT)
return -errno;
return log_device_debug_errno(dev, errno, "Failed to stat %s: %m", dirname);
r = link_find_prioritized(dev, add, dirname, &target);
if (r == -ENOENT) {
log_device_debug(dev, "No reference left, removing '%s'", slink);
if (unlink(slink) == 0)
(void) rmdir_parents(slink, "/");
if (r < 0)
return log_device_debug_errno(dev, r, "Failed to determine highest priority for symlink '%s': %m", slink);
if (r == 0) {
log_device_debug(dev, "No reference left for '%s', removing", 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;
} else if (r < 0)
return log_device_error_errno(dev, r, "Failed to determine highest priority symlink: %m");
}
r = node_symlink(dev, target, slink);
if (r < 0) {
(void) unlink(filename);
break;
} else if (r == 1)
if (r < 0)
return r;
if (r == 1)
/* 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
* the error if other device actually claims the symlink with higher priority. */
@ -306,7 +347,7 @@ static int link_update(sd_device *dev, const char *slink, bool add) {
if ((st1.st_mode & S_IFMT) != 0) {
r = stat(dirname, &st2);
if (r < 0 && errno != ENOENT)
return -errno;
return log_device_debug_errno(dev, errno, "Failed to stat %s: %m", dirname);
if (stat_inode_unmodified(&st1, &st2))
break;
@ -317,16 +358,12 @@ static int link_update(sd_device *dev, const char *slink, bool add) {
}
int udev_node_update_old_links(sd_device *dev, sd_device *dev_old) {
const char *name, *devpath;
const char *name;
int r;
assert(dev);
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 */
FOREACH_DEVICE_DEVLINK(dev_old, name) {
const char *name_current;
@ -342,8 +379,10 @@ int udev_node_update_old_links(sd_device *dev, sd_device *dev_old) {
if (found)
continue;
log_device_debug(dev, "Updating old name, '%s' no longer belonging to '%s'",
name, devpath);
log_device_debug(dev,
"Updating old device symlink '%s', which is no longer belonging to this device.",
name);
r = link_update(dev, name, false);
if (r < 0)
log_device_warning_errno(dev, r,
@ -477,7 +516,6 @@ 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) {
char filename[DEV_NUM_PATH_MAX], *s;
const char *subsystem;
dev_t devnum;
int r;
@ -492,16 +530,7 @@ static int xsprintf_dev_num_path_from_sd_device(sd_device *dev, char **ret) {
if (r < 0)
return r;
xsprintf_dev_num_path(filename,
streq(subsystem, "block") ? "block" : "char",
devnum);
s = strdup(filename);
if (!s)
return -ENOMEM;
*ret = s;
return 0;
return device_path_make_major_minor(streq(subsystem, "block") ? S_IFBLK : S_IFCHR, devnum, ret);
}
int udev_node_add(sd_device *dev, bool apply,
@ -528,13 +557,6 @@ int udev_node_add(sd_device *dev, bool apply,
if (r < 0)
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 */
FOREACH_DEVICE_DEVLINK(dev, devlink) {
r = link_update(dev, devlink, true);
@ -544,6 +566,15 @@ int udev_node_add(sd_device *dev, bool apply,
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;
}
@ -568,7 +599,8 @@ int udev_node_remove(sd_device *dev) {
return log_device_debug_errno(dev, r, "Failed to get device path: %m");
/* remove /dev/{block,char}/$major:$minor */
(void) unlink(filename);
if (unlink(filename) < 0 && errno != ENOENT)
return log_device_debug_errno(dev, errno, "Failed to remove '%s': %m", filename);
return 0;
}

View File

@ -13,3 +13,5 @@ int udev_node_add(sd_device *dev, bool apply,
OrderedHashmap *seclabel_list);
int udev_node_remove(sd_device *dev);
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);

78
tools/analyze-dump-sort.py Executable file
View File

@ -0,0 +1,78 @@
#!/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)