diff --git a/NEWS b/NEWS
index f6f49cb902..fe91391627 100644
--- a/NEWS
+++ b/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.
diff --git a/man/systemd.unit.xml b/man/systemd.unit.xml
index 7c1f7186e2..02d5e72aee 100644
--- a/man/systemd.unit.xml
+++ b/man/systemd.unit.xml
@@ -1244,15 +1244,17 @@
device-tree-compatible(value) for systems with a device tree that is compatible to
value.
- smbios-field(field operator value)
- for systems with a SMBIOS field containing a certain value.
- field is the name of the SMBIOS field exposed as sysfs attribute file
- below /sys/class/dmi/id/.
+ smbios-field(field operator
+ value) for systems with a SMBIOS field containing a certain
+ value. field is the name of the SMBIOS field exposed as
+ sysfs attribute file below /sys/class/dmi/id/.
operator is one of <, <=,
- >=, >, =, != for version
- comparison, or =$, !=$ for string comparison.
- value is the expected value of the SMBIOS field (shell-style globs are possible if
- =$ or!=$ is used).
+ >=, >, ==,
+ <> for version comparison, = and !=
+ for literal string comparison, or $=, !$= for shell-style
+ glob comparison. value is the expected value of the SMBIOS field value
+ (possibly containing shell style globs in case $=/!$= is
+ used).
@@ -1332,10 +1334,11 @@
ConditionKernelVersion= may be used to check whether the kernel
version (as reported by uname -r) 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 <,
- <=, =, !=, >=,
- > a relative version comparison is done, otherwise the specified string is
- matched with shell-style globs.
+ expressions. Each expression starts with one of = or != for
+ string comparisons, <, <=, ==,
+ <>, >=, > for a relative
+ version comparison, or $=, !$= for a shell-style glob
+ match. If no operator is specified $= is implied.
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 @@
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
- <, <=, =, !=,
- >=, >. 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.
+ <, <=, = (or ==),
+ != (or <>), >=,
+ >. 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.
@@ -1617,13 +1621,14 @@
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
- <, <=, =, !=,
- >=, >. 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.
+ <, <=, = (or ==),
+ != (or <>), >=,
+ >. 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.
@@ -1694,10 +1699,12 @@
Verify that a specific key=value pair is set in the host's
os-release5.
- Other than exact matching with =, and !=, relative
- comparisons are supported for versioned parameters (e.g. VERSION_ID). The
- comparator can be one of <, <=, =,
- !=, >= and >.
+ Other than exact string matching (with = and !=),
+ relative comparisons are supported for versioned parameters (e.g. VERSION_ID;
+ with <, <=, ==,
+ <>, >=, >), and shell-style
+ wildcard comparisons (*, ?, []) are
+ supported with the $= (match) and !$= (non-match).
diff --git a/src/analyze/analyze-compare-versions.c b/src/analyze/analyze-compare-versions.c
index 202b8a7d09..07b52a55f2 100644
--- a/src/analyze/analyze-compare-versions.c
+++ b/src/analyze/analyze-compare-versions.c
@@ -3,6 +3,7 @@
#include
#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;
}
}
diff --git a/src/shared/compare-operator.c b/src/shared/compare-operator.c
new file mode 100644
index 0000000000..ad931b778f
--- /dev/null
+++ b/src/shared/compare-operator.c
@@ -0,0 +1,119 @@
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+
+#include
+
+#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;
+ }
+}
diff --git a/src/shared/compare-operator.h b/src/shared/compare-operator.h
new file mode 100644
index 0000000000..900f3e54b2
--- /dev/null
+++ b/src/shared/compare-operator.h
@@ -0,0 +1,62 @@
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+#pragma once
+
+#include
+#include
+
+#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);
diff --git a/src/shared/condition.c b/src/shared/condition.c
index 00d732e6ef..ffca2006c0 100644
--- a/src/shared/condition.c
+++ b/src/shared/condition.c
@@ -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) {
diff --git a/src/shared/meson.build b/src/shared/meson.build
index 426a87b70c..abac8a7eb8 100644
--- a/src/shared/meson.build
+++ b/src/shared/meson.build
@@ -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',
diff --git a/src/test/test-condition.c b/src/test/test-condition.c
index c430110acd..10494b010c 100644
--- a/src/test/test-condition.c
+++ b/src/test/test-condition.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 */