summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorZbigniew Jędrzejewski-Szmek <zbyszek@in.waw.pl>2019-06-28 10:58:06 +0200
committerZbigniew Jędrzejewski-Szmek <zbyszek@in.waw.pl>2019-06-29 17:11:03 +0200
commit910c6d09311ff41ee6f913ff4881f4d8059c2a33 (patch)
tree0ab30166ce1ac5923f50240597551c8504247233
parentfd4487f01ac5d9ae5e03adcf8120e64f2e7c6a52 (diff)
downloadsystemd-910c6d09311ff41ee6f913ff4881f4d8059c2a33.tar.gz
systemd-910c6d09311ff41ee6f913ff4881f4d8059c2a33.tar.bz2
systemd-910c6d09311ff41ee6f913ff4881f4d8059c2a33.zip
Treat kernel version condition as a list of quoted checks
Before only one comparison was allowed. Let's make this more flexible: ConditionKernelVersion = ">=4.0" "<=4.5" Fixes #12881. This also fixes expressions like "ConditionKernelVersion=>" which would evaluate as true.
-rw-r--r--man/systemd.unit.xml9
-rw-r--r--src/shared/condition.c47
-rw-r--r--src/test/test-condition.c24
-rw-r--r--test/test-execute/exec-basic.service2
4 files changed, 72 insertions, 10 deletions
diff --git a/man/systemd.unit.xml b/man/systemd.unit.xml
index 045931038b..0ac9ff4882 100644
--- a/man/systemd.unit.xml
+++ b/man/systemd.unit.xml
@@ -1136,10 +1136,11 @@
<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 single string. If the string 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>
+ 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>
<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 fixes from newer upstream
diff --git a/src/shared/condition.c b/src/shared/condition.c
index a54323a55a..8c613fcd5f 100644
--- a/src/shared/condition.c
+++ b/src/shared/condition.c
@@ -207,6 +207,7 @@ static int condition_test_kernel_version(Condition *c) {
OrderOperator order;
struct utsname u;
const char *p;
+ bool first = true;
assert(c);
assert(c->parameter);
@@ -215,13 +216,49 @@ static int condition_test_kernel_version(Condition *c) {
assert_se(uname(&u) >= 0);
p = c->parameter;
- order = parse_order(&p);
- /* No prefix? Then treat as glob string */
- if (order < 0)
- return fnmatch(skip_leading_chars(c->parameter, NULL), u.release, 0) == 0;
+ for (;;) {
+ _cleanup_free_ char *word = NULL;
+ const char *s;
+ int r;
+
+ r = extract_first_word(&p, &word, NULL, EXTRACT_UNQUOTE);
+ if (r < 0)
+ return log_debug_errno(r, "Failed to parse condition string \"%s\": %m", p);
+ if (r == 0)
+ break;
+
+ s = strstrip(word);
+ order = parse_order(&s);
+ 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
+ return log_debug_errno(SYNTHETIC_ERRNO(EINVAL), "Unexpected end of expression: %s", p);
+ }
+
+ r = test_order(str_verscmp(u.release, s), order);
+ } else
+ /* No prefix? Then treat as glob string */
+ r = fnmatch(s, u.release, 0) == 0;
+
+ if (r == 0)
+ return false;
+
+ first = false;
+ }
- return test_order(str_verscmp(u.release, skip_leading_chars(p, NULL)), order);
+ return true;
}
static int condition_test_memory(Condition *c) {
diff --git a/src/test/test-condition.c b/src/test/test-condition.c
index 4bbca2074f..4fa4974adc 100644
--- a/src/test/test-condition.c
+++ b/src/test/test-condition.c
@@ -301,9 +301,11 @@ static void test_condition_test_kernel_version(void) {
assert_se(condition_test(condition));
condition_free(condition);
+ /* An artificially empty condition. It evaluates to true, but normally
+ * such condition cannot be created, because the condition list is reset instead. */
condition = condition_new(CONDITION_KERNEL_VERSION, "", false, false);
assert_se(condition);
- assert_se(!condition_test(condition));
+ assert_se(condition_test(condition) > 0);
condition_free(condition);
assert_se(uname(&u) >= 0);
@@ -327,6 +329,26 @@ static void test_condition_test_kernel_version(void) {
assert_se(condition_test(condition));
condition_free(condition);
+ condition = condition_new(CONDITION_KERNEL_VERSION, ">0.1.2", false, false);
+ assert_se(condition);
+ assert_se(condition_test(condition) > 0);
+ condition_free(condition);
+
+ condition = condition_new(CONDITION_KERNEL_VERSION, "'>0.1.2' '<9.0.0'", false, false);
+ assert_se(condition);
+ assert_se(condition_test(condition) > 0);
+ condition_free(condition);
+
+ condition = condition_new(CONDITION_KERNEL_VERSION, "> 0.1.2 < 9.0.0", false, false);
+ assert_se(condition);
+ assert_se(condition_test(condition) == -EINVAL);
+ condition_free(condition);
+
+ condition = condition_new(CONDITION_KERNEL_VERSION, ">", false, false);
+ assert_se(condition);
+ assert_se(condition_test(condition) == -EINVAL);
+ condition_free(condition);
+
condition = condition_new(CONDITION_KERNEL_VERSION, ">= 0.1.2", false, false);
assert_se(condition);
assert_se(condition_test(condition));
diff --git a/test/test-execute/exec-basic.service b/test/test-execute/exec-basic.service
index 3c90f548a4..ae4618c3f3 100644
--- a/test/test-execute/exec-basic.service
+++ b/test/test-execute/exec-basic.service
@@ -2,6 +2,8 @@
Description=Test for basic execution
ConditionKernelVersion=">=3.0"
ConditionKernelVersion=">=2.0" "<=60" "!=1.4"
+ConditionKernelVersion=" >= 2.0" " <= 60 " "!= 1.4"
+ConditionKernelVersion=" >= 2.0" " * " "*.*"
[Service]
ExecStart=touch /tmp/a ; /bin/sh -c 'touch /tmp/b' ; touch /tmp/c