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 | 267 ++++++++++++++++++++++++++++++++++++++++ src/bhyve/bhyve_parse_command.h | 30 +++++ 5 files changed, 342 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 f44a501..18a651d 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 275bfc7..7af985c 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -912,6 +912,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 8036661..97bba9a 100644 --- a/src/bhyve/bhyve_driver.c +++ b/src/bhyve/bhyve_driver.c @@ -57,6 +57,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" @@ -1539,6 +1540,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 */ @@ -1592,6 +1633,7 @@ static virHypervisorDriver bhyveHypervisorDriver = { .connectIsAlive = bhyveConnectIsAlive, /* 1.3.5 */ .connectIsSecure = bhyveConnectIsSecure, /* 1.3.5 */ .connectIsEncrypted = bhyveConnectIsEncrypted, /* 1.3.5 */ + .connectDomainXMLFromNative = bhyveConnectDomainXMLFromNative, /* 2.0.1 */ }; diff --git a/src/bhyve/bhyve_parse_command.c b/src/bhyve/bhyve_parse_command.c new file mode 100644 index 0000000..0c4d3f5 --- /dev/null +++ b/src/bhyve/bhyve_parse_command.c @@ -0,0 +1,267 @@ +/* + * 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+1) < 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 +bhyveCommandLineToArgv(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; + } else + /* To prevent a use-after-free here, only free the argument list + * when it is definitely not going to be used */ + virStringFreeList(arglist); + } + + *loader_argv = _loader_argv; + if (!(*bhyve_argv = _bhyve_argv)) + goto error; + + 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. */ + def->virtType = VIR_DOMAIN_VIRT_BHYVE; + if (virUUIDGenerate(def->uuid) < 0) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("failed to generate uuid")); + VIR_FREE(def); + goto cleanup; + } + def->id = -1; + def->clock.offset = VIR_DOMAIN_CLOCK_OFFSET_LOCALTIME; + + if (bhyveCommandLineToArgv(nativeConfig, + &loader_argc, &loader_argv, + &bhyve_argc, &bhyve_argv)) { + virReportError(VIR_ERR_INTERNAL_ERROR, + _("Failed to convert the command string to argv-lists..")); + goto error; + } + +cleanup: + virStringFreeList(loader_argv); + virStringFreeList(bhyve_argv); + return def; +error: + VIR_FREE(def); + goto cleanup; +} 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.5.5
Attachment:
signature.asc
Description: OpenPGP digital signature
-- libvir-list mailing list libvir-list@xxxxxxxxxx https://www.redhat.com/mailman/listinfo/libvir-list