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
|
* When operating on disk images via the --image= switch of various
|
||||||
tools (such as systemd-nspawn or systemd-dissect), and multiple
|
tools (such as systemd-nspawn or systemd-dissect), and multiple
|
||||||
suitable root or /usr/ partitions exist in the image, then a simple
|
suitable root or /usr/ partitions exist in the image, then a simple
|
||||||
strverscmp() inspired comparison is done on the GPT partition label,
|
comparison inspired by strverscmp() is done on the GPT partition
|
||||||
and the newest partition picked. This permits a simple and generic
|
label, and the newest partition is picked. This permits a simple and
|
||||||
whole-file-system A/B update logic where new operating system
|
generic whole-file-system A/B update logic where new operating system
|
||||||
versions are dropped into partitions whose label is then updated with
|
versions are dropped into partitions whose label is then updated with
|
||||||
a matching version identifier.
|
a matching version identifier.
|
||||||
|
|
||||||
@ -34,9 +34,6 @@ CHANGES WITH 249 in spe:
|
|||||||
previously unprovisioned images (i.e. images with a mostly empty
|
previously unprovisioned images (i.e. images with a mostly empty
|
||||||
/etc/).
|
/etc/).
|
||||||
|
|
||||||
* The systemd-ask-password now also supports reading passwords from the
|
|
||||||
credentials subsystem, via the new --credential= switch.
|
|
||||||
|
|
||||||
* Services gained a new ExitType= setting which can configure how to
|
* Services gained a new ExitType= setting which can configure how to
|
||||||
determine when a service exited: the default is "main" which defines
|
determine when a service exited: the default is "main" which defines
|
||||||
the runtime by the service's main process lifetime (this matches the
|
the runtime by the service's main process lifetime (this matches the
|
||||||
@ -47,30 +44,31 @@ CHANGES WITH 249 in spe:
|
|||||||
* The systemd-machine-id-setup tool now supports a --image= switch for
|
* The systemd-machine-id-setup tool now supports a --image= switch for
|
||||||
provisioning a machine ID file into an OS disk image, similar to how
|
provisioning a machine ID file into an OS disk image, similar to how
|
||||||
--root= operates on an OS file tree. This matches the existing switch
|
--root= operates on an OS file tree. This matches the existing switch
|
||||||
of the same name to the systemd-tmpfiles, systemd-firstboot or
|
of the same name for systemd-tmpfiles, systemd-firstboot, and
|
||||||
systemd-sysusers tools.
|
systemd-sysusers tools.
|
||||||
|
|
||||||
* Similar, systemd-repart gained support for a --image= switch, too. In
|
* Similarly, systemd-repart gained support for the --image= switch too.
|
||||||
combination with the existing --size= makes the tool particularly
|
In combination with the existing --size= option, this makes the tool
|
||||||
useful for easily growing disk images in a single command invocation,
|
particularly useful for easily growing disk images in a single
|
||||||
following the declarative rules included in the image itself.
|
invocation, following the declarative rules included in the image
|
||||||
|
itself.
|
||||||
|
|
||||||
* systemd-repart's partition configuration files gained support for a
|
* systemd-repart's partition configuration files gained support for a
|
||||||
new switch MakeDirectories= which may be used to create arbitrary
|
new switch MakeDirectories= which may be used to create arbitrary
|
||||||
directories inside file systems that are created, before registering
|
directories inside file systems that are created, before registering
|
||||||
them in the partition table. This is useful in particular if root
|
them in the partition table. This is useful in particular for root
|
||||||
partitions are created that way to create mount point directories for
|
partitions to create mount point directories for other partitions
|
||||||
other partitions included in the image. For example, a disk image
|
included in the image. For example, a disk image that contains a
|
||||||
that is created to contain a /home/ and a /var/ partition in addition
|
root, /home/, and /var/ partitions, may set MakeDirectories=yes to
|
||||||
to a root partition may MakeDirectories= to create /home/ and /var/
|
create /home/ and /var/ as empty directories in the root file system
|
||||||
as empty directories in the root file system on creation so that the
|
on its creation, so that the resulting image can mounted immediately,
|
||||||
resulting image can mounted immediately, even in read-only mode.
|
even in read-only mode.
|
||||||
|
|
||||||
* systemd-repart's CopyBlocks= setting gained support for a special
|
* systemd-repart's CopyBlocks= setting gained support for the special
|
||||||
value "auto". If used a suitable, matching partition on the booted OS
|
value "auto". If used, a suitable matching partition on the booted OS
|
||||||
is found as source to copy blocks from. This is useful for
|
is found as source to copy blocks from. This is useful when
|
||||||
implementing replicating installers, that are booted from one medium
|
implementing replicating installers, that are booted from one medium
|
||||||
and then stream their own root partition 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
|
* systemd-repart's partition configuration files gained support for a
|
||||||
Flags= and a ReadOnly= setting, allowing control of the GPT partition
|
Flags= and a ReadOnly= setting, allowing control of the GPT partition
|
||||||
@ -78,36 +76,54 @@ CHANGES WITH 249 in spe:
|
|||||||
created partitions as read-only from the start.
|
created partitions as read-only from the start.
|
||||||
|
|
||||||
* The /etc/os-release file has been extended with two new (optional)
|
* The /etc/os-release file has been extended with two new (optional)
|
||||||
variables IMAGE_VERSION= and IMAGE_ID=, for carrying identification
|
variables IMAGE_VERSION= and IMAGE_ID=, carrying identity and version
|
||||||
and versioning information for OS images that are updated
|
information for OS images that are updated comprehensively and
|
||||||
consistently, comprehensively and atomically as one image. The two
|
atomically as one image. Two new specifiers %M, %A now resolve to
|
||||||
new specifiers %M, %A now resolve to these two fields in the various
|
these two fields in the various configuration options that resolve
|
||||||
configuration options that resolve specifiers.
|
specifiers.
|
||||||
|
|
||||||
* portablectl gained a new switch --extension= for enabling portable
|
* portablectl gained a new switch --extension= for enabling portable
|
||||||
service images with extensions that follow the extension image
|
service images with extensions that follow the extension image
|
||||||
concept introduced with v248.
|
concept introduced with v248.
|
||||||
|
|
||||||
* systemd-coredump will now extract ELF build-id information from
|
* systemd-coredump will now extract ELF build-id information from
|
||||||
processes dumping core and include it in the coredump
|
processes dumping core and include it in the coredump report.
|
||||||
report. Moreover, if will look for ELF .note.package sections that
|
Moreover, it will look for ELF .note.package sections with
|
||||||
may carry distribution packaging meta-information about the crashing
|
distribution packaging meta-information about the crashing process.
|
||||||
process. This is useful to directly embed the RPM or Debian (or any
|
This is useful to directly embed the rpm or deb (or any other)
|
||||||
other) package name and version in the ELF files where they originate
|
package name and version in ELF files, making it easy to match
|
||||||
from, making it easy to match up coredump reports with the software
|
coredump reports with the specific package for which the software was
|
||||||
versions they were found it. This is particular useful on
|
compiled. This is particularly useful on environments with ELF files
|
||||||
environments with ELF files form multiple vendors, different
|
from multiple vendors, different distributions and versions, as is
|
||||||
distributions and versions, as it is common today, in particular in a
|
common today in our containerized and sand-boxed world. For further
|
||||||
containerized and sand-boxed world. For further information, see:
|
information, see:
|
||||||
|
|
||||||
https://systemd.io/COREDUMP_PACKAGE_METADATA
|
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
|
* sd-bus' sd_bus_is_ready() and sd_bus_is_open() calls now accept a
|
||||||
NULL bus object, for which they will return false. Or in other words,
|
NULL bus object, for which they will return false. Or in other words,
|
||||||
an unallocated bus connection is neither ready nor open.
|
an unallocated bus connection is neither ready nor open.
|
||||||
|
|
||||||
|
* The sd-device API acquired a new API function
|
||||||
|
sd_device_get_usec_initialized() that returns the monotonic time when
|
||||||
|
the udev device first appeared in the database.
|
||||||
|
|
||||||
|
* sd-device gained a new APIs sd_device_trigger_with_uuid() and
|
||||||
|
sd_device_get_trigger_uuid(). The former is similar to
|
||||||
|
sd_device_trigger() but returns a randomly generated UUID that is
|
||||||
|
associated with the synthetic uevent generated by the call. This UUID
|
||||||
|
may be read from the sd_device object a monitor eventually receives,
|
||||||
|
via the sd_device_get_trigger_uuid(). This interface requires kernel
|
||||||
|
4.13 or above to work, and allows tracking a synthetic uevent through
|
||||||
|
the entire device management stack. The "udevadm trigger --settle"
|
||||||
|
logic has been updated to make use of this concept if available to
|
||||||
|
wait precisely for the uevents it generates. "udevadm trigger" also
|
||||||
|
gained a new parameter --uuid that prints the UUID for each generated
|
||||||
|
uevent.
|
||||||
|
|
||||||
* The native Journal protocol has been documented. Clients may talk
|
* The native Journal protocol has been documented. Clients may talk
|
||||||
this as alternative to the classic BSD syslog protocol for locally
|
this as alternative to the classic BSD syslog protocol for locally
|
||||||
delivering log records to the Journal. The protocol has been stable
|
delivering log records to the Journal. The protocol has been stable
|
||||||
@ -142,81 +158,90 @@ CHANGES WITH 249 in spe:
|
|||||||
fine-tune whether to require an IPv4 or IPv6 address in order to
|
fine-tune whether to require an IPv4 or IPv6 address in order to
|
||||||
consider an interface "online".
|
consider an interface "online".
|
||||||
|
|
||||||
|
* networkctl will now show an over-all "online" state in the per-link
|
||||||
|
information.
|
||||||
|
|
||||||
|
* In .network files a new OutgoingInterface= setting has been added to
|
||||||
|
specify the output interface in bridge FDB setups.
|
||||||
|
|
||||||
|
* In ,network files the Multipath group ID may now be configured for
|
||||||
|
[NextHop] entries, via the new Group= setting.
|
||||||
|
|
||||||
|
* The DHCP server logic configured in .network files gained a new
|
||||||
|
setting RelayTarget= that turns the server into a DHCP server relay.
|
||||||
|
The RelayAgentCircuitId= and RelayAgentRemoteId= settings may be used
|
||||||
|
to further tweak the DHCP relay behaviour.
|
||||||
|
|
||||||
|
* The DHCP server logic also gained a new ServerAddress= setting in
|
||||||
|
.network files that explicitly specifies the server IP address to
|
||||||
|
use. If not specified, the address is determined automatically, as
|
||||||
|
before.
|
||||||
|
|
||||||
|
* The DHCP server logic in systemd-networkd gained support for static
|
||||||
|
DHCP leases, configurable via the [DHCPServerStaticLease]
|
||||||
|
section. This allows explicitly mapping specific MAC addresses to
|
||||||
|
fixed IP addresses and vice versa.
|
||||||
|
|
||||||
* The RestrictAddressFamilies= setting in service files now supports a
|
* The RestrictAddressFamilies= setting in service files now supports a
|
||||||
new special value "none". If specified sockets of all address
|
new special value "none". If specified sockets of all address
|
||||||
families will be made unavailable to services configured that way.
|
families will be made unavailable to services configured that way.
|
||||||
|
|
||||||
* The DHCP server logic configured in .network files gained a new
|
|
||||||
setting RelayTarget= that turns the server into a DHCP server
|
|
||||||
relay. The RelayAgentCircuitId= and RelayAgentRemoteId= settings may
|
|
||||||
be used to further tweak the DHCP relay behaviour.
|
|
||||||
|
|
||||||
* The DHCP server logic also gained a new setting ServerAddress= in
|
|
||||||
.network files for explicitly specifying the server IP address to
|
|
||||||
use. If not used, the address is determined automatically, as before.
|
|
||||||
|
|
||||||
* The sd-device API acquired a new API function
|
|
||||||
sd_device_get_usec_initialized() that returns the monotonic timestamp
|
|
||||||
when a udev device appeared in the database first.
|
|
||||||
|
|
||||||
* systemd-fstab-generator and systemd-repart have been updated to
|
* systemd-fstab-generator and systemd-repart have been updated to
|
||||||
support booting from disks that carry only a /usr/ partition but no
|
support booting from disks that carry only a /usr/ partition but no
|
||||||
root partition yet, but where systemd-repart can add them in on first
|
root partition yet, and where systemd-repart can add it in on the
|
||||||
boot. This is useful for implementing systems that ship with a single
|
first boot. This is useful for implementing systems that ship with a
|
||||||
/usr/ file system and whose root file system shall be set up on a
|
single /usr/ file system, and whose root file system shall be set up
|
||||||
LUKS encrypted volume whose key is generated locally (and possibly
|
and formatted on a LUKS-encrypted volume whose key is generated
|
||||||
enrolled in TPM), and which is set up and formatted during first
|
locally (and possibly enrolled in the TPM) during the first boot.
|
||||||
boot.
|
|
||||||
|
|
||||||
* The [Address] section of .network files now accepts a new
|
* The [Address] section of .network files now accepts a new
|
||||||
RouteMetric= setting that configures the routing metric to use for
|
RouteMetric= setting that configures the routing metric to use for
|
||||||
the prefix route created as effect of the address
|
the prefix route created as effect of the address configuration.
|
||||||
configuration. Similar, the [DHCPv6PrefixDelegation] and [IPv6Prefix]
|
Similarly, the [DHCPv6PrefixDelegation] and [IPv6Prefix] sections
|
||||||
sections gained matching settings for their prefix routes. (The
|
gained matching settings for their prefix routes. (The option of the
|
||||||
option of the same name of the [DHCPv6] section is moved to
|
same name in the [DHCPv6] section is moved to [IPv6AcceptRA], since
|
||||||
[IPv6AcceptRA], since it conceptually belongs there; the old option
|
it conceptually belongs there; the old option is still understood for
|
||||||
is still understood for compatibility.)
|
compatibility.)
|
||||||
|
|
||||||
* The DHCPv6 IAID and DUID are now explicitly configurable in .network
|
* The DHCPv6 IAID and DUID are now explicitly configurable in .network
|
||||||
files.
|
files.
|
||||||
|
|
||||||
* A new udev property ID_NET_DHCP_BROADCAST on network interface
|
* A new udev property ID_NET_DHCP_BROADCAST on network interface
|
||||||
devices is 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
|
issue DHCP offers via broadcasting. This is used to ensure that s390
|
||||||
layer 3 network interfaces work out-of-the-box with systemd-networkd.
|
layer 3 network interfaces work out-of-the-box with systemd-networkd.
|
||||||
|
|
||||||
* nss-myhostname and systemd-resolved will now synthesize address
|
* nss-myhostname and systemd-resolved will now synthesize address
|
||||||
records for a new special hostname "_outbound". The name will always
|
records for a new special hostname "_outbound". The name will always
|
||||||
resolve to the local IP addresses most likely used for outbound
|
resolve to the local IP addresses most likely used for outbound
|
||||||
connections towards the default 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
|
useful to have a stable handle referring to "the" local IP address
|
||||||
that matters most, to the point where this is defined.
|
that matters most, to the point where this is defined.
|
||||||
|
|
||||||
* The Discoverable Partition Specification has been updated with a new
|
* The Discoverable Partition Specification has been updated with a new
|
||||||
GPT partition flag defined its partition types: a "growfs"
|
GPT partition flag "growsfs" defined for its partition types.
|
||||||
flag. Whenever partitions with this flag set are automatically
|
Whenever partitions with this flag set are automatically mounted
|
||||||
mounted (i.e. via systemd-gpt-auto-generator or the --image= switch
|
(i.e. via systemd-gpt-auto-generator or the --image= switch of
|
||||||
of systemd-nspawn or other tools; and as opposed to explicit mounting
|
systemd-nspawn or other tools; and as opposed to explicit mounting
|
||||||
via /etc/fstab), the file system within the partition is
|
via /etc/fstab), the file system within the partition is
|
||||||
automatically grown to the full size of the partition should it be
|
automatically grown to the full size of the partition. If the file
|
||||||
smaller. If the file system size already matches the partition size
|
system size already matches the partition size this flag has no
|
||||||
this flag has no effect. Previously, this functionality has been
|
effect. Previously, this functionality has been available via the
|
||||||
available via the explicit x-systemd.growfs mount option, and this
|
explicit x-systemd.growfs mount option, and this new flag extends
|
||||||
new flag extends this to automatically discovered mounts. A new
|
this to automatically discovered mounts. A new GrowFileSystem=
|
||||||
GrowFileSystem= setting has been added to systemd-repart drop-in
|
setting has been added to systemd-repart drop-in files that allows
|
||||||
files that allows configuring this partition flag. This new flag
|
configuring this partition flag. This new flag defaults to on for
|
||||||
defaults to on for partitions automatically created by
|
partitions automatically created by systemd-repart, except if they
|
||||||
systemd-repart, except if they are marked read-only. See the
|
are marked read-only. See the specification for further details:
|
||||||
specification for further details:
|
|
||||||
|
|
||||||
https://systemd.io/DISCOVERABLE_PARTITIONS
|
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
|
section. If enabled (which is the default), and an NTP server address
|
||||||
is acquired through a DHCP lease on this interface an explicit route
|
is acquired through a DHCP lease on this interface an explicit route
|
||||||
to this address is created on this interface to ensure that NTP
|
to this address is created on this interface to ensure that NTP
|
||||||
traffic to the NTP server acquired on an interface is also routed
|
traffic to the NTP server acquired on an interface is also routed
|
||||||
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.
|
implements the same for DNS servers is now enabled by default.
|
||||||
|
|
||||||
* A pair of service settings SocketBindAllow= + SocketBindDeny= have
|
* A pair of service settings SocketBindAllow= + SocketBindDeny= have
|
||||||
@ -224,27 +249,27 @@ CHANGES WITH 249 in spe:
|
|||||||
sockets created by the service may be bound to. This is implemented
|
sockets created by the service may be bound to. This is implemented
|
||||||
via BPF.
|
via BPF.
|
||||||
|
|
||||||
* A new ConditionFirmware= condition type has been added to unit
|
* A new ConditionFirmware= condition type has been added to unit files.
|
||||||
files. It may be used to detect certain firmware features. At the
|
It may be used to detect certain firmware features. At the moment it
|
||||||
moment it may check whether running on an UEFI system, a device.tree
|
may check whether running on an UEFI system, a device.tree system, or
|
||||||
system or if the system is compatible with some specified device-tree
|
if the system is compatible with some specified device-tree feature.
|
||||||
feature.
|
|
||||||
|
|
||||||
* hostnamed gained a new Describe() D-Bus method that returns a JSON
|
* hostnamed gained a new Describe() D-Bus method that returns a JSON
|
||||||
serialization of the host data it exposes. This is exposed via
|
serialization of the host data it exposes. This is exposed via
|
||||||
"hostnamectl --json=" to acquire a host identity description in
|
"hostnamectl --json=" to acquire a host identity description in JSON.
|
||||||
JSON. It's our intention to add a similar concept to most services
|
It's our intention to add a similar features to most services and
|
||||||
and objects systemd manages, in order to simplify integration with
|
objects systemd manages, in order to simplify integration with
|
||||||
program code the consumes JSON.
|
program code that can consume JSON.
|
||||||
|
|
||||||
* Similar, networkd gained a Describe() method on its Manager and Link
|
* Similarly, networkd gained a Describe() method on its Manager and
|
||||||
bus objects. This is exposed via "networkctl --json=".
|
Link bus objects. This is exposed via "networkctl --json=".
|
||||||
|
|
||||||
* hostnamectl's various set-xyz verbs (e.g. "hostnamectl set-hostname")
|
* hostnamectl's various "get-xyz"/"set-xyz" verb pairs
|
||||||
have been renamed to just xyz (e.g. "hostnamectl hostname") and may
|
(e.g. "hostnamectl get-hostname", "hostnamectl "set-hostname") have
|
||||||
now be used to print the indicated data in terse form, instead of
|
been replaced by a single "xyz" verb (e.g. "hostnamectl hostname")
|
||||||
only setting it. The old names continue to be supported for
|
that is used both to get the value (when no argument is given), and
|
||||||
compatibility.
|
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
|
* systemd-detect-virt and ConditionVirtualization= are now able to
|
||||||
correctly identify Amazon EC2 environments.
|
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
|
* bootctl gained support for a new --make-machine-id-directory= switch
|
||||||
that allows precise control on whether to create the top-level
|
that allows precise control on whether to create the top-level
|
||||||
per-machine directory in the boot partition that typically contain
|
per-machine directory in the boot partition that typically contains
|
||||||
Type #1 boot loader entries.
|
Type 1 boot loader entries.
|
||||||
|
|
||||||
* During build SBAT data to include in the systemd-boot EFI PE binaries
|
* During build SBAT data to include in the systemd-boot EFI PE binaries
|
||||||
may be specified now.
|
may be specified now.
|
||||||
@ -344,15 +369,6 @@ CHANGES WITH 249 in spe:
|
|||||||
journalctl, and permit exposing only the specified subset of the
|
journalctl, and permit exposing only the specified subset of the
|
||||||
Journal records.
|
Journal records.
|
||||||
|
|
||||||
* networkctl will now show an over-all "online" state in the per-link
|
|
||||||
information.
|
|
||||||
|
|
||||||
* In .network files a new OutgoingInterface= setting has been added to
|
|
||||||
specify the output interface in bridge FDB setups.
|
|
||||||
|
|
||||||
* In ,network files the Multipath group ID may now be configured for
|
|
||||||
[NextHop] entries, via the new Group= setting.
|
|
||||||
|
|
||||||
* The OnFailure= dependency between units is now augmented with a
|
* The OnFailure= dependency between units is now augmented with a
|
||||||
implicit reverse dependency OnFailureOf= (this new dependency cannot
|
implicit reverse dependency OnFailureOf= (this new dependency cannot
|
||||||
be configured directly it's only created as effect of an OnFailure=
|
be configured directly it's only created as effect of an OnFailure=
|
||||||
@ -366,6 +382,46 @@ CHANGES WITH 249 in spe:
|
|||||||
stop events between two units. It operates similar to the existing
|
stop events between two units. It operates similar to the existing
|
||||||
PropagatesReloadTo= + ReloadPropagatedFrom= dependencies.
|
PropagatesReloadTo= + ReloadPropagatedFrom= dependencies.
|
||||||
|
|
||||||
|
* A new dependency type OnSuccess= has been added (plus the reverse
|
||||||
|
dependency OnSuccessOf=, which cannot be configured directly, but
|
||||||
|
exists only as effect of the reverse OnSuccess=). It is similar to
|
||||||
|
OnFailure=, but triggers in the opposite case: when a service exits
|
||||||
|
cleanly. This allows "chaining up" of services where one or more
|
||||||
|
services are started once another service has successfully completed.
|
||||||
|
|
||||||
|
* A new dependency type Upholds= has been added (plus the reverse
|
||||||
|
dependency UpheldBy=, which cannot be configured directly, but exists
|
||||||
|
only as effect of Upholds=). This dependency type is a stronger form
|
||||||
|
of Wants=: if a unit has an UpHolds= dependency on some other unit
|
||||||
|
and the former is active then the latter is started whenever it is
|
||||||
|
found inactive (and no job is queued for it). This is an alternative
|
||||||
|
to Restart= inside service units, but less configurable, and the
|
||||||
|
request to uphold a unit is not encoded in the unit itself but in
|
||||||
|
another unit that intends to uphold it.
|
||||||
|
|
||||||
|
* The systemd-ask-password tool now also supports reading passwords
|
||||||
|
from the credentials subsystem, via the new --credential= switch.
|
||||||
|
|
||||||
|
* The systemd-ask-password tool learnt a new switch --emoji= which may
|
||||||
|
be used to explicit control whether the lock and key emoji (🔐) is
|
||||||
|
shown in the password prompt on suitable TTYs.
|
||||||
|
|
||||||
|
* The --echo switch of systemd-ask-password now optionally takes a
|
||||||
|
parameter that controls character echo. It may either show asterisks
|
||||||
|
(default, as before), turn echo off entirely, or echo the typed
|
||||||
|
characters literally.
|
||||||
|
|
||||||
|
* New documentation has been added that describes the organization of
|
||||||
|
the systemd source code tree:
|
||||||
|
|
||||||
|
https://systemd.io/ARCHITECTURE
|
||||||
|
|
||||||
|
* …
|
||||||
|
|
||||||
|
Contributions from: …
|
||||||
|
|
||||||
|
— Berlin, 2021-06-XX
|
||||||
|
|
||||||
CHANGES WITH 248:
|
CHANGES WITH 248:
|
||||||
|
|
||||||
* A concept of system extension images is introduced. Such images may
|
* A concept of system extension images is introduced. Such images may
|
||||||
|
|||||||
@ -1,5 +1,5 @@
|
|||||||
---
|
---
|
||||||
title: systemd repository architecture
|
title: systemd Repository Architecture
|
||||||
category: Contributing
|
category: Contributing
|
||||||
layout: default
|
layout: default
|
||||||
---
|
---
|
||||||
@ -13,19 +13,19 @@ components of the systemd repository.
|
|||||||
|
|
||||||
Directories in `src/` provide the implementation of all daemons, libraries and
|
Directories in `src/` provide the implementation of all daemons, libraries and
|
||||||
command-line tools shipped by the project. There are many, and more are
|
command-line tools shipped by the project. There are many, and more are
|
||||||
constantly added, so we will not enumerate them all here - the directory
|
constantly added, so we will not enumerate them all here — the directory
|
||||||
names are self-explanatory.
|
names are self-explanatory.
|
||||||
|
|
||||||
## Shared Code
|
## Shared Code
|
||||||
|
|
||||||
You might wonder what kind of common code belongs in `src/shared/` and what
|
You might wonder what kind of common code belongs in `src/shared/` and what
|
||||||
belongs in `src/basic/`. The split is like this: anything that is used to
|
belongs in `src/basic/`. The split is like this: anything that is used to
|
||||||
implement the public shared object we provide (sd-bus, sd-login, sd-id128,
|
implement the public shared objects we provide (`sd-bus`, `sd-login`,
|
||||||
nss-systemd, nss-mymachines, nss-resolve, nss-myhostname, pam_systemd), must
|
`sd-id128`, `nss-systemd`, `nss-mymachines`, `nss-resolve`, `nss-myhostname`,
|
||||||
be located in `src/basic` (those objects are not allowed to link to
|
`pam_systemd`), must be located in `src/basic` (those objects are not allowed
|
||||||
libsystemd-shared.so). Conversely, anything which is shared between multiple
|
to link to `libsystemd-shared.so`). Conversely, anything which is shared
|
||||||
components and does not need to be in `src/basic/`, should be in
|
between multiple components and does not need to be in `src/basic/`, should be
|
||||||
`src/shared/`.
|
in `src/shared/`.
|
||||||
|
|
||||||
To summarize:
|
To summarize:
|
||||||
|
|
||||||
@ -63,10 +63,10 @@ file is compiled in a standalone binary that can be run to exercise the
|
|||||||
corresponding module. While most of the tests can be ran by any user, some
|
corresponding module. While most of the tests can be ran by any user, some
|
||||||
require privileges, and will attempt to clearly log about what they need
|
require privileges, and will attempt to clearly log about what they need
|
||||||
(mostly in the form of effective capabilities). These tests are self-contained,
|
(mostly in the form of effective capabilities). These tests are self-contained,
|
||||||
and generally safe to run on 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
|
Ideally, every module in `src/basic/` and `src/shared/` should have a
|
||||||
unit test under `src/test/`, which exercises every helper function.
|
corresponding unit test under `src/test/`, exercising every helper function.
|
||||||
|
|
||||||
# Integration Tests
|
# Integration Tests
|
||||||
|
|
||||||
|
|||||||
@ -625,9 +625,9 @@ evdev:atkbd:dmi:bvn*:bvr*:bd*:svnHewlett-Packard*:pnHPEliteBook*:*
|
|||||||
evdev:atkbd:dmi:bvn*:bvr*:bd*:svnHP*:pnHPEliteBook*:*
|
evdev:atkbd:dmi:bvn*:bvr*:bd*:svnHP*:pnHPEliteBook*:*
|
||||||
# HP ProBook 440 G2
|
# HP ProBook 440 G2
|
||||||
evdev:atkbd:dmi:bvn*:bvr*:bd*:svnHewlett-Packard*:pnHP440G2:*
|
evdev:atkbd:dmi:bvn*:bvr*:bd*:svnHewlett-Packard*:pnHP440G2:*
|
||||||
# several HP ProBooks 4xx
|
# HP ProBook
|
||||||
evdev:atkbd:dmi:bvn*:bvr*:bd*:svnHewlett-Packard*:pnHP*ProBook4*:*
|
evdev:atkbd:dmi:bvn*:bvr*:bd*:svnHewlett-Packard*:pnHP*ProBook*:*
|
||||||
evdev:atkbd:dmi:bvn*:bvr*:bd*:svnHP*:pnHP*ProBook*4*:*
|
evdev:atkbd:dmi:bvn*:bvr*:bd*:svnHP*:pnHP*ProBook*:*
|
||||||
# HP ZBook
|
# HP ZBook
|
||||||
evdev:atkbd:dmi:bvn*:bvr*:bd*:svnHewlett-Packard*:pnHPZBook*:*
|
evdev:atkbd:dmi:bvn*:bvr*:bd*:svnHewlett-Packard*:pnHPZBook*:*
|
||||||
evdev:atkbd:dmi:bvn*:bvr*:bd*:svnHP*:pnHPZBook*:*
|
evdev:atkbd:dmi:bvn*:bvr*:bd*:svnHP*:pnHPZBook*:*
|
||||||
|
|||||||
@ -51,7 +51,7 @@ _udevadm() {
|
|||||||
[INFO_STANDALONE]='-r --root -a --attribute-walk -x --export -e --export-db -c --cleanup-db
|
[INFO_STANDALONE]='-r --root -a --attribute-walk -x --export -e --export-db -c --cleanup-db
|
||||||
-w --wait-for-initialization'
|
-w --wait-for-initialization'
|
||||||
[INFO_ARG]='-q --query -p --path -n --name -P --export-prefix -d --device-id-of-file'
|
[INFO_ARG]='-q --query -p --path -n --name -P --export-prefix -d --device-id-of-file'
|
||||||
[TRIGGER_STANDALONE]='-v --verbose -n --dry-run -q --quiet -w --settle --wait-daemon'
|
[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
|
[TRIGGER_ARG]='-t --type -c --action -s --subsystem-match -S --subsystem-nomatch
|
||||||
-a --attr-match -A --attr-nomatch -p --property-match
|
-a --attr-match -A --attr-nomatch -p --property-match
|
||||||
-g --tag-match -y --sysname-match --name-match -b --parent-match'
|
-g --tag-match -y --sysname-match --name-match -b --parent-match'
|
||||||
|
|||||||
@ -31,7 +31,8 @@ _udevadm_trigger(){
|
|||||||
'--property-match=[Trigger events for devices with a matching property value.]' \
|
'--property-match=[Trigger events for devices with a matching property value.]' \
|
||||||
'--tag-match=property[Trigger events for devices with a matching tag.]' \
|
'--tag-match=property[Trigger events for devices with a matching tag.]' \
|
||||||
'--sysname-match=[Trigger events for devices with a matching sys device name.]' \
|
'--sysname-match=[Trigger events for devices with a matching sys device name.]' \
|
||||||
'--parent-match=[Trigger events for all children of a given device.]'
|
'--parent-match=[Trigger events for all children of a given device.]' \
|
||||||
|
'--uuid[Print synthetic uevent UUID.]'
|
||||||
}
|
}
|
||||||
|
|
||||||
(( $+functions[_udevadm_settle] )) ||
|
(( $+functions[_udevadm_settle] )) ||
|
||||||
|
|||||||
@ -19,7 +19,6 @@
|
|||||||
#include "btrfs-util.h"
|
#include "btrfs-util.h"
|
||||||
#include "chattr-util.h"
|
#include "chattr-util.h"
|
||||||
#include "copy.h"
|
#include "copy.h"
|
||||||
#include "device-nodes.h"
|
|
||||||
#include "fd-util.h"
|
#include "fd-util.h"
|
||||||
#include "fileio.h"
|
#include "fileio.h"
|
||||||
#include "fs-util.h"
|
#include "fs-util.h"
|
||||||
|
|||||||
@ -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];
|
return table[x & 63];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* This is almost base64char(), but not entirely, as it uses the "url and filename safe" alphabet,
|
||||||
|
* since we don't want "/" appear in interface names (since interfaces appear in sysfs as filenames).
|
||||||
|
* See section #5 of RFC 4648. */
|
||||||
|
char urlsafe_base64char(int x) {
|
||||||
|
static const char table[64] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
|
||||||
|
"abcdefghijklmnopqrstuvwxyz"
|
||||||
|
"0123456789-_";
|
||||||
|
return table[x & 63];
|
||||||
|
}
|
||||||
|
|
||||||
int unbase64char(char c) {
|
int unbase64char(char c) {
|
||||||
unsigned offset;
|
unsigned offset;
|
||||||
|
|
||||||
|
|||||||
@ -27,6 +27,7 @@ char base32hexchar(int x) _const_;
|
|||||||
int unbase32hexchar(char c) _const_;
|
int unbase32hexchar(char c) _const_;
|
||||||
|
|
||||||
char base64char(int x) _const_;
|
char base64char(int x) _const_;
|
||||||
|
char urlsafe_base64char(int x) _const_;
|
||||||
int unbase64char(char c) _const_;
|
int unbase64char(char c) _const_;
|
||||||
|
|
||||||
char *base32hexmem(const void *p, size_t l, bool padding);
|
char *base32hexmem(const void *p, size_t l, bool padding);
|
||||||
|
|||||||
@ -38,8 +38,6 @@ basic_sources = files('''
|
|||||||
creds-util.c
|
creds-util.c
|
||||||
creds-util.h
|
creds-util.h
|
||||||
def.h
|
def.h
|
||||||
device-nodes.c
|
|
||||||
device-nodes.h
|
|
||||||
dirent-util.c
|
dirent-util.c
|
||||||
dirent-util.h
|
dirent-util.h
|
||||||
dlfcn-util.c
|
dlfcn-util.c
|
||||||
|
|||||||
@ -1475,7 +1475,7 @@ static int unit_add_mount_dependencies(Unit *u) {
|
|||||||
r = unit_name_from_path(prefix, ".mount", &p);
|
r = unit_name_from_path(prefix, ".mount", &p);
|
||||||
if (IN_SET(r, -EINVAL, -ENAMETOOLONG))
|
if (IN_SET(r, -EINVAL, -ENAMETOOLONG))
|
||||||
continue; /* If the path cannot be converted to a mount unit name, then it's
|
continue; /* If the path cannot be converted to a mount unit name, then it's
|
||||||
* not 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. */
|
* dependency on it. Let's thus silently ignore the issue. */
|
||||||
if (r < 0)
|
if (r < 0)
|
||||||
return r;
|
return r;
|
||||||
|
|||||||
@ -11,6 +11,7 @@
|
|||||||
|
|
||||||
#include "alloc-util.h"
|
#include "alloc-util.h"
|
||||||
#include "ether-addr-util.h"
|
#include "ether-addr-util.h"
|
||||||
|
#include "hexdecoct.h"
|
||||||
#include "lockfile-util.h"
|
#include "lockfile-util.h"
|
||||||
#include "missing_network.h"
|
#include "missing_network.h"
|
||||||
#include "netif-naming-scheme.h"
|
#include "netif-naming-scheme.h"
|
||||||
@ -200,16 +201,6 @@ static int add_veth(
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* This is almost base64char(), but not entirely, as it uses the "url and filename safe" alphabet, since we
|
|
||||||
* don't want "/" appear in interface names (since interfaces appear in sysfs as filenames). See section #5
|
|
||||||
* of RFC 4648. */
|
|
||||||
static char urlsafe_base64char(int x) {
|
|
||||||
static const char table[64] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
|
|
||||||
"abcdefghijklmnopqrstuvwxyz"
|
|
||||||
"0123456789-_";
|
|
||||||
return table[x & 63];
|
|
||||||
}
|
|
||||||
|
|
||||||
static int shorten_ifname(char *ifname) {
|
static int shorten_ifname(char *ifname) {
|
||||||
char new_ifname[IFNAMSIZ];
|
char new_ifname[IFNAMSIZ];
|
||||||
|
|
||||||
|
|||||||
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
|
daemon-util.h
|
||||||
dev-setup.c
|
dev-setup.c
|
||||||
dev-setup.h
|
dev-setup.h
|
||||||
|
device-nodes.c
|
||||||
|
device-nodes.h
|
||||||
devnode-acl.h
|
devnode-acl.h
|
||||||
discover-image.c
|
discover-image.c
|
||||||
discover-image.h
|
discover-image.h
|
||||||
|
|||||||
@ -1,11 +1,11 @@
|
|||||||
/* SPDX-License-Identifier: LGPL-2.1-or-later */
|
/* SPDX-License-Identifier: LGPL-2.1-or-later */
|
||||||
|
|
||||||
|
#include <stdio.h>
|
||||||
#include <sys/types.h>
|
#include <sys/types.h>
|
||||||
|
|
||||||
#include "alloc-util.h"
|
#include "alloc-util.h"
|
||||||
#include "device-nodes.h"
|
#include "device-nodes.h"
|
||||||
#include "string-util.h"
|
#include "string-util.h"
|
||||||
#include "util.h"
|
|
||||||
|
|
||||||
/* helpers for test_encode_devnode_name */
|
/* helpers for test_encode_devnode_name */
|
||||||
static char *do_encode_string(const char *in) {
|
static char *do_encode_string(const char *in) {
|
||||||
|
|||||||
@ -316,7 +316,7 @@ static void test_raw_clone(void) {
|
|||||||
|
|
||||||
errno = 0;
|
errno = 0;
|
||||||
assert_se(raw_clone(CLONE_FS|CLONE_NEWNS) == -1);
|
assert_se(raw_clone(CLONE_FS|CLONE_NEWNS) == -1);
|
||||||
assert_se(errno == EINVAL);
|
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) {
|
static void test_physical_memory(void) {
|
||||||
|
|||||||
@ -200,6 +200,12 @@ tests += [
|
|||||||
[threads,
|
[threads,
|
||||||
libacl]],
|
libacl]],
|
||||||
|
|
||||||
|
[['src/udev/test-udev-node.c'],
|
||||||
|
[libudevd_core,
|
||||||
|
libshared],
|
||||||
|
[threads,
|
||||||
|
libacl]],
|
||||||
|
|
||||||
[['src/udev/test-udev-builtin.c'],
|
[['src/udev/test-udev-builtin.c'],
|
||||||
[libudevd_core,
|
[libudevd_core,
|
||||||
libshared],
|
libshared],
|
||||||
|
|||||||
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 <sys/stat.h>
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
|
|
||||||
|
#include "sd-id128.h"
|
||||||
|
|
||||||
#include "alloc-util.h"
|
#include "alloc-util.h"
|
||||||
#include "device-nodes.h"
|
|
||||||
#include "device-private.h"
|
#include "device-private.h"
|
||||||
#include "device-util.h"
|
#include "device-util.h"
|
||||||
#include "dirent-util.h"
|
#include "dirent-util.h"
|
||||||
#include "fd-util.h"
|
#include "fd-util.h"
|
||||||
#include "format-util.h"
|
#include "format-util.h"
|
||||||
#include "fs-util.h"
|
#include "fs-util.h"
|
||||||
|
#include "hexdecoct.h"
|
||||||
#include "mkdir.h"
|
#include "mkdir.h"
|
||||||
#include "path-util.h"
|
#include "path-util.h"
|
||||||
#include "selinux-util.h"
|
#include "selinux-util.h"
|
||||||
@ -26,7 +28,36 @@
|
|||||||
#include "udev-node.h"
|
#include "udev-node.h"
|
||||||
#include "user-util.h"
|
#include "user-util.h"
|
||||||
|
|
||||||
|
#define CREATE_LINK_MAX_RETRIES 128
|
||||||
#define LINK_UPDATE_MAX_RETRIES 128
|
#define LINK_UPDATE_MAX_RETRIES 128
|
||||||
|
#define TOUCH_FILE_MAX_RETRIES 128
|
||||||
|
#define UDEV_NODE_HASH_KEY SD_ID128_MAKE(b9,6a,f1,ce,40,31,44,1a,9e,19,ec,8b,ae,f3,e3,2f)
|
||||||
|
|
||||||
|
static int create_symlink(const char *target, const char *slink) {
|
||||||
|
int r;
|
||||||
|
|
||||||
|
assert(target);
|
||||||
|
assert(slink);
|
||||||
|
|
||||||
|
for (unsigned i = 0; i < CREATE_LINK_MAX_RETRIES; i++) {
|
||||||
|
r = mkdir_parents_label(slink, 0755);
|
||||||
|
if (r == -ENOENT)
|
||||||
|
continue;
|
||||||
|
if (r < 0)
|
||||||
|
return r;
|
||||||
|
|
||||||
|
mac_selinux_create_file_prepare(slink, S_IFLNK);
|
||||||
|
if (symlink(target, slink) < 0)
|
||||||
|
r = -errno;
|
||||||
|
else
|
||||||
|
r = 0;
|
||||||
|
mac_selinux_create_file_clear();
|
||||||
|
if (r != -ENOENT)
|
||||||
|
return r;
|
||||||
|
}
|
||||||
|
|
||||||
|
return r;
|
||||||
|
}
|
||||||
|
|
||||||
static int node_symlink(sd_device *dev, const char *node, const char *slink) {
|
static int node_symlink(sd_device *dev, const char *node, const char *slink) {
|
||||||
_cleanup_free_ char *slink_dirname = NULL, *target = NULL;
|
_cleanup_free_ char *slink_dirname = NULL, *target = NULL;
|
||||||
@ -38,87 +69,80 @@ static int node_symlink(sd_device *dev, const char *node, const char *slink) {
|
|||||||
assert(node);
|
assert(node);
|
||||||
assert(slink);
|
assert(slink);
|
||||||
|
|
||||||
slink_dirname = dirname_malloc(slink);
|
r = path_extract_directory(slink, &slink_dirname);
|
||||||
if (!slink_dirname)
|
if (r < 0)
|
||||||
return log_oom();
|
return log_device_debug_errno(dev, r, "Failed to get parent directory of '%s': %m", slink);
|
||||||
|
|
||||||
/* use relative link */
|
/* use relative link */
|
||||||
r = path_make_relative(slink_dirname, node, &target);
|
r = path_make_relative(slink_dirname, node, &target);
|
||||||
if (r < 0)
|
if (r < 0)
|
||||||
return log_device_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 (lstat(slink, &stats) == 0) {
|
_cleanup_free_ char *buf = NULL;
|
||||||
if (S_ISBLK(stats.st_mode) || S_ISCHR(stats.st_mode))
|
|
||||||
return log_device_error_errno(dev, SYNTHETIC_ERRNO(EOPNOTSUPP),
|
if (!S_ISLNK(stats.st_mode))
|
||||||
"Conflicting device node '%s' found, link to '%s' will not be created.", slink, node);
|
return log_device_debug_errno(dev, SYNTHETIC_ERRNO(EEXIST),
|
||||||
else if (S_ISLNK(stats.st_mode)) {
|
"Conflicting inode '%s' found, link to '%s' will not be created.", slink, node);
|
||||||
_cleanup_free_ char *buf = NULL;
|
|
||||||
|
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;
|
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);
|
log_device_debug(dev, "Atomically replace '%s'", slink);
|
||||||
|
|
||||||
r = device_get_device_id(dev, &id);
|
r = device_get_device_id(dev, &id);
|
||||||
if (r < 0)
|
if (r < 0)
|
||||||
return log_device_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);
|
slink_tmp = strjoina(slink, ".tmp-", id);
|
||||||
|
|
||||||
(void) unlink(slink_tmp);
|
(void) unlink(slink_tmp);
|
||||||
do {
|
|
||||||
r = mkdir_parents_label(slink_tmp, 0755);
|
r = create_symlink(target, slink_tmp);
|
||||||
if (!IN_SET(r, 0, -ENOENT))
|
|
||||||
break;
|
|
||||||
mac_selinux_create_file_prepare(slink_tmp, S_IFLNK);
|
|
||||||
if (symlink(target, slink_tmp) < 0)
|
|
||||||
r = -errno;
|
|
||||||
mac_selinux_create_file_clear();
|
|
||||||
} while (r == -ENOENT);
|
|
||||||
if (r < 0)
|
if (r < 0)
|
||||||
return log_device_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) {
|
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);
|
(void) unlink(slink_tmp);
|
||||||
} else
|
return r;
|
||||||
/* 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) {
|
static int link_find_prioritized(sd_device *dev, bool add, const char *stackdir, char **ret) {
|
||||||
_cleanup_closedir_ DIR *dir = NULL;
|
_cleanup_closedir_ DIR *dir = NULL;
|
||||||
_cleanup_free_ char *target = NULL;
|
_cleanup_free_ char *target = NULL;
|
||||||
struct dirent *dent;
|
struct dirent *dent;
|
||||||
int r, priority = 0;
|
int r, priority = 0;
|
||||||
|
const char *id;
|
||||||
|
|
||||||
assert(!add || dev);
|
assert(dev);
|
||||||
assert(stackdir);
|
assert(stackdir);
|
||||||
assert(ret);
|
assert(ret);
|
||||||
|
|
||||||
|
/* Find device node of device with highest priority. This returns 1 if a device found, 0 if no
|
||||||
|
* device found, or a negative errno. */
|
||||||
|
|
||||||
if (add) {
|
if (add) {
|
||||||
const char *devnode;
|
const char *devnode;
|
||||||
|
|
||||||
@ -137,17 +161,21 @@ static int link_find_prioritized(sd_device *dev, bool add, const char *stackdir,
|
|||||||
|
|
||||||
dir = opendir(stackdir);
|
dir = opendir(stackdir);
|
||||||
if (!dir) {
|
if (!dir) {
|
||||||
if (target) {
|
if (errno == ENOENT) {
|
||||||
*ret = TAKE_PTR(target);
|
*ret = TAKE_PTR(target);
|
||||||
return 0;
|
return !!*ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
return -errno;
|
return -errno;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
r = device_get_device_id(dev, &id);
|
||||||
|
if (r < 0)
|
||||||
|
return r;
|
||||||
|
|
||||||
FOREACH_DIRENT_ALL(dent, dir, break) {
|
FOREACH_DIRENT_ALL(dent, dir, break) {
|
||||||
_cleanup_(sd_device_unrefp) sd_device *dev_db = NULL;
|
_cleanup_(sd_device_unrefp) sd_device *dev_db = NULL;
|
||||||
const char *devnode, *id;
|
const char *devnode;
|
||||||
int db_prio = 0;
|
int db_prio = 0;
|
||||||
|
|
||||||
if (dent->d_name[0] == '\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);
|
log_device_debug(dev, "Found '%s' claiming '%s'", dent->d_name, stackdir);
|
||||||
|
|
||||||
if (device_get_device_id(dev, &id) < 0)
|
|
||||||
continue;
|
|
||||||
|
|
||||||
/* did we find ourself? */
|
/* did we find ourself? */
|
||||||
if (streq(dent->d_name, id))
|
if (streq(dent->d_name, id))
|
||||||
continue;
|
continue;
|
||||||
@ -184,59 +209,73 @@ static int link_find_prioritized(sd_device *dev, bool add, const char *stackdir,
|
|||||||
priority = db_prio;
|
priority = db_prio;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!target)
|
|
||||||
return -ENOENT;
|
|
||||||
|
|
||||||
*ret = TAKE_PTR(target);
|
*ret = TAKE_PTR(target);
|
||||||
return 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;
|
size_t i, j;
|
||||||
|
uint64_t h;
|
||||||
|
|
||||||
assert(src);
|
assert(src);
|
||||||
assert(dest);
|
assert(dest);
|
||||||
|
|
||||||
for (i = 0, j = 0; src[i] != '\0'; i++) {
|
for (i = 0, j = 0; src[i] != '\0'; i++) {
|
||||||
if (src[i] == '/') {
|
if (src[i] == '/') {
|
||||||
if (j+4 >= size) {
|
if (j+4 >= size)
|
||||||
j = 0;
|
goto toolong;
|
||||||
break;
|
|
||||||
}
|
|
||||||
memcpy(&dest[j], "\\x2f", 4);
|
memcpy(&dest[j], "\\x2f", 4);
|
||||||
j += 4;
|
j += 4;
|
||||||
} else if (src[i] == '\\') {
|
} else if (src[i] == '\\') {
|
||||||
if (j+4 >= size) {
|
if (j+4 >= size)
|
||||||
j = 0;
|
goto toolong;
|
||||||
break;
|
|
||||||
}
|
|
||||||
memcpy(&dest[j], "\\x5c", 4);
|
memcpy(&dest[j], "\\x5c", 4);
|
||||||
j += 4;
|
j += 4;
|
||||||
} else {
|
} else {
|
||||||
if (j+1 >= size) {
|
if (j+1 >= size)
|
||||||
j = 0;
|
goto toolong;
|
||||||
break;
|
|
||||||
}
|
|
||||||
dest[j] = src[i];
|
dest[j] = src[i];
|
||||||
j++;
|
j++;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
dest[j] = '\0';
|
dest[j] = '\0';
|
||||||
return j;
|
return j;
|
||||||
|
|
||||||
|
toolong:
|
||||||
|
/* If the input path is too long to encode as a filename, then let's suffix with a string
|
||||||
|
* generated from the hash of the path. */
|
||||||
|
|
||||||
|
h = siphash24_string(src, UDEV_NODE_HASH_KEY.bytes);
|
||||||
|
|
||||||
|
assert(size >= 12);
|
||||||
|
|
||||||
|
for (unsigned k = 0; k <= 10; k++)
|
||||||
|
dest[size - k - 2] = urlsafe_base64char((h >> (k * 6)) & 63);
|
||||||
|
|
||||||
|
dest[size - 1] = '\0';
|
||||||
|
return size - 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* manage "stack of names" with possibly specified device priorities */
|
/* manage "stack of names" with possibly specified device priorities */
|
||||||
static int link_update(sd_device *dev, const char *slink, bool add) {
|
static int link_update(sd_device *dev, const char *slink_in, bool add) {
|
||||||
_cleanup_free_ char *filename = NULL, *dirname = NULL;
|
_cleanup_free_ char *slink = NULL, *filename = NULL, *dirname = NULL;
|
||||||
const char *slink_name, *id;
|
const char *slink_name, *id;
|
||||||
char name_enc[PATH_MAX];
|
char name_enc[NAME_MAX+1];
|
||||||
int i, r, retries;
|
int i, r, retries;
|
||||||
|
|
||||||
assert(dev);
|
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");
|
slink_name = path_startswith(slink, "/dev");
|
||||||
if (!slink_name)
|
if (!slink_name ||
|
||||||
|
empty_or_root(slink_name) ||
|
||||||
|
!path_is_normalized(slink_name))
|
||||||
return log_device_debug_errno(dev, SYNTHETIC_ERRNO(EINVAL),
|
return log_device_debug_errno(dev, SYNTHETIC_ERRNO(EINVAL),
|
||||||
"Invalid symbolic link of device node: %s", slink);
|
"Invalid symbolic link of device node: %s", slink);
|
||||||
|
|
||||||
@ -244,31 +283,31 @@ static int link_update(sd_device *dev, const char *slink, bool add) {
|
|||||||
if (r < 0)
|
if (r < 0)
|
||||||
return log_device_debug_errno(dev, r, "Failed to get device id: %m");
|
return log_device_debug_errno(dev, r, "Failed to get device id: %m");
|
||||||
|
|
||||||
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);
|
dirname = path_join("/run/udev/links/", name_enc);
|
||||||
if (!dirname)
|
if (!dirname)
|
||||||
return log_oom();
|
return log_oom_debug();
|
||||||
|
|
||||||
filename = path_join(dirname, id);
|
filename = path_join(dirname, id);
|
||||||
if (!filename)
|
if (!filename)
|
||||||
return log_oom();
|
return log_oom_debug();
|
||||||
|
|
||||||
if (!add) {
|
if (!add) {
|
||||||
if (unlink(filename) == 0)
|
if (unlink(filename) < 0 && errno != ENOENT)
|
||||||
(void) rmdir(dirname);
|
log_device_debug_errno(dev, errno, "Failed to remove %s, ignoring: %m", filename);
|
||||||
} else
|
|
||||||
for (;;) {
|
|
||||||
_cleanup_close_ int fd = -1;
|
|
||||||
|
|
||||||
r = mkdir_parents(filename, 0755);
|
(void) rmdir(dirname);
|
||||||
if (!IN_SET(r, 0, -ENOENT))
|
} else {
|
||||||
return r;
|
for (unsigned j = 0; j < TOUCH_FILE_MAX_RETRIES; j++) {
|
||||||
|
/* This may fail with -ENOENT when the parent directory is removed during
|
||||||
fd = open(filename, O_WRONLY|O_CREAT|O_CLOEXEC|O_TRUNC|O_NOFOLLOW, 0444);
|
* creating the file by another udevd worker. */
|
||||||
if (fd >= 0)
|
r = touch_file(filename, /* parents= */ true, USEC_INFINITY, UID_INVALID, GID_INVALID, 0444);
|
||||||
|
if (r != -ENOENT)
|
||||||
break;
|
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
|
/* 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. */
|
* 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);
|
r = stat(dirname, &st1);
|
||||||
if (r < 0 && errno != ENOENT)
|
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);
|
r = link_find_prioritized(dev, add, dirname, &target);
|
||||||
if (r == -ENOENT) {
|
if (r < 0)
|
||||||
log_device_debug(dev, "No reference left, removing '%s'", slink);
|
return log_device_debug_errno(dev, r, "Failed to determine highest priority for symlink '%s': %m", slink);
|
||||||
if (unlink(slink) == 0)
|
if (r == 0) {
|
||||||
(void) rmdir_parents(slink, "/");
|
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;
|
break;
|
||||||
} else if (r < 0)
|
}
|
||||||
return log_device_error_errno(dev, r, "Failed to determine highest priority symlink: %m");
|
|
||||||
|
|
||||||
r = node_symlink(dev, target, slink);
|
r = node_symlink(dev, target, slink);
|
||||||
if (r < 0) {
|
if (r < 0)
|
||||||
(void) unlink(filename);
|
return r;
|
||||||
break;
|
if (r == 1)
|
||||||
} else if (r == 1)
|
|
||||||
/* We have replaced already existing symlink, possibly there is some other device trying
|
/* We have replaced already existing symlink, possibly there is some other device trying
|
||||||
* to claim the same symlink. Let's do one more iteration to give us a chance to fix
|
* to claim the same symlink. Let's do one more iteration to give us a chance to fix
|
||||||
* the error if other device actually claims the symlink with higher priority. */
|
* the error if other device actually claims the symlink with higher priority. */
|
||||||
@ -306,7 +347,7 @@ static int link_update(sd_device *dev, const char *slink, bool add) {
|
|||||||
if ((st1.st_mode & S_IFMT) != 0) {
|
if ((st1.st_mode & S_IFMT) != 0) {
|
||||||
r = stat(dirname, &st2);
|
r = stat(dirname, &st2);
|
||||||
if (r < 0 && errno != ENOENT)
|
if (r < 0 && errno != ENOENT)
|
||||||
return -errno;
|
return log_device_debug_errno(dev, errno, "Failed to stat %s: %m", dirname);
|
||||||
|
|
||||||
if (stat_inode_unmodified(&st1, &st2))
|
if (stat_inode_unmodified(&st1, &st2))
|
||||||
break;
|
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) {
|
int udev_node_update_old_links(sd_device *dev, sd_device *dev_old) {
|
||||||
const char *name, *devpath;
|
const char *name;
|
||||||
int r;
|
int r;
|
||||||
|
|
||||||
assert(dev);
|
assert(dev);
|
||||||
assert(dev_old);
|
assert(dev_old);
|
||||||
|
|
||||||
r = sd_device_get_devpath(dev, &devpath);
|
|
||||||
if (r < 0)
|
|
||||||
return log_device_debug_errno(dev, r, "Failed to get devpath: %m");
|
|
||||||
|
|
||||||
/* update possible left-over symlinks */
|
/* update possible left-over symlinks */
|
||||||
FOREACH_DEVICE_DEVLINK(dev_old, name) {
|
FOREACH_DEVICE_DEVLINK(dev_old, name) {
|
||||||
const char *name_current;
|
const char *name_current;
|
||||||
@ -342,8 +379,10 @@ int udev_node_update_old_links(sd_device *dev, sd_device *dev_old) {
|
|||||||
if (found)
|
if (found)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
log_device_debug(dev, "Updating old name, '%s' no longer belonging to '%s'",
|
log_device_debug(dev,
|
||||||
name, devpath);
|
"Updating old device symlink '%s', which is no longer belonging to this device.",
|
||||||
|
name);
|
||||||
|
|
||||||
r = link_update(dev, name, false);
|
r = link_update(dev, name, false);
|
||||||
if (r < 0)
|
if (r < 0)
|
||||||
log_device_warning_errno(dev, r,
|
log_device_warning_errno(dev, r,
|
||||||
@ -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) {
|
static int xsprintf_dev_num_path_from_sd_device(sd_device *dev, char **ret) {
|
||||||
char filename[DEV_NUM_PATH_MAX], *s;
|
|
||||||
const char *subsystem;
|
const char *subsystem;
|
||||||
dev_t devnum;
|
dev_t devnum;
|
||||||
int r;
|
int r;
|
||||||
@ -492,16 +530,7 @@ static int xsprintf_dev_num_path_from_sd_device(sd_device *dev, char **ret) {
|
|||||||
if (r < 0)
|
if (r < 0)
|
||||||
return r;
|
return r;
|
||||||
|
|
||||||
xsprintf_dev_num_path(filename,
|
return device_path_make_major_minor(streq(subsystem, "block") ? S_IFBLK : S_IFCHR, devnum, ret);
|
||||||
streq(subsystem, "block") ? "block" : "char",
|
|
||||||
devnum);
|
|
||||||
|
|
||||||
s = strdup(filename);
|
|
||||||
if (!s)
|
|
||||||
return -ENOMEM;
|
|
||||||
|
|
||||||
*ret = s;
|
|
||||||
return 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
int udev_node_add(sd_device *dev, bool apply,
|
int udev_node_add(sd_device *dev, bool apply,
|
||||||
@ -528,13 +557,6 @@ int udev_node_add(sd_device *dev, bool apply,
|
|||||||
if (r < 0)
|
if (r < 0)
|
||||||
return r;
|
return r;
|
||||||
|
|
||||||
r = xsprintf_dev_num_path_from_sd_device(dev, &filename);
|
|
||||||
if (r < 0)
|
|
||||||
return log_device_debug_errno(dev, r, "Failed to get device path: %m");
|
|
||||||
|
|
||||||
/* always add /dev/{block,char}/$major:$minor */
|
|
||||||
(void) node_symlink(dev, devnode, filename);
|
|
||||||
|
|
||||||
/* create/update symlinks, add symlinks to name index */
|
/* create/update symlinks, add symlinks to name index */
|
||||||
FOREACH_DEVICE_DEVLINK(dev, devlink) {
|
FOREACH_DEVICE_DEVLINK(dev, devlink) {
|
||||||
r = link_update(dev, devlink, true);
|
r = link_update(dev, devlink, true);
|
||||||
@ -544,6 +566,15 @@ int udev_node_add(sd_device *dev, bool apply,
|
|||||||
devlink);
|
devlink);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
r = xsprintf_dev_num_path_from_sd_device(dev, &filename);
|
||||||
|
if (r < 0)
|
||||||
|
return log_device_debug_errno(dev, r, "Failed to get device path: %m");
|
||||||
|
|
||||||
|
/* always add /dev/{block,char}/$major:$minor */
|
||||||
|
r = node_symlink(dev, devnode, filename);
|
||||||
|
if (r < 0)
|
||||||
|
return log_device_warning_errno(dev, r, "Failed to create device symlink '%s': %m", filename);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -568,7 +599,8 @@ int udev_node_remove(sd_device *dev) {
|
|||||||
return log_device_debug_errno(dev, r, "Failed to get device path: %m");
|
return log_device_debug_errno(dev, r, "Failed to get device path: %m");
|
||||||
|
|
||||||
/* remove /dev/{block,char}/$major:$minor */
|
/* remove /dev/{block,char}/$major:$minor */
|
||||||
(void) unlink(filename);
|
if (unlink(filename) < 0 && errno != ENOENT)
|
||||||
|
return log_device_debug_errno(dev, errno, "Failed to remove '%s': %m", filename);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -13,3 +13,5 @@ int udev_node_add(sd_device *dev, bool apply,
|
|||||||
OrderedHashmap *seclabel_list);
|
OrderedHashmap *seclabel_list);
|
||||||
int udev_node_remove(sd_device *dev);
|
int udev_node_remove(sd_device *dev);
|
||||||
int udev_node_update_old_links(sd_device *dev, sd_device *dev_old);
|
int udev_node_update_old_links(sd_device *dev, sd_device *dev_old);
|
||||||
|
|
||||||
|
size_t udev_node_escape_path(const char *src, char *dest, size_t size);
|
||||||
|
|||||||
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