]> git.ipfire.org Git - thirdparty/systemd.git/commitdiff
condition: extend ConditionKernelVersion= with relative version checks 7629/head
authorLennart Poettering <lennart@poettering.net>
Sat, 23 Dec 2017 14:02:58 +0000 (15:02 +0100)
committerLennart Poettering <lennart@poettering.net>
Tue, 26 Dec 2017 16:43:29 +0000 (17:43 +0100)
Now that we have str_verscmp() in our source tree anyway, let's make it
generic and reuse it for ConditionKernelVersion=.

man/systemd.unit.xml
src/basic/util.c
src/basic/util.h
src/shared/bootspec.c
src/shared/condition.c
src/test/test-condition.c

index 73f33a4580d6786626159b23cffa93bda726594f..19a6db7f65b8332385a4c5781e261b3623d4c6e3 100644 (file)
 
         <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, optionally containing shell-style globs.</para>
+        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>&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
index 5dd3c3014f258cd3c48da5b27969b5ba18372a14..2a39ff2b5383686c6261e9d7900eeb16a927969b 100644 (file)
@@ -553,3 +553,65 @@ int version(void) {
              SYSTEMD_FEATURES);
         return 0;
 }
+
+/* This is a direct translation of str_verscmp from boot.c */
+static bool is_digit(int c) {
+        return c >= '0' && c <= '9';
+}
+
+static int c_order(int c) {
+        if (c == 0 || is_digit(c))
+                return 0;
+
+        if ((c >= 'a') && (c <= 'z'))
+                return c;
+
+        return c + 0x10000;
+}
+
+int str_verscmp(const char *s1, const char *s2) {
+        const char *os1, *os2;
+
+        assert(s1);
+        assert(s2);
+
+        os1 = s1;
+        os2 = s2;
+
+        while (*s1 || *s2) {
+                int first;
+
+                while ((*s1 && !is_digit(*s1)) || (*s2 && !is_digit(*s2))) {
+                        int order;
+
+                        order = c_order(*s1) - c_order(*s2);
+                        if (order != 0)
+                                return order;
+                        s1++;
+                        s2++;
+                }
+
+                while (*s1 == '0')
+                        s1++;
+                while (*s2 == '0')
+                        s2++;
+
+                first = 0;
+                while (is_digit(*s1) && is_digit(*s2)) {
+                        if (first == 0)
+                                first = *s1 - *s2;
+                        s1++;
+                        s2++;
+                }
+
+                if (is_digit(*s1))
+                        return 1;
+                if (is_digit(*s2))
+                        return -1;
+
+                if (first != 0)
+                        return first;
+        }
+
+        return strcmp(os1, os2);
+}
index 8cd78171bcb95a00305d0847d22dcc5af6b1b0d6..20181ab91702e4349076e66ad07bef6b66ea2d7b 100644 (file)
@@ -189,3 +189,5 @@ uint64_t system_tasks_max_scale(uint64_t v, uint64_t max);
 int update_reboot_parameter_and_warn(const char *param);
 
 int version(void);
+
+int str_verscmp(const char *s1, const char *s2);
index e97b5cdb79732226bd92dd4c548e855cb765c732..4f255f620bada024ce28f8222a30460dd730d6c1 100644 (file)
@@ -203,67 +203,8 @@ int boot_loader_read_conf(const char *path, BootConfig *config) {
         return 0;
 }
 
-/* This is a direct translation of str_verscmp from boot.c */
-static bool is_digit(int c) {
-        return c >= '0' && c <= '9';
-}
-
-static int c_order(int c) {
-        if (c == '\0')
-                return 0;
-        if (is_digit(c))
-                return 0;
-        else if ((c >= 'a') && (c <= 'z'))
-                return c;
-        else
-                return c + 0x10000;
-}
-
-static int str_verscmp(const char *s1, const char *s2) {
-        const char *os1 = s1;
-        const char *os2 = s2;
-
-        while (*s1 || *s2) {
-                int first;
-
-                while ((*s1 && !is_digit(*s1)) || (*s2 && !is_digit(*s2))) {
-                        int order;
-
-                        order = c_order(*s1) - c_order(*s2);
-                        if (order)
-                                return order;
-                        s1++;
-                        s2++;
-                }
-
-                while (*s1 == '0')
-                        s1++;
-                while (*s2 == '0')
-                        s2++;
-
-                first = 0;
-                while (is_digit(*s1) && is_digit(*s2)) {
-                        if (first == 0)
-                                first = *s1 - *s2;
-                        s1++;
-                        s2++;
-                }
-
-                if (is_digit(*s1))
-                        return 1;
-                if (is_digit(*s2))
-                        return -1;
-
-                if (first != 0)
-                        return first;
-        }
-
-        return strcmp(os1, os2);
-}
-
 static int boot_entry_compare(const void *a, const void *b) {
-        const BootEntry *aa = a;
-        const BootEntry *bb = b;
+        const BootEntry *aa = a, *bb = b;
 
         return str_verscmp(aa->filename, bb->filename);
 }
index 887c9a758864d9486f7cc9b268c4e5417548214a..7a825c397321962270b2a7023bba2f17e5ea6f45 100644 (file)
@@ -145,7 +145,28 @@ static int condition_test_kernel_command_line(Condition *c) {
 }
 
 static int condition_test_kernel_version(Condition *c) {
+        enum {
+                /* Listed in order of checking. Note that some comparators are prefixes of others, hence the longest
+                 * should be listed first. */
+                LOWER_OR_EQUAL,
+                GREATER_OR_EQUAL,
+                LOWER,
+                GREATER,
+                EQUAL,
+                _ORDER_MAX,
+        };
+
+        static const char *const prefix[_ORDER_MAX] = {
+                [LOWER_OR_EQUAL] = "<=",
+                [GREATER_OR_EQUAL] = ">=",
+                [LOWER] = "<",
+                [GREATER] = ">",
+                [EQUAL] = "=",
+        };
+        const char *p = NULL;
         struct utsname u;
+        size_t i;
+        int k;
 
         assert(c);
         assert(c->parameter);
@@ -153,7 +174,38 @@ static int condition_test_kernel_version(Condition *c) {
 
         assert_se(uname(&u) >= 0);
 
-        return fnmatch(c->parameter, u.release, 0) == 0;
+        for (i = 0; i < _ORDER_MAX; i++) {
+                p = startswith(c->parameter, prefix[i]);
+                if (p)
+                        break;
+        }
+
+        /* No prefix? Then treat as glob string */
+        if (!p)
+                return fnmatch(skip_leading_chars(c->parameter, NULL), u.release, 0) == 0;
+
+        k = str_verscmp(u.release, skip_leading_chars(p, NULL));
+
+        switch (i) {
+
+        case LOWER:
+                return k < 0;
+
+        case LOWER_OR_EQUAL:
+                return k <= 0;
+
+        case EQUAL:
+                return k == 0;
+
+        case GREATER_OR_EQUAL:
+                return k >= 0;
+
+        case GREATER:
+                return k > 0;
+
+        default:
+                assert_not_reached("Can't compare");
+        }
 }
 
 static int condition_test_user(Condition *c) {
index 8fb2ad5d18c29640e5774540790169136fe9abac..8323a66ad39b0483e614b36bccfa65e399ee073c 100644 (file)
@@ -303,6 +303,7 @@ static void test_condition_test_kernel_command_line(void) {
 static void test_condition_test_kernel_version(void) {
         Condition *condition;
         struct utsname u;
+        const char *v;
 
         condition = condition_new(CONDITION_KERNEL_VERSION, "*thisreallyshouldntbeinthekernelversion*", false, false);
         assert_se(condition);
@@ -333,6 +334,90 @@ static void test_condition_test_kernel_version(void) {
         assert_se(condition);
         assert_se(condition_test(condition));
         condition_free(condition);
+
+        /* 0.1.2 would be a very very very old kernel */
+        condition = condition_new(CONDITION_KERNEL_VERSION, "> 0.1.2", false, false);
+        assert_se(condition);
+        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));
+        condition_free(condition);
+
+        condition = condition_new(CONDITION_KERNEL_VERSION, "< 0.1.2", false, false);
+        assert_se(condition);
+        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));
+        condition_free(condition);
+
+        condition = condition_new(CONDITION_KERNEL_VERSION, "= 0.1.2", false, false);
+        assert_se(condition);
+        assert_se(!condition_test(condition));
+        condition_free(condition);
+
+        /* 4711.8.15 is a very very very future kernel */
+        condition = condition_new(CONDITION_KERNEL_VERSION, "< 4711.8.15", false, false);
+        assert_se(condition);
+        assert_se(condition_test(condition));
+        condition_free(condition);
+
+        condition = condition_new(CONDITION_KERNEL_VERSION, "<= 4711.8.15", false, false);
+        assert_se(condition);
+        assert_se(condition_test(condition));
+        condition_free(condition);
+
+        condition = condition_new(CONDITION_KERNEL_VERSION, "= 4711.8.15", false, false);
+        assert_se(condition);
+        assert_se(!condition_test(condition));
+        condition_free(condition);
+
+        condition = condition_new(CONDITION_KERNEL_VERSION, "> 4711.8.15", false, false);
+        assert_se(condition);
+        assert_se(!condition_test(condition));
+        condition_free(condition);
+
+        condition = condition_new(CONDITION_KERNEL_VERSION, ">= 4711.8.15", false, false);
+        assert_se(condition);
+        assert_se(!condition_test(condition));
+        condition_free(condition);
+
+        assert_se(uname(&u) >= 0);
+
+        v = strjoina(">=", u.release);
+        condition = condition_new(CONDITION_KERNEL_VERSION, v, false, false);
+        assert_se(condition);
+        assert_se(condition_test(condition));
+        condition_free(condition);
+
+        v = strjoina("=  ", u.release);
+        condition = condition_new(CONDITION_KERNEL_VERSION, v, false, false);
+        assert_se(condition);
+        assert_se(condition_test(condition));
+        condition_free(condition);
+
+        v = strjoina("<=", u.release);
+        condition = condition_new(CONDITION_KERNEL_VERSION, v, false, false);
+        assert_se(condition);
+        assert_se(condition_test(condition));
+        condition_free(condition);
+
+        v = strjoina("> ", u.release);
+        condition = condition_new(CONDITION_KERNEL_VERSION, v, false, false);
+        assert_se(condition);
+        assert_se(!condition_test(condition));
+        condition_free(condition);
+
+        v = strjoina("<   ", u.release);
+        condition = condition_new(CONDITION_KERNEL_VERSION, v, false, false);
+        assert_se(condition);
+        assert_se(!condition_test(condition));
+        condition_free(condition);
 }
 
 static void test_condition_test_null(void) {