[libvirt] [PATCH 1/3] bhyve: implement virConnectDomainXMLFromNative

Fabian Freyer fabian.freyer at physik.tu-berlin.de
Wed Jun 1 08:21:37 UTC 2016


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 at physik.tu-berlin.de>
---
 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 at 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) < 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 at 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.7.0




More information about the libvir-list mailing list