Compare commits
108 Commits
c77389e0c1
...
b3c41c9849
Author | SHA1 | Date |
---|---|---|
Mike Yuan | b3c41c9849 | |
Mike Yuan | 97cdb64101 | |
Mike Yuan | 8606e63208 | |
Mike Yuan | a05de0a7fa | |
Mike Yuan | 77e528bda1 | |
Mike Yuan | dd3672539a | |
Mike Yuan | 1f62c885d7 | |
Mike Yuan | da9ff07fa4 | |
Mike Yuan | 394068f136 | |
Mike Yuan | 79495b0904 | |
Mike Yuan | 727bb81a99 | |
Mike Yuan | 97f00da913 | |
Mike Yuan | 8dd6249e0d | |
Mike Yuan | 3cb72f0fe2 | |
Mike Yuan | b9503dfa80 | |
Mike Yuan | c0e8dcfe1a | |
Mike Yuan | 395f57bbce | |
Mike Yuan | bd8316cb29 | |
Mike Yuan | b7d5b0d33b | |
Daan De Meyer | bc9a9177b2 | |
Yu Watanabe | a13ead6814 | |
Yu Watanabe | f901a7b39f | |
Yu Watanabe | 9b01cf0406 | |
Yu Watanabe | d5aae0713d | |
Daan De Meyer | 86c1317270 | |
Daan De Meyer | f4faac2073 | |
Yu Watanabe | 2bcc2a89f3 | |
Yu Watanabe | 07e6a111c0 | |
Yu Watanabe | c2648f6e23 | |
Daan De Meyer | 1d5b4317cd | |
Frantisek Sumsal | cd57920fbf | |
Yu Watanabe | 8d6eedd8a3 | |
Yu Watanabe | 91eaa90b81 | |
Yu Watanabe | 3b5c5da73a | |
Yu Watanabe | 1775654e2c | |
Yu Watanabe | 0ea6d55a4b | |
Yu Watanabe | 26d35019de | |
Yu Watanabe | b962338104 | |
Yu Watanabe | fae0b00434 | |
Yu Watanabe | f7923ef318 | |
Yu Watanabe | 36df48d863 | |
Yu Watanabe | 53c638db16 | |
Yu Watanabe | 751a247794 | |
Yu Watanabe | 07dbbda0fc | |
Yu Watanabe | ed4a6c476e | |
Antonio Alvarez Feijoo | fb4c82b643 | |
Daan De Meyer | 4d9ccdc9ae | |
Antonio Alvarez Feijoo | bf39626d61 | |
Marius Hoch | ff831e7c50 | |
Daan De Meyer | 81af8f998e | |
chenjiayi | 4fc8a63f9e | |
Jason Yundt | dfb3155419 | |
Daan De Meyer | fc5037e7d7 | |
Yu Watanabe | 13f6ec7ce7 | |
Yu Watanabe | 6e1816ef16 | |
Yu Watanabe | 7ac1ad90d0 | |
Daan De Meyer | 099b16c3e7 | |
Daan De Meyer | 7a7f306b6c | |
Yu Watanabe | 4f2975385f | |
Daan De Meyer | 0432e28394 | |
Yu Watanabe | fc956a3973 | |
Yu Watanabe | d265b8afb7 | |
Yu Watanabe | 1aab0a5b10 | |
Yu Watanabe | b0dbb4aa3a | |
Michael Ferrari | 91ea3dcf35 | |
Yu Watanabe | a95ae2d36a | |
Yu Watanabe | be8e4b1a87 | |
Adrian Vovk | cf612c5fd5 | |
Adrian Vovk | 2cb9c68c3a | |
Adrian Vovk | 78e9059208 | |
Adrian Vovk | e671bdc5c3 | |
Yu Watanabe | 572d031eca | |
Yu Watanabe | 25da422bd1 | |
Yu Watanabe | 5872ea7008 | |
PavlNekrasov | d80a9042ca | |
Yu Watanabe | a7afe5a3e7 | |
Lennart Poettering | a2369d0224 | |
Lennart Poettering | a37640653c | |
Yu Watanabe | a65b864835 | |
Yu Watanabe | 9959681a0d | |
Daan De Meyer | b3ebd480d6 | |
Arian van Putten | 6695ff4c15 | |
Yu Watanabe | 4d6ad22f8d | |
Yu Watanabe | 099ee34ca1 | |
Yu Watanabe | a2fbe9f3f9 | |
Yu Watanabe | 7c778cecdb | |
Yu Watanabe | 46718d344f | |
Yu Watanabe | 9295c7ae09 | |
Yu Watanabe | 41afafbf2a | |
Yu Watanabe | 9671efff78 | |
Yu Watanabe | 4f0bc2582e | |
Yu Watanabe | 3292120adf | |
Yu Watanabe | f6cc5e1c8d | |
Yu Watanabe | 590f430cac | |
Mike Yuan | 93d2d36638 | |
Lennart Poettering | 369b12375b | |
Yu Watanabe | b5ec8f77e0 | |
Lennart Poettering | 3e0a3a0259 | |
Celeste Liu | 6573f0c82c | |
Daan De Meyer | e0258ac886 | |
Lennart Poettering | a859d0d378 | |
Lennart Poettering | db15657dfb | |
Lennart Poettering | 2aa3005ad2 | |
Lennart Poettering | 90cf998875 | |
Lennart Poettering | c8d60ae79d | |
Lennart Poettering | bfcf48b842 | |
Yu Watanabe | 5f5c5c48b9 | |
Yu Watanabe | 68fdef46a7 |
13
NEWS
13
NEWS
|
@ -2,6 +2,15 @@ systemd System and Service Manager
|
|||
|
||||
CHANGES WITH 257 in spe:
|
||||
|
||||
Incompatible changes:
|
||||
|
||||
* The --purge switch of systemd-tmpfiles (which was added in v256) has
|
||||
been reworked: it will now only apply to tmpfiles.d/ lines marked
|
||||
with the new "$" flag. This is an incompatible change, and means any
|
||||
tmpfiles.d/ files which shall be used together with --purge need to
|
||||
be updated accordingly. This change has been made to make it harder
|
||||
to accidentally delete too many files when using --purge incorrectly.
|
||||
|
||||
Announcements of Future Feature Removals and Incompatible Changes:
|
||||
|
||||
* Support for automatic flushing of the nscd user/group database caches
|
||||
|
@ -85,7 +94,7 @@ CHANGES WITH 257 in spe:
|
|||
/usr/lib/clock-epoch, and /var/lib/systemd/timesync/clock. See
|
||||
systemd(1) for an detailed updated description.
|
||||
|
||||
* Ctrl-Alt-Delete is reenabled during late shutdown, so that the user
|
||||
* Ctrl-Alt-Delete is re-enabled during late shutdown, so that the user
|
||||
can still initiate a reboot if the system freezes.
|
||||
|
||||
* Unit option PrivateUsers=identity can be used to request a user
|
||||
|
@ -202,7 +211,7 @@ CHANGES WITH 257 in spe:
|
|||
versions.
|
||||
|
||||
* systemd-sysupdate gained a new --transfer-source= option to set the
|
||||
directory to which transfer sources cofigured with
|
||||
directory to which transfer sources configured with
|
||||
PathRelativeTo=explicit will be interpreted.
|
||||
|
||||
Miscellaneous:
|
||||
|
|
4
TODO
4
TODO
|
@ -189,6 +189,8 @@ Features:
|
|||
* go through our codebase, and convert "vertical tables" (i.e. things such as
|
||||
"systemctl status") to use table_new_vertical() for output
|
||||
|
||||
* pcrlock: add support for multi-profile UKIs
|
||||
|
||||
* logind: when logging in use new tmpfs quota support to configure quota on
|
||||
/tmp/ + /dev/shm/. But do so only in case of tmpfs, because otherwise quota
|
||||
is persistent and any persistent settings mean we don#t have to reapply them.
|
||||
|
@ -1860,7 +1862,7 @@ Features:
|
|||
|
||||
* fstab-generator: default to tmpfs-as-root if only usr= is specified on the kernel cmdline
|
||||
|
||||
* docs: bring https://systemd.io/MY_SERVICE_CANT_GET_REATLIME up to date
|
||||
* docs: bring https://systemd.io/MY_SERVICE_CANT_GET_REALTIME up to date
|
||||
|
||||
* add a job mode that will fail if a transaction would mean stopping
|
||||
running units. Use this in timedated to manage the NTP service
|
||||
|
|
|
@ -247,4 +247,4 @@ Note that scope units created by `machined`'s `CreateMachine()` call have this f
|
|||
|
||||
### Example
|
||||
|
||||
Please see the [systemd-run sources](http://cgit.freedesktop.org/systemd/systemd/plain/src/run/run.c) for a relatively simple example how to create scope or service units transiently and pass properties to them.
|
||||
Please see the [systemd-run sources](https://github.com/systemd/systemd/blob/main/src/run/run.c) for a relatively simple example how to create scope or service units transiently and pass properties to them.
|
||||
|
|
|
@ -104,7 +104,7 @@ A: Use:
|
|||
|
||||
**Q: Whenever my service tries to acquire RT scheduling for one of its threads this is refused with EPERM even though my service is running with full privileges. This works fine on my non-systemd system!**
|
||||
|
||||
A: By default, systemd places all systemd daemons in their own cgroup in the "cpu" hierarchy. Unfortunately, due to a kernel limitation, this has the effect of disallowing RT entirely for the service. See [My Service Can't Get Realtime!](/MY_SERVICE_CANT_GET_REATLIME) for a longer discussion and what to do about this.
|
||||
A: By default, systemd places all systemd daemons in their own cgroup in the "cpu" hierarchy. Unfortunately, due to a kernel limitation, this has the effect of disallowing RT entirely for the service. See [My Service Can't Get Realtime!](/MY_SERVICE_CANT_GET_REALTIME) for a longer discussion and what to do about this.
|
||||
|
||||
**Q: My service is ordered after `network.target` but at boot it is still called before the network is up. What's going on?**
|
||||
|
||||
|
|
|
@ -760,8 +760,9 @@ sensor:modalias:i2c:bmc150_accel:dmi:*:svnLENOVO:*:pvrLenovoYoga300-11IBR:*
|
|||
sensor:modalias:acpi:ACCL0001*:dmi:*:svnLENOVO:pn60072:pvr851*:*
|
||||
ACCEL_MOUNT_MATRIX=0, 1, 0; -1, 0, 0; 0, 0, 1
|
||||
|
||||
# IdeaPad Duet 3 10IGL5 (82AT)
|
||||
# IdeaPad Duet 3 10IGL5 (82AT) and 10IGL5-LTE (82HK)
|
||||
sensor:modalias:acpi:SMO8B30*:dmi:*:svnLENOVO*:pn82AT:*
|
||||
sensor:modalias:acpi:SMO8B30*:dmi:*:svnLENOVO*:pn82HK:*
|
||||
ACCEL_MOUNT_MATRIX=0, 1, 0; -1, 0, 0; 0, 0, 1
|
||||
|
||||
#########################################
|
||||
|
|
|
@ -310,6 +310,10 @@ mouse:bluetooth:v047dp8019:name:Expert Wireless TB Mouse:*
|
|||
ID_INPUT_TRACKBALL=1
|
||||
MOUSE_DPI=400@125
|
||||
|
||||
# Kensington SlimBlade Pro trackball (via Bluetooth)
|
||||
mouse:bluetooth:v047dp80d4:name:SlimBlade Pro:*
|
||||
ID_INPUT_TRACKBALL=1
|
||||
|
||||
##########################################
|
||||
# Lenovo
|
||||
##########################################
|
||||
|
|
|
@ -267,7 +267,8 @@
|
|||
<term><option>kernel-identify</option> <replaceable>kernel</replaceable></term>
|
||||
|
||||
<listitem><para>Takes a kernel image as argument. Checks what kind of kernel the image is. Returns
|
||||
one of <literal>uki</literal>, <literal>pe</literal>, and <literal>unknown</literal>.
|
||||
one of <literal>uki</literal>, <literal>addon</literal>, <literal>pe</literal>, and
|
||||
<literal>unknown</literal>.
|
||||
</para>
|
||||
|
||||
<xi:include href="version-info.xml" xpointer="v253"/></listitem>
|
||||
|
@ -360,6 +361,24 @@
|
|||
<xi:include href="version-info.xml" xpointer="v242"/></listitem>
|
||||
</varlistentry>
|
||||
|
||||
<varlistentry>
|
||||
<term><option>--print-loader-path</option></term>
|
||||
<listitem><para>This option modifies the behaviour of <command>status</command>: it shows the
|
||||
absolute path to the boot loader EFI binary used for the current boot if this information is
|
||||
available. Note that no attempt is made to verify whether the binary still exists.</para>
|
||||
|
||||
<xi:include href="version-info.xml" xpointer="v257"/></listitem>
|
||||
</varlistentry>
|
||||
|
||||
<varlistentry>
|
||||
<term><option>--print-stub-path</option></term>
|
||||
<listitem><para>This option modifies the behaviour of <command>status</command>: it shows the
|
||||
absolute path to the UKI/stub EFI binary used for the current boot if this information is
|
||||
available. Note that no attempt is made to verify whether the binary still exists.</para>
|
||||
|
||||
<xi:include href="version-info.xml" xpointer="v257"/></listitem>
|
||||
</varlistentry>
|
||||
|
||||
<varlistentry>
|
||||
<term><option>-R</option></term>
|
||||
<term><option>--print-root-device</option></term>
|
||||
|
|
|
@ -593,8 +593,6 @@ node /org/freedesktop/systemd1 {
|
|||
|
||||
<!--method GetJobBefore is not documented!-->
|
||||
|
||||
<!--method SetShowStatus is not documented!-->
|
||||
|
||||
<!--method ListUnitsFiltered is not documented!-->
|
||||
|
||||
<!--method ListUnitsByPatterns is not documented!-->
|
||||
|
@ -673,8 +671,6 @@ node /org/freedesktop/systemd1 {
|
|||
|
||||
<!--property ConfirmSpawn is not documented!-->
|
||||
|
||||
<!--property ShowStatus is not documented!-->
|
||||
|
||||
<!--property DefaultStandardOutput is not documented!-->
|
||||
|
||||
<!--property DefaultStandardError is not documented!-->
|
||||
|
@ -1362,6 +1358,24 @@ node /org/freedesktop/systemd1 {
|
|||
|
||||
<para><function>ResetFailedUnit()</function> resets the "failed" state of a specific unit.</para>
|
||||
|
||||
<para><function>SetShowStatus()</function> configures the display of status messages during bootup and
|
||||
shutdown. The <varname>mode</varname> parameter can be set to any value that's valid for the
|
||||
<varname>systemd.show_status</varname> kernel parameter. For more information about
|
||||
<varname>systemd.show_status</varname>, see
|
||||
<citerefentry project="man-pages"><refentrytitle>systemd</refentrytitle><manvolnum>1</manvolnum></citerefentry>.
|
||||
The <varname>mode</varname> parameter can also be set to an empty string. When <varname>mode</varname>
|
||||
is set to an empty string, <function>SetShowStatus()</function> will reset
|
||||
<varname>ShowStatus</varname> back to its original value. You can use
|
||||
<function>SetShowStatus()</function> create a service that does something like this:
|
||||
<orderedlist>
|
||||
<listitem><para>Send a D-Bus message that will turn off status messages.</para></listitem>
|
||||
<listitem><para>Block until a reply to that message is received.</para></listitem>
|
||||
<listitem><para>Print multiples lines without being interrupted by status messages.</para></listitem>
|
||||
<listitem><para>Send a D-Bus message that will reset <varname>ShowStatus</varname> back to its
|
||||
original value.</para></listitem>
|
||||
</orderedlist>
|
||||
</para>
|
||||
|
||||
<para><function>ResetFailed()</function> resets the "failed" state of all units.</para>
|
||||
|
||||
<para><function>ListUnits()</function> returns an array of all currently loaded units. Note that
|
||||
|
@ -1788,6 +1802,12 @@ node /org/freedesktop/systemd1 {
|
|||
<para><varname>Environment</varname> encodes the environment block passed to all executed services. It
|
||||
may be altered with bus calls such as <function>SetEnvironment()</function> (see above).</para>
|
||||
|
||||
<para><varname>ShowStatus</varname> encodes systemd's current policy for displaying status messages
|
||||
during bootup and shutdown. Its value can be any valid value for the
|
||||
<varname>systemd.show_status</varname> kernel parameter (see
|
||||
<citerefentry project="man-pages"><refentrytitle>systemd</refentrytitle><manvolnum>1</manvolnum></citerefentry>).
|
||||
It may be altered using <function>SetShowStatus()</function> (see above).</para>
|
||||
|
||||
<para><varname>UnitPath</varname> encodes the currently active unit file search path. It is an array of
|
||||
file system paths encoded as strings.</para>
|
||||
|
||||
|
|
|
@ -76,16 +76,7 @@
|
|||
<term><varname>Type=</varname></term>
|
||||
|
||||
<listitem><para>The GPT partition type UUID to match. This may be a GPT partition type UUID such as
|
||||
<constant>4f68bce3-e8cd-4db1-96e7-fbcaf984b709</constant>, or an identifier.
|
||||
Architecture specific partition types can use one of these architecture identifiers:
|
||||
<constant>alpha</constant>, <constant>arc</constant>, <constant>arm</constant> (32-bit),
|
||||
<constant>arm64</constant> (64-bit, aka aarch64), <constant>ia64</constant>,
|
||||
<constant>loongarch64</constant>, <constant>mips-le</constant>, <constant>mips64-le</constant>,
|
||||
<constant>parisc</constant>, <constant>ppc</constant>, <constant>ppc64</constant>,
|
||||
<constant>ppc64-le</constant>, <constant>riscv32</constant>, <constant>riscv64</constant>,
|
||||
<constant>s390</constant>, <constant>s390x</constant>, <constant>tilegx</constant>,
|
||||
<constant>x86</constant> (32-bit, aka i386) and <constant>x86-64</constant> (64-bit, aka amd64).
|
||||
</para>
|
||||
<constant>4f68bce3-e8cd-4db1-96e7-fbcaf984b709</constant>, or an identifier.</para>
|
||||
|
||||
<para>The supported identifiers are:</para>
|
||||
|
||||
|
@ -237,7 +228,14 @@
|
|||
</tgroup>
|
||||
</table>
|
||||
|
||||
<para>This setting defaults to <constant>linux-generic</constant>.</para>
|
||||
<para>Architecture specific partition types can use one of these architecture identifiers:
|
||||
<constant>alpha</constant>, <constant>arc</constant>, <constant>arm</constant> (32-bit),
|
||||
<constant>arm64</constant> (64-bit, aka aarch64), <constant>ia64</constant>,
|
||||
<constant>loongarch64</constant>, <constant>mips-le</constant>, <constant>mips64-le</constant>,
|
||||
<constant>parisc</constant>, <constant>ppc</constant>, <constant>ppc64</constant>,
|
||||
<constant>ppc64-le</constant>, <constant>riscv32</constant>, <constant>riscv64</constant>,
|
||||
<constant>s390</constant>, <constant>s390x</constant>, <constant>tilegx</constant>,
|
||||
<constant>x86</constant> (32-bit, aka i386) and <constant>x86-64</constant> (64-bit, aka amd64).</para>
|
||||
|
||||
<para>Most of the partition type UUIDs listed above are defined in the <ulink
|
||||
url="https://uapi-group.org/specifications/specs/discoverable_partitions_specification">Discoverable Partitions
|
||||
|
@ -485,18 +483,18 @@
|
|||
<term><varname>ExcludeFiles=</varname></term>
|
||||
<term><varname>ExcludeFilesTarget=</varname></term>
|
||||
|
||||
<listitem><para>Takes an absolute file system path referring to a source file or directory on the
|
||||
host. This setting may be used to exclude files or directories from the host from being copied into
|
||||
the file system when <varname>CopyFiles=</varname> is used. This option may be used multiple times to
|
||||
exclude multiple files or directories from host from being copied into the newly formatted file
|
||||
system.</para>
|
||||
<listitem><para>Takes one or more absolute paths, separated by whitespace, each referring to a
|
||||
source file or directory on the host. This setting may be used to exclude files or directories from
|
||||
the host from being copied into the file system when <varname>CopyFiles=</varname> is used. This
|
||||
option may be used multiple times to exclude multiple files or directories from host from being
|
||||
copied into the newly formatted file system.</para>
|
||||
|
||||
<para>If the path is a directory and ends with <literal>/</literal>, only the directory's
|
||||
contents are excluded but not the directory itself. If the path is a directory and does not end with
|
||||
<literal>/</literal>, both the directory and its contents are excluded.</para>
|
||||
|
||||
<para><varname>ExcludeFilesTarget=</varname> is like <varname>ExcludeFiles=</varname> except that
|
||||
instead of excluding the path on the host from being copied into the partition, we exclude any files
|
||||
instead of excluding the path on the host from being copied into the partition, it exclude any files
|
||||
and directories from being copied into the given path in the partition.</para>
|
||||
|
||||
<para>When
|
||||
|
@ -897,6 +895,59 @@
|
|||
|
||||
<xi:include href="version-info.xml" xpointer="v257"/></listitem>
|
||||
</varlistentry>
|
||||
|
||||
<varlistentry>
|
||||
<term><varname>SupplementFor=</varname></term>
|
||||
|
||||
<listitem><para>Takes a partition definition name, such as <literal>10-esp</literal>. If specified,
|
||||
<command>systemd-repart</command> will avoid creating this partition and instead prefer to partially
|
||||
merge the two definitions. However, depending on the existing layout of partitions on disk,
|
||||
<command>systemd-repart</command> may be forced to fall back onto un-merging the definitions and
|
||||
using them as originally written, potentially creating this partition. Specifically,
|
||||
<command>systemd-repart</command> will fall back if this partition is found to already exist on disk,
|
||||
or if the target partition already exists on disk but is too small, or if it cannot allocate space
|
||||
for the merged partition for some other reason.</para>
|
||||
|
||||
<para>The following fields are merged into the target definition in the specified ways:
|
||||
<varname>Weight=</varname> and <varname>PaddingWeight=</varname> are simply overwritten;
|
||||
<varname>SizeMinBytes=</varname> and <varname>PaddingMinBytes=</varname> use the larger of the two
|
||||
values; <varname>SizeMaxBytes=</varname> and <varname>PaddingMaxBytes=</varname> use the smaller
|
||||
value; and <varname>CopyFiles=</varname>, <varname>ExcludeFiles=</varname>,
|
||||
<varname>ExcludeFilesTarget=</varname>, <varname>MakeDirectories=</varname>, and
|
||||
<varname>Subvolumes=</varname> are concatenated.</para>
|
||||
|
||||
<para>Usage of this option in combination with <varname>CopyBlocks=</varname>,
|
||||
<varname>Encrypt=</varname>, or <varname>Verity=</varname> is not supported. The target definition
|
||||
cannot set these settings either. A definition cannot simultaneously be a supplement and act as a
|
||||
target for some other supplement definition. A target cannot have more than one supplement partition
|
||||
associated with it.</para>
|
||||
|
||||
<para>For example, distributions can use this to implement <varname>$BOOT</varname> as defined in
|
||||
the <ulink url="https://uapi-group.org/specifications/specs/boot_loader_specification/">Boot Loader
|
||||
Specification</ulink>. Distributions may prefer to use the ESP as <varname>$BOOT</varname> whenever
|
||||
possible, but to adhere to the spec XBOOTLDR must sometimes be used instead. So, they should create
|
||||
two definitions: the first defining an ESP big enough to hold just the bootloader, and a second for
|
||||
the XBOOTLDR that's sufficiently large to hold kernels and configured as a supplement for the ESP.
|
||||
Whenever possible, <command>systemd-repart</command> will try to merge the two definitions to create
|
||||
one large ESP, but if that's not allowable due to the existing conditions on disk a small ESP and a
|
||||
large XBOOTLDR will be created instead.</para>
|
||||
|
||||
<para>As another example, distributions can also use this to seamlessly share a single
|
||||
<filename>/home</filename> partition in a multi-boot scenario, while preferring to keep
|
||||
<filename>/home</filename> on the root partition by default. Having a <filename>/home</filename>
|
||||
partition separated from the root partition entails some extra complexity: someone has to decide how
|
||||
to split the space between the two partitions. On the other hand, it allows a user to share their
|
||||
home area between multiple installed OSs (i.e. via <citerefentry><refentrytitle>systemd-homed.service
|
||||
</refentrytitle><manvolnum>8</manvolnum></citerefentry>). Distributions should create two definitions:
|
||||
the first for a root partition that takes up some relatively small percentage of the disk, and the
|
||||
second as a supplement for the first to create a <filename>/home</filename> partition that takes up
|
||||
all the remaining free space. On first boot, if <command>systemd-repart</command> finds an existing
|
||||
<filename>/home</filename> partition on disk, it'll un-merge the definitions and create just a small
|
||||
root partition. Otherwise, the definitions will be merged and a single large root partition will be
|
||||
created.</para>
|
||||
|
||||
<xi:include href="version-info.xml" xpointer="v257"/></listitem>
|
||||
</varlistentry>
|
||||
</variablelist>
|
||||
</refsect1>
|
||||
|
||||
|
|
|
@ -29,7 +29,7 @@
|
|||
<refsect1>
|
||||
<title>Description</title>
|
||||
|
||||
<para><command>systemd-nsresourced</command> is a system service that permits transient delegation of a a
|
||||
<para><command>systemd-nsresourced</command> is a system service that permits transient delegation of a
|
||||
UID/GID range to a user namespace (see <citerefentry
|
||||
project='man-pages'><refentrytitle>user_namespaces</refentrytitle><manvolnum>7</manvolnum></citerefentry>)
|
||||
allocated by a client, via a Varlink IPC API.</para>
|
||||
|
|
|
@ -115,7 +115,7 @@
|
|||
result can be pre-calculated without too much effort. The <literal>.pcrsig</literal> section is not
|
||||
included in this PCR measurement, since it is supposed to contain signatures for the output of the
|
||||
measurement operation, and thus cannot also be input to it. If an UKI contains multiple profiles, only
|
||||
the PE sections of the selected profile (and those of the base profile, except if overriden) are
|
||||
the PE sections of the selected profile (and those of the base profile, except if overridden) are
|
||||
measured.</para>
|
||||
|
||||
<para>If non-zero, the selected numeric profile is measured into PCR 12.</para>
|
||||
|
|
|
@ -152,10 +152,11 @@
|
|||
<varlistentry>
|
||||
<term><option>--purge</option></term>
|
||||
|
||||
<listitem><para>If this option is passed, all files and directories marked for
|
||||
<emphasis>creation</emphasis> by the <filename>tmpfiles.d/</filename> files specified on the command
|
||||
line will be <emphasis>deleted</emphasis>. Specifically, this acts on all files and directories
|
||||
marked with <varname>f</varname>, <varname>F</varname>, <varname>d</varname>, <varname>D</varname>,
|
||||
<listitem><para>If this option is passed, all files and directories declared for
|
||||
<emphasis>creation</emphasis> and marked with the <literal>$</literal> character by the
|
||||
<filename>tmpfiles.d/</filename> files specified on the command line will be
|
||||
<emphasis>deleted</emphasis>. Specifically, this acts on all files and directories marked with
|
||||
<varname>f</varname>, <varname>F</varname>, <varname>d</varname>, <varname>D</varname>,
|
||||
<varname>v</varname>, <varname>q</varname>, <varname>Q</varname>, <varname>p</varname>,
|
||||
<varname>L</varname>, <varname>c</varname>, <varname>b</varname>, <varname>C</varname>,
|
||||
<varname>w</varname>, <varname>e</varname>. If this switch is used at least one
|
||||
|
|
|
@ -3001,7 +3001,12 @@ SystemCallErrorNumber=EPERM</programlisting>
|
|||
|
||||
<para><option>tty</option> connects standard output to a tty (as configured via <varname>TTYPath=</varname>,
|
||||
see below). If the TTY is used for output only, the executed process will not become the controlling process of
|
||||
the terminal, and will not fail or wait for other processes to release the terminal.</para>
|
||||
the terminal, and will not fail or wait for other processes to release the terminal. Note: if a unit
|
||||
tries to print multiple lines to a TTY during bootup or shutdown, then there's a chance that those
|
||||
lines will be broken up by status messages. <function>SetShowStatus()</function> can be used to
|
||||
prevent this problem. See
|
||||
<citerefentry project="man-pages"><refentrytitle>org.freedesktop.systemd1</refentrytitle><manvolnum>5</manvolnum></citerefentry>
|
||||
for details.</para>
|
||||
|
||||
<para><option>journal</option> connects standard output with the journal, which is accessible via
|
||||
<citerefentry><refentrytitle>journalctl</refentrytitle><manvolnum>1</manvolnum></citerefentry>. Note
|
||||
|
|
|
@ -568,7 +568,11 @@
|
|||
<listitem><para>Enables display of status messages on the
|
||||
console, as controlled via
|
||||
<varname>systemd.show_status=1</varname> on the kernel command
|
||||
line.</para></listitem>
|
||||
line.</para>
|
||||
<para>You may want to use <function>SetShowStatus()</function> instead of
|
||||
<constant>SIGRTMIN+20</constant> in order to prevent race conditions. See
|
||||
<citerefentry project="man-pages"><refentrytitle>org.freedesktop.systemd1</refentrytitle><manvolnum>5</manvolnum></citerefentry>.
|
||||
</para></listitem>
|
||||
</varlistentry>
|
||||
|
||||
<varlistentry>
|
||||
|
@ -579,7 +583,11 @@
|
|||
controlled via
|
||||
<varname>systemd.show_status=0</varname>
|
||||
on the kernel command
|
||||
line.</para></listitem>
|
||||
line.</para>
|
||||
<para>You may want to use <function>SetShowStatus()</function> instead of
|
||||
<constant>SIGRTMIN+21</constant> in order to prevent race conditions. See
|
||||
<citerefentry project="man-pages"><refentrytitle>org.freedesktop.systemd1</refentrytitle><manvolnum>5</manvolnum></citerefentry>.
|
||||
</para></listitem>
|
||||
</varlistentry>
|
||||
|
||||
<varlistentry>
|
||||
|
|
|
@ -539,6 +539,10 @@ w- /proc/sys/vm/swappiness - - - - 10</programlisting></para>
|
|||
service, the line is silently skipped. If <literal>^</literal> and <literal>~</literal> are combined
|
||||
Base64 decoding is applied to the credential contents.</para>
|
||||
|
||||
<para>If the dollar sign (<literal>$</literal>) is used, the file becomes subject to removal when
|
||||
<command>systemd-tmpfiles</command> is invoked with the <option>--purge</option> switch. Lines without
|
||||
this character are unaffected by that switch.</para>
|
||||
|
||||
<para>Note that for all line types that result in creation of any kind of file node
|
||||
(i.e. <varname>f</varname>,
|
||||
<varname>d</varname>/<varname>D</varname>/<varname>v</varname>/<varname>q</varname>/<varname>Q</varname>,
|
||||
|
|
|
@ -141,6 +141,12 @@
|
|||
For example, e"string\n" is parsed as 7 characters: 6 lowercase letters and a newline.
|
||||
This can be useful for writing special characters when a kernel driver requires them.</para>
|
||||
|
||||
<para>The string can be prefixed with a lowercase i (i"string") to mark that the string or pattern
|
||||
will match case-insensitively. For example, i"foo" will match
|
||||
<literal>foo</literal>, <literal>FOO</literal>, <literal>FoO</literal> and so on. The prefix can be
|
||||
used only for match (<literal>==</literal>) or unmatch (<literal>!=</literal>) rules, e.g.
|
||||
<varname>ATTR{foo}==i"abcd"</varname>.</para>
|
||||
|
||||
<para>Please note that <constant>NUL</constant> is not allowed in either string variant.</para>
|
||||
</refsect2>
|
||||
|
||||
|
|
|
@ -2084,6 +2084,7 @@ libsystemd_includes = [basic_includes, include_directories(
|
|||
'src/libsystemd/sd-json',
|
||||
'src/libsystemd/sd-netlink',
|
||||
'src/libsystemd/sd-network',
|
||||
'src/libsystemd/sd-path',
|
||||
'src/libsystemd/sd-resolve',
|
||||
'src/libsystemd/sd-varlink')]
|
||||
|
||||
|
|
|
@ -3,18 +3,11 @@
|
|||
set -e
|
||||
set -o nounset
|
||||
|
||||
if [[ "$DISTRIBUTION" =~ ubuntu|debian ]]; then
|
||||
SUDO_GROUP=sudo
|
||||
else
|
||||
SUDO_GROUP=wheel
|
||||
fi
|
||||
|
||||
useradd \
|
||||
--uid 4711 \
|
||||
--user-group \
|
||||
--create-home \
|
||||
--password "$(openssl passwd -1 testuser)" \
|
||||
--groups "$SUDO_GROUP",systemd-journal \
|
||||
--shell /bin/bash \
|
||||
testuser
|
||||
|
||||
|
|
|
@ -11,6 +11,7 @@
|
|||
#include "bus-map-properties.h"
|
||||
#include "bus-unit-util.h"
|
||||
#include "bus-util.h"
|
||||
#include "conf-files.h"
|
||||
#include "copy.h"
|
||||
#include "env-util.h"
|
||||
#include "fd-util.h"
|
||||
|
|
|
@ -221,6 +221,12 @@ const char* const systemd_features =
|
|||
" -BPF_FRAMEWORK"
|
||||
#endif
|
||||
|
||||
#if HAVE_VMLINUX_H
|
||||
" +BTF"
|
||||
#else
|
||||
" -BTF"
|
||||
#endif
|
||||
|
||||
#if HAVE_XKBCOMMON
|
||||
" +XKBCOMMON"
|
||||
#else
|
||||
|
@ -247,7 +253,7 @@ const char* const systemd_features =
|
|||
|
||||
;
|
||||
|
||||
static char *systemd_features_with_color(void) {
|
||||
static char* systemd_features_with_color(void) {
|
||||
const char *p = systemd_features;
|
||||
_cleanup_free_ char *ret = NULL;
|
||||
int r;
|
||||
|
|
|
@ -469,3 +469,30 @@ int conf_file_read(
|
|||
|
||||
return r;
|
||||
}
|
||||
|
||||
int find_portable_profile(const char *name, const char *unit, char **ret_path) {
|
||||
const char *dot;
|
||||
|
||||
assert(name);
|
||||
assert(ret_path);
|
||||
|
||||
assert_se(dot = strrchr(unit, '.'));
|
||||
|
||||
NULSTR_FOREACH(p, PORTABLE_PROFILE_DIRS) {
|
||||
_cleanup_free_ char *joined = NULL;
|
||||
|
||||
joined = strjoin(p, "/", name, "/", dot + 1, ".conf");
|
||||
if (!joined)
|
||||
return -ENOMEM;
|
||||
|
||||
if (laccess(joined, F_OK) >= 0) {
|
||||
*ret_path = TAKE_PTR(joined);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (errno != ENOENT)
|
||||
return -errno;
|
||||
}
|
||||
|
||||
return -ENOENT;
|
||||
}
|
||||
|
|
|
@ -47,3 +47,9 @@ int conf_file_read(
|
|||
void *userdata,
|
||||
bool ignore_enoent,
|
||||
bool *invalid_config);
|
||||
|
||||
#define NETWORK_DIRS ((const char* const*) CONF_PATHS_STRV("systemd/network"))
|
||||
#define NETWORK_DIRS_NULSTR CONF_PATHS_NULSTR("systemd/network")
|
||||
|
||||
#define PORTABLE_PROFILE_DIRS CONF_PATHS_NULSTR("systemd/portable/profile")
|
||||
int find_portable_profile(const char *name, const char *unit, char **ret_path);
|
||||
|
|
|
@ -145,8 +145,10 @@ int efi_get_variable(
|
|||
int efi_get_variable_string(const char *variable, char **ret) {
|
||||
_cleanup_free_ void *s = NULL;
|
||||
size_t ss = 0;
|
||||
int r;
|
||||
char *x;
|
||||
int r;
|
||||
|
||||
assert(variable);
|
||||
|
||||
r = efi_get_variable(variable, NULL, &s, &ss);
|
||||
if (r < 0)
|
||||
|
@ -156,10 +158,27 @@ int efi_get_variable_string(const char *variable, char **ret) {
|
|||
if (!x)
|
||||
return -ENOMEM;
|
||||
|
||||
*ret = x;
|
||||
if (ret)
|
||||
*ret = x;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int efi_get_variable_path(const char *variable, char **ret) {
|
||||
int r;
|
||||
|
||||
assert(variable);
|
||||
|
||||
r = efi_get_variable_string(variable, ret);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
if (ret)
|
||||
efi_tilt_backslashes(*ret);
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
static int efi_verify_variable(const char *variable, uint32_t attr, const void *value, size_t size) {
|
||||
_cleanup_free_ void *buf = NULL;
|
||||
size_t n;
|
||||
|
|
|
@ -11,6 +11,7 @@
|
|||
#include "sd-id128.h"
|
||||
|
||||
#include "efivars-fundamental.h"
|
||||
#include "string-util.h"
|
||||
#include "time-util.h"
|
||||
|
||||
#define EFI_VENDOR_LOADER SD_ID128_MAKE(4a,67,b0,82,0a,4c,41,cf,b6,c7,44,0b,29,bb,8c,4f)
|
||||
|
@ -47,6 +48,7 @@
|
|||
|
||||
int efi_get_variable(const char *variable, uint32_t *attribute, void **ret_value, size_t *ret_size);
|
||||
int efi_get_variable_string(const char *variable, char **ret);
|
||||
int efi_get_variable_path(const char *variable, char **ret);
|
||||
int efi_set_variable(const char *variable, const void *value, size_t size);
|
||||
int efi_set_variable_string(const char *variable, const char *p);
|
||||
|
||||
|
@ -68,6 +70,10 @@ static inline int efi_get_variable_string(const char *variable, char **ret) {
|
|||
return -EOPNOTSUPP;
|
||||
}
|
||||
|
||||
static inline int efi_get_variable_path(const char *variable, char **ret) {
|
||||
return -EOPNOTSUPP;
|
||||
}
|
||||
|
||||
static inline int efi_set_variable(const char *variable, const void *value, size_t size) {
|
||||
return -EOPNOTSUPP;
|
||||
}
|
||||
|
@ -100,3 +106,7 @@ static inline int systemd_efi_options_efivarfs_if_newer(char **line) {
|
|||
return -ENODATA;
|
||||
}
|
||||
#endif
|
||||
|
||||
static inline char *efi_tilt_backslashes(char *s) {
|
||||
return string_replace_char(s, '\\', '/');
|
||||
}
|
||||
|
|
|
@ -300,9 +300,10 @@ int log_emergency_level(void);
|
|||
#define log_dump(level, buffer) \
|
||||
log_dump_internal(level, 0, PROJECT_FILE, __LINE__, __func__, buffer)
|
||||
|
||||
#define log_oom() log_oom_internal(LOG_ERR, PROJECT_FILE, __LINE__, __func__)
|
||||
#define log_oom_debug() log_oom_internal(LOG_DEBUG, PROJECT_FILE, __LINE__, __func__)
|
||||
#define log_oom_warning() log_oom_internal(LOG_WARNING, PROJECT_FILE, __LINE__, __func__)
|
||||
#define log_oom_full(level) log_oom_internal(level, PROJECT_FILE, __LINE__, __func__)
|
||||
#define log_oom() log_oom_full(LOG_ERR)
|
||||
#define log_oom_debug() log_oom_full(LOG_DEBUG)
|
||||
#define log_oom_warning() log_oom_full(LOG_WARNING)
|
||||
|
||||
bool log_on_console(void) _pure_;
|
||||
|
||||
|
|
|
@ -70,7 +70,6 @@ basic_sources = files(
|
|||
'ordered-set.c',
|
||||
'os-util.c',
|
||||
'parse-util.c',
|
||||
'path-lookup.c',
|
||||
'path-util.c',
|
||||
'percent-util.c',
|
||||
'pidref.c',
|
||||
|
@ -106,7 +105,6 @@ basic_sources = files(
|
|||
'uid-classification.c',
|
||||
'uid-range.c',
|
||||
'unit-def.c',
|
||||
'unit-file.c',
|
||||
'unit-name.c',
|
||||
'user-util.c',
|
||||
'utf8.c',
|
||||
|
|
|
@ -1,930 +0,0 @@
|
|||
/* SPDX-License-Identifier: LGPL-2.1-or-later */
|
||||
|
||||
#include <errno.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include "alloc-util.h"
|
||||
#include "fs-util.h"
|
||||
#include "log.h"
|
||||
#include "macro.h"
|
||||
#include "nulstr-util.h"
|
||||
#include "path-lookup.h"
|
||||
#include "path-util.h"
|
||||
#include "stat-util.h"
|
||||
#include "string-util.h"
|
||||
#include "strv.h"
|
||||
#include "tmpfile-util.h"
|
||||
#include "user-util.h"
|
||||
|
||||
int xdg_user_runtime_dir(char **ret, const char *suffix) {
|
||||
const char *e;
|
||||
char *j;
|
||||
|
||||
assert(ret);
|
||||
assert(suffix);
|
||||
|
||||
e = getenv("XDG_RUNTIME_DIR");
|
||||
if (!e)
|
||||
return -ENXIO;
|
||||
|
||||
j = path_join(e, suffix);
|
||||
if (!j)
|
||||
return -ENOMEM;
|
||||
|
||||
*ret = j;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int xdg_user_config_dir(char **ret, const char *suffix) {
|
||||
_cleanup_free_ char *j = NULL;
|
||||
const char *e;
|
||||
int r;
|
||||
|
||||
assert(ret);
|
||||
|
||||
e = getenv("XDG_CONFIG_HOME");
|
||||
if (e) {
|
||||
j = path_join(e, suffix);
|
||||
if (!j)
|
||||
return -ENOMEM;
|
||||
} else {
|
||||
r = get_home_dir(&j);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
if (!path_extend(&j, "/.config", suffix))
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
*ret = TAKE_PTR(j);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int xdg_user_data_dir(char **ret, const char *suffix) {
|
||||
_cleanup_free_ char *j = NULL;
|
||||
const char *e;
|
||||
int r;
|
||||
|
||||
assert(ret);
|
||||
assert(suffix);
|
||||
|
||||
/* We don't treat /etc/xdg/systemd here as the spec
|
||||
* suggests because we assume that is a link to
|
||||
* /etc/systemd/ anyway. */
|
||||
|
||||
e = getenv("XDG_DATA_HOME");
|
||||
if (e) {
|
||||
j = path_join(e, suffix);
|
||||
if (!j)
|
||||
return -ENOMEM;
|
||||
} else {
|
||||
r = get_home_dir(&j);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
if (!path_extend(&j, "/.local/share", suffix))
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
*ret = TAKE_PTR(j);
|
||||
return 1;
|
||||
}
|
||||
|
||||
int runtime_directory(char **ret, RuntimeScope scope, const char *suffix) {
|
||||
int r;
|
||||
|
||||
assert(ret);
|
||||
assert(suffix);
|
||||
assert(IN_SET(scope, RUNTIME_SCOPE_SYSTEM, RUNTIME_SCOPE_USER, RUNTIME_SCOPE_GLOBAL));
|
||||
|
||||
/* Accept $RUNTIME_DIRECTORY as authoritative
|
||||
* If its missing apply the suffix to /run or $XDG_RUNTIME_DIR
|
||||
* if we are in a user runtime scope.
|
||||
*
|
||||
* Return value indicates whether the suffix was applied or not */
|
||||
|
||||
const char *e = secure_getenv("RUNTIME_DIRECTORY");
|
||||
if (e)
|
||||
return strdup_to(ret, e);
|
||||
|
||||
if (scope == RUNTIME_SCOPE_USER) {
|
||||
r = xdg_user_runtime_dir(ret, suffix);
|
||||
if (r < 0)
|
||||
return r;
|
||||
} else {
|
||||
char *d = path_join("/run", suffix);
|
||||
if (!d)
|
||||
return -ENOMEM;
|
||||
*ret = d;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static const char* const user_data_unit_paths[] = {
|
||||
"/usr/local/lib/systemd/user",
|
||||
"/usr/local/share/systemd/user",
|
||||
USER_DATA_UNIT_DIR,
|
||||
"/usr/lib/systemd/user",
|
||||
"/usr/share/systemd/user",
|
||||
NULL
|
||||
};
|
||||
|
||||
static const char* const user_config_unit_paths[] = {
|
||||
USER_CONFIG_UNIT_DIR,
|
||||
"/etc/systemd/user",
|
||||
NULL
|
||||
};
|
||||
|
||||
int xdg_user_dirs(char ***ret_config_dirs, char ***ret_data_dirs) {
|
||||
/* Implement the mechanisms defined in
|
||||
*
|
||||
* https://standards.freedesktop.org/basedir-spec/basedir-spec-0.6.html
|
||||
*
|
||||
* We look in both the config and the data dirs because we
|
||||
* want to encourage that distributors ship their unit files
|
||||
* as data, and allow overriding as configuration.
|
||||
*/
|
||||
const char *e;
|
||||
_cleanup_strv_free_ char **config_dirs = NULL, **data_dirs = NULL;
|
||||
|
||||
e = getenv("XDG_CONFIG_DIRS");
|
||||
if (e)
|
||||
config_dirs = strv_split(e, ":");
|
||||
else
|
||||
config_dirs = strv_new("/etc/xdg");
|
||||
if (!config_dirs)
|
||||
return -ENOMEM;
|
||||
|
||||
e = getenv("XDG_DATA_DIRS");
|
||||
if (e)
|
||||
data_dirs = strv_split(e, ":");
|
||||
else
|
||||
data_dirs = strv_new("/usr/local/share",
|
||||
"/usr/share");
|
||||
if (!data_dirs)
|
||||
return -ENOMEM;
|
||||
|
||||
*ret_config_dirs = TAKE_PTR(config_dirs);
|
||||
*ret_data_dirs = TAKE_PTR(data_dirs);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static char** user_dirs(
|
||||
const char *persistent_config,
|
||||
const char *runtime_config,
|
||||
const char *global_persistent_config,
|
||||
const char *global_runtime_config,
|
||||
const char *generator,
|
||||
const char *generator_early,
|
||||
const char *generator_late,
|
||||
const char *transient,
|
||||
const char *persistent_control,
|
||||
const char *runtime_control) {
|
||||
|
||||
_cleanup_strv_free_ char **config_dirs = NULL, **data_dirs = NULL;
|
||||
_cleanup_free_ char *data_home = NULL;
|
||||
_cleanup_strv_free_ char **res = NULL;
|
||||
int r;
|
||||
|
||||
r = xdg_user_dirs(&config_dirs, &data_dirs);
|
||||
if (r < 0)
|
||||
return NULL;
|
||||
|
||||
r = xdg_user_data_dir(&data_home, "/systemd/user");
|
||||
if (r < 0 && r != -ENXIO)
|
||||
return NULL;
|
||||
|
||||
/* Now merge everything we found. */
|
||||
if (strv_extend_many(
|
||||
&res,
|
||||
persistent_control,
|
||||
runtime_control,
|
||||
transient,
|
||||
generator_early,
|
||||
persistent_config) < 0)
|
||||
return NULL;
|
||||
|
||||
if (strv_extend_strv_concat(&res, (const char* const*) config_dirs, "/systemd/user") < 0)
|
||||
return NULL;
|
||||
|
||||
/* global config has lower priority than the user config of the same type */
|
||||
if (strv_extend(&res, global_persistent_config) < 0)
|
||||
return NULL;
|
||||
|
||||
if (strv_extend_strv(&res, (char**) user_config_unit_paths, false) < 0)
|
||||
return NULL;
|
||||
|
||||
if (strv_extend_many(
|
||||
&res,
|
||||
runtime_config,
|
||||
global_runtime_config,
|
||||
generator,
|
||||
data_home) < 0)
|
||||
return NULL;
|
||||
|
||||
if (strv_extend_strv_concat(&res, (const char* const*) data_dirs, "/systemd/user") < 0)
|
||||
return NULL;
|
||||
|
||||
if (strv_extend_strv(&res, (char**) user_data_unit_paths, false) < 0)
|
||||
return NULL;
|
||||
|
||||
if (strv_extend(&res, generator_late) < 0)
|
||||
return NULL;
|
||||
|
||||
if (path_strv_make_absolute_cwd(res) < 0)
|
||||
return NULL;
|
||||
|
||||
return TAKE_PTR(res);
|
||||
}
|
||||
|
||||
bool path_is_user_data_dir(const char *path) {
|
||||
assert(path);
|
||||
|
||||
return strv_contains((char**) user_data_unit_paths, path);
|
||||
}
|
||||
|
||||
bool path_is_user_config_dir(const char *path) {
|
||||
assert(path);
|
||||
|
||||
return strv_contains((char**) user_config_unit_paths, path);
|
||||
}
|
||||
|
||||
static int acquire_generator_dirs(
|
||||
RuntimeScope scope,
|
||||
const char *tempdir,
|
||||
char **generator,
|
||||
char **generator_early,
|
||||
char **generator_late) {
|
||||
|
||||
_cleanup_free_ char *x = NULL, *y = NULL, *z = NULL, *p = NULL;
|
||||
const char *prefix;
|
||||
|
||||
assert(generator);
|
||||
assert(generator_early);
|
||||
assert(generator_late);
|
||||
assert(IN_SET(scope, RUNTIME_SCOPE_SYSTEM, RUNTIME_SCOPE_USER, RUNTIME_SCOPE_GLOBAL));
|
||||
|
||||
if (scope == RUNTIME_SCOPE_GLOBAL)
|
||||
return -EOPNOTSUPP;
|
||||
|
||||
if (tempdir)
|
||||
prefix = tempdir;
|
||||
else if (scope == RUNTIME_SCOPE_SYSTEM)
|
||||
prefix = "/run/systemd";
|
||||
else {
|
||||
/* RUNTIME_SCOPE_USER */
|
||||
const char *e;
|
||||
|
||||
e = getenv("XDG_RUNTIME_DIR");
|
||||
if (!e)
|
||||
return -ENXIO;
|
||||
|
||||
p = path_join(e, "/systemd");
|
||||
if (!p)
|
||||
return -ENOMEM;
|
||||
|
||||
prefix = p;
|
||||
}
|
||||
|
||||
x = path_join(prefix, "generator");
|
||||
if (!x)
|
||||
return -ENOMEM;
|
||||
|
||||
y = path_join(prefix, "generator.early");
|
||||
if (!y)
|
||||
return -ENOMEM;
|
||||
|
||||
z = path_join(prefix, "generator.late");
|
||||
if (!z)
|
||||
return -ENOMEM;
|
||||
|
||||
*generator = TAKE_PTR(x);
|
||||
*generator_early = TAKE_PTR(y);
|
||||
*generator_late = TAKE_PTR(z);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int acquire_transient_dir(
|
||||
RuntimeScope scope,
|
||||
const char *tempdir,
|
||||
char **ret) {
|
||||
|
||||
char *transient;
|
||||
|
||||
assert(ret);
|
||||
assert(IN_SET(scope, RUNTIME_SCOPE_SYSTEM, RUNTIME_SCOPE_USER, RUNTIME_SCOPE_GLOBAL));
|
||||
|
||||
if (scope == RUNTIME_SCOPE_GLOBAL)
|
||||
return -EOPNOTSUPP;
|
||||
|
||||
if (tempdir)
|
||||
transient = path_join(tempdir, "transient");
|
||||
else if (scope == RUNTIME_SCOPE_SYSTEM)
|
||||
transient = strdup("/run/systemd/transient");
|
||||
else
|
||||
return xdg_user_runtime_dir(ret, "/systemd/transient");
|
||||
|
||||
if (!transient)
|
||||
return -ENOMEM;
|
||||
*ret = transient;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int acquire_config_dirs(RuntimeScope scope, char **persistent, char **runtime) {
|
||||
_cleanup_free_ char *a = NULL, *b = NULL;
|
||||
int r;
|
||||
|
||||
assert(persistent);
|
||||
assert(runtime);
|
||||
|
||||
switch (scope) {
|
||||
|
||||
case RUNTIME_SCOPE_SYSTEM:
|
||||
a = strdup(SYSTEM_CONFIG_UNIT_DIR);
|
||||
b = strdup("/run/systemd/system");
|
||||
break;
|
||||
|
||||
case RUNTIME_SCOPE_GLOBAL:
|
||||
a = strdup(USER_CONFIG_UNIT_DIR);
|
||||
b = strdup("/run/systemd/user");
|
||||
break;
|
||||
|
||||
case RUNTIME_SCOPE_USER:
|
||||
r = xdg_user_config_dir(&a, "/systemd/user");
|
||||
if (r < 0 && r != -ENXIO)
|
||||
return r;
|
||||
|
||||
r = xdg_user_runtime_dir(runtime, "/systemd/user");
|
||||
if (r < 0) {
|
||||
if (r != -ENXIO)
|
||||
return r;
|
||||
|
||||
/* If XDG_RUNTIME_DIR is not set, don't consider that fatal, simply initialize the runtime
|
||||
* directory to NULL */
|
||||
*runtime = NULL;
|
||||
}
|
||||
|
||||
*persistent = TAKE_PTR(a);
|
||||
|
||||
return 0;
|
||||
|
||||
default:
|
||||
assert_not_reached();
|
||||
}
|
||||
|
||||
if (!a || !b)
|
||||
return -ENOMEM;
|
||||
|
||||
*persistent = TAKE_PTR(a);
|
||||
*runtime = TAKE_PTR(b);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int acquire_control_dirs(RuntimeScope scope, char **persistent, char **runtime) {
|
||||
_cleanup_free_ char *a = NULL;
|
||||
int r;
|
||||
|
||||
assert(persistent);
|
||||
assert(runtime);
|
||||
|
||||
switch (scope) {
|
||||
|
||||
case RUNTIME_SCOPE_SYSTEM: {
|
||||
_cleanup_free_ char *b = NULL;
|
||||
|
||||
a = strdup("/etc/systemd/system.control");
|
||||
if (!a)
|
||||
return -ENOMEM;
|
||||
|
||||
b = strdup("/run/systemd/system.control");
|
||||
if (!b)
|
||||
return -ENOMEM;
|
||||
|
||||
*runtime = TAKE_PTR(b);
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
case RUNTIME_SCOPE_USER:
|
||||
r = xdg_user_config_dir(&a, "/systemd/user.control");
|
||||
if (r < 0 && r != -ENXIO)
|
||||
return r;
|
||||
|
||||
r = xdg_user_runtime_dir(runtime, "/systemd/user.control");
|
||||
if (r < 0) {
|
||||
if (r != -ENXIO)
|
||||
return r;
|
||||
|
||||
/* If XDG_RUNTIME_DIR is not set, don't consider this fatal, simply initialize the directory to
|
||||
* NULL */
|
||||
*runtime = NULL;
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
case RUNTIME_SCOPE_GLOBAL:
|
||||
return -EOPNOTSUPP;
|
||||
|
||||
default:
|
||||
assert_not_reached();
|
||||
}
|
||||
|
||||
*persistent = TAKE_PTR(a);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int acquire_attached_dirs(
|
||||
RuntimeScope scope,
|
||||
char **ret_persistent,
|
||||
char **ret_runtime) {
|
||||
|
||||
_cleanup_free_ char *a = NULL, *b = NULL;
|
||||
|
||||
assert(ret_persistent);
|
||||
assert(ret_runtime);
|
||||
|
||||
/* Portable services are not available to regular users for now. */
|
||||
if (scope != RUNTIME_SCOPE_SYSTEM)
|
||||
return -EOPNOTSUPP;
|
||||
|
||||
a = strdup("/etc/systemd/system.attached");
|
||||
if (!a)
|
||||
return -ENOMEM;
|
||||
|
||||
b = strdup("/run/systemd/system.attached");
|
||||
if (!b)
|
||||
return -ENOMEM;
|
||||
|
||||
*ret_persistent = TAKE_PTR(a);
|
||||
*ret_runtime = TAKE_PTR(b);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int patch_root_prefix(char **p, const char *root_dir) {
|
||||
char *c;
|
||||
|
||||
assert(p);
|
||||
|
||||
if (!*p)
|
||||
return 0;
|
||||
|
||||
c = path_join(root_dir, *p);
|
||||
if (!c)
|
||||
return -ENOMEM;
|
||||
|
||||
free_and_replace(*p, c);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int patch_root_prefix_strv(char **l, const char *root_dir) {
|
||||
int r;
|
||||
|
||||
if (!root_dir)
|
||||
return 0;
|
||||
|
||||
STRV_FOREACH(i, l) {
|
||||
r = patch_root_prefix(i, root_dir);
|
||||
if (r < 0)
|
||||
return r;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int get_paths_from_environ(const char *var, char ***paths, bool *append) {
|
||||
const char *e;
|
||||
int r;
|
||||
|
||||
assert(var);
|
||||
assert(paths);
|
||||
assert(append);
|
||||
|
||||
*append = false;
|
||||
|
||||
e = getenv(var);
|
||||
if (e) {
|
||||
const char *k;
|
||||
|
||||
k = endswith(e, ":");
|
||||
if (k) {
|
||||
e = strndupa_safe(e, k - e);
|
||||
*append = true;
|
||||
}
|
||||
|
||||
/* FIXME: empty components in other places should be rejected. */
|
||||
|
||||
r = path_split_and_make_absolute(e, paths);
|
||||
if (r < 0)
|
||||
return r;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int lookup_paths_init(
|
||||
LookupPaths *lp,
|
||||
RuntimeScope scope,
|
||||
LookupPathsFlags flags,
|
||||
const char *root_dir) {
|
||||
|
||||
_cleanup_(rmdir_and_freep) char *tempdir = NULL;
|
||||
_cleanup_free_ char
|
||||
*root = NULL,
|
||||
*persistent_config = NULL, *runtime_config = NULL,
|
||||
*global_persistent_config = NULL, *global_runtime_config = NULL,
|
||||
*generator = NULL, *generator_early = NULL, *generator_late = NULL,
|
||||
*transient = NULL,
|
||||
*persistent_control = NULL, *runtime_control = NULL,
|
||||
*persistent_attached = NULL, *runtime_attached = NULL;
|
||||
bool append = false; /* Add items from SYSTEMD_UNIT_PATH before normal directories */
|
||||
_cleanup_strv_free_ char **paths = NULL;
|
||||
int r;
|
||||
|
||||
assert(lp);
|
||||
assert(scope >= 0);
|
||||
assert(scope < _RUNTIME_SCOPE_MAX);
|
||||
|
||||
if (!empty_or_root(root_dir)) {
|
||||
if (scope == RUNTIME_SCOPE_USER)
|
||||
return -EINVAL;
|
||||
|
||||
r = is_dir(root_dir, true);
|
||||
if (r < 0)
|
||||
return r;
|
||||
if (r == 0)
|
||||
return -ENOTDIR;
|
||||
|
||||
root = strdup(root_dir);
|
||||
if (!root)
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
if (flags & LOOKUP_PATHS_TEMPORARY_GENERATED) {
|
||||
r = mkdtemp_malloc("/tmp/systemd-temporary-XXXXXX", &tempdir);
|
||||
if (r < 0)
|
||||
return log_debug_errno(r, "Failed to create temporary directory: %m");
|
||||
}
|
||||
|
||||
/* Note: when XDG_RUNTIME_DIR is not set this will not return -ENXIO, but simply set runtime_config to NULL */
|
||||
r = acquire_config_dirs(scope, &persistent_config, &runtime_config);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
if (scope == RUNTIME_SCOPE_USER) {
|
||||
r = acquire_config_dirs(RUNTIME_SCOPE_GLOBAL, &global_persistent_config, &global_runtime_config);
|
||||
if (r < 0)
|
||||
return r;
|
||||
}
|
||||
|
||||
if ((flags & LOOKUP_PATHS_EXCLUDE_GENERATED) == 0) {
|
||||
/* Note: if XDG_RUNTIME_DIR is not set, this will fail completely with ENXIO */
|
||||
r = acquire_generator_dirs(scope, tempdir,
|
||||
&generator, &generator_early, &generator_late);
|
||||
if (r < 0 && !IN_SET(r, -EOPNOTSUPP, -ENXIO))
|
||||
return r;
|
||||
}
|
||||
|
||||
/* Note: if XDG_RUNTIME_DIR is not set, this will fail completely with ENXIO */
|
||||
r = acquire_transient_dir(scope, tempdir, &transient);
|
||||
if (r < 0 && !IN_SET(r, -EOPNOTSUPP, -ENXIO))
|
||||
return r;
|
||||
|
||||
/* Note: when XDG_RUNTIME_DIR is not set this will not return -ENXIO, but simply set runtime_control to NULL */
|
||||
r = acquire_control_dirs(scope, &persistent_control, &runtime_control);
|
||||
if (r < 0 && r != -EOPNOTSUPP)
|
||||
return r;
|
||||
|
||||
r = acquire_attached_dirs(scope, &persistent_attached, &runtime_attached);
|
||||
if (r < 0 && r != -EOPNOTSUPP)
|
||||
return r;
|
||||
|
||||
/* First priority is whatever has been passed to us via env vars */
|
||||
r = get_paths_from_environ("SYSTEMD_UNIT_PATH", &paths, &append);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
if (!paths || append) {
|
||||
/* Let's figure something out. */
|
||||
|
||||
_cleanup_strv_free_ char **add = NULL;
|
||||
|
||||
/* For the user units we include share/ in the search
|
||||
* path in order to comply with the XDG basedir spec.
|
||||
* For the system stuff we avoid such nonsense. OTOH
|
||||
* we include /lib in the search path for the system
|
||||
* stuff but avoid it for user stuff. */
|
||||
|
||||
switch (scope) {
|
||||
|
||||
case RUNTIME_SCOPE_SYSTEM:
|
||||
add = strv_new(
|
||||
/* If you modify this you also want to modify
|
||||
* systemdsystemunitpath= in systemd.pc.in! */
|
||||
STRV_IFNOTNULL(persistent_control),
|
||||
STRV_IFNOTNULL(runtime_control),
|
||||
STRV_IFNOTNULL(transient),
|
||||
STRV_IFNOTNULL(generator_early),
|
||||
persistent_config,
|
||||
SYSTEM_CONFIG_UNIT_DIR,
|
||||
"/etc/systemd/system",
|
||||
STRV_IFNOTNULL(persistent_attached),
|
||||
runtime_config,
|
||||
"/run/systemd/system",
|
||||
STRV_IFNOTNULL(runtime_attached),
|
||||
STRV_IFNOTNULL(generator),
|
||||
"/usr/local/lib/systemd/system",
|
||||
SYSTEM_DATA_UNIT_DIR,
|
||||
"/usr/lib/systemd/system",
|
||||
/* To be used ONLY for images which might be legacy split-usr */
|
||||
STRV_IFNOTNULL(flags & LOOKUP_PATHS_SPLIT_USR ? "/lib/systemd/system" : NULL),
|
||||
STRV_IFNOTNULL(generator_late));
|
||||
break;
|
||||
|
||||
case RUNTIME_SCOPE_GLOBAL:
|
||||
add = strv_new(
|
||||
/* If you modify this you also want to modify
|
||||
* systemduserunitpath= in systemd.pc.in, and
|
||||
* the arrays in user_dirs() above! */
|
||||
STRV_IFNOTNULL(persistent_control),
|
||||
STRV_IFNOTNULL(runtime_control),
|
||||
STRV_IFNOTNULL(transient),
|
||||
STRV_IFNOTNULL(generator_early),
|
||||
persistent_config,
|
||||
USER_CONFIG_UNIT_DIR,
|
||||
"/etc/systemd/user",
|
||||
runtime_config,
|
||||
"/run/systemd/user",
|
||||
STRV_IFNOTNULL(generator),
|
||||
"/usr/local/share/systemd/user",
|
||||
"/usr/share/systemd/user",
|
||||
"/usr/local/lib/systemd/user",
|
||||
USER_DATA_UNIT_DIR,
|
||||
"/usr/lib/systemd/user",
|
||||
STRV_IFNOTNULL(generator_late));
|
||||
break;
|
||||
|
||||
case RUNTIME_SCOPE_USER:
|
||||
add = user_dirs(persistent_config, runtime_config,
|
||||
global_persistent_config, global_runtime_config,
|
||||
generator, generator_early, generator_late,
|
||||
transient,
|
||||
persistent_control, runtime_control);
|
||||
break;
|
||||
|
||||
default:
|
||||
assert_not_reached();
|
||||
}
|
||||
|
||||
if (!add)
|
||||
return -ENOMEM;
|
||||
|
||||
if (paths) {
|
||||
r = strv_extend_strv(&paths, add, true);
|
||||
if (r < 0)
|
||||
return r;
|
||||
} else
|
||||
/* Small optimization: if paths is NULL (and it usually is), we can simply assign 'add' to it,
|
||||
* and don't have to copy anything */
|
||||
paths = TAKE_PTR(add);
|
||||
}
|
||||
|
||||
r = patch_root_prefix(&persistent_config, root);
|
||||
if (r < 0)
|
||||
return r;
|
||||
r = patch_root_prefix(&runtime_config, root);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
r = patch_root_prefix(&generator, root);
|
||||
if (r < 0)
|
||||
return r;
|
||||
r = patch_root_prefix(&generator_early, root);
|
||||
if (r < 0)
|
||||
return r;
|
||||
r = patch_root_prefix(&generator_late, root);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
r = patch_root_prefix(&transient, root);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
r = patch_root_prefix(&persistent_control, root);
|
||||
if (r < 0)
|
||||
return r;
|
||||
r = patch_root_prefix(&runtime_control, root);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
r = patch_root_prefix(&persistent_attached, root);
|
||||
if (r < 0)
|
||||
return r;
|
||||
r = patch_root_prefix(&runtime_attached, root);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
r = patch_root_prefix_strv(paths, root);
|
||||
if (r < 0)
|
||||
return -ENOMEM;
|
||||
|
||||
*lp = (LookupPaths) {
|
||||
.search_path = strv_uniq(TAKE_PTR(paths)),
|
||||
|
||||
.persistent_config = TAKE_PTR(persistent_config),
|
||||
.runtime_config = TAKE_PTR(runtime_config),
|
||||
|
||||
.generator = TAKE_PTR(generator),
|
||||
.generator_early = TAKE_PTR(generator_early),
|
||||
.generator_late = TAKE_PTR(generator_late),
|
||||
|
||||
.transient = TAKE_PTR(transient),
|
||||
|
||||
.persistent_control = TAKE_PTR(persistent_control),
|
||||
.runtime_control = TAKE_PTR(runtime_control),
|
||||
|
||||
.persistent_attached = TAKE_PTR(persistent_attached),
|
||||
.runtime_attached = TAKE_PTR(runtime_attached),
|
||||
|
||||
.root_dir = TAKE_PTR(root),
|
||||
.temporary_dir = TAKE_PTR(tempdir),
|
||||
};
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int lookup_paths_init_or_warn(LookupPaths *lp, RuntimeScope scope, LookupPathsFlags flags, const char *root_dir) {
|
||||
int r;
|
||||
|
||||
r = lookup_paths_init(lp, scope, flags, root_dir);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to initialize unit search paths%s%s: %m",
|
||||
isempty(root_dir) ? "" : " for root directory ", strempty(root_dir));
|
||||
return r;
|
||||
}
|
||||
|
||||
void lookup_paths_done(LookupPaths *lp) {
|
||||
assert(lp);
|
||||
|
||||
lp->search_path = strv_free(lp->search_path);
|
||||
|
||||
lp->persistent_config = mfree(lp->persistent_config);
|
||||
lp->runtime_config = mfree(lp->runtime_config);
|
||||
|
||||
lp->persistent_attached = mfree(lp->persistent_attached);
|
||||
lp->runtime_attached = mfree(lp->runtime_attached);
|
||||
|
||||
lp->generator = mfree(lp->generator);
|
||||
lp->generator_early = mfree(lp->generator_early);
|
||||
lp->generator_late = mfree(lp->generator_late);
|
||||
|
||||
lp->transient = mfree(lp->transient);
|
||||
|
||||
lp->persistent_control = mfree(lp->persistent_control);
|
||||
lp->runtime_control = mfree(lp->runtime_control);
|
||||
|
||||
lp->root_dir = mfree(lp->root_dir);
|
||||
lp->temporary_dir = mfree(lp->temporary_dir);
|
||||
}
|
||||
|
||||
void lookup_paths_log(LookupPaths *lp) {
|
||||
assert(lp);
|
||||
|
||||
if (strv_isempty(lp->search_path)) {
|
||||
log_debug("Ignoring unit files.");
|
||||
lp->search_path = strv_free(lp->search_path);
|
||||
} else {
|
||||
_cleanup_free_ char *t = NULL;
|
||||
|
||||
t = strv_join(lp->search_path, "\n\t");
|
||||
log_debug("Looking for unit files in (higher priority first):\n\t%s", strna(t));
|
||||
}
|
||||
}
|
||||
|
||||
char **generator_binary_paths(RuntimeScope scope) {
|
||||
bool append = false; /* Add items from SYSTEMD_GENERATOR_PATH before normal directories */
|
||||
_cleanup_strv_free_ char **paths = NULL;
|
||||
int r;
|
||||
|
||||
/* First priority is whatever has been passed to us via env vars */
|
||||
r = get_paths_from_environ("SYSTEMD_GENERATOR_PATH", &paths, &append);
|
||||
if (r < 0)
|
||||
return NULL;
|
||||
|
||||
if (!paths || append) {
|
||||
_cleanup_strv_free_ char **add = NULL;
|
||||
|
||||
switch (scope) {
|
||||
|
||||
case RUNTIME_SCOPE_SYSTEM:
|
||||
add = strv_new("/run/systemd/system-generators",
|
||||
"/etc/systemd/system-generators",
|
||||
"/usr/local/lib/systemd/system-generators",
|
||||
SYSTEM_GENERATOR_DIR);
|
||||
break;
|
||||
|
||||
case RUNTIME_SCOPE_GLOBAL:
|
||||
case RUNTIME_SCOPE_USER:
|
||||
add = strv_new("/run/systemd/user-generators",
|
||||
"/etc/systemd/user-generators",
|
||||
"/usr/local/lib/systemd/user-generators",
|
||||
USER_GENERATOR_DIR);
|
||||
break;
|
||||
|
||||
default:
|
||||
assert_not_reached();
|
||||
}
|
||||
if (!add)
|
||||
return NULL;
|
||||
|
||||
if (paths) {
|
||||
r = strv_extend_strv(&paths, add, true);
|
||||
if (r < 0)
|
||||
return NULL;
|
||||
} else
|
||||
/* Small optimization: if paths is NULL (and it usually is), we can simply assign 'add' to it,
|
||||
* and don't have to copy anything */
|
||||
paths = TAKE_PTR(add);
|
||||
}
|
||||
|
||||
return TAKE_PTR(paths);
|
||||
}
|
||||
|
||||
char **env_generator_binary_paths(RuntimeScope runtime_scope) {
|
||||
_cleanup_strv_free_ char **paths = NULL, **add = NULL;
|
||||
bool append = false; /* Add items from SYSTEMD_ENVIRONMENT_GENERATOR_PATH before normal directories */
|
||||
int r;
|
||||
|
||||
/* First priority is whatever has been passed to us via env vars */
|
||||
r = get_paths_from_environ("SYSTEMD_ENVIRONMENT_GENERATOR_PATH", &paths, &append);
|
||||
if (r < 0)
|
||||
return NULL;
|
||||
|
||||
if (!paths || append) {
|
||||
switch (runtime_scope) {
|
||||
|
||||
case RUNTIME_SCOPE_SYSTEM:
|
||||
add = strv_new("/run/systemd/system-environment-generators",
|
||||
"/etc/systemd/system-environment-generators",
|
||||
"/usr/local/lib/systemd/system-environment-generators",
|
||||
SYSTEM_ENV_GENERATOR_DIR);
|
||||
break;
|
||||
|
||||
case RUNTIME_SCOPE_USER:
|
||||
add = strv_new("/run/systemd/user-environment-generators",
|
||||
"/etc/systemd/user-environment-generators",
|
||||
"/usr/local/lib/systemd/user-environment-generators",
|
||||
USER_ENV_GENERATOR_DIR);
|
||||
break;
|
||||
|
||||
default:
|
||||
assert_not_reached();
|
||||
}
|
||||
if (!add)
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (paths) {
|
||||
r = strv_extend_strv(&paths, add, true);
|
||||
if (r < 0)
|
||||
return NULL;
|
||||
} else
|
||||
/* Small optimization: if paths is NULL (and it usually is), we can simply assign 'add' to it,
|
||||
* and don't have to copy anything */
|
||||
paths = TAKE_PTR(add);
|
||||
|
||||
return TAKE_PTR(paths);
|
||||
}
|
||||
|
||||
int find_portable_profile(const char *name, const char *unit, char **ret_path) {
|
||||
const char *dot;
|
||||
|
||||
assert(name);
|
||||
assert(ret_path);
|
||||
|
||||
assert_se(dot = strrchr(unit, '.'));
|
||||
|
||||
NULSTR_FOREACH(p, PORTABLE_PROFILE_DIRS) {
|
||||
_cleanup_free_ char *joined = NULL;
|
||||
|
||||
joined = strjoin(p, "/", name, "/", dot + 1, ".conf");
|
||||
if (!joined)
|
||||
return -ENOMEM;
|
||||
|
||||
if (laccess(joined, F_OK) >= 0) {
|
||||
*ret_path = TAKE_PTR(joined);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (errno != ENOENT)
|
||||
return -errno;
|
||||
}
|
||||
|
||||
return -ENOENT;
|
||||
}
|
|
@ -153,7 +153,7 @@ bool strv_overlap(char * const *a, char * const *b) _pure_;
|
|||
_STRV_FOREACH_BACKWARDS(s, l, UNIQ_T(h, UNIQ), UNIQ_T(i, UNIQ))
|
||||
|
||||
#define _STRV_FOREACH_PAIR(x, y, l, i) \
|
||||
for (typeof(*l) *x, *y, *i = (l); \
|
||||
for (typeof(*(l)) *x, *y, *i = (l); \
|
||||
i && *(x = i) && *(y = i + 1); \
|
||||
i += 2)
|
||||
|
||||
|
|
|
@ -255,6 +255,25 @@ int ask_string(char **ret, const char *text, ...) {
|
|||
return 0;
|
||||
}
|
||||
|
||||
bool any_key_to_proceed(void) {
|
||||
char key = 0;
|
||||
bool need_nl = true;
|
||||
|
||||
/*
|
||||
* Insert a new line here as well as to when the user inputs, as this is also used during the
|
||||
* boot up sequence when status messages may be interleaved with the current program output.
|
||||
* This ensures that the status messages aren't appended on the same line as this message.
|
||||
*/
|
||||
puts("-- Press any key to proceed --");
|
||||
|
||||
(void) read_one_char(stdin, &key, USEC_INFINITY, &need_nl);
|
||||
|
||||
if (need_nl)
|
||||
putchar('\n');
|
||||
|
||||
return key != 'q';
|
||||
}
|
||||
|
||||
int open_terminal(const char *name, int mode) {
|
||||
_cleanup_close_ int fd = -EBADF;
|
||||
unsigned c = 0;
|
||||
|
|
|
@ -78,6 +78,7 @@ int chvt(int vt);
|
|||
int read_one_char(FILE *f, char *ret, usec_t timeout, bool *need_nl);
|
||||
int ask_char(char *ret, const char *replies, const char *text, ...) _printf_(3, 4);
|
||||
int ask_string(char **ret, const char *text, ...) _printf_(2, 3);
|
||||
bool any_key_to_proceed(void);
|
||||
|
||||
int vt_disallocate(const char *name);
|
||||
|
||||
|
|
|
@ -219,14 +219,12 @@ static int acquire_boot_count_path(
|
|||
uint64_t left, done;
|
||||
int r;
|
||||
|
||||
r = efi_get_variable_string(EFI_LOADER_VARIABLE(LoaderBootCountPath), &path);
|
||||
r = efi_get_variable_path(EFI_LOADER_VARIABLE(LoaderBootCountPath), &path);
|
||||
if (r == -ENOENT)
|
||||
return -EUNATCH; /* in this case, let the caller print a message */
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to read LoaderBootCountPath EFI variable: %m");
|
||||
|
||||
efi_tilt_backslashes(path);
|
||||
|
||||
if (!path_is_normalized(path))
|
||||
return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
|
||||
"Path read from LoaderBootCountPath is not normalized, refusing: %s",
|
||||
|
|
|
@ -298,12 +298,24 @@ fail:
|
|||
return r;
|
||||
}
|
||||
|
||||
static void read_efi_var(const char *variable, char **ret) {
|
||||
static int efi_get_variable_string_and_warn(const char *variable, char **ret) {
|
||||
int r;
|
||||
|
||||
r = efi_get_variable_string(variable, ret);
|
||||
if (r < 0 && r != -ENOENT)
|
||||
log_warning_errno(r, "Failed to read EFI variable %s: %m", variable);
|
||||
return log_warning_errno(r, "Failed to read EFI variable '%s', ignoring: %m", variable);
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
static int efi_get_variable_path_and_warn(const char *variable, char **ret) {
|
||||
int r;
|
||||
|
||||
r = efi_get_variable_path(variable, ret);
|
||||
if (r < 0 && r != -ENOENT)
|
||||
return log_warning_errno(r, "Failed to read EFI variable '%s', ignoring: %m", variable);
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
static void print_yes_no_line(bool first, bool good, const char *name) {
|
||||
|
@ -396,26 +408,23 @@ int verb_status(int argc, char *argv[], void *userdata) {
|
|||
{ EFI_STUB_FEATURE_MULTI_PROFILE_UKI, "Stub understands profile selector" },
|
||||
{ EFI_STUB_FEATURE_REPORT_STUB_PARTITION, "Stub sets stub partition information" },
|
||||
};
|
||||
_cleanup_free_ char *fw_type = NULL, *fw_info = NULL, *loader = NULL, *loader_path = NULL, *stub = NULL;
|
||||
sd_id128_t loader_part_uuid = SD_ID128_NULL;
|
||||
_cleanup_free_ char *fw_type = NULL, *fw_info = NULL, *loader = NULL, *loader_path = NULL, *stub = NULL, *stub_path = NULL,
|
||||
*current_entry = NULL, *oneshot_entry = NULL, *default_entry = NULL;
|
||||
uint64_t loader_features = 0, stub_features = 0;
|
||||
Tpm2Support s;
|
||||
int have;
|
||||
|
||||
read_efi_var(EFI_LOADER_VARIABLE(LoaderFirmwareType), &fw_type);
|
||||
read_efi_var(EFI_LOADER_VARIABLE(LoaderFirmwareInfo), &fw_info);
|
||||
read_efi_var(EFI_LOADER_VARIABLE(LoaderInfo), &loader);
|
||||
read_efi_var(EFI_LOADER_VARIABLE(StubInfo), &stub);
|
||||
read_efi_var(EFI_LOADER_VARIABLE(LoaderImageIdentifier), &loader_path);
|
||||
(void) efi_get_variable_string_and_warn(EFI_LOADER_VARIABLE(LoaderFirmwareType), &fw_type);
|
||||
(void) efi_get_variable_string_and_warn(EFI_LOADER_VARIABLE(LoaderFirmwareInfo), &fw_info);
|
||||
(void) efi_get_variable_string_and_warn(EFI_LOADER_VARIABLE(LoaderInfo), &loader);
|
||||
(void) efi_get_variable_string_and_warn(EFI_LOADER_VARIABLE(StubInfo), &stub);
|
||||
(void) efi_get_variable_path_and_warn(EFI_LOADER_VARIABLE(LoaderImageIdentifier), &loader_path);
|
||||
(void) efi_get_variable_path_and_warn(EFI_LOADER_VARIABLE(StubImageIdentifier), &stub_path);
|
||||
(void) efi_loader_get_features(&loader_features);
|
||||
(void) efi_stub_get_features(&stub_features);
|
||||
|
||||
if (loader_path)
|
||||
efi_tilt_backslashes(loader_path);
|
||||
|
||||
k = efi_loader_get_device_part_uuid(&loader_part_uuid);
|
||||
if (k < 0 && k != -ENOENT)
|
||||
r = log_warning_errno(k, "Failed to read EFI variable LoaderDevicePartUUID: %m");
|
||||
(void) efi_get_variable_string_and_warn(EFI_LOADER_VARIABLE(LoaderEntrySelected), ¤t_entry);
|
||||
(void) efi_get_variable_string_and_warn(EFI_LOADER_VARIABLE(LoaderEntryOneShot), &oneshot_entry);
|
||||
(void) efi_get_variable_string_and_warn(EFI_LOADER_VARIABLE(LoaderEntryDefault), &default_entry);
|
||||
|
||||
SecureBootMode secure = efi_get_secure_boot_mode();
|
||||
printf("%sSystem:%s\n", ansi_underline(), ansi_normal());
|
||||
|
@ -463,34 +472,58 @@ int verb_status(int argc, char *argv[], void *userdata) {
|
|||
}
|
||||
printf("\n");
|
||||
|
||||
printf("%sCurrent Boot Loader:%s\n", ansi_underline(), ansi_normal());
|
||||
printf(" Product: %s%s%s\n", ansi_highlight(), strna(loader), ansi_normal());
|
||||
if (loader) {
|
||||
printf("%sCurrent Boot Loader:%s\n", ansi_underline(), ansi_normal());
|
||||
printf(" Product: %s%s%s\n", ansi_highlight(), loader, ansi_normal());
|
||||
for (size_t i = 0; i < ELEMENTSOF(loader_flags); i++)
|
||||
print_yes_no_line(i == 0, FLAGS_SET(loader_features, loader_flags[i].flag), loader_flags[i].name);
|
||||
|
||||
for (size_t i = 0; i < ELEMENTSOF(loader_flags); i++)
|
||||
print_yes_no_line(i == 0, FLAGS_SET(loader_features, loader_flags[i].flag), loader_flags[i].name);
|
||||
sd_id128_t loader_partition_uuid;
|
||||
bool have_loader_partition_uuid = efi_loader_get_device_part_uuid(&loader_partition_uuid) >= 0;
|
||||
|
||||
sd_id128_t bootloader_esp_uuid;
|
||||
bool have_bootloader_esp_uuid = efi_loader_get_device_part_uuid(&bootloader_esp_uuid) >= 0;
|
||||
print_yes_no_line(false, have_loader_partition_uuid, "Boot loader set ESP information");
|
||||
|
||||
print_yes_no_line(false, have_bootloader_esp_uuid, "Boot loader sets ESP information");
|
||||
if (have_bootloader_esp_uuid && !sd_id128_is_null(esp_uuid) &&
|
||||
!sd_id128_equal(esp_uuid, bootloader_esp_uuid))
|
||||
printf("WARNING: The boot loader reports a different ESP UUID than detected ("SD_ID128_UUID_FORMAT_STR" vs. "SD_ID128_UUID_FORMAT_STR")!\n",
|
||||
SD_ID128_FORMAT_VAL(bootloader_esp_uuid),
|
||||
SD_ID128_FORMAT_VAL(esp_uuid));
|
||||
if (current_entry)
|
||||
printf("Current Entry: %s\n", current_entry);
|
||||
if (default_entry)
|
||||
printf("Default Entry: %s\n", default_entry);
|
||||
if (oneshot_entry && !streq_ptr(oneshot_entry, default_entry))
|
||||
printf("OneShot Entry: %s\n", oneshot_entry);
|
||||
|
||||
if (have_loader_partition_uuid && !sd_id128_is_null(esp_uuid) && !sd_id128_equal(esp_uuid, loader_partition_uuid))
|
||||
printf("WARNING: The boot loader reports a different partition UUID than the detected ESP ("SD_ID128_UUID_FORMAT_STR" vs. "SD_ID128_UUID_FORMAT_STR")!\n",
|
||||
SD_ID128_FORMAT_VAL(loader_partition_uuid), SD_ID128_FORMAT_VAL(esp_uuid));
|
||||
|
||||
if (!sd_id128_is_null(loader_partition_uuid))
|
||||
printf(" Partition: /dev/disk/by-partuuid/" SD_ID128_UUID_FORMAT_STR "\n",
|
||||
SD_ID128_FORMAT_VAL(loader_partition_uuid));
|
||||
else
|
||||
printf(" Partition: n/a\n");
|
||||
printf(" Loader: %s%s\n", special_glyph(SPECIAL_GLYPH_TREE_RIGHT), strna(loader_path));
|
||||
printf("\n");
|
||||
}
|
||||
|
||||
if (stub) {
|
||||
printf(" Stub: %s\n", stub);
|
||||
printf("%sCurrent Stub:%s\n", ansi_underline(), ansi_normal());
|
||||
printf(" Product: %s%s%s\n", ansi_highlight(), stub, ansi_normal());
|
||||
for (size_t i = 0; i < ELEMENTSOF(stub_flags); i++)
|
||||
print_yes_no_line(i == 0, FLAGS_SET(stub_features, stub_flags[i].flag), stub_flags[i].name);
|
||||
|
||||
sd_id128_t stub_partition_uuid;
|
||||
bool have_stub_partition_uuid = efi_stub_get_device_part_uuid(&stub_partition_uuid) >= 0;
|
||||
|
||||
if (have_stub_partition_uuid && (!(!sd_id128_is_null(esp_uuid) && sd_id128_equal(esp_uuid, stub_partition_uuid)) &&
|
||||
!(!sd_id128_is_null(xbootldr_uuid) && sd_id128_equal(xbootldr_uuid, stub_partition_uuid))))
|
||||
printf("WARNING: The stub loader reports a different UUID than the detected ESP or XBOOTDLR partition ("SD_ID128_UUID_FORMAT_STR" vs. "SD_ID128_UUID_FORMAT_STR"/"SD_ID128_UUID_FORMAT_STR")!\n",
|
||||
SD_ID128_FORMAT_VAL(stub_partition_uuid), SD_ID128_FORMAT_VAL(esp_uuid), SD_ID128_FORMAT_VAL(xbootldr_uuid));
|
||||
if (!sd_id128_is_null(stub_partition_uuid))
|
||||
printf(" Partition: /dev/disk/by-partuuid/" SD_ID128_UUID_FORMAT_STR "\n",
|
||||
SD_ID128_FORMAT_VAL(stub_partition_uuid));
|
||||
else
|
||||
printf(" Partition: n/a\n");
|
||||
printf(" Stub: %s%s\n", special_glyph(SPECIAL_GLYPH_TREE_RIGHT), strna(stub_path));
|
||||
printf("\n");
|
||||
}
|
||||
if (!sd_id128_is_null(loader_part_uuid))
|
||||
printf(" ESP: /dev/disk/by-partuuid/" SD_ID128_UUID_FORMAT_STR "\n",
|
||||
SD_ID128_FORMAT_VAL(loader_part_uuid));
|
||||
else
|
||||
printf(" ESP: n/a\n");
|
||||
printf(" File: %s%s\n", special_glyph(SPECIAL_GLYPH_TREE_RIGHT), strna(loader_path));
|
||||
printf("\n");
|
||||
|
||||
printf("%sRandom Seed:%s\n", ansi_underline(), ansi_normal());
|
||||
have = access(EFIVAR_PATH(EFI_LOADER_VARIABLE(LoaderSystemToken)), F_OK) >= 0;
|
||||
|
|
|
@ -16,12 +16,14 @@
|
|||
#include "build.h"
|
||||
#include "devnum-util.h"
|
||||
#include "dissect-image.h"
|
||||
#include "efi-loader.h"
|
||||
#include "escape.h"
|
||||
#include "find-esp.h"
|
||||
#include "main-func.h"
|
||||
#include "mount-util.h"
|
||||
#include "pager.h"
|
||||
#include "parse-argument.h"
|
||||
#include "path-util.h"
|
||||
#include "pretty-print.h"
|
||||
#include "utf8.h"
|
||||
#include "varlink-io.systemd.BootControl.h"
|
||||
|
@ -38,6 +40,8 @@ char *arg_esp_path = NULL;
|
|||
char *arg_xbootldr_path = NULL;
|
||||
bool arg_print_esp_path = false;
|
||||
bool arg_print_dollar_boot_path = false;
|
||||
bool arg_print_loader_path = false;
|
||||
bool arg_print_stub_path = false;
|
||||
unsigned arg_print_root_device = 0;
|
||||
bool arg_touch_variables = true;
|
||||
bool arg_install_random_seed = true;
|
||||
|
@ -133,6 +137,71 @@ int acquire_xbootldr(
|
|||
return 1;
|
||||
}
|
||||
|
||||
static int print_loader_or_stub_path(void) {
|
||||
_cleanup_free_ char *p = NULL;
|
||||
sd_id128_t uuid;
|
||||
int r;
|
||||
|
||||
if (arg_print_loader_path) {
|
||||
r = efi_loader_get_device_part_uuid(&uuid);
|
||||
if (r == -ENOENT)
|
||||
return log_error_errno(r, "No loader partition UUID passed.");
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Unable to determine loader partition UUID: %m");
|
||||
|
||||
r = efi_get_variable_path(EFI_LOADER_VARIABLE(LoaderImageIdentifier), &p);
|
||||
if (r == -ENOENT)
|
||||
return log_error_errno(r, "No loader EFI binary path passed.");
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Unable to determine loader EFI binary path: %m");
|
||||
} else {
|
||||
assert(arg_print_stub_path);
|
||||
|
||||
r = efi_stub_get_device_part_uuid(&uuid);
|
||||
if (r == -ENOENT)
|
||||
return log_error_errno(r, "No stub partition UUID passed.");
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Unable to determine stub partition UUID: %m");
|
||||
|
||||
r = efi_get_variable_path(EFI_LOADER_VARIABLE(StubImageIdentifier), &p);
|
||||
if (r == -ENOENT)
|
||||
return log_error_errno(r, "No stub EFI binary path passed.");
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Unable to determine stub EFI binary path: %m");
|
||||
}
|
||||
|
||||
sd_id128_t esp_uuid;
|
||||
r = acquire_esp(/* unprivileged_mode= */ false, /* graceful= */ false,
|
||||
/* ret_part= */ NULL, /* ret_pstart= */ NULL, /* ret_psize= */ NULL,
|
||||
&esp_uuid, /* ret_devid= */ NULL);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
const char *found_path = NULL;
|
||||
if (sd_id128_equal(esp_uuid, uuid))
|
||||
found_path = arg_esp_path;
|
||||
else if (arg_print_stub_path) { /* In case of the stub, also look for things in the xbootldr partition */
|
||||
sd_id128_t xbootldr_uuid;
|
||||
|
||||
r = acquire_xbootldr(/* unprivileged_mode= */ false, &xbootldr_uuid, /* ret_devid= */ NULL);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
if (sd_id128_equal(xbootldr_uuid, uuid))
|
||||
found_path = arg_xbootldr_path;
|
||||
}
|
||||
|
||||
if (!found_path)
|
||||
return log_error_errno(SYNTHETIC_ERRNO(ENOENT), "Failed to discover partition " SD_ID128_FORMAT_STR " among mounted boot partitions.", SD_ID128_FORMAT_VAL(uuid));
|
||||
|
||||
_cleanup_free_ char *j = path_join(found_path, p);
|
||||
if (!j)
|
||||
return log_oom();
|
||||
|
||||
puts(j);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int help(int argc, char *argv[], void *userdata) {
|
||||
_cleanup_free_ char *link = NULL;
|
||||
int r;
|
||||
|
@ -182,6 +251,9 @@ static int help(int argc, char *argv[], void *userdata) {
|
|||
" Where to pick files when using --root=/--image=\n"
|
||||
" -p --print-esp-path Print path to the EFI System Partition mount point\n"
|
||||
" -x --print-boot-path Print path to the $BOOT partition mount point\n"
|
||||
" --print-loader-path\n"
|
||||
" Print path to currently booted boot loader binary\n"
|
||||
" --print-stub-path Print path to currently booted unified kernel binary\n"
|
||||
" -R --print-root-device\n"
|
||||
" Print path to the block device node backing the\n"
|
||||
" root file system (returns e.g. /dev/nvme0n1p5)\n"
|
||||
|
@ -235,6 +307,8 @@ static int parse_argv(int argc, char *argv[]) {
|
|||
ARG_ARCH_ALL,
|
||||
ARG_EFI_BOOT_OPTION_DESCRIPTION,
|
||||
ARG_DRY_RUN,
|
||||
ARG_PRINT_LOADER_PATH,
|
||||
ARG_PRINT_STUB_PATH,
|
||||
};
|
||||
|
||||
static const struct option options[] = {
|
||||
|
@ -250,6 +324,8 @@ static int parse_argv(int argc, char *argv[]) {
|
|||
{ "print-esp-path", no_argument, NULL, 'p' },
|
||||
{ "print-path", no_argument, NULL, 'p' }, /* Compatibility alias */
|
||||
{ "print-boot-path", no_argument, NULL, 'x' },
|
||||
{ "print-loader-path", no_argument, NULL, ARG_PRINT_LOADER_PATH },
|
||||
{ "print-stub-path", no_argument, NULL, ARG_PRINT_STUB_PATH },
|
||||
{ "print-root-device", no_argument, NULL, 'R' },
|
||||
{ "no-variables", no_argument, NULL, ARG_NO_VARIABLES },
|
||||
{ "random-seed", required_argument, NULL, ARG_RANDOM_SEED },
|
||||
|
@ -332,6 +408,14 @@ static int parse_argv(int argc, char *argv[]) {
|
|||
arg_print_dollar_boot_path = true;
|
||||
break;
|
||||
|
||||
case ARG_PRINT_LOADER_PATH:
|
||||
arg_print_loader_path = true;
|
||||
break;
|
||||
|
||||
case ARG_PRINT_STUB_PATH:
|
||||
arg_print_stub_path = true;
|
||||
break;
|
||||
|
||||
case 'R':
|
||||
arg_print_root_device++;
|
||||
break;
|
||||
|
@ -414,9 +498,9 @@ static int parse_argv(int argc, char *argv[]) {
|
|||
assert_not_reached();
|
||||
}
|
||||
|
||||
if (!!arg_print_esp_path + !!arg_print_dollar_boot_path + (arg_print_root_device > 0) > 1)
|
||||
if (!!arg_print_esp_path + !!arg_print_dollar_boot_path + (arg_print_root_device > 0) + arg_print_loader_path + arg_print_stub_path > 1)
|
||||
return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
|
||||
"--print-esp-path/-p, --print-boot-path/-x, --print-root-device=/-R cannot be combined.");
|
||||
"--print-esp-path/-p, --print-boot-path/-x, --print-root-device=/-R, --print-loader-path, --print-stub-path cannot be combined.");
|
||||
|
||||
if ((arg_root || arg_image) && argv[optind] && !STR_IN_SET(argv[optind], "status", "list",
|
||||
"install", "update", "remove", "is-installed", "random-seed", "unlink", "cleanup"))
|
||||
|
@ -541,6 +625,9 @@ static int run(int argc, char *argv[]) {
|
|||
return 0;
|
||||
}
|
||||
|
||||
if (arg_print_loader_path || arg_print_stub_path)
|
||||
return print_loader_or_stub_path();
|
||||
|
||||
/* Open up and mount the image */
|
||||
if (arg_image) {
|
||||
assert(!arg_root);
|
||||
|
|
|
@ -450,7 +450,7 @@ static size_t pe_section_table_find_profile_length(
|
|||
assert(start >= section_table);
|
||||
assert(start < section_table + n_section_table);
|
||||
|
||||
/* Look for the next .profile (or the end of the table), this is where the the sections for this
|
||||
/* Look for the next .profile (or the end of the table), this is where the sections for this
|
||||
* profile end. The base profile does not start with a .profile, the others do, hence conditionally
|
||||
* skip over the first entry. */
|
||||
const PeSectionHeader *e;
|
||||
|
@ -485,7 +485,7 @@ EFI_STATUS pe_locate_profile_sections(
|
|||
if (!p)
|
||||
return EFI_NOT_FOUND;
|
||||
|
||||
/* Look for the next .profile (or the end of the table), this is where the the sections for this
|
||||
/* Look for the next .profile (or the end of the table), this is where the sections for this
|
||||
* profile end. */
|
||||
size_t n = pe_section_table_find_profile_length(section_table, n_section_table, p, profile);
|
||||
|
||||
|
|
|
@ -181,7 +181,7 @@ static int manager_find_user_config_paths(char ***ret_files, char ***ret_dirs) {
|
|||
_cleanup_strv_free_ char **files = NULL, **dirs = NULL;
|
||||
int r;
|
||||
|
||||
r = xdg_user_config_dir(&base, "/systemd");
|
||||
r = xdg_user_config_dir("/systemd", &base);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
|
@ -2478,7 +2478,7 @@ static int initialize_runtime(
|
|||
/* Create the runtime directory and place the inaccessible device nodes there, if we run in
|
||||
* user mode. In system mode mount_setup() already did that. */
|
||||
|
||||
r = xdg_user_runtime_dir(&p, "/systemd");
|
||||
r = xdg_user_runtime_dir("/systemd", &p);
|
||||
if (r < 0) {
|
||||
*ret_error_message = "$XDG_RUNTIME_DIR is not set";
|
||||
return log_struct_errno(LOG_EMERG, r,
|
||||
|
|
|
@ -1031,7 +1031,7 @@ int manager_new(RuntimeScope runtime_scope, ManagerTestRunFlags test_run_flags,
|
|||
r = mkdir_label("/run/systemd/units", 0755);
|
||||
else {
|
||||
_cleanup_free_ char *units_path = NULL;
|
||||
r = xdg_user_runtime_dir(&units_path, "/systemd/units");
|
||||
r = xdg_user_runtime_dir("/systemd/units", &units_path);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
|
|
|
@ -4169,7 +4169,7 @@ static void service_sigchld_event(Unit *u, pid_t pid, int code, int status) {
|
|||
* detect when the cgroup becomes empty. Note that the control process is always
|
||||
* our child so it's pointless to watch all other processes. */
|
||||
if (!control_pid_good(s))
|
||||
if (!s->main_pid_known || s->main_pid_alien)
|
||||
if (!s->main_pid_known || s->main_pid_alien || unit_cgroup_delegate(u))
|
||||
(void) unit_enqueue_rewatch_pids(u);
|
||||
}
|
||||
|
||||
|
|
|
@ -5576,12 +5576,13 @@ static int unit_get_invocation_path(Unit *u, char **ret) {
|
|||
p = strjoin("/run/systemd/units/invocation:", u->id);
|
||||
else {
|
||||
_cleanup_free_ char *user_path = NULL;
|
||||
r = xdg_user_runtime_dir(&user_path, "/systemd/units/invocation:");
|
||||
|
||||
r = xdg_user_runtime_dir("/systemd/units/invocation:", &user_path);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
p = strjoin(user_path, u->id);
|
||||
}
|
||||
|
||||
if (!p)
|
||||
return -ENOMEM;
|
||||
|
||||
|
|
|
@ -1046,7 +1046,6 @@ static int process_socket(int fd) {
|
|||
_cleanup_close_ int input_fd = -EBADF, mount_tree_fd = -EBADF;
|
||||
Context context = {};
|
||||
struct iovec_wrapper iovw = {};
|
||||
struct iovec iovec;
|
||||
bool first = true;
|
||||
int r;
|
||||
|
||||
|
@ -1063,8 +1062,7 @@ static int process_socket(int fd) {
|
|||
.msg_controllen = sizeof(control),
|
||||
.msg_iovlen = 1,
|
||||
};
|
||||
ssize_t n;
|
||||
ssize_t l;
|
||||
ssize_t n, l;
|
||||
|
||||
l = next_datagram_size_fd(fd);
|
||||
if (l < 0) {
|
||||
|
@ -1072,8 +1070,10 @@ static int process_socket(int fd) {
|
|||
goto finish;
|
||||
}
|
||||
|
||||
iovec.iov_len = l;
|
||||
iovec.iov_base = malloc(l + 1);
|
||||
_cleanup_(iovec_done) struct iovec iovec = {
|
||||
.iov_len = l,
|
||||
.iov_base = malloc(l + 1),
|
||||
};
|
||||
if (!iovec.iov_base) {
|
||||
r = log_oom();
|
||||
goto finish;
|
||||
|
@ -1083,7 +1083,6 @@ static int process_socket(int fd) {
|
|||
|
||||
n = recvmsg_safe(fd, &mh, MSG_CMSG_CLOEXEC);
|
||||
if (n < 0) {
|
||||
free(iovec.iov_base);
|
||||
r = log_error_errno(n, "Failed to receive datagram: %m");
|
||||
goto finish;
|
||||
}
|
||||
|
@ -1093,8 +1092,6 @@ static int process_socket(int fd) {
|
|||
if (n == 0) {
|
||||
struct cmsghdr *found;
|
||||
|
||||
free(iovec.iov_base);
|
||||
|
||||
found = cmsg_find(&mh, SOL_SOCKET, SCM_RIGHTS, CMSG_LEN(sizeof(int) * 2));
|
||||
if (found) {
|
||||
int fds[2] = EBADF_PAIR;
|
||||
|
@ -1134,6 +1131,8 @@ static int process_socket(int fd) {
|
|||
r = iovw_put(&iovw, iovec.iov_base, iovec.iov_len);
|
||||
if (r < 0)
|
||||
goto finish;
|
||||
|
||||
TAKE_STRUCT(iovec);
|
||||
}
|
||||
|
||||
/* Make sure we got all data we really need */
|
||||
|
|
|
@ -93,20 +93,6 @@ STATIC_DESTRUCTOR_REGISTER(arg_root_shell, freep);
|
|||
STATIC_DESTRUCTOR_REGISTER(arg_kernel_cmdline, freep);
|
||||
STATIC_DESTRUCTOR_REGISTER(arg_image_policy, image_policy_freep);
|
||||
|
||||
static bool press_any_key(void) {
|
||||
char k = 0;
|
||||
bool need_nl = true;
|
||||
|
||||
puts("-- Press any key to proceed --");
|
||||
|
||||
(void) read_one_char(stdin, &k, USEC_INFINITY, &need_nl);
|
||||
|
||||
if (need_nl)
|
||||
putchar('\n');
|
||||
|
||||
return k != 'q';
|
||||
}
|
||||
|
||||
static void print_welcome(int rfd) {
|
||||
_cleanup_free_ char *pretty_name = NULL, *os_name = NULL, *ansi_color = NULL;
|
||||
static bool done = false;
|
||||
|
@ -141,7 +127,7 @@ static void print_welcome(int rfd) {
|
|||
|
||||
printf("\nPlease configure your system!\n\n");
|
||||
|
||||
press_any_key();
|
||||
any_key_to_proceed();
|
||||
|
||||
done = true;
|
||||
}
|
||||
|
@ -184,7 +170,7 @@ static int show_menu(char **x, unsigned n_columns, unsigned width, unsigned perc
|
|||
|
||||
/* on the first screen we reserve 2 extra lines for the title */
|
||||
if (i % break_lines == break_modulo) {
|
||||
if (!press_any_key())
|
||||
if (!any_key_to_proceed())
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2434,6 +2434,8 @@ static int create_interactively(void) {
|
|||
return 0;
|
||||
}
|
||||
|
||||
any_key_to_proceed();
|
||||
|
||||
r = acquire_bus(&bus);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
|
|
@ -404,15 +404,16 @@ static int context_set_path_strv(Context *c, char* const* strv, const char *sour
|
|||
|
||||
static int context_set_plugins(Context *c, const char *s, const char *source) {
|
||||
_cleanup_strv_free_ char **v = NULL;
|
||||
int r;
|
||||
|
||||
assert(c);
|
||||
|
||||
if (c->plugins || !s)
|
||||
return 0;
|
||||
|
||||
v = strv_split(s, NULL);
|
||||
if (!v)
|
||||
return log_oom();
|
||||
r = strv_split_full(&v, s, NULL, EXTRACT_UNQUOTE);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to parse plugin paths from %s: %m", source);
|
||||
|
||||
return context_set_path_strv(c, v, source, "plugins", &c->plugins);
|
||||
}
|
||||
|
|
|
@ -46,7 +46,13 @@ echo 'DTBDTBDTBDTB' >"$D/sources/subdir/whatever.dtb"
|
|||
|
||||
export KERNEL_INSTALL_CONF_ROOT="$D/sources"
|
||||
# We "install" multiple plugins, but control which ones will be active via install.conf.
|
||||
export KERNEL_INSTALL_PLUGINS="${ukify_install} ${loaderentry_install} ${uki_copy_install}"
|
||||
KERNEL_INSTALL_PLUGINS="'${loaderentry_install}' '${uki_copy_install}'"
|
||||
if [[ -n "$ukify_install" ]]; then
|
||||
# shellcheck disable=SC2089
|
||||
KERNEL_INSTALL_PLUGINS="'${ukify_install}' $KERNEL_INSTALL_PLUGINS"
|
||||
fi
|
||||
# shellcheck disable=SC2090
|
||||
export KERNEL_INSTALL_PLUGINS
|
||||
export BOOT_ROOT="$D/boot"
|
||||
export BOOT_MNT="$D/boot"
|
||||
export MACHINE_ID='3e0484f3634a418b8e6a39e8828b03e3'
|
||||
|
|
|
@ -44,7 +44,7 @@ sd_journal_sources += [audit_type_to_name]
|
|||
|
||||
############################################################
|
||||
|
||||
id128_sources = files(
|
||||
sd_id128_sources = files(
|
||||
'sd-id128/id128-util.c',
|
||||
'sd-id128/sd-id128.c',
|
||||
)
|
||||
|
@ -62,6 +62,41 @@ sd_event_sources = files(
|
|||
|
||||
############################################################
|
||||
|
||||
sd_bus_sources = files(
|
||||
'sd-bus/bus-common-errors.c',
|
||||
'sd-bus/bus-container.c',
|
||||
'sd-bus/bus-control.c',
|
||||
'sd-bus/bus-convenience.c',
|
||||
'sd-bus/bus-creds.c',
|
||||
'sd-bus/bus-dump.c',
|
||||
'sd-bus/bus-error.c',
|
||||
'sd-bus/bus-internal.c',
|
||||
'sd-bus/bus-introspect.c',
|
||||
'sd-bus/bus-kernel.c',
|
||||
'sd-bus/bus-match.c',
|
||||
'sd-bus/bus-message.c',
|
||||
'sd-bus/bus-objects.c',
|
||||
'sd-bus/bus-signature.c',
|
||||
'sd-bus/bus-slot.c',
|
||||
'sd-bus/bus-socket.c',
|
||||
'sd-bus/bus-track.c',
|
||||
'sd-bus/bus-type.c',
|
||||
'sd-bus/sd-bus.c',
|
||||
)
|
||||
|
||||
############################################################
|
||||
|
||||
sd_device_sources = files(
|
||||
'sd-device/device-enumerator.c',
|
||||
'sd-device/device-filter.c',
|
||||
'sd-device/device-monitor.c',
|
||||
'sd-device/device-private.c',
|
||||
'sd-device/device-util.c',
|
||||
'sd-device/sd-device.c',
|
||||
)
|
||||
|
||||
############################################################
|
||||
|
||||
sd_login_sources = files('sd-login/sd-login.c')
|
||||
|
||||
############################################################
|
||||
|
@ -83,33 +118,14 @@ sd_varlink_sources = files(
|
|||
|
||||
############################################################
|
||||
|
||||
libsystemd_sources = files(
|
||||
'sd-bus/bus-common-errors.c',
|
||||
'sd-bus/bus-container.c',
|
||||
'sd-bus/bus-control.c',
|
||||
'sd-bus/bus-convenience.c',
|
||||
'sd-bus/bus-creds.c',
|
||||
'sd-bus/bus-dump.c',
|
||||
'sd-bus/bus-error.c',
|
||||
'sd-bus/bus-internal.c',
|
||||
'sd-bus/bus-introspect.c',
|
||||
'sd-bus/bus-kernel.c',
|
||||
'sd-bus/bus-match.c',
|
||||
'sd-bus/bus-message.c',
|
||||
'sd-bus/bus-objects.c',
|
||||
'sd-bus/bus-signature.c',
|
||||
'sd-bus/bus-slot.c',
|
||||
'sd-bus/bus-socket.c',
|
||||
'sd-bus/bus-track.c',
|
||||
'sd-bus/bus-type.c',
|
||||
'sd-bus/sd-bus.c',
|
||||
'sd-device/device-enumerator.c',
|
||||
'sd-device/device-filter.c',
|
||||
'sd-device/device-monitor.c',
|
||||
'sd-device/device-private.c',
|
||||
'sd-device/device-util.c',
|
||||
'sd-device/sd-device.c',
|
||||
'sd-hwdb/sd-hwdb.c',
|
||||
sd_path_sources = files(
|
||||
'sd-path/path-lookup.c',
|
||||
'sd-path/sd-path.c',
|
||||
)
|
||||
|
||||
############################################################
|
||||
|
||||
sd_netlink_sources = files(
|
||||
'sd-netlink/netlink-genl.c',
|
||||
'sd-netlink/netlink-message-nfnl.c',
|
||||
'sd-netlink/netlink-message-rtnl.c',
|
||||
|
@ -122,11 +138,24 @@ libsystemd_sources = files(
|
|||
'sd-netlink/netlink-types.c',
|
||||
'sd-netlink/netlink-util.c',
|
||||
'sd-netlink/sd-netlink.c',
|
||||
)
|
||||
|
||||
############################################################
|
||||
|
||||
sd_network_sources = files(
|
||||
'sd-network/network-util.c',
|
||||
'sd-network/sd-network.c',
|
||||
'sd-path/sd-path.c',
|
||||
)
|
||||
|
||||
############################################################
|
||||
|
||||
libsystemd_sources = files(
|
||||
'sd-hwdb/sd-hwdb.c',
|
||||
'sd-resolve/sd-resolve.c',
|
||||
) + sd_journal_sources + id128_sources + sd_daemon_sources + sd_event_sources + sd_login_sources + sd_json_sources + sd_varlink_sources
|
||||
) + sd_journal_sources + sd_id128_sources + sd_daemon_sources \
|
||||
+ sd_event_sources + sd_bus_sources + sd_device_sources \
|
||||
+ sd_login_sources + sd_json_sources + sd_varlink_sources \
|
||||
+ sd_path_sources + sd_netlink_sources + sd_network_sources
|
||||
|
||||
libsystemd_c_args = ['-fvisibility=default']
|
||||
|
||||
|
|
|
@ -0,0 +1,734 @@
|
|||
/* SPDX-License-Identifier: LGPL-2.1-or-later */
|
||||
|
||||
#include <errno.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include "alloc-util.h"
|
||||
#include "fs-util.h"
|
||||
#include "log.h"
|
||||
#include "macro.h"
|
||||
#include "nulstr-util.h"
|
||||
#include "path-lookup.h"
|
||||
#include "path-util.h"
|
||||
#include "stat-util.h"
|
||||
#include "string-util.h"
|
||||
#include "strv.h"
|
||||
#include "tmpfile-util.h"
|
||||
#include "user-util.h"
|
||||
|
||||
int user_search_dirs(const char *suffix, char ***ret_config_dirs, char ***ret_data_dirs) {
|
||||
_cleanup_strv_free_ char **config_dirs = NULL, **data_dirs = NULL;
|
||||
int r;
|
||||
|
||||
assert(ret_config_dirs);
|
||||
assert(ret_data_dirs);
|
||||
|
||||
r = sd_path_lookup_strv(SD_PATH_SEARCH_CONFIGURATION, suffix, &config_dirs);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
r = sd_path_lookup_strv(SD_PATH_SEARCH_SHARED, suffix, &data_dirs);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
*ret_config_dirs = TAKE_PTR(config_dirs);
|
||||
*ret_data_dirs = TAKE_PTR(data_dirs);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int runtime_directory(RuntimeScope scope, const char *suffix, char **ret) {
|
||||
int r;
|
||||
|
||||
assert(IN_SET(scope, RUNTIME_SCOPE_SYSTEM, RUNTIME_SCOPE_USER));
|
||||
assert(suffix);
|
||||
assert(ret);
|
||||
|
||||
/* Accept $RUNTIME_DIRECTORY as authoritative
|
||||
* If its missing apply the suffix to /run/, or $XDG_RUNTIME_DIR if we are in a user runtime scope.
|
||||
*
|
||||
* Return value indicates whether the suffix was applied or not */
|
||||
|
||||
const char *e = secure_getenv("RUNTIME_DIRECTORY");
|
||||
if (e)
|
||||
return strdup_to(ret, e);
|
||||
|
||||
if (scope == RUNTIME_SCOPE_USER) {
|
||||
r = xdg_user_runtime_dir(suffix, ret);
|
||||
if (r < 0)
|
||||
return r;
|
||||
} else {
|
||||
char *d = path_join("/run", suffix);
|
||||
if (!d)
|
||||
return -ENOMEM;
|
||||
*ret = d;
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static const char* const user_data_unit_paths[] = {
|
||||
"/usr/local/lib/systemd/user",
|
||||
"/usr/local/share/systemd/user",
|
||||
USER_DATA_UNIT_DIR,
|
||||
"/usr/lib/systemd/user",
|
||||
"/usr/share/systemd/user",
|
||||
NULL
|
||||
};
|
||||
|
||||
static const char* const user_config_unit_paths[] = {
|
||||
USER_CONFIG_UNIT_DIR,
|
||||
"/etc/systemd/user",
|
||||
NULL
|
||||
};
|
||||
|
||||
bool path_is_user_data_dir(const char *path) {
|
||||
assert(path);
|
||||
|
||||
return path_strv_contains((char* const*) user_data_unit_paths, path);
|
||||
}
|
||||
|
||||
bool path_is_user_config_dir(const char *path) {
|
||||
assert(path);
|
||||
|
||||
return path_strv_contains((char* const*) user_config_unit_paths, path);
|
||||
}
|
||||
|
||||
static int acquire_generator_dirs(
|
||||
RuntimeScope scope,
|
||||
const char *tempdir,
|
||||
char **ret,
|
||||
char **ret_early,
|
||||
char **ret_late) {
|
||||
|
||||
_cleanup_free_ char *prefix_alloc = NULL, *g = NULL, *early = NULL, *late = NULL;
|
||||
const char *prefix;
|
||||
int r;
|
||||
|
||||
assert(IN_SET(scope, RUNTIME_SCOPE_SYSTEM, RUNTIME_SCOPE_USER, RUNTIME_SCOPE_GLOBAL));
|
||||
assert(ret);
|
||||
assert(ret_early);
|
||||
assert(ret_late);
|
||||
|
||||
if (scope == RUNTIME_SCOPE_GLOBAL)
|
||||
return -EOPNOTSUPP;
|
||||
|
||||
if (tempdir)
|
||||
prefix = tempdir;
|
||||
else if (scope == RUNTIME_SCOPE_SYSTEM)
|
||||
prefix = "/run/systemd";
|
||||
else { /* RUNTIME_SCOPE_USER */
|
||||
r = xdg_user_runtime_dir("/systemd", &prefix_alloc);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
prefix = prefix_alloc;
|
||||
}
|
||||
|
||||
g = path_join(prefix, "generator");
|
||||
if (!g)
|
||||
return -ENOMEM;
|
||||
|
||||
early = path_join(prefix, "generator.early");
|
||||
if (!early)
|
||||
return -ENOMEM;
|
||||
|
||||
late = path_join(prefix, "generator.late");
|
||||
if (!late)
|
||||
return -ENOMEM;
|
||||
|
||||
*ret = TAKE_PTR(g);
|
||||
*ret_early = TAKE_PTR(early);
|
||||
*ret_late = TAKE_PTR(late);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int acquire_transient_dir(RuntimeScope scope, const char *tempdir, char **ret) {
|
||||
char *transient;
|
||||
|
||||
assert(IN_SET(scope, RUNTIME_SCOPE_SYSTEM, RUNTIME_SCOPE_USER, RUNTIME_SCOPE_GLOBAL));
|
||||
assert(ret);
|
||||
|
||||
if (scope == RUNTIME_SCOPE_GLOBAL)
|
||||
return -EOPNOTSUPP;
|
||||
|
||||
if (tempdir)
|
||||
transient = path_join(tempdir, "transient");
|
||||
else if (scope == RUNTIME_SCOPE_SYSTEM)
|
||||
transient = strdup("/run/systemd/transient");
|
||||
else /* RUNTIME_SCOPE_USER */
|
||||
return xdg_user_runtime_dir("/systemd/transient", ret);
|
||||
|
||||
if (!transient)
|
||||
return -ENOMEM;
|
||||
|
||||
*ret = transient;
|
||||
return 0;
|
||||
}
|
||||
|
||||
typedef enum LookupDirType {
|
||||
LOOKUP_DIR_CONFIG,
|
||||
LOOKUP_DIR_CONTROL,
|
||||
LOOKUP_DIR_ATTACHED,
|
||||
_LOOKUP_DIR_MAX,
|
||||
_LOOKUP_DIR_INVALID = -EINVAL,
|
||||
} LookupDirType;
|
||||
|
||||
static int acquire_lookup_dirs(
|
||||
LookupDirType type,
|
||||
RuntimeScope scope,
|
||||
char **ret_persistent,
|
||||
char **ret_runtime) {
|
||||
|
||||
/* RUNTIME_SCOPE_USER dirs are relative to XDG_CONFIG_DIR and XDG_RUNTIME_DIR, respectively */
|
||||
static const struct {
|
||||
const char *persistent;
|
||||
const char *runtime;
|
||||
} dirs[_LOOKUP_DIR_MAX][_RUNTIME_SCOPE_MAX] = {
|
||||
[LOOKUP_DIR_CONFIG] = {
|
||||
[RUNTIME_SCOPE_SYSTEM] = { SYSTEM_CONFIG_UNIT_DIR, "/run/systemd/system" },
|
||||
[RUNTIME_SCOPE_GLOBAL] = { USER_CONFIG_UNIT_DIR, "/run/systemd/user" },
|
||||
[RUNTIME_SCOPE_USER] = { "systemd/user", "systemd/user" },
|
||||
},
|
||||
[LOOKUP_DIR_CONTROL] = {
|
||||
[RUNTIME_SCOPE_SYSTEM] = { "/etc/systemd/system.control", "/run/systemd/system.control" },
|
||||
[RUNTIME_SCOPE_USER] = { "systemd/user.control", "systemd/user.control" },
|
||||
},
|
||||
[LOOKUP_DIR_ATTACHED] = {
|
||||
[RUNTIME_SCOPE_SYSTEM] = { "/etc/systemd/system.attached", "/run/systemd/system.attached" },
|
||||
/* Portable services are not available to regular users for now. */
|
||||
},
|
||||
};
|
||||
|
||||
_cleanup_free_ char *a = NULL, *b = NULL;
|
||||
int r;
|
||||
|
||||
assert(type >= 0 && type < _LOOKUP_DIR_MAX);
|
||||
assert(IN_SET(scope, RUNTIME_SCOPE_SYSTEM, RUNTIME_SCOPE_USER, RUNTIME_SCOPE_GLOBAL));
|
||||
assert(ret_persistent);
|
||||
assert(ret_runtime);
|
||||
|
||||
const char *persistent = dirs[type][scope].persistent;
|
||||
const char *runtime = dirs[type][scope].runtime;
|
||||
assert(!persistent == !runtime);
|
||||
|
||||
if (!persistent)
|
||||
return -EOPNOTSUPP;
|
||||
|
||||
switch (scope) {
|
||||
|
||||
case RUNTIME_SCOPE_SYSTEM:
|
||||
case RUNTIME_SCOPE_GLOBAL:
|
||||
a = strdup(persistent);
|
||||
b = strdup(runtime);
|
||||
if (!a || !b)
|
||||
return -ENOMEM;
|
||||
|
||||
*ret_persistent = TAKE_PTR(a);
|
||||
*ret_runtime = TAKE_PTR(b);
|
||||
|
||||
return 0;
|
||||
|
||||
case RUNTIME_SCOPE_USER:
|
||||
r = xdg_user_config_dir(persistent, &a);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
r = xdg_user_runtime_dir(runtime, ret_runtime);
|
||||
if (r < 0) {
|
||||
if (r != -ENXIO)
|
||||
return r;
|
||||
|
||||
/* If XDG_RUNTIME_DIR is not set, don't consider that fatal, simply initialize the runtime
|
||||
* directory to NULL */
|
||||
*ret_runtime = NULL;
|
||||
}
|
||||
|
||||
*ret_persistent = TAKE_PTR(a);
|
||||
|
||||
return 0;
|
||||
|
||||
default:
|
||||
assert_not_reached();
|
||||
}
|
||||
}
|
||||
|
||||
static int patch_root_prefix(char **p, const char *root_dir) {
|
||||
char *c;
|
||||
|
||||
assert(p);
|
||||
|
||||
if (!root_dir)
|
||||
return 0;
|
||||
|
||||
if (!*p)
|
||||
return 0;
|
||||
|
||||
c = path_join(root_dir, *p);
|
||||
if (!c)
|
||||
return -ENOMEM;
|
||||
|
||||
free_and_replace(*p, c);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int patch_root_prefix_strv(char **l, const char *root_dir) {
|
||||
int r;
|
||||
|
||||
if (!root_dir)
|
||||
return 0;
|
||||
|
||||
STRV_FOREACH(i, l) {
|
||||
r = patch_root_prefix(i, root_dir);
|
||||
if (r < 0)
|
||||
return r;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int get_paths_from_environ(const char *var, char ***ret) {
|
||||
const char *e;
|
||||
int r;
|
||||
|
||||
assert(var);
|
||||
assert(ret);
|
||||
|
||||
e = getenv(var);
|
||||
if (!e) {
|
||||
*ret = NULL;
|
||||
return 0;
|
||||
}
|
||||
|
||||
bool append = endswith(e, ":"); /* Whether to append the normal search paths after what's obtained
|
||||
from envvar */
|
||||
|
||||
/* FIXME: empty components in other places should be rejected. */
|
||||
|
||||
r = path_split_and_make_absolute(e, ret);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
return append;
|
||||
}
|
||||
|
||||
static char** user_unit_search_dirs(
|
||||
const char *persistent_config,
|
||||
const char *runtime_config,
|
||||
const char *global_persistent_config,
|
||||
const char *global_runtime_config,
|
||||
const char *generator,
|
||||
const char *generator_early,
|
||||
const char *generator_late,
|
||||
const char *transient,
|
||||
const char *persistent_control,
|
||||
const char *runtime_control) {
|
||||
|
||||
_cleanup_strv_free_ char **paths = NULL, **config_dirs = NULL, **data_dirs = NULL;
|
||||
|
||||
/* The returned strv might contain duplicates, and we expect caller to filter them. */
|
||||
|
||||
assert(persistent_config);
|
||||
assert(global_persistent_config);
|
||||
assert(global_runtime_config);
|
||||
assert(persistent_control);
|
||||
|
||||
if (user_search_dirs("/systemd/user", &config_dirs, &data_dirs) < 0)
|
||||
return NULL;
|
||||
|
||||
paths = strv_new(persistent_control,
|
||||
STRV_IFNOTNULL(runtime_control),
|
||||
STRV_IFNOTNULL(transient),
|
||||
STRV_IFNOTNULL(generator_early),
|
||||
persistent_config);
|
||||
if (!paths)
|
||||
return NULL;
|
||||
|
||||
if (strv_extend_strv(&paths, config_dirs, /* filter_duplicates = */ false) < 0)
|
||||
return NULL;
|
||||
|
||||
/* global config has lower priority than the user config of the same type */
|
||||
if (strv_extend(&paths, global_persistent_config) < 0)
|
||||
return NULL;
|
||||
|
||||
if (strv_extend_strv(&paths, (char* const*) user_config_unit_paths, /* filter_duplicates = */ false) < 0)
|
||||
return NULL;
|
||||
|
||||
if (strv_extend_many(&paths,
|
||||
STRV_IFNOTNULL(runtime_config),
|
||||
global_runtime_config,
|
||||
STRV_IFNOTNULL(generator)) < 0)
|
||||
return NULL;
|
||||
|
||||
if (strv_extend_strv(&paths, data_dirs, /* filter_duplicates = */ false) < 0)
|
||||
return NULL;
|
||||
|
||||
if (strv_extend_strv(&paths, (char* const*) user_data_unit_paths, false) < 0)
|
||||
return NULL;
|
||||
|
||||
if (strv_extend(&paths, generator_late) < 0)
|
||||
return NULL;
|
||||
|
||||
if (path_strv_make_absolute_cwd(paths) < 0)
|
||||
return NULL;
|
||||
|
||||
return TAKE_PTR(paths);
|
||||
}
|
||||
|
||||
int lookup_paths_init(
|
||||
LookupPaths *lp,
|
||||
RuntimeScope scope,
|
||||
LookupPathsFlags flags,
|
||||
const char *root_dir) {
|
||||
|
||||
_cleanup_(rmdir_and_freep) char *tempdir = NULL;
|
||||
_cleanup_free_ char
|
||||
*root = NULL,
|
||||
*persistent_config = NULL, *runtime_config = NULL,
|
||||
*global_persistent_config = NULL, *global_runtime_config = NULL,
|
||||
*generator = NULL, *generator_early = NULL, *generator_late = NULL,
|
||||
*transient = NULL,
|
||||
*persistent_control = NULL, *runtime_control = NULL,
|
||||
*persistent_attached = NULL, *runtime_attached = NULL;
|
||||
_cleanup_strv_free_ char **paths = NULL;
|
||||
int r;
|
||||
|
||||
assert(lp);
|
||||
assert(scope >= 0);
|
||||
assert(scope < _RUNTIME_SCOPE_MAX);
|
||||
|
||||
if (!empty_or_root(root_dir)) {
|
||||
if (scope == RUNTIME_SCOPE_USER)
|
||||
return -EINVAL;
|
||||
|
||||
r = is_dir(root_dir, true);
|
||||
if (r < 0)
|
||||
return r;
|
||||
if (r == 0)
|
||||
return -ENOTDIR;
|
||||
|
||||
root = strdup(root_dir);
|
||||
if (!root)
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
if (FLAGS_SET(flags, LOOKUP_PATHS_TEMPORARY_GENERATED)) {
|
||||
r = mkdtemp_malloc("/tmp/systemd-temporary-XXXXXX", &tempdir);
|
||||
if (r < 0)
|
||||
return log_debug_errno(r, "Failed to create temporary directory: %m");
|
||||
}
|
||||
|
||||
/* Note: when XDG_RUNTIME_DIR is not set this will not return -ENXIO, but simply set runtime_config to NULL */
|
||||
r = acquire_lookup_dirs(LOOKUP_DIR_CONFIG, scope, &persistent_config, &runtime_config);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
if (scope == RUNTIME_SCOPE_USER) {
|
||||
r = acquire_lookup_dirs(LOOKUP_DIR_CONFIG, RUNTIME_SCOPE_GLOBAL, &global_persistent_config, &global_runtime_config);
|
||||
if (r < 0)
|
||||
return r;
|
||||
}
|
||||
|
||||
r = acquire_lookup_dirs(LOOKUP_DIR_CONTROL, scope, &persistent_control, &runtime_control);
|
||||
if (r < 0 && r != -EOPNOTSUPP)
|
||||
return r;
|
||||
|
||||
r = acquire_lookup_dirs(LOOKUP_DIR_ATTACHED, scope, &persistent_attached, &runtime_attached);
|
||||
if (r < 0 && r != -EOPNOTSUPP)
|
||||
return r;
|
||||
|
||||
if (!FLAGS_SET(flags, LOOKUP_PATHS_EXCLUDE_GENERATED)) {
|
||||
/* Note: if XDG_RUNTIME_DIR is not set, this will fail completely with ENXIO */
|
||||
r = acquire_generator_dirs(scope, tempdir,
|
||||
&generator, &generator_early, &generator_late);
|
||||
if (r < 0 && !IN_SET(r, -EOPNOTSUPP, -ENXIO))
|
||||
return r;
|
||||
}
|
||||
|
||||
/* Note: if XDG_RUNTIME_DIR is not set, this will fail completely with ENXIO */
|
||||
r = acquire_transient_dir(scope, tempdir, &transient);
|
||||
if (r < 0 && !IN_SET(r, -EOPNOTSUPP, -ENXIO))
|
||||
return r;
|
||||
|
||||
/* First priority is whatever has been passed to us via env vars */
|
||||
r = get_paths_from_environ("SYSTEMD_UNIT_PATH", &paths);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
if (!paths || r > 0) {
|
||||
/* Let's figure something out. */
|
||||
|
||||
_cleanup_strv_free_ char **add = NULL;
|
||||
|
||||
/* For the user units we include share/ in the search paths in order to comply with
|
||||
* the XDG basedir spec. For the system stuff we avoid such nonsense. OTOH we include (/usr/)lib/
|
||||
* in the search paths for the system stuff but avoid it for user stuff. */
|
||||
|
||||
assert(persistent_config);
|
||||
|
||||
switch (scope) {
|
||||
|
||||
case RUNTIME_SCOPE_SYSTEM:
|
||||
/* If you modify this you also want to modify systemdsystemunitpath= in systemd.pc.in! */
|
||||
add = strv_new(ASSERT_PTR(persistent_control),
|
||||
ASSERT_PTR(runtime_control),
|
||||
ASSERT_PTR(transient),
|
||||
ASSERT_PTR(generator_early),
|
||||
persistent_config,
|
||||
SYSTEM_CONFIG_UNIT_DIR,
|
||||
"/etc/systemd/system",
|
||||
ASSERT_PTR(persistent_attached),
|
||||
ASSERT_PTR(runtime_config),
|
||||
"/run/systemd/system",
|
||||
ASSERT_PTR(runtime_attached),
|
||||
ASSERT_PTR(generator),
|
||||
"/usr/local/lib/systemd/system",
|
||||
SYSTEM_DATA_UNIT_DIR,
|
||||
"/usr/lib/systemd/system",
|
||||
/* To be used ONLY for images which might be legacy split-usr */
|
||||
FLAGS_SET(flags, LOOKUP_PATHS_SPLIT_USR) ? "/lib/systemd/system" : STRV_IGNORE,
|
||||
ASSERT_PTR(generator_late));
|
||||
break;
|
||||
|
||||
case RUNTIME_SCOPE_GLOBAL:
|
||||
/* If you modify this you also want to modify systemduserunitpath= in systemd.pc.in,
|
||||
* and RUNTIME_SCOPE_USER search paths below! */
|
||||
add = strv_new(STRV_IFNOTNULL(persistent_control),
|
||||
STRV_IFNOTNULL(runtime_control),
|
||||
STRV_IFNOTNULL(transient),
|
||||
STRV_IFNOTNULL(generator_early),
|
||||
persistent_config,
|
||||
USER_CONFIG_UNIT_DIR,
|
||||
"/etc/systemd/user",
|
||||
ASSERT_PTR(runtime_config),
|
||||
"/run/systemd/user",
|
||||
STRV_IFNOTNULL(generator),
|
||||
"/usr/local/share/systemd/user",
|
||||
"/usr/share/systemd/user",
|
||||
"/usr/local/lib/systemd/user",
|
||||
USER_DATA_UNIT_DIR,
|
||||
"/usr/lib/systemd/user",
|
||||
STRV_IFNOTNULL(generator_late));
|
||||
break;
|
||||
|
||||
case RUNTIME_SCOPE_USER:
|
||||
add = user_unit_search_dirs(persistent_config, runtime_config,
|
||||
global_persistent_config, global_runtime_config,
|
||||
generator, generator_early, generator_late,
|
||||
transient,
|
||||
persistent_control, runtime_control);
|
||||
break;
|
||||
|
||||
default:
|
||||
assert_not_reached();
|
||||
}
|
||||
|
||||
if (!add)
|
||||
return -ENOMEM;
|
||||
|
||||
if (paths) {
|
||||
/* strv_uniq() below would filter all duplicates against the final strv */
|
||||
r = strv_extend_strv(&paths, add, /* filter_duplicates = */ false);
|
||||
if (r < 0)
|
||||
return r;
|
||||
} else
|
||||
/* Small optimization: if paths is NULL (and it usually is), we can simply assign 'add' to it,
|
||||
* and don't have to copy anything */
|
||||
paths = TAKE_PTR(add);
|
||||
}
|
||||
|
||||
r = patch_root_prefix(&persistent_config, root);
|
||||
if (r < 0)
|
||||
return r;
|
||||
r = patch_root_prefix(&runtime_config, root);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
r = patch_root_prefix(&generator, root);
|
||||
if (r < 0)
|
||||
return r;
|
||||
r = patch_root_prefix(&generator_early, root);
|
||||
if (r < 0)
|
||||
return r;
|
||||
r = patch_root_prefix(&generator_late, root);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
r = patch_root_prefix(&transient, root);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
r = patch_root_prefix(&persistent_control, root);
|
||||
if (r < 0)
|
||||
return r;
|
||||
r = patch_root_prefix(&runtime_control, root);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
r = patch_root_prefix(&persistent_attached, root);
|
||||
if (r < 0)
|
||||
return r;
|
||||
r = patch_root_prefix(&runtime_attached, root);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
r = patch_root_prefix_strv(paths, root);
|
||||
if (r < 0)
|
||||
return -ENOMEM;
|
||||
|
||||
*lp = (LookupPaths) {
|
||||
.search_path = strv_uniq(TAKE_PTR(paths)),
|
||||
|
||||
.persistent_config = TAKE_PTR(persistent_config),
|
||||
.runtime_config = TAKE_PTR(runtime_config),
|
||||
|
||||
.generator = TAKE_PTR(generator),
|
||||
.generator_early = TAKE_PTR(generator_early),
|
||||
.generator_late = TAKE_PTR(generator_late),
|
||||
|
||||
.transient = TAKE_PTR(transient),
|
||||
|
||||
.persistent_control = TAKE_PTR(persistent_control),
|
||||
.runtime_control = TAKE_PTR(runtime_control),
|
||||
|
||||
.persistent_attached = TAKE_PTR(persistent_attached),
|
||||
.runtime_attached = TAKE_PTR(runtime_attached),
|
||||
|
||||
.root_dir = TAKE_PTR(root),
|
||||
.temporary_dir = TAKE_PTR(tempdir),
|
||||
};
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int lookup_paths_init_or_warn(LookupPaths *lp, RuntimeScope scope, LookupPathsFlags flags, const char *root_dir) {
|
||||
int r;
|
||||
|
||||
r = lookup_paths_init(lp, scope, flags, root_dir);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to initialize unit search paths%s%s: %m",
|
||||
isempty(root_dir) ? "" : " for root directory ", strempty(root_dir));
|
||||
return r;
|
||||
}
|
||||
|
||||
void lookup_paths_done(LookupPaths *lp) {
|
||||
assert(lp);
|
||||
|
||||
lp->search_path = strv_free(lp->search_path);
|
||||
|
||||
lp->persistent_config = mfree(lp->persistent_config);
|
||||
lp->runtime_config = mfree(lp->runtime_config);
|
||||
|
||||
lp->persistent_attached = mfree(lp->persistent_attached);
|
||||
lp->runtime_attached = mfree(lp->runtime_attached);
|
||||
|
||||
lp->generator = mfree(lp->generator);
|
||||
lp->generator_early = mfree(lp->generator_early);
|
||||
lp->generator_late = mfree(lp->generator_late);
|
||||
|
||||
lp->transient = mfree(lp->transient);
|
||||
|
||||
lp->persistent_control = mfree(lp->persistent_control);
|
||||
lp->runtime_control = mfree(lp->runtime_control);
|
||||
|
||||
lp->root_dir = mfree(lp->root_dir);
|
||||
lp->temporary_dir = mfree(lp->temporary_dir);
|
||||
}
|
||||
|
||||
void lookup_paths_log(LookupPaths *lp) {
|
||||
assert(lp);
|
||||
|
||||
if (strv_isempty(lp->search_path)) {
|
||||
log_debug("Ignoring unit files.");
|
||||
lp->search_path = strv_free(lp->search_path);
|
||||
} else {
|
||||
_cleanup_free_ char *t = NULL;
|
||||
|
||||
t = strv_join(lp->search_path, "\n\t");
|
||||
log_debug("Looking for unit files in (higher priority first):\n\t%s", strna(t));
|
||||
}
|
||||
}
|
||||
|
||||
static const char* const system_generator_paths[] = {
|
||||
"/run/systemd/system-generators",
|
||||
"/etc/systemd/system-generators",
|
||||
"/usr/local/lib/systemd/system-generators",
|
||||
SYSTEM_GENERATOR_DIR,
|
||||
NULL,
|
||||
};
|
||||
|
||||
static const char* const user_generator_paths[] = {
|
||||
"/run/systemd/user-generators",
|
||||
"/etc/systemd/user-generators",
|
||||
"/usr/local/lib/systemd/user-generators",
|
||||
USER_GENERATOR_DIR,
|
||||
NULL,
|
||||
};
|
||||
|
||||
static const char* const system_env_generator_paths[] = {
|
||||
"/run/systemd/system-environment-generators",
|
||||
"/etc/systemd/system-environment-generators",
|
||||
"/usr/local/lib/systemd/system-environment-generators",
|
||||
SYSTEM_ENV_GENERATOR_DIR,
|
||||
NULL,
|
||||
};
|
||||
|
||||
static const char* const user_env_generator_paths[] = {
|
||||
"/run/systemd/user-environment-generators",
|
||||
"/etc/systemd/user-environment-generators",
|
||||
"/usr/local/lib/systemd/user-environment-generators",
|
||||
USER_ENV_GENERATOR_DIR,
|
||||
NULL,
|
||||
};
|
||||
|
||||
char** generator_binary_paths_internal(RuntimeScope scope, bool env_generator) {
|
||||
|
||||
static const struct {
|
||||
const char *env_name;
|
||||
const char * const *paths[_RUNTIME_SCOPE_MAX];
|
||||
} unit_generator = {
|
||||
"SYSTEMD_GENERATOR_PATH",
|
||||
{
|
||||
[RUNTIME_SCOPE_SYSTEM] = system_generator_paths,
|
||||
[RUNTIME_SCOPE_USER] = user_generator_paths,
|
||||
}
|
||||
}, environment_generator = {
|
||||
"SYSTEMD_ENVIRONMENT_GENERATOR_PATH",
|
||||
{
|
||||
[RUNTIME_SCOPE_SYSTEM] = system_env_generator_paths,
|
||||
[RUNTIME_SCOPE_USER] = user_env_generator_paths,
|
||||
}
|
||||
};
|
||||
|
||||
_cleanup_strv_free_ char **paths = NULL;
|
||||
int r;
|
||||
|
||||
assert(IN_SET(scope, RUNTIME_SCOPE_SYSTEM, RUNTIME_SCOPE_USER));
|
||||
|
||||
const char *env_name = ASSERT_PTR((env_generator ? environment_generator : unit_generator).env_name);
|
||||
const char * const *search_paths = ASSERT_PTR((env_generator ? environment_generator : unit_generator).paths[scope]);
|
||||
|
||||
/* First priority is whatever has been passed to us via env vars */
|
||||
r = get_paths_from_environ(env_name, &paths);
|
||||
if (r < 0)
|
||||
return NULL;
|
||||
|
||||
if (!paths || r > 0) {
|
||||
_cleanup_strv_free_ char **add = strv_copy((char* const*) search_paths);
|
||||
if (!add)
|
||||
return NULL;
|
||||
|
||||
if (paths) {
|
||||
r = strv_extend_strv(&paths, add, /* filter_duplicates = */ true);
|
||||
if (r < 0)
|
||||
return NULL;
|
||||
} else
|
||||
/* Small optimization: if paths is NULL (and it usually is), we can simply assign 'add' to it,
|
||||
* and don't have to copy anything */
|
||||
paths = TAKE_PTR(add);
|
||||
}
|
||||
|
||||
return TAKE_PTR(paths);
|
||||
}
|
|
@ -3,8 +3,8 @@
|
|||
|
||||
#include <stdbool.h>
|
||||
|
||||
#include "constants.h"
|
||||
#include "macro.h"
|
||||
#include "sd-path.h"
|
||||
|
||||
#include "runtime-scope.h"
|
||||
|
||||
typedef enum LookupPathsFlags {
|
||||
|
@ -55,11 +55,21 @@ typedef struct LookupPaths {
|
|||
int lookup_paths_init(LookupPaths *lp, RuntimeScope scope, LookupPathsFlags flags, const char *root_dir);
|
||||
int lookup_paths_init_or_warn(LookupPaths *lp, RuntimeScope scope, LookupPathsFlags flags, const char *root_dir);
|
||||
|
||||
int xdg_user_dirs(char ***ret_config_dirs, char ***ret_data_dirs);
|
||||
int xdg_user_runtime_dir(char **ret, const char *suffix);
|
||||
int xdg_user_config_dir(char **ret, const char *suffix);
|
||||
int xdg_user_data_dir(char **ret, const char *suffix);
|
||||
int runtime_directory(char **ret, RuntimeScope scope, const char *suffix);
|
||||
int runtime_directory(RuntimeScope scope, const char *suffix, char **ret);
|
||||
|
||||
/* We don't treat /etc/xdg/systemd in these functions as the xdg base dir spec suggests because we assume
|
||||
* that is a link to /etc/systemd/ anyway. */
|
||||
|
||||
int user_search_dirs(const char *suffix, char ***ret_config_dirs, char ***ret_data_dirs);
|
||||
static inline int xdg_user_runtime_dir(const char *suffix, char **ret) {
|
||||
return sd_path_lookup(SD_PATH_USER_RUNTIME, suffix, ret);
|
||||
}
|
||||
static inline int xdg_user_config_dir(const char *suffix, char **ret) {
|
||||
return sd_path_lookup(SD_PATH_USER_CONFIGURATION, suffix, ret);
|
||||
}
|
||||
static inline int xdg_user_data_dir(const char *suffix, char **ret) {
|
||||
return sd_path_lookup(SD_PATH_USER_SHARED, suffix, ret);
|
||||
}
|
||||
|
||||
bool path_is_user_data_dir(const char *path);
|
||||
bool path_is_user_config_dir(const char *path);
|
||||
|
@ -67,11 +77,10 @@ bool path_is_user_config_dir(const char *path);
|
|||
void lookup_paths_log(LookupPaths *p);
|
||||
void lookup_paths_done(LookupPaths *p);
|
||||
|
||||
char **generator_binary_paths(RuntimeScope scope);
|
||||
char **env_generator_binary_paths(RuntimeScope scope);
|
||||
|
||||
#define NETWORK_DIRS ((const char* const*) CONF_PATHS_STRV("systemd/network"))
|
||||
#define NETWORK_DIRS_NULSTR CONF_PATHS_NULSTR("systemd/network")
|
||||
|
||||
#define PORTABLE_PROFILE_DIRS CONF_PATHS_NULSTR("systemd/portable/profile")
|
||||
int find_portable_profile(const char *name, const char *unit, char **ret_path);
|
||||
char** generator_binary_paths_internal(RuntimeScope scope, bool env_generator);
|
||||
static inline char** generator_binary_paths(RuntimeScope runtime_scope) {
|
||||
return generator_binary_paths_internal(runtime_scope, false);
|
||||
}
|
||||
static inline char** env_generator_binary_paths(RuntimeScope runtime_scope) {
|
||||
return generator_binary_paths_internal(runtime_scope, true);
|
||||
}
|
|
@ -4,6 +4,7 @@
|
|||
|
||||
#include "alloc-util.h"
|
||||
#include "architecture.h"
|
||||
#include "conf-files.h"
|
||||
#include "fd-util.h"
|
||||
#include "fileio.h"
|
||||
#include "fs-util.h"
|
||||
|
@ -65,26 +66,20 @@ static int from_home_dir(const char *envname, const char *suffix, char **buffer,
|
|||
return 0;
|
||||
}
|
||||
|
||||
static int from_user_dir(const char *field, char **buffer, const char **ret) {
|
||||
static int from_xdg_user_dir(const char *field, char **buffer, const char **ret) {
|
||||
_cleanup_free_ char *user_dirs = NULL;
|
||||
_cleanup_fclose_ FILE *f = NULL;
|
||||
_cleanup_free_ char *b = NULL;
|
||||
_cleanup_free_ const char *fn = NULL;
|
||||
const char *c = NULL;
|
||||
int r;
|
||||
|
||||
assert(field);
|
||||
assert(buffer);
|
||||
assert(ret);
|
||||
|
||||
r = from_home_dir("XDG_CONFIG_HOME", ".config", &b, &c);
|
||||
r = sd_path_lookup(SD_PATH_USER_CONFIGURATION, "user-dirs.dirs", &user_dirs);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
fn = path_join(c, "user-dirs.dirs");
|
||||
if (!fn)
|
||||
return -ENOMEM;
|
||||
|
||||
f = fopen(fn, "re");
|
||||
f = fopen(user_dirs, "re");
|
||||
if (!f) {
|
||||
if (errno == ENOENT)
|
||||
goto fallback;
|
||||
|
@ -107,14 +102,12 @@ static int from_user_dir(const char *field, char **buffer, const char **ret) {
|
|||
if (!p)
|
||||
continue;
|
||||
|
||||
p += strspn(p, WHITESPACE);
|
||||
|
||||
p = skip_leading_chars(p, WHITESPACE);
|
||||
if (*p != '=')
|
||||
continue;
|
||||
p++;
|
||||
|
||||
p += strspn(p, WHITESPACE);
|
||||
|
||||
p = skip_leading_chars(p, WHITESPACE);
|
||||
if (*p != '"')
|
||||
continue;
|
||||
p++;
|
||||
|
@ -125,62 +118,34 @@ static int from_user_dir(const char *field, char **buffer, const char **ret) {
|
|||
*e = 0;
|
||||
|
||||
/* Three syntaxes permitted: relative to $HOME, $HOME itself, and absolute path */
|
||||
if (startswith(p, "$HOME/")) {
|
||||
_cleanup_free_ char *h = NULL;
|
||||
if (streq(p, "$HOME"))
|
||||
goto home;
|
||||
|
||||
r = get_home_dir(&h);
|
||||
if (r < 0)
|
||||
return r;
|
||||
const char *s = startswith(p, "$HOME/");
|
||||
if (s)
|
||||
return from_home_dir(/* envname = */ NULL, s, buffer, ret);
|
||||
|
||||
if (!path_extend(&h, p+5))
|
||||
if (path_is_absolute(p)) {
|
||||
char *c = strdup(p);
|
||||
if (!c)
|
||||
return -ENOMEM;
|
||||
|
||||
*buffer = h;
|
||||
*ret = TAKE_PTR(h);
|
||||
return 0;
|
||||
} else if (streq(p, "$HOME")) {
|
||||
|
||||
r = get_home_dir(buffer);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
*ret = *buffer;
|
||||
return 0;
|
||||
} else if (path_is_absolute(p)) {
|
||||
char *copy;
|
||||
|
||||
copy = strdup(p);
|
||||
if (!copy)
|
||||
return -ENOMEM;
|
||||
|
||||
*buffer = copy;
|
||||
*ret = copy;
|
||||
*ret = *buffer = c;
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
fallback:
|
||||
/* The desktop directory defaults to $HOME/Desktop, the others to $HOME */
|
||||
if (streq(field, "XDG_DESKTOP_DIR")) {
|
||||
_cleanup_free_ char *h = NULL;
|
||||
if (streq(field, "XDG_DESKTOP_DIR"))
|
||||
return from_home_dir(/* envname = */ NULL, "Desktop", buffer, ret);
|
||||
|
||||
r = get_home_dir(&h);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
if (!path_extend(&h, "Desktop"))
|
||||
return -ENOMEM;
|
||||
|
||||
*buffer = h;
|
||||
*ret = TAKE_PTR(h);
|
||||
} else {
|
||||
r = get_home_dir(buffer);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
*ret = *buffer;
|
||||
}
|
||||
home:
|
||||
r = get_home_dir(buffer);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
*ret = *buffer;
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -287,28 +252,28 @@ static int get_path(uint64_t type, char **buffer, const char **ret) {
|
|||
return 0;
|
||||
|
||||
case SD_PATH_USER_DOCUMENTS:
|
||||
return from_user_dir("XDG_DOCUMENTS_DIR", buffer, ret);
|
||||
return from_xdg_user_dir("XDG_DOCUMENTS_DIR", buffer, ret);
|
||||
|
||||
case SD_PATH_USER_MUSIC:
|
||||
return from_user_dir("XDG_MUSIC_DIR", buffer, ret);
|
||||
return from_xdg_user_dir("XDG_MUSIC_DIR", buffer, ret);
|
||||
|
||||
case SD_PATH_USER_PICTURES:
|
||||
return from_user_dir("XDG_PICTURES_DIR", buffer, ret);
|
||||
return from_xdg_user_dir("XDG_PICTURES_DIR", buffer, ret);
|
||||
|
||||
case SD_PATH_USER_VIDEOS:
|
||||
return from_user_dir("XDG_VIDEOS_DIR", buffer, ret);
|
||||
return from_xdg_user_dir("XDG_VIDEOS_DIR", buffer, ret);
|
||||
|
||||
case SD_PATH_USER_DOWNLOAD:
|
||||
return from_user_dir("XDG_DOWNLOAD_DIR", buffer, ret);
|
||||
return from_xdg_user_dir("XDG_DOWNLOAD_DIR", buffer, ret);
|
||||
|
||||
case SD_PATH_USER_PUBLIC:
|
||||
return from_user_dir("XDG_PUBLICSHARE_DIR", buffer, ret);
|
||||
return from_xdg_user_dir("XDG_PUBLICSHARE_DIR", buffer, ret);
|
||||
|
||||
case SD_PATH_USER_TEMPLATES:
|
||||
return from_user_dir("XDG_TEMPLATES_DIR", buffer, ret);
|
||||
return from_xdg_user_dir("XDG_TEMPLATES_DIR", buffer, ret);
|
||||
|
||||
case SD_PATH_USER_DESKTOP:
|
||||
return from_user_dir("XDG_DESKTOP_DIR", buffer, ret);
|
||||
return from_xdg_user_dir("XDG_DESKTOP_DIR", buffer, ret);
|
||||
|
||||
case SD_PATH_SYSTEMD_UTIL:
|
||||
*ret = PREFIX_NOSLASH "/lib/systemd";
|
||||
|
@ -390,55 +355,56 @@ static int get_path(uint64_t type, char **buffer, const char **ret) {
|
|||
return -EOPNOTSUPP;
|
||||
}
|
||||
|
||||
static int get_path_alloc(uint64_t type, const char *suffix, char **path) {
|
||||
static int get_path_alloc(uint64_t type, const char *suffix, char **ret) {
|
||||
_cleanup_free_ char *buffer = NULL;
|
||||
char *buffer2 = NULL;
|
||||
const char *ret;
|
||||
const char *p;
|
||||
int r;
|
||||
|
||||
assert(path);
|
||||
assert(ret);
|
||||
|
||||
r = get_path(type, &buffer, &ret);
|
||||
r = get_path(type, &buffer, &p);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
if (suffix) {
|
||||
suffix += strspn(suffix, "/");
|
||||
buffer2 = path_join(ret, suffix);
|
||||
if (!buffer2)
|
||||
char *suffixed = path_join(p, suffix);
|
||||
if (!suffixed)
|
||||
return -ENOMEM;
|
||||
|
||||
path_simplify(suffixed);
|
||||
|
||||
free_and_replace(buffer, suffixed);
|
||||
} else if (!buffer) {
|
||||
buffer = strdup(ret);
|
||||
buffer = strdup(p);
|
||||
if (!buffer)
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
*path = buffer2 ?: TAKE_PTR(buffer);
|
||||
*ret = TAKE_PTR(buffer);
|
||||
return 0;
|
||||
}
|
||||
|
||||
_public_ int sd_path_lookup(uint64_t type, const char *suffix, char **path) {
|
||||
_public_ int sd_path_lookup(uint64_t type, const char *suffix, char **ret) {
|
||||
int r;
|
||||
|
||||
assert_return(path, -EINVAL);
|
||||
assert_return(ret, -EINVAL);
|
||||
|
||||
r = get_path_alloc(type, suffix, path);
|
||||
r = get_path_alloc(type, suffix, ret);
|
||||
if (r != -EOPNOTSUPP)
|
||||
return r;
|
||||
|
||||
/* Fall back to sd_path_lookup_strv */
|
||||
_cleanup_strv_free_ char **l = NULL;
|
||||
char *buffer;
|
||||
|
||||
r = sd_path_lookup_strv(type, suffix, &l);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
buffer = strv_join(l, ":");
|
||||
if (!buffer)
|
||||
char *joined = strv_join(l, ":");
|
||||
if (!joined)
|
||||
return -ENOMEM;
|
||||
|
||||
*path = buffer;
|
||||
*ret = joined;
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -615,25 +581,16 @@ static int get_search(uint64_t type, char ***ret) {
|
|||
}
|
||||
|
||||
case SD_PATH_SYSTEMD_SEARCH_SYSTEM_GENERATOR:
|
||||
case SD_PATH_SYSTEMD_SEARCH_USER_GENERATOR: {
|
||||
RuntimeScope scope = type == SD_PATH_SYSTEMD_SEARCH_SYSTEM_GENERATOR ?
|
||||
RUNTIME_SCOPE_SYSTEM : RUNTIME_SCOPE_USER;
|
||||
char **t;
|
||||
|
||||
t = generator_binary_paths(scope);
|
||||
if (!t)
|
||||
return -ENOMEM;
|
||||
|
||||
*ret = t;
|
||||
return 0;
|
||||
}
|
||||
|
||||
case SD_PATH_SYSTEMD_SEARCH_USER_GENERATOR:
|
||||
case SD_PATH_SYSTEMD_SEARCH_SYSTEM_ENVIRONMENT_GENERATOR:
|
||||
case SD_PATH_SYSTEMD_SEARCH_USER_ENVIRONMENT_GENERATOR: {
|
||||
char **t;
|
||||
RuntimeScope scope = IN_SET(type, SD_PATH_SYSTEMD_SEARCH_SYSTEM_GENERATOR,
|
||||
SD_PATH_SYSTEMD_SEARCH_SYSTEM_ENVIRONMENT_GENERATOR) ?
|
||||
RUNTIME_SCOPE_SYSTEM : RUNTIME_SCOPE_USER;
|
||||
bool env_generator = IN_SET(type, SD_PATH_SYSTEMD_SEARCH_SYSTEM_ENVIRONMENT_GENERATOR,
|
||||
SD_PATH_SYSTEMD_SEARCH_USER_ENVIRONMENT_GENERATOR);
|
||||
|
||||
t = env_generator_binary_paths(type == SD_PATH_SYSTEMD_SEARCH_SYSTEM_ENVIRONMENT_GENERATOR ?
|
||||
RUNTIME_SCOPE_SYSTEM : RUNTIME_SCOPE_USER);
|
||||
char **t = generator_binary_paths_internal(scope, env_generator);
|
||||
if (!t)
|
||||
return -ENOMEM;
|
||||
|
||||
|
@ -649,11 +606,11 @@ static int get_search(uint64_t type, char ***ret) {
|
|||
return -EOPNOTSUPP;
|
||||
}
|
||||
|
||||
_public_ int sd_path_lookup_strv(uint64_t type, const char *suffix, char ***paths) {
|
||||
_cleanup_strv_free_ char **l = NULL, **n = NULL;
|
||||
_public_ int sd_path_lookup_strv(uint64_t type, const char *suffix, char ***ret) {
|
||||
_cleanup_strv_free_ char **l = NULL;
|
||||
int r;
|
||||
|
||||
assert_return(paths, -EINVAL);
|
||||
assert_return(ret, -EINVAL);
|
||||
|
||||
r = get_search(type, &l);
|
||||
if (r == -EOPNOTSUPP) {
|
||||
|
@ -669,31 +626,20 @@ _public_ int sd_path_lookup_strv(uint64_t type, const char *suffix, char ***path
|
|||
l[0] = TAKE_PTR(t);
|
||||
l[1] = NULL;
|
||||
|
||||
*paths = TAKE_PTR(l);
|
||||
*ret = TAKE_PTR(l);
|
||||
return 0;
|
||||
|
||||
} else if (r < 0)
|
||||
}
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
if (!suffix) {
|
||||
*paths = TAKE_PTR(l);
|
||||
return 0;
|
||||
}
|
||||
if (suffix)
|
||||
STRV_FOREACH(i, l) {
|
||||
if (!path_extend(i, suffix))
|
||||
return -ENOMEM;
|
||||
|
||||
n = new(char*, strv_length(l)+1);
|
||||
if (!n)
|
||||
return -ENOMEM;
|
||||
path_simplify(*i);
|
||||
}
|
||||
|
||||
char **j = n;
|
||||
STRV_FOREACH(i, l) {
|
||||
*j = path_join(*i, suffix);
|
||||
if (!*j)
|
||||
return -ENOMEM;
|
||||
|
||||
j++;
|
||||
}
|
||||
*j = NULL;
|
||||
|
||||
*paths = TAKE_PTR(n);
|
||||
*ret = TAKE_PTR(l);
|
||||
return 0;
|
||||
}
|
||||
|
|
|
@ -1229,7 +1229,7 @@ static int generic_method_get_interface_description(
|
|||
sd_varlink_method_flags_t flags,
|
||||
void *userdata) {
|
||||
|
||||
static const struct sd_json_dispatch_field dispatch_table[] = {
|
||||
static const sd_json_dispatch_field dispatch_table[] = {
|
||||
{ "interface", SD_JSON_VARIANT_STRING, sd_json_dispatch_const_string, 0, SD_JSON_MANDATORY },
|
||||
{}
|
||||
};
|
||||
|
|
|
@ -416,19 +416,18 @@ static int list_machine_one(sd_varlink *link, Machine *m, bool more) {
|
|||
}
|
||||
|
||||
static int vl_method_list(sd_varlink *link, sd_json_variant *parameters, sd_varlink_method_flags_t flags, void *userdata) {
|
||||
Manager *m = ASSERT_PTR(userdata);
|
||||
const char *mn = NULL;
|
||||
|
||||
const sd_json_dispatch_field dispatch_table[] = {
|
||||
{ "name", SD_JSON_VARIANT_STRING, sd_json_dispatch_const_string, PTR_TO_SIZE(&mn), 0 },
|
||||
static const sd_json_dispatch_field dispatch_table[] = {
|
||||
{ "name", SD_JSON_VARIANT_STRING, sd_json_dispatch_const_string, 0, 0 },
|
||||
{}
|
||||
};
|
||||
|
||||
Manager *m = ASSERT_PTR(userdata);
|
||||
const char *mn = NULL;
|
||||
int r;
|
||||
|
||||
assert(parameters);
|
||||
|
||||
r = sd_varlink_dispatch(link, parameters, dispatch_table, 0);
|
||||
r = sd_varlink_dispatch(link, parameters, dispatch_table, &mn);
|
||||
if (r != 0)
|
||||
return r;
|
||||
|
||||
|
|
|
@ -36,23 +36,22 @@ struct str {
|
|||
static long cut_last(u32 i, struct str *str) {
|
||||
char *s;
|
||||
|
||||
/* Sanity check for the preverifier */
|
||||
if (i >= str->l)
|
||||
return 1; /* exit from the loop */
|
||||
|
||||
i = str->l - i - 1;
|
||||
s = str->s + i;
|
||||
|
||||
/* Sanity check for the preverifier */
|
||||
if (i >= str->l)
|
||||
return 1;
|
||||
|
||||
if (*s == 0)
|
||||
return 0;
|
||||
return 0; /* continue */
|
||||
|
||||
if (*s == '\n' || *s == '\r' || *s == ' ' || *s == '\t') {
|
||||
*s = 0;
|
||||
|
||||
return 0;
|
||||
return 0; /* continue */
|
||||
}
|
||||
|
||||
return 1;
|
||||
return 1; /* exit from the loop */
|
||||
}
|
||||
|
||||
/* Cut off trailing whitespace and newlines */
|
||||
|
|
|
@ -973,7 +973,7 @@ int netdev_load_one(Manager *manager, const char *filename) {
|
|||
if (r < 0)
|
||||
return r;
|
||||
|
||||
log_netdev_debug(netdev, "loaded \"%s\"", netdev_kind_to_string(netdev->kind));
|
||||
log_syntax(/* unit = */ NULL, LOG_DEBUG, filename, /* config_line = */ 0, /* error = */ 0, "Successfully loaded.");
|
||||
|
||||
r = netdev_request_to_create(netdev);
|
||||
if (r < 0)
|
||||
|
|
|
@ -6,6 +6,7 @@
|
|||
#include <linux/if_arp.h>
|
||||
|
||||
#include "alloc-util.h"
|
||||
#include "device-private.h"
|
||||
#include "dhcp-client-internal.h"
|
||||
#include "hostname-setup.h"
|
||||
#include "hostname-util.h"
|
||||
|
@ -1428,27 +1429,33 @@ static int dhcp4_set_request_address(Link *link) {
|
|||
}
|
||||
|
||||
static bool link_needs_dhcp_broadcast(Link *link) {
|
||||
const char *val;
|
||||
int r;
|
||||
|
||||
assert(link);
|
||||
assert(link->network);
|
||||
|
||||
/* Return the setting in DHCP[4].RequestBroadcast if specified. Otherwise return the device property
|
||||
* ID_NET_DHCP_BROADCAST setting, which may be set for interfaces requiring that the DHCPOFFER message
|
||||
* is being broadcast because they can't handle unicast messages while not fully configured.
|
||||
* If neither is set or a failure occurs, return false, which is the default for this flag.
|
||||
*/
|
||||
r = link->network->dhcp_broadcast;
|
||||
if (r < 0 && link->dev && sd_device_get_property_value(link->dev, "ID_NET_DHCP_BROADCAST", &val) >= 0) {
|
||||
r = parse_boolean(val);
|
||||
if (r < 0)
|
||||
log_link_debug_errno(link, r, "DHCPv4 CLIENT: Failed to parse ID_NET_DHCP_BROADCAST, ignoring: %m");
|
||||
else
|
||||
log_link_debug(link, "DHCPv4 CLIENT: Detected ID_NET_DHCP_BROADCAST='%d'.", r);
|
||||
* ID_NET_DHCP_BROADCAST setting, which may be set for interfaces requiring that the DHCPOFFER
|
||||
* message is being broadcast because they can't handle unicast messages while not fully configured.
|
||||
* If neither is set or a failure occurs, return false, which is the default for this flag. */
|
||||
|
||||
r = link->network->dhcp_broadcast;
|
||||
if (r >= 0)
|
||||
return r;
|
||||
|
||||
if (!link->dev)
|
||||
return false;
|
||||
|
||||
r = device_get_property_bool(link->dev, "ID_NET_DHCP_BROADCAST");
|
||||
if (r < 0) {
|
||||
if (r != -ENOENT)
|
||||
log_link_warning_errno(link, r, "DHCPv4 CLIENT: Failed to get or parse ID_NET_DHCP_BROADCAST, ignoring: %m");
|
||||
|
||||
return false;
|
||||
}
|
||||
return r == true;
|
||||
|
||||
log_link_debug(link, "DHCPv4 CLIENT: Detected ID_NET_DHCP_BROADCAST='%d'.", r);
|
||||
return r;
|
||||
}
|
||||
|
||||
static bool link_dhcp4_ipv6_only_mode(Link *link) {
|
||||
|
|
|
@ -221,7 +221,7 @@ int link_set_ipv6ll_stable_secret(Link *link) {
|
|||
}
|
||||
|
||||
return sysctl_write_ip_property(AF_INET6, link->ifname, "stable_secret",
|
||||
IN6_ADDR_TO_STRING(&a), &link->manager->sysctl_shadow);
|
||||
IN6_ADDR_TO_STRING(&a), manager_get_sysctl_shadow(link->manager));
|
||||
}
|
||||
|
||||
int link_set_ipv6ll_addrgen_mode(Link *link, IPv6LinkLocalAddressGenMode mode) {
|
||||
|
@ -232,7 +232,7 @@ int link_set_ipv6ll_addrgen_mode(Link *link, IPv6LinkLocalAddressGenMode mode) {
|
|||
if (mode == link->ipv6ll_address_gen_mode)
|
||||
return 0;
|
||||
|
||||
return sysctl_write_ip_property_uint32(AF_INET6, link->ifname, "addr_gen_mode", mode, &link->manager->sysctl_shadow);
|
||||
return sysctl_write_ip_property_uint32(AF_INET6, link->ifname, "addr_gen_mode", mode, manager_get_sysctl_shadow(link->manager));
|
||||
}
|
||||
|
||||
static const char* const ipv6_link_local_address_gen_mode_table[_IPV6_LINK_LOCAL_ADDRESS_GEN_MODE_MAX] = {
|
||||
|
|
|
@ -97,8 +97,8 @@ static int neighbor_append_json(Neighbor *n, sd_json_variant **array) {
|
|||
|
||||
return sd_json_variant_append_arraybo(
|
||||
array,
|
||||
SD_JSON_BUILD_PAIR_INTEGER("Family", n->family),
|
||||
JSON_BUILD_PAIR_IN_ADDR("Destination", &n->in_addr, n->family),
|
||||
SD_JSON_BUILD_PAIR_INTEGER("Family", n->dst_addr.family),
|
||||
JSON_BUILD_PAIR_IN_ADDR("Destination", &n->dst_addr.address, n->dst_addr.family),
|
||||
JSON_BUILD_PAIR_HW_ADDR("LinkLayerAddress", &n->ll_addr),
|
||||
SD_JSON_BUILD_PAIR_STRING("ConfigSource", network_config_source_to_string(n->source)),
|
||||
SD_JSON_BUILD_PAIR_STRING("ConfigState", state));
|
||||
|
@ -168,7 +168,7 @@ static int nexthop_append_json(NextHop *n, sd_json_variant **array) {
|
|||
return sd_json_variant_append_arraybo(
|
||||
array,
|
||||
SD_JSON_BUILD_PAIR_UNSIGNED("ID", n->id),
|
||||
JSON_BUILD_PAIR_IN_ADDR_NON_NULL("Gateway", &n->gw, n->family),
|
||||
JSON_BUILD_PAIR_IN_ADDR_NON_NULL("Gateway", &n->gw.address, n->family),
|
||||
SD_JSON_BUILD_PAIR_UNSIGNED("Flags", n->flags),
|
||||
SD_JSON_BUILD_PAIR_STRING("FlagsString", strempty(flags)),
|
||||
SD_JSON_BUILD_PAIR_UNSIGNED("Protocol", n->protocol),
|
||||
|
|
|
@ -1293,9 +1293,9 @@ static int link_get_network(Link *link, Network **ret) {
|
|||
}
|
||||
|
||||
log_link_full(link, warn ? LOG_WARNING : LOG_DEBUG,
|
||||
"found matching network '%s'%s.",
|
||||
network->filename,
|
||||
warn ? ", based on potentially unpredictable interface name" : "");
|
||||
"Found matching .network file%s: %s",
|
||||
warn ? ", based on potentially unpredictable interface name" : "",
|
||||
network->filename);
|
||||
|
||||
if (network->unmanaged)
|
||||
return -ENOENT;
|
||||
|
@ -1304,7 +1304,7 @@ static int link_get_network(Link *link, Network **ret) {
|
|||
return 0;
|
||||
}
|
||||
|
||||
return -ENOENT;
|
||||
return log_link_debug_errno(link, SYNTHETIC_ERRNO(ENOENT), "No matching .network found.");
|
||||
}
|
||||
|
||||
int link_reconfigure_impl(Link *link, bool force) {
|
||||
|
|
|
@ -604,7 +604,9 @@ int manager_new(Manager **ret, bool test_mode) {
|
|||
.duid_product_uuid.type = DUID_TYPE_UUID,
|
||||
.dhcp_server_persist_leases = true,
|
||||
.ip_forwarding = { -1, -1, },
|
||||
#if HAVE_VMLINUX_H
|
||||
.cgroup_fd = -EBADF,
|
||||
#endif
|
||||
};
|
||||
|
||||
*ret = TAKE_PTR(m);
|
||||
|
@ -624,8 +626,6 @@ Manager* manager_free(Manager *m) {
|
|||
HASHMAP_FOREACH(link, m->links_by_index)
|
||||
(void) link_stop_engines(link, true);
|
||||
|
||||
hashmap_free(m->sysctl_shadow);
|
||||
|
||||
m->request_queue = ordered_set_free(m->request_queue);
|
||||
m->remove_request_queue = ordered_set_free(m->remove_request_queue);
|
||||
|
||||
|
|
|
@ -122,12 +122,14 @@ struct Manager {
|
|||
|
||||
/* sysctl */
|
||||
int ip_forwarding[2];
|
||||
#if HAVE_VMLINUX_H
|
||||
Hashmap *sysctl_shadow;
|
||||
sd_event_source *sysctl_event_source;
|
||||
struct ring_buffer *sysctl_buffer;
|
||||
struct sysctl_monitor_bpf *sysctl_skel;
|
||||
struct bpf_link *sysctl_link;
|
||||
int cgroup_fd;
|
||||
#endif
|
||||
};
|
||||
|
||||
int manager_new(Manager **ret, bool test_mode);
|
||||
|
@ -150,4 +152,12 @@ int manager_set_timezone(Manager *m, const char *timezone);
|
|||
|
||||
int manager_reload(Manager *m, sd_bus_message *message);
|
||||
|
||||
static inline Hashmap** manager_get_sysctl_shadow(Manager *manager) {
|
||||
#if HAVE_VMLINUX_H
|
||||
return &ASSERT_PTR(manager)->sysctl_shadow;
|
||||
#else
|
||||
return NULL;
|
||||
#endif
|
||||
}
|
||||
|
||||
DEFINE_TRIVIAL_CLEANUP_FUNC(Manager*, manager_free);
|
||||
|
|
|
@ -987,7 +987,7 @@ static int ndisc_router_process_reachable_time(Link *link, sd_ndisc_router *rt)
|
|||
}
|
||||
|
||||
/* Set the reachable time for Neighbor Solicitations. */
|
||||
r = sysctl_write_ip_neighbor_property_uint32(AF_INET6, link->ifname, "base_reachable_time_ms", (uint32_t) msec, &link->manager->sysctl_shadow);
|
||||
r = sysctl_write_ip_neighbor_property_uint32(AF_INET6, link->ifname, "base_reachable_time_ms", (uint32_t) msec, manager_get_sysctl_shadow(link->manager));
|
||||
if (r < 0)
|
||||
log_link_warning_errno(link, r, "Failed to apply neighbor reachable time (%"PRIu64"), ignoring: %m", msec);
|
||||
|
||||
|
@ -1021,7 +1021,7 @@ static int ndisc_router_process_retransmission_time(Link *link, sd_ndisc_router
|
|||
}
|
||||
|
||||
/* Set the retransmission time for Neighbor Solicitations. */
|
||||
r = sysctl_write_ip_neighbor_property_uint32(AF_INET6, link->ifname, "retrans_time_ms", (uint32_t) msec, &link->manager->sysctl_shadow);
|
||||
r = sysctl_write_ip_neighbor_property_uint32(AF_INET6, link->ifname, "retrans_time_ms", (uint32_t) msec, manager_get_sysctl_shadow(link->manager));
|
||||
if (r < 0)
|
||||
log_link_warning_errno(link, r, "Failed to apply neighbor retransmission time (%"PRIu64"), ignoring: %m", msec);
|
||||
|
||||
|
@ -1057,7 +1057,7 @@ static int ndisc_router_process_hop_limit(Link *link, sd_ndisc_router *rt) {
|
|||
if (hop_limit <= 0)
|
||||
return 0;
|
||||
|
||||
r = sysctl_write_ip_property_uint32(AF_INET6, link->ifname, "hop_limit", (uint32_t) hop_limit, &link->manager->sysctl_shadow);
|
||||
r = sysctl_write_ip_property_uint32(AF_INET6, link->ifname, "hop_limit", (uint32_t) hop_limit, manager_get_sysctl_shadow(link->manager));
|
||||
if (r < 0)
|
||||
log_link_warning_errno(link, r, "Failed to apply hop_limit (%u), ignoring: %m", hop_limit);
|
||||
|
||||
|
|
|
@ -147,29 +147,29 @@ static int neighbor_dup(const Neighbor *neighbor, Neighbor **ret) {
|
|||
static void neighbor_hash_func(const Neighbor *neighbor, struct siphash *state) {
|
||||
assert(neighbor);
|
||||
|
||||
siphash24_compress_typesafe(neighbor->family, state);
|
||||
siphash24_compress_typesafe(neighbor->dst_addr.family, state);
|
||||
|
||||
if (!IN_SET(neighbor->family, AF_INET, AF_INET6))
|
||||
if (!IN_SET(neighbor->dst_addr.family, AF_INET, AF_INET6))
|
||||
/* treat any other address family as AF_UNSPEC */
|
||||
return;
|
||||
|
||||
/* Equality of neighbors are given by the destination address.
|
||||
* See neigh_lookup() in the kernel. */
|
||||
in_addr_hash_func(&neighbor->in_addr, neighbor->family, state);
|
||||
in_addr_hash_func(&neighbor->dst_addr.address, neighbor->dst_addr.family, state);
|
||||
}
|
||||
|
||||
static int neighbor_compare_func(const Neighbor *a, const Neighbor *b) {
|
||||
int r;
|
||||
|
||||
r = CMP(a->family, b->family);
|
||||
r = CMP(a->dst_addr.family, b->dst_addr.family);
|
||||
if (r != 0)
|
||||
return r;
|
||||
|
||||
if (!IN_SET(a->family, AF_INET, AF_INET6))
|
||||
if (!IN_SET(a->dst_addr.family, AF_INET, AF_INET6))
|
||||
/* treat any other address family as AF_UNSPEC */
|
||||
return 0;
|
||||
|
||||
return memcmp(&a->in_addr, &b->in_addr, FAMILY_ADDRESS_SIZE(a->family));
|
||||
return memcmp(&a->dst_addr.address, &b->dst_addr.address, FAMILY_ADDRESS_SIZE(a->dst_addr.family));
|
||||
}
|
||||
|
||||
static int neighbor_get_request(Link *link, const Neighbor *neighbor, Request **ret) {
|
||||
|
@ -244,7 +244,7 @@ static void log_neighbor_debug(const Neighbor *neighbor, const char *str, const
|
|||
"%s %s neighbor (%s): lladdr: %s, dst: %s",
|
||||
str, strna(network_config_source_to_string(neighbor->source)), strna(state),
|
||||
HW_ADDR_TO_STR(&neighbor->ll_addr),
|
||||
IN_ADDR_TO_STRING(neighbor->family, &neighbor->in_addr));
|
||||
IN_ADDR_TO_STRING(neighbor->dst_addr.family, &neighbor->dst_addr.address));
|
||||
}
|
||||
|
||||
static int neighbor_configure(Neighbor *neighbor, Link *link, Request *req) {
|
||||
|
@ -261,7 +261,7 @@ static int neighbor_configure(Neighbor *neighbor, Link *link, Request *req) {
|
|||
log_neighbor_debug(neighbor, "Configuring", link);
|
||||
|
||||
r = sd_rtnl_message_new_neigh(link->manager->rtnl, &m, RTM_NEWNEIGH,
|
||||
link->ifindex, neighbor->family);
|
||||
link->ifindex, neighbor->dst_addr.family);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
|
@ -273,7 +273,7 @@ static int neighbor_configure(Neighbor *neighbor, Link *link, Request *req) {
|
|||
if (r < 0)
|
||||
return r;
|
||||
|
||||
r = netlink_message_append_in_addr_union(m, NDA_DST, neighbor->family, &neighbor->in_addr);
|
||||
r = netlink_message_append_in_addr_union(m, NDA_DST, neighbor->dst_addr.family, &neighbor->dst_addr.address);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
|
@ -338,7 +338,7 @@ static int link_request_neighbor(Link *link, const Neighbor *neighbor) {
|
|||
"The link layer address length (%zu) for neighbor %s does not match with "
|
||||
"the hardware address length (%zu), ignoring the setting.",
|
||||
neighbor->ll_addr.length,
|
||||
IN_ADDR_TO_STRING(neighbor->family, &neighbor->in_addr),
|
||||
IN_ADDR_TO_STRING(neighbor->dst_addr.family, &neighbor->dst_addr.address),
|
||||
link->hw_addr.length);
|
||||
return 0;
|
||||
}
|
||||
|
@ -451,11 +451,11 @@ int neighbor_remove(Neighbor *neighbor, Link *link) {
|
|||
log_neighbor_debug(neighbor, "Removing", link);
|
||||
|
||||
r = sd_rtnl_message_new_neigh(link->manager->rtnl, &m, RTM_DELNEIGH,
|
||||
link->ifindex, neighbor->family);
|
||||
link->ifindex, neighbor->dst_addr.family);
|
||||
if (r < 0)
|
||||
return log_link_error_errno(link, r, "Could not allocate RTM_DELNEIGH message: %m");
|
||||
|
||||
r = netlink_message_append_in_addr_union(m, NDA_DST, neighbor->family, &neighbor->in_addr);
|
||||
r = netlink_message_append_in_addr_union(m, NDA_DST, neighbor->dst_addr.family, &neighbor->dst_addr.address);
|
||||
if (r < 0)
|
||||
return log_link_error_errno(link, r, "Could not append NDA_DST attribute: %m");
|
||||
|
||||
|
@ -593,19 +593,19 @@ int manager_rtnl_process_neighbor(sd_netlink *rtnl, sd_netlink_message *message,
|
|||
return log_oom();
|
||||
|
||||
/* First, retrieve the fundamental information about the neighbor. */
|
||||
r = sd_rtnl_message_neigh_get_family(message, &tmp->family);
|
||||
r = sd_rtnl_message_neigh_get_family(message, &tmp->dst_addr.family);
|
||||
if (r < 0) {
|
||||
log_link_warning(link, "rtnl: received neighbor message without family, ignoring.");
|
||||
return 0;
|
||||
}
|
||||
if (tmp->family == AF_BRIDGE) /* Currently, we do not support it. */
|
||||
if (tmp->dst_addr.family == AF_BRIDGE) /* Currently, we do not support it. */
|
||||
return 0;
|
||||
if (!IN_SET(tmp->family, AF_INET, AF_INET6)) {
|
||||
log_link_debug(link, "rtnl: received neighbor message with invalid family '%i', ignoring.", tmp->family);
|
||||
if (!IN_SET(tmp->dst_addr.family, AF_INET, AF_INET6)) {
|
||||
log_link_debug(link, "rtnl: received neighbor message with invalid family '%i', ignoring.", tmp->dst_addr.family);
|
||||
return 0;
|
||||
}
|
||||
|
||||
r = netlink_message_read_in_addr_union(message, NDA_DST, tmp->family, &tmp->in_addr);
|
||||
r = netlink_message_read_in_addr_union(message, NDA_DST, tmp->dst_addr.family, &tmp->dst_addr.address);
|
||||
if (r < 0) {
|
||||
log_link_warning_errno(link, r, "rtnl: received neighbor message without valid address, ignoring: %m");
|
||||
return 0;
|
||||
|
@ -660,28 +660,28 @@ int manager_rtnl_process_neighbor(sd_netlink *rtnl, sd_netlink_message *message,
|
|||
return 1;
|
||||
}
|
||||
|
||||
#define log_neighbor_section(neighbor, fmt, ...) \
|
||||
({ \
|
||||
const Neighbor *_neighbor = (neighbor); \
|
||||
log_section_warning_errno( \
|
||||
_neighbor ? _neighbor->section : NULL, \
|
||||
SYNTHETIC_ERRNO(EINVAL), \
|
||||
fmt " Ignoring [Neighbor] section.", \
|
||||
##__VA_ARGS__); \
|
||||
})
|
||||
|
||||
static int neighbor_section_verify(Neighbor *neighbor) {
|
||||
if (section_is_invalid(neighbor->section))
|
||||
return -EINVAL;
|
||||
|
||||
if (neighbor->family == AF_UNSPEC)
|
||||
return log_warning_errno(SYNTHETIC_ERRNO(EINVAL),
|
||||
"%s: Neighbor section without Address= configured. "
|
||||
"Ignoring [Neighbor] section from line %u.",
|
||||
neighbor->section->filename, neighbor->section->line);
|
||||
if (neighbor->dst_addr.family == AF_UNSPEC)
|
||||
return log_neighbor_section(neighbor, "Neighbor section without Address= configured.");
|
||||
|
||||
if (neighbor->family == AF_INET6 && !socket_ipv6_is_supported())
|
||||
return log_warning_errno(SYNTHETIC_ERRNO(EINVAL),
|
||||
"%s: Neighbor section with an IPv6 destination address configured, "
|
||||
"but the kernel does not support IPv6. "
|
||||
"Ignoring [Neighbor] section from line %u.",
|
||||
neighbor->section->filename, neighbor->section->line);
|
||||
if (neighbor->dst_addr.family == AF_INET6 && !socket_ipv6_is_supported())
|
||||
return log_neighbor_section(neighbor, "Neighbor section with an IPv6 destination address configured, but the kernel does not support IPv6.");
|
||||
|
||||
if (neighbor->ll_addr.length == 0)
|
||||
return log_warning_errno(SYNTHETIC_ERRNO(EINVAL),
|
||||
"%s: Neighbor section without LinkLayerAddress= configured. "
|
||||
"Ignoring [Neighbor] section from line %u.",
|
||||
neighbor->section->filename, neighbor->section->line);
|
||||
return log_neighbor_section(neighbor, "Neighbor section without LinkLayerAddress= configured.");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
@ -709,7 +709,7 @@ int network_drop_invalid_neighbors(Network *network) {
|
|||
log_warning("%s: Duplicated neighbor settings for %s is specified at line %u and %u, "
|
||||
"dropping the neighbor setting specified at line %u.",
|
||||
dup->section->filename,
|
||||
IN_ADDR_TO_STRING(neighbor->family, &neighbor->in_addr),
|
||||
IN_ADDR_TO_STRING(neighbor->dst_addr.family, &neighbor->dst_addr.address),
|
||||
neighbor->section->line,
|
||||
dup->section->line, dup->section->line);
|
||||
/* neighbor_detach() will drop the neighbor from neighbors_by_section. */
|
||||
|
@ -728,7 +728,7 @@ int network_drop_invalid_neighbors(Network *network) {
|
|||
}
|
||||
|
||||
|
||||
int config_parse_neighbor_address(
|
||||
int config_parse_neighbor_section(
|
||||
const char *unit,
|
||||
const char *filename,
|
||||
unsigned line,
|
||||
|
@ -740,76 +740,26 @@ int config_parse_neighbor_address(
|
|||
void *data,
|
||||
void *userdata) {
|
||||
|
||||
_cleanup_(neighbor_unref_or_set_invalidp) Neighbor *n = NULL;
|
||||
static const ConfigSectionParser table[_NEIGHBOR_CONF_PARSER_MAX] = {
|
||||
[NEIGHBOR_DESTINATION_ADDRESS] = { .parser = config_parse_in_addr_data, .ltype = 0, .offset = offsetof(Neighbor, dst_addr), },
|
||||
[NEIGHBOR_LINK_LAYER_ADDRESS] = { .parser = config_parse_hw_addr, .ltype = 0, .offset = offsetof(Neighbor, ll_addr), },
|
||||
};
|
||||
|
||||
_cleanup_(neighbor_unref_or_set_invalidp) Neighbor *neighbor = NULL;
|
||||
Network *network = ASSERT_PTR(userdata);
|
||||
int r;
|
||||
|
||||
assert(filename);
|
||||
assert(section);
|
||||
assert(lvalue);
|
||||
assert(rvalue);
|
||||
|
||||
r = neighbor_new_static(network, filename, section_line, &n);
|
||||
r = neighbor_new_static(network, filename, section_line, &neighbor);
|
||||
if (r < 0)
|
||||
return log_oom();
|
||||
|
||||
if (isempty(rvalue)) {
|
||||
n->family = AF_UNSPEC;
|
||||
n->in_addr = IN_ADDR_NULL;
|
||||
TAKE_PTR(n);
|
||||
return 0;
|
||||
}
|
||||
r = config_section_parse(table, ELEMENTSOF(table),
|
||||
unit, filename, line, section, section_line, lvalue, ltype, rvalue, neighbor);
|
||||
if (r <= 0) /* 0 means non-critical error, but the section will be ignored. */
|
||||
return r;
|
||||
|
||||
r = in_addr_from_string_auto(rvalue, &n->family, &n->in_addr);
|
||||
if (r < 0) {
|
||||
log_syntax(unit, LOG_WARNING, filename, line, r,
|
||||
"Neighbor Address is invalid, ignoring assignment: %s", rvalue);
|
||||
return 0;
|
||||
}
|
||||
|
||||
TAKE_PTR(n);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int config_parse_neighbor_lladdr(
|
||||
const char *unit,
|
||||
const char *filename,
|
||||
unsigned line,
|
||||
const char *section,
|
||||
unsigned section_line,
|
||||
const char *lvalue,
|
||||
int ltype,
|
||||
const char *rvalue,
|
||||
void *data,
|
||||
void *userdata) {
|
||||
|
||||
_cleanup_(neighbor_unref_or_set_invalidp) Neighbor *n = NULL;
|
||||
Network *network = ASSERT_PTR(userdata);
|
||||
int r;
|
||||
|
||||
assert(filename);
|
||||
assert(section);
|
||||
assert(lvalue);
|
||||
assert(rvalue);
|
||||
|
||||
r = neighbor_new_static(network, filename, section_line, &n);
|
||||
if (r < 0)
|
||||
return log_oom();
|
||||
|
||||
if (isempty(rvalue)) {
|
||||
n->ll_addr = HW_ADDR_NULL;
|
||||
TAKE_PTR(n);
|
||||
return 0;
|
||||
}
|
||||
|
||||
r = parse_hw_addr(rvalue, &n->ll_addr);
|
||||
if (r < 0) {
|
||||
log_syntax(unit, LOG_WARNING, filename, line, r,
|
||||
"Neighbor %s= is invalid, ignoring assignment: %s",
|
||||
lvalue, rvalue);
|
||||
return 0;
|
||||
}
|
||||
|
||||
TAKE_PTR(n);
|
||||
TAKE_PTR(neighbor);
|
||||
return 0;
|
||||
}
|
||||
|
|
|
@ -23,8 +23,7 @@ typedef struct Neighbor {
|
|||
|
||||
unsigned n_ref;
|
||||
|
||||
int family;
|
||||
union in_addr_union in_addr;
|
||||
struct in_addr_data dst_addr;
|
||||
struct hw_addr_data ll_addr;
|
||||
} Neighbor;
|
||||
|
||||
|
@ -46,5 +45,11 @@ int manager_rtnl_process_neighbor(sd_netlink *rtnl, sd_netlink_message *message,
|
|||
|
||||
DEFINE_NETWORK_CONFIG_STATE_FUNCTIONS(Neighbor, neighbor);
|
||||
|
||||
CONFIG_PARSER_PROTOTYPE(config_parse_neighbor_address);
|
||||
CONFIG_PARSER_PROTOTYPE(config_parse_neighbor_lladdr);
|
||||
typedef enum NeighborConfParserType {
|
||||
NEIGHBOR_DESTINATION_ADDRESS,
|
||||
NEIGHBOR_LINK_LAYER_ADDRESS,
|
||||
_NEIGHBOR_CONF_PARSER_MAX,
|
||||
_NEIGHBOR_CONF_PARSER_INVALID = -EINVAL,
|
||||
} NeighborConfParserType;
|
||||
|
||||
CONFIG_PARSER_PROTOTYPE(config_parse_neighbor_section);
|
||||
|
|
|
@ -173,9 +173,9 @@ Address.NetLabel, config_parse_address_section,
|
|||
Address.NFTSet, config_parse_address_section, ADDRESS_NFT_SET, 0
|
||||
IPv6AddressLabel.Prefix, config_parse_ipv6_address_label_section, IPV6_ADDRESS_LABEL_PREFIX, 0
|
||||
IPv6AddressLabel.Label, config_parse_ipv6_address_label_section, IPV6_ADDRESS_LABEL, 0
|
||||
Neighbor.Address, config_parse_neighbor_address, 0, 0
|
||||
Neighbor.LinkLayerAddress, config_parse_neighbor_lladdr, 0, 0
|
||||
Neighbor.MACAddress, config_parse_neighbor_lladdr, 0, 0 /* deprecated */
|
||||
Neighbor.Address, config_parse_neighbor_section, NEIGHBOR_DESTINATION_ADDRESS, 0
|
||||
Neighbor.LinkLayerAddress, config_parse_neighbor_section, NEIGHBOR_LINK_LAYER_ADDRESS, 0
|
||||
Neighbor.MACAddress, config_parse_neighbor_section, NEIGHBOR_LINK_LAYER_ADDRESS, 0 /* deprecated */
|
||||
RoutingPolicyRule.TypeOfService, config_parse_routing_policy_rule, ROUTING_POLICY_RULE_TOS, 0
|
||||
RoutingPolicyRule.Priority, config_parse_routing_policy_rule, ROUTING_POLICY_RULE_PRIORITY, 0
|
||||
RoutingPolicyRule.GoTo, config_parse_routing_policy_rule, ROUTING_POLICY_RULE_GOTO, 0
|
||||
|
@ -219,12 +219,12 @@ Route.QuickAck, config_parse_route_metric_boolean,
|
|||
Route.TCPCongestionControlAlgorithm, config_parse_route_metric_tcp_congestion, RTAX_CC_ALGO, 0
|
||||
Route.FastOpenNoCookie, config_parse_route_metric_boolean, RTAX_FASTOPEN_NO_COOKIE, 0
|
||||
Route.TTLPropagate, config_parse_warn_compat, DISABLED_LEGACY, 0
|
||||
NextHop.Id, config_parse_nexthop_id, 0, 0
|
||||
NextHop.Gateway, config_parse_nexthop_gateway, 0, 0
|
||||
NextHop.Family, config_parse_nexthop_family, 0, 0
|
||||
NextHop.OnLink, config_parse_nexthop_onlink, 0, 0
|
||||
NextHop.Blackhole, config_parse_nexthop_blackhole, 0, 0
|
||||
NextHop.Group, config_parse_nexthop_group, 0, 0
|
||||
NextHop.Id, config_parse_nexthop_section, NEXTHOP_ID, 0
|
||||
NextHop.Gateway, config_parse_nexthop_section, NEXTHOP_GATEWAY, 0
|
||||
NextHop.Family, config_parse_nexthop_section, NEXTHOP_FAMILY, 0
|
||||
NextHop.OnLink, config_parse_nexthop_section, NEXTHOP_ONLINK, 0
|
||||
NextHop.Blackhole, config_parse_nexthop_section, NEXTHOP_BLACKHOLE, 0
|
||||
NextHop.Group, config_parse_nexthop_section, NEXTHOP_GROUP, 0
|
||||
DHCPv4.RequestAddress, config_parse_in_addr_non_null, AF_INET, offsetof(Network, dhcp_request_address)
|
||||
DHCPv4.ClientIdentifier, config_parse_dhcp_client_identifier, 0, offsetof(Network, dhcp_client_identifier)
|
||||
DHCPv4.UseDNS, config_parse_tristate, 0, offsetof(Network, dhcp_use_dns)
|
||||
|
|
|
@ -590,6 +590,7 @@ int network_load_one(Manager *manager, OrderedHashmap **networks, const char *fi
|
|||
return log_warning_errno(r, "%s: Failed to store configuration into hashmap: %m", filename);
|
||||
|
||||
TAKE_PTR(network);
|
||||
log_syntax(/* unit = */ NULL, LOG_DEBUG, filename, /* config_line = */ 0, /* error = */ 0, "Successfully loaded.");
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -853,7 +854,7 @@ bool network_has_static_ipv6_configurations(Network *network) {
|
|||
return true;
|
||||
|
||||
ORDERED_HASHMAP_FOREACH(neighbor, network->neighbors_by_section)
|
||||
if (neighbor->family == AF_INET6)
|
||||
if (neighbor->dst_addr.family == AF_INET6)
|
||||
return true;
|
||||
|
||||
if (!hashmap_isempty(network->address_labels_by_section))
|
||||
|
|
|
@ -236,7 +236,7 @@ static int nexthop_compare_full(const NextHop *a, const NextHop *b) {
|
|||
return r;
|
||||
|
||||
if (IN_SET(a->family, AF_INET, AF_INET6)) {
|
||||
r = memcmp(&a->gw, &b->gw, FAMILY_ADDRESS_SIZE(a->family));
|
||||
r = memcmp(&a->gw.address, &b->gw.address, FAMILY_ADDRESS_SIZE(a->family));
|
||||
if (r != 0)
|
||||
return r;
|
||||
}
|
||||
|
@ -481,7 +481,7 @@ static void log_nexthop_debug(const NextHop *nexthop, const char *str, Manager *
|
|||
log_link_debug(link, "%s %s nexthop (%s): id: %"PRIu32", gw: %s, blackhole: %s, group: %s, flags: %s",
|
||||
str, strna(network_config_source_to_string(nexthop->source)), strna(state),
|
||||
nexthop->id,
|
||||
IN_ADDR_TO_STRING(nexthop->family, &nexthop->gw),
|
||||
IN_ADDR_TO_STRING(nexthop->family, &nexthop->gw.address),
|
||||
yes_no(nexthop->blackhole), strna(group), strna(flags));
|
||||
}
|
||||
|
||||
|
@ -627,8 +627,8 @@ static int nexthop_configure(NextHop *nexthop, Link *link, Request *req) {
|
|||
if (r < 0)
|
||||
return r;
|
||||
|
||||
if (in_addr_is_set(nexthop->family, &nexthop->gw)) {
|
||||
r = netlink_message_append_in_addr_union(m, NHA_GATEWAY, nexthop->family, &nexthop->gw);
|
||||
if (in_addr_is_set(nexthop->family, &nexthop->gw.address)) {
|
||||
r = netlink_message_append_in_addr_union(m, NHA_GATEWAY, nexthop->family, &nexthop->gw.address);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
|
@ -722,7 +722,7 @@ static bool nexthop_is_ready_to_configure(Link *link, const NextHop *nexthop) {
|
|||
return r;
|
||||
}
|
||||
|
||||
return gateway_is_ready(link, FLAGS_SET(nexthop->flags, RTNH_F_ONLINK), nexthop->family, &nexthop->gw);
|
||||
return gateway_is_ready(link, FLAGS_SET(nexthop->flags, RTNH_F_ONLINK), nexthop->family, &nexthop->gw.address);
|
||||
}
|
||||
|
||||
static int nexthop_process_request(Request *req, Link *link, NextHop *nexthop) {
|
||||
|
@ -1093,9 +1093,9 @@ int manager_rtnl_process_nexthop(sd_netlink *rtnl, sd_netlink_message *message,
|
|||
(void) nexthop_update_group(nexthop, message);
|
||||
|
||||
if (nexthop->family != AF_UNSPEC) {
|
||||
r = netlink_message_read_in_addr_union(message, NHA_GATEWAY, nexthop->family, &nexthop->gw);
|
||||
r = netlink_message_read_in_addr_union(message, NHA_GATEWAY, nexthop->family, &nexthop->gw.address);
|
||||
if (r == -ENODATA)
|
||||
nexthop->gw = IN_ADDR_NULL;
|
||||
nexthop->gw.address = IN_ADDR_NULL;
|
||||
else if (r < 0)
|
||||
log_debug_errno(r, "rtnl: could not get NHA_GATEWAY attribute, ignoring: %m");
|
||||
}
|
||||
|
@ -1129,66 +1129,60 @@ int manager_rtnl_process_nexthop(sd_netlink *rtnl, sd_netlink_message *message,
|
|||
return 1;
|
||||
}
|
||||
|
||||
#define log_nexthop_section(nexthop, fmt, ...) \
|
||||
({ \
|
||||
const NextHop *_nexthop = (nexthop); \
|
||||
log_section_warning_errno( \
|
||||
_nexthop ? _nexthop->section : NULL, \
|
||||
SYNTHETIC_ERRNO(EINVAL), \
|
||||
fmt " Ignoring [NextHop] section.", \
|
||||
##__VA_ARGS__); \
|
||||
})
|
||||
|
||||
static int nexthop_section_verify(NextHop *nh) {
|
||||
if (section_is_invalid(nh->section))
|
||||
return -EINVAL;
|
||||
|
||||
if (!nh->network->manager->manage_foreign_nexthops && nh->id == 0)
|
||||
return log_warning_errno(SYNTHETIC_ERRNO(EINVAL),
|
||||
"%s: [NextHop] section without specifying Id= is not supported "
|
||||
"if ManageForeignNextHops=no is set in networkd.conf. "
|
||||
"Ignoring [NextHop] section from line %u.",
|
||||
nh->section->filename, nh->section->line);
|
||||
return log_nexthop_section(nh, "Nexthop without specifying Id= is not supported if ManageForeignNextHops=no is set in networkd.conf.");
|
||||
|
||||
if (nh->family == AF_UNSPEC)
|
||||
nh->family = nh->gw.family;
|
||||
else if (nh->gw.family != AF_UNSPEC && nh->gw.family != nh->family)
|
||||
return log_nexthop_section(nh, "Family= and Gateway= settings for nexthop contradict each other.");
|
||||
|
||||
assert(nh->gw.family == nh->family || nh->gw.family == AF_UNSPEC);
|
||||
|
||||
if (!hashmap_isempty(nh->group)) {
|
||||
if (in_addr_is_set(nh->family, &nh->gw))
|
||||
return log_warning_errno(SYNTHETIC_ERRNO(EINVAL),
|
||||
"%s: nexthop group cannot have gateway address. "
|
||||
"Ignoring [NextHop] section from line %u.",
|
||||
nh->section->filename, nh->section->line);
|
||||
if (in_addr_is_set(nh->family, &nh->gw.address))
|
||||
return log_nexthop_section(nh, "Nexthop group cannot have gateway address.");
|
||||
|
||||
if (nh->family != AF_UNSPEC)
|
||||
return log_warning_errno(SYNTHETIC_ERRNO(EINVAL),
|
||||
"%s: nexthop group cannot have Family= setting. "
|
||||
"Ignoring [NextHop] section from line %u.",
|
||||
nh->section->filename, nh->section->line);
|
||||
return log_nexthop_section(nh, "Nexthop group cannot have Family= setting.");
|
||||
|
||||
if (nh->blackhole)
|
||||
return log_warning_errno(SYNTHETIC_ERRNO(EINVAL),
|
||||
"%s: nexthop group cannot be a blackhole. "
|
||||
"Ignoring [NextHop] section from line %u.",
|
||||
nh->section->filename, nh->section->line);
|
||||
return log_nexthop_section(nh, "Nexthop group cannot be a blackhole.");
|
||||
|
||||
if (nh->onlink > 0)
|
||||
return log_warning_errno(SYNTHETIC_ERRNO(EINVAL),
|
||||
"%s: nexthop group cannot have on-link flag. "
|
||||
"Ignoring [NextHop] section from line %u.",
|
||||
nh->section->filename, nh->section->line);
|
||||
return log_nexthop_section(nh, "Nexthop group cannot have on-link flag.");
|
||||
|
||||
} else if (nh->family == AF_UNSPEC)
|
||||
/* When neither Family=, Gateway=, nor Group= is specified, assume IPv4. */
|
||||
nh->family = AF_INET;
|
||||
|
||||
if (nh->blackhole) {
|
||||
if (in_addr_is_set(nh->family, &nh->gw))
|
||||
return log_warning_errno(SYNTHETIC_ERRNO(EINVAL),
|
||||
"%s: blackhole nexthop cannot have gateway address. "
|
||||
"Ignoring [NextHop] section from line %u.",
|
||||
nh->section->filename, nh->section->line);
|
||||
if (in_addr_is_set(nh->family, &nh->gw.address))
|
||||
return log_nexthop_section(nh, "Blackhole nexthop cannot have gateway address.");
|
||||
|
||||
if (nh->onlink > 0)
|
||||
return log_warning_errno(SYNTHETIC_ERRNO(EINVAL),
|
||||
"%s: blackhole nexthop cannot have on-link flag. "
|
||||
"Ignoring [NextHop] section from line %u.",
|
||||
nh->section->filename, nh->section->line);
|
||||
return log_nexthop_section(nh, "Blackhole nexthop cannot have on-link flag.");
|
||||
}
|
||||
|
||||
if (nh->onlink < 0 && in_addr_is_set(nh->family, &nh->gw) &&
|
||||
if (nh->onlink < 0 && in_addr_is_set(nh->family, &nh->gw.address) &&
|
||||
ordered_hashmap_isempty(nh->network->addresses_by_section)) {
|
||||
/* If no address is configured, in most cases the gateway cannot be reachable.
|
||||
* TODO: we may need to improve the condition above. */
|
||||
log_warning("%s: Gateway= without static address configured. "
|
||||
"Enabling OnLink= option.",
|
||||
nh->section->filename);
|
||||
log_section_warning(nh->section, "Nexthop with Gateway= specified, but no static address configured. Enabling OnLink= option.");
|
||||
nh->onlink = true;
|
||||
}
|
||||
|
||||
|
@ -1262,7 +1256,7 @@ int manager_build_nexthop_ids(Manager *manager) {
|
|||
return 0;
|
||||
}
|
||||
|
||||
int config_parse_nexthop_id(
|
||||
static int config_parse_nexthop_family(
|
||||
const char *unit,
|
||||
const char *filename,
|
||||
unsigned line,
|
||||
|
@ -1274,261 +1268,38 @@ int config_parse_nexthop_id(
|
|||
void *data,
|
||||
void *userdata) {
|
||||
|
||||
_cleanup_(nexthop_unref_or_set_invalidp) NextHop *n = NULL;
|
||||
Network *network = userdata;
|
||||
uint32_t id;
|
||||
int *family = ASSERT_PTR(data);
|
||||
|
||||
if (isempty(rvalue))
|
||||
*family = AF_UNSPEC;
|
||||
else if (streq(rvalue, "ipv4"))
|
||||
*family = AF_INET;
|
||||
else if (streq(rvalue, "ipv6"))
|
||||
*family = AF_INET6;
|
||||
else
|
||||
return log_syntax_parse_error(unit, filename, line, 0, lvalue, rvalue);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int config_parse_nexthop_group(
|
||||
const char *unit,
|
||||
const char *filename,
|
||||
unsigned line,
|
||||
const char *section,
|
||||
unsigned section_line,
|
||||
const char *lvalue,
|
||||
int ltype,
|
||||
const char *rvalue,
|
||||
void *data,
|
||||
void *userdata) {
|
||||
|
||||
Hashmap **group = ASSERT_PTR(data);
|
||||
int r;
|
||||
|
||||
assert(filename);
|
||||
assert(section);
|
||||
assert(lvalue);
|
||||
assert(rvalue);
|
||||
assert(data);
|
||||
|
||||
r = nexthop_new_static(network, filename, section_line, &n);
|
||||
if (r < 0)
|
||||
return log_oom();
|
||||
|
||||
if (isempty(rvalue)) {
|
||||
n->id = 0;
|
||||
TAKE_PTR(n);
|
||||
return 0;
|
||||
}
|
||||
|
||||
r = safe_atou32(rvalue, &id);
|
||||
if (r < 0) {
|
||||
log_syntax(unit, LOG_WARNING, filename, line, r,
|
||||
"Could not parse nexthop id \"%s\", ignoring assignment: %m", rvalue);
|
||||
return 0;
|
||||
}
|
||||
if (id == 0) {
|
||||
log_syntax(unit, LOG_WARNING, filename, line, 0,
|
||||
"Invalid nexthop id \"%s\", ignoring assignment: %m", rvalue);
|
||||
return 0;
|
||||
}
|
||||
|
||||
n->id = id;
|
||||
TAKE_PTR(n);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int config_parse_nexthop_gateway(
|
||||
const char *unit,
|
||||
const char *filename,
|
||||
unsigned line,
|
||||
const char *section,
|
||||
unsigned section_line,
|
||||
const char *lvalue,
|
||||
int ltype,
|
||||
const char *rvalue,
|
||||
void *data,
|
||||
void *userdata) {
|
||||
|
||||
_cleanup_(nexthop_unref_or_set_invalidp) NextHop *n = NULL;
|
||||
Network *network = userdata;
|
||||
int r;
|
||||
|
||||
assert(filename);
|
||||
assert(section);
|
||||
assert(lvalue);
|
||||
assert(rvalue);
|
||||
assert(data);
|
||||
|
||||
r = nexthop_new_static(network, filename, section_line, &n);
|
||||
if (r < 0)
|
||||
return log_oom();
|
||||
|
||||
if (isempty(rvalue)) {
|
||||
n->family = AF_UNSPEC;
|
||||
n->gw = IN_ADDR_NULL;
|
||||
|
||||
TAKE_PTR(n);
|
||||
return 0;
|
||||
}
|
||||
|
||||
r = in_addr_from_string_auto(rvalue, &n->family, &n->gw);
|
||||
if (r < 0) {
|
||||
log_syntax(unit, LOG_WARNING, filename, line, r,
|
||||
"Invalid %s='%s', ignoring assignment: %m", lvalue, rvalue);
|
||||
return 0;
|
||||
}
|
||||
|
||||
TAKE_PTR(n);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int config_parse_nexthop_family(
|
||||
const char *unit,
|
||||
const char *filename,
|
||||
unsigned line,
|
||||
const char *section,
|
||||
unsigned section_line,
|
||||
const char *lvalue,
|
||||
int ltype,
|
||||
const char *rvalue,
|
||||
void *data,
|
||||
void *userdata) {
|
||||
|
||||
_cleanup_(nexthop_unref_or_set_invalidp) NextHop *n = NULL;
|
||||
Network *network = userdata;
|
||||
AddressFamily a;
|
||||
int r;
|
||||
|
||||
assert(filename);
|
||||
assert(section);
|
||||
assert(lvalue);
|
||||
assert(rvalue);
|
||||
assert(data);
|
||||
|
||||
r = nexthop_new_static(network, filename, section_line, &n);
|
||||
if (r < 0)
|
||||
return log_oom();
|
||||
|
||||
if (isempty(rvalue) &&
|
||||
!in_addr_is_set(n->family, &n->gw)) {
|
||||
/* Accept an empty string only when Gateway= is null or not specified. */
|
||||
n->family = AF_UNSPEC;
|
||||
TAKE_PTR(n);
|
||||
return 0;
|
||||
}
|
||||
|
||||
a = nexthop_address_family_from_string(rvalue);
|
||||
if (a < 0) {
|
||||
log_syntax(unit, LOG_WARNING, filename, line, 0,
|
||||
"Invalid %s='%s', ignoring assignment: %m", lvalue, rvalue);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (in_addr_is_set(n->family, &n->gw) &&
|
||||
((a == ADDRESS_FAMILY_IPV4 && n->family == AF_INET6) ||
|
||||
(a == ADDRESS_FAMILY_IPV6 && n->family == AF_INET))) {
|
||||
log_syntax(unit, LOG_WARNING, filename, line, 0,
|
||||
"Specified family '%s' conflicts with the family of the previously specified Gateway=, "
|
||||
"ignoring assignment.", rvalue);
|
||||
return 0;
|
||||
}
|
||||
|
||||
switch (a) {
|
||||
case ADDRESS_FAMILY_IPV4:
|
||||
n->family = AF_INET;
|
||||
break;
|
||||
case ADDRESS_FAMILY_IPV6:
|
||||
n->family = AF_INET6;
|
||||
break;
|
||||
default:
|
||||
assert_not_reached();
|
||||
}
|
||||
|
||||
TAKE_PTR(n);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int config_parse_nexthop_onlink(
|
||||
const char *unit,
|
||||
const char *filename,
|
||||
unsigned line,
|
||||
const char *section,
|
||||
unsigned section_line,
|
||||
const char *lvalue,
|
||||
int ltype,
|
||||
const char *rvalue,
|
||||
void *data,
|
||||
void *userdata) {
|
||||
|
||||
_cleanup_(nexthop_unref_or_set_invalidp) NextHop *n = NULL;
|
||||
Network *network = userdata;
|
||||
int r;
|
||||
|
||||
assert(filename);
|
||||
assert(section);
|
||||
assert(lvalue);
|
||||
assert(rvalue);
|
||||
assert(data);
|
||||
|
||||
r = nexthop_new_static(network, filename, section_line, &n);
|
||||
if (r < 0)
|
||||
return log_oom();
|
||||
|
||||
r = parse_tristate(rvalue, &n->onlink);
|
||||
if (r < 0) {
|
||||
log_syntax(unit, LOG_WARNING, filename, line, r,
|
||||
"Failed to parse %s=, ignoring assignment: %s", lvalue, rvalue);
|
||||
return 0;
|
||||
}
|
||||
|
||||
TAKE_PTR(n);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int config_parse_nexthop_blackhole(
|
||||
const char *unit,
|
||||
const char *filename,
|
||||
unsigned line,
|
||||
const char *section,
|
||||
unsigned section_line,
|
||||
const char *lvalue,
|
||||
int ltype,
|
||||
const char *rvalue,
|
||||
void *data,
|
||||
void *userdata) {
|
||||
|
||||
_cleanup_(nexthop_unref_or_set_invalidp) NextHop *n = NULL;
|
||||
Network *network = userdata;
|
||||
int r;
|
||||
|
||||
assert(filename);
|
||||
assert(section);
|
||||
assert(lvalue);
|
||||
assert(rvalue);
|
||||
assert(data);
|
||||
|
||||
r = nexthop_new_static(network, filename, section_line, &n);
|
||||
if (r < 0)
|
||||
return log_oom();
|
||||
|
||||
r = parse_boolean(rvalue);
|
||||
if (r < 0) {
|
||||
log_syntax(unit, LOG_WARNING, filename, line, r,
|
||||
"Failed to parse %s=, ignoring assignment: %s", lvalue, rvalue);
|
||||
return 0;
|
||||
}
|
||||
|
||||
n->blackhole = r;
|
||||
|
||||
TAKE_PTR(n);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int config_parse_nexthop_group(
|
||||
const char *unit,
|
||||
const char *filename,
|
||||
unsigned line,
|
||||
const char *section,
|
||||
unsigned section_line,
|
||||
const char *lvalue,
|
||||
int ltype,
|
||||
const char *rvalue,
|
||||
void *data,
|
||||
void *userdata) {
|
||||
|
||||
_cleanup_(nexthop_unref_or_set_invalidp) NextHop *n = NULL;
|
||||
Network *network = userdata;
|
||||
int r;
|
||||
|
||||
assert(filename);
|
||||
assert(section);
|
||||
assert(lvalue);
|
||||
assert(rvalue);
|
||||
assert(data);
|
||||
|
||||
r = nexthop_new_static(network, filename, section_line, &n);
|
||||
if (r < 0)
|
||||
return log_oom();
|
||||
|
||||
if (isempty(rvalue)) {
|
||||
n->group = hashmap_free_free(n->group);
|
||||
TAKE_PTR(n);
|
||||
return 0;
|
||||
*group = hashmap_free_free(*group);
|
||||
return 1;
|
||||
}
|
||||
|
||||
for (const char *p = rvalue;;) {
|
||||
|
@ -1538,15 +1309,10 @@ int config_parse_nexthop_group(
|
|||
char *sep;
|
||||
|
||||
r = extract_first_word(&p, &word, NULL, 0);
|
||||
if (r == -ENOMEM)
|
||||
return log_oom();
|
||||
if (r < 0) {
|
||||
log_syntax(unit, LOG_WARNING, filename, line, r,
|
||||
"Invalid %s=, ignoring assignment: %s", lvalue, rvalue);
|
||||
return 0;
|
||||
}
|
||||
if (r < 0)
|
||||
return log_syntax_parse_error(unit, filename, line, r, lvalue, rvalue);
|
||||
if (r == 0)
|
||||
break;
|
||||
return 1;
|
||||
|
||||
nhg = new0(struct nexthop_grp, 1);
|
||||
if (!nhg)
|
||||
|
@ -1586,7 +1352,7 @@ int config_parse_nexthop_group(
|
|||
continue;
|
||||
}
|
||||
|
||||
r = hashmap_ensure_put(&n->group, NULL, UINT32_TO_PTR(nhg->id), nhg);
|
||||
r = hashmap_ensure_put(group, NULL, UINT32_TO_PTR(nhg->id), nhg);
|
||||
if (r == -ENOMEM)
|
||||
return log_oom();
|
||||
if (r == -EEXIST) {
|
||||
|
@ -1598,7 +1364,44 @@ int config_parse_nexthop_group(
|
|||
assert(r > 0);
|
||||
TAKE_PTR(nhg);
|
||||
}
|
||||
}
|
||||
|
||||
TAKE_PTR(n);
|
||||
int config_parse_nexthop_section(
|
||||
const char *unit,
|
||||
const char *filename,
|
||||
unsigned line,
|
||||
const char *section,
|
||||
unsigned section_line,
|
||||
const char *lvalue,
|
||||
int ltype,
|
||||
const char *rvalue,
|
||||
void *data,
|
||||
void *userdata) {
|
||||
|
||||
static const ConfigSectionParser table[_NEXTHOP_CONF_PARSER_MAX] = {
|
||||
[NEXTHOP_ID] = { .parser = config_parse_uint32, .ltype = 0, .offset = offsetof(NextHop, id), },
|
||||
[NEXTHOP_GATEWAY] = { .parser = config_parse_in_addr_data, .ltype = 0, .offset = offsetof(NextHop, gw), },
|
||||
[NEXTHOP_FAMILY] = { .parser = config_parse_nexthop_family, .ltype = 0, .offset = offsetof(NextHop, family), },
|
||||
[NEXTHOP_ONLINK] = { .parser = config_parse_tristate, .ltype = 0, .offset = offsetof(NextHop, onlink), },
|
||||
[NEXTHOP_BLACKHOLE] = { .parser = config_parse_bool, .ltype = 0, .offset = offsetof(NextHop, blackhole), },
|
||||
[NEXTHOP_GROUP] = { .parser = config_parse_nexthop_group, .ltype = 0, .offset = offsetof(NextHop, group), },
|
||||
};
|
||||
|
||||
_cleanup_(nexthop_unref_or_set_invalidp) NextHop *nexthop = NULL;
|
||||
Network *network = ASSERT_PTR(userdata);
|
||||
int r;
|
||||
|
||||
assert(filename);
|
||||
|
||||
r = nexthop_new_static(network, filename, section_line, &nexthop);
|
||||
if (r < 0)
|
||||
return log_oom();
|
||||
|
||||
r = config_section_parse(table, ELEMENTSOF(table),
|
||||
unit, filename, line, section, section_line, lvalue, ltype, rvalue, nexthop);
|
||||
if (r <= 0) /* 0 means non-critical error, but the section will be ignored. */
|
||||
return r;
|
||||
|
||||
TAKE_PTR(nexthop);
|
||||
return 0;
|
||||
}
|
||||
|
|
|
@ -36,7 +36,7 @@ typedef struct NextHop {
|
|||
Hashmap *group; /* NHA_GROUP */
|
||||
bool blackhole; /* NHA_BLACKHOLE */
|
||||
int ifindex; /* NHA_OIF */
|
||||
union in_addr_union gw; /* NHA_GATEWAY */
|
||||
struct in_addr_data gw; /* NHA_GATEWAY, gw.family is only used by conf parser. */
|
||||
|
||||
/* Only used in conf parser and nexthop_section_verify(). */
|
||||
int onlink;
|
||||
|
@ -71,9 +71,15 @@ int manager_build_nexthop_ids(Manager *manager);
|
|||
|
||||
DEFINE_NETWORK_CONFIG_STATE_FUNCTIONS(NextHop, nexthop);
|
||||
|
||||
CONFIG_PARSER_PROTOTYPE(config_parse_nexthop_id);
|
||||
CONFIG_PARSER_PROTOTYPE(config_parse_nexthop_gateway);
|
||||
CONFIG_PARSER_PROTOTYPE(config_parse_nexthop_family);
|
||||
CONFIG_PARSER_PROTOTYPE(config_parse_nexthop_onlink);
|
||||
CONFIG_PARSER_PROTOTYPE(config_parse_nexthop_blackhole);
|
||||
CONFIG_PARSER_PROTOTYPE(config_parse_nexthop_group);
|
||||
typedef enum NextHopConfParserType {
|
||||
NEXTHOP_ID,
|
||||
NEXTHOP_GATEWAY,
|
||||
NEXTHOP_FAMILY,
|
||||
NEXTHOP_ONLINK,
|
||||
NEXTHOP_BLACKHOLE,
|
||||
NEXTHOP_GROUP,
|
||||
_NEXTHOP_CONF_PARSER_MAX,
|
||||
_NEXTHOP_CONF_PARSER_INVALID = -EINVAL,
|
||||
} NextHopConfParserType;
|
||||
|
||||
CONFIG_PARSER_PROTOTYPE(config_parse_nexthop_section);
|
||||
|
|
|
@ -34,13 +34,7 @@ static struct sysctl_monitor_bpf* sysctl_monitor_bpf_free(struct sysctl_monitor_
|
|||
return NULL;
|
||||
}
|
||||
|
||||
static struct ring_buffer* rb_free(struct ring_buffer *rb) {
|
||||
sym_ring_buffer__free(rb);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
DEFINE_TRIVIAL_CLEANUP_FUNC(struct sysctl_monitor_bpf *, sysctl_monitor_bpf_free);
|
||||
DEFINE_TRIVIAL_CLEANUP_FUNC(struct ring_buffer *, rb_free);
|
||||
|
||||
static int sysctl_event_handler(void *ctx, void *data, size_t data_sz) {
|
||||
struct sysctl_write_event *we = ASSERT_PTR(data);
|
||||
|
@ -99,10 +93,10 @@ static int on_ringbuf_io(sd_event_source *s, int fd, uint32_t revents, void *use
|
|||
int sysctl_add_monitor(Manager *manager) {
|
||||
_cleanup_(sysctl_monitor_bpf_freep) struct sysctl_monitor_bpf *obj = NULL;
|
||||
_cleanup_(bpf_link_freep) struct bpf_link *sysctl_link = NULL;
|
||||
_cleanup_(rb_freep) struct ring_buffer *sysctl_buffer = NULL;
|
||||
_cleanup_close_ int cgroup_fd = -EBADF, rootcg = -EBADF;
|
||||
_cleanup_(bpf_ring_buffer_freep) struct ring_buffer *sysctl_buffer = NULL;
|
||||
_cleanup_close_ int cgroup_fd = -EBADF, root_cgroup_fd = -EBADF;
|
||||
_cleanup_free_ char *cgroup = NULL;
|
||||
int idx = 0, r;
|
||||
int idx = 0, r, fd;
|
||||
|
||||
assert(manager);
|
||||
|
||||
|
@ -116,9 +110,9 @@ int sysctl_add_monitor(Manager *manager) {
|
|||
if (r < 0)
|
||||
return log_warning_errno(r, "Failed to get cgroup path, ignoring: %m.");
|
||||
|
||||
rootcg = cg_path_open(SYSTEMD_CGROUP_CONTROLLER, "/");
|
||||
if (rootcg < 0)
|
||||
return log_warning_errno(rootcg, "Failed to open cgroup, ignoring: %m.");
|
||||
root_cgroup_fd = cg_path_open(SYSTEMD_CGROUP_CONTROLLER, "/");
|
||||
if (root_cgroup_fd < 0)
|
||||
return log_warning_errno(root_cgroup_fd, "Failed to open cgroup, ignoring: %m.");
|
||||
|
||||
obj = sysctl_monitor_bpf__open_and_load();
|
||||
if (!obj) {
|
||||
|
@ -133,21 +127,27 @@ int sysctl_add_monitor(Manager *manager) {
|
|||
if (sym_bpf_map_update_elem(sym_bpf_map__fd(obj->maps.cgroup_map), &idx, &cgroup_fd, BPF_ANY))
|
||||
return log_warning_errno(errno, "Failed to update cgroup map: %m");
|
||||
|
||||
sysctl_link = sym_bpf_program__attach_cgroup(obj->progs.sysctl_monitor, rootcg);
|
||||
sysctl_link = sym_bpf_program__attach_cgroup(obj->progs.sysctl_monitor, root_cgroup_fd);
|
||||
r = bpf_get_error_translated(sysctl_link);
|
||||
if (r < 0) {
|
||||
log_info_errno(r, "Unable to attach sysctl monitor BPF program to cgroup, ignoring: %m.");
|
||||
return 0;
|
||||
}
|
||||
|
||||
sysctl_buffer = sym_ring_buffer__new(
|
||||
sym_bpf_map__fd(obj->maps.written_sysctls),
|
||||
sysctl_event_handler, &manager->sysctl_shadow, NULL);
|
||||
fd = sym_bpf_map__fd(obj->maps.written_sysctls);
|
||||
if (fd < 0)
|
||||
return log_warning_errno(fd, "Failed to get fd of sysctl maps: %m");
|
||||
|
||||
sysctl_buffer = sym_ring_buffer__new(fd, sysctl_event_handler, &manager->sysctl_shadow, NULL);
|
||||
if (!sysctl_buffer)
|
||||
return log_warning_errno(errno, "Failed to create ring buffer: %m");
|
||||
|
||||
fd = sym_ring_buffer__epoll_fd(sysctl_buffer);
|
||||
if (fd < 0)
|
||||
return log_warning_errno(fd, "Failed to get poll fd of ring buffer: %m");
|
||||
|
||||
r = sd_event_add_io(manager->event, &manager->sysctl_event_source,
|
||||
sym_ring_buffer__epoll_fd(sysctl_buffer), EPOLLIN, on_ringbuf_io, sysctl_buffer);
|
||||
fd, EPOLLIN, on_ringbuf_io, sysctl_buffer);
|
||||
if (r < 0)
|
||||
return log_warning_errno(r, "Failed to watch sysctl event ringbuffer: %m");
|
||||
|
||||
|
@ -163,23 +163,11 @@ void sysctl_remove_monitor(Manager *manager) {
|
|||
assert(manager);
|
||||
|
||||
manager->sysctl_event_source = sd_event_source_disable_unref(manager->sysctl_event_source);
|
||||
|
||||
if (manager->sysctl_buffer) {
|
||||
sym_ring_buffer__free(manager->sysctl_buffer);
|
||||
manager->sysctl_buffer = NULL;
|
||||
}
|
||||
|
||||
if (manager->sysctl_link) {
|
||||
sym_bpf_link__destroy(manager->sysctl_link);
|
||||
manager->sysctl_link = NULL;
|
||||
}
|
||||
|
||||
if (manager->sysctl_skel) {
|
||||
sysctl_monitor_bpf__destroy(manager->sysctl_skel);
|
||||
manager->sysctl_skel = NULL;
|
||||
}
|
||||
|
||||
manager->sysctl_buffer = bpf_ring_buffer_free(manager->sysctl_buffer);
|
||||
manager->sysctl_link = bpf_link_free(manager->sysctl_link);
|
||||
manager->sysctl_skel = sysctl_monitor_bpf_free(manager->sysctl_skel);
|
||||
manager->cgroup_fd = safe_close(manager->cgroup_fd);
|
||||
manager->sysctl_shadow = hashmap_free(manager->sysctl_shadow);
|
||||
}
|
||||
|
||||
int sysctl_clear_link_shadows(Link *link) {
|
||||
|
@ -222,13 +210,13 @@ static void manager_set_ip_forwarding(Manager *manager, int family) {
|
|||
return; /* keep */
|
||||
|
||||
/* First, set the default value. */
|
||||
r = sysctl_write_ip_property_boolean(family, "default", "forwarding", t, &manager->sysctl_shadow);
|
||||
r = sysctl_write_ip_property_boolean(family, "default", "forwarding", t, manager_get_sysctl_shadow(manager));
|
||||
if (r < 0)
|
||||
log_warning_errno(r, "Failed to %s the default %s forwarding: %m",
|
||||
enable_disable(t), af_to_ipv4_ipv6(family));
|
||||
|
||||
/* Then, set the value to all interfaces. */
|
||||
r = sysctl_write_ip_property_boolean(family, "all", "forwarding", t, &manager->sysctl_shadow);
|
||||
r = sysctl_write_ip_property_boolean(family, "all", "forwarding", t, manager_get_sysctl_shadow(manager));
|
||||
if (r < 0)
|
||||
log_warning_errno(r, "Failed to %s %s forwarding for all interfaces: %m",
|
||||
enable_disable(t), af_to_ipv4_ipv6(family));
|
||||
|
@ -273,7 +261,7 @@ static int link_update_ipv6_sysctl(Link *link) {
|
|||
if (!link_ipv6_enabled(link))
|
||||
return 0;
|
||||
|
||||
return sysctl_write_ip_property_boolean(AF_INET6, link->ifname, "disable_ipv6", false, &link->manager->sysctl_shadow);
|
||||
return sysctl_write_ip_property_boolean(AF_INET6, link->ifname, "disable_ipv6", false, manager_get_sysctl_shadow(link->manager));
|
||||
}
|
||||
|
||||
static int link_set_proxy_arp(Link *link) {
|
||||
|
@ -286,7 +274,7 @@ static int link_set_proxy_arp(Link *link) {
|
|||
if (link->network->proxy_arp < 0)
|
||||
return 0;
|
||||
|
||||
return sysctl_write_ip_property_boolean(AF_INET, link->ifname, "proxy_arp", link->network->proxy_arp > 0, &link->manager->sysctl_shadow);
|
||||
return sysctl_write_ip_property_boolean(AF_INET, link->ifname, "proxy_arp", link->network->proxy_arp > 0, manager_get_sysctl_shadow(link->manager));
|
||||
}
|
||||
|
||||
static int link_set_proxy_arp_pvlan(Link *link) {
|
||||
|
@ -299,7 +287,7 @@ static int link_set_proxy_arp_pvlan(Link *link) {
|
|||
if (link->network->proxy_arp_pvlan < 0)
|
||||
return 0;
|
||||
|
||||
return sysctl_write_ip_property_boolean(AF_INET, link->ifname, "proxy_arp_pvlan", link->network->proxy_arp_pvlan > 0, &link->manager->sysctl_shadow);
|
||||
return sysctl_write_ip_property_boolean(AF_INET, link->ifname, "proxy_arp_pvlan", link->network->proxy_arp_pvlan > 0, manager_get_sysctl_shadow(link->manager));
|
||||
}
|
||||
|
||||
int link_get_ip_forwarding(Link *link, int family) {
|
||||
|
@ -341,7 +329,7 @@ static int link_set_ip_forwarding_impl(Link *link, int family) {
|
|||
if (t < 0)
|
||||
return 0; /* keep */
|
||||
|
||||
r = sysctl_write_ip_property_boolean(family, link->ifname, "forwarding", t, &link->manager->sysctl_shadow);
|
||||
r = sysctl_write_ip_property_boolean(family, link->ifname, "forwarding", t, manager_get_sysctl_shadow(link->manager));
|
||||
if (r < 0)
|
||||
return log_link_warning_errno(link, r, "Failed to %s %s forwarding, ignoring: %m",
|
||||
enable_disable(t), af_to_ipv4_ipv6(family));
|
||||
|
@ -418,7 +406,7 @@ static int link_set_ipv4_rp_filter(Link *link) {
|
|||
if (link->network->ipv4_rp_filter < 0)
|
||||
return 0;
|
||||
|
||||
return sysctl_write_ip_property_int(AF_INET, link->ifname, "rp_filter", link->network->ipv4_rp_filter, &link->manager->sysctl_shadow);
|
||||
return sysctl_write_ip_property_int(AF_INET, link->ifname, "rp_filter", link->network->ipv4_rp_filter, manager_get_sysctl_shadow(link->manager));
|
||||
}
|
||||
|
||||
static int link_set_ipv6_privacy_extensions(Link *link) {
|
||||
|
@ -438,7 +426,7 @@ static int link_set_ipv6_privacy_extensions(Link *link) {
|
|||
if (val == IPV6_PRIVACY_EXTENSIONS_KERNEL)
|
||||
return 0;
|
||||
|
||||
return sysctl_write_ip_property_int(AF_INET6, link->ifname, "use_tempaddr", (int) val, &link->manager->sysctl_shadow);
|
||||
return sysctl_write_ip_property_int(AF_INET6, link->ifname, "use_tempaddr", (int) val, manager_get_sysctl_shadow(link->manager));
|
||||
}
|
||||
|
||||
static int link_set_ipv6_accept_ra(Link *link) {
|
||||
|
@ -448,7 +436,7 @@ static int link_set_ipv6_accept_ra(Link *link) {
|
|||
if (!link_is_configured_for_family(link, AF_INET6))
|
||||
return 0;
|
||||
|
||||
return sysctl_write_ip_property(AF_INET6, link->ifname, "accept_ra", "0", &link->manager->sysctl_shadow);
|
||||
return sysctl_write_ip_property(AF_INET6, link->ifname, "accept_ra", "0", manager_get_sysctl_shadow(link->manager));
|
||||
}
|
||||
|
||||
static int link_set_ipv6_dad_transmits(Link *link) {
|
||||
|
@ -461,7 +449,7 @@ static int link_set_ipv6_dad_transmits(Link *link) {
|
|||
if (link->network->ipv6_dad_transmits < 0)
|
||||
return 0;
|
||||
|
||||
return sysctl_write_ip_property_int(AF_INET6, link->ifname, "dad_transmits", link->network->ipv6_dad_transmits, &link->manager->sysctl_shadow);
|
||||
return sysctl_write_ip_property_int(AF_INET6, link->ifname, "dad_transmits", link->network->ipv6_dad_transmits, manager_get_sysctl_shadow(link->manager));
|
||||
}
|
||||
|
||||
static int link_set_ipv6_hop_limit(Link *link) {
|
||||
|
@ -474,7 +462,7 @@ static int link_set_ipv6_hop_limit(Link *link) {
|
|||
if (link->network->ipv6_hop_limit <= 0)
|
||||
return 0;
|
||||
|
||||
return sysctl_write_ip_property_int(AF_INET6, link->ifname, "hop_limit", link->network->ipv6_hop_limit, &link->manager->sysctl_shadow);
|
||||
return sysctl_write_ip_property_int(AF_INET6, link->ifname, "hop_limit", link->network->ipv6_hop_limit, manager_get_sysctl_shadow(link->manager));
|
||||
}
|
||||
|
||||
static int link_set_ipv6_retransmission_time(Link *link) {
|
||||
|
@ -493,7 +481,7 @@ static int link_set_ipv6_retransmission_time(Link *link) {
|
|||
if (retrans_time_ms <= 0 || retrans_time_ms > UINT32_MAX)
|
||||
return 0;
|
||||
|
||||
return sysctl_write_ip_neighbor_property_uint32(AF_INET6, link->ifname, "retrans_time_ms", retrans_time_ms, &link->manager->sysctl_shadow);
|
||||
return sysctl_write_ip_neighbor_property_uint32(AF_INET6, link->ifname, "retrans_time_ms", retrans_time_ms, manager_get_sysctl_shadow(link->manager));
|
||||
}
|
||||
|
||||
static int link_set_ipv6_proxy_ndp(Link *link) {
|
||||
|
@ -510,7 +498,7 @@ static int link_set_ipv6_proxy_ndp(Link *link) {
|
|||
else
|
||||
v = !set_isempty(link->network->ipv6_proxy_ndp_addresses);
|
||||
|
||||
return sysctl_write_ip_property_boolean(AF_INET6, link->ifname, "proxy_ndp", v, &link->manager->sysctl_shadow);
|
||||
return sysctl_write_ip_property_boolean(AF_INET6, link->ifname, "proxy_ndp", v, manager_get_sysctl_shadow(link->manager));
|
||||
}
|
||||
|
||||
int link_set_ipv6_mtu(Link *link, int log_level) {
|
||||
|
@ -538,7 +526,7 @@ int link_set_ipv6_mtu(Link *link, int log_level) {
|
|||
mtu = link->mtu;
|
||||
}
|
||||
|
||||
return sysctl_write_ip_property_uint32(AF_INET6, link->ifname, "mtu", mtu, &link->manager->sysctl_shadow);
|
||||
return sysctl_write_ip_property_uint32(AF_INET6, link->ifname, "mtu", mtu, manager_get_sysctl_shadow(link->manager));
|
||||
}
|
||||
|
||||
static int link_set_ipv4_accept_local(Link *link) {
|
||||
|
@ -551,7 +539,7 @@ static int link_set_ipv4_accept_local(Link *link) {
|
|||
if (link->network->ipv4_accept_local < 0)
|
||||
return 0;
|
||||
|
||||
return sysctl_write_ip_property_boolean(AF_INET, link->ifname, "accept_local", link->network->ipv4_accept_local > 0, &link->manager->sysctl_shadow);
|
||||
return sysctl_write_ip_property_boolean(AF_INET, link->ifname, "accept_local", link->network->ipv4_accept_local > 0, manager_get_sysctl_shadow(link->manager));
|
||||
}
|
||||
|
||||
static int link_set_ipv4_route_localnet(Link *link) {
|
||||
|
@ -564,7 +552,7 @@ static int link_set_ipv4_route_localnet(Link *link) {
|
|||
if (link->network->ipv4_route_localnet < 0)
|
||||
return 0;
|
||||
|
||||
return sysctl_write_ip_property_boolean(AF_INET, link->ifname, "route_localnet", link->network->ipv4_route_localnet > 0, &link->manager->sysctl_shadow);
|
||||
return sysctl_write_ip_property_boolean(AF_INET, link->ifname, "route_localnet", link->network->ipv4_route_localnet > 0, manager_get_sysctl_shadow(link->manager));
|
||||
}
|
||||
|
||||
static int link_set_ipv4_promote_secondaries(Link *link) {
|
||||
|
@ -579,7 +567,7 @@ static int link_set_ipv4_promote_secondaries(Link *link) {
|
|||
* otherwise. The way systemd-networkd works is that the new IP of a lease is added as a
|
||||
* secondary IP and when the primary one expires it relies on the kernel to promote the
|
||||
* secondary IP. See also https://github.com/systemd/systemd/issues/7163 */
|
||||
return sysctl_write_ip_property_boolean(AF_INET, link->ifname, "promote_secondaries", true, &link->manager->sysctl_shadow);
|
||||
return sysctl_write_ip_property_boolean(AF_INET, link->ifname, "promote_secondaries", true, manager_get_sysctl_shadow(link->manager));
|
||||
}
|
||||
|
||||
int link_set_sysctl(Link *link) {
|
||||
|
|
|
@ -1742,7 +1742,7 @@ static int oci_seccomp_args(const char *name, sd_json_variant *v, sd_json_dispat
|
|||
int r;
|
||||
|
||||
JSON_VARIANT_ARRAY_FOREACH(e, v) {
|
||||
static const struct sd_json_dispatch_field table[] = {
|
||||
static const sd_json_dispatch_field table[] = {
|
||||
{ "index", SD_JSON_VARIANT_UNSIGNED, sd_json_dispatch_uint32, offsetof(struct scmp_arg_cmp, arg), SD_JSON_MANDATORY },
|
||||
{ "value", SD_JSON_VARIANT_UNSIGNED, sd_json_dispatch_uint64, offsetof(struct scmp_arg_cmp, datum_a), SD_JSON_MANDATORY },
|
||||
{ "valueTwo", SD_JSON_VARIANT_UNSIGNED, sd_json_dispatch_uint64, offsetof(struct scmp_arg_cmp, datum_b), 0 },
|
||||
|
|
|
@ -5602,7 +5602,7 @@ static int run_container(
|
|||
}
|
||||
|
||||
/* Note: we do not use SD_EVENT_SIGNAL_PROCMASK or sd_event_set_signal_exit(), since we want the
|
||||
* signals to be block continously, even if we destroy the event loop and allocate a new one on
|
||||
* signals to be block continuously, even if we destroy the event loop and allocate a new one on
|
||||
* container reboot. */
|
||||
|
||||
if (arg_kill_signal > 0) {
|
||||
|
|
|
@ -6,6 +6,9 @@
|
|||
#include "sd-daemon.h"
|
||||
|
||||
#include "bpf-dlopen.h"
|
||||
#if HAVE_VMLINUX_H
|
||||
#include "bpf-link.h"
|
||||
#endif
|
||||
#include "build-path.h"
|
||||
#include "common-signal.h"
|
||||
#include "env-util.h"
|
||||
|
@ -141,8 +144,7 @@ Manager* manager_free(Manager *m) {
|
|||
|
||||
#if HAVE_VMLINUX_H
|
||||
sd_event_source_disable_unref(m->userns_restrict_bpf_ring_buffer_event_source);
|
||||
if (m->userns_restrict_bpf_ring_buffer)
|
||||
sym_ring_buffer__free(m->userns_restrict_bpf_ring_buffer);
|
||||
bpf_ring_buffer_free(m->userns_restrict_bpf_ring_buffer);
|
||||
userns_restrict_bpf_free(m->userns_restrict_bpf);
|
||||
#endif
|
||||
|
||||
|
|
|
@ -304,19 +304,6 @@ static SubvolumeFlags subvolume_flags_from_string(const char *s) {
|
|||
return flags;
|
||||
}
|
||||
|
||||
static char* subvolume_flags_to_string(SubvolumeFlags flags) {
|
||||
const char *l[CONST_LOG2U(_SUBVOLUME_FLAGS_MASK + 1) + 1]; /* one string per known flag at most */
|
||||
size_t m = 0;
|
||||
|
||||
if (FLAGS_SET(flags, SUBVOLUME_RO))
|
||||
l[m++] = "ro";
|
||||
|
||||
assert(m < ELEMENTSOF(l));
|
||||
l[m] = NULL;
|
||||
|
||||
return strv_join((char**) l, ",");
|
||||
}
|
||||
|
||||
typedef struct Subvolume {
|
||||
char *path;
|
||||
SubvolumeFlags flags;
|
||||
|
@ -404,6 +391,10 @@ typedef struct Partition {
|
|||
|
||||
PartitionEncryptedVolume *encrypted_volume;
|
||||
|
||||
char *supplement_for_name;
|
||||
struct Partition *supplement_for, *supplement_target_for;
|
||||
struct Partition *suppressing;
|
||||
|
||||
struct Partition *siblings[_VERITY_MODE_MAX];
|
||||
|
||||
LIST_FIELDS(struct Partition, partitions);
|
||||
|
@ -411,6 +402,7 @@ typedef struct Partition {
|
|||
|
||||
#define PARTITION_IS_FOREIGN(p) (!(p)->definition_path)
|
||||
#define PARTITION_EXISTS(p) (!!(p)->current_partition)
|
||||
#define PARTITION_SUPPRESSED(p) ((p)->supplement_for && (p)->supplement_for->suppressing == (p))
|
||||
|
||||
struct FreeArea {
|
||||
Partition *after;
|
||||
|
@ -520,6 +512,28 @@ static Partition *partition_new(void) {
|
|||
return p;
|
||||
}
|
||||
|
||||
static void partition_unlink_supplement(Partition *p) {
|
||||
assert(p);
|
||||
|
||||
assert(!p->supplement_for || !p->supplement_target_for); /* Can't be both */
|
||||
|
||||
if (p->supplement_target_for) {
|
||||
assert(p->supplement_target_for->supplement_for == p);
|
||||
|
||||
p->supplement_target_for->supplement_for = NULL;
|
||||
}
|
||||
|
||||
if (p->supplement_for) {
|
||||
assert(p->supplement_for->supplement_target_for == p);
|
||||
assert(!p->supplement_for->suppressing || p->supplement_for->suppressing == p);
|
||||
|
||||
p->supplement_for->supplement_target_for = p->supplement_for->suppressing = NULL;
|
||||
}
|
||||
|
||||
p->supplement_for_name = mfree(p->supplement_for_name);
|
||||
p->supplement_target_for = p->supplement_for = p->suppressing = NULL;
|
||||
}
|
||||
|
||||
static Partition* partition_free(Partition *p) {
|
||||
if (!p)
|
||||
return NULL;
|
||||
|
@ -563,6 +577,8 @@ static Partition* partition_free(Partition *p) {
|
|||
|
||||
partition_encrypted_volume_free(p->encrypted_volume);
|
||||
|
||||
partition_unlink_supplement(p);
|
||||
|
||||
return mfree(p);
|
||||
}
|
||||
|
||||
|
@ -608,6 +624,8 @@ static void partition_foreignize(Partition *p) {
|
|||
p->n_mountpoints = 0;
|
||||
|
||||
p->encrypted_volume = partition_encrypted_volume_free(p->encrypted_volume);
|
||||
|
||||
partition_unlink_supplement(p);
|
||||
}
|
||||
|
||||
static bool partition_type_exclude(const GptPartitionType *type) {
|
||||
|
@ -740,6 +758,10 @@ static void partition_drop_or_foreignize(Partition *p) {
|
|||
|
||||
p->dropped = true;
|
||||
p->allocated_to_area = NULL;
|
||||
|
||||
/* If a supplement partition is dropped, we don't want to merge in its settings. */
|
||||
if (PARTITION_SUPPRESSED(p))
|
||||
p->supplement_for->suppressing = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -775,7 +797,7 @@ static bool context_drop_or_foreignize_one_priority(Context *context) {
|
|||
}
|
||||
|
||||
static uint64_t partition_min_size(const Context *context, const Partition *p) {
|
||||
uint64_t sz;
|
||||
uint64_t sz, override_min;
|
||||
|
||||
assert(context);
|
||||
assert(p);
|
||||
|
@ -817,11 +839,13 @@ static uint64_t partition_min_size(const Context *context, const Partition *p) {
|
|||
sz = d;
|
||||
}
|
||||
|
||||
return MAX(round_up_size(p->size_min != UINT64_MAX ? p->size_min : DEFAULT_MIN_SIZE, context->grain_size), sz);
|
||||
override_min = p->suppressing ? MAX(p->size_min, p->suppressing->size_min) : p->size_min;
|
||||
|
||||
return MAX(round_up_size(override_min != UINT64_MAX ? override_min : DEFAULT_MIN_SIZE, context->grain_size), sz);
|
||||
}
|
||||
|
||||
static uint64_t partition_max_size(const Context *context, const Partition *p) {
|
||||
uint64_t sm;
|
||||
uint64_t sm, override_max;
|
||||
|
||||
/* Calculate how large the partition may become at max. This is generally the configured maximum
|
||||
* size, except when it already exists and is larger than that. In that case it's the existing size,
|
||||
|
@ -839,10 +863,11 @@ static uint64_t partition_max_size(const Context *context, const Partition *p) {
|
|||
if (p->verity == VERITY_SIG)
|
||||
return VERITY_SIG_SIZE;
|
||||
|
||||
if (p->size_max == UINT64_MAX)
|
||||
override_max = p->suppressing ? MIN(p->size_max, p->suppressing->size_max) : p->size_max;
|
||||
if (override_max == UINT64_MAX)
|
||||
return UINT64_MAX;
|
||||
|
||||
sm = round_down_size(p->size_max, context->grain_size);
|
||||
sm = round_down_size(override_max, context->grain_size);
|
||||
|
||||
if (p->current_size != UINT64_MAX)
|
||||
sm = MAX(p->current_size, sm);
|
||||
|
@ -851,13 +876,17 @@ static uint64_t partition_max_size(const Context *context, const Partition *p) {
|
|||
}
|
||||
|
||||
static uint64_t partition_min_padding(const Partition *p) {
|
||||
uint64_t override_min;
|
||||
|
||||
assert(p);
|
||||
return p->padding_min != UINT64_MAX ? p->padding_min : 0;
|
||||
|
||||
override_min = p->suppressing ? MAX(p->padding_min, p->suppressing->padding_min) : p->padding_min;
|
||||
return override_min != UINT64_MAX ? override_min : 0;
|
||||
}
|
||||
|
||||
static uint64_t partition_max_padding(const Partition *p) {
|
||||
assert(p);
|
||||
return p->padding_max;
|
||||
return p->suppressing ? MIN(p->padding_max, p->suppressing->padding_max) : p->padding_max;
|
||||
}
|
||||
|
||||
static uint64_t partition_min_size_with_padding(Context *context, const Partition *p) {
|
||||
|
@ -977,14 +1006,22 @@ static bool context_allocate_partitions(Context *context, uint64_t *ret_largest_
|
|||
uint64_t required;
|
||||
FreeArea *a = NULL;
|
||||
|
||||
/* Skip partitions we already dropped or that already exist */
|
||||
if (p->dropped || PARTITION_EXISTS(p))
|
||||
if (p->dropped || PARTITION_IS_FOREIGN(p) || PARTITION_SUPPRESSED(p))
|
||||
continue;
|
||||
|
||||
/* How much do we need to fit? */
|
||||
required = partition_min_size_with_padding(context, p);
|
||||
assert(required % context->grain_size == 0);
|
||||
|
||||
/* For existing partitions, we should verify that they'll actually fit */
|
||||
if (PARTITION_EXISTS(p)) {
|
||||
if (p->current_size + p->current_padding < required)
|
||||
return false; /* 😢 We won't be able to grow to the required min size! */
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
/* For new partitions, see if there's a free area big enough */
|
||||
for (size_t i = 0; i < context->n_free_areas; i++) {
|
||||
a = context->free_areas[i];
|
||||
|
||||
|
@ -1007,6 +1044,57 @@ static bool context_allocate_partitions(Context *context, uint64_t *ret_largest_
|
|||
return true;
|
||||
}
|
||||
|
||||
static bool context_unmerge_and_allocate_partitions(Context *context) {
|
||||
assert(context);
|
||||
|
||||
/* This should only be called after plain context_allocate_partitions fails. This algorithm will
|
||||
* try, in the order that minimizes the number of created supplement partitions, all combinations of
|
||||
* un-suppressing supplement partitions until it finds one that works. */
|
||||
|
||||
/* First, let's try to un-suppress just one supplement partition and see if that gets us anywhere */
|
||||
LIST_FOREACH(partitions, p, context->partitions) {
|
||||
Partition *unsuppressed;
|
||||
|
||||
if (!p->suppressing)
|
||||
continue;
|
||||
|
||||
unsuppressed = TAKE_PTR(p->suppressing);
|
||||
|
||||
if (context_allocate_partitions(context, NULL))
|
||||
return true;
|
||||
|
||||
p->suppressing = unsuppressed;
|
||||
}
|
||||
|
||||
/* Looks like not. So we have to un-suppress at least two partitions. We can do this recursively */
|
||||
LIST_FOREACH(partitions, p, context->partitions) {
|
||||
Partition *unsuppressed;
|
||||
|
||||
if (!p->suppressing)
|
||||
continue;
|
||||
|
||||
unsuppressed = TAKE_PTR(p->suppressing);
|
||||
|
||||
if (context_unmerge_and_allocate_partitions(context))
|
||||
return true;
|
||||
|
||||
p->suppressing = unsuppressed;
|
||||
}
|
||||
|
||||
/* No combination of un-suppressed supplements made it possible to fit the partitions */
|
||||
return false;
|
||||
}
|
||||
|
||||
static uint32_t partition_weight(const Partition *p) {
|
||||
assert(p);
|
||||
return p->suppressing ? p->suppressing->weight : p->weight;
|
||||
}
|
||||
|
||||
static uint32_t partition_padding_weight(const Partition *p) {
|
||||
assert(p);
|
||||
return p->suppressing ? p->suppressing->padding_weight : p->padding_weight;
|
||||
}
|
||||
|
||||
static int context_sum_weights(Context *context, FreeArea *a, uint64_t *ret) {
|
||||
uint64_t weight_sum = 0;
|
||||
|
||||
|
@ -1020,13 +1108,11 @@ static int context_sum_weights(Context *context, FreeArea *a, uint64_t *ret) {
|
|||
if (p->padding_area != a && p->allocated_to_area != a)
|
||||
continue;
|
||||
|
||||
if (p->weight > UINT64_MAX - weight_sum)
|
||||
if (!INC_SAFE(&weight_sum, partition_weight(p)))
|
||||
goto overflow_sum;
|
||||
weight_sum += p->weight;
|
||||
|
||||
if (p->padding_weight > UINT64_MAX - weight_sum)
|
||||
if (!INC_SAFE(&weight_sum, partition_padding_weight(p)))
|
||||
goto overflow_sum;
|
||||
weight_sum += p->padding_weight;
|
||||
}
|
||||
|
||||
*ret = weight_sum;
|
||||
|
@ -1091,7 +1177,6 @@ static bool context_grow_partitions_phase(
|
|||
* get any additional room from the left-overs. Similar, if two partitions have the same weight they
|
||||
* should get the same space if possible, even if one has a smaller minimum size than the other. */
|
||||
LIST_FOREACH(partitions, p, context->partitions) {
|
||||
|
||||
/* Look only at partitions associated with this free area, i.e. immediately
|
||||
* preceding it, or allocated into it */
|
||||
if (p->allocated_to_area != a && p->padding_area != a)
|
||||
|
@ -1099,11 +1184,14 @@ static bool context_grow_partitions_phase(
|
|||
|
||||
if (p->new_size == UINT64_MAX) {
|
||||
uint64_t share, rsz, xsz;
|
||||
uint32_t weight;
|
||||
bool charge = false;
|
||||
|
||||
weight = partition_weight(p);
|
||||
|
||||
/* Calculate how much this space this partition needs if everyone would get
|
||||
* the weight based share */
|
||||
share = scale_by_weight(*span, p->weight, *weight_sum);
|
||||
share = scale_by_weight(*span, weight, *weight_sum);
|
||||
|
||||
rsz = partition_min_size(context, p);
|
||||
xsz = partition_max_size(context, p);
|
||||
|
@ -1143,15 +1231,18 @@ static bool context_grow_partitions_phase(
|
|||
|
||||
if (charge) {
|
||||
*span = charge_size(context, *span, p->new_size);
|
||||
*weight_sum = charge_weight(*weight_sum, p->weight);
|
||||
*weight_sum = charge_weight(*weight_sum, weight);
|
||||
}
|
||||
}
|
||||
|
||||
if (p->new_padding == UINT64_MAX) {
|
||||
uint64_t share, rsz, xsz;
|
||||
uint32_t padding_weight;
|
||||
bool charge = false;
|
||||
|
||||
share = scale_by_weight(*span, p->padding_weight, *weight_sum);
|
||||
padding_weight = partition_padding_weight(p);
|
||||
|
||||
share = scale_by_weight(*span, padding_weight, *weight_sum);
|
||||
|
||||
rsz = partition_min_padding(p);
|
||||
xsz = partition_max_padding(p);
|
||||
|
@ -1170,7 +1261,7 @@ static bool context_grow_partitions_phase(
|
|||
|
||||
if (charge) {
|
||||
*span = charge_size(context, *span, p->new_padding);
|
||||
*weight_sum = charge_weight(*weight_sum, p->padding_weight);
|
||||
*weight_sum = charge_weight(*weight_sum, padding_weight);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1638,8 +1729,9 @@ static int config_parse_exclude_files(
|
|||
const char *rvalue,
|
||||
void *data,
|
||||
void *userdata) {
|
||||
_cleanup_free_ char *resolved = NULL;
|
||||
|
||||
char ***exclude_files = ASSERT_PTR(data);
|
||||
const char *p = ASSERT_PTR(rvalue);
|
||||
int r;
|
||||
|
||||
if (isempty(rvalue)) {
|
||||
|
@ -1647,20 +1739,34 @@ static int config_parse_exclude_files(
|
|||
return 0;
|
||||
}
|
||||
|
||||
r = specifier_printf(rvalue, PATH_MAX-1, system_and_tmp_specifier_table, arg_root, NULL, &resolved);
|
||||
if (r < 0) {
|
||||
log_syntax(unit, LOG_WARNING, filename, line, r,
|
||||
"Failed to expand specifiers in ExcludeFiles= path, ignoring: %s", rvalue);
|
||||
return 0;
|
||||
for (;;) {
|
||||
_cleanup_free_ char *word = NULL, *resolved = NULL;
|
||||
|
||||
r = extract_first_word(&p, &word, NULL, EXTRACT_UNQUOTE);
|
||||
if (r == -ENOMEM)
|
||||
return log_oom();
|
||||
if (r < 0) {
|
||||
log_syntax(unit, LOG_WARNING, filename, line, r, "Invalid syntax, ignoring: %s", p);
|
||||
return 0;
|
||||
}
|
||||
if (r == 0)
|
||||
return 0;
|
||||
|
||||
r = specifier_printf(word, PATH_MAX-1, system_and_tmp_specifier_table, arg_root, NULL, &resolved);
|
||||
if (r < 0) {
|
||||
log_syntax(unit, LOG_WARNING, filename, line, r,
|
||||
"Failed to expand specifiers in %s path, ignoring: %s", lvalue, word);
|
||||
return 0;
|
||||
}
|
||||
|
||||
r = path_simplify_and_warn(resolved, PATH_CHECK_ABSOLUTE|PATH_KEEP_TRAILING_SLASH, unit, filename, line, lvalue);
|
||||
if (r < 0)
|
||||
return 0;
|
||||
|
||||
if (strv_consume(exclude_files, TAKE_PTR(resolved)) < 0)
|
||||
return log_oom();
|
||||
}
|
||||
|
||||
r = path_simplify_and_warn(resolved, PATH_CHECK_ABSOLUTE|PATH_KEEP_TRAILING_SLASH, unit, filename, line, lvalue);
|
||||
if (r < 0)
|
||||
return 0;
|
||||
|
||||
if (strv_consume(exclude_files, TAKE_PTR(resolved)) < 0)
|
||||
return log_oom();
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -2155,7 +2261,9 @@ static int partition_finalize_fstype(Partition *p, const char *path) {
|
|||
|
||||
static bool partition_needs_populate(const Partition *p) {
|
||||
assert(p);
|
||||
return !strv_isempty(p->copy_files) || !strv_isempty(p->make_directories) || !strv_isempty(p->make_symlinks);
|
||||
assert(!p->supplement_for || !p->suppressing); /* Avoid infinite recursion */
|
||||
return !strv_isempty(p->copy_files) || !strv_isempty(p->make_directories) || !strv_isempty(p->make_symlinks) ||
|
||||
(p->suppressing && partition_needs_populate(p->suppressing));
|
||||
}
|
||||
|
||||
static int partition_read_definition(Partition *p, const char *path, const char *const *conf_file_dirs) {
|
||||
|
@ -2196,6 +2304,7 @@ static int partition_read_definition(Partition *p, const char *path, const char
|
|||
{ "Partition", "EncryptedVolume", config_parse_encrypted_volume, 0, p },
|
||||
{ "Partition", "Compression", config_parse_string, CONFIG_PARSE_STRING_SAFE_AND_ASCII, &p->compression },
|
||||
{ "Partition", "CompressionLevel", config_parse_string, CONFIG_PARSE_STRING_SAFE_AND_ASCII, &p->compression_level },
|
||||
{ "Partition", "SupplementFor", config_parse_string, 0, &p->supplement_for_name },
|
||||
{}
|
||||
};
|
||||
_cleanup_free_ char *filename = NULL;
|
||||
|
@ -2316,10 +2425,30 @@ static int partition_read_definition(Partition *p, const char *path, const char
|
|||
"SizeMinBytes=/SizeMaxBytes= cannot be used with Verity=%s.",
|
||||
verity_mode_to_string(p->verity));
|
||||
|
||||
if (!ordered_hashmap_isempty(p->subvolumes) && arg_offline > 0)
|
||||
return log_syntax(NULL, LOG_ERR, path, 1, SYNTHETIC_ERRNO(EOPNOTSUPP),
|
||||
"Subvolumes= cannot be used with --offline=yes.");
|
||||
|
||||
if (p->default_subvolume && arg_offline > 0)
|
||||
return log_syntax(NULL, LOG_ERR, path, 1, SYNTHETIC_ERRNO(EOPNOTSUPP),
|
||||
"DefaultSubvolume= cannot be used with --offline=yes.");
|
||||
|
||||
if (p->default_subvolume && !ordered_hashmap_contains(p->subvolumes, p->default_subvolume))
|
||||
return log_syntax(NULL, LOG_ERR, path, 1, SYNTHETIC_ERRNO(EINVAL),
|
||||
"DefaultSubvolume= must be one of the paths in Subvolumes=.");
|
||||
|
||||
if (p->supplement_for_name) {
|
||||
if (!filename_part_is_valid(p->supplement_for_name))
|
||||
return log_syntax(NULL, LOG_ERR, path, 1, SYNTHETIC_ERRNO(EINVAL),
|
||||
"SupplementFor= is an invalid filename: %s",
|
||||
p->supplement_for_name);
|
||||
|
||||
if (p->copy_blocks_path || p->copy_blocks_auto || p->encrypt != ENCRYPT_OFF ||
|
||||
p->verity != VERITY_OFF)
|
||||
return log_syntax(NULL, LOG_ERR, path, 1, SYNTHETIC_ERRNO(EINVAL),
|
||||
"SupplementFor= cannot be combined with CopyBlocks=/Encrypt=/Verity=");
|
||||
}
|
||||
|
||||
/* Verity partitions are read only, let's imply the RO flag hence, unless explicitly configured otherwise. */
|
||||
if ((IN_SET(p->type.designator,
|
||||
PARTITION_ROOT_VERITY,
|
||||
|
@ -2626,6 +2755,58 @@ static int context_copy_from(Context *context) {
|
|||
return 0;
|
||||
}
|
||||
|
||||
static bool check_cross_def_ranges_valid(uint64_t a_min, uint64_t a_max, uint64_t b_min, uint64_t b_max) {
|
||||
if (a_min == UINT64_MAX && b_min == UINT64_MAX)
|
||||
return true;
|
||||
|
||||
if (a_max == UINT64_MAX && b_max == UINT64_MAX)
|
||||
return true;
|
||||
|
||||
return MAX(a_min != UINT64_MAX ? a_min : 0, b_min != UINT64_MAX ? b_min : 0) <= MIN(a_max, b_max);
|
||||
}
|
||||
|
||||
static int supplement_find_target(const Context *context, const Partition *supplement, Partition **ret) {
|
||||
int r;
|
||||
|
||||
assert(context);
|
||||
assert(supplement);
|
||||
assert(ret);
|
||||
|
||||
LIST_FOREACH(partitions, p, context->partitions) {
|
||||
_cleanup_free_ char *filename = NULL;
|
||||
|
||||
if (p == supplement)
|
||||
continue;
|
||||
|
||||
r = path_extract_filename(p->definition_path, &filename);
|
||||
if (r < 0)
|
||||
return log_error_errno(r,
|
||||
"Failed to extract filename from path '%s': %m",
|
||||
p->definition_path);
|
||||
|
||||
*ASSERT_PTR(endswith(filename, ".conf")) = 0; /* Remove the file extension */
|
||||
|
||||
if (!streq(supplement->supplement_for_name, filename))
|
||||
continue;
|
||||
|
||||
if (p->supplement_for_name)
|
||||
return log_syntax(NULL, LOG_ERR, supplement->definition_path, 1, SYNTHETIC_ERRNO(EINVAL),
|
||||
"SupplementFor= target is itself configured as a supplement.");
|
||||
|
||||
if (p->suppressing)
|
||||
return log_syntax(NULL, LOG_ERR, supplement->definition_path, 1, SYNTHETIC_ERRNO(EINVAL),
|
||||
"SupplementFor= target already has a supplement defined: %s",
|
||||
p->suppressing->definition_path);
|
||||
|
||||
*ret = p;
|
||||
return 0;
|
||||
}
|
||||
|
||||
return log_syntax(NULL, LOG_ERR, supplement->definition_path, 1, SYNTHETIC_ERRNO(EINVAL),
|
||||
"Couldn't find target partition for SupplementFor=%s",
|
||||
supplement->supplement_for_name);
|
||||
}
|
||||
|
||||
static int context_read_definitions(Context *context) {
|
||||
_cleanup_strv_free_ char **files = NULL;
|
||||
Partition *last = LIST_FIND_TAIL(partitions, context->partitions);
|
||||
|
@ -2717,7 +2898,33 @@ static int context_read_definitions(Context *context) {
|
|||
if (dp->minimize == MINIMIZE_OFF && !(dp->copy_blocks_path || dp->copy_blocks_auto))
|
||||
return log_syntax(NULL, LOG_ERR, p->definition_path, 1, SYNTHETIC_ERRNO(EINVAL),
|
||||
"Minimize= set for verity hash partition but data partition does not set CopyBlocks= or Minimize=.");
|
||||
}
|
||||
|
||||
LIST_FOREACH(partitions, p, context->partitions) {
|
||||
Partition *tgt = NULL;
|
||||
|
||||
if (!p->supplement_for_name)
|
||||
continue;
|
||||
|
||||
r = supplement_find_target(context, p, &tgt);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
if (tgt->copy_blocks_path || tgt->copy_blocks_auto || tgt->encrypt != ENCRYPT_OFF ||
|
||||
tgt->verity != VERITY_OFF)
|
||||
return log_syntax(NULL, LOG_ERR, p->definition_path, 1, SYNTHETIC_ERRNO(EINVAL),
|
||||
"SupplementFor= target uses CopyBlocks=/Encrypt=/Verity=");
|
||||
|
||||
if (!check_cross_def_ranges_valid(p->size_min, p->size_max, tgt->size_min, tgt->size_max))
|
||||
return log_syntax(NULL, LOG_ERR, p->definition_path, 1, SYNTHETIC_ERRNO(EINVAL),
|
||||
"SizeMinBytes= larger than SizeMaxBytes= when merged with SupplementFor= target.");
|
||||
|
||||
if (!check_cross_def_ranges_valid(p->padding_min, p->padding_max, tgt->padding_min, tgt->padding_max))
|
||||
return log_syntax(NULL, LOG_ERR, p->definition_path, 1, SYNTHETIC_ERRNO(EINVAL),
|
||||
"PaddingMinBytes= larger than PaddingMaxBytes= when merged with SupplementFor= target.");
|
||||
|
||||
p->supplement_for = tgt;
|
||||
tgt->suppressing = tgt->supplement_target_for = p;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
@ -3101,6 +3308,10 @@ static int context_load_partition_table(Context *context) {
|
|||
}
|
||||
}
|
||||
|
||||
LIST_FOREACH(partitions, p, context->partitions)
|
||||
if (PARTITION_SUPPRESSED(p) && PARTITION_EXISTS(p))
|
||||
p->supplement_for->suppressing = NULL;
|
||||
|
||||
add_initial_free_area:
|
||||
nsectors = fdisk_get_nsectors(c);
|
||||
assert(nsectors <= UINT64_MAX/secsz);
|
||||
|
@ -3192,6 +3403,11 @@ static void context_unload_partition_table(Context *context) {
|
|||
|
||||
p->current_uuid = SD_ID128_NULL;
|
||||
p->current_label = mfree(p->current_label);
|
||||
|
||||
/* A supplement partition is only ever un-suppressed if the existing partition table prevented
|
||||
* us from suppressing it. So when unloading the partition table, we must re-suppress. */
|
||||
if (p->supplement_for)
|
||||
p->supplement_for->suppressing = p;
|
||||
}
|
||||
|
||||
context->start = UINT64_MAX;
|
||||
|
@ -4065,7 +4281,7 @@ static int prepare_temporary_file(Context *context, PartitionTarget *t, uint64_t
|
|||
|
||||
static bool loop_device_error_is_fatal(const Partition *p, int r) {
|
||||
assert(p);
|
||||
return arg_offline == 0 || (r != -ENOENT && !ERRNO_IS_PRIVILEGE(r));
|
||||
return arg_offline == 0 || (r != -ENOENT && !ERRNO_IS_PRIVILEGE(r)) || !ordered_hashmap_isempty(p->subvolumes) || p->default_subvolume;
|
||||
}
|
||||
|
||||
static int partition_target_prepare(
|
||||
|
@ -4969,6 +5185,31 @@ static int add_exclude_path(const char *path, Hashmap **denylist, DenyType type)
|
|||
return 0;
|
||||
}
|
||||
|
||||
static int shallow_join_strv(char ***ret, char **a, char **b) {
|
||||
_cleanup_free_ char **joined = NULL;
|
||||
char **iter;
|
||||
|
||||
assert(ret);
|
||||
|
||||
joined = new(char*, strv_length(a) + strv_length(b) + 1);
|
||||
if (!joined)
|
||||
return log_oom();
|
||||
|
||||
iter = joined;
|
||||
|
||||
STRV_FOREACH(i, a)
|
||||
*(iter++) = *i;
|
||||
|
||||
STRV_FOREACH(i, b)
|
||||
if (!strv_contains(joined, *i))
|
||||
*(iter++) = *i;
|
||||
|
||||
*iter = NULL;
|
||||
|
||||
*ret = TAKE_PTR(joined);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int make_copy_files_denylist(
|
||||
Context *context,
|
||||
const Partition *p,
|
||||
|
@ -4977,6 +5218,7 @@ static int make_copy_files_denylist(
|
|||
Hashmap **ret) {
|
||||
|
||||
_cleanup_hashmap_free_ Hashmap *denylist = NULL;
|
||||
_cleanup_free_ char **override_exclude_src = NULL, **override_exclude_tgt = NULL;
|
||||
int r;
|
||||
|
||||
assert(context);
|
||||
|
@ -4996,13 +5238,26 @@ static int make_copy_files_denylist(
|
|||
|
||||
/* Add the user configured excludes. */
|
||||
|
||||
STRV_FOREACH(e, p->exclude_files_source) {
|
||||
if (p->suppressing) {
|
||||
r = shallow_join_strv(&override_exclude_src,
|
||||
p->exclude_files_source,
|
||||
p->suppressing->exclude_files_source);
|
||||
if (r < 0)
|
||||
return r;
|
||||
r = shallow_join_strv(&override_exclude_tgt,
|
||||
p->exclude_files_target,
|
||||
p->suppressing->exclude_files_target);
|
||||
if (r < 0)
|
||||
return r;
|
||||
}
|
||||
|
||||
STRV_FOREACH(e, override_exclude_src ?: p->exclude_files_source) {
|
||||
r = add_exclude_path(*e, &denylist, endswith(*e, "/") ? DENY_CONTENTS : DENY_INODE);
|
||||
if (r < 0)
|
||||
return r;
|
||||
}
|
||||
|
||||
STRV_FOREACH(e, p->exclude_files_target) {
|
||||
STRV_FOREACH(e, override_exclude_tgt ?: p->exclude_files_target) {
|
||||
_cleanup_free_ char *path = NULL;
|
||||
|
||||
const char *s = path_startswith(*e, target);
|
||||
|
@ -5096,6 +5351,7 @@ static int add_subvolume_path(const char *path, Set **subvolumes) {
|
|||
static int make_subvolumes_strv(const Partition *p, char ***ret) {
|
||||
_cleanup_strv_free_ char **subvolumes = NULL;
|
||||
Subvolume *subvolume;
|
||||
int r;
|
||||
|
||||
assert(p);
|
||||
assert(ret);
|
||||
|
@ -5104,6 +5360,18 @@ static int make_subvolumes_strv(const Partition *p, char ***ret) {
|
|||
if (strv_extend(&subvolumes, subvolume->path) < 0)
|
||||
return log_oom();
|
||||
|
||||
if (p->suppressing) {
|
||||
_cleanup_strv_free_ char **suppressing = NULL;
|
||||
|
||||
r = make_subvolumes_strv(p->suppressing, &suppressing);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
r = strv_extend_strv(&subvolumes, suppressing, /* filter_duplicates= */ true);
|
||||
if (r < 0)
|
||||
return log_oom();
|
||||
}
|
||||
|
||||
*ret = TAKE_PTR(subvolumes);
|
||||
return 0;
|
||||
}
|
||||
|
@ -5114,18 +5382,22 @@ static int make_subvolumes_set(
|
|||
const char *target,
|
||||
Set **ret) {
|
||||
|
||||
_cleanup_strv_free_ char **paths = NULL;
|
||||
_cleanup_set_free_ Set *subvolumes = NULL;
|
||||
Subvolume *subvolume;
|
||||
int r;
|
||||
|
||||
assert(p);
|
||||
assert(target);
|
||||
assert(ret);
|
||||
|
||||
ORDERED_HASHMAP_FOREACH(subvolume, p->subvolumes) {
|
||||
r = make_subvolumes_strv(p, &paths);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
STRV_FOREACH(subvolume, paths) {
|
||||
_cleanup_free_ char *path = NULL;
|
||||
|
||||
const char *s = path_startswith(subvolume->path, target);
|
||||
const char *s = path_startswith(*subvolume, target);
|
||||
if (!s)
|
||||
continue;
|
||||
|
||||
|
@ -5168,6 +5440,7 @@ static usec_t epoch_or_infinity(void) {
|
|||
|
||||
static int do_copy_files(Context *context, Partition *p, const char *root) {
|
||||
_cleanup_strv_free_ char **subvolumes = NULL;
|
||||
_cleanup_free_ char **override_copy_files = NULL;
|
||||
int r;
|
||||
|
||||
assert(p);
|
||||
|
@ -5177,11 +5450,17 @@ static int do_copy_files(Context *context, Partition *p, const char *root) {
|
|||
if (r < 0)
|
||||
return r;
|
||||
|
||||
if (p->suppressing) {
|
||||
r = shallow_join_strv(&override_copy_files, p->copy_files, p->suppressing->copy_files);
|
||||
if (r < 0)
|
||||
return r;
|
||||
}
|
||||
|
||||
/* copy_tree_at() automatically copies the permissions of source directories to target directories if
|
||||
* it created them. However, the root directory is created by us, so we have to manually take care
|
||||
* that it is initialized. We use the first source directory targeting "/" as the metadata source for
|
||||
* the root directory. */
|
||||
STRV_FOREACH_PAIR(source, target, p->copy_files) {
|
||||
STRV_FOREACH_PAIR(source, target, override_copy_files ?: p->copy_files) {
|
||||
_cleanup_close_ int rfd = -EBADF, sfd = -EBADF;
|
||||
|
||||
if (!path_equal(*target, "/"))
|
||||
|
@ -5202,7 +5481,7 @@ static int do_copy_files(Context *context, Partition *p, const char *root) {
|
|||
break;
|
||||
}
|
||||
|
||||
STRV_FOREACH_PAIR(source, target, p->copy_files) {
|
||||
STRV_FOREACH_PAIR(source, target, override_copy_files ?: p->copy_files) {
|
||||
_cleanup_hashmap_free_ Hashmap *denylist = NULL;
|
||||
_cleanup_set_free_ Set *subvolumes_by_source_inode = NULL;
|
||||
_cleanup_close_ int sfd = -EBADF, pfd = -EBADF, tfd = -EBADF;
|
||||
|
@ -5320,6 +5599,7 @@ static int do_copy_files(Context *context, Partition *p, const char *root) {
|
|||
|
||||
static int do_make_directories(Partition *p, const char *root) {
|
||||
_cleanup_strv_free_ char **subvolumes = NULL;
|
||||
_cleanup_free_ char **override_dirs = NULL;
|
||||
int r;
|
||||
|
||||
assert(p);
|
||||
|
@ -5329,7 +5609,13 @@ static int do_make_directories(Partition *p, const char *root) {
|
|||
if (r < 0)
|
||||
return r;
|
||||
|
||||
STRV_FOREACH(d, p->make_directories) {
|
||||
if (p->suppressing) {
|
||||
r = shallow_join_strv(&override_dirs, p->make_directories, p->suppressing->make_directories);
|
||||
if (r < 0)
|
||||
return r;
|
||||
}
|
||||
|
||||
STRV_FOREACH(d, override_dirs ?: p->make_directories) {
|
||||
r = mkdir_p_root_full(root, *d, UID_INVALID, GID_INVALID, 0755, epoch_or_infinity(), subvolumes);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to create directory '%s' in file system: %m", *d);
|
||||
|
@ -5377,6 +5663,12 @@ static int make_subvolumes_read_only(Partition *p, const char *root) {
|
|||
return log_error_errno(r, "Failed to make subvolume '%s' read-only: %m", subvolume->path);
|
||||
}
|
||||
|
||||
if (p->suppressing) {
|
||||
r = make_subvolumes_read_only(p->suppressing, root);
|
||||
if (r < 0)
|
||||
return r;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -5408,7 +5700,7 @@ static int partition_populate_directory(Context *context, Partition *p, char **r
|
|||
|
||||
assert(ret);
|
||||
|
||||
log_info("Populating %s filesystem.", p->format);
|
||||
log_info("Preparing to populate %s filesystem.", p->format);
|
||||
|
||||
r = var_tmp_dir(&vt);
|
||||
if (r < 0)
|
||||
|
@ -5434,7 +5726,7 @@ static int partition_populate_directory(Context *context, Partition *p, char **r
|
|||
if (r < 0)
|
||||
return r;
|
||||
|
||||
log_info("Successfully populated %s filesystem.", p->format);
|
||||
log_info("Ready to populate %s filesystem.", p->format);
|
||||
|
||||
*ret = TAKE_PTR(root);
|
||||
return 0;
|
||||
|
@ -5509,32 +5801,6 @@ static int finalize_extra_mkfs_options(const Partition *p, const char *root, cha
|
|||
"Failed to determine mkfs command line options for '%s': %m",
|
||||
p->format);
|
||||
|
||||
if (partition_needs_populate(p) && root && streq(p->format, "btrfs")) {
|
||||
Subvolume *subvolume;
|
||||
|
||||
ORDERED_HASHMAP_FOREACH(subvolume, p->subvolumes) {
|
||||
_cleanup_free_ char *s = NULL, *f = NULL;
|
||||
|
||||
s = strdup(subvolume->path);
|
||||
if (!s)
|
||||
return log_oom();
|
||||
|
||||
f = subvolume_flags_to_string(subvolume->flags);
|
||||
if (!f)
|
||||
return log_oom();
|
||||
|
||||
if (streq_ptr(subvolume->path, p->default_subvolume) && !strextend_with_separator(&f, ",", "default"))
|
||||
return log_oom();
|
||||
|
||||
if (!isempty(f) && !strextend_with_separator(&s, ":", f))
|
||||
return log_oom();
|
||||
|
||||
r = strv_extend_many(&sv, "--subvol", s);
|
||||
if (r < 0)
|
||||
return log_oom();
|
||||
}
|
||||
}
|
||||
|
||||
*ret = TAKE_PTR(sv);
|
||||
return 0;
|
||||
}
|
||||
|
@ -8524,7 +8790,7 @@ static int determine_auto_size(Context *c) {
|
|||
LIST_FOREACH(partitions, p, c->partitions) {
|
||||
uint64_t m;
|
||||
|
||||
if (p->dropped)
|
||||
if (p->dropped || PARTITION_SUPPRESSED(p))
|
||||
continue;
|
||||
|
||||
m = partition_min_size_with_padding(c, p);
|
||||
|
@ -8756,13 +9022,36 @@ static int run(int argc, char *argv[]) {
|
|||
if (context_allocate_partitions(context, &largest_free_area))
|
||||
break; /* Success! */
|
||||
|
||||
if (!context_drop_or_foreignize_one_priority(context)) {
|
||||
r = log_error_errno(SYNTHETIC_ERRNO(ENOSPC),
|
||||
"Can't fit requested partitions into available free space (%s), refusing.",
|
||||
FORMAT_BYTES(largest_free_area));
|
||||
determine_auto_size(context);
|
||||
return r;
|
||||
}
|
||||
if (context_unmerge_and_allocate_partitions(context))
|
||||
break; /* We had to un-suppress a supplement or few, but still success! */
|
||||
|
||||
if (context_drop_or_foreignize_one_priority(context))
|
||||
continue; /* Still no luck. Let's drop a priority and try again. */
|
||||
|
||||
/* No more priorities left to drop. This configuration just doesn't fit on this disk... */
|
||||
r = log_error_errno(SYNTHETIC_ERRNO(ENOSPC),
|
||||
"Can't fit requested partitions into available free space (%s), refusing.",
|
||||
FORMAT_BYTES(largest_free_area));
|
||||
determine_auto_size(context);
|
||||
return r;
|
||||
}
|
||||
|
||||
LIST_FOREACH(partitions, p, context->partitions) {
|
||||
if (!p->supplement_for)
|
||||
continue;
|
||||
|
||||
if (PARTITION_SUPPRESSED(p)) {
|
||||
assert(!p->allocated_to_area);
|
||||
p->dropped = true;
|
||||
|
||||
log_debug("Partition %s can be merged into %s, suppressing supplement.",
|
||||
p->definition_path, p->supplement_for->definition_path);
|
||||
} else if (PARTITION_EXISTS(p))
|
||||
log_info("Partition %s already exists on disk, using supplement verbatim.",
|
||||
p->definition_path);
|
||||
else
|
||||
log_info("Couldn't allocate partitions with %s merged into %s, using supplement verbatim.",
|
||||
p->definition_path, p->supplement_for->definition_path);
|
||||
}
|
||||
|
||||
/* Now assign free space according to the weight logic */
|
||||
|
|
|
@ -2876,55 +2876,76 @@ static int print_answer(sd_json_variant *answer) {
|
|||
return 0;
|
||||
}
|
||||
|
||||
typedef struct MonitorQueryParams {
|
||||
sd_json_variant *question;
|
||||
sd_json_variant *answer;
|
||||
sd_json_variant *collected_questions;
|
||||
int rcode;
|
||||
int error;
|
||||
int ede_code;
|
||||
const char *state;
|
||||
const char *result;
|
||||
const char *ede_msg;
|
||||
} MonitorQueryParams;
|
||||
|
||||
static void monitor_query_params_done(MonitorQueryParams *p) {
|
||||
assert(p);
|
||||
|
||||
sd_json_variant_unref(p->question);
|
||||
sd_json_variant_unref(p->answer);
|
||||
sd_json_variant_unref(p->collected_questions);
|
||||
}
|
||||
|
||||
static void monitor_query_dump(sd_json_variant *v) {
|
||||
_cleanup_(sd_json_variant_unrefp) sd_json_variant *question = NULL, *answer = NULL, *collected_questions = NULL;
|
||||
int rcode = -1, error = 0, ede_code = -1;
|
||||
const char *state = NULL, *result = NULL, *ede_msg = NULL;
|
||||
|
||||
assert(v);
|
||||
|
||||
sd_json_dispatch_field dispatch_table[] = {
|
||||
{ "question", SD_JSON_VARIANT_ARRAY, sd_json_dispatch_variant, PTR_TO_SIZE(&question), SD_JSON_MANDATORY },
|
||||
{ "answer", SD_JSON_VARIANT_ARRAY, sd_json_dispatch_variant, PTR_TO_SIZE(&answer), 0 },
|
||||
{ "collectedQuestions", SD_JSON_VARIANT_ARRAY, sd_json_dispatch_variant, PTR_TO_SIZE(&collected_questions), 0 },
|
||||
{ "state", SD_JSON_VARIANT_STRING, sd_json_dispatch_const_string, PTR_TO_SIZE(&state), SD_JSON_MANDATORY },
|
||||
{ "result", SD_JSON_VARIANT_STRING, sd_json_dispatch_const_string, PTR_TO_SIZE(&result), 0 },
|
||||
{ "rcode", _SD_JSON_VARIANT_TYPE_INVALID, sd_json_dispatch_int, PTR_TO_SIZE(&rcode), 0 },
|
||||
{ "errno", _SD_JSON_VARIANT_TYPE_INVALID, sd_json_dispatch_int, PTR_TO_SIZE(&error), 0 },
|
||||
{ "extendedDNSErrorCode", _SD_JSON_VARIANT_TYPE_INVALID, sd_json_dispatch_int, PTR_TO_SIZE(&ede_code), 0 },
|
||||
{ "extendedDNSErrorMessage", SD_JSON_VARIANT_STRING, sd_json_dispatch_const_string, PTR_TO_SIZE(&ede_msg), 0 },
|
||||
static const sd_json_dispatch_field dispatch_table[] = {
|
||||
{ "question", SD_JSON_VARIANT_ARRAY, sd_json_dispatch_variant, offsetof(MonitorQueryParams, question), SD_JSON_MANDATORY },
|
||||
{ "answer", SD_JSON_VARIANT_ARRAY, sd_json_dispatch_variant, offsetof(MonitorQueryParams, answer), 0 },
|
||||
{ "collectedQuestions", SD_JSON_VARIANT_ARRAY, sd_json_dispatch_variant, offsetof(MonitorQueryParams, collected_questions), 0 },
|
||||
{ "state", SD_JSON_VARIANT_STRING, sd_json_dispatch_const_string, offsetof(MonitorQueryParams, state), SD_JSON_MANDATORY },
|
||||
{ "result", SD_JSON_VARIANT_STRING, sd_json_dispatch_const_string, offsetof(MonitorQueryParams, result), 0 },
|
||||
{ "rcode", _SD_JSON_VARIANT_TYPE_INVALID, sd_json_dispatch_int, offsetof(MonitorQueryParams, rcode), 0 },
|
||||
{ "errno", _SD_JSON_VARIANT_TYPE_INVALID, sd_json_dispatch_int, offsetof(MonitorQueryParams, error), 0 },
|
||||
{ "extendedDNSErrorCode", _SD_JSON_VARIANT_TYPE_INVALID, sd_json_dispatch_int, offsetof(MonitorQueryParams, ede_code), 0 },
|
||||
{ "extendedDNSErrorMessage", SD_JSON_VARIANT_STRING, sd_json_dispatch_const_string, offsetof(MonitorQueryParams, ede_msg), 0 },
|
||||
{}
|
||||
};
|
||||
|
||||
if (sd_json_dispatch(v, dispatch_table, SD_JSON_LOG|SD_JSON_ALLOW_EXTENSIONS, NULL) < 0)
|
||||
_cleanup_(monitor_query_params_done) MonitorQueryParams p = {
|
||||
.rcode = -1,
|
||||
.ede_code = -1,
|
||||
};
|
||||
|
||||
assert(v);
|
||||
|
||||
if (sd_json_dispatch(v, dispatch_table, SD_JSON_LOG|SD_JSON_ALLOW_EXTENSIONS, &p) < 0)
|
||||
return;
|
||||
|
||||
/* First show the current question */
|
||||
print_question('Q', ansi_highlight_cyan(), question);
|
||||
print_question('Q', ansi_highlight_cyan(), p.question);
|
||||
|
||||
/* And then show the questions that led to this one in case this was a CNAME chain */
|
||||
print_question('C', ansi_highlight_grey(), collected_questions);
|
||||
print_question('C', ansi_highlight_grey(), p.collected_questions);
|
||||
|
||||
printf("%s%s S%s: %s",
|
||||
streq_ptr(state, "success") ? ansi_highlight_green() : ansi_highlight_red(),
|
||||
streq_ptr(p.state, "success") ? ansi_highlight_green() : ansi_highlight_red(),
|
||||
special_glyph(SPECIAL_GLYPH_ARROW_LEFT),
|
||||
ansi_normal(),
|
||||
strna(streq_ptr(state, "errno") ? errno_to_name(error) :
|
||||
streq_ptr(state, "rcode-failure") ? dns_rcode_to_string(rcode) :
|
||||
state));
|
||||
strna(streq_ptr(p.state, "errno") ? errno_to_name(p.error) :
|
||||
streq_ptr(p.state, "rcode-failure") ? dns_rcode_to_string(p.rcode) :
|
||||
p.state));
|
||||
|
||||
if (!isempty(result))
|
||||
printf(": %s", result);
|
||||
if (!isempty(p.result))
|
||||
printf(": %s", p.result);
|
||||
|
||||
if (ede_code >= 0)
|
||||
if (p.ede_code >= 0)
|
||||
printf(" (%s%s%s)",
|
||||
FORMAT_DNS_EDE_RCODE(ede_code),
|
||||
!isempty(ede_msg) ? ": " : "",
|
||||
strempty(ede_msg));
|
||||
FORMAT_DNS_EDE_RCODE(p.ede_code),
|
||||
!isempty(p.ede_msg) ? ": " : "",
|
||||
strempty(p.ede_msg));
|
||||
|
||||
puts("");
|
||||
|
||||
print_answer(answer);
|
||||
print_answer(p.answer);
|
||||
}
|
||||
|
||||
static int monitor_reply(
|
||||
|
|
|
@ -2145,26 +2145,31 @@ int dns_resource_key_to_json(DnsResourceKey *key, sd_json_variant **ret) {
|
|||
}
|
||||
|
||||
int dns_resource_key_from_json(sd_json_variant *v, DnsResourceKey **ret) {
|
||||
_cleanup_(dns_resource_key_unrefp) DnsResourceKey *key = NULL;
|
||||
uint16_t type = 0, class = 0;
|
||||
const char *name = NULL;
|
||||
int r;
|
||||
struct params {
|
||||
uint16_t type;
|
||||
uint16_t class;
|
||||
const char *name;
|
||||
};
|
||||
|
||||
sd_json_dispatch_field dispatch_table[] = {
|
||||
{ "class", _SD_JSON_VARIANT_TYPE_INVALID, sd_json_dispatch_uint16, PTR_TO_SIZE(&class), SD_JSON_MANDATORY },
|
||||
{ "type", _SD_JSON_VARIANT_TYPE_INVALID, sd_json_dispatch_uint16, PTR_TO_SIZE(&type), SD_JSON_MANDATORY },
|
||||
{ "name", SD_JSON_VARIANT_STRING, sd_json_dispatch_const_string, PTR_TO_SIZE(&name), SD_JSON_MANDATORY },
|
||||
static const sd_json_dispatch_field dispatch_table[] = {
|
||||
{ "class", _SD_JSON_VARIANT_TYPE_INVALID, sd_json_dispatch_uint16, offsetof(struct params, class), SD_JSON_MANDATORY },
|
||||
{ "type", _SD_JSON_VARIANT_TYPE_INVALID, sd_json_dispatch_uint16, offsetof(struct params, type), SD_JSON_MANDATORY },
|
||||
{ "name", SD_JSON_VARIANT_STRING, sd_json_dispatch_const_string, offsetof(struct params, name), SD_JSON_MANDATORY },
|
||||
{}
|
||||
};
|
||||
|
||||
_cleanup_(dns_resource_key_unrefp) DnsResourceKey *key = NULL;
|
||||
struct params p;
|
||||
int r;
|
||||
|
||||
assert(v);
|
||||
assert(ret);
|
||||
|
||||
r = sd_json_dispatch(v, dispatch_table, 0, NULL);
|
||||
r = sd_json_dispatch(v, dispatch_table, 0, &p);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
key = dns_resource_key_new(class, type, name);
|
||||
key = dns_resource_key_new(p.class, p.type, p.name);
|
||||
if (!key)
|
||||
return -ENOMEM;
|
||||
|
||||
|
|
|
@ -31,7 +31,7 @@ int bpf_serialize_link(FILE *f, FDSet *fds, const char *key, struct bpf_link *li
|
|||
return serialize_fd(f, fds, key, sym_bpf_link__fd(link));
|
||||
}
|
||||
|
||||
struct bpf_link *bpf_link_free(struct bpf_link *link) {
|
||||
struct bpf_link* bpf_link_free(struct bpf_link *link) {
|
||||
/* If libbpf wasn't dlopen()ed, sym_bpf_link__destroy might be unresolved (NULL), so let's not try to
|
||||
* call it if link is NULL. link might also be a non-null "error pointer", but such a value can only
|
||||
* originate from a call to libbpf, but that means that libbpf is available, and we can let
|
||||
|
@ -41,3 +41,10 @@ struct bpf_link *bpf_link_free(struct bpf_link *link) {
|
|||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
struct ring_buffer* bpf_ring_buffer_free(struct ring_buffer *rb) {
|
||||
if (rb) /* See the comment in bpf_link_free(). */
|
||||
sym_ring_buffer__free(rb);
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
|
|
@ -12,5 +12,8 @@ bool bpf_can_link_program(struct bpf_program *prog);
|
|||
|
||||
int bpf_serialize_link(FILE *f, FDSet *fds, const char *key, struct bpf_link *link);
|
||||
|
||||
struct bpf_link *bpf_link_free(struct bpf_link *p);
|
||||
struct bpf_link* bpf_link_free(struct bpf_link *p);
|
||||
DEFINE_TRIVIAL_CLEANUP_FUNC(struct bpf_link *, bpf_link_free);
|
||||
|
||||
struct ring_buffer* bpf_ring_buffer_free(struct ring_buffer *rb);
|
||||
DEFINE_TRIVIAL_CLEANUP_FUNC(struct ring_buffer *, bpf_ring_buffer_free);
|
||||
|
|
|
@ -465,10 +465,6 @@ int hashmap_put_stats_by_path(Hashmap **stats_by_path, const char *path, const s
|
|||
assert(path);
|
||||
assert(st);
|
||||
|
||||
r = hashmap_ensure_allocated(stats_by_path, &path_hash_ops_free_free);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
st_copy = newdup(struct stat, st, 1);
|
||||
if (!st_copy)
|
||||
return -ENOMEM;
|
||||
|
@ -477,7 +473,7 @@ int hashmap_put_stats_by_path(Hashmap **stats_by_path, const char *path, const s
|
|||
if (!path_copy)
|
||||
return -ENOMEM;
|
||||
|
||||
r = hashmap_put(*stats_by_path, path_copy, st_copy);
|
||||
r = hashmap_ensure_put(stats_by_path, &path_hash_ops_free_free, path_copy, st_copy);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
|
@ -502,12 +498,12 @@ static int config_parse_many_files(
|
|||
_cleanup_ordered_hashmap_free_ OrderedHashmap *dropins = NULL;
|
||||
_cleanup_set_free_ Set *inodes = NULL;
|
||||
struct stat st;
|
||||
int r;
|
||||
int r, level = FLAGS_SET(flags, CONFIG_PARSE_WARN) ? LOG_WARNING : LOG_DEBUG;
|
||||
|
||||
if (ret_stats_by_path) {
|
||||
stats_by_path = hashmap_new(&path_hash_ops_free_free);
|
||||
if (!stats_by_path)
|
||||
return -ENOMEM;
|
||||
return log_oom_full(level);
|
||||
}
|
||||
|
||||
STRV_FOREACH(fn, files) {
|
||||
|
@ -518,14 +514,14 @@ static int config_parse_many_files(
|
|||
if (r == -ENOENT)
|
||||
continue;
|
||||
if (r < 0)
|
||||
return r;
|
||||
return log_full_errno(level, r, "Failed to open %s: %m", *fn);
|
||||
|
||||
int fd = fileno(f);
|
||||
|
||||
r = ordered_hashmap_ensure_put(&dropins, &config_file_hash_ops_fclose, *fn, f);
|
||||
if (r < 0) {
|
||||
assert(r != -EEXIST);
|
||||
return r;
|
||||
assert(r == -ENOMEM);
|
||||
return log_oom_full(level);
|
||||
}
|
||||
assert(r > 0);
|
||||
TAKE_PTR(f);
|
||||
|
@ -535,14 +531,14 @@ static int config_parse_many_files(
|
|||
|
||||
_cleanup_free_ struct stat *st_dropin = new(struct stat, 1);
|
||||
if (!st_dropin)
|
||||
return -ENOMEM;
|
||||
return log_oom_full(level);
|
||||
|
||||
if (fstat(fd, st_dropin) < 0)
|
||||
return -errno;
|
||||
return log_full_errno(level, errno, "Failed to stat %s: %m", *fn);
|
||||
|
||||
r = set_ensure_consume(&inodes, &inode_hash_ops, TAKE_PTR(st_dropin));
|
||||
if (r < 0)
|
||||
return r;
|
||||
return log_oom_full(level);
|
||||
}
|
||||
|
||||
/* First read the first found main config file. */
|
||||
|
@ -553,11 +549,11 @@ static int config_parse_many_files(
|
|||
if (r == -ENOENT)
|
||||
continue;
|
||||
if (r < 0)
|
||||
return r;
|
||||
return log_full_errno(level, r, "Failed to open %s: %m", *fn);
|
||||
|
||||
if (inodes) {
|
||||
if (fstat(fileno(f), &st) < 0)
|
||||
return -errno;
|
||||
return log_full_errno(level, errno, "Failed to stat %s: %m", *fn);
|
||||
|
||||
if (set_contains(inodes, &st)) {
|
||||
log_debug("%s: symlink to/symlinked as drop-in, will be read later.", *fn);
|
||||
|
@ -567,13 +563,13 @@ static int config_parse_many_files(
|
|||
|
||||
r = config_parse(/* unit= */ NULL, *fn, f, sections, lookup, table, flags, userdata, &st);
|
||||
if (r < 0)
|
||||
return r;
|
||||
return r; /* config_parse() logs internally. */
|
||||
assert(r > 0);
|
||||
|
||||
if (ret_stats_by_path) {
|
||||
r = hashmap_put_stats_by_path(&stats_by_path, *fn, &st);
|
||||
if (r < 0)
|
||||
return r;
|
||||
return log_full_errno(level, r, "Failed to save stats of %s: %m", *fn);
|
||||
}
|
||||
|
||||
break;
|
||||
|
@ -586,13 +582,13 @@ static int config_parse_many_files(
|
|||
ORDERED_HASHMAP_FOREACH_KEY(f_dropin, path_dropin, dropins) {
|
||||
r = config_parse(/* unit= */ NULL, path_dropin, f_dropin, sections, lookup, table, flags, userdata, &st);
|
||||
if (r < 0)
|
||||
return r;
|
||||
return r; /* config_parse() logs internally. */
|
||||
assert(r > 0);
|
||||
|
||||
if (ret_stats_by_path) {
|
||||
r = hashmap_put_stats_by_path(&stats_by_path, path_dropin, &st);
|
||||
if (r < 0)
|
||||
return r;
|
||||
return log_full_errno(level, r, "Failed to save stats of %s: %m", path_dropin);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -625,11 +621,12 @@ int config_parse_many(
|
|||
|
||||
r = conf_files_list_dropins(&files, dropin_dirname, root, conf_file_dirs);
|
||||
if (r < 0)
|
||||
return r;
|
||||
return log_full_errno(FLAGS_SET(flags, CONFIG_PARSE_WARN) ? LOG_WARNING : LOG_DEBUG, r,
|
||||
"Failed to list up drop-in configs in %s: %m", dropin_dirname);
|
||||
|
||||
r = config_parse_many_files(root, conf_files, files, sections, lookup, table, flags, userdata, ret_stats_by_path);
|
||||
if (r < 0)
|
||||
return r;
|
||||
return r; /* config_parse_many_files() logs internally. */
|
||||
|
||||
if (ret_dropin_files)
|
||||
*ret_dropin_files = TAKE_PTR(files);
|
||||
|
@ -650,22 +647,16 @@ int config_parse_standard_file_with_dropins_full(
|
|||
|
||||
const char* const *conf_paths = (const char* const*) CONF_PATHS_STRV("");
|
||||
_cleanup_strv_free_ char **configs = NULL;
|
||||
int r;
|
||||
int r, level = FLAGS_SET(flags, CONFIG_PARSE_WARN) ? LOG_WARNING : LOG_DEBUG;
|
||||
|
||||
/* Build the list of main config files */
|
||||
r = strv_extend_strv_biconcat(&configs, root, conf_paths, main_file);
|
||||
if (r < 0) {
|
||||
if (flags & CONFIG_PARSE_WARN)
|
||||
log_oom();
|
||||
return r;
|
||||
}
|
||||
if (r < 0)
|
||||
return log_oom_full(level);
|
||||
|
||||
_cleanup_free_ char *dropin_dirname = strjoin(main_file, ".d");
|
||||
if (!dropin_dirname) {
|
||||
if (flags & CONFIG_PARSE_WARN)
|
||||
log_oom();
|
||||
return -ENOMEM;
|
||||
}
|
||||
if (!dropin_dirname)
|
||||
return log_oom_full(level);
|
||||
|
||||
return config_parse_many(
|
||||
(const char* const*) configs,
|
||||
|
@ -1773,7 +1764,7 @@ int config_parse_hw_addr(
|
|||
void *data,
|
||||
void *userdata) {
|
||||
|
||||
struct hw_addr_data a, *hwaddr = ASSERT_PTR(data);
|
||||
struct hw_addr_data *hwaddr = ASSERT_PTR(data);
|
||||
int r;
|
||||
|
||||
assert(filename);
|
||||
|
@ -1785,11 +1776,10 @@ int config_parse_hw_addr(
|
|||
return 1;
|
||||
}
|
||||
|
||||
r = parse_hw_addr_full(rvalue, ltype, &a);
|
||||
r = parse_hw_addr_full(rvalue, ltype, hwaddr);
|
||||
if (r < 0)
|
||||
return log_syntax_parse_error(unit, filename, line, r, lvalue, rvalue);
|
||||
|
||||
*hwaddr = a;
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
@ -1982,6 +1972,36 @@ int config_parse_in_addr_non_null(
|
|||
return 1;
|
||||
}
|
||||
|
||||
int config_parse_in_addr_data(
|
||||
const char *unit,
|
||||
const char *filename,
|
||||
unsigned line,
|
||||
const char *section,
|
||||
unsigned section_line,
|
||||
const char *lvalue,
|
||||
int ltype,
|
||||
const char *rvalue,
|
||||
void *data,
|
||||
void *userdata) {
|
||||
|
||||
struct in_addr_data *p = ASSERT_PTR(data);
|
||||
int r;
|
||||
|
||||
assert(filename);
|
||||
assert(lvalue);
|
||||
|
||||
if (isempty(rvalue)) {
|
||||
*p = (struct in_addr_data) {};
|
||||
return 1;
|
||||
}
|
||||
|
||||
r = in_addr_from_string_auto(rvalue, &p->family, &p->address);
|
||||
if (r < 0)
|
||||
return log_syntax_parse_error(unit, filename, line, r, lvalue, rvalue);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
int config_parse_unsigned_bounded(
|
||||
const char *unit,
|
||||
const char *filename,
|
||||
|
|
|
@ -304,6 +304,7 @@ CONFIG_PARSER_PROTOTYPE(config_parse_hw_addrs);
|
|||
CONFIG_PARSER_PROTOTYPE(config_parse_ether_addr);
|
||||
CONFIG_PARSER_PROTOTYPE(config_parse_ether_addrs);
|
||||
CONFIG_PARSER_PROTOTYPE(config_parse_in_addr_non_null);
|
||||
CONFIG_PARSER_PROTOTYPE(config_parse_in_addr_data);
|
||||
CONFIG_PARSER_PROTOTYPE(config_parse_percent);
|
||||
CONFIG_PARSER_PROTOTYPE(config_parse_permyriad);
|
||||
CONFIG_PARSER_PROTOTYPE(config_parse_pid);
|
||||
|
|
|
@ -1582,14 +1582,12 @@ int ipc_encrypt_credential(const char *name, usec_t timestamp, usec_t not_after,
|
|||
return log_error_errno(sd_varlink_error_to_errno(error_id, reply), "Failed to encrypt: %s", error_id);
|
||||
}
|
||||
|
||||
r = sd_json_dispatch(
|
||||
reply,
|
||||
(const sd_json_dispatch_field[]) {
|
||||
{ "blob", SD_JSON_VARIANT_STRING, json_dispatch_unbase64_iovec, PTR_TO_SIZE(ret), SD_JSON_MANDATORY },
|
||||
{},
|
||||
},
|
||||
SD_JSON_LOG|SD_JSON_ALLOW_EXTENSIONS,
|
||||
/* userdata= */ NULL);
|
||||
static const sd_json_dispatch_field dispatch_table[] = {
|
||||
{ "blob", SD_JSON_VARIANT_STRING, json_dispatch_unbase64_iovec, 0, SD_JSON_MANDATORY },
|
||||
{},
|
||||
};
|
||||
|
||||
r = sd_json_dispatch(reply, dispatch_table, SD_JSON_LOG|SD_JSON_ALLOW_EXTENSIONS, ret);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
|
@ -1649,14 +1647,12 @@ int ipc_decrypt_credential(const char *validate_name, usec_t validate_timestamp,
|
|||
return log_error_errno(sd_varlink_error_to_errno(error_id, reply), "Failed to decrypt: %s", error_id);
|
||||
}
|
||||
|
||||
r = sd_json_dispatch(
|
||||
reply,
|
||||
(const sd_json_dispatch_field[]) {
|
||||
{ "data", SD_JSON_VARIANT_STRING, json_dispatch_unbase64_iovec, PTR_TO_SIZE(ret), SD_JSON_MANDATORY },
|
||||
{},
|
||||
},
|
||||
SD_JSON_LOG|SD_JSON_ALLOW_EXTENSIONS,
|
||||
/* userdata= */ NULL);
|
||||
static const sd_json_dispatch_field dispatch_table[] = {
|
||||
{ "data", SD_JSON_VARIANT_STRING, json_dispatch_unbase64_iovec, 0, SD_JSON_MANDATORY },
|
||||
{},
|
||||
};
|
||||
|
||||
r = sd_json_dispatch(reply, dispatch_table, SD_JSON_LOG|SD_JSON_ALLOW_EXTENSIONS, ret);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
|
|
|
@ -66,9 +66,5 @@ static inline bool efi_has_tpm2(void) {
|
|||
|
||||
#endif
|
||||
|
||||
static inline char *efi_tilt_backslashes(char *s) {
|
||||
return string_replace_char(s, '\\', '/');
|
||||
}
|
||||
|
||||
sd_id128_t efi_guid_to_id128(const void *guid);
|
||||
void efi_id128_to_guid(sd_id128_t id, void *ret_guid);
|
||||
|
|
|
@ -61,30 +61,19 @@ int efi_loader_get_boot_usec(usec_t *ret_firmware, usec_t *ret_loader) {
|
|||
return 0;
|
||||
}
|
||||
|
||||
int efi_loader_get_device_part_uuid(sd_id128_t *ret) {
|
||||
_cleanup_free_ char *p = NULL;
|
||||
int r;
|
||||
unsigned parsed[16];
|
||||
|
||||
static int get_device_part_uuid(const char *variable, sd_id128_t *ret) {
|
||||
if (!is_efi_boot())
|
||||
return -EOPNOTSUPP;
|
||||
|
||||
r = efi_get_variable_string(EFI_LOADER_VARIABLE(LoaderDevicePartUUID), &p);
|
||||
if (r < 0)
|
||||
return r;
|
||||
return efi_get_variable_id128(variable, ret);
|
||||
}
|
||||
|
||||
if (sscanf(p, SD_ID128_UUID_FORMAT_STR,
|
||||
&parsed[0], &parsed[1], &parsed[2], &parsed[3],
|
||||
&parsed[4], &parsed[5], &parsed[6], &parsed[7],
|
||||
&parsed[8], &parsed[9], &parsed[10], &parsed[11],
|
||||
&parsed[12], &parsed[13], &parsed[14], &parsed[15]) != 16)
|
||||
return -EIO;
|
||||
int efi_loader_get_device_part_uuid(sd_id128_t *ret) {
|
||||
return get_device_part_uuid(EFI_LOADER_VARIABLE(LoaderDevicePartUUID), ret);
|
||||
}
|
||||
|
||||
if (ret)
|
||||
for (unsigned i = 0; i < ELEMENTSOF(parsed); i++)
|
||||
ret->bytes[i] = parsed[i];
|
||||
|
||||
return 0;
|
||||
int efi_stub_get_device_part_uuid(sd_id128_t *ret) {
|
||||
return get_device_part_uuid(EFI_LOADER_VARIABLE(StubDevicePartUUID), ret);
|
||||
}
|
||||
|
||||
int efi_loader_get_entries(char ***ret) {
|
||||
|
@ -353,6 +342,22 @@ int efi_loader_update_entry_one_shot_cache(char **cache, struct stat *cache_stat
|
|||
return 0;
|
||||
}
|
||||
|
||||
int efi_get_variable_id128(const char *variable, sd_id128_t *ret) {
|
||||
int r;
|
||||
|
||||
assert(variable);
|
||||
|
||||
/* This is placed here (rather than in basic/efivars.c) because code in basic/ is not allowed to link
|
||||
* against libsystemd.so */
|
||||
|
||||
_cleanup_free_ char *p = NULL;
|
||||
r = efi_get_variable_string(variable, &p);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
return sd_id128_from_string(p, ret);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
bool efi_loader_entry_name_valid(const char *s) {
|
||||
|
|
|
@ -11,6 +11,7 @@
|
|||
#if ENABLE_EFI
|
||||
|
||||
int efi_loader_get_device_part_uuid(sd_id128_t *ret);
|
||||
int efi_stub_get_device_part_uuid(sd_id128_t *ret);
|
||||
int efi_loader_get_boot_usec(usec_t *ret_firmware, usec_t *ret_loader);
|
||||
|
||||
int efi_loader_get_entries(char ***ret);
|
||||
|
@ -23,6 +24,8 @@ int efi_measured_uki(int log_level);
|
|||
int efi_loader_get_config_timeout_one_shot(usec_t *ret);
|
||||
int efi_loader_update_entry_one_shot_cache(char **cache, struct stat *cache_stat);
|
||||
|
||||
int efi_get_variable_id128(const char *variable, sd_id128_t *ret);
|
||||
|
||||
#else
|
||||
|
||||
static inline int efi_loader_get_device_part_uuid(sd_id128_t *u) {
|
||||
|
@ -58,6 +61,10 @@ static inline int efi_loader_update_entry_one_shot_cache(char **cache, struct st
|
|||
return -EOPNOTSUPP;
|
||||
}
|
||||
|
||||
static inline int efi_get_variable_id128(const char *variable, sd_id128_t *ret) {
|
||||
return -EOPNOTSUPP;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
bool efi_loader_entry_name_valid(const char *s);
|
||||
|
|
|
@ -968,7 +968,9 @@ void log_setup_generator(void) {
|
|||
|
||||
/* This effectively means: journal for per-user generators, kmsg otherwise */
|
||||
log_set_target(LOG_TARGET_JOURNAL_OR_KMSG);
|
||||
}
|
||||
} else
|
||||
log_set_target(LOG_TARGET_AUTO);
|
||||
|
||||
log_setup();
|
||||
log_parse_environment();
|
||||
log_open();
|
||||
}
|
||||
|
|
|
@ -14,6 +14,7 @@
|
|||
static const char * const kernel_image_type_table[_KERNEL_IMAGE_TYPE_MAX] = {
|
||||
[KERNEL_IMAGE_TYPE_UNKNOWN] = "unknown",
|
||||
[KERNEL_IMAGE_TYPE_UKI] = "uki",
|
||||
[KERNEL_IMAGE_TYPE_ADDON] = "addon",
|
||||
[KERNEL_IMAGE_TYPE_PE] = "pe",
|
||||
};
|
||||
|
||||
|
@ -159,6 +160,16 @@ int inspect_kernel(
|
|||
|
||||
t = KERNEL_IMAGE_TYPE_UKI;
|
||||
goto done;
|
||||
} else if (pe_is_addon(pe_header, sections)) {
|
||||
r = inspect_uki(fd, pe_header, sections, ret_cmdline, ret_uname, /* ret_pretty_name= */ NULL);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
if (ret_pretty_name)
|
||||
*ret_pretty_name = NULL;
|
||||
|
||||
t = KERNEL_IMAGE_TYPE_ADDON;
|
||||
goto done;
|
||||
} else
|
||||
t = KERNEL_IMAGE_TYPE_PE;
|
||||
|
||||
|
|
|
@ -8,6 +8,7 @@
|
|||
typedef enum KernelImageType {
|
||||
KERNEL_IMAGE_TYPE_UNKNOWN,
|
||||
KERNEL_IMAGE_TYPE_UKI,
|
||||
KERNEL_IMAGE_TYPE_ADDON,
|
||||
KERNEL_IMAGE_TYPE_PE,
|
||||
_KERNEL_IMAGE_TYPE_MAX,
|
||||
_KERNEL_IMAGE_TYPE_INVALID = -EINVAL,
|
||||
|
|
|
@ -170,6 +170,7 @@ shared_sources = files(
|
|||
'tpm2-util.c',
|
||||
'tpm2-event-log.c',
|
||||
'udev-util.c',
|
||||
'unit-file.c',
|
||||
'user-record-nss.c',
|
||||
'user-record-show.c',
|
||||
'user-record.c',
|
||||
|
|
|
@ -250,6 +250,18 @@ int nsresource_add_cgroup(int userns_fd, int cgroup_fd) {
|
|||
return 1;
|
||||
}
|
||||
|
||||
typedef struct InterfaceParams {
|
||||
char *host_interface_name;
|
||||
char *namespace_interface_name;
|
||||
} InterfaceParams;
|
||||
|
||||
static void interface_params_done(InterfaceParams *p) {
|
||||
assert(p);
|
||||
|
||||
free(p->host_interface_name);
|
||||
free(p->namespace_interface_name);
|
||||
}
|
||||
|
||||
int nsresource_add_netif(
|
||||
int userns_fd,
|
||||
int netns_fd,
|
||||
|
@ -313,20 +325,20 @@ int nsresource_add_netif(
|
|||
if (error_id)
|
||||
return log_debug_errno(sd_varlink_error_to_errno(error_id, reply), "Failed to add network to user namespace: %s", error_id);
|
||||
|
||||
_cleanup_free_ char *host_interface_name = NULL, *namespace_interface_name = NULL;
|
||||
r = sd_json_dispatch(
|
||||
reply,
|
||||
(const sd_json_dispatch_field[]) {
|
||||
{ "hostInterfaceName", SD_JSON_VARIANT_STRING, sd_json_dispatch_string, PTR_TO_SIZE(&host_interface_name) },
|
||||
{ "namespaceInterfaceName", SD_JSON_VARIANT_STRING, sd_json_dispatch_string, PTR_TO_SIZE(&namespace_interface_name) },
|
||||
},
|
||||
SD_JSON_ALLOW_EXTENSIONS,
|
||||
/* userdata= */ NULL);
|
||||
static const sd_json_dispatch_field dispatch_table[] = {
|
||||
{ "hostInterfaceName", SD_JSON_VARIANT_STRING, sd_json_dispatch_string, offsetof(InterfaceParams, host_interface_name), 0 },
|
||||
{ "namespaceInterfaceName", SD_JSON_VARIANT_STRING, sd_json_dispatch_string, offsetof(InterfaceParams, namespace_interface_name), 0 },
|
||||
};
|
||||
|
||||
_cleanup_(interface_params_done) InterfaceParams p = {};
|
||||
r = sd_json_dispatch(reply, dispatch_table, SD_JSON_ALLOW_EXTENSIONS, &p);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
if (ret_host_ifname)
|
||||
*ret_host_ifname = TAKE_PTR(host_interface_name);
|
||||
*ret_host_ifname = TAKE_PTR(p.host_interface_name);
|
||||
if (ret_namespace_ifname)
|
||||
*ret_namespace_ifname = TAKE_PTR(namespace_interface_name);
|
||||
*ret_namespace_ifname = TAKE_PTR(p.namespace_interface_name);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
|
|
@ -16,25 +16,6 @@
|
|||
#include "strv.h"
|
||||
#include "unit-file.h"
|
||||
|
||||
bool unit_type_may_alias(UnitType type) {
|
||||
return IN_SET(type,
|
||||
UNIT_SERVICE,
|
||||
UNIT_SOCKET,
|
||||
UNIT_TARGET,
|
||||
UNIT_DEVICE,
|
||||
UNIT_TIMER,
|
||||
UNIT_PATH);
|
||||
}
|
||||
|
||||
bool unit_type_may_template(UnitType type) {
|
||||
return IN_SET(type,
|
||||
UNIT_SERVICE,
|
||||
UNIT_SOCKET,
|
||||
UNIT_TARGET,
|
||||
UNIT_TIMER,
|
||||
UNIT_PATH);
|
||||
}
|
||||
|
||||
int unit_symlink_name_compatible(const char *symlink, const char *target, bool instance_propagation) {
|
||||
_cleanup_free_ char *template = NULL;
|
||||
int r, un_type1, un_type2;
|
|
@ -28,8 +28,24 @@ enum UnitFileState {
|
|||
_UNIT_FILE_STATE_INVALID = -EINVAL,
|
||||
};
|
||||
|
||||
bool unit_type_may_alias(UnitType type) _const_;
|
||||
bool unit_type_may_template(UnitType type) _const_;
|
||||
static inline bool unit_type_may_alias(UnitType type) {
|
||||
return IN_SET(type,
|
||||
UNIT_SERVICE,
|
||||
UNIT_SOCKET,
|
||||
UNIT_TARGET,
|
||||
UNIT_DEVICE,
|
||||
UNIT_TIMER,
|
||||
UNIT_PATH);
|
||||
}
|
||||
|
||||
static inline bool unit_type_may_template(UnitType type) {
|
||||
return IN_SET(type,
|
||||
UNIT_SERVICE,
|
||||
UNIT_SOCKET,
|
||||
UNIT_TARGET,
|
||||
UNIT_TIMER,
|
||||
UNIT_PATH);
|
||||
}
|
||||
|
||||
int unit_symlink_name_compatible(const char *symlink, const char *target, bool instance_propagation);
|
||||
int unit_validate_alias_symlink_or_warn(int log_level, const char *filename, const char *target);
|
|
@ -161,12 +161,12 @@ static int process_machine(const char *machine, const char *port) {
|
|||
|
||||
uint32_t cid = VMADDR_CID_ANY;
|
||||
|
||||
const sd_json_dispatch_field dispatch_table[] = {
|
||||
{ "vSockCid", SD_JSON_VARIANT_UNSIGNED, sd_json_dispatch_uint32, PTR_TO_SIZE(&cid), 0 },
|
||||
static const sd_json_dispatch_field dispatch_table[] = {
|
||||
{ "vSockCid", SD_JSON_VARIANT_UNSIGNED, sd_json_dispatch_uint32, 0, 0 },
|
||||
{}
|
||||
};
|
||||
|
||||
r = sd_json_dispatch(result, dispatch_table, SD_JSON_ALLOW_EXTENSIONS, NULL);
|
||||
r = sd_json_dispatch(result, dispatch_table, SD_JSON_ALLOW_EXTENSIONS, &cid);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to parse Varlink reply: %m");
|
||||
|
||||
|
|
|
@ -28,17 +28,16 @@ static int output_waiting_jobs(sd_bus *bus, Table *table, uint32_t id, const cha
|
|||
|
||||
while ((r = sd_bus_message_read(reply, "(usssoo)", &other_id, &name, &type, NULL, NULL, NULL)) > 0) {
|
||||
_cleanup_free_ char *row = NULL;
|
||||
int rc;
|
||||
|
||||
if (asprintf(&row, "%s %u (%s/%s)", prefix, other_id, name, type) < 0)
|
||||
return log_oom();
|
||||
|
||||
rc = table_add_many(table,
|
||||
r = table_add_many(table,
|
||||
TABLE_STRING, special_glyph(SPECIAL_GLYPH_TREE_RIGHT),
|
||||
TABLE_STRING, row,
|
||||
TABLE_EMPTY,
|
||||
TABLE_EMPTY);
|
||||
if (rc < 0)
|
||||
if (r < 0)
|
||||
return table_log_add_error(r);
|
||||
}
|
||||
|
||||
|
|
|
@ -123,8 +123,8 @@ enum {
|
|||
_SD_PATH_MAX
|
||||
};
|
||||
|
||||
int sd_path_lookup(uint64_t type, const char *suffix, char **path);
|
||||
int sd_path_lookup_strv(uint64_t type, const char *suffix, char ***paths);
|
||||
int sd_path_lookup(uint64_t type, const char *suffix, char **ret);
|
||||
int sd_path_lookup_strv(uint64_t type, const char *suffix, char ***ret);
|
||||
|
||||
_SD_END_DECLARATIONS;
|
||||
|
||||
|
|
|
@ -321,12 +321,27 @@ static int list_targets(sd_bus *bus) {
|
|||
return table_print_with_pager(table, SD_JSON_FORMAT_OFF, arg_pager_flags, arg_legend);
|
||||
}
|
||||
|
||||
typedef struct DescribeParams {
|
||||
Version v;
|
||||
sd_json_variant *contents_json;
|
||||
bool newest;
|
||||
bool available;
|
||||
bool installed;
|
||||
bool obsolete;
|
||||
bool protected;
|
||||
bool incomplete;
|
||||
} DescribeParams;
|
||||
|
||||
static void describe_params_done(DescribeParams *p) {
|
||||
assert(p);
|
||||
|
||||
version_done(&p->v);
|
||||
sd_json_variant_unref(p->contents_json);
|
||||
}
|
||||
|
||||
static int parse_describe(sd_bus_message *reply, Version *ret) {
|
||||
Version v = {};
|
||||
char *version_json = NULL;
|
||||
_cleanup_(sd_json_variant_unrefp) sd_json_variant *json = NULL, *contents_json = NULL;
|
||||
bool newest = false, available = false, installed = false, obsolete = false, protected = false,
|
||||
incomplete = false;
|
||||
_cleanup_(sd_json_variant_unrefp) sd_json_variant *json = NULL;
|
||||
int r;
|
||||
|
||||
assert(reply);
|
||||
|
@ -342,36 +357,37 @@ static int parse_describe(sd_bus_message *reply, Version *ret) {
|
|||
|
||||
assert(sd_json_variant_is_object(json));
|
||||
|
||||
r = sd_json_dispatch(json,
|
||||
(const sd_json_dispatch_field[]) {
|
||||
{ "version", SD_JSON_VARIANT_STRING, sd_json_dispatch_string, PTR_TO_SIZE(&v.version), 0 },
|
||||
{ "newest", SD_JSON_VARIANT_BOOLEAN, sd_json_dispatch_stdbool, PTR_TO_SIZE(&newest), 0 },
|
||||
{ "available", SD_JSON_VARIANT_BOOLEAN, sd_json_dispatch_stdbool, PTR_TO_SIZE(&available), 0 },
|
||||
{ "installed", SD_JSON_VARIANT_BOOLEAN, sd_json_dispatch_stdbool, PTR_TO_SIZE(&installed), 0 },
|
||||
{ "obsolete", SD_JSON_VARIANT_BOOLEAN, sd_json_dispatch_stdbool, PTR_TO_SIZE(&obsolete), 0 },
|
||||
{ "protected", SD_JSON_VARIANT_BOOLEAN, sd_json_dispatch_stdbool, PTR_TO_SIZE(&protected), 0 },
|
||||
{ "incomplete", SD_JSON_VARIANT_BOOLEAN, sd_json_dispatch_stdbool, PTR_TO_SIZE(&incomplete), 0 },
|
||||
{ "changelog_urls", SD_JSON_VARIANT_ARRAY, sd_json_dispatch_strv, PTR_TO_SIZE(&v.changelog), 0 },
|
||||
{ "contents", SD_JSON_VARIANT_ARRAY, sd_json_dispatch_variant, PTR_TO_SIZE(&contents_json), 0 },
|
||||
{},
|
||||
},
|
||||
SD_JSON_ALLOW_EXTENSIONS,
|
||||
/* userdata= */ NULL);
|
||||
static const sd_json_dispatch_field dispatch_table[] = {
|
||||
{ "version", SD_JSON_VARIANT_STRING, sd_json_dispatch_string, offsetof(DescribeParams, v.version), 0 },
|
||||
{ "newest", SD_JSON_VARIANT_BOOLEAN, sd_json_dispatch_stdbool, offsetof(DescribeParams, newest), 0 },
|
||||
{ "available", SD_JSON_VARIANT_BOOLEAN, sd_json_dispatch_stdbool, offsetof(DescribeParams, available), 0 },
|
||||
{ "installed", SD_JSON_VARIANT_BOOLEAN, sd_json_dispatch_stdbool, offsetof(DescribeParams, installed), 0 },
|
||||
{ "obsolete", SD_JSON_VARIANT_BOOLEAN, sd_json_dispatch_stdbool, offsetof(DescribeParams, obsolete), 0 },
|
||||
{ "protected", SD_JSON_VARIANT_BOOLEAN, sd_json_dispatch_stdbool, offsetof(DescribeParams, protected), 0 },
|
||||
{ "incomplete", SD_JSON_VARIANT_BOOLEAN, sd_json_dispatch_stdbool, offsetof(DescribeParams, incomplete), 0 },
|
||||
{ "changelog_urls", SD_JSON_VARIANT_ARRAY, sd_json_dispatch_strv, offsetof(DescribeParams, v.changelog), 0 },
|
||||
{ "contents", SD_JSON_VARIANT_ARRAY, sd_json_dispatch_variant, offsetof(DescribeParams, contents_json), 0 },
|
||||
{},
|
||||
};
|
||||
|
||||
_cleanup_(describe_params_done) DescribeParams p = {};
|
||||
|
||||
r = sd_json_dispatch(json, dispatch_table, SD_JSON_ALLOW_EXTENSIONS, &p);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to parse JSON: %m");
|
||||
|
||||
SET_FLAG(v.flags, UPDATE_NEWEST, newest);
|
||||
SET_FLAG(v.flags, UPDATE_AVAILABLE, available);
|
||||
SET_FLAG(v.flags, UPDATE_INSTALLED, installed);
|
||||
SET_FLAG(v.flags, UPDATE_OBSOLETE, obsolete);
|
||||
SET_FLAG(v.flags, UPDATE_PROTECTED, protected);
|
||||
SET_FLAG(v.flags, UPDATE_INCOMPLETE, incomplete);
|
||||
SET_FLAG(p.v.flags, UPDATE_NEWEST, p.newest);
|
||||
SET_FLAG(p.v.flags, UPDATE_AVAILABLE, p.available);
|
||||
SET_FLAG(p.v.flags, UPDATE_INSTALLED, p.installed);
|
||||
SET_FLAG(p.v.flags, UPDATE_OBSOLETE, p.obsolete);
|
||||
SET_FLAG(p.v.flags, UPDATE_PROTECTED, p.protected);
|
||||
SET_FLAG(p.v.flags, UPDATE_INCOMPLETE, p.incomplete);
|
||||
|
||||
r = sd_json_variant_format(contents_json, 0, &v.contents_json);
|
||||
r = sd_json_variant_format(p.contents_json, 0, &p.v.contents_json);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to format JSON for contents: %m");
|
||||
|
||||
*ret = TAKE_STRUCT(v);
|
||||
*ret = TAKE_STRUCT(p.v);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
|
|
@ -197,7 +197,7 @@ _unused_ static void test_compress_stream(const char *compression,
|
|||
ASSERT_OK(compress(src, dst, -1, &uncompressed_size));
|
||||
|
||||
if (cat) {
|
||||
assert_se(asprintf(&cmd, "%s %s | diff %s -", cat, pattern, srcfile) > 0);
|
||||
assert_se(asprintf(&cmd, "%s %s | diff '%s' -", cat, pattern, srcfile) > 0);
|
||||
assert_se(system(cmd) == 0);
|
||||
}
|
||||
|
||||
|
@ -212,7 +212,7 @@ _unused_ static void test_compress_stream(const char *compression,
|
|||
r = decompress(dst, dst2, st.st_size);
|
||||
assert_se(r == 0);
|
||||
|
||||
assert_se(asprintf(&cmd2, "diff %s %s", srcfile, pattern2) > 0);
|
||||
assert_se(asprintf(&cmd2, "diff '%s' %s", srcfile, pattern2) > 0);
|
||||
assert_se(system(cmd2) == 0);
|
||||
|
||||
log_debug("/* test faulty decompression */");
|
||||
|
|
|
@ -170,6 +170,8 @@ typedef struct Item {
|
|||
|
||||
bool try_replace:1;
|
||||
|
||||
bool purge:1;
|
||||
|
||||
OperationMask done;
|
||||
} Item;
|
||||
|
||||
|
@ -248,12 +250,12 @@ static void context_done(Context *c) {
|
|||
}
|
||||
|
||||
/* Different kinds of errors that mean that information is not available in the environment. */
|
||||
static bool ERRNO_IS_NOINFO(int r) {
|
||||
return IN_SET(abs(r),
|
||||
EUNATCH, /* os-release or machine-id missing */
|
||||
ENOMEDIUM, /* machine-id or another file empty */
|
||||
ENOPKG, /* machine-id is uninitialized */
|
||||
ENXIO); /* env var is unset */
|
||||
static bool ERRNO_IS_NEG_NOINFO(intmax_t r) {
|
||||
return IN_SET(r,
|
||||
-EUNATCH, /* os-release or machine-id missing */
|
||||
-ENOMEDIUM, /* machine-id or another file empty */
|
||||
-ENOPKG, /* machine-id is uninitialized */
|
||||
-ENXIO); /* env var is unset */
|
||||
}
|
||||
|
||||
static int specifier_directory(
|
||||
|
@ -347,49 +349,35 @@ static int log_unresolvable_specifier(const char *filename, unsigned line) {
|
|||
arg_dry_run ? (would) : (doing), \
|
||||
__VA_ARGS__)
|
||||
|
||||
static int user_config_paths(char*** ret) {
|
||||
static int user_config_paths(char ***ret) {
|
||||
_cleanup_strv_free_ char **config_dirs = NULL, **data_dirs = NULL;
|
||||
_cleanup_free_ char *persistent_config = NULL, *runtime_config = NULL, *data_home = NULL;
|
||||
_cleanup_strv_free_ char **res = NULL;
|
||||
_cleanup_free_ char *runtime_config = NULL;
|
||||
int r;
|
||||
|
||||
r = xdg_user_dirs(&config_dirs, &data_dirs);
|
||||
assert(ret);
|
||||
|
||||
/* Combined user-specific and global dirs */
|
||||
r = user_search_dirs("/user-tmpfiles.d", &config_dirs, &data_dirs);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
r = xdg_user_config_dir(&persistent_config, "/user-tmpfiles.d");
|
||||
if (r < 0 && !ERRNO_IS_NOINFO(r))
|
||||
r = xdg_user_runtime_dir("/user-tmpfiles.d", &runtime_config);
|
||||
if (r < 0 && !ERRNO_IS_NEG_NOINFO(r))
|
||||
return r;
|
||||
|
||||
r = xdg_user_runtime_dir(&runtime_config, "/user-tmpfiles.d");
|
||||
if (r < 0 && !ERRNO_IS_NOINFO(r))
|
||||
return r;
|
||||
|
||||
r = xdg_user_data_dir(&data_home, "/user-tmpfiles.d");
|
||||
if (r < 0 && !ERRNO_IS_NOINFO(r))
|
||||
return r;
|
||||
|
||||
r = strv_extend_strv_concat(&res, (const char* const*) config_dirs, "/user-tmpfiles.d");
|
||||
r = strv_consume(&config_dirs, TAKE_PTR(runtime_config));
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
r = strv_extend_many(
|
||||
&res,
|
||||
persistent_config,
|
||||
runtime_config,
|
||||
data_home);
|
||||
r = strv_extend_strv(&config_dirs, data_dirs, /* filter_duplicates = */ true);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
r = strv_extend_strv_concat(&res, (const char* const*) data_dirs, "/user-tmpfiles.d");
|
||||
r = path_strv_make_absolute_cwd(config_dirs);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
r = path_strv_make_absolute_cwd(res);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
*ret = TAKE_PTR(res);
|
||||
*ret = TAKE_PTR(config_dirs);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -3046,6 +3034,9 @@ static int purge_item(Context *c, Item *i) {
|
|||
if (!needs_purge(i->type))
|
||||
return 0;
|
||||
|
||||
if (!i->purge)
|
||||
return 0;
|
||||
|
||||
log_debug("Running purge action for entry %c %s", (char) i->type, i->path);
|
||||
|
||||
if (needs_glob(i->type))
|
||||
|
@ -3602,7 +3593,7 @@ static int parse_line(
|
|||
ItemArray *existing;
|
||||
OrderedHashmap *h;
|
||||
bool append_or_force = false, boot = false, allow_failure = false, try_replace = false,
|
||||
unbase64 = false, from_cred = false, missing_user_or_group = false;
|
||||
unbase64 = false, from_cred = false, missing_user_or_group = false, purge = false;
|
||||
int r;
|
||||
|
||||
assert(fname);
|
||||
|
@ -3668,6 +3659,8 @@ static int parse_line(
|
|||
unbase64 = true;
|
||||
else if (action[pos] == '^' && !from_cred)
|
||||
from_cred = true;
|
||||
else if (action[pos] == '$' && !purge)
|
||||
purge = true;
|
||||
else {
|
||||
*invalid_config = true;
|
||||
return log_syntax(NULL, LOG_ERR, fname, line, SYNTHETIC_ERRNO(EBADMSG),
|
||||
|
@ -3684,9 +3677,10 @@ static int parse_line(
|
|||
i.append_or_force = append_or_force;
|
||||
i.allow_failure = allow_failure;
|
||||
i.try_replace = try_replace;
|
||||
i.purge = purge;
|
||||
|
||||
r = specifier_printf(path, PATH_MAX-1, specifier_table, arg_root, NULL, &i.path);
|
||||
if (ERRNO_IS_NOINFO(r))
|
||||
if (ERRNO_IS_NEG_NOINFO(r))
|
||||
return log_unresolvable_specifier(fname, line);
|
||||
if (r < 0) {
|
||||
if (IN_SET(r, -EINVAL, -EBADSLT))
|
||||
|
@ -3838,13 +3832,19 @@ static int parse_line(
|
|||
"Unknown command type '%c'.", (char) i.type);
|
||||
}
|
||||
|
||||
if (i.purge && !needs_purge(i.type)) {
|
||||
*invalid_config = true;
|
||||
return log_syntax(NULL, LOG_ERR, fname, line, SYNTHETIC_ERRNO(EBADMSG),
|
||||
"Purge flag '$' combined with line type '%c' which does not support purging.", (char) i.type);
|
||||
}
|
||||
|
||||
if (!should_include_path(i.path))
|
||||
return 0;
|
||||
|
||||
if (!unbase64) {
|
||||
/* Do specifier expansion except if base64 mode is enabled */
|
||||
r = specifier_expansion_from_arg(specifier_table, &i);
|
||||
if (ERRNO_IS_NOINFO(r))
|
||||
if (ERRNO_IS_NEG_NOINFO(r))
|
||||
return log_unresolvable_specifier(fname, line);
|
||||
if (r < 0) {
|
||||
if (IN_SET(r, -EINVAL, -EBADSLT))
|
||||
|
@ -4537,7 +4537,7 @@ static int run(int argc, char *argv[]) {
|
|||
PHASE_CREATE,
|
||||
_PHASE_MAX
|
||||
} phase;
|
||||
int r, k;
|
||||
int r;
|
||||
|
||||
r = parse_argv(argc, argv);
|
||||
if (r <= 0)
|
||||
|
@ -4680,21 +4680,15 @@ static int run(int argc, char *argv[]) {
|
|||
continue;
|
||||
|
||||
/* The non-globbing ones usually create things, hence we apply them first */
|
||||
ORDERED_HASHMAP_FOREACH(a, c.items) {
|
||||
k = process_item_array(&c, a, op);
|
||||
if (k < 0 && r >= 0)
|
||||
r = k;
|
||||
}
|
||||
ORDERED_HASHMAP_FOREACH(a, c.items)
|
||||
RET_GATHER(r, process_item_array(&c, a, op));
|
||||
|
||||
/* The globbing ones usually alter things, hence we apply them second. */
|
||||
ORDERED_HASHMAP_FOREACH(a, c.globs) {
|
||||
k = process_item_array(&c, a, op);
|
||||
if (k < 0 && r >= 0)
|
||||
r = k;
|
||||
}
|
||||
ORDERED_HASHMAP_FOREACH(a, c.globs)
|
||||
RET_GATHER(r, process_item_array(&c, a, op));
|
||||
}
|
||||
|
||||
if (ERRNO_IS_RESOURCE(r))
|
||||
if (ERRNO_IS_NEG_RESOURCE(r))
|
||||
return r;
|
||||
if (invalid_config)
|
||||
return EX_DATAERR;
|
||||
|
|
|
@ -11,6 +11,7 @@ int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) {
|
|||
int r;
|
||||
char *value = UINT_TO_PTR(0x12345678U);
|
||||
char *endpos = UINT_TO_PTR(0x87654321U);
|
||||
bool is_case_sensitive;
|
||||
|
||||
fuzz_setup_logging();
|
||||
|
||||
|
@ -18,7 +19,7 @@ int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) {
|
|||
memcpy(str, data, size);
|
||||
str[size] = '\0';
|
||||
|
||||
r = udev_rule_parse_value(str, &value, &endpos);
|
||||
r = udev_rule_parse_value(str, &value, &endpos, &is_case_sensitive);
|
||||
if (r < 0) {
|
||||
/* not modified on failure */
|
||||
assert_se(value == UINT_TO_PTR(0x12345678U));
|
||||
|
|
|
@ -4,15 +4,16 @@
|
|||
#include "tests.h"
|
||||
#include "udev-rules.h"
|
||||
|
||||
static void test_udev_rule_parse_value_one(const char *in, const char *expected_value, int expected_retval) {
|
||||
static void test_udev_rule_parse_value_one(const char *in, const char *expected_value, bool expected_case_insensitive, int expected_retval) {
|
||||
_cleanup_free_ char *str = NULL;
|
||||
char *value = UINT_TO_PTR(0x12345678U);
|
||||
char *endpos = UINT_TO_PTR(0x87654321U);
|
||||
bool i;
|
||||
|
||||
log_info("/* %s (%s, %s, %d) */", __func__, in, strnull(expected_value), expected_retval);
|
||||
|
||||
assert_se(str = strdup(in));
|
||||
assert_se(udev_rule_parse_value(str, &value, &endpos) == expected_retval);
|
||||
assert_se(udev_rule_parse_value(str, &value, &endpos, &i) == expected_retval);
|
||||
if (expected_retval < 0) {
|
||||
/* not modified on failure */
|
||||
assert_se(value == UINT_TO_PTR(0x12345678U));
|
||||
|
@ -25,6 +26,7 @@ static void test_udev_rule_parse_value_one(const char *in, const char *expected_
|
|||
* so it could be safely interpreted as nulstr.
|
||||
*/
|
||||
assert_se(value[strlen(value) + 1] == '\0');
|
||||
assert_se(i == expected_case_insensitive);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -33,45 +35,61 @@ TEST(udev_rule_parse_value) {
|
|||
* parsed: valid operand
|
||||
* use the following command to help generate textual C strings:
|
||||
* python3 -c 'import json; print(json.dumps(input()))' */
|
||||
test_udev_rule_parse_value_one("\"valid operand\"", "valid operand", 0);
|
||||
test_udev_rule_parse_value_one("\"valid operand\"", "valid operand", /* case_insensitive = */ false, 0);
|
||||
/* input: "va'l\'id\"op\"erand"
|
||||
* parsed: va'l\'id"op"erand */
|
||||
test_udev_rule_parse_value_one("\"va'l\\'id\\\"op\\\"erand\"", "va'l\\'id\"op\"erand", 0);
|
||||
test_udev_rule_parse_value_one("no quotes", NULL, -EINVAL);
|
||||
test_udev_rule_parse_value_one("\"\\\\a\\b\\x\\y\"", "\\\\a\\b\\x\\y", 0);
|
||||
test_udev_rule_parse_value_one("\"reject\0nul\"", NULL, -EINVAL);
|
||||
test_udev_rule_parse_value_one("\"va'l\\'id\\\"op\\\"erand\"", "va'l\\'id\"op\"erand", /* case_insensitive = */ false, 0);
|
||||
test_udev_rule_parse_value_one("no quotes", NULL, /* case_insensitive = */ false, -EINVAL);
|
||||
test_udev_rule_parse_value_one("\"\\\\a\\b\\x\\y\"", "\\\\a\\b\\x\\y", /* case_insensitive = */ false, 0);
|
||||
test_udev_rule_parse_value_one("\"reject\0nul\"", NULL, /* case_insensitive = */ false, -EINVAL);
|
||||
/* input: e"" */
|
||||
test_udev_rule_parse_value_one("e\"\"", "", 0);
|
||||
test_udev_rule_parse_value_one("e\"\"", "", /* case_insensitive = */ false, 0);
|
||||
/* input: e"1234" */
|
||||
test_udev_rule_parse_value_one("e\"1234\"", "1234", 0);
|
||||
test_udev_rule_parse_value_one("e\"1234\"", "1234", /* case_insensitive = */ false, 0);
|
||||
/* input: e"\"" */
|
||||
test_udev_rule_parse_value_one("e\"\\\"\"", "\"", 0);
|
||||
test_udev_rule_parse_value_one("e\"\\\"\"", "\"", /* case_insensitive = */ false, 0);
|
||||
/* input: e"\ */
|
||||
test_udev_rule_parse_value_one("e\"\\", NULL, -EINVAL);
|
||||
test_udev_rule_parse_value_one("e\"\\", NULL, /* case_insensitive = */ false, -EINVAL);
|
||||
/* input: e"\" */
|
||||
test_udev_rule_parse_value_one("e\"\\\"", NULL, -EINVAL);
|
||||
test_udev_rule_parse_value_one("e\"\\\"", NULL, /* case_insensitive = */ false, -EINVAL);
|
||||
/* input: e"\\" */
|
||||
test_udev_rule_parse_value_one("e\"\\\\\"", "\\", 0);
|
||||
test_udev_rule_parse_value_one("e\"\\\\\"", "\\", /* case_insensitive = */ false, 0);
|
||||
/* input: e"\\\" */
|
||||
test_udev_rule_parse_value_one("e\"\\\\\\\"", NULL, -EINVAL);
|
||||
test_udev_rule_parse_value_one("e\"\\\\\\\"", NULL, /* case_insensitive = */ false, -EINVAL);
|
||||
/* input: e"\\\"" */
|
||||
test_udev_rule_parse_value_one("e\"\\\\\\\"\"", "\\\"", 0);
|
||||
test_udev_rule_parse_value_one("e\"\\\\\\\"\"", "\\\"", /* case_insensitive = */ false, 0);
|
||||
/* input: e"\\\\" */
|
||||
test_udev_rule_parse_value_one("e\"\\\\\\\\\"", "\\\\", 0);
|
||||
test_udev_rule_parse_value_one("e\"\\\\\\\\\"", "\\\\", /* case_insensitive = */ false, 0);
|
||||
/* input: e"operand with newline\n" */
|
||||
test_udev_rule_parse_value_one("e\"operand with newline\\n\"", "operand with newline\n", 0);
|
||||
test_udev_rule_parse_value_one("e\"operand with newline\\n\"", "operand with newline\n", /* case_insensitive = */ false, 0);
|
||||
/* input: e"single\rcharacter\t\aescape\bsequence" */
|
||||
test_udev_rule_parse_value_one(
|
||||
"e\"single\\rcharacter\\t\\aescape\\bsequence\"", "single\rcharacter\t\aescape\bsequence", 0);
|
||||
"e\"single\\rcharacter\\t\\aescape\\bsequence\"", "single\rcharacter\t\aescape\bsequence", /* case_insensitive = */ false, 0);
|
||||
/* input: e"reject\invalid escape sequence" */
|
||||
test_udev_rule_parse_value_one("e\"reject\\invalid escape sequence", NULL, -EINVAL);
|
||||
test_udev_rule_parse_value_one("e\"reject\\invalid escape sequence", NULL, /* case_insensitive = */ false, -EINVAL);
|
||||
/* input: e"\ */
|
||||
test_udev_rule_parse_value_one("e\"\\", NULL, -EINVAL);
|
||||
test_udev_rule_parse_value_one("e\"\\", NULL, /* case_insensitive = */ false, -EINVAL);
|
||||
/* input: "s\u1d1c\u1d04\u029c \u1d1c\u0274\u026a\u1d04\u1d0f\u1d05\u1d07 \U0001d568\U0001d560\U0001d568" */
|
||||
test_udev_rule_parse_value_one(
|
||||
"e\"s\\u1d1c\\u1d04\\u029c \\u1d1c\\u0274\\u026a\\u1d04\\u1d0f\\u1d05\\u1d07 \\U0001d568\\U0001d560\\U0001d568\"",
|
||||
"s\xe1\xb4\x9c\xe1\xb4\x84\xca\x9c \xe1\xb4\x9c\xc9\xb4\xc9\xaa\xe1\xb4\x84\xe1\xb4\x8f\xe1\xb4\x85\xe1\xb4\x87 \xf0\x9d\x95\xa8\xf0\x9d\x95\xa0\xf0\x9d\x95\xa8",
|
||||
0);
|
||||
/* case_insensitive = */ false, 0);
|
||||
/* input: i"ABCD1234" */
|
||||
test_udev_rule_parse_value_one("i\"ABCD1234\"", "ABCD1234", /* case_insensitive = */ true, 0);
|
||||
/* input: i"ABCD1234" */
|
||||
test_udev_rule_parse_value_one("e\"ABCD1234\"", "ABCD1234", /* case_insensitive = */ false, 0);
|
||||
/* input: ei"\\"ABCD1234 */
|
||||
test_udev_rule_parse_value_one("ei\"\\\\ABCD1234\"", "\\ABCD1234", /* case_insensitive = */ true, 0);
|
||||
/* input: ie"\\"ABCD1234 */
|
||||
test_udev_rule_parse_value_one("ie\"\\\\ABCD1234\"", "\\ABCD1234", /* case_insensitive = */ true, 0);
|
||||
/* input: i */
|
||||
test_udev_rule_parse_value_one("i", NULL, /* case_insensitive = */ false, -EINVAL);
|
||||
/* input: ee"" */
|
||||
test_udev_rule_parse_value_one("ee\"\"", NULL, /* case_insensitive = */ false, -EINVAL);
|
||||
/* input: iei"" */
|
||||
test_udev_rule_parse_value_one("iei\"\"", NULL, /* case_insensitive = */ false, -EINVAL);
|
||||
/* input: a"" */
|
||||
test_udev_rule_parse_value_one("a\"\"", NULL, /* case_insensitive = */ false, -EINVAL);
|
||||
}
|
||||
|
||||
DEFINE_TEST_MAIN(LOG_DEBUG);
|
||||
|
|
|
@ -52,7 +52,8 @@ static void test_event_spawn_self(const char *self, const char *arg, bool with_p
|
|||
|
||||
log_debug("/* %s(%s, %s) */", __func__, arg, yes_no(with_pidfd));
|
||||
|
||||
assert_se(cmd = strjoin(self, " ", arg));
|
||||
/* 'self' may contain spaces, hence needs to be quoted. */
|
||||
assert_se(cmd = strjoin("'", self, "' ", arg));
|
||||
|
||||
test_event_spawn_core(with_pidfd, cmd, result_buf, BUF_SIZE);
|
||||
|
||||
|
|
|
@ -63,9 +63,15 @@ typedef enum {
|
|||
MATCH_TYPE_GLOB_WITH_EMPTY, /* shell globs ?,*,[] with empty string, e.g., "|foo*" */
|
||||
MATCH_TYPE_SUBSYSTEM, /* "subsystem", "bus", or "class" */
|
||||
_MATCH_TYPE_MAX,
|
||||
_MATCH_TYPE_INVALID = -EINVAL,
|
||||
|
||||
_MATCH_TYPE_MASK = (1 << 5) - 1,
|
||||
MATCH_REMOVE_TRAILING_WHITESPACE = 1 << 5, /* Remove trailing whitespaces in attribute */
|
||||
MATCH_CASE_INSENSITIVE = 1 << 6, /* string or pattern is matched case-insensitively */
|
||||
_MATCH_TYPE_INVALID = -EINVAL,
|
||||
} UdevRuleMatchType;
|
||||
|
||||
assert_cc(_MATCH_TYPE_MAX <= _MATCH_TYPE_MASK);
|
||||
|
||||
typedef enum {
|
||||
SUBST_TYPE_PLAIN, /* no substitution */
|
||||
SUBST_TYPE_FORMAT, /* % or $ */
|
||||
|
@ -155,8 +161,7 @@ struct UdevRuleToken {
|
|||
UdevRuleTokenType type:8;
|
||||
UdevRuleOperatorType op:8;
|
||||
UdevRuleMatchType match_type:8;
|
||||
UdevRuleSubstituteType attr_subst_type:7;
|
||||
bool attr_match_remove_trailing_whitespace:1;
|
||||
UdevRuleSubstituteType attr_subst_type:8;
|
||||
const char *value;
|
||||
void *data;
|
||||
|
||||
|
@ -295,6 +300,7 @@ struct UdevRules {
|
|||
|
||||
#define log_line_invalid_op(line, key) _log_line_invalid_token(line, key, "operator")
|
||||
#define log_line_invalid_attr(line, key) _log_line_invalid_token(line, key, "attribute")
|
||||
#define log_line_invalid_prefix(line, key) _log_line_invalid_token(line, key, "prefix 'i'")
|
||||
|
||||
#define log_line_invalid_attr_format(line, key, attr, offset, hint) \
|
||||
log_line_error_errno(line, SYNTHETIC_ERRNO(EINVAL), \
|
||||
|
@ -488,12 +494,10 @@ static bool type_has_nulstr_value(UdevRuleTokenType type) {
|
|||
return type < TK_M_TEST || type == TK_M_RESULT;
|
||||
}
|
||||
|
||||
static int rule_line_add_token(UdevRuleLine *rule_line, UdevRuleTokenType type, UdevRuleOperatorType op, char *value, void *data) {
|
||||
static int rule_line_add_token(UdevRuleLine *rule_line, UdevRuleTokenType type, UdevRuleOperatorType op, char *value, void *data, bool is_case_insensitive) {
|
||||
_cleanup_(udev_rule_token_freep) UdevRuleToken *token = NULL;
|
||||
UdevRuleMatchType match_type = _MATCH_TYPE_INVALID;
|
||||
UdevRuleSubstituteType subst_type = _SUBST_TYPE_INVALID;
|
||||
bool remove_trailing_whitespace = false;
|
||||
size_t len;
|
||||
|
||||
assert(rule_line);
|
||||
assert(type >= 0 && type < _TK_TYPE_MAX);
|
||||
|
@ -552,16 +556,21 @@ static int rule_line_add_token(UdevRuleLine *rule_line, UdevRuleTokenType type,
|
|||
}
|
||||
|
||||
if (IN_SET(type, TK_M_ATTR, TK_M_PARENTS_ATTR)) {
|
||||
size_t len;
|
||||
|
||||
assert(value);
|
||||
assert(data);
|
||||
assert(match_type >= 0 && match_type < _MATCH_TYPE_MAX);
|
||||
|
||||
len = strlen(value);
|
||||
if (len > 0 && !isspace(value[len - 1]))
|
||||
remove_trailing_whitespace = true;
|
||||
match_type |= MATCH_REMOVE_TRAILING_WHITESPACE;
|
||||
|
||||
subst_type = rule_get_substitution_type(data);
|
||||
}
|
||||
|
||||
SET_FLAG(match_type, MATCH_CASE_INSENSITIVE, is_case_insensitive);
|
||||
|
||||
token = new(UdevRuleToken, 1);
|
||||
if (!token)
|
||||
return -ENOMEM;
|
||||
|
@ -573,7 +582,6 @@ static int rule_line_add_token(UdevRuleLine *rule_line, UdevRuleTokenType type,
|
|||
.data = data,
|
||||
.match_type = match_type,
|
||||
.attr_subst_type = subst_type,
|
||||
.attr_match_remove_trailing_whitespace = remove_trailing_whitespace,
|
||||
.rule_line = rule_line,
|
||||
};
|
||||
|
||||
|
@ -621,7 +629,7 @@ static int check_attr_format_and_warn(UdevRuleLine *line, const char *key, const
|
|||
return 0;
|
||||
}
|
||||
|
||||
static int parse_token(UdevRuleLine *rule_line, const char *key, char *attr, UdevRuleOperatorType op, char *value) {
|
||||
static int parse_token(UdevRuleLine *rule_line, const char *key, char *attr, UdevRuleOperatorType op, char *value, bool is_case_insensitive) {
|
||||
ResolveNameTiming resolve_name_timing = LINE_GET_RULES(rule_line)->resolve_name_timing;
|
||||
bool is_match = IN_SET(op, OP_MATCH, OP_NOMATCH);
|
||||
int r;
|
||||
|
@ -629,35 +637,39 @@ static int parse_token(UdevRuleLine *rule_line, const char *key, char *attr, Ude
|
|||
assert(key);
|
||||
assert(value);
|
||||
|
||||
if (!is_match && is_case_insensitive)
|
||||
return log_line_error_errno(rule_line, SYNTHETIC_ERRNO(EINVAL),
|
||||
"Invalid prefix 'i' for '%s'. The 'i' prefix can be specified only for '==' or '!=' operator.", key);
|
||||
|
||||
if (streq(key, "ACTION")) {
|
||||
if (attr)
|
||||
return log_line_invalid_attr(rule_line, key);
|
||||
if (!is_match)
|
||||
return log_line_invalid_op(rule_line, key);
|
||||
|
||||
r = rule_line_add_token(rule_line, TK_M_ACTION, op, value, NULL);
|
||||
r = rule_line_add_token(rule_line, TK_M_ACTION, op, value, NULL, is_case_insensitive);
|
||||
} else if (streq(key, "DEVPATH")) {
|
||||
if (attr)
|
||||
return log_line_invalid_attr(rule_line, key);
|
||||
if (!is_match)
|
||||
return log_line_invalid_op(rule_line, key);
|
||||
|
||||
r = rule_line_add_token(rule_line, TK_M_DEVPATH, op, value, NULL);
|
||||
r = rule_line_add_token(rule_line, TK_M_DEVPATH, op, value, NULL, is_case_insensitive);
|
||||
} else if (streq(key, "KERNEL")) {
|
||||
if (attr)
|
||||
return log_line_invalid_attr(rule_line, key);
|
||||
if (!is_match)
|
||||
return log_line_invalid_op(rule_line, key);
|
||||
|
||||
r = rule_line_add_token(rule_line, TK_M_KERNEL, op, value, NULL);
|
||||
r = rule_line_add_token(rule_line, TK_M_KERNEL, op, value, NULL, is_case_insensitive);
|
||||
} else if (streq(key, "SYMLINK")) {
|
||||
if (attr)
|
||||
return log_line_invalid_attr(rule_line, key);
|
||||
if (!is_match) {
|
||||
check_value_format_and_warn(rule_line, key, value, false);
|
||||
r = rule_line_add_token(rule_line, TK_A_DEVLINK, op, value, NULL);
|
||||
r = rule_line_add_token(rule_line, TK_A_DEVLINK, op, value, NULL, /* is_case_insensitive = */ false);
|
||||
} else
|
||||
r = rule_line_add_token(rule_line, TK_M_DEVLINK, op, value, NULL);
|
||||
r = rule_line_add_token(rule_line, TK_M_DEVLINK, op, value, NULL, is_case_insensitive);
|
||||
} else if (streq(key, "NAME")) {
|
||||
if (attr)
|
||||
return log_line_invalid_attr(rule_line, key);
|
||||
|
@ -677,9 +689,9 @@ static int parse_token(UdevRuleLine *rule_line, const char *key, char *attr, Ude
|
|||
"Ignoring NAME=\"\", as udev will not delete any network interfaces.");
|
||||
check_value_format_and_warn(rule_line, key, value, false);
|
||||
|
||||
r = rule_line_add_token(rule_line, TK_A_NAME, op, value, NULL);
|
||||
r = rule_line_add_token(rule_line, TK_A_NAME, op, value, NULL, /* is_case_insensitive = */ false);
|
||||
} else
|
||||
r = rule_line_add_token(rule_line, TK_M_NAME, op, value, NULL);
|
||||
r = rule_line_add_token(rule_line, TK_M_NAME, op, value, NULL, is_case_insensitive);
|
||||
} else if (streq(key, "ENV")) {
|
||||
if (isempty(attr))
|
||||
return log_line_invalid_attr(rule_line, key);
|
||||
|
@ -697,15 +709,15 @@ static int parse_token(UdevRuleLine *rule_line, const char *key, char *attr, Ude
|
|||
|
||||
check_value_format_and_warn(rule_line, key, value, false);
|
||||
|
||||
r = rule_line_add_token(rule_line, TK_A_ENV, op, value, attr);
|
||||
r = rule_line_add_token(rule_line, TK_A_ENV, op, value, attr, /* is_case_insensitive = */ false);
|
||||
} else
|
||||
r = rule_line_add_token(rule_line, TK_M_ENV, op, value, attr);
|
||||
r = rule_line_add_token(rule_line, TK_M_ENV, op, value, attr, is_case_insensitive);
|
||||
} else if (streq(key, "CONST")) {
|
||||
if (isempty(attr) || !STR_IN_SET(attr, "arch", "virt"))
|
||||
return log_line_invalid_attr(rule_line, key);
|
||||
if (!is_match)
|
||||
return log_line_invalid_op(rule_line, key);
|
||||
r = rule_line_add_token(rule_line, TK_M_CONST, op, value, attr);
|
||||
r = rule_line_add_token(rule_line, TK_M_CONST, op, value, attr, is_case_insensitive);
|
||||
} else if (streq(key, "TAG")) {
|
||||
if (attr)
|
||||
return log_line_invalid_attr(rule_line, key);
|
||||
|
@ -717,9 +729,9 @@ static int parse_token(UdevRuleLine *rule_line, const char *key, char *attr, Ude
|
|||
if (!is_match) {
|
||||
check_value_format_and_warn(rule_line, key, value, true);
|
||||
|
||||
r = rule_line_add_token(rule_line, TK_A_TAG, op, value, NULL);
|
||||
r = rule_line_add_token(rule_line, TK_A_TAG, op, value, NULL, /* is_case_insensitive = */ false);
|
||||
} else
|
||||
r = rule_line_add_token(rule_line, TK_M_TAG, op, value, NULL);
|
||||
r = rule_line_add_token(rule_line, TK_M_TAG, op, value, NULL, is_case_insensitive);
|
||||
} else if (streq(key, "SUBSYSTEM")) {
|
||||
if (attr)
|
||||
return log_line_invalid_attr(rule_line, key);
|
||||
|
@ -729,14 +741,14 @@ static int parse_token(UdevRuleLine *rule_line, const char *key, char *attr, Ude
|
|||
if (STR_IN_SET(value, "bus", "class"))
|
||||
log_line_warning(rule_line, "\"%s\" must be specified as \"subsystem\".", value);
|
||||
|
||||
r = rule_line_add_token(rule_line, TK_M_SUBSYSTEM, op, value, NULL);
|
||||
r = rule_line_add_token(rule_line, TK_M_SUBSYSTEM, op, value, NULL, is_case_insensitive);
|
||||
} else if (streq(key, "DRIVER")) {
|
||||
if (attr)
|
||||
return log_line_invalid_attr(rule_line, key);
|
||||
if (!is_match)
|
||||
return log_line_invalid_op(rule_line, key);
|
||||
|
||||
r = rule_line_add_token(rule_line, TK_M_DRIVER, op, value, NULL);
|
||||
r = rule_line_add_token(rule_line, TK_M_DRIVER, op, value, NULL, is_case_insensitive);
|
||||
} else if (streq(key, "ATTR")) {
|
||||
r = check_attr_format_and_warn(rule_line, key, attr);
|
||||
if (r < 0)
|
||||
|
@ -750,9 +762,9 @@ static int parse_token(UdevRuleLine *rule_line, const char *key, char *attr, Ude
|
|||
|
||||
if (!is_match) {
|
||||
check_value_format_and_warn(rule_line, key, value, false);
|
||||
r = rule_line_add_token(rule_line, TK_A_ATTR, op, value, attr);
|
||||
r = rule_line_add_token(rule_line, TK_A_ATTR, op, value, attr, /* is_case_insensitive = */ false);
|
||||
} else
|
||||
r = rule_line_add_token(rule_line, TK_M_ATTR, op, value, attr);
|
||||
r = rule_line_add_token(rule_line, TK_M_ATTR, op, value, attr, is_case_insensitive);
|
||||
} else if (streq(key, "SYSCTL")) {
|
||||
r = check_attr_format_and_warn(rule_line, key, attr);
|
||||
if (r < 0)
|
||||
|
@ -766,30 +778,30 @@ static int parse_token(UdevRuleLine *rule_line, const char *key, char *attr, Ude
|
|||
|
||||
if (!is_match) {
|
||||
check_value_format_and_warn(rule_line, key, value, false);
|
||||
r = rule_line_add_token(rule_line, TK_A_SYSCTL, op, value, attr);
|
||||
r = rule_line_add_token(rule_line, TK_A_SYSCTL, op, value, attr, /* is_case_insensitive = */ false);
|
||||
} else
|
||||
r = rule_line_add_token(rule_line, TK_M_SYSCTL, op, value, attr);
|
||||
r = rule_line_add_token(rule_line, TK_M_SYSCTL, op, value, attr, is_case_insensitive);
|
||||
} else if (streq(key, "KERNELS")) {
|
||||
if (attr)
|
||||
return log_line_invalid_attr(rule_line, key);
|
||||
if (!is_match)
|
||||
return log_line_invalid_op(rule_line, key);
|
||||
|
||||
r = rule_line_add_token(rule_line, TK_M_PARENTS_KERNEL, op, value, NULL);
|
||||
r = rule_line_add_token(rule_line, TK_M_PARENTS_KERNEL, op, value, NULL, is_case_insensitive);
|
||||
} else if (streq(key, "SUBSYSTEMS")) {
|
||||
if (attr)
|
||||
return log_line_invalid_attr(rule_line, key);
|
||||
if (!is_match)
|
||||
return log_line_invalid_op(rule_line, key);
|
||||
|
||||
r = rule_line_add_token(rule_line, TK_M_PARENTS_SUBSYSTEM, op, value, NULL);
|
||||
r = rule_line_add_token(rule_line, TK_M_PARENTS_SUBSYSTEM, op, value, NULL, is_case_insensitive);
|
||||
} else if (streq(key, "DRIVERS")) {
|
||||
if (attr)
|
||||
return log_line_invalid_attr(rule_line, key);
|
||||
if (!is_match)
|
||||
return log_line_invalid_op(rule_line, key);
|
||||
|
||||
r = rule_line_add_token(rule_line, TK_M_PARENTS_DRIVER, op, value, NULL);
|
||||
r = rule_line_add_token(rule_line, TK_M_PARENTS_DRIVER, op, value, NULL, is_case_insensitive);
|
||||
} else if (streq(key, "ATTRS")) {
|
||||
r = check_attr_format_and_warn(rule_line, key, attr);
|
||||
if (r < 0)
|
||||
|
@ -802,14 +814,14 @@ static int parse_token(UdevRuleLine *rule_line, const char *key, char *attr, Ude
|
|||
if (strstr(attr, "../"))
|
||||
log_line_warning(rule_line, "Direct reference to parent sysfs directory, may break in future kernels.");
|
||||
|
||||
r = rule_line_add_token(rule_line, TK_M_PARENTS_ATTR, op, value, attr);
|
||||
r = rule_line_add_token(rule_line, TK_M_PARENTS_ATTR, op, value, attr, is_case_insensitive);
|
||||
} else if (streq(key, "TAGS")) {
|
||||
if (attr)
|
||||
return log_line_invalid_attr(rule_line, key);
|
||||
if (!is_match)
|
||||
return log_line_invalid_op(rule_line, key);
|
||||
|
||||
r = rule_line_add_token(rule_line, TK_M_PARENTS_TAG, op, value, NULL);
|
||||
r = rule_line_add_token(rule_line, TK_M_PARENTS_TAG, op, value, NULL, is_case_insensitive);
|
||||
} else if (streq(key, "TEST")) {
|
||||
mode_t mode = MODE_INVALID;
|
||||
|
||||
|
@ -821,8 +833,10 @@ static int parse_token(UdevRuleLine *rule_line, const char *key, char *attr, Ude
|
|||
check_value_format_and_warn(rule_line, key, value, true);
|
||||
if (!is_match)
|
||||
return log_line_invalid_op(rule_line, key);
|
||||
if (is_case_insensitive)
|
||||
return log_line_invalid_prefix(rule_line, key);
|
||||
|
||||
r = rule_line_add_token(rule_line, TK_M_TEST, op, value, MODE_TO_PTR(mode));
|
||||
r = rule_line_add_token(rule_line, TK_M_TEST, op, value, MODE_TO_PTR(mode), is_case_insensitive);
|
||||
} else if (streq(key, "PROGRAM")) {
|
||||
if (attr)
|
||||
return log_line_invalid_attr(rule_line, key);
|
||||
|
@ -831,8 +845,10 @@ static int parse_token(UdevRuleLine *rule_line, const char *key, char *attr, Ude
|
|||
return log_line_invalid_op(rule_line, key);
|
||||
if (!is_match)
|
||||
op = OP_MATCH;
|
||||
if (is_case_insensitive)
|
||||
return log_line_invalid_prefix(rule_line, key);
|
||||
|
||||
r = rule_line_add_token(rule_line, TK_M_PROGRAM, op, value, NULL);
|
||||
r = rule_line_add_token(rule_line, TK_M_PROGRAM, op, value, NULL, /* is_case_insensitive */ false);
|
||||
} else if (streq(key, "IMPORT")) {
|
||||
if (isempty(attr))
|
||||
return log_line_invalid_attr(rule_line, key);
|
||||
|
@ -841,18 +857,20 @@ static int parse_token(UdevRuleLine *rule_line, const char *key, char *attr, Ude
|
|||
return log_line_invalid_op(rule_line, key);
|
||||
if (!is_match)
|
||||
op = OP_MATCH;
|
||||
if (is_case_insensitive)
|
||||
return log_line_invalid_prefix(rule_line, key);
|
||||
|
||||
if (streq(attr, "file"))
|
||||
r = rule_line_add_token(rule_line, TK_M_IMPORT_FILE, op, value, NULL);
|
||||
r = rule_line_add_token(rule_line, TK_M_IMPORT_FILE, op, value, NULL, /* is_case_insensitive = */ false);
|
||||
else if (streq(attr, "program")) {
|
||||
UdevBuiltinCommand cmd;
|
||||
|
||||
cmd = udev_builtin_lookup(value);
|
||||
if (cmd >= 0) {
|
||||
log_line_debug(rule_line, "Found builtin command '%s' for %s, replacing attribute.", value, key);
|
||||
r = rule_line_add_token(rule_line, TK_M_IMPORT_BUILTIN, op, value, UDEV_BUILTIN_CMD_TO_PTR(cmd));
|
||||
r = rule_line_add_token(rule_line, TK_M_IMPORT_BUILTIN, op, value, UDEV_BUILTIN_CMD_TO_PTR(cmd), /* is_case_insensitive = */ false);
|
||||
} else
|
||||
r = rule_line_add_token(rule_line, TK_M_IMPORT_PROGRAM, op, value, NULL);
|
||||
r = rule_line_add_token(rule_line, TK_M_IMPORT_PROGRAM, op, value, NULL, /* is_case_insensitive = */ false);
|
||||
} else if (streq(attr, "builtin")) {
|
||||
UdevBuiltinCommand cmd;
|
||||
|
||||
|
@ -860,13 +878,13 @@ static int parse_token(UdevRuleLine *rule_line, const char *key, char *attr, Ude
|
|||
if (cmd < 0)
|
||||
return log_line_error_errno(rule_line, SYNTHETIC_ERRNO(EINVAL),
|
||||
"Unknown builtin command: %s", value);
|
||||
r = rule_line_add_token(rule_line, TK_M_IMPORT_BUILTIN, op, value, UDEV_BUILTIN_CMD_TO_PTR(cmd));
|
||||
r = rule_line_add_token(rule_line, TK_M_IMPORT_BUILTIN, op, value, UDEV_BUILTIN_CMD_TO_PTR(cmd), /* is_case_insensitive = */ false);
|
||||
} else if (streq(attr, "db"))
|
||||
r = rule_line_add_token(rule_line, TK_M_IMPORT_DB, op, value, NULL);
|
||||
r = rule_line_add_token(rule_line, TK_M_IMPORT_DB, op, value, NULL, /* is_case_insensitive = */ false);
|
||||
else if (streq(attr, "cmdline"))
|
||||
r = rule_line_add_token(rule_line, TK_M_IMPORT_CMDLINE, op, value, NULL);
|
||||
r = rule_line_add_token(rule_line, TK_M_IMPORT_CMDLINE, op, value, NULL, /* is_case_insensitive = */ false);
|
||||
else if (streq(attr, "parent"))
|
||||
r = rule_line_add_token(rule_line, TK_M_IMPORT_PARENT, op, value, NULL);
|
||||
r = rule_line_add_token(rule_line, TK_M_IMPORT_PARENT, op, value, NULL, /* is_case_insensitive = */ false);
|
||||
else
|
||||
return log_line_invalid_attr(rule_line, key);
|
||||
} else if (streq(key, "RESULT")) {
|
||||
|
@ -875,7 +893,7 @@ static int parse_token(UdevRuleLine *rule_line, const char *key, char *attr, Ude
|
|||
if (!is_match)
|
||||
return log_line_invalid_op(rule_line, key);
|
||||
|
||||
r = rule_line_add_token(rule_line, TK_M_RESULT, op, value, NULL);
|
||||
r = rule_line_add_token(rule_line, TK_M_RESULT, op, value, NULL, is_case_insensitive);
|
||||
} else if (streq(key, "OPTIONS")) {
|
||||
char *tmp;
|
||||
|
||||
|
@ -887,24 +905,24 @@ static int parse_token(UdevRuleLine *rule_line, const char *key, char *attr, Ude
|
|||
op = OP_ASSIGN;
|
||||
|
||||
if (streq(value, "string_escape=none"))
|
||||
r = rule_line_add_token(rule_line, TK_A_OPTIONS_STRING_ESCAPE_NONE, op, NULL, NULL);
|
||||
r = rule_line_add_token(rule_line, TK_A_OPTIONS_STRING_ESCAPE_NONE, op, NULL, NULL, /* is_case_insensitive = */ false);
|
||||
else if (streq(value, "string_escape=replace"))
|
||||
r = rule_line_add_token(rule_line, TK_A_OPTIONS_STRING_ESCAPE_REPLACE, op, NULL, NULL);
|
||||
r = rule_line_add_token(rule_line, TK_A_OPTIONS_STRING_ESCAPE_REPLACE, op, NULL, NULL, /* is_case_insensitive = */ false);
|
||||
else if (streq(value, "db_persist"))
|
||||
r = rule_line_add_token(rule_line, TK_A_OPTIONS_DB_PERSIST, op, NULL, NULL);
|
||||
r = rule_line_add_token(rule_line, TK_A_OPTIONS_DB_PERSIST, op, NULL, NULL, /* is_case_insensitive = */ false);
|
||||
else if (streq(value, "watch"))
|
||||
r = rule_line_add_token(rule_line, TK_A_OPTIONS_INOTIFY_WATCH, op, NULL, INT_TO_PTR(1));
|
||||
r = rule_line_add_token(rule_line, TK_A_OPTIONS_INOTIFY_WATCH, op, NULL, INT_TO_PTR(1), /* is_case_insensitive = */ false);
|
||||
else if (streq(value, "nowatch"))
|
||||
r = rule_line_add_token(rule_line, TK_A_OPTIONS_INOTIFY_WATCH, op, NULL, INT_TO_PTR(0));
|
||||
r = rule_line_add_token(rule_line, TK_A_OPTIONS_INOTIFY_WATCH, op, NULL, INT_TO_PTR(0), /* is_case_insensitive = */ false);
|
||||
else if ((tmp = startswith(value, "static_node=")))
|
||||
r = rule_line_add_token(rule_line, TK_A_OPTIONS_STATIC_NODE, op, tmp, NULL);
|
||||
r = rule_line_add_token(rule_line, TK_A_OPTIONS_STATIC_NODE, op, tmp, NULL, /* is_case_insensitive = */ false);
|
||||
else if ((tmp = startswith(value, "link_priority="))) {
|
||||
int prio;
|
||||
|
||||
r = safe_atoi(tmp, &prio);
|
||||
if (r < 0)
|
||||
return log_line_error_errno(rule_line, r, "Failed to parse link priority '%s': %m", tmp);
|
||||
r = rule_line_add_token(rule_line, TK_A_OPTIONS_DEVLINK_PRIORITY, op, NULL, INT_TO_PTR(prio));
|
||||
r = rule_line_add_token(rule_line, TK_A_OPTIONS_DEVLINK_PRIORITY, op, NULL, INT_TO_PTR(prio), /* is_case_insensitive = */ false);
|
||||
} else if ((tmp = startswith(value, "log_level="))) {
|
||||
int level;
|
||||
|
||||
|
@ -915,7 +933,7 @@ static int parse_token(UdevRuleLine *rule_line, const char *key, char *attr, Ude
|
|||
if (level < 0)
|
||||
return log_line_error_errno(rule_line, level, "Failed to parse log level '%s': %m", tmp);
|
||||
}
|
||||
r = rule_line_add_token(rule_line, TK_A_OPTIONS_LOG_LEVEL, op, NULL, INT_TO_PTR(level));
|
||||
r = rule_line_add_token(rule_line, TK_A_OPTIONS_LOG_LEVEL, op, NULL, INT_TO_PTR(level), /* is_case_insensitive = */ false);
|
||||
} else {
|
||||
log_line_warning(rule_line, "Invalid value for OPTIONS key, ignoring: '%s'", value);
|
||||
return 0;
|
||||
|
@ -933,17 +951,17 @@ static int parse_token(UdevRuleLine *rule_line, const char *key, char *attr, Ude
|
|||
}
|
||||
|
||||
if (parse_uid(value, &uid) >= 0)
|
||||
r = rule_line_add_token(rule_line, TK_A_OWNER_ID, op, NULL, UID_TO_PTR(uid));
|
||||
r = rule_line_add_token(rule_line, TK_A_OWNER_ID, op, NULL, UID_TO_PTR(uid), /* is_case_insensitive = */ false);
|
||||
else if (resolve_name_timing == RESOLVE_NAME_EARLY &&
|
||||
rule_get_substitution_type(value) == SUBST_TYPE_PLAIN) {
|
||||
r = rule_resolve_user(rule_line, value, &uid);
|
||||
if (r < 0)
|
||||
return log_line_error_errno(rule_line, r, "Failed to resolve user name '%s': %m", value);
|
||||
|
||||
r = rule_line_add_token(rule_line, TK_A_OWNER_ID, op, NULL, UID_TO_PTR(uid));
|
||||
r = rule_line_add_token(rule_line, TK_A_OWNER_ID, op, NULL, UID_TO_PTR(uid), /* is_case_insensitive = */ false);
|
||||
} else if (resolve_name_timing != RESOLVE_NAME_NEVER) {
|
||||
check_value_format_and_warn(rule_line, key, value, true);
|
||||
r = rule_line_add_token(rule_line, TK_A_OWNER, op, value, NULL);
|
||||
r = rule_line_add_token(rule_line, TK_A_OWNER, op, value, NULL, /* is_case_insensitive = */ false);
|
||||
} else {
|
||||
log_line_debug(rule_line, "User name resolution is disabled, ignoring %s=\"%s\".", key, value);
|
||||
return 0;
|
||||
|
@ -961,17 +979,17 @@ static int parse_token(UdevRuleLine *rule_line, const char *key, char *attr, Ude
|
|||
}
|
||||
|
||||
if (parse_gid(value, &gid) >= 0)
|
||||
r = rule_line_add_token(rule_line, TK_A_GROUP_ID, op, NULL, GID_TO_PTR(gid));
|
||||
r = rule_line_add_token(rule_line, TK_A_GROUP_ID, op, NULL, GID_TO_PTR(gid), /* is_case_insensitive = */ false);
|
||||
else if (resolve_name_timing == RESOLVE_NAME_EARLY &&
|
||||
rule_get_substitution_type(value) == SUBST_TYPE_PLAIN) {
|
||||
r = rule_resolve_group(rule_line, value, &gid);
|
||||
if (r < 0)
|
||||
return log_line_error_errno(rule_line, r, "Failed to resolve group name '%s': %m", value);
|
||||
|
||||
r = rule_line_add_token(rule_line, TK_A_GROUP_ID, op, NULL, GID_TO_PTR(gid));
|
||||
r = rule_line_add_token(rule_line, TK_A_GROUP_ID, op, NULL, GID_TO_PTR(gid), /* is_case_insensitive = */ false);
|
||||
} else if (resolve_name_timing != RESOLVE_NAME_NEVER) {
|
||||
check_value_format_and_warn(rule_line, key, value, true);
|
||||
r = rule_line_add_token(rule_line, TK_A_GROUP, op, value, NULL);
|
||||
r = rule_line_add_token(rule_line, TK_A_GROUP, op, value, NULL, /* is_case_insensitive = */ false);
|
||||
} else {
|
||||
log_line_debug(rule_line, "Resolving group name is disabled, ignoring GROUP=\"%s\".", value);
|
||||
return 0;
|
||||
|
@ -989,10 +1007,10 @@ static int parse_token(UdevRuleLine *rule_line, const char *key, char *attr, Ude
|
|||
}
|
||||
|
||||
if (parse_mode(value, &mode) >= 0)
|
||||
r = rule_line_add_token(rule_line, TK_A_MODE_ID, op, NULL, MODE_TO_PTR(mode));
|
||||
r = rule_line_add_token(rule_line, TK_A_MODE_ID, op, NULL, MODE_TO_PTR(mode), /* is_case_insensitive = */ false);
|
||||
else {
|
||||
check_value_format_and_warn(rule_line, key, value, true);
|
||||
r = rule_line_add_token(rule_line, TK_A_MODE, op, value, NULL);
|
||||
r = rule_line_add_token(rule_line, TK_A_MODE, op, value, NULL, /* is_case_insensitive = */ false);
|
||||
}
|
||||
} else if (streq(key, "SECLABEL")) {
|
||||
if (isempty(attr))
|
||||
|
@ -1005,13 +1023,13 @@ static int parse_token(UdevRuleLine *rule_line, const char *key, char *attr, Ude
|
|||
op = OP_ASSIGN;
|
||||
}
|
||||
|
||||
r = rule_line_add_token(rule_line, TK_A_SECLABEL, op, value, attr);
|
||||
r = rule_line_add_token(rule_line, TK_A_SECLABEL, op, value, attr, /* is_case_insensitive = */ false);
|
||||
} else if (streq(key, "RUN")) {
|
||||
if (is_match || op == OP_REMOVE)
|
||||
return log_line_invalid_op(rule_line, key);
|
||||
check_value_format_and_warn(rule_line, key, value, true);
|
||||
if (!attr || streq(attr, "program"))
|
||||
r = rule_line_add_token(rule_line, TK_A_RUN_PROGRAM, op, value, NULL);
|
||||
r = rule_line_add_token(rule_line, TK_A_RUN_PROGRAM, op, value, NULL, /* is_case_insensitive = */ false);
|
||||
else if (streq(attr, "builtin")) {
|
||||
UdevBuiltinCommand cmd;
|
||||
|
||||
|
@ -1019,7 +1037,7 @@ static int parse_token(UdevRuleLine *rule_line, const char *key, char *attr, Ude
|
|||
if (cmd < 0)
|
||||
return log_line_error_errno(rule_line, SYNTHETIC_ERRNO(EINVAL),
|
||||
"Unknown builtin command '%s', ignoring.", value);
|
||||
r = rule_line_add_token(rule_line, TK_A_RUN_BUILTIN, op, value, UDEV_BUILTIN_CMD_TO_PTR(cmd));
|
||||
r = rule_line_add_token(rule_line, TK_A_RUN_BUILTIN, op, value, UDEV_BUILTIN_CMD_TO_PTR(cmd), /* is_case_insensitive = */ false);
|
||||
} else
|
||||
return log_line_invalid_attr(rule_line, key);
|
||||
} else if (streq(key, "GOTO")) {
|
||||
|
@ -1119,13 +1137,30 @@ static void check_token_delimiters(UdevRuleLine *rule_line, const char *line) {
|
|||
}
|
||||
}
|
||||
|
||||
int udev_rule_parse_value(char *str, char **ret_value, char **ret_endpos) {
|
||||
int udev_rule_parse_value(char *str, char **ret_value, char **ret_endpos, bool *ret_is_case_insensitive) {
|
||||
char *i, *j;
|
||||
bool is_escaped;
|
||||
bool is_escaped = false, is_case_insensitive = false;
|
||||
|
||||
assert(str);
|
||||
assert(ret_value);
|
||||
assert(ret_endpos);
|
||||
assert(ret_is_case_insensitive);
|
||||
|
||||
/* check if string is prefixed with:
|
||||
* - "e" for escaped
|
||||
* - "i" for case insensitive match
|
||||
*
|
||||
* Note both e and i can be set but do not allow duplicates ("eei", "eii"). */
|
||||
for (const char *k = str; *k != '"' && k < str + 2; k++)
|
||||
if (*k == 'e' && !is_escaped)
|
||||
is_escaped = true;
|
||||
else if (*k == 'i' && !is_case_insensitive)
|
||||
is_case_insensitive = true;
|
||||
else
|
||||
return -EINVAL;
|
||||
|
||||
/* value must be double quotated */
|
||||
is_escaped = str[0] == 'e';
|
||||
str += is_escaped;
|
||||
str += is_escaped + is_case_insensitive;
|
||||
if (str[0] != '"')
|
||||
return -EINVAL;
|
||||
|
||||
|
@ -1172,10 +1207,11 @@ int udev_rule_parse_value(char *str, char **ret_value, char **ret_endpos) {
|
|||
|
||||
*ret_value = str;
|
||||
*ret_endpos = i + 1;
|
||||
*ret_is_case_insensitive = is_case_insensitive;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int parse_line(char **line, char **ret_key, char **ret_attr, UdevRuleOperatorType *ret_op, char **ret_value) {
|
||||
static int parse_line(char **line, char **ret_key, char **ret_attr, UdevRuleOperatorType *ret_op, char **ret_value, bool *ret_is_case_insensitive) {
|
||||
char *key_begin, *key_end, *attr, *tmp;
|
||||
UdevRuleOperatorType op;
|
||||
int r;
|
||||
|
@ -1185,6 +1221,7 @@ static int parse_line(char **line, char **ret_key, char **ret_attr, UdevRuleOper
|
|||
assert(ret_key);
|
||||
assert(ret_op);
|
||||
assert(ret_value);
|
||||
assert(ret_is_case_insensitive);
|
||||
|
||||
key_begin = skip_leading_chars(*line, WHITESPACE ",");
|
||||
|
||||
|
@ -1219,7 +1256,7 @@ static int parse_line(char **line, char **ret_key, char **ret_attr, UdevRuleOper
|
|||
|
||||
tmp += op == OP_ASSIGN ? 1 : 2;
|
||||
tmp = skip_leading_chars(tmp, NULL);
|
||||
r = udev_rule_parse_value(tmp, ret_value, line);
|
||||
r = udev_rule_parse_value(tmp, ret_value, line, ret_is_case_insensitive);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
|
@ -1291,17 +1328,18 @@ static int rule_add_line(UdevRuleFile *rule_file, const char *line_str, unsigned
|
|||
for (p = rule_line->line; !isempty(p); ) {
|
||||
char *key, *attr, *value;
|
||||
UdevRuleOperatorType op;
|
||||
bool is_case_insensitive;
|
||||
|
||||
if (extra_checks)
|
||||
check_token_delimiters(rule_line, p);
|
||||
|
||||
r = parse_line(&p, &key, &attr, &op, &value);
|
||||
r = parse_line(&p, &key, &attr, &op, &value, &is_case_insensitive);
|
||||
if (r < 0)
|
||||
return log_line_error_errno(rule_line, r, "Invalid key/value pair, ignoring.");
|
||||
if (r == 0)
|
||||
break;
|
||||
|
||||
r = parse_token(rule_line, key, attr, op, value);
|
||||
r = parse_token(rule_line, key, attr, op, value, is_case_insensitive);
|
||||
if (r < 0)
|
||||
return r;
|
||||
}
|
||||
|
@ -1412,7 +1450,6 @@ static bool tokens_eq(const UdevRuleToken *a, const UdevRuleToken *b) {
|
|||
assert(b);
|
||||
|
||||
return a->attr_subst_type == b->attr_subst_type &&
|
||||
a->attr_match_remove_trailing_whitespace == b->attr_match_remove_trailing_whitespace &&
|
||||
token_type_and_value_eq(a, b) &&
|
||||
token_type_and_data_eq(a, b);
|
||||
}
|
||||
|
@ -1427,7 +1464,6 @@ static bool nulstr_tokens_conflict(const UdevRuleToken *a, const UdevRuleToken *
|
|||
a->op == OP_MATCH &&
|
||||
a->match_type == b->match_type &&
|
||||
a->attr_subst_type == b->attr_subst_type &&
|
||||
a->attr_match_remove_trailing_whitespace == b->attr_match_remove_trailing_whitespace &&
|
||||
token_type_and_data_eq(a, b)))
|
||||
return false;
|
||||
|
||||
|
@ -1696,7 +1732,7 @@ bool udev_rules_should_reload(UdevRules *rules) {
|
|||
|
||||
static bool token_match_string(UdevRuleToken *token, const char *str) {
|
||||
const char *value;
|
||||
bool match = false;
|
||||
bool match = false, case_insensitive;
|
||||
|
||||
assert(token);
|
||||
assert(token->value);
|
||||
|
@ -1704,13 +1740,17 @@ static bool token_match_string(UdevRuleToken *token, const char *str) {
|
|||
|
||||
str = strempty(str);
|
||||
value = token->value;
|
||||
case_insensitive = FLAGS_SET(token->match_type, MATCH_CASE_INSENSITIVE);
|
||||
|
||||
switch (token->match_type) {
|
||||
switch (token->match_type & _MATCH_TYPE_MASK) {
|
||||
case MATCH_TYPE_EMPTY:
|
||||
match = isempty(str);
|
||||
break;
|
||||
case MATCH_TYPE_SUBSYSTEM:
|
||||
match = STR_IN_SET(str, "subsystem", "class", "bus");
|
||||
if (case_insensitive)
|
||||
match = STRCASE_IN_SET(str, "subsystem", "class", "bus");
|
||||
else
|
||||
match = STR_IN_SET(str, "subsystem", "class", "bus");
|
||||
break;
|
||||
case MATCH_TYPE_PLAIN_WITH_EMPTY:
|
||||
if (isempty(str)) {
|
||||
|
@ -1720,7 +1760,7 @@ static bool token_match_string(UdevRuleToken *token, const char *str) {
|
|||
_fallthrough_;
|
||||
case MATCH_TYPE_PLAIN:
|
||||
NULSTR_FOREACH(i, value)
|
||||
if (streq(i, str)) {
|
||||
if (case_insensitive ? strcaseeq(i, str) : streq(i, str)) {
|
||||
match = true;
|
||||
break;
|
||||
}
|
||||
|
@ -1733,7 +1773,7 @@ static bool token_match_string(UdevRuleToken *token, const char *str) {
|
|||
_fallthrough_;
|
||||
case MATCH_TYPE_GLOB:
|
||||
NULSTR_FOREACH(i, value)
|
||||
if ((fnmatch(i, str, 0) == 0)) {
|
||||
if ((fnmatch(i, str, case_insensitive ? FNM_CASEFOLD : 0) == 0)) {
|
||||
match = true;
|
||||
break;
|
||||
}
|
||||
|
@ -1773,7 +1813,7 @@ static bool token_match_attr(UdevRuleToken *token, sd_device *dev, UdevEvent *ev
|
|||
return false;
|
||||
|
||||
/* remove trailing whitespace, if not asked to match for it */
|
||||
if (token->attr_match_remove_trailing_whitespace) {
|
||||
if (FLAGS_SET(token->match_type, MATCH_REMOVE_TRAILING_WHITESPACE)) {
|
||||
strscpy(vbuf, sizeof(vbuf), value);
|
||||
value = delete_trailing_chars(vbuf, NULL);
|
||||
}
|
||||
|
@ -1785,7 +1825,7 @@ static bool token_match_attr(UdevRuleToken *token, sd_device *dev, UdevEvent *ev
|
|||
return false;
|
||||
|
||||
/* remove trailing whitespace, if not asked to match for it */
|
||||
if (token->attr_match_remove_trailing_whitespace)
|
||||
if (FLAGS_SET(token->match_type, MATCH_REMOVE_TRAILING_WHITESPACE))
|
||||
delete_trailing_chars(vbuf, NULL);
|
||||
|
||||
return token_match_string(token, vbuf);
|
||||
|
|
|
@ -29,7 +29,7 @@ typedef enum ResolveNameTiming {
|
|||
_RESOLVE_NAME_TIMING_INVALID = -EINVAL,
|
||||
} ResolveNameTiming;
|
||||
|
||||
int udev_rule_parse_value(char *str, char **ret_value, char **ret_endpos);
|
||||
int udev_rule_parse_value(char *str, char **ret_value, char **ret_endpos, bool *ret_is_case_insensitive);
|
||||
int udev_rules_parse_file(UdevRules *rules, const char *filename, bool extra_checks, UdevRuleFile **ret);
|
||||
unsigned udev_rule_file_get_issues(UdevRuleFile *rule_file);
|
||||
UdevRules* udev_rules_new(ResolveNameTiming resolve_name_timing);
|
||||
|
|
|
@ -526,9 +526,6 @@ def call_systemd_measure(uki, linux, opts):
|
|||
|
||||
# First, pick up the sections we shall measure now */
|
||||
for s in uki.sections:
|
||||
|
||||
print(s)
|
||||
|
||||
if not s.measure:
|
||||
continue
|
||||
|
||||
|
|
|
@ -288,7 +288,7 @@ static int verb_info(int argc, char *argv[], void *userdata) {
|
|||
pager_open(arg_pager_flags);
|
||||
|
||||
if (FLAGS_SET(arg_json_format_flags, SD_JSON_FORMAT_OFF)) {
|
||||
static const struct sd_json_dispatch_field dispatch_table[] = {
|
||||
static const sd_json_dispatch_field dispatch_table[] = {
|
||||
{ "vendor", SD_JSON_VARIANT_STRING, sd_json_dispatch_const_string, offsetof(GetInfoData, vendor), SD_JSON_MANDATORY },
|
||||
{ "product", SD_JSON_VARIANT_STRING, sd_json_dispatch_const_string, offsetof(GetInfoData, product), SD_JSON_MANDATORY },
|
||||
{ "version", SD_JSON_VARIANT_STRING, sd_json_dispatch_const_string, offsetof(GetInfoData, version), SD_JSON_MANDATORY },
|
||||
|
@ -380,12 +380,12 @@ static int verb_introspect(int argc, char *argv[], void *userdata) {
|
|||
if (r < 0)
|
||||
return r;
|
||||
|
||||
const struct sd_json_dispatch_field dispatch_table[] = {
|
||||
{ "interfaces", SD_JSON_VARIANT_ARRAY, sd_json_dispatch_strv, PTR_TO_SIZE(&auto_interfaces), SD_JSON_MANDATORY },
|
||||
static const sd_json_dispatch_field dispatch_table[] = {
|
||||
{ "interfaces", SD_JSON_VARIANT_ARRAY, sd_json_dispatch_strv, 0, SD_JSON_MANDATORY },
|
||||
{}
|
||||
};
|
||||
|
||||
r = sd_json_dispatch(reply, dispatch_table, SD_JSON_LOG|SD_JSON_ALLOW_EXTENSIONS, NULL);
|
||||
r = sd_json_dispatch(reply, dispatch_table, SD_JSON_LOG|SD_JSON_ALLOW_EXTENSIONS, &auto_interfaces);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
|
@ -412,7 +412,7 @@ static int verb_introspect(int argc, char *argv[], void *userdata) {
|
|||
return r;
|
||||
|
||||
if (FLAGS_SET(arg_json_format_flags, SD_JSON_FORMAT_OFF) || list_methods) {
|
||||
static const struct sd_json_dispatch_field dispatch_table[] = {
|
||||
static const sd_json_dispatch_field dispatch_table[] = {
|
||||
{ "description", SD_JSON_VARIANT_STRING, sd_json_dispatch_const_string, 0, SD_JSON_MANDATORY },
|
||||
{}
|
||||
};
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue