about summary refs log tree commit diff
path: root/src/dns-manager/nm-dns-manager.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/dns-manager/nm-dns-manager.c')
-rw-r--r--src/dns-manager/nm-dns-manager.c161
1 files changed, 100 insertions, 61 deletions
diff --git a/src/dns-manager/nm-dns-manager.c b/src/dns-manager/nm-dns-manager.c
index 6272e374..38ef08ea 100644
--- a/src/dns-manager/nm-dns-manager.c
+++ b/src/dns-manager/nm-dns-manager.c
@@ -16,7 +16,7 @@
  * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
  *
  * Copyright (C) 2004 - 2005 Colin Walters <walters@redhat.com>
- * Copyright (C) 2004 - 2011 Red Hat, Inc.
+ * Copyright (C) 2004 - 2012 Red Hat, Inc.
  * Copyright (C) 2005 - 2008 Novell, Inc.
  *   and others
  */
@@ -42,6 +42,7 @@
 #include "nm-logging.h"
 #include "backends/nm-backend.h"
 #include "NetworkManagerUtils.h"
+#include "nm-posix-signals.h"
 
 #include "nm-dns-plugin.h"
 #include "nm-dns-dnsmasq.h"
@@ -61,6 +62,8 @@ G_DEFINE_TYPE(NMDnsManager, nm_dns_manager, G_TYPE_OBJECT)
                                        NM_TYPE_DNS_MANAGER, \
                                        NMDnsManagerPrivate))
 
+#define HASH_LEN 20
+
 typedef struct {
 	gboolean disposed;
 
@@ -70,14 +73,10 @@ typedef struct {
 	NMIP6Config *ip6_device_config;
 	GSList *configs;
 	char *hostname;
+	guint updates_queue;
 
-	/* poor man's hash; we assume that the IP4 config object won't change
-	 * after it's given to us, which is (at this time) a fair assumption. So
-	 * we track the order of the currently applied IP configs and if they
-	 * haven't changed we don't need to rewrite resolv.conf.
-	 */
-	#define HLEN 6
-	gpointer hash[HLEN];
+	guint8 hash[HASH_LEN];  /* SHA1 hash of current DNS config */
+	guint8 prev_hash[HASH_LEN];  /* Hash when begin_updates() was called */
 
 	GSList *plugins;
 
@@ -218,6 +217,12 @@ netconfig_child_setup (gpointer user_data G_GNUC_UNUSED)
 {
 	pid_t pid = getpid ();
 	setpgid (pid, pid);
+
+	/*
+	 * We blocked signals in main(). We need to restore original signal
+	 * mask for netconfig here so that it can receive signals.
+	 */
+	nm_unblock_posix_signals (NULL);
 }
 
 static GPid
@@ -529,34 +534,43 @@ out:
 }
 
 static void
-compute_hash (NMDnsManager *self, gpointer *hash)
+compute_hash (NMDnsManager *self, guint8 buffer[HASH_LEN])
 {
 	NMDnsManagerPrivate *priv = NM_DNS_MANAGER_GET_PRIVATE (self);
-	gpointer check[HLEN];
+	GChecksum *sum;
 	GSList *iter;
-	int i = 0;
+	gsize len = HASH_LEN;
 
-	memset (check, 0, sizeof (check));
+	sum = g_checksum_new (G_CHECKSUM_SHA1);
+	g_assert (len == g_checksum_type_get_length (G_CHECKSUM_SHA1));
 
 	if (priv->ip4_vpn_config)
-		check[i++] = priv->ip4_vpn_config;
+		nm_ip4_config_hash (priv->ip4_vpn_config, sum, TRUE);
 	if (priv->ip4_device_config)
-		check[i++] = priv->ip4_device_config;
+		nm_ip4_config_hash (priv->ip4_device_config, sum, TRUE);
 
 	if (priv->ip6_vpn_config)
-		check[i++] = priv->ip6_vpn_config;
+		nm_ip6_config_hash (priv->ip6_vpn_config, sum, TRUE);
 	if (priv->ip6_device_config)
-		check[i++] = priv->ip6_device_config;
+		nm_ip6_config_hash (priv->ip6_device_config, sum, TRUE);
 
-	/* Add two more "other" configs if any exist */
-	for (iter = priv->configs; iter && i < HLEN; iter = g_slist_next (iter)) {
-		if (   (iter->data != priv->ip4_vpn_config)
-		    && (iter->data != priv->ip4_device_config)
-		    && (iter->data != priv->ip6_vpn_config)
-		    && (iter->data != priv->ip6_device_config))
-			check[i++] = iter->data;
+	/* add any other configs we know about */
+	for (iter = priv->configs; iter; iter = g_slist_next (iter)) {
+		if (   (iter->data == priv->ip4_vpn_config)
+		    && (iter->data == priv->ip4_device_config)
+		    && (iter->data == priv->ip6_vpn_config)
+		    && (iter->data == priv->ip6_device_config))
+			continue;
+
+		if (NM_IS_IP4_CONFIG (iter->data))
+			nm_ip4_config_hash (NM_IP4_CONFIG (iter->data), sum, TRUE);
+		else if (NM_IS_IP6_CONFIG (iter->data))
+			nm_ip6_config_hash (NM_IP6_CONFIG (iter->data), sum, TRUE);
 	}
-	memcpy (hash, check, sizeof (check));
+
+	memset (buffer, 0, sizeof (buffer));
+	g_checksum_get_digest (sum, buffer, &len);
+	g_checksum_free (sum);
 }
 
 static gboolean
@@ -581,6 +595,8 @@ update_dns (NMDnsManager *self,
 
 	priv = NM_DNS_MANAGER_GET_PRIVATE (self);
 
+	nm_log_dbg (LOGD_DNS, "updating resolv.conf");
+
 	if (iface && (iface != priv->last_iface)) {
 		g_free (priv->last_iface);
 		priv->last_iface = g_strdup (iface);
@@ -784,23 +800,6 @@ plugin_failed (NMDnsPlugin *plugin, gpointer user_data)
 	}
 }
 
-static gboolean
-config_changed (NMDnsManager *self)
-{
-	NMDnsManagerPrivate *priv = NM_DNS_MANAGER_GET_PRIVATE (self);
-	gpointer check[HLEN];
-
-	/* We only store HLEN configs; so if there are actually more than that,
-	 * we have to assume that the config has changed.
-	 */
-	if (g_slist_length (priv->configs) > HLEN)
-		return TRUE;
-
-	/* Otherwise return TRUE if the configuration has changed */
-	compute_hash (self, check);
-	return memcmp (check, priv->hash, sizeof (check)) ? TRUE : FALSE;
-}
-
 gboolean
 nm_dns_manager_add_ip4_config (NMDnsManager *mgr,
                                const char *iface,
@@ -831,10 +830,7 @@ nm_dns_manager_add_ip4_config (NMDnsManager *mgr,
 	if (!g_slist_find (priv->configs, config))
 		priv->configs = g_slist_append (priv->configs, g_object_ref (config));
 
-	if (!config_changed (mgr))
-		return TRUE;
-
-	if (!update_dns (mgr, iface, FALSE, &error)) {
+	if (!priv->updates_queue && !update_dns (mgr, iface, FALSE, &error)) {
 		nm_log_warn (LOGD_DNS, "could not commit DNS changes: (%d) %s",
 		             error ? error->code : -1,
 		             error && error->message ? error->message : "(unknown)");
@@ -871,10 +867,7 @@ nm_dns_manager_remove_ip4_config (NMDnsManager *mgr,
 
 	g_object_unref (config);
 
-	if (!config_changed (mgr))
-		return TRUE;
-
-	if (!update_dns (mgr, iface, FALSE, &error)) {
+	if (!priv->updates_queue && !update_dns (mgr, iface, FALSE, &error)) {
 		nm_log_warn (LOGD_DNS, "could not commit DNS changes: (%d) %s",
 		             error ? error->code : -1,
 		             error && error->message ? error->message : "(unknown)");
@@ -901,8 +894,6 @@ nm_dns_manager_add_ip6_config (NMDnsManager *mgr,
 
 	switch (cfg_type) {
 	case NM_DNS_IP_CONFIG_TYPE_VPN:
-		/* FIXME: not quite yet... */
-		g_return_val_if_fail (cfg_type != NM_DNS_IP_CONFIG_TYPE_VPN, FALSE);
 		priv->ip6_vpn_config = config;
 		break;
 	case NM_DNS_IP_CONFIG_TYPE_BEST_DEVICE:
@@ -916,10 +907,7 @@ nm_dns_manager_add_ip6_config (NMDnsManager *mgr,
 	if (!g_slist_find (priv->configs, config))
 		priv->configs = g_slist_append (priv->configs, g_object_ref (config));
 
-	if (!config_changed (mgr))
-		return TRUE;
-
-	if (!update_dns (mgr, iface, FALSE, &error)) {
+	if (!priv->updates_queue && !update_dns (mgr, iface, FALSE, &error)) {
 		nm_log_warn (LOGD_DNS, "could not commit DNS changes: (%d) %s",
 		             error ? error->code : -1,
 		             error && error->message ? error->message : "(unknown)");
@@ -956,10 +944,7 @@ nm_dns_manager_remove_ip6_config (NMDnsManager *mgr,
 
 	g_object_unref (config);	
 
-	if (!config_changed (mgr))
-		return TRUE;
-
-	if (!update_dns (mgr, iface, FALSE, &error)) {
+	if (!priv->updates_queue && !update_dns (mgr, iface, FALSE, &error)) {
 		nm_log_warn (LOGD_DNS, "could not commit DNS changes: (%d) %s",
 		             error ? error->code : -1,
 		             error && error->message ? error->message : "(unknown)");
@@ -997,7 +982,7 @@ nm_dns_manager_set_hostname (NMDnsManager *mgr,
 	 * wants one.  But hostname changes are system-wide and *not* tied to a
 	 * specific interface, so netconfig can't really handle this.  Fake it.
 	 */
-	if (!update_dns (mgr, priv->last_iface, FALSE, &error)) {
+	if (!priv->updates_queue && !update_dns (mgr, priv->last_iface, FALSE, &error)) {
 		nm_log_warn (LOGD_DNS, "could not commit DNS changes: (%d) %s",
 		             error ? error->code : -1,
 		             error && error->message ? error->message : "(unknown)");
@@ -1005,6 +990,58 @@ nm_dns_manager_set_hostname (NMDnsManager *mgr,
 	}
 }
 
+void
+nm_dns_manager_begin_updates (NMDnsManager *mgr, const char *func)
+{
+	NMDnsManagerPrivate *priv;
+
+	g_return_if_fail (mgr != NULL);
+	priv = NM_DNS_MANAGER_GET_PRIVATE (mgr);
+
+	/* Save current hash when starting a new batch */
+	if (priv->updates_queue == 0)
+		memcpy (priv->prev_hash, priv->hash, sizeof (priv->hash));
+
+	priv->updates_queue++;
+
+	nm_log_dbg (LOGD_DNS, "(%s): queueing DNS updates (%d)", func, priv->updates_queue);
+}
+
+void
+nm_dns_manager_end_updates (NMDnsManager *mgr, const char *func)
+{
+	NMDnsManagerPrivate *priv;
+	GError *error = NULL;
+	gboolean changed;
+	guint8 new[HASH_LEN];
+
+	g_return_if_fail (mgr != NULL);
+
+	priv = NM_DNS_MANAGER_GET_PRIVATE (mgr);
+	g_return_if_fail (priv->updates_queue > 0);
+
+	compute_hash (mgr, new);
+	changed = (memcmp (new, priv->prev_hash, sizeof (new)) != 0) ? TRUE : FALSE;
+	nm_log_dbg (LOGD_DNS, "(%s): DNS configuration %s", __func__, changed ? "changed" : "did not change");
+
+	priv->updates_queue--;
+	if ((priv->updates_queue > 0) || (changed == FALSE)) {
+		nm_log_dbg (LOGD_DNS, "(%s): no DNS changes to commit (%d)", func, priv->updates_queue);
+		return;
+	}
+
+	/* Commit all the outstanding changes */
+	nm_log_dbg (LOGD_DNS, "(%s): committing DNS changes (%d)", func, priv->updates_queue);
+	if (!update_dns (mgr, priv->last_iface, FALSE, &error)) {
+		nm_log_warn (LOGD_DNS, "could not commit DNS changes: (%d) %s",
+			         error ? error->code : -1,
+			         error && error->message ? error->message : "(unknown)");
+		g_clear_error (&error);
+	}
+
+	memset (priv->prev_hash, 0, sizeof (priv->prev_hash));
+}
+
 static void
 load_plugins (NMDnsManager *self, const char **plugins)
 {
@@ -1079,8 +1116,10 @@ nm_dns_manager_error_quark (void)
 }
 
 static void
-nm_dns_manager_init (NMDnsManager *mgr)
+nm_dns_manager_init (NMDnsManager *self)
 {
+	/* Set the initial hash */
+	compute_hash (self, NM_DNS_MANAGER_GET_PRIVATE (self)->hash);
 }
 
 static void