diff options
| author | Michael Biebl <biebl@debian.org> | 2022-08-16 18:24:19 +0200 |
|---|---|---|
| committer | Michael Biebl <biebl@debian.org> | 2022-08-16 18:24:19 +0200 |
| commit | 0018d1f3cf71d680d7b6bceda55a5717244d8b26 (patch) | |
| tree | a058f1d106d172d3354179437ef034c9355cdf9c /src/core/dns | |
| parent | 6accbd3ec0e42d8633bbde4d47ed7bfe854e7e0b (diff) | |
New upstream version 1.39.90 upstream/1.39.90
Diffstat (limited to 'src/core/dns')
| -rw-r--r-- | src/core/dns/nm-dns-dnsmasq.c | 123 | ||||
| -rw-r--r-- | src/core/dns/nm-dns-manager.c | 202 | ||||
| -rw-r--r-- | src/core/dns/nm-dns-manager.h | 9 | ||||
| -rw-r--r-- | src/core/dns/nm-dns-plugin.c | 161 | ||||
| -rw-r--r-- | src/core/dns/nm-dns-plugin.h | 13 | ||||
| -rw-r--r-- | src/core/dns/nm-dns-systemd-resolved.c | 425 | ||||
| -rw-r--r-- | src/core/dns/nm-dns-unbound.c | 84 | ||||
| -rw-r--r-- | src/core/dns/nm-dns-unbound.h | 27 |
8 files changed, 722 insertions, 322 deletions
diff --git a/src/core/dns/nm-dns-dnsmasq.c b/src/core/dns/nm-dns-dnsmasq.c index 43426882..7d0f0490 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 566f3d66..1e54452a 100644 --- a/src/core/dns/nm-dns-manager.c +++ b/src/core/dns/nm-dns-manager.c @@ -26,7 +26,6 @@ #include "libnm-core-intern/nm-core-internal.h" #include "libnm-glib-aux/nm-str-buf.h" -#include "libnm-systemd-shared/nm-sd-utils-shared.h" #include "NetworkManagerUtils.h" #include "devices/nm-device.h" @@ -35,7 +34,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 +55,8 @@ #define HAS_NETCONFIG 1 #endif +#define UPDATE_PENDING_UNBLOCK_TIMEOUT_MSEC 5000 + /*****************************************************************************/ typedef enum { SR_SUCCESS, SR_NOTFOUND, SR_ERROR } SpawnResult; @@ -78,7 +78,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 +93,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 +107,8 @@ typedef struct { bool config_changed : 1; + bool update_pending : 1; + char *hostdomain; guint updates_queue; @@ -109,6 +120,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 +151,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 +216,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) { @@ -2015,7 +2103,7 @@ nm_dns_manager_set_hostname(NMDnsManager *self, const char *hostname, gboolean s domain = hostname; } - if (!nm_sd_hostname_is_valid(domain, FALSE)) + if (!nm_hostname_is_valid(domain, FALSE)) domain = NULL; } } @@ -2120,6 +2208,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 +2216,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 +2416,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 +2460,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 +2491,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 +2712,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 +2762,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 +2842,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 c30d4b3a..210f9f6c 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 847d7839..41a0dbc1 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 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_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 *plugin_class) -{} +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 f9c424ab..24d6083b 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 c4993884..e0b94647 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 { @@ -77,15 +78,18 @@ typedef struct { GDBusConnection *dbus_connection; GHashTable *dirty_interfaces; GCancellable *cancellable; - GSource *try_start_timeout_source; + GCancellable *service_start_cancellable; CList request_queue_lst_head; 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 gboolean +_update_pending_detect(NMDnsSystemdResolved *self) +{ + 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->service_start_cancellable) { + /* 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_free(RequestItem *request_item) +_request_item_unref(RequestItem *request_item) { - c_list_unlink_stale(&request_item->request_queue_lst); + 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)); + } } } @@ -413,33 +522,45 @@ prepare_one_interface(NMDnsSystemdResolved *self, InterfaceConfig *ic) return has_config; } -static gboolean -_ensure_resolved_running_timeout(gpointer user_data) +static void +start_resolved_cb(GObject *source, GAsyncResult *result, gpointer user_data) { - NMDnsSystemdResolved *self = user_data; - NMDnsSystemdResolvedPrivate *priv = NM_DNS_SYSTEMD_RESOLVED_GET_PRIVATE(self); + gs_unref_variant GVariant *res = NULL; + gs_free_error GError *error = NULL; + NMDnsSystemdResolved *self; + NMDnsSystemdResolvedPrivate *priv; NMDnsSystemdResolvedResolveHandle *handle; - nm_clear_g_source_inst(&priv->try_start_timeout_source); + res = g_dbus_connection_call_finish(G_DBUS_CONNECTION(source), result, &error); + if (nm_utils_error_is_cancelled(error)) + return; + + self = user_data; + priv = NM_DNS_SYSTEMD_RESOLVED_GET_PRIVATE(self); + nm_clear_g_cancellable(&priv->service_start_cancellable); - _LOGT("timeout waiting to D-Bus activate systemd-resolved. Systemd-resolved won't be " - "used until it appears on the bus"); + if (!res) { + g_dbus_error_strip_remote_error(error); + _LOGD("error activating systemd-resolved: %s", error->message); again: - c_list_for_each_entry (handle, &priv->handle_lst_head, handle_lst) { - gs_free_error GError *error = NULL; + c_list_for_each_entry (handle, &priv->handle_lst_head, handle_lst) { + gs_free_error GError *local = NULL; - if (handle->is_failing_on_idle) - continue; + if (handle->is_failing_on_idle) + continue; - nm_utils_error_set_literal(&error, - NM_UTILS_ERROR_NOT_READY, - "timeout waiting for systemd-resolved to start"); - _resolve_complete_error(handle, error); - goto again; - } + nm_utils_error_set(&local, + NM_UTILS_ERROR_NOT_READY, + "error activating systemd-resolved: %s", + error->message); + _resolve_complete_error(handle, local); + goto again; + } + } else + _LOGD("systemd-resolved successfully started"); - return G_SOURCE_CONTINUE; + _update_pending_maybe_changed(self); } static NMTernary @@ -447,6 +568,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; @@ -459,16 +583,15 @@ ensure_resolved_running(NMDnsSystemdResolved *self) _LOGT("try D-Bus activating systemd-resolved..."); priv->try_start_blocked = TRUE; - - priv->try_start_timeout_source = - nm_g_timeout_add_source(4000, _ensure_resolved_running_timeout, self); - + nm_clear_g_cancellable(&priv->service_start_cancellable); + priv->service_start_cancellable = g_cancellable_new(); nm_dbus_connection_call_start_service_by_name(priv->dbus_connection, SYSTEMD_RESOLVED_DBUS_SERVICE, - -1, - NULL, - NULL, - NULL); + 4000, + priv->service_start_cancellable, + start_resolved_cb, + self); + _update_pending_maybe_changed(self); return NM_TERNARY_DEFAULT; } @@ -523,6 +646,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 +663,7 @@ send_updates(NMDnsSystemdResolved *self) -1, priv->cancellable, call_done, - request_item); + _request_item_ref(request_item)); } start_resolve: @@ -554,43 +683,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 +742,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; } @@ -636,8 +791,7 @@ name_owner_changed(NMDnsSystemdResolved *self, const char *owner) else _LOGT("D-Bus name for systemd-resolved has owner %s", owner); - nm_clear_g_source_inst(&priv->try_start_timeout_source); - + nm_clear_g_cancellable(&priv->service_start_cancellable); nm_strdup_reset(&priv->dbus_owner, owner); if (owner) { @@ -649,6 +803,7 @@ name_owner_changed(NMDnsSystemdResolved *self, const char *owner) } send_updates(self); + _update_pending_maybe_changed(self); } static void @@ -957,6 +1112,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_cancellable(&priv->service_start_cancellable); +} + +/*****************************************************************************/ + +static void nm_dns_systemd_resolved_init(NMDnsSystemdResolved *self) { NMDnsSystemdResolvedPrivate *priv = NM_DNS_SYSTEMD_RESOLVED_GET_PRIVATE(self); @@ -974,6 +1171,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 +1197,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; - - 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); + NMDnsSystemdResolved *self = NM_DNS_SYSTEMD_RESOLVED(object); + NMDnsSystemdResolvedPrivate *priv = NM_DNS_SYSTEMD_RESOLVED_GET_PRIVATE(self); - 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 +1216,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 8a75cf08..00000000 --- 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 <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; -} diff --git a/src/core/dns/nm-dns-unbound.h b/src/core/dns/nm-dns-unbound.h deleted file mode 100644 index feb33099..00000000 --- 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__ */ |