summary refs log tree commit diff
path: root/src/settings/nm-settings.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/settings/nm-settings.c')
-rw-r--r--src/settings/nm-settings.c236
1 files changed, 137 insertions, 99 deletions
diff --git a/src/settings/nm-settings.c b/src/settings/nm-settings.c
index 73c678a6..1f691c0b 100644
--- a/src/settings/nm-settings.c
+++ b/src/settings/nm-settings.c
@@ -25,6 +25,8 @@
 
 #include "nm-default.h"
 
+#include "nm-settings.h"
+
 #include <unistd.h>
 #include <sys/stat.h>
 #include <errno.h>
@@ -60,7 +62,6 @@
 #include "nm-core-internal.h"
 
 #include "nm-device-ethernet.h"
-#include "nm-settings.h"
 #include "nm-settings-connection.h"
 #include "nm-settings-plugin.h"
 #include "nm-bus-manager.h"
@@ -69,7 +70,6 @@
 #include "nm-session-monitor.h"
 #include "plugins/keyfile/plugin.h"
 #include "nm-agent-manager.h"
-#include "nm-connection-provider.h"
 #include "nm-config.h"
 #include "nm-audit-manager.h"
 #include "NetworkManagerUtils.h"
@@ -133,11 +133,11 @@ static void claim_connection (NMSettings *self,
 static void unmanaged_specs_changed (NMSettingsPlugin *config, gpointer user_data);
 static void unrecognized_specs_changed (NMSettingsPlugin *config, gpointer user_data);
 
-static void connection_provider_iface_init (NMConnectionProviderInterface *cp_iface);
-
-G_DEFINE_TYPE_EXTENDED (NMSettings, nm_settings, NM_TYPE_EXPORTED_OBJECT, 0,
-                        G_IMPLEMENT_INTERFACE (NM_TYPE_CONNECTION_PROVIDER, connection_provider_iface_init))
+static void connection_ready_changed (NMSettingsConnection *conn,
+                                      GParamSpec *pspec,
+                                      gpointer user_data);
 
+G_DEFINE_TYPE (NMSettings, nm_settings, NM_TYPE_EXPORTED_OBJECT);
 
 typedef struct {
 	NMAgentManager *agent_mgr;
@@ -149,9 +149,9 @@ typedef struct {
 	GSList *plugins;
 	gboolean connections_loaded;
 	GHashTable *connections;
+	NMSettingsConnection **connections_cached_list;
 	GSList *unmanaged_specs;
 	GSList *unrecognized_specs;
-	GSList *get_connections_cache;
 
 	gboolean started;
 	gboolean startup_complete;
@@ -204,6 +204,11 @@ check_startup_complete (NMSettings *self)
 			return;
 	}
 
+	/* the connection_ready_changed signal handler is no longer needed. */
+	g_hash_table_iter_init (&iter, priv->connections);
+	while (g_hash_table_iter_next (&iter, NULL, (gpointer *) &conn))
+		g_signal_handlers_disconnect_by_func (conn, G_CALLBACK (connection_ready_changed), self);
+
 	priv->startup_complete = TRUE;
 	_notify (self, PROP_STARTUP_COMPLETE);
 }
@@ -258,7 +263,7 @@ load_connections (NMSettings *self)
 	}
 
 	priv->connections_loaded = TRUE;
-	g_object_notify (G_OBJECT (self), NM_SETTINGS_CONNECTIONS);
+	_notify (self, PROP_CONNECTIONS);
 
 	unmanaged_specs_changed (NULL, self);
 	unrecognized_specs_changed (NULL, self);
@@ -275,7 +280,7 @@ nm_settings_for_each_connection (NMSettings *self,
 
 	g_return_if_fail (NM_IS_SETTINGS (self));
 	g_return_if_fail (for_each_func != NULL);
-	
+
 	priv = NM_SETTINGS_GET_PRIVATE (self);
 
 	g_hash_table_iter_init (&iter, priv->connections);
@@ -401,13 +406,60 @@ connection_sort (gconstpointer pa, gconstpointer pb)
 	return 1;
 }
 
+/**
+ * nm_settings_get_connections:
+ * @self: the #NMSettings
+ * @out_len: (out): (allow-none): returns the number of returned
+ *   connections.
+ *
+ * Returns: (transfer-none): a list of NMSettingsConnections. The list is
+ * unsorted and NULL terminated. The result is never %NULL, in case of no
+ * connections, it returns an empty list.
+ * The returned list is cached internally, only valid until the next
+ * NMSettings operation.
+ */
+NMSettingsConnection *const*
+nm_settings_get_connections (NMSettings *self, guint *out_len)
+{
+	GHashTableIter iter;
+	NMSettingsPrivate *priv;
+	guint l, i;
+	NMSettingsConnection **v;
+	NMSettingsConnection *con;
+
+	g_return_val_if_fail (NM_IS_SETTINGS (self), NULL);
+
+	priv = NM_SETTINGS_GET_PRIVATE (self);
+
+	if (priv->connections_cached_list) {
+		NM_SET_OUT (out_len, g_hash_table_size (priv->connections));
+		return priv->connections_cached_list;
+	}
+
+	l = g_hash_table_size (priv->connections);
+
+	v = g_new (NMSettingsConnection *, l + 1);
+
+	i = 0;
+	g_hash_table_iter_init (&iter, priv->connections);
+	while (g_hash_table_iter_next (&iter, NULL, (gpointer *) &con))
+		v[i++] = con;
+	v[i] = NULL;
+
+	nm_assert (i == l);
+
+	NM_SET_OUT (out_len, l);
+	priv->connections_cached_list = v;
+	return v;
+}
+
 /* Returns a list of NMSettingsConnections.
  * The list is sorted in the order suitable for auto-connecting, i.e.
  * first go connections with autoconnect=yes and most recent timestamp.
  * Caller must free the list with g_slist_free().
  */
 GSList *
-nm_settings_get_connections (NMSettings *self)
+nm_settings_get_connections_sorted (NMSettings *self)
 {
 	GHashTableIter iter;
 	gpointer data = NULL;
@@ -886,7 +938,6 @@ connection_updated (NMSettingsConnection *connection, gboolean by_user, gpointer
 	               0,
 	               connection,
 	               by_user);
-	g_signal_emit_by_name (NM_SETTINGS (user_data), NM_CP_SIGNAL_CONNECTION_UPDATED, connection);
 }
 
 static void
@@ -920,17 +971,18 @@ connection_removed (NMSettingsConnection *connection, gpointer user_data)
 	g_signal_handlers_disconnect_by_func (connection, G_CALLBACK (connection_removed), self);
 	g_signal_handlers_disconnect_by_func (connection, G_CALLBACK (connection_updated), self);
 	g_signal_handlers_disconnect_by_func (connection, G_CALLBACK (connection_visibility_changed), self);
-	g_signal_handlers_disconnect_by_func (connection, G_CALLBACK (connection_ready_changed), self);
+	if (!priv->startup_complete)
+		g_signal_handlers_disconnect_by_func (connection, G_CALLBACK (connection_ready_changed), self);
 	g_object_unref (self);
 
 	/* Forget about the connection internally */
 	g_hash_table_remove (priv->connections, (gpointer) cpath);
+	g_clear_pointer (&priv->connections_cached_list, g_free);
 
 	/* Notify D-Bus */
 	g_signal_emit (self, signals[CONNECTION_REMOVED], 0, connection);
 
 	/* Re-emit for listeners like NMPolicy */
-	g_signal_emit_by_name (self, NM_CP_SIGNAL_CONNECTION_REMOVED, connection);
 	_notify (self, PROP_CONNECTIONS);
 	if (nm_exported_object_is_exported (NM_EXPORTED_OBJECT (connection)))
 		nm_exported_object_unexport (NM_EXPORTED_OBJECT (connection));
@@ -1069,6 +1121,7 @@ claim_connection (NMSettings *self, NMSettingsConnection *connection)
 	g_hash_table_insert (priv->connections,
 	                     (gpointer) nm_connection_get_path (NM_CONNECTION (connection)),
 	                     g_object_ref (connection));
+	g_clear_pointer (&priv->connections_cached_list, g_free);
 
 	nm_utils_log_connection_diff (NM_CONNECTION (connection), NULL, LOGL_DEBUG, LOGD_CORE, "new connection", "++ ");
 
@@ -1078,7 +1131,6 @@ claim_connection (NMSettings *self, NMSettingsConnection *connection)
 	if (priv->connections_loaded) {
 		/* Internal added signal */
 		g_signal_emit (self, signals[CONNECTION_ADDED], 0, connection);
-		g_signal_emit_by_name (self, NM_CP_SIGNAL_CONNECTION_ADDED, connection);
 		_notify (self, PROP_CONNECTIONS);
 
 		/* Exported D-Bus signal */
@@ -1154,16 +1206,6 @@ nm_settings_add_connection (NMSettings *self,
 	return NULL;
 }
 
-static NMConnection *
-_nm_connection_provider_add_connection (NMConnectionProvider *provider,
-                                        NMConnection *connection,
-                                        gboolean save_to_disk,
-                                        GError **error)
-{
-	g_assert (NM_IS_CONNECTION_PROVIDER (provider) && NM_IS_SETTINGS (provider));
-	return NM_CONNECTION (nm_settings_add_connection (NM_SETTINGS (provider), connection, save_to_disk, error));
-}
-
 static gboolean
 secrets_filter_cb (NMSetting *setting,
                    const char *secret,
@@ -1411,12 +1453,12 @@ impl_settings_add_connection_add_cb (NMSettings *self,
 {
 	if (error) {
 		g_dbus_method_invocation_return_gerror (context, error);
-		nm_audit_log_connection_op (NM_AUDIT_OP_CONN_ADD, NULL, FALSE, subject, error->message);
+		nm_audit_log_connection_op (NM_AUDIT_OP_CONN_ADD, NULL, FALSE, NULL, subject, error->message);
 	} else {
 		g_dbus_method_invocation_return_value (
 		    context,
 		    g_variant_new ("(o)", nm_connection_get_path (NM_CONNECTION (connection))));
-		nm_audit_log_connection_op (NM_AUDIT_OP_CONN_ADD, connection, TRUE,
+		nm_audit_log_connection_op (NM_AUDIT_OP_CONN_ADD, connection, TRUE, NULL,
 		                            subject, NULL);
 	}
 }
@@ -1470,31 +1512,6 @@ impl_settings_add_connection_unsaved (NMSettings *self,
 	impl_settings_add_connection_helper (self, context, settings, FALSE);
 }
 
-static gboolean
-ensure_root (NMBusManager          *dbus_mgr,
-             GDBusMethodInvocation *context)
-{
-	gulong caller_uid;
-	GError *error = NULL;
-
-	if (!nm_bus_manager_get_caller_info (dbus_mgr, context, NULL, &caller_uid, NULL)) {
-		error = g_error_new_literal (NM_SETTINGS_ERROR,
-		                             NM_SETTINGS_ERROR_PERMISSION_DENIED,
-		                             "Unable to determine request UID.");
-		g_dbus_method_invocation_take_error (context, error);
-		return FALSE;
-	}
-	if (caller_uid != 0) {
-		error = g_error_new_literal (NM_SETTINGS_ERROR,
-		                             NM_SETTINGS_ERROR_PERMISSION_DENIED,
-		                             "Permission denied");
-		g_dbus_method_invocation_take_error (context, error);
-		return FALSE;
-	}
-
-	return TRUE;
-}
-
 static void
 impl_settings_load_connections (NMSettings *self,
                                 GDBusMethodInvocation *context,
@@ -1505,7 +1522,15 @@ impl_settings_load_connections (NMSettings *self,
 	GSList *iter;
 	int i;
 
-	if (!ensure_root (nm_bus_manager_get (), context))
+	/* The permission is already enforced by the D-Bus daemon, but we ensure
+	 * that the caller is still alive so that clients are forced to wait and
+	 * we'll be able to switch to polkit without breaking behavior.
+	 */
+	if (!nm_bus_manager_ensure_uid (nm_bus_manager_get (),
+	                                context,
+	                                G_MAXULONG,
+	                                NM_SETTINGS_ERROR,
+	                                NM_SETTINGS_ERROR_PERMISSION_DENIED))
 		return;
 
 	failures = g_ptr_array_new ();
@@ -1541,7 +1566,15 @@ impl_settings_reload_connections (NMSettings *self,
 	NMSettingsPrivate *priv = NM_SETTINGS_GET_PRIVATE (self);
 	GSList *iter;
 
-	if (!ensure_root (nm_bus_manager_get (), context))
+	/* The permission is already enforced by the D-Bus daemon, but we ensure
+	 * that the caller is still alive so that clients are forced to wait and
+	 * we'll be able to switch to polkit without breaking behavior.
+	 */
+	if (!nm_bus_manager_ensure_uid (nm_bus_manager_get (),
+	                                context,
+	                                G_MAXULONG,
+	                                NM_SETTINGS_ERROR,
+	                                NM_SETTINGS_ERROR_PERMISSION_DENIED))
 		return;
 
 	for (iter = priv->plugins; iter; iter = g_slist_next (iter)) {
@@ -1837,11 +1870,11 @@ have_connection_for_device (NMSettings *self, NMDevice *device)
 	NMSettingConnection *s_con;
 	NMSettingWired *s_wired;
 	const char *setting_hwaddr;
-	const char *device_hwaddr;
+	const char *perm_hw_addr;
 
 	g_return_val_if_fail (NM_IS_SETTINGS (self), FALSE);
 
-	device_hwaddr = nm_device_get_hw_address (device);
+	perm_hw_addr = nm_device_get_permanent_hw_address (device, FALSE);
 
 	/* Find a wired connection locked to the given MAC address, if any */
 	g_hash_table_iter_init (&iter, priv->connections);
@@ -1875,8 +1908,8 @@ have_connection_for_device (NMSettings *self, NMDevice *device)
 		setting_hwaddr = nm_setting_wired_get_mac_address (s_wired);
 		if (setting_hwaddr) {
 			/* A connection mac-locked to this device */
-			if (   device_hwaddr
-			    && nm_utils_hwaddr_matches (setting_hwaddr, -1, device_hwaddr, -1))
+			if (   perm_hw_addr
+			    && nm_utils_hwaddr_matches (setting_hwaddr, -1, perm_hw_addr, -1))
 				return TRUE;
 		} else {
 			/* A connection that applies to any wired device */
@@ -2071,22 +2104,41 @@ nm_settings_sort_connections (gconstpointer a, gconstpointer b)
 	return 0;
 }
 
-static GSList *
-get_best_connections (NMConnectionProvider *provider,
-                      guint max_requested,
-                      const char *ctype1,
-                      const char *ctype2,
-                      NMConnectionFilterFunc func,
-                      gpointer func_data)
+/**
+ * nm_settings_get_best_connections:
+ * @self: the #NMSetting
+ * @max_requested: if non-zero, the maximum number of connections to return
+ * @ctype1: an #NMSetting base type (eg NM_SETTING_WIRELESS_SETTING_NAME) to
+ *   filter connections against
+ * @ctype2: a second #NMSetting base type (eg NM_SETTING_WIRELESS_SETTING_NAME)
+ *   to filter connections against
+ * @func: caller-supplied function for filtering connections
+ * @func_data: caller-supplied data passed to @func
+ *
+ * Returns: a #GSList of #NMConnection objects in sorted order representing the
+ *   "best" or highest-priority connections filtered by @ctype1 and/or @ctype2,
+ *   and/or @func.  Caller is responsible for freeing the returned #GSList, but
+ *   the contained values do not need to be unreffed.
+ */
+GSList *
+nm_settings_get_best_connections (NMSettings *self,
+                                  guint max_requested,
+                                  const char *ctype1,
+                                  const char *ctype2,
+                                  NMConnectionFilterFunc func,
+                                  gpointer func_data)
 {
-	NMSettings *self = NM_SETTINGS (provider);
-	NMSettingsPrivate *priv = NM_SETTINGS_GET_PRIVATE (self);
+	NMSettingsPrivate *priv;
 	GSList *sorted = NULL;
 	GHashTableIter iter;
 	NMSettingsConnection *connection;
 	guint added = 0;
 	guint64 oldest = 0;
 
+	g_return_val_if_fail (NM_IS_SETTINGS (self), NULL);
+
+	priv = NM_SETTINGS_GET_PRIVATE (self);
+
 	g_hash_table_iter_init (&iter, priv->connections);
 	while (g_hash_table_iter_next (&iter, NULL, (gpointer) &connection)) {
 		guint64 cur_ts = 0;
@@ -2095,7 +2147,7 @@ get_best_connections (NMConnectionProvider *provider,
 			continue;
 		if (ctype2 && !nm_connection_is_type (NM_CONNECTION (connection), ctype2))
 			continue;
-		if (func && !func (provider, NM_CONNECTION (connection), func_data))
+		if (func && !func (self, NM_CONNECTION (connection), func_data))
 			continue;
 
 		/* Don't bother with a connection that's older than the oldest one in the list */
@@ -2121,27 +2173,6 @@ get_best_connections (NMConnectionProvider *provider,
 	return g_slist_reverse (sorted);
 }
 
-static const GSList *
-get_connections (NMConnectionProvider *provider)
-{
-	GSList *list = NULL;
-	NMSettings *self = NM_SETTINGS (provider);
-	NMSettingsPrivate *priv = NM_SETTINGS_GET_PRIVATE (self);
-
-	list = _nm_utils_hash_values_to_slist (priv->connections);
-
-	/* Cache the list every call so we can keep it 'const' for callers */
-	g_slist_free (priv->get_connections_cache);
-	priv->get_connections_cache = list;
-	return list;
-}
-
-static NMConnection *
-cp_get_connection_by_uuid (NMConnectionProvider *provider, const char *uuid)
-{
-	return NM_CONNECTION (nm_settings_get_connection_by_uuid (NM_SETTINGS (provider), uuid));
-}
-
 /***************************************************************/
 
 gboolean
@@ -2190,14 +2221,30 @@ setup_hostname_file_monitors (NMSettings *self)
 {
 	NMSettingsPrivate *priv = NM_SETTINGS_GET_PRIVATE (self);
 	GFileMonitor *monitor;
+	const char *path = HOSTNAME_FILE;
+	char *link_path = NULL;
+	struct stat file_stat;
 	GFile *file;
 
 	priv->hostname.value = nm_settings_get_hostname (self);
 
+	/* resolve the path to the hostname file if it is a symbolic link */
+	if (   lstat(path, &file_stat) == 0
+	    && S_ISLNK (file_stat.st_mode)
+	    && (link_path = nm_utils_read_link_absolute (path, NULL))) {
+		path = link_path;
+		if (   lstat(link_path, &file_stat) == 0
+		    && S_ISLNK (file_stat.st_mode)) {
+			_LOGW ("only one level of symbolic link indirection is allowed when monitoring "
+			       HOSTNAME_FILE);
+		}
+	}
+
 	/* monitor changes to hostname file */
-	file = g_file_new_for_path (HOSTNAME_FILE);
+	file = g_file_new_for_path (path);
 	monitor = g_file_monitor_file (file, G_FILE_MONITOR_NONE, NULL, NULL);
 	g_object_unref (file);
+	g_free(link_path);
 	if (monitor) {
 		priv->hostname.monitor_id = g_signal_connect (monitor, "changed",
 		                                              G_CALLBACK (hostname_file_changed_cb),
@@ -2287,15 +2334,6 @@ nm_settings_start (NMSettings *self, GError **error)
 }
 
 static void
-connection_provider_iface_init (NMConnectionProviderInterface *cp_iface)
-{
-    cp_iface->get_best_connections = get_best_connections;
-    cp_iface->get_connections = get_connections;
-    cp_iface->add_connection = _nm_connection_provider_add_connection;
-    cp_iface->get_connection_by_uuid = cp_get_connection_by_uuid;
-}
-
-static void
 nm_settings_init (NMSettings *self)
 {
 	NMSettingsPrivate *priv = NM_SETTINGS_GET_PRIVATE (self);
@@ -2359,7 +2397,7 @@ finalize (GObject *object)
 	NMSettingsPrivate *priv = NM_SETTINGS_GET_PRIVATE (self);
 
 	g_hash_table_destroy (priv->connections);
-	g_slist_free (priv->get_connections_cache);
+	g_clear_pointer (&priv->connections_cached_list, g_free);
 
 	g_slist_free_full (priv->unmanaged_specs, g_free);
 	g_slist_free_full (priv->unrecognized_specs, g_free);