mirror of
https://github.com/systemd/systemd
synced 2026-03-17 18:44:46 +01:00
Compare commits
27 Commits
5f94ac5435
...
efe76b273a
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
efe76b273a | ||
|
|
16f9036471 | ||
|
|
a7161e0288 | ||
|
|
d414f41535 | ||
|
|
19c79550b4 | ||
|
|
771bdb6aae | ||
|
|
287079696c | ||
|
|
4076ad9daf | ||
|
|
00d06c996d | ||
|
|
99c2a9552b | ||
|
|
78d8eae9a5 | ||
|
|
902b4c677e | ||
|
|
ebb0a0155d | ||
|
|
c891389a16 | ||
|
|
5802d4ea03 | ||
|
|
1ddfb6cf29 | ||
|
|
f3b393e951 | ||
|
|
d2b50631fb | ||
|
|
e7f3b33e70 | ||
|
|
a33dc87e42 | ||
|
|
e91454231b | ||
|
|
5733bd4862 | ||
|
|
286bedd7a4 | ||
|
|
be322ecafb | ||
|
|
52fde28014 | ||
|
|
e64943363a | ||
|
|
130298ba10 |
272
NEWS
272
NEWS
@ -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
|
||||
|
||||
@ -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
|
||||
|
||||
|
||||
@ -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*:*
|
||||
|
||||
@ -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'
|
||||
|
||||
@ -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] )) ||
|
||||
|
||||
@ -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"
|
||||
|
||||
@ -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))
|
||||
@ -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;
|
||||
|
||||
|
||||
@ -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);
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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;
|
||||
|
||||
@ -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];
|
||||
|
||||
|
||||
7
src/shared/device-nodes.h
Normal file
7
src/shared/device-nodes.h
Normal 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);
|
||||
@ -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
|
||||
|
||||
@ -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) {
|
||||
|
||||
@ -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) {
|
||||
|
||||
@ -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
44
src/udev/test-udev-node.c
Normal 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;
|
||||
}
|
||||
@ -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)) {
|
||||
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 &&
|
||||
streq(target, buf)) {
|
||||
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);
|
||||
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
} else if (errno == ENOENT) {
|
||||
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)
|
||||
|
||||
r = create_symlink(target, slink);
|
||||
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
|
||||
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;
|
||||
}
|
||||
|
||||
/* 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,30 +283,30 @@ 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)
|
||||
if (unlink(filename) < 0 && errno != ENOENT)
|
||||
log_device_debug_errno(dev, errno, "Failed to remove %s, ignoring: %m", filename);
|
||||
|
||||
(void) rmdir(dirname);
|
||||
} else
|
||||
for (;;) {
|
||||
_cleanup_close_ int fd = -1;
|
||||
|
||||
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)
|
||||
} 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
|
||||
@ -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;
|
||||
}
|
||||
|
||||
@ -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
78
tools/analyze-dump-sort.py
Executable 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)
|
||||
Loading…
x
Reference in New Issue
Block a user