diff options
-rw-r--r-- | src/dns/nm-dns-dnsmasq.c | 117 | ||||
-rw-r--r-- | src/dns/nm-dns-manager.c | 74 | ||||
-rw-r--r-- | src/dns/nm-dns-plugin.c | 30 | ||||
-rw-r--r-- | src/dns/nm-dns-plugin.h | 7 |
4 files changed, 90 insertions, 138 deletions
diff --git a/src/dns/nm-dns-dnsmasq.c b/src/dns/nm-dns-dnsmasq.c index 49c5203966..4265a66660 100644 --- a/src/dns/nm-dns-dnsmasq.c +++ b/src/dns/nm-dns-dnsmasq.c @@ -31,6 +31,9 @@ #define DNSMASQ_DBUS_SERVICE "org.freedesktop.NetworkManager.dnsmasq" #define DNSMASQ_DBUS_PATH "/uk/org/thekelleys/dnsmasq" +#define RATELIMIT_INTERVAL_MSEC 30000 +#define RATELIMIT_BURST 5 + #define _NMLOG_DOMAIN LOGD_DNS /*****************************************************************************/ @@ -647,11 +650,19 @@ typedef struct { char *name_owner; + 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; + } NMDnsDnsmasqPrivate; struct _NMDnsDnsmasq { @@ -674,6 +685,10 @@ G_DEFINE_TYPE (NMDnsDnsmasq, nm_dns_dnsmasq, NM_TYPE_DNS_PLUGIN) /*****************************************************************************/ +static gboolean start_dnsmasq (NMDnsDnsmasq *self, gboolean force_start, GError **error); + +/*****************************************************************************/ + static void add_dnsmasq_nameserver (NMDnsDnsmasq *self, GVariantBuilder *servers, @@ -839,30 +854,27 @@ send_dnsmasq_update (NMDnsDnsmasq *self) { NMDnsDnsmasqPrivate *priv = NM_DNS_DNSMASQ_GET_PRIVATE (self); - if (!priv->set_server_ex_args) - return; + if ( !priv->name_owner + || !priv->set_server_ex_args) + return; - if (priv->name_owner) { - _LOGD ("trying to update dnsmasq nameservers"); - - nm_clear_g_cancellable (&priv->update_cancellable); - priv->update_cancellable = g_cancellable_new (); - - g_dbus_connection_call (priv->dbus_connection, - priv->name_owner, - DNSMASQ_DBUS_PATH, - DNSMASQ_DBUS_SERVICE, - "SetServersEx", - priv->set_server_ex_args, - NULL, - G_DBUS_CALL_FLAGS_NO_AUTO_START, - 20000, - priv->update_cancellable, - dnsmasq_update_done, - self); - g_clear_pointer (&priv->set_server_ex_args, g_variant_unref); - } else - _LOGD ("dnsmasq is starting... The nameserver update will be sent when dnsmasq appears"); + _LOGD ("trying to update dnsmasq nameservers"); + + nm_clear_g_cancellable (&priv->update_cancellable); + priv->update_cancellable = g_cancellable_new (); + + g_dbus_connection_call (priv->dbus_connection, + priv->name_owner, + DNSMASQ_DBUS_PATH, + DNSMASQ_DBUS_SERVICE, + "SetServersEx", + priv->set_server_ex_args, + NULL, + G_DBUS_CALL_FLAGS_NO_AUTO_START, + 20000, + priv->update_cancellable, + dnsmasq_update_done, + self); } /*****************************************************************************/ @@ -888,8 +900,11 @@ _main_cleanup (NMDnsDnsmasq *self, gboolean emit_failed) * process in the background. */ nm_clear_g_cancellable (&priv->main_cancellable); - if (emit_failed) - _nm_dns_plugin_emit_failed (self, TRUE); + if ( !priv->is_stopped + && priv->burst_retry_timeout_id == 0) { + start_dnsmasq (self, FALSE, NULL); + send_dnsmasq_update (self); + } } static void @@ -1001,10 +1016,24 @@ spawn_notify (GCancellable *cancellable, } static gboolean -start_dnsmasq (NMDnsDnsmasq *self, GError **error) +_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; + + start_dnsmasq (self, TRUE, NULL); + send_dnsmasq_update (self); + return G_SOURCE_REMOVE; +} + +static gboolean +start_dnsmasq (NMDnsDnsmasq *self, gboolean force_start, GError **error) { NMDnsDnsmasqPrivate *priv = NM_DNS_DNSMASQ_GET_PRIVATE (self); const char *dm_binary; + gint64 now; if (G_LIKELY (priv->main_cancellable)) { /* The process is already running or about to be started. Nothing to do. */ @@ -1030,6 +1059,31 @@ start_dnsmasq (NMDnsDnsmasq *self, GError **error) } } + now = nm_utils_get_monotonic_timestamp_ms (); + if ( force_start + || priv->burst_start_at == 0 + || 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); + _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); + 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) { + _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); + } else + _LOGT ("rate-limit: currently rate-limited from restart"); + return TRUE; + } + priv->main_timeout_id = g_timeout_add (10000, spawn_timeout_cb, self); @@ -1053,7 +1107,7 @@ update (NMDnsPlugin *plugin, NMDnsDnsmasq *self = NM_DNS_DNSMASQ (plugin); NMDnsDnsmasqPrivate *priv = NM_DNS_DNSMASQ_GET_PRIVATE (self); - if (!start_dnsmasq (self, error)) + if (!start_dnsmasq (self, TRUE, error)) return FALSE; nm_clear_pointer (&priv->set_server_ex_args, g_variant_unref); @@ -1072,6 +1126,11 @@ static void stop (NMDnsPlugin *plugin) { NMDnsDnsmasq *self = NM_DNS_DNSMASQ (plugin); + NMDnsDnsmasqPrivate *priv = NM_DNS_DNSMASQ_GET_PRIVATE (self); + + priv->is_stopped = TRUE; + priv->burst_start_at = 0; + nm_clear_g_source (&priv->burst_retry_timeout_id); /* Cancelling the cancellable will also terminate the * process (in the background). */ @@ -1097,6 +1156,10 @@ dispose (GObject *object) NMDnsDnsmasq *self = NM_DNS_DNSMASQ (object); NMDnsDnsmasqPrivate *priv = NM_DNS_DNSMASQ_GET_PRIVATE (self); + priv->is_stopped = TRUE; + + nm_clear_g_source (&priv->burst_retry_timeout_id); + _main_cleanup (self, FALSE); g_clear_pointer (&priv->set_server_ex_args, g_variant_unref); diff --git a/src/dns/nm-dns-manager.c b/src/dns/nm-dns-manager.c index 4c619d55f6..3234c4e798 100644 --- a/src/dns/nm-dns-manager.c +++ b/src/dns/nm-dns-manager.c @@ -50,10 +50,6 @@ #define NETCONFIG_PATH "/sbin/netconfig" #endif -#define PLUGIN_RATELIMIT_INTERVAL 30 -#define PLUGIN_RATELIMIT_BURST 5 -#define PLUGIN_RATELIMIT_DELAY 300 - /*****************************************************************************/ typedef enum { @@ -1557,71 +1553,6 @@ update_dns (NMDnsManager *self, return !update || result == SR_SUCCESS; } -static void -plugin_failed (NMDnsManager *self, NMDnsPlugin *plugin) -{ - GError *error = NULL; - - /* Errors with non-caching plugins aren't fatal */ - if (!nm_dns_plugin_is_caching (plugin)) - return; - - /* Disable caching until the next DNS update */ - if (!update_dns (self, TRUE, &error)) { - _LOGW ("could not commit DNS changes: %s", error->message); - g_clear_error (&error); - } -} - -static gboolean -plugin_child_quit_update_dns (gpointer user_data) -{ - GError *error = NULL; - NMDnsManager *self = NM_DNS_MANAGER (user_data); - - /* Let the plugin try to spawn the child again */ - if (!update_dns (self, FALSE, &error)) { - _LOGW ("could not commit DNS changes: %s", error->message); - g_clear_error (&error); - } - - return G_SOURCE_REMOVE; -} - -static void -plugin_failed_cb (NMDnsPlugin *plugin, gboolean is_fatal, NMDnsManager *self) -{ - NMDnsManagerPrivate *priv = NM_DNS_MANAGER_GET_PRIVATE (self); - - if (is_fatal) { - gint64 ts = nm_utils_get_monotonic_timestamp_ms (); - - _LOGW ("plugin %s died unexpectedly", nm_dns_plugin_get_name (plugin)); - - if ( !priv->plugin_ratelimit.ts - || (ts - priv->plugin_ratelimit.ts) / 1000 > PLUGIN_RATELIMIT_INTERVAL) { - priv->plugin_ratelimit.ts = ts; - priv->plugin_ratelimit.num_restarts = 0; - } else { - priv->plugin_ratelimit.num_restarts++; - if (priv->plugin_ratelimit.num_restarts > PLUGIN_RATELIMIT_BURST) { - plugin_failed (self, plugin); - _LOGW ("plugin %s child respawning too fast, delaying update for %u seconds", - nm_dns_plugin_get_name (plugin), PLUGIN_RATELIMIT_DELAY); - priv->plugin_ratelimit.timer = g_timeout_add_seconds (PLUGIN_RATELIMIT_DELAY, - plugin_child_quit_update_dns, - self); - return; - } - } - - plugin_child_quit_update_dns (self); - return; - } - - plugin_failed (self, plugin); -} - /*****************************************************************************/ static void @@ -1856,7 +1787,6 @@ _clear_plugin (NMDnsManager *self) nm_clear_g_source (&priv->plugin_ratelimit.timer); if (priv->plugin) { - g_signal_handlers_disconnect_by_func (priv->plugin, plugin_failed_cb, self); nm_dns_plugin_stop (priv->plugin); g_clear_object (&priv->plugin); return TRUE; @@ -2079,10 +2009,6 @@ again: } else if (nm_clear_g_object (&priv->sd_resolve_plugin)) systemd_resolved_changed = TRUE; - if ( plugin_changed - && priv->plugin) - g_signal_connect (priv->plugin, NM_DNS_PLUGIN_FAILED, G_CALLBACK (plugin_failed_cb), self); - g_object_freeze_notify (G_OBJECT (self)); if (!nm_streq0 (priv->mode, mode)) { diff --git a/src/dns/nm-dns-plugin.c b/src/dns/nm-dns-plugin.c index d7d3672ec7..6930b7a6cd 100644 --- a/src/dns/nm-dns-plugin.c +++ b/src/dns/nm-dns-plugin.c @@ -17,13 +17,6 @@ /*****************************************************************************/ -enum { - FAILED, - LAST_SIGNAL, -}; - -static guint signals[LAST_SIGNAL] = { 0 }; - typedef struct _NMDnsPluginPrivate { GPid pid; guint watch_id; @@ -105,15 +98,6 @@ nm_dns_plugin_stop (NMDnsPlugin *self) klass->stop (self); } -void -_nm_dns_plugin_emit_failed (gpointer /* NMDnsPlugin * */ self, - gboolean is_fatal) -{ - nm_assert (NM_IS_DNS_PLUGIN (self)); - - g_signal_emit (self, signals[FAILED], 0, (gboolean) (!!is_fatal)); -} - /*****************************************************************************/ static void @@ -124,18 +108,4 @@ nm_dns_plugin_init (NMDnsPlugin *self) static void nm_dns_plugin_class_init (NMDnsPluginClass *plugin_class) { - GObjectClass *object_class = G_OBJECT_CLASS (plugin_class); - - /* Emitted by the plugin and consumed by NMDnsManager when - * some error happens with the nameserver subprocess. Causes NM to fall - * back to writing out a non-local-caching resolv.conf until the next - * DNS update. - */ - signals[FAILED] = - g_signal_new (NM_DNS_PLUGIN_FAILED, - G_OBJECT_CLASS_TYPE (object_class), - G_SIGNAL_RUN_FIRST, - 0, NULL, NULL, - g_cclosure_marshal_VOID__BOOLEAN, - G_TYPE_NONE, 1, G_TYPE_BOOLEAN); } diff --git a/src/dns/nm-dns-plugin.h b/src/dns/nm-dns-plugin.h index 326bc50eae..e96d67f097 100644 --- a/src/dns/nm-dns-plugin.h +++ b/src/dns/nm-dns-plugin.h @@ -15,8 +15,6 @@ #define NM_IS_DNS_PLUGIN_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), NM_TYPE_DNS_PLUGIN)) #define NM_DNS_PLUGIN_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), NM_TYPE_DNS_PLUGIN, NMDnsPluginClass)) -#define NM_DNS_PLUGIN_FAILED "failed" - typedef struct { GObject parent; } NMDnsPlugin; @@ -61,9 +59,4 @@ gboolean nm_dns_plugin_update (NMDnsPlugin *self, void nm_dns_plugin_stop (NMDnsPlugin *self); -/*****************************************************************************/ - -void _nm_dns_plugin_emit_failed (gpointer /* NMDnsPlugin * */ self, - gboolean is_fatal); - #endif /* __NM_DNS_PLUGIN_H__ */ |