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(a)physik.tu-berlin.de>
---
po/POTFILES.in | 1 +
src/Makefile.am | 2 +
src/bhyve/bhyve_driver.c | 41 ++++++
src/bhyve/bhyve_parse_command.c | 276 ++++++++++++++++++++++++++++++++++++++++
src/bhyve/bhyve_parse_command.h | 30 +++++
5 files changed, 350 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 ca07582..09e8177 100644
--- a/po/POTFILES.in
+++ b/po/POTFILES.in
@@ -16,6 +16,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 0214995..78c493c 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..c7afe7e 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,45 @@ 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)
+ return NULL;
+
+ capabilities = bhyveDriverGetCapabilities(privconn);
+ if (!capabilities)
+ return NULL;
+
+ 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 +1632,7 @@ static virHypervisorDriver bhyveHypervisorDriver = {
.connectIsAlive = bhyveConnectIsAlive, /* 1.3.5 */
.connectIsSecure = bhyveConnectIsSecure, /* 1.3.5 */
.connectIsEncrypted = bhyveConnectIsEncrypted, /* 1.3.5 */
+ .connectDomainXMLFromNative = bhyveConnectDomainXMLFromNative, /* 2.1.0 */
};
diff --git a/src/bhyve/bhyve_parse_command.c b/src/bhyve/bhyve_parse_command.c
new file mode 100644
index 0000000..e3bc1eb
--- /dev/null
+++ b/src/bhyve/bhyve_parse_command.c
@@ -0,0 +1,276 @@
+/*
+ * 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(a)physik.tu-berlin.de>
+ */
+
+#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 i;
+ 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, "%s",
+ _("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 (i = 0; i < line_count; i++) {
+ curr = lines[i];
+ size_t 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;
+ }
+
+ VIR_FREE(nativeConfig_unescaped);
+
+ /* 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;
+ VIR_FREE(arglist);
+ } 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;
+ VIR_FREE(arglist);
+ } 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"));
+ virDomainDefFree(def);
+ def = NULL;
+ 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, "%s",
+ _("Failed to convert the command string to
argv-lists"));
+ goto error;
+ }
+
+ cleanup:
+ virStringFreeList(loader_argv);
+ virStringFreeList(bhyve_argv);
+ return def;
+ error:
+ virDomainDefFree(def);
+ def = NULL;
+ goto cleanup;
+}
diff --git a/src/bhyve/bhyve_parse_command.h b/src/bhyve/bhyve_parse_command.h
new file mode 100644
index 0000000..1e45ef2
--- /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(a)physik.tu-berlin.de>
+ */
+
+#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