From 64ac9101316471cce38f360d438dd8f948de6dcb Mon Sep 17 00:00:00 2001 From: Beniamino Galvani Date: Fri, 15 Jan 2016 15:04:39 +0100 Subject: [PATCH] dns-manager: prevent DNS plugins from respawning too quickly If dnsmasq (or another DNS plugin) exits immediately (for example due to an already used port), the DNS manager keeps restarting it forever, wasting system resources and filling logs. Add a simple rate-limiting mechanism. https://bugzilla.gnome.org/show_bug.cgi?id=760691 --- src/dns-manager/nm-dns-manager.c | 50 ++++++++++++++++++++++++++++---- src/dns-manager/nm-dns-plugin.c | 3 +- 2 files changed, 46 insertions(+), 7 deletions(-) diff --git a/src/dns-manager/nm-dns-manager.c b/src/dns-manager/nm-dns-manager.c index 01e8bf1db..20af6d20e 100644 --- a/src/dns-manager/nm-dns-manager.c +++ b/src/dns-manager/nm-dns-manager.c @@ -82,6 +82,10 @@ G_DEFINE_TYPE (NMDnsManager, nm_dns_manager, G_TYPE_OBJECT) #define NETCONFIG_PATH "/sbin/netconfig" #endif +#define PLUGIN_RATELIMIT_INTERVAL 30 +#define PLUGIN_RATELIMIT_BURST 5 +#define PLUGIN_RATELIMIT_DELAY 300 + NM_DEFINE_SINGLETON_INSTANCE (NMDnsManager); /*********************************************************************************************/ @@ -130,6 +134,12 @@ typedef struct { NMConfig *config; gboolean dns_touched; + + struct { + guint64 ts; + guint num_restarts; + guint timer; + } plugin_ratelimit; } NMDnsManagerPrivate; enum { @@ -799,6 +809,7 @@ update_dns (NMDnsManager *self, g_return_val_if_fail (!error || !*error, FALSE); priv = NM_DNS_MANAGER_GET_PRIVATE (self); + nm_clear_g_source (&priv->plugin_ratelimit.timer); if (priv->resolv_conf_mode == NM_DNS_MANAGER_RESOLV_CONF_UNMANAGED) { update = FALSE; @@ -1022,20 +1033,47 @@ plugin_failed (NMDnsPlugin *plugin, gpointer user_data) } } -static void -plugin_child_quit (NMDnsPlugin *plugin, int exit_status, gpointer user_data) +static gboolean +plugin_child_quit_update_dns (gpointer user_data) { - NMDnsManager *self = NM_DNS_MANAGER (user_data); GError *error = NULL; - - _LOGW ("plugin %s child quit unexpectedly; refreshing DNS", - nm_dns_plugin_get_name (plugin)); + 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_child_quit (NMDnsPlugin *plugin, int exit_status, gpointer user_data) +{ + NMDnsManager *self = NM_DNS_MANAGER (user_data); + NMDnsManagerPrivate *priv = NM_DNS_MANAGER_GET_PRIVATE (self); + gint64 ts = nm_utils_get_monotonic_timestamp_ms (); + + _LOGW ("plugin %s child quit 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) { + _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); } gboolean diff --git a/src/dns-manager/nm-dns-plugin.c b/src/dns-manager/nm-dns-plugin.c index a82366962..d473b9deb 100644 --- a/src/dns-manager/nm-dns-plugin.c +++ b/src/dns-manager/nm-dns-plugin.c @@ -33,7 +33,7 @@ typedef struct { gboolean disposed; GPid pid; - guint32 watch_id; + guint watch_id; char *progname; char *pidfile; } NMDnsPluginPrivate; @@ -130,6 +130,7 @@ watch_cb (GPid pid, gint status, gpointer user_data) NMDnsPluginPrivate *priv = NM_DNS_PLUGIN_GET_PRIVATE (self); priv->pid = 0; + priv->watch_id = 0; g_free (priv->progname); priv->progname = NULL;