about summary refs log tree commit diff
path: root/src/NetworkManagerUtils.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/NetworkManagerUtils.c')
-rw-r--r--src/NetworkManagerUtils.c257
1 files changed, 159 insertions, 98 deletions
diff --git a/src/NetworkManagerUtils.c b/src/NetworkManagerUtils.c
index 89bd357f..a3bd6013 100644
--- a/src/NetworkManagerUtils.c
+++ b/src/NetworkManagerUtils.c
@@ -23,6 +23,8 @@
 
 #include "NetworkManagerUtils.h"
 
+#include "nm-utils/nm-c-list.h"
+
 #include "nm-common-macros.h"
 #include "nm-utils.h"
 #include "nm-setting-connection.h"
@@ -31,7 +33,6 @@
 #include "nm-core-internal.h"
 
 #include "platform/nm-platform.h"
-#include "nm-exported-object.h"
 #include "nm-auth-utils.h"
 
 /*****************************************************************************/
@@ -68,98 +69,93 @@ nm_utils_get_shared_wifi_permission (NMConnection *connection)
 /*****************************************************************************/
 
 static char *
-get_new_connection_name (const GSList *existing,
+get_new_connection_name (NMConnection *const*existing_connections,
                          const char *preferred,
                          const char *fallback_prefix)
 {
-	GSList *names = NULL;
-	const GSList *iter;
-	char *cname = NULL;
-	int i = 0;
-	gboolean preferred_found = FALSE;
+	gs_free const char **existing_names = NULL;
+	guint i, existing_len = 0;
 
 	g_assert (fallback_prefix);
 
-	for (iter = existing; iter; iter = g_slist_next (iter)) {
-		NMConnection *candidate = NM_CONNECTION (iter->data);
-		const char *id;
+	if (existing_connections) {
+		existing_len = NM_PTRARRAY_LEN (existing_connections);
+		existing_names = g_new (const char *, existing_len);
+		for (i = 0; i < existing_len; i++) {
+			NMConnection *candidate;
+			const char *id;
+
+			candidate = existing_connections[i];
+			nm_assert (NM_IS_CONNECTION (candidate));
+
+			id = nm_connection_get_id (candidate);
+			nm_assert (id);
 
-		id = nm_connection_get_id (candidate);
-		g_assert (id);
-		names = g_slist_append (names, (gpointer) id);
+			existing_names[i] = id;
 
-		if (preferred && !preferred_found && (strcmp (preferred, id) == 0))
-			preferred_found = TRUE;
+			if (   preferred
+				&& nm_streq (preferred, id)) {
+				/* the preferred name is already taken. Forget about it. */
+				preferred = NULL;
+			}
+		}
+		nm_assert (!existing_connections[i]);
 	}
 
 	/* Return the preferred name if it was unique */
-	if (preferred && !preferred_found) {
-		g_slist_free (names);
+	if (preferred)
 		return g_strdup (preferred);
-	}
 
 	/* Otherwise find the next available unique connection name using the given
 	 * connection name template.
 	 */
-	while (!cname && (i++ < 10000)) {
+	for (i = 1; TRUE; i++) {
 		char *temp;
-		gboolean found = FALSE;
 
-		/* Translators: the first %s is a prefix for the connection id, such
+		/* TRANSLATORS: the first %s is a prefix for the connection id, such
 		 * as "Wired Connection" or "VPN Connection". The %d is a number
 		 * that is combined with the first argument to create a unique
 		 * connection id. */
-		temp = g_strdup_printf (C_("connection id fallback", "%s %d"),
+		temp = g_strdup_printf (C_("connection id fallback", "%s %u"),
 		                        fallback_prefix, i);
-		for (iter = names; iter; iter = g_slist_next (iter)) {
-			if (!strcmp (iter->data, temp)) {
-				found = TRUE;
-				break;
-			}
-		}
-		if (!found)
-			cname = temp;
-		else
-			g_free (temp);
-	}
 
-	g_slist_free (names);
-	return cname;
+		if (nm_utils_strv_find_first ((char **) existing_names,
+		                              existing_len,
+		                              temp) < 0)
+			return temp;
+
+		g_free (temp);
+	}
 }
 
 static char *
 get_new_connection_ifname (NMPlatform *platform,
-                           const GSList *existing,
+                           NMConnection *const*existing_connections,
                            const char *prefix)
 {
-	int i;
-	char *name;
-	const GSList *iter;
-	gboolean found;
+	guint i, j;
+
+	for (i = 0; TRUE; i++) {
+		char *name;
 
-	for (i = 0; i < 500; i++) {
 		name = g_strdup_printf ("%s%d", prefix, i);
 
 		if (nm_platform_link_get_by_ifname (platform, name))
 			goto next;
 
-		for (iter = existing, found = FALSE; iter; iter = g_slist_next (iter)) {
-			NMConnection *candidate = iter->data;
-
-			if (g_strcmp0 (nm_connection_get_interface_name (candidate), name) == 0) {
-				found = TRUE;
-				break;
+		if (existing_connections) {
+			for (j = 0; existing_connections[j]; j++) {
+				if (nm_streq0 (nm_connection_get_interface_name (existing_connections[j]),
+				               name))
+					goto next;
 			}
 		}
 
-		if (!found)
-			return name;
+		return name;
 
-	next:
+next:
 		g_free (name);
 	}
-
-	return NULL;
 }
 
 const char *
@@ -251,7 +247,7 @@ void
 nm_utils_complete_generic (NMPlatform *platform,
                            NMConnection *connection,
                            const char *ctype,
-                           const GSList *existing,
+                           NMConnection *const*existing_connections,
                            const char *preferred_id,
                            const char *fallback_id_prefix,
                            const char *ifname_prefix,
@@ -278,14 +274,14 @@ nm_utils_complete_generic (NMPlatform *platform,
 
 	/* Add a connection ID if absent */
 	if (!nm_setting_connection_get_id (s_con)) {
-		id = get_new_connection_name (existing, preferred_id, fallback_id_prefix);
+		id = get_new_connection_name (existing_connections, preferred_id, fallback_id_prefix);
 		g_object_set (G_OBJECT (s_con), NM_SETTING_CONNECTION_ID, id, NULL);
 		g_free (id);
 	}
 
 	/* Add an interface name, if requested */
 	if (ifname_prefix && !nm_setting_connection_get_interface_name (s_con)) {
-		ifname = get_new_connection_ifname (platform, existing, ifname_prefix);
+		ifname = get_new_connection_ifname (platform, existing_connections, ifname_prefix);
 		g_object_set (G_OBJECT (s_con), NM_SETTING_CONNECTION_INTERFACE_NAME, ifname, NULL);
 		g_free (ifname);
 	}
@@ -880,58 +876,123 @@ nm_utils_match_connection (NMConnection *const*connections,
 
 /*****************************************************************************/
 
-/**
- * nm_utils_g_value_set_object_path:
- * @value: a #GValue, initialized to store an object path
- * @object: (allow-none): an #NMExportedObject
- *
- * Sets @value to @object's object path. If @object is %NULL, or not
- * exported, @value is set to "/".
- */
-void
-nm_utils_g_value_set_object_path (GValue *value, gpointer object)
+int
+nm_match_spec_device_by_pllink (const NMPlatformLink *pllink,
+                                const char *match_device_type,
+                                const GSList *specs,
+                                int no_match_value)
 {
-	g_return_if_fail (!object || NM_IS_EXPORTED_OBJECT (object));
+	NMMatchSpecMatchType m;
+
+	/* we can only match by certain properties that are available on the
+	 * platform link (and even @pllink might be missing.
+	 *
+	 * It's still useful because of specs like "*" and "except:interface-name:eth0",
+	 * which match even in that case. */
+	m = nm_match_spec_device (specs,
+	                          pllink ? pllink->name : NULL,
+	                          match_device_type,
+	                          pllink ? pllink->driver : NULL,
+	                          NULL,
+	                          NULL,
+	                          NULL);
+
+	switch (m) {
+	case NM_MATCH_SPEC_MATCH:
+		return TRUE;
+	case NM_MATCH_SPEC_NEG_MATCH:
+		return FALSE;
+	case NM_MATCH_SPEC_NO_MATCH:
+		return no_match_value;
+	}
+	nm_assert_not_reached ();
+	return no_match_value;
+}
 
-	if (object && nm_exported_object_is_exported (object))
-		g_value_set_string (value, nm_exported_object_get_path (object));
-	else
-		g_value_set_string (value, "/");
+/*****************************************************************************/
+
+struct _NMShutdownWaitObjHandle {
+	CList lst;
+	GObject *watched_obj;
+	const char *msg_reason;
+};
+
+static CList _shutdown_waitobj_lst_head;
+
+static void
+_shutdown_waitobj_unregister (NMShutdownWaitObjHandle *handle)
+{
+	c_list_unlink_stale (&handle->lst);
+	g_slice_free (NMShutdownWaitObjHandle, handle);
+
+	/* FIXME(shutdown): check whether the object list is empty, and
+	 * signal shutdown-complete */
+}
+
+static void
+_shutdown_waitobj_cb (gpointer user_data,
+                       GObject *where_the_object_was)
+{
+	NMShutdownWaitObjHandle *handle = user_data;
+
+	nm_assert (handle);
+	nm_assert (handle->watched_obj == where_the_object_was);
+	_shutdown_waitobj_unregister (handle);
 }
 
 /**
- * nm_utils_g_value_set_object_path_array:
- * @value: a #GValue, initialized to store an object path
- * @objects: a #GSList of #NMExportedObjects
- * @filter_func: (allow-none): function to call on each object in @objects
- * @user_data: data to pass to @filter_func
+ * _nm_shutdown_wait_obj_register:
+ * @watched_obj: the object to watch. Takes a weak reference on the object
+ *   to be notified when it gets destroyed.
+ * @msg_reason: a reason message, for debugging and logging purposes. It
+ *   must be a static string. Or at least, be alive at least as long as
+ *   @watched_obj. So, theoretically, if you need a dynamic @msg_reason,
+ *   you could attach it to @watched_obj's user-data.
+ *
+ * Keep track of @watched_obj until it gets destroyed. During shutdown,
+ * we wait until all watched objects are destroyed. This is useful, if
+ * this object still conducts some asynchronous action, which needs to
+ * complete before NetworkManager is allowed to terminate. We re-use
+ * the reference-counter of @watched_obj as signal, that the object
+ * is still used.
+ *
+ * FIXME(shutdown): proper shutdown is not yet implemented, and registering
+ *   an object (currently) has no effect.
  *
- * Sets @value to an array of object paths of the objects in @objects.
+ * Returns: a handle to unregister the object. The caller may choose to ignore
+ *   the handle, in which case, the object will be automatically unregistered,
+ *   once it gets destroyed.
  */
+NMShutdownWaitObjHandle *
+_nm_shutdown_wait_obj_register (GObject *watched_obj,
+                                const char *msg_reason)
+{
+	NMShutdownWaitObjHandle *handle;
+
+	g_return_val_if_fail (G_IS_OBJECT (watched_obj), NULL);
+
+	if (G_UNLIKELY (!_shutdown_waitobj_lst_head.next))
+		c_list_init (&_shutdown_waitobj_lst_head);
+
+	handle = g_slice_new (NMShutdownWaitObjHandle);
+	handle->watched_obj = watched_obj;
+	/* we don't clone the string. We require the caller to use pass a static message.
+	 * If he really cannot do that, he should attach the string to the watched_obj
+	 * as user-data. */
+	handle->msg_reason = msg_reason;
+	c_list_link_tail (&_shutdown_waitobj_lst_head, &handle->lst);
+	g_object_weak_ref (watched_obj, _shutdown_waitobj_cb, handle);
+	return handle;
+}
+
 void
-nm_utils_g_value_set_object_path_array (GValue *value,
-                                        GSList *objects,
-                                        NMUtilsObjectFunc filter_func,
-                                        gpointer user_data)
+nm_shutdown_wait_obj_unregister (NMShutdownWaitObjHandle *handle)
 {
-	char **paths;
-	guint i;
-	GSList *iter;
+	g_return_if_fail (handle);
 
-	paths = g_new (char *, g_slist_length (objects) + 1);
-	for (i = 0, iter = objects; iter; iter = iter->next) {
-		NMExportedObject *object = iter->data;
-		const char *path;
+	nm_assert (G_IS_OBJECT (handle->watched_obj));
+	nm_assert (nm_c_list_contains_entry (&_shutdown_waitobj_lst_head, handle, lst));
 
-		path = nm_exported_object_get_path (object);
-		if (!path)
-			continue;
-		if (filter_func && !filter_func ((GObject *) object, user_data))
-			continue;
-		paths[i++] = g_strdup (path);
-	}
-	paths[i] = NULL;
-	g_value_take_boxed (value, paths);
+	g_object_weak_unref (handle->watched_obj, _shutdown_waitobj_cb, handle);
+	_shutdown_waitobj_unregister (handle);
 }
-
-/*****************************************************************************/