]> git.ipfire.org Git - thirdparty/systemd.git/commitdiff
analyze: add compare-versions
authorZbigniew Jędrzejewski-Szmek <zbyszek@in.waw.pl>
Tue, 17 May 2022 14:28:45 +0000 (16:28 +0200)
committerZbigniew Jędrzejewski-Szmek <zbyszek@in.waw.pl>
Thu, 19 May 2022 07:07:34 +0000 (09:07 +0200)
The interface, output, and exit status convention are all taken directly from
rpmdev-vercmp and dpkg --compare-versions. The implementation is different
though. See test-string-util for a list of known cases where we compare
strings incompatibly.

The idea is that this string comparison function will be declared as "the"
method to use for boot entry ordering in the specification and similar
uses. Thus it's nice to allow users to compare strings.

man/systemd-analyze.xml
src/analyze/analyze-compare-versions.c [new file with mode: 0644]
src/analyze/analyze-compare-versions.h [new file with mode: 0644]
src/analyze/analyze.c
src/analyze/meson.build

index 97290d479b4403dab90b5df829ed2110882c010b..8061743c567195551cc052e56fc97b0a09fd0cc5 100644 (file)
       <arg choice="plain">cat-config</arg>
       <arg choice="plain" rep="repeat"><replaceable>NAME</replaceable>|<replaceable>PATH</replaceable></arg>
     </cmdsynopsis>
+    <cmdsynopsis>
+      <command>systemd-analyze</command>
+      <arg choice="opt" rep="repeat">OPTIONS</arg>
+      <arg choice="plain">compare-versions</arg>
+      <arg choice="plain"><replaceable>VERSION1</replaceable></arg>
+      <arg choice="opt"><replaceable>OP</replaceable></arg>
+      <arg choice="plain"><replaceable>VERSION2</replaceable></arg>
+    </cmdsynopsis>
     <cmdsynopsis>
       <command>systemd-analyze</command>
       <arg choice="opt" rep="repeat">OPTIONS</arg>
@@ -547,6 +555,52 @@ NAutoVTs=8
       </example>
     </refsect2>
 
+    <refsect2>
+      <title><command>systemd-analyze compare-versions
+      <replaceable>VERSION1</replaceable>
+      <optional><replaceable>OP</replaceable></optional>
+      <replaceable>VERSION2</replaceable></command></title>
+
+      <para>This command has two distinct modes of operation, depending on whether the operator
+      <replaceable>OP</replaceable> is specified.</para>
+
+      <para>In the first mode — when <replaceable>OP</replaceable> is not specified — it will compare the two
+      version strings and print either <literal><replaceable>VERSION1</replaceable> &lt;
+      <replaceable>VERSION2</replaceable></literal>, or <literal><replaceable>VERSION1</replaceable> ==
+      <replaceable>VERSION2</replaceable></literal>, or <literal><replaceable>VERSION1</replaceable> &gt;
+      <replaceable>VERSION2</replaceable></literal> as appropriate.</para>
+
+      <para>The exit status is <constant>0</constant> if the versions are equal, <constant>11</constant> if
+      the version of the right is smaller, and <constant>12</constant> if the version of the left is
+      smaller. (This matches the convention used by <command>rpmdev-vercmp</command>.)</para>
+
+      <para>In the second mode — when <replaceable>OP</replaceable> is specified — it will compare the two
+      version strings using the operation <replaceable>OP</replaceable> and return <constant>0</constant>
+      (success) if they condition is satisfied, and <constant>1</constant> (failure)
+      otherwise. <constant>OP</constant> may be <command>lt</command>, <command>le</command>,
+      <command>eq</command>, <command>ne</command>, <command>ge</command>, <command>gt</command>. In this
+      mode, no output is printed.
+      (This matches the convention used by
+      <citerefentry project='die-net'><refentrytitle>dpkg</refentrytitle><manvolnum>1</manvolnum></citerefentry>
+      <option>--compare-versions</option>.)</para>
+
+      <example>
+        <title>Compare versions of a package</title>
+
+        <programlisting>
+$ systemd-analyze compare-versions systemd-250~rc1.fc36.aarch64 systemd-251.fc36.aarch64
+systemd-250~rc1.fc36.aarch64 &lt; systemd-251.fc36.aarch64
+$ echo $?
+12
+
+$ systemd-analyze compare-versions 1 lt 2; echo $?
+0
+$ systemd-analyze compare-versions 1 ge 2; echo $?
+1
+        </programlisting>
+      </example>
+    </refsect2>
+
     <refsect2>
       <title><command>systemd-analyze verify <replaceable>FILE</replaceable>...</command></title>
 
@@ -1197,8 +1251,12 @@ $ systemd-analyze verify /tmp/source:alias.service
   <refsect1>
     <title>Exit status</title>
 
-    <para>On success, 0 is returned, a non-zero failure code
-    otherwise.</para>
+    <para>For most commands, 0 is returned on success, and a non-zero failure code otherwise.</para>
+
+    <para>With the verb <command>compare-versions</command>, in the two-argument form,
+    <constant>12</constant>, <constant>0</constant>, <constant>11</constant> is returned if the second
+    version string is respectively larger, equal, or smaller to the first. In the three-argument form,
+    <constant>0</constant> or <constant>1</constant> if the condition is respectively true or false.</para>
   </refsect1>
 
   <xi:include href="common-variables.xml" />
diff --git a/src/analyze/analyze-compare-versions.c b/src/analyze/analyze-compare-versions.c
new file mode 100644 (file)
index 0000000..9545326
--- /dev/null
@@ -0,0 +1,47 @@
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+
+#include <stdio.h>
+
+#include "analyze-compare-versions.h"
+#include "macro.h"
+#include "string-util.h"
+#include "strv.h"
+
+int verb_compare_versions(int argc, char *argv[], void *userdata) {
+        int r;
+
+        assert(argc == 3 || argc == 4);
+        assert(argv);
+
+        if (argc == 3) {
+                r = strverscmp_improved(ASSERT_PTR(argv[1]), ASSERT_PTR(argv[2]));
+                printf("%s %s %s\n",
+                       argv[1],
+                       r < 0 ? "<" : r > 0 ? ">" : "==",
+                       argv[2]);
+
+                /* This matches the exit convention used by rpmdev-vercmp.
+                 * We don't use named values because 11 and 12 don't have names. */
+                return r < 0 ? 12 : r > 0 ? 11 : 0;
+
+        } else {
+                const char *op = ASSERT_PTR(argv[2]);
+
+                r = strverscmp_improved(ASSERT_PTR(argv[1]), ASSERT_PTR(argv[3]));
+
+                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);
+        }
+}
diff --git a/src/analyze/analyze-compare-versions.h b/src/analyze/analyze-compare-versions.h
new file mode 100644 (file)
index 0000000..ac90ede
--- /dev/null
@@ -0,0 +1,3 @@
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+
+int verb_compare_versions(int argc, char *argv[], void *userdata);
index 2935ecea7af52ae03bfe8ca027c0f382a372e545..4968946963aecd6b0219b938bab51d7f6843815f 100644 (file)
@@ -35,6 +35,7 @@
 #include "analyze-timestamp.h"
 #include "analyze-unit-files.h"
 #include "analyze-unit-paths.h"
+#include "analyze-compare-versions.h"
 #include "analyze-verify.h"
 #include "bus-error.h"
 #include "bus-locator.h"
@@ -199,6 +200,8 @@ static int help(int argc, char *argv[], void *userdata) {
                "  syscall-filter [NAME...]   List syscalls in seccomp filters\n"
                "  filesystems [NAME...]      List known filesystems\n"
                "  condition CONDITION...     Evaluate conditions and asserts\n"
+               "  compare-versions VERSION1 [OP] VERSION2\n"
+               "                             Compare two version strings\n"
                "  verify FILE...             Check unit files for correctness\n"
                "  calendar SPEC...           Validate repetitive calendar time\n"
                "                             events\n"
@@ -564,6 +567,7 @@ static int run(int argc, char *argv[]) {
                 { "capability",        VERB_ANY, VERB_ANY, 0,            verb_capabilities      },
                 { "filesystems",       VERB_ANY, VERB_ANY, 0,            verb_filesystems       },
                 { "condition",         VERB_ANY, VERB_ANY, 0,            verb_condition         },
+                { "compare-versions",  3,        4,        0,            verb_compare_versions  },
                 { "verify",            2,        VERB_ANY, 0,            verb_verify            },
                 { "calendar",          2,        VERB_ANY, 0,            verb_calendar          },
                 { "timestamp",         2,        VERB_ANY, 0,            verb_timestamp         },
index f0cfbb195eab224b4bf73dae2ab0a32e8b4d4492..24ef94149c91476e693ad443357df98ebf4613c0 100644 (file)
@@ -9,6 +9,8 @@ systemd_analyze_sources = files(
         'analyze-capability.h',
         'analyze-cat-config.c',
         'analyze-cat-config.h',
+        'analyze-compare-versions.c',
+        'analyze-compare-versions.h',
         'analyze-condition.c',
         'analyze-condition.h',
         'analyze-critical-chain.c',