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:
@@ -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
|
||||
|
@@ -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;
|
||||
|
||||
|
Reference in New Issue
Block a user