mirror of
https://github.com/systemd/systemd-stable.git
synced 2025-02-04 17:47:03 +03:00
Merge pull request #24491 from poettering/compare-order
condition: unify operator parsing handling
This commit is contained in:
commit
81e327c42c
17
NEWS
17
NEWS
@ -2,7 +2,7 @@ systemd System and Service Manager
|
||||
|
||||
CHANGES WITH 252 in spe:
|
||||
|
||||
Announcement of Future Feature Removal
|
||||
Announcement of Future Feature Removal:
|
||||
|
||||
* Please note that we intend to remove cgroupsv1 support from systemd
|
||||
release after EOY 2023. If you run services that make explicit use of
|
||||
@ -10,7 +10,20 @@ CHANGES WITH 252 in spe:
|
||||
sooner rather than later, if you haven't done so yet. Most of Linux
|
||||
userspace has been ported over already.
|
||||
|
||||
New features:
|
||||
Compatibility Breaks:
|
||||
|
||||
* ConditionKernelVersion= checks that use the = or != operator will now
|
||||
do simple string compares (as opposed to version compare – á la
|
||||
stverscmp() — as before, which is still done for the ordering
|
||||
operators <, >, <=, >=). Moreover, if no operator is specified a
|
||||
shell-style glob match is now done. This creates a minor
|
||||
incompatibility compared to older systemd versions, in case the *, ?,
|
||||
[, ], characters have been used in such condition expressions before,
|
||||
as these will now match per shell glob rules instead of
|
||||
literally. Given that kernel version strings typically do not include
|
||||
these characters we expect little breakage through this change.
|
||||
|
||||
New Features:
|
||||
|
||||
* systemd-measure is a new helper to precalculate PCR measurements
|
||||
to make it easier to set TPM2 policies.
|
||||
|
@ -1244,15 +1244,17 @@
|
||||
<para><literal>device-tree-compatible(<replaceable>value</replaceable>)</literal> for systems with a device tree that is compatible to
|
||||
<literal>value</literal>.</para>
|
||||
|
||||
<para><literal>smbios-field(<replaceable>field</replaceable> <replaceable>operator</replaceable> <replaceable>value</replaceable>)</literal>
|
||||
for systems with a SMBIOS field containing a certain value.
|
||||
<literal>field</literal> is the name of the SMBIOS field exposed as <literal>sysfs</literal> attribute file
|
||||
below <filename>/sys/class/dmi/id/</filename>.
|
||||
<para><literal>smbios-field(<replaceable>field</replaceable> <replaceable>operator</replaceable>
|
||||
<replaceable>value</replaceable>)</literal> for systems with a SMBIOS field containing a certain
|
||||
value. <literal>field</literal> is the name of the SMBIOS field exposed as
|
||||
<literal>sysfs</literal> attribute file below <filename>/sys/class/dmi/id/</filename>.
|
||||
<literal>operator</literal> is one of <literal><</literal>, <literal><=</literal>,
|
||||
<literal>>=</literal>, <literal>></literal>, <literal>=</literal>, <literal>!=</literal> for version
|
||||
comparison, or <literal>=$</literal>, <literal>!=$</literal> for string comparison.
|
||||
<literal>value</literal> is the expected value of the SMBIOS field (shell-style globs are possible if
|
||||
<literal>=$</literal> or<literal>!=$</literal> is used).</para>
|
||||
<literal>>=</literal>, <literal>></literal>, <literal>==</literal>,
|
||||
<literal><></literal> for version comparison, <literal>=</literal> and <literal>!=</literal>
|
||||
for literal string comparison, or <literal>$=</literal>, <literal>!$=</literal> for shell-style
|
||||
glob comparison. <literal>value</literal> is the expected value of the SMBIOS field value
|
||||
(possibly containing shell style globs in case <literal>$=</literal>/<literal>!$=</literal> is
|
||||
used).</para>
|
||||
</listitem>
|
||||
</varlistentry>
|
||||
|
||||
@ -1332,10 +1334,11 @@
|
||||
<listitem><para><varname>ConditionKernelVersion=</varname> may be used to check whether the kernel
|
||||
version (as reported by <command>uname -r</command>) matches a certain expression (or if prefixed
|
||||
with the exclamation mark does not match it). The argument must be a list of (potentially quoted)
|
||||
expressions. For each of the expressions, if it starts with one of <literal><</literal>,
|
||||
<literal><=</literal>, <literal>=</literal>, <literal>!=</literal>, <literal>>=</literal>,
|
||||
<literal>></literal> a relative version comparison is done, otherwise the specified string is
|
||||
matched with shell-style globs.</para>
|
||||
expressions. Each expression starts with one of <literal>=</literal> or <literal>!=</literal> for
|
||||
string comparisons, <literal><</literal>, <literal><=</literal>, <literal>==</literal>,
|
||||
<literal><></literal>, <literal>>=</literal>, <literal>></literal> for a relative
|
||||
version comparison, or <literal>$=</literal>, <literal>!$=</literal> for a shell-style glob
|
||||
match. If no operator is specified <literal>$=</literal> is implied.</para>
|
||||
|
||||
<para>Note that using the kernel version string is an unreliable way to determine which features
|
||||
are supported by a kernel, because of the widespread practice of backporting drivers, features, and
|
||||
@ -1605,10 +1608,11 @@
|
||||
|
||||
<listitem><para>Verify that the specified amount of system memory is available to the current
|
||||
system. Takes a memory size in bytes as argument, optionally prefixed with a comparison operator
|
||||
<literal><</literal>, <literal><=</literal>, <literal>=</literal>, <literal>!=</literal>,
|
||||
<literal>>=</literal>, <literal>></literal>. On bare-metal systems compares the amount of
|
||||
physical memory in the system with the specified size, adhering to the specified comparison
|
||||
operator. In containers compares the amount of memory assigned to the container instead.</para>
|
||||
<literal><</literal>, <literal><=</literal>, <literal>=</literal> (or <literal>==</literal>),
|
||||
<literal>!=</literal> (or <literal><></literal>), <literal>>=</literal>,
|
||||
<literal>></literal>. On bare-metal systems compares the amount of physical memory in the system
|
||||
with the specified size, adhering to the specified comparison operator. In containers compares the
|
||||
amount of memory assigned to the container instead.</para>
|
||||
</listitem>
|
||||
</varlistentry>
|
||||
|
||||
@ -1617,13 +1621,14 @@
|
||||
|
||||
<listitem><para>Verify that the specified number of CPUs is available to the current system. Takes
|
||||
a number of CPUs as argument, optionally prefixed with a comparison operator
|
||||
<literal><</literal>, <literal><=</literal>, <literal>=</literal>, <literal>!=</literal>,
|
||||
<literal>>=</literal>, <literal>></literal>. Compares the number of CPUs in the CPU affinity
|
||||
mask configured of the service manager itself with the specified number, adhering to the specified
|
||||
comparison operator. On physical systems the number of CPUs in the affinity mask of the service
|
||||
manager usually matches the number of physical CPUs, but in special and virtual environments might
|
||||
differ. In particular, in containers the affinity mask usually matches the number of CPUs assigned
|
||||
to the container and not the physically available ones.</para></listitem>
|
||||
<literal><</literal>, <literal><=</literal>, <literal>=</literal> (or <literal>==</literal>),
|
||||
<literal>!=</literal> (or <literal><></literal>), <literal>>=</literal>,
|
||||
<literal>></literal>. Compares the number of CPUs in the CPU affinity mask configured of the
|
||||
service manager itself with the specified number, adhering to the specified comparison operator. On
|
||||
physical systems the number of CPUs in the affinity mask of the service manager usually matches the
|
||||
number of physical CPUs, but in special and virtual environments might differ. In particular, in
|
||||
containers the affinity mask usually matches the number of CPUs assigned to the container and not
|
||||
the physically available ones.</para></listitem>
|
||||
</varlistentry>
|
||||
|
||||
<varlistentry>
|
||||
@ -1694,10 +1699,12 @@
|
||||
<listitem><para>Verify that a specific <literal>key=value</literal> pair is set in the host's
|
||||
<citerefentry><refentrytitle>os-release</refentrytitle><manvolnum>5</manvolnum></citerefentry>.</para>
|
||||
|
||||
<para>Other than exact matching with <literal>=</literal>, and <literal>!=</literal>, relative
|
||||
comparisons are supported for versioned parameters (e.g. <literal>VERSION_ID</literal>). The
|
||||
comparator can be one of <literal><</literal>, <literal><=</literal>, <literal>=</literal>,
|
||||
<literal>!=</literal>, <literal>>=</literal> and <literal>></literal>.</para>
|
||||
<para>Other than exact string matching (with <literal>=</literal> and <literal>!=</literal>),
|
||||
relative comparisons are supported for versioned parameters (e.g. <literal>VERSION_ID</literal>;
|
||||
with <literal><</literal>, <literal><=</literal>, <literal>==</literal>,
|
||||
<literal><></literal>, <literal>>=</literal>, <literal>></literal>), and shell-style
|
||||
wildcard comparisons (<literal>*</literal>, <literal>?</literal>, <literal>[]</literal>) are
|
||||
supported with the <literal>$=</literal> (match) and <literal>!$=</literal> (non-match).</para>
|
||||
</listitem>
|
||||
</varlistentry>
|
||||
|
||||
|
@ -3,6 +3,7 @@
|
||||
#include <stdio.h>
|
||||
|
||||
#include "analyze-compare-versions.h"
|
||||
#include "compare-operator.h"
|
||||
#include "macro.h"
|
||||
#include "string-util.h"
|
||||
#include "strv.h"
|
||||
@ -26,22 +27,16 @@ int verb_compare_versions(int argc, char *argv[], void *userdata) {
|
||||
|
||||
} else {
|
||||
const char *op = ASSERT_PTR(argv[2]);
|
||||
CompareOperator operator;
|
||||
|
||||
r = strverscmp_improved(ASSERT_PTR(argv[1]), ASSERT_PTR(argv[3]));
|
||||
operator = parse_compare_operator(&op, COMPARE_ALLOW_TEXTUAL);
|
||||
if (operator < 0 || !isempty(op))
|
||||
return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Unknown operator \"%s\".", op);
|
||||
|
||||
if (STR_IN_SET(op, "lt", "<"))
|
||||
return r < 0 ? EXIT_SUCCESS : EXIT_FAILURE;
|
||||
if (STR_IN_SET(op, "le", "<="))
|
||||
return r <= 0 ? EXIT_SUCCESS : EXIT_FAILURE;
|
||||
if (STR_IN_SET(op, "eq", "=="))
|
||||
return r == 0 ? EXIT_SUCCESS : EXIT_FAILURE;
|
||||
if (STR_IN_SET(op, "ne", "!="))
|
||||
return r != 0 ? EXIT_SUCCESS : EXIT_FAILURE;
|
||||
if (STR_IN_SET(op, "ge", ">="))
|
||||
return r >= 0 ? EXIT_SUCCESS : EXIT_FAILURE;
|
||||
if (STR_IN_SET(op, "gt", ">"))
|
||||
return r > 0 ? EXIT_SUCCESS : EXIT_FAILURE;
|
||||
return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
|
||||
"Unknown operator \"%s\".", op);
|
||||
r = version_or_fnmatch_compare(operator, ASSERT_PTR(argv[1]), ASSERT_PTR(argv[3]));
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to compare versions: %m");
|
||||
|
||||
return r ? EXIT_SUCCESS : EXIT_FAILURE;
|
||||
}
|
||||
}
|
||||
|
119
src/shared/compare-operator.c
Normal file
119
src/shared/compare-operator.c
Normal file
@ -0,0 +1,119 @@
|
||||
/* SPDX-License-Identifier: LGPL-2.1-or-later */
|
||||
|
||||
#include <fnmatch.h>
|
||||
|
||||
#include "compare-operator.h"
|
||||
#include "string-util.h"
|
||||
|
||||
CompareOperator parse_compare_operator(const char **s, CompareOperatorParseFlags flags) {
|
||||
static const struct {
|
||||
CompareOperator op;
|
||||
const char *str;
|
||||
CompareOperatorParseFlags valid_mask; /* If this operator appears when flags in mask not set, fail */
|
||||
CompareOperatorParseFlags need_mask; /* Skip over this operattor when flags in mask not set */
|
||||
} table[] = {
|
||||
{ COMPARE_FNMATCH_EQUAL, "$=", .valid_mask = COMPARE_ALLOW_FNMATCH },
|
||||
{ COMPARE_FNMATCH_UNEQUAL, "!$=", .valid_mask = COMPARE_ALLOW_FNMATCH },
|
||||
|
||||
{ COMPARE_UNEQUAL, "<>" },
|
||||
{ COMPARE_LOWER_OR_EQUAL, "<=" },
|
||||
{ COMPARE_GREATER_OR_EQUAL, ">=" },
|
||||
{ COMPARE_LOWER, "<" },
|
||||
{ COMPARE_GREATER, ">" },
|
||||
{ COMPARE_EQUAL, "==" },
|
||||
{ COMPARE_STRING_EQUAL, "=", .need_mask = COMPARE_EQUAL_BY_STRING },
|
||||
{ COMPARE_EQUAL, "=" },
|
||||
{ COMPARE_STRING_UNEQUAL, "!=", .need_mask = COMPARE_EQUAL_BY_STRING },
|
||||
{ COMPARE_UNEQUAL, "!=" },
|
||||
|
||||
{ COMPARE_LOWER, "lt", .valid_mask = COMPARE_ALLOW_TEXTUAL },
|
||||
{ COMPARE_LOWER_OR_EQUAL, "le", .valid_mask = COMPARE_ALLOW_TEXTUAL },
|
||||
{ COMPARE_EQUAL, "eq", .valid_mask = COMPARE_ALLOW_TEXTUAL },
|
||||
{ COMPARE_UNEQUAL, "ne", .valid_mask = COMPARE_ALLOW_TEXTUAL },
|
||||
{ COMPARE_GREATER_OR_EQUAL, "ge", .valid_mask = COMPARE_ALLOW_TEXTUAL },
|
||||
{ COMPARE_GREATER, "gt", .valid_mask = COMPARE_ALLOW_TEXTUAL },
|
||||
};
|
||||
|
||||
assert(s);
|
||||
|
||||
if (!*s) /* Hmm, we already reached the end, for example because extract_first_word() and
|
||||
* parse_compare_operator() are use on the same string? */
|
||||
return _COMPARE_OPERATOR_INVALID;
|
||||
|
||||
for (size_t i = 0; i < ELEMENTSOF(table); i ++) {
|
||||
const char *e;
|
||||
|
||||
if (table[i].need_mask != 0 && !FLAGS_SET(flags, table[i].need_mask))
|
||||
continue;
|
||||
|
||||
e = startswith(*s, table[i].str);
|
||||
if (e) {
|
||||
if (table[i].valid_mask != 0 && !FLAGS_SET(flags, table[i].valid_mask))
|
||||
return _COMPARE_OPERATOR_INVALID;
|
||||
|
||||
*s = e;
|
||||
return table[i].op;
|
||||
}
|
||||
}
|
||||
|
||||
return _COMPARE_OPERATOR_INVALID;
|
||||
}
|
||||
|
||||
int test_order(int k, CompareOperator op) {
|
||||
|
||||
switch (op) {
|
||||
|
||||
case COMPARE_LOWER:
|
||||
return k < 0;
|
||||
|
||||
case COMPARE_LOWER_OR_EQUAL:
|
||||
return k <= 0;
|
||||
|
||||
case COMPARE_EQUAL:
|
||||
return k == 0;
|
||||
|
||||
case COMPARE_UNEQUAL:
|
||||
return k != 0;
|
||||
|
||||
case COMPARE_GREATER_OR_EQUAL:
|
||||
return k >= 0;
|
||||
|
||||
case COMPARE_GREATER:
|
||||
return k > 0;
|
||||
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
}
|
||||
|
||||
int version_or_fnmatch_compare(
|
||||
CompareOperator op,
|
||||
const char *a,
|
||||
const char *b) {
|
||||
int r;
|
||||
|
||||
switch (op) {
|
||||
|
||||
case COMPARE_STRING_EQUAL:
|
||||
return streq_ptr(a, b);
|
||||
|
||||
case COMPARE_STRING_UNEQUAL:
|
||||
return !streq_ptr(a, b);
|
||||
|
||||
case COMPARE_FNMATCH_EQUAL:
|
||||
r = fnmatch(b, a, 0);
|
||||
return r == 0 ? true :
|
||||
r == FNM_NOMATCH ? false : -EINVAL;
|
||||
|
||||
case COMPARE_FNMATCH_UNEQUAL:
|
||||
r = fnmatch(b, a, 0);
|
||||
return r == FNM_NOMATCH ? true:
|
||||
r == 0 ? false : -EINVAL;
|
||||
|
||||
case _COMPARE_OPERATOR_ORDER_FIRST..._COMPARE_OPERATOR_ORDER_LAST:
|
||||
return test_order(strverscmp_improved(a, b), op);
|
||||
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
}
|
62
src/shared/compare-operator.h
Normal file
62
src/shared/compare-operator.h
Normal file
@ -0,0 +1,62 @@
|
||||
/* SPDX-License-Identifier: LGPL-2.1-or-later */
|
||||
#pragma once
|
||||
|
||||
#include <errno.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
#define COMPARE_OPERATOR_CHARS "!<=>"
|
||||
#define COMPARE_OPERATOR_WITH_FNMATCH_CHARS COMPARE_OPERATOR_CHARS "$"
|
||||
|
||||
typedef enum CompareOperator {
|
||||
/* Listed in order of checking. Note that some comparators are prefixes of others, hence the longest
|
||||
* should be listed first. */
|
||||
|
||||
/* Simple string compare operators */
|
||||
_COMPARE_OPERATOR_STRING_FIRST,
|
||||
COMPARE_STRING_EQUAL = _COMPARE_OPERATOR_STRING_FIRST,
|
||||
COMPARE_STRING_UNEQUAL,
|
||||
_COMPARE_OPERATOR_STRING_LAST = COMPARE_STRING_UNEQUAL,
|
||||
|
||||
/* fnmatch() compare operators */
|
||||
_COMPARE_OPERATOR_FNMATCH_FIRST,
|
||||
COMPARE_FNMATCH_EQUAL = _COMPARE_OPERATOR_FNMATCH_FIRST,
|
||||
COMPARE_FNMATCH_UNEQUAL,
|
||||
_COMPARE_OPERATOR_FNMATCH_LAST = COMPARE_FNMATCH_UNEQUAL,
|
||||
|
||||
/* Order compare operators */
|
||||
_COMPARE_OPERATOR_ORDER_FIRST,
|
||||
COMPARE_LOWER_OR_EQUAL = _COMPARE_OPERATOR_ORDER_FIRST,
|
||||
COMPARE_GREATER_OR_EQUAL,
|
||||
COMPARE_LOWER,
|
||||
COMPARE_GREATER,
|
||||
COMPARE_EQUAL,
|
||||
COMPARE_UNEQUAL,
|
||||
_COMPARE_OPERATOR_ORDER_LAST = COMPARE_UNEQUAL,
|
||||
|
||||
_COMPARE_OPERATOR_MAX,
|
||||
_COMPARE_OPERATOR_INVALID = -EINVAL,
|
||||
} CompareOperator;
|
||||
|
||||
static inline bool COMPARE_OPERATOR_IS_STRING(CompareOperator c) {
|
||||
return c >= _COMPARE_OPERATOR_STRING_FIRST && c <= _COMPARE_OPERATOR_STRING_LAST;
|
||||
}
|
||||
|
||||
static inline bool COMPARE_OPERATOR_IS_FNMATCH(CompareOperator c) {
|
||||
return c >= _COMPARE_OPERATOR_FNMATCH_FIRST && c <= _COMPARE_OPERATOR_FNMATCH_LAST;
|
||||
}
|
||||
|
||||
static inline bool COMPARE_OPERATOR_IS_ORDER(CompareOperator c) {
|
||||
return c >= _COMPARE_OPERATOR_ORDER_FIRST && c <= _COMPARE_OPERATOR_ORDER_LAST;
|
||||
}
|
||||
|
||||
typedef enum CompareOperatorParseFlags {
|
||||
COMPARE_ALLOW_FNMATCH = 1 << 0,
|
||||
COMPARE_EQUAL_BY_STRING = 1 << 1,
|
||||
COMPARE_ALLOW_TEXTUAL = 1 << 2,
|
||||
} CompareOperatorParseFlags;
|
||||
|
||||
CompareOperator parse_compare_operator(const char **s, CompareOperatorParseFlags flags);
|
||||
|
||||
int test_order(int k, CompareOperator op);
|
||||
|
||||
int version_or_fnmatch_compare(CompareOperator op, const char *a, const char *b);
|
@ -21,6 +21,7 @@
|
||||
#include "blockdev-util.h"
|
||||
#include "cap-list.h"
|
||||
#include "cgroup-util.h"
|
||||
#include "compare-operator.h"
|
||||
#include "condition.h"
|
||||
#include "cpu-set-util.h"
|
||||
#include "creds-util.h"
|
||||
@ -182,80 +183,8 @@ static int condition_test_credential(Condition *c, char **env) {
|
||||
return false;
|
||||
}
|
||||
|
||||
typedef enum {
|
||||
/* Listed in order of checking. Note that some comparators are prefixes of others, hence the longest
|
||||
* should be listed first. */
|
||||
_ORDER_FNMATCH_FIRST,
|
||||
ORDER_FNMATCH_EQUAL = _ORDER_FNMATCH_FIRST,
|
||||
ORDER_FNMATCH_UNEQUAL,
|
||||
_ORDER_FNMATCH_LAST = ORDER_FNMATCH_UNEQUAL,
|
||||
ORDER_LOWER_OR_EQUAL,
|
||||
ORDER_GREATER_OR_EQUAL,
|
||||
ORDER_LOWER,
|
||||
ORDER_GREATER,
|
||||
ORDER_EQUAL,
|
||||
ORDER_UNEQUAL,
|
||||
_ORDER_MAX,
|
||||
_ORDER_INVALID = -EINVAL,
|
||||
} OrderOperator;
|
||||
|
||||
static OrderOperator parse_order(const char **s, bool allow_fnmatch) {
|
||||
static const char *const prefix[_ORDER_MAX] = {
|
||||
[ORDER_FNMATCH_EQUAL] = "=$",
|
||||
[ORDER_FNMATCH_UNEQUAL] = "!=$",
|
||||
[ORDER_LOWER_OR_EQUAL] = "<=",
|
||||
[ORDER_GREATER_OR_EQUAL] = ">=",
|
||||
[ORDER_LOWER] = "<",
|
||||
[ORDER_GREATER] = ">",
|
||||
[ORDER_EQUAL] = "=",
|
||||
[ORDER_UNEQUAL] = "!=",
|
||||
};
|
||||
|
||||
for (OrderOperator i = 0; i < _ORDER_MAX; i++) {
|
||||
const char *e;
|
||||
|
||||
e = startswith(*s, prefix[i]);
|
||||
if (e) {
|
||||
if (!allow_fnmatch && (i >= _ORDER_FNMATCH_FIRST && i <= _ORDER_FNMATCH_LAST))
|
||||
break;
|
||||
*s = e;
|
||||
return i;
|
||||
}
|
||||
}
|
||||
|
||||
return _ORDER_INVALID;
|
||||
}
|
||||
|
||||
static bool test_order(int k, OrderOperator p) {
|
||||
|
||||
switch (p) {
|
||||
|
||||
case ORDER_LOWER:
|
||||
return k < 0;
|
||||
|
||||
case ORDER_LOWER_OR_EQUAL:
|
||||
return k <= 0;
|
||||
|
||||
case ORDER_EQUAL:
|
||||
return k == 0;
|
||||
|
||||
case ORDER_UNEQUAL:
|
||||
return k != 0;
|
||||
|
||||
case ORDER_GREATER_OR_EQUAL:
|
||||
return k >= 0;
|
||||
|
||||
case ORDER_GREATER:
|
||||
return k > 0;
|
||||
|
||||
default:
|
||||
assert_not_reached();
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
static int condition_test_kernel_version(Condition *c, char **env) {
|
||||
OrderOperator order;
|
||||
CompareOperator operator;
|
||||
struct utsname u;
|
||||
bool first = true;
|
||||
|
||||
@ -277,30 +206,30 @@ static int condition_test_kernel_version(Condition *c, char **env) {
|
||||
break;
|
||||
|
||||
s = strstrip(word);
|
||||
order = parse_order(&s, /* allow_fnmatch= */ false);
|
||||
if (order >= 0) {
|
||||
s += strspn(s, WHITESPACE);
|
||||
if (isempty(s)) {
|
||||
if (first) {
|
||||
/* For backwards compatibility, allow whitespace between the operator and
|
||||
* value, without quoting, but only in the first expression. */
|
||||
word = mfree(word);
|
||||
r = extract_first_word(&p, &word, NULL, 0);
|
||||
if (r < 0)
|
||||
return log_debug_errno(r, "Failed to parse condition string \"%s\": %m", p);
|
||||
if (r == 0)
|
||||
return log_debug_errno(SYNTHETIC_ERRNO(EINVAL), "Unexpected end of expression: %s", p);
|
||||
s = word;
|
||||
} else
|
||||
operator = parse_compare_operator(&s, COMPARE_ALLOW_FNMATCH|COMPARE_EQUAL_BY_STRING);
|
||||
if (operator < 0) /* No prefix? Then treat as glob string */
|
||||
operator = COMPARE_FNMATCH_EQUAL;
|
||||
|
||||
s += strspn(s, WHITESPACE);
|
||||
if (isempty(s)) {
|
||||
if (first) {
|
||||
/* For backwards compatibility, allow whitespace between the operator and
|
||||
* value, without quoting, but only in the first expression. */
|
||||
word = mfree(word);
|
||||
r = extract_first_word(&p, &word, NULL, 0);
|
||||
if (r < 0)
|
||||
return log_debug_errno(r, "Failed to parse condition string \"%s\": %m", p);
|
||||
if (r == 0)
|
||||
return log_debug_errno(SYNTHETIC_ERRNO(EINVAL), "Unexpected end of expression: %s", p);
|
||||
}
|
||||
s = word;
|
||||
} else
|
||||
return log_debug_errno(SYNTHETIC_ERRNO(EINVAL), "Unexpected end of expression: %s", p);
|
||||
}
|
||||
|
||||
r = test_order(strverscmp_improved(u.release, s), order);
|
||||
} else
|
||||
/* No prefix? Then treat as glob string */
|
||||
r = fnmatch(s, u.release, 0) == 0;
|
||||
|
||||
if (r == 0)
|
||||
r = version_or_fnmatch_compare(operator, u.release, s);
|
||||
if (r < 0)
|
||||
return r;
|
||||
if (!r)
|
||||
return false;
|
||||
|
||||
first = false;
|
||||
@ -317,9 +246,8 @@ static int condition_test_osrelease(Condition *c, char **env) {
|
||||
|
||||
for (const char *parameter = ASSERT_PTR(c->parameter);;) {
|
||||
_cleanup_free_ char *key = NULL, *condition = NULL, *actual_value = NULL;
|
||||
OrderOperator order;
|
||||
CompareOperator operator;
|
||||
const char *word;
|
||||
bool matches;
|
||||
|
||||
r = extract_first_word(¶meter, &condition, NULL, EXTRACT_UNQUOTE);
|
||||
if (r < 0)
|
||||
@ -327,9 +255,9 @@ static int condition_test_osrelease(Condition *c, char **env) {
|
||||
if (r == 0)
|
||||
break;
|
||||
|
||||
/* parse_order() needs the string to start with the comparators */
|
||||
/* parse_compare_operator() needs the string to start with the comparators */
|
||||
word = condition;
|
||||
r = extract_first_word(&word, &key, "!<=>", EXTRACT_RETAIN_SEPARATORS);
|
||||
r = extract_first_word(&word, &key, COMPARE_OPERATOR_WITH_FNMATCH_CHARS, EXTRACT_RETAIN_SEPARATORS);
|
||||
if (r < 0)
|
||||
return log_debug_errno(r, "Failed to parse parameter: %m");
|
||||
/* The os-release spec mandates env-var-like key names */
|
||||
@ -338,8 +266,8 @@ static int condition_test_osrelease(Condition *c, char **env) {
|
||||
"Failed to parse parameter, key/value format expected: %m");
|
||||
|
||||
/* Do not allow whitespace after the separator, as that's not a valid os-release format */
|
||||
order = parse_order(&word, /* allow_fnmatch= */ false);
|
||||
if (order < 0 || isempty(word) || strchr(WHITESPACE, *word) != NULL)
|
||||
operator = parse_compare_operator(&word, COMPARE_ALLOW_FNMATCH|COMPARE_EQUAL_BY_STRING);
|
||||
if (operator < 0 || isempty(word) || strchr(WHITESPACE, *word) != NULL)
|
||||
return log_debug_errno(SYNTHETIC_ERRNO(EINVAL),
|
||||
"Failed to parse parameter, key/value format expected: %m");
|
||||
|
||||
@ -347,15 +275,10 @@ static int condition_test_osrelease(Condition *c, char **env) {
|
||||
if (r < 0)
|
||||
return log_debug_errno(r, "Failed to parse os-release: %m");
|
||||
|
||||
/* Might not be comparing versions, so do exact string matching */
|
||||
if (order == ORDER_EQUAL)
|
||||
matches = streq_ptr(actual_value, word);
|
||||
else if (order == ORDER_UNEQUAL)
|
||||
matches = !streq_ptr(actual_value, word);
|
||||
else
|
||||
matches = test_order(strverscmp_improved(actual_value, word), order);
|
||||
|
||||
if (!matches)
|
||||
r = version_or_fnmatch_compare(operator, actual_value, word);
|
||||
if (r < 0)
|
||||
return r;
|
||||
if (!r)
|
||||
return false;
|
||||
}
|
||||
|
||||
@ -363,7 +286,7 @@ static int condition_test_osrelease(Condition *c, char **env) {
|
||||
}
|
||||
|
||||
static int condition_test_memory(Condition *c, char **env) {
|
||||
OrderOperator order;
|
||||
CompareOperator operator;
|
||||
uint64_t m, k;
|
||||
const char *p;
|
||||
int r;
|
||||
@ -375,19 +298,19 @@ static int condition_test_memory(Condition *c, char **env) {
|
||||
m = physical_memory();
|
||||
|
||||
p = c->parameter;
|
||||
order = parse_order(&p, /* allow_fnmatch= */ false);
|
||||
if (order < 0)
|
||||
order = ORDER_GREATER_OR_EQUAL; /* default to >= check, if nothing is specified. */
|
||||
operator = parse_compare_operator(&p, 0);
|
||||
if (operator < 0)
|
||||
operator = COMPARE_GREATER_OR_EQUAL; /* default to >= check, if nothing is specified. */
|
||||
|
||||
r = parse_size(p, 1024, &k);
|
||||
if (r < 0)
|
||||
return log_debug_errno(r, "Failed to parse size '%s': %m", p);
|
||||
|
||||
return test_order(CMP(m, k), order);
|
||||
return test_order(CMP(m, k), operator);
|
||||
}
|
||||
|
||||
static int condition_test_cpus(Condition *c, char **env) {
|
||||
OrderOperator order;
|
||||
CompareOperator operator;
|
||||
const char *p;
|
||||
unsigned k;
|
||||
int r, n;
|
||||
@ -401,15 +324,15 @@ static int condition_test_cpus(Condition *c, char **env) {
|
||||
return log_debug_errno(n, "Failed to determine CPUs in affinity mask: %m");
|
||||
|
||||
p = c->parameter;
|
||||
order = parse_order(&p, /* allow_fnmatch= */ false);
|
||||
if (order < 0)
|
||||
order = ORDER_GREATER_OR_EQUAL; /* default to >= check, if nothing is specified. */
|
||||
operator = parse_compare_operator(&p, 0);
|
||||
if (operator < 0)
|
||||
operator = COMPARE_GREATER_OR_EQUAL; /* default to >= check, if nothing is specified. */
|
||||
|
||||
r = safe_atou(p, &k);
|
||||
if (r < 0)
|
||||
return log_debug_errno(r, "Failed to parse number of CPUs: %m");
|
||||
|
||||
return test_order(CMP((unsigned) n, k), order);
|
||||
return test_order(CMP((unsigned) n, k), operator);
|
||||
}
|
||||
|
||||
static int condition_test_user(Condition *c, char **env) {
|
||||
@ -589,13 +512,13 @@ static int condition_test_firmware_devicetree_compatible(const char *dtcarg) {
|
||||
|
||||
static int condition_test_firmware_smbios_field(const char *expression) {
|
||||
_cleanup_free_ char *field = NULL, *expected_value = NULL, *actual_value = NULL;
|
||||
OrderOperator operator;
|
||||
CompareOperator operator;
|
||||
int r;
|
||||
|
||||
assert(expression);
|
||||
|
||||
/* Parse SMBIOS field */
|
||||
r = extract_first_word(&expression, &field, "!<=>$", EXTRACT_RETAIN_SEPARATORS);
|
||||
r = extract_first_word(&expression, &field, COMPARE_OPERATOR_WITH_FNMATCH_CHARS, EXTRACT_RETAIN_SEPARATORS);
|
||||
if (r < 0)
|
||||
return r;
|
||||
if (r == 0 || isempty(expression))
|
||||
@ -605,7 +528,7 @@ static int condition_test_firmware_smbios_field(const char *expression) {
|
||||
delete_trailing_chars(field, WHITESPACE);
|
||||
|
||||
/* Parse operator */
|
||||
operator = parse_order(&expression, /* allow_fnmatch= */ true);
|
||||
operator = parse_compare_operator(&expression, COMPARE_ALLOW_FNMATCH|COMPARE_EQUAL_BY_STRING);
|
||||
if (operator < 0)
|
||||
return operator;
|
||||
|
||||
@ -633,11 +556,7 @@ static int condition_test_firmware_smbios_field(const char *expression) {
|
||||
delete_trailing_chars(actual_value, WHITESPACE);
|
||||
|
||||
/* Finally compare actual and expected value */
|
||||
if (operator == ORDER_FNMATCH_EQUAL)
|
||||
return fnmatch(expected_value, actual_value, FNM_EXTMATCH) != FNM_NOMATCH;
|
||||
if (operator == ORDER_FNMATCH_UNEQUAL)
|
||||
return fnmatch(expected_value, actual_value, FNM_EXTMATCH) == FNM_NOMATCH;
|
||||
return test_order(strverscmp_improved(actual_value, expected_value), operator);
|
||||
return version_or_fnmatch_compare(operator, actual_value, expected_value);
|
||||
}
|
||||
|
||||
static int condition_test_firmware(Condition *c, char **env) {
|
||||
|
@ -71,6 +71,8 @@ shared_sources = files(
|
||||
'clean-ipc.h',
|
||||
'clock-util.c',
|
||||
'clock-util.h',
|
||||
'compare-operator.c',
|
||||
'compare-operator.h',
|
||||
'condition.c',
|
||||
'condition.h',
|
||||
'conf-parser.c',
|
||||
|
@ -359,31 +359,31 @@ TEST(condition_test_firmware_smbios_field) {
|
||||
const char *quote = strchr(bios_vendor, ' ') ? "\"" : "";
|
||||
|
||||
/* Test equality / inequality using fnmatch() */
|
||||
expression = strjoina("smbios-field(bios_vendor =$ ", quote, bios_vendor, quote, ")");
|
||||
expression = strjoina("smbios-field(bios_vendor $= ", quote, bios_vendor, quote, ")");
|
||||
condition = condition_new(CONDITION_FIRMWARE, expression, false, false);
|
||||
assert_se(condition);
|
||||
assert_se(condition_test(condition, environ) > 0);
|
||||
condition_free(condition);
|
||||
|
||||
expression = strjoina("smbios-field(bios_vendor=$", quote, bios_vendor, quote, ")");
|
||||
expression = strjoina("smbios-field(bios_vendor$=", quote, bios_vendor, quote, ")");
|
||||
condition = condition_new(CONDITION_FIRMWARE, expression, false, false);
|
||||
assert_se(condition);
|
||||
assert_se(condition_test(condition, environ) > 0);
|
||||
condition_free(condition);
|
||||
|
||||
expression = strjoina("smbios-field(bios_vendor !=$ ", quote, bios_vendor, quote, ")");
|
||||
expression = strjoina("smbios-field(bios_vendor !$= ", quote, bios_vendor, quote, ")");
|
||||
condition = condition_new(CONDITION_FIRMWARE, expression, false, false);
|
||||
assert_se(condition);
|
||||
assert_se(condition_test(condition, environ) == 0);
|
||||
condition_free(condition);
|
||||
|
||||
expression = strjoina("smbios-field(bios_vendor!=$", quote, bios_vendor, quote, ")");
|
||||
expression = strjoina("smbios-field(bios_vendor!$=", quote, bios_vendor, quote, ")");
|
||||
condition = condition_new(CONDITION_FIRMWARE, expression, false, false);
|
||||
assert_se(condition);
|
||||
assert_se(condition_test(condition, environ) == 0);
|
||||
condition_free(condition);
|
||||
|
||||
expression = strjoina("smbios-field(bios_vendor =$ ", quote, bios_vendor, "*", quote, ")");
|
||||
expression = strjoina("smbios-field(bios_vendor $= ", quote, bios_vendor, "*", quote, ")");
|
||||
condition = condition_new(CONDITION_FIRMWARE, expression, false, false);
|
||||
assert_se(condition);
|
||||
assert_se(condition_test(condition, environ) > 0);
|
||||
@ -1174,16 +1174,16 @@ TEST(condition_test_os_release) {
|
||||
condition_free(condition);
|
||||
|
||||
/* Test fnmatch() operators */
|
||||
key_value_pair = strjoina(os_release_pairs[0], "=$", quote, os_release_pairs[1], quote);
|
||||
key_value_pair = strjoina(os_release_pairs[0], "$=", quote, os_release_pairs[1], quote);
|
||||
condition = condition_new(CONDITION_OS_RELEASE, key_value_pair, false, false);
|
||||
assert_se(condition);
|
||||
assert_se(condition_test(condition, environ) == -EINVAL);
|
||||
assert_se(condition_test(condition, environ) > 0);
|
||||
condition_free(condition);
|
||||
|
||||
key_value_pair = strjoina(os_release_pairs[0], "!=$", quote, os_release_pairs[1], quote);
|
||||
key_value_pair = strjoina(os_release_pairs[0], "!$=", quote, os_release_pairs[1], quote);
|
||||
condition = condition_new(CONDITION_OS_RELEASE, key_value_pair, false, false);
|
||||
assert_se(condition);
|
||||
assert_se(condition_test(condition, environ) == -EINVAL);
|
||||
assert_se(condition_test(condition, environ) == 0);
|
||||
condition_free(condition);
|
||||
|
||||
/* Some distros (eg: Arch) do not set VERSION_ID */
|
||||
|
Loading…
x
Reference in New Issue
Block a user