core: support "except:" spec to negate match

Extend nm_match_spec_*() to support an "except:" prefix to negate
the result of a match. "except:" only works when followed by
an exact match type, for example "except:interface-name:vboxnet0",
but not "except:vboxnet0".

A matching "except:" spec always wins, regardless of other positive
matchings.

(cherry picked from commit 5c2e1afd1b)
This commit is contained in:
Thomas Haller 2015-01-23 17:02:16 +01:00 committed by Beniamino Galvani
parent 3de7acc37a
commit aaca52b261
6 changed files with 120 additions and 53 deletions

View file

@ -833,47 +833,72 @@ nm_utils_find_helper(const char *progname, const char *try_first, GError **error
#define MAC_TAG "mac:"
#define INTERFACE_NAME_TAG "interface-name:"
#define SUBCHAN_TAG "s390-subchannels:"
#define EXCEPT_TAG "except:"
gboolean
static const char *
_match_except (const char *spec_str, gboolean *out_except)
{
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;
}
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 (!spec_str || !*spec_str)
continue;
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))
return TRUE;
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)
{
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);
for (iter = specs; iter; iter = g_slist_next (iter)) {
const char *spec_str = iter->data;
gboolean use_pattern = FALSE;
gboolean except;
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;
@ -887,14 +912,17 @@ nm_match_spec_interface_name (const GSList *specs, const char *interface_name)
spec_str += 1;
use_pattern=TRUE;
}
}
} else if (except)
continue;
if (!strcmp (spec_str, interface_name))
return TRUE;
if (use_pattern && g_pattern_match_simple (spec_str, interface_name))
return TRUE;
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 FALSE;
return match;
}
#define BUFSIZE 10
@ -963,34 +991,40 @@ parse_subchannels (const char *subchannels, guint32 *a, guint32 *b, guint32 *c)
return TRUE;
}
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_str = iter->data;
gboolean except;
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)
return TRUE;
if (a == spec_a && b == spec_b && c == spec_c) {
if (except)
return NM_MATCH_SPEC_NEG_MATCH;
match = NM_MATCH_SPEC_MATCH;
}
}
}
}
return FALSE;
return match;
}
const char *

View file

@ -94,9 +94,15 @@ const char *nm_utils_find_helper (const char *progname,
const char *try_first,
GError **error);
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);
const char *nm_utils_get_shared_wifi_permission (NMConnection *connection);

View file

@ -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

View file

@ -8209,27 +8209,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;
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, "*"))
return TRUE;
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);
}
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));
return matched;
}

View file

@ -213,7 +213,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);

View file

@ -803,7 +803,7 @@ _test_match_spec_contains (const char **matches, const char *match)
}
static void
test_match_spec_ifname (const char *spec_str, const char **matches)
test_match_spec_ifname (const char *spec_str, const char **matches, const char **neg_matches)
{
const char *m;
char **spec_str_split;
@ -819,14 +819,20 @@ test_match_spec_ifname (const char *spec_str, const char **matches)
specs = g_slist_reverse (g_slist_copy (specs_reverse));
for (i = 0; matches && matches[i]; i++) {
g_assert (nm_match_spec_interface_name (specs, matches[i]));
g_assert (nm_match_spec_interface_name (specs_reverse, matches[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;
g_assert (!nm_match_spec_interface_name (specs, m));
g_assert (!nm_match_spec_interface_name (specs_reverse, m));
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);
@ -839,20 +845,34 @@ test_nm_match_spec_interface_name (void)
{
#define S(...) ((const char *[]) { __VA_ARGS__, NULL } )
test_match_spec_ifname ("em1",
S ("em1"));
S ("em1"),
NULL);
test_match_spec_ifname ("em1,em2",
S ("em1", "em2"));
S ("em1", "em2"),
NULL);
test_match_spec_ifname ("em1,em2,interface-name:em2",
S ("em1", "em2"));
S ("em1", "em2"),
NULL);
test_match_spec_ifname ("interface-name:em1",
S ("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"));
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"));
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"));
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*"));
#undef S
}