summary refs log tree commit diff
path: root/libnm-core/nm-setting-wired.c
diff options
context:
space:
mode:
Diffstat (limited to 'libnm-core/nm-setting-wired.c')
-rw-r--r--libnm-core/nm-setting-wired.c380
1 files changed, 299 insertions, 81 deletions
diff --git a/libnm-core/nm-setting-wired.c b/libnm-core/nm-setting-wired.c
index 182e7193..379bd173 100644
--- a/libnm-core/nm-setting-wired.c
+++ b/libnm-core/nm-setting-wired.c
@@ -1,5 +1,3 @@
-/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
-
 /*
  * This library is free software; you can redistribute it and/or
  * modify it under the terms of the GNU Lesser General Public
@@ -75,7 +73,11 @@ typedef struct {
 	guint32 mtu;
 	char **s390_subchannels;
 	char *s390_nettype;
-	GHashTable *s390_options;
+	struct {
+		NMUtilsNamedValue *arr;
+		guint len;
+		guint n_alloc;
+	} s390_options;
 	NMSettingWiredWakeOnLan wol;
 	char *wol_password;
 } NMSettingWiredPrivate;
@@ -87,15 +89,68 @@ G_DEFINE_TYPE (NMSettingWired, nm_setting_wired, NM_TYPE_SETTING)
 /*****************************************************************************/
 
 static const char *valid_s390_opts[] = {
-	"portno", "layer2", "portname", "protocol", "priority_queueing",
-	"buffer_count", "isolation", "total", "inter", "inter_jumbo", "route4",
-	"route6", "fake_broadcast", "broadcast_mode", "canonical_macaddr",
-	"checksumming", "sniffer", "large_send", "ipato_enable", "ipato_invert4",
-	"ipato_add4", "ipato_invert6", "ipato_add6", "vipa_add4", "vipa_add6",
-	"rxip_add4", "rxip_add6", "lancmd_timeout", "ctcprot",
-	NULL
+	"broadcast_mode",
+	"buffer_count",
+	"canonical_macaddr",
+	"checksumming",
+	"ctcprot",
+	"fake_broadcast",
+	"inter",
+	"inter_jumbo",
+	"ipato_add4",
+	"ipato_add6",
+	"ipato_enable",
+	"ipato_invert4",
+	"ipato_invert6",
+	"isolation",
+	"lancmd_timeout",
+	"large_send",
+	"layer2",
+	"portname",
+	"portno",
+	"priority_queueing",
+	"protocol",
+	"route4",
+	"route6",
+	"rxip_add4",
+	"rxip_add6",
+	"sniffer",
+	"total",
+	"vipa_add4",
+	"vipa_add6",
+	NULL,
 };
 
+static gboolean
+valid_s390_opts_check (const char *option)
+{
+#if NM_MORE_ASSERTS > 5
+	nm_assert (NM_PTRARRAY_LEN (valid_s390_opts) + 1 == G_N_ELEMENTS (valid_s390_opts));
+	{
+		gsize i;
+
+		for (i = 0; i < G_N_ELEMENTS (valid_s390_opts); i++) {
+			if (i == G_N_ELEMENTS (valid_s390_opts) - 1)
+				nm_assert (!valid_s390_opts[i]);
+			else {
+				nm_assert (valid_s390_opts[i]);
+				nm_assert (valid_s390_opts[i][0] != '\0');
+				if (i > 0)
+					g_assert (strcmp (valid_s390_opts[i - 1], valid_s390_opts[i]) < 0);
+			}
+		}
+	}
+#endif
+
+	return    option
+	       && (nm_utils_array_find_binary_search (valid_s390_opts,
+	                                              sizeof (const char *),
+	                                              G_N_ELEMENTS (valid_s390_opts) - 1,
+	                                              &option,
+	                                              nm_strcmp_p_with_data,
+	                                              NULL) >= 0);
+}
+
 /**
  * nm_setting_wired_get_port:
  * @setting: the #NMSettingWired
@@ -417,7 +472,7 @@ nm_setting_wired_get_num_s390_options (NMSettingWired *setting)
 {
 	g_return_val_if_fail (NM_IS_SETTING_WIRED (setting), 0);
 
-	return g_hash_table_size (NM_SETTING_WIRED_GET_PRIVATE (setting)->s390_options);
+	return NM_SETTING_WIRED_GET_PRIVATE (setting)->s390_options.len;
 }
 
 /**
@@ -447,24 +502,17 @@ nm_setting_wired_get_s390_option (NMSettingWired *setting,
                                   const char **out_key,
                                   const char **out_value)
 {
-	const char *_key, *_value;
-	GHashTableIter iter;
-	guint i = 0;
+	NMSettingWiredPrivate *priv;
 
 	g_return_val_if_fail (NM_IS_SETTING_WIRED (setting), FALSE);
 
-	g_hash_table_iter_init (&iter, NM_SETTING_WIRED_GET_PRIVATE (setting)->s390_options);
-	while (g_hash_table_iter_next (&iter, (gpointer) &_key, (gpointer) &_value)) {
-		if (i == idx) {
-			if (out_key)
-				*out_key = _key;
-			if (out_value)
-				*out_value = _value;
-			return TRUE;
-		}
-		i++;
-	}
-	g_return_val_if_reached (FALSE);
+	priv = NM_SETTING_WIRED_GET_PRIVATE (setting);
+
+	g_return_val_if_fail (idx < priv->s390_options.len, FALSE);
+
+	NM_SET_OUT (out_key,   priv->s390_options.arr[idx].name);
+	NM_SET_OUT (out_value, priv->s390_options.arr[idx].value_str);
+	return TRUE;
 }
 
 /**
@@ -482,10 +530,21 @@ const char *
 nm_setting_wired_get_s390_option_by_key (NMSettingWired *setting,
                                          const char *key)
 {
+	NMSettingWiredPrivate *priv;
+	gssize idx;
+
 	g_return_val_if_fail (NM_IS_SETTING_WIRED (setting), NULL);
 	g_return_val_if_fail (key && key[0], NULL);
 
-	return g_hash_table_lookup (NM_SETTING_WIRED_GET_PRIVATE (setting)->s390_options, key);
+	priv = NM_SETTING_WIRED_GET_PRIVATE (setting);
+
+	idx = nm_utils_named_value_list_find (priv->s390_options.arr,
+	                                      priv->s390_options.len,
+	                                      key,
+	                                      TRUE);
+	if (idx < 0)
+		return NULL;
+	return priv->s390_options.arr[idx].value_str;
 }
 
 /**
@@ -507,14 +566,51 @@ nm_setting_wired_add_s390_option (NMSettingWired *setting,
                                   const char *key,
                                   const char *value)
 {
+	NMSettingWiredPrivate *priv;
+	gssize idx;
+	NMUtilsNamedValue *v;
+
 	g_return_val_if_fail (NM_IS_SETTING_WIRED (setting), FALSE);
-	g_return_val_if_fail (key && key[0], FALSE);
-	g_return_val_if_fail (g_strv_contains (valid_s390_opts, key), FALSE);
-	g_return_val_if_fail (value != NULL, FALSE);
+	g_return_val_if_fail (value, FALSE);
+
+	if (!valid_s390_opts_check (key)) {
+		g_return_val_if_fail (key, FALSE);
+		return FALSE;
+	}
+
+	priv = NM_SETTING_WIRED_GET_PRIVATE (setting);
+
+	idx = nm_utils_named_value_list_find (priv->s390_options.arr,
+	                                      priv->s390_options.len,
+	                                      key,
+	                                      TRUE);
+	if (idx < 0) {
+		gsize dst_idx = ~idx;
+
+		if (priv->s390_options.n_alloc < priv->s390_options.len + 1) {
+			priv->s390_options.n_alloc = NM_MAX (4,
+			                                     (priv->s390_options.len + 1) * 2);
+			priv->s390_options.arr = g_realloc (priv->s390_options.arr,
+			                                    priv->s390_options.n_alloc * sizeof (NMUtilsNamedValue));
+		}
+		if (dst_idx < priv->s390_options.len) {
+			memmove (&priv->s390_options.arr[dst_idx + 1],
+			         &priv->s390_options.arr[dst_idx],
+			         (priv->s390_options.len - dst_idx) * sizeof (NMUtilsNamedValue));
+		}
+		priv->s390_options.arr[dst_idx] = (NMUtilsNamedValue) {
+			.name      = g_strdup (key),
+			.value_str = g_strdup (value),
+		};
+		priv->s390_options.len++;
+	} else {
+		v = &priv->s390_options.arr[idx];
+		if (nm_streq (value, v->value_str))
+			return TRUE;
+		g_free ((char *) v->value_str);
+		v->value_str = g_strdup (value);
+	}
 
-	g_hash_table_insert (NM_SETTING_WIRED_GET_PRIVATE (setting)->s390_options,
-	                     g_strdup (key),
-	                     g_strdup (value));
 	_notify (setting, PROP_S390_OPTIONS);
 	return TRUE;
 }
@@ -534,20 +630,64 @@ gboolean
 nm_setting_wired_remove_s390_option (NMSettingWired *setting,
                                      const char *key)
 {
-	gboolean found;
+	NMSettingWiredPrivate *priv;
+	gsize dst_idx;
+	gssize idx;
 
 	g_return_val_if_fail (NM_IS_SETTING_WIRED (setting), FALSE);
-	g_return_val_if_fail (key && key[0], FALSE);
+	g_return_val_if_fail (key, FALSE);
+
+	priv = NM_SETTING_WIRED_GET_PRIVATE (setting);
+
+	idx = nm_utils_named_value_list_find (priv->s390_options.arr,
+	                                      priv->s390_options.len,
+	                                      key,
+	                                      TRUE);
+	if (idx < 0)
+		return FALSE;
+
+	dst_idx = idx;
+
+	g_free ((char *) priv->s390_options.arr[dst_idx].name);
+	g_free ((char *) priv->s390_options.arr[dst_idx].value_str);
+	if (dst_idx + 1 != priv->s390_options.len) {
+		memmove (&priv->s390_options.arr[dst_idx],
+		         &priv->s390_options.arr[dst_idx + 1],
+		         (priv->s390_options.len - dst_idx - 1) * sizeof (NMUtilsNamedValue));
+	}
+
+	priv->s390_options.len--;
 
-	found = g_hash_table_remove (NM_SETTING_WIRED_GET_PRIVATE (setting)->s390_options, key);
-	if (found)
-		_notify (setting, PROP_S390_OPTIONS);
-	return found;
+	_notify (setting, PROP_S390_OPTIONS);
+	return TRUE;
+}
+
+static void
+_s390_options_clear (NMSettingWiredPrivate *priv)
+{
+	guint i;
+
+	for (i = 0; i < priv->s390_options.len; i++) {
+		g_free ((char *) priv->s390_options.arr[i].name);
+		g_free ((char *) priv->s390_options.arr[i].value_str);
+	}
+	nm_clear_g_free (&priv->s390_options.arr);
+	priv->s390_options.len = 0;
+	priv->s390_options.n_alloc = 0;
+}
+
+void
+_nm_setting_wired_clear_s390_options (NMSettingWired *setting)
+{
+	g_return_if_fail (NM_IS_SETTING_WIRED (setting));
+
+	_s390_options_clear (NM_SETTING_WIRED_GET_PRIVATE (setting));
 }
 
 /**
  * nm_setting_wired_get_valid_s390_options:
- * @setting: (allow-none): the #NMSettingWired
+ * @setting: (allow-none): the #NMSettingWired. This argument is unused
+ *   and you may pass %NULL.
  *
  * Returns a list of valid s390 options.
  *
@@ -602,10 +742,8 @@ static gboolean
 verify (NMSetting *setting, NMConnection *connection, GError **error)
 {
 	NMSettingWiredPrivate *priv = NM_SETTING_WIRED_GET_PRIVATE (setting);
-	GHashTableIter iter;
-	const char *key, *value;
-	int i;
 	GError *local = NULL;
+	guint i;
 
 	if (!NM_IN_STRSET (priv->port, NULL, "tp", "aui", "bnc", "mii")) {
 		g_set_error (error,
@@ -628,10 +766,11 @@ verify (NMSetting *setting, NMConnection *connection, GError **error)
 	}
 
 	if (priv->device_mac_address && !nm_utils_hwaddr_valid (priv->device_mac_address, ETH_ALEN)) {
-		g_set_error_literal (error,
-		                     NM_CONNECTION_ERROR,
-		                     NM_CONNECTION_ERROR_INVALID_PROPERTY,
-		                     _("is not a valid MAC address"));
+		g_set_error (error,
+		             NM_CONNECTION_ERROR,
+		             NM_CONNECTION_ERROR_INVALID_PROPERTY,
+		             _("'%s' is not a valid MAC address"),
+		             priv->device_mac_address);
 		g_prefix_error (error, "%s.%s: ", NM_SETTING_WIRED_SETTING_NAME, NM_SETTING_WIRED_MAC_ADDRESS);
 		return FALSE;
 	}
@@ -672,16 +811,19 @@ verify (NMSetting *setting, NMConnection *connection, GError **error)
 		return FALSE;
 	}
 
-	g_hash_table_iter_init (&iter, priv->s390_options);
-	while (g_hash_table_iter_next (&iter, (gpointer) &key, (gpointer) &value)) {
-		if (   !g_strv_contains (valid_s390_opts, key)
-		    || value[0] == '\0'
-		    || (strlen (value) > 200)) {
+	for (i = 0; i < priv->s390_options.len; i++) {
+		const NMUtilsNamedValue *v = &priv->s390_options.arr[i];
+
+		nm_assert (v->name);
+
+		if (   !valid_s390_opts_check (v->name)
+		    || v->value_str[0] == '\0'
+		    || strlen (v->value_str) > 200) {
 			g_set_error (error,
 			             NM_CONNECTION_ERROR,
 			             NM_CONNECTION_ERROR_INVALID_PROPERTY,
 			             _("invalid '%s' or its value '%s'"),
-			             key, value);
+			             v->name, v->value_str);
 			g_prefix_error (error, "%s.%s: ", NM_SETTING_WIRED_SETTING_NAME, NM_SETTING_WIRED_S390_OPTIONS);
 			return FALSE;
 		}
@@ -690,10 +832,11 @@ verify (NMSetting *setting, NMConnection *connection, GError **error)
 	if (   priv->cloned_mac_address
 	    && !NM_CLONED_MAC_IS_SPECIAL (priv->cloned_mac_address)
 	    && !nm_utils_hwaddr_valid (priv->cloned_mac_address, ETH_ALEN)) {
-		g_set_error_literal (error,
-		                     NM_CONNECTION_ERROR,
-		                     NM_CONNECTION_ERROR_INVALID_PROPERTY,
-		                     _("is not a valid MAC address"));
+		g_set_error (error,
+		             NM_CONNECTION_ERROR,
+		             NM_CONNECTION_ERROR_INVALID_PROPERTY,
+		             _("'%s' is not a valid MAC address"),
+		             priv->cloned_mac_address);
 		g_prefix_error (error, "%s.%s: ", NM_SETTING_WIRED_SETTING_NAME, NM_SETTING_WIRED_CLONED_MAC_ADDRESS);
 		return FALSE;
 	}
@@ -732,10 +875,11 @@ verify (NMSetting *setting, NMConnection *connection, GError **error)
 	}
 
 	if (priv->wol_password && !nm_utils_hwaddr_valid (priv->wol_password, ETH_ALEN)) {
-		g_set_error_literal (error,
-		                     NM_CONNECTION_ERROR,
-		                     NM_CONNECTION_ERROR_INVALID_PROPERTY,
-		                     _("is not a valid MAC address"));
+		g_set_error (error,
+		             NM_CONNECTION_ERROR,
+		             NM_CONNECTION_ERROR_INVALID_PROPERTY,
+		             _("'%s' is not a valid MAC address"),
+		             priv->wol_password);
 		g_prefix_error (error, "%s.%s: ", NM_SETTING_WIRED_SETTING_NAME, NM_SETTING_WIRED_WAKE_ON_LAN_PASSWORD);
 		return FALSE;
 	}
@@ -762,26 +906,35 @@ verify (NMSetting *setting, NMConnection *connection, GError **error)
 static NMTernary
 compare_property (const NMSettInfoSetting *sett_info,
                   guint property_idx,
-                  NMSetting *setting,
-                  NMSetting *other,
+                  NMConnection *con_a,
+                  NMSetting *set_a,
+                  NMConnection *con_b,
+                  NMSetting *set_b,
                   NMSettingCompareFlags flags)
 {
 
 	if (nm_streq (sett_info->property_infos[property_idx].name, NM_SETTING_WIRED_CLONED_MAC_ADDRESS)) {
-		return    !other
-		       || nm_streq0 (NM_SETTING_WIRED_GET_PRIVATE (setting)->cloned_mac_address,
-		                     NM_SETTING_WIRED_GET_PRIVATE (other)->cloned_mac_address);
+		return    !set_b
+		       || nm_streq0 (NM_SETTING_WIRED_GET_PRIVATE (set_a)->cloned_mac_address,
+		                     NM_SETTING_WIRED_GET_PRIVATE (set_b)->cloned_mac_address);
 	}
 
 	return NM_SETTING_CLASS (nm_setting_wired_parent_class)->compare_property (sett_info,
 	                                                                           property_idx,
-	                                                                           setting,
-	                                                                           other,
+	                                                                           con_a,
+	                                                                           set_a,
+	                                                                           con_b,
+	                                                                           set_b,
 	                                                                           flags);
 }
 
 static GVariant *
-_override_autoneg_get (NMSetting *setting, const char *property)
+_override_autoneg_get (const NMSettInfoSetting *sett_info,
+                       guint property_idx,
+                       NMConnection *connection,
+                       NMSetting *setting,
+                       NMConnectionSerializationFlags flags,
+                       const NMConnectionSerializationOptions *options)
 {
 	return g_variant_new_boolean (nm_setting_wired_get_auto_negotiate ((NMSettingWired *) setting));
 }
@@ -802,6 +955,8 @@ get_property (GObject *object, guint prop_id,
 {
 	NMSettingWired *setting = NM_SETTING_WIRED (object);
 	NMSettingWiredPrivate *priv = NM_SETTING_WIRED_GET_PRIVATE (setting);
+	GHashTable *hash;
+	guint i;
 
 	switch (prop_id) {
 	case PROP_PORT:
@@ -838,7 +993,16 @@ get_property (GObject *object, guint prop_id,
 		g_value_set_string (value, nm_setting_wired_get_s390_nettype (setting));
 		break;
 	case PROP_S390_OPTIONS:
-		g_value_take_boxed (value, _nm_utils_copy_strdict (priv->s390_options));
+		hash = g_hash_table_new_full (nm_str_hash,
+		                              g_str_equal,
+		                              g_free,
+		                              g_free);
+		for (i = 0; i < priv->s390_options.len; i++) {
+			g_hash_table_insert (hash,
+			                     g_strdup (priv->s390_options.arr[i].name),
+			                     g_strdup (priv->s390_options.arr[i].value_str));
+		}
+		g_value_take_boxed (value, hash);
 		break;
 	case PROP_WAKE_ON_LAN:
 		g_value_set_uint (value, priv->wol);
@@ -859,7 +1023,6 @@ set_property (GObject *object, guint prop_id,
 	NMSettingWiredPrivate *priv = NM_SETTING_WIRED_GET_PRIVATE (object);
 	const char * const *blacklist;
 	const char *mac;
-	int i;
 
 	switch (prop_id) {
 	case PROP_PORT:
@@ -894,6 +1057,8 @@ set_property (GObject *object, guint prop_id,
 		blacklist = g_value_get_boxed (value);
 		g_array_set_size (priv->mac_address_blacklist, 0);
 		if (blacklist && *blacklist) {
+			guint i;
+
 			for (i = 0; blacklist[i]; i++) {
 				mac = _nm_utils_hwaddr_canonical_or_invalid (blacklist[i], ETH_ALEN);
 				g_array_append_val (priv->mac_address_blacklist, mac);
@@ -913,8 +1078,63 @@ set_property (GObject *object, guint prop_id,
 		priv->s390_nettype = g_value_dup_string (value);
 		break;
 	case PROP_S390_OPTIONS:
-		g_hash_table_unref (priv->s390_options);
-		priv->s390_options = _nm_utils_copy_strdict (g_value_get_boxed (value));
+		{
+			GHashTable *hash;
+
+			_s390_options_clear (priv);
+
+			hash = g_value_get_boxed (value);
+
+			priv->s390_options.n_alloc = hash ? g_hash_table_size (hash) : 0u;
+
+			if (priv->s390_options.n_alloc > 0) {
+				gboolean invalid_content = FALSE;
+				GHashTableIter iter;
+				const char *key;
+				const char *val;
+				guint i, j;
+
+				priv->s390_options.arr = g_new (NMUtilsNamedValue, priv->s390_options.n_alloc);
+				g_hash_table_iter_init (&iter, hash);
+				while (g_hash_table_iter_next (&iter, (gpointer *) &key, (gpointer *) &val)) {
+					if (!key || !val) {
+						invalid_content = TRUE;
+						continue;
+					}
+					nm_assert (priv->s390_options.len < priv->s390_options.n_alloc);
+					priv->s390_options.arr[priv->s390_options.len] = (NMUtilsNamedValue) {
+						.name      = g_strdup (key),
+						.value_str = g_strdup (val),
+					};
+					priv->s390_options.len++;
+				}
+				if (priv->s390_options.len > 1) {
+					nm_utils_named_value_list_sort (priv->s390_options.arr,
+					                                priv->s390_options.len,
+					                                NULL,
+					                                NULL);
+					/* prune duplicate keys. This is only possible if @hash does not use
+					 * g_str_equal() as compare function (which would be a bug).
+					 * Still, handle this, because we use later binary sort and rely
+					 * on unique names. One bug here, should not bork the remainder
+					 * of the program. */
+					j = 1;
+					for (i = 1; i < priv->s390_options.len; i++) {
+						if (nm_streq (priv->s390_options.arr[j - 1].name,
+						              priv->s390_options.arr[i].name)) {
+							g_free ((char *) priv->s390_options.arr[i].name);
+							g_free ((char *) priv->s390_options.arr[i].value_str);
+							invalid_content = TRUE;
+							continue;
+						}
+						priv->s390_options.arr[j++] = priv->s390_options.arr[i];
+					}
+					priv->s390_options.len = j;
+				}
+
+				g_return_if_fail (!invalid_content);
+			}
+		}
 		break;
 	case PROP_WAKE_ON_LAN:
 		priv->wol = g_value_get_uint (value);
@@ -936,8 +1156,6 @@ nm_setting_wired_init (NMSettingWired *setting)
 {
 	NMSettingWiredPrivate *priv = NM_SETTING_WIRED_GET_PRIVATE (setting);
 
-	priv->s390_options = g_hash_table_new_full (nm_str_hash, g_str_equal, g_free, g_free);
-
 	/* We use GArray rather than GPtrArray so it will automatically be NULL-terminated */
 	priv->mac_address_blacklist = g_array_new (TRUE, FALSE, sizeof (char *));
 	g_array_set_clear_func (priv->mac_address_blacklist, (GDestroyNotify) clear_blacklist_item);
@@ -965,7 +1183,7 @@ finalize (GObject *object)
 	g_free (priv->duplex);
 	g_free (priv->s390_nettype);
 
-	g_hash_table_destroy (priv->s390_options);
+	_s390_options_clear (priv);
 
 	g_free (priv->device_mac_address);
 	g_free (priv->cloned_mac_address);
@@ -1198,11 +1416,11 @@ nm_setting_wired_class_init (NMSettingWiredClass *klass)
 	                         G_PARAM_STATIC_STRINGS);
 
 	_properties_override_add_override (properties_override,
-	                                     obj_properties[PROP_CLONED_MAC_ADDRESS],
-	                                     G_VARIANT_TYPE_BYTESTRING,
-	                                     _nm_utils_hwaddr_cloned_get,
-	                                     _nm_utils_hwaddr_cloned_set,
-	                                     _nm_utils_hwaddr_cloned_not_set);
+	                                   obj_properties[PROP_CLONED_MAC_ADDRESS],
+	                                   G_VARIANT_TYPE_BYTESTRING,
+	                                   _nm_utils_hwaddr_cloned_get,
+	                                   _nm_utils_hwaddr_cloned_set,
+	                                   _nm_utils_hwaddr_cloned_not_set);
 
 	/* ---dbus---
 	 * property: assigned-mac-address