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 */