1
1
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:
Lennart Poettering 2022-09-02 07:18:10 +02:00 committed by GitHub
commit 81e327c42c
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 298 additions and 181 deletions

17
NEWS
View File

@ -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.

View File

@ -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>&lt;</literal>, <literal>&lt;=</literal>,
<literal>&gt;=</literal>, <literal>&gt;</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>&gt;=</literal>, <literal>&gt;</literal>, <literal>==</literal>,
<literal>&lt;&gt;</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>&lt;</literal>,
<literal>&lt;=</literal>, <literal>=</literal>, <literal>!=</literal>, <literal>&gt;=</literal>,
<literal>&gt;</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>&lt;</literal>, <literal>&lt;=</literal>, <literal>==</literal>,
<literal>&lt;&gt;</literal>, <literal>&gt;=</literal>, <literal>&gt;</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>&lt;</literal>, <literal>&lt;=</literal>, <literal>=</literal>, <literal>!=</literal>,
<literal>&gt;=</literal>, <literal>&gt;</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>&lt;</literal>, <literal>&lt;=</literal>, <literal>=</literal> (or <literal>==</literal>),
<literal>!=</literal> (or <literal>&lt;&gt;</literal>), <literal>&gt;=</literal>,
<literal>&gt;</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>&lt;</literal>, <literal>&lt;=</literal>, <literal>=</literal>, <literal>!=</literal>,
<literal>&gt;=</literal>, <literal>&gt;</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>&lt;</literal>, <literal>&lt;=</literal>, <literal>=</literal> (or <literal>==</literal>),
<literal>!=</literal> (or <literal>&lt;&gt;</literal>), <literal>&gt;=</literal>,
<literal>&gt;</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>&lt;</literal>, <literal>&lt;=</literal>, <literal>=</literal>,
<literal>!=</literal>, <literal>&gt;=</literal> and <literal>&gt;</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>&lt;</literal>, <literal>&lt;=</literal>, <literal>==</literal>,
<literal>&lt;&gt;</literal>, <literal>&gt;=</literal>, <literal>&gt;</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>

View File

@ -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;
}
}

View 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;
}
}

View 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);

View File

@ -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(&parameter, &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) {

View File

@ -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',

View File

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