a41c76
From 8b59b9dab54c094dfc9bafd9d9f2c18f25877f36 Mon Sep 17 00:00:00 2001
a41c76
Message-Id: <8b59b9dab54c094dfc9bafd9d9f2c18f25877f36@dist-git>
a41c76
From: Paulo de Rezende Pinatti <ppinatti@linux.ibm.com>
a41c76
Date: Wed, 24 Jun 2020 13:16:17 +0200
a41c76
Subject: [PATCH] util: Introduce a parser for kernel cmdline arguments
a41c76
MIME-Version: 1.0
a41c76
Content-Type: text/plain; charset=UTF-8
a41c76
Content-Transfer-Encoding: 8bit
a41c76
a41c76
Introduce two utility functions to parse a kernel command
a41c76
line string according to the kernel code parsing rules in
a41c76
order to enable the caller to perform operations such as
a41c76
verifying whether certain argument=value combinations are
a41c76
present or retrieving an argument's value.
a41c76
a41c76
Signed-off-by: Paulo de Rezende Pinatti <ppinatti@linux.ibm.com>
a41c76
Signed-off-by: Boris Fiuczynski <fiuczy@linux.ibm.com>
a41c76
Reviewed-by: Erik Skultety <eskultet@redhat.com>
a41c76
(cherry picked from commit c5fffb959d93b83d87e70b21d19424e9722700b0)
a41c76
a41c76
https://bugzilla.redhat.com/show_bug.cgi?id=1848997
a41c76
https://bugzilla.redhat.com/show_bug.cgi?id=1850351
a41c76
a41c76
Conflicts:
a41c76
	src/util/virutil.c
a41c76
            - unrelated commits db72866310d and ab36f729470 were not
a41c76
              backported
a41c76
a41c76
Signed-off-by: Jiri Denemark <jdenemar@redhat.com>
a41c76
Message-Id: <784fbc062d41f991b6321ac051b05e6c80a470cd.1592996194.git.jdenemar@redhat.com>
a41c76
Reviewed-by: Ján Tomko <jtomko@redhat.com>
a41c76
---
a41c76
 src/libvirt_private.syms |   2 +
a41c76
 src/util/virutil.c       | 185 +++++++++++++++++++++++++++++++++++++++
a41c76
 src/util/virutil.h       |  34 +++++++
a41c76
 tests/utiltest.c         | 136 ++++++++++++++++++++++++++++
a41c76
 4 files changed, 357 insertions(+)
a41c76
a41c76
diff --git a/src/libvirt_private.syms b/src/libvirt_private.syms
a41c76
index a3fe49ae33..9e290c7bdf 100644
a41c76
--- a/src/libvirt_private.syms
a41c76
+++ b/src/libvirt_private.syms
a41c76
@@ -3395,6 +3395,8 @@ virHexToBin;
a41c76
 virHostGetDRMRenderNode;
a41c76
 virHostHasIOMMU;
a41c76
 virIndexToDiskName;
a41c76
+virKernelCmdlineMatchParam;
a41c76
+virKernelCmdlineNextParam;
a41c76
 virMemoryLimitIsSet;
a41c76
 virMemoryLimitTruncate;
a41c76
 virMemoryMaxValue;
a41c76
diff --git a/src/util/virutil.c b/src/util/virutil.c
a41c76
index 261b2d2af6..17fd06dbb2 100644
a41c76
--- a/src/util/virutil.c
a41c76
+++ b/src/util/virutil.c
a41c76
@@ -1673,3 +1673,188 @@ virHostGetDRMRenderNode(void)
a41c76
     VIR_DIR_CLOSE(driDir);
a41c76
     return ret;
a41c76
 }
a41c76
+
a41c76
+
a41c76
+static const char *virKernelCmdlineSkipQuote(const char *cmdline,
a41c76
+                                             bool *is_quoted)
a41c76
+{
a41c76
+    if (cmdline[0] == '"') {
a41c76
+        *is_quoted = !(*is_quoted);
a41c76
+        cmdline++;
a41c76
+    }
a41c76
+    return cmdline;
a41c76
+}
a41c76
+
a41c76
+
a41c76
+/**
a41c76
+ * virKernelCmdlineFindEqual:
a41c76
+ * @cmdline: target kernel command line string
a41c76
+ * @is_quoted: indicates whether the string begins with quotes
a41c76
+ * @res: pointer to the position immediately after the parsed parameter,
a41c76
+ * can be used in subsequent calls to process further parameters until
a41c76
+ * the end of the string.
a41c76
+ *
a41c76
+ * Iterate over the provided kernel command line string while honoring
a41c76
+ * the kernel quoting rules and returns the index of the equal sign
a41c76
+ * separating argument and value.
a41c76
+ *
a41c76
+ * Returns 0 for the cases where no equal sign is found or the argument
a41c76
+ * itself begins with the equal sign (both cases indicating that the
a41c76
+ * argument has no value). Otherwise, returns the index of the equal
a41c76
+ * sign in the string.
a41c76
+ */
a41c76
+static size_t virKernelCmdlineFindEqual(const char *cmdline,
a41c76
+                                        bool is_quoted,
a41c76
+                                        const char **res)
a41c76
+{
a41c76
+    size_t i;
a41c76
+    size_t equal_index = 0;
a41c76
+
a41c76
+    for (i = 0; cmdline[i]; i++) {
a41c76
+        if (!(is_quoted) && g_ascii_isspace(cmdline[i]))
a41c76
+            break;
a41c76
+        if (equal_index == 0 && cmdline[i] == '=') {
a41c76
+            equal_index = i;
a41c76
+            continue;
a41c76
+        }
a41c76
+        virKernelCmdlineSkipQuote(cmdline + i, &is_quoted);
a41c76
+    }
a41c76
+    *res = cmdline + i;
a41c76
+    return equal_index;
a41c76
+}
a41c76
+
a41c76
+
a41c76
+static char* virKernelArgNormalize(const char *arg)
a41c76
+{
a41c76
+    return virStringReplace(arg, "_", "-");
a41c76
+}
a41c76
+
a41c76
+
a41c76
+/**
a41c76
+ * virKernelCmdlineNextParam:
a41c76
+ * @cmdline: kernel command line string to be checked for next parameter
a41c76
+ * @param: pointer to hold retrieved parameter, will be NULL if none found
a41c76
+ * @val: pointer to hold retrieved value of @param
a41c76
+ *
a41c76
+ * Parse the kernel cmdline and store the next parameter in @param
a41c76
+ * and the value of @param in @val which can be NULL if @param has
a41c76
+ * no value. In addition returns the address right after @param=@value
a41c76
+ * for possible further processing.
a41c76
+ *
a41c76
+ * Returns a pointer to address right after @param=@val in the
a41c76
+ * kernel command line, will point to the string's end (NULL)
a41c76
+ * in case no next parameter is found
a41c76
+ */
a41c76
+const char *virKernelCmdlineNextParam(const char *cmdline,
a41c76
+                                      char **param,
a41c76
+                                      char **val)
a41c76
+{
a41c76
+    const char *next;
a41c76
+    int equal_index;
a41c76
+    bool is_quoted = false;
a41c76
+    *param = NULL;
a41c76
+    *val = NULL;
a41c76
+
a41c76
+    virSkipSpaces(&cmdline);
a41c76
+    cmdline = virKernelCmdlineSkipQuote(cmdline, &is_quoted);
a41c76
+    equal_index = virKernelCmdlineFindEqual(cmdline, is_quoted, &next;;
a41c76
+
a41c76
+    if (next == cmdline)
a41c76
+        return next;
a41c76
+
a41c76
+    /* param has no value */
a41c76
+    if (equal_index == 0) {
a41c76
+        if (is_quoted && next[-1] == '"')
a41c76
+            *param = g_strndup(cmdline, next - cmdline - 1);
a41c76
+        else
a41c76
+            *param = g_strndup(cmdline, next - cmdline);
a41c76
+        return next;
a41c76
+    }
a41c76
+
a41c76
+    *param = g_strndup(cmdline, equal_index);
a41c76
+
a41c76
+    if (cmdline[equal_index + 1] == '"') {
a41c76
+        is_quoted = true;
a41c76
+        equal_index++;
a41c76
+    }
a41c76
+
a41c76
+    if (is_quoted && next[-1] == '"')
a41c76
+        *val = g_strndup(cmdline + equal_index + 1,
a41c76
+                         next - cmdline - equal_index - 2);
a41c76
+    else
a41c76
+        *val = g_strndup(cmdline + equal_index + 1,
a41c76
+                         next - cmdline - equal_index - 1);
a41c76
+    return next;
a41c76
+}
a41c76
+
a41c76
+
a41c76
+static bool virKernelCmdlineStrCmp(const char *kernel_val,
a41c76
+                                   const char *caller_val,
a41c76
+                                   virKernelCmdlineFlags flags)
a41c76
+{
a41c76
+    if (flags & VIR_KERNEL_CMDLINE_FLAGS_CMP_PREFIX)
a41c76
+        return STRPREFIX(kernel_val, caller_val);
a41c76
+    return STREQ(kernel_val, caller_val);
a41c76
+}
a41c76
+
a41c76
+
a41c76
+/**
a41c76
+ * virKernelCmdlineMatchParam:
a41c76
+ * @cmdline: kernel command line string to be checked for @arg
a41c76
+ * @arg: kernel command line argument
a41c76
+ * @values: array of possible values to match @arg
a41c76
+ * @len_values: size of array, it can be 0 meaning a match will be positive if
a41c76
+ *              the argument has no value.
a41c76
+ * @flags: bitwise-OR of virKernelCmdlineFlags
a41c76
+ *
a41c76
+ * Try to match the provided kernel cmdline string with the provided @arg
a41c76
+ * and the list @values of possible values according to the matching strategy
a41c76
+ * defined in @flags.
a41c76
+ *
a41c76
+ *
a41c76
+ * Returns true if a match is found, false otherwise
a41c76
+ */
a41c76
+bool virKernelCmdlineMatchParam(const char *cmdline,
a41c76
+                                const char *arg,
a41c76
+                                const char **values,
a41c76
+                                size_t len_values,
a41c76
+                                virKernelCmdlineFlags flags)
a41c76
+{
a41c76
+    bool match = false;
a41c76
+    size_t i;
a41c76
+    const char *next = cmdline;
a41c76
+    g_autofree char *arg_norm = virKernelArgNormalize(arg);
a41c76
+
a41c76
+    while (next[0] != '\0') {
a41c76
+        g_autofree char *kparam = NULL;
a41c76
+        g_autofree char *kparam_norm = NULL;
a41c76
+        g_autofree char *kval = NULL;
a41c76
+
a41c76
+        next = virKernelCmdlineNextParam(next, &kparam, &kval);
a41c76
+
a41c76
+        if (!kparam)
a41c76
+            break;
a41c76
+
a41c76
+        kparam_norm = virKernelArgNormalize(kparam);
a41c76
+
a41c76
+        if (STRNEQ(kparam_norm, arg_norm))
a41c76
+            continue;
a41c76
+
a41c76
+        if (!kval) {
a41c76
+            match = (len_values == 0) ? true : false;
a41c76
+        } else {
a41c76
+            match = false;
a41c76
+            for (i = 0; i < len_values; i++) {
a41c76
+                if (virKernelCmdlineStrCmp(kval, values[i], flags)) {
a41c76
+                    match = true;
a41c76
+                    break;
a41c76
+                }
a41c76
+            }
a41c76
+        }
a41c76
+
a41c76
+        if (match && (flags & VIR_KERNEL_CMDLINE_FLAGS_SEARCH_FIRST))
a41c76
+            break;
a41c76
+    }
a41c76
+
a41c76
+    return match;
a41c76
+}
a41c76
diff --git a/src/util/virutil.h b/src/util/virutil.h
a41c76
index 0dcaff79ac..f1d2ccdd1f 100644
a41c76
--- a/src/util/virutil.h
a41c76
+++ b/src/util/virutil.h
a41c76
@@ -145,6 +145,40 @@ bool virHostHasIOMMU(void);
a41c76
 
a41c76
 char *virHostGetDRMRenderNode(void) G_GNUC_NO_INLINE;
a41c76
 
a41c76
+/* Kernel cmdline match and comparison strategy for arg=value pairs */
a41c76
+typedef enum {
a41c76
+    /* substring comparison of argument values */
a41c76
+    VIR_KERNEL_CMDLINE_FLAGS_CMP_PREFIX = 1,
a41c76
+
a41c76
+    /* strict string comparison of argument values */
a41c76
+    VIR_KERNEL_CMDLINE_FLAGS_CMP_EQ = 2,
a41c76
+
a41c76
+    /* look for any occurrence of the argument with the expected value,
a41c76
+     * this should be used when an argument set to the expected value overrides
a41c76
+     * all the other occurrences of the argument, e.g. when looking for 'arg=1'
a41c76
+     * in 'arg=0 arg=1 arg=0' the search would succeed with this flag
a41c76
+     */
a41c76
+    VIR_KERNEL_CMDLINE_FLAGS_SEARCH_FIRST = 4,
a41c76
+
a41c76
+    /* look for the last occurrence of argument with the expected value,
a41c76
+     * this should be used when the last occurrence of the argument overrides
a41c76
+     * all the other ones, e.g. when looking for 'arg=1' in 'arg=0 arg=1' the
a41c76
+     * search would succeed with this flag, but in 'arg=1 arg=0' it would not,
a41c76
+     * because 'arg=0' overrides all the previous occurrences of 'arg'
a41c76
+     */
a41c76
+    VIR_KERNEL_CMDLINE_FLAGS_SEARCH_LAST = 8,
a41c76
+} virKernelCmdlineFlags;
a41c76
+
a41c76
+const char *virKernelCmdlineNextParam(const char *cmdline,
a41c76
+                                      char **param,
a41c76
+                                      char **val);
a41c76
+
a41c76
+bool virKernelCmdlineMatchParam(const char *cmdline,
a41c76
+                                const char *arg,
a41c76
+                                const char **values,
a41c76
+                                size_t len_values,
a41c76
+                                virKernelCmdlineFlags flags);
a41c76
+
a41c76
 /**
a41c76
  * VIR_ASSIGN_IS_OVERFLOW:
a41c76
  * @rvalue: value that is checked (evaluated twice)
a41c76
diff --git a/tests/utiltest.c b/tests/utiltest.c
a41c76
index 5ae04585cb..2bff7859dc 100644
a41c76
--- a/tests/utiltest.c
a41c76
+++ b/tests/utiltest.c
a41c76
@@ -254,6 +254,140 @@ testOverflowCheckMacro(const void *data G_GNUC_UNUSED)
a41c76
 }
a41c76
 
a41c76
 
a41c76
+struct testKernelCmdlineNextParamData
a41c76
+{
a41c76
+    const char *cmdline;
a41c76
+    const char *param;
a41c76
+    const char *val;
a41c76
+    const char *next;
a41c76
+};
a41c76
+
a41c76
+static struct testKernelCmdlineNextParamData kEntries[] = {
a41c76
+    { "arg1 arg2 arg3=val1",                        "arg1",              NULL,                  " arg2 arg3=val1"      },
a41c76
+    { "arg1=val1 arg2 arg3=val3 arg4",              "arg1",              "val1",                " arg2 arg3=val3 arg4" },
a41c76
+    { "arg1=sub1=val1,sub2=val2 arg3=val3 arg4",    "arg1",              "sub1=val1,sub2=val2", " arg3=val3 arg4"      },
a41c76
+    { "arg3=val3 ",                                 "arg3",              "val3",                " "                    },
a41c76
+    { "arg3=val3",                                  "arg3",              "val3",                ""                     },
a41c76
+    { "arg-3=val3 arg4",                            "arg-3",             "val3",                " arg4"                },
a41c76
+    { " arg_3=val3 arg4",                           "arg_3",             "val3",                " arg4"                },
a41c76
+    { "arg2=\"value with space\" arg3=val3",        "arg2",              "value with space",    " arg3=val3"           },
a41c76
+    { " arg2=\"value with space\"   arg3=val3",     "arg2",              "value with space",    "   arg3=val3"         },
a41c76
+    { "  \"arg2=value with space\" arg3=val3",      "arg2",              "value with space",    " arg3=val3"           },
a41c76
+    { "arg2=\"val\"ue arg3",                        "arg2",              "val\"ue",             " arg3"                },
a41c76
+    { "arg2=value\" long\" arg3",                   "arg2",              "value\" long\"",      " arg3"                },
a41c76
+    { " \"arg2 with space=value with space\" arg3", "arg2 with space",   "value with space",    " arg3"                },
a41c76
+    { " arg2\" with space=val2\" arg3",             "arg2\" with space", "val2\"",              " arg3"                },
a41c76
+    { " arg2longer=someval\" long\" arg2=val2",     "arg2longer",        "someval\" long\"",    " arg2=val2"           },
a41c76
+    { "=val1 arg2=val2",                            "=val1",             NULL,                  " arg2=val2"           },
a41c76
+    { " ",                                          NULL,                NULL,                  ""                     },
a41c76
+    { "",                                           NULL,                NULL,                  ""                     },
a41c76
+};
a41c76
+
a41c76
+static int
a41c76
+testKernelCmdlineNextParam(const void *data G_GNUC_UNUSED)
a41c76
+{
a41c76
+    const char *next;
a41c76
+    size_t i;
a41c76
+
a41c76
+    for (i = 0; i < G_N_ELEMENTS(kEntries); ++i) {
a41c76
+        g_autofree char * param = NULL;
a41c76
+        g_autofree char * val = NULL;
a41c76
+
a41c76
+        next = virKernelCmdlineNextParam(kEntries[i].cmdline, &param, &val;;
a41c76
+
a41c76
+        if (STRNEQ_NULLABLE(param, kEntries[i].param) ||
a41c76
+            STRNEQ_NULLABLE(val, kEntries[i].val) ||
a41c76
+            STRNEQ(next, kEntries[i].next)) {
a41c76
+            VIR_TEST_DEBUG("\nKernel cmdline [%s]", kEntries[i].cmdline);
a41c76
+            VIR_TEST_DEBUG("Expect param [%s]", kEntries[i].param);
a41c76
+            VIR_TEST_DEBUG("Actual param [%s]", param);
a41c76
+            VIR_TEST_DEBUG("Expect value [%s]", kEntries[i].val);
a41c76
+            VIR_TEST_DEBUG("Actual value [%s]", val);
a41c76
+            VIR_TEST_DEBUG("Expect next [%s]", kEntries[i].next);
a41c76
+            VIR_TEST_DEBUG("Actual next [%s]", next);
a41c76
+
a41c76
+            return -1;
a41c76
+        }
a41c76
+    }
a41c76
+
a41c76
+    return 0;
a41c76
+}
a41c76
+
a41c76
+
a41c76
+struct testKernelCmdlineMatchData
a41c76
+{
a41c76
+    const char *cmdline;
a41c76
+    const char *arg;
a41c76
+    const char *values[2];
a41c76
+    virKernelCmdlineFlags flags;
a41c76
+    bool result;
a41c76
+};
a41c76
+
a41c76
+static struct testKernelCmdlineMatchData kMatchEntries[] = {
a41c76
+    {"arg1 myarg=no arg2=val2 myarg=yes arg4=val4 myarg=no arg5", "myarg",  {"1", "y"},          VIR_KERNEL_CMDLINE_FLAGS_SEARCH_FIRST | VIR_KERNEL_CMDLINE_FLAGS_CMP_EQ,     false },
a41c76
+    {"arg1 myarg=no arg2=val2 myarg=yes arg4=val4 myarg=no arg5", "myarg",  {"on", "yes"},       VIR_KERNEL_CMDLINE_FLAGS_SEARCH_FIRST | VIR_KERNEL_CMDLINE_FLAGS_CMP_EQ,     true  },
a41c76
+    {"arg1 myarg=no arg2=val2 myarg=yes arg4=val4 myarg=no arg5", "myarg",  {"1", "y"},          VIR_KERNEL_CMDLINE_FLAGS_SEARCH_FIRST | VIR_KERNEL_CMDLINE_FLAGS_CMP_PREFIX, true  },
a41c76
+    {"arg1 myarg=no arg2=val2 myarg=yes arg4=val4 myarg=no arg5", "myarg",  {"a", "b"},          VIR_KERNEL_CMDLINE_FLAGS_SEARCH_FIRST | VIR_KERNEL_CMDLINE_FLAGS_CMP_PREFIX, false },
a41c76
+    {"arg1 myarg=no arg2=val2 myarg=yes arg4=val4 myarg=no arg5", "myarg",  {"on", "yes"},       VIR_KERNEL_CMDLINE_FLAGS_SEARCH_LAST | VIR_KERNEL_CMDLINE_FLAGS_CMP_EQ,      false },
a41c76
+    {"arg1 myarg=no arg2=val2 myarg=yes arg4=val4 myarg=no arg5", "myarg",  {"1", "y"},          VIR_KERNEL_CMDLINE_FLAGS_SEARCH_LAST | VIR_KERNEL_CMDLINE_FLAGS_CMP_PREFIX,  false },
a41c76
+    {"arg1 myarg=no arg2=val2 arg4=val4 myarg=yes arg5",          "myarg",  {"on", "yes"},       VIR_KERNEL_CMDLINE_FLAGS_SEARCH_LAST | VIR_KERNEL_CMDLINE_FLAGS_CMP_EQ,      true  },
a41c76
+    {"arg1 myarg=no arg2=val2 arg4=val4 myarg=yes arg5",          "myarg",  {"1", "y"},          VIR_KERNEL_CMDLINE_FLAGS_SEARCH_LAST | VIR_KERNEL_CMDLINE_FLAGS_CMP_PREFIX,  true  },
a41c76
+    {"arg1 myarg=no arg2=val2 arg4=val4 myarg arg5",              "myarg",  {NULL, NULL},        VIR_KERNEL_CMDLINE_FLAGS_SEARCH_LAST,                                        true  },
a41c76
+    {"arg1 myarg arg2=val2 arg4=val4 myarg=yes arg5",             "myarg",  {NULL, NULL},        VIR_KERNEL_CMDLINE_FLAGS_SEARCH_FIRST,                                       true  },
a41c76
+    {"arg1 myarg arg2=val2 arg4=val4 myarg=yes arg5",             "myarg",  {NULL, NULL},        VIR_KERNEL_CMDLINE_FLAGS_SEARCH_LAST,                                        false },
a41c76
+    {"arg1 my-arg=no arg2=val2 arg4=val4 my_arg=yes arg5",        "my-arg", {"on", "yes"},       VIR_KERNEL_CMDLINE_FLAGS_SEARCH_LAST,                                        true  },
a41c76
+    {"arg1 my-arg=no arg2=val2 arg4=val4 my_arg=yes arg5 ",       "my-arg", {"on", "yes"},       VIR_KERNEL_CMDLINE_FLAGS_SEARCH_LAST | VIR_KERNEL_CMDLINE_FLAGS_CMP_EQ,      true  },
a41c76
+    {"arg1 my-arg arg2=val2 arg4=val4 my_arg=yes arg5",           "my_arg", {NULL, NULL},        VIR_KERNEL_CMDLINE_FLAGS_SEARCH_FIRST,                                       true  },
a41c76
+    {"arg1 my-arg arg2=val2 arg4=val4 my-arg=yes arg5",           "my_arg", {NULL, NULL},        VIR_KERNEL_CMDLINE_FLAGS_SEARCH_FIRST,                                       true  },
a41c76
+    {"=arg1 my-arg arg2=val2 arg4=val4 my-arg=yes arg5",          "my_arg", {NULL, NULL},        VIR_KERNEL_CMDLINE_FLAGS_SEARCH_FIRST,                                       true  },
a41c76
+    {"my-arg =arg1 arg2=val2 arg4=val4 my-arg=yes arg5",          "=arg1",  {NULL, NULL},        VIR_KERNEL_CMDLINE_FLAGS_SEARCH_LAST,                                        true  },
a41c76
+    {"arg1 arg2=val2 myarg=sub1=val1 arg5",                       "myarg",  {"sub1=val1", NULL}, VIR_KERNEL_CMDLINE_FLAGS_SEARCH_LAST,                                        true  },
a41c76
+    {"arg1 arg2=",                                                "arg2",   {"", ""},            VIR_KERNEL_CMDLINE_FLAGS_SEARCH_LAST | VIR_KERNEL_CMDLINE_FLAGS_CMP_EQ,      true  },
a41c76
+    {" ",                                                         "myarg",  {NULL, NULL},        VIR_KERNEL_CMDLINE_FLAGS_SEARCH_LAST,                                        false },
a41c76
+    {"",                                                          "",       {NULL, NULL},        VIR_KERNEL_CMDLINE_FLAGS_SEARCH_LAST,                                        false },
a41c76
+};
a41c76
+
a41c76
+
a41c76
+static int
a41c76
+testKernelCmdlineMatchParam(const void *data G_GNUC_UNUSED)
a41c76
+{
a41c76
+    bool result;
a41c76
+    size_t i, lenValues;
a41c76
+
a41c76
+    for (i = 0; i < G_N_ELEMENTS(kMatchEntries); ++i) {
a41c76
+        if (kMatchEntries[i].values[0] == NULL)
a41c76
+            lenValues = 0;
a41c76
+        else
a41c76
+            lenValues = G_N_ELEMENTS(kMatchEntries[i].values);
a41c76
+
a41c76
+        result = virKernelCmdlineMatchParam(kMatchEntries[i].cmdline,
a41c76
+                                            kMatchEntries[i].arg,
a41c76
+                                            kMatchEntries[i].values,
a41c76
+                                            lenValues,
a41c76
+                                            kMatchEntries[i].flags);
a41c76
+
a41c76
+        if (result != kMatchEntries[i].result) {
a41c76
+            VIR_TEST_DEBUG("\nKernel cmdline [%s]", kMatchEntries[i].cmdline);
a41c76
+            VIR_TEST_DEBUG("Kernel argument [%s]", kMatchEntries[i].arg);
a41c76
+            VIR_TEST_DEBUG("Kernel values [%s] [%s]", kMatchEntries[i].values[0],
a41c76
+                           kMatchEntries[i].values[1]);
a41c76
+            if (kMatchEntries[i].flags & VIR_KERNEL_CMDLINE_FLAGS_CMP_PREFIX)
a41c76
+                VIR_TEST_DEBUG("Flag [VIR_KERNEL_CMDLINE_FLAGS_CMP_PREFIX]");
a41c76
+            if (kMatchEntries[i].flags & VIR_KERNEL_CMDLINE_FLAGS_CMP_EQ)
a41c76
+                VIR_TEST_DEBUG("Flag [VIR_KERNEL_CMDLINE_FLAGS_CMP_EQ]");
a41c76
+            if (kMatchEntries[i].flags & VIR_KERNEL_CMDLINE_FLAGS_SEARCH_FIRST)
a41c76
+                VIR_TEST_DEBUG("Flag [VIR_KERNEL_CMDLINE_FLAGS_SEARCH_FIRST]");
a41c76
+            if (kMatchEntries[i].flags & VIR_KERNEL_CMDLINE_FLAGS_SEARCH_LAST)
a41c76
+                VIR_TEST_DEBUG("Flag [VIR_KERNEL_CMDLINE_FLAGS_SEARCH_LAST]");
a41c76
+            VIR_TEST_DEBUG("Expect result [%d]", kMatchEntries[i].result);
a41c76
+            VIR_TEST_DEBUG("Actual result [%d]", result);
a41c76
+
a41c76
+            return -1;
a41c76
+        }
a41c76
+    }
a41c76
+
a41c76
+    return 0;
a41c76
+}
a41c76
 
a41c76
 
a41c76
 static int
a41c76
@@ -277,6 +411,8 @@ mymain(void)
a41c76
     DO_TEST(ParseVersionString);
a41c76
     DO_TEST(RoundValueToPowerOfTwo);
a41c76
     DO_TEST(OverflowCheckMacro);
a41c76
+    DO_TEST(KernelCmdlineNextParam);
a41c76
+    DO_TEST(KernelCmdlineMatchParam);
a41c76
 
a41c76
     return result == 0 ? EXIT_SUCCESS : EXIT_FAILURE;
a41c76
 }
a41c76
-- 
a41c76
2.27.0
a41c76