core: add nm_utils_connection_match_spec_list()

Add function nm_utils_connection_match_spec_list() to check whether a
connection matches a spec list. Also document the supported syntax in
the man page.
This commit is contained in:
Beniamino Galvani
2021-06-03 09:01:18 +02:00
parent 86f22ce8ba
commit 604c611cd0
3 changed files with 159 additions and 0 deletions

View File

@@ -1685,6 +1685,55 @@ interface-name:vboxnet*,except:interface-name:vboxnet2
</programlisting>
</para>
</refsect2>
<refsect2 id="connection-spec">
<title>Connection List Format</title>
<para>
Connections can be specified using the following format:
</para>
<para>
<variablelist>
<varlistentry>
<term>*</term>
<listitem><para>Matches every connection.</para></listitem>
</varlistentry>
<varlistentry>
<term>uuid:UUID</term>
<listitem><para>Match the connection by UUID, for example
<literal>"uuid:83037490-1d17-4986-a397-01f1db3a7fc2"</literal></para></listitem>
</varlistentry>
<varlistentry>
<term>id=ID</term>
<listitem><para>Match the connection by name.</para></listitem>
</varlistentry>
<varlistentry>
<term>origin:ORIGIN</term>
<listitem><para>Match the connection by origin, stored in the
<literal>org.freedesktop.NetworkManager.origin</literal> tag of the user setting. For example, use
<literal>"except:origin:nm-initrd-generator"</literal> to forbid activation of connections created by the
initrd generator.</para></listitem>
</varlistentry>
<varlistentry>
<term>except:SPEC</term>
<listitem><para>Negative match of a connection. A negative match has higher priority then the positive
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.</para></listitem>
</varlistentry>
<varlistentry>
<term>SPEC[,;]SPEC</term>
<listitem><para>Multiple specs can be concatenated with commas or semicolons. The order does not matter as
matches are either inclusive or negative (<literal>except:</literal>), with negative matches having higher
priority.</para>
<para>Backslash is supported to escape the separators ';' and ',', and to express special characters such as
newline ('\n'), tabulator ('\t'), whitespace ('\s') and backslash ('\\'). Whitespace is not a separator but
will be trimmed between two specs (unless escaped as '\s').</para></listitem>
</varlistentry>
</variablelist>
</para>
</refsect2>
</refsect1>
<refsect1>

View File

@@ -1484,6 +1484,112 @@ nm_match_spec_device(const GSList *specs,
return _match_result(has_except, has_not_except, has_match, has_match_except);
}
typedef struct {
const char *uuid;
const char *id;
const char *origin;
} MatchConnectionData;
static gboolean
match_connection_eval(const char *spec_str, const MatchConnectionData *match_data)
{
if (spec_str[0] == '*' && spec_str[1] == '\0')
return TRUE;
if (_MATCH_CHECK(spec_str, "id:"))
return nm_streq0(spec_str, match_data->id);
if (_MATCH_CHECK(spec_str, "uuid:"))
return nm_streq0(spec_str, match_data->uuid);
if (_MATCH_CHECK(spec_str, "origin:"))
return nm_streq0(spec_str, match_data->origin);
return FALSE;
}
static NMMatchSpecMatchType
match_spec_connection(const GSList *specs, const char *id, const char *uuid, const char *origin)
{
const GSList * iter;
gboolean has_match = FALSE;
gboolean has_match_except = FALSE;
gboolean has_except = FALSE;
gboolean has_not_except = FALSE;
const char * spec_str;
const MatchConnectionData match_data = {
.id = nm_str_not_empty(id),
.uuid = nm_str_not_empty(uuid),
.origin = nm_str_not_empty(origin),
};
if (!specs)
return NM_MATCH_SPEC_NO_MATCH;
for (iter = specs; iter; iter = iter->next) {
gboolean except;
spec_str = iter->data;
if (!spec_str || !*spec_str)
continue;
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_connection_eval(spec_str, &match_data))
continue;
if (except)
has_match_except = TRUE;
else
has_match = TRUE;
}
return _match_result(has_except, has_not_except, has_match, has_match_except);
}
int
nm_utils_connection_match_spec_list(NMConnection *connection,
const GSList *specs,
int no_match_value)
{
NMMatchSpecMatchType m;
NMSettingUser * s_user;
const char * origin = NULL;
if (!specs)
return no_match_value;
s_user = _nm_connection_get_setting(connection, NM_TYPE_SETTING_USER);
if (s_user)
origin = nm_setting_user_get_data(s_user, NM_USER_TAG_ORIGIN);
m = match_spec_connection(specs,
nm_connection_get_id(connection),
nm_connection_get_uuid(connection),
origin);
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;
}
static gboolean
match_config_eval(const char *str, const char *tag, guint cur_nm_version)
{

View File

@@ -211,6 +211,10 @@ gboolean nm_utils_kernel_cmdline_match_check(const char *const *proc_cmdline,
guint num_patterns,
GError ** error);
int nm_utils_connection_match_spec_list(NMConnection *connection,
const GSList *specs,
int no_match_value);
/*****************************************************************************/
gboolean nm_utils_connection_has_default_route(NMConnection *connection,