mirror of
https://gitlab.freedesktop.org/NetworkManager/NetworkManager.git
synced 2025-12-29 23:10:18 +01:00
dns: merge branch 'th/dns-update-pending-rh2049421'
https://bugzilla.redhat.com/show_bug.cgi?id=2049421 https://gitlab.freedesktop.org/NetworkManager/NetworkManager/-/merge_requests/1189
This commit is contained in:
commit
f9f7f231c8
20 changed files with 873 additions and 387 deletions
|
|
@ -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 \
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
||||
|
|
|
|||
12
configure.ac
12
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]))
|
||||
|
|
|
|||
|
|
@ -345,19 +345,12 @@ no-auto-default=*
|
|||
<para><literal>systemd-resolved</literal>: NetworkManager will
|
||||
push the DNS configuration to systemd-resolved</para>
|
||||
|
||||
<para><literal>unbound</literal>: NetworkManager will talk
|
||||
to unbound and dnssec-triggerd, using "Conditional Forwarding"
|
||||
with DNSSEC support. <filename>/etc/resolv.conf</filename>
|
||||
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.</para>
|
||||
|
||||
<para><literal>none</literal>: NetworkManager will not
|
||||
modify resolv.conf. This implies
|
||||
<literal>rc-manager</literal> <literal>unmanaged</literal></para>
|
||||
|
||||
<para>Note that the plugins <literal>dnsmasq</literal>, <literal>systemd-resolved</literal>
|
||||
and <literal>unbound</literal> are caching local nameservers.
|
||||
<para>Note that the plugins <literal>dnsmasq</literal> and <literal>systemd-resolved</literal>
|
||||
are caching local nameservers.
|
||||
Hence, when NetworkManager writes <filename>&nmrundir;/resolv.conf</filename>
|
||||
and <filename>/etc/resolv.conf</filename> (according to <literal>rc-manager</literal>
|
||||
setting below), the name server there will be localhost only.
|
||||
|
|
|
|||
13
meson.build
13
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
|
||||
|
|
|
|||
|
|
@ -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')
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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__ */
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,84 +0,0 @@
|
|||
/* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
/*
|
||||
* Copyright (C) 2014 Red Hat, Inc.
|
||||
* Author: Pavel Šimerda <psimerda@redhat.com>
|
||||
*/
|
||||
|
||||
#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;
|
||||
}
|
||||
|
|
@ -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__ */
|
||||
|
|
@ -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',
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
||||
/*****************************************************************************/
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
{
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue