1
0
mirror of https://github.com/systemd/systemd.git synced 2024-10-26 08:55:40 +03:00

Compare commits

...

27 Commits

Author SHA1 Message Date
Lennart Poettering
9edf3b7374
Merge 95b1dd00b6 into f7078de515 2024-10-25 11:43:59 -07:00
Yu Watanabe
f7078de515
Merge pull request #34884 from poettering/run0-disconnect-fix
run: reconnect if our dbus connection is terminated
2024-10-26 02:50:48 +09:00
Yu Watanabe
6d6048b4cb
Merge pull request #34881 from poettering/run0-ui-tweaks
run0: various UI tweaks
2024-10-26 02:49:48 +09:00
Ivan Kruglov
10a48938ef machine: operation should not send a response when 'done' callback set 2024-10-26 02:45:53 +09:00
Lennart Poettering
b58b13f1c6 test: add brief testcase for systemd-run disconnect handling 2024-10-25 17:51:04 +02:00
Lennart Poettering
c8f59296bf run: reconnect if our dbus connection is terminated
We must be prepared that systemd temporarily drops off the bus or
disconnects our direct connections (due to systemctl daemon-reexec or
so). Hence automatically reconnect when we watch the unit status, and
handle this case gracefully.

Fixes: #32906 #27204
2024-10-25 17:51:04 +02:00
Lennart Poettering
d585085f57 update TODO 2024-10-25 17:32:19 +02:00
Lennart Poettering
ff4b6a1915 run: drop "-" prefix from command line when generating unit description
Let's not confuse users with the login shell indicator and drop it from
the description. This means a run0 session will now usually show up with
a description of "[run0] /bin/bash" rather than "[run0] -/bin/bash".
2024-10-25 17:32:19 +02:00
Lennart Poettering
d9f68f48f7 run: prefix unit description with our own process name
I think we should try to communicate clearly if something is a run0
session, or a systemd-run invocation. Hence, let's initialize the
description so that the command is prefixed by
program_invocation_short_name.

Effectively this means that our run0 sessions now appear as services
with a description of "[run0] -/bin/bash"
2024-10-25 17:32:19 +02:00
Lennart Poettering
0310b2a60b run: tweak how we name our transient units
The current logic is a bit complex how systemd-run units are called. It
used to be just the unique ID of the dbus connection. Which was nice,
since its system-widely, uniquely assigned to us. But this didn't work
out well, due to direct connections to PID 1 and due to soft reboots.

We nowadays have a better ID to use though, with nicer properties: the
kernel manages a pidfd ID for every process after all, and it's globally
unique, for any process, and regardless of soft reboots. Hence use that
for naming preferably, and just keep one branch with a randomized name
as fallback.
2024-10-25 17:32:19 +02:00
Lennart Poettering
115fac3c29 run0: optionally show superhero emoji on each shell prompt
This makes use of the infra introduced in 229d4a9806 to indicate visually on each prompt that we are in superuser mode temporarily.
pick ad5de3222f userdbctl: add some basic client-side filtering
2024-10-25 17:31:06 +02:00
Lennart Poettering
9d8f5e22f8
Merge pull request #34891 from poettering/run0-pty
run0: add --pty and --pipe switches to force allocation of a pty or pipe
2024-10-25 16:25:01 +02:00
Lennart Poettering
6fb0c52295 ci: add some basic testing of the new --pty and --pipe switches 2024-10-25 14:14:26 +02:00
Lennart Poettering
edd10ab29c run0: add options to force allocation of PTY or of pipe use
Fixes: #33033
2024-10-25 14:14:26 +02:00
Lennart Poettering
988053eac3 tree-wide: use isatty_safe() everywhere 2024-10-25 14:09:38 +02:00
Lennart Poettering
a586f57eb2 update TODO 2024-10-25 13:57:44 +02:00
Lennart Poettering
c18ac81f17
Merge pull request #34877 from aafeijoo-suse/veritysetup-fixes
veritysetup-generator: minor man/code changes
2024-10-25 10:06:31 +02:00
Lennart Poettering
c4363051e4
Merge pull request #34880 from poettering/change-user-on-pam-always
core: make sure that if PAMName= is set we always do the full user ch…
2024-10-25 09:22:03 +02:00
Lennart Poettering
f515ea1cd4 test: add quick test to verify the PAM stack really ran in all run0 modes of operation 2024-10-24 22:56:44 +02:00
Lennart Poettering
e4b4d9cc7a core: make sure that if PAMName= is set we always do the full user changing even if no user is specified explicitly
When PAMName= is set this should be enough to go through our entire user
changing story, so that PAM is definitely run, and environment variables
definitely pulled in and so on.

Previously, it would happen that under some circumstances we might no do
this when transitioning from root to root itself even though PAM was
enabled.

Fixes: #34682
2024-10-24 22:37:00 +02:00
Antonio Alvarez Feijoo
11de19f3da
veritysetup-generator: remove unused code 2024-10-24 10:07:45 +02:00
Antonio Alvarez Feijoo
e98e3f856d
man/veritysetup-generator: document veritytab kernel command line option 2024-10-24 10:07:28 +02:00
Antonio Alvarez Feijoo
dcbfc7872e
man: fix links to veritysetup(8) 2024-10-24 09:54:48 +02: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
41 changed files with 1738 additions and 413 deletions

8
TODO
View File

@ -129,6 +129,11 @@ Deprecations and removals:
Features:
* $LISTEN_PID, $MAINPID and $SYSTEMD_EXECPID env vars that the service manager
sets should be augmented with $LISTEN_PIDFDID, $MAINPIDFDID and
$SYSTEMD_EXECPIDFD (and similar for other env vars we might send). Also,
MAINPID= in sd_notify() should be augmented with MAINPIDFDID=, and so on.
* port copy.c over to use LabelOps for all labelling.
* port remaining getmntent() users over to libmount. There are subtle
@ -157,9 +162,6 @@ Features:
sd_event_add_child(), and then get rid of many more explicit sigprocmask()
calls.
* maybe set shell.prompt.prefix credential in run0 to some warning emoji,
i.e. ⚠️ or ☢️ or ⚡ or 👊 or 🧑‍🔧 or so.
* introduce new structure Tpm2CombinedPolicy, that combines the various TPm2
policy bits into one structure, i.e. public key info, pcr masks, pcrlock
stuff, pin and so on. Then pass that around in tpm2_seal() and tpm2_unseal().

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

@ -192,6 +192,35 @@
</listitem>
</varlistentry>
<varlistentry>
<term><option>--pty</option></term>
<term><option>--pipe</option></term>
<listitem><para>Request allocation of a pseudo TTY for the <command>run0</command> session (in case
of <option>--pty</option>), or request passing the caller's STDIO file descriptors directly through
(in case of <option>--pipe</option>). If neither switch is specified, or if both switches are
specified, the mode will be picked automatically: if standard input, standard output and standard
error output are all connected to a TTY then a pseudo TTY is allocated, otherwise the relevant file
descriptors are passed through directly.</para>
<xi:include href="version-info.xml" xpointer="v257"/>
</listitem>
</varlistentry>
<varlistentry>
<term><option>--shell-prompt-prefix=<replaceable>STRING</replaceable></option></term>
<listitem><para>Set a shell prompt prefix string. This ultimately controls the
<varname>$SHELL_PROMPT_PREFIX</varname> environment variable for the invoked program, which is
typically imported into the shell prompt. By default if emojis are supported a superhero emoji is
shown (🦸). This default may also be changed (or turned off) by passing the
<varname>$SYSTEMD_RUN_SHELL_PROMPT_PREFIX</varname> environment variable to <varname>run0</varname>,
see below. Set to an empty string to disable shell prompt prefixing.</para>
<xi:include href="version-info.xml" xpointer="v257"/>
</listitem>
</varlistentry>
<varlistentry>
<term><option>--machine=</option></term>
@ -256,7 +285,30 @@
<xi:include href="version-info.xml" xpointer="v256"/></listitem>
</varlistentry>
<varlistentry>
<term><varname>$SHELL_PROMPT_PREFIX</varname></term>
<listitem><para>By default set to the superhero emoji (if supported), but may be overriden with the
<varname>$SYSTEMD_RUN_SHELL_PROMPT_PREFIX</varname> environment variable (see below), or the
<option>--shell-prompt-prefix=</option> switch (see above).</para>
<xi:include href="version-info.xml" xpointer="v257"/></listitem>
</varlistentry>
</variablelist>
<para>The following variables may be passed to <command>run0</command>:</para>
<variablelist>
<varlistentry>
<term><varname>$SYSTEMD_RUN_SHELL_PROMPT_PREFIX</varname></term>
<listitem><para>If set, overrides the default shell prompt prefix that <command>run0</command> sets
for the invoked shell (the superhero emoji). Set to an empty string to disable shell prompt
prefixing.</para>
<xi:include href="version-info.xml" xpointer="v257"/></listitem>
</varlistentry>
</variablelist>
</refsect1>
<refsect1>

View File

@ -54,12 +54,24 @@
<listitem><para>Takes a boolean argument. Defaults to <literal>yes</literal>. If
<literal>no</literal>, disables the generator entirely. <varname>rd.systemd.verity=</varname> is
honored only by the initrd while <varname>systemd.verity=</varname> is honored by both the host
system and the initrd.</para>
honored only in the initrd while <varname>systemd.verity=</varname> is honored by both the main
system and in the initrd.</para>
<xi:include href="version-info.xml" xpointer="v233"/></listitem>
</varlistentry>
<varlistentry>
<term><varname>veritytab=</varname></term>
<term><varname>rd.veritytab=</varname></term>
<listitem><para>Takes a boolean argument. Defaults to <literal>yes</literal>. If
<literal>no</literal>, causes the generator to ignore any devices configured in
<filename>/etc/veritytab</filename>. <varname>rd.veritytab=</varname> is honored only in the initrd
while <varname>veritytab=</varname> is honored by both the main system and in the initrd.</para>
<xi:include href="version-info.xml" xpointer="v248"/></listitem>
</varlistentry>
<varlistentry>
<term><varname>roothash=</varname></term>
@ -104,7 +116,7 @@
<option>hash=<replaceable>HASH</replaceable></option>, <option>fec-device=<replaceable>PATH</replaceable></option>,
<option>fec-offset=<replaceable>BYTES</replaceable></option>, <option>fec-roots=<replaceable>NUM</replaceable></option> and
<option>root-hash-signature=<replaceable>PATH</replaceable>|base64:<replaceable>HEX</replaceable></option>. See
<citerefentry project='die-net'><refentrytitle>veritysetup</refentrytitle><manvolnum>8</manvolnum></citerefentry> for more
<citerefentry project='man-pages'><refentrytitle>veritysetup</refentrytitle><manvolnum>8</manvolnum></citerefentry> for more
details.</para>
<xi:include href="version-info.xml" xpointer="v248"/></listitem>
@ -129,7 +141,7 @@
<para><simplelist type="inline">
<member><citerefentry><refentrytitle>systemd</refentrytitle><manvolnum>1</manvolnum></citerefentry></member>
<member><citerefentry><refentrytitle>systemd-veritysetup@.service</refentrytitle><manvolnum>8</manvolnum></citerefentry></member>
<member><citerefentry project='die-net'><refentrytitle>veritysetup</refentrytitle><manvolnum>8</manvolnum></citerefentry></member>
<member><citerefentry project='man-pages'><refentrytitle>veritysetup</refentrytitle><manvolnum>8</manvolnum></citerefentry></member>
<member><citerefentry><refentrytitle>systemd-fstab-generator</refentrytitle><manvolnum>8</manvolnum></citerefentry></member>
</simplelist></para>
</refsect1>

View File

@ -97,7 +97,7 @@
<para><simplelist type="inline">
<member><citerefentry><refentrytitle>systemd</refentrytitle><manvolnum>1</manvolnum></citerefentry></member>
<member><citerefentry><refentrytitle>systemd-veritysetup-generator</refentrytitle><manvolnum>8</manvolnum></citerefentry></member>
<member><citerefentry project='die-net'><refentrytitle>veritysetup</refentrytitle><manvolnum>8</manvolnum></citerefentry></member>
<member><citerefentry project='man-pages'><refentrytitle>veritysetup</refentrytitle><manvolnum>8</manvolnum></citerefentry></member>
</simplelist></para>
</refsect1>

View File

@ -319,7 +319,7 @@ data /etc/data /etc/hash a5ee4b42f70ae1f46a08a7c92c2e0a20672ad2f514792730f5d49d7
<member><citerefentry><refentrytitle>systemd-veritysetup@.service</refentrytitle><manvolnum>8</manvolnum></citerefentry></member>
<member><citerefentry><refentrytitle>systemd-veritysetup-generator</refentrytitle><manvolnum>8</manvolnum></citerefentry></member>
<member><citerefentry project='man-pages'><refentrytitle>fstab</refentrytitle><manvolnum>5</manvolnum></citerefentry></member>
<member><citerefentry project='die-net'><refentrytitle>veritysetup</refentrytitle><manvolnum>8</manvolnum></citerefentry></member>
<member><citerefentry project='man-pages'><refentrytitle>veritysetup</refentrytitle><manvolnum>8</manvolnum></citerefentry></member>
</simplelist></para>
</refsect1>

View File

@ -80,6 +80,7 @@ const char* special_glyph_full(SpecialGlyph code, bool force_utf) {
[SPECIAL_GLYPH_YELLOW_CIRCLE] = "o",
[SPECIAL_GLYPH_BLUE_CIRCLE] = "o",
[SPECIAL_GLYPH_GREEN_CIRCLE] = "o",
[SPECIAL_GLYPH_SUPERHERO] = "S",
},
/* UTF-8 */
@ -149,6 +150,7 @@ const char* special_glyph_full(SpecialGlyph code, bool force_utf) {
[SPECIAL_GLYPH_YELLOW_CIRCLE] = u8"🟡",
[SPECIAL_GLYPH_BLUE_CIRCLE] = u8"🔵",
[SPECIAL_GLYPH_GREEN_CIRCLE] = u8"🟢",
[SPECIAL_GLYPH_SUPERHERO] = u8"🦸",
},
};

View File

@ -55,6 +55,7 @@ typedef enum SpecialGlyph {
SPECIAL_GLYPH_YELLOW_CIRCLE,
SPECIAL_GLYPH_BLUE_CIRCLE,
SPECIAL_GLYPH_GREEN_CIRCLE,
SPECIAL_GLYPH_SUPERHERO,
_SPECIAL_GLYPH_MAX,
_SPECIAL_GLYPH_INVALID = -EINVAL,
} SpecialGlyph;

View File

@ -4061,7 +4061,7 @@ int exec_invoke(
int r, ngids = 0;
_cleanup_free_ gid_t *supplementary_gids = NULL;
const char *username = NULL, *groupname = NULL;
_cleanup_free_ char *home_buffer = NULL, *memory_pressure_path = NULL;
_cleanup_free_ char *home_buffer = NULL, *memory_pressure_path = NULL, *own_user = NULL;
const char *home = NULL, *shell = NULL;
char **final_argv = NULL;
dev_t journal_stream_dev = 0;
@ -4298,8 +4298,23 @@ int exec_invoke(
username = runtime->dynamic_creds->user->name;
} else {
if (context->user) {
r = get_fixed_user(context->user, &username, &uid, &gid, &home, &shell);
const char *u;
if (context->user)
u = context->user;
else if (context->pam_name) {
/* If PAM is enabled but no user name is explicitly selected, then use our own one. */
own_user = getusername_malloc();
if (!own_user) {
*exit_status = EXIT_USER;
return log_exec_error_errno(context, params, r, "Failed to determine my own user ID: %m");
}
u = own_user;
} else
u = NULL;
if (u) {
r = get_fixed_user(u, &username, &uid, &gid, &home, &shell);
if (r < 0) {
*exit_status = EXIT_USER;
return log_exec_error_errno(context, params, r, "Failed to determine user credentials: %m");

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

@ -8,7 +8,7 @@
#include "operation.h"
#include "process-util.h"
static int operation_done_internal(const siginfo_t *si, Operation *o, sd_bus_error *error) {
static int read_operation_errno(const siginfo_t *si, Operation *o) {
int r;
assert(si);
@ -27,15 +27,6 @@ static int operation_done_internal(const siginfo_t *si, Operation *o, sd_bus_err
return log_debug_errno(SYNTHETIC_ERRNO(EIO), "Received unexpectedly short message when reading operation's errno");
}
if (o->done)
/* A completion routine is set for this operation, call it. */
return o->done(o, r, error);
/* The default operation when done is to simply return an error on failure or an empty success
* message on success. */
if (r < 0)
log_debug_errno(r, "Operation failed: %m");
return r;
}
@ -51,10 +42,18 @@ static int operation_done(sd_event_source *s, const siginfo_t *si, void *userdat
o->pid = 0;
r = read_operation_errno(si, o);
if (r < 0)
log_debug_errno(r, "Operation failed: %m");
/* If a completion routine (o->done) is set for this operation, call it. It sends a response, but can return an error in which case it expect us to reply.
* Otherwise, the default action is to simply return an error on failure or an empty success message on success. */
if (o->message) {
_cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
if (o->done)
r = o->done(o, r, &error);
r = operation_done_internal(si, o, &error);
if (r < 0) {
if (!sd_bus_error_is_set(&error))
sd_bus_error_set_errno(&error, r);
@ -62,16 +61,20 @@ static int operation_done(sd_event_source *s, const siginfo_t *si, void *userdat
r = sd_bus_reply_method_error(o->message, &error);
if (r < 0)
log_error_errno(r, "Failed to reply to dbus message: %m");
} else {
} else if (!o->done) {
/* when o->done set it's responsible for sending reply in a happy-path case */
r = sd_bus_reply_method_return(o->message, NULL);
if (r < 0)
log_error_errno(r, "Failed to reply to dbus message: %m");
}
} else if (o->link) {
r = operation_done_internal(si, o, /* error = */ NULL);
if (o->done)
r = o->done(o, r, /* error = */ NULL);
if (r < 0)
(void) sd_varlink_error_errno(o->link, r);
else
else if (!o->done)
/* when o->done set it's responsible for sending reply in a happy-path case */
(void) sd_varlink_reply(o->link, NULL);
} else
assert_not_reached();

View File

@ -290,7 +290,7 @@ static int handle_arg_console(const char *arg) {
else if (streq(arg, "passive"))
arg_console_mode = CONSOLE_PASSIVE;
else if (streq(arg, "pipe")) {
if (isatty_safe(STDIN_FILENO) && isatty(STDOUT_FILENO))
if (isatty_safe(STDIN_FILENO) && isatty_safe(STDOUT_FILENO))
log_full(arg_quiet ? LOG_DEBUG : LOG_NOTICE,
"Console mode 'pipe' selected, but standard input/output are connected to an interactive TTY. "
"Most likely you want to use 'interactive' console mode for proper interactivity and shell job control. "
@ -298,7 +298,7 @@ static int handle_arg_console(const char *arg) {
arg_console_mode = CONSOLE_PIPE;
} else if (streq(arg, "autopipe")) {
if (isatty_safe(STDIN_FILENO) && isatty(STDOUT_FILENO))
if (isatty_safe(STDIN_FILENO) && isatty_safe(STDOUT_FILENO))
arg_console_mode = CONSOLE_INTERACTIVE;
else
arg_console_mode = CONSOLE_PIPE;
@ -5981,7 +5981,7 @@ static int run(int argc, char *argv[]) {
umask(0022);
if (arg_console_mode < 0)
arg_console_mode = isatty_safe(STDIN_FILENO) && isatty(STDOUT_FILENO) ?
arg_console_mode = isatty_safe(STDIN_FILENO) && isatty_safe(STDOUT_FILENO) ?
CONSOLE_INTERACTIVE : CONSOLE_READ_ONLY;
if (arg_console_mode == CONSOLE_PIPE) /* if we pass STDERR on to the container, don't add our own logs into it too */

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,18 +1752,27 @@ 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)
if (scope->link) {
/* Honour whatever is explicitly configured. This is really the best approach, and trumps any
* automatic logic. */
if (scope->link->default_route >= 0)
return scope->link->default_route;
/* 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;
/* Honour whatever is explicitly configured. This is really the best approach, and trumps any
* automatic logic. */
if (scope->link->default_route >= 0)
return scope->link->default_route;
/* 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);
}
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)
manager_set_dns_server(m, m->fallback_dns_servers);
}
/* 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"
@ -522,6 +523,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);
}
@ -599,6 +604,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);
@ -616,9 +622,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;
@ -699,7 +707,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;
@ -768,7 +778,6 @@ int manager_start(Manager *m) {
Manager *manager_free(Manager *m) {
Link *l;
DnssdService *s;
if (!m)
return NULL;
@ -780,12 +789,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 */
@ -833,6 +843,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);
@ -1560,7 +1571,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)
@ -1568,7 +1579,17 @@ 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 */
if (ordered_set_isempty(*dns)) {
@ -1590,7 +1611,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);
@ -1613,8 +1633,23 @@ int manager_compile_search_domains(Manager *m, OrderedSet **domains, int filter_
return r;
}
HASHMAP_FOREACH(l, m->links) {
DnsDelegate *delegate;
HASHMAP_FOREACH(delegate, m->delegates)
LIST_FOREACH(domains, d, delegate->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;
}
Link *l;
HASHMAP_FOREACH(l, m->links)
LIST_FOREACH(domains, d, l->search_domains) {
if (filter_route >= 0 &&
@ -1627,7 +1662,6 @@ int manager_compile_search_domains(Manager *m, OrderedSet **domains, int filter_
if (r < 0)
return r;
}
}
return 0;
}
@ -1710,14 +1744,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

@ -22,6 +22,7 @@
#include "chase.h"
#include "env-util.h"
#include "escape.h"
#include "event-util.h"
#include "exec-util.h"
#include "exit-status.h"
#include "fd-util.h"
@ -85,6 +86,7 @@ static char *arg_exec_path = NULL;
static bool arg_ignore_failure = false;
static char *arg_background = NULL;
static sd_json_format_flags_t arg_json_format_flags = SD_JSON_FORMAT_OFF;
static char *arg_shell_prompt_prefix = NULL;
STATIC_DESTRUCTOR_REGISTER(arg_description, freep);
STATIC_DESTRUCTOR_REGISTER(arg_environment, strv_freep);
@ -96,6 +98,7 @@ STATIC_DESTRUCTOR_REGISTER(arg_working_directory, freep);
STATIC_DESTRUCTOR_REGISTER(arg_cmdline, strv_freep);
STATIC_DESTRUCTOR_REGISTER(arg_exec_path, freep);
STATIC_DESTRUCTOR_REGISTER(arg_background, freep);
STATIC_DESTRUCTOR_REGISTER(arg_shell_prompt_prefix, freep);
static int help(void) {
_cleanup_free_ char *link = NULL;
@ -171,6 +174,10 @@ static int help_sudo_mode(void) {
if (r < 0)
return log_oom();
/* NB: Let's not go overboard with short options: we try to keep a modicum of compatibility with
* sudo's short switches, hence please do not introduce new short switches unless they have a roughly
* equivalent purpose on sudo. Use long options for everything private to run0. */
printf("%s [OPTIONS...] COMMAND [ARGUMENTS...]\n"
"\n%sElevate privileges interactively.%s\n\n"
" -h --help Show this help\n"
@ -188,6 +195,9 @@ static int help_sudo_mode(void) {
" -D --chdir=PATH Set working directory\n"
" --setenv=NAME[=VALUE] Set environment variable\n"
" --background=COLOR Set ANSI color for background\n"
" --pty Request allocation of a pseudo TTY for stdio\n"
" --pipe Request direct pipe for stdio\n"
" --shell-prompt-prefix=PREFIX Set $SHELL_PROMPT_PREFIX\n"
"\nSee the %s for details.\n",
program_invocation_short_name,
ansi_highlight(),
@ -674,7 +684,7 @@ static int parse_argv(int argc, char *argv[]) {
/* If we both --pty and --pipe are specified we'll automatically pick --pty if we are connected fully
* to a TTY and pick direct fd passing otherwise. This way, we automatically adapt to usage in a shell
* pipeline, but we are neatly interactive with tty-level isolation otherwise. */
arg_stdio = isatty_safe(STDIN_FILENO) && isatty(STDOUT_FILENO) && isatty(STDERR_FILENO) ?
arg_stdio = isatty_safe(STDIN_FILENO) && isatty_safe(STDOUT_FILENO) && isatty_safe(STDERR_FILENO) ?
ARG_STDIO_PTY :
ARG_STDIO_DIRECT;
@ -770,27 +780,33 @@ static int parse_argv_sudo_mode(int argc, char *argv[]) {
ARG_NICE,
ARG_SETENV,
ARG_BACKGROUND,
ARG_PTY,
ARG_PIPE,
ARG_SHELL_PROMPT_PREFIX,
};
/* If invoked as "run0" binary, let's expose a more sudo-like interface. We add various extensions
* though (but limit the extension to long options). */
static const struct option options[] = {
{ "help", no_argument, NULL, 'h' },
{ "version", no_argument, NULL, 'V' },
{ "no-ask-password", no_argument, NULL, ARG_NO_ASK_PASSWORD },
{ "machine", required_argument, NULL, ARG_MACHINE },
{ "unit", required_argument, NULL, ARG_UNIT },
{ "property", required_argument, NULL, ARG_PROPERTY },
{ "description", required_argument, NULL, ARG_DESCRIPTION },
{ "slice", required_argument, NULL, ARG_SLICE },
{ "slice-inherit", no_argument, NULL, ARG_SLICE_INHERIT },
{ "user", required_argument, NULL, 'u' },
{ "group", required_argument, NULL, 'g' },
{ "nice", required_argument, NULL, ARG_NICE },
{ "chdir", required_argument, NULL, 'D' },
{ "setenv", required_argument, NULL, ARG_SETENV },
{ "background", required_argument, NULL, ARG_BACKGROUND },
{ "help", no_argument, NULL, 'h' },
{ "version", no_argument, NULL, 'V' },
{ "no-ask-password", no_argument, NULL, ARG_NO_ASK_PASSWORD },
{ "machine", required_argument, NULL, ARG_MACHINE },
{ "unit", required_argument, NULL, ARG_UNIT },
{ "property", required_argument, NULL, ARG_PROPERTY },
{ "description", required_argument, NULL, ARG_DESCRIPTION },
{ "slice", required_argument, NULL, ARG_SLICE },
{ "slice-inherit", no_argument, NULL, ARG_SLICE_INHERIT },
{ "user", required_argument, NULL, 'u' },
{ "group", required_argument, NULL, 'g' },
{ "nice", required_argument, NULL, ARG_NICE },
{ "chdir", required_argument, NULL, 'D' },
{ "setenv", required_argument, NULL, ARG_SETENV },
{ "background", required_argument, NULL, ARG_BACKGROUND },
{ "pty", no_argument, NULL, ARG_PTY },
{ "pipe", no_argument, NULL, ARG_PIPE },
{ "shell-prompt-prefix", required_argument, NULL, ARG_SHELL_PROMPT_PREFIX },
{},
};
@ -883,6 +899,26 @@ static int parse_argv_sudo_mode(int argc, char *argv[]) {
break;
case ARG_PTY:
if (IN_SET(arg_stdio, ARG_STDIO_DIRECT, ARG_STDIO_AUTO)) /* if --pipe is already used, upgrade to auto mode */
arg_stdio = ARG_STDIO_AUTO;
else
arg_stdio = ARG_STDIO_PTY;
break;
case ARG_PIPE:
if (IN_SET(arg_stdio, ARG_STDIO_PTY, ARG_STDIO_AUTO)) /* If --pty is already used, upgrade to auto mode */
arg_stdio = ARG_STDIO_AUTO;
else
arg_stdio = ARG_STDIO_DIRECT;
break;
case ARG_SHELL_PROMPT_PREFIX:
r = free_and_strdup_warn(&arg_shell_prompt_prefix, optarg);
if (r < 0)
return r;
break;
case '?':
return -EINVAL;
@ -913,7 +949,9 @@ static int parse_argv_sudo_mode(int argc, char *argv[]) {
arg_wait = true;
arg_aggressive_gc = true;
arg_stdio = isatty_safe(STDIN_FILENO) && isatty(STDOUT_FILENO) && isatty(STDERR_FILENO) ? ARG_STDIO_PTY : ARG_STDIO_DIRECT;
if (IN_SET(arg_stdio, ARG_STDIO_NONE, ARG_STDIO_AUTO))
arg_stdio = isatty_safe(STDIN_FILENO) && isatty_safe(STDOUT_FILENO) && isatty_safe(STDERR_FILENO) ? ARG_STDIO_PTY : ARG_STDIO_DIRECT;
arg_expand_environment = false;
arg_send_sighup = true;
@ -993,6 +1031,25 @@ static int parse_argv_sudo_mode(int argc, char *argv[]) {
log_debug_errno(r, "Unable to get terminal background color, not tinting background: %m");
}
if (!arg_shell_prompt_prefix) {
const char *e = secure_getenv("SYSTEMD_RUN_SHELL_PROMPT_PREFIX");
if (e) {
arg_shell_prompt_prefix = strdup(e);
if (!arg_shell_prompt_prefix)
return log_oom();
} else if (emoji_enabled()) {
arg_shell_prompt_prefix = strjoin(special_glyph(SPECIAL_GLYPH_SUPERHERO), " ");
if (!arg_shell_prompt_prefix)
return log_oom();
}
}
if (!isempty(arg_shell_prompt_prefix)) {
r = strv_env_assign(&arg_environment, "SHELL_PROMPT_PREFIX", arg_shell_prompt_prefix);
if (r < 0)
return log_error_errno(r, "Failed to set $SHELL_PROMPT_PREFIX environment variable: %m");
}
return 1;
}
@ -1181,7 +1238,7 @@ static int transient_service_set_properties(sd_bus_message *m, const char *pty_p
if (r < 0)
return bus_log_create_error(r);
send_term = isatty_safe(STDIN_FILENO) || isatty(STDOUT_FILENO) || isatty(STDERR_FILENO);
send_term = isatty_safe(STDIN_FILENO) || isatty_safe(STDOUT_FILENO) || isatty_safe(STDERR_FILENO);
}
if (send_term) {
@ -1345,78 +1402,75 @@ static int transient_timer_set_properties(sd_bus_message *m) {
return 0;
}
static int make_unit_name(sd_bus *bus, UnitType t, char **ret) {
unsigned soft_reboots_count = 0;
const char *unique, *id;
char *p;
static int make_unit_name(UnitType t, char **ret) {
int r;
assert(bus);
assert(t >= 0);
assert(t < _UNIT_TYPE_MAX);
assert(ret);
r = sd_bus_get_unique_name(bus, &unique);
/* Preferably use our PID + pidfd ID as identifier, if available. It's a boot time unique identifier
* managed by the kernel. Unfortunately only new kernels support this, hence we keep some fallback
* logic in place. */
_cleanup_(pidref_done) PidRef self = PIDREF_NULL;
r = pidref_set_self(&self);
if (r < 0)
return log_error_errno(r, "Failed to get reference to my own process: %m");
r = pidref_acquire_pidfd_id(&self);
if (r < 0) {
log_debug_errno(r, "Failed to acquire pidfd ID of myself, defaulting to randomized unit name: %m");
/* We couldn't get the pidfd id. In that case, just pick a random uuid as name */
sd_id128_t rnd;
/* We couldn't get the unique name, which is a pretty
* common case if we are connected to systemd
* directly. In that case, just pick a random uuid as
* name */
r = sd_id128_randomize(&rnd);
if (r < 0)
return log_error_errno(r, "Failed to generate random run unit name: %m");
if (asprintf(ret, "run-r" SD_ID128_FORMAT_STR ".%s", SD_ID128_FORMAT_VAL(rnd), unit_type_to_string(t)) < 0)
return log_oom();
r = asprintf(ret, "run-r" SD_ID128_FORMAT_STR ".%s", SD_ID128_FORMAT_VAL(rnd), unit_type_to_string(t));
} else
r = asprintf(ret, "run-p" PID_FMT "-i%" PRIu64 ".%s", self.pid, self.fd_id, unit_type_to_string(t));
if (r < 0)
return log_oom();
return 0;
}
return 0;
}
/* We managed to get the unique name, then let's use that to name our transient units. */
static int connect_bus(sd_bus **ret) {
_cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL;
int r;
id = startswith(unique, ":1."); /* let' strip the usual prefix */
if (!id)
id = startswith(unique, ":"); /* the spec only requires things to start with a colon, hence
* let's add a generic fallback for that. */
if (!id)
return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
"Unique name %s has unexpected format.",
unique);
assert(ret);
/* The unique D-Bus names are actually unique per D-Bus instance, so on soft-reboot they will wrap
* and start over since the D-Bus broker is restarted. If there's a failed unit left behind that
* hasn't been garbage collected, we'll conflict. Append the soft-reboot counter to avoid clashing. */
if (arg_runtime_scope == RUNTIME_SCOPE_SYSTEM) {
_cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
r = bus_get_property_trivial(
bus, bus_systemd_mgr, "SoftRebootsCount", &error, 'u', &soft_reboots_count);
if (r < 0)
log_debug_errno(r,
"Failed to get SoftRebootsCount property, ignoring: %s",
bus_error_message(&error, r));
}
/* If --wait is used connect via the bus, unconditionally, as ref/unref is not supported via the
* limited direct connection */
if (arg_wait ||
arg_stdio != ARG_STDIO_NONE ||
(arg_runtime_scope == RUNTIME_SCOPE_USER && !IN_SET(arg_transport, BUS_TRANSPORT_LOCAL, BUS_TRANSPORT_CAPSULE)))
r = bus_connect_transport(arg_transport, arg_host, arg_runtime_scope, &bus);
else
r = bus_connect_transport_systemd(arg_transport, arg_host, arg_runtime_scope, &bus);
if (r < 0)
return bus_log_connect_error(r, arg_transport, arg_runtime_scope);
if (soft_reboots_count > 0) {
if (asprintf(&p, "run-u%s-s%u.%s", id, soft_reboots_count, unit_type_to_string(t)) < 0)
return log_oom();
} else {
p = strjoin("run-u", id, ".", unit_type_to_string(t));
if (!p)
return log_oom();
}
(void) sd_bus_set_allow_interactive_authorization(bus, arg_ask_password);
*ret = p;
*ret = TAKE_PTR(bus);
return 0;
}
typedef struct RunContext {
sd_bus *bus;
sd_event *event;
PTYForward *forward;
sd_bus_slot *match;
char *service;
char *bus_path;
/* Bus objects */
sd_bus *bus;
sd_bus_slot *match_properties_changed;
sd_bus_slot *match_disconnected;
sd_event_source *retry_timer;
/* Current state of the unit */
char *active_state;
@ -1437,16 +1491,72 @@ typedef struct RunContext {
uint32_t exit_status;
} RunContext;
static void run_context_free(RunContext *c) {
static int run_context_update(RunContext *c);
static int run_context_attach_bus(RunContext *c, sd_bus *bus);
static void run_context_detach_bus(RunContext *c);
static int run_context_reconnect(RunContext *c);
static void run_context_done(RunContext *c) {
assert(c);
run_context_detach_bus(c);
c->retry_timer = sd_event_source_disable_unref(c->retry_timer);
c->forward = pty_forward_free(c->forward);
c->match = sd_bus_slot_unref(c->match);
c->bus = sd_bus_unref(c->bus);
c->event = sd_event_unref(c->event);
free(c->active_state);
free(c->result);
free(c->bus_path);
free(c->service);
}
static int on_retry_timer(sd_event_source *s, uint64_t usec, void *userdata) {
RunContext *c = ASSERT_PTR(userdata);
c->retry_timer = sd_event_source_disable_unref(c->retry_timer);
return run_context_reconnect(c);
}
static int run_context_reconnect(RunContext *c) {
int r;
assert(c);
run_context_detach_bus(c);
_cleanup_(sd_bus_unrefp) sd_bus *bus = NULL;
r = connect_bus(&bus);
if (r < 0) {
log_warning_errno(r, "Failed to reconnect, retrying in 2s: %m");
r = event_reset_time_relative(
c->event,
&c->retry_timer,
CLOCK_MONOTONIC,
2 * USEC_PER_SEC, /* accuracy= */ 0,
on_retry_timer, c,
SD_EVENT_PRIORITY_NORMAL,
"retry-timeout",
/* force_reset= */ false);
if (r < 0) {
(void) sd_event_exit(c->event, EXIT_FAILURE);
return log_error_errno(r, "Failed to install retry timer: %m");
}
return 0;
}
r = run_context_attach_bus(c, bus);
if (r < 0) {
(void) sd_event_exit(c->event, EXIT_FAILURE);
return r;
}
log_info("Reconnected to bus.");
return run_context_update(c);
}
static void run_context_check_done(RunContext *c) {
@ -1454,16 +1564,13 @@ static void run_context_check_done(RunContext *c) {
assert(c);
if (c->match)
done = STRPTR_IN_SET(c->active_state, "inactive", "failed") && !c->has_job;
else
done = true;
done = STRPTR_IN_SET(c->active_state, "inactive", "failed") && !c->has_job;
if (c->forward && !pty_forward_is_done(c->forward) && done) /* If the service is gone, it's time to drain the output */
done = pty_forward_drain(c->forward);
if (done)
sd_event_exit(c->event, EXIT_SUCCESS);
(void) sd_event_exit(c->event, EXIT_SUCCESS);
}
static int map_job(sd_bus *bus, const char *member, sd_bus_message *m, sd_bus_error *error, void *userdata) {
@ -1480,7 +1587,7 @@ static int map_job(sd_bus *bus, const char *member, sd_bus_message *m, sd_bus_er
return 0;
}
static int run_context_update(RunContext *c, const char *path) {
static int run_context_update(RunContext *c) {
static const struct bus_properties_map map[] = {
{ "ActiveState", "s", NULL, offsetof(RunContext, active_state) },
@ -1503,16 +1610,35 @@ static int run_context_update(RunContext *c, const char *path) {
_cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
int r;
r = bus_map_all_properties(c->bus,
"org.freedesktop.systemd1",
path,
map,
BUS_MAP_STRDUP,
&error,
NULL,
c);
assert(c);
assert(c->bus);
r = bus_map_all_properties(
c->bus,
"org.freedesktop.systemd1",
c->bus_path,
map,
BUS_MAP_STRDUP,
&error,
NULL,
c);
if (r < 0) {
sd_event_exit(c->event, EXIT_FAILURE);
/* If this is a connection error, then try to reconnect. This might be because the service
* manager is being restarted. Handle this gracefully. */
if (sd_bus_error_has_names(
&error,
SD_BUS_ERROR_NO_REPLY,
SD_BUS_ERROR_DISCONNECTED,
SD_BUS_ERROR_TIMED_OUT,
SD_BUS_ERROR_SERVICE_UNKNOWN,
SD_BUS_ERROR_NAME_HAS_NO_OWNER)) {
log_info("Bus call failed due to connection problems. Trying to reconnect...");
/* Not propagating error, because we handled it already, by reconnecting. */
return run_context_reconnect(c);
}
(void) sd_event_exit(c->event, EXIT_FAILURE);
return log_error_errno(r, "Failed to query unit state: %s", bus_error_message(&error, r));
}
@ -1521,11 +1647,67 @@ static int run_context_update(RunContext *c, const char *path) {
}
static int on_properties_changed(sd_bus_message *m, void *userdata, sd_bus_error *error) {
RunContext *c = ASSERT_PTR(userdata);
return run_context_update(ASSERT_PTR(userdata));
}
assert(m);
static int on_disconnected(sd_bus_message *m, void *userdata, sd_bus_error *error) {
/* If our connection gets terminated, then try to reconnect. This might be because the service
* manager is being restarted. Handle this gracefully. */
log_info("Got disconnected from bus connection. Trying to reconnect...");
return run_context_reconnect(ASSERT_PTR(userdata));
}
return run_context_update(c, sd_bus_message_get_path(m));
static int run_context_attach_bus(RunContext *c, sd_bus *bus) {
int r;
assert(c);
assert(bus);
assert(!c->bus);
assert(!c->match_properties_changed);
assert(!c->match_disconnected);
c->bus = sd_bus_ref(bus);
r = sd_bus_match_signal_async(
c->bus,
&c->match_properties_changed,
"org.freedesktop.systemd1",
c->bus_path,
"org.freedesktop.DBus.Properties",
"PropertiesChanged",
on_properties_changed, NULL, c);
if (r < 0)
return log_error_errno(r, "Failed to request PropertiesChanged signal match: %m");
r = sd_bus_match_signal_async(
bus,
&c->match_disconnected,
"org.freedesktop.DBus.Local",
/* path= */ NULL,
"org.freedesktop.DBus.Local",
"Disconnected",
on_disconnected, NULL, c);
if (r < 0)
return log_error_errno(r, "Failed to request Disconnected signal match: %m");
r = sd_bus_attach_event(c->bus, c->event, SD_EVENT_PRIORITY_NORMAL);
if (r < 0)
return log_error_errno(r, "Failed to attach bus to event loop: %m");
return 0;
}
static void run_context_detach_bus(RunContext *c) {
assert(c);
if (c->bus) {
(void) sd_bus_detach_event(c->bus);
c->bus = sd_bus_unref(c->bus);
}
c->match_properties_changed = sd_bus_slot_unref(c->match_properties_changed);
c->match_disconnected = sd_bus_slot_unref(c->match_disconnected);
}
static int pty_forward_handler(PTYForward *f, int rcode, void *userdata) {
@ -1541,7 +1723,7 @@ static int pty_forward_handler(PTYForward *f, int rcode, void *userdata) {
/* If --wait is specified, we'll only exit the pty forwarding, but will continue to wait
* for the service to end. If the user hits ^C we'll exit too. */
} else if (rcode < 0) {
sd_event_exit(c->event, EXIT_FAILURE);
(void) sd_event_exit(c->event, EXIT_FAILURE);
return log_error_errno(rcode, "Error on PTY forwarding logic: %m");
}
@ -1818,7 +2000,7 @@ static int start_transient_service(sd_bus *bus) {
if (r < 0)
return log_error_errno(r, "Failed to mangle unit name: %m");
} else {
r = make_unit_name(bus, UNIT_SERVICE, &service);
r = make_unit_name(UNIT_SERVICE, &service);
if (r < 0)
return r;
}
@ -1860,7 +2042,7 @@ static int start_transient_service(sd_bus *bus) {
}
if (arg_wait || arg_stdio != ARG_STDIO_NONE) {
_cleanup_(run_context_free) RunContext c = {
_cleanup_(run_context_done) RunContext c = {
.cpu_usage_nsec = NSEC_INFINITY,
.memory_peak = UINT64_MAX,
.memory_swap_peak = UINT64_MAX,
@ -1871,14 +2053,19 @@ static int start_transient_service(sd_bus *bus) {
.inactive_exit_usec = USEC_INFINITY,
.inactive_enter_usec = USEC_INFINITY,
};
_cleanup_free_ char *path = NULL;
c.bus = sd_bus_ref(bus);
r = sd_event_default(&c.event);
if (r < 0)
return log_error_errno(r, "Failed to get event loop: %m");
c.service = strdup(service);
if (!c.service)
return log_oom();
c.bus_path = unit_dbus_path_from_name(service);
if (!c.bus_path)
return log_oom();
if (master >= 0) {
(void) sd_event_set_signal_exit(c.event, true);
@ -1900,26 +2087,11 @@ static int start_transient_service(sd_bus *bus) {
set_window_title(c.forward);
}
path = unit_dbus_path_from_name(service);
if (!path)
return log_oom();
r = sd_bus_match_signal_async(
bus,
&c.match,
"org.freedesktop.systemd1",
path,
"org.freedesktop.DBus.Properties",
"PropertiesChanged",
on_properties_changed, NULL, &c);
r = run_context_attach_bus(&c, bus);
if (r < 0)
return log_error_errno(r, "Failed to request properties changed signal match: %m");
return r;
r = sd_bus_attach_event(bus, c.event, SD_EVENT_PRIORITY_NORMAL);
if (r < 0)
return log_error_errno(r, "Failed to attach bus to event loop: %m");
r = run_context_update(&c, path);
r = run_context_update(&c);
if (r < 0)
return r;
@ -2033,7 +2205,7 @@ static int start_transient_scope(sd_bus *bus) {
if (r < 0)
return log_error_errno(r, "Failed to mangle scope name: %m");
} else {
r = make_unit_name(bus, UNIT_SCOPE, &scope);
r = make_unit_name(UNIT_SCOPE, &scope);
if (r < 0)
return r;
}
@ -2332,7 +2504,7 @@ static int start_transient_trigger(sd_bus *bus, const char *suffix) {
break;
}
} else {
r = make_unit_name(bus, UNIT_SERVICE, &service);
r = make_unit_name(UNIT_SERVICE, &service);
if (r < 0)
return r;
@ -2411,16 +2583,30 @@ static int run(int argc, char* argv[]) {
}
if (!arg_description) {
char *t;
_cleanup_free_ char *t = NULL;
if (strv_isempty(arg_cmdline))
t = strdup(arg_unit);
else
else if (startswith(arg_cmdline[0], "-")) {
/* Drop the login shell marker from the command line when generating the description,
* in order to minimize user confusion. */
_cleanup_strv_free_ char **l = strv_copy(arg_cmdline);
if (!l)
return log_oom();
r = free_and_strdup_warn(l + 0, l[0] + 1);
if (r < 0)
return r;
t = quote_command_line(l, SHELL_ESCAPE_EMPTY);
} else
t = quote_command_line(arg_cmdline, SHELL_ESCAPE_EMPTY);
if (!t)
return log_oom();
free_and_replace(arg_description, t);
arg_description = strjoin("[", program_invocation_short_name, "] ", t);
if (!arg_description)
return log_oom();
}
/* For backward compatibility reasons env var expansion is disabled by default for scopes, and
@ -2435,18 +2621,9 @@ static int run(int argc, char* argv[]) {
" Use --expand-environment=yes/no to explicitly control it as needed.");
}
/* If --wait is used connect via the bus, unconditionally, as ref/unref is not supported via the
* limited direct connection */
if (arg_wait ||
arg_stdio != ARG_STDIO_NONE ||
(arg_runtime_scope == RUNTIME_SCOPE_USER && !IN_SET(arg_transport, BUS_TRANSPORT_LOCAL, BUS_TRANSPORT_CAPSULE)))
r = bus_connect_transport(arg_transport, arg_host, arg_runtime_scope, &bus);
else
r = bus_connect_transport_systemd(arg_transport, arg_host, arg_runtime_scope, &bus);
r = connect_bus(&bus);
if (r < 0)
return bus_log_connect_error(r, arg_transport, arg_runtime_scope);
(void) sd_bus_set_allow_interactive_authorization(bus, arg_ask_password);
return r;
if (arg_scope)
return start_transient_scope(bus);

View File

@ -82,7 +82,7 @@ TEST(keymaps) {
#define dump_glyph(x) log_info(STRINGIFY(x) ": %s", special_glyph(x))
TEST(dump_special_glyphs) {
assert_cc(SPECIAL_GLYPH_GREEN_CIRCLE + 1 == _SPECIAL_GLYPH_MAX);
assert_cc(SPECIAL_GLYPH_SUPERHERO + 1 == _SPECIAL_GLYPH_MAX);
log_info("is_locale_utf8: %s", yes_no(is_locale_utf8()));
@ -133,6 +133,7 @@ TEST(dump_special_glyphs) {
dump_glyph(SPECIAL_GLYPH_YELLOW_CIRCLE);
dump_glyph(SPECIAL_GLYPH_BLUE_CIRCLE);
dump_glyph(SPECIAL_GLYPH_GREEN_CIRCLE);
dump_glyph(SPECIAL_GLYPH_SUPERHERO);
}
DEFINE_TEST_MAIN(LOG_INFO);

View File

@ -453,7 +453,6 @@ static int add_veritytab_devices(void) {
for (;;) {
_cleanup_free_ char *line = NULL, *name = NULL, *data_device = NULL, *hash_device = NULL,
*roothash = NULL, *options = NULL;
char *data_uuid, *hash_uuid;
r = read_stripped_line(f, LONG_LINE_MAX, &line);
if (r < 0)
@ -472,14 +471,6 @@ static int add_veritytab_devices(void) {
continue;
}
data_uuid = startswith(data_device, "UUID=");
if (!data_uuid)
data_uuid = path_startswith(data_device, "/dev/disk/by-uuid/");
hash_uuid = startswith(hash_device, "UUID=");
if (!hash_uuid)
hash_uuid = path_startswith(hash_device, "/dev/disk/by-uuid/");
r = create_veritytab_device(
name,
data_device,

View File

@ -238,10 +238,13 @@ if [[ -e /usr/lib/pam.d/systemd-run0 ]] || [[ -e /etc/pam.d/systemd-run0 ]]; the
run0 ls /
assert_eq "$(run0 echo foo)" "foo"
# Check if we set some expected environment variables
for arg in "" "--user=root" "--user=testuser"; do
for arg in "" "--user=root" "--user=0" "--user=testuser"; do
assert_eq "$(run0 ${arg:+"$arg"} bash -c 'echo $SUDO_USER')" "$USER"
assert_eq "$(run0 ${arg:+"$arg"} bash -c 'echo $SUDO_UID')" "$(id -u "$USER")"
assert_eq "$(run0 ${arg:+"$arg"} bash -c 'echo $SUDO_GID')" "$(id -u "$USER")"
# Validate that we actually went properly through PAM (XDG_SESSION_TYPE is set by pam_systemd)
assert_eq "$(run0 ${arg:+"$arg"} bash -c 'echo $XDG_SESSION_TYPE')" "unspecified"
done
# Let's chain a couple of run0 calls together, for fun
readarray -t cmdline < <(printf "%.0srun0\n" {0..31})
@ -258,4 +261,17 @@ if [[ -e /usr/lib/pam.d/systemd-run0 ]] || [[ -e /etc/pam.d/systemd-run0 ]]; the
assert_eq "$(run0 -D / pwd)" "/"
assert_eq "$(run0 --user=testuser pwd)" "/home/testuser"
assert_eq "$(run0 -D / --user=testuser pwd)" "/"
# Verify that all combinations of --pty/--pipe come to the sam results
assert_eq "$(run0 echo -n foo)" "foo"
assert_eq "$(run0 --pty echo -n foo)" "foo"
assert_eq "$(run0 --pipe echo -n foo)" "foo"
assert_eq "$(run0 --pipe --pty echo -n foo)" "foo"
# Validate when we invoke run0 without a tty, that depending on --pty it either allocates a tty or not
assert_neq "$(run0 --pty tty < /dev/null)" "not a tty"
assert_eq "$(run0 --pipe tty < /dev/null)" "not a tty"
fi
# Tests whether intermediate disconnects corrupt us (modified testcase from https://github.com/systemd/systemd/issues/27204)
assert_rc "37" systemd-run --unit=disconnecttest --wait --pipe --user -M testuser@.host bash -ec 'systemctl --user daemon-reexec; sleep 3; exit 37'

View File

@ -39,6 +39,15 @@ assert_eq() {(
fi
)}
assert_neq() {(
set +ex
if [[ "${1?}" = "${2?}" ]]; then
echo "FAIL: not expected: '$2' actual: '$1'" >&2
exit 1
fi
)}
assert_le() {(
set +ex