summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBeniamino Galvani <bgalvani@redhat.com>2016-01-15 15:04:39 +0100
committerBeniamino Galvani <bgalvani@redhat.com>2016-01-15 15:17:07 +0100
commit2a50450c9fd57a94be2b0eda70e6fc97dac4c0df (patch)
tree9ec6dd623d58c72e81c918cc1894f63496b97cf9
parentc4bd0cc5cb87801691122c340c7ec0c3f7190ac4 (diff)
downloadNetworkManager-bg/dns-plugin-ratelimit.tar.gz
dns-manager: prevent DNS plugins from respawning too quicklybg/dns-plugin-ratelimit
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.
-rw-r--r--src/dns-manager/nm-dns-manager.c50
-rw-r--r--src/dns-manager/nm-dns-plugin.c2
2 files changed, 46 insertions, 6 deletions
diff --git a/src/dns-manager/nm-dns-manager.c b/src/dns-manager/nm-dns-manager.c
index 01e8bf1db5..605a5b3874 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);
+ guint64 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 a82366962f..9eadf191a9 100644
--- a/src/dns-manager/nm-dns-plugin.c
+++ b/src/dns-manager/nm-dns-plugin.c
@@ -133,6 +133,8 @@ watch_cb (GPid pid, gint status, gpointer user_data)
g_free (priv->progname);
priv->progname = NULL;
+ nm_clear_g_source (&priv->watch_id);
+
g_signal_emit (self, signals[CHILD_QUIT], 0, status);
}