diff --git a/Makefile.am b/Makefile.am
index 91eaf67aec..72f224fc20 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -2530,8 +2530,6 @@ src_core_libNetworkManager_la_SOURCES = \
src/core/dns/nm-dns-dnsmasq.h \
src/core/dns/nm-dns-systemd-resolved.c \
src/core/dns/nm-dns-systemd-resolved.h \
- src/core/dns/nm-dns-unbound.c \
- src/core/dns/nm-dns-unbound.h \
\
src/core/dnsmasq/nm-dnsmasq-manager.c \
src/core/dnsmasq/nm-dnsmasq-manager.h \
diff --git a/config.h.meson b/config.h.meson
index 7d1feb53ad..7337165082 100644
--- a/config.h.meson
+++ b/config.h.meson
@@ -13,9 +13,6 @@
/* Define to path of dnsmasq binary */
#mesondefine DNSMASQ_PATH
-/* Define to path of unbound dnssec-trigger-script */
-#mesondefine DNSSEC_TRIGGER_PATH
-
/* Gettext package */
#mesondefine GETTEXT_PACKAGE
diff --git a/configure.ac b/configure.ac
index 8ed50706b9..24107f163b 100644
--- a/configure.ac
+++ b/configure.ac
@@ -1006,18 +1006,6 @@ fi
AC_DEFINE_UNQUOTED(DNSMASQ_PATH, "$DNSMASQ_PATH", [Define to path of dnsmasq binary])
AC_SUBST(DNSMASQ_PATH)
-# dnssec-trigger-script path
-AC_ARG_WITH(dnssec_trigger,
- AS_HELP_STRING([--with-dnssec-trigger=/path/to/dnssec-trigger-script], [path to unbound dnssec-trigger-script]))
-if test "x${with_dnssec_trigger}" = x; then
- AC_PATH_PROG(DNSSEC_TRIGGER_PATH, dnssec-trigger-script, /usr/libexec/dnssec-trigger-script,
- /usr/local/libexec:/usr/local/lib:/usr/local/lib/dnssec-trigger:/usr/libexec:/usr/lib:/usr/lib/dnssec-trigger)
-else
- DNSSEC_TRIGGER_PATH="$with_dnssec_trigger"
-fi
-AC_DEFINE_UNQUOTED(DNSSEC_TRIGGER_PATH, "$DNSSEC_TRIGGER_PATH", [Define to path of unbound dnssec-trigger-script])
-AC_SUBST(DNSSEC_TRIGGER_PATH)
-
# system CA certificates path
AC_ARG_WITH(system-ca-path,
AS_HELP_STRING([--with-system-ca-path=/path/to/ssl/certs], [path to system CA certificates]))
diff --git a/man/NetworkManager.conf.xml b/man/NetworkManager.conf.xml
index cb6b40afa0..18b25d9370 100644
--- a/man/NetworkManager.conf.xml
+++ b/man/NetworkManager.conf.xml
@@ -345,19 +345,12 @@ no-auto-default=*
systemd-resolved: NetworkManager will
push the DNS configuration to systemd-resolved
- unbound: NetworkManager will talk
- to unbound and dnssec-triggerd, using "Conditional Forwarding"
- with DNSSEC support. /etc/resolv.conf
- will be managed by dnssec-trigger daemon. This option is
- deprecated. Note that dnssec-trigger ships a NetworkManager dispatcher
- script so this DNS plugin is not necessary.
-
none: NetworkManager will not
modify resolv.conf. This implies
rc-manager unmanaged
- Note that the plugins dnsmasq, systemd-resolved
- and unbound are caching local nameservers.
+ Note that the plugins dnsmasq and systemd-resolved
+ are caching local nameservers.
Hence, when NetworkManager writes &nmrundir;/resolv.conf
and /etc/resolv.conf (according to rc-manager
setting below), the name server there will be localhost only.
diff --git a/meson.build b/meson.build
index 45d6970894..edf4b377fa 100644
--- a/meson.build
+++ b/meson.build
@@ -683,18 +683,11 @@ endforeach
# external misc tools paths
default_paths = ['/sbin', '/usr/sbin']
-dnssec_ts_paths = ['/usr/local/libexec',
- '/usr/local/lib',
- '/usr/local/lib/dnssec-trigger',
- '/usr/libexec',
- '/usr/lib',
- '/usr/lib/dnssec-trigger']
# 0: cmdline option, 1: paths, 2: fallback
-progs = [['iptables', default_paths, '/usr/sbin/iptables'],
- ['nft', default_paths, '/usr/sbin/nft'],
- ['dnsmasq', default_paths, ''],
- ['dnssec_trigger', dnssec_ts_paths, join_paths(nm_libexecdir, 'dnssec-trigger-script') ],
+progs = [['iptables', default_paths, '/usr/sbin/iptables'],
+ ['nft', default_paths, '/usr/sbin/nft'],
+ ['dnsmasq', default_paths, ''],
]
foreach prog : progs
diff --git a/meson_options.txt b/meson_options.txt
index 42f84711d0..cec0664186 100644
--- a/meson_options.txt
+++ b/meson_options.txt
@@ -7,7 +7,6 @@ option('kernel_firmware_dir', type: 'string', value: '/lib/firmware', descriptio
option('iptables', type: 'string', value: '', description: 'path to iptables')
option('nft', type: 'string', value: '', description: 'path to nft')
option('dnsmasq', type: 'string', value: '', description: 'path to dnsmasq')
-option('dnssec_trigger', type: 'string', value: '', description: 'path to unbound dnssec-trigger-script')
# platform
option('dist_version', type: 'string', value: '', description: 'Define the NM\'s distribution version string')
diff --git a/src/core/devices/nm-device.c b/src/core/devices/nm-device.c
index f554ccfd25..90b1920cc0 100644
--- a/src/core/devices/nm-device.c
+++ b/src/core/devices/nm-device.c
@@ -604,6 +604,7 @@ typedef struct _NMDevicePrivate {
const NMDeviceIPState state;
NMDeviceIPState state_;
};
+ gulong dnsmgr_update_pending_signal_id;
} ip_data;
union {
@@ -2929,6 +2930,13 @@ _add_capabilities(NMDevice *self, NMDeviceCapabilities capabilities)
/*****************************************************************************/
+static void
+_dev_ip_state_dnsmgr_update_pending_changed(NMDnsManager *dnsmgr, GParamSpec *pspec, NMDevice *self)
+{
+ _dev_ip_state_check(self, AF_INET);
+ _dev_ip_state_check(self, AF_INET6);
+}
+
static void
_dev_ip_state_req_timeout_cancel(NMDevice *self, int addr_family)
{
@@ -3321,6 +3329,27 @@ got_ip_state:
combinedip_state = priv->ip_data.state;
}
+ if (combinedip_state == NM_DEVICE_IP_STATE_READY
+ && priv->ip_data.state <= NM_DEVICE_IP_STATE_PENDING
+ && nm_dns_manager_get_update_pending(nm_manager_get_dns_manager(priv->manager))) {
+ /* We would be ready, but a DNS update is pending. That prevents us from getting fully ready. */
+ if (priv->ip_data.dnsmgr_update_pending_signal_id == 0) {
+ priv->ip_data.dnsmgr_update_pending_signal_id =
+ g_signal_connect(nm_manager_get_dns_manager(priv->manager),
+ "notify::" NM_DNS_MANAGER_UPDATE_PENDING,
+ G_CALLBACK(_dev_ip_state_dnsmgr_update_pending_changed),
+ self);
+ _LOGT_ip(AF_UNSPEC,
+ "check-state: (combined) state: wait for DNS before becoming ready");
+ }
+ combinedip_state = NM_DEVICE_IP_STATE_PENDING;
+ }
+ if (combinedip_state != NM_DEVICE_IP_STATE_PENDING
+ && priv->ip_data.dnsmgr_update_pending_signal_id != 0) {
+ nm_clear_g_signal_handler(nm_manager_get_dns_manager(priv->manager),
+ &priv->ip_data.dnsmgr_update_pending_signal_id);
+ }
+
_LOGT_ip(AF_UNSPEC,
"check-state: (combined) state %s => %s",
nm_device_ip_state_to_string(priv->ip_data.state),
@@ -12329,7 +12358,8 @@ delete_on_deactivate_check_and_schedule(NMDevice *self)
static void
_cleanup_ip_pre(NMDevice *self, int addr_family, CleanupType cleanup_type, gboolean from_reapply)
{
- const int IS_IPv4 = NM_IS_IPv4(addr_family);
+ const int IS_IPv4 = NM_IS_IPv4(addr_family);
+ NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE(self);
_dev_ipsharedx_cleanup(self, addr_family);
@@ -12345,6 +12375,9 @@ _cleanup_ip_pre(NMDevice *self, int addr_family, CleanupType cleanup_type, gbool
_dev_ipmanual_cleanup(self);
+ nm_clear_g_signal_handler(nm_manager_get_dns_manager(priv->manager),
+ &priv->ip_data.dnsmgr_update_pending_signal_id);
+
_dev_ip_state_cleanup(self, AF_UNSPEC, from_reapply);
_dev_ip_state_cleanup(self, addr_family, from_reapply);
}
diff --git a/src/core/dns/nm-dns-dnsmasq.c b/src/core/dns/nm-dns-dnsmasq.c
index 43426882ed..7d0f0490e0 100644
--- a/src/core/dns/nm-dns-dnsmasq.c
+++ b/src/core/dns/nm-dns-dnsmasq.c
@@ -678,19 +678,23 @@ typedef struct {
char *name_owner;
+ GSource *main_timeout_source;
+ GSource *burst_retry_timeout_source;
+
gint64 burst_start_at;
GPid process_pid;
guint name_owner_changed_id;
- guint main_timeout_id;
-
- guint burst_retry_timeout_id;
guint8 burst_count;
bool is_stopped : 1;
+ bool set_server_ex_args_dirty : 1;
+
+ bool update_pending : 1;
+
} NMDnsDnsmasqPrivate;
struct _NMDnsDnsmasq {
@@ -704,7 +708,8 @@ struct _NMDnsDnsmasqClass {
G_DEFINE_TYPE(NMDnsDnsmasq, nm_dns_dnsmasq, NM_TYPE_DNS_PLUGIN)
-#define NM_DNS_DNSMASQ_GET_PRIVATE(self) _NM_GET_PRIVATE(self, NMDnsDnsmasq, NM_IS_DNS_DNSMASQ)
+#define NM_DNS_DNSMASQ_GET_PRIVATE(self) \
+ _NM_GET_PRIVATE(self, NMDnsDnsmasq, NM_IS_DNS_DNSMASQ, NMDnsPlugin)
/*****************************************************************************/
@@ -717,6 +722,55 @@ static gboolean start_dnsmasq(NMDnsDnsmasq *self, gboolean force_start, GError *
/*****************************************************************************/
+static gboolean
+_update_pending_detect(NMDnsDnsmasq *self)
+{
+ NMDnsDnsmasqPrivate *priv = NM_DNS_DNSMASQ_GET_PRIVATE(self);
+
+ if (priv->is_stopped)
+ return FALSE;
+ if (priv->main_timeout_source) {
+ /* we are waiting for dnsmasq to start. */
+ return TRUE;
+ }
+ if (priv->update_cancellable) {
+ /* An update is in progress. Busy. */
+ return TRUE;
+ }
+ if (priv->set_server_ex_args_dirty) {
+ /* the args just changed and were not yet sent. Busy. */
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+static void
+_update_pending_maybe_changed(NMDnsDnsmasq *self)
+{
+ NMDnsDnsmasqPrivate *priv = NM_DNS_DNSMASQ_GET_PRIVATE(self);
+ gboolean update_pending;
+
+ update_pending = _update_pending_detect(self);
+ if (priv->update_pending == update_pending)
+ return;
+
+ priv->update_pending = update_pending;
+ _nm_dns_plugin_update_pending_maybe_changed(NM_DNS_PLUGIN(self));
+}
+
+static gboolean
+get_update_pending(NMDnsPlugin *plugin)
+{
+ NMDnsDnsmasq *self = NM_DNS_DNSMASQ(plugin);
+ NMDnsDnsmasqPrivate *priv = NM_DNS_DNSMASQ_GET_PRIVATE(self);
+
+ nm_assert(priv->update_pending == _update_pending_detect(self));
+ return priv->update_pending;
+}
+
+/*****************************************************************************/
+
static void
add_dnsmasq_nameserver(NMDnsDnsmasq *self,
GVariantBuilder *servers,
@@ -871,6 +925,7 @@ static void
dnsmasq_update_done(GObject *source_object, GAsyncResult *res, gpointer user_data)
{
NMDnsDnsmasq *self;
+ NMDnsDnsmasqPrivate *priv;
gs_free_error GError *error = NULL;
gs_unref_variant GVariant *response = NULL;
@@ -880,10 +935,16 @@ dnsmasq_update_done(GObject *source_object, GAsyncResult *res, gpointer user_dat
return;
self = user_data;
+ priv = NM_DNS_DNSMASQ_GET_PRIVATE(self);
+
+ nm_clear_g_cancellable(&priv->update_cancellable);
+
if (!response)
_LOGW("dnsmasq update failed: %s", error->message);
else
_LOGD("dnsmasq update successful");
+
+ _update_pending_maybe_changed(self);
}
static void
@@ -899,6 +960,8 @@ send_dnsmasq_update(NMDnsDnsmasq *self)
nm_clear_g_cancellable(&priv->update_cancellable);
priv->update_cancellable = g_cancellable_new();
+ priv->set_server_ex_args_dirty = FALSE;
+
g_dbus_connection_call(priv->dbus_connection,
priv->name_owner,
DNSMASQ_DBUS_PATH,
@@ -911,6 +974,8 @@ send_dnsmasq_update(NMDnsDnsmasq *self)
priv->update_cancellable,
dnsmasq_update_done,
self);
+
+ _update_pending_maybe_changed(self);
}
/*****************************************************************************/
@@ -928,17 +993,19 @@ _main_cleanup(NMDnsDnsmasq *self, gboolean emit_failed)
nm_clear_g_dbus_connection_signal(priv->dbus_connection, &priv->name_owner_changed_id);
- nm_clear_g_source(&priv->main_timeout_id);
+ nm_clear_g_source_inst(&priv->main_timeout_source);
nm_clear_g_cancellable(&priv->update_cancellable);
/* cancelling the main_cancellable will also cause _gl_pid_spawn*() to terminate the
* process in the background. */
nm_clear_g_cancellable(&priv->main_cancellable);
- if (!priv->is_stopped && priv->burst_retry_timeout_id == 0) {
+ if (!priv->is_stopped && !priv->burst_retry_timeout_source) {
start_dnsmasq(self, FALSE, NULL);
send_dnsmasq_update(self);
}
+
+ _update_pending_maybe_changed(self);
}
static void
@@ -961,8 +1028,10 @@ name_owner_changed(NMDnsDnsmasq *self, const char *name_owner)
}
_LOGT("D-Bus name for dnsmasq got owner %s", name_owner);
- nm_clear_g_source(&priv->main_timeout_id);
+ nm_clear_g_source_inst(&priv->main_timeout_source);
send_dnsmasq_update(self);
+
+ _update_pending_maybe_changed(self);
}
static void
@@ -1047,11 +1116,11 @@ _burst_retry_timeout_cb(gpointer user_data)
NMDnsDnsmasq *self = user_data;
NMDnsDnsmasqPrivate *priv = NM_DNS_DNSMASQ_GET_PRIVATE(self);
- priv->burst_retry_timeout_id = 0;
+ nm_clear_g_source_inst(&priv->burst_retry_timeout_source);
start_dnsmasq(self, TRUE, NULL);
send_dnsmasq_update(self);
- return G_SOURCE_REMOVE;
+ return G_SOURCE_CONTINUE;
}
static gboolean
@@ -1090,33 +1159,35 @@ start_dnsmasq(NMDnsDnsmasq *self, gboolean force_start, GError **error)
|| priv->burst_start_at + RATELIMIT_INTERVAL_MSEC <= now) {
priv->burst_start_at = now;
priv->burst_count = 1;
- nm_clear_g_source(&priv->burst_retry_timeout_id);
+ nm_clear_g_source_inst(&priv->burst_retry_timeout_source);
_LOGT("rate-limit: start burst interval of %d seconds %s",
RATELIMIT_INTERVAL_MSEC / 1000,
force_start ? " (force)" : "");
} else if (priv->burst_count < RATELIMIT_BURST) {
- nm_assert(priv->burst_retry_timeout_id == 0);
+ nm_assert(!priv->burst_retry_timeout_source);
priv->burst_count++;
_LOGT("rate-limit: %u try within burst interval of %d seconds",
(guint) priv->burst_count,
RATELIMIT_INTERVAL_MSEC / 1000);
} else {
- if (priv->burst_retry_timeout_id == 0) {
+ if (!priv->burst_retry_timeout_source) {
_LOGW("dnsmasq dies and gets respawned too quickly. Back off. Something is very wrong");
- priv->burst_retry_timeout_id =
- g_timeout_add_seconds((2 * RATELIMIT_INTERVAL_MSEC) / 1000,
- _burst_retry_timeout_cb,
- self);
+ priv->burst_retry_timeout_source =
+ nm_g_timeout_add_seconds_source((2 * RATELIMIT_INTERVAL_MSEC) / 1000,
+ _burst_retry_timeout_cb,
+ self);
} else
_LOGT("rate-limit: currently rate-limited from restart");
return TRUE;
}
- priv->main_timeout_id = g_timeout_add(10000, spawn_timeout_cb, self);
+ priv->main_timeout_source = nm_g_timeout_add_source(10000, spawn_timeout_cb, self);
priv->main_cancellable = g_cancellable_new();
_gl_pid_spawn(dm_binary, priv->main_cancellable, spawn_notify, self);
+
+ _update_pending_maybe_changed(self);
return TRUE;
}
@@ -1136,8 +1207,11 @@ update(NMDnsPlugin *plugin,
nm_clear_pointer(&priv->set_server_ex_args, g_variant_unref);
priv->set_server_ex_args =
g_variant_ref_sink(create_update_args(self, global_config, ip_data_lst_head, hostdomain));
+ priv->set_server_ex_args_dirty = TRUE;
send_dnsmasq_update(self);
+
+ _update_pending_maybe_changed(self);
return TRUE;
}
@@ -1151,11 +1225,13 @@ stop(NMDnsPlugin *plugin)
priv->is_stopped = TRUE;
priv->burst_start_at = 0;
- nm_clear_g_source(&priv->burst_retry_timeout_id);
+ nm_clear_g_source_inst(&priv->burst_retry_timeout_source);
/* Cancelling the cancellable will also terminate the
* process (in the background). */
_main_cleanup(self, FALSE);
+
+ _update_pending_maybe_changed(self);
}
/*****************************************************************************/
@@ -1178,7 +1254,7 @@ dispose(GObject *object)
priv->is_stopped = TRUE;
- nm_clear_g_source(&priv->burst_retry_timeout_id);
+ nm_clear_g_source_inst(&priv->burst_retry_timeout_source);
_main_cleanup(self, FALSE);
@@ -1197,8 +1273,9 @@ nm_dns_dnsmasq_class_init(NMDnsDnsmasqClass *dns_class)
object_class->dispose = dispose;
- plugin_class->plugin_name = "dnsmasq";
- plugin_class->is_caching = TRUE;
- plugin_class->stop = stop;
- plugin_class->update = update;
+ plugin_class->plugin_name = "dnsmasq";
+ plugin_class->is_caching = TRUE;
+ plugin_class->stop = stop;
+ plugin_class->update = update;
+ plugin_class->get_update_pending = get_update_pending;
}
diff --git a/src/core/dns/nm-dns-manager.c b/src/core/dns/nm-dns-manager.c
index 566f3d6626..afda300bd2 100644
--- a/src/core/dns/nm-dns-manager.c
+++ b/src/core/dns/nm-dns-manager.c
@@ -35,7 +35,6 @@
#include "nm-dns-dnsmasq.h"
#include "nm-dns-plugin.h"
#include "nm-dns-systemd-resolved.h"
-#include "nm-dns-unbound.h"
#include "nm-ip-config.h"
#include "nm-l3-config-data.h"
#include "nm-manager.h"
@@ -57,6 +56,8 @@
#define HAS_NETCONFIG 1
#endif
+#define UPDATE_PENDING_UNBLOCK_TIMEOUT_MSEC 5000
+
/*****************************************************************************/
typedef enum { SR_SUCCESS, SR_NOTFOUND, SR_ERROR } SpawnResult;
@@ -78,7 +79,11 @@ enum {
LAST_SIGNAL
};
-NM_GOBJECT_PROPERTIES_DEFINE(NMDnsManager, PROP_MODE, PROP_RC_MANAGER, PROP_CONFIGURATION, );
+NM_GOBJECT_PROPERTIES_DEFINE(NMDnsManager,
+ PROP_MODE,
+ PROP_RC_MANAGER,
+ PROP_CONFIGURATION,
+ PROP_UPDATE_PENDING, );
static guint signals[LAST_SIGNAL] = {0};
@@ -89,6 +94,11 @@ typedef struct {
CList ip_data_lst_head;
GVariant *config_variant;
+ /* A DNS plugin should not be marked as pending indefinitely.
+ * We are only blocked if "update_pending" is TRUE and we have
+ * "update_pending_unblock" timer ticking. */
+ GSource *update_pending_unblock;
+
bool ip_data_lst_need_sort : 1;
bool configs_lst_need_sort : 1;
@@ -98,6 +108,8 @@ typedef struct {
bool config_changed : 1;
+ bool update_pending : 1;
+
char *hostdomain;
guint updates_queue;
@@ -109,6 +121,9 @@ typedef struct {
NMDnsPlugin *sd_resolve_plugin;
NMDnsPlugin *plugin;
+ gulong update_changed_signal_id_sd;
+ gulong update_changed_signal_id;
+
NMConfig *config;
struct {
@@ -137,28 +152,23 @@ NM_DEFINE_SINGLETON_GETTER(NMDnsManager, nm_dns_manager_get, NM_TYPE_DNS_MANAGER
#define _NMLOG_PREFIX_NAME "dns-mgr"
#define _NMLOG_DOMAIN LOGD_DNS
-#define _NMLOG(level, ...) \
- G_STMT_START \
- { \
- const NMLogLevel __level = (level); \
- \
- if (nm_logging_enabled(__level, _NMLOG_DOMAIN)) { \
- char __prefix[20]; \
- const NMDnsManager *const __self = (self); \
- \
- _nm_log(__level, \
- _NMLOG_DOMAIN, \
- 0, \
- NULL, \
- NULL, \
- "%s%s: " _NM_UTILS_MACRO_FIRST(__VA_ARGS__), \
- _NMLOG_PREFIX_NAME, \
- ((!__self || __self == singleton_instance) \
- ? "" \
- : nm_sprintf_buf(__prefix, "[%p]", __self)) \
- _NM_UTILS_MACRO_REST(__VA_ARGS__)); \
- } \
- } \
+#define _NMLOG(level, ...) \
+ G_STMT_START \
+ { \
+ const NMLogLevel __level = (level); \
+ \
+ if (nm_logging_enabled(__level, _NMLOG_DOMAIN)) { \
+ _nm_unused const NMDnsManager *const __self = (self); \
+ \
+ _nm_log(__level, \
+ _NMLOG_DOMAIN, \
+ 0, \
+ NULL, \
+ NULL, \
+ "%s: " _NM_UTILS_MACRO_FIRST(__VA_ARGS__), \
+ _NMLOG_PREFIX_NAME _NM_UTILS_MACRO_REST(__VA_ARGS__)); \
+ } \
+ } \
G_STMT_END
/*****************************************************************************/
@@ -207,6 +217,85 @@ static NM_UTILS_LOOKUP_STR_DEFINE(
/*****************************************************************************/
+static gboolean
+_update_pending_detect(NMDnsManager *self)
+{
+ NMDnsManagerPrivate *priv = NM_DNS_MANAGER_GET_PRIVATE(self);
+
+ if (priv->plugin && nm_dns_plugin_get_update_pending(priv->plugin))
+ return TRUE;
+ if (priv->sd_resolve_plugin && nm_dns_plugin_get_update_pending(priv->sd_resolve_plugin))
+ return TRUE;
+ return FALSE;
+}
+
+static gboolean
+_update_pending_unblock_cb(gpointer user_data)
+{
+ NMDnsManager *self = user_data;
+ NMDnsManagerPrivate *priv = NM_DNS_MANAGER_GET_PRIVATE(self);
+
+ nm_assert(priv->update_pending);
+ nm_assert(priv->update_pending_unblock);
+ nm_assert(_update_pending_detect(self));
+
+ nm_clear_g_source_inst(&priv->update_pending_unblock);
+
+ _LOGW(
+ "update-pending changed: DNS plugin did not become ready again. Assume something is wrong");
+
+ _notify(self, PROP_UPDATE_PENDING);
+ return G_SOURCE_CONTINUE;
+}
+
+static void
+_update_pending_maybe_changed(NMDnsManager *self)
+{
+ NMDnsManagerPrivate *priv = NM_DNS_MANAGER_GET_PRIVATE(self);
+ gboolean update_pending;
+
+ update_pending = _update_pending_detect(self);
+ if (priv->update_pending == update_pending)
+ return;
+
+ if (update_pending) {
+ nm_assert(!priv->update_pending_unblock);
+ priv->update_pending_unblock = nm_g_timeout_add_source(UPDATE_PENDING_UNBLOCK_TIMEOUT_MSEC,
+ _update_pending_unblock_cb,
+ self);
+ } else
+ nm_clear_g_source_inst(&priv->update_pending_unblock);
+
+ priv->update_pending = update_pending;
+ _LOGD("update-pending changed: %spending", update_pending ? "" : "not ");
+ _notify(self, PROP_UPDATE_PENDING);
+}
+
+static void
+_update_pending_changed_cb(NMDnsPlugin *plugin, gboolean update_pending, NMDnsManager *self)
+{
+ _update_pending_maybe_changed(self);
+}
+
+gboolean
+nm_dns_manager_get_update_pending(NMDnsManager *self)
+{
+ NMDnsManagerPrivate *priv;
+
+ g_return_val_if_fail(NM_IS_DNS_MANAGER(self), FALSE);
+
+ priv = NM_DNS_MANAGER_GET_PRIVATE(self);
+ nm_assert(priv->update_pending == _update_pending_detect(self));
+ nm_assert(priv->update_pending || !priv->update_pending_unblock);
+
+ /* update-pending can only be TRUE for a certain time (before we assume
+ * something is really wrong with the plugin). That is, as long as
+ * update_pending_unblock is ticking. */
+ return !!priv->update_pending_unblock;
+}
+
+/*****************************************************************************/
+
static int
_dns_config_ip_data_get_dns_priority1(const NML3ConfigData *l3cd, int addr_family)
{
@@ -2120,6 +2209,7 @@ _clear_plugin(NMDnsManager *self)
nm_clear_g_source(&priv->plugin_ratelimit.timer);
if (priv->plugin) {
+ nm_clear_g_signal_handler(priv->plugin, &priv->update_changed_signal_id);
nm_dns_plugin_stop(priv->plugin);
g_clear_object(&priv->plugin);
return TRUE;
@@ -2127,6 +2217,20 @@ _clear_plugin(NMDnsManager *self)
return FALSE;
}
+static gboolean
+_clear_sd_resolved_plugin(NMDnsManager *self)
+{
+ NMDnsManagerPrivate *priv = NM_DNS_MANAGER_GET_PRIVATE(self);
+
+ if (priv->sd_resolve_plugin) {
+ nm_clear_g_signal_handler(priv->sd_resolve_plugin, &priv->update_changed_signal_id_sd);
+ nm_dns_plugin_stop(priv->sd_resolve_plugin);
+ g_clear_object(&priv->sd_resolve_plugin);
+ return TRUE;
+ }
+ return FALSE;
+}
+
static NMDnsManagerResolvConfManager
_check_resconf_immutable(NMDnsManagerResolvConfManager rc_manager)
{
@@ -2313,16 +2417,14 @@ again:
priv->plugin = nm_dns_dnsmasq_new();
plugin_changed = TRUE;
}
- } else if (nm_streq0(mode, "unbound")) {
- if (force_reload_plugin || !NM_IS_DNS_UNBOUND(priv->plugin)) {
- _clear_plugin(self);
- priv->plugin = nm_dns_unbound_new();
- plugin_changed = TRUE;
- }
} else {
if (!NM_IN_STRSET(mode, "none", "default")) {
- if (mode)
- _LOGW("init: unknown dns mode '%s'", mode);
+ if (mode) {
+ if (nm_streq(mode, "unbound"))
+ _LOGW("init: ns mode 'unbound' was removed. Update your configuration");
+ else
+ _LOGW("init: unknown dns mode '%s'", mode);
+ }
mode = "default";
}
if (_clear_plugin(self))
@@ -2359,7 +2461,7 @@ again:
priv->sd_resolve_plugin = nm_dns_systemd_resolved_new();
systemd_resolved_changed = TRUE;
}
- } else if (nm_clear_g_object(&priv->sd_resolve_plugin))
+ } else if (_clear_sd_resolved_plugin(self))
systemd_resolved_changed = TRUE;
g_object_freeze_notify(G_OBJECT(self));
@@ -2390,6 +2492,23 @@ again:
""));
}
+ if (plugin_changed && priv->plugin && priv->update_changed_signal_id == 0) {
+ priv->update_changed_signal_id = g_signal_connect(priv->plugin,
+ NM_DNS_PLUGIN_UPDATE_PENDING_CHANGED,
+ G_CALLBACK(_update_pending_changed_cb),
+ self);
+ }
+
+ if (systemd_resolved_changed && priv->sd_resolve_plugin
+ && priv->update_changed_signal_id_sd == 0) {
+ priv->update_changed_signal_id_sd = g_signal_connect(priv->sd_resolve_plugin,
+ NM_DNS_PLUGIN_UPDATE_PENDING_CHANGED,
+ G_CALLBACK(_update_pending_changed_cb),
+ self);
+ }
+
+ _update_pending_maybe_changed(self);
+
g_object_thaw_notify(G_OBJECT(self));
}
@@ -2594,6 +2713,9 @@ get_property(GObject *object, guint prop_id, GValue *value, GParamSpec *pspec)
case PROP_CONFIGURATION:
g_value_set_variant(value, _get_config_variant(self));
break;
+ case PROP_UPDATE_PENDING:
+ g_value_set_boolean(value, nm_dns_manager_get_update_pending(self));
+ break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
break;
@@ -2641,9 +2763,11 @@ dispose(GObject *object)
if (priv->config)
g_signal_handlers_disconnect_by_func(priv->config, config_changed_cb, self);
- g_clear_object(&priv->sd_resolve_plugin);
+ _clear_sd_resolved_plugin(self);
_clear_plugin(self);
+ nm_clear_g_source_inst(&priv->update_pending_unblock);
+
c_list_for_each_entry_safe (ip_data, ip_data_safe, &priv->ip_data_lst_head, ip_data_lst)
_dns_config_ip_data_free(ip_data);
@@ -2719,6 +2843,13 @@ nm_dns_manager_class_init(NMDnsManagerClass *klass)
NULL,
G_PARAM_READABLE | G_PARAM_STATIC_STRINGS);
+ obj_properties[PROP_UPDATE_PENDING] =
+ g_param_spec_boolean(NM_DNS_MANAGER_UPDATE_PENDING,
+ "",
+ "",
+ FALSE,
+ G_PARAM_READABLE | G_PARAM_STATIC_STRINGS);
+
g_object_class_install_properties(object_class, _PROPERTY_ENUMS_LAST, obj_properties);
signals[CONFIG_CHANGED] = g_signal_new(NM_DNS_MANAGER_CONFIG_CHANGED,
diff --git a/src/core/dns/nm-dns-manager.h b/src/core/dns/nm-dns-manager.h
index c30d4b3ac6..210f9f6cb3 100644
--- a/src/core/dns/nm-dns-manager.h
+++ b/src/core/dns/nm-dns-manager.h
@@ -80,9 +80,10 @@ typedef struct _NMDnsConfigData {
(G_TYPE_INSTANCE_GET_CLASS((o), NM_TYPE_DNS_MANAGER, NMDnsManagerClass))
/* properties */
-#define NM_DNS_MANAGER_MODE "mode"
-#define NM_DNS_MANAGER_RC_MANAGER "rc-manager"
-#define NM_DNS_MANAGER_CONFIGURATION "configuration"
+#define NM_DNS_MANAGER_MODE "mode"
+#define NM_DNS_MANAGER_RC_MANAGER "rc-manager"
+#define NM_DNS_MANAGER_CONFIGURATION "configuration"
+#define NM_DNS_MANAGER_UPDATE_PENDING "update-pending"
/* internal signals */
#define NM_DNS_MANAGER_CONFIG_CHANGED "config-changed"
@@ -149,6 +150,8 @@ void nm_dns_manager_stop(NMDnsManager *self);
NMDnsPlugin *nm_dns_manager_get_systemd_resolved(NMDnsManager *self);
+gboolean nm_dns_manager_get_update_pending(NMDnsManager *self);
+
/*****************************************************************************/
char *nmtst_dns_create_resolv_conf(const char *const *searches,
diff --git a/src/core/dns/nm-dns-plugin.c b/src/core/dns/nm-dns-plugin.c
index 847d783996..41a0dbc138 100644
--- a/src/core/dns/nm-dns-plugin.c
+++ b/src/core/dns/nm-dns-plugin.c
@@ -17,11 +17,16 @@
/*****************************************************************************/
+enum {
+ UPDATE_PENDING_CHANGED,
+ LAST_SIGNAL,
+};
+
+static guint signals[LAST_SIGNAL] = {0};
+
typedef struct _NMDnsPluginPrivate {
- GPid pid;
- guint watch_id;
- char *progname;
- char *pidfile;
+ bool update_pending_inited : 1;
+ bool update_pending : 1;
} NMDnsPluginPrivate;
G_DEFINE_ABSTRACT_TYPE(NMDnsPlugin, nm_dns_plugin, G_TYPE_OBJECT)
@@ -32,26 +37,29 @@ G_DEFINE_ABSTRACT_TYPE(NMDnsPlugin, nm_dns_plugin, G_TYPE_OBJECT)
#define _NMLOG_PREFIX_NAME "dns-plugin"
#define _NMLOG_DOMAIN LOGD_DNS
-#define _NMLOG(level, ...) \
- G_STMT_START \
- { \
- const NMLogLevel __level = (level); \
- \
- if (nm_logging_enabled(__level, _NMLOG_DOMAIN)) { \
- char __prefix[20]; \
- const NMDnsPlugin *const __self = (self); \
- \
- _nm_log(__level, \
- _NMLOG_DOMAIN, \
- 0, \
- NULL, \
- NULL, \
- "%s%s: " _NM_UTILS_MACRO_FIRST(__VA_ARGS__), \
- _NMLOG_PREFIX_NAME, \
- (!__self ? "" : nm_sprintf_buf(__prefix, "[%p]", __self)) \
- _NM_UTILS_MACRO_REST(__VA_ARGS__)); \
- } \
- } \
+#define _NMLOG(level, ...) \
+ G_STMT_START \
+ { \
+ const NMLogLevel __level = (level); \
+ \
+ if (nm_logging_enabled(__level, _NMLOG_DOMAIN)) { \
+ char __prefix[20]; \
+ const NMDnsPlugin *const __self = (self); \
+ \
+ _nm_log(__level, \
+ _NMLOG_DOMAIN, \
+ 0, \
+ NULL, \
+ NULL, \
+ "%s%s: " _NM_UTILS_MACRO_FIRST(__VA_ARGS__), \
+ _NMLOG_PREFIX_NAME, \
+ (!__self ? "" \
+ : nm_sprintf_buf(__prefix, \
+ "[" NM_HASH_OBFUSCATE_PTR_FMT "]", \
+ NM_HASH_OBFUSCATE_PTR( \
+ __self))) _NM_UTILS_MACRO_REST(__VA_ARGS__)); \
+ } \
+ } \
G_STMT_END
/*****************************************************************************/
@@ -104,10 +112,109 @@ nm_dns_plugin_stop(NMDnsPlugin *self)
/*****************************************************************************/
-static void
-nm_dns_plugin_init(NMDnsPlugin *self)
-{}
+static gboolean
+_get_update_pending(NMDnsPlugin *self)
+{
+ NMDnsPluginClass *klass;
+
+ nm_assert(NM_IS_DNS_PLUGIN(self));
+
+ klass = NM_DNS_PLUGIN_GET_CLASS(self);
+ if (klass->get_update_pending) {
+ if (klass->get_update_pending(self))
+ return TRUE;
+ }
+ return FALSE;
+}
+
+gboolean
+nm_dns_plugin_get_update_pending(NMDnsPlugin *self)
+{
+ NMDnsPluginPrivate *priv;
+
+ g_return_val_if_fail(NM_IS_DNS_PLUGIN(self), FALSE);
+
+ priv = NM_DNS_PLUGIN_GET_PRIVATE(self);
+
+ /* We cache the boolean and rely on the subclass to call
+ * _nm_dns_plugin_update_pending_maybe_changed(). The subclass
+ * anyway must get it right to notify us when the value (maybe)
+ * changes. By caching the value, the subclass is free to notify
+ * even if the value did not actually change.
+ *
+ * Also, this allows the base implementation to combine multiple
+ * sources/reasons (if we need that in the future). */
+
+ if (!priv->update_pending_inited) {
+ priv->update_pending_inited = TRUE;
+ priv->update_pending = _get_update_pending(self);
+ _LOGD("[%s] update-pending changed (%spending)",
+ nm_dns_plugin_get_name(self),
+ priv->update_pending ? "" : "not ");
+ } else
+ nm_assert(priv->update_pending == _get_update_pending(self));
+
+ return priv->update_pending;
+}
+
+void
+_nm_dns_plugin_update_pending_maybe_changed(NMDnsPlugin *self)
+{
+ NMDnsPluginPrivate *priv;
+ gboolean v;
+
+ g_return_if_fail(NM_IS_DNS_PLUGIN(self));
+
+ priv = NM_DNS_PLUGIN_GET_PRIVATE(self);
+
+ v = _get_update_pending(self);
+
+ if (!priv->update_pending_inited)
+ priv->update_pending_inited = TRUE;
+ else if (priv->update_pending == v)
+ return;
+
+ priv->update_pending = v;
+
+ _LOGD("[%s] update-pending changed (%spending)",
+ nm_dns_plugin_get_name(self),
+ priv->update_pending ? "" : "not ");
+
+ g_signal_emit(self, signals[UPDATE_PENDING_CHANGED], 0, (gboolean) priv->update_pending);
+}
+
+/*****************************************************************************/
static void
-nm_dns_plugin_class_init(NMDnsPluginClass *plugin_class)
-{}
+nm_dns_plugin_init(NMDnsPlugin *self)
+{
+ NMDnsPluginPrivate *priv;
+
+ priv = G_TYPE_INSTANCE_GET_PRIVATE(self, NM_TYPE_DNS_PLUGIN, NMDnsPluginPrivate);
+
+ self->_priv = priv;
+
+ nm_assert(priv->update_pending_inited == FALSE);
+ nm_assert(priv->update_pending == FALSE);
+
+ nm_shutdown_wait_obj_register_object(self, "dns-plugin");
+}
+
+static void
+nm_dns_plugin_class_init(NMDnsPluginClass *klass)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS(klass);
+
+ g_type_class_add_private(object_class, sizeof(NMDnsPluginPrivate));
+
+ signals[UPDATE_PENDING_CHANGED] = g_signal_new(NM_DNS_PLUGIN_UPDATE_PENDING_CHANGED,
+ G_OBJECT_CLASS_TYPE(klass),
+ G_SIGNAL_RUN_FIRST,
+ 0,
+ NULL,
+ NULL,
+ NULL,
+ G_TYPE_NONE,
+ 1,
+ G_TYPE_BOOLEAN);
+}
diff --git a/src/core/dns/nm-dns-plugin.h b/src/core/dns/nm-dns-plugin.h
index f9c424abfa..24d6083be1 100644
--- a/src/core/dns/nm-dns-plugin.h
+++ b/src/core/dns/nm-dns-plugin.h
@@ -19,8 +19,13 @@
#define NM_DNS_PLUGIN_GET_CLASS(obj) \
(G_TYPE_INSTANCE_GET_CLASS((obj), NM_TYPE_DNS_PLUGIN, NMDnsPluginClass))
+#define NM_DNS_PLUGIN_UPDATE_PENDING_CHANGED "update-pending-changed"
+
+struct _NMDnsPluginPrivate;
+
typedef struct {
- GObject parent;
+ GObject parent;
+ struct _NMDnsPluginPrivate *_priv;
} NMDnsPlugin;
typedef struct {
@@ -39,6 +44,8 @@ typedef struct {
void (*stop)(NMDnsPlugin *self);
+ gboolean (*get_update_pending)(NMDnsPlugin *self);
+
const char *plugin_name;
/* Types should set to TRUE if they start a local caching nameserver
@@ -63,4 +70,8 @@ gboolean nm_dns_plugin_update(NMDnsPlugin *self,
void nm_dns_plugin_stop(NMDnsPlugin *self);
+gboolean nm_dns_plugin_get_update_pending(NMDnsPlugin *self);
+
+void _nm_dns_plugin_update_pending_maybe_changed(NMDnsPlugin *self);
+
#endif /* __NM_DNS_PLUGIN_H__ */
diff --git a/src/core/dns/nm-dns-systemd-resolved.c b/src/core/dns/nm-dns-systemd-resolved.c
index c4993884d2..ac6fe5dedb 100644
--- a/src/core/dns/nm-dns-systemd-resolved.c
+++ b/src/core/dns/nm-dns-systemd-resolved.c
@@ -40,8 +40,8 @@ static const char *const DBUS_OP_SET_LINK_DNS_OVER_TLS = "SetLinkDNSOverTLS";
/*****************************************************************************/
typedef struct {
- int ifindex;
- CList configs_lst_head;
+ int ifindex;
+ GPtrArray *ip_data_list;
} InterfaceConfig;
typedef struct {
@@ -50,6 +50,7 @@ typedef struct {
GVariant *argument;
NMDnsSystemdResolved *self;
int ifindex;
+ int ref_count;
} RequestItem;
struct _NMDnsSystemdResolvedResolveHandle {
@@ -82,10 +83,13 @@ typedef struct {
char *dbus_owner;
CList handle_lst_head;
guint name_owner_changed_id;
+ guint n_pending;
bool send_updates_warn_ratelimited : 1;
bool try_start_blocked : 1;
+ bool stopped : 1;
bool dbus_initied : 1;
bool send_updates_waiting : 1;
+ bool update_pending : 1;
/* These two variables ensure that the log is not spammed with
* API (not) supported messages.
* They can be removed when no distro uses systemd-resolved < v240 anymore
@@ -106,7 +110,7 @@ struct _NMDnsSystemdResolvedClass {
G_DEFINE_TYPE(NMDnsSystemdResolved, nm_dns_systemd_resolved, NM_TYPE_DNS_PLUGIN)
#define NM_DNS_SYSTEMD_RESOLVED_GET_PRIVATE(self) \
- _NM_GET_PRIVATE(self, NMDnsSystemdResolved, NM_IS_DNS_SYSTEMD_RESOLVED)
+ _NM_GET_PRIVATE(self, NMDnsSystemdResolved, NM_IS_DNS_SYSTEMD_RESOLVED, NMDnsPlugin)
/*****************************************************************************/
@@ -146,10 +150,88 @@ static void _resolve_start(NMDnsSystemdResolved *self, NMDnsSystemdResolvedResol
/*****************************************************************************/
-static void
-_request_item_free(RequestItem *request_item)
+static gboolean
+_update_pending_detect(NMDnsSystemdResolved *self)
{
- c_list_unlink_stale(&request_item->request_queue_lst);
+ NMDnsSystemdResolvedPrivate *priv = NM_DNS_SYSTEMD_RESOLVED_GET_PRIVATE(self);
+
+ if (priv->n_pending > 0) {
+ /* we have pending calls. We definitely want to wait for them to complete. */
+ return TRUE;
+ }
+ if (!priv->dbus_initied) {
+ if (!priv->dbus_connection)
+ return FALSE;
+ /* D-Bus not yet initialized (and we don't know the name owner yet). Pending. */
+ return TRUE;
+ }
+ if (priv->try_start_timeout_source) {
+ /* We are waiting to D-Bus activate resolved. Pending. */
+ return TRUE;
+ }
+ if (priv->try_start_blocked) {
+ /* We earlier tried to start resolved, but are rate limited. We are not pending an update
+ * (that we expect to complete any time soon). */
+ return FALSE;
+ }
+ if (priv->send_updates_waiting) {
+ /* we wait to send updates. We are pending. */
+ return TRUE;
+ }
+ return FALSE;
+}
+
+static void
+_update_pending_maybe_changed(NMDnsSystemdResolved *self)
+{
+ NMDnsSystemdResolvedPrivate *priv = NM_DNS_SYSTEMD_RESOLVED_GET_PRIVATE(self);
+ gboolean update_pending;
+
+ /* Important: we need to make sure that we call _update_pending_maybe_changed(), when
+ * the state changes. */
+
+ update_pending = _update_pending_detect(self);
+ if (priv->update_pending != update_pending) {
+ priv->update_pending = update_pending;
+ _nm_dns_plugin_update_pending_maybe_changed(NM_DNS_PLUGIN(self));
+ }
+}
+
+static gboolean
+get_update_pending(NMDnsPlugin *plugin)
+{
+ NMDnsSystemdResolved *self = NM_DNS_SYSTEMD_RESOLVED(plugin);
+ NMDnsSystemdResolvedPrivate *priv = NM_DNS_SYSTEMD_RESOLVED_GET_PRIVATE(self);
+
+ nm_assert(priv->update_pending == _update_pending_detect(self));
+ return priv->update_pending;
+}
+
+/*****************************************************************************/
+
+static RequestItem *
+_request_item_ref(RequestItem *request_item)
+{
+ nm_assert(request_item);
+ nm_assert(request_item->ref_count > 0);
+ nm_assert(request_item->ref_count < G_MAXINT);
+ nm_assert(!c_list_is_empty(&request_item->request_queue_lst));
+
+ request_item->ref_count++;
+ return request_item;
+}
+
+static void
+_request_item_unref(RequestItem *request_item)
+{
+ nm_assert(request_item);
+ nm_assert(request_item->ref_count > 0);
+
+ if (--request_item->ref_count > 0)
+ return;
+
+ nm_assert(c_list_is_empty(&request_item->request_queue_lst));
+
g_variant_unref(request_item->argument);
nm_g_slice_free(request_item);
}
@@ -165,6 +247,7 @@ _request_item_append(NMDnsSystemdResolved *self,
request_item = g_slice_new(RequestItem);
*request_item = (RequestItem){
+ .ref_count = 1,
.operation = operation,
.argument = g_variant_ref_sink(argument),
.self = self,
@@ -178,8 +261,8 @@ _request_item_append(NMDnsSystemdResolved *self,
static void
_interface_config_free(InterfaceConfig *config)
{
- nm_c_list_elem_free_all(&config->configs_lst_head, NULL);
- g_slice_free(InterfaceConfig, config);
+ nm_g_ptr_array_unref(config->ip_data_list);
+ nm_g_slice_free(config);
}
static void
@@ -191,42 +274,48 @@ call_done(GObject *source, GAsyncResult *r, gpointer user_data)
NMDnsSystemdResolvedPrivate *priv;
RequestItem *request_item;
NMLogLevel log_level;
-
- v = g_dbus_connection_call_finish(G_DBUS_CONNECTION(source), r, &error);
- if (nm_utils_error_is_cancelled(error))
- return;
+ const char *operation;
+ int ifindex;
request_item = user_data;
self = request_item->self;
- priv = NM_DNS_SYSTEMD_RESOLVED_GET_PRIVATE(self);
+ operation = request_item->operation;
+ ifindex = request_item->ifindex;
+ _request_item_unref(request_item);
+
+ priv = NM_DNS_SYSTEMD_RESOLVED_GET_PRIVATE(self);
+
+ v = g_dbus_connection_call_finish(G_DBUS_CONNECTION(source), r, &error);
+ if (nm_utils_error_is_cancelled(error))
+ goto out_dec_pending;
if (v) {
- if (request_item->operation == DBUS_OP_SET_LINK_DEFAULT_ROUTE
+ if (operation == DBUS_OP_SET_LINK_DEFAULT_ROUTE
&& priv->has_link_default_route == NM_TERNARY_DEFAULT) {
priv->has_link_default_route = NM_TERNARY_TRUE;
_LOGD("systemd-resolved support for SetLinkDefaultRoute(): API supported");
}
- if (request_item->operation == DBUS_OP_SET_LINK_DNS_OVER_TLS
+ if (operation == DBUS_OP_SET_LINK_DNS_OVER_TLS
&& priv->has_link_dns_over_tls == NM_TERNARY_DEFAULT) {
priv->has_link_dns_over_tls = NM_TERNARY_TRUE;
_LOGD("systemd-resolved support for SetLinkDNSOverTLS(): API supported");
}
priv->send_updates_warn_ratelimited = FALSE;
- return;
+ goto out_dec_pending;
}
if (nm_g_error_matches(error, G_DBUS_ERROR, G_DBUS_ERROR_UNKNOWN_METHOD)) {
if (priv->has_link_default_route == NM_TERNARY_DEFAULT
- && request_item->operation == DBUS_OP_SET_LINK_DEFAULT_ROUTE) {
+ && operation == DBUS_OP_SET_LINK_DEFAULT_ROUTE) {
priv->has_link_default_route = NM_TERNARY_FALSE;
_LOGD("systemd-resolved support for SetLinkDefaultRoute(): API not supported");
}
if (priv->has_link_dns_over_tls == NM_TERNARY_DEFAULT
- && request_item->operation == DBUS_OP_SET_LINK_DNS_OVER_TLS) {
+ && operation == DBUS_OP_SET_LINK_DNS_OVER_TLS) {
priv->has_link_dns_over_tls = NM_TERNARY_FALSE;
_LOGD("systemd-resolved support for SetLinkDNSOverTLS(): API not supported");
}
- return;
+ goto out_dec_pending;
}
log_level = LOGL_DEBUG;
@@ -234,18 +323,25 @@ call_done(GObject *source, GAsyncResult *r, gpointer user_data)
priv->send_updates_warn_ratelimited = TRUE;
log_level = LOGL_WARN;
}
- _NMLOG(log_level,
- "send-updates %s@%d failed: %s",
- request_item->operation,
- request_item->ifindex,
- error->message);
+ _NMLOG(log_level, "send-updates %s@%d failed: %s", operation, ifindex, error->message);
+
+out_dec_pending:
+ nm_assert(priv->n_pending > 0);
+ if (--priv->n_pending <= 0) {
+ _update_pending_maybe_changed(self);
+ /* We keep @self alive while pending operations are in progress. It's simpler
+ * to implement. But this requires that we implement "stop()" signal to cancel
+ * all pending requests. Cancelling is necessary, because during shutdown,
+ * we must wrap up fast, and not hang an undefined amount time. */
+ g_object_unref(self);
+ }
}
static gboolean
-update_add_ip_config(NMDnsSystemdResolved *self,
- GVariantBuilder *dns,
- GVariantBuilder *domains,
- NMDnsConfigIPData *ip_data)
+update_add_ip_config(NMDnsSystemdResolved *self,
+ GVariantBuilder *dns,
+ GVariantBuilder *domains,
+ const NMDnsConfigIPData *ip_data)
{
gsize addr_size;
guint n;
@@ -258,8 +354,12 @@ update_add_ip_config(NMDnsSystemdResolved *self,
addr_size = nm_utils_addr_family_to_size(ip_data->addr_family);
if ((!ip_data->domains.search || !ip_data->domains.search[0])
- && !ip_data->domains.has_default_route_exclusive && !ip_data->domains.has_default_route)
+ && !ip_data->domains.has_default_route_exclusive && !ip_data->domains.has_default_route) {
+ /* we have no search domain (which systemd-resolved uses to routing the request), but
+ * also the "DefaultRoute" is not set on the interface. This setting has no effect and
+ * gets ignored. */
return FALSE;
+ }
nameservers = nm_l3_config_data_get_nameservers(ip_data->l3cd, ip_data->addr_family, &n);
for (i = 0; i < n; i++) {
@@ -295,23 +395,28 @@ free_pending_updates(NMDnsSystemdResolved *self)
NMDnsSystemdResolvedPrivate *priv = NM_DNS_SYSTEMD_RESOLVED_GET_PRIVATE(self);
RequestItem *request_item;
- while ((request_item =
- c_list_first_entry(&priv->request_queue_lst_head, RequestItem, request_queue_lst)))
- _request_item_free(request_item);
+ while (
+ (request_item =
+ c_list_first_entry(&priv->request_queue_lst_head, RequestItem, request_queue_lst))) {
+ c_list_unlink(&request_item->request_queue_lst);
+ _request_item_unref(request_item);
+ }
}
static gboolean
-prepare_one_interface(NMDnsSystemdResolved *self, InterfaceConfig *ic)
+prepare_one_interface(NMDnsSystemdResolved *self, const InterfaceConfig *ic)
{
GVariantBuilder dns;
GVariantBuilder domains;
- NMCListElem *elem;
- NMSettingConnectionMdns mdns = NM_SETTING_CONNECTION_MDNS_DEFAULT;
- NMSettingConnectionLlmnr llmnr = NM_SETTING_CONNECTION_LLMNR_DEFAULT;
- NMSettingConnectionDnsOverTls dns_over_tls = NM_SETTING_CONNECTION_DNS_OVER_TLS_DEFAULT;
- const char *mdns_arg = NULL, *llmnr_arg = NULL, *dns_over_tls_arg = NULL;
+ NMSettingConnectionMdns mdns = NM_SETTING_CONNECTION_MDNS_DEFAULT;
+ NMSettingConnectionLlmnr llmnr = NM_SETTING_CONNECTION_LLMNR_DEFAULT;
+ NMSettingConnectionDnsOverTls dns_over_tls = NM_SETTING_CONNECTION_DNS_OVER_TLS_DEFAULT;
+ const char *mdns_arg = NULL;
+ const char *llmnr_arg = NULL;
+ const char *dns_over_tls_arg = NULL;
gboolean has_config = FALSE;
gboolean has_default_route = FALSE;
+ guint i;
g_variant_builder_init(&dns, G_VARIANT_TYPE("(ia(iay))"));
g_variant_builder_add(&dns, "i", ic->ifindex);
@@ -321,18 +426,22 @@ prepare_one_interface(NMDnsSystemdResolved *self, InterfaceConfig *ic)
g_variant_builder_add(&domains, "i", ic->ifindex);
g_variant_builder_open(&domains, G_VARIANT_TYPE("a(sb)"));
- c_list_for_each_entry (elem, &ic->configs_lst_head, lst) {
- NMDnsConfigIPData *ip_data = elem->data;
+ if (ic->ip_data_list) {
+ for (i = 0; i < ic->ip_data_list->len; i++) {
+ const NMDnsConfigIPData *ip_data = ic->ip_data_list->pdata[i];
- has_config |= update_add_ip_config(self, &dns, &domains, ip_data);
+ if (update_add_ip_config(self, &dns, &domains, ip_data))
+ has_config = TRUE;
- if (ip_data->domains.has_default_route)
- has_default_route = TRUE;
+ if (ip_data->domains.has_default_route)
+ has_default_route = TRUE;
- if (NM_IS_IPv4(ip_data->addr_family)) {
- mdns = NM_MAX(mdns, nm_l3_config_data_get_mdns(ip_data->l3cd));
- llmnr = NM_MAX(llmnr, nm_l3_config_data_get_llmnr(ip_data->l3cd));
- dns_over_tls = NM_MAX(dns_over_tls, nm_l3_config_data_get_dns_over_tls(ip_data->l3cd));
+ if (NM_IS_IPv4(ip_data->addr_family)) {
+ mdns = NM_MAX(mdns, nm_l3_config_data_get_mdns(ip_data->l3cd));
+ llmnr = NM_MAX(llmnr, nm_l3_config_data_get_llmnr(ip_data->l3cd));
+ dns_over_tls =
+ NM_MAX(dns_over_tls, nm_l3_config_data_get_dns_over_tls(ip_data->l3cd));
+ }
}
}
@@ -439,6 +548,7 @@ again:
goto again;
}
+ _update_pending_maybe_changed(self);
return G_SOURCE_CONTINUE;
}
@@ -447,6 +557,9 @@ ensure_resolved_running(NMDnsSystemdResolved *self)
{
NMDnsSystemdResolvedPrivate *priv = NM_DNS_SYSTEMD_RESOLVED_GET_PRIVATE(self);
+ if (priv->stopped)
+ return NM_TERNARY_FALSE;
+
if (!priv->dbus_initied)
return NM_TERNARY_DEFAULT;
@@ -469,6 +582,7 @@ ensure_resolved_running(NMDnsSystemdResolved *self)
NULL,
NULL,
NULL);
+ _update_pending_maybe_changed(self);
return NM_TERNARY_DEFAULT;
}
@@ -523,6 +637,12 @@ send_updates(NMDnsSystemdResolved *self)
request_item->operation,
(ss = g_variant_print(request_item->argument, FALSE)));
+ if (priv->n_pending++ == 0) {
+ /* We are inside send_updates(). All callers are already calling
+ * _update_pending_maybe_changed() afterwards. */
+ g_object_ref(self);
+ }
+
g_dbus_connection_call(priv->dbus_connection,
priv->dbus_owner,
SYSTEMD_RESOLVED_DBUS_PATH,
@@ -534,7 +654,7 @@ send_updates(NMDnsSystemdResolved *self)
-1,
priv->cancellable,
call_done,
- request_item);
+ _request_item_ref(request_item));
}
start_resolve:
@@ -554,43 +674,54 @@ update(NMDnsPlugin *plugin,
const char *hostdomain,
GError **error)
{
- NMDnsSystemdResolved *self = NM_DNS_SYSTEMD_RESOLVED(plugin);
- NMDnsSystemdResolvedPrivate *priv = NM_DNS_SYSTEMD_RESOLVED_GET_PRIVATE(self);
- gs_unref_hashtable GHashTable *interfaces = NULL;
- gs_free gpointer *interfaces_keys = NULL;
+ NMDnsSystemdResolved *self = NM_DNS_SYSTEMD_RESOLVED(plugin);
+ NMDnsSystemdResolvedPrivate *priv = NM_DNS_SYSTEMD_RESOLVED_GET_PRIVATE(self);
+ gs_unref_hashtable GHashTable *interfaces = NULL;
+ const NMUtilsNamedValue *interfaces_arr;
+ NMUtilsNamedValue interfaces_arr_stack[50];
+ gs_free NMUtilsNamedValue *interfaces_arr_heap = NULL;
guint interfaces_len;
- int ifindex;
gpointer pointer;
NMDnsConfigIPData *ip_data;
GHashTableIter iter;
+ gs_unref_array GArray *dirty_array = NULL;
guint i;
+ nm_assert(!priv->stopped);
+
+ /* Group configs by ifindex/interfaces. */
interfaces =
g_hash_table_new_full(nm_direct_hash, NULL, NULL, (GDestroyNotify) _interface_config_free);
c_list_for_each_entry (ip_data, ip_data_lst_head, ip_data_lst) {
- InterfaceConfig *ic = NULL;
+ InterfaceConfig *ic = NULL;
+ int ifindex = ip_data->data->ifindex;
- ifindex = ip_data->data->ifindex;
nm_assert(ifindex == nm_l3_config_data_get_ifindex(ip_data->l3cd));
ic = g_hash_table_lookup(interfaces, GINT_TO_POINTER(ifindex));
if (!ic) {
- ic = g_slice_new(InterfaceConfig);
- ic->ifindex = ifindex;
- c_list_init(&ic->configs_lst_head);
+ ic = g_slice_new(InterfaceConfig);
+ *ic = (InterfaceConfig){
+ .ifindex = ifindex,
+ .ip_data_list = g_ptr_array_sized_new(4),
+ };
g_hash_table_insert(interfaces, GINT_TO_POINTER(ifindex), ic);
}
- c_list_link_tail(&ic->configs_lst_head, &nm_c_list_elem_new_stale(ip_data)->lst);
+ g_ptr_array_add(ic->ip_data_list, ip_data);
}
free_pending_updates(self);
- interfaces_keys =
- nm_utils_hash_keys_to_array(interfaces, nm_cmp_int2ptr_p_with_data, NULL, &interfaces_len);
+ interfaces_arr = nm_utils_hash_to_array_with_buffer(interfaces,
+ &interfaces_len,
+ nm_cmp_int2ptr_p_with_data,
+ NULL,
+ interfaces_arr_stack,
+ &interfaces_arr_heap);
for (i = 0; i < interfaces_len; i++) {
- InterfaceConfig *ic = g_hash_table_lookup(interfaces, GINT_TO_POINTER(interfaces_keys[i]));
+ const InterfaceConfig *ic = interfaces_arr[i].value_ptr;
if (prepare_one_interface(self, ic))
g_hash_table_add(priv->dirty_interfaces, GINT_TO_POINTER(ic->ifindex));
@@ -602,23 +733,38 @@ update(NMDnsPlugin *plugin,
* resolved, and the current update doesn't contain that interface,
* reset the resolved configuration for that ifindex. */
g_hash_table_iter_init(&iter, priv->dirty_interfaces);
- while (g_hash_table_iter_next(&iter, (gpointer *) &pointer, NULL)) {
- ifindex = GPOINTER_TO_INT(pointer);
- if (!g_hash_table_contains(interfaces, GINT_TO_POINTER(ifindex))) {
+ while (g_hash_table_iter_next(&iter, &pointer, NULL)) {
+ int ifindex = GPOINTER_TO_INT(pointer);
+
+ if (g_hash_table_contains(interfaces, GINT_TO_POINTER(ifindex))) {
+ /* the interface is still tracked and still dirty. Keep. */
+ continue;
+ }
+
+ if (!dirty_array)
+ dirty_array = g_array_new(FALSE, FALSE, sizeof(int));
+ g_array_append_val(dirty_array, ifindex);
+
+ g_hash_table_iter_remove(&iter);
+ }
+ if (dirty_array) {
+ g_array_sort_with_data(dirty_array, nm_cmp_int2ptr_p_with_data, NULL);
+ for (i = 0; i < dirty_array->len; i++) {
+ int ifindex = g_array_index(dirty_array, int, i);
InterfaceConfig ic;
_LOGT("clear previously configured ifindex %d", ifindex);
ic = (InterfaceConfig){
- .ifindex = ifindex,
- .configs_lst_head = C_LIST_INIT(ic.configs_lst_head),
+ .ifindex = ifindex,
+ .ip_data_list = NULL,
};
prepare_one_interface(self, &ic);
- g_hash_table_iter_remove(&iter);
}
}
priv->send_updates_waiting = TRUE;
send_updates(self);
+ _update_pending_maybe_changed(self);
return TRUE;
}
@@ -649,6 +795,7 @@ name_owner_changed(NMDnsSystemdResolved *self, const char *owner)
}
send_updates(self);
+ _update_pending_maybe_changed(self);
}
static void
@@ -956,6 +1103,48 @@ nm_dns_systemd_resolved_resolve_cancel(NMDnsSystemdResolvedResolveHandle *handle
/*****************************************************************************/
+static void
+stop(NMDnsPlugin *plugin)
+{
+ NMDnsSystemdResolved *self = NM_DNS_SYSTEMD_RESOLVED(plugin);
+ NMDnsSystemdResolvedPrivate *priv = NM_DNS_SYSTEMD_RESOLVED_GET_PRIVATE(self);
+ NMDnsSystemdResolvedResolveHandle *handle;
+
+ /* This function must be re-entrant!!
+ *
+ * Currently there is no concept of unregistering/shutting down. It's not
+ * clear whether we should de-configure anything in systemd-resolved, we
+ * don't.
+ *
+ * Implementing stop() is important because pending operations take a
+ * reference on @self. We can only cancel (fast shutdown) the instance
+ * by cancelling those requests. */
+
+ priv->stopped = TRUE;
+ priv->try_start_blocked = TRUE;
+
+ nm_clear_g_cancellable(&priv->cancellable);
+
+ nm_clear_g_free(&priv->dbus_owner);
+
+ while ((handle = c_list_first_entry(&priv->handle_lst_head,
+ NMDnsSystemdResolvedResolveHandle,
+ handle_lst))) {
+ gs_free_error GError *error = NULL;
+
+ nm_utils_error_set_cancelled(&error, TRUE, "NMDnsSystemdResolved");
+ _resolve_complete_error(handle, error);
+ }
+
+ free_pending_updates(self);
+
+ nm_clear_g_dbus_connection_signal(priv->dbus_connection, &priv->name_owner_changed_id);
+
+ nm_clear_g_source_inst(&priv->try_start_timeout_source);
+}
+
+/*****************************************************************************/
+
static void
nm_dns_systemd_resolved_init(NMDnsSystemdResolved *self)
{
@@ -974,6 +1163,8 @@ nm_dns_systemd_resolved_init(NMDnsSystemdResolved *self)
return;
}
+ priv->update_pending = TRUE;
+
priv->name_owner_changed_id =
nm_dbus_connection_signal_subscribe_name_owner_changed(priv->dbus_connection,
SYSTEMD_RESOLVED_DBUS_SERVICE,
@@ -998,33 +1189,15 @@ nm_dns_systemd_resolved_new(void)
static void
dispose(GObject *object)
{
- NMDnsSystemdResolved *self = NM_DNS_SYSTEMD_RESOLVED(object);
- NMDnsSystemdResolvedPrivate *priv = NM_DNS_SYSTEMD_RESOLVED_GET_PRIVATE(self);
- NMDnsSystemdResolvedResolveHandle *handle;
+ NMDnsSystemdResolved *self = NM_DNS_SYSTEMD_RESOLVED(object);
+ NMDnsSystemdResolvedPrivate *priv = NM_DNS_SYSTEMD_RESOLVED_GET_PRIVATE(self);
- while ((handle = c_list_first_entry(&priv->handle_lst_head,
- NMDnsSystemdResolvedResolveHandle,
- handle_lst))) {
- gs_free_error GError *error = NULL;
-
- nm_utils_error_set_cancelled(&error, TRUE, "NMDnsSystemdResolved");
- _resolve_complete_error(handle, error);
- }
-
- free_pending_updates(self);
-
- nm_clear_g_dbus_connection_signal(priv->dbus_connection, &priv->name_owner_changed_id);
-
- nm_clear_g_cancellable(&priv->cancellable);
-
- nm_clear_g_source_inst(&priv->try_start_timeout_source);
+ stop(NM_DNS_PLUGIN(self));
g_clear_object(&priv->dbus_connection);
- nm_clear_pointer(&priv->dirty_interfaces, g_hash_table_unref);
+ nm_clear_pointer(&priv->dirty_interfaces, g_hash_table_destroy);
G_OBJECT_CLASS(nm_dns_systemd_resolved_parent_class)->dispose(object);
-
- nm_clear_g_free(&priv->dbus_owner);
}
static void
@@ -1035,7 +1208,9 @@ nm_dns_systemd_resolved_class_init(NMDnsSystemdResolvedClass *dns_class)
object_class->dispose = dispose;
- plugin_class->plugin_name = "systemd-resolved";
- plugin_class->is_caching = TRUE;
- plugin_class->update = update;
+ plugin_class->plugin_name = "systemd-resolved";
+ plugin_class->is_caching = TRUE;
+ plugin_class->stop = stop;
+ plugin_class->update = update;
+ plugin_class->get_update_pending = get_update_pending;
}
diff --git a/src/core/dns/nm-dns-unbound.c b/src/core/dns/nm-dns-unbound.c
deleted file mode 100644
index 8a75cf08f0..0000000000
--- a/src/core/dns/nm-dns-unbound.c
+++ /dev/null
@@ -1,84 +0,0 @@
-/* SPDX-License-Identifier: GPL-2.0-or-later */
-/*
- * Copyright (C) 2014 Red Hat, Inc.
- * Author: Pavel Šimerda
- */
-
-#include "src/core/nm-default-daemon.h"
-
-#include "nm-dns-unbound.h"
-
-#include "NetworkManagerUtils.h"
-
-/*****************************************************************************/
-
-struct _NMDnsUnbound {
- NMDnsPlugin parent;
-};
-
-struct _NMDnsUnboundClass {
- NMDnsPluginClass parent;
-};
-
-G_DEFINE_TYPE(NMDnsUnbound, nm_dns_unbound, NM_TYPE_DNS_PLUGIN)
-
-/*****************************************************************************/
-
-static gboolean
-update(NMDnsPlugin *plugin,
- const NMGlobalDnsConfig *global_config,
- const CList *ip_config_lst_head,
- const char *hostdomain,
- GError **error)
-{
- char *argv[] = {DNSSEC_TRIGGER_PATH, "--async", "--update", NULL};
- gs_free_error GError *local = NULL;
- int status;
-
- /* TODO: We currently call a script installed with the dnssec-trigger
- * package that queries all information itself. Later, the dependency
- * on that package will be optional and the only hard dependency will
- * be unbound.
- *
- * Unbound configuration should be later handled by this plugin directly,
- * without calling custom scripts. The dnssec-trigger functionality
- * may be eventually merged into NetworkManager.
- */
- if (!g_spawn_sync("/", argv, NULL, 0, NULL, NULL, NULL, NULL, &status, &local)) {
- nm_utils_error_set(error,
- NM_UTILS_ERROR_UNKNOWN,
- "error spawning dns-trigger: %s",
- local->message);
- return FALSE;
- }
- if (status != 0) {
- nm_utils_error_set(error,
- NM_UTILS_ERROR_UNKNOWN,
- "dns-trigger exited with error code %d",
- status);
- return FALSE;
- }
- return TRUE;
-}
-
-/*****************************************************************************/
-
-static void
-nm_dns_unbound_init(NMDnsUnbound *unbound)
-{}
-
-NMDnsPlugin *
-nm_dns_unbound_new(void)
-{
- return g_object_new(NM_TYPE_DNS_UNBOUND, NULL);
-}
-
-static void
-nm_dns_unbound_class_init(NMDnsUnboundClass *klass)
-{
- NMDnsPluginClass *plugin_class = NM_DNS_PLUGIN_CLASS(klass);
-
- plugin_class->plugin_name = "unbound";
- plugin_class->is_caching = TRUE;
- plugin_class->update = update;
-}
diff --git a/src/core/dns/nm-dns-unbound.h b/src/core/dns/nm-dns-unbound.h
deleted file mode 100644
index feb3309913..0000000000
--- a/src/core/dns/nm-dns-unbound.h
+++ /dev/null
@@ -1,27 +0,0 @@
-/* SPDX-License-Identifier: GPL-2.0-or-later */
-/*
- * Copyright (C) 2014 Red Hat, Inc.
- */
-
-#ifndef __NETWORKMANAGER_DNS_UNBOUND_H__
-#define __NETWORKMANAGER_DNS_UNBOUND_H__
-
-#include "nm-dns-plugin.h"
-
-#define NM_TYPE_DNS_UNBOUND (nm_dns_unbound_get_type())
-#define NM_DNS_UNBOUND(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), NM_TYPE_DNS_UNBOUND, NMDnsUnbound))
-#define NM_DNS_UNBOUND_CLASS(klass) \
- (G_TYPE_CHECK_CLASS_CAST((klass), NM_TYPE_DNS_UNBOUND, NMDnsUnboundClass))
-#define NM_IS_DNS_UNBOUND(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), NM_TYPE_DNS_UNBOUND))
-#define NM_IS_DNS_UNBOUND_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass), NM_TYPE_DNS_UNBOUND))
-#define NM_DNS_UNBOUND_GET_CLASS(obj) \
- (G_TYPE_INSTANCE_GET_CLASS((obj), NM_TYPE_DNS_UNBOUND, NMDnsUnboundClass))
-
-typedef struct _NMDnsUnbound NMDnsUnbound;
-typedef struct _NMDnsUnboundClass NMDnsUnboundClass;
-
-GType nm_dns_unbound_get_type(void);
-
-NMDnsPlugin *nm_dns_unbound_new(void);
-
-#endif /* __NETWORKMANAGER_DNS_UNBOUND_H__ */
diff --git a/src/core/meson.build b/src/core/meson.build
index 2148d23b76..f3359ad0f5 100644
--- a/src/core/meson.build
+++ b/src/core/meson.build
@@ -123,7 +123,6 @@ libNetworkManager = static_library(
'dns/nm-dns-manager.c',
'dns/nm-dns-plugin.c',
'dns/nm-dns-systemd-resolved.c',
- 'dns/nm-dns-unbound.c',
'dnsmasq/nm-dnsmasq-manager.c',
'dnsmasq/nm-dnsmasq-utils.c',
'ppp/nm-ppp-manager-call.c',
diff --git a/src/core/nm-manager.c b/src/core/nm-manager.c
index 521536979f..191d65bf65 100644
--- a/src/core/nm-manager.c
+++ b/src/core/nm-manager.c
@@ -20,6 +20,7 @@
#include "devices/nm-device-factory.h"
#include "devices/nm-device-generic.h"
#include "devices/nm-device.h"
+#include "dns/nm-dns-manager.h"
#include "dhcp/nm-dhcp-manager.h"
#include "libnm-core-aux-intern/nm-common-macros.h"
#include "libnm-core-intern/nm-core-internal.h"
@@ -144,6 +145,9 @@ NM_GOBJECT_PROPERTIES_DEFINE(NMManager,
typedef struct {
NMPlatform *platform;
+ NMDnsManager *dns_mgr;
+ gulong dns_mgr_update_pending_signal_id;
+
GArray *capabilities;
CList active_connections_lst_head; /* Oldest ACs at the beginning */
@@ -346,6 +350,9 @@ static NMActiveConnection *_new_active_connection(NMManager *self,
static void policy_activating_ac_changed(GObject *object, GParamSpec *pspec, gpointer user_data);
+static void device_has_pending_action_changed(NMDevice *device, GParamSpec *pspec, NMManager *self);
+static void check_if_startup_complete(NMManager *self);
+
static gboolean find_master(NMManager *self,
NMConnection *connection,
NMDevice *device,
@@ -1601,7 +1608,11 @@ manager_device_state_changed(NMDevice *device,
nm_settings_device_added(priv->settings, device);
}
-static void device_has_pending_action_changed(NMDevice *device, GParamSpec *pspec, NMManager *self);
+static void
+_dns_mgr_update_pending_cb(NMDevice *device, GParamSpec *pspec, NMManager *self)
+{
+ check_if_startup_complete(self);
+}
static void
check_if_startup_complete(NMManager *self)
@@ -1616,6 +1627,20 @@ check_if_startup_complete(NMManager *self)
if (!priv->devices_inited)
return;
+ if (nm_dns_manager_get_update_pending(nm_manager_get_dns_manager(self))) {
+ if (priv->dns_mgr_update_pending_signal_id == 0) {
+ priv->dns_mgr_update_pending_signal_id =
+ g_signal_connect(nm_manager_get_dns_manager(self),
+ "notify::" NM_DNS_MANAGER_UPDATE_PENDING,
+ G_CALLBACK(_dns_mgr_update_pending_cb),
+ self);
+ }
+ return;
+ }
+
+ nm_clear_g_signal_handler(nm_manager_get_dns_manager(self),
+ &priv->dns_mgr_update_pending_signal_id);
+
c_list_for_each_entry (device, &priv->devices_lst_head, devices_lst) {
reason = nm_device_has_pending_action_reason(device);
if (reason) {
@@ -7790,6 +7815,28 @@ impl_manager_checkpoint_adjust_rollback_timeout(NMDBusObject
/*****************************************************************************/
+NMDnsManager *
+nm_manager_get_dns_manager(NMManager *self)
+{
+ NMManagerPrivate *priv;
+
+ g_return_val_if_fail(NM_IS_MANAGER(self), NULL);
+
+ priv = NM_MANAGER_GET_PRIVATE(self);
+
+ if (G_UNLIKELY(!priv->dns_mgr)) {
+ /* Initialize lazily on first use.
+ *
+ * But keep a reference. This is to ensure proper lifetimes between
+ * singleton instances (i.e. nm_dns_manager_get() outlives NMManager). */
+ priv->dns_mgr = g_object_ref(nm_dns_manager_get());
+ }
+
+ return priv->dns_mgr;
+}
+
+/*****************************************************************************/
+
static void
auth_mgr_changed(NMAuthManager *auth_manager, gpointer user_data)
{
@@ -8250,6 +8297,9 @@ dispose(GObject *object)
g_clear_object(&priv->concheck_mgr);
}
+ nm_clear_g_signal_handler(priv->dns_mgr, &priv->dns_mgr_update_pending_signal_id);
+ g_clear_object(&priv->dns_mgr);
+
if (priv->auth_mgr) {
g_signal_handlers_disconnect_by_func(priv->auth_mgr, G_CALLBACK(auth_mgr_changed), self);
g_clear_object(&priv->auth_mgr);
diff --git a/src/core/nm-manager.h b/src/core/nm-manager.h
index f8563c3aa1..fcb0022720 100644
--- a/src/core/nm-manager.h
+++ b/src/core/nm-manager.h
@@ -200,6 +200,10 @@ NMMetered nm_manager_get_metered(NMManager *self);
void nm_manager_notify_device_availability_maybe_changed(NMManager *self);
+struct _NMDnsManager;
+
+struct _NMDnsManager *nm_manager_get_dns_manager(NMManager *self);
+
/*****************************************************************************/
void nm_manager_device_auth_request(NMManager *self,
diff --git a/src/libnm-glib-aux/nm-shared-utils.c b/src/libnm-glib-aux/nm-shared-utils.c
index ad99a6b929..1da2a68293 100644
--- a/src/libnm-glib-aux/nm-shared-utils.c
+++ b/src/libnm-glib-aux/nm-shared-utils.c
@@ -3432,51 +3432,6 @@ nm_utils_named_value_clear_with_g_free(NMUtilsNamedValue *val)
G_STATIC_ASSERT(G_STRUCT_OFFSET(NMUtilsNamedValue, name) == 0);
-NMUtilsNamedValue *
-nm_utils_named_values_from_strdict_full(GHashTable *hash,
- guint *out_len,
- GCompareDataFunc compare_func,
- gpointer user_data,
- NMUtilsNamedValue *provided_buffer,
- guint provided_buffer_len,
- NMUtilsNamedValue **out_allocated_buffer)
-{
- GHashTableIter iter;
- NMUtilsNamedValue *values;
- guint i, len;
-
- nm_assert(provided_buffer_len == 0 || provided_buffer);
- nm_assert(!out_allocated_buffer || !*out_allocated_buffer);
-
- if (!hash || !(len = g_hash_table_size(hash))) {
- NM_SET_OUT(out_len, 0);
- return NULL;
- }
-
- if (provided_buffer_len >= len + 1) {
- /* the buffer provided by the caller is large enough. Use it. */
- values = provided_buffer;
- } else {
- /* allocate a new buffer. */
- values = g_new(NMUtilsNamedValue, len + 1);
- NM_SET_OUT(out_allocated_buffer, values);
- }
-
- i = 0;
- g_hash_table_iter_init(&iter, hash);
- while (g_hash_table_iter_next(&iter, (gpointer *) &values[i].name, &values[i].value_ptr))
- i++;
- nm_assert(i == len);
- values[i].name = NULL;
- values[i].value_ptr = NULL;
-
- if (compare_func)
- nm_utils_named_value_list_sort(values, len, compare_func, user_data);
-
- NM_SET_OUT(out_len, len);
- return values;
-}
-
gssize
nm_utils_named_value_list_find(const NMUtilsNamedValue *arr,
gsize len,
@@ -3626,6 +3581,52 @@ nm_utils_hash_values_to_array(GHashTable *hash,
return arr;
}
+NMUtilsNamedValue *
+nm_utils_hash_to_array_full(GHashTable *hash,
+ guint *out_len,
+ GCompareDataFunc compare_func,
+ gpointer user_data,
+ NMUtilsNamedValue *provided_buffer,
+ guint provided_buffer_len,
+ NMUtilsNamedValue **out_allocated_buffer)
+{
+ GHashTableIter iter;
+ NMUtilsNamedValue *values;
+ guint len;
+ guint i;
+
+ nm_assert(provided_buffer_len == 0 || provided_buffer);
+ nm_assert(!out_allocated_buffer || !*out_allocated_buffer);
+
+ if (!hash || ((len = g_hash_table_size(hash)) == 0)) {
+ NM_SET_OUT(out_len, 0);
+ return NULL;
+ }
+
+ if (provided_buffer_len >= len + 1) {
+ /* the buffer provided by the caller is large enough. Use it. */
+ values = provided_buffer;
+ } else {
+ /* allocate a new buffer. */
+ values = g_new(NMUtilsNamedValue, len + 1);
+ NM_SET_OUT(out_allocated_buffer, values);
+ }
+
+ i = 0;
+ g_hash_table_iter_init(&iter, hash);
+ while (g_hash_table_iter_next(&iter, &values[i].name_ptr, &values[i].value_ptr))
+ i++;
+ nm_assert(i == len);
+ values[i].name_ptr = NULL;
+ values[i].value_ptr = NULL;
+
+ if (compare_func && len > 1)
+ g_qsort_with_data(values, len, sizeof(NMUtilsNamedValue), compare_func, user_data);
+
+ NM_SET_OUT(out_len, len);
+ return values;
+}
+
/*****************************************************************************/
/**
diff --git a/src/libnm-glib-aux/nm-shared-utils.h b/src/libnm-glib-aux/nm-shared-utils.h
index 941312bd0e..0d2403ca66 100644
--- a/src/libnm-glib-aux/nm-shared-utils.h
+++ b/src/libnm-glib-aux/nm-shared-utils.h
@@ -1977,6 +1977,7 @@ typedef struct {
NMUtilsNamedEntry named_entry;
const char *name;
char *name_mutable;
+ gpointer name_ptr;
};
union {
const char *value_str;
@@ -1990,14 +1991,28 @@ typedef struct {
.name = (n), .value_ptr = (v) \
}
-NMUtilsNamedValue *
-nm_utils_named_values_from_strdict_full(GHashTable *hash,
- guint *out_len,
- GCompareDataFunc compare_func,
- gpointer user_data,
- NMUtilsNamedValue *provided_buffer,
- guint provided_buffer_len,
- NMUtilsNamedValue **out_allocated_buffer);
+NMUtilsNamedValue *nm_utils_hash_to_array_full(GHashTable *hash,
+ guint *out_len,
+ GCompareDataFunc compare_func,
+ gpointer user_data,
+ NMUtilsNamedValue *provided_buffer,
+ guint provided_buffer_len,
+ NMUtilsNamedValue **out_allocated_buffer);
+
+#define nm_utils_named_values_from_strdict_full(hash, \
+ out_len, \
+ compare_func, \
+ user_data, \
+ provided_buffer, \
+ provided_buffer_len, \
+ out_allocated_buffer) \
+ nm_utils_hash_to_array_full((hash), \
+ (out_len), \
+ (compare_func), \
+ (user_data), \
+ (provided_buffer), \
+ (provided_buffer_len), \
+ (out_allocated_buffer))
#define nm_utils_named_values_from_strdict(hash, out_len, array, out_allocated_buffer) \
nm_utils_named_values_from_strdict_full((hash), \
@@ -2038,6 +2053,29 @@ gpointer *nm_utils_hash_values_to_array(GHashTable *hash,
gpointer user_data,
guint *out_len);
+static inline NMUtilsNamedValue *
+nm_utils_hash_to_array(GHashTable *hash,
+ GCompareDataFunc compare_func,
+ gpointer user_data,
+ guint *out_len)
+{
+ return nm_utils_hash_to_array_full(hash, out_len, compare_func, user_data, NULL, 0, NULL);
+}
+
+#define nm_utils_hash_to_array_with_buffer(hash, \
+ out_len, \
+ compare_func, \
+ user_data, \
+ array, \
+ out_allocated_buffer) \
+ nm_utils_hash_to_array_full((hash), \
+ (out_len), \
+ (compare_func), \
+ (user_data), \
+ (array), \
+ G_N_ELEMENTS(array), \
+ (out_allocated_buffer))
+
static inline const char **
nm_strdict_get_keys(const GHashTable *hash, gboolean sorted, guint *out_length)
{