core: merge branch 'th/device-match-fix'

https://bugzilla.redhat.com/show_bug.cgi?id=1658057

(cherry picked from commit 066c464576)
This commit is contained in:
Thomas Haller 2018-12-11 14:02:21 +01:00
commit 429c311275
5 changed files with 87 additions and 34 deletions

View file

@ -2,5 +2,5 @@
# But don't do so for dhclient DHCP plugin, as the default of dhclient may
# be specified via /etc/dhcp (and anyway defaults to "hardware" already).
[connection-00-server-dhcp-client-id]
match-device=except:dhcp-plugin:dhclient
match-device=*,except:dhcp-plugin:dhclient
ipv4.dhcp-client-id=mac

View file

@ -1170,6 +1170,8 @@ enable=env:TAG1
be used to negate the match. Note that if one except-predicate
matches, the entire configuration will be disabled.
In other words, a except predicate always wins over other predicates.
If the setting only consists of "except:" matches and none of the
negative conditions are satisfied, the configuration is still enabled.
<programlisting>
# enable the configuration either when the environment variable
# is present or the version is at least 1.2.0.
@ -1364,7 +1366,11 @@ enable=nm-version-min:1.3,nm-version-min:1.2.6,nm-version-min:1.0.16
<term>except:SPEC</term>
<listitem><para>Negative match of a device. <literal>SPEC</literal> must be explicitly qualified with
a prefix such as <literal>interface-name:</literal>. A negative match has higher priority then the positive
matches above.</para></listitem>
matches above.</para>
<para>If there is a list consisting only of negative matches, the behavior is the same as if there
is also match-all. That means, if none of all the negative matches is satisfied, the overall result is
still a positive match. That means, <literal>"except:interface-name:eth0"</literal> is the same as
<literal>"*,except:interface-name:eth0"</literal>.</para></listitem>
</varlistentry>
<varlistentry>
<term>SPEC[,;]SPEC</term>

View file

@ -15778,8 +15778,8 @@ nm_device_spec_match_list_full (NMDevice *self, const GSList *specs, int no_matc
nm_device_get_driver (self),
nm_device_get_driver_version (self),
nm_device_get_permanent_hw_address (self),
nm_dhcp_manager_get_config (nm_dhcp_manager_get ()),
klass->get_s390_subchannels ? klass->get_s390_subchannels (self) : NULL);
klass->get_s390_subchannels ? klass->get_s390_subchannels (self) : NULL,
nm_dhcp_manager_get_config (nm_dhcp_manager_get ()));
switch (m) {
case NM_MATCH_SPEC_MATCH:

View file

@ -1295,6 +1295,34 @@ match_device_hwaddr_eval (const char *spec_str,
_has; \
})
static NMMatchSpecMatchType
_match_result (gboolean has_except,
gboolean has_not_except,
gboolean has_match,
gboolean has_match_except)
{
if ( has_except
&& !has_not_except) {
/* a match spec that only consists of a list of except matches is treated specially. */
nm_assert (!has_match);
if (has_match_except) {
/* one of the "except:" matches matched. The result is an explicit
* negative match. */
return NM_MATCH_SPEC_NEG_MATCH;
} else {
/* none of the "except:" matches matched. The result is a positive match,
* despite there being no positive match. */
return NM_MATCH_SPEC_MATCH;
}
}
if (has_match_except)
return NM_MATCH_SPEC_NEG_MATCH;
if (has_match)
return NM_MATCH_SPEC_MATCH;
return NM_MATCH_SPEC_NO_MATCH;
}
static const char *
match_except (const char *spec_str, gboolean *out_except)
{
@ -1401,9 +1429,11 @@ nm_match_spec_device (const GSList *specs,
const char *dhcp_plugin)
{
const GSList *iter;
NMMatchSpecMatchType match;
gboolean has_match = FALSE;
gboolean has_match_except = FALSE;
gboolean has_except = FALSE;
gboolean has_not_except = FALSE;
const char *spec_str;
gboolean except;
MatchDeviceData match_data = {
.interface_name = interface_name,
.device_type = nm_str_not_empty (device_type),
@ -1423,19 +1453,9 @@ nm_match_spec_device (const GSList *specs,
if (!specs)
return NM_MATCH_SPEC_NO_MATCH;
match = NM_MATCH_SPEC_NO_MATCH;
/* pre-search for "*" */
for (iter = specs; iter; iter = iter->next) {
spec_str = iter->data;
gboolean except;
if (spec_str && spec_str[0] == '*' && spec_str[1] == '\0') {
match = NM_MATCH_SPEC_MATCH;
break;
}
}
for (iter = specs; iter; iter = iter->next) {
spec_str = iter->data;
if (!spec_str || !*spec_str)
@ -1443,10 +1463,14 @@ nm_match_spec_device (const GSList *specs,
spec_str = match_except (spec_str, &except);
if ( !except
&& match == NM_MATCH_SPEC_MATCH) {
/* we have no "except-match" but already match. No need to evaluate
* the match, we cannot match stronger. */
if (except)
has_except = TRUE;
else
has_not_except = TRUE;
if ( ( except && has_match_except)
|| (!except && has_match)) {
/* evaluating the match does not give new information. Skip it. */
continue;
}
@ -1456,11 +1480,12 @@ nm_match_spec_device (const GSList *specs,
continue;
if (except)
return NM_MATCH_SPEC_NEG_MATCH;
match = NM_MATCH_SPEC_MATCH;
has_match_except = TRUE;
else
has_match = TRUE;
}
return match;
return _match_result (has_except, has_not_except, has_match, has_match_except);
}
static gboolean
@ -1530,7 +1555,10 @@ NMMatchSpecMatchType
nm_match_spec_config (const GSList *specs, guint cur_nm_version, const char *env)
{
const GSList *iter;
NMMatchSpecMatchType match = NM_MATCH_SPEC_NO_MATCH;
gboolean has_match = FALSE;
gboolean has_match_except = FALSE;
gboolean has_except = FALSE;
gboolean has_not_except = FALSE;
if (!specs)
return NM_MATCH_SPEC_NO_MATCH;
@ -1545,6 +1573,17 @@ nm_match_spec_config (const GSList *specs, guint cur_nm_version, const char *env
spec_str = match_except (spec_str, &except);
if (except)
has_except = TRUE;
else
has_not_except = TRUE;
if ( ( except && has_match_except)
|| (!except && has_match)) {
/* evaluating the match does not give new information. Skip it. */
continue;
}
if (_MATCH_CHECK (spec_str, MATCH_TAG_CONFIG_NM_VERSION))
v_match = match_config_eval (spec_str, MATCH_TAG_CONFIG_NM_VERSION, cur_nm_version);
else if (_MATCH_CHECK (spec_str, MATCH_TAG_CONFIG_NM_VERSION_MIN))
@ -1554,15 +1593,18 @@ nm_match_spec_config (const GSList *specs, guint cur_nm_version, const char *env
else if (_MATCH_CHECK (spec_str, MATCH_TAG_CONFIG_ENV))
v_match = env && env[0] && !strcmp (spec_str, env);
else
v_match = FALSE;
if (!v_match)
continue;
if (v_match) {
if (except)
return NM_MATCH_SPEC_NEG_MATCH;
match = NM_MATCH_SPEC_MATCH;
}
if (except)
has_match_except = TRUE;
else
has_match = TRUE;
}
return match;
return _match_result (has_except, has_not_except, has_match, has_match_except);
}
#undef _MATCH_CHECK

View file

@ -1233,6 +1233,10 @@ test_match_spec_device (void)
S ("em", "em\\", "em\\*", "em\\1", "em\\11", "em\\2", "em1", "em11", "em2", "em3"),
NULL,
S ("em*"));
_do_test_match_spec_device ("except:interface-name:em*",
S ("", "eth", "eth1", "e1"),
S (NULL),
S ("em", "em\\", "em\\*", "em\\1", "em\\11", "em\\2", "em1", "em11", "em2", "em3"));
_do_test_match_spec_device ("aa,bb,cc\\,dd,e,,",
S ("aa", "bb", "cc,dd", "e"),
NULL,
@ -1305,7 +1309,8 @@ _do_test_match_spec_config (const char *file, int line, const char *spec_str, gu
if (expected != match_result)
g_error ("%s:%d: faild comparing \"%s\" with %u.%u.%u. Expected %d, but got %d", file, line, spec_str, v_maj, v_min, v_mic, (int) expected, (int) match_result);
if (g_slist_length (specs) == 1 && match_result != NM_MATCH_SPEC_NEG_MATCH) {
if ( g_slist_length (specs) == 1
&& !g_str_has_prefix (specs->data, "except:")) {
/* there is only one spec in the list... test that we match except: */
char *sss = g_strdup_printf ("except:%s", (char *) specs->data);
GSList *specs2 = g_slist_append (NULL, sss);
@ -1313,7 +1318,7 @@ _do_test_match_spec_config (const char *file, int line, const char *spec_str, gu
match_result2 = nm_match_spec_config (specs2, version, NULL);
if (match_result == NM_MATCH_SPEC_NO_MATCH)
g_assert_cmpint (match_result2, ==, NM_MATCH_SPEC_NO_MATCH);
g_assert_cmpint (match_result2, ==, NM_MATCH_SPEC_MATCH);
else
g_assert_cmpint (match_result2, ==, NM_MATCH_SPEC_NEG_MATCH);
@ -1397,7 +1402,7 @@ test_match_spec_config (void)
do_test_match_spec_config ("nm-version-max:1", 1, 4, 30, NM_MATCH_SPEC_MATCH);
do_test_match_spec_config ("nm-version-max:1", 2, 4, 30, NM_MATCH_SPEC_NO_MATCH);
do_test_match_spec_config ("except:nm-version:1.4.8", 1, 6, 0, NM_MATCH_SPEC_NO_MATCH);
do_test_match_spec_config ("except:nm-version:1.4.8", 1, 6, 0, NM_MATCH_SPEC_MATCH);
do_test_match_spec_config ("nm-version-min:1.6,except:nm-version:1.4.8", 1, 6, 0, NM_MATCH_SPEC_MATCH);
do_test_match_spec_config ("nm-version-min:1.6,nm-version-min:1.4.6,nm-version-min:1.2.16,except:nm-version:1.4.8", 1, 2, 0, NM_MATCH_SPEC_NO_MATCH);