summary refs log tree commit diff
path: root/src/dns-manager
diff options
context:
space:
mode:
Diffstat (limited to 'src/dns-manager')
-rw-r--r--src/dns-manager/nm-dns-dnsmasq.c47
-rw-r--r--src/dns-manager/nm-dns-manager.c238
-rw-r--r--src/dns-manager/nm-dns-manager.h10
-rw-r--r--src/dns-manager/nm-dns-plugin.c120
4 files changed, 264 insertions, 151 deletions
diff --git a/src/dns-manager/nm-dns-dnsmasq.c b/src/dns-manager/nm-dns-dnsmasq.c
index 8663dfd8..460feaac 100644
--- a/src/dns-manager/nm-dns-dnsmasq.c
+++ b/src/dns-manager/nm-dns-dnsmasq.c
@@ -46,7 +46,20 @@ typedef struct {
 	guint32 foo;
 } NMDnsDnsmasqPrivate;
 
-/*******************************************/
+/*****************************************************************************/
+
+#define _NMLOG_DOMAIN         LOGD_DNS
+#define _NMLOG_PREFIX_NAME    "dnsmasq"
+#define _NMLOG(level, ...) \
+    G_STMT_START { \
+        nm_log ((level), _NMLOG_DOMAIN, \
+                "%s[%p]: " _NM_UTILS_MACRO_FIRST(__VA_ARGS__), \
+                _NMLOG_PREFIX_NAME, \
+                (self) \
+                _NM_UTILS_MACRO_REST(__VA_ARGS__)); \
+    } G_STMT_END
+
+/*****************************************************************************/
 
 static gboolean
 add_ip4_config (GString *str, NMIP4Config *ip4, gboolean split)
@@ -246,7 +259,7 @@ update (NMDnsPlugin *plugin,
 
 	dm_binary = nm_utils_find_helper ("dnsmasq", DNSMASQ_PATH, NULL);
 	if (!dm_binary) {
-		nm_log_warn (LOGD_DNS, "Could not find dnsmasq binary");
+		_LOGW ("could not find dnsmasq binary");
 		return FALSE;
 	}
 
@@ -283,17 +296,16 @@ update (NMDnsPlugin *plugin,
 
 	/* Write out the config file */
 	if (!g_file_set_contents (CONFFILE, conf->str, -1, &error)) {
-		nm_log_warn (LOGD_DNS, "Failed to write dnsmasq config file %s: (%d) %s",
-		             CONFFILE,
-		             error ? error->code : -1,
-		             error && error->message ? error->message : "(unknown)");
+		_LOGW ("failed to write dnsmasq config file %s: %s",
+		       CONFFILE,
+		       error->message);
 		g_clear_error (&error);
 		goto out;
 	}
 	ignored = chmod (CONFFILE, 0644);
 
-	nm_log_dbg (LOGD_DNS, "dnsmasq local caching DNS configuration:");
-	nm_log_dbg (LOGD_DNS, "%s", conf->str);
+	_LOGD ("dnsmasq local caching DNS configuration:");
+	_LOGD ("%s", conf->str);
 
 	argv[idx++] = dm_binary;
 	argv[idx++] = "--no-resolv";  /* Use only commandline */
@@ -351,18 +363,17 @@ child_quit (NMDnsPlugin *plugin, gint status)
 	if (WIFEXITED (status)) {
 		err = WEXITSTATUS (status);
 		if (err) {
-			nm_log_warn (LOGD_DNS, "dnsmasq exited with error: %s (%d)",
-			             dm_exit_code_to_msg (err),
-			             err);
+			_LOGW ("dnsmasq exited with error: %s (%d)",
+			       dm_exit_code_to_msg (err),
+			       err);
 		} else
 			failed = FALSE;
-	} else if (WIFSTOPPED (status)) {
-		nm_log_warn (LOGD_DNS, "dnsmasq stopped unexpectedly with signal %d", WSTOPSIG (status));
-	} else if (WIFSIGNALED (status)) {
-		nm_log_warn (LOGD_DNS, "dnsmasq died with signal %d", WTERMSIG (status));
-	} else {
-		nm_log_warn (LOGD_DNS, "dnsmasq died from an unknown cause");
-	}
+	} else if (WIFSTOPPED (status))
+		_LOGW ("dnsmasq stopped unexpectedly with signal %d", WSTOPSIG (status));
+	else if (WIFSIGNALED (status))
+		_LOGW ("dnsmasq died with signal %d", WTERMSIG (status));
+	else
+		_LOGW ("dnsmasq died from an unknown cause");
 	unlink (CONFFILE);
 
 	if (failed)
diff --git a/src/dns-manager/nm-dns-manager.c b/src/dns-manager/nm-dns-manager.c
index 79d345be..ec83e600 100644
--- a/src/dns-manager/nm-dns-manager.c
+++ b/src/dns-manager/nm-dns-manager.c
@@ -100,14 +100,11 @@ NM_DEFINE_SINGLETON_INSTANCE (NMDnsManager);
             const NMDnsManager *const __self = (self); \
             \
             _nm_log (__level, _NMLOG_DOMAIN, 0, \
-                     "%s: " _NM_UTILS_MACRO_FIRST (__VA_ARGS__), \
-                     ((__self == singleton_instance) \
-                        ? _NMLOG_PREFIX_NAME \
-                        : ({ \
-                                g_snprintf (__prefix, sizeof (__prefix), "%s[%p]", _NMLOG_PREFIX_NAME, __self); \
-                                __prefix; \
-                           }) \
-                     ) \
+                     "%s%s: " _NM_UTILS_MACRO_FIRST (__VA_ARGS__), \
+                     _NMLOG_PREFIX_NAME, \
+                     ((!__self || __self == singleton_instance) \
+                        ? "" \
+                        : nm_sprintf_buf (__prefix, "[%p]", __self)) \
                      _NM_UTILS_MACRO_REST (__VA_ARGS__)); \
         } \
     } G_STMT_END
@@ -128,6 +125,9 @@ typedef struct {
 
 	NMDnsManagerResolvConfMode resolv_conf_mode;
 	NMDnsManagerResolvConfManager rc_manager;
+	char *last_mode;
+	bool last_immutable:1;
+	bool mode_initialized:1;
 	NMDnsPlugin *plugin;
 
 	NMConfig *config;
@@ -164,6 +164,15 @@ typedef struct {
 	GPtrArray *nis_servers;
 } NMResolvConfData;
 
+NM_UTILS_LOOKUP_STR_DEFINE_STATIC (_rc_manager_to_string, NMDnsManagerResolvConfManager,
+	NM_UTILS_LOOKUP_DEFAULT_WARN (NULL),
+	NM_UTILS_LOOKUP_STR_ITEM (NM_DNS_MANAGER_RESOLV_CONF_MAN_NONE,       "none"),
+	NM_UTILS_LOOKUP_STR_ITEM (NM_DNS_MANAGER_RESOLV_CONF_MAN_FILE,       "file"),
+	NM_UTILS_LOOKUP_STR_ITEM (NM_DNS_MANAGER_RESOLV_CONF_MAN_RESOLVCONF, "resolvconf"),
+	NM_UTILS_LOOKUP_STR_ITEM (NM_DNS_MANAGER_RESOLV_CONF_MAN_NETCONFIG,  "netconfig"),
+	NM_UTILS_LOOKUP_ITEM_IGNORE (_NM_DNS_MANAGER_RESOLV_CONF_MAN_INTERNAL_ONLY),
+);
+
 static void
 add_string_item (GPtrArray *array, const char *str)
 {
@@ -406,12 +415,10 @@ dispatch_netconfig (NMDnsManager *self,
 	return SR_SUCCESS;
 }
 
-static gboolean
-write_resolv_conf (FILE *f,
-                   char **searches,
-                   char **nameservers,
-                   char **options,
-                   GError **error)
+static char *
+create_resolv_conf (char **searches,
+                    char **nameservers,
+                    char **options)
 {
 	gs_free char *searches_str = NULL;
 	gs_free char *nameservers_str = NULL;
@@ -439,9 +446,9 @@ write_resolv_conf (FILE *f,
 		for (i = 0; i < num; i++) {
 			if (i == 3) {
 				g_string_append (str, "# ");
-				g_string_append (str, _("NOTE: the libc resolver may not support more than 3 nameservers."));
+				g_string_append (str, "NOTE: the libc resolver may not support more than 3 nameservers.");
 				g_string_append (str, "\n# ");
-				g_string_append (str, _("The nameservers listed below may not be recognized."));
+				g_string_append (str, "The nameservers listed below may not be recognized.");
 				g_string_append_c (str, '\n');
 			}
 
@@ -452,10 +459,18 @@ write_resolv_conf (FILE *f,
 		nameservers_str = g_string_free (str, FALSE);
 	}
 
-	if (fprintf (f, "# Generated by NetworkManager\n%s%s%s",
-	             searches_str ? searches_str : "",
-	             nameservers_str ? nameservers_str : "",
-	             options_str ? options_str : "") < 0) {
+	return g_strdup_printf ("# Generated by NetworkManager\n%s%s%s",
+	                        searches_str ?: "",
+	                        nameservers_str ?: "",
+	                        options_str ?: "");
+}
+
+static gboolean
+write_resolv_conf_contents (FILE *f,
+                            const char *content,
+                            GError **error)
+{
+	if (fprintf (f, "%s", content) < 0) {
 		g_set_error (error,
 		             NM_MANAGER_ERROR,
 		             NM_MANAGER_ERROR_FAILED,
@@ -467,6 +482,19 @@ write_resolv_conf (FILE *f,
 	return TRUE;
 }
 
+static gboolean
+write_resolv_conf (FILE *f,
+                   char **searches,
+                   char **nameservers,
+                   char **options,
+                   GError **error)
+{
+	gs_free char *content = NULL;
+
+	content = create_resolv_conf (searches, nameservers, options);
+	return write_resolv_conf_contents (f, content, error);
+}
+
 static SpawnResult
 dispatch_resolvconf (NMDnsManager *self,
                      char **searches,
@@ -539,18 +567,22 @@ update_resolv_conf (NMDnsManager *self,
                     char **nameservers,
                     char **options,
                     GError **error,
-                    gboolean install_etc)
+                    NMDnsManagerResolvConfManager rc_manager)
 {
 	FILE *f;
 	struct stat st;
 	gboolean success;
+	gs_free char *content = NULL;
+	SpawnResult write_file_result = SR_SUCCESS;
 
 	/* If we are not managing /etc/resolv.conf and it points to
 	 * MY_RESOLV_CONF, don't write the private DNS configuration to
 	 * MY_RESOLV_CONF otherwise we would overwrite the changes done by
 	 * some external application.
-	 */
-	if (!install_etc) {
+	 *
+	 * This is the only situation, where we don't try to update our
+	 * internal resolv.conf file. */
+	if (rc_manager == _NM_DNS_MANAGER_RESOLV_CONF_MAN_INTERNAL_ONLY) {
 		gs_free char *path = g_file_read_link (_PATH_RESCONF, NULL);
 
 		if (g_strcmp0 (path, MY_RESOLV_CONF) == 0) {
@@ -560,6 +592,18 @@ update_resolv_conf (NMDnsManager *self,
 		}
 	}
 
+	content = create_resolv_conf (searches, nameservers, options);
+
+	if (rc_manager == NM_DNS_MANAGER_RESOLV_CONF_MAN_FILE) {
+		/* we first write to /etc/resolv.conf directly. If that fails,
+		 * we still continue to write to runstatedir but remember the
+		 * error. */
+		if (!g_file_set_contents (_PATH_RESCONF, content, -1, error)) {
+			write_file_result = SR_ERROR;
+			error = NULL;
+		}
+	}
+
 	if ((f = fopen (MY_RESOLV_CONF_TMP, "w")) == NULL) {
 		g_set_error (error,
 		             NM_MANAGER_ERROR,
@@ -570,7 +614,7 @@ update_resolv_conf (NMDnsManager *self,
 		return SR_ERROR;
 	}
 
-	success = write_resolv_conf (f, searches, nameservers, options, error);
+	success = write_resolv_conf_contents (f, content, error);
 
 	if (fclose (f) < 0) {
 		if (success) {
@@ -598,7 +642,10 @@ update_resolv_conf (NMDnsManager *self,
 		return SR_ERROR;
 	}
 
-	if (!install_etc)
+	if (rc_manager == NM_DNS_MANAGER_RESOLV_CONF_MAN_FILE)
+		return write_file_result;
+
+	if (rc_manager != NM_DNS_MANAGER_RESOLV_CONF_MAN_NONE)
 		return SR_SUCCESS;
 
 	/* A symlink pointing to NM's own resolv.conf (MY_RESOLV_CONF) is always
@@ -920,7 +967,7 @@ update_dns (NMDnsManager *self,
 	nis_domain = rc.nis_domain;
 
 	/* Let any plugins do their thing first */
-	if (update && priv->plugin) {
+	if (priv->plugin) {
 		NMDnsPlugin *plugin = priv->plugin;
 		const char *plugin_name = nm_dns_plugin_get_name (plugin);
 		GSList *vpn_configs = NULL, *dev_configs = NULL, *other_configs = NULL;
@@ -973,7 +1020,8 @@ update_dns (NMDnsManager *self,
 	if (update) {
 		switch (priv->rc_manager) {
 		case NM_DNS_MANAGER_RESOLV_CONF_MAN_NONE:
-			result = update_resolv_conf (self, searches, nameservers, options, error, TRUE);
+		case NM_DNS_MANAGER_RESOLV_CONF_MAN_FILE:
+			result = update_resolv_conf (self, searches, nameservers, options, error, priv->rc_manager);
 			resolv_conf_updated = TRUE;
 			break;
 		case NM_DNS_MANAGER_RESOLV_CONF_MAN_RESOLVCONF:
@@ -990,7 +1038,7 @@ update_dns (NMDnsManager *self,
 		if (result == SR_NOTFOUND) {
 			_LOGD ("update-dns: program not available, writing to resolv.conf");
 			g_clear_error (error);
-			result = update_resolv_conf (self, searches, nameservers, options, error, TRUE);
+			result = update_resolv_conf (self, searches, nameservers, options, error, NM_DNS_MANAGER_RESOLV_CONF_MAN_NONE);
 			resolv_conf_updated = TRUE;
 		}
 	}
@@ -998,7 +1046,7 @@ update_dns (NMDnsManager *self,
 	/* Unless we've already done it, update private resolv.conf in NMRUNDIR
 	   ignoring any errors */
 	if (!resolv_conf_updated)
-		update_resolv_conf (self, searches, nameservers, options, NULL, FALSE);
+		update_resolv_conf (self, searches, nameservers, options, NULL, _NM_DNS_MANAGER_RESOLV_CONF_MAN_INTERNAL_ONLY);
 
 	/* signal that resolv.conf was changed */
 	if (update && result == SR_SUCCESS)
@@ -1318,70 +1366,105 @@ nm_dns_manager_end_updates (NMDnsManager *self, const char *func)
 
 /******************************************************************/
 
+static bool
+_get_resconf_immutable (int *immutable_cached)
+{
+	int fd, flags;
+	int immutable;
+
+	immutable = *immutable_cached;
+	if (!NM_IN_SET (immutable, FALSE, TRUE)) {
+		immutable = FALSE;
+		fd = open (_PATH_RESCONF, O_RDONLY);
+		if (fd != -1) {
+			if (ioctl (fd, FS_IOC_GETFLAGS, &flags) != -1)
+				immutable = NM_FLAGS_HAS (flags, FS_IMMUTABLE_FL);
+			close (fd);
+		}
+		*immutable_cached = immutable;
+	}
+	return immutable;
+}
+
 NM_DEFINE_SINGLETON_GETTER (NMDnsManager, nm_dns_manager_get, NM_TYPE_DNS_MANAGER);
 
 static void
 init_resolv_conf_mode (NMDnsManager *self)
 {
 	NMDnsManagerPrivate *priv = NM_DNS_MANAGER_GET_PRIVATE (self);
-	const char *mode;
-	int fd, flags;
-
-	g_clear_object (&priv->plugin);
+	const char *mode, *mode_unknown;
+	int immutable = -1;
 
 	mode = nm_config_data_get_dns_mode (nm_config_get_data (priv->config));
-	if (!g_strcmp0 (mode, "none")) {
-		priv->resolv_conf_mode = NM_DNS_MANAGER_RESOLV_CONF_UNMANAGED;
-		goto out;
+
+	if (   priv->mode_initialized
+	    && nm_streq0 (mode, priv->last_mode)
+	    && (   nm_streq0 (mode, "none")
+	        || priv->last_immutable == _get_resconf_immutable (&immutable))) {
+		/* we call init_resolv_conf_mode() on every SIGHUP to possibly reload
+		 * when either "mode" or "immutable" changed. However, we don't want to
+		 * re-create the plugin, when the paramters didn't actually change. So
+		 * detect that we would recreate the same plugin and return early. */
+		return;
 	}
 
-	fd = open (_PATH_RESCONF, O_RDONLY);
-	if (fd != -1) {
-		if (ioctl (fd, FS_IOC_GETFLAGS, &flags) == -1)
-			flags = 0;
-		close (fd);
+	priv->mode_initialized = TRUE;
+	g_free (priv->last_mode);
+	priv->last_mode = g_strdup (mode);
+	priv->last_immutable = FALSE;
+	g_clear_object (&priv->plugin);
+	priv->resolv_conf_mode = NM_DNS_MANAGER_RESOLV_CONF_UNMANAGED;
 
-		if (flags & FS_IMMUTABLE_FL) {
-			_LOGI ("set resolv-conf-mode: none -- " _PATH_RESCONF " is immutable");
-			priv->resolv_conf_mode = NM_DNS_MANAGER_RESOLV_CONF_UNMANAGED;
-			return;
-		}
+	if (nm_streq0 (mode, "none")) {
+		_LOGI ("%s%s", "set resolv-conf-mode: ", "none");
+		return;
 	}
 
-	if (!g_strcmp0 (mode, "dnsmasq")) {
-		priv->resolv_conf_mode = NM_DNS_MANAGER_RESOLV_CONF_PROXY;
-		priv->plugin = nm_dns_dnsmasq_new ();
-	} else if (!g_strcmp0 (mode, "unbound")) {
-		priv->resolv_conf_mode = NM_DNS_MANAGER_RESOLV_CONF_PROXY;
-		priv->plugin = nm_dns_unbound_new ();
-	} else {
-		priv->resolv_conf_mode = NM_DNS_MANAGER_RESOLV_CONF_EXPLICIT;
-		if (mode && g_strcmp0 (mode, "default") != 0) {
-			_LOGW ("set resolve-conf-mode: default -- unknown configuration '%s'", mode);
-			return;
-		}
-		mode = "default";
-	}
+	priv->last_immutable = _get_resconf_immutable (&immutable);
+
+	if (NM_IN_STRSET (mode, "dnsmasq", "unbound")) {
+		if (!immutable)
+			priv->resolv_conf_mode = NM_DNS_MANAGER_RESOLV_CONF_PROXY;
+		if (nm_streq (mode, "dnsmasq"))
+			priv->plugin = nm_dns_dnsmasq_new ();
+		else
+			priv->plugin = nm_dns_unbound_new ();
 
-	if (priv->plugin) {
 		g_signal_connect (priv->plugin, NM_DNS_PLUGIN_FAILED, G_CALLBACK (plugin_failed), self);
 		g_signal_connect (priv->plugin, NM_DNS_PLUGIN_CHILD_QUIT, G_CALLBACK (plugin_child_quit), self);
+
+		_NMLOG (immutable ? LOGL_WARN : LOGL_INFO,
+		        "%s%s%s%s%s%s",
+		        "set resolv-conf-mode: ",
+		        immutable ? "none" : mode,
+		        ", plugin=\"", nm_dns_plugin_get_name (priv->plugin), "\"",
+		        immutable ? ", resolv.conf immutable" : "");
+		return;
 	}
 
-out:
-	_LOGI ("set resolv-conf-mode: %s%s%s%s", mode,
-	       NM_PRINT_FMT_QUOTED (priv->plugin, ", plugin=\"", nm_dns_plugin_get_name (priv->plugin), "\"", ""));
+	if (!immutable)
+		priv->resolv_conf_mode = NM_DNS_MANAGER_RESOLV_CONF_EXPLICIT;
+
+	mode_unknown = mode && !nm_streq (mode, "default") ? mode : NULL;
+	_NMLOG (mode_unknown ? LOGL_WARN : LOGL_INFO,
+	        "%s%s%s%s%s%s",
+	        "set resolv-conf-mode: ",
+	        immutable ? "none" : "default",
+	        NM_PRINT_FMT_QUOTED (mode_unknown, " -- unknown configuration '", mode_unknown, "'", ""),
+	        immutable ? ", resolv.conf immutable" : "");
 }
 
 static void
 init_resolv_conf_manager (NMDnsManager *self)
 {
 	NMDnsManagerPrivate *priv = NM_DNS_MANAGER_GET_PRIVATE (self);
-	const char *man, *desc = "";
+	const char *man;
 
 	man = nm_config_data_get_rc_manager (nm_config_get_data (priv->config));
 	if (!g_strcmp0 (man, "none"))
 		priv->rc_manager = NM_DNS_MANAGER_RESOLV_CONF_MAN_NONE;
+	else if (nm_streq0 (man, "file"))
+		priv->rc_manager = NM_DNS_MANAGER_RESOLV_CONF_MAN_FILE;
 	else if (!g_strcmp0 (man, "resolvconf"))
 		priv->rc_manager = NM_DNS_MANAGER_RESOLV_CONF_MAN_RESOLVCONF;
 	else if (!g_strcmp0 (man, "netconfig"))
@@ -1398,19 +1481,7 @@ init_resolv_conf_manager (NMDnsManager *self)
 			_LOGW ("unknown resolv.conf manager '%s'", man);
 	}
 
-	switch (priv->rc_manager) {
-	case NM_DNS_MANAGER_RESOLV_CONF_MAN_RESOLVCONF:
-		desc = "resolvconf";
-		break;
-	case NM_DNS_MANAGER_RESOLV_CONF_MAN_NETCONFIG:
-		desc = "netconfig";
-		break;
-	case NM_DNS_MANAGER_RESOLV_CONF_MAN_NONE:
-		desc = "none";
-		break;
-	}
-
-	_LOGI ("using resolv.conf manager '%s'", desc);
+	_LOGI ("using resolv.conf manager '%s'", _rc_manager_to_string (priv->rc_manager));
 }
 
 static void
@@ -1422,8 +1493,15 @@ config_changed_cb (NMConfig *config,
 {
 	GError *error = NULL;
 
-	if (NM_FLAGS_HAS (changes, NM_CONFIG_CHANGE_DNS_MODE))
+	if (NM_FLAGS_ANY (changes, NM_CONFIG_CHANGE_DNS_MODE |
+	                           NM_CONFIG_CHANGE_SIGHUP)) {
+		/* reload the resolv-conf mode also on SIGHUP (when DNS_MODE didn't change).
+		 * The reason is, that the configuration also depends on whether resolv.conf
+		 * is immutable, thus, without the configuration changing, we always want to
+		 * re-configure the mode. */
 		init_resolv_conf_mode (self);
+	}
+
 	if (NM_FLAGS_HAS (changes, NM_CONFIG_CHANGE_RC_MANAGER))
 		init_resolv_conf_manager (self);
 
@@ -1474,6 +1552,8 @@ dispose (GObject *object)
 		g_clear_object (&priv->plugin);
 	}
 
+	g_clear_pointer (&priv->last_mode, g_free);
+
 	/* If we're quitting, leave a valid resolv.conf in place, not one
 	 * pointing to 127.0.0.1 if any plugins were active.  Thus update
 	 * DNS after disposing of all plugins.  But if we haven't done any
diff --git a/src/dns-manager/nm-dns-manager.h b/src/dns-manager/nm-dns-manager.h
index 7a55f1a2..dd5c9e98 100644
--- a/src/dns-manager/nm-dns-manager.h
+++ b/src/dns-manager/nm-dns-manager.h
@@ -101,7 +101,13 @@ typedef enum {
 
 /**
  * NMDnsManagerResolvConfManager
- * @NM_DNS_MANAGER_RESOLV_CONF_MAN_NONE: NM directly writes resolv.conf
+ * @_NM_DNS_MANAGER_RESOLV_CONF_MAN_INTERNAL_ONLY: dummy-manager
+ *   to not write resolv.conf at all, only the internal file in
+ *   NM's run state directory.
+ * @NM_DNS_MANAGER_RESOLV_CONF_MAN_NONE: NM writes resolv.conf
+ *   by symlinking it to the run state directory.
+ * @NM_DNS_MANAGER_RESOLV_CONF_MAN_FILE: Like NONE, but instead of symlinking
+ *   resolv.conf, write it as a file.
  * @NM_DNS_MANAGER_RESOLV_CONF_MAN_RESOLVCONF: NM is managing resolv.conf
      through resolvconf
  * @NM_DNS_MANAGER_RESOLV_CONF_MAN_NETCONFIG: NM is managing resolv.conf
@@ -110,7 +116,9 @@ typedef enum {
  * NMDnsManager's management of resolv.conf
  */
 typedef enum {
+	_NM_DNS_MANAGER_RESOLV_CONF_MAN_INTERNAL_ONLY,
 	NM_DNS_MANAGER_RESOLV_CONF_MAN_NONE,
+	NM_DNS_MANAGER_RESOLV_CONF_MAN_FILE,
 	NM_DNS_MANAGER_RESOLV_CONF_MAN_RESOLVCONF,
 	NM_DNS_MANAGER_RESOLV_CONF_MAN_NETCONFIG,
 } NMDnsManagerResolvConfManager;
diff --git a/src/dns-manager/nm-dns-plugin.c b/src/dns-manager/nm-dns-plugin.c
index 8313ed13..47322d2a 100644
--- a/src/dns-manager/nm-dns-plugin.c
+++ b/src/dns-manager/nm-dns-plugin.c
@@ -25,12 +25,12 @@
 #include <sys/types.h>
 #include <sys/wait.h>
 
+#include "nm-core-internal.h"
+
 #include "nm-dns-plugin.h"
 #include "NetworkManagerUtils.h"
 
 typedef struct {
-	gboolean disposed;
-
 	GPid pid;
 	guint watch_id;
 	char *progname;
@@ -90,36 +90,60 @@ nm_dns_plugin_get_name (NMDnsPlugin *self)
 /********************************************/
 
 static void
+_clear_pidfile (NMDnsPlugin *self)
+{
+	NMDnsPluginPrivate *priv = NM_DNS_PLUGIN_GET_PRIVATE (self);
+
+	if (priv->pidfile) {
+		unlink (priv->pidfile);
+		g_free (priv->pidfile);
+		priv->pidfile = NULL;
+	}
+}
+
+static void
 kill_existing (const char *progname, const char *pidfile, const char *kill_match)
 {
-	char *contents = NULL;
 	glong pid;
-	char *proc_path = NULL;
-	char *cmdline_contents = NULL;
+	gs_free char *contents = NULL;
+	gs_free char *cmdline_contents = NULL;
+	guint64 start_time;
+	char proc_path[256];
+	gs_free_error GError *error = NULL;
 
-	if (!g_file_get_contents (pidfile, &contents, NULL, NULL))
+	if (!pidfile)
 		return;
 
-	pid = strtol (contents, NULL, 10);
-	if (pid < 1 || pid > INT_MAX)
+	if (!kill_match)
+		g_return_if_reached ();
+
+	if (!g_file_get_contents (pidfile, &contents, NULL, &error)) {
+		if (g_error_matches (error, G_FILE_ERROR, G_FILE_ERROR_NOENT))
+			return;
+		goto out;
+	}
+
+	pid = _nm_utils_ascii_str_to_int64 (contents, 10, 2, INT_MAX, -1);
+	if (pid == -1)
 		goto out;
 
-	proc_path = g_strdup_printf ("/proc/%ld/cmdline", pid);
+	start_time = nm_utils_get_start_time_for_pid (pid, NULL, NULL);
+	if (start_time == 0)
+		goto out;
+
+	nm_sprintf_buf (proc_path, "/proc/%ld/cmdline", pid);
 	if (!g_file_get_contents (proc_path, &cmdline_contents, NULL, NULL))
 		goto out;
 
-	if (strstr (cmdline_contents, kill_match)) {
-		if (kill (pid, 0) == 0) {
-			nm_log_dbg (LOGD_DNS, "Killing stale %s child process %ld", progname, pid);
-			kill (pid, SIGKILL);
-		}
-		unlink (pidfile);
-	}
+	if (!strstr (cmdline_contents, kill_match))
+		goto out;
+
+	nm_utils_kill_process_sync (pid, start_time, SIGKILL, LOGD_DNS,
+	                            progname ?: "<dns-process>",
+	                            0, 0, 1000);
 
 out:
-	g_free (cmdline_contents);
-	g_free (proc_path);
-	g_free (contents);
+	unlink (pidfile);
 }
 
 static void
@@ -133,6 +157,8 @@ watch_cb (GPid pid, gint status, gpointer user_data)
 	g_free (priv->progname);
 	priv->progname = NULL;
 
+	_clear_pidfile (self);
+
 	g_signal_emit (self, signals[CHILD_QUIT], 0, status);
 }
 
@@ -153,13 +179,11 @@ nm_dns_plugin_child_spawn (NMDnsPlugin *self,
 	g_free (priv->progname);
 	priv->progname = g_path_get_basename (argv[0]);
 
-	if (pidfile) {
-		g_return_val_if_fail (kill_match != NULL, 0);
-		kill_existing (priv->progname, pidfile, kill_match);
+	kill_existing (priv->progname, pidfile, kill_match);
 
-		g_free (priv->pidfile);
-		priv->pidfile = g_strdup (pidfile);
-	}
+	g_warn_if_fail (priv->pidfile == NULL);
+	g_clear_pointer (&priv->pidfile, g_free);
+	priv->pidfile = g_strdup (pidfile);
 
 	nm_log_info (LOGD_DNS, "DNS: starting %s...", priv->progname);
 	cmdline = g_strjoinv (" ", (char **) argv);
@@ -175,9 +199,8 @@ nm_dns_plugin_child_spawn (NMDnsPlugin *self,
 		nm_log_dbg (LOGD_DNS, "%s started with pid %d", priv->progname, priv->pid);
 		priv->watch_id = g_child_watch_add (priv->pid, (GChildWatchFunc) watch_cb, self);
 	} else {
-		nm_log_warn (LOGD_DNS, "Failed to spawn %s: (%d) %s",
-		             priv->progname, error ? error->code : -1,
-		             error && error->message ? error->message : "(unknown)");
+		nm_log_warn (LOGD_DNS, "Failed to spawn %s: %s",
+		             priv->progname, error->message);
 		g_clear_error (&error);
 	}
 
@@ -198,11 +221,7 @@ nm_dns_plugin_child_kill (NMDnsPlugin *self)
 		priv->progname = NULL;
 	}
 
-	if (priv->pidfile) {
-		unlink (priv->pidfile);
-		g_free (priv->pidfile);
-		priv->pidfile = NULL;
-	}
+	_clear_pidfile (self);
 
 	return TRUE;
 }
@@ -218,13 +237,8 @@ static void
 dispose (GObject *object)
 {
 	NMDnsPlugin *self = NM_DNS_PLUGIN (object);
-	NMDnsPluginPrivate *priv = NM_DNS_PLUGIN_GET_PRIVATE (self);
 
-	if (!priv->disposed) {
-		priv->disposed = TRUE;
-
-		nm_dns_plugin_child_kill (self);
-	}
+	nm_dns_plugin_child_kill (self);
 
 	G_OBJECT_CLASS (nm_dns_plugin_parent_class)->dispose (object);
 }
@@ -255,21 +269,21 @@ nm_dns_plugin_class_init (NMDnsPluginClass *plugin_class)
 
 	/* signals */
 	signals[FAILED] =
-		g_signal_new (NM_DNS_PLUGIN_FAILED,
-					  G_OBJECT_CLASS_TYPE (object_class),
-					  G_SIGNAL_RUN_FIRST,
-					  G_STRUCT_OFFSET (NMDnsPluginClass, failed),
-					  NULL, NULL,
-					  g_cclosure_marshal_VOID__VOID,
-					  G_TYPE_NONE, 0);
+	    g_signal_new (NM_DNS_PLUGIN_FAILED,
+	                  G_OBJECT_CLASS_TYPE (object_class),
+	                  G_SIGNAL_RUN_FIRST,
+	                  G_STRUCT_OFFSET (NMDnsPluginClass, failed),
+	                  NULL, NULL,
+	                  g_cclosure_marshal_VOID__VOID,
+	                  G_TYPE_NONE, 0);
 
 	signals[CHILD_QUIT] =
-		g_signal_new (NM_DNS_PLUGIN_CHILD_QUIT,
-					  G_OBJECT_CLASS_TYPE (object_class),
-					  G_SIGNAL_RUN_FIRST,
-					  G_STRUCT_OFFSET (NMDnsPluginClass, child_quit),
-					  NULL, NULL,
-					  g_cclosure_marshal_VOID__INT,
-					  G_TYPE_NONE, 1, G_TYPE_INT);
+	    g_signal_new (NM_DNS_PLUGIN_CHILD_QUIT,
+	                  G_OBJECT_CLASS_TYPE (object_class),
+	                  G_SIGNAL_RUN_FIRST,
+	                  G_STRUCT_OFFSET (NMDnsPluginClass, child_quit),
+	                  NULL, NULL,
+	                  g_cclosure_marshal_VOID__INT,
+	                  G_TYPE_NONE, 1, G_TYPE_INT);
 }