Sign Up
Log In
Log In
or
Sign Up
Places
All Projects
Status Monitor
Collapse sidebar
SUSE:SLE-15-SP7:Update
libvirt.16732
c5fffb95-kernel-cmdline-parser.patch
Overview
Repositories
Revisions
Requests
Users
Attributes
Meta
File c5fffb95-kernel-cmdline-parser.patch of Package libvirt.16732
commit c5fffb959d93b83d87e70b21d19424e9722700b0 Author: Paulo de Rezende Pinatti <ppinatti@linux.ibm.com> Date: Mon Jun 15 10:28:06 2020 +0200 util: Introduce a parser for kernel cmdline arguments Introduce two utility functions to parse a kernel command line string according to the kernel code parsing rules in order to enable the caller to perform operations such as verifying whether certain argument=value combinations are present or retrieving an argument's value. Signed-off-by: Paulo de Rezende Pinatti <ppinatti@linux.ibm.com> Signed-off-by: Boris Fiuczynski <fiuczy@linux.ibm.com> Reviewed-by: Erik Skultety <eskultet@redhat.com> Index: libvirt-6.0.0/src/libvirt_private.syms =================================================================== --- libvirt-6.0.0.orig/src/libvirt_private.syms +++ libvirt-6.0.0/src/libvirt_private.syms @@ -3393,6 +3393,8 @@ virHostGetDRMRenderNode; virHostHasIOMMU; virIndexToDiskName; virIsDevMapperDevice; +virKernelCmdlineMatchParam; +virKernelCmdlineNextParam; virMemoryLimitIsSet; virMemoryLimitTruncate; virMemoryMaxValue; Index: libvirt-6.0.0/src/util/virutil.c =================================================================== --- libvirt-6.0.0.orig/src/util/virutil.c +++ libvirt-6.0.0/src/util/virutil.c @@ -1712,3 +1712,188 @@ virHostGetDRMRenderNode(void) VIR_DIR_CLOSE(driDir); return ret; } + + +static const char *virKernelCmdlineSkipQuote(const char *cmdline, + bool *is_quoted) +{ + if (cmdline[0] == '"') { + *is_quoted = !(*is_quoted); + cmdline++; + } + return cmdline; +} + + +/** + * virKernelCmdlineFindEqual: + * @cmdline: target kernel command line string + * @is_quoted: indicates whether the string begins with quotes + * @res: pointer to the position immediately after the parsed parameter, + * can be used in subsequent calls to process further parameters until + * the end of the string. + * + * Iterate over the provided kernel command line string while honoring + * the kernel quoting rules and returns the index of the equal sign + * separating argument and value. + * + * Returns 0 for the cases where no equal sign is found or the argument + * itself begins with the equal sign (both cases indicating that the + * argument has no value). Otherwise, returns the index of the equal + * sign in the string. + */ +static size_t virKernelCmdlineFindEqual(const char *cmdline, + bool is_quoted, + const char **res) +{ + size_t i; + size_t equal_index = 0; + + for (i = 0; cmdline[i]; i++) { + if (!(is_quoted) && g_ascii_isspace(cmdline[i])) + break; + if (equal_index == 0 && cmdline[i] == '=') { + equal_index = i; + continue; + } + virKernelCmdlineSkipQuote(cmdline + i, &is_quoted); + } + *res = cmdline + i; + return equal_index; +} + + +static char* virKernelArgNormalize(const char *arg) +{ + return virStringReplace(arg, "_", "-"); +} + + +/** + * virKernelCmdlineNextParam: + * @cmdline: kernel command line string to be checked for next parameter + * @param: pointer to hold retrieved parameter, will be NULL if none found + * @val: pointer to hold retrieved value of @param + * + * Parse the kernel cmdline and store the next parameter in @param + * and the value of @param in @val which can be NULL if @param has + * no value. In addition returns the address right after @param=@value + * for possible further processing. + * + * Returns a pointer to address right after @param=@val in the + * kernel command line, will point to the string's end (NULL) + * in case no next parameter is found + */ +const char *virKernelCmdlineNextParam(const char *cmdline, + char **param, + char **val) +{ + const char *next; + int equal_index; + bool is_quoted = false; + *param = NULL; + *val = NULL; + + virSkipSpaces(&cmdline); + cmdline = virKernelCmdlineSkipQuote(cmdline, &is_quoted); + equal_index = virKernelCmdlineFindEqual(cmdline, is_quoted, &next); + + if (next == cmdline) + return next; + + /* param has no value */ + if (equal_index == 0) { + if (is_quoted && next[-1] == '"') + *param = g_strndup(cmdline, next - cmdline - 1); + else + *param = g_strndup(cmdline, next - cmdline); + return next; + } + + *param = g_strndup(cmdline, equal_index); + + if (cmdline[equal_index + 1] == '"') { + is_quoted = true; + equal_index++; + } + + if (is_quoted && next[-1] == '"') + *val = g_strndup(cmdline + equal_index + 1, + next - cmdline - equal_index - 2); + else + *val = g_strndup(cmdline + equal_index + 1, + next - cmdline - equal_index - 1); + return next; +} + + +static bool virKernelCmdlineStrCmp(const char *kernel_val, + const char *caller_val, + virKernelCmdlineFlags flags) +{ + if (flags & VIR_KERNEL_CMDLINE_FLAGS_CMP_PREFIX) + return STRPREFIX(kernel_val, caller_val); + return STREQ(kernel_val, caller_val); +} + + +/** ++ * virKernelCmdlineMatchParam: ++ * @cmdline: kernel command line string to be checked for @arg ++ * @arg: kernel command line argument ++ * @values: array of possible values to match @arg ++ * @len_values: size of array, it can be 0 meaning a match will be positive if + * the argument has no value. + * @flags: bitwise-OR of virKernelCmdlineFlags + * + * Try to match the provided kernel cmdline string with the provided @arg + * and the list @values of possible values according to the matching strategy + * defined in @flags. + * + * + * Returns true if a match is found, false otherwise + */ +bool virKernelCmdlineMatchParam(const char *cmdline, + const char *arg, + const char **values, + size_t len_values, + virKernelCmdlineFlags flags) +{ + bool match = false; + size_t i; + const char *next = cmdline; + g_autofree char *arg_norm = virKernelArgNormalize(arg); + + while (next[0] != '\0') { + g_autofree char *kparam = NULL; + g_autofree char *kparam_norm = NULL; + g_autofree char *kval = NULL; + + next = virKernelCmdlineNextParam(next, &kparam, &kval); + + if (!kparam) + break; + + kparam_norm = virKernelArgNormalize(kparam); + + if (STRNEQ(kparam_norm, arg_norm)) + continue; + + if (!kval) { + match = (len_values == 0) ? true : false; + } else { + match = false; + for (i = 0; i < len_values; i++) { + if (virKernelCmdlineStrCmp(kval, values[i], flags)) { + match = true; + break; + } + } + } + + if (match && (flags & VIR_KERNEL_CMDLINE_FLAGS_SEARCH_FIRST)) + break; + } + + return match; +} Index: libvirt-6.0.0/src/util/virutil.h =================================================================== --- libvirt-6.0.0.orig/src/util/virutil.h +++ libvirt-6.0.0/src/util/virutil.h @@ -149,6 +149,40 @@ bool virHostHasIOMMU(void); char *virHostGetDRMRenderNode(void) G_GNUC_NO_INLINE; +/* Kernel cmdline match and comparison strategy for arg=value pairs */ +typedef enum { + /* substring comparison of argument values */ + VIR_KERNEL_CMDLINE_FLAGS_CMP_PREFIX = 1, + + /* strict string comparison of argument values */ + VIR_KERNEL_CMDLINE_FLAGS_CMP_EQ = 2, + + /* look for any occurrence of the argument with the expected value, + * this should be used when an argument set to the expected value overrides + * all the other occurrences of the argument, e.g. when looking for 'arg=1' + * in 'arg=0 arg=1 arg=0' the search would succeed with this flag + */ + VIR_KERNEL_CMDLINE_FLAGS_SEARCH_FIRST = 4, + + /* look for the last occurrence of argument with the expected value, + * this should be used when the last occurrence of the argument overrides + * all the other ones, e.g. when looking for 'arg=1' in 'arg=0 arg=1' the + * search would succeed with this flag, but in 'arg=1 arg=0' it would not, + * because 'arg=0' overrides all the previous occurrences of 'arg' + */ + VIR_KERNEL_CMDLINE_FLAGS_SEARCH_LAST = 8, +} virKernelCmdlineFlags; + +const char *virKernelCmdlineNextParam(const char *cmdline, + char **param, + char **val); + +bool virKernelCmdlineMatchParam(const char *cmdline, + const char *arg, + const char **values, + size_t len_values, + virKernelCmdlineFlags flags); + /** * VIR_ASSIGN_IS_OVERFLOW: * @rvalue: value that is checked (evaluated twice) Index: libvirt-6.0.0/tests/utiltest.c =================================================================== --- libvirt-6.0.0.orig/tests/utiltest.c +++ libvirt-6.0.0/tests/utiltest.c @@ -254,6 +254,140 @@ testOverflowCheckMacro(const void *data } +struct testKernelCmdlineNextParamData +{ + const char *cmdline; + const char *param; + const char *val; + const char *next; +}; + +static struct testKernelCmdlineNextParamData kEntries[] = { + { "arg1 arg2 arg3=val1", "arg1", NULL, " arg2 arg3=val1" }, + { "arg1=val1 arg2 arg3=val3 arg4", "arg1", "val1", " arg2 arg3=val3 arg4" }, + { "arg1=sub1=val1,sub2=val2 arg3=val3 arg4", "arg1", "sub1=val1,sub2=val2", " arg3=val3 arg4" }, + { "arg3=val3 ", "arg3", "val3", " " }, + { "arg3=val3", "arg3", "val3", "" }, + { "arg-3=val3 arg4", "arg-3", "val3", " arg4" }, + { " arg_3=val3 arg4", "arg_3", "val3", " arg4" }, + { "arg2=\"value with space\" arg3=val3", "arg2", "value with space", " arg3=val3" }, + { " arg2=\"value with space\" arg3=val3", "arg2", "value with space", " arg3=val3" }, + { " \"arg2=value with space\" arg3=val3", "arg2", "value with space", " arg3=val3" }, + { "arg2=\"val\"ue arg3", "arg2", "val\"ue", " arg3" }, + { "arg2=value\" long\" arg3", "arg2", "value\" long\"", " arg3" }, + { " \"arg2 with space=value with space\" arg3", "arg2 with space", "value with space", " arg3" }, + { " arg2\" with space=val2\" arg3", "arg2\" with space", "val2\"", " arg3" }, + { " arg2longer=someval\" long\" arg2=val2", "arg2longer", "someval\" long\"", " arg2=val2" }, + { "=val1 arg2=val2", "=val1", NULL, " arg2=val2" }, + { " ", NULL, NULL, "" }, + { "", NULL, NULL, "" }, +}; + +static int +testKernelCmdlineNextParam(const void *data G_GNUC_UNUSED) +{ + const char *next; + size_t i; + + for (i = 0; i < G_N_ELEMENTS(kEntries); ++i) { + g_autofree char * param = NULL; + g_autofree char * val = NULL; + + next = virKernelCmdlineNextParam(kEntries[i].cmdline, ¶m, &val); + + if (STRNEQ_NULLABLE(param, kEntries[i].param) || + STRNEQ_NULLABLE(val, kEntries[i].val) || + STRNEQ(next, kEntries[i].next)) { + VIR_TEST_DEBUG("\nKernel cmdline [%s]", kEntries[i].cmdline); + VIR_TEST_DEBUG("Expect param [%s]", kEntries[i].param); + VIR_TEST_DEBUG("Actual param [%s]", param); + VIR_TEST_DEBUG("Expect value [%s]", kEntries[i].val); + VIR_TEST_DEBUG("Actual value [%s]", val); + VIR_TEST_DEBUG("Expect next [%s]", kEntries[i].next); + VIR_TEST_DEBUG("Actual next [%s]", next); + + return -1; + } + } + + return 0; +} + + +struct testKernelCmdlineMatchData +{ + const char *cmdline; + const char *arg; + const char *values[2]; + virKernelCmdlineFlags flags; + bool result; +}; + +static struct testKernelCmdlineMatchData kMatchEntries[] = { + {"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 }, + {"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 }, + {"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 }, + {"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 }, + {"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 }, + {"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 }, + {"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 }, + {"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 }, + {"arg1 myarg=no arg2=val2 arg4=val4 myarg arg5", "myarg", {NULL, NULL}, VIR_KERNEL_CMDLINE_FLAGS_SEARCH_LAST, true }, + {"arg1 myarg arg2=val2 arg4=val4 myarg=yes arg5", "myarg", {NULL, NULL}, VIR_KERNEL_CMDLINE_FLAGS_SEARCH_FIRST, true }, + {"arg1 myarg arg2=val2 arg4=val4 myarg=yes arg5", "myarg", {NULL, NULL}, VIR_KERNEL_CMDLINE_FLAGS_SEARCH_LAST, false }, + {"arg1 my-arg=no arg2=val2 arg4=val4 my_arg=yes arg5", "my-arg", {"on", "yes"}, VIR_KERNEL_CMDLINE_FLAGS_SEARCH_LAST, true }, + {"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 }, + {"arg1 my-arg arg2=val2 arg4=val4 my_arg=yes arg5", "my_arg", {NULL, NULL}, VIR_KERNEL_CMDLINE_FLAGS_SEARCH_FIRST, true }, + {"arg1 my-arg arg2=val2 arg4=val4 my-arg=yes arg5", "my_arg", {NULL, NULL}, VIR_KERNEL_CMDLINE_FLAGS_SEARCH_FIRST, true }, + {"=arg1 my-arg arg2=val2 arg4=val4 my-arg=yes arg5", "my_arg", {NULL, NULL}, VIR_KERNEL_CMDLINE_FLAGS_SEARCH_FIRST, true }, + {"my-arg =arg1 arg2=val2 arg4=val4 my-arg=yes arg5", "=arg1", {NULL, NULL}, VIR_KERNEL_CMDLINE_FLAGS_SEARCH_LAST, true }, + {"arg1 arg2=val2 myarg=sub1=val1 arg5", "myarg", {"sub1=val1", NULL}, VIR_KERNEL_CMDLINE_FLAGS_SEARCH_LAST, true }, + {"arg1 arg2=", "arg2", {"", ""}, VIR_KERNEL_CMDLINE_FLAGS_SEARCH_LAST | VIR_KERNEL_CMDLINE_FLAGS_CMP_EQ, true }, + {" ", "myarg", {NULL, NULL}, VIR_KERNEL_CMDLINE_FLAGS_SEARCH_LAST, false }, + {"", "", {NULL, NULL}, VIR_KERNEL_CMDLINE_FLAGS_SEARCH_LAST, false }, +}; + + +static int +testKernelCmdlineMatchParam(const void *data G_GNUC_UNUSED) +{ + bool result; + size_t i, lenValues; + + for (i = 0; i < G_N_ELEMENTS(kMatchEntries); ++i) { + if (kMatchEntries[i].values[0] == NULL) + lenValues = 0; + else + lenValues = G_N_ELEMENTS(kMatchEntries[i].values); + + result = virKernelCmdlineMatchParam(kMatchEntries[i].cmdline, + kMatchEntries[i].arg, + kMatchEntries[i].values, + lenValues, + kMatchEntries[i].flags); + + if (result != kMatchEntries[i].result) { + VIR_TEST_DEBUG("\nKernel cmdline [%s]", kMatchEntries[i].cmdline); + VIR_TEST_DEBUG("Kernel argument [%s]", kMatchEntries[i].arg); + VIR_TEST_DEBUG("Kernel values [%s] [%s]", kMatchEntries[i].values[0], + kMatchEntries[i].values[1]); + if (kMatchEntries[i].flags & VIR_KERNEL_CMDLINE_FLAGS_CMP_PREFIX) + VIR_TEST_DEBUG("Flag [VIR_KERNEL_CMDLINE_FLAGS_CMP_PREFIX]"); + if (kMatchEntries[i].flags & VIR_KERNEL_CMDLINE_FLAGS_CMP_EQ) + VIR_TEST_DEBUG("Flag [VIR_KERNEL_CMDLINE_FLAGS_CMP_EQ]"); + if (kMatchEntries[i].flags & VIR_KERNEL_CMDLINE_FLAGS_SEARCH_FIRST) + VIR_TEST_DEBUG("Flag [VIR_KERNEL_CMDLINE_FLAGS_SEARCH_FIRST]"); + if (kMatchEntries[i].flags & VIR_KERNEL_CMDLINE_FLAGS_SEARCH_LAST) + VIR_TEST_DEBUG("Flag [VIR_KERNEL_CMDLINE_FLAGS_SEARCH_LAST]"); + VIR_TEST_DEBUG("Expect result [%d]", kMatchEntries[i].result); + VIR_TEST_DEBUG("Actual result [%d]", result); + + return -1; + } + } + + return 0; +} static int @@ -277,6 +411,8 @@ mymain(void) DO_TEST(ParseVersionString); DO_TEST(RoundValueToPowerOfTwo); DO_TEST(OverflowCheckMacro); + DO_TEST(KernelCmdlineNextParam); + DO_TEST(KernelCmdlineMatchParam); return result == 0 ? EXIT_SUCCESS : EXIT_FAILURE; }
Locations
Projects
Search
Status Monitor
Help
OpenBuildService.org
Documentation
API Documentation
Code of Conduct
Contact
Support
@OBShq
Terms
openSUSE Build Service is sponsored by
The Open Build Service is an
openSUSE project
.
Sign Up
Log In
Places
Places
All Projects
Status Monitor