mirror of
https://github.com/systemd/systemd.git
synced 2024-10-26 08:55:40 +03:00
Compare commits
25 Commits
c1892f6f9d
...
8a4a18f5a0
Author | SHA1 | Date | |
---|---|---|---|
|
8a4a18f5a0 | ||
|
f7078de515 | ||
|
6d6048b4cb | ||
|
10a48938ef | ||
|
b58b13f1c6 | ||
|
c8f59296bf | ||
|
d585085f57 | ||
|
ff4b6a1915 | ||
|
d9f68f48f7 | ||
|
0310b2a60b | ||
|
115fac3c29 | ||
|
9d8f5e22f8 | ||
|
6fb0c52295 | ||
|
edd10ab29c | ||
|
988053eac3 | ||
|
0eacbc1240 | ||
|
2405c6c278 | ||
|
0ceda51f91 | ||
|
f9764f05e4 | ||
|
0a2ec4a0b3 | ||
|
04c6160684 | ||
|
f503b73855 | ||
|
c292ae6eca | ||
|
5d79dde7cf | ||
|
56078802c1 |
3
TODO
3
TODO
@ -162,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().
|
||||
|
52
man/run0.xml
52
man/run0.xml
@ -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>
|
||||
|
@ -144,6 +144,20 @@
|
||||
<xi:include href="version-info.xml" xpointer="v249"/></listitem>
|
||||
</varlistentry>
|
||||
|
||||
<varlistentry>
|
||||
<term><option>--dns</option></term>
|
||||
|
||||
<listitem><para>Waiting for a DNS server of each network interface to be accessible. If this
|
||||
option is specified with <option>--any</option>, then
|
||||
<command>systemd-networkd-wait-online</command> exits with success when at least one interface
|
||||
becomes online and has an accessible DNS server.</para>
|
||||
<para>If there are search domains configured on a link, or if a link has <varname>DNSDefaultRoute=yes</varname>,
|
||||
then there must be an accessible link-specific DNS server configured. Otherwise, global DNS servers
|
||||
may be considered.</para>
|
||||
|
||||
<xi:include href="version-info.xml" xpointer="v257"/></listitem>
|
||||
</varlistentry>
|
||||
|
||||
<varlistentry>
|
||||
<term><option>--any</option></term>
|
||||
|
||||
|
@ -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"🦸",
|
||||
},
|
||||
};
|
||||
|
||||
|
@ -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;
|
||||
|
@ -124,6 +124,22 @@ int json_dispatch_in_addr(const char *name, sd_json_variant *variant, sd_json_di
|
||||
return 0;
|
||||
}
|
||||
|
||||
int json_dispatch_in_addr_union(const char *name, sd_json_variant *variant, sd_json_dispatch_flags_t flags, void *userdata) {
|
||||
union in_addr_union *address = ASSERT_PTR(userdata);
|
||||
_cleanup_(iovec_done) struct iovec iov = {};
|
||||
int r;
|
||||
|
||||
r = json_dispatch_byte_array_iovec(name, variant, flags, &iov);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
if (!IN_SET(iov.iov_len, sizeof(struct in_addr), sizeof(struct in6_addr)))
|
||||
return json_log(variant, flags, SYNTHETIC_ERRNO(EINVAL), "JSON field '%s' is array of unexpected size.", strna(name));
|
||||
|
||||
memcpy(address, iov.iov_base, iov.iov_len);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int json_dispatch_path(const char *name, sd_json_variant *variant, sd_json_dispatch_flags_t flags, void *userdata) {
|
||||
char **p = ASSERT_PTR(userdata);
|
||||
const char *path;
|
||||
|
@ -112,6 +112,7 @@ int json_dispatch_byte_array_iovec(const char *name, sd_json_variant *variant, s
|
||||
int json_dispatch_user_group_name(const char *name, sd_json_variant *variant, sd_json_dispatch_flags_t flags, void *userdata);
|
||||
int json_dispatch_const_user_group_name(const char *name, sd_json_variant *variant, sd_json_dispatch_flags_t flags, void *userdata);
|
||||
int json_dispatch_in_addr(const char *name, sd_json_variant *variant, sd_json_dispatch_flags_t flags, void *userdata);
|
||||
int json_dispatch_in_addr_union(const char *name, sd_json_variant *variant, sd_json_dispatch_flags_t flags, void *userdata);
|
||||
int json_dispatch_path(const char *name, sd_json_variant *variant, sd_json_dispatch_flags_t flags, void *userdata);
|
||||
int json_dispatch_pidref(const char *name, sd_json_variant *variant, sd_json_dispatch_flags_t flags, void *userdata);
|
||||
int json_dispatch_devnum(const char *name, sd_json_variant *variant, sd_json_dispatch_flags_t flags, void *userdata);
|
||||
|
@ -86,6 +86,19 @@ int varlink_callb_and_log(
|
||||
return varlink_call_and_log(v, method, parameters, ret_parameters);
|
||||
}
|
||||
|
||||
int varlink_many_notify(Set *s, sd_json_variant *parameters) {
|
||||
sd_varlink *link;
|
||||
int r = 1;
|
||||
|
||||
if (set_isempty(s))
|
||||
return 0;
|
||||
|
||||
SET_FOREACH(link, s)
|
||||
RET_GATHER(r, sd_varlink_notify(link, parameters));
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
int varlink_many_notifyb(Set *s, ...) {
|
||||
int r;
|
||||
|
||||
@ -102,12 +115,7 @@ int varlink_many_notifyb(Set *s, ...) {
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
r = 1;
|
||||
sd_varlink *link;
|
||||
SET_FOREACH(link, s)
|
||||
RET_GATHER(r, sd_varlink_notify(link, parameters));
|
||||
|
||||
return r;
|
||||
return varlink_many_notify(s, parameters);
|
||||
}
|
||||
|
||||
int varlink_many_reply(Set *s, sd_json_variant *parameters) {
|
||||
|
@ -13,6 +13,7 @@ int varlink_callb_and_log(sd_varlink *v, const char *method, sd_json_variant **r
|
||||
#define varlink_callbo_and_log(v, method, ret_parameters, ...) \
|
||||
varlink_callb_and_log((v), (method), (ret_parameters), SD_JSON_BUILD_OBJECT(__VA_ARGS__))
|
||||
|
||||
int varlink_many_notify(Set *s, sd_json_variant *parameters);
|
||||
int varlink_many_notifyb(Set *s, ...);
|
||||
#define varlink_many_notifybo(s, ...) \
|
||||
varlink_many_notifyb((s), SD_JSON_BUILD_OBJECT(__VA_ARGS__))
|
||||
|
@ -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();
|
||||
|
@ -113,6 +113,7 @@ sources = files(
|
||||
systemd_networkd_sources = files('networkd.c')
|
||||
|
||||
systemd_networkd_wait_online_sources = files(
|
||||
'wait-online/dns-configuration.c',
|
||||
'wait-online/link.c',
|
||||
'wait-online/manager.c',
|
||||
'wait-online/wait-online.c',
|
||||
|
131
src/network/wait-online/dns-configuration.c
Normal file
131
src/network/wait-online/dns-configuration.c
Normal file
@ -0,0 +1,131 @@
|
||||
/* SPDX-License-Identifier: LGPL-2.1-or-later */
|
||||
|
||||
#include "sd-json.h"
|
||||
|
||||
#include "alloc-util.h"
|
||||
#include "dns-configuration.h"
|
||||
#include "json-util.h"
|
||||
#include "set.h"
|
||||
#include "strv.h"
|
||||
|
||||
DNSServer *dns_server_free(DNSServer *s) {
|
||||
if (!s)
|
||||
return NULL;
|
||||
|
||||
free(s->server_name);
|
||||
|
||||
return mfree(s);
|
||||
}
|
||||
|
||||
static int dispatch_dns_server(const char *name, sd_json_variant *variant, sd_json_dispatch_flags_t flags, void *userdata) {
|
||||
static const sd_json_dispatch_field dns_server_dispatch_table[] = {
|
||||
{ "address", SD_JSON_VARIANT_ARRAY, json_dispatch_in_addr_union, offsetof(DNSServer, addr), SD_JSON_MANDATORY },
|
||||
{ "addressFamily", SD_JSON_VARIANT_UNSIGNED, sd_json_dispatch_uint, offsetof(DNSServer, family), SD_JSON_MANDATORY },
|
||||
{ "port", SD_JSON_VARIANT_UNSIGNED, sd_json_dispatch_uint16, offsetof(DNSServer, port), 0 },
|
||||
{ "interfaceSpecifier", SD_JSON_VARIANT_UNSIGNED, sd_json_dispatch_uint, offsetof(DNSServer, ifindex), 0 },
|
||||
{ "serverName", SD_JSON_VARIANT_STRING, sd_json_dispatch_string, offsetof(DNSServer, server_name), 0 },
|
||||
{ "accessible", SD_JSON_VARIANT_BOOLEAN, sd_json_dispatch_stdbool, offsetof(DNSServer, accessible), SD_JSON_MANDATORY },
|
||||
{},
|
||||
};
|
||||
DNSServer **ret = ASSERT_PTR(userdata);
|
||||
_cleanup_(dns_server_freep) DNSServer *s = NULL;
|
||||
int r;
|
||||
|
||||
s = new0(DNSServer, 1);
|
||||
if (!s)
|
||||
return log_oom();
|
||||
|
||||
r = sd_json_dispatch(variant, dns_server_dispatch_table, flags, s);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
*ret = TAKE_PTR(s);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int dispatch_dns_server_array(const char *name, sd_json_variant *variant, sd_json_dispatch_flags_t flags, void *userdata) {
|
||||
Set **ret = ASSERT_PTR(userdata);
|
||||
Set *dns_servers = set_new(NULL);
|
||||
sd_json_variant *v = NULL;
|
||||
int r;
|
||||
|
||||
JSON_VARIANT_ARRAY_FOREACH(v, variant) {
|
||||
_cleanup_(dns_server_freep) DNSServer *s = NULL;
|
||||
|
||||
s = new0(DNSServer, 1);
|
||||
if (!s)
|
||||
return log_oom();
|
||||
|
||||
r = dispatch_dns_server(name, v, flags, &s);
|
||||
if (r < 0)
|
||||
return json_log(v, flags, r, "JSON array element is not a valid DNSServer.");
|
||||
|
||||
r = set_put(dns_servers, TAKE_PTR(s));
|
||||
if (r < 0)
|
||||
return log_oom();
|
||||
}
|
||||
|
||||
set_free_and_replace(*ret, dns_servers);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
DNSConfiguration *dns_configuration_free(DNSConfiguration *c) {
|
||||
if (!c)
|
||||
return NULL;
|
||||
|
||||
dns_server_free(c->current_dns_server);
|
||||
set_free_with_destructor(c->dns_servers, dns_server_free);
|
||||
free(c->ifname);
|
||||
strv_free(c->search_domains);
|
||||
|
||||
return mfree(c);
|
||||
}
|
||||
|
||||
static int dispatch_dns_configuration(const char *name, sd_json_variant *variant, sd_json_dispatch_flags_t flags, void *userdata) {
|
||||
static const sd_json_dispatch_field dns_configuration_dispatch_table[] = {
|
||||
{ "interface", SD_JSON_VARIANT_STRING, sd_json_dispatch_string, offsetof(DNSConfiguration, ifname), 0 },
|
||||
{ "interfaceIndex", SD_JSON_VARIANT_UNSIGNED, sd_json_dispatch_uint, offsetof(DNSConfiguration, ifindex), 0 },
|
||||
{ "currentDNSServer", SD_JSON_VARIANT_OBJECT, dispatch_dns_server, offsetof(DNSConfiguration, current_dns_server), 0 },
|
||||
{ "dnsServers", SD_JSON_VARIANT_ARRAY, dispatch_dns_server_array, offsetof(DNSConfiguration, dns_servers), 0 },
|
||||
{ "searchDomains", SD_JSON_VARIANT_ARRAY, sd_json_dispatch_strv, offsetof(DNSConfiguration, search_domains), 0 },
|
||||
{},
|
||||
|
||||
};
|
||||
DNSConfiguration **ret = ASSERT_PTR(userdata);
|
||||
_cleanup_(dns_configuration_freep) DNSConfiguration *c = NULL;
|
||||
int r;
|
||||
|
||||
c = new0(DNSConfiguration, 1);
|
||||
if (!c)
|
||||
return log_oom();
|
||||
|
||||
r = sd_json_dispatch(variant, dns_configuration_dispatch_table, flags, c);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
*ret = TAKE_PTR(c);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int dns_configuration_from_json(sd_json_variant *variant, DNSConfiguration **ret) {
|
||||
return dispatch_dns_configuration(NULL, variant, SD_JSON_LOG, ret);
|
||||
}
|
||||
|
||||
bool dns_is_accessible(DNSConfiguration *c) {
|
||||
DNSServer *s = NULL;
|
||||
|
||||
if (!c)
|
||||
return false;
|
||||
|
||||
if (c->current_dns_server && c->current_dns_server->accessible)
|
||||
return true;
|
||||
|
||||
SET_FOREACH(s, c->dns_servers)
|
||||
if (s->accessible)
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
37
src/network/wait-online/dns-configuration.h
Normal file
37
src/network/wait-online/dns-configuration.h
Normal file
@ -0,0 +1,37 @@
|
||||
/* SPDX-License-Identifier: LGPL-2.1-or-later */
|
||||
#pragma once
|
||||
|
||||
#include "sd-json.h"
|
||||
|
||||
#include "in-addr-util.h"
|
||||
#include "macro-fundamental.h"
|
||||
#include "set.h"
|
||||
|
||||
typedef struct DNSServer DNSServer;
|
||||
typedef struct DNSConfiguration DNSConfiguration;
|
||||
|
||||
struct DNSServer {
|
||||
union in_addr_union addr;
|
||||
int family;
|
||||
uint16_t port;
|
||||
int ifindex;
|
||||
char *server_name;
|
||||
bool accessible;
|
||||
};
|
||||
|
||||
DNSServer *dns_server_free(DNSServer *s);
|
||||
DEFINE_TRIVIAL_CLEANUP_FUNC(DNSServer*, dns_server_free);
|
||||
|
||||
struct DNSConfiguration {
|
||||
char *ifname;
|
||||
int ifindex;
|
||||
DNSServer *current_dns_server;
|
||||
Set *dns_servers;
|
||||
char **search_domains;
|
||||
};
|
||||
|
||||
int dns_configuration_from_json(sd_json_variant *variant, DNSConfiguration **ret);
|
||||
bool dns_is_accessible(DNSConfiguration *c);
|
||||
|
||||
DNSConfiguration *dns_configuration_free(DNSConfiguration *c);
|
||||
DEFINE_TRIVIAL_CLEANUP_FUNC(DNSConfiguration*, dns_configuration_free);
|
@ -3,6 +3,7 @@
|
||||
#include "sd-network.h"
|
||||
|
||||
#include "alloc-util.h"
|
||||
#include "dns-configuration.h"
|
||||
#include "format-ifname.h"
|
||||
#include "hashmap.h"
|
||||
#include "link.h"
|
||||
@ -62,6 +63,8 @@ Link *link_free(Link *l) {
|
||||
hashmap_remove(l->manager->links_by_name, *n);
|
||||
}
|
||||
|
||||
dns_configuration_free(l->dns_configuration);
|
||||
|
||||
free(l->state);
|
||||
free(l->ifname);
|
||||
strv_free(l->altnames);
|
||||
@ -246,5 +249,11 @@ int link_update_monitor(Link *l) {
|
||||
else
|
||||
free_and_replace(l->state, state);
|
||||
|
||||
r = sd_network_link_get_dns_default_route(l->ifindex);
|
||||
if (r < 0)
|
||||
ret = log_link_debug_errno(l, r, "Failed to get DNS default route, ignoring: %m");
|
||||
else
|
||||
l->dns_default_route = r > 0;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
@ -3,6 +3,7 @@
|
||||
|
||||
#include "sd-netlink.h"
|
||||
|
||||
#include "dns-configuration.h"
|
||||
#include "log-link.h"
|
||||
#include "network-util.h"
|
||||
|
||||
@ -24,6 +25,8 @@ struct Link {
|
||||
LinkAddressState ipv4_address_state;
|
||||
LinkAddressState ipv6_address_state;
|
||||
char *state;
|
||||
bool dns_default_route;
|
||||
DNSConfiguration *dns_configuration;
|
||||
};
|
||||
|
||||
int link_new(Manager *m, Link **ret, int ifindex, const char *ifname);
|
||||
|
@ -4,7 +4,13 @@
|
||||
#include <linux/if.h>
|
||||
#include <fnmatch.h>
|
||||
|
||||
#include "sd-event.h"
|
||||
#include "sd-json.h"
|
||||
#include "sd-varlink.h"
|
||||
|
||||
#include "alloc-util.h"
|
||||
#include "dns-configuration.h"
|
||||
#include "json-util.h"
|
||||
#include "link.h"
|
||||
#include "manager.h"
|
||||
#include "netlink-util.h"
|
||||
@ -133,6 +139,28 @@ static int manager_link_is_online(Manager *m, Link *l, const LinkOperationalStat
|
||||
"No routable IPv6 address is configured.");
|
||||
}
|
||||
|
||||
if (m->requires_dns) {
|
||||
bool require_link_dns = false;
|
||||
|
||||
if (l->dns_configuration)
|
||||
require_link_dns = !strv_isempty(l->dns_configuration->search_domains);
|
||||
|
||||
require_link_dns = require_link_dns || l->dns_default_route;
|
||||
|
||||
if (!dns_is_accessible(l->dns_configuration)) {
|
||||
/* When the link is configured with DNSDefaultRoute=yes, or there are search domains
|
||||
* configured for the link, we require that link-specific DNS servers are configured.
|
||||
* Otherwise, we are free to fallback to global DNS. */
|
||||
if (require_link_dns)
|
||||
return log_link_debug_errno(l, SYNTHETIC_ERRNO(EADDRNOTAVAIL),
|
||||
"No link-specific DNS server is accessible.");
|
||||
|
||||
if (!dns_is_accessible(m->dns_configuration))
|
||||
return log_link_debug_errno(l, SYNTHETIC_ERRNO(EADDRNOTAVAIL),
|
||||
"No DNS server is accessible.");
|
||||
}
|
||||
}
|
||||
|
||||
log_link_debug(l, "link is configured by networkd and online.");
|
||||
return true;
|
||||
}
|
||||
@ -381,13 +409,102 @@ static int manager_network_monitor_listen(Manager *m) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int on_dns_configuration_event(
|
||||
sd_varlink *link,
|
||||
sd_json_variant *parameters,
|
||||
const char *error_id,
|
||||
sd_varlink_reply_flags_t flags,
|
||||
void *userdata) {
|
||||
|
||||
Manager *m = ASSERT_PTR(userdata);
|
||||
sd_json_variant *configurations = NULL, *v = NULL;
|
||||
int r;
|
||||
|
||||
assert(link);
|
||||
|
||||
if (error_id) {
|
||||
log_warning("DNS configuration event error, ignoring: %s", error_id);
|
||||
return 0;
|
||||
}
|
||||
|
||||
configurations = sd_json_variant_by_key(parameters, "configuration");
|
||||
if (!configurations || !sd_json_variant_is_array(configurations)) {
|
||||
log_warning("DNS configuration JSON data does not have configuration key, ignoring.");
|
||||
return 0;
|
||||
}
|
||||
|
||||
JSON_VARIANT_ARRAY_FOREACH(v, configurations) {
|
||||
_cleanup_(dns_configuration_freep) DNSConfiguration *c = NULL;
|
||||
|
||||
r = dns_configuration_from_json(v, &c);
|
||||
if (r < 0) {
|
||||
log_warning_errno(r, "Failed to get DNS configuration JSON, ignoring: %m");
|
||||
continue;
|
||||
}
|
||||
|
||||
if (c->ifindex > 0) {
|
||||
Link *l = hashmap_get(m->links_by_index, INT_TO_PTR(c->ifindex));
|
||||
if (l)
|
||||
free_and_replace_full(l->dns_configuration, c, dns_configuration_free);
|
||||
} else
|
||||
/* Global DNS configuration */
|
||||
free_and_replace_full(m->dns_configuration, c, dns_configuration_free);
|
||||
}
|
||||
|
||||
if (manager_configured(m))
|
||||
sd_event_exit(m->event, 0);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int manager_dns_configuration_listen(Manager *m) {
|
||||
_cleanup_(sd_varlink_unrefp) sd_varlink *vl = NULL;
|
||||
int r;
|
||||
|
||||
assert(m);
|
||||
assert(m->event);
|
||||
|
||||
if (!m->requires_dns)
|
||||
return 0;
|
||||
|
||||
r = sd_varlink_connect_address(&vl, "/run/systemd/resolve/io.systemd.Resolve.Monitor");
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to connect to io.systemd.Resolve.Monitor: %m");
|
||||
|
||||
r = sd_varlink_set_relative_timeout(vl, USEC_INFINITY);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to set varlink timeout: %m");
|
||||
|
||||
r = sd_varlink_attach_event(vl, m->event, SD_EVENT_PRIORITY_NORMAL);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to attach varlink connection to event loop: %m");
|
||||
|
||||
(void) sd_varlink_set_userdata(vl, m);
|
||||
|
||||
r = sd_varlink_bind_reply(vl, on_dns_configuration_event);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to bind varlink reply callback: %m");
|
||||
|
||||
r = sd_varlink_observebo(
|
||||
vl,
|
||||
"io.systemd.Resolve.Monitor.SubscribeDNSConfiguration",
|
||||
SD_JSON_BUILD_PAIR_BOOLEAN("allowInteractiveAuthentication", false));
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to issue SubscribeDNSConfiguration: %m");
|
||||
|
||||
m->varlink_client = TAKE_PTR(vl);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int manager_new(Manager **ret,
|
||||
Hashmap *command_line_interfaces_by_name,
|
||||
char **ignored_interfaces,
|
||||
LinkOperationalStateRange required_operstate,
|
||||
AddressFamily required_family,
|
||||
bool any,
|
||||
usec_t timeout) {
|
||||
usec_t timeout,
|
||||
bool requires_dns) {
|
||||
|
||||
_cleanup_(manager_freep) Manager *m = NULL;
|
||||
int r;
|
||||
@ -404,6 +521,7 @@ int manager_new(Manager **ret,
|
||||
.required_operstate = required_operstate,
|
||||
.required_family = required_family,
|
||||
.any = any,
|
||||
.requires_dns = requires_dns,
|
||||
};
|
||||
|
||||
r = sd_event_default(&m->event);
|
||||
@ -428,6 +546,10 @@ int manager_new(Manager **ret,
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
r = manager_dns_configuration_listen(m);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
*ret = TAKE_PTR(m);
|
||||
|
||||
return 0;
|
||||
@ -445,6 +567,9 @@ Manager* manager_free(Manager *m) {
|
||||
sd_event_source_unref(m->rtnl_event_source);
|
||||
sd_netlink_unref(m->rtnl);
|
||||
sd_event_unref(m->event);
|
||||
sd_varlink_unref(m->varlink_client);
|
||||
|
||||
dns_configuration_free(m->dns_configuration);
|
||||
|
||||
return mfree(m);
|
||||
}
|
||||
|
@ -4,7 +4,9 @@
|
||||
#include "sd-event.h"
|
||||
#include "sd-netlink.h"
|
||||
#include "sd-network.h"
|
||||
#include "sd-varlink.h"
|
||||
|
||||
#include "dns-configuration.h"
|
||||
#include "hashmap.h"
|
||||
#include "network-util.h"
|
||||
#include "time-util.h"
|
||||
@ -23,6 +25,7 @@ struct Manager {
|
||||
LinkOperationalStateRange required_operstate;
|
||||
AddressFamily required_family;
|
||||
bool any;
|
||||
bool requires_dns;
|
||||
|
||||
sd_netlink *rtnl;
|
||||
sd_event_source *rtnl_event_source;
|
||||
@ -31,13 +34,16 @@ struct Manager {
|
||||
sd_event_source *network_monitor_event_source;
|
||||
|
||||
sd_event *event;
|
||||
|
||||
sd_varlink *varlink_client;
|
||||
DNSConfiguration *dns_configuration;
|
||||
};
|
||||
|
||||
Manager* manager_free(Manager *m);
|
||||
int manager_new(Manager **ret, Hashmap *command_line_interfaces_by_name, char **ignored_interfaces,
|
||||
LinkOperationalStateRange required_operstate,
|
||||
AddressFamily required_family,
|
||||
bool any, usec_t timeout);
|
||||
bool any, usec_t timeout, bool requires_dns);
|
||||
|
||||
DEFINE_TRIVIAL_CLEANUP_FUNC(Manager*, manager_free);
|
||||
|
||||
|
@ -22,6 +22,7 @@ static char **arg_ignore = NULL;
|
||||
static LinkOperationalStateRange arg_required_operstate = LINK_OPERSTATE_RANGE_INVALID;
|
||||
static AddressFamily arg_required_family = ADDRESS_FAMILY_NO;
|
||||
static bool arg_any = false;
|
||||
static bool arg_requires_dns = false;
|
||||
|
||||
STATIC_DESTRUCTOR_REGISTER(arg_interfaces, hashmap_free_free_freep);
|
||||
STATIC_DESTRUCTOR_REGISTER(arg_ignore, strv_freep);
|
||||
@ -48,6 +49,7 @@ static int help(void) {
|
||||
" -6 --ipv6 Requires at least one IPv6 address\n"
|
||||
" --any Wait until at least one of the interfaces is online\n"
|
||||
" --timeout=SECS Maximum time to wait for network connectivity\n"
|
||||
" --dns Requires at least one DNS server to be accessible\n"
|
||||
"\nSee the %s for details.\n",
|
||||
program_invocation_short_name,
|
||||
link);
|
||||
@ -106,6 +108,7 @@ static int parse_argv(int argc, char *argv[]) {
|
||||
ARG_IGNORE,
|
||||
ARG_ANY,
|
||||
ARG_TIMEOUT,
|
||||
ARG_DNS,
|
||||
};
|
||||
|
||||
static const struct option options[] = {
|
||||
@ -119,6 +122,7 @@ static int parse_argv(int argc, char *argv[]) {
|
||||
{ "ipv6", no_argument, NULL, '6' },
|
||||
{ "any", no_argument, NULL, ARG_ANY },
|
||||
{ "timeout", required_argument, NULL, ARG_TIMEOUT },
|
||||
{ "dns", no_argument, NULL, ARG_DNS },
|
||||
{}
|
||||
};
|
||||
|
||||
@ -178,6 +182,10 @@ static int parse_argv(int argc, char *argv[]) {
|
||||
return r;
|
||||
break;
|
||||
|
||||
case ARG_DNS:
|
||||
arg_requires_dns = true;
|
||||
break;
|
||||
|
||||
case '?':
|
||||
return -EINVAL;
|
||||
|
||||
@ -204,7 +212,14 @@ static int run(int argc, char *argv[]) {
|
||||
if (arg_quiet)
|
||||
log_set_max_level(LOG_ERR);
|
||||
|
||||
r = manager_new(&m, arg_interfaces, arg_ignore, arg_required_operstate, arg_required_family, arg_any, arg_timeout);
|
||||
r = manager_new(&m,
|
||||
arg_interfaces,
|
||||
arg_ignore,
|
||||
arg_required_operstate,
|
||||
arg_required_family,
|
||||
arg_any,
|
||||
arg_timeout,
|
||||
arg_requires_dns);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Could not create manager: %m");
|
||||
|
||||
|
@ -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 */
|
||||
|
@ -150,6 +150,17 @@
|
||||
<annotate key="org.freedesktop.policykit.owner">unix-user:systemd-resolve</annotate>
|
||||
</action>
|
||||
|
||||
<action id="org.freedesktop.resolve1.subscribe-dns-configuration">
|
||||
<description gettext-domain="systemd">Subscribe to DNS configuration</description>
|
||||
<message gettext-domain="systemd">Authentication is required to subscribe to DNS configuration.</message>
|
||||
<defaults>
|
||||
<allow_any>auth_admin</allow_any>
|
||||
<allow_inactive>auth_admin</allow_inactive>
|
||||
<allow_active>auth_admin_keep</allow_active>
|
||||
</defaults>
|
||||
<annotate key="org.freedesktop.policykit.owner">unix-user:systemd-resolve</annotate>
|
||||
</action>
|
||||
|
||||
<action id="org.freedesktop.resolve1.dump-cache">
|
||||
<description gettext-domain="systemd">Dump cache</description>
|
||||
<message gettext-domain="systemd">Authentication is required to dump cache.</message>
|
||||
|
@ -1,14 +1,20 @@
|
||||
/* SPDX-License-Identifier: LGPL-2.1-or-later */
|
||||
|
||||
#include <net/if_arp.h>
|
||||
|
||||
#include "sd-messages.h"
|
||||
|
||||
#include "alloc-util.h"
|
||||
#include "errno-util.h"
|
||||
#include "fd-util.h"
|
||||
#include "json-util.h"
|
||||
#include "resolved-bus.h"
|
||||
#include "resolved-dns-server.h"
|
||||
#include "resolved-dns-stub.h"
|
||||
#include "resolved-manager.h"
|
||||
#include "resolved-resolv-conf.h"
|
||||
#include "siphash24.h"
|
||||
#include "socket-util.h"
|
||||
#include "string-table.h"
|
||||
#include "string-util.h"
|
||||
|
||||
@ -69,6 +75,7 @@ int dns_server_new(
|
||||
.ifindex = ifindex,
|
||||
.server_name = TAKE_PTR(name),
|
||||
.config_source = config_source,
|
||||
.accessible = -1,
|
||||
};
|
||||
|
||||
dns_server_reset_features(s);
|
||||
@ -875,6 +882,7 @@ DnsServer *manager_set_dns_server(Manager *m, DnsServer *s) {
|
||||
dns_cache_flush(&m->unicast_scope->cache);
|
||||
|
||||
(void) manager_send_changed(m, "CurrentDNSServer");
|
||||
(void) manager_send_dns_configuration_changed(m);
|
||||
|
||||
return s;
|
||||
}
|
||||
@ -1128,3 +1136,58 @@ int dns_server_dump_state_to_json(DnsServer *server, sd_json_variant **ret) {
|
||||
SD_JSON_BUILD_PAIR_BOOLEAN("PacketInvalid", server->packet_invalid),
|
||||
SD_JSON_BUILD_PAIR_BOOLEAN("PacketDoOff", server->packet_do_off));
|
||||
}
|
||||
|
||||
int dns_server_is_accessible(DnsServer *server) {
|
||||
_cleanup_close_ int fd = -EBADF;
|
||||
union sockaddr_union sa;
|
||||
int ifindex, r;
|
||||
|
||||
assert(server);
|
||||
|
||||
if (server->accessible >= 0)
|
||||
return server->accessible;
|
||||
|
||||
r = sockaddr_set_in_addr(&sa, server->family, &server->address, dns_server_port(server));
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
fd = socket(server->family, SOCK_DGRAM|SOCK_CLOEXEC, 0);
|
||||
if (fd < 0)
|
||||
return -errno;
|
||||
|
||||
ifindex = dns_server_ifindex(server);
|
||||
if (ifindex > 0) {
|
||||
r = socket_bind_to_ifindex(fd, ifindex);
|
||||
if (r < 0)
|
||||
return r;
|
||||
}
|
||||
|
||||
r = RET_NERRNO(connect(fd, &sa.sa, SOCKADDR_LEN(sa)));
|
||||
|
||||
return (server->accessible = r >= 0);
|
||||
}
|
||||
|
||||
int dns_server_dump_configuration_to_json(DnsServer *server, sd_json_variant **ret) {
|
||||
bool accessible = false;
|
||||
int ifindex, r;
|
||||
|
||||
assert(server);
|
||||
assert(ret);
|
||||
|
||||
ifindex = dns_server_ifindex(server);
|
||||
|
||||
r = dns_server_is_accessible(server);
|
||||
if (r < 0)
|
||||
log_debug_errno(r, "Failed to check if %s is accessible, assume not: %m", dns_server_string_full(server));
|
||||
else
|
||||
accessible = r;
|
||||
|
||||
return sd_json_buildo(
|
||||
ret,
|
||||
JSON_BUILD_PAIR_IN_ADDR("address", &server->address, server->family),
|
||||
SD_JSON_BUILD_PAIR_INTEGER("addressFamily", server->family),
|
||||
SD_JSON_BUILD_PAIR_UNSIGNED("port", dns_server_port(server)),
|
||||
SD_JSON_BUILD_PAIR_CONDITION(ifindex > 0, "interfaceSpecifier", SD_JSON_BUILD_UNSIGNED(ifindex)),
|
||||
JSON_BUILD_PAIR_STRING_NON_EMPTY("serverName", server->server_name),
|
||||
SD_JSON_BUILD_PAIR_BOOLEAN("accessible", accessible));
|
||||
}
|
||||
|
@ -106,6 +106,9 @@ struct DnsServer {
|
||||
|
||||
/* Servers registered via D-Bus are not removed on reload */
|
||||
ResolveConfigSource config_source;
|
||||
|
||||
/* Tri-state to indicate if the DNS server is accessible. */
|
||||
int accessible;
|
||||
};
|
||||
|
||||
int dns_server_new(
|
||||
@ -187,3 +190,6 @@ static inline bool dns_server_is_fallback(DnsServer *s) {
|
||||
}
|
||||
|
||||
int dns_server_dump_state_to_json(DnsServer *server, sd_json_variant **ret);
|
||||
int dns_server_dump_configuration_to_json(DnsServer *server, sd_json_variant **ret);
|
||||
|
||||
int dns_server_is_accessible(DnsServer *server);
|
||||
|
@ -293,6 +293,7 @@ static int bus_link_method_set_dns_servers_internal(sd_bus_message *message, voi
|
||||
(void) link_save_user(l);
|
||||
(void) manager_write_resolv_conf(l->manager);
|
||||
(void) manager_send_changed(l->manager, "DNS");
|
||||
(void) manager_send_dns_configuration_changed(l->manager);
|
||||
|
||||
if (j)
|
||||
log_link_info(l, "Bus client set DNS server list to: %s", j);
|
||||
@ -768,6 +769,7 @@ int bus_link_method_revert(sd_bus_message *message, void *userdata, sd_bus_error
|
||||
(void) link_save_user(l);
|
||||
(void) manager_write_resolv_conf(l->manager);
|
||||
(void) manager_send_changed(l->manager, "DNS");
|
||||
(void) manager_send_dns_configuration_changed(l->manager);
|
||||
|
||||
return sd_bus_reply_method_return(message, NULL);
|
||||
}
|
||||
|
@ -299,6 +299,12 @@ static int link_update_dns_servers(Link *l) {
|
||||
}
|
||||
|
||||
dns_server_unlink_marked(l->dns_servers);
|
||||
|
||||
LIST_FOREACH(servers, s, l->dns_servers)
|
||||
/* If the link state changed, we should re-check if DNS servers
|
||||
* are accessible. */
|
||||
s->accessible = -1;
|
||||
|
||||
return 0;
|
||||
|
||||
clear:
|
||||
|
@ -22,6 +22,7 @@
|
||||
#include "idn-util.h"
|
||||
#include "io-util.h"
|
||||
#include "iovec-util.h"
|
||||
#include "json-util.h"
|
||||
#include "memstream-util.h"
|
||||
#include "missing_network.h"
|
||||
#include "missing_socket.h"
|
||||
@ -286,6 +287,7 @@ static int on_network_event(sd_event_source *s, int fd, uint32_t revents, void *
|
||||
|
||||
(void) manager_write_resolv_conf(m);
|
||||
(void) manager_send_changed(m, "DNS");
|
||||
(void) manager_send_dns_configuration_changed(m);
|
||||
|
||||
return 0;
|
||||
}
|
||||
@ -1184,7 +1186,7 @@ int manager_monitor_send(Manager *m, DnsQuery *q) {
|
||||
|
||||
assert(m);
|
||||
|
||||
if (set_isempty(m->varlink_subscription))
|
||||
if (set_isempty(m->varlink_query_results_subscription))
|
||||
return 0;
|
||||
|
||||
/* Merge all questions into one */
|
||||
@ -1234,7 +1236,7 @@ int manager_monitor_send(Manager *m, DnsQuery *q) {
|
||||
}
|
||||
|
||||
r = varlink_many_notifybo(
|
||||
m->varlink_subscription,
|
||||
m->varlink_query_results_subscription,
|
||||
SD_JSON_BUILD_PAIR("state", SD_JSON_BUILD_STRING(dns_transaction_state_to_string(q->state))),
|
||||
SD_JSON_BUILD_PAIR_CONDITION(q->state == DNS_TRANSACTION_DNSSEC_FAILED,
|
||||
"result", SD_JSON_BUILD_STRING(dnssec_result_to_string(q->answer_dnssec_result))),
|
||||
@ -1932,3 +1934,114 @@ void dns_manager_reset_statistics(Manager *m) {
|
||||
m->n_failure_responses_served_stale_total = 0;
|
||||
zero(m->n_dnssec_verdict);
|
||||
}
|
||||
|
||||
static int dns_configuration_json_append(
|
||||
const char *ifname,
|
||||
int ifindex,
|
||||
DnsServer *current_dns_server,
|
||||
DnsServer *dns_servers,
|
||||
DnsSearchDomain *search_domains,
|
||||
sd_json_variant **configuration) {
|
||||
|
||||
_cleanup_(sd_json_variant_unrefp) sd_json_variant *dns_servers_json = NULL,
|
||||
*search_domains_json = NULL,
|
||||
*current_dns_server_json = NULL;
|
||||
int r;
|
||||
|
||||
assert(configuration);
|
||||
|
||||
r = sd_json_variant_new_array(&dns_servers_json, NULL, 0);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
r = sd_json_variant_new_array(&search_domains_json, NULL, 0);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
if (current_dns_server) {
|
||||
r = dns_server_dump_configuration_to_json(current_dns_server, ¤t_dns_server_json);
|
||||
if (r < 0)
|
||||
return r;
|
||||
}
|
||||
|
||||
LIST_FOREACH(servers, s, dns_servers) {
|
||||
_cleanup_(sd_json_variant_unrefp) sd_json_variant *v = NULL;
|
||||
|
||||
r = dns_server_dump_configuration_to_json(s, &v);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
r = sd_json_variant_append_array(&dns_servers_json, v);
|
||||
if (r < 0)
|
||||
return r;
|
||||
}
|
||||
|
||||
LIST_FOREACH(domains, d, search_domains) {
|
||||
r = sd_json_variant_append_arrayb(&search_domains_json, SD_JSON_BUILD_STRING(DNS_SEARCH_DOMAIN_NAME(d)));
|
||||
if (r < 0)
|
||||
return r;
|
||||
}
|
||||
|
||||
return sd_json_variant_append_arraybo(
|
||||
configuration,
|
||||
JSON_BUILD_PAIR_STRING_NON_EMPTY("interface", ifname),
|
||||
SD_JSON_BUILD_PAIR_CONDITION(ifindex > 0, "interfaceIndex", SD_JSON_BUILD_UNSIGNED(ifindex)),
|
||||
JSON_BUILD_PAIR_VARIANT_NON_NULL("currentDNSServer", current_dns_server_json),
|
||||
SD_JSON_BUILD_PAIR_VARIANT("dnsServers", dns_servers_json),
|
||||
SD_JSON_BUILD_PAIR_VARIANT("searchDomains", search_domains_json));
|
||||
}
|
||||
|
||||
int manager_dump_dns_configuration_json(Manager *m, sd_json_variant **ret) {
|
||||
_cleanup_(sd_json_variant_unrefp) sd_json_variant *configuration = NULL;
|
||||
Link *l;
|
||||
int r;
|
||||
|
||||
assert(m);
|
||||
assert(ret);
|
||||
|
||||
/* Global DNS configuration */
|
||||
r = dns_configuration_json_append(
|
||||
/* ifname = */ NULL,
|
||||
/* ifindex = */ 0,
|
||||
manager_get_dns_server(m),
|
||||
m->dns_servers,
|
||||
m->search_domains,
|
||||
&configuration);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
/* Append configuration for each link */
|
||||
HASHMAP_FOREACH(l, m->links) {
|
||||
r = dns_configuration_json_append(
|
||||
l->ifname,
|
||||
l->ifindex,
|
||||
link_get_dns_server(l),
|
||||
l->dns_servers,
|
||||
l->search_domains,
|
||||
&configuration);
|
||||
if (r < 0)
|
||||
return r;
|
||||
}
|
||||
|
||||
return sd_json_buildo(ret, SD_JSON_BUILD_PAIR_VARIANT("configuration", configuration));
|
||||
}
|
||||
|
||||
int manager_send_dns_configuration_changed(Manager *m) {
|
||||
_cleanup_(sd_json_variant_unrefp) sd_json_variant *configuration = NULL;
|
||||
int r;
|
||||
|
||||
assert(m);
|
||||
|
||||
if (set_isempty(m->varlink_dns_configuration_subscription))
|
||||
return 0;
|
||||
|
||||
r = manager_dump_dns_configuration_json(m, &configuration);
|
||||
if (r < 0)
|
||||
return log_warning_errno(r, "Failed to dump DNS configuration json: %m");
|
||||
|
||||
r = varlink_many_notify(m->varlink_dns_configuration_subscription, configuration);
|
||||
if (r < 0)
|
||||
return log_warning_errno(r, "Failed to send DNS configuration event: %m");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
@ -152,7 +152,8 @@ struct Manager {
|
||||
sd_varlink_server *varlink_server;
|
||||
sd_varlink_server *varlink_monitor_server;
|
||||
|
||||
Set *varlink_subscription;
|
||||
Set *varlink_query_results_subscription;
|
||||
Set *varlink_dns_configuration_subscription;
|
||||
|
||||
sd_event_source *clock_change_event_source;
|
||||
|
||||
@ -225,3 +226,6 @@ int socket_disable_pmtud(int fd, int af);
|
||||
int dns_manager_dump_statistics_json(Manager *m, sd_json_variant **ret);
|
||||
|
||||
void dns_manager_reset_statistics(Manager *m);
|
||||
|
||||
int manager_dump_dns_configuration_json(Manager *m, sd_json_variant **ret);
|
||||
int manager_send_dns_configuration_changed(Manager *m);
|
||||
|
@ -125,14 +125,21 @@ static void vl_on_disconnect(sd_varlink_server *s, sd_varlink *link, void *userd
|
||||
|
||||
static void vl_on_notification_disconnect(sd_varlink_server *s, sd_varlink *link, void *userdata) {
|
||||
Manager *m = ASSERT_PTR(userdata);
|
||||
sd_varlink *removed_link = NULL;
|
||||
|
||||
assert(s);
|
||||
assert(link);
|
||||
|
||||
sd_varlink *removed_link = set_remove(m->varlink_subscription, link);
|
||||
removed_link = set_remove(m->varlink_query_results_subscription, link);
|
||||
if (removed_link) {
|
||||
sd_varlink_unref(removed_link);
|
||||
log_debug("%u monitor clients remain active", set_size(m->varlink_subscription));
|
||||
log_debug("%u query result monitor clients remain active", set_size(m->varlink_query_results_subscription));
|
||||
}
|
||||
|
||||
removed_link = set_remove(m->varlink_dns_configuration_subscription, link);
|
||||
if (removed_link) {
|
||||
sd_varlink_unref(removed_link);
|
||||
log_debug("%u DNS monitor clients remain active", set_size(m->varlink_dns_configuration_subscription));
|
||||
}
|
||||
}
|
||||
|
||||
@ -1275,12 +1282,12 @@ static int vl_method_subscribe_query_results(sd_varlink *link, sd_json_variant *
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to report monitor to be established: %m");
|
||||
|
||||
r = set_ensure_put(&m->varlink_subscription, NULL, link);
|
||||
r = set_ensure_put(&m->varlink_query_results_subscription, NULL, link);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to add subscription to set: %m");
|
||||
sd_varlink_ref(link);
|
||||
|
||||
log_debug("%u clients now attached for varlink notifications", set_size(m->varlink_subscription));
|
||||
log_debug("%u clients now attached for query result varlink notifications", set_size(m->varlink_query_results_subscription));
|
||||
|
||||
return 1;
|
||||
}
|
||||
@ -1400,6 +1407,38 @@ static int vl_method_reset_statistics(sd_varlink *link, sd_json_variant *paramet
|
||||
return sd_varlink_replyb(link, SD_JSON_BUILD_EMPTY_OBJECT);
|
||||
}
|
||||
|
||||
static int vl_method_subscribe_dns_configuration(sd_varlink *link, sd_json_variant *parameters, sd_varlink_method_flags_t flags, void *userdata) {
|
||||
_cleanup_(sd_json_variant_unrefp) sd_json_variant *configuration = NULL;
|
||||
Manager *m = ASSERT_PTR(sd_varlink_get_userdata(ASSERT_PTR(link)));
|
||||
int r;
|
||||
|
||||
/* if the client didn't set the more flag, it is using us incorrectly */
|
||||
if (!FLAGS_SET(flags, SD_VARLINK_METHOD_MORE))
|
||||
return sd_varlink_error(link, SD_VARLINK_ERROR_EXPECTED_MORE, NULL);
|
||||
|
||||
r = verify_polkit(link, parameters, "org.freedesktop.resolve1.subscribe-dns-configuration");
|
||||
if (r <= 0)
|
||||
return r;
|
||||
|
||||
r = manager_dump_dns_configuration_json(m, &configuration);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to dump current DNS configuration: %m");
|
||||
|
||||
r = sd_varlink_notify(link, configuration);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to send current DNS configuration: %m");
|
||||
|
||||
r = set_ensure_put(&m->varlink_dns_configuration_subscription, NULL, link);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to add subscription to set: %m");
|
||||
sd_varlink_ref(link);
|
||||
|
||||
log_debug("%u clients now attached for link configuration varlink notifications",
|
||||
set_size(m->varlink_dns_configuration_subscription));
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int varlink_monitor_server_init(Manager *m) {
|
||||
_cleanup_(sd_varlink_server_unrefp) sd_varlink_server *server = NULL;
|
||||
int r;
|
||||
@ -1425,7 +1464,8 @@ static int varlink_monitor_server_init(Manager *m) {
|
||||
"io.systemd.Resolve.Monitor.DumpCache", vl_method_dump_cache,
|
||||
"io.systemd.Resolve.Monitor.DumpServerState", vl_method_dump_server_state,
|
||||
"io.systemd.Resolve.Monitor.DumpStatistics", vl_method_dump_statistics,
|
||||
"io.systemd.Resolve.Monitor.ResetStatistics", vl_method_reset_statistics);
|
||||
"io.systemd.Resolve.Monitor.ResetStatistics", vl_method_reset_statistics,
|
||||
"io.systemd.Resolve.Monitor.SubscribeDNSConfiguration", vl_method_subscribe_dns_configuration);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to register varlink methods: %m");
|
||||
|
||||
|
431
src/run/run.c
431
src/run/run.c
@ -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);
|
||||
|
@ -112,6 +112,30 @@ static SD_VARLINK_DEFINE_METHOD(
|
||||
ResetStatistics,
|
||||
VARLINK_DEFINE_POLKIT_INPUT);
|
||||
|
||||
static SD_VARLINK_DEFINE_STRUCT_TYPE(
|
||||
DNSServer,
|
||||
SD_VARLINK_DEFINE_FIELD(address, SD_VARLINK_INT, SD_VARLINK_ARRAY),
|
||||
SD_VARLINK_DEFINE_FIELD(addressFamily, SD_VARLINK_INT, 0),
|
||||
SD_VARLINK_DEFINE_FIELD(port, SD_VARLINK_INT, 0),
|
||||
SD_VARLINK_DEFINE_FIELD(interfaceSpecifier, SD_VARLINK_INT, SD_VARLINK_NULLABLE),
|
||||
SD_VARLINK_DEFINE_FIELD(serverName, SD_VARLINK_STRING, SD_VARLINK_NULLABLE),
|
||||
SD_VARLINK_DEFINE_FIELD(accessible, SD_VARLINK_BOOL, 0));
|
||||
|
||||
static SD_VARLINK_DEFINE_STRUCT_TYPE(
|
||||
DNSConfiguration,
|
||||
SD_VARLINK_DEFINE_FIELD(interface, SD_VARLINK_STRING, SD_VARLINK_NULLABLE),
|
||||
SD_VARLINK_DEFINE_FIELD(interfaceIndex, SD_VARLINK_INT, SD_VARLINK_NULLABLE),
|
||||
SD_VARLINK_DEFINE_FIELD_BY_TYPE(currentDNSServer, DNSServer, SD_VARLINK_NULLABLE),
|
||||
SD_VARLINK_DEFINE_FIELD_BY_TYPE(dnsServers, DNSServer, SD_VARLINK_ARRAY|SD_VARLINK_NULLABLE),
|
||||
SD_VARLINK_DEFINE_FIELD(searchDomains, SD_VARLINK_STRING, SD_VARLINK_ARRAY|SD_VARLINK_NULLABLE));
|
||||
|
||||
static SD_VARLINK_DEFINE_METHOD_FULL(
|
||||
SubscribeDNSConfiguration,
|
||||
SD_VARLINK_REQUIRES_MORE,
|
||||
SD_VARLINK_DEFINE_INPUT(allowInteractiveAuthentication, SD_VARLINK_BOOL, SD_VARLINK_NULLABLE),
|
||||
SD_VARLINK_FIELD_COMMENT("The current global and per-interface DNS configurations"),
|
||||
SD_VARLINK_DEFINE_OUTPUT_BY_TYPE(configuration, DNSConfiguration, SD_VARLINK_ARRAY));
|
||||
|
||||
SD_VARLINK_DEFINE_INTERFACE(
|
||||
io_systemd_Resolve_Monitor,
|
||||
"io.systemd.Resolve.Monitor",
|
||||
@ -129,4 +153,7 @@ SD_VARLINK_DEFINE_INTERFACE(
|
||||
&vl_type_TransactionStatistics,
|
||||
&vl_type_CacheStatistics,
|
||||
&vl_type_DnssecStatistics,
|
||||
&vl_type_ServerState);
|
||||
&vl_type_ServerState,
|
||||
&vl_type_DNSServer,
|
||||
&vl_type_DNSConfiguration,
|
||||
&vl_method_SubscribeDNSConfiguration);
|
||||
|
@ -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);
|
||||
|
@ -1067,6 +1067,55 @@ DNS=127.0.0.1
|
||||
self.show_journal('systemd-timedated.service')
|
||||
self.fail(f'Timezone: {tz}, expected: Pacific/Honolulu')
|
||||
|
||||
def test_wait_online_dns(self):
|
||||
''' test systemd-networkd-wait-online with --dns '''
|
||||
self.start_unit('systemd-resolved')
|
||||
self.write_network(
|
||||
self.config,
|
||||
(
|
||||
'[Match]\n'
|
||||
f'Name={self.iface}\n'
|
||||
'[Network]\n'
|
||||
'DHCP=ipv4\n'
|
||||
'UseDNS=yes\n'
|
||||
'DNSDefaultRoute=yes\n'
|
||||
)
|
||||
)
|
||||
self.create_iface()
|
||||
self.start_unit('systemd-networkd')
|
||||
subprocess.check_call(
|
||||
[NETWORKD_WAIT_ONLINE, '--dns', '--interface', self.iface, '--timeout=10']
|
||||
)
|
||||
|
||||
def test_wait_online_dns_expect_timeout(self):
|
||||
''' test systemd-networkd-wait-online with --dns, and expect timeout '''
|
||||
self.start_unit('systemd-resolved')
|
||||
self.write_network(
|
||||
self.config,
|
||||
(
|
||||
'[Match]\n'
|
||||
f'Name={self.iface}\n'
|
||||
'[Network]\n'
|
||||
'DHCP=ipv4\n'
|
||||
'UseDNS=no\n'
|
||||
'DNSDefaultRoute=yes\n'
|
||||
)
|
||||
)
|
||||
self.create_iface()
|
||||
self.start_unit('systemd-networkd')
|
||||
|
||||
env = os.environ.copy()
|
||||
env['SYSTEMD_LOG_LEVEL'] = 'debug'
|
||||
r = subprocess.run(
|
||||
[NETWORKD_WAIT_ONLINE, '--dns', '--interface', self.iface, '--timeout=5'],
|
||||
stdout=subprocess.PIPE,
|
||||
stderr=subprocess.PIPE,
|
||||
env=env,
|
||||
)
|
||||
subprocess.check_call(['resolvectl'])
|
||||
self.assertNotEqual(r.returncode, 0)
|
||||
self.assertRegex(r.stderr, rb'No.*DNS server is accessible')
|
||||
|
||||
|
||||
class MatchClientTest(unittest.TestCase, NetworkdTestingUtilities):
|
||||
"""Test [Match] sections in .network files.
|
||||
|
@ -261,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'
|
||||
|
@ -934,6 +934,15 @@ testcase_11_nft() {
|
||||
|
||||
# Test resolvectl show-server-state
|
||||
testcase_12_resolvectl2() {
|
||||
# Cleanup
|
||||
# shellcheck disable=SC2317
|
||||
cleanup() {
|
||||
rm -f /run/systemd/resolved.conf.d/reload.conf
|
||||
systemctl reload systemd-resolved.service
|
||||
}
|
||||
|
||||
trap cleanup RETURN
|
||||
|
||||
run resolvectl show-server-state
|
||||
grep -qF "10.0.0.1" "$RUN_OUT"
|
||||
grep -qF "Interface" "$RUN_OUT"
|
||||
@ -996,6 +1005,63 @@ testcase_12_resolvectl2() {
|
||||
restart_resolved
|
||||
}
|
||||
|
||||
# Test io.systemd.Resolve.Monitor.SubscribeDNSConfiguration
|
||||
testcase_13_varlink_subscribe_dns_configuration() {
|
||||
# Cleanup
|
||||
# shellcheck disable=SC2317
|
||||
cleanup() {
|
||||
rm -f /run/systemd/resolved.conf.d/global-dns.conf
|
||||
restart_resolved
|
||||
}
|
||||
|
||||
trap cleanup RETURN
|
||||
|
||||
local unit
|
||||
local tmpfile
|
||||
|
||||
unit="subscribe-dns-configuration-$(systemd-id128 new -u).service"
|
||||
tmpfile=$(mktemp)
|
||||
|
||||
# Clear global and per-interface DNS before monitoring the configuration change.
|
||||
mkdir -p /run/systemd/resolved.conf.d/
|
||||
{
|
||||
echo "[Resolve]"
|
||||
echo "DNS="
|
||||
} > /run/systemd/resolved.conf.d/global-dns.conf
|
||||
systemctl reload systemd-resolved.service
|
||||
resolvectl dns dns0 ""
|
||||
|
||||
# Start the call to io.systemd.Resolve.Monitor.SubscribeDNSConfiguration
|
||||
systemd-run -u "$unit" -p "Type=exec" -p "StandardOutput=truncate:$tmpfile" \
|
||||
varlinkctl call -E /run/systemd/resolve/io.systemd.Resolve.Monitor io.systemd.Resolve.Monitor.SubscribeDNSConfiguration '{}'
|
||||
|
||||
# Check that an initial reply was given with the current expected settings.
|
||||
assert_in \
|
||||
'{"global":{"servers":[],"domains":[]}}' \
|
||||
"$(jq -cr --seq '.configuration.[] | select(.interface == null) | {"global": {servers: .dnsServers, domains: .searchDomains}}' "$tmpfile" | tr -d '\030')"
|
||||
assert_in \
|
||||
'{"dns0":{"servers":[],"domains":[]}}' \
|
||||
"$(jq -cr --seq '.configuration.[] | select(.interface == "dns0") | {"dns0": {servers: .dnsServers, domains: .searchDomains}}' "$tmpfile" | tr -d '\030')"
|
||||
|
||||
# Test that changes in global DNS configuration are reflected.
|
||||
mkdir -p /run/systemd/resolved.conf.d/
|
||||
{
|
||||
echo "[Resolve]"
|
||||
echo "DNS=8.8.8.8"
|
||||
echo "Domains=lan"
|
||||
} > /run/systemd/resolved.conf.d/global-dns.conf
|
||||
systemctl reload systemd-resolved.service
|
||||
assert_in \
|
||||
'{"global":{"servers":[[8,8,8,8]],"domains":["lan"]}}' \
|
||||
"$(jq -cr --seq '.configuration.[] | select(.interface == null) | {"global":{servers: [.dnsServers.[] | .address], domains: .searchDomains}}' "$tmpfile" | tr -d '\030')"
|
||||
|
||||
# Test that changes to per-interface DNS are reflected.
|
||||
resolvectl dns dns0 8.8.4.4 1.1.1.1
|
||||
assert_in \
|
||||
'{"dns0":{"servers":[[8,8,4,4],[1,1,1,1]],"domains":[""]}}' \
|
||||
"$(jq -cr --seq '.configuration.[] | select(.interface == "dns0") | {"dns0":{servers: [.dnsServers.[] | .address], domains: .searchDomains}}' "$tmpfile" | tr -d '\030')"
|
||||
}
|
||||
|
||||
# PRE-SETUP
|
||||
systemctl unmask systemd-resolved.service
|
||||
systemctl enable --now systemd-resolved.service
|
||||
|
@ -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
|
||||
|
||||
|
@ -14,7 +14,7 @@ ConditionCapability=CAP_NET_ADMIN
|
||||
DefaultDependencies=no
|
||||
Conflicts=shutdown.target
|
||||
BindsTo=systemd-networkd.service
|
||||
After=systemd-networkd.service
|
||||
After=systemd-networkd.service systemd-resolved.service
|
||||
Before=network-online.target shutdown.target
|
||||
|
||||
[Service]
|
||||
|
Loading…
Reference in New Issue
Block a user