mirror of
https://gitlab.com/libvirt/libvirt.git
synced 2024-11-01 10:51:12 +00:00
c0cf17c280
The bhyve driver only works with the virtio and e1000 models, which we already have in the enum. Some error reporting is slightly downgraded to avoid some subtle usage of modelstr Acked-by: Michal Privoznik <mprivozn@redhat.com> Signed-off-by: Cole Robinson <crobinso@redhat.com>
912 lines
26 KiB
C
912 lines
26 KiB
C
/*
|
|
* bhyve_parse_command.c: Bhyve command parser
|
|
*
|
|
* Copyright (C) 2006-2016 Red Hat, Inc.
|
|
* Copyright (C) 2006 Daniel P. Berrange
|
|
* Copyright (c) 2011 NetApp, Inc.
|
|
* 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/>.
|
|
*/
|
|
|
|
#include <config.h>
|
|
#include <libutil.h>
|
|
#include <getopt_int.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;
|
|
}
|
|
|
|
/*
|
|
* This function is adapted from vm_parse_memsize in
|
|
* /lib/libvmmapi/vmmapi.c in the FreeBSD Source tree.
|
|
*/
|
|
static int
|
|
bhyveParseMemsize(const char *arg, size_t *ret_memsize)
|
|
{
|
|
size_t val;
|
|
int error;
|
|
|
|
if (virStrToLong_ul(arg, NULL, 10, &val) == 0) {
|
|
/*
|
|
* For the sake of backward compatibility if the memory size
|
|
* specified on the command line is less than a megabyte then
|
|
* it is interpreted as being in units of MB.
|
|
*/
|
|
if (val < 1024 * 1024UL)
|
|
val *= 1024 * 1024UL;
|
|
*ret_memsize = val;
|
|
error = 0;
|
|
} else {
|
|
error = expand_number(arg, ret_memsize);
|
|
}
|
|
|
|
/* use memory in KiB here */
|
|
*ret_memsize /= 1024UL;
|
|
|
|
return error;
|
|
}
|
|
|
|
/*
|
|
* 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 */
|
|
virStringListFree(arglist);
|
|
}
|
|
}
|
|
|
|
*loader_argv = _loader_argv;
|
|
if (!(*bhyve_argv = _bhyve_argv))
|
|
goto error;
|
|
|
|
virStringListFree(lines);
|
|
return 0;
|
|
|
|
error:
|
|
VIR_FREE(_loader_argv);
|
|
VIR_FREE(_bhyve_argv);
|
|
virStringListFree(lines);
|
|
return -1;
|
|
}
|
|
|
|
static int
|
|
bhyveParseBhyveLPCArg(virDomainDefPtr def,
|
|
unsigned caps ATTRIBUTE_UNUSED,
|
|
const char *arg)
|
|
{
|
|
/* -l emulation[,config] */
|
|
const char *separator = NULL;
|
|
const char *param = NULL;
|
|
size_t last = 0;
|
|
virDomainChrDefPtr chr = NULL;
|
|
char *type = NULL;
|
|
|
|
separator = strchr(arg, ',');
|
|
param = separator + 1;
|
|
|
|
if (!separator)
|
|
goto error;
|
|
|
|
if (VIR_STRNDUP(type, arg, separator - arg) < 0)
|
|
goto error;
|
|
|
|
/* Only support com%d */
|
|
if (STRPREFIX(type, "com") && type[4] == 0) {
|
|
if (!(chr = virDomainChrDefNew(NULL)))
|
|
goto error;
|
|
|
|
chr->source->type = VIR_DOMAIN_CHR_TYPE_NMDM;
|
|
chr->source->data.nmdm.master = NULL;
|
|
chr->source->data.nmdm.slave = NULL;
|
|
chr->deviceType = VIR_DOMAIN_CHR_DEVICE_TYPE_SERIAL;
|
|
|
|
if (!STRPREFIX(param, "/dev/nmdm")) {
|
|
virReportError(VIR_ERR_OPERATION_FAILED,
|
|
_("Failed to set com port %s: does not start with "
|
|
"'/dev/nmdm'."), type);
|
|
goto error;
|
|
}
|
|
|
|
if (VIR_STRDUP(chr->source->data.nmdm.master, param) < 0) {
|
|
virDomainChrDefFree(chr);
|
|
goto error;
|
|
}
|
|
|
|
if (VIR_STRDUP(chr->source->data.nmdm.slave, chr->source->data.file.path)
|
|
< 0) {
|
|
virDomainChrDefFree(chr);
|
|
goto error;
|
|
}
|
|
|
|
/* If the last character of the master is 'A', the slave will be 'B'
|
|
* and vice versa */
|
|
last = strlen(chr->source->data.nmdm.master) - 1;
|
|
switch (chr->source->data.file.path[last]) {
|
|
case 'A':
|
|
chr->source->data.nmdm.slave[last] = 'B';
|
|
break;
|
|
case 'B':
|
|
chr->source->data.nmdm.slave[last] = 'A';
|
|
break;
|
|
default:
|
|
virReportError(VIR_ERR_OPERATION_FAILED,
|
|
_("Failed to set slave for %s: last letter not "
|
|
"'A' or 'B'"),
|
|
NULLSTR(chr->source->data.nmdm.master));
|
|
goto error;
|
|
}
|
|
|
|
switch (type[3]-'0') {
|
|
case 1:
|
|
case 2:
|
|
chr->target.port = type[3] - '1';
|
|
break;
|
|
default:
|
|
virReportError(VIR_ERR_OPERATION_FAILED,
|
|
_("Failed to parse %s: only com1 and com2"
|
|
" supported."), type);
|
|
goto error;
|
|
break;
|
|
}
|
|
|
|
if (VIR_APPEND_ELEMENT(def->serials, def->nserials, chr) < 0) {
|
|
virDomainChrDefFree(chr);
|
|
goto error;
|
|
}
|
|
}
|
|
|
|
VIR_FREE(type);
|
|
return 0;
|
|
|
|
error:
|
|
virDomainChrDefFree(chr);
|
|
VIR_FREE(type);
|
|
return -1;
|
|
}
|
|
|
|
static int
|
|
bhyveParsePCISlot(const char *slotdef,
|
|
unsigned *pcislot,
|
|
unsigned *bus,
|
|
unsigned *function)
|
|
{
|
|
/* slot[:function] | bus:slot:function */
|
|
const char *curr = NULL;
|
|
const char *next = NULL;
|
|
unsigned values[3];
|
|
size_t i;
|
|
|
|
curr = slotdef;
|
|
for (i = 0; i < 3; i++) {
|
|
char *val = NULL;
|
|
|
|
next = strchr(curr, ':');
|
|
|
|
if (VIR_STRNDUP(val, curr, next? next - curr : -1) < 0)
|
|
goto error;
|
|
|
|
if (virStrToLong_ui(val, NULL, 10, &values[i]) < 0)
|
|
goto error;
|
|
|
|
VIR_FREE(val);
|
|
|
|
if (!next)
|
|
break;
|
|
|
|
curr = next +1;
|
|
}
|
|
|
|
*bus = 0;
|
|
*pcislot = 0;
|
|
*function = 0;
|
|
|
|
switch (i + 1) {
|
|
case 2:
|
|
/* pcislot[:function] */
|
|
*function = values[1];
|
|
case 1:
|
|
*pcislot = values[0];
|
|
break;
|
|
case 3:
|
|
/* bus:pcislot:function */
|
|
*bus = values[0];
|
|
*pcislot = values[1];
|
|
*function = values[2];
|
|
break;
|
|
}
|
|
|
|
return 0;
|
|
error:
|
|
return -1;
|
|
}
|
|
|
|
static int
|
|
bhyveParsePCIDisk(virDomainDefPtr def,
|
|
unsigned caps ATTRIBUTE_UNUSED,
|
|
unsigned pcislot,
|
|
unsigned pcibus,
|
|
unsigned function,
|
|
int bus,
|
|
int device,
|
|
unsigned *nvirtiodisk,
|
|
unsigned *nahcidisk,
|
|
char *config)
|
|
{
|
|
/* -s slot,virtio-blk|ahci-cd|ahci-hd,/path/to/file */
|
|
const char *separator = NULL;
|
|
int idx = -1;
|
|
virDomainDiskDefPtr disk = NULL;
|
|
|
|
if (!(disk = virDomainDiskDefNew(NULL)))
|
|
goto cleanup;
|
|
|
|
disk->bus = bus;
|
|
disk->device = device;
|
|
|
|
disk->info.type = VIR_DOMAIN_DEVICE_ADDRESS_TYPE_PCI;
|
|
disk->info.addr.pci.slot = pcislot;
|
|
disk->info.addr.pci.bus = pcibus;
|
|
disk->info.addr.pci.function = function;
|
|
|
|
if (STRPREFIX(config, "/dev/"))
|
|
disk->src->type = VIR_STORAGE_TYPE_BLOCK;
|
|
else
|
|
disk->src->type = VIR_STORAGE_TYPE_FILE;
|
|
|
|
if (!config)
|
|
goto error;
|
|
|
|
separator = strchr(config, ',');
|
|
if (VIR_STRNDUP(disk->src->path, config,
|
|
separator? separator - config : -1) < 0)
|
|
goto error;
|
|
|
|
if (bus == VIR_DOMAIN_DISK_BUS_VIRTIO) {
|
|
idx = *nvirtiodisk;
|
|
*nvirtiodisk += 1;
|
|
if (VIR_STRDUP(disk->dst, "vda") < 0)
|
|
goto error;
|
|
} else if (bus == VIR_DOMAIN_DISK_BUS_SATA) {
|
|
idx = *nahcidisk;
|
|
*nahcidisk += 1;
|
|
if (VIR_STRDUP(disk->dst, "sda") < 0)
|
|
goto error;
|
|
}
|
|
|
|
if (idx > 'z' - 'a') {
|
|
virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
|
|
_("too many disks"));
|
|
goto error;
|
|
}
|
|
|
|
disk->dst[2] = 'a' + idx;
|
|
|
|
if (VIR_APPEND_ELEMENT(def->disks, def->ndisks, disk) < 0)
|
|
goto error;
|
|
|
|
cleanup:
|
|
return 0;
|
|
|
|
error:
|
|
virDomainDiskDefFree(disk);
|
|
return -1;
|
|
}
|
|
|
|
static int
|
|
bhyveParsePCINet(virDomainDefPtr def,
|
|
virDomainXMLOptionPtr xmlopt,
|
|
unsigned caps ATTRIBUTE_UNUSED,
|
|
unsigned pcislot,
|
|
unsigned pcibus,
|
|
unsigned function,
|
|
int model,
|
|
const char *config)
|
|
{
|
|
/* -s slot,virtio-net,tapN[,mac=xx:xx:xx:xx:xx:xx] */
|
|
|
|
virDomainNetDefPtr net = NULL;
|
|
const char *separator = NULL;
|
|
const char *mac = NULL;
|
|
|
|
if (VIR_ALLOC(net) < 0)
|
|
goto cleanup;
|
|
|
|
/* As we only support interface type='bridge' and cannot
|
|
* guess the actual bridge name from the command line,
|
|
* try to come up with some reasonable defaults */
|
|
net->type = VIR_DOMAIN_NET_TYPE_BRIDGE;
|
|
if (VIR_STRDUP(net->data.bridge.brname, "virbr0") < 0)
|
|
goto error;
|
|
|
|
net->model = model;
|
|
net->info.type = VIR_DOMAIN_DEVICE_ADDRESS_TYPE_PCI;
|
|
net->info.addr.pci.slot = pcislot;
|
|
net->info.addr.pci.bus = pcibus;
|
|
net->info.addr.pci.function = function;
|
|
|
|
if (!config)
|
|
goto error;
|
|
|
|
if (!STRPREFIX(config, "tap")) {
|
|
virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
|
|
_("Only tap devices supported"));
|
|
goto error;
|
|
}
|
|
|
|
separator = strchr(config, ',');
|
|
if (VIR_STRNDUP(net->ifname, config,
|
|
separator? separator - config : -1) < 0)
|
|
goto error;
|
|
|
|
if (!separator)
|
|
goto cleanup;
|
|
|
|
if (!STRPREFIX(++separator, "mac=")) {
|
|
virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
|
|
_("Only mac option can be specified for virt-net"));
|
|
goto error;
|
|
}
|
|
mac = separator + 4;
|
|
|
|
if (virMacAddrParse(mac, &net->mac) < 0) {
|
|
virReportError(VIR_ERR_INTERNAL_ERROR,
|
|
_("unable to parse mac address '%s'"),
|
|
mac);
|
|
goto cleanup;
|
|
}
|
|
|
|
cleanup:
|
|
if (!mac)
|
|
virDomainNetGenerateMAC(xmlopt, &net->mac);
|
|
|
|
if (VIR_APPEND_ELEMENT(def->nets, def->nnets, net) < 0)
|
|
goto error;
|
|
return 0;
|
|
|
|
error:
|
|
virDomainNetDefFree(net);
|
|
return -1;
|
|
}
|
|
|
|
static int
|
|
bhyveParseBhyvePCIArg(virDomainDefPtr def,
|
|
virDomainXMLOptionPtr xmlopt,
|
|
unsigned caps,
|
|
unsigned *nvirtiodisk,
|
|
unsigned *nahcidisk,
|
|
const char *arg)
|
|
{
|
|
/* -s slot,emulation[,conf] */
|
|
const char *separator = NULL;
|
|
char *slotdef = NULL;
|
|
char *emulation = NULL;
|
|
char *conf = NULL;
|
|
unsigned pcislot, bus, function;
|
|
|
|
separator = strchr(arg, ',');
|
|
|
|
if (!separator)
|
|
goto error;
|
|
else
|
|
separator++; /* Skip comma */
|
|
|
|
if (VIR_STRNDUP(slotdef, arg, separator - arg - 1) < 0)
|
|
goto error;
|
|
|
|
conf = strchr(separator+1, ',');
|
|
if (conf)
|
|
conf++; /* Skip initial comma */
|
|
|
|
if (VIR_STRNDUP(emulation, separator, conf? conf - separator - 1 : -1) < 0)
|
|
goto error;
|
|
|
|
if (bhyveParsePCISlot(slotdef, &pcislot, &bus, &function) < 0)
|
|
goto error;
|
|
|
|
if (STREQ(emulation, "ahci-cd"))
|
|
bhyveParsePCIDisk(def, caps, pcislot, bus, function,
|
|
VIR_DOMAIN_DISK_BUS_SATA,
|
|
VIR_DOMAIN_DISK_DEVICE_CDROM,
|
|
nvirtiodisk,
|
|
nahcidisk,
|
|
conf);
|
|
else if (STREQ(emulation, "ahci-hd"))
|
|
bhyveParsePCIDisk(def, caps, pcislot, bus, function,
|
|
VIR_DOMAIN_DISK_BUS_SATA,
|
|
VIR_DOMAIN_DISK_DEVICE_DISK,
|
|
nvirtiodisk,
|
|
nahcidisk,
|
|
conf);
|
|
else if (STREQ(emulation, "virtio-blk"))
|
|
bhyveParsePCIDisk(def, caps, pcislot, bus, function,
|
|
VIR_DOMAIN_DISK_BUS_VIRTIO,
|
|
VIR_DOMAIN_DISK_DEVICE_DISK,
|
|
nvirtiodisk,
|
|
nahcidisk,
|
|
conf);
|
|
else if (STREQ(emulation, "virtio-net"))
|
|
bhyveParsePCINet(def, xmlopt, caps, pcislot, bus, function,
|
|
VIR_DOMAIN_NET_MODEL_VIRTIO, conf);
|
|
else if (STREQ(emulation, "e1000"))
|
|
bhyveParsePCINet(def, xmlopt, caps, pcislot, bus, function,
|
|
VIR_DOMAIN_NET_MODEL_E1000, conf);
|
|
|
|
VIR_FREE(emulation);
|
|
VIR_FREE(slotdef);
|
|
return 0;
|
|
error:
|
|
VIR_FREE(emulation);
|
|
VIR_FREE(slotdef);
|
|
return -1;
|
|
}
|
|
|
|
/*
|
|
* Parse the /usr/sbin/bhyve command line.
|
|
*/
|
|
static int
|
|
bhyveParseBhyveCommandLine(virDomainDefPtr def,
|
|
virDomainXMLOptionPtr xmlopt,
|
|
unsigned caps,
|
|
int argc, char **argv)
|
|
{
|
|
int c;
|
|
const char optstr[] = "abehuwxACHIPSWYp:g:c:s:m:l:U:";
|
|
int vcpus = 1;
|
|
size_t memory = 0;
|
|
unsigned nahcidisks = 0;
|
|
unsigned nvirtiodisks = 0;
|
|
struct _getopt_data *parser;
|
|
|
|
if (!argv)
|
|
goto error;
|
|
|
|
if (VIR_ALLOC(parser) < 0)
|
|
goto error;
|
|
|
|
while ((c = _getopt_internal_r(argc, argv, optstr,
|
|
NULL, NULL, 0, parser, 0)) != -1) {
|
|
switch (c) {
|
|
case 'A':
|
|
def->features[VIR_DOMAIN_FEATURE_ACPI] = VIR_TRISTATE_SWITCH_ON;
|
|
break;
|
|
case 'c':
|
|
if (virStrToLong_i(parser->optarg, NULL, 10, &vcpus) < 0) {
|
|
virReportError(VIR_ERR_OPERATION_FAILED, "%s",
|
|
_("Failed to parse number of vCPUs"));
|
|
goto error;
|
|
}
|
|
if (virDomainDefSetVcpusMax(def, vcpus, xmlopt) < 0)
|
|
goto error;
|
|
if (virDomainDefSetVcpus(def, vcpus) < 0)
|
|
goto error;
|
|
break;
|
|
case 'l':
|
|
if (bhyveParseBhyveLPCArg(def, caps, parser->optarg))
|
|
goto error;
|
|
break;
|
|
case 's':
|
|
if (bhyveParseBhyvePCIArg(def,
|
|
xmlopt,
|
|
caps,
|
|
&nahcidisks,
|
|
&nvirtiodisks,
|
|
parser->optarg))
|
|
goto error;
|
|
break;
|
|
case 'm':
|
|
if (bhyveParseMemsize(parser->optarg, &memory)) {
|
|
virReportError(VIR_ERR_OPERATION_FAILED, "%s",
|
|
_("Failed to parse memory"));
|
|
goto error;
|
|
}
|
|
if (def->mem.cur_balloon != 0 && def->mem.cur_balloon != memory) {
|
|
virReportError(VIR_ERR_OPERATION_FAILED, "%s",
|
|
_("Failed to parse memory: size mismatch"));
|
|
goto error;
|
|
}
|
|
def->mem.cur_balloon = memory;
|
|
virDomainDefSetMemoryTotal(def, memory);
|
|
break;
|
|
case 'I':
|
|
/* While this flag was deprecated in FreeBSD r257423, keep checking
|
|
* for it for backwards compatibility. */
|
|
def->features[VIR_DOMAIN_FEATURE_APIC] = VIR_TRISTATE_SWITCH_ON;
|
|
break;
|
|
case 'u':
|
|
def->clock.offset = VIR_DOMAIN_CLOCK_OFFSET_UTC;
|
|
break;
|
|
case 'U':
|
|
if (virUUIDParse(parser->optarg, def->uuid) < 0) {
|
|
virReportError(VIR_ERR_INTERNAL_ERROR,
|
|
_("Cannot parse UUID '%s'"), parser->optarg);
|
|
goto error;
|
|
}
|
|
break;
|
|
case 'S':
|
|
def->mem.locked = true;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (argc != parser->optind) {
|
|
virReportError(VIR_ERR_OPERATION_FAILED, "%s",
|
|
_("Failed to parse arguments for bhyve command"));
|
|
goto error;
|
|
}
|
|
|
|
if (def->name == NULL) {
|
|
if (VIR_STRDUP(def->name, argv[argc]) < 0)
|
|
goto error;
|
|
} else if (STRNEQ(def->name, argv[argc])) {
|
|
/* the vm name of the loader and the bhyverun command differ, throw an
|
|
* error here */
|
|
virReportError(VIR_ERR_OPERATION_FAILED, "%s",
|
|
_("Failed to parse arguments: VM name mismatch"));
|
|
goto error;
|
|
}
|
|
|
|
VIR_FREE(parser);
|
|
return 0;
|
|
|
|
error:
|
|
VIR_FREE(parser);
|
|
return -1;
|
|
}
|
|
|
|
/*
|
|
* Parse the /usr/sbin/bhyveload command line.
|
|
*/
|
|
static int
|
|
bhyveParseBhyveLoadCommandLine(virDomainDefPtr def,
|
|
int argc, char **argv)
|
|
{
|
|
int c;
|
|
/* bhyveload called with default arguments when only -m and -d are given.
|
|
* Store this in a bit field and check if only those two options are given
|
|
* later */
|
|
unsigned arguments = 0;
|
|
size_t memory = 0;
|
|
struct _getopt_data *parser;
|
|
size_t i = 0;
|
|
int ret = -1;
|
|
|
|
const char optstr[] = "CSc:d:e:h:l:m:";
|
|
|
|
if (!argv)
|
|
goto error;
|
|
|
|
if (VIR_ALLOC(parser) < 0)
|
|
goto error;
|
|
|
|
while ((c = _getopt_internal_r(argc, argv, optstr,
|
|
NULL, NULL, 0, parser, 0)) != -1) {
|
|
switch (c) {
|
|
case 'd':
|
|
arguments |= 1;
|
|
/* Iterate over the disks of the domain trying to match up the
|
|
* source */
|
|
for (i = 0; i < def->ndisks; i++) {
|
|
if (STREQ(virDomainDiskGetSource(def->disks[i]),
|
|
parser->optarg)) {
|
|
def->disks[i]->info.bootIndex = i;
|
|
break;
|
|
}
|
|
}
|
|
break;
|
|
case 'm':
|
|
arguments |= 2;
|
|
if (bhyveParseMemsize(parser->optarg, &memory)) {
|
|
virReportError(VIR_ERR_OPERATION_FAILED, "%s",
|
|
_("Failed to parse memory"));
|
|
goto error;
|
|
}
|
|
if (def->mem.cur_balloon != 0 && def->mem.cur_balloon != memory) {
|
|
virReportError(VIR_ERR_OPERATION_FAILED, "%s",
|
|
_("Failed to parse memory: size mismatch"));
|
|
goto error;
|
|
}
|
|
def->mem.cur_balloon = memory;
|
|
virDomainDefSetMemoryTotal(def, memory);
|
|
break;
|
|
default:
|
|
arguments |= 4;
|
|
}
|
|
}
|
|
|
|
if (arguments != 3) {
|
|
/* Set os.bootloader since virDomainDefFormatInternal will only format
|
|
* the bootloader arguments if os->bootloader is set. */
|
|
if (VIR_STRDUP(def->os.bootloader, argv[0]) < 0)
|
|
goto error;
|
|
|
|
def->os.bootloaderArgs = virStringListJoin((const char**) &argv[1], " ");
|
|
}
|
|
|
|
if (argc != parser->optind) {
|
|
virReportError(VIR_ERR_OPERATION_FAILED, "%s",
|
|
_("Failed to parse arguments for bhyveload command"));
|
|
goto error;
|
|
}
|
|
|
|
if (def->name == NULL) {
|
|
if (VIR_STRDUP(def->name, argv[argc]) < 0)
|
|
goto error;
|
|
} else if (STRNEQ(def->name, argv[argc])) {
|
|
/* the vm name of the loader and the bhyverun command differ, throw an
|
|
* error here */
|
|
virReportError(VIR_ERR_OPERATION_FAILED, "%s",
|
|
_("Failed to parse arguments: VM name mismatch"));
|
|
goto error;
|
|
}
|
|
|
|
ret = 0;
|
|
error:
|
|
VIR_FREE(parser);
|
|
return ret;
|
|
}
|
|
|
|
static int
|
|
bhyveParseCustomLoaderCommandLine(virDomainDefPtr def,
|
|
int argc ATTRIBUTE_UNUSED,
|
|
char **argv)
|
|
{
|
|
if (!argv)
|
|
goto error;
|
|
|
|
if (VIR_STRDUP(def->os.bootloader, argv[0]) < 0)
|
|
goto error;
|
|
|
|
def->os.bootloaderArgs = virStringListJoin((const char**) &argv[1], " ");
|
|
|
|
return 0;
|
|
error:
|
|
return -1;
|
|
}
|
|
|
|
virDomainDefPtr
|
|
bhyveParseCommandLineString(const char* nativeConfig,
|
|
unsigned caps,
|
|
virDomainXMLOptionPtr xmlopt)
|
|
{
|
|
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;
|
|
}
|
|
|
|
if (bhyveParseBhyveCommandLine(def, xmlopt, caps, bhyve_argc, bhyve_argv))
|
|
goto error;
|
|
if (loader_argv && STREQ(loader_argv[0], "/usr/sbin/bhyveload")) {
|
|
if (bhyveParseBhyveLoadCommandLine(def, loader_argc, loader_argv))
|
|
goto error;
|
|
} else if (loader_argv) {
|
|
if (bhyveParseCustomLoaderCommandLine(def, loader_argc, loader_argv))
|
|
goto error;
|
|
}
|
|
|
|
cleanup:
|
|
virStringListFree(loader_argv);
|
|
virStringListFree(bhyve_argv);
|
|
return def;
|
|
error:
|
|
virDomainDefFree(def);
|
|
def = NULL;
|
|
goto cleanup;
|
|
}
|