First, remove escaped newlines and split up the string into an argv-list for the bhyve and loader commands, respectively. This is done by iterating over the string splitting it by newlines, and then re-iterating over each line, splitting it by spaces. Since this code reuses part of the code of qemu_parse_command.c (in bhyveCommandLine2argv), add the appropriate copyright notices. Signed-off-by: Fabian Freyer <fabian.freyer@xxxxxxxxxxxxxxxxxxx> --- po/POTFILES.in | 1 + src/Makefile.am | 2 + src/bhyve/bhyve_driver.c | 42 +++++++ src/bhyve/bhyve_parse_command.c | 263 ++++++++++++++++++++++++++++++++++++++++ src/bhyve/bhyve_parse_command.h | 30 +++++ 5 files changed, 338 insertions(+) create mode 100644 src/bhyve/bhyve_parse_command.c create mode 100644 src/bhyve/bhyve_parse_command.h diff --git a/po/POTFILES.in b/po/POTFILES.in index 0d92448..b1580b7 100644 --- a/po/POTFILES.in +++ b/po/POTFILES.in @@ -15,6 +15,7 @@ src/bhyve/bhyve_command.c src/bhyve/bhyve_device.c src/bhyve/bhyve_driver.c src/bhyve/bhyve_monitor.c +src/bhyve/bhyve_parse_command.c src/bhyve/bhyve_process.c src/conf/capabilities.c src/conf/cpu_conf.c diff --git a/src/Makefile.am b/src/Makefile.am index 12b66c2..d53c98f 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -901,6 +901,8 @@ BHYVE_DRIVER_SOURCES = \ bhyve/bhyve_capabilities.h \ bhyve/bhyve_command.c \ bhyve/bhyve_command.h \ + bhyve/bhyve_parse_command.c \ + bhyve/bhyve_parse_command.h \ bhyve/bhyve_device.c \ bhyve/bhyve_device.h \ bhyve/bhyve_domain.c \ diff --git a/src/bhyve/bhyve_driver.c b/src/bhyve/bhyve_driver.c index c4051a1..c7abea4 100644 --- a/src/bhyve/bhyve_driver.c +++ b/src/bhyve/bhyve_driver.c @@ -55,6 +55,7 @@ #include "bhyve_device.h" #include "bhyve_driver.h" #include "bhyve_command.h" +#include "bhyve_parse_command.h" #include "bhyve_domain.h" #include "bhyve_process.h" #include "bhyve_capabilities.h" @@ -1536,6 +1537,46 @@ bhyveConnectIsEncrypted(virConnectPtr conn ATTRIBUTE_UNUSED) return 0; } +static char * +bhyveConnectDomainXMLFromNative(virConnectPtr conn, + const char *nativeFormat, + const char *nativeConfig, + unsigned int flags) +{ + char *xml = NULL; + virDomainDefPtr def = NULL; + bhyveConnPtr privconn = conn->privateData; + virCapsPtr capabilities = NULL; + unsigned caps = bhyveDriverGetCaps(conn); + + virCheckFlags(0, NULL); + + if (virConnectDomainXMLFromNativeEnsureACL(conn) < 0) + goto cleanup; + + capabilities = bhyveDriverGetCapabilities(privconn); + + if (!capabilities) + goto cleanup; + + if (STRNEQ(nativeFormat, BHYVE_CONFIG_FORMAT_ARGV)) { + virReportError(VIR_ERR_INVALID_ARG, + _("unsupported config type %s"), nativeFormat); + goto cleanup; + } + + def = bhyveParseCommandLineString(nativeConfig, caps, privconn->xmlopt); + if (def == NULL) + goto cleanup; + + xml = virDomainDefFormat(def, capabilities, 0); + + cleanup: + virObjectUnref(capabilities); + virDomainDefFree(def); + return xml; +} + static virHypervisorDriver bhyveHypervisorDriver = { .name = "bhyve", .connectOpen = bhyveConnectOpen, /* 1.2.2 */ @@ -1589,6 +1630,7 @@ static virHypervisorDriver bhyveHypervisorDriver = { .connectIsAlive = bhyveConnectIsAlive, /* 1.3.5 */ .connectIsSecure = bhyveConnectIsSecure, /* 1.3.5 */ .connectIsEncrypted = bhyveConnectIsEncrypted, /* 1.3.5 */ + .connectDomainXMLFromNative = bhyveConnectDomainXMLFromNative, /* 1.3.6 */ }; diff --git a/src/bhyve/bhyve_parse_command.c b/src/bhyve/bhyve_parse_command.c new file mode 100644 index 0000000..72367bb --- /dev/null +++ b/src/bhyve/bhyve_parse_command.c @@ -0,0 +1,263 @@ +/* + * bhyve_parse_command.c: Bhyve command parser + * + * Copyright (C) 2006-2016 Red Hat, Inc. + * Copyright (C) 2006 Daniel P. Berrange + * Copyright (C) 2016 Fabian Freyer + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library. If not, see + * <http://www.gnu.org/licenses/>. + * + * Author: Fabian Freyer <fabian.freyer@xxxxxxxxxxxxxxxxxxx> + */ + +#include <config.h> + +#include "bhyve_capabilities.h" +#include "bhyve_command.h" +#include "bhyve_parse_command.h" +#include "viralloc.h" +#include "virlog.h" +#include "virstring.h" +#include "virutil.h" +#include "c-ctype.h" + +#define VIR_FROM_THIS VIR_FROM_BHYVE + +VIR_LOG_INIT("bhyve.bhyve_parse_command"); + +/* + * This function takes a string representation of the command line and removes + * all newline characters, if they are prefixed by a backslash. The result + * should be a string with one command per line. + * + * NB: command MUST be NULL-Terminated. + */ +static char * +bhyveParseCommandLineUnescape(const char *command) +{ + size_t len = strlen(command); + char *unescaped = NULL; + char *curr_src = NULL; + char *curr_dst = NULL; + + /* Since we are only removing characters, allocating a buffer of the same + * size as command shouldn't be a problem here */ + if (VIR_ALLOC_N(unescaped, len) < 0) + return NULL; + + /* Iterate over characters in the command, skipping "\\\n", "\\\r" as well + * as "\\\r\n". */ + for (curr_src = (char*) command, curr_dst = unescaped; *curr_src != '\0'; + curr_src++, curr_dst++) { + if (*curr_src == '\\') { + switch (*(curr_src + 1)) { + case '\n': /* \LF */ + curr_src++; + curr_dst--; + break; + case '\r': /* \CR */ + curr_src++; + curr_dst--; + if (*curr_src == '\n') /* \CRLF */ + curr_src++; + break; + default: + *curr_dst = '\\'; + } + } + else *curr_dst = *curr_src; + } + + return unescaped; +} + +/* + * Try to extract loader and bhyve argv lists from a command line string. + */ +static int +bhyveCommandLine2argv(const char *nativeConfig, + int *loader_argc, + char ***loader_argv, + int *bhyve_argc, + char ***bhyve_argv) +{ + const char *curr = NULL; + char *nativeConfig_unescaped = NULL; + const char *start; + const char *next; + char *line; + char **lines = NULL; + size_t line_count = 0; + size_t lines_alloc = 0; + char **_bhyve_argv = NULL; + char **_loader_argv = NULL; + + nativeConfig_unescaped = bhyveParseCommandLineUnescape(nativeConfig); + if (nativeConfig_unescaped == NULL) { + virReportError(VIR_ERR_INTERNAL_ERROR, + _("Failed to unescape command line string")); + goto error; + } + + curr = nativeConfig_unescaped; + + /* Iterate over string, splitting on sequences of '\n' */ + while (curr && *curr != '\0') { + start = curr; + next = strchr(curr, '\n'); + + if (VIR_STRNDUP(line, curr, next ? next - curr : -1) < 0) + goto error; + + if (VIR_RESIZE_N(lines, lines_alloc, line_count, 2) < 0) { + VIR_FREE(line); + goto error; + } + + if (*line) + lines[line_count++] = line; + lines[line_count] = NULL; + + while (next && (*next == '\n' || *next == '\r' + || STRPREFIX(next, "\r\n"))) + next++; + + curr = next; + } + + for (int i = 0; i < line_count; i++) { + curr = lines[i]; + int j; + char **arglist = NULL; + size_t args_count = 0; + size_t args_alloc = 0; + + /* iterate over each line, splitting on sequences of ' '. This code is + * adapted from qemu/qemu_parse_command.c. */ + while (curr && *curr != '\0') { + char *arg; + start = curr; + + if (*start == '\'') { + if (start == curr) + curr++; + next = strchr(start + 1, '\''); + } else if (*start == '"') { + if (start == curr) + curr++; + next = strchr(start + 1, '"'); + } else { + next = strchr(start, ' '); + } + + if (VIR_STRNDUP(arg, curr, next ? next - curr : -1) < 0) + goto error; + + if (next && (*next == '\'' || *next == '"')) + next++; + + if (VIR_RESIZE_N(arglist, args_alloc, args_count, 2) < 0) { + VIR_FREE(arg); + goto error; + } + + arglist[args_count++] = arg; + arglist[args_count] = NULL; + + while (next && c_isspace(*next)) + next++; + + curr = next; + } + + /* To prevent a memory leak here, only set the argument lists when + * the first matching command is found. This shouldn't really be a + * problem, since usually no multiple loaders or bhyverun commands + * are specified (this wouldn't really be valid anyways). + * Otherwise, later argument lists may be assigned to _argv without + * freeing the earlier ones. */ + if (!_bhyve_argv && STREQ(arglist[0], "/usr/sbin/bhyve")) { + if ((VIR_REALLOC_N(_bhyve_argv, args_count + 1) < 0) + || (!bhyve_argc)) + goto error; + for (j = 0; j < args_count; j++) + _bhyve_argv[j] = arglist[j]; + _bhyve_argv[j] = NULL; + *bhyve_argc = args_count-1; + } + else if (!_loader_argv) { + if ((VIR_REALLOC_N(_loader_argv, args_count + 1) < 0) + || (!loader_argc)) + goto error; + for (j = 0; j < args_count; j++) + _loader_argv[j] = arglist[j]; + _loader_argv[j] = NULL; + *loader_argc = args_count-1; + } + /* To prevent a use-after-free here, only free the argument list when + * it is definitely not going to be used */ + else + virStringFreeList(arglist); + } + + *loader_argv = _loader_argv; + *bhyve_argv = _bhyve_argv; + + virStringFreeList(lines); + return 0; + + error: + VIR_FREE(_loader_argv); + VIR_FREE(_bhyve_argv); + virStringFreeList(lines); + return -1; +} + +virDomainDefPtr +bhyveParseCommandLineString(const char* nativeConfig, + unsigned caps ATTRIBUTE_UNUSED, + virDomainXMLOptionPtr xmlopt ATTRIBUTE_UNUSED) +{ + virDomainDefPtr def = NULL; + int bhyve_argc = 0; + char **bhyve_argv = NULL; + int loader_argc = 0; + char **loader_argv = NULL; + + if (!(def = virDomainDefNew())) + goto cleanup; + + // Initialize defaults. + if (virUUIDGenerate(def->uuid) < 0) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("failed to generate uuid")); + goto cleanup; + } + def->id = -1; + def->clock.offset = VIR_DOMAIN_CLOCK_OFFSET_LOCALTIME; + + if (bhyveCommandLine2argv(nativeConfig, + &loader_argc, &loader_argv, + &bhyve_argc, &bhyve_argv)) { + virReportError(VIR_ERR_INTERNAL_ERROR, + _("Failed to convert the command string to argv-lists..")); + goto cleanup; + } + +cleanup: + virStringFreeList(loader_argv); + virStringFreeList(bhyve_argv); + return def; +} diff --git a/src/bhyve/bhyve_parse_command.h b/src/bhyve/bhyve_parse_command.h new file mode 100644 index 0000000..7ffe26c --- /dev/null +++ b/src/bhyve/bhyve_parse_command.h @@ -0,0 +1,30 @@ +/* + * bhyve_parse_command.h: Bhyve command parser + * + * Copyright (C) 2016 Fabian Freyer + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library. If not, see + * <http://www.gnu.org/licenses/>. + * + * Author: Fabian Freyer <fabian.freyer@xxxxxxxxxxxxxxxxxxx> + */ + +#ifndef __BHYVE_PARSE_COMMAND_H__ +#define __BHYVE_PARSE_COMMAND_H__ + +virDomainDefPtr bhyveParseCommandLineString(const char* nativeConfig, + unsigned caps, + virDomainXMLOptionPtr xmlopt); + +#endif /* __BHYVE_PARSE_COMMAND_H__*/ -- 2.7.0 -- libvir-list mailing list libvir-list@xxxxxxxxxx https://www.redhat.com/mailman/listinfo/libvir-list