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
This commit is contained in:
Beniamino Galvani
2016-01-15 15:04:39 +01:00
parent ec1185d6dd
commit 64ac910131
2 changed files with 46 additions and 7 deletions

View File

@@ -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

View File

@@ -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;