diff --git a/man/systemd-networkd-wait-online.service.xml b/man/systemd-networkd-wait-online.service.xml
index e696a2ba9a..2daeabfe7b 100644
--- a/man/systemd-networkd-wait-online.service.xml
+++ b/man/systemd-networkd-wait-online.service.xml
@@ -144,6 +144,22 @@
+
+
+
+ Waiting for DNS servers to be accessible on each interface. If this
+ option is specified with , then
+ systemd-networkd-wait-online exits with success when at least one interface
+ becomes online and has an accessible DNS server.
+ If a link has the property DefaultRoute=yes (either because the
+ DNSDefaultRoute= network property is explicitly configured, or
+ because the link does not have any "routing-only" domains), or if the search domain "." is
+ configured, then wait for link-specific DNS servers to be accessible. Otherwise, allow global
+ DNS servers to satisfy the condition.
+
+
+
+
diff --git a/src/network/meson.build b/src/network/meson.build
index 295d015e7c..5bcce39738 100644
--- a/src/network/meson.build
+++ b/src/network/meson.build
@@ -114,6 +114,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',
diff --git a/src/network/wait-online/dns-configuration.c b/src/network/wait-online/dns-configuration.c
new file mode 100644
index 0000000000..c369cc115a
--- /dev/null
+++ b/src/network/wait-online/dns-configuration.c
@@ -0,0 +1,233 @@
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+
+#include "sd-json.h"
+
+#include "af-list.h"
+#include "alloc-util.h"
+#include "dns-configuration.h"
+#include "hash-funcs.h"
+#include "in-addr-util.h"
+#include "iovec-util.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);
+ iovec_done(&s->addr);
+
+ return mfree(s);
+}
+
+DEFINE_PRIVATE_HASH_OPS_WITH_VALUE_DESTRUCTOR(
+ dns_server_hash_ops,
+ void,
+ trivial_hash_func,
+ trivial_compare_func,
+ DNSServer,
+ dns_server_free);
+
+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_byte_array_iovec, offsetof(DNSServer, addr), SD_JSON_MANDATORY },
+ { "family", 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 },
+ { "ifindex", SD_JSON_VARIANT_UNSIGNED, json_dispatch_ifindex, offsetof(DNSServer, ifindex), SD_JSON_RELAX },
+ { "name", 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;
+
+ if (s->addr.iov_len != FAMILY_ADDRESS_SIZE_SAFE(s->family))
+ return json_log(variant, flags, SYNTHETIC_ERRNO(EINVAL),
+ "Dispatched address size (%zu) is incompatible with the family (%s).",
+ s->addr.iov_len, af_to_ipv4_ipv6(s->family));
+
+ *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);
+ _cleanup_set_free_ Set *dns_servers = NULL;
+ sd_json_variant *v;
+ int r;
+
+ JSON_VARIANT_ARRAY_FOREACH(v, variant) {
+ _cleanup_(dns_server_freep) DNSServer *s = NULL;
+
+ 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_ensure_consume(&dns_servers, &dns_server_hash_ops, TAKE_PTR(s));
+ if (r < 0)
+ return r;
+ }
+
+ set_free_and_replace(*ret, dns_servers);
+
+ return 0;
+}
+
+SearchDomain* search_domain_free(SearchDomain *d) {
+ if (!d)
+ return NULL;
+
+ free(d->name);
+
+ return mfree(d);
+}
+
+DEFINE_PRIVATE_HASH_OPS_WITH_VALUE_DESTRUCTOR(
+ search_domain_hash_ops,
+ void,
+ trivial_hash_func,
+ trivial_compare_func,
+ SearchDomain,
+ search_domain_free);
+
+static int dispatch_search_domain(const char *name, sd_json_variant *variant, sd_json_dispatch_flags_t flags, void *userdata) {
+ static const sd_json_dispatch_field search_domain_dispatch_table[] = {
+ { "name", SD_JSON_VARIANT_STRING, sd_json_dispatch_string, offsetof(SearchDomain, name), SD_JSON_MANDATORY },
+ { "routeOnly", SD_JSON_VARIANT_BOOLEAN, sd_json_dispatch_stdbool, offsetof(SearchDomain, route_only), SD_JSON_MANDATORY },
+ { "ifindex", SD_JSON_VARIANT_UNSIGNED, json_dispatch_ifindex, offsetof(SearchDomain, ifindex), SD_JSON_RELAX },
+ {},
+ };
+ SearchDomain **ret = ASSERT_PTR(userdata);
+ _cleanup_(search_domain_freep) SearchDomain *d = NULL;
+ int r;
+
+ d = new0(SearchDomain, 1);
+ if (!d)
+ return log_oom();
+
+ r = sd_json_dispatch(variant, search_domain_dispatch_table, flags, d);
+ if (r < 0)
+ return r;
+
+ *ret = TAKE_PTR(d);
+
+ return 0;
+}
+
+static int dispatch_search_domain_array(const char *name, sd_json_variant *variant, sd_json_dispatch_flags_t flags, void *userdata) {
+ Set **ret = ASSERT_PTR(userdata);
+ _cleanup_set_free_ Set *search_domains = NULL;
+ sd_json_variant *v;
+ int r;
+
+ JSON_VARIANT_ARRAY_FOREACH(v, variant) {
+ _cleanup_(search_domain_freep) SearchDomain *d = NULL;
+
+ r = dispatch_search_domain(name, v, flags, &d);
+ if (r < 0)
+ return json_log(v, flags, r, "JSON array element is not a valid SearchDomain.");
+
+ r = set_ensure_consume(&search_domains, &search_domain_hash_ops, TAKE_PTR(d));
+ if (r < 0)
+ return r;
+ }
+
+ set_free_and_replace(*ret, search_domains);
+
+ return 0;
+}
+
+DNSConfiguration* dns_configuration_free(DNSConfiguration *c) {
+ if (!c)
+ return NULL;
+
+ dns_server_free(c->current_dns_server);
+ set_free(c->dns_servers);
+ set_free(c->search_domains);
+ free(c->ifname);
+
+ return mfree(c);
+}
+
+DEFINE_HASH_OPS_WITH_VALUE_DESTRUCTOR(
+ dns_configuration_hash_ops,
+ void,
+ trivial_hash_func,
+ trivial_compare_func,
+ DNSConfiguration,
+ dns_configuration_free);
+
+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[] = {
+ { "ifname", SD_JSON_VARIANT_STRING, sd_json_dispatch_string, offsetof(DNSConfiguration, ifname), 0 },
+ { "ifindex", SD_JSON_VARIANT_UNSIGNED, json_dispatch_ifindex, offsetof(DNSConfiguration, ifindex), SD_JSON_RELAX },
+ { "defaultRoute", SD_JSON_VARIANT_BOOLEAN, sd_json_dispatch_stdbool, offsetof(DNSConfiguration, default_route), 0 },
+ { "currentServer", SD_JSON_VARIANT_OBJECT, dispatch_dns_server, offsetof(DNSConfiguration, current_dns_server), 0 },
+ { "servers", SD_JSON_VARIANT_ARRAY, dispatch_dns_server_array, offsetof(DNSConfiguration, dns_servers), 0 },
+ { "searchDomains", SD_JSON_VARIANT_ARRAY, dispatch_search_domain_array, 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;
+}
+
+bool dns_configuration_contains_search_domain(DNSConfiguration *c, const char *domain) {
+ SearchDomain *d = NULL;
+
+ assert(domain);
+
+ if (!c)
+ return false;
+
+ SET_FOREACH(d, c->search_domains)
+ if (streq(d->name, domain))
+ return true;
+
+ return false;
+}
diff --git a/src/network/wait-online/dns-configuration.h b/src/network/wait-online/dns-configuration.h
new file mode 100644
index 0000000000..6e6890d925
--- /dev/null
+++ b/src/network/wait-online/dns-configuration.h
@@ -0,0 +1,52 @@
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+#pragma once
+
+#include "sd-json.h"
+
+#include "hash-funcs.h"
+#include "iovec-util.h"
+#include "macro-fundamental.h"
+#include "set.h"
+
+typedef struct DNSServer DNSServer;
+typedef struct SearchDomain SearchDomain;
+typedef struct DNSConfiguration DNSConfiguration;
+
+struct DNSServer {
+ struct iovec 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 SearchDomain {
+ char *name;
+ bool route_only;
+ int ifindex;
+};
+
+SearchDomain* search_domain_free(SearchDomain *d);
+DEFINE_TRIVIAL_CLEANUP_FUNC(SearchDomain*, search_domain_free);
+
+struct DNSConfiguration {
+ char *ifname;
+ int ifindex;
+ bool default_route;
+ DNSServer *current_dns_server;
+ Set *dns_servers;
+ Set *search_domains;
+};
+
+DNSConfiguration* dns_configuration_free(DNSConfiguration *c);
+DEFINE_TRIVIAL_CLEANUP_FUNC(DNSConfiguration*, dns_configuration_free);
+
+int dns_configuration_from_json(sd_json_variant *variant, DNSConfiguration **ret);
+bool dns_is_accessible(DNSConfiguration *c);
+bool dns_configuration_contains_search_domain(DNSConfiguration *c, const char *domain);
+
+extern const struct hash_ops dns_configuration_hash_ops;
diff --git a/src/network/wait-online/link.c b/src/network/wait-online/link.c
index 3097611d98..a4a64462ff 100644
--- a/src/network/wait-online/link.c
+++ b/src/network/wait-online/link.c
@@ -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"
@@ -32,6 +33,7 @@ int link_new(Manager *m, Link **ret, int ifindex, const char *ifname) {
.ifname = TAKE_PTR(n),
.ifindex = ifindex,
.required_operstate = LINK_OPERSTATE_RANGE_DEFAULT,
+ .dns_configuration = hashmap_remove(m->dns_configuration_by_link_index, INT_TO_PTR(ifindex)),
};
r = hashmap_ensure_put(&m->links_by_index, NULL, INT_TO_PTR(ifindex), l);
@@ -62,6 +64,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);
diff --git a/src/network/wait-online/link.h b/src/network/wait-online/link.h
index 5dc26d9c40..ec352c4113 100644
--- a/src/network/wait-online/link.h
+++ b/src/network/wait-online/link.h
@@ -3,6 +3,7 @@
#include "sd-netlink.h"
+#include "dns-configuration.h"
#include "log-link.h"
#include "network-util.h"
@@ -24,6 +25,7 @@ struct Link {
LinkAddressState ipv4_address_state;
LinkAddressState ipv6_address_state;
char *state;
+ DNSConfiguration *dns_configuration;
};
int link_new(Manager *m, Link **ret, int ifindex, const char *ifname);
diff --git a/src/network/wait-online/manager.c b/src/network/wait-online/manager.c
index 47f5df6a5f..4bad0f97e2 100644
--- a/src/network/wait-online/manager.c
+++ b/src/network/wait-online/manager.c
@@ -4,7 +4,13 @@
#include
#include
+#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,26 @@ static int manager_link_is_online(Manager *m, Link *l, const LinkOperationalStat
"No routable IPv6 address is configured.");
}
+ if (m->requires_dns) {
+ if (!l->dns_configuration)
+ return log_link_debug_errno(l, SYNTHETIC_ERRNO(EADDRNOTAVAIL),
+ "No DNS configuration yet");
+
+ /* If a link is configured with DNSDefaultRoute=yes, or is configured with the
+ * search domain '.', then require link-specific DNS servers to be available.
+ * Otherwise, we check the global DNS configuration. */
+ if (l->dns_configuration->default_route ||
+ dns_configuration_contains_search_domain(l->dns_configuration, ".")) {
+
+ if (!dns_is_accessible(l->dns_configuration))
+ return log_link_debug_errno(l, SYNTHETIC_ERRNO(EADDRNOTAVAIL),
+ "No link-specific DNS server is accessible.");
+
+ } else 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 +407,117 @@ 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 (!sd_json_variant_is_array(configurations)) {
+ log_warning("DNS configuration JSON data does not have configuration key, ignoring.");
+ return 0;
+ }
+
+ /* Clear any existing link DNS configuration saved by the manager. */
+ hashmap_clear(m->dns_configuration_by_link_index);
+
+ 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 {
+ r = hashmap_ensure_put(
+ &m->dns_configuration_by_link_index,
+ &dns_configuration_hash_ops,
+ INT_TO_PTR(c->ifindex),
+ c);
+ if (r < 0) {
+ log_warning_errno(r, "Failed to save DNS configuration for link %i, ignoring: %m", c->ifindex);
+ continue;
+ }
+ TAKE_PTR(c);
+ }
+ } 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 +534,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 +559,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 +580,10 @@ 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);
+ hashmap_free(m->dns_configuration_by_link_index);
return mfree(m);
}
diff --git a/src/network/wait-online/manager.h b/src/network/wait-online/manager.h
index 01ad18f8f6..e5ff8c21ba 100644
--- a/src/network/wait-online/manager.h
+++ b/src/network/wait-online/manager.h
@@ -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,17 @@ struct Manager {
sd_event_source *network_monitor_event_source;
sd_event *event;
+
+ sd_varlink *varlink_client;
+ DNSConfiguration *dns_configuration;
+ Hashmap *dns_configuration_by_link_index;
};
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);
diff --git a/src/network/wait-online/wait-online.c b/src/network/wait-online/wait-online.c
index 48c4d1c0ee..467d2e7da7 100644
--- a/src/network/wait-online/wait-online.c
+++ b/src/network/wait-online/wait-online.c
@@ -10,6 +10,7 @@
#include "daemon-util.h"
#include "main-func.h"
#include "manager.h"
+#include "parse-argument.h"
#include "pretty-print.h"
#include "signal-util.h"
#include "socket-util.h"
@@ -22,6 +23,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_freep);
STATIC_DESTRUCTOR_REGISTER(arg_ignore, strv_freep);
@@ -48,6 +50,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 +109,7 @@ static int parse_argv(int argc, char *argv[]) {
ARG_IGNORE,
ARG_ANY,
ARG_TIMEOUT,
+ ARG_DNS,
};
static const struct option options[] = {
@@ -119,6 +123,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", optional_argument, NULL, ARG_DNS },
{}
};
@@ -178,6 +183,12 @@ static int parse_argv(int argc, char *argv[]) {
return r;
break;
+ case ARG_DNS:
+ r = parse_boolean_argument("--dns", optarg, &arg_requires_dns);
+ if (r < 0)
+ return r;
+ break;
+
case '?':
return -EINVAL;
@@ -204,7 +215,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");