Compare commits

...

23 Commits

Author SHA1 Message Date
Lennart Poettering cfdc0c6721
Merge 95b1dd00b6 into 9671efff78 2024-09-15 23:02:18 -07:00
Yu Watanabe 9671efff78 NEWS: fix typo
Follow-up for dcc359010c.
2024-09-16 11:50:48 +09:00
Yu Watanabe 4f0bc2582e man: fix typo
Follow-up for a632d8dd9f.
2024-09-16 11:49:04 +09:00
Yu Watanabe 3292120adf nspawn: fix typo
Follow-up for d7a6bb9891.
2024-09-16 11:47:43 +09:00
Yu Watanabe f6cc5e1c8d
Merge pull request #34393 from poettering/tmpfiles-ownership-flag
tmpfiles: introduce an explicit line flag $ for enabling purge logic …
2024-09-16 10:51:09 +09:00
Yu Watanabe 590f430cac
Merge pull request #34425 from yuwata/udev-rules-case-insensitive-match
udev-rules: support case insensitive match
2024-09-16 10:42:37 +09:00
Mike Yuan 93d2d36638 basic/build: also include BTF status 2024-09-16 10:42:16 +09:00
Lennart Poettering 369b12375b coredump: use _cleanup_(iovec_done) where appropriate 2024-09-16 10:42:02 +09:00
Yu Watanabe b5ec8f77e0
Merge pull request #34434 from poettering/bootctl-stub-paths
bootctl: expose new stub path efi vars and related
2024-09-16 10:41:24 +09:00
Lennart Poettering 3e0a3a0259 bootctl: show whether a PE file is an addon in 'booctl kernel-identify' 2024-09-16 10:41:10 +09:00
Celeste Liu 6573f0c82c hwdb: add Kensington SlimBlade Pro trackball (Bluetooth mode)
Wired and 2.4G dongle connectivity is covered by general trackball rule,
but with Bluetooth connectivity Kensington SlimBlade Pro uses the name
"SlimBlade Pro" which doesn't contain "[Tt]rack[Bb]all". We need to
process it specially.

Signed-off-by: Celeste Liu <CoelacanthusHex@gmail.com>
2024-09-16 10:40:56 +09:00
Lennart Poettering a859d0d378 tmpfiles.d: add $ flag to all lines which are clearly private to our packages, and should be removed on package removal
(This excludes any dirs that contain resources placed there by the user)

(I also didn't bother marking resources belonging to components that are
really not optional for us)
2024-09-15 19:44:05 +02:00
Lennart Poettering db15657dfb tmpfiles: introduce an explicit line flag $ for enabling purge logic for a line
Let's make the risk of accidental misuse, and mark lines that shall be
covered by --purge with an explicit new flag "$".

See: #33349
2024-09-15 19:43:09 +02:00
Lennart Poettering 2aa3005ad2 bootctl: also show current/default/oneshot entry literally in output 2024-09-15 19:34:19 +02:00
Lennart Poettering 90cf998875 bootctl: add --print-loader-path + --print-stub-path
These are inspired by the existing commands that return the path to the
boot or ESP partitions. However, these new commands show the path to the
boot loader (systemd-boot) or UKI/stub (systemd-stub) that was used on
the current boot. This information is derived from EFI variables.
2024-09-15 19:34:19 +02:00
Lennart Poettering c8d60ae79d efivars: add helper that reads an fs path from an efi var 2024-09-15 19:34:19 +02:00
Lennart Poettering bfcf48b842 bootctl: show stub partition data too in "status" too 2024-09-15 19:33:48 +02:00
Yu Watanabe 5f5c5c48b9 udev-rules: support case insensitive match
This introduces 'i' prefix for match string. When specified, string or
pattern will match case-insensitively.

Closes #34359.

Co-authored-by: Ryan Wilson <ryantimwilson@meta.com>
2024-09-15 23:09:26 +09:00
Yu Watanabe 68fdef46a7 udev-rules: embed UdevRuleToken.attr_match_remove_trailing_whitespace flag into UdevRuleMatchType
No functional change, just refactoring and preparation for later change.
2024-09-15 13:52:50 +09:00
Lennart Poettering 95b1dd00b6 resolvectl: show delegate information, too 2024-09-12 10:01:15 +02:00
Lennart Poettering fe09c8398e resolvectl: rework parsing of dns server + search domain bus properties
Let's handle the per-link and the global dns server/search domain
property parsing the same. Let's use a flags field for three separate
booleans, and unify more code.
2024-09-12 10:01:15 +02:00
Lennart Poettering da1b3c55ff resolved: add concept of delegating lookups below certain domains to specific DNS servers
For now, this allows only static configuration, but eventually we should
open this up to IPC.

Fixes: #5573 #14159 #20485 #21260 #24532 #32022

(Fixes #32022, because now redundant)
2024-09-12 10:01:15 +02:00
Lennart Poettering 568bf0b4e8 resolved: add a new DnsScopeOrigin enum, to delcare the "origin" of a DnsScope explicitly
This new enum field is supposed to indicate why a DnsScope came to be.
For now it distinguishes two origins: the "global" one (which is what is
configured in resolved.conf) and "link" ones (which are synthesized for
each link).

The field as is is pretty redundant, the same information can be
determined from whether the .link field is set or not.

This is pretty much just preparation for later commits that add
statically configured additional DnsScopes whose origin shall be encoded
with this.
2024-09-12 10:01:15 +02:00
61 changed files with 1810 additions and 450 deletions

13
NEWS
View File

@ -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:

View File

@ -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
##########################################

View File

@ -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>

View File

@ -115,6 +115,9 @@ node /org/freedesktop/resolve1 {
ResetStatistics();
FlushCaches();
ResetServerFeatures();
GetDelegate(in s id,
out o path);
ListDelegates(out a(so) delegates);
properties:
readonly s LLMNRHostname = '...';
@org.freedesktop.DBus.Property.EmitsChangedSignal("false")
@ -202,6 +205,10 @@ node /org/freedesktop/resolve1 {
<variablelist class="dbus-method" generated="True" extra-ref="ResetServerFeatures()"/>
<variablelist class="dbus-method" generated="True" extra-ref="GetDelegate()"/>
<variablelist class="dbus-method" generated="True" extra-ref="ListDelegates()"/>
<variablelist class="dbus-property" generated="True" extra-ref="LLMNRHostname"/>
<variablelist class="dbus-property" generated="True" extra-ref="LLMNR"/>
@ -423,13 +430,13 @@ node /org/freedesktop/resolve1 {
<para>The <function>RevertLink()</function> method may be used to revert all per-link settings
described above to the defaults.</para>
<para>The <function>FlushCaches()</function> flushes all resource record caches maintained by the
<para>The <function>FlushCaches()</function> method flushes all resource record caches maintained by the
resolver, and ensures that any subsequent lookups re-request their responses from their sources.</para>
<para>The <function>ResetServerFeatures()</function> flushes any feature information learned about
remote DNS servers. This ensures that subsequent lookups will be initially attempted at the highest DNS
protocol feature level again, possibly requiring a (potentially slow) downgrade cycle to recognize the
supported feature level again.</para>
<para>The <function>ResetServerFeatures()</function> method flushes any feature information learned
about remote DNS servers. This ensures that subsequent lookups will be initially attempted at the
highest DNS protocol feature level again, possibly requiring a (potentially slow) downgrade cycle to
recognize the supported feature level again.</para>
<para>The <function>RegisterService()</function> method may be used to register a DNS-SD service on the
host. This functionality is closely related to the functionality provided by
@ -447,6 +454,12 @@ node /org/freedesktop/resolve1 {
<function>RegisterService()</function> and deletes a DNS-SD service previously created via IPC
again.</para>
<para>The <function>GetDelegate()</function> method returns the D-Bus object path for the specified DNS
delegate ID.</para>
<para>The <function>ListDelegates()</function> method returns a list of the IDs and D-Bus object paths
of the currently configured DNS delegates.</para>
<refsect3>
<title>The Flags Parameter</title>
@ -935,4 +948,14 @@ $ gdbus introspect --system \
</refsect1>
<xi:include href="org.freedesktop.locale1.xml" xpointer="versioning"/>
<refsect1>
<title>History</title>
<refsect2>
<title>The Manager Object</title>
<para><function>GetDelegate()</function> and <function>ListDelegates()</function> were added in version 257.</para>
</refsect2>
</refsect1>
</refentry>

View File

@ -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>

View File

@ -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

View File

@ -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>,

View File

@ -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>

View File

@ -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;

View File

@ -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;
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;

View File

@ -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, '\\', '/');
}

View File

@ -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",

View File

@ -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), &current_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");
if (loader) {
printf("%sCurrent Boot Loader:%s\n", ansi_underline(), ansi_normal());
printf(" Product: %s%s%s\n", ansi_highlight(), strna(loader), 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);
sd_id128_t bootloader_esp_uuid;
bool have_bootloader_esp_uuid = efi_loader_get_device_part_uuid(&bootloader_esp_uuid) >= 0;
sd_id128_t loader_partition_uuid;
bool have_loader_partition_uuid = efi_loader_get_device_part_uuid(&loader_partition_uuid) >= 0;
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));
print_yes_no_line(false, have_loader_partition_uuid, "Boot loader set ESP information");
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);
}
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));
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(" ESP: n/a\n");
printf(" File: %s%s\n", special_glyph(SPECIAL_GLYPH_TREE_RIGHT), strna(loader_path));
printf(" Partition: n/a\n");
printf(" Stub: %s%s\n", special_glyph(SPECIAL_GLYPH_TREE_RIGHT), strna(stub_path));
printf("\n");
}
printf("%sRandom Seed:%s\n", ansi_underline(), ansi_normal());
have = access(EFIVAR_PATH(EFI_LOADER_VARIABLE(LoaderSystemToken)), F_OK) >= 0;

View File

@ -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);

View File

@ -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 */

View File

@ -85,6 +85,7 @@ BUS_ERROR_MAP_ELF_REGISTER const sd_bus_error_map bus_common_errors[] = {
SD_BUS_ERROR_MAP(BUS_ERROR_STUB_LOOP, ELOOP),
SD_BUS_ERROR_MAP(BUS_ERROR_NO_SUCH_DNSSD_SERVICE, ENOENT),
SD_BUS_ERROR_MAP(BUS_ERROR_DNSSD_SERVICE_EXISTS, EEXIST),
SD_BUS_ERROR_MAP(BUS_ERROR_NO_SUCH_DELEGATE, ENXIO),
SD_BUS_ERROR_MAP(BUS_ERROR_DNS_FORMERR, EBADMSG),
SD_BUS_ERROR_MAP(BUS_ERROR_DNS_SERVFAIL, EHOSTDOWN),

View File

@ -85,6 +85,7 @@
#define BUS_ERROR_STUB_LOOP "org.freedesktop.resolve1.StubLoop"
#define BUS_ERROR_NO_SUCH_DNSSD_SERVICE "org.freedesktop.resolve1.NoSuchDnssdService"
#define BUS_ERROR_DNSSD_SERVICE_EXISTS "org.freedesktop.resolve1.DnssdServiceExists"
#define BUS_ERROR_NO_SUCH_DELEGATE "org.freedesktop.resolve1.NoSuchDelegate"
#define _BUS_ERROR_DNS "org.freedesktop.resolve1.DnsError."
#define BUS_ERROR_DNS_FORMERR _BUS_ERROR_DNS "FORMERR"

View File

@ -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) {

View File

@ -16,6 +16,8 @@ systemd_resolved_sources = files(
'resolved-bus.c',
'resolved-conf.c',
'resolved-dns-cache.c',
'resolved-dns-delegate.c',
'resolved-dns-delegate-bus.c',
'resolved-dns-query.c',
'resolved-dns-scope.c',
'resolved-dns-search-domain.c',
@ -100,6 +102,12 @@ systemd_resolved_sources += custom_target(
output : 'resolved-dnssd-gperf.c',
command : [gperf, '@INPUT@', '--output-file', '@OUTPUT@'])
systemd_resolved_sources += custom_target(
'resolved_dns_delegate_gperf.c',
input : 'resolved-dns-delegate-gperf.gperf',
output : 'resolved-dns-delegate-gperf.c',
command : [gperf, '@INPUT@', '--output-file', '@OUTPUT@'])
systemd_resolved_dependencies = [threads, libm] + [lib_openssl_or_gcrypt]
if conf.get('ENABLE_DNS_OVER_TLS') == 1
if conf.get('DNS_OVER_TLS_USE_GNUTLS') == 1

View File

@ -1354,11 +1354,36 @@ static int reset_server_features(int argc, char **argv, void *userdata) {
return 0;
}
typedef enum DnsServerPropertyFlags {
DNS_SERVER_WITH_IFINDEX = 1 << 0, /* prefixed with a field for an ifindex */
DNS_SERVER_WITH_PORT_NUMBER_AND_SERVER_NAME = 1 << 1, /* suffix with port nr and server name */
DNS_SERVER_ONLY_GLOBAL = 1 << 2, /* filter entries with an (non-loopback) ifindex set (i.e. which are specific to some interface) */
_DNS_SERVER_PROPERTY_FLAGS_MAX = DNS_SERVER_WITH_IFINDEX|DNS_SERVER_WITH_PORT_NUMBER_AND_SERVER_NAME|DNS_SERVER_ONLY_GLOBAL,
} DnsServerPropertyFlags;
static const char *dns_server_property_signature(DnsServerPropertyFlags flags) {
switch (flags & (DNS_SERVER_WITH_IFINDEX|DNS_SERVER_WITH_PORT_NUMBER_AND_SERVER_NAME)) {
case 0:
return "iay";
case DNS_SERVER_WITH_IFINDEX:
return "iiay";
case DNS_SERVER_WITH_PORT_NUMBER_AND_SERVER_NAME:
return "iayqs";
case DNS_SERVER_WITH_IFINDEX|DNS_SERVER_WITH_PORT_NUMBER_AND_SERVER_NAME:
return "iiayqs";
default:
assert_not_reached();
}
}
static int read_dns_server_one(
sd_bus_message *m,
bool with_ifindex, /* read "ifindex" reply that also carries an interface index */
bool extended, /* read "extended" reply, i.e. with port number and server name */
bool only_global, /* suppress entries with an (non-loopback) ifindex set (i.e. which are specific to some interface) */
DnsServerPropertyFlags flags,
char **ret) {
_cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
@ -1375,12 +1400,11 @@ static int read_dns_server_one(
r = sd_bus_message_enter_container(
m,
'r',
with_ifindex ? (extended ? "iiayqs" : "iiay") :
(extended ? "iayqs" : "iay"));
dns_server_property_signature(flags));
if (r <= 0)
return r;
if (with_ifindex) {
if (FLAGS_SET(flags, DNS_SERVER_WITH_IFINDEX)) {
r = sd_bus_message_read(m, "i", &ifindex);
if (r < 0)
return r;
@ -1390,12 +1414,8 @@ static int read_dns_server_one(
if (k < 0 && !sd_bus_error_has_name(&error, SD_BUS_ERROR_INVALID_ARGS))
return k;
if (extended) {
r = sd_bus_message_read(m, "q", &port);
if (r < 0)
return r;
r = sd_bus_message_read(m, "s", &name);
if (FLAGS_SET(flags, DNS_SERVER_WITH_PORT_NUMBER_AND_SERVER_NAME)) {
r = sd_bus_message_read(m, "qs", &port, &name);
if (r < 0)
return r;
}
@ -1410,7 +1430,7 @@ static int read_dns_server_one(
return 1;
}
if (only_global && ifindex > 0 && ifindex != LOOPBACK_IFINDEX) {
if (FLAGS_SET(flags, DNS_SERVER_ONLY_GLOBAL) && ifindex > 0 && ifindex != LOOPBACK_IFINDEX) {
/* This one has an (non-loopback) ifindex set, and we were told to suppress those. Hence do so. */
*ret = NULL;
return 1;
@ -1424,7 +1444,14 @@ static int read_dns_server_one(
return 1;
}
static int map_link_dns_servers_internal(sd_bus *bus, const char *member, sd_bus_message *m, sd_bus_error *error, void *userdata, bool extended) {
static int map_dns_servers_internal(
sd_bus *bus,
const char *member,
sd_bus_message *m,
DnsServerPropertyFlags flags,
sd_bus_error *error,
void *userdata) {
char ***l = ASSERT_PTR(userdata);
int r;
@ -1432,14 +1459,16 @@ static int map_link_dns_servers_internal(sd_bus *bus, const char *member, sd_bus
assert(member);
assert(m);
r = sd_bus_message_enter_container(m, 'a', extended ? "(iayqs)" : "(iay)");
const char *sig = strjoina("(", dns_server_property_signature(flags), ")");
r = sd_bus_message_enter_container(m, 'a', sig);
if (r < 0)
return r;
for (;;) {
_cleanup_free_ char *pretty = NULL;
r = read_dns_server_one(m, /* with_ifindex= */ false, extended, /* only_global= */ false, &pretty);
r = read_dns_server_one(m, flags, &pretty);
if (r < 0)
return r;
if (r == 0)
@ -1461,25 +1490,25 @@ static int map_link_dns_servers_internal(sd_bus *bus, const char *member, sd_bus
}
static int map_link_dns_servers(sd_bus *bus, const char *member, sd_bus_message *m, sd_bus_error *error, void *userdata) {
return map_link_dns_servers_internal(bus, member, m, error, userdata, false);
return map_dns_servers_internal(bus, member, m, /* flags= */ 0, error, userdata);
}
static int map_link_dns_servers_ex(sd_bus *bus, const char *member, sd_bus_message *m, sd_bus_error *error, void *userdata) {
return map_link_dns_servers_internal(bus, member, m, error, userdata, true);
return map_dns_servers_internal(bus, member, m, DNS_SERVER_WITH_PORT_NUMBER_AND_SERVER_NAME, error, userdata);
}
static int map_link_current_dns_server(sd_bus *bus, const char *member, sd_bus_message *m, sd_bus_error *error, void *userdata) {
assert(m);
assert(userdata);
return read_dns_server_one(m, /* with_ifindex= */ false, /* extended= */ false, /* only_global= */ false, userdata);
return read_dns_server_one(m, /* flags= */ 0, userdata);
}
static int map_link_current_dns_server_ex(sd_bus *bus, const char *member, sd_bus_message *m, sd_bus_error *error, void *userdata) {
assert(m);
assert(userdata);
return read_dns_server_one(m, /* with_ifindex= */ false, /* extended= */ true, /* only_global= */ false, userdata);
return read_dns_server_one(m, DNS_SERVER_WITH_PORT_NUMBER_AND_SERVER_NAME, userdata);
}
static int read_domain_one(sd_bus_message *m, bool with_ifindex, char **ret) {
@ -1511,11 +1540,17 @@ static int read_domain_one(sd_bus_message *m, bool with_ifindex, char **ret) {
return -ENOMEM;
*ret = TAKE_PTR(str);
return 1;
}
static int map_link_domains(sd_bus *bus, const char *member, sd_bus_message *m, sd_bus_error *error, void *userdata) {
static int map_domains_internal(
sd_bus *bus,
const char *member,
sd_bus_message *m,
bool with_ifindex,
sd_bus_error *error,
void *userdata) {
char ***l = ASSERT_PTR(userdata);
int r;
@ -1523,14 +1558,14 @@ static int map_link_domains(sd_bus *bus, const char *member, sd_bus_message *m,
assert(member);
assert(m);
r = sd_bus_message_enter_container(m, 'a', "(sb)");
r = sd_bus_message_enter_container(m, 'a', with_ifindex ? "(isb)" : "(sb)");
if (r < 0)
return r;
for (;;) {
_cleanup_free_ char *pretty = NULL;
r = read_domain_one(m, false, &pretty);
r = read_domain_one(m, with_ifindex, &pretty);
if (r < 0)
return r;
if (r == 0)
@ -1551,12 +1586,18 @@ static int map_link_domains(sd_bus *bus, const char *member, sd_bus_message *m,
return 0;
}
static int status_print_strv_ifindex(int ifindex, const char *ifname, char **p) {
static int map_link_domains(sd_bus *bus, const char *member, sd_bus_message *m, sd_bus_error *error, void *userdata) {
return map_domains_internal(bus, member, m, /* with_ifindex= */ false, error, userdata);
}
static int status_print_strv_full(int ifindex, const char *ifname, const char *delegate_id, char **p) {
const unsigned indent = strlen("Global: "); /* Use the same indentation everywhere to make things nice */
int pos1, pos2;
if (ifname)
printf("%s%nLink %i (%s)%n%s:", ansi_highlight(), &pos1, ifindex, ifname, &pos2, ansi_normal());
else if (delegate_id)
printf("%s%nDelegate %s%n%s:", ansi_highlight(), &pos1, delegate_id, &pos2, ansi_normal());
else
printf("%s%nGlobal%n%s:", ansi_highlight(), &pos1, &pos2, ansi_normal());
@ -1580,6 +1621,14 @@ static int status_print_strv_ifindex(int ifindex, const char *ifname, char **p)
return 0;
}
static int status_print_strv_ifindex(int ifindex, const char *ifname, char **p) {
return status_print_strv_full(ifindex, ifname, NULL, p);
}
static int status_print_strv_delegate(const char *delegate_id, char **p) {
return status_print_strv_full(0, NULL, delegate_id, p);
}
static int status_print_strv_global(char **p) {
return status_print_strv_ifindex(0, NULL, p);
}
@ -1899,101 +1948,24 @@ static int status_ifindex(sd_bus *bus, int ifindex, const char *name, StatusMode
return 0;
}
static int map_global_dns_servers_internal(
sd_bus *bus,
const char *member,
sd_bus_message *m,
sd_bus_error *error,
void *userdata,
bool extended) {
char ***l = ASSERT_PTR(userdata);
int r;
assert(bus);
assert(member);
assert(m);
r = sd_bus_message_enter_container(m, 'a', extended ? "(iiayqs)" : "(iiay)");
if (r < 0)
return r;
for (;;) {
_cleanup_free_ char *pretty = NULL;
r = read_dns_server_one(m, /* with_ifindex= */ true, extended, /* only_global= */ true, &pretty);
if (r < 0)
return r;
if (r == 0)
break;
if (isempty(pretty))
continue;
r = strv_consume(l, TAKE_PTR(pretty));
if (r < 0)
return r;
}
r = sd_bus_message_exit_container(m);
if (r < 0)
return r;
return 0;
}
static int map_global_dns_servers(sd_bus *bus, const char *member, sd_bus_message *m, sd_bus_error *error, void *userdata) {
return map_global_dns_servers_internal(bus, member, m, error, userdata, /* extended= */ false);
return map_dns_servers_internal(bus, member, m, DNS_SERVER_WITH_IFINDEX|DNS_SERVER_ONLY_GLOBAL, error, userdata);
}
static int map_global_dns_servers_ex(sd_bus *bus, const char *member, sd_bus_message *m, sd_bus_error *error, void *userdata) {
return map_global_dns_servers_internal(bus, member, m, error, userdata, /* extended= */ true);
return map_dns_servers_internal(bus, member, m, DNS_SERVER_WITH_IFINDEX|DNS_SERVER_ONLY_GLOBAL|DNS_SERVER_WITH_PORT_NUMBER_AND_SERVER_NAME, error, userdata);
}
static int map_global_current_dns_server(sd_bus *bus, const char *member, sd_bus_message *m, sd_bus_error *error, void *userdata) {
return read_dns_server_one(m, /* with_ifindex= */ true, /* extended= */ false, /* only_global= */ true, userdata);
return read_dns_server_one(m, DNS_SERVER_WITH_IFINDEX|DNS_SERVER_ONLY_GLOBAL, userdata);
}
static int map_global_current_dns_server_ex(sd_bus *bus, const char *member, sd_bus_message *m, sd_bus_error *error, void *userdata) {
return read_dns_server_one(m, /* with_ifindex= */ true, /* extended= */ true, /* only_global= */ true, userdata);
return read_dns_server_one(m, DNS_SERVER_WITH_IFINDEX|DNS_SERVER_WITH_PORT_NUMBER_AND_SERVER_NAME|DNS_SERVER_ONLY_GLOBAL, userdata);
}
static int map_global_domains(sd_bus *bus, const char *member, sd_bus_message *m, sd_bus_error *error, void *userdata) {
char ***l = ASSERT_PTR(userdata);
int r;
assert(bus);
assert(member);
assert(m);
r = sd_bus_message_enter_container(m, 'a', "(isb)");
if (r < 0)
return r;
for (;;) {
_cleanup_free_ char *pretty = NULL;
r = read_domain_one(m, true, &pretty);
if (r < 0)
return r;
if (r == 0)
break;
if (isempty(pretty))
continue;
r = strv_consume(l, TAKE_PTR(pretty));
if (r < 0)
return r;
}
r = sd_bus_message_exit_container(m);
if (r < 0)
return r;
strv_sort(*l);
return 0;
return map_domains_internal(bus, member, m, /* with_ifindex= */ true, error, userdata);
}
static int status_global(sd_bus *bus, StatusMode mode, bool *empty_line) {
@ -2132,18 +2104,13 @@ static int status_global(sd_bus *bus, StatusMode mode, bool *empty_line) {
return 0;
}
static int status_all(sd_bus *bus, StatusMode mode) {
static int status_links(sd_bus *bus, StatusMode mode, bool *empty_line) {
_cleanup_(sd_netlink_message_unrefp) sd_netlink_message *req = NULL, *reply = NULL;
_cleanup_(sd_netlink_unrefp) sd_netlink *rtnl = NULL;
bool empty_line = false;
int ret = 0, r;
assert(bus);
r = status_global(bus, mode, &empty_line);
if (r < 0)
return r;
r = sd_netlink_open(&rtnl);
if (r < 0)
return log_error_errno(r, "Failed to connect to netlink: %m");
@ -2195,11 +2162,206 @@ static int status_all(sd_bus *bus, StatusMode mode) {
typesafe_qsort(infos, n_infos, interface_info_compare);
FOREACH_ARRAY(info, infos, n_infos)
RET_GATHER(ret, status_ifindex(bus, info->index, info->name, mode, &empty_line));
RET_GATHER(ret, status_ifindex(bus, info->index, info->name, mode, empty_line));
return ret;
}
typedef struct DelegateInfo {
char *current_dns;
char **dns;
char **domains;
bool default_route;
} DelegateInfo;
static void delegate_info_done(DelegateInfo *p) {
assert(p);
free(p->current_dns);
strv_free(p->dns);
strv_free(p->domains);
}
static int map_delegate_dns_servers(sd_bus *bus, const char *member, sd_bus_message *m, sd_bus_error *error, void *userdata) {
return map_dns_servers_internal(bus, member, m, DNS_SERVER_WITH_IFINDEX|DNS_SERVER_WITH_PORT_NUMBER_AND_SERVER_NAME, error, userdata);
}
static int map_delegate_current_dns_server(sd_bus *bus, const char *member, sd_bus_message *m, sd_bus_error *error, void *userdata) {
return read_dns_server_one(m, DNS_SERVER_WITH_IFINDEX|DNS_SERVER_WITH_PORT_NUMBER_AND_SERVER_NAME, userdata);
}
static int map_delegate_domains(sd_bus *bus, const char *member, sd_bus_message *m, sd_bus_error *error, void *userdata) {
return map_domains_internal(bus, member, m, /* with_ifindex= */ false, error, userdata);
}
static int status_delegate_one(sd_bus *bus, const char *id, StatusMode mode, bool *empty_line) {
static const struct bus_properties_map property_map[] = {
{ "DNS", "a(iiayqs)", map_delegate_dns_servers, offsetof(DelegateInfo, dns) },
{ "CurrentDNSServer", "(iiayqs)", map_delegate_current_dns_server, offsetof(DelegateInfo, current_dns) },
{ "Domains", "a(sb)", map_delegate_domains, offsetof(DelegateInfo, domains) },
{ "DefaultRoute", "b", NULL, offsetof(DelegateInfo, default_route) },
{}
};
_cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
_cleanup_(delegate_info_done) DelegateInfo delegate_info = {};
_cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL;
_cleanup_free_ char *p = NULL;
int r;
assert(bus);
assert(id);
r = sd_bus_path_encode("/org/freedesktop/resolve1/dns_delegate", id, &p);
if (r < 0)
return log_oom();
log_info("→ %s", p);
r = bus_map_all_properties(
bus,
"org.freedesktop.resolve1",
p,
property_map,
BUS_MAP_BOOLEAN_AS_BOOL,
&error,
&m,
&delegate_info);
if (r < 0)
return log_error_errno(r, "Failed to get delegate data for %s: %s", id, bus_error_message(&error, r));
pager_open(arg_pager_flags);
switch (mode) {
case STATUS_DNS:
return status_print_strv_delegate(id, delegate_info.dns);
case STATUS_DOMAIN:
return status_print_strv_delegate(id, delegate_info.domains);
case STATUS_DEFAULT_ROUTE:
printf("%sDelegate %s%s: %s\n",
ansi_highlight(), id, ansi_normal(),
yes_no(delegate_info.default_route));
return 0;
case STATUS_ALL:
break;
default:
return 0;
}
if (empty_line && *empty_line)
fputc('\n', stdout);
printf("%sDelegate %s%s\n",
ansi_highlight(), id, ansi_normal());
_cleanup_(table_unrefp) Table *table = table_new_vertical();
if (!table)
return log_oom();
if (delegate_info.current_dns) {
r = table_add_many(table,
TABLE_FIELD, "Current DNS Server",
TABLE_STRING, delegate_info.current_dns);
if (r < 0)
return table_log_add_error(r);
}
r = dump_list(table, "DNS Servers", delegate_info.dns);
if (r < 0)
return r;
r = dump_list(table, "DNS Domain", delegate_info.domains);
if (r < 0)
return r;
r = table_add_many(table,
TABLE_FIELD, "Default Route",
TABLE_BOOLEAN, delegate_info.default_route);
r = table_print(table, NULL);
if (r < 0)
return table_log_print_error(r);
if (empty_line)
*empty_line = true;
return 0;
}
static int status_delegates(sd_bus *bus, StatusMode mode, bool *empty_line) {
_cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL;
_cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
int r, ret = 0;
assert(bus);
r = bus_call_method(bus, bus_resolve_mgr, "ListDelegates", &error, &reply, NULL);
if (r < 0) {
if (sd_bus_error_has_name(&error, SD_BUS_ERROR_UNKNOWN_METHOD)) {
log_debug("Delegates not supported, skipping.");
return 0;
}
return log_error_errno(r, "Failed to list delegates: %s", bus_error_message(&error, r));
}
r = sd_bus_message_enter_container(reply, 'a', "(so)");
if (r < 0)
return bus_log_parse_error(r);
_cleanup_strv_free_ char **l = NULL;
for (;;) {
const char *id;
r = sd_bus_message_read(reply, "(so)", &id, NULL);
if (r < 0)
return bus_log_parse_error(r);
if (r == 0)
break;
if (strv_extend(&l, id) < 0)
return log_oom();
}
r = sd_bus_message_exit_container(reply);
if (r < 0)
return bus_log_parse_error(r);
strv_sort(l);
STRV_FOREACH(i, l)
RET_GATHER(ret, status_delegate_one(bus, *i, mode, empty_line));
return ret;
}
static int status_all(sd_bus *bus, StatusMode mode) {
bool empty_line = false;
int r;
assert(bus);
r = status_global(bus, mode, &empty_line);
if (r < 0)
return r;
r = status_links(bus, mode, &empty_line);
if (r < 0)
return r;
r = status_delegates(bus, mode, &empty_line);
if (r < 0)
return r;
return 0;
}
static int verb_status(int argc, char **argv, void *userdata) {
_cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL;
_cleanup_(sd_netlink_unrefp) sd_netlink *rtnl = NULL;

View File

@ -14,10 +14,11 @@
#include "path-util.h"
#include "resolved-bus.h"
#include "resolved-def.h"
#include "resolved-dns-delegate-bus.h"
#include "resolved-dns-stream.h"
#include "resolved-dns-synthesize.h"
#include "resolved-dnssd-bus.h"
#include "resolved-dnssd.h"
#include "resolved-dnssd-bus.h"
#include "resolved-link-bus.h"
#include "resolved-resolv-conf.h"
#include "socket-netlink.h"
@ -2079,6 +2080,64 @@ static int bus_method_unregister_service(sd_bus_message *message, void *userdata
return call_dnssd_method(m, message, bus_dnssd_method_unregister, error);
}
static int bus_method_get_delegate(sd_bus_message *message, void *userdata, sd_bus_error *error) {
_cleanup_free_ char *p = NULL;
Manager *m = ASSERT_PTR(userdata);
int r;
assert(message);
const char *id;
r = sd_bus_message_read(message, "s", &id);
if (r < 0)
return r;
DnsDelegate *d = hashmap_get(m->delegates, id);
if (!d)
return sd_bus_error_setf(error, BUS_ERROR_NO_SUCH_DELEGATE, "Delegate '%s' not known", id);
p = dns_delegate_bus_path(d);
if (!p)
return -ENOMEM;
return sd_bus_reply_method_return(message, "o", p);
}
static int bus_method_list_delegates(sd_bus_message *message, void *userdata, sd_bus_error *error) {
_cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL;
Manager *m = ASSERT_PTR(userdata);
int r;
assert(message);
r = sd_bus_message_new_method_return(message, &reply);
if (r < 0)
return r;
r = sd_bus_message_open_container(reply, 'a', "(so)");
if (r < 0)
return r;
DnsDelegate *d;
HASHMAP_FOREACH(d, m->delegates) {
_cleanup_free_ char *p = NULL;
p = dns_delegate_bus_path(d);
if (!p)
return -ENOMEM;
r = sd_bus_message_append(reply, "(so)", d->id, p);
if (r < 0)
return r;
}
r = sd_bus_message_close_container(reply);
if (r < 0)
return r;
return sd_bus_send(NULL, reply, NULL);
}
static const sd_bus_vtable resolve_vtable[] = {
SD_BUS_VTABLE_START(0),
SD_BUS_PROPERTY("LLMNRHostname", "s", NULL, offsetof(Manager, llmnr_hostname), SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
@ -2217,6 +2276,16 @@ static const sd_bus_vtable resolve_vtable[] = {
SD_BUS_NO_RESULT,
bus_method_reset_server_features,
SD_BUS_VTABLE_UNPRIVILEGED),
SD_BUS_METHOD_WITH_ARGS("GetDelegate",
SD_BUS_ARGS("s", id),
SD_BUS_RESULT("o", path),
bus_method_get_delegate,
SD_BUS_VTABLE_UNPRIVILEGED),
SD_BUS_METHOD_WITH_ARGS("ListDelegates",
SD_BUS_NO_ARGS,
SD_BUS_RESULT("a(so)", delegates),
bus_method_list_delegates,
SD_BUS_VTABLE_UNPRIVILEGED),
SD_BUS_VTABLE_END,
};
@ -2226,7 +2295,8 @@ const BusObjectImplementation manager_object = {
"org.freedesktop.resolve1.Manager",
.vtables = BUS_VTABLES(resolve_vtable),
.children = BUS_IMPLEMENTATIONS(&link_object,
&dnssd_object),
&dnssd_object,
&dns_delegate_object),
};
static int match_prepare_for_sleep(sd_bus_message *message, void *userdata, sd_bus_error *ret_error) {

View File

@ -55,7 +55,7 @@ static int manager_add_dns_server_by_string(Manager *m, DnsServerType type, cons
return 0;
}
return dns_server_new(m, NULL, type, NULL, family, &address, port, ifindex, server_name, RESOLVE_CONFIG_SOURCE_FILE);
return dns_server_new(m, /* ret= */ NULL, type, /* link= */ NULL, /* delegate= */ NULL, family, &address, port, ifindex, server_name, RESOLVE_CONFIG_SOURCE_FILE);
}
int manager_parse_dns_server_string_and_warn(Manager *m, DnsServerType type, const char *string) {
@ -100,7 +100,7 @@ static int manager_add_search_domain_by_string(Manager *m, const char *domain) {
if (r > 0)
dns_search_domain_move_back_and_unmark(d);
else {
r = dns_search_domain_new(m, &d, DNS_SEARCH_DOMAIN_SYSTEM, NULL, domain);
r = dns_search_domain_new(m, &d, DNS_SEARCH_DOMAIN_SYSTEM, /* link= */ NULL, /* delegate= */ NULL, domain);
if (r < 0)
return r;
}

View File

@ -0,0 +1,153 @@
/* SPDX-License-Identifier: LGPL-2.1-or-later */
#include "bus-get-properties.h"
#include "resolved-bus.h"
#include "resolved-dns-delegate-bus.h"
#include "resolved-manager.h"
#include "strv.h"
static int property_get_dns(
sd_bus *bus,
const char *path,
const char *interface,
const char *property,
sd_bus_message *reply,
void *userdata,
sd_bus_error *error) {
DnsDelegate *d = ASSERT_PTR(userdata);
int r;
assert(reply);
r = sd_bus_message_open_container(reply, 'a', "(iiayqs)");
if (r < 0)
return r;
LIST_FOREACH(servers, s, d->dns_servers) {
r = bus_dns_server_append(reply, s, /* with_ifindex= */ true, /* extended= */ true);
if (r < 0)
return r;
}
return sd_bus_message_close_container(reply);
}
static int property_get_current_dns_server(
sd_bus *bus,
const char *path,
const char *interface,
const char *property,
sd_bus_message *reply,
void *userdata,
sd_bus_error *error) {
DnsDelegate *d = ASSERT_PTR(userdata);
assert(reply);
return bus_dns_server_append(reply, d->current_dns_server, /* with_ifindex= */ true, /* extended= */ true);
}
static int property_get_domains(
sd_bus *bus,
const char *path,
const char *interface,
const char *property,
sd_bus_message *reply,
void *userdata,
sd_bus_error *error) {
DnsDelegate *delegate = ASSERT_PTR(userdata);
int r;
assert(reply);
r = sd_bus_message_open_container(reply, 'a', "(sb)");
if (r < 0)
return r;
LIST_FOREACH(domains, d, delegate->search_domains) {
r = sd_bus_message_append(reply, "(sb)", d->name, d->route_only);
if (r < 0)
return r;
}
return sd_bus_message_close_container(reply);
}
static int dns_delegate_object_find(sd_bus *bus, const char *path, const char *interface, void *userdata, void **found, sd_bus_error *error) {
Manager *m = ASSERT_PTR(userdata);
assert(bus);
assert(path);
assert(interface);
assert(found);
_cleanup_free_ char *e = NULL;
if (sd_bus_path_decode(path, "/org/freedesktop/resolve1/dns_delegate", &e) <= 0)
return 0;
DnsDelegate *d = hashmap_get(m->delegates, e);
if (!d)
return 0;
*found = d;
return 1;
}
char* dns_delegate_bus_path(const DnsDelegate *d) {
char *p;
assert(d);
if (sd_bus_path_encode("/org/freedesktop/resolve1/dns_delegate", d->id, &p) < 0)
return NULL;
return p;
}
static int dns_delegate_node_enumerator(sd_bus *bus, const char *path, void *userdata, char ***nodes, sd_bus_error *error) {
Manager *m = ASSERT_PTR(userdata);
int r;
assert(bus);
assert(path);
assert(nodes);
_cleanup_strv_free_ char **l = NULL;
DnsDelegate *d;
HASHMAP_FOREACH(d, m->delegates) {
_cleanup_free_ char *p = NULL;
p = dns_delegate_bus_path(d);
if (!p)
return -ENOMEM;
r = strv_consume(&l, TAKE_PTR(p));
if (r < 0)
return r;
}
*nodes = TAKE_PTR(l);
return 1;
}
static const sd_bus_vtable dns_delegate_vtable[] = {
SD_BUS_VTABLE_START(0),
SD_BUS_PROPERTY("DNS", "a(iiayqs)", property_get_dns, 0, 0),
SD_BUS_PROPERTY("CurrentDNSServer", "(iiayqs)", property_get_current_dns_server, 0, 0),
SD_BUS_PROPERTY("Domains", "a(sb)", property_get_domains, 0, 0),
SD_BUS_PROPERTY("DefaultRoute", "b", bus_property_get_tristate, offsetof(DnsDelegate, default_route), 0),
SD_BUS_VTABLE_END
};
const BusObjectImplementation dns_delegate_object = {
"/org/freedesktop/resolve1/dns_delegate",
"org.freedesktop.resolve1.DnsDelegate",
.fallback_vtables = BUS_FALLBACK_VTABLES({dns_delegate_vtable, dns_delegate_object_find}),
.node_enumerator = dns_delegate_node_enumerator,
};

View File

@ -0,0 +1,12 @@
/* SPDX-License-Identifier: LGPL-2.1-or-later */
#pragma once
#include "sd-bus.h"
#include "bus-object.h"
#include "bus-util.h"
#include "resolved-dns-delegate.h"
extern const BusObjectImplementation dns_delegate_object;
char* dns_delegate_bus_path(const DnsDelegate *d);

View File

@ -0,0 +1,20 @@
/* SPDX-License-Identifier: LGPL-2.1-or-later */
%{
#include <stddef.h>
#include "conf-parser.h"
#include "resolved-dns-delegate.h"
%}
struct ConfigPerfItem;
%null_strings
%language=ANSI-C
%define slot-name section_and_lvalue
%define hash-function-name resolved_dns_delegate_gperf_hash
%define lookup-function-name resolved_dns_delegate_gperf_lookup
%readonly-tables
%omit-struct-type
%struct-type
%includes
%%
Delegate.DNS, config_parse_delegate_dns_servers, 0, 0
Delegate.Domains, config_parse_delegate_domains, 0, 0
Delegate.DefaultRoute, config_parse_tristate, 0, offsetof(DnsDelegate, default_route),

View File

@ -0,0 +1,356 @@
/* SPDX-License-Identifier: LGPL-2.1-or-later */
#include "conf-files.h"
#include "in-addr-util.h"
#include "path-util.h"
#include "resolved-dns-delegate.h"
#include "resolved-manager.h"
#include "socket-netlink.h"
#define DNS_DELEGATES_MAX 4096U
#define DNS_DELEGATE_SEARCH_DIRS ((const char* const*) CONF_PATHS_STRV("systemd/dns-delegate"))
DEFINE_PRIVATE_HASH_OPS_WITH_VALUE_DESTRUCTOR(
dns_delegate_hash_ops,
char,
string_hash_func,
string_compare_func,
DnsDelegate,
dns_delegate_free);
int dns_delegate_new(Manager *m, const char *id, DnsDelegate **ret) {
int r;
assert(m);
assert(id);
if (hashmap_size(m->delegates) >= DNS_DELEGATES_MAX)
return -E2BIG;
_cleanup_free_ char *id_copy = strdup(id);
if (!id_copy)
return -ENOMEM;
_cleanup_(dns_delegate_freep) DnsDelegate *d = new(DnsDelegate, 1);
if (!d)
return -ENOMEM;
*d = (DnsDelegate) {
.id = TAKE_PTR(id_copy),
.default_route = -1,
};
r = dns_scope_new(
m,
&d->scope,
DNS_SCOPE_DELEGATE,
/* link= */ NULL,
d,
DNS_PROTOCOL_DNS,
AF_UNSPEC);
if (r < 0)
return r;
r = hashmap_ensure_put(&m->delegates, &dns_delegate_hash_ops, d->id, d);
if (r < 0)
return r;
d->manager = m;
log_debug("New delegate '%s'.", id);
if (ret)
*ret = d;
TAKE_PTR(d);
return 0;
}
DnsDelegate *dns_delegate_free(DnsDelegate *d) {
if (!d)
return NULL;
Manager *m = d->manager;
log_debug("Removing delegate '%s'.", d->id);
dns_server_unlink_all(d->dns_servers);
dns_search_domain_unlink_all(d->search_domains);
dns_scope_free(d->scope);
if (m)
hashmap_remove(m->delegates, d->id);
free(d->id);
return mfree(d);
}
DnsServer* dns_delegate_set_dns_server(DnsDelegate *d, DnsServer *s) {
assert(d);
if (d->current_dns_server == s)
return s;
if (s)
log_debug("Switching delegate '%s' to DNS server %s.", d->id, strna(dns_server_string_full(s)));
dns_server_unref(d->current_dns_server);
d->current_dns_server = dns_server_ref(s);
/* Skip flushing the cache if server stale feature is enabled. */
if (d->manager->stale_retention_usec == 0)
dns_cache_flush(&d->scope->cache);
return s;
}
DnsServer *dns_delegate_get_dns_server(DnsDelegate *d) {
assert(d);
if (!d->current_dns_server)
dns_delegate_set_dns_server(d, d->dns_servers);
return d->current_dns_server;
}
void dns_delegate_next_dns_server(DnsDelegate *d, DnsServer *if_current) {
assert(d);
/* If the current server of the transaction is specified, and we already are at a different one,
* don't do anything */
if (if_current && d->current_dns_server != if_current)
return;
/* If currently have no DNS server, then don't do anything, we'll pick it lazily the next time a DNS
* server is needed. */
if (!d->current_dns_server)
return;
/* Change to the next one, but make sure to follow the linked list only if this server is actually
* still linked. */
if (d->current_dns_server->linked && d->current_dns_server->servers_next) {
dns_delegate_set_dns_server(d, d->current_dns_server->servers_next);
return;
}
/* Pick the first one again, after we reached the end */
dns_delegate_set_dns_server(d, d->dns_servers);
}
static int dns_delegate_load(Manager *m, const char *path) {
int r;
assert(m);
assert(path);
_cleanup_free_ char *fn = NULL;
r = path_extract_filename(path, &fn);
if (r < 0)
return log_error_errno(r, "Failed to extract filename from path '%s': %m", path);
const char *e = endswith(fn, ".dns-delegate");
if (!e)
return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "DNS delegate file name does not end in .dns-delegate, refusing: %s", fn);
_cleanup_free_ char *id = strndup(fn, e - fn);
if (!string_is_safe(id))
return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "DNS delegate file name contains weird characters, refusing: %s", fn);
_cleanup_free_ char *dropin_dirname = strjoin(id, ".dns-delegate.d");
if (!dropin_dirname)
return log_oom();
_cleanup_(dns_delegate_freep) DnsDelegate *d = NULL;
r = dns_delegate_new(m, id, &d);
if (r < 0)
return log_error_errno(r, "Failed to allocate delegate '%s': %m", id);
r = config_parse_many(
STRV_MAKE_CONST(path),
DNS_DELEGATE_SEARCH_DIRS,
dropin_dirname,
/* root= */ NULL,
"Delegate\0",
config_item_perf_lookup,
resolved_dns_delegate_gperf_lookup,
/* flags= */ 0,
d,
/* ret_stats_by_path= */ NULL,
/* ret_drop_in_files= */ NULL);
if (r < 0)
return r;
log_info("Successfully loaded delegate '%s'.", d->id);
TAKE_PTR(d);
return 0;
}
int manager_load_delegates(Manager *m) {
_cleanup_strv_free_ char **files = NULL;
int r;
assert(m);
r = conf_files_list_strv(&files, ".dns-delegate", /* root= */ NULL, /* flags= */ 0, DNS_DELEGATE_SEARCH_DIRS);
if (r < 0)
return log_error_errno(r, "Failed to enumerate .dns-delegate files: %m");
STRV_FOREACH(f, files)
(void) dns_delegate_load(m, *f);
return 0;
}
int config_parse_delegate_dns_servers(
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) {
DnsDelegate *d = ASSERT_PTR(userdata);
int r;
assert(filename);
assert(lvalue);
assert(rvalue);
/* Empty assignment means clear the list */
if (isempty(rvalue)) {
dns_server_unlink_all(d->dns_servers);
return 0;
}
/* Otherwise, add to the list */
for (;;) {
_cleanup_free_ char *word = NULL;
r = extract_first_word(&rvalue, &word, NULL, 0);
if (r < 0) {
log_syntax(unit, LOG_WARNING, filename, line, r,
"Failed to parse DNS server string '%s', ignoring.", rvalue);
return 0;
}
if (r == 0)
break;
_cleanup_free_ char *server_name = NULL;
union in_addr_union address;
int family, ifindex = 0;
uint16_t port;
r = in_addr_port_ifindex_name_from_string_auto(word, &family, &address, &port, &ifindex, &server_name);
if (r < 0) {
log_syntax(unit, LOG_WARNING, filename, line, r,
"Failed to parse DNS server string '%s', ignoring.", word);
continue;
}
/* Silently filter out 0.0.0.0, 127.0.0.53, 127.0.0.54 (our own stub DNS listener) */
if (!dns_server_address_valid(family, &address))
continue;
/* By default, the port number is determined with the transaction feature level.
* See dns_transaction_port() and dns_server_port(). */
if (IN_SET(port, 53, 853))
port = 0;
/* Filter out duplicates */
DnsServer *s = dns_server_find(d->dns_servers, family, &address, port, ifindex, server_name);
if (s) {
/* Drop the marker. This is used to find the servers that ceased to exist, see
* manager_mark_dns_servers() and manager_flush_marked_dns_servers(). */
dns_server_move_back_and_unmark(s);
return 0;
}
r = dns_server_new(
d->manager,
/* ret= */ NULL,
DNS_SERVER_DELEGATE,
/* link= */ NULL,
d,
family,
&address,
port,
ifindex,
server_name,
RESOLVE_CONFIG_SOURCE_FILE);
if (r < 0)
return log_error_errno(r, "Failed to add DNS server: %m");
}
return 0;
}
int config_parse_delegate_domains(
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) {
DnsDelegate *d = ASSERT_PTR(userdata);
int r;
assert(filename);
assert(lvalue);
assert(rvalue);
/* Empty assignment means clear the list */
if (isempty(rvalue)) {
dns_search_domain_unlink_all(d->search_domains);
return 0;
}
/* Otherwise, add to the list */
for (;;) {
_cleanup_free_ char *word = NULL;
r = extract_first_word(&rvalue, &word, NULL, 0);
if (r < 0) {
log_syntax(unit, LOG_WARNING, filename, line, r,
"Failed to parse search domains string '%s', ignoring.", rvalue);
return 0;
}
if (r == 0)
return 0;
const char *name = word;
bool route_only = name[0] == '~';
if (route_only)
name++;
if (dns_name_is_root(name) || streq(name, "*")) {
route_only = true;
name = ".";
}
DnsSearchDomain *domain;
r = dns_search_domain_find(d->search_domains, name, &domain);
if (r < 0)
return log_error_errno(r, "Failed to find search domain: %m");
if (r > 0)
dns_search_domain_move_back_and_unmark(domain);
else {
r = dns_search_domain_new(d->manager, &domain, DNS_SEARCH_DOMAIN_DELEGATE, /* link= */ NULL, d, name);
if (r < 0)
return r;
}
domain->route_only = route_only;
}
return 0;
}

View File

@ -0,0 +1,48 @@
/* SPDX-License-Identifier: LGPL-2.1-or-later */
#pragma once
typedef struct DnsDelegate DnsDelegate;
#include "resolved-dns-scope.h"
#include "resolved-dns-search-domain.h"
#include "resolved-dns-server.h"
#define DELEGATE_SEARCH_DOMAINS_MAX 256
#define DELEGATE_DNS_SERVERS_MAX 256
/* A DnsDelegate object is used to manage additional, explicitly configured unicast DNS lookup scopes,
* independent from any network link and from the global scope. */
struct DnsDelegate {
Manager *manager;
char *id;
LIST_HEAD(DnsServer, dns_servers);
unsigned n_dns_servers;
DnsServer *current_dns_server;
LIST_HEAD(DnsSearchDomain, search_domains);
unsigned n_search_domains;
int default_route;
DnsScope *scope;
LIST_FIELDS(DnsDelegate, delegates);
};
int dns_delegate_new(Manager *m, const char *id, DnsDelegate **ret);
DnsDelegate *dns_delegate_free(DnsDelegate *d);
DEFINE_TRIVIAL_CLEANUP_FUNC(DnsDelegate*, dns_delegate_free);
DnsServer* dns_delegate_set_dns_server(DnsDelegate *d, DnsServer *s);
DnsServer *dns_delegate_get_dns_server(DnsDelegate *d);
void dns_delegate_next_dns_server(DnsDelegate *d, DnsServer *if_current);
int manager_load_delegates(Manager *m);
const struct ConfigPerfItem* resolved_dns_delegate_gperf_lookup(const char *key, GPERF_LEN_TYPE length);
CONFIG_PARSER_PROTOTYPE(config_parse_delegate_dns_servers);
CONFIG_PARSER_PROTOTYPE(config_parse_delegate_domains);

View File

@ -11,12 +11,14 @@
#include "missing_network.h"
#include "random-util.h"
#include "resolved-dnssd.h"
#include "resolved-dns-delegate.h"
#include "resolved-dns-scope.h"
#include "resolved-dns-synthesize.h"
#include "resolved-dns-zone.h"
#include "resolved-llmnr.h"
#include "resolved-mdns.h"
#include "socket-util.h"
#include "string-table.h"
#include "strv.h"
#define MULTICAST_RATELIMIT_INTERVAL_USEC (1*USEC_PER_SEC)
@ -26,11 +28,24 @@
#define MULTICAST_RESEND_TIMEOUT_MIN_USEC (100 * USEC_PER_MSEC)
#define MULTICAST_RESEND_TIMEOUT_MAX_USEC (1 * USEC_PER_SEC)
int dns_scope_new(Manager *m, DnsScope **ret, Link *l, DnsProtocol protocol, int family) {
int dns_scope_new(
Manager *m,
DnsScope **ret,
DnsScopeOrigin origin,
Link *link,
DnsDelegate *delegate,
DnsProtocol protocol,
int family) {
DnsScope *s;
assert(m);
assert(ret);
assert(origin >= 0);
assert(origin < _DNS_SCOPE_ORIGIN_MAX);
assert(!!link == (origin == DNS_SCOPE_LINK));
assert(!!delegate == (origin == DNS_SCOPE_DELEGATE));
s = new(DnsScope, 1);
if (!s)
@ -38,7 +53,9 @@ int dns_scope_new(Manager *m, DnsScope **ret, Link *l, DnsProtocol protocol, int
*s = (DnsScope) {
.manager = m,
.link = l,
.link = link,
.delegate = delegate,
.origin = origin,
.protocol = protocol,
.family = family,
.resend_timeout = MULTICAST_RESEND_TIMEOUT_MIN_USEC,
@ -54,9 +71,9 @@ int dns_scope_new(Manager *m, DnsScope **ret, Link *l, DnsProtocol protocol, int
* not update it from the on, even if the setting
* changes. */
if (l) {
s->dnssec_mode = link_get_dnssec_mode(l);
s->dns_over_tls_mode = link_get_dns_over_tls_mode(l);
if (link) {
s->dnssec_mode = link_get_dnssec_mode(link);
s->dns_over_tls_mode = link_get_dns_over_tls_mode(link);
} else {
s->dnssec_mode = manager_get_dnssec_mode(m);
s->dns_over_tls_mode = manager_get_dns_over_tls_mode(m);
@ -72,7 +89,12 @@ int dns_scope_new(Manager *m, DnsScope **ret, Link *l, DnsProtocol protocol, int
dns_scope_llmnr_membership(s, true);
dns_scope_mdns_membership(s, true);
log_debug("New scope on link %s, protocol %s, family %s", l ? l->ifname : "*", dns_protocol_to_string(protocol), family == AF_UNSPEC ? "*" : af_to_name(family));
log_debug("New scope on link %s, protocol %s, family %s, origin %s, delegate %s",
link ? link->ifname : "*",
dns_protocol_to_string(protocol),
family == AF_UNSPEC ? "*" : af_to_name(family),
dns_scope_origin_to_string(origin),
s->delegate ? s->delegate->id : "n/a");
*ret = s;
return 0;
@ -100,7 +122,12 @@ DnsScope* dns_scope_free(DnsScope *s) {
if (!s)
return NULL;
log_debug("Removing scope on link %s, protocol %s, family %s", s->link ? s->link->ifname : "*", dns_protocol_to_string(s->protocol), s->family == AF_UNSPEC ? "*" : af_to_name(s->family));
log_debug("Removing scope on link %s, protocol %s, family %s, origin %s, delegate %s",
s->link ? s->link->ifname : "*",
dns_protocol_to_string(s->protocol),
s->family == AF_UNSPEC ? "*" : af_to_name(s->family),
dns_scope_origin_to_string(s->origin),
s->delegate ? s->delegate->id : "n/a");
dns_scope_llmnr_membership(s, false);
dns_scope_mdns_membership(s, false);
@ -133,6 +160,8 @@ DnsServer *dns_scope_get_dns_server(DnsScope *s) {
if (s->link)
return link_get_dns_server(s->link);
else if (s->delegate)
return dns_delegate_get_dns_server(s->delegate);
else
return manager_get_dns_server(s->manager);
}
@ -145,6 +174,8 @@ unsigned dns_scope_get_n_dns_servers(DnsScope *s) {
if (s->link)
return s->link->n_dns_servers;
else if (s->delegate)
return s->delegate->n_dns_servers;
else
return s->manager->n_dns_servers;
}
@ -160,6 +191,8 @@ void dns_scope_next_dns_server(DnsScope *s, DnsServer *if_current) {
if (s->link)
link_next_dns_server(s->link, if_current);
else if (s->delegate)
dns_delegate_next_dns_server(s->delegate, if_current);
else
manager_next_dns_server(s->manager, if_current);
}
@ -267,6 +300,7 @@ static int dns_scope_emit_one(DnsScope *s, int fd, int family, DnsPacket *p) {
if (fd < 0)
return fd;
assert(s->link);
r = manager_send(s->manager, fd, s->link->ifindex, family, &addr, LLMNR_PORT, NULL, p);
if (r < 0)
return r;
@ -298,6 +332,7 @@ static int dns_scope_emit_one(DnsScope *s, int fd, int family, DnsPacket *p) {
if (fd < 0)
return fd;
assert(s->link);
r = manager_send(s->manager, fd, s->link->ifindex, family, &addr, p->destination_port ?: MDNS_PORT, NULL, p);
if (r < 0)
return r;
@ -1374,6 +1409,14 @@ void dns_scope_dump(DnsScope *s, FILE *f) {
fputs(af_to_name(s->family), f);
}
fputs(" origin=", f);
fputs(dns_scope_origin_to_string(s->origin), f);
if (s->delegate) {
fputs(" id=", f);
fputs(s->delegate->id, f);
}
fputs("]\n", f);
if (!dns_zone_is_empty(&s->zone)) {
@ -1395,6 +1438,8 @@ DnsSearchDomain *dns_scope_get_search_domains(DnsScope *s) {
if (s->link)
return s->link->search_domains;
if (s->delegate)
return s->delegate->search_domains;
return s->manager->search_domains;
}
@ -1680,6 +1725,8 @@ static bool dns_scope_has_route_only_domains(DnsScope *scope) {
if (scope->link)
first = scope->link->search_domains;
else if (scope->delegate)
first = scope->delegate->search_domains;
else
first = scope->manager->search_domains;
@ -1705,9 +1752,7 @@ bool dns_scope_is_default_route(DnsScope *scope) {
if (scope->protocol != DNS_PROTOCOL_DNS)
return false;
/* The global DNS scope is always suitable as default route */
if (!scope->link)
return true;
if (scope->link) {
/* Honour whatever is explicitly configured. This is really the best approach, and trumps any
* automatic logic. */
@ -1717,6 +1762,17 @@ bool dns_scope_is_default_route(DnsScope *scope) {
/* Otherwise check if we have any route-only domains, as a sensible heuristic: if so, let's not
* volunteer as default route. */
return !dns_scope_has_route_only_domains(scope);
} else if (scope->delegate) {
if (scope->delegate->default_route >= 0)
return scope->delegate->default_route;
/* Delegates are by default not used as default route */
return false;
} else
/* The global DNS scope is always suitable as default route */
return true;
}
int dns_scope_dump_cache_to_json(DnsScope *scope, sd_json_variant **ret) {
@ -1800,3 +1856,11 @@ int dns_question_types_suitable_for_protocol(DnsQuestion *q, DnsProtocol protoco
return false;
}
static const char* const dns_scope_origin_table[_DNS_SCOPE_ORIGIN_MAX] = {
[DNS_SCOPE_GLOBAL] = "global",
[DNS_SCOPE_LINK] = "link",
[DNS_SCOPE_DELEGATE] = "delegate",
};
DEFINE_STRING_TABLE_LOOKUP(dns_scope_origin, DnsScopeOrigin);

View File

@ -26,9 +26,19 @@ typedef enum DnsScopeMatch {
_DNS_SCOPE_MATCH_INVALID = -EINVAL,
} DnsScopeMatch;
typedef enum DnsScopeOrigin {
DNS_SCOPE_GLOBAL,
DNS_SCOPE_LINK,
DNS_SCOPE_DELEGATE,
_DNS_SCOPE_ORIGIN_MAX,
_DNS_SCOPE_ORIGIN_INVALID = -EINVAL,
} DnsScopeOrigin;
struct DnsScope {
Manager *manager;
DnsScopeOrigin origin;
DnsProtocol protocol;
int family;
@ -37,6 +47,7 @@ struct DnsScope {
DnsOverTlsMode dns_over_tls_mode;
Link *link;
DnsDelegate *delegate;
DnsCache cache;
DnsZone zone;
@ -69,7 +80,7 @@ struct DnsScope {
bool announced;
};
int dns_scope_new(Manager *m, DnsScope **ret, Link *l, DnsProtocol p, int family);
int dns_scope_new(Manager *m, DnsScope **ret, DnsScopeOrigin origin, Link *link, DnsDelegate *delegate, DnsProtocol protocol, int family);
DnsScope* dns_scope_free(DnsScope *s);
void dns_scope_packet_received(DnsScope *s, usec_t rtt);
@ -119,3 +130,6 @@ int dns_scope_dump_cache_to_json(DnsScope *scope, sd_json_variant **ret);
int dns_type_suitable_for_protocol(uint16_t type, DnsProtocol protocol);
int dns_question_types_suitable_for_protocol(DnsQuestion *q, DnsProtocol protocol);
const char* dns_scope_origin_to_string(DnsScopeOrigin origin) _const_;
DnsScopeOrigin dns_scope_origin_from_string(const char *s) _pure_;

View File

@ -2,6 +2,7 @@
#include "alloc-util.h"
#include "dns-domain.h"
#include "resolved-dns-delegate.h"
#include "resolved-dns-search-domain.h"
#include "resolved-link.h"
#include "resolved-manager.h"
@ -10,7 +11,8 @@ int dns_search_domain_new(
Manager *m,
DnsSearchDomain **ret,
DnsSearchDomainType type,
Link *l,
Link *link,
DnsDelegate *delegate,
const char *name) {
_cleanup_free_ char *normalized = NULL;
@ -18,15 +20,19 @@ int dns_search_domain_new(
int r;
assert(m);
assert((type == DNS_SEARCH_DOMAIN_LINK) == !!l);
assert((type == DNS_SEARCH_DOMAIN_LINK) == !!link);
assert((type == DNS_SEARCH_DOMAIN_DELEGATE) == !!delegate);
assert(name);
r = dns_name_normalize(name, 0, &normalized);
if (r < 0)
return r;
if (l) {
if (l->n_search_domains >= LINK_SEARCH_DOMAINS_MAX)
if (link) {
if (link->n_search_domains >= LINK_SEARCH_DOMAINS_MAX)
return -E2BIG;
} else if (delegate) {
if (delegate->n_search_domains >= DELEGATE_SEARCH_DOMAINS_MAX)
return -E2BIG;
} else {
if (m->n_search_domains >= MANAGER_SEARCH_DOMAINS_MAX)
@ -47,9 +53,9 @@ int dns_search_domain_new(
switch (type) {
case DNS_SEARCH_DOMAIN_LINK:
d->link = l;
LIST_APPEND(domains, l->search_domains, d);
l->n_search_domains++;
d->link = link;
LIST_APPEND(domains, link->search_domains, d);
link->n_search_domains++;
break;
case DNS_SEARCH_DOMAIN_SYSTEM:
@ -57,6 +63,12 @@ int dns_search_domain_new(
m->n_search_domains++;
break;
case DNS_SEARCH_DOMAIN_DELEGATE:
d->delegate = delegate;
LIST_APPEND(domains, delegate->search_domains, d);
delegate->n_search_domains++;
break;
default:
assert_not_reached();
}
@ -99,6 +111,13 @@ void dns_search_domain_unlink(DnsSearchDomain *d) {
LIST_REMOVE(domains, d->manager->search_domains, d);
d->manager->n_search_domains--;
break;
case DNS_SEARCH_DOMAIN_DELEGATE:
assert(d->delegate);
assert(d->delegate->n_search_domains > 0);
LIST_REMOVE(domains, d->delegate->search_domains, d);
d->delegate->n_search_domains--;
break;
}
d->linked = false;
@ -134,6 +153,13 @@ void dns_search_domain_move_back_and_unmark(DnsSearchDomain *d) {
LIST_INSERT_AFTER(domains, d->manager->search_domains, tail, d);
break;
case DNS_SEARCH_DOMAIN_DELEGATE:
assert(d->delegate);
tail = LIST_FIND_TAIL(domains, d);
LIST_REMOVE(domains, d->delegate->search_domains, d);
LIST_INSERT_AFTER(domains, d->delegate->search_domains, tail, d);
break;
default:
assert_not_reached();
}

View File

@ -7,10 +7,12 @@
typedef struct DnsSearchDomain DnsSearchDomain;
typedef struct Link Link;
typedef struct Manager Manager;
typedef struct DnsDelegate DnsDelegate;
typedef enum DnsSearchDomainType {
DNS_SEARCH_DOMAIN_SYSTEM,
DNS_SEARCH_DOMAIN_LINK,
DNS_SEARCH_DOMAIN_DELEGATE,
} DnsSearchDomainType;
struct DnsSearchDomain {
@ -20,6 +22,7 @@ struct DnsSearchDomain {
DnsSearchDomainType type;
Link *link;
DnsDelegate *delegate;
char *name;
@ -35,6 +38,7 @@ int dns_search_domain_new(
DnsSearchDomain **ret,
DnsSearchDomainType type,
Link *link,
DnsDelegate *delegate,
const char *name);
DnsSearchDomain* dns_search_domain_ref(DnsSearchDomain *d);

View File

@ -4,6 +4,7 @@
#include "alloc-util.h"
#include "resolved-bus.h"
#include "resolved-dns-delegate.h"
#include "resolved-dns-server.h"
#include "resolved-dns-stub.h"
#include "resolved-manager.h"
@ -23,7 +24,8 @@ int dns_server_new(
Manager *m,
DnsServer **ret,
DnsServerType type,
Link *l,
Link *link,
DnsDelegate *delegate,
int family,
const union in_addr_union *in_addr,
uint16_t port,
@ -35,14 +37,18 @@ int dns_server_new(
DnsServer *s;
assert(m);
assert((type == DNS_SERVER_LINK) == !!l);
assert((type == DNS_SERVER_LINK) == !!link);
assert((type == DNS_SERVER_DELEGATE) == !!delegate);
assert(in_addr);
if (!IN_SET(family, AF_INET, AF_INET6))
return -EAFNOSUPPORT;
if (l) {
if (l->n_dns_servers >= LINK_DNS_SERVERS_MAX)
if (link) {
if (link->n_dns_servers >= LINK_DNS_SERVERS_MAX)
return -E2BIG;
} else if (delegate) {
if (delegate->n_dns_servers >= DELEGATE_DNS_SERVERS_MAX)
return -E2BIG;
} else {
if (m->n_dns_servers >= MANAGER_DNS_SERVERS_MAX)
@ -76,9 +82,9 @@ int dns_server_new(
switch (type) {
case DNS_SERVER_LINK:
s->link = l;
LIST_APPEND(servers, l->dns_servers, s);
l->n_dns_servers++;
s->link = link;
LIST_APPEND(servers, link->dns_servers, s);
link->n_dns_servers++;
break;
case DNS_SERVER_SYSTEM:
@ -91,16 +97,20 @@ int dns_server_new(
m->n_dns_servers++;
break;
case DNS_SERVER_DELEGATE:
s->delegate = delegate;
LIST_APPEND(servers, delegate->dns_servers, s);
delegate->n_dns_servers++;
break;
default:
assert_not_reached();
}
s->linked = true;
/* A new DNS server that isn't fallback is added and the one
* we used so far was a fallback one? Then let's try to pick
* the new one */
if (type != DNS_SERVER_FALLBACK && dns_server_is_fallback(m->current_dns_server))
/* A new non-fallback DNS server is added and the one we used so far was a fallback one? Then
* let's try to pick the new one */
if (type == DNS_SERVER_SYSTEM && dns_server_is_fallback(m->current_dns_server))
manager_set_dns_server(m, NULL);
if (ret)
@ -157,6 +167,14 @@ void dns_server_unlink(DnsServer *s) {
LIST_REMOVE(servers, s->manager->fallback_dns_servers, s);
s->manager->n_dns_servers--;
break;
case DNS_SERVER_DELEGATE:
assert(s->delegate);
assert(s->delegate->n_dns_servers > 0);
LIST_REMOVE(servers, s->delegate->dns_servers, s);
s->delegate->n_dns_servers--;
break;
default:
assert_not_reached();
}
@ -169,6 +187,9 @@ void dns_server_unlink(DnsServer *s) {
if (s->manager->current_dns_server == s)
manager_set_dns_server(s->manager, NULL);
if (s->delegate && s->delegate->current_dns_server == s)
dns_delegate_set_dns_server(s->delegate, NULL);
/* No need to keep a default stream around anymore */
dns_server_unref_stream(s);
@ -188,8 +209,8 @@ void dns_server_move_back_and_unmark(DnsServer *s) {
if (!s->linked || !s->servers_next)
return;
/* Move us to the end of the list, so that the order is
* strictly kept, if we are not at the end anyway. */
/* Move us to the end of the list, so that the order is strictly kept, if we are not at the end
* anyway. */
switch (s->type) {
@ -212,6 +233,13 @@ void dns_server_move_back_and_unmark(DnsServer *s) {
LIST_INSERT_AFTER(servers, s->manager->fallback_dns_servers, tail, s);
break;
case DNS_SERVER_DELEGATE:
assert(s->delegate);
tail = LIST_FIND_TAIL(servers, s);
LIST_REMOVE(servers, s->delegate->dns_servers, s);
LIST_INSERT_AFTER(servers, s->delegate->dns_servers, tail, s);
break;
default:
assert_not_reached();
}
@ -879,8 +907,23 @@ DnsServer *manager_set_dns_server(Manager *m, DnsServer *s) {
return s;
}
DnsServer *manager_get_dns_server(Manager *m) {
static bool manager_search_default_rote_dns_server(Manager *m) {
assert(m);
Link *l;
HASHMAP_FOREACH(l, m->links)
if (l->dns_servers && l->default_route)
return true;
DnsDelegate *d;
HASHMAP_FOREACH(d, m->delegates)
if (d->dns_servers && d->default_route)
return true;
return false;
}
DnsServer *manager_get_dns_server(Manager *m) {
assert(m);
/* Try to read updates resolv.conf */
@ -899,22 +942,10 @@ DnsServer *manager_get_dns_server(Manager *m) {
manager_set_dns_server(m, NULL);
}
if (!m->current_dns_server) {
bool found = false;
/* No DNS servers configured, let's see if there are
* any on any links. If not, we use the fallback
* servers */
HASHMAP_FOREACH(l, m->links)
if (l->dns_servers && l->default_route) {
found = true;
break;
}
if (!found)
/* If no DNS servers are configured, let's see if there are any on any links. If not, we use the
* fallback servers */
if (!m->current_dns_server && !manager_search_default_rote_dns_server(m))
manager_set_dns_server(m, m->fallback_dns_servers);
}
return m->current_dns_server;
}
@ -970,11 +1001,15 @@ void dns_server_flush_cache(DnsServer *s) {
/* Flush the cache of the scope this server belongs to */
current = s->link ? s->link->current_dns_server : s->manager->current_dns_server;
current = s->link ? s->link->current_dns_server :
s->delegate ? s->delegate->current_dns_server :
s->manager->current_dns_server;
if (current != s)
return;
scope = s->link ? s->link->unicast_scope : s->manager->unicast_scope;
scope = s->link ? s->link->unicast_scope :
s->delegate ? s->delegate->scope :
s->manager->unicast_scope;
if (!scope)
return;
@ -1079,10 +1114,14 @@ void dns_server_unref_stream(DnsServer *s) {
DnsScope *dns_server_scope(DnsServer *s) {
assert(s);
assert(s->linked);
assert((s->type == DNS_SERVER_LINK) == !!s->link);
assert((s->type == DNS_SERVER_DELEGATE) == !!s->delegate);
if (s->link)
return s->link->unicast_scope;
if (s->delegate)
return s->delegate->scope;
return s->manager->unicast_scope;
}

View File

@ -9,6 +9,7 @@
#include "time-util.h"
typedef struct DnsScope DnsScope;
typedef struct DnsDelegate DnsDelegate;
typedef struct DnsServer DnsServer;
typedef struct DnsStream DnsStream;
typedef struct DnsPacket DnsPacket;
@ -21,6 +22,7 @@ typedef enum DnsServerType {
DNS_SERVER_SYSTEM,
DNS_SERVER_FALLBACK,
DNS_SERVER_LINK,
DNS_SERVER_DELEGATE,
_DNS_SERVER_TYPE_MAX,
_DNS_SERVER_TYPE_INVALID = -EINVAL,
} DnsServerType;
@ -58,6 +60,7 @@ struct DnsServer {
DnsServerType type;
Link *link;
DnsDelegate *delegate;
int family;
union in_addr_union address;
@ -113,6 +116,7 @@ int dns_server_new(
DnsServer **ret,
DnsServerType type,
Link *link,
DnsDelegate *delegate,
int family,
const union in_addr_union *address,
uint16_t port,

View File

@ -274,7 +274,7 @@ static int bus_link_method_set_dns_servers_internal(sd_bus_message *message, voi
if (s)
dns_server_move_back_and_unmark(s);
else {
r = dns_server_new(l->manager, NULL, DNS_SERVER_LINK, l, dns[i]->family, &dns[i]->address, dns[i]->port, 0, dns[i]->server_name, RESOLVE_CONFIG_SOURCE_DBUS);
r = dns_server_new(l->manager, NULL, DNS_SERVER_LINK, l, /* delegate= */ NULL, dns[i]->family, &dns[i]->address, dns[i]->port, 0, dns[i]->server_name, RESOLVE_CONFIG_SOURCE_DBUS);
if (r < 0) {
dns_server_unlink_all(l->dns_servers);
goto finalize;
@ -282,7 +282,6 @@ static int bus_link_method_set_dns_servers_internal(sd_bus_message *message, voi
changed = true;
}
}
changed = dns_server_unlink_marked(l->dns_servers) || changed;
@ -402,7 +401,7 @@ int bus_link_method_set_domains(sd_bus_message *message, void *userdata, sd_bus_
if (r > 0)
dns_search_domain_move_back_and_unmark(d);
else {
r = dns_search_domain_new(l->manager, &d, DNS_SEARCH_DOMAIN_LINK, l, name);
r = dns_search_domain_new(l->manager, &d, DNS_SEARCH_DOMAIN_LINK, l, /* delegate= */ NULL, name);
if (r < 0)
goto clear;

View File

@ -133,7 +133,7 @@ void link_allocate_scopes(Link *l) {
if (!l->unicast_scope) {
dns_server_reset_features_all(l->dns_servers);
r = dns_scope_new(l->manager, &l->unicast_scope, l, DNS_PROTOCOL_DNS, AF_UNSPEC);
r = dns_scope_new(l->manager, &l->unicast_scope, DNS_SCOPE_LINK, l, /* delegate= */ NULL, DNS_PROTOCOL_DNS, AF_UNSPEC);
if (r < 0)
log_link_warning_errno(l, r, "Failed to allocate DNS scope, ignoring: %m");
}
@ -143,7 +143,7 @@ void link_allocate_scopes(Link *l) {
if (link_relevant(l, AF_INET, true) &&
link_get_llmnr_support(l) != RESOLVE_SUPPORT_NO) {
if (!l->llmnr_ipv4_scope) {
r = dns_scope_new(l->manager, &l->llmnr_ipv4_scope, l, DNS_PROTOCOL_LLMNR, AF_INET);
r = dns_scope_new(l->manager, &l->llmnr_ipv4_scope, DNS_SCOPE_LINK, l, /* delegate= */ NULL, DNS_PROTOCOL_LLMNR, AF_INET);
if (r < 0)
log_link_warning_errno(l, r, "Failed to allocate LLMNR IPv4 scope, ignoring: %m");
}
@ -153,7 +153,7 @@ void link_allocate_scopes(Link *l) {
if (link_relevant(l, AF_INET6, true) &&
link_get_llmnr_support(l) != RESOLVE_SUPPORT_NO) {
if (!l->llmnr_ipv6_scope) {
r = dns_scope_new(l->manager, &l->llmnr_ipv6_scope, l, DNS_PROTOCOL_LLMNR, AF_INET6);
r = dns_scope_new(l->manager, &l->llmnr_ipv6_scope, DNS_SCOPE_LINK, l, /* delegate= */ NULL, DNS_PROTOCOL_LLMNR, AF_INET6);
if (r < 0)
log_link_warning_errno(l, r, "Failed to allocate LLMNR IPv6 scope, ignoring: %m");
}
@ -163,7 +163,7 @@ void link_allocate_scopes(Link *l) {
if (link_relevant(l, AF_INET, true) &&
link_get_mdns_support(l) != RESOLVE_SUPPORT_NO) {
if (!l->mdns_ipv4_scope) {
r = dns_scope_new(l->manager, &l->mdns_ipv4_scope, l, DNS_PROTOCOL_MDNS, AF_INET);
r = dns_scope_new(l->manager, &l->mdns_ipv4_scope, DNS_SCOPE_LINK, l, /* delegate= */ NULL, DNS_PROTOCOL_MDNS, AF_INET);
if (r < 0)
log_link_warning_errno(l, r, "Failed to allocate mDNS IPv4 scope, ignoring: %m");
}
@ -173,7 +173,7 @@ void link_allocate_scopes(Link *l) {
if (link_relevant(l, AF_INET6, true) &&
link_get_mdns_support(l) != RESOLVE_SUPPORT_NO) {
if (!l->mdns_ipv6_scope) {
r = dns_scope_new(l->manager, &l->mdns_ipv6_scope, l, DNS_PROTOCOL_MDNS, AF_INET6);
r = dns_scope_new(l->manager, &l->mdns_ipv6_scope, DNS_SCOPE_LINK, l, /* delegate= */ NULL, DNS_PROTOCOL_MDNS, AF_INET6);
if (r < 0)
log_link_warning_errno(l, r, "Failed to allocate mDNS IPv6 scope, ignoring: %m");
}
@ -273,7 +273,7 @@ static int link_update_dns_server_one(Link *l, const char *str) {
return 0;
}
return dns_server_new(l->manager, NULL, DNS_SERVER_LINK, l, family, &a, port, 0, name, RESOLVE_CONFIG_SOURCE_NETWORKD);
return dns_server_new(l->manager, /* ret= */ NULL, DNS_SERVER_LINK, l, /* delegate= */ NULL, family, &a, port, 0, name, RESOLVE_CONFIG_SOURCE_NETWORKD);
}
static int link_update_dns_servers(Link *l) {
@ -493,7 +493,7 @@ static int link_update_search_domain_one(Link *l, const char *name, bool route_o
if (r > 0)
dns_search_domain_move_back_and_unmark(d);
else {
r = dns_search_domain_new(l->manager, &d, DNS_SEARCH_DOMAIN_LINK, l, name);
r = dns_search_domain_new(l->manager, &d, DNS_SEARCH_DOMAIN_LINK, l, /* delegate= */ NULL, name);
if (r < 0)
return r;
}

View File

@ -31,6 +31,7 @@
#include "random-util.h"
#include "resolved-bus.h"
#include "resolved-conf.h"
#include "resolved-dns-delegate.h"
#include "resolved-dns-stub.h"
#include "resolved-dnssd.h"
#include "resolved-etc-hosts.h"
@ -519,6 +520,10 @@ static int manager_sigusr1(sd_event_source *s, const struct signalfd_siginfo *si
HASHMAP_FOREACH(l, m->links)
LIST_FOREACH(servers, server, l->dns_servers)
dns_server_dump(server, f);
DnsDelegate *delegate;
HASHMAP_FOREACH(delegate, m->delegates)
LIST_FOREACH(servers, server, delegate->dns_servers)
dns_server_dump(server, f);
return memstream_dump(LOG_INFO, &ms);
}
@ -596,6 +601,7 @@ static int manager_dispatch_reload_signal(sd_event_source *s, const struct signa
m->dns_extra_stub_listeners = ordered_set_free(m->dns_extra_stub_listeners);
dnssd_service_clear_on_reload(m->dnssd_services);
m->unicast_scope = dns_scope_free(m->unicast_scope);
m->delegates = hashmap_free(m->delegates);
dns_trust_anchor_flush(&m->trust_anchor);
@ -613,9 +619,11 @@ static int manager_dispatch_reload_signal(sd_event_source *s, const struct signa
if (r < 0)
log_warning_errno(r, "Failed to load DNS-SD configuration files: %m");
manager_load_delegates(m);
/* The default scope configuration is influenced by the manager's configuration (modes, etc.), so
* recreate it on reload. */
r = dns_scope_new(m, &m->unicast_scope, NULL, DNS_PROTOCOL_DNS, AF_UNSPEC);
r = dns_scope_new(m, &m->unicast_scope, DNS_SCOPE_GLOBAL, /* link= */ NULL, /* delegate= */ NULL, DNS_PROTOCOL_DNS, AF_UNSPEC);
if (r < 0)
return r;
@ -696,7 +704,9 @@ int manager_new(Manager **ret) {
if (r < 0)
log_warning_errno(r, "Failed to load DNS-SD configuration files: %m");
r = dns_scope_new(m, &m->unicast_scope, NULL, DNS_PROTOCOL_DNS, AF_UNSPEC);
manager_load_delegates(m);
r = dns_scope_new(m, &m->unicast_scope, DNS_SCOPE_GLOBAL, /* link= */ NULL, /* delegate= */ NULL, DNS_PROTOCOL_DNS, AF_UNSPEC);
if (r < 0)
return r;
@ -765,7 +775,6 @@ int manager_start(Manager *m) {
Manager *manager_free(Manager *m) {
Link *l;
DnssdService *s;
if (!m)
return NULL;
@ -777,12 +786,13 @@ Manager *manager_free(Manager *m) {
while ((l = hashmap_first(m->links)))
link_free(l);
m->delegates = hashmap_free(m->delegates);
while (m->dns_queries)
dns_query_free(m->dns_queries);
m->stub_queries_by_packet = hashmap_free(m->stub_queries_by_packet);
dns_scope_free(m->unicast_scope);
m->unicast_scope = dns_scope_free(m->unicast_scope);
/* At this point only orphaned streams should remain. All others should have been freed already by their
* owners */
@ -830,6 +840,7 @@ Manager *manager_free(Manager *m) {
free(m->llmnr_hostname);
free(m->mdns_hostname);
DnssdService *s;
while ((s = hashmap_first(m->dnssd_services)))
dnssd_service_free(s);
hashmap_free(m->dnssd_services);
@ -1557,7 +1568,7 @@ int manager_compile_dns_servers(Manager *m, OrderedSet **dns) {
}
/* Then, add the per-link servers */
HASHMAP_FOREACH(l, m->links) {
HASHMAP_FOREACH(l, m->links)
LIST_FOREACH(servers, s, l->dns_servers) {
r = ordered_set_put(*dns, s);
if (r == -EEXIST)
@ -1565,6 +1576,16 @@ int manager_compile_dns_servers(Manager *m, OrderedSet **dns) {
if (r < 0)
return r;
}
/* Third, add the delegate servers and domains */
DnsDelegate *d;
HASHMAP_FOREACH(d, m->delegates)
LIST_FOREACH(servers, s, d->dns_servers) {
r = ordered_set_put(*dns, s);
if (r == -EEXIST)
continue;
if (r < 0)
return r;
}
/* If we found nothing, add the fallback servers */
@ -1587,7 +1608,6 @@ int manager_compile_dns_servers(Manager *m, OrderedSet **dns) {
* > 0 or true: return only domains which are for routing only
*/
int manager_compile_search_domains(Manager *m, OrderedSet **domains, int filter_route) {
Link *l;
int r;
assert(m);
@ -1610,9 +1630,9 @@ int manager_compile_search_domains(Manager *m, OrderedSet **domains, int filter_
return r;
}
HASHMAP_FOREACH(l, m->links) {
LIST_FOREACH(domains, d, l->search_domains) {
DnsDelegate *delegate;
HASHMAP_FOREACH(delegate, m->delegates)
LIST_FOREACH(domains, d, delegate->search_domains) {
if (filter_route >= 0 &&
d->route_only != !!filter_route)
@ -1624,6 +1644,20 @@ int manager_compile_search_domains(Manager *m, OrderedSet **domains, int filter_
if (r < 0)
return r;
}
Link *l;
HASHMAP_FOREACH(l, m->links)
LIST_FOREACH(domains, d, l->search_domains) {
if (filter_route >= 0 &&
d->route_only != !!filter_route)
continue;
r = ordered_set_put(*domains, d->name);
if (r == -EEXIST)
continue;
if (r < 0)
return r;
}
return 0;
@ -1707,14 +1741,18 @@ void manager_flush_caches(Manager *m, int log_level) {
}
void manager_reset_server_features(Manager *m) {
Link *l;
dns_server_reset_features_all(m->dns_servers);
dns_server_reset_features_all(m->fallback_dns_servers);
Link *l;
HASHMAP_FOREACH(l, m->links)
dns_server_reset_features_all(l->dns_servers);
DnsDelegate *d;
HASHMAP_FOREACH(d, m->delegates)
dns_server_reset_features_all(d->dns_servers);
log_info("Resetting learnt feature levels on all servers.");
}

View File

@ -87,6 +87,8 @@ struct Manager {
LIST_HEAD(DnsScope, dns_scopes);
DnsScope *unicast_scope;
Hashmap *delegates; /* id string → DnsDelegate objects */
/* LLMNR */
int llmnr_ipv4_udp_fd;
int llmnr_ipv6_udp_fd;

View File

@ -781,7 +781,7 @@ static void go_env_setup(GoEnvironment *env, GoConfig *cfg) {
}
if (cfg->has_scope) {
ASSERT_OK(dns_scope_new(&env->manager, &env->scope, env->link, env->protocol, env->family));
ASSERT_OK(dns_scope_new(&env->manager, &env->scope, env->link ? DNS_SCOPE_LINK : DNS_SCOPE_GLOBAL, env->link, /* delegate= */ NULL, env->protocol, env->family));
ASSERT_NOT_NULL(env->scope);
env->server_addr.in.s_addr = htobe32(0x7f000001);
@ -789,7 +789,7 @@ static void go_env_setup(GoEnvironment *env, GoConfig *cfg) {
env->server_port = 53;
ASSERT_OK(dns_server_new(&env->manager, &env->server, env->server_type,
env->link, env->family, &env->server_addr, env->server_port,
env->link, /* delegate= */ NULL, env->family, &env->server_addr, env->server_port,
env->ifindex, env->server_name, RESOLVE_CONFIG_SOURCE_DBUS));
ASSERT_NOT_NULL(env->server);
@ -803,7 +803,7 @@ static void go_env_setup(GoEnvironment *env, GoConfig *cfg) {
for (size_t i = 0 ; i < env->n_search_domains; i++) {
DnsSearchDomainType type = (env->link == NULL) ? DNS_SEARCH_DOMAIN_SYSTEM : DNS_SEARCH_DOMAIN_LINK;
ASSERT_OK(dns_search_domain_new(&env->manager, &env->search_domains[i], type, env->link, SEARCH_DOMAINS[i]));
ASSERT_OK(dns_search_domain_new(&env->manager, &env->search_domains[i], type, env->link, /* delegate= */ NULL, SEARCH_DOMAINS[i]));
ASSERT_NOT_NULL(env->search_domains[i]);
}
}

View File

@ -29,7 +29,7 @@ TEST(dns_search_domain_new_system) {
Manager manager = {};
_cleanup_(dns_search_domain_unrefp) DnsSearchDomain *sd = NULL;
ASSERT_OK(dns_search_domain_new(&manager, &sd, DNS_SEARCH_DOMAIN_SYSTEM, NULL, "local"));
ASSERT_OK(dns_search_domain_new(&manager, &sd, DNS_SEARCH_DOMAIN_SYSTEM, /* link= */ NULL, /* delegate= */ NULL, "local"));
ASSERT_NOT_NULL(sd);
ASSERT_TRUE(sd->linked);
@ -41,12 +41,12 @@ TEST(dns_search_domain_new_system_limit) {
DnsSearchDomain *sd = NULL;
for (size_t i = 0; i < MANAGER_SEARCH_DOMAINS_MAX; i++) {
ASSERT_OK(dns_search_domain_new(&manager, &sd, DNS_SEARCH_DOMAIN_SYSTEM, NULL, "local"));
ASSERT_OK(dns_search_domain_new(&manager, &sd, DNS_SEARCH_DOMAIN_SYSTEM, /* link= */ NULL, /* delegate= */ NULL, "local"));
ASSERT_NOT_NULL(sd);
ASSERT_EQ(manager.n_search_domains, i + 1);
}
ASSERT_ERROR(dns_search_domain_new(&manager, &sd, DNS_SEARCH_DOMAIN_SYSTEM, NULL, "local"), E2BIG);
ASSERT_ERROR(dns_search_domain_new(&manager, &sd, DNS_SEARCH_DOMAIN_SYSTEM, /* link= */ NULL, /* delegate= */ NULL, "local"), E2BIG);
ASSERT_NOT_NULL(sd);
dns_search_domain_unlink_all(manager.search_domains);
@ -60,7 +60,7 @@ TEST(dns_search_domain_new_link) {
ASSERT_OK(link_new(&manager, &link, 1));
ASSERT_NOT_NULL(link);
ASSERT_OK(dns_search_domain_new(&manager, &sd, DNS_SEARCH_DOMAIN_LINK, link, "local."));
ASSERT_OK(dns_search_domain_new(&manager, &sd, DNS_SEARCH_DOMAIN_LINK, link, /* delegate= */ NULL, "local."));
ASSERT_NOT_NULL(sd);
ASSERT_TRUE(sd->linked);
@ -76,12 +76,12 @@ TEST(dns_search_domain_new_link_limit) {
ASSERT_NOT_NULL(link);
for (size_t i = 0; i < LINK_SEARCH_DOMAINS_MAX; i++) {
ASSERT_OK(dns_search_domain_new(&manager, &sd, DNS_SEARCH_DOMAIN_LINK, link, "local"));
ASSERT_OK(dns_search_domain_new(&manager, &sd, DNS_SEARCH_DOMAIN_LINK, link, /* delegate= */ NULL, "local"));
ASSERT_NOT_NULL(sd);
ASSERT_EQ(link->n_search_domains, i + 1);
}
ASSERT_ERROR(dns_search_domain_new(&manager, &sd, DNS_SEARCH_DOMAIN_LINK, link, "local"), E2BIG);
ASSERT_ERROR(dns_search_domain_new(&manager, &sd, DNS_SEARCH_DOMAIN_LINK, link, /* delegate= */ NULL, "local"), E2BIG);
ASSERT_NOT_NULL(sd);
}
@ -94,13 +94,13 @@ TEST(dns_search_domain_unlink_system) {
_cleanup_(dns_search_domain_unrefp) DnsSearchDomain *sd1 = NULL, *sd3 = NULL;
DnsSearchDomain *sd2 = NULL;
dns_search_domain_new(&manager, &sd1, DNS_SEARCH_DOMAIN_SYSTEM, NULL, "local");
dns_search_domain_new(&manager, &sd1, DNS_SEARCH_DOMAIN_SYSTEM, /* link= */ NULL, /* delegate= */ NULL, "local");
ASSERT_NOT_NULL(sd1);
dns_search_domain_new(&manager, &sd2, DNS_SEARCH_DOMAIN_SYSTEM, NULL, "vpn.example.com");
dns_search_domain_new(&manager, &sd2, DNS_SEARCH_DOMAIN_SYSTEM, /* link= */ NULL, /* delegate= */ NULL, "vpn.example.com");
ASSERT_NOT_NULL(sd2);
dns_search_domain_new(&manager, &sd3, DNS_SEARCH_DOMAIN_SYSTEM, NULL, "org");
dns_search_domain_new(&manager, &sd3, DNS_SEARCH_DOMAIN_SYSTEM, /* link= */ NULL, /* delegate= */ NULL, "org");
ASSERT_NOT_NULL(sd3);
ASSERT_TRUE(sd2->linked);
@ -123,13 +123,13 @@ TEST(dns_search_domain_unlink_link) {
ASSERT_OK(link_new(&manager, &link, 1));
ASSERT_NOT_NULL(link);
dns_search_domain_new(&manager, &sd1, DNS_SEARCH_DOMAIN_LINK, link, "local");
dns_search_domain_new(&manager, &sd1, DNS_SEARCH_DOMAIN_LINK, link, /* delegate= */ NULL, "local");
ASSERT_NOT_NULL(sd1);
dns_search_domain_new(&manager, &sd2, DNS_SEARCH_DOMAIN_LINK, link, "vpn.example.com");
dns_search_domain_new(&manager, &sd2, DNS_SEARCH_DOMAIN_LINK, link, /* delegate= */ NULL, "vpn.example.com");
ASSERT_NOT_NULL(sd2);
dns_search_domain_new(&manager, &sd3, DNS_SEARCH_DOMAIN_LINK, link, "org");
dns_search_domain_new(&manager, &sd3, DNS_SEARCH_DOMAIN_LINK, link, /* delegate= */ NULL, "org");
ASSERT_NOT_NULL(sd3);
ASSERT_TRUE(sd2->linked);
@ -151,13 +151,13 @@ TEST(dns_search_domain_mark_all) {
Manager manager = {};
_cleanup_(dns_search_domain_unrefp) DnsSearchDomain *sd1 = NULL, *sd2 = NULL, *sd3 = NULL;
dns_search_domain_new(&manager, &sd1, DNS_SEARCH_DOMAIN_SYSTEM, NULL, "local");
dns_search_domain_new(&manager, &sd1, DNS_SEARCH_DOMAIN_SYSTEM, /* link= */ NULL, /* delegate= */ NULL, "local");
ASSERT_NOT_NULL(sd1);
dns_search_domain_new(&manager, &sd2, DNS_SEARCH_DOMAIN_SYSTEM, NULL, "vpn.example.com");
dns_search_domain_new(&manager, &sd2, DNS_SEARCH_DOMAIN_SYSTEM, /* link= */ NULL, /* delegate= */ NULL, "vpn.example.com");
ASSERT_NOT_NULL(sd2);
dns_search_domain_new(&manager, &sd3, DNS_SEARCH_DOMAIN_SYSTEM, NULL, "org");
dns_search_domain_new(&manager, &sd3, DNS_SEARCH_DOMAIN_SYSTEM, /* link= */ NULL, /* delegate= */ NULL, "org");
ASSERT_NOT_NULL(sd3);
ASSERT_FALSE(sd1->marked);
@ -179,13 +179,13 @@ TEST(dns_search_domain_move_back_and_unmark) {
Manager manager = {};
_cleanup_(dns_search_domain_unrefp) DnsSearchDomain *sd1 = NULL, *sd2 = NULL, *sd3 = NULL;
dns_search_domain_new(&manager, &sd1, DNS_SEARCH_DOMAIN_SYSTEM, NULL, "local");
dns_search_domain_new(&manager, &sd1, DNS_SEARCH_DOMAIN_SYSTEM, /* link= */ NULL, /* delegate= */ NULL, "local");
ASSERT_NOT_NULL(sd1);
dns_search_domain_new(&manager, &sd2, DNS_SEARCH_DOMAIN_SYSTEM, NULL, "vpn.example.com");
dns_search_domain_new(&manager, &sd2, DNS_SEARCH_DOMAIN_SYSTEM, /* link= */ NULL, /* delegate= */ NULL, "vpn.example.com");
ASSERT_NOT_NULL(sd2);
dns_search_domain_new(&manager, &sd3, DNS_SEARCH_DOMAIN_SYSTEM, NULL, "org");
dns_search_domain_new(&manager, &sd3, DNS_SEARCH_DOMAIN_SYSTEM, /* link= */ NULL, /* delegate= */ NULL, "org");
ASSERT_NOT_NULL(sd3);
dns_search_domain_move_back_and_unmark(sd1);
@ -211,13 +211,13 @@ TEST(dns_search_domain_unlink_marked) {
DnsSearchDomain *sd1 = NULL, *sd2 = NULL;
_cleanup_(dns_search_domain_unrefp) DnsSearchDomain *sd3 = NULL;
dns_search_domain_new(&manager, &sd1, DNS_SEARCH_DOMAIN_SYSTEM, NULL, "local");
dns_search_domain_new(&manager, &sd1, DNS_SEARCH_DOMAIN_SYSTEM, /* link= */ NULL, /* delegate= */ NULL, "local");
ASSERT_NOT_NULL(sd1);
dns_search_domain_new(&manager, &sd2, DNS_SEARCH_DOMAIN_SYSTEM, NULL, "vpn.example.com");
dns_search_domain_new(&manager, &sd2, DNS_SEARCH_DOMAIN_SYSTEM, /* link= */ NULL, /* delegate= */ NULL, "vpn.example.com");
ASSERT_NOT_NULL(sd2);
dns_search_domain_new(&manager, &sd3, DNS_SEARCH_DOMAIN_SYSTEM, NULL, "org");
dns_search_domain_new(&manager, &sd3, DNS_SEARCH_DOMAIN_SYSTEM, /* link= */ NULL, /* delegate= */ NULL, "org");
ASSERT_NOT_NULL(sd3);
dns_search_domain_unlink_marked(sd1);
@ -245,13 +245,13 @@ TEST(dns_search_domain_unlink_all) {
Manager manager = {};
DnsSearchDomain *sd1 = NULL, *sd2 = NULL, *sd3 = NULL;
dns_search_domain_new(&manager, &sd1, DNS_SEARCH_DOMAIN_SYSTEM, NULL, "local");
dns_search_domain_new(&manager, &sd1, DNS_SEARCH_DOMAIN_SYSTEM, /* link= */ NULL, /* delegate= */ NULL, "local");
ASSERT_NOT_NULL(sd1);
dns_search_domain_new(&manager, &sd2, DNS_SEARCH_DOMAIN_SYSTEM, NULL, "vpn.example.com");
dns_search_domain_new(&manager, &sd2, DNS_SEARCH_DOMAIN_SYSTEM, /* link= */ NULL, /* delegate= */ NULL, "vpn.example.com");
ASSERT_NOT_NULL(sd2);
dns_search_domain_new(&manager, &sd3, DNS_SEARCH_DOMAIN_SYSTEM, NULL, "org");
dns_search_domain_new(&manager, &sd3, DNS_SEARCH_DOMAIN_SYSTEM, /* link= */ NULL, /* delegate= */ NULL, "org");
ASSERT_NOT_NULL(sd3);
dns_search_domain_unlink_all(sd1);
@ -267,13 +267,13 @@ TEST(dns_search_domain_find) {
Manager manager = {};
_cleanup_(dns_search_domain_unrefp) DnsSearchDomain *sd1 = NULL, *sd2 = NULL, *sd3 = NULL, *ret = NULL;
dns_search_domain_new(&manager, &sd1, DNS_SEARCH_DOMAIN_SYSTEM, NULL, "local");
dns_search_domain_new(&manager, &sd1, DNS_SEARCH_DOMAIN_SYSTEM, /* link= */ NULL, /* delegate= */ NULL, "local");
ASSERT_NOT_NULL(sd1);
dns_search_domain_new(&manager, &sd2, DNS_SEARCH_DOMAIN_SYSTEM, NULL, "vpn.example.com");
dns_search_domain_new(&manager, &sd2, DNS_SEARCH_DOMAIN_SYSTEM, /* link= */ NULL, /* delegate= */ NULL, "vpn.example.com");
ASSERT_NOT_NULL(sd2);
dns_search_domain_new(&manager, &sd3, DNS_SEARCH_DOMAIN_SYSTEM, NULL, "org");
dns_search_domain_new(&manager, &sd3, DNS_SEARCH_DOMAIN_SYSTEM, /* link= */ NULL, /* delegate= */ NULL, "org");
ASSERT_NOT_NULL(sd3);
ASSERT_TRUE(dns_search_domain_find(sd1, "local", &ret));

View File

@ -26,7 +26,7 @@ TEST(dns_zone_put_simple) {
DnsZoneItem *item = NULL;
_cleanup_(dns_resource_record_unrefp) DnsResourceRecord *rr = NULL;
ASSERT_OK(dns_scope_new(&manager, &scope, NULL, DNS_PROTOCOL_DNS, AF_INET));
ASSERT_OK(dns_scope_new(&manager, &scope, DNS_SCOPE_GLOBAL, /* link= */ NULL, /* delegate= */ NULL, DNS_PROTOCOL_DNS, AF_INET));
ASSERT_NOT_NULL(scope);
zone = &scope->zone;
@ -50,7 +50,7 @@ TEST(dns_zone_put_any_class_is_invalid) {
DnsZone *zone = NULL;
_cleanup_(dns_resource_record_unrefp) DnsResourceRecord *rr = NULL;
dns_scope_new(&manager, &scope, NULL, DNS_PROTOCOL_DNS, AF_INET);
dns_scope_new(&manager, &scope, DNS_SCOPE_GLOBAL, /* link= */ NULL, /* delegate= */ NULL, DNS_PROTOCOL_DNS, AF_INET);
ASSERT_NOT_NULL(scope);
zone = &scope->zone;
@ -68,7 +68,7 @@ TEST(dns_zone_put_any_type_is_invalid) {
DnsZone *zone = NULL;
_cleanup_(dns_resource_record_unrefp) DnsResourceRecord *rr = NULL;
dns_scope_new(&manager, &scope, NULL, DNS_PROTOCOL_DNS, AF_INET);
dns_scope_new(&manager, &scope, DNS_SCOPE_GLOBAL, /* link= */ NULL, /* delegate= */ NULL, DNS_PROTOCOL_DNS, AF_INET);
ASSERT_NOT_NULL(scope);
zone = &scope->zone;
@ -90,7 +90,7 @@ TEST(dns_zone_remove_rr_match) {
DnsZone *zone = NULL;
_cleanup_(dns_resource_record_unrefp) DnsResourceRecord *rr_in = NULL, *rr_out = NULL;
dns_scope_new(&manager, &scope, NULL, DNS_PROTOCOL_DNS, AF_INET);
dns_scope_new(&manager, &scope, DNS_SCOPE_GLOBAL, /* link= */ NULL, /* delegate= */ NULL, DNS_PROTOCOL_DNS, AF_INET);
ASSERT_NOT_NULL(scope);
zone = &scope->zone;
@ -115,7 +115,7 @@ TEST(dns_zone_remove_rr_match_one) {
DnsZone *zone = NULL;
_cleanup_(dns_resource_record_unrefp) DnsResourceRecord *rr_in = NULL, *rr_out = NULL;
dns_scope_new(&manager, &scope, NULL, DNS_PROTOCOL_DNS, AF_INET);
dns_scope_new(&manager, &scope, DNS_SCOPE_GLOBAL, /* link= */ NULL, /* delegate= */ NULL, DNS_PROTOCOL_DNS, AF_INET);
ASSERT_NOT_NULL(scope);
zone = &scope->zone;
@ -148,7 +148,7 @@ TEST(dns_zone_remove_rr_different_payload) {
DnsZone *zone = NULL;
_cleanup_(dns_resource_record_unrefp) DnsResourceRecord *rr_in = NULL, *rr_out = NULL;
dns_scope_new(&manager, &scope, NULL, DNS_PROTOCOL_DNS, AF_INET);
dns_scope_new(&manager, &scope, DNS_SCOPE_GLOBAL, /* link= */ NULL, /* delegate= */ NULL, DNS_PROTOCOL_DNS, AF_INET);
ASSERT_NOT_NULL(scope);
zone = &scope->zone;
@ -178,7 +178,7 @@ TEST(dns_zone_remove_rrs_by_key) {
_cleanup_(dns_resource_record_unrefp) DnsResourceRecord *rr1 = NULL, *rr2 = NULL, *rr3 = NULL;
DnsResourceKey *key = NULL;
dns_scope_new(&manager, &scope, NULL, DNS_PROTOCOL_DNS, AF_INET);
dns_scope_new(&manager, &scope, DNS_SCOPE_GLOBAL, /* link= */ NULL, /* delegate= */ NULL, DNS_PROTOCOL_DNS, AF_INET);
ASSERT_NOT_NULL(scope);
zone = &scope->zone;
@ -248,7 +248,7 @@ TEST(dns_zone_lookup_match_a) {
_cleanup_(dns_answer_unrefp) DnsAnswer *answer = NULL, *soa = NULL;
bool tentative;
dns_scope_new(&manager, &scope, NULL, DNS_PROTOCOL_DNS, AF_INET);
dns_scope_new(&manager, &scope, DNS_SCOPE_GLOBAL, /* link= */ NULL, /* delegate= */ NULL, DNS_PROTOCOL_DNS, AF_INET);
ASSERT_NOT_NULL(scope);
add_zone_rrs(scope);
@ -270,7 +270,7 @@ TEST(dns_zone_lookup_match_cname) {
_cleanup_(dns_answer_unrefp) DnsAnswer *answer = NULL, *soa = NULL;
bool tentative;
dns_scope_new(&manager, &scope, NULL, DNS_PROTOCOL_DNS, AF_INET);
dns_scope_new(&manager, &scope, DNS_SCOPE_GLOBAL, /* link= */ NULL, /* delegate= */ NULL, DNS_PROTOCOL_DNS, AF_INET);
ASSERT_NOT_NULL(scope);
add_zone_rrs(scope);
@ -293,7 +293,7 @@ TEST(dns_zone_lookup_match_any) {
_cleanup_(dns_answer_unrefp) DnsAnswer *answer = NULL, *soa = NULL;
bool tentative;
dns_scope_new(&manager, &scope, NULL, DNS_PROTOCOL_DNS, AF_INET);
dns_scope_new(&manager, &scope, DNS_SCOPE_GLOBAL, /* link= */ NULL, /* delegate= */ NULL, DNS_PROTOCOL_DNS, AF_INET);
ASSERT_NOT_NULL(scope);
add_zone_rrs(scope);
@ -324,7 +324,7 @@ TEST(dns_zone_lookup_match_any_apex) {
_cleanup_(dns_answer_unrefp) DnsAnswer *answer = NULL, *soa = NULL;
bool tentative;
dns_scope_new(&manager, &scope, NULL, DNS_PROTOCOL_DNS, AF_INET);
dns_scope_new(&manager, &scope, DNS_SCOPE_GLOBAL, /* link= */ NULL, /* delegate= */ NULL, DNS_PROTOCOL_DNS, AF_INET);
ASSERT_NOT_NULL(scope);
add_zone_rrs(scope);
@ -349,7 +349,7 @@ TEST(dns_zone_lookup_match_nothing) {
_cleanup_(dns_answer_unrefp) DnsAnswer *answer = NULL, *soa = NULL;
bool tentative;
dns_scope_new(&manager, &scope, NULL, DNS_PROTOCOL_DNS, AF_INET);
dns_scope_new(&manager, &scope, DNS_SCOPE_GLOBAL, /* link= */ NULL, /* delegate= */ NULL, DNS_PROTOCOL_DNS, AF_INET);
ASSERT_NOT_NULL(scope);
add_zone_rrs(scope);
@ -370,7 +370,7 @@ TEST(dns_zone_lookup_match_nothing_with_soa) {
_cleanup_(dns_answer_unrefp) DnsAnswer *answer = NULL, *soa = NULL;
bool tentative;
dns_scope_new(&manager, &scope, NULL, DNS_PROTOCOL_DNS, AF_INET);
dns_scope_new(&manager, &scope, DNS_SCOPE_GLOBAL, /* link= */ NULL, /* delegate= */ NULL, DNS_PROTOCOL_DNS, AF_INET);
ASSERT_NOT_NULL(scope);
add_zone_rrs(scope);

View File

@ -182,7 +182,7 @@ static void link_alloc_env_setup(LinkAllocEnv *env, int family, DnsServerType se
link = env->link;
ASSERT_OK(dns_server_new(&env->manager, &env->server, env->server_type,
link, family, &env->server_addr, env->server_port,
link, /* delegate= */ NULL, family, &env->server_addr, env->server_port,
env->ifindex, env->server_name, RESOLVE_CONFIG_SOURCE_DBUS));
ASSERT_NOT_NULL(env->server);

View File

@ -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);

View File

@ -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) {

View File

@ -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);

View File

@ -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;

View File

@ -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,

View File

@ -170,6 +170,8 @@ typedef struct Item {
bool try_replace:1;
bool purge:1;
OperationMask done;
} Item;
@ -3046,6 +3048,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 +3607,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 +3673,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,6 +3691,7 @@ 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))
@ -3838,6 +3846,12 @@ 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;

View File

@ -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));

View File

@ -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);

View File

@ -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_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,12 +1740,16 @@ 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:
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:
@ -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);

View File

@ -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);

View File

@ -2313,6 +2313,17 @@ SUBSYSTEMS=="scsi", PROGRAM=="/bin/sh -c \"printf %%s 'foo1 foo2' | grep 'foo1 f
SUBSYSTEM=="block", SUBSYSTEMS=="scsi", KERNEL=="sd*", SYMLINK+="blockdev"
KERNEL=="sda6", OPTIONS+="link_priority=10"
"""),
Rules.new(
"case insensitive match",
Device(
"/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda/sda1",
exp_links = ["ok"],
),
rules = r"""
KERNEL==i"SDA1", SUBSYSTEMS==i"SCSI", ATTRS{vendor}==i"a?a", SYMLINK+="ok"
"""),
]
def fork_and_run_udev(action: str, rules: Rules) -> None:

View File

@ -237,6 +237,8 @@ test_syntax_error 'ENV=="b"' 'Invalid attribute for ENV.'
test_syntax_error 'ENV{a}-="b"' 'Invalid operator for ENV.'
test_syntax_error 'ENV{a}:="b"' "ENV key takes '==', '!=', '=', or '+=' operator, assuming '='."
test_syntax_error 'ENV{ACTION}="b"' "Invalid ENV attribute. 'ACTION' cannot be set."
test_syntax_error 'ENV{a}=i"b"' "Invalid prefix 'i' for 'ENV'. The 'i' prefix can be specified only for '==' or '!=' operator."
test_syntax_error 'ENV{a}+=i"b"' "Invalid prefix 'i' for 'ENV'. The 'i' prefix can be specified only for '==' or '!=' operator."
test_syntax_error 'CONST=="b"' 'Invalid attribute for CONST.'
test_syntax_error 'CONST{a}=="b"' 'Invalid attribute for CONST.'
test_syntax_error 'CONST{arch}="b"' 'Invalid operator for CONST.'
@ -275,10 +277,12 @@ test_syntax_error 'TEST{0644}="b"' 'Invalid operator for TEST.'
test_syntax_error 'PROGRAM{a}=="b"' 'Invalid attribute for PROGRAM.'
test_syntax_error 'PROGRAM-="b"' 'Invalid operator for PROGRAM.'
test_syntax_error 'PROGRAM=="%", NAME="b"' 'Invalid value "%" for PROGRAM (char 1: invalid substitution type), ignoring.'
test_syntax_error 'PROGRAM==i"b"' "Invalid prefix 'i' for PROGRAM."
test_syntax_error 'IMPORT="b"' 'Invalid attribute for IMPORT.'
test_syntax_error 'IMPORT{a}="b"' 'Invalid attribute for IMPORT.'
test_syntax_error 'IMPORT{a}-="b"' 'Invalid operator for IMPORT.'
test_syntax_error 'IMPORT{file}=="%", NAME="b"' 'Invalid value "%" for IMPORT (char 1: invalid substitution type), ignoring.'
test_syntax_error 'IMPORT{file}==i"a", NAME="b"' "Invalid prefix 'i' for IMPORT."
test_syntax_error 'IMPORT{builtin}!="foo"' 'Unknown builtin command: foo'
test_syntax_error 'RESULT{a}=="b"' 'Invalid attribute for RESULT.'
test_syntax_error 'RESULT:="b"' 'Invalid operator for RESULT.'

View File

@ -9,26 +9,39 @@ set -o pipefail
export SYSTEMD_LOG_LEVEL=debug
c='
d /tmp/somedir
f /tmp/somedir/somefile - - - - baz
d$ /tmp/somedir
f$ /tmp/somedir/somefile - - - - baz
f /tmp/someotherfile - - - - qux
'
systemd-tmpfiles --create - <<<"$c"
test -f /tmp/somedir/somefile
grep -q baz /tmp/somedir/somefile
grep -q qux /tmp/someotherfile
systemd-tmpfiles --purge --dry-run - <<<"$c"
test -f /tmp/somedir/somefile
grep -q baz /tmp/somedir/somefile
grep -q qux /tmp/someotherfile
systemd-tmpfiles --purge - <<<"$c"
test ! -f /tmp/somedir/somefile
test ! -d /tmp/somedir/
grep -q qux /tmp/someotherfile
systemd-tmpfiles --create --purge --dry-run - <<<"$c"
test ! -f /tmp/somedir/somefile
test ! -d /tmp/somedir/
grep -q qux /tmp/someotherfile
systemd-tmpfiles --create --purge - <<<"$c"
test -f /tmp/somedir/somefile
grep -q baz /tmp/somedir/somefile
grep -q qux /tmp/someotherfile
systemd-tmpfiles --purge - <<<"$c"
test ! -f /tmp/somedir/somefile
test ! -d /tmp/somedir/
grep -q qux /tmp/someotherfile
rm /tmp/someotherfile

View File

@ -8,5 +8,5 @@
# See tmpfiles.d(5) for details
{% if LINK_SHELL_EXTRA_DROPIN %}
L {{SHELLPROFILEDIR}}/70-systemd-shell-extra.sh - - - - {{LIBEXECDIR}}/profile.d/70-systemd-shell-extra.sh
L$ {{SHELLPROFILEDIR}}/70-systemd-shell-extra.sh - - - - {{LIBEXECDIR}}/profile.d/70-systemd-shell-extra.sh
{% endif %}

View File

@ -8,7 +8,7 @@
# See tmpfiles.d(5) for details
{% if LINK_SSH_PROXY_DROPIN %}
L {{SSHCONFDIR}}/20-systemd-ssh-proxy.conf - - - - {{LIBEXECDIR}}/ssh_config.d/20-systemd-ssh-proxy.conf
L$ {{SSHCONFDIR}}/20-systemd-ssh-proxy.conf - - - - {{LIBEXECDIR}}/ssh_config.d/20-systemd-ssh-proxy.conf
{% endif %}
{% if CREATE_SSHDPRIVSEPDIR %}
d {{SSHDPRIVSEPDIR}} 0755

View File

@ -13,7 +13,7 @@
d /run/lock 0755 root root -
L /var/lock - - - - ../run/lock
{% if CREATE_LOG_DIRS %}
L /var/log/README - - - - ../..{{DOC_DIR}}/README.logs
L$ /var/log/README - - - - ../..{{DOC_DIR}}/README.logs
{% endif %}
# /run/lock/subsys is used for serializing SysV service execution, and

View File

@ -7,7 +7,7 @@
# See tmpfiles.d(5) for details
d /run/systemd/netif 0755 systemd-network systemd-network -
d /run/systemd/netif/links 0755 systemd-network systemd-network -
d /run/systemd/netif/leases 0755 systemd-network systemd-network -
d /var/lib/systemd/network 0755 systemd-network systemd-network -
d$ /run/systemd/netif 0755 systemd-network systemd-network -
d$ /run/systemd/netif/links 0755 systemd-network systemd-network -
d$ /run/systemd/netif/leases 0755 systemd-network systemd-network -
d$ /var/lib/systemd/network 0755 systemd-network systemd-network -

View File

@ -19,5 +19,5 @@ Q /var/lib/machines 0700 - - -
# systemd-nspawn --ephemeral places snapshots) we are more strict, to
# avoid removing unrelated temporary files.
R! /var/lib/machines/.#*
R! /.#machine.*
R!$ /var/lib/machines/.#*
R!$ /.#machine.*

View File

@ -14,10 +14,10 @@ x /var/tmp/systemd-private-%b-*
X /var/tmp/systemd-private-%b-*/tmp
# Remove top-level private temporary directories on each boot
R! /tmp/systemd-private-*
R! /var/tmp/systemd-private-*
R!$ /tmp/systemd-private-*
R!$ /var/tmp/systemd-private-*
# Handle lost systemd-coredump temp files. They could be lost on old filesystems,
# for example, after hard reboot.
x /var/lib/systemd/coredump/.#core*.%b*
r! /var/lib/systemd/coredump/.#*
r!$ /var/lib/systemd/coredump/.#*

View File

@ -13,11 +13,11 @@ f+! /run/utmp 0664 root utmp -
{% endif %}
d /run/systemd/ask-password 0755 root root -
d /run/systemd/seats 0755 root root -
d /run/systemd/sessions 0755 root root -
d /run/systemd/users 0755 root root -
d$ /run/systemd/seats 0755 root root -
d$ /run/systemd/sessions 0755 root root -
d$ /run/systemd/users 0755 root root -
d /run/systemd/machines 0755 root root -
d /run/systemd/shutdown 0755 root root -
d$ /run/systemd/shutdown 0755 root root -
d /run/log 0755 root root -