about summary refs log tree commit diff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/Makefile.am1
-rw-r--r--src/Makefile.in1
-rw-r--r--src/devices/bluetooth/nm-bluez-device.c2
-rw-r--r--src/devices/nm-device-ethernet.c37
-rw-r--r--src/devices/nm-device-factory.c10
-rw-r--r--src/devices/nm-device.c139
-rw-r--r--src/devices/wwan/nm-modem-broadband.c23
-rw-r--r--src/devices/wwan/nm-modem.c3
-rw-r--r--src/dhcp-manager/nm-dhcp-client.c58
-rw-r--r--src/dhcp-manager/nm-dhcp-systemd.c9
-rw-r--r--src/dns-manager/nm-dns-dnsmasq.c332
-rw-r--r--src/dns-manager/nm-dns-plugin.c106
-rw-r--r--src/dnsmasq-manager/nm-dnsmasq-manager.c16
-rw-r--r--src/nm-auth-subject.c15
-rw-r--r--src/nm-core-utils.c168
-rw-r--r--src/nm-core-utils.h5
-rw-r--r--src/org.freedesktop.NetworkManager.conf10
-rw-r--r--src/platform/nm-linux-platform.c5
-rw-r--r--src/settings/nm-settings.c84
-rw-r--r--src/settings/plugins/ifnet/connection_parser.c11
-rw-r--r--src/settings/plugins/ifupdown/interface_parser.c16
-rw-r--r--src/tests/Makefile.am1
-rw-r--r--src/tests/Makefile.in1
-rw-r--r--src/tests/test-utils.c6
24 files changed, 702 insertions, 357 deletions
diff --git a/src/Makefile.am b/src/Makefile.am
index bf668d74..aa35eefb 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -208,6 +208,7 @@ libNetworkManager_base_la_CPPFLAGS = \
 	-DNETWORKMANAGER_COMPILATION=NM_NETWORKMANAGER_COMPILATION_INSIDE_DAEMON \
 	-DNO_SYSTEMD_JOURNAL \
 	-DPREFIX=\"$(prefix)\" \
+	-DLOCALSTATEDIR=\"$(localstatedir)\" \
 	-DNMSTATEDIR=\"$(nmstatedir)\" \
 	$(GLIB_CFLAGS)
 
diff --git a/src/Makefile.in b/src/Makefile.in
index 1c8821f8..51ded6bc 100644
--- a/src/Makefile.in
+++ b/src/Makefile.in
@@ -994,6 +994,7 @@ libsystemd_nm_la_LIBADD = \
 @ENABLE_TESTS_TRUE@	-DNETWORKMANAGER_COMPILATION=NM_NETWORKMANAGER_COMPILATION_INSIDE_DAEMON \
 @ENABLE_TESTS_TRUE@	-DNO_SYSTEMD_JOURNAL \
 @ENABLE_TESTS_TRUE@	-DPREFIX=\"$(prefix)\" \
+@ENABLE_TESTS_TRUE@	-DLOCALSTATEDIR=\"$(localstatedir)\" \
 @ENABLE_TESTS_TRUE@	-DNMSTATEDIR=\"$(nmstatedir)\" \
 @ENABLE_TESTS_TRUE@	$(GLIB_CFLAGS)
 
diff --git a/src/devices/bluetooth/nm-bluez-device.c b/src/devices/bluetooth/nm-bluez-device.c
index a009746d..5ee22f93 100644
--- a/src/devices/bluetooth/nm-bluez-device.c
+++ b/src/devices/bluetooth/nm-bluez-device.c
@@ -788,7 +788,7 @@ static void
 adapter5_on_acquired (GObject *object, GAsyncResult *res, NMBluezDevice *self)
 {
 	NMBluezDevicePrivate *priv = NM_BLUEZ_DEVICE_GET_PRIVATE (self);
-	GError *error;
+	GError *error = NULL;
 	GVariant *v;
 
 	priv->adapter5 = g_dbus_proxy_new_for_bus_finish (res, &error);
diff --git a/src/devices/nm-device-ethernet.c b/src/devices/nm-device-ethernet.c
index 6225ae7b..d27f7dca 100644
--- a/src/devices/nm-device-ethernet.c
+++ b/src/devices/nm-device-ethernet.c
@@ -194,16 +194,21 @@ _update_s390_subchannels (NMDeviceEthernet *self)
 		} else if (   !strcmp (item, "layer2")
 		           || !strcmp (item, "portname")
 		           || !strcmp (item, "portno")) {
-			char *path, *value;
+			gs_free char *path = NULL, *value = NULL;
+
 			path = g_strdup_printf ("%s/%s", parent_path, item);
 			value = nm_platform_sysctl_get (NM_PLATFORM_GET, path);
-			if (value && *value)
-				g_hash_table_insert (priv->s390_options, g_strdup (item), g_strdup (value));
-			else
+
+			if (   !strcmp (item, "portname")
+			    && !g_strcmp0 (value, "no portname required")) {
+				/* Do nothing */
+			} else if (value && *value) {
+				g_hash_table_insert (priv->s390_options, g_strdup (item), value);
+				value = NULL;
+			} else
 				_LOGW (LOGD_DEVICE | LOGD_HW, "error reading %s", path);
-			g_free (path);
-			g_free (value);
 		}
+
 		if (error) {
 			_LOGW (LOGD_DEVICE | LOGD_HW, "%s", error->message);
 			g_clear_error (&error);
@@ -1433,7 +1438,9 @@ new_default_connection (NMDevice *self)
 	const GSList *connections;
 	NMSetting *setting;
 	const char *hw_address;
-	char *defname, *uuid;
+	gs_free char *defname = NULL;
+	gs_free char *uuid = NULL;
+	gs_free char *machine_id = NULL;
 
 	if (nm_config_get_no_auto_default_for_device (nm_config_get (), self))
 		return NULL;
@@ -1448,7 +1455,19 @@ new_default_connection (NMDevice *self)
 
 	connections = nm_connection_provider_get_connections (nm_connection_provider_get ());
 	defname = nm_device_ethernet_utils_get_default_wired_name (connections);
-	uuid = nm_utils_uuid_generate ();
+	if (!defname)
+		return NULL;
+
+	machine_id = nm_utils_machine_id_read ();
+
+	/* Create a stable UUID. The UUID is also the Network_ID for stable-privacy addr-gen-mode,
+	 * thus when it changes we will also generate different IPv6 addresses. */
+	uuid = _nm_utils_uuid_generate_from_strings ("default-wired",
+	                                             machine_id ?: "",
+	                                             defname,
+	                                             hw_address,
+	                                             NULL);
+
 	g_object_set (setting,
 	              NM_SETTING_CONNECTION_ID, defname,
 	              NM_SETTING_CONNECTION_TYPE, NM_SETTING_WIRED_SETTING_NAME,
@@ -1457,8 +1476,6 @@ new_default_connection (NMDevice *self)
 	              NM_SETTING_CONNECTION_UUID, uuid,
 	              NM_SETTING_CONNECTION_TIMESTAMP, (guint64) time (NULL),
 	              NULL);
-	g_free (uuid);
-	g_free (defname);
 
 	/* Lock the connection to the device */
 	setting = nm_setting_wired_new ();
diff --git a/src/devices/nm-device-factory.c b/src/devices/nm-device-factory.c
index 482eb185..7873756f 100644
--- a/src/devices/nm-device-factory.c
+++ b/src/devices/nm-device-factory.c
@@ -512,20 +512,20 @@ nm_device_factory_manager_load_factories (NMDeviceFactoryManagerFactoryFunc call
 			continue;
 		}
 
+		/* after loading glib types from the plugin, we cannot unload the library anymore.
+		 * Make it resident. */
+		g_module_make_resident (plugin);
+
 		factory = create_func (&error);
 		if (!factory) {
 			nm_log_warn (LOGD_HW, "(%s): failed to initialize device factory: %s",
 			             item, NM_G_ERROR_MSG (error));
 			g_clear_error (&error);
-			g_module_close (plugin);
 			continue;
 		}
 		g_clear_error (&error);
 
-		if (_add_factory (factory, TRUE, g_module_name (plugin), callback, user_data))
-			g_module_make_resident (plugin);
-		else
-			g_module_close (plugin);
+		_add_factory (factory, TRUE, g_module_name (plugin), callback, user_data);
 
 		g_object_unref (factory);
 	}
diff --git a/src/devices/nm-device.c b/src/devices/nm-device.c
index 08cf6dce..8472ff80 100644
--- a/src/devices/nm-device.c
+++ b/src/devices/nm-device.c
@@ -1276,7 +1276,7 @@ void
 nm_device_update_dynamic_ip_setup (NMDevice *self)
 {
 	NMDevicePrivate *priv;
-	GError *error;
+	GError *error = NULL;
 	gconstpointer addr;
 	size_t addr_length;
 
@@ -1887,6 +1887,8 @@ realize_start_setup (NMDevice *self, const NMPlatformLink *plink)
 	g_return_if_fail (nm_device_get_unmanaged_flags (self, NM_UNMANAGED_PLATFORM_INIT));
 	g_return_if_fail (priv->ip_ifindex <= 0);
 	g_return_if_fail (priv->ip_iface == NULL);
+	g_return_if_fail (!priv->queued_ip4_config_id);
+	g_return_if_fail (!priv->queued_ip6_config_id);
 
 	_LOGD (LOGD_DEVICE, "start setup of %s, kernel ifindex %d", G_OBJECT_TYPE_NAME (self), plink ? plink->ifindex : 0);
 
@@ -3382,6 +3384,15 @@ activation_source_schedule (NMDevice *self, ActivationHandleFunc func, int famil
 	act_data->id = new_id;
 }
 
+static gboolean
+activation_source_is_scheduled (NMDevice *self, ActivationHandleFunc func, int family)
+{
+	ActivationHandleData *act_data;
+
+	act_data = activation_source_get_by_family (self, family, NULL);
+	return act_data->func == func;
+}
+
 /*****************************************************************************/
 
 static gboolean
@@ -4456,6 +4467,8 @@ dhcp4_lease_change (NMDevice *self, NMIP4Config *config)
 		                    NULL,
 		                    NULL,
 		                    NULL);
+
+		nm_device_remove_pending_action (self, PENDING_ACTION_DHCP4, FALSE);
 	}
 }
 
@@ -5187,6 +5200,8 @@ dhcp6_lease_change (NMDevice *self)
 		                    settings_connection,
 		                    nm_device_get_applied_connection (self),
 		                    self, NULL, NULL, NULL);
+
+		nm_device_remove_pending_action (self, PENDING_ACTION_DHCP6, FALSE);
 	}
 }
 
@@ -6827,30 +6842,6 @@ activate_stage5_ip4_config_commit (NMDevice *self)
 	check_ip_done (self);
 }
 
-static void
-queued_ip4_config_change_clear (NMDevice *self)
-{
-	NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self);
-
-	if (priv->queued_ip4_config_id) {
-		_LOGD (LOGD_DEVICE, "clearing queued IP4 config change");
-		g_source_remove (priv->queued_ip4_config_id);
-		priv->queued_ip4_config_id = 0;
-	}
-}
-
-static void
-queued_ip6_config_change_clear (NMDevice *self)
-{
-	NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self);
-
-	if (priv->queued_ip6_config_id) {
-		_LOGD (LOGD_DEVICE, "clearing queued IP6 config change");
-		g_source_remove (priv->queued_ip6_config_id);
-		priv->queued_ip6_config_id = 0;
-	}
-}
-
 void
 nm_device_activate_schedule_ip4_config_result (NMDevice *self, NMIP4Config *config)
 {
@@ -6863,7 +6854,6 @@ nm_device_activate_schedule_ip4_config_result (NMDevice *self, NMIP4Config *conf
 	if (config)
 		priv->dev_ip4_config = g_object_ref (config);
 
-	queued_ip4_config_change_clear (self);
 	activation_source_schedule (self, activate_stage5_ip4_config_commit, AF_INET);
 }
 
@@ -7109,7 +7099,9 @@ _cleanup_ip4_pre (NMDevice *self, CleanupType cleanup_type)
 	NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self);
 
 	priv->ip4_state =  IP_NONE;
-	queued_ip4_config_change_clear (self);
+
+	if (nm_clear_g_source (&priv->queued_ip4_config_id))
+		_LOGD (LOGD_DEVICE, "clearing queued IP4 config change");
 
 	dhcp4_cleanup (self, cleanup_type, FALSE);
 	arp_cleanup (self);
@@ -7123,7 +7115,9 @@ _cleanup_ip6_pre (NMDevice *self, CleanupType cleanup_type)
 	NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self);
 
 	priv->ip6_state = IP_NONE;
-	queued_ip6_config_change_clear (self);
+
+	if (nm_clear_g_source (&priv->queued_ip6_config_id))
+		_LOGD (LOGD_DEVICE, "clearing queued IP6 config change");
 
 	dhcp6_cleanup (self, cleanup_type, FALSE);
 	linklocal6_cleanup (self);
@@ -7169,22 +7163,25 @@ _hash_check_invalid_keys_impl (GHashTable *hash, const char *setting_name, GErro
 
 		g_hash_table_iter_init (&iter, hash);
 		while (g_hash_table_iter_next (&iter, (gpointer *) &k, NULL)) {
-			for (i = 0; argv[i]; i++) {
-				if (!strcmp (argv[i], k)) {
-					first_invalid_key = k;
-					break;
-				}
-			}
-			if (first_invalid_key)
+			if (_nm_utils_strv_find_first ((char **) argv, -1, k) < 0) {
+				first_invalid_key = k;
 				break;
+			}
+		}
+		if (setting_name) {
+			g_set_error (error,
+			             NM_DEVICE_ERROR,
+			             NM_DEVICE_ERROR_INCOMPATIBLE_CONNECTION,
+			             "Can't reapply changes to '%s.%s' setting",
+			             setting_name,
+			             first_invalid_key);
+		} else {
+			g_set_error (error,
+			             NM_DEVICE_ERROR,
+			             NM_DEVICE_ERROR_INCOMPATIBLE_CONNECTION,
+			             "Can't reapply any changes to '%s' setting",
+			             first_invalid_key);
 		}
-		g_set_error (error,
-		             NM_DEVICE_ERROR,
-		             NM_DEVICE_ERROR_INCOMPATIBLE_CONNECTION,
-		             "Can't reapply changes to '%s%s%s' setting",
-		             setting_name ? : "",
-		             setting_name ? "." : "",
-		             first_invalid_key ? : "<UNKNOWN>");
 		g_return_val_if_fail (first_invalid_key, FALSE);
 		return FALSE;
 	}
@@ -7305,9 +7302,16 @@ reapply_connection (NMDevice *self,
 	                               NM_SETTING_CONNECTION_SETTING_NAME))
 		return FALSE;
 
+	/* whitelist allowed properties from "connection" setting which are allowed to differ.
+	 *
+	 * This includes UUID, there is no principal problem with reapplying a connection
+	 * and changing it's UUID. In fact, disallowing it makes it cumbersome for the user
+	 * to reapply any connection but the original settings-connection. */
 	if (!_hash_check_invalid_keys (diffs ? g_hash_table_lookup (diffs, NM_SETTING_CONNECTION_SETTING_NAME) : NULL,
 	                               NM_SETTING_CONNECTION_SETTING_NAME,
 	                               error,
+	                               NM_SETTING_CONNECTION_ID,
+	                               NM_SETTING_CONNECTION_UUID,
 	                               NM_SETTING_CONNECTION_ZONE,
 	                               NM_SETTING_CONNECTION_METERED))
 		return FALSE;
@@ -7336,6 +7340,7 @@ reapply_connection (NMDevice *self,
 		con_old = applied_clone  = nm_simple_connection_new_clone (applied);
 		con_new = applied;
 		nm_connection_replace_settings_from_connection (applied, connection);
+		nm_connection_clear_secrets (applied);
 	} else
 		con_old = con_new = applied;
 
@@ -8779,6 +8784,19 @@ update_ip4_config (NMDevice *self, gboolean initial)
 	gboolean capture_resolv_conf;
 	NMDnsManagerResolvConfMode resolv_conf_mode;
 
+	/* If a commit is scheduled, this function would potentially interfere with
+	 * it changing IP configurations before they are applied. Postpone the
+	 * update in such case.
+	 */
+	if (   !initial
+	    && activation_source_is_scheduled (self,
+	                                       activate_stage5_ip4_config_commit,
+	                                       AF_INET)) {
+		priv->queued_ip4_config_id = g_idle_add (queued_ip4_config_change, self);
+		_LOGT (LOGD_DEVICE, "IP4 update was postponed");
+		return;
+	}
+
 	ifindex = nm_device_get_ip_ifindex (self);
 	if (!ifindex)
 		return;
@@ -8857,6 +8875,19 @@ update_ip6_config (NMDevice *self, gboolean initial)
 	gboolean capture_resolv_conf;
 	NMDnsManagerResolvConfMode resolv_conf_mode;
 
+	/* If a commit is scheduled, this function would potentially interfere with
+	 * it changing IP configurations before they are applied. Postpone the
+	 * update in such case.
+	 */
+	if (   !initial
+	    && activation_source_is_scheduled (self,
+	                                       activate_stage5_ip6_config_commit,
+	                                       AF_INET6)) {
+		priv->queued_ip6_config_id = g_idle_add (queued_ip6_config_change, self);
+		_LOGT (LOGD_DEVICE, "IP6 update was postponed");
+		return;
+	}
+
 	ifindex = nm_device_get_ip_ifindex (self);
 	if (!ifindex)
 		return;
@@ -8922,8 +8953,12 @@ nm_device_capture_initial_config (NMDevice *self)
 static gboolean
 queued_ip4_config_change (gpointer user_data)
 {
-	NMDevice *self = NM_DEVICE (user_data);
-	NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self);
+	NMDevice *self = user_data;
+	NMDevicePrivate *priv;
+
+	g_return_val_if_fail (NM_IS_DEVICE (self), G_SOURCE_REMOVE);
+
+	priv = NM_DEVICE_GET_PRIVATE (self);
 
 	/* Wait for any queued state changes */
 	if (priv->queued_state.id)
@@ -8942,11 +8977,15 @@ queued_ip4_config_change (gpointer user_data)
 static gboolean
 queued_ip6_config_change (gpointer user_data)
 {
-	NMDevice *self = NM_DEVICE (user_data);
-	NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self);
+	NMDevice *self = user_data;
+	NMDevicePrivate *priv;
 	GSList *iter;
 	gboolean need_ipv6ll = FALSE;
 
+	g_return_val_if_fail (NM_IS_DEVICE (self), G_SOURCE_REMOVE);
+
+	priv = NM_DEVICE_GET_PRIVATE (self);
+
 	/* Wait for any queued state changes */
 	if (priv->queued_state.id)
 		return TRUE;
@@ -11201,6 +11240,10 @@ dispose (GObject *object)
 
 	_LOGD (LOGD_DEVICE, "disposing");
 
+	platform = nm_platform_get ();
+	g_signal_handlers_disconnect_by_func (platform, G_CALLBACK (device_ipx_changed), self);
+	g_signal_handlers_disconnect_by_func (platform, G_CALLBACK (link_changed_cb), self);
+
 	g_slist_free_full (priv->arping.dad_list, (GDestroyNotify) nm_arping_manager_destroy);
 	priv->arping.dad_list = NULL;
 
@@ -11241,10 +11284,6 @@ dispose (GObject *object)
 
 	_clear_queued_act_request (priv);
 
-	platform = nm_platform_get ();
-	g_signal_handlers_disconnect_by_func (platform, G_CALLBACK (device_ipx_changed), self);
-	g_signal_handlers_disconnect_by_func (platform, G_CALLBACK (link_changed_cb), self);
-
 	nm_clear_g_source (&priv->device_link_changed_id);
 	nm_clear_g_source (&priv->device_ip_link_changed_id);
 
diff --git a/src/devices/wwan/nm-modem-broadband.c b/src/devices/wwan/nm-modem-broadband.c
index 9539fefd..1a6e6b25 100644
--- a/src/devices/wwan/nm-modem-broadband.c
+++ b/src/devices/wwan/nm-modem-broadband.c
@@ -310,6 +310,10 @@ connect_ready (MMModemSimple *simple_iface,
 	NMModemIPMethod ip6_method = NM_MODEM_IP_METHOD_UNKNOWN;
 
 	self->priv->bearer = mm_modem_simple_connect_finish (simple_iface, res, &error);
+
+	if (!ctx)
+		return;
+
 	if (!self->priv->bearer) {
 		if (g_error_matches (error, MM_MOBILE_EQUIPMENT_ERROR, MM_MOBILE_EQUIPMENT_ERROR_SIM_PIN) ||
 		    (g_error_matches (error, MM_CORE_ERROR, MM_CORE_ERROR_UNAUTHORIZED) &&
@@ -377,9 +381,17 @@ connect_ready (MMModemSimple *simple_iface,
 static void
 send_pin_ready (MMSim *sim, GAsyncResult *result, NMModemBroadband *self)
 {
-    GError *error = NULL;
+	gs_free_error GError *error = NULL;
+
+	mm_sim_send_pin_finish (sim, result, &error);
+
+	if (g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED))
+		return;
+
+	if (!self->priv->ctx || self->priv->ctx->step != CONNECT_STEP_UNLOCK)
+		return;
 
-    if (!mm_sim_send_pin_finish (sim, result, &error)) {
+	if (error) {
 		if (g_error_matches (error, MM_MOBILE_EQUIPMENT_ERROR, MM_MOBILE_EQUIPMENT_ERROR_SIM_PIN) ||
 		    (g_error_matches (error, MM_CORE_ERROR, MM_CORE_ERROR_UNAUTHORIZED) &&
 		     mm_modem_get_unlock_required (self->priv->modem_iface) == MM_MODEM_LOCK_SIM_PIN)) {
@@ -387,9 +399,8 @@ send_pin_ready (MMSim *sim, GAsyncResult *result, NMModemBroadband *self)
 		} else {
 			g_signal_emit_by_name (self, NM_MODEM_PREPARE_RESULT, FALSE, translate_mm_error (error));
 		}
-		g_error_free (error);
 		return;
-    }
+	}
 
 	self->priv->ctx->step++;
 	connect_context_step (self);
@@ -1079,14 +1090,16 @@ simple_disconnect_ready (MMModemSimple *modem_iface,
 }
 
 static void
-disconnect (NMModem *self,
+disconnect (NMModem *modem,
             gboolean warn,
             GCancellable *cancellable,
             GAsyncReadyCallback callback,
             gpointer user_data)
 {
+	NMModemBroadband *self = NM_MODEM_BROADBAND (modem);
 	DisconnectContext *ctx;
 
+	connect_context_clear (self);
 	ctx = g_slice_new (DisconnectContext);
 	ctx->self = g_object_ref (self);
 	ctx->result = g_simple_async_result_new (G_OBJECT (self),
diff --git a/src/devices/wwan/nm-modem.c b/src/devices/wwan/nm-modem.c
index 804a6d9c..a6c77007 100644
--- a/src/devices/wwan/nm-modem.c
+++ b/src/devices/wwan/nm-modem.c
@@ -1187,6 +1187,7 @@ nm_modem_device_state_changed (NMModem *self,
 	case NM_DEVICE_STATE_UNMANAGED:
 	case NM_DEVICE_STATE_UNAVAILABLE:
 	case NM_DEVICE_STATE_FAILED:
+	case NM_DEVICE_STATE_DISCONNECTED:
 		if (priv->act_request) {
 			cancel_get_secrets (self);
 			g_object_unref (priv->act_request);
@@ -1195,7 +1196,7 @@ nm_modem_device_state_changed (NMModem *self,
 
 		if (was_connected) {
 			/* Don't bother warning on FAILED since the modem is already gone */
-			if (new_state == NM_DEVICE_STATE_FAILED)
+			if (new_state == NM_DEVICE_STATE_FAILED || new_state == NM_DEVICE_STATE_DISCONNECTED)
 				warn = FALSE;
 			/* First cleanup */
 			NM_MODEM_GET_CLASS (self)->deactivate_cleanup (self, NULL);
diff --git a/src/dhcp-manager/nm-dhcp-client.c b/src/dhcp-manager/nm-dhcp-client.c
index b8161ab5..9f4cdf3e 100644
--- a/src/dhcp-manager/nm-dhcp-client.c
+++ b/src/dhcp-manager/nm-dhcp-client.c
@@ -436,37 +436,10 @@ nm_dhcp_client_start_ip4 (NMDhcpClient *self,
 	return NM_DHCP_CLIENT_GET_CLASS (self)->ip4_start (self, dhcp_anycast_addr, last_ip4_address);
 }
 
-/* uuid_parse does not work for machine-id, so we use our own converter */
-static gboolean
-machine_id_parse (const char *in, uuid_t uu)
-{
-	const char *cp;
-	int i;
-	char buf[3];
-
-	g_return_val_if_fail (in != NULL, FALSE);
-	g_return_val_if_fail (strlen (in) == 32, FALSE);
-
-	for (i = 0; i < 32; i++) {
-		if (!g_ascii_isxdigit (in[i]))
-			return FALSE;
-	}
-
-	buf[2] = 0;
-	cp = in;
-	for (i = 0; i < 16; i++) {
-		buf[0] = *cp++;
-		buf[1] = *cp++;
-		uu[i] = ((unsigned char) strtoul (buf, NULL, 16)) & 0xFF;
-	}
-	return TRUE;
-}
-
 static GByteArray *
 generate_duid_from_machine_id (void)
 {
 	GByteArray *duid;
-	char *contents = NULL;
 	GChecksum *sum;
 	guint8 buffer[32]; /* SHA256 digest size */
 	gsize sumlen = sizeof (buffer);
@@ -474,27 +447,16 @@ generate_duid_from_machine_id (void)
 	uuid_t uuid;
 	GRand *generator;
 	guint i;
-	gboolean success = FALSE;
-
-	/* Get the machine ID from /etc/machine-id; it's always in /etc no matter
-	 * where our configured SYSCONFDIR is.  Alternatively, it might be in
-	 * LOCALSTATEDIR /lib/dbus/machine-id.
-	 */
-	if (   g_file_get_contents ("/etc/machine-id", &contents, NULL, NULL)
-	    || g_file_get_contents (LOCALSTATEDIR "/lib/dbus/machine-id", &contents, NULL, NULL)) {
-		contents = g_strstrip (contents);
-		success = machine_id_parse (contents, uuid);
-		if (success) {
-			/* Hash the machine ID so it's not leaked to the network */
-			sum = g_checksum_new (G_CHECKSUM_SHA256);
-			g_checksum_update (sum, (const guchar *) &uuid, sizeof (uuid));
-			g_checksum_get_digest (sum, buffer, &sumlen);
-			g_checksum_free (sum);
-		}
-		g_free (contents);
-	}
-
-	if (!success) {
+	gs_free char *machine_id_s = NULL;
+
+	machine_id_s = nm_utils_machine_id_read ();
+	if (nm_utils_machine_id_parse (machine_id_s, uuid)) {
+		/* Hash the machine ID so it's not leaked to the network */
+		sum = g_checksum_new (G_CHECKSUM_SHA256);
+		g_checksum_update (sum, (const guchar *) &uuid, sizeof (uuid));
+		g_checksum_get_digest (sum, buffer, &sumlen);
+		g_checksum_free (sum);
+	} else {
 		nm_log_warn (LOGD_DHCP6, "dhcp6: failed to read " SYSCONFDIR "/machine-id "
 		             "or " LOCALSTATEDIR "/lib/dbus/machine-id to generate "
 		             "DHCPv6 DUID; creating non-persistent random DUID.");
diff --git a/src/dhcp-manager/nm-dhcp-systemd.c b/src/dhcp-manager/nm-dhcp-systemd.c
index 5d49c27e..82cb3350 100644
--- a/src/dhcp-manager/nm-dhcp-systemd.c
+++ b/src/dhcp-manager/nm-dhcp-systemd.c
@@ -279,8 +279,9 @@ lease_to_ip4_config (const char *iface,
 	/* Domain Name */
 	r = sd_dhcp_lease_get_domainname (lease, &str);
 	if (r == 0) {
-		/* Multiple domains sometimes stuffed into the option */
-		char **domains = g_strsplit (str, " ", 0);
+		/* Multiple domains sometimes stuffed into option 15 "Domain Name".
+		 * As systemd escapes such characters, split them at \\032. */
+		char **domains = g_strsplit (str, "\\032", 0);
 		char **s;
 
 		for (s = domains; *s; s++) {
@@ -857,6 +858,7 @@ dhcp6_event_cb (sd_dhcp6_client *client, int event, gpointer user_data)
 		nm_dhcp_client_set_state (NM_DHCP_CLIENT (user_data), NM_DHCP_STATE_FAIL, NULL, NULL);
 		break;
 	case SD_DHCP6_CLIENT_EVENT_IP_ACQUIRE:
+	case SD_DHCP6_CLIENT_EVENT_INFORMATION_REQUEST:
 		bound6_handle (self);
 		break;
 	default:
@@ -893,6 +895,9 @@ ip6_start (NMDhcpClient *client,
 		return FALSE;
 	}
 
+	if (info_only)
+	    sd_dhcp6_client_set_information_request (priv->client6, 1);
+
 	/* NM stores the entire DUID which includes the uint16 "type", while systemd
 	 * wants the type passed separately from the following data.
 	 */
diff --git a/src/dns-manager/nm-dns-dnsmasq.c b/src/dns-manager/nm-dns-dnsmasq.c
index 5ead5ecf..4cee1608 100644
--- a/src/dns-manager/nm-dns-dnsmasq.c
+++ b/src/dns-manager/nm-dns-dnsmasq.c
@@ -32,6 +32,7 @@
 #include "nm-ip4-config.h"
 #include "nm-ip6-config.h"
 #include "nm-dns-utils.h"
+#include "nm-bus-manager.h"
 #include "NetworkManagerUtils.h"
 
 G_DEFINE_TYPE (NMDnsDnsmasq, nm_dns_dnsmasq, NM_TYPE_DNS_PLUGIN)
@@ -39,11 +40,17 @@ G_DEFINE_TYPE (NMDnsDnsmasq, nm_dns_dnsmasq, NM_TYPE_DNS_PLUGIN)
 #define NM_DNS_DNSMASQ_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), NM_TYPE_DNS_DNSMASQ, NMDnsDnsmasqPrivate))
 
 #define PIDFILE NMRUNDIR "/dnsmasq.pid"
-#define CONFFILE NMRUNDIR "/dnsmasq.conf"
 #define CONFDIR NMCONFDIR "/dnsmasq.d"
 
+#define DNSMASQ_DBUS_SERVICE "org.freedesktop.NetworkManager.dnsmasq"
+#define DNSMASQ_DBUS_PATH "/uk/org/thekelleys/dnsmasq"
+
 typedef struct {
-	guint32 foo;
+	GDBusProxy *dnsmasq;
+	GCancellable *dnsmasq_cancellable;
+	gboolean running;
+
+	GVariant *set_server_ex_args;
 } NMDnsDnsmasqPrivate;
 
 /*****************************************************************************/
@@ -61,8 +68,28 @@ typedef struct {
 
 /*****************************************************************************/
 
+static void
+add_dnsmasq_nameserver (NMDnsDnsmasq *self,
+                        GVariantBuilder *servers,
+                        const char *ip,
+                        const char *domain)
+{
+	g_return_if_fail (ip);
+
+	_LOGD ("adding nameserver '%s'%s%s%s", ip,
+	       NM_PRINT_FMT_QUOTED (domain, " for domain \"", domain, "\"", ""));
+
+	g_variant_builder_open (servers, G_VARIANT_TYPE ("as"));
+
+	g_variant_builder_add (servers, "s", ip);
+	if (domain)
+		g_variant_builder_add (servers, "s", domain);
+
+	g_variant_builder_close (servers);
+}
+
 static gboolean
-add_ip4_config (GString *str, NMIP4Config *ip4, gboolean split)
+add_ip4_config (NMDnsDnsmasq *self, GVariantBuilder *servers, NMIP4Config *ip4, gboolean split)
 {
 	char buf[INET_ADDRSTRLEN];
 	in_addr_t addr;
@@ -84,9 +111,10 @@ add_ip4_config (GString *str, NMIP4Config *ip4, gboolean split)
 			/* searches are preferred over domains */
 			n = nm_ip4_config_get_num_searches (ip4);
 			for (i = 0; i < n; i++) {
-				g_string_append_printf (str, "server=/%s/%s\n",
-				                        nm_ip4_config_get_search (ip4, i),
-				                        buf);
+				add_dnsmasq_nameserver (self,
+				                        servers,
+				                        buf,
+				                        nm_ip4_config_get_search (ip4, i));
 				added = TRUE;
 			}
 
@@ -94,9 +122,10 @@ add_ip4_config (GString *str, NMIP4Config *ip4, gboolean split)
 				/* If not searches, use any domains */
 				n = nm_ip4_config_get_num_domains (ip4);
 				for (i = 0; i < n; i++) {
-					g_string_append_printf (str, "server=/%s/%s\n",
-					                        nm_ip4_config_get_domain (ip4, i),
-					                        buf);
+					add_dnsmasq_nameserver (self,
+					                        servers,
+					                        buf,
+					                        nm_ip4_config_get_domain (ip4, i));
 					added = TRUE;
 				}
 			}
@@ -107,7 +136,7 @@ add_ip4_config (GString *str, NMIP4Config *ip4, gboolean split)
 			domains = nm_dns_utils_get_ip4_rdns_domains (ip4);
 			if (domains) {
 				for (iter = domains; iter && *iter; iter++)
-					g_string_append_printf (str, "server=/%s/%s\n", *iter, buf);
+					add_dnsmasq_nameserver (self, servers, buf, *iter);
 				g_strfreev (domains);
 				added = TRUE;
 			}
@@ -118,7 +147,8 @@ add_ip4_config (GString *str, NMIP4Config *ip4, gboolean split)
 	if (!added) {
 		for (i = 0; i < nnameservers; i++) {
 			addr = nm_ip4_config_get_nameserver (ip4, i);
-			g_string_append_printf (str, "server=%s\n", nm_utils_inet4_ntop (addr, NULL));
+			add_dnsmasq_nameserver (self, servers,
+			                        nm_utils_inet4_ntop (addr, NULL), NULL);
 		}
 	}
 
@@ -148,7 +178,7 @@ ip6_addr_to_string (const struct in6_addr *addr, const char *iface)
 }
 
 static void
-add_global_config (GString *str, const NMGlobalDnsConfig *config)
+add_global_config (NMDnsDnsmasq *self, GVariantBuilder *dnsmasq_servers, const NMGlobalDnsConfig *config)
 {
 	guint i, j;
 
@@ -163,16 +193,16 @@ add_global_config (GString *str, const NMGlobalDnsConfig *config)
 
 		for (j = 0; servers && servers[j]; j++) {
 			if (!strcmp (name, "*"))
-				g_string_append_printf (str, "server=%s\n", servers[j]);
+				add_dnsmasq_nameserver (self, dnsmasq_servers, servers[j], NULL);
 			else
-				g_string_append_printf (str, "server=/%s/%s\n", name, servers[j]);
+				add_dnsmasq_nameserver (self, dnsmasq_servers, servers[j], name);
 		}
 
 	}
 }
 
 static gboolean
-add_ip6_config (GString *str, NMIP6Config *ip6, gboolean split)
+add_ip6_config (NMDnsDnsmasq *self, GVariantBuilder *servers, NMIP6Config *ip6, gboolean split)
 {
 	const struct in6_addr *addr;
 	char *buf = NULL;
@@ -196,9 +226,10 @@ add_ip6_config (GString *str, NMIP6Config *ip6, gboolean split)
 			/* searches are preferred over domains */
 			n = nm_ip6_config_get_num_searches (ip6);
 			for (i = 0; i < n; i++) {
-				g_string_append_printf (str, "server=/%s/%s\n",
-				                        nm_ip6_config_get_search (ip6, i),
-				                        buf);
+				add_dnsmasq_nameserver (self,
+				                        servers,
+				                        buf,
+				                        nm_ip6_config_get_search (ip6, i));
 				added = TRUE;
 			}
 
@@ -206,9 +237,10 @@ add_ip6_config (GString *str, NMIP6Config *ip6, gboolean split)
 				/* If not searches, use any domains */
 				n = nm_ip6_config_get_num_domains (ip6);
 				for (i = 0; i < n; i++) {
-					g_string_append_printf (str, "server=/%s/%s\n",
-					                        nm_ip6_config_get_domain (ip6, i),
-					                        buf);
+					add_dnsmasq_nameserver (self,
+					                        servers,
+					                        buf,
+					                        nm_ip6_config_get_domain (ip6, i));
 					added = TRUE;
 				}
 			}
@@ -223,7 +255,7 @@ add_ip6_config (GString *str, NMIP6Config *ip6, gboolean split)
 			addr = nm_ip6_config_get_nameserver (ip6, i);
 			buf = ip6_addr_to_string (addr, iface);
 			if (buf) {
-				g_string_append_printf (str, "server=%s\n", buf);
+				add_dnsmasq_nameserver (self, servers, buf, NULL);
 				g_free (buf);
 			}
 		}
@@ -232,105 +264,220 @@ add_ip6_config (GString *str, NMIP6Config *ip6, gboolean split)
 	return TRUE;
 }
 
-static gboolean
-update (NMDnsPlugin *plugin,
-        const GSList *vpn_configs,
-        const GSList *dev_configs,
-        const GSList *other_configs,
-        const NMGlobalDnsConfig *global_config,
-        const char *hostname)
+static void
+dnsmasq_update_done (GObject *source, GAsyncResult *res, gpointer user_data)
 {
-	NMDnsDnsmasq *self = NM_DNS_DNSMASQ (plugin);
+	NMDnsDnsmasq *self = NM_DNS_DNSMASQ (user_data);
+	NMDnsDnsmasqPrivate *priv = NM_DNS_DNSMASQ_GET_PRIVATE (self);
+	gs_free_error GError *error = NULL;
+	gs_unref_variant GVariant *response = NULL;
+
+	response = g_dbus_proxy_call_finish (priv->dnsmasq, res, &error);
+	if (!response)
+		_LOGW ("dnsmasq update failed: %s", error->message);
+	else
+		_LOGD ("dnsmasq update successful");
+}
+
+static void
+send_dnsmasq_update (NMDnsDnsmasq *self)
+{
+	NMDnsDnsmasqPrivate *priv = NM_DNS_DNSMASQ_GET_PRIVATE (self);
+
+	if (!priv->set_server_ex_args)
+		return;
+
+	if (priv->running) {
+		_LOGD ("trying to update dnsmasq nameservers");
+
+		g_dbus_proxy_call (priv->dnsmasq,
+		                   "SetServersEx",
+		                   priv->set_server_ex_args,
+		                   G_DBUS_CALL_FLAGS_NONE,
+		                   -1,
+		                   NULL,
+		                   (GAsyncReadyCallback) dnsmasq_update_done,
+		                   self);
+		g_clear_pointer (&priv->set_server_ex_args, g_variant_unref);
+	} else
+		_LOGD ("dnsmasq not found on the bus. The nameserver update will be sent when dnsmasq appears");
+}
+
+static void
+name_owner_changed (GObject    *object,
+                    GParamSpec *pspec,
+                    gpointer    user_data)
+{
+	NMDnsDnsmasq *self = NM_DNS_DNSMASQ (user_data);
+	NMDnsDnsmasqPrivate *priv = NM_DNS_DNSMASQ_GET_PRIVATE (self);
+	gs_free char *owner = NULL;
+
+	owner = g_dbus_proxy_get_name_owner (G_DBUS_PROXY (object));
+	if (owner) {
+		_LOGI ("dnsmasq appeared as %s", owner);
+		priv->running = TRUE;
+		send_dnsmasq_update (self);
+	} else {
+		_LOGI ("dnsmasq disappeared");
+		priv->running = FALSE;
+		g_signal_emit_by_name (self, NM_DNS_PLUGIN_FAILED);
+	}
+}
+
+static void
+dnsmasq_proxy_cb (GObject *source, GAsyncResult *res, gpointer user_data)
+{
+	NMDnsDnsmasq *self;
+	NMDnsDnsmasqPrivate *priv;
+	gs_free_error GError *error = NULL;
+	gs_free char *owner = NULL;
+	GDBusProxy *proxy;
+
+	proxy = g_dbus_proxy_new_finish (res, &error);
+	if (   !proxy
+	    && g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED))
+		return;
+
+	self = NM_DNS_DNSMASQ (user_data);
+
+	if (!proxy) {
+		_LOGW ("failed to connect to dnsmasq via DBus: %s", error->message);
+		g_signal_emit_by_name (self, NM_DNS_PLUGIN_FAILED);
+		return;
+	}
+
+	priv = NM_DNS_DNSMASQ_GET_PRIVATE (self);
+
+	priv->dnsmasq = proxy;
+	nm_clear_g_cancellable (&priv->dnsmasq_cancellable);
+
+	_LOGD ("dnsmasq proxy creation successful");
+
+	g_signal_connect (priv->dnsmasq, "notify::g-name-owner",
+	                  G_CALLBACK (name_owner_changed), self);
+	owner = g_dbus_proxy_get_name_owner (priv->dnsmasq);
+	priv->running = (owner != NULL);
+
+	if (priv->running)
+		send_dnsmasq_update (self);
+}
+
+static void
+start_dnsmasq (NMDnsDnsmasq *self)
+{
+	NMDnsDnsmasqPrivate *priv = NM_DNS_DNSMASQ_GET_PRIVATE (self);
 	const char *dm_binary;
-	GString *conf;
-	GSList *iter;
 	const char *argv[15];
-	GError *error = NULL;
-	int ignored;
 	GPid pid = 0;
 	guint idx = 0;
+	NMBusManager *dbus_mgr;
+	GDBusConnection *connection;
+
 
-	/* Kill the old dnsmasq; there doesn't appear to be a way to get dnsmasq
-	 * to reread the config file using SIGHUP or similar.  This is a small race
-	 * here when restarting dnsmasq when DNS requests could go to the upstream
-	 * servers instead of to dnsmasq.
-	 */
-	nm_dns_plugin_child_kill (plugin);
+	if (   priv->running
+	    || priv->dnsmasq
+	    || priv->dnsmasq_cancellable)
+		return;
 
 	dm_binary = nm_utils_find_helper ("dnsmasq", DNSMASQ_PATH, NULL);
 	if (!dm_binary) {
 		_LOGW ("could not find dnsmasq binary");
-		return FALSE;
+		return;
 	}
 
-	/* Build up the new dnsmasq config file */
-	conf = g_string_sized_new (150);
+	argv[idx++] = dm_binary;
+	argv[idx++] = "--no-resolv";  /* Use only commandline */
+	argv[idx++] = "--keep-in-foreground";
+	argv[idx++] = "--no-hosts"; /* don't use /etc/hosts to resolve */
+	argv[idx++] = "--bind-interfaces";
+	argv[idx++] = "--pid-file=" PIDFILE;
+	argv[idx++] = "--listen-address=127.0.0.1"; /* Should work for both 4 and 6 */
+	argv[idx++] = "--cache-size=400";
+	argv[idx++] = "--conf-file=/dev/null"; /* avoid loading /etc/dnsmasq.conf */
+	argv[idx++] = "--proxy-dnssec"; /* Allow DNSSEC to pass through */
+	argv[idx++] = "--enable-dbus=" DNSMASQ_DBUS_SERVICE;
+
+	/* dnsmasq exits if the conf dir is not present */
+	if (g_file_test (CONFDIR, G_FILE_TEST_IS_DIR))
+		argv[idx++] = "--conf-dir=" CONFDIR;
+
+	argv[idx++] = NULL;
+	nm_assert (idx <= G_N_ELEMENTS (argv));
+
+	/* And finally spawn dnsmasq */
+	pid = nm_dns_plugin_child_spawn (NM_DNS_PLUGIN (self), argv, PIDFILE, "bin/dnsmasq");
+	if (!pid)
+		return;
+
+	dbus_mgr = nm_bus_manager_get ();
+	g_return_if_fail (dbus_mgr);
+
+	connection = nm_bus_manager_get_connection (dbus_mgr);
+	g_return_if_fail (connection);
+
+	priv->dnsmasq_cancellable = g_cancellable_new ();
+	g_dbus_proxy_new (connection,
+	                  G_DBUS_PROXY_FLAGS_DO_NOT_AUTO_START,
+	                  NULL,
+	                  DNSMASQ_DBUS_SERVICE,
+	                  DNSMASQ_DBUS_PATH,
+	                  DNSMASQ_DBUS_SERVICE,
+	                  priv->dnsmasq_cancellable,
+	                  dnsmasq_proxy_cb,
+	                  self);
+}
+
+static gboolean
+update (NMDnsPlugin *plugin,
+        const GSList *vpn_configs,
+        const GSList *dev_configs,
+        const GSList *other_configs,
+        const NMGlobalDnsConfig *global_config,
+        const char *hostname)
+{
+	NMDnsDnsmasq *self = NM_DNS_DNSMASQ (plugin);
+	NMDnsDnsmasqPrivate *priv = NM_DNS_DNSMASQ_GET_PRIVATE (self);
+	const GSList *iter;
+	GVariantBuilder servers;
+
+	start_dnsmasq (self);
+
+	g_variant_builder_init (&servers, G_VARIANT_TYPE ("aas"));
 
 	if (global_config)
-		add_global_config (conf, global_config);
+		add_global_config (self, &servers, global_config);
 	else {
 		/* Use split DNS for VPN configs */
-		for (iter = (GSList *) vpn_configs; iter; iter = g_slist_next (iter)) {
+		for (iter = vpn_configs; iter; iter = g_slist_next (iter)) {
 			if (NM_IS_IP4_CONFIG (iter->data))
-				add_ip4_config (conf, NM_IP4_CONFIG (iter->data), TRUE);
+				add_ip4_config (self, &servers, iter->data, TRUE);
 			else if (NM_IS_IP6_CONFIG (iter->data))
-				add_ip6_config (conf, NM_IP6_CONFIG (iter->data), TRUE);
+				add_ip6_config (self, &servers, iter->data, TRUE);
 		}
 
 		/* Now add interface configs without split DNS */
-		for (iter = (GSList *) dev_configs; iter; iter = g_slist_next (iter)) {
+		for (iter = dev_configs; iter; iter = g_slist_next (iter)) {
 			if (NM_IS_IP4_CONFIG (iter->data))
-				add_ip4_config (conf, NM_IP4_CONFIG (iter->data), FALSE);
+				add_ip4_config (self, &servers, iter->data, FALSE);
 			else if (NM_IS_IP6_CONFIG (iter->data))
-				add_ip6_config (conf, NM_IP6_CONFIG (iter->data), FALSE);
+				add_ip6_config (self, &servers, iter->data, FALSE);
 		}
 
 		/* And any other random configs */
-		for (iter = (GSList *) other_configs; iter; iter = g_slist_next (iter)) {
+		for (iter = other_configs; iter; iter = g_slist_next (iter)) {
 			if (NM_IS_IP4_CONFIG (iter->data))
-				add_ip4_config (conf, NM_IP4_CONFIG (iter->data), FALSE);
+				add_ip4_config (self, &servers, iter->data, FALSE);
 			else if (NM_IS_IP6_CONFIG (iter->data))
-				add_ip6_config (conf, NM_IP6_CONFIG (iter->data), FALSE);
+				add_ip6_config (self, &servers, iter->data, FALSE);
 		}
 	}
 
-	/* Write out the config file */
-	if (!g_file_set_contents (CONFFILE, conf->str, -1, &error)) {
-		_LOGW ("failed to write dnsmasq config file %s: %s",
-		       CONFFILE,
-		       error->message);
-		g_clear_error (&error);
-		goto out;
-	}
-	ignored = chmod (CONFFILE, 0644);
-
-	_LOGD ("dnsmasq local caching DNS configuration:");
-	_LOGD ("%s", conf->str);
-
-	argv[idx++] = dm_binary;
-	argv[idx++] = "--no-resolv";  /* Use only commandline */
-	argv[idx++] = "--keep-in-foreground";
-	argv[idx++] = "--no-hosts"; /* don't use /etc/hosts to resolve */
-	argv[idx++] = "--bind-interfaces";
-	argv[idx++] = "--pid-file=" PIDFILE;
-	argv[idx++] = "--listen-address=127.0.0.1"; /* Should work for both 4 and 6 */
-	argv[idx++] = "--conf-file=" CONFFILE;
-	argv[idx++] = "--cache-size=400";
-	argv[idx++] = "--proxy-dnssec"; /* Allow DNSSEC to pass through */
-
-	/* dnsmasq exits if the conf dir is not present */
-	if (g_file_test (CONFDIR, G_FILE_TEST_IS_DIR))
-		argv[idx++] = "--conf-dir=" CONFDIR;
+	g_clear_pointer (&priv->set_server_ex_args, g_variant_unref);
+	priv->set_server_ex_args = g_variant_ref_sink (g_variant_new ("(aas)", &servers));
 
-	argv[idx++] = NULL;
-	g_warn_if_fail (idx <= G_N_ELEMENTS (argv));
+	send_dnsmasq_update (self);
 
-	/* And finally spawn dnsmasq */
-	pid = nm_dns_plugin_child_spawn (NM_DNS_PLUGIN (self), argv, PIDFILE, "bin/dnsmasq");
-
-out:
-	g_string_free (conf, TRUE);
-	return pid ? TRUE : FALSE;
+	return TRUE;
 }
 
 /****************************************************************/
@@ -374,7 +521,6 @@ child_quit (NMDnsPlugin *plugin, gint status)
 		_LOGW ("dnsmasq died with signal %d", WTERMSIG (status));
 	else
 		_LOGW ("dnsmasq died from an unknown cause");
-	unlink (CONFFILE);
 
 	if (failed)
 		g_signal_emit_by_name (self, NM_DNS_PLUGIN_FAILED);
@@ -410,7 +556,13 @@ nm_dns_dnsmasq_init (NMDnsDnsmasq *self)
 static void
 dispose (GObject *object)
 {
-	unlink (CONFFILE);
+	NMDnsDnsmasqPrivate *priv = NM_DNS_DNSMASQ_GET_PRIVATE (object);
+
+	nm_clear_g_cancellable (&priv->dnsmasq_cancellable);
+
+	g_clear_object (&priv->dnsmasq);
+
+	g_clear_pointer (&priv->set_server_ex_args, g_variant_unref);
 
 	G_OBJECT_CLASS (nm_dns_dnsmasq_parent_class)->dispose (object);
 }
diff --git a/src/dns-manager/nm-dns-plugin.c b/src/dns-manager/nm-dns-plugin.c
index 47322d2a..d5cb8822 100644
--- a/src/dns-manager/nm-dns-plugin.c
+++ b/src/dns-manager/nm-dns-plugin.c
@@ -48,7 +48,29 @@ enum {
 };
 static guint signals[LAST_SIGNAL] = { 0 };
 
-/********************************************/
+/******************************************************************************/
+
+#define _NMLOG_PREFIX_NAME                "dns-plugin"
+#define _NMLOG_DOMAIN                     LOGD_DNS
+#define _NMLOG(level, ...) \
+    G_STMT_START { \
+        const NMLogLevel __level = (level); \
+        \
+        if (nm_logging_enabled (__level, _NMLOG_DOMAIN)) { \
+            char __prefix[20]; \
+            const NMDnsPlugin *const __self = (self); \
+            \
+            _nm_log (__level, _NMLOG_DOMAIN, 0, \
+                     "%s%s: " _NM_UTILS_MACRO_FIRST (__VA_ARGS__), \
+                     _NMLOG_PREFIX_NAME, \
+                     (!__self \
+                        ? "" \
+                        : nm_sprintf_buf (__prefix, "[%p]", __self)) \
+                     _NM_UTILS_MACRO_REST (__VA_ARGS__)); \
+        } \
+    } G_STMT_END
+
+/******************************************************************************/
 
 gboolean
 nm_dns_plugin_update (NMDnsPlugin *self,
@@ -96,8 +118,7 @@ _clear_pidfile (NMDnsPlugin *self)
 
 	if (priv->pidfile) {
 		unlink (priv->pidfile);
-		g_free (priv->pidfile);
-		priv->pidfile = NULL;
+		g_clear_pointer (&priv->pidfile, g_free);
 	}
 }
 
@@ -138,7 +159,7 @@ kill_existing (const char *progname, const char *pidfile, const char *kill_match
 	if (!strstr (cmdline_contents, kill_match))
 		goto out;
 
-	nm_utils_kill_process_sync (pid, start_time, SIGKILL, LOGD_DNS,
+	nm_utils_kill_process_sync (pid, start_time, SIGKILL, _NMLOG_DOMAIN,
 	                            progname ?: "<dns-process>",
 	                            0, 0, 1000);
 
@@ -154,9 +175,7 @@ watch_cb (GPid pid, gint status, gpointer user_data)
 
 	priv->pid = 0;
 	priv->watch_id = 0;
-	g_free (priv->progname);
-	priv->progname = NULL;
-
+	g_clear_pointer (&priv->progname, g_free);
 	_clear_pidfile (self);
 
 	g_signal_emit (self, signals[CHILD_QUIT], 0, status);
@@ -168,43 +187,47 @@ nm_dns_plugin_child_spawn (NMDnsPlugin *self,
                            const char *pidfile,
                            const char *kill_match)
 {
-	NMDnsPluginPrivate *priv = NM_DNS_PLUGIN_GET_PRIVATE (self);
+	NMDnsPluginPrivate *priv;
 	GError *error = NULL;
-	char *cmdline;
+	GPid pid;
+	gs_free char *cmdline = NULL;
+	gs_free char *progname = NULL;
 
-	g_return_val_if_fail (argv != NULL, 0);
-	g_return_val_if_fail (argv[0] != NULL, 0);
+	g_return_val_if_fail (argv && argv[0], 0);
+	g_return_val_if_fail (NM_IS_DNS_PLUGIN (self), 0);
 
-	g_warn_if_fail (priv->progname == NULL);
-	g_free (priv->progname);
-	priv->progname = g_path_get_basename (argv[0]);
+	priv = NM_DNS_PLUGIN_GET_PRIVATE (self);
 
-	kill_existing (priv->progname, pidfile, kill_match);
+	g_return_val_if_fail (!priv->pid, 0);
+	nm_assert (!priv->progname);
+	nm_assert (!priv->watch_id);
+	nm_assert (!priv->pidfile);
 
-	g_warn_if_fail (priv->pidfile == NULL);
-	g_clear_pointer (&priv->pidfile, g_free);
-	priv->pidfile = g_strdup (pidfile);
+	progname = g_path_get_basename (argv[0]);
+	kill_existing (progname, pidfile, kill_match);
 
-	nm_log_info (LOGD_DNS, "DNS: starting %s...", priv->progname);
-	cmdline = g_strjoinv (" ", (char **) argv);
-	nm_log_dbg (LOGD_DNS, "DNS: command line: %s", cmdline);
-	g_free (cmdline);
+	_LOGI ("starting %s...", progname);
+	_LOGD ("command line: %s",
+	       (cmdline = g_strjoinv (" ", (char **) argv)));
 
-	priv->pid = 0;
-	if (g_spawn_async (NULL, (char **) argv, NULL,
+	if (!g_spawn_async (NULL, (char **) argv, NULL,
 	                   G_SPAWN_DO_NOT_REAP_CHILD,
 	                   nm_utils_setpgid, NULL,
-	                   &priv->pid,
+	                   &pid,
 	                   &error)) {
-		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: %s",
-		             priv->progname, error->message);
+		_LOGW ("failed to spawn %s: %s",
+		       progname, error->message);
 		g_clear_error (&error);
+		return 0;
 	}
 
-	return priv->pid;
+	_LOGD ("%s started with pid %d", progname, pid);
+	priv->watch_id = g_child_watch_add (pid, (GChildWatchFunc) watch_cb, self);
+	priv->pid = pid;
+	priv->progname = nm_unauto (&progname);
+	priv->pidfile = g_strdup (pidfile);
+
+	return pid;
 }
 
 gboolean
@@ -213,14 +236,12 @@ nm_dns_plugin_child_kill (NMDnsPlugin *self)
 	NMDnsPluginPrivate *priv = NM_DNS_PLUGIN_GET_PRIVATE (self);
 
 	nm_clear_g_source (&priv->watch_id);
-
 	if (priv->pid) {
-		nm_utils_kill_child_sync (priv->pid, SIGTERM, LOGD_DNS, priv->progname, NULL, 1000, 0);
+		nm_utils_kill_child_sync (priv->pid, SIGTERM, _NMLOG_DOMAIN,
+		                          priv->progname ?: "<dns-process>", NULL, 1000, 0);
 		priv->pid = 0;
-		g_free (priv->progname);
-		priv->progname = NULL;
+		g_clear_pointer (&priv->progname, g_free);
 	}
-
 	_clear_pidfile (self);
 
 	return TRUE;
@@ -244,18 +265,6 @@ dispose (GObject *object)
 }
 
 static void
-finalize (GObject *object)
-{
-	NMDnsPlugin *self = NM_DNS_PLUGIN (object);
-	NMDnsPluginPrivate *priv = NM_DNS_PLUGIN_GET_PRIVATE (self);
-
-	g_free (priv->progname);
-	g_free (priv->pidfile);
-
-	G_OBJECT_CLASS (nm_dns_plugin_parent_class)->finalize (object);
-}
-
-static void
 nm_dns_plugin_class_init (NMDnsPluginClass *plugin_class)
 {
 	GObjectClass *object_class = G_OBJECT_CLASS (plugin_class);
@@ -264,7 +273,6 @@ nm_dns_plugin_class_init (NMDnsPluginClass *plugin_class)
 
 	/* virtual methods */
 	object_class->dispose = dispose;
-	object_class->finalize = finalize;
 	plugin_class->is_caching = is_caching;
 
 	/* signals */
diff --git a/src/dnsmasq-manager/nm-dnsmasq-manager.c b/src/dnsmasq-manager/nm-dnsmasq-manager.c
index 31c1db24..764aba29 100644
--- a/src/dnsmasq-manager/nm-dnsmasq-manager.c
+++ b/src/dnsmasq-manager/nm-dnsmasq-manager.c
@@ -221,7 +221,7 @@ dm_watch_cb (GPid pid, gint status, gpointer user_data)
 
 static NMCmdLine *
 create_dm_cmd_line (const char *iface,
-                    const NMPlatformIP4Address *listen_address,
+                    const NMIP4Config *ip4_config,
                     const char *pidfile,
                     GError **error)
 {
@@ -232,7 +232,9 @@ create_dm_cmd_line (const char *iface,
 	char localaddr[INET_ADDRSTRLEN];
 	char *error_desc = NULL;
 	const char *dm_binary;
+	const NMPlatformIP4Address *listen_address;
 
+	listen_address = nm_ip4_config_get_address (ip4_config, 0);
 	g_return_val_if_fail (listen_address, NULL);
 
 	dm_binary = nm_utils_find_helper ("dnsmasq", DNSMASQ_PATH, error);
@@ -290,10 +292,12 @@ create_dm_cmd_line (const char *iface,
 	nm_cmd_line_add_string (cmd, s->str);
 	g_string_free (s, TRUE);
 
-	s = g_string_new ("--dhcp-option=option:router,");
-	g_string_append (s, localaddr);
-	nm_cmd_line_add_string (cmd, s->str);
-	g_string_free (s, TRUE);
+	if (!nm_ip4_config_get_never_default (ip4_config)) {
+		s = g_string_new ("--dhcp-option=option:router,");
+		g_string_append (s, localaddr);
+		nm_cmd_line_add_string (cmd, s->str);
+		g_string_free (s, TRUE);
+	}
 
 	nm_cmd_line_add_string (cmd, "--dhcp-lease-max=50");
 
@@ -365,7 +369,7 @@ nm_dnsmasq_manager_start (NMDnsMasqManager *manager,
 
 	kill_existing_by_pidfile (priv->pidfile);
 
-	dm_cmd = create_dm_cmd_line (priv->iface, nm_ip4_config_get_address (ip4_config, 0), priv->pidfile, error);
+	dm_cmd = create_dm_cmd_line (priv->iface, ip4_config, priv->pidfile, error);
 	if (!dm_cmd)
 		return FALSE;
 
diff --git a/src/nm-auth-subject.c b/src/nm-auth-subject.c
index 494d52d3..86b873f0 100644
--- a/src/nm-auth-subject.c
+++ b/src/nm-auth-subject.c
@@ -360,9 +360,18 @@ constructed (GObject *object)
 		priv->unix_process.start_time = nm_utils_get_start_time_for_pid (priv->unix_process.pid, NULL, NULL);
 
 		if (!priv->unix_process.start_time) {
-			/* could not detect the process start time. The subject is invalid, but don't
-			 * assert against it. */
-			_clear_private (priv);
+			/* Is the process already gone? Then fail creation of the auth subject
+			 * by clearing the type. */
+			if (kill (priv->unix_process.pid, 0) != 0)
+				_clear_private (priv);
+
+			/* Otherwise, although we didn't detect a start_time, the process is still around.
+			 * That could be due to procfs mounted with hidepid. So just accept the request.
+			 *
+			 * Polkit on the other side, will accept 0 and try to lookup /proc/$PID/stat
+			 * itself (and if it fails to do so, assume a start-time of 0 and proceed).
+			 * The only combination that would fail here, is when NM is able to read the
+			 * start-time, but polkit is not. */
 		}
 		return;
 	default:
diff --git a/src/nm-core-utils.c b/src/nm-core-utils.c
index bbe46654..ef44a139 100644
--- a/src/nm-core-utils.c
+++ b/src/nm-core-utils.c
@@ -2559,6 +2559,121 @@ nm_utils_is_specific_hostname (const char *name)
 
 /******************************************************************/
 
+gboolean
+nm_utils_machine_id_parse (const char *id_str, /*uuid_t*/ guchar *out_uuid)
+{
+	int i;
+	guint8 v0, v1;
+
+	if (!id_str)
+		return FALSE;
+
+	for (i = 0; i < 32; i++) {
+		if (!g_ascii_isxdigit (id_str[i]))
+			return FALSE;
+	}
+	if (id_str[i] != '\0')
+		return FALSE;
+
+	if (out_uuid) {
+		for (i = 0; i < 16; i++) {
+			v0 = g_ascii_xdigit_value (*(id_str++));
+			v1 = g_ascii_xdigit_value (*(id_str++));
+			out_uuid[i] = (v0 << 4) + v1;
+		}
+	}
+	return TRUE;
+}
+
+char *
+nm_utils_machine_id_read (void)
+{
+	gs_free char *contents = NULL;
+	int i;
+
+	/* Get the machine ID from /etc/machine-id; it's always in /etc no matter
+	 * where our configured SYSCONFDIR is.  Alternatively, it might be in
+	 * LOCALSTATEDIR /lib/dbus/machine-id.
+	 */
+	if (   !g_file_get_contents ("/etc/machine-id", &contents, NULL, NULL)
+	    && !g_file_get_contents (LOCALSTATEDIR "/lib/dbus/machine-id", &contents, NULL, NULL))
+		return FALSE;
+
+	contents = g_strstrip (contents);
+
+	for (i = 0; i < 32; i++) {
+		if (!g_ascii_isxdigit (contents[i]))
+			return FALSE;
+		if (contents[i] >= 'A' && contents[i] <= 'F') {
+			/* canonicalize to lower-case */
+			contents[i] = 'a' + (contents[i] - 'A');
+		}
+	}
+	if (contents[i] != '\0')
+		return FALSE;
+
+	return nm_unauto (&contents);
+}
+
+/*****************************************************************************/
+
+guint8 *
+nm_utils_secret_key_read (gsize *out_key_len, GError **error)
+{
+	guint8 *secret_key = NULL;
+	gsize key_len;
+
+	/* out_key_len is not optional, because without it you cannot safely
+	 * access the returned memory. */
+	*out_key_len = 0;
+
+	/* Let's try to load a saved secret key first. */
+	if (g_file_get_contents (NMSTATEDIR "/secret_key", (char **) &secret_key, &key_len, NULL)) {
+		if (key_len < 16) {
+			g_set_error_literal (error, NM_UTILS_ERROR, NM_UTILS_ERROR_UNKNOWN,
+			                     "Key is too short to be usable");
+			key_len = 0;
+		}
+	} else {
+		int urandom = open ("/dev/urandom", O_RDONLY);
+		mode_t key_mask;
+
+		if (urandom == -1) {
+			g_set_error (error, NM_UTILS_ERROR, NM_UTILS_ERROR_UNKNOWN,
+			             "Can't open /dev/urandom: %s", strerror (errno));
+			key_len = 0;
+			goto out;
+		}
+
+		/* RFC7217 mandates the key SHOULD be at least 128 bits.
+		 * Let's use twice as much. */
+		key_len = 32;
+		secret_key = g_malloc (key_len);
+
+		key_mask = umask (0077);
+		if (read (urandom, secret_key, key_len) == key_len) {
+			if (!g_file_set_contents (NMSTATEDIR "/secret_key", (char *) secret_key, key_len, error)) {
+				g_prefix_error (error, "Can't write " NMSTATEDIR "/secret_key: ");
+				key_len = 0;
+			}
+		} else {
+			g_set_error_literal (error, NM_UTILS_ERROR, NM_UTILS_ERROR_UNKNOWN,
+			                     "Could not obtain a secret");
+			key_len = 0;
+		}
+		umask (key_mask);
+		close (urandom);
+	}
+
+out:
+	if (key_len) {
+		*out_key_len = key_len;
+		return secret_key;
+	}
+	g_free (secret_key);
+	return NULL;
+}
+
 /* Returns the "u" (universal/local) bit value for a Modified EUI-64 */
 static gboolean
 get_gre_eui64_u_bit (guint32 addr)
@@ -2686,7 +2801,7 @@ _set_stable_privacy (struct in6_addr *addr,
                      const char *ifname,
                      const char *uuid,
                      guint dad_counter,
-                     gchar *secret_key,
+                     guint8 *secret_key,
                      gsize key_len,
                      GError **error)
 {
@@ -2744,9 +2859,8 @@ nm_utils_ipv6_addr_set_stable_privacy (struct in6_addr *addr,
                                        guint dad_counter,
                                        GError **error)
 {
-	gchar *secret_key = NULL;
+	gs_free guint8 *secret_key = NULL;
 	gsize key_len = 0;
-	gboolean success = FALSE;
 
 	if (dad_counter >= RFC7217_IDGEN_RETRIES) {
 		g_set_error_literal (error, NM_UTILS_ERROR, NM_UTILS_ERROR_UNKNOWN,
@@ -2754,50 +2868,12 @@ nm_utils_ipv6_addr_set_stable_privacy (struct in6_addr *addr,
 		return FALSE;
 	}
 
-	/* Let's try to load a saved secret key first. */
-	if (g_file_get_contents (NMSTATEDIR "/secret_key", &secret_key, &key_len, NULL)) {
-		if (key_len < 16) {
-			g_set_error_literal (error, NM_UTILS_ERROR, NM_UTILS_ERROR_UNKNOWN,
-			                     "Key is too short to be usable");
-			key_len = 0;
-		}
-	} else {
-		int urandom = open ("/dev/urandom", O_RDONLY);
-		mode_t key_mask;
-
-		if (urandom == -1) {
-			g_set_error (error, NM_UTILS_ERROR, NM_UTILS_ERROR_UNKNOWN,
-			             "Can't open /dev/urandom: %s", strerror (errno));
-			return FALSE;
-		}
-
-		/* RFC7217 mandates the key SHOULD be at least 128 bits.
-		 * Let's use twice as much. */
-		key_len = 32;
-		secret_key = g_malloc (key_len);
-
-		key_mask = umask (0077);
-		if (read (urandom, secret_key, key_len) == key_len) {
-			if (!g_file_set_contents (NMSTATEDIR "/secret_key", secret_key, key_len, error)) {
-				g_prefix_error (error, "Can't write " NMSTATEDIR "/secret_key: ");
-				key_len = 0;
-			}
-		} else {
-			g_set_error_literal (error, NM_UTILS_ERROR, NM_UTILS_ERROR_UNKNOWN,
-			                     "Could not obtain a secret");
-			key_len = 0;
-		}
-		umask (key_mask);
-		close (urandom);
-	}
-
-	if (key_len) {
-		success = _set_stable_privacy (addr, ifname, uuid, dad_counter,
-		                               secret_key, key_len, error);
-	}
+	secret_key = nm_utils_secret_key_read (&key_len, error);
+	if (!secret_key)
+		return FALSE;
 
-	g_free (secret_key);
-	return success;
+	return _set_stable_privacy (addr, ifname, uuid, dad_counter,
+	                            secret_key, key_len, error);
 }
 
 /**
diff --git a/src/nm-core-utils.h b/src/nm-core-utils.h
index 528288c3..ad0f995f 100644
--- a/src/nm-core-utils.h
+++ b/src/nm-core-utils.h
@@ -306,6 +306,11 @@ const char *nm_utils_ip4_property_path (const char *ifname, const char *property
 
 gboolean nm_utils_is_specific_hostname (const char *name);
 
+char *nm_utils_machine_id_read (void);
+gboolean nm_utils_machine_id_parse (const char *id_str, /*uuid_t*/ guchar *out_uuid);
+
+guint8 *nm_utils_secret_key_read (gsize *out_key_len, GError **error);
+
 /* IPv6 Interface Identifer helpers */
 
 /**
diff --git a/src/org.freedesktop.NetworkManager.conf b/src/org.freedesktop.NetworkManager.conf
index dd630e19..d130f7e2 100644
--- a/src/org.freedesktop.NetworkManager.conf
+++ b/src/org.freedesktop.NetworkManager.conf
@@ -26,6 +26,13 @@
                 <allow send_destination="org.freedesktop.NetworkManager.fortisslvpn"/>
                 <allow send_destination="org.freedesktop.NetworkManager.strongswan"/>
                 <allow send_interface="org.freedesktop.NetworkManager.VPN.Plugin"/>
+
+                <!-- Allow the custom name for the dnsmasq instance spawned by NM
+                     from the dns dnsmasq plugin to own it's dbus name, and for
+                     messages to be sent to it.
+                 -->
+                <allow own="org.freedesktop.NetworkManager.dnsmasq"/>
+                <allow send_destination="org.freedesktop.NetworkManager.dnsmasq"/>
         </policy>
         <policy context="default">
                 <deny own="org.freedesktop.NetworkManager"/>
@@ -127,6 +134,9 @@
                 <deny send_destination="org.freedesktop.NetworkManager"
                       send_interface="org.freedesktop.NetworkManager.Settings"
                       send_member="ReloadConnections"/>
+
+                <deny own="org.freedesktop.NetworkManager.dnsmasq"/>
+                <deny send_destination="org.freedesktop.NetworkManager.dnsmasq"/>
         </policy>
 
 	<limit name="max_replies_per_connection">1024</limit>
diff --git a/src/platform/nm-linux-platform.c b/src/platform/nm-linux-platform.c
index 254f9c85..c5045503 100644
--- a/src/platform/nm-linux-platform.c
+++ b/src/platform/nm-linux-platform.c
@@ -632,7 +632,9 @@ _linktype_get_type (NMPlatform *platform,
 		obj = _lookup_cached_link (cache, ifindex, completed_from_cache, link_cached);
 
 		/* If we detected the link type before, we stick to that
-		 * decision unless the "kind" changed.
+		 * decision unless the "kind" no "name" changed. If "name" changed,
+		 * it means that their type may not have been determined correctly
+		 * due to race conditions while accessing sysfs.
 		 *
 		 * This way, we save edditional ethtool/sysctl lookups, but moreover,
 		 * we keep the linktype stable and don't change it as long as the link
@@ -643,6 +645,7 @@ _linktype_get_type (NMPlatform *platform,
 		 * of messing stuff up. */
 		if (   obj
 		    && !NM_IN_SET (obj->link.type, NM_LINK_TYPE_UNKNOWN, NM_LINK_TYPE_NONE)
+		    && !g_strcmp0 (ifname, obj->link.name)
 		    && (   !kind
 		        || !g_strcmp0 (kind, obj->link.kind))) {
 			nm_assert (obj->link.kind == g_intern_string (obj->link.kind));
diff --git a/src/settings/nm-settings.c b/src/settings/nm-settings.c
index 4ad395a1..65148994 100644
--- a/src/settings/nm-settings.c
+++ b/src/settings/nm-settings.c
@@ -112,6 +112,10 @@ EXPORT(nm_settings_connection_replace_and_commit)
 
 #define PLUGIN_MODULE_PATH      "plugin-module-path"
 
+#if (defined(HOSTNAME_PERSIST_SUSE) + defined(HOSTNAME_PERSIST_SLACKWARE) + defined(HOSTNAME_PERSIST_GENTOO)) > 1
+#error "Can only define one of HOSTNAME_PERSIST_*"
+#endif
+
 #if defined(HOSTNAME_PERSIST_SUSE)
 #define HOSTNAME_FILE           HOSTNAME_FILE_UCASE_HOSTNAME
 #elif defined(HOSTNAME_PERSIST_SLACKWARE)
@@ -258,6 +262,7 @@ load_connections (NMSettings *self)
 	}
 
 	priv->connections_loaded = TRUE;
+	g_object_notify (G_OBJECT (self), NM_SETTINGS_CONNECTIONS);
 
 	unmanaged_specs_changed (NULL, self);
 	unrecognized_specs_changed (NULL, self);
@@ -480,27 +485,58 @@ get_plugin (NMSettings *self, guint32 capability)
 static gchar *
 read_hostname_gentoo (const char *path)
 {
-	gchar *contents = NULL, *result = NULL, *tmp;
-	gchar **all_lines = NULL;
-	guint line_num, i;
+	gs_free char *contents = NULL;
+	gs_strfreev char **all_lines = NULL;
+	const char *tmp;
+	guint i;
 
 	if (!g_file_get_contents (path, &contents, NULL, NULL))
 		return NULL;
+
 	all_lines = g_strsplit (contents, "\n", 0);
-	line_num = g_strv_length (all_lines);
-	for (i = 0; i < line_num; i++) {
+	for (i = 0; all_lines[i]; i++) {
 		g_strstrip (all_lines[i]);
 		if (all_lines[i][0] == '#' || all_lines[i][0] == '\0')
 			continue;
 		if (g_str_has_prefix (all_lines[i], "hostname=")) {
 			tmp = &all_lines[i][NM_STRLEN ("hostname=")];
-			result = g_shell_unquote (tmp, NULL);
-			break;
+			return g_shell_unquote (tmp, NULL);
 		}
 	}
-	g_strfreev (all_lines);
-	g_free (contents);
-	return result;
+	return NULL;
+}
+#endif
+
+#if defined(HOSTNAME_PERSIST_SLACKWARE)
+static gchar *
+read_hostname_slackware (const char *path)
+{
+	gs_free char *contents = NULL;
+	gs_strfreev char **all_lines = NULL;
+	char *tmp;
+	guint i, j = 0;
+
+	if (!g_file_get_contents (path, &contents, NULL, NULL))
+		return NULL;
+
+	all_lines = g_strsplit (contents, "\n", 0);
+	for (i = 0; all_lines[i]; i++) {
+		g_strstrip (all_lines[i]);
+		if (all_lines[i][0] == '#' || all_lines[i][0] == '\0')
+			continue;
+		tmp = &all_lines[i][0];
+		/* We only want up to the first '.' -- the rest of the */
+		/* fqdn is defined in /etc/hosts */
+		while (tmp[j] != '\0') {
+			if (tmp[j] == '.') {
+				tmp[j] = '\0';
+				break;
+			}
+			j++;
+		}
+		return g_shell_unquote (tmp, NULL);
+	}
+	return NULL;
 }
 #endif
 
@@ -547,18 +583,19 @@ nm_settings_get_hostname (NMSettings *self)
 		goto out;
 	}
 
-#if defined(HOSTNAME_PERSIST_GENTOO)
-	hostname = read_hostname_gentoo (priv->hostname.file);
-#else
-
 #if defined(HOSTNAME_PERSIST_SUSE)
 	if (priv->hostname.dhcp_monitor_id && hostname_is_dynamic ())
 		return NULL;
 #endif
+
+#if defined(HOSTNAME_PERSIST_GENTOO)
+	hostname = read_hostname_gentoo (priv->hostname.file);
+#elif defined(HOSTNAME_PERSIST_SLACKWARE)
+	hostname = read_hostname_slackware (priv->hostname.file);
+#else
 	if (g_file_get_contents (priv->hostname.file, &hostname, NULL, NULL))
 		g_strchomp (hostname);
-
-#endif /* HOSTNAME_PERSIST_GENTOO */
+#endif
 
 out:
 	if (hostname && !hostname[0]) {
@@ -806,18 +843,19 @@ load_plugin:
 				break;
 			}
 
+			/* after accessing the plugin we cannot unload it anymore, because the glib
+			 * types cannot be properly unregistered. */
+			g_module_make_resident (plugin);
+
 			obj = (*factory_func) ();
 			if (!obj || !NM_IS_SETTINGS_PLUGIN (obj)) {
 				g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_FAILED,
 				             "Plugin '%s' returned invalid system config object.",
 				             pname);
 				success = FALSE;
-				g_module_close (plugin);
 				break;
 			}
 
-			g_module_make_resident (plugin);
-			g_object_weak_ref (obj, (GWeakNotify) g_module_close, plugin);
 			g_object_set_data_full (obj, PLUGIN_MODULE_PATH, path, g_free);
 			path = NULL;
 			if (add_plugin (self, NM_SETTINGS_PLUGIN (obj)))
@@ -1960,9 +1998,11 @@ device_realized (NMDevice *device, GParamSpec *pspec, NMSettings *self)
 	g_object_unref (connection);
 
 	if (!added) {
-		_LOGW ("(%s) couldn't create default wired connection: %s",
-		       nm_device_get_iface (device),
-		       error->message);
+		if (!g_error_matches (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_UUID_EXISTS)) {
+			_LOGW ("(%s) couldn't create default wired connection: %s",
+			       nm_device_get_iface (device),
+			       error->message);
+		}
 		g_clear_error (&error);
 		return;
 	}
diff --git a/src/settings/plugins/ifnet/connection_parser.c b/src/settings/plugins/ifnet/connection_parser.c
index d9ff97dc..80b82535 100644
--- a/src/settings/plugins/ifnet/connection_parser.c
+++ b/src/settings/plugins/ifnet/connection_parser.c
@@ -1541,15 +1541,12 @@ make_wireless_security_setting (const char *conn_name,
 		if (wsec == NULL)
 			goto error;
 	}
-
-	if (!wsec) {
-		g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION,
-			     "Can't handle security information for ssid: %s",
-			     conn_name);
-	}
-
 	return wsec;
+
 error:
+	g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION,
+		     "Can't handle security information for ssid: %s",
+		     conn_name);
 	return NULL;
 }
 
diff --git a/src/settings/plugins/ifupdown/interface_parser.c b/src/settings/plugins/ifupdown/interface_parser.c
index d342501b..7ad902d4 100644
--- a/src/settings/plugins/ifupdown/interface_parser.c
+++ b/src/settings/plugins/ifupdown/interface_parser.c
@@ -39,7 +39,7 @@ if_data* last_data;
 
 void add_block(const char *type, const char* name)
 {
-	if_block *ret = (if_block*)calloc(1,sizeof(struct _if_block));
+	if_block *ret = g_slice_new0 (struct _if_block);
 	ret->name = g_strdup(name);
 	ret->type = g_strdup(type);
 	if (first == NULL)
@@ -61,7 +61,7 @@ void add_data(const char *key,const char *data)
 	if (first == NULL)
 		return;
 
-	ret = (if_data*) calloc(1,sizeof(struct _if_data));
+	ret = g_slice_new0 (struct _if_data);
 	ret->key = g_strdup(key);
 
 	/* Normalize keys. Convert '_' to '-', as ifupdown accepts both variants.
@@ -298,9 +298,9 @@ void _destroy_data(if_data *ifd)
 	if (ifd == NULL)
 		return;
 	_destroy_data(ifd->next);
-	free(ifd->key);
-	free(ifd->data);
-	free(ifd);
+	g_free(ifd->key);
+	g_free(ifd->data);
+	g_slice_free(struct _if_data, ifd);
 	return;
 }
 
@@ -310,9 +310,9 @@ void _destroy_block(if_block* ifb)
 		return;
 	_destroy_block(ifb->next);
 	_destroy_data(ifb->info);
-	free(ifb->name);
-	free(ifb->type);
-	free(ifb);
+	g_free(ifb->name);
+	g_free(ifb->type);
+	g_slice_free(struct _if_block, ifb);
 	return;
 }
 
diff --git a/src/tests/Makefile.am b/src/tests/Makefile.am
index 5dacae8d..e1734b6d 100644
--- a/src/tests/Makefile.am
+++ b/src/tests/Makefile.am
@@ -137,6 +137,7 @@ test_utils_DEPENDENCIES = \
 test_utils_CPPFLAGS = \
 	$(AM_CPPFLAGS) \
 	-DPREFIX=\"/nonexistent\" \
+	-DLOCALSTATEDIR=\"$(localstatedir)\" \
 	-DNMSTATEDIR=\"/nonsense\"
 
 test_utils_LDADD = \
diff --git a/src/tests/Makefile.in b/src/tests/Makefile.in
index 380220f3..a4a135c5 100644
--- a/src/tests/Makefile.in
+++ b/src/tests/Makefile.in
@@ -887,6 +887,7 @@ test_utils_DEPENDENCIES = \
 test_utils_CPPFLAGS = \
 	$(AM_CPPFLAGS) \
 	-DPREFIX=\"/nonexistent\" \
+	-DLOCALSTATEDIR=\"$(localstatedir)\" \
 	-DNMSTATEDIR=\"/nonsense\"
 
 test_utils_LDADD = \
diff --git a/src/tests/test-utils.c b/src/tests/test-utils.c
index c8c82aef..6fc3978b 100644
--- a/src/tests/test-utils.c
+++ b/src/tests/test-utils.c
@@ -34,17 +34,17 @@ test_stable_privacy (void)
 	struct in6_addr addr1;
 
 	inet_pton (AF_INET6, "1234::", &addr1);
-	_set_stable_privacy (&addr1, "eth666", "6b138152-9f3e-4b97-aaf7-e6e553f2a24e", 0, "key", 3, NULL);
+	_set_stable_privacy (&addr1, "eth666", "6b138152-9f3e-4b97-aaf7-e6e553f2a24e", 0, (guint8 *) "key", 3, NULL);
 	nmtst_assert_ip6_address (&addr1, "1234::4ceb:14cd:3d54:793f");
 
 	/* We get an address without the UUID. */
 	inet_pton (AF_INET6, "1::", &addr1);
-	_set_stable_privacy (&addr1, "eth666", NULL, 384, "key", 3, NULL);
+	_set_stable_privacy (&addr1, "eth666", NULL, 384, (guint8 *) "key", 3, NULL);
 	nmtst_assert_ip6_address (&addr1, "1::11aa:2530:9144:dafa");
 
 	/* We get a different address in a different network. */
 	inet_pton (AF_INET6, "2::", &addr1);
-	_set_stable_privacy (&addr1, "eth666", NULL, 384, "key", 3, NULL);
+	_set_stable_privacy (&addr1, "eth666", NULL, 384, (guint8 *) "key", 3, NULL);
 	nmtst_assert_ip6_address (&addr1, "2::338e:8d:c11:8726");
 }