bhyve: implement virConnectDomainXMLFromNative

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@physik.tu-berlin.de>
This commit is contained in:
Fabian Freyer 2016-06-01 09:57:43 +02:00 committed by Roman Bogorodskiy
parent b436a8ae5c
commit 01163b1b1f
5 changed files with 350 additions and 0 deletions

View File

@ -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

View File

@ -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 \

View File

@ -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"
@ -1532,6 +1533,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 */
@ -1585,6 +1625,7 @@ static virHypervisorDriver bhyveHypervisorDriver = {
.connectIsAlive = bhyveConnectIsAlive, /* 1.3.5 */
.connectIsSecure = bhyveConnectIsSecure, /* 1.3.5 */
.connectIsEncrypted = bhyveConnectIsEncrypted, /* 1.3.5 */
.connectDomainXMLFromNative = bhyveConnectDomainXMLFromNative, /* 2.1.0 */
};

View File

@ -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@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;
}

View File

@ -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@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__*/