diff --git a/man/NetworkManager.conf.xml.in b/man/NetworkManager.conf.xml.in
index d9d51b846..ecba84439 100644
--- a/man/NetworkManager.conf.xml.in
+++ b/man/NetworkManager.conf.xml.in
@@ -148,7 +148,7 @@ Copyright 2010 - 2014 Red Hat, Inc.
no-auto-default
- Comma-separated list of devices for which
+ Specify devices for which
NetworkManager shouldn't create default wired connection
(Auto eth0). By default, NetworkManager creates a temporary
wired connection for any Ethernet device that is managed and
@@ -162,11 +162,15 @@ Copyright 2010 - 2014 Red Hat, Inc.
/var/run/NetworkManager/no-auto-default.state
to prevent creating the default connection for that device
again.
+ See for the syntax how to
+ specify a device.
+
+ Example:
- no-auto-default=00:22:68:5c:5d:c4,00:1e:65:ff:aa:ee
- no-auto-default=eth0,eth1
- no-auto-default=*
+no-auto-default=00:22:68:5c:5d:c4,00:1e:65:ff:aa:ee
+no-auto-default=eth0,eth1
+no-auto-default=*
@@ -176,8 +180,8 @@ Copyright 2010 - 2014 Red Hat, Inc.
ignore-carrier
- Comma-separated list of devices for which NetworkManager
- will (partially) ignore the carrier state. Normally, for
+ Specify devices for which NetworkManager will (partially)
+ ignore the carrier state. Normally, for
device types that support carrier-detect, such as Ethernet
and InfiniBand, NetworkManager will only allow a
connection to be activated on the device if carrier is
@@ -193,15 +197,14 @@ Copyright 2010 - 2014 Red Hat, Inc.
connection (whether static or dynamic) to remain active on
the device when carrier is lost.
-
- May have the special value * to apply
- to all devices.
-
Note that the "carrier" property of NMDevices and device D-Bus
interfaces will still reflect the actual device state; it's just
that NetworkManager will not make use of that information.
+ See for the syntax how to
+ specify a device.
+
@@ -279,17 +282,11 @@ Copyright 2010 - 2014 Red Hat, Inc.
unmanaged-devices
Set devices that should be ignored by
- NetworkManager when using the keyfile
- plugin. Devices are specified in the following
- format:
- mac:<hwaddr> or
- interface-name:<ifname>. Here
- hwaddr is the MAC address of the device
- to be ignored, in hex-digits-and-colons notation.
- ifname is the interface name of the
- ignored device.
- Multiple entries are separated with semicolons. No
- spaces are allowed in the value.
+ NetworkManager.
+
+ See for the syntax how to
+ specify a device.
+
Example:
@@ -545,6 +542,77 @@ unmanaged-devices=mac:00:22:68:1c:59:b1;mac:00:1E:65:30:D1:C4;interface-name:eth
+
+ Appendix
+
+ Device List Format
+
+ The configuration options main.no-auto-default, main.ignore-carrier,
+ and keyfile.unmanaged-devices select devices based on a list of matchings.
+ Devices can be specified using the following format:
+
+
+
+
+ *
+ Matches every device.
+
+
+ IFNAME
+ Case sensitive match of interface name of the device. Globbing is not supported.
+
+
+ HWADDR
+ Match the MAC address of the device. Globbing is not supported
+
+
+ interface-name:IFNAME
+ interface-name:~IFNAME
+ Case sensitive match of interface name of the device. Simple globbing is supported with
+ * and ?. Ranges and escaping is not supported.
+
+
+ interface-name:=IFNAME
+ Case sensitive match of interface name of the device. Globbing is disabled and IFNAME
+ is taken literally.
+
+
+ mac:HWADDR
+ Match the MAC address of the device. Globbing is not supported
+
+
+ s390-subchannels:HWADDR
+ Match the device based on the subchannel address. Globbing is not supported
+
+
+ except:SPEC
+ Negative match of a device. SPEC must be explicitly qualified with
+ a prefix such as interface-name:. A negative match has higher priority then the positive
+ matches above.
+
+
+ SPEC[,;]SPEC
+ Multiple specs can be concatenated with comman or semicolon. The order does not matter as
+ matches are either positive (inclusive) or negative, with negative matches having higher priority.
+ Backslash is supported to escape the separators ';' and ',', and to express special
+ characters such as newline ('\n'), tabulator ('\t'), whitespace ('\s') and backslash ('\\'). The globbing of
+ interface names cannot be escaped. Whitespace is taken literally so usually the specs will be concatenated
+ without spaces.
+
+
+
+
+ Example:
+
+interface-name:em4
+mac:00:22:68:1c:59:b1;mac:00:1E:65:30:D1:C4;interface-name:eth2
+interface-name:vboxnet*,except:interface-name:vboxnet2
+*,except:mac:00:22:68:1c:59:b1
+
+
+
+
+
See Also
diff --git a/src/NetworkManagerUtils.c b/src/NetworkManagerUtils.c
index 5b4cac94e..04ef2aa76 100644
--- a/src/NetworkManagerUtils.c
+++ b/src/NetworkManagerUtils.c
@@ -864,55 +864,99 @@ nm_utils_find_helper(const char *progname, const char *try_first, GError **error
/******************************************************************************************/
-gboolean
-nm_match_spec_string (const GSList *specs, const char *match)
+#define MAC_TAG "mac:"
+#define INTERFACE_NAME_TAG "interface-name:"
+#define SUBCHAN_TAG "s390-subchannels:"
+#define EXCEPT_TAG "except:"
+
+static const char *
+_match_except (const char *spec_str, gboolean *out_except)
{
- const GSList *iter;
-
- for (iter = specs; iter; iter = g_slist_next (iter)) {
- if (!g_ascii_strcasecmp ((const char *) iter->data, match))
- return TRUE;
- }
-
- return FALSE;
+ if (!g_ascii_strncasecmp (spec_str, EXCEPT_TAG, STRLEN (EXCEPT_TAG))) {
+ spec_str += STRLEN (EXCEPT_TAG);
+ *out_except = TRUE;
+ } else
+ *out_except = FALSE;
+ return spec_str;
}
-gboolean
+NMMatchSpecMatchType
nm_match_spec_hwaddr (const GSList *specs, const char *hwaddr)
{
const GSList *iter;
+ NMMatchSpecMatchType match = NM_MATCH_SPEC_NO_MATCH;
- g_return_val_if_fail (hwaddr != NULL, FALSE);
+ g_return_val_if_fail (hwaddr != NULL, NM_MATCH_SPEC_NO_MATCH);
for (iter = specs; iter; iter = g_slist_next (iter)) {
const char *spec_str = iter->data;
+ gboolean except;
- if ( !g_ascii_strncasecmp (spec_str, "mac:", 4)
- && nm_utils_hwaddr_matches (spec_str + 4, -1, hwaddr, -1))
- return TRUE;
+ if (!spec_str || !*spec_str)
+ continue;
- if (nm_utils_hwaddr_matches (spec_str, -1, hwaddr, -1))
- return TRUE;
+ spec_str = _match_except (spec_str, &except);
+
+ if ( !g_ascii_strncasecmp (spec_str, INTERFACE_NAME_TAG, STRLEN (INTERFACE_NAME_TAG))
+ || !g_ascii_strncasecmp (spec_str, SUBCHAN_TAG, STRLEN (SUBCHAN_TAG)))
+ continue;
+
+ if (!g_ascii_strncasecmp (spec_str, MAC_TAG, STRLEN (MAC_TAG)))
+ spec_str += STRLEN (MAC_TAG);
+ else if (except)
+ continue;
+
+ if (nm_utils_hwaddr_matches (spec_str, -1, hwaddr, -1)) {
+ if (except)
+ return NM_MATCH_SPEC_NEG_MATCH;
+ match = NM_MATCH_SPEC_MATCH;
+ }
}
-
- return FALSE;
+ return match;
}
-gboolean
+NMMatchSpecMatchType
nm_match_spec_interface_name (const GSList *specs, const char *interface_name)
{
- char *iface_match;
- gboolean matched;
+ const GSList *iter;
+ NMMatchSpecMatchType match = NM_MATCH_SPEC_NO_MATCH;
- g_return_val_if_fail (interface_name != NULL, FALSE);
+ g_return_val_if_fail (interface_name != NULL, NM_MATCH_SPEC_NO_MATCH);
- if (nm_match_spec_string (specs, interface_name))
- return TRUE;
+ for (iter = specs; iter; iter = g_slist_next (iter)) {
+ const char *spec_str = iter->data;
+ gboolean use_pattern = FALSE;
+ gboolean except;
- iface_match = g_strdup_printf ("interface-name:%s", interface_name);
- matched = nm_match_spec_string (specs, iface_match);
- g_free (iface_match);
- return matched;
+ if (!spec_str || !*spec_str)
+ continue;
+
+ spec_str = _match_except (spec_str, &except);
+
+ if ( !g_ascii_strncasecmp (spec_str, MAC_TAG, STRLEN (MAC_TAG))
+ || !g_ascii_strncasecmp (spec_str, SUBCHAN_TAG, STRLEN (SUBCHAN_TAG)))
+ continue;
+
+ if (!g_ascii_strncasecmp (spec_str, INTERFACE_NAME_TAG, STRLEN (INTERFACE_NAME_TAG))) {
+ spec_str += STRLEN (INTERFACE_NAME_TAG);
+ if (spec_str[0] == '=')
+ spec_str += 1;
+ else {
+ if (spec_str[0] == '~')
+ spec_str += 1;
+ use_pattern=TRUE;
+ }
+ } else if (except)
+ continue;
+
+ if ( !strcmp (spec_str, interface_name)
+ || (use_pattern && g_pattern_match_simple (spec_str, interface_name))) {
+ if (except)
+ return NM_MATCH_SPEC_NEG_MATCH;
+ match = NM_MATCH_SPEC_MATCH;
+ }
+ }
+ return match;
}
#define BUFSIZE 10
@@ -981,33 +1025,108 @@ parse_subchannels (const char *subchannels, guint32 *a, guint32 *b, guint32 *c)
return TRUE;
}
-#define SUBCHAN_TAG "s390-subchannels:"
-
-gboolean
+NMMatchSpecMatchType
nm_match_spec_s390_subchannels (const GSList *specs, const char *subchannels)
{
const GSList *iter;
guint32 a = 0, b = 0, c = 0;
guint32 spec_a = 0, spec_b = 0, spec_c = 0;
+ NMMatchSpecMatchType match = NM_MATCH_SPEC_NO_MATCH;
- g_return_val_if_fail (subchannels != NULL, FALSE);
+ g_return_val_if_fail (subchannels != NULL, NM_MATCH_SPEC_NO_MATCH);
if (!parse_subchannels (subchannels, &a, &b, &c))
- return FALSE;
+ return NM_MATCH_SPEC_NO_MATCH;
for (iter = specs; iter; iter = g_slist_next (iter)) {
- const char *spec = iter->data;
+ const char *spec_str = iter->data;
+ gboolean except;
- if (!strncmp (spec, SUBCHAN_TAG, strlen (SUBCHAN_TAG))) {
- spec += strlen (SUBCHAN_TAG);
- if (parse_subchannels (spec, &spec_a, &spec_b, &spec_c)) {
- if (a == spec_a && b == spec_b && c == spec_c)
- return TRUE;
+ if (!spec_str || !*spec_str)
+ continue;
+
+ spec_str = _match_except (spec_str, &except);
+
+ if (!g_ascii_strncasecmp (spec_str, SUBCHAN_TAG, STRLEN (SUBCHAN_TAG))) {
+ spec_str += STRLEN (SUBCHAN_TAG);
+ if (parse_subchannels (spec_str, &spec_a, &spec_b, &spec_c)) {
+ if (a == spec_a && b == spec_b && c == spec_c) {
+ if (except)
+ return NM_MATCH_SPEC_NEG_MATCH;
+ match = NM_MATCH_SPEC_MATCH;
+ }
}
}
}
+ return match;
+}
- return FALSE;
+GSList *
+nm_match_spec_split (const char *value)
+{
+ char *string_value, *p, *q0, *q;
+ GSList *pieces = NULL;
+
+ if (!value || !*value)
+ return NULL;
+
+ /* Copied from glibs g_key_file_parse_value_as_string() function
+ * and adjusted. */
+
+ string_value = g_new (gchar, strlen (value) + 1);
+
+ p = (gchar *) value;
+ q0 = q = string_value;
+ while (*p) {
+ if (*p == '\\') {
+ p++;
+
+ switch (*p) {
+ case 's':
+ *q = ' ';
+ break;
+ case 'n':
+ *q = '\n';
+ break;
+ case 't':
+ *q = '\t';
+ break;
+ case 'r':
+ *q = '\r';
+ break;
+ case '\\':
+ *q = '\\';
+ break;
+ case '\0':
+ break;
+ default:
+ if (NM_IN_SET (*p, ',', ';'))
+ *q = *p;
+ else {
+ *q++ = '\\';
+ *q = *p;
+ }
+ break;
+ }
+ } else {
+ *q = *p;
+ if (NM_IN_SET (*p, ',', ';')) {
+ if (q0 < q)
+ pieces = g_slist_prepend (pieces, g_strndup (q0, q - q0));
+ q0 = q + 1;
+ }
+ }
+ if (*p == '\0')
+ break;
+ q++;
+ p++;
+ }
+
+ *q = '\0';
+ if (q0 < q)
+ pieces = g_slist_prepend (pieces, g_strndup (q0, q - q0));
+ g_free (string_value);
+ return g_slist_reverse (pieces);
}
const char *
diff --git a/src/NetworkManagerUtils.h b/src/NetworkManagerUtils.h
index 049856556..4d797fafe 100644
--- a/src/NetworkManagerUtils.h
+++ b/src/NetworkManagerUtils.h
@@ -96,10 +96,16 @@ const char *nm_utils_find_helper (const char *progname,
const char *try_first,
GError **error);
-gboolean nm_match_spec_string (const GSList *specs, const char *string);
-gboolean nm_match_spec_hwaddr (const GSList *specs, const char *hwaddr);
-gboolean nm_match_spec_s390_subchannels (const GSList *specs, const char *subchannels);
-gboolean nm_match_spec_interface_name (const GSList *specs, const char *interface_name);
+typedef enum {
+ NM_MATCH_SPEC_NO_MATCH = 0,
+ NM_MATCH_SPEC_MATCH = 1,
+ NM_MATCH_SPEC_NEG_MATCH = 2,
+} NMMatchSpecMatchType;
+
+NMMatchSpecMatchType nm_match_spec_hwaddr (const GSList *specs, const char *hwaddr);
+NMMatchSpecMatchType nm_match_spec_s390_subchannels (const GSList *specs, const char *subchannels);
+NMMatchSpecMatchType nm_match_spec_interface_name (const GSList *specs, const char *interface_name);
+GSList *nm_match_spec_split (const char *value);
const char *nm_utils_get_shared_wifi_permission (NMConnection *connection);
diff --git a/src/devices/nm-device-ethernet.c b/src/devices/nm-device-ethernet.c
index af24f8be6..24aa8967d 100644
--- a/src/devices/nm-device-ethernet.c
+++ b/src/devices/nm-device-ethernet.c
@@ -1495,15 +1495,19 @@ new_default_connection (NMDevice *self)
return connection;
}
-static gboolean
+static NMMatchSpecMatchType
spec_match_list (NMDevice *device, const GSList *specs)
{
+ NMMatchSpecMatchType matched = NM_MATCH_SPEC_NO_MATCH, m;
NMDeviceEthernetPrivate *priv = NM_DEVICE_ETHERNET_GET_PRIVATE (device);
- if (priv->subchannels && nm_match_spec_s390_subchannels (specs, priv->subchannels))
- return TRUE;
-
- return NM_DEVICE_CLASS (nm_device_ethernet_parent_class)->spec_match_list (device, specs);
+ if (priv->subchannels)
+ matched = nm_match_spec_s390_subchannels (specs, priv->subchannels);
+ if (matched != NM_MATCH_SPEC_NEG_MATCH) {
+ m = NM_DEVICE_CLASS (nm_device_ethernet_parent_class)->spec_match_list (device, specs);
+ matched = MAX (matched, m);
+ }
+ return matched;
}
static void
diff --git a/src/devices/nm-device.c b/src/devices/nm-device.c
index 9df3712ed..64140de7e 100644
--- a/src/devices/nm-device.c
+++ b/src/devices/nm-device.c
@@ -8080,24 +8080,30 @@ nm_device_spec_match_list (NMDevice *self, const GSList *specs)
if (!specs)
return FALSE;
- return NM_DEVICE_GET_CLASS (self)->spec_match_list (self, specs);
+ return NM_DEVICE_GET_CLASS (self)->spec_match_list (self, specs) == NM_MATCH_SPEC_MATCH;
}
-static gboolean
+static NMMatchSpecMatchType
spec_match_list (NMDevice *self, const GSList *specs)
{
NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self);
- gboolean matched = FALSE;
-
- if (nm_match_spec_string (specs, "*"))
- return TRUE;
-
- if (priv->hw_addr_len)
- matched = nm_match_spec_hwaddr (specs, priv->hw_addr);
-
- if (!matched)
- matched = nm_match_spec_interface_name (specs, nm_device_get_iface (self));
+ NMMatchSpecMatchType matched = NM_MATCH_SPEC_NO_MATCH, m;
+ const GSList *iter;
+ for (iter = specs; iter; iter = g_slist_next (iter)) {
+ if (!strcmp ((const char *) iter->data, "*")) {
+ matched = NM_MATCH_SPEC_MATCH;
+ break;
+ }
+ }
+ if (priv->hw_addr_len) {
+ m = nm_match_spec_hwaddr (specs, priv->hw_addr);
+ matched = MAX (matched, m);
+ }
+ if (matched != NM_MATCH_SPEC_NEG_MATCH) {
+ m = nm_match_spec_interface_name (specs, nm_device_get_iface (self));
+ matched = MAX (matched, m);
+ }
return matched;
}
diff --git a/src/devices/nm-device.h b/src/devices/nm-device.h
index e6f07f2ef..d3b4139d3 100644
--- a/src/devices/nm-device.h
+++ b/src/devices/nm-device.h
@@ -187,7 +187,7 @@ typedef struct {
/* Sync deactivating (in the DISCONNECTED phase) */
void (* deactivate) (NMDevice *self);
- gboolean (* spec_match_list) (NMDevice *self, const GSList *specs);
+ NMMatchSpecMatchType (* spec_match_list) (NMDevice *self, const GSList *specs);
/* Update the connection with currently configured L2 settings */
void (* update_connection) (NMDevice *device, NMConnection *connection);
diff --git a/src/nm-config.c b/src/nm-config.c
index 6b9887ad1..594d05f2e 100644
--- a/src/nm-config.c
+++ b/src/nm-config.c
@@ -32,6 +32,7 @@
#include "NetworkManagerUtils.h"
#include "gsystem-local-alloc.h"
#include "nm-enum-types.h"
+#include "nm-core-internal.h"
#include
#include
@@ -76,7 +77,7 @@ typedef struct {
char *debug;
- char **ignore_carrier;
+ GSList *ignore_carrier;
gboolean configure_and_quit;
} NMConfigPrivate;
@@ -237,21 +238,10 @@ nm_config_get_configure_and_quit (NMConfig *config)
gboolean
nm_config_get_ignore_carrier (NMConfig *config, NMDevice *device)
{
- NMConfigPrivate *priv = NM_CONFIG_GET_PRIVATE (config);
- GSList *specs = NULL;
- int i;
- gboolean match;
+ g_return_val_if_fail (NM_IS_CONFIG (config), FALSE);
+ g_return_val_if_fail (NM_IS_DEVICE (device), FALSE);
- if (!priv->ignore_carrier)
- return FALSE;
-
- for (i = 0; priv->ignore_carrier[i]; i++)
- specs = g_slist_prepend (specs, priv->ignore_carrier[i]);
-
- match = nm_device_spec_match_list (device, specs);
-
- g_slist_free (specs);
- return match;
+ return nm_device_spec_match_list (device, NM_CONFIG_GET_PRIVATE (config)->ignore_carrier);
}
/************************************************************************/
@@ -689,6 +679,15 @@ read_entire_config (const NMConfigCmdLineOptions *cli,
return keyfile;
}
+GSList *
+nm_config_get_device_match_spec (const GKeyFile *keyfile, const char *group, const char *key)
+{
+ gs_free char *value = NULL;
+
+ value = g_key_file_get_string ((GKeyFile *) keyfile, group, key, NULL);
+ return nm_match_spec_split (value);
+}
+
/************************************************************************/
void
@@ -813,7 +812,8 @@ init_sync (GInitable *initable, GCancellable *cancellable, GError **error)
char *config_main_file = NULL;
char *config_description = NULL;
char **no_auto_default;
- char **no_auto_default_orig;
+ GSList *no_auto_default_orig_list;
+ GPtrArray *no_auto_default_orig;
if (priv->config_dir) {
/* Object is already initialized. */
@@ -859,17 +859,22 @@ init_sync (GInitable *initable, GCancellable *cancellable, GError **error)
priv->debug = g_key_file_get_value (keyfile, "main", "debug", NULL);
- priv->ignore_carrier = g_key_file_get_string_list (keyfile, "main", "ignore-carrier", NULL, NULL);
+ priv->ignore_carrier = nm_config_get_device_match_spec (keyfile, "main", "ignore-carrier");
priv->configure_and_quit = _get_bool_value (keyfile, "main", "configure-and-quit", FALSE);
- no_auto_default_orig = g_key_file_get_string_list (keyfile, "main", "no-auto-default", NULL, NULL);
- no_auto_default = no_auto_default_merge_from_file (priv->no_auto_default_file, (const char *const *) no_auto_default_orig);
+ no_auto_default_orig_list = nm_config_get_device_match_spec (keyfile, "main", "no-auto-default");
+
+ no_auto_default_orig = _nm_utils_copy_slist_to_array (no_auto_default_orig_list, NULL, NULL);
+ g_ptr_array_add (no_auto_default_orig, NULL);
+ no_auto_default = no_auto_default_merge_from_file (priv->no_auto_default_file, (const char *const *) no_auto_default_orig->pdata);
+ g_ptr_array_unref (no_auto_default_orig);
+
+ g_slist_free_full (no_auto_default_orig_list, g_free);
priv->config_data_orig = nm_config_data_new (config_main_file, config_description, (const char *const*) no_auto_default, keyfile);
g_strfreev (no_auto_default);
- g_strfreev (no_auto_default_orig);
priv->config_data = g_object_ref (priv->config_data_orig);
@@ -910,7 +915,7 @@ finalize (GObject *gobject)
g_free (priv->log_level);
g_free (priv->log_domains);
g_free (priv->debug);
- g_strfreev (priv->ignore_carrier);
+ g_slist_free_full (priv->ignore_carrier, g_free);
_nm_config_cmd_line_options_clear (&priv->cli);
diff --git a/src/nm-config.h b/src/nm-config.h
index 530cbade9..46388b08e 100644
--- a/src/nm-config.h
+++ b/src/nm-config.h
@@ -90,6 +90,7 @@ NMConfig *nm_config_setup (const NMConfigCmdLineOptions *cli, GError **error);
void nm_config_reload (NMConfig *config);
GKeyFile *nm_config_create_keyfile (void);
+GSList *nm_config_get_device_match_spec (const GKeyFile *keyfile, const char *group, const char *key);
G_END_DECLS
diff --git a/src/settings/plugins/keyfile/plugin.c b/src/settings/plugins/keyfile/plugin.c
index edaa7d456..611192baa 100644
--- a/src/settings/plugins/keyfile/plugin.c
+++ b/src/settings/plugins/keyfile/plugin.c
@@ -585,45 +585,19 @@ get_unmanaged_specs (NMSystemConfigInterface *config)
GKeyFile *key_file;
GSList *specs = NULL;
GError *error = NULL;
- char *str;
if (!priv->conf_file)
return NULL;
- key_file = g_key_file_new ();
- if (!parse_key_file_allow_none (priv, key_file, &error))
- goto out;
+ key_file = nm_config_create_keyfile ();
+ if (parse_key_file_allow_none (priv, key_file, &error))
+ specs = nm_config_get_device_match_spec (key_file, "keyfile", "unmanaged-devices");
- str = g_key_file_get_value (key_file, "keyfile", "unmanaged-devices", NULL);
- if (str) {
- char **udis;
- int i;
-
- udis = g_strsplit_set (str, ";,", -1);
- g_free (str);
-
- for (i = 0; udis[i] != NULL; i++) {
- /* Verify unmanaged specification and add it to the list */
- if (!strncmp (udis[i], "mac:", 4) && nm_utils_hwaddr_valid (udis[i] + 4, -1)) {
- specs = g_slist_append (specs, udis[i]);
- } else if (!strncmp (udis[i], "interface-name:", 15) && nm_utils_iface_valid_name (udis[i] + 15)) {
- specs = g_slist_append (specs, udis[i]);
- } else {
- nm_log_warn (LOGD_SETTINGS, "keyfile: error in file '%s': invalid unmanaged-devices entry: '%s'", priv->conf_file, udis[i]);
- g_free (udis[i]);
- }
- }
-
- g_free (udis); /* Yes, g_free, not g_strfreev because we need the strings in the list */
- }
-
- out:
if (error) {
nm_log_warn (LOGD_SETTINGS, "keyfile: error getting unmanaged specs: %s", error->message);
g_error_free (error);
}
- if (key_file)
- g_key_file_free (key_file);
+ g_key_file_free (key_file);
return specs;
}
diff --git a/src/tests/test-general.c b/src/tests/test-general.c
index f118156b3..21533cc74 100644
--- a/src/tests/test-general.c
+++ b/src/tests/test-general.c
@@ -774,6 +774,118 @@ test_nm_utils_uuid_generate_from_strings (void)
/*******************************************/
+static const char *_test_match_spec_all[] = {
+ "e",
+ "em",
+ "em*",
+ "em\\",
+ "em\\*",
+ "em\\1",
+ "em\\11",
+ "em\\2",
+ "em1",
+ "em11",
+ "em2",
+ "=em*",
+ NULL
+};
+
+static gboolean
+_test_match_spec_contains (const char **matches, const char *match)
+{
+ guint i;
+
+ for (i = 0; matches && matches[i]; i++) {
+ if (strcmp (match, matches[i]) == 0)
+ return TRUE;
+ }
+ return FALSE;
+}
+
+static void
+test_match_spec_ifname (const char *spec_str, const char **matches, const char **neg_matches)
+{
+ const char *m;
+ GSList *specs, *specs_reverse = NULL;
+ guint i;
+
+ g_assert (spec_str);
+
+ specs = nm_match_spec_split (spec_str);
+ specs_reverse = g_slist_reverse (g_slist_copy (specs));
+
+ for (i = 0; matches && matches[i]; i++) {
+ g_assert (nm_match_spec_interface_name (specs, matches[i]) == NM_MATCH_SPEC_MATCH);
+ g_assert (nm_match_spec_interface_name (specs_reverse, matches[i]) == NM_MATCH_SPEC_MATCH);
+ }
+ for (i = 0; neg_matches && neg_matches[i]; i++) {
+ g_assert (nm_match_spec_interface_name (specs, neg_matches[i]) == NM_MATCH_SPEC_NEG_MATCH);
+ g_assert (nm_match_spec_interface_name (specs_reverse, neg_matches[i]) == NM_MATCH_SPEC_NEG_MATCH);
+ }
+ for (i = 0; (m = _test_match_spec_all[i]); i++) {
+ if (_test_match_spec_contains (matches, m))
+ continue;
+ if (_test_match_spec_contains (neg_matches, m))
+ continue;
+ g_assert (nm_match_spec_interface_name (specs, m) == NM_MATCH_SPEC_NO_MATCH);
+ g_assert (nm_match_spec_interface_name (specs_reverse, m) == NM_MATCH_SPEC_NO_MATCH);
+ }
+
+ g_slist_free (specs_reverse);
+ g_slist_free_full (specs, g_free);
+}
+
+static void
+test_nm_match_spec_interface_name (void)
+{
+#define S(...) ((const char *[]) { __VA_ARGS__, NULL } )
+ test_match_spec_ifname ("em1",
+ S ("em1"),
+ NULL);
+ test_match_spec_ifname ("em1,em2",
+ S ("em1", "em2"),
+ NULL);
+ test_match_spec_ifname ("em1,em2,interface-name:em2",
+ S ("em1", "em2"),
+ NULL);
+ test_match_spec_ifname ("interface-name:em1",
+ S ("em1"),
+ NULL);
+ test_match_spec_ifname ("interface-name:em*",
+ S ("em", "em*", "em\\", "em\\*", "em\\1", "em\\11", "em\\2", "em1", "em11", "em2", "em3"),
+ NULL);
+ test_match_spec_ifname ("interface-name:em\\*",
+ S ("em\\", "em\\*", "em\\1", "em\\11", "em\\2"),
+ NULL);
+ test_match_spec_ifname ("interface-name:~em\\*",
+ S ("em\\", "em\\*", "em\\1", "em\\11", "em\\2"),
+ NULL);
+ test_match_spec_ifname ("interface-name:=em*",
+ S ("em*"),
+ NULL);
+ test_match_spec_ifname ("interface-name:em*,except:interface-name:em1*",
+ S ("em", "em*", "em\\", "em\\*", "em\\1", "em\\11", "em\\2", "em2", "em3"),
+ S ("em1", "em11"));
+ test_match_spec_ifname ("interface-name:em*,except:interface-name:=em*",
+ S ("em", "em\\", "em\\*", "em\\1", "em\\11", "em\\2", "em1", "em11", "em2", "em3"),
+ S ("em*"));
+ test_match_spec_ifname ("aa,bb,cc\\,dd,e,,",
+ S ("aa", "bb", "cc,dd", "e"),
+ NULL);
+ test_match_spec_ifname ("aa;bb;cc\\;dd;e,;",
+ S ("aa", "bb", "cc;dd", "e"),
+ NULL);
+ test_match_spec_ifname ("interface-name:em\\;1,em\\,2,\\,,\\\\,,em\\\\x",
+ S ("em;1", "em,2", ",", "\\", "em\\x"),
+ NULL);
+ test_match_spec_ifname (" , interface-name:a, ,",
+ S (" ", " ", " interface-name:a"),
+ NULL);
+#undef S
+}
+
+/*******************************************/
+
NMTST_DEFINE ();
int
@@ -797,6 +909,7 @@ main (int argc, char **argv)
g_test_add_func ("/general/connection-sort/autoconnect-priority", test_connection_sort_autoconnect_priority);
g_test_add_func ("/general/nm_utils_uuid_generate_from_strings", test_nm_utils_uuid_generate_from_strings);
+ g_test_add_func ("/general/nm_match_spec_interface_name", test_nm_match_spec_interface_name);
return g_test_run ();
}