libvirt/src/qemu/qemu_parse_command.c
Cole Robinson e6ad2b69ae qemu: parse: drop redundant AddImplicitControllers
PostParse handles it for us now.

This causes some test suite churn; qemu's custom PostParse could is
now invoked before the generic AddImplicitControllers, so PCI
controllers end up sequentially in the XML before the generically
added IDE controllers. So it's just some XML reordering
2016-02-19 09:45:23 -05:00

2740 lines
91 KiB
C

/*
* qemu_parse_command.c: QEMU command parser
*
* Copyright (C) 2006-2016 Red Hat, Inc.
* Copyright (C) 2006 Daniel P. Berrange
*
* 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: Daniel P. Berrange <berrange@redhat.com>
*/
#include <config.h>
#include "qemu_command.h"
#include "qemu_parse_command.h"
#include "dirname.h"
#include "viralloc.h"
#include "virlog.h"
#include "virstring.h"
#include "c-ctype.h"
#include "secret_conf.h"
#define VIR_FROM_THIS VIR_FROM_QEMU
VIR_LOG_INIT("qemu.qemu_parse_command");
static int
qemuParseRBDString(virDomainDiskDefPtr disk)
{
char *source = disk->src->path;
int ret;
disk->src->path = NULL;
ret = virStorageSourceParseRBDColonString(source, disk->src);
VIR_FREE(source);
return ret;
}
static int
qemuParseDriveURIString(virDomainDiskDefPtr def, virURIPtr uri,
const char *scheme)
{
int ret = -1;
char *transp = NULL;
char *sock = NULL;
char *volimg = NULL;
char *secret = NULL;
virStorageAuthDefPtr authdef = NULL;
if (VIR_ALLOC(def->src->hosts) < 0)
goto error;
transp = strchr(uri->scheme, '+');
if (transp)
*transp++ = 0;
if (STRNEQ(uri->scheme, scheme)) {
virReportError(VIR_ERR_INTERNAL_ERROR,
_("Invalid transport/scheme '%s'"), uri->scheme);
goto error;
}
if (!transp) {
def->src->hosts->transport = VIR_STORAGE_NET_HOST_TRANS_TCP;
} else {
def->src->hosts->transport = virStorageNetHostTransportTypeFromString(transp);
if (def->src->hosts->transport < 0) {
virReportError(VIR_ERR_INTERNAL_ERROR,
_("Invalid %s transport type '%s'"), scheme, transp);
goto error;
}
}
def->src->nhosts = 0; /* set to 1 once everything succeeds */
if (def->src->hosts->transport != VIR_STORAGE_NET_HOST_TRANS_UNIX) {
if (VIR_STRDUP(def->src->hosts->name, uri->server) < 0)
goto error;
if (virAsprintf(&def->src->hosts->port, "%d", uri->port) < 0)
goto error;
} else {
def->src->hosts->name = NULL;
def->src->hosts->port = 0;
if (uri->query) {
if (STRPREFIX(uri->query, "socket=")) {
sock = strchr(uri->query, '=') + 1;
if (VIR_STRDUP(def->src->hosts->socket, sock) < 0)
goto error;
} else {
virReportError(VIR_ERR_INTERNAL_ERROR,
_("Invalid query parameter '%s'"), uri->query);
goto error;
}
}
}
if (uri->path) {
volimg = uri->path + 1; /* skip the prefix slash */
VIR_FREE(def->src->path);
if (VIR_STRDUP(def->src->path, volimg) < 0)
goto error;
} else {
VIR_FREE(def->src->path);
}
if (uri->user) {
const char *secrettype;
/* formulate authdef for disk->src->auth */
if (VIR_ALLOC(authdef) < 0)
goto error;
secret = strchr(uri->user, ':');
if (secret)
*secret = '\0';
if (VIR_STRDUP(authdef->username, uri->user) < 0)
goto error;
if (STREQ(scheme, "iscsi")) {
secrettype =
virSecretUsageTypeToString(VIR_SECRET_USAGE_TYPE_ISCSI);
if (VIR_STRDUP(authdef->secrettype, secrettype) < 0)
goto error;
}
def->src->auth = authdef;
authdef = NULL;
/* Cannot formulate a secretType (eg, usage or uuid) given
* what is provided.
*/
}
def->src->nhosts = 1;
ret = 0;
cleanup:
virURIFree(uri);
return ret;
error:
virStorageNetHostDefClear(def->src->hosts);
VIR_FREE(def->src->hosts);
virStorageAuthDefFree(authdef);
goto cleanup;
}
static int
qemuParseGlusterString(virDomainDiskDefPtr def)
{
virURIPtr uri = NULL;
if (!(uri = virURIParse(def->src->path)))
return -1;
return qemuParseDriveURIString(def, uri, "gluster");
}
static int
qemuParseISCSIString(virDomainDiskDefPtr def)
{
virURIPtr uri = NULL;
char *slash;
unsigned lun;
if (!(uri = virURIParse(def->src->path)))
return -1;
if (uri->path &&
(slash = strchr(uri->path + 1, '/')) != NULL) {
if (slash[1] == '\0') {
*slash = '\0';
} else if (virStrToLong_ui(slash + 1, NULL, 10, &lun) == -1) {
virReportError(VIR_ERR_INTERNAL_ERROR,
_("invalid name '%s' for iSCSI disk"),
def->src->path);
virURIFree(uri);
return -1;
}
}
return qemuParseDriveURIString(def, uri, "iscsi");
}
static int
qemuParseNBDString(virDomainDiskDefPtr disk)
{
virStorageNetHostDefPtr h = NULL;
char *host, *port;
char *src;
virURIPtr uri = NULL;
if (strstr(disk->src->path, "://")) {
if (!(uri = virURIParse(disk->src->path)))
return -1;
return qemuParseDriveURIString(disk, uri, "nbd");
}
if (VIR_ALLOC(h) < 0)
goto error;
host = disk->src->path + strlen("nbd:");
if (STRPREFIX(host, "unix:/")) {
src = strchr(host + strlen("unix:"), ':');
if (src)
*src++ = '\0';
h->transport = VIR_STORAGE_NET_HOST_TRANS_UNIX;
if (VIR_STRDUP(h->socket, host + strlen("unix:")) < 0)
goto error;
} else {
port = strchr(host, ':');
if (!port) {
virReportError(VIR_ERR_INTERNAL_ERROR,
_("cannot parse nbd filename '%s'"), disk->src->path);
goto error;
}
*port++ = '\0';
if (VIR_STRDUP(h->name, host) < 0)
goto error;
src = strchr(port, ':');
if (src)
*src++ = '\0';
if (VIR_STRDUP(h->port, port) < 0)
goto error;
}
if (src && STRPREFIX(src, "exportname=")) {
if (VIR_STRDUP(src, strchr(src, '=') + 1) < 0)
goto error;
} else {
src = NULL;
}
VIR_FREE(disk->src->path);
disk->src->path = src;
disk->src->nhosts = 1;
disk->src->hosts = h;
return 0;
error:
virStorageNetHostDefClear(h);
VIR_FREE(h);
return -1;
}
/*
* This method takes a string representing a QEMU command line ARGV set
* optionally prefixed by a list of environment variables. It then tries
* to split it up into a NULL terminated list of env & argv, splitting
* on space
*/
static int qemuStringToArgvEnv(const char *args,
char ***retenv,
char ***retargv)
{
char **arglist = NULL;
size_t argcount = 0;
size_t argalloc = 0;
size_t envend;
size_t i;
const char *curr = args;
const char *start;
char **progenv = NULL;
char **progargv = NULL;
/* Iterate over string, splitting on sequences of ' ' */
while (curr && *curr != '\0') {
char *arg;
const char *next;
start = curr;
/* accept a space in CEPH_ARGS */
if (STRPREFIX(curr, "CEPH_ARGS=-m "))
start += strlen("CEPH_ARGS=-m ");
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 (!next)
next = strchr(curr, '\n');
if (VIR_STRNDUP(arg, curr, next ? next - curr : -1) < 0)
goto error;
if (next && (*next == '\'' || *next == '"'))
next++;
if (VIR_RESIZE_N(arglist, argalloc, argcount, 2) < 0) {
VIR_FREE(arg);
goto error;
}
arglist[argcount++] = arg;
arglist[argcount] = NULL;
while (next && c_isspace(*next))
next++;
curr = next;
}
/* Iterate over list of args, finding first arg not containing
* the '=' character (eg, skip over env vars FOO=bar) */
for (envend = 0; ((envend < argcount) &&
(strchr(arglist[envend], '=') != NULL));
envend++)
; /* nada */
/* Copy the list of env vars */
if (envend > 0) {
if (VIR_REALLOC_N(progenv, envend+1) < 0)
goto error;
for (i = 0; i < envend; i++)
progenv[i] = arglist[i];
progenv[i] = NULL;
}
/* Copy the list of argv */
if (VIR_REALLOC_N(progargv, argcount-envend + 1) < 0)
goto error;
for (i = envend; i < argcount; i++)
progargv[i-envend] = arglist[i];
progargv[i-envend] = NULL;
VIR_FREE(arglist);
*retenv = progenv;
*retargv = progargv;
return 0;
error:
VIR_FREE(progenv);
VIR_FREE(progargv);
virStringFreeList(arglist);
return -1;
}
/*
* Search for a named env variable, and return the value part
*/
static const char *qemuFindEnv(char **progenv,
const char *name)
{
size_t i;
int len = strlen(name);
for (i = 0; progenv && progenv[i]; i++) {
if (STREQLEN(progenv[i], name, len) &&
progenv[i][len] == '=')
return progenv[i] + len + 1;
}
return NULL;
}
/*
* Takes a string containing a set of key=value,key=value,key...
* parameters and splits them up, returning two arrays with
* the individual keys and values. If allowEmptyValue is nonzero,
* the "=value" part is optional and if a key with no value is found,
* NULL is be placed into corresponding place in retvalues.
*/
int
qemuParseKeywords(const char *str,
char ***retkeywords,
char ***retvalues,
int *retnkeywords,
int allowEmptyValue)
{
int keywordCount = 0;
int keywordAlloc = 0;
char **keywords = NULL;
char **values = NULL;
const char *start = str;
const char *end;
size_t i;
*retkeywords = NULL;
*retvalues = NULL;
*retnkeywords = 0;
end = start + strlen(str);
while (start) {
const char *separator;
const char *endmark;
char *keyword;
char *value = NULL;
endmark = start;
do {
/* Qemu accepts ',,' as an escape for a literal comma;
* skip past those here while searching for the end of the
* value, then strip them down below */
endmark = strchr(endmark, ',');
} while (endmark && endmark[1] == ',' && (endmark += 2));
if (!endmark)
endmark = end;
if (!(separator = strchr(start, '=')))
separator = end;
if (separator >= endmark) {
if (!allowEmptyValue) {
virReportError(VIR_ERR_INTERNAL_ERROR,
_("malformed keyword arguments in '%s'"), str);
goto error;
}
separator = endmark;
}
if (VIR_STRNDUP(keyword, start, separator - start) < 0)
goto error;
if (separator < endmark) {
separator++;
if (VIR_STRNDUP(value, separator, endmark - separator) < 0) {
VIR_FREE(keyword);
goto error;
}
if (strchr(value, ',')) {
char *p = strchr(value, ',') + 1;
char *q = p + 1;
while (*q) {
if (*q == ',')
q++;
*p++ = *q++;
}
*p = '\0';
}
}
if (keywordAlloc == keywordCount) {
if (VIR_REALLOC_N(keywords, keywordAlloc + 10) < 0 ||
VIR_REALLOC_N(values, keywordAlloc + 10) < 0) {
VIR_FREE(keyword);
VIR_FREE(value);
goto error;
}
keywordAlloc += 10;
}
keywords[keywordCount] = keyword;
values[keywordCount] = value;
keywordCount++;
start = endmark < end ? endmark + 1 : NULL;
}
*retkeywords = keywords;
*retvalues = values;
*retnkeywords = keywordCount;
return 0;
error:
for (i = 0; i < keywordCount; i++) {
VIR_FREE(keywords[i]);
VIR_FREE(values[i]);
}
VIR_FREE(keywords);
VIR_FREE(values);
return -1;
}
/* qemuParseCommandLineVnc
*
* Tries to parse the various "-vnc ..." argument formats.
*/
static int
qemuParseCommandLineVnc(virDomainDefPtr def,
const char *val)
{
int ret = -1;
virDomainGraphicsDefPtr vnc = NULL;
char *tmp;
if (VIR_ALLOC(vnc) < 0)
goto cleanup;
vnc->type = VIR_DOMAIN_GRAPHICS_TYPE_VNC;
if (STRPREFIX(val, "unix:")) {
/* -vnc unix:/some/big/path */
if (VIR_STRDUP(vnc->data.vnc.socket, val + 5) < 0)
goto cleanup;
} else {
/*
* -vnc 127.0.0.1:4
* -vnc [2001:1:2:3:4:5:1234:1234]:4
* -vnc some.host.name:4
*/
char *opts;
char *port;
const char *sep = ":";
if (val[0] == '[')
sep = "]:";
tmp = strstr(val, sep);
if (!tmp) {
virReportError(VIR_ERR_INTERNAL_ERROR,
_("missing VNC port number in '%s'"), val);
goto cleanup;
}
port = tmp + strlen(sep);
if (virStrToLong_i(port, &opts, 10,
&vnc->data.vnc.port) < 0) {
virReportError(VIR_ERR_INTERNAL_ERROR,
_("cannot parse VNC port '%s'"), port);
goto cleanup;
}
if (val[0] == '[')
val++;
if (virDomainGraphicsListenSetAddress(vnc, 0, val, tmp-val, true) < 0)
goto cleanup;
if (*opts == ',') {
char *orig_opts;
if (VIR_STRDUP(orig_opts, opts + 1) < 0)
goto cleanup;
opts = orig_opts;
while (opts && *opts) {
char *nextopt = strchr(opts, ',');
if (nextopt)
*(nextopt++) = '\0';
if (STRPREFIX(opts, "websocket")) {
char *websocket = opts + strlen("websocket");
if (*(websocket++) == '=' &&
*websocket) {
/* If the websocket continues with
* '=<something>', we'll parse it */
if (virStrToLong_i(websocket,
NULL, 0,
&vnc->data.vnc.websocket) < 0) {
virReportError(VIR_ERR_INTERNAL_ERROR,
_("cannot parse VNC "
"WebSocket port '%s'"),
websocket);
VIR_FREE(orig_opts);
goto cleanup;
}
} else {
/* Otherwise, we'll compute the port the same
* way QEMU does, by adding a 5700 to the
* display value. */
vnc->data.vnc.websocket =
vnc->data.vnc.port + 5700;
}
} else if (STRPREFIX(opts, "share=")) {
char *sharePolicy = opts + strlen("share=");
if (sharePolicy && *sharePolicy) {
int policy =
virDomainGraphicsVNCSharePolicyTypeFromString(sharePolicy);
if (policy < 0) {
virReportError(VIR_ERR_INTERNAL_ERROR,
_("unknown vnc display sharing policy '%s'"),
sharePolicy);
VIR_FREE(orig_opts);
goto cleanup;
} else {
vnc->data.vnc.sharePolicy = policy;
}
} else {
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
_("missing vnc sharing policy"));
VIR_FREE(orig_opts);
goto cleanup;
}
}
opts = nextopt;
}
VIR_FREE(orig_opts);
}
vnc->data.vnc.port += 5900;
vnc->data.vnc.autoport = false;
}
if (VIR_APPEND_ELEMENT(def->graphics, def->ngraphics, vnc) < 0)
goto cleanup;
ret = 0;
cleanup:
virDomainGraphicsDefFree(vnc);
return ret;
}
/*
* Tries to parse new style QEMU -drive args.
*
* eg -drive file=/dev/HostVG/VirtData1,if=ide,index=1
*
* Will fail if not using the 'index' keyword
*/
static virDomainDiskDefPtr
qemuParseCommandLineDisk(virDomainXMLOptionPtr xmlopt,
const char *val,
virDomainDefPtr dom,
int nvirtiodisk,
bool old_style_ceph_args)
{
virDomainDiskDefPtr def = NULL;
char **keywords;
char **values;
int nkeywords;
size_t i;
int idx = -1;
int busid = -1;
int unitid = -1;
if (qemuParseKeywords(val,
&keywords,
&values,
&nkeywords,
0) < 0)
return NULL;
if (VIR_ALLOC(def) < 0)
goto cleanup;
if (VIR_ALLOC(def->src) < 0)
goto error;
if ((ARCH_IS_PPC64(dom->os.arch) &&
dom->os.machine && STRPREFIX(dom->os.machine, "pseries")))
def->bus = VIR_DOMAIN_DISK_BUS_SCSI;
else
def->bus = VIR_DOMAIN_DISK_BUS_IDE;
def->device = VIR_DOMAIN_DISK_DEVICE_DISK;
def->src->type = VIR_STORAGE_TYPE_FILE;
for (i = 0; i < nkeywords; i++) {
if (STREQ(keywords[i], "file")) {
if (values[i] && STRNEQ(values[i], "")) {
def->src->path = values[i];
values[i] = NULL;
if (STRPREFIX(def->src->path, "/dev/"))
def->src->type = VIR_STORAGE_TYPE_BLOCK;
else if (STRPREFIX(def->src->path, "nbd:") ||
STRPREFIX(def->src->path, "nbd+")) {
def->src->type = VIR_STORAGE_TYPE_NETWORK;
def->src->protocol = VIR_STORAGE_NET_PROTOCOL_NBD;
if (qemuParseNBDString(def) < 0)
goto error;
} else if (STRPREFIX(def->src->path, "rbd:")) {
char *p = def->src->path;
def->src->type = VIR_STORAGE_TYPE_NETWORK;
def->src->protocol = VIR_STORAGE_NET_PROTOCOL_RBD;
if (VIR_STRDUP(def->src->path, p + strlen("rbd:")) < 0)
goto error;
/* old-style CEPH_ARGS env variable is parsed later */
if (!old_style_ceph_args && qemuParseRBDString(def) < 0) {
VIR_FREE(p);
goto error;
}
VIR_FREE(p);
} else if (STRPREFIX(def->src->path, "gluster:") ||
STRPREFIX(def->src->path, "gluster+")) {
def->src->type = VIR_STORAGE_TYPE_NETWORK;
def->src->protocol = VIR_STORAGE_NET_PROTOCOL_GLUSTER;
if (qemuParseGlusterString(def) < 0)
goto error;
} else if (STRPREFIX(def->src->path, "iscsi:")) {
def->src->type = VIR_STORAGE_TYPE_NETWORK;
def->src->protocol = VIR_STORAGE_NET_PROTOCOL_ISCSI;
if (qemuParseISCSIString(def) < 0)
goto error;
} else if (STRPREFIX(def->src->path, "sheepdog:")) {
char *p = def->src->path;
char *port, *vdi;
def->src->type = VIR_STORAGE_TYPE_NETWORK;
def->src->protocol = VIR_STORAGE_NET_PROTOCOL_SHEEPDOG;
if (VIR_STRDUP(def->src->path, p + strlen("sheepdog:")) < 0)
goto error;
VIR_FREE(p);
/* def->src->path must be [vdiname] or [host]:[port]:[vdiname] */
port = strchr(def->src->path, ':');
if (port) {
*port = '\0';
vdi = strchr(port + 1, ':');
if (!vdi) {
*port = ':';
virReportError(VIR_ERR_INTERNAL_ERROR,
_("cannot parse sheepdog filename '%s'"),
def->src->path);
goto error;
}
port++;
*vdi++ = '\0';
if (VIR_ALLOC(def->src->hosts) < 0)
goto error;
def->src->nhosts = 1;
def->src->hosts->name = def->src->path;
if (VIR_STRDUP(def->src->hosts->port, port) < 0)
goto error;
def->src->hosts->transport = VIR_STORAGE_NET_HOST_TRANS_TCP;
def->src->hosts->socket = NULL;
if (VIR_STRDUP(def->src->path, vdi) < 0)
goto error;
}
} else {
def->src->type = VIR_STORAGE_TYPE_FILE;
}
} else {
def->src->type = VIR_STORAGE_TYPE_FILE;
}
} else if (STREQ(keywords[i], "if")) {
if (STREQ(values[i], "ide")) {
def->bus = VIR_DOMAIN_DISK_BUS_IDE;
if ((ARCH_IS_PPC64(dom->os.arch) &&
dom->os.machine && STRPREFIX(dom->os.machine, "pseries"))) {
virReportError(VIR_ERR_INTERNAL_ERROR,
_("pseries systems do not support ide devices '%s'"), val);
goto error;
}
} else if (STREQ(values[i], "scsi")) {
def->bus = VIR_DOMAIN_DISK_BUS_SCSI;
} else if (STREQ(values[i], "floppy")) {
def->bus = VIR_DOMAIN_DISK_BUS_FDC;
def->device = VIR_DOMAIN_DISK_DEVICE_FLOPPY;
} else if (STREQ(values[i], "virtio")) {
def->bus = VIR_DOMAIN_DISK_BUS_VIRTIO;
} else if (STREQ(values[i], "xen")) {
def->bus = VIR_DOMAIN_DISK_BUS_XEN;
} else if (STREQ(values[i], "sd")) {
def->bus = VIR_DOMAIN_DISK_BUS_SD;
}
} else if (STREQ(keywords[i], "media")) {
if (STREQ(values[i], "cdrom")) {
def->device = VIR_DOMAIN_DISK_DEVICE_CDROM;
def->src->readonly = true;
} else if (STREQ(values[i], "floppy")) {
def->device = VIR_DOMAIN_DISK_DEVICE_FLOPPY;
}
} else if (STREQ(keywords[i], "format")) {
if (VIR_STRDUP(def->src->driverName, "qemu") < 0)
goto error;
def->src->format = virStorageFileFormatTypeFromString(values[i]);
} else if (STREQ(keywords[i], "cache")) {
if (STREQ(values[i], "off") ||
STREQ(values[i], "none"))
def->cachemode = VIR_DOMAIN_DISK_CACHE_DISABLE;
else if (STREQ(values[i], "writeback") ||
STREQ(values[i], "on"))
def->cachemode = VIR_DOMAIN_DISK_CACHE_WRITEBACK;
else if (STREQ(values[i], "writethrough"))
def->cachemode = VIR_DOMAIN_DISK_CACHE_WRITETHRU;
else if (STREQ(values[i], "directsync"))
def->cachemode = VIR_DOMAIN_DISK_CACHE_DIRECTSYNC;
else if (STREQ(values[i], "unsafe"))
def->cachemode = VIR_DOMAIN_DISK_CACHE_UNSAFE;
} else if (STREQ(keywords[i], "werror")) {
if (STREQ(values[i], "stop"))
def->error_policy = VIR_DOMAIN_DISK_ERROR_POLICY_STOP;
else if (STREQ(values[i], "report"))
def->error_policy = VIR_DOMAIN_DISK_ERROR_POLICY_REPORT;
else if (STREQ(values[i], "ignore"))
def->error_policy = VIR_DOMAIN_DISK_ERROR_POLICY_IGNORE;
else if (STREQ(values[i], "enospc"))
def->error_policy = VIR_DOMAIN_DISK_ERROR_POLICY_ENOSPACE;
} else if (STREQ(keywords[i], "rerror")) {
if (STREQ(values[i], "stop"))
def->rerror_policy = VIR_DOMAIN_DISK_ERROR_POLICY_STOP;
else if (STREQ(values[i], "report"))
def->rerror_policy = VIR_DOMAIN_DISK_ERROR_POLICY_REPORT;
else if (STREQ(values[i], "ignore"))
def->rerror_policy = VIR_DOMAIN_DISK_ERROR_POLICY_IGNORE;
} else if (STREQ(keywords[i], "index")) {
if (virStrToLong_i(values[i], NULL, 10, &idx) < 0) {
virReportError(VIR_ERR_INTERNAL_ERROR,
_("cannot parse drive index '%s'"), val);
goto error;
}
} else if (STREQ(keywords[i], "bus")) {
if (virStrToLong_i(values[i], NULL, 10, &busid) < 0) {
virReportError(VIR_ERR_INTERNAL_ERROR,
_("cannot parse drive bus '%s'"), val);
goto error;
}
} else if (STREQ(keywords[i], "unit")) {
if (virStrToLong_i(values[i], NULL, 10, &unitid) < 0) {
virReportError(VIR_ERR_INTERNAL_ERROR,
_("cannot parse drive unit '%s'"), val);
goto error;
}
} else if (STREQ(keywords[i], "readonly")) {
if ((values[i] == NULL) || STREQ(values[i], "on"))
def->src->readonly = true;
} else if (STREQ(keywords[i], "aio")) {
if ((def->iomode = virDomainDiskIoTypeFromString(values[i])) < 0) {
virReportError(VIR_ERR_INTERNAL_ERROR,
_("cannot parse io mode '%s'"), values[i]);
goto error;
}
} else if (STREQ(keywords[i], "cyls")) {
if (virStrToLong_ui(values[i], NULL, 10,
&(def->geometry.cylinders)) < 0) {
virDomainDiskDefFree(def);
def = NULL;
virReportError(VIR_ERR_INTERNAL_ERROR,
_("cannot parse cylinders value'%s'"),
values[i]);
goto error;
}
} else if (STREQ(keywords[i], "heads")) {
if (virStrToLong_ui(values[i], NULL, 10,
&(def->geometry.heads)) < 0) {
virDomainDiskDefFree(def);
def = NULL;
virReportError(VIR_ERR_INTERNAL_ERROR,
_("cannot parse heads value'%s'"),
values[i]);
goto error;
}
} else if (STREQ(keywords[i], "secs")) {
if (virStrToLong_ui(values[i], NULL, 10,
&(def->geometry.sectors)) < 0) {
virDomainDiskDefFree(def);
def = NULL;
virReportError(VIR_ERR_INTERNAL_ERROR,
_("cannot parse sectors value'%s'"),
values[i]);
goto error;
}
} else if (STREQ(keywords[i], "trans")) {
def->geometry.trans =
virDomainDiskGeometryTransTypeFromString(values[i]);
if ((def->geometry.trans < VIR_DOMAIN_DISK_TRANS_DEFAULT) ||
(def->geometry.trans >= VIR_DOMAIN_DISK_TRANS_LAST)) {
virDomainDiskDefFree(def);
def = NULL;
virReportError(VIR_ERR_INTERNAL_ERROR,
_("cannot parse translation value '%s'"),
values[i]);
goto error;
}
}
}
if (def->rerror_policy == def->error_policy)
def->rerror_policy = 0;
if (!def->src->path &&
def->device == VIR_DOMAIN_DISK_DEVICE_DISK &&
def->src->type != VIR_STORAGE_TYPE_NETWORK) {
virReportError(VIR_ERR_INTERNAL_ERROR,
_("missing file parameter in drive '%s'"), val);
goto error;
}
if (idx == -1 &&
def->bus == VIR_DOMAIN_DISK_BUS_VIRTIO)
idx = nvirtiodisk;
if (idx == -1 &&
unitid == -1 &&
busid == -1) {
virReportError(VIR_ERR_INTERNAL_ERROR,
_("missing index/unit/bus parameter in drive '%s'"),
val);
goto error;
}
if (idx == -1) {
if (unitid == -1)
unitid = 0;
if (busid == -1)
busid = 0;
switch (def->bus) {
case VIR_DOMAIN_DISK_BUS_IDE:
idx = (busid * 2) + unitid;
break;
case VIR_DOMAIN_DISK_BUS_SCSI:
idx = (busid * 7) + unitid;
break;
default:
idx = unitid;
break;
}
}
if (def->bus == VIR_DOMAIN_DISK_BUS_IDE) {
ignore_value(VIR_STRDUP(def->dst, "hda"));
} else if (def->bus == VIR_DOMAIN_DISK_BUS_SCSI ||
def->bus == VIR_DOMAIN_DISK_BUS_SD) {
ignore_value(VIR_STRDUP(def->dst, "sda"));
} else if (def->bus == VIR_DOMAIN_DISK_BUS_VIRTIO) {
ignore_value(VIR_STRDUP(def->dst, "vda"));
} else if (def->bus == VIR_DOMAIN_DISK_BUS_XEN) {
ignore_value(VIR_STRDUP(def->dst, "xvda"));
} else if (def->bus == VIR_DOMAIN_DISK_BUS_FDC) {
ignore_value(VIR_STRDUP(def->dst, "fda"));
} else {
ignore_value(VIR_STRDUP(def->dst, "hda"));
}
if (!def->dst)
goto error;
if (STREQ(def->dst, "xvda"))
def->dst[3] = 'a' + idx;
else
def->dst[2] = 'a' + idx;
if (virDomainDiskDefAssignAddress(xmlopt, def, dom) < 0) {
virReportError(VIR_ERR_INTERNAL_ERROR,
_("invalid device name '%s'"), def->dst);
virDomainDiskDefFree(def);
def = NULL;
goto cleanup;
}
cleanup:
for (i = 0; i < nkeywords; i++) {
VIR_FREE(keywords[i]);
VIR_FREE(values[i]);
}
VIR_FREE(keywords);
VIR_FREE(values);
return def;
error:
virDomainDiskDefFree(def);
def = NULL;
goto cleanup;
}
/*
* Tries to find a NIC definition matching a vlan we want
*/
static const char *
qemuFindNICForVLAN(int nnics,
const char **nics,
int wantvlan)
{
size_t i;
for (i = 0; i < nnics; i++) {
int gotvlan;
const char *tmp = strstr(nics[i], "vlan=");
char *end;
if (!tmp)
continue;
tmp += strlen("vlan=");
if (virStrToLong_i(tmp, &end, 10, &gotvlan) < 0) {
virReportError(VIR_ERR_INTERNAL_ERROR,
_("cannot parse NIC vlan in '%s'"), nics[i]);
return NULL;
}
if (gotvlan == wantvlan)
return nics[i];
}
if (wantvlan == 0 && nnics > 0)
return nics[0];
virReportError(VIR_ERR_INTERNAL_ERROR,
_("cannot find NIC definition for vlan %d"), wantvlan);
return NULL;
}
/*
* Tries to parse a QEMU -net backend argument. Gets given
* a list of all known -net frontend arguments to try and
* match up against. Horribly complicated stuff
*/
static virDomainNetDefPtr
qemuParseCommandLineNet(virDomainXMLOptionPtr xmlopt,
const char *val,
int nnics,
const char **nics)
{
virDomainNetDefPtr def = NULL;
char **keywords = NULL;
char **values = NULL;
int nkeywords;
const char *nic;
int wantvlan = 0;
const char *tmp;
bool genmac = true;
size_t i;
tmp = strchr(val, ',');
if (tmp) {
if (qemuParseKeywords(tmp+1,
&keywords,
&values,
&nkeywords,
0) < 0)
return NULL;
} else {
nkeywords = 0;
}
if (VIR_ALLOC(def) < 0)
goto cleanup;
/* 'tap' could turn into libvirt type=ethernet, type=bridge or
* type=network, but we can't tell, so use the generic config */
if (STRPREFIX(val, "tap,"))
def->type = VIR_DOMAIN_NET_TYPE_ETHERNET;
else if (STRPREFIX(val, "socket"))
def->type = VIR_DOMAIN_NET_TYPE_CLIENT;
else if (STRPREFIX(val, "user"))
def->type = VIR_DOMAIN_NET_TYPE_USER;
else
def->type = VIR_DOMAIN_NET_TYPE_ETHERNET;
for (i = 0; i < nkeywords; i++) {
if (STREQ(keywords[i], "vlan")) {
if (virStrToLong_i(values[i], NULL, 10, &wantvlan) < 0) {
virReportError(VIR_ERR_INTERNAL_ERROR,
_("cannot parse vlan in '%s'"), val);
virDomainNetDefFree(def);
def = NULL;
goto cleanup;
}
} else if (def->type == VIR_DOMAIN_NET_TYPE_ETHERNET &&
STREQ(keywords[i], "script") && STRNEQ(values[i], "")) {
def->script = values[i];
values[i] = NULL;
} else if (def->type == VIR_DOMAIN_NET_TYPE_ETHERNET &&
STREQ(keywords[i], "ifname")) {
def->ifname = values[i];
values[i] = NULL;
}
}
/* Done parsing the nic backend. Now to try and find corresponding
* frontend, based off vlan number. NB this assumes a 1-1 mapping
*/
nic = qemuFindNICForVLAN(nnics, nics, wantvlan);
if (!nic) {
virDomainNetDefFree(def);
def = NULL;
goto cleanup;
}
if (!STRPREFIX(nic, "nic")) {
virReportError(VIR_ERR_INTERNAL_ERROR,
_("cannot parse NIC definition '%s'"), nic);
virDomainNetDefFree(def);
def = NULL;
goto cleanup;
}
for (i = 0; i < nkeywords; i++) {
VIR_FREE(keywords[i]);
VIR_FREE(values[i]);
}
VIR_FREE(keywords);
VIR_FREE(values);
if (STRPREFIX(nic, "nic,")) {
if (qemuParseKeywords(nic + strlen("nic,"),
&keywords,
&values,
&nkeywords,
0) < 0) {
virDomainNetDefFree(def);
def = NULL;
goto cleanup;
}
} else {
nkeywords = 0;
}
for (i = 0; i < nkeywords; i++) {
if (STREQ(keywords[i], "macaddr")) {
genmac = false;
if (virMacAddrParse(values[i], &def->mac) < 0) {
virReportError(VIR_ERR_INTERNAL_ERROR,
_("unable to parse mac address '%s'"),
values[i]);
virDomainNetDefFree(def);
def = NULL;
goto cleanup;
}
} else if (STREQ(keywords[i], "model")) {
def->model = values[i];
values[i] = NULL;
} else if (STREQ(keywords[i], "vhost")) {
if ((values[i] == NULL) || STREQ(values[i], "on")) {
def->driver.virtio.name = VIR_DOMAIN_NET_BACKEND_TYPE_VHOST;
} else if (STREQ(keywords[i], "off")) {
def->driver.virtio.name = VIR_DOMAIN_NET_BACKEND_TYPE_QEMU;
}
} else if (STREQ(keywords[i], "sndbuf") && values[i]) {
if (virStrToLong_ul(values[i], NULL, 10, &def->tune.sndbuf) < 0) {
virReportError(VIR_ERR_INTERNAL_ERROR,
_("cannot parse sndbuf size in '%s'"), val);
virDomainNetDefFree(def);
def = NULL;
goto cleanup;
}
def->tune.sndbuf_specified = true;
}
}
if (genmac)
virDomainNetGenerateMAC(xmlopt, &def->mac);
cleanup:
for (i = 0; i < nkeywords; i++) {
VIR_FREE(keywords[i]);
VIR_FREE(values[i]);
}
VIR_FREE(keywords);
VIR_FREE(values);
return def;
}
/*
* Tries to parse a QEMU PCI device
*/
static virDomainHostdevDefPtr
qemuParseCommandLinePCI(const char *val)
{
int bus = 0, slot = 0, func = 0;
const char *start;
char *end;
virDomainHostdevDefPtr def = virDomainHostdevDefAlloc();
if (!def)
goto error;
if (!STRPREFIX(val, "host=")) {
virReportError(VIR_ERR_INTERNAL_ERROR,
_("unknown PCI device syntax '%s'"), val);
goto error;
}
start = val + strlen("host=");
if (virStrToLong_i(start, &end, 16, &bus) < 0 || *end != ':') {
virReportError(VIR_ERR_INTERNAL_ERROR,
_("cannot extract PCI device bus '%s'"), val);
goto error;
}
start = end + 1;
if (virStrToLong_i(start, &end, 16, &slot) < 0 || *end != '.') {
virReportError(VIR_ERR_INTERNAL_ERROR,
_("cannot extract PCI device slot '%s'"), val);
goto error;
}
start = end + 1;
if (virStrToLong_i(start, NULL, 16, &func) < 0) {
virReportError(VIR_ERR_INTERNAL_ERROR,
_("cannot extract PCI device function '%s'"), val);
goto error;
}
def->mode = VIR_DOMAIN_HOSTDEV_MODE_SUBSYS;
def->managed = true;
def->source.subsys.type = VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_PCI;
def->source.subsys.u.pci.addr.bus = bus;
def->source.subsys.u.pci.addr.slot = slot;
def->source.subsys.u.pci.addr.function = func;
return def;
error:
virDomainHostdevDefFree(def);
return NULL;
}
/*
* Tries to parse a QEMU USB device
*/
static virDomainHostdevDefPtr
qemuParseCommandLineUSB(const char *val)
{
virDomainHostdevDefPtr def = virDomainHostdevDefAlloc();
virDomainHostdevSubsysUSBPtr usbsrc;
int first = 0, second = 0;
const char *start;
char *end;
if (!def)
goto error;
usbsrc = &def->source.subsys.u.usb;
if (!STRPREFIX(val, "host:")) {
virReportError(VIR_ERR_INTERNAL_ERROR,
_("unknown USB device syntax '%s'"), val);
goto error;
}
start = val + strlen("host:");
if (strchr(start, ':')) {
if (virStrToLong_i(start, &end, 16, &first) < 0 || *end != ':') {
virReportError(VIR_ERR_INTERNAL_ERROR,
_("cannot extract USB device vendor '%s'"), val);
goto error;
}
start = end + 1;
if (virStrToLong_i(start, NULL, 16, &second) < 0) {
virReportError(VIR_ERR_INTERNAL_ERROR,
_("cannot extract USB device product '%s'"), val);
goto error;
}
} else {
if (virStrToLong_i(start, &end, 10, &first) < 0 || *end != '.') {
virReportError(VIR_ERR_INTERNAL_ERROR,
_("cannot extract USB device bus '%s'"), val);
goto error;
}
start = end + 1;
if (virStrToLong_i(start, NULL, 10, &second) < 0) {
virReportError(VIR_ERR_INTERNAL_ERROR,
_("cannot extract USB device address '%s'"), val);
goto error;
}
}
def->mode = VIR_DOMAIN_HOSTDEV_MODE_SUBSYS;
def->managed = false;
def->source.subsys.type = VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_USB;
if (*end == '.') {
usbsrc->bus = first;
usbsrc->device = second;
} else {
usbsrc->vendor = first;
usbsrc->product = second;
}
return def;
error:
virDomainHostdevDefFree(def);
return NULL;
}
/*
* Tries to parse a QEMU serial/parallel device
*/
static int
qemuParseCommandLineChr(virDomainChrSourceDefPtr source,
const char *val)
{
if (STREQ(val, "null")) {
source->type = VIR_DOMAIN_CHR_TYPE_NULL;
} else if (STREQ(val, "vc")) {
source->type = VIR_DOMAIN_CHR_TYPE_VC;
} else if (STREQ(val, "pty")) {
source->type = VIR_DOMAIN_CHR_TYPE_PTY;
} else if (STRPREFIX(val, "file:")) {
source->type = VIR_DOMAIN_CHR_TYPE_FILE;
if (VIR_STRDUP(source->data.file.path, val + strlen("file:")) < 0)
goto error;
} else if (STRPREFIX(val, "pipe:")) {
source->type = VIR_DOMAIN_CHR_TYPE_PIPE;
if (VIR_STRDUP(source->data.file.path, val + strlen("pipe:")) < 0)
goto error;
} else if (STREQ(val, "stdio")) {
source->type = VIR_DOMAIN_CHR_TYPE_STDIO;
} else if (STRPREFIX(val, "udp:")) {
const char *svc1, *host2, *svc2;
source->type = VIR_DOMAIN_CHR_TYPE_UDP;
val += strlen("udp:");
svc1 = strchr(val, ':');
host2 = svc1 ? strchr(svc1, '@') : NULL;
svc2 = host2 ? strchr(host2, ':') : NULL;
if (svc1 && svc1 != val &&
VIR_STRNDUP(source->data.udp.connectHost, val, svc1 - val) < 0)
goto error;
if (svc1) {
svc1++;
if (VIR_STRNDUP(source->data.udp.connectService, svc1,
host2 ? host2 - svc1 : strlen(svc1)) < 0)
goto error;
}
if (host2) {
host2++;
if (svc2 && svc2 != host2 &&
VIR_STRNDUP(source->data.udp.bindHost, host2, svc2 - host2) < 0)
goto error;
}
if (svc2) {
svc2++;
if (STRNEQ(svc2, "0")) {
if (VIR_STRDUP(source->data.udp.bindService, svc2) < 0)
goto error;
}
}
} else if (STRPREFIX(val, "tcp:") ||
STRPREFIX(val, "telnet:")) {
const char *opt, *svc;
source->type = VIR_DOMAIN_CHR_TYPE_TCP;
if (STRPREFIX(val, "tcp:")) {
val += strlen("tcp:");
} else {
val += strlen("telnet:");
source->data.tcp.protocol = VIR_DOMAIN_CHR_TCP_PROTOCOL_TELNET;
}
svc = strchr(val, ':');
if (!svc) {
virReportError(VIR_ERR_INTERNAL_ERROR,
_("cannot find port number in character device %s"), val);
goto error;
}
opt = strchr(svc, ',');
if (opt && strstr(opt, "server"))
source->data.tcp.listen = true;
if (VIR_STRNDUP(source->data.tcp.host, val, svc - val) < 0)
goto error;
svc++;
if (VIR_STRNDUP(source->data.tcp.service, svc, opt ? opt - svc : -1) < 0)
goto error;
} else if (STRPREFIX(val, "unix:")) {
const char *opt;
val += strlen("unix:");
opt = strchr(val, ',');
source->type = VIR_DOMAIN_CHR_TYPE_UNIX;
if (VIR_STRNDUP(source->data.nix.path, val, opt ? opt - val : -1) < 0)
goto error;
} else if (STRPREFIX(val, "/dev")) {
source->type = VIR_DOMAIN_CHR_TYPE_DEV;
if (VIR_STRDUP(source->data.file.path, val) < 0)
goto error;
} else {
virReportError(VIR_ERR_INTERNAL_ERROR,
_("unknown character device syntax %s"), val);
goto error;
}
return 0;
error:
return -1;
}
static virCPUDefPtr
qemuInitGuestCPU(virDomainDefPtr dom)
{
if (!dom->cpu) {
virCPUDefPtr cpu;
if (VIR_ALLOC(cpu) < 0)
return NULL;
cpu->type = VIR_CPU_TYPE_GUEST;
cpu->match = VIR_CPU_MATCH_EXACT;
dom->cpu = cpu;
}
return dom->cpu;
}
static int
qemuParseCommandLineCPU(virDomainDefPtr dom,
const char *val)
{
virCPUDefPtr cpu = NULL;
char **tokens;
char **hv_tokens = NULL;
char *model = NULL;
int ret = -1;
size_t i;
if (!(tokens = virStringSplit(val, ",", 0)))
goto cleanup;
if (tokens[0] == NULL)
goto syntax;
for (i = 0; tokens[i] != NULL; i++) {
if (*tokens[i] == '\0')
goto syntax;
if (i == 0) {
if (VIR_STRDUP(model, tokens[i]) < 0)
goto cleanup;
if (STRNEQ(model, "qemu32") && STRNEQ(model, "qemu64")) {
if (!(cpu = qemuInitGuestCPU(dom)))
goto cleanup;
cpu->model = model;
model = NULL;
}
} else if (*tokens[i] == '+' || *tokens[i] == '-') {
const char *feature = tokens[i] + 1; /* '+' or '-' */
int policy;
if (*tokens[i] == '+')
policy = VIR_CPU_FEATURE_REQUIRE;
else
policy = VIR_CPU_FEATURE_DISABLE;
if (*feature == '\0')
goto syntax;
if (dom->os.arch != VIR_ARCH_X86_64 &&
dom->os.arch != VIR_ARCH_I686) {
virReportError(VIR_ERR_INTERNAL_ERROR,
_("%s platform doesn't support CPU features'"),
virArchToString(dom->os.arch));
goto cleanup;
}
if (STREQ(feature, "kvmclock")) {
bool present = (policy == VIR_CPU_FEATURE_REQUIRE);
size_t j;
for (j = 0; j < dom->clock.ntimers; j++) {
if (dom->clock.timers[j]->name == VIR_DOMAIN_TIMER_NAME_KVMCLOCK)
break;
}
if (j == dom->clock.ntimers) {
virDomainTimerDefPtr timer;
if (VIR_ALLOC(timer) < 0 ||
VIR_APPEND_ELEMENT_COPY(dom->clock.timers,
dom->clock.ntimers, timer) < 0) {
VIR_FREE(timer);
goto cleanup;
}
timer->name = VIR_DOMAIN_TIMER_NAME_KVMCLOCK;
timer->present = present;
timer->tickpolicy = -1;
timer->track = -1;
timer->mode = -1;
} else if (dom->clock.timers[j]->present != -1 &&
dom->clock.timers[j]->present != present) {
virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
_("conflicting occurrences of kvmclock feature"));
goto cleanup;
}
} else if (STREQ(feature, "kvm_pv_eoi")) {
if (policy == VIR_CPU_FEATURE_REQUIRE)
dom->apic_eoi = VIR_TRISTATE_SWITCH_ON;
else
dom->apic_eoi = VIR_TRISTATE_SWITCH_OFF;
} else {
if (!cpu) {
if (!(cpu = qemuInitGuestCPU(dom)))
goto cleanup;
cpu->model = model;
model = NULL;
}
if (virCPUDefAddFeature(cpu, feature, policy) < 0)
goto cleanup;
}
} else if (STREQ(tokens[i], "hv_crash")) {
size_t j;
for (j = 0; j < dom->npanics; j++) {
if (dom->panics[j]->model == VIR_DOMAIN_PANIC_MODEL_HYPERV)
break;
}
if (j == dom->npanics) {
virDomainPanicDefPtr panic;
if (VIR_ALLOC(panic) < 0 ||
VIR_APPEND_ELEMENT_COPY(dom->panics,
dom->npanics, panic) < 0) {
VIR_FREE(panic);
goto cleanup;
}
panic->model = VIR_DOMAIN_PANIC_MODEL_HYPERV;
}
} else if (STRPREFIX(tokens[i], "hv_")) {
const char *token = tokens[i] + 3; /* "hv_" */
const char *feature, *value;
int f;
if (*token == '\0')
goto syntax;
if (!(hv_tokens = virStringSplit(token, "=", 2)))
goto cleanup;
feature = hv_tokens[0];
value = hv_tokens[1];
if (*feature == '\0')
goto syntax;
dom->features[VIR_DOMAIN_FEATURE_HYPERV] = VIR_TRISTATE_SWITCH_ON;
if ((f = virDomainHypervTypeFromString(feature)) < 0) {
virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
_("unsupported HyperV Enlightenment feature "
"'%s'"), feature);
goto cleanup;
}
switch ((virDomainHyperv) f) {
case VIR_DOMAIN_HYPERV_RELAXED:
case VIR_DOMAIN_HYPERV_VAPIC:
if (value) {
virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
_("HyperV feature '%s' should not "
"have a value"), feature);
goto cleanup;
}
dom->hyperv_features[f] = VIR_TRISTATE_SWITCH_ON;
break;
case VIR_DOMAIN_HYPERV_SPINLOCKS:
dom->hyperv_features[f] = VIR_TRISTATE_SWITCH_ON;
if (!value) {
virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
_("missing HyperV spinlock retry count"));
goto cleanup;
}
if (virStrToLong_ui(value, NULL, 0, &dom->hyperv_spinlocks) < 0) {
virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
_("cannot parse HyperV spinlock retry count"));
goto cleanup;
}
if (dom->hyperv_spinlocks < 0xFFF)
dom->hyperv_spinlocks = 0xFFF;
break;
case VIR_DOMAIN_HYPERV_LAST:
break;
}
virStringFreeList(hv_tokens);
hv_tokens = NULL;
} else if (STREQ(tokens[i], "kvm=off")) {
dom->features[VIR_DOMAIN_FEATURE_KVM] = VIR_TRISTATE_SWITCH_ON;
dom->kvm_features[VIR_DOMAIN_KVM_HIDDEN] = VIR_TRISTATE_SWITCH_ON;
}
}
if (dom->os.arch == VIR_ARCH_X86_64) {
bool is_32bit = false;
if (cpu) {
virCPUDataPtr cpuData = NULL;
if (cpuEncode(VIR_ARCH_X86_64, cpu, NULL, &cpuData,
NULL, NULL, NULL, NULL) < 0)
goto cleanup;
is_32bit = (cpuHasFeature(cpuData, "lm") != 1);
cpuDataFree(cpuData);
} else if (model) {
is_32bit = STREQ(model, "qemu32");
}
if (is_32bit)
dom->os.arch = VIR_ARCH_I686;
}
ret = 0;
cleanup:
VIR_FREE(model);
virStringFreeList(tokens);
virStringFreeList(hv_tokens);
return ret;
syntax:
virReportError(VIR_ERR_INTERNAL_ERROR,
_("unknown CPU syntax '%s'"), val);
goto cleanup;
}
static int
qemuParseCommandLineSmp(virDomainDefPtr dom,
const char *val)
{
unsigned int sockets = 0;
unsigned int cores = 0;
unsigned int threads = 0;
unsigned int maxcpus = 0;
unsigned int vcpus = 0;
size_t i;
int nkws;
char **kws;
char **vals;
int n;
char *end;
int ret;
if (qemuParseKeywords(val, &kws, &vals, &nkws, 1) < 0)
return -1;
for (i = 0; i < nkws; i++) {
if (vals[i] == NULL) {
if (i > 0 ||
virStrToLong_ui(kws[i], &end, 10, &vcpus) < 0 || *end != '\0')
goto syntax;
} else {
if (virStrToLong_i(vals[i], &end, 10, &n) < 0 || *end != '\0')
goto syntax;
if (STREQ(kws[i], "sockets"))
sockets = n;
else if (STREQ(kws[i], "cores"))
cores = n;
else if (STREQ(kws[i], "threads"))
threads = n;
else if (STREQ(kws[i], "maxcpus"))
maxcpus = n;
else
goto syntax;
}
}
if (maxcpus == 0)
maxcpus = vcpus;
if (virDomainDefSetVcpusMax(dom, maxcpus) < 0)
goto error;
if (virDomainDefSetVcpus(dom, vcpus) < 0)
goto error;
if (sockets && cores && threads) {
virCPUDefPtr cpu;
if (!(cpu = qemuInitGuestCPU(dom)))
goto error;
cpu->sockets = sockets;
cpu->cores = cores;
cpu->threads = threads;
} else if (sockets || cores || threads) {
goto syntax;
}
ret = 0;
cleanup:
for (i = 0; i < nkws; i++) {
VIR_FREE(kws[i]);
VIR_FREE(vals[i]);
}
VIR_FREE(kws);
VIR_FREE(vals);
return ret;
syntax:
virReportError(VIR_ERR_INTERNAL_ERROR,
_("cannot parse CPU topology '%s'"), val);
error:
ret = -1;
goto cleanup;
}
static void
qemuParseCommandLineBootDevs(virDomainDefPtr def, const char *str)
{
int n, b = 0;
for (n = 0; str[n] && b < VIR_DOMAIN_BOOT_LAST; n++) {
if (str[n] == 'a')
def->os.bootDevs[b++] = VIR_DOMAIN_BOOT_FLOPPY;
else if (str[n] == 'c')
def->os.bootDevs[b++] = VIR_DOMAIN_BOOT_DISK;
else if (str[n] == 'd')
def->os.bootDevs[b++] = VIR_DOMAIN_BOOT_CDROM;
else if (str[n] == 'n')
def->os.bootDevs[b++] = VIR_DOMAIN_BOOT_NET;
else if (str[n] == ',')
break;
}
def->os.nBootDevs = b;
}
/*
* Analyse the env and argv settings and reconstruct a
* virDomainDefPtr representing these settings as closely
* as is practical. This is not an exact science....
*/
static virDomainDefPtr
qemuParseCommandLine(virCapsPtr caps,
virDomainXMLOptionPtr xmlopt,
char **progenv,
char **progargv,
char **pidfile,
virDomainChrSourceDefPtr *monConfig,
bool *monJSON)
{
virDomainDefPtr def;
size_t i;
bool nographics = false;
bool fullscreen = false;
char **list = NULL;
char *path;
size_t nnics = 0;
const char **nics = NULL;
int video = VIR_DOMAIN_VIDEO_TYPE_CIRRUS;
int nvirtiodisk = 0;
qemuDomainCmdlineDefPtr cmd = NULL;
virDomainDiskDefPtr disk = NULL;
const char *ceph_args = qemuFindEnv(progenv, "CEPH_ARGS");
bool have_sdl = false;
if (pidfile)
*pidfile = NULL;
if (monConfig)
*monConfig = NULL;
if (monJSON)
*monJSON = false;
if (!progargv[0]) {
virReportError(VIR_ERR_INTERNAL_ERROR,
"%s", _("no emulator path found"));
return NULL;
}
if (!(def = virDomainDefNew()))
goto error;
/* allocate the cmdlinedef up-front; if it's unused, we'll free it later */
if (VIR_ALLOC(cmd) < 0)
goto error;
if (virUUIDGenerate(def->uuid) < 0) {
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
_("failed to generate uuid"));
goto error;
}
def->id = -1;
def->mem.cur_balloon = 64 * 1024;
virDomainDefSetMemoryTotal(def, def->mem.cur_balloon);
if (virDomainDefSetVcpusMax(def, 1) < 0)
goto error;
if (virDomainDefSetVcpus(def, 1) < 0)
goto error;
def->clock.offset = VIR_DOMAIN_CLOCK_OFFSET_UTC;
def->onReboot = VIR_DOMAIN_LIFECYCLE_RESTART;
def->onCrash = VIR_DOMAIN_LIFECYCLE_CRASH_DESTROY;
def->onPoweroff = VIR_DOMAIN_LIFECYCLE_DESTROY;
def->virtType = VIR_DOMAIN_VIRT_QEMU;
if (VIR_STRDUP(def->emulator, progargv[0]) < 0)
goto error;
if (!(path = last_component(def->emulator)))
goto error;
def->os.type = VIR_DOMAIN_OSTYPE_HVM;
if (strstr(path, "kvm")) {
def->virtType = VIR_DOMAIN_VIRT_KVM;
def->features[VIR_DOMAIN_FEATURE_PAE] = VIR_TRISTATE_SWITCH_ON;
}
if (def->virtType == VIR_DOMAIN_VIRT_KVM)
def->os.arch = caps->host.arch;
else if (STRPREFIX(path, "qemu-system-"))
def->os.arch = virArchFromString(path + strlen("qemu-system-"));
else
def->os.arch = VIR_ARCH_I686;
if ((def->os.arch == VIR_ARCH_I686) ||
(def->os.arch == VIR_ARCH_X86_64))
def->features[VIR_DOMAIN_FEATURE_ACPI] = VIR_TRISTATE_SWITCH_ON;
#define WANT_VALUE() \
const char *val = progargv[++i]; \
if (!val) { \
virReportError(VIR_ERR_INTERNAL_ERROR, \
_("missing value for %s argument"), arg); \
goto error; \
}
/* One initial loop to get list of NICs, so we
* can correlate them later */
for (i = 1; progargv[i]; i++) {
const char *arg = progargv[i];
/* Make sure we have a single - for all options to
simplify next logic */
if (STRPREFIX(arg, "--"))
arg++;
if (STREQ(arg, "-net")) {
WANT_VALUE();
if (STRPREFIX(val, "nic") &&
VIR_APPEND_ELEMENT(nics, nnics, val) < 0)
goto error;
}
}
/* Now the real processing loop */
for (i = 1; progargv[i]; i++) {
const char *arg = progargv[i];
bool argRecognized = true;
/* Make sure we have a single - for all options to
simplify next logic */
if (STRPREFIX(arg, "--"))
arg++;
if (STREQ(arg, "-vnc")) {
WANT_VALUE();
if (qemuParseCommandLineVnc(def, val) < 0)
goto error;
} else if (STREQ(arg, "-sdl")) {
have_sdl = true;
} else if (STREQ(arg, "-m")) {
int mem;
WANT_VALUE();
if (virStrToLong_i(val, NULL, 10, &mem) < 0) {
virReportError(VIR_ERR_INTERNAL_ERROR, \
_("cannot parse memory level '%s'"), val);
goto error;
}
virDomainDefSetMemoryTotal(def, mem * 1024);
def->mem.cur_balloon = mem * 1024;
} else if (STREQ(arg, "-smp")) {
WANT_VALUE();
if (qemuParseCommandLineSmp(def, val) < 0)
goto error;
} else if (STREQ(arg, "-uuid")) {
WANT_VALUE();
if (virUUIDParse(val, def->uuid) < 0) {
virReportError(VIR_ERR_INTERNAL_ERROR, \
_("cannot parse UUID '%s'"), val);
goto error;
}
} else if (STRPREFIX(arg, "-hd") ||
STRPREFIX(arg, "-sd") ||
STRPREFIX(arg, "-fd") ||
STREQ(arg, "-cdrom")) {
WANT_VALUE();
if (!(disk = virDomainDiskDefNew(xmlopt)))
goto error;
if (STRPREFIX(val, "/dev/")) {
disk->src->type = VIR_STORAGE_TYPE_BLOCK;
} else if (STRPREFIX(val, "nbd:")) {
disk->src->type = VIR_STORAGE_TYPE_NETWORK;
disk->src->protocol = VIR_STORAGE_NET_PROTOCOL_NBD;
} else if (STRPREFIX(val, "rbd:")) {
disk->src->type = VIR_STORAGE_TYPE_NETWORK;
disk->src->protocol = VIR_STORAGE_NET_PROTOCOL_RBD;
val += strlen("rbd:");
} else if (STRPREFIX(val, "gluster")) {
disk->src->type = VIR_STORAGE_TYPE_NETWORK;
disk->src->protocol = VIR_STORAGE_NET_PROTOCOL_GLUSTER;
} else if (STRPREFIX(val, "sheepdog:")) {
disk->src->type = VIR_STORAGE_TYPE_NETWORK;
disk->src->protocol = VIR_STORAGE_NET_PROTOCOL_SHEEPDOG;
val += strlen("sheepdog:");
} else {
disk->src->type = VIR_STORAGE_TYPE_FILE;
}
if (STREQ(arg, "-cdrom")) {
disk->device = VIR_DOMAIN_DISK_DEVICE_CDROM;
if ((ARCH_IS_PPC64(def->os.arch) &&
def->os.machine && STRPREFIX(def->os.machine, "pseries")))
disk->bus = VIR_DOMAIN_DISK_BUS_SCSI;
if (VIR_STRDUP(disk->dst, "hdc") < 0)
goto error;
disk->src->readonly = true;
} else {
if (STRPREFIX(arg, "-fd")) {
disk->device = VIR_DOMAIN_DISK_DEVICE_FLOPPY;
disk->bus = VIR_DOMAIN_DISK_BUS_FDC;
} else {
disk->device = VIR_DOMAIN_DISK_DEVICE_DISK;
if (STRPREFIX(arg, "-hd"))
disk->bus = VIR_DOMAIN_DISK_BUS_IDE;
else
disk->bus = VIR_DOMAIN_DISK_BUS_SCSI;
if ((ARCH_IS_PPC64(def->os.arch) &&
def->os.machine && STRPREFIX(def->os.machine, "pseries")))
disk->bus = VIR_DOMAIN_DISK_BUS_SCSI;
}
if (VIR_STRDUP(disk->dst, arg + 1) < 0)
goto error;
}
if (VIR_STRDUP(disk->src->path, val) < 0)
goto error;
if (disk->src->type == VIR_STORAGE_TYPE_NETWORK) {
char *port;
switch ((virStorageNetProtocol) disk->src->protocol) {
case VIR_STORAGE_NET_PROTOCOL_NBD:
if (qemuParseNBDString(disk) < 0)
goto error;
break;
case VIR_STORAGE_NET_PROTOCOL_RBD:
/* old-style CEPH_ARGS env variable is parsed later */
if (!ceph_args && qemuParseRBDString(disk) < 0)
goto error;
break;
case VIR_STORAGE_NET_PROTOCOL_SHEEPDOG:
/* disk->src must be [vdiname] or [host]:[port]:[vdiname] */
port = strchr(disk->src->path, ':');
if (port) {
char *vdi;
*port++ = '\0';
vdi = strchr(port, ':');
if (!vdi) {
virReportError(VIR_ERR_INTERNAL_ERROR,
_("cannot parse sheepdog filename '%s'"), val);
goto error;
}
*vdi++ = '\0';
if (VIR_ALLOC(disk->src->hosts) < 0)
goto error;
disk->src->nhosts = 1;
disk->src->hosts->name = disk->src->path;
if (VIR_STRDUP(disk->src->hosts->port, port) < 0)
goto error;
if (VIR_STRDUP(disk->src->path, vdi) < 0)
goto error;
}
break;
case VIR_STORAGE_NET_PROTOCOL_GLUSTER:
if (qemuParseGlusterString(disk) < 0)
goto error;
break;
case VIR_STORAGE_NET_PROTOCOL_ISCSI:
if (qemuParseISCSIString(disk) < 0)
goto error;
break;
case VIR_STORAGE_NET_PROTOCOL_HTTP:
case VIR_STORAGE_NET_PROTOCOL_HTTPS:
case VIR_STORAGE_NET_PROTOCOL_FTP:
case VIR_STORAGE_NET_PROTOCOL_FTPS:
case VIR_STORAGE_NET_PROTOCOL_TFTP:
case VIR_STORAGE_NET_PROTOCOL_LAST:
case VIR_STORAGE_NET_PROTOCOL_NONE:
/* ignored for now */
break;
}
}
if (virDomainDiskDefAssignAddress(xmlopt, disk, def) < 0) {
virReportError(VIR_ERR_INTERNAL_ERROR,
_("Cannot assign address for device name '%s'"),
disk->dst);
goto error;
}
if (VIR_APPEND_ELEMENT(def->disks, def->ndisks, disk) < 0)
goto error;
} else if (STREQ(arg, "-no-acpi")) {
def->features[VIR_DOMAIN_FEATURE_ACPI] = VIR_TRISTATE_SWITCH_ABSENT;
} else if (STREQ(arg, "-no-reboot")) {
def->onReboot = VIR_DOMAIN_LIFECYCLE_DESTROY;
} else if (STREQ(arg, "-no-kvm")) {
def->virtType = VIR_DOMAIN_VIRT_QEMU;
} else if (STREQ(arg, "-enable-kvm")) {
def->virtType = VIR_DOMAIN_VIRT_KVM;
} else if (STREQ(arg, "-nographic")) {
nographics = true;
} else if (STREQ(arg, "-full-screen")) {
fullscreen = true;
} else if (STREQ(arg, "-localtime")) {
def->clock.offset = VIR_DOMAIN_CLOCK_OFFSET_LOCALTIME;
} else if (STREQ(arg, "-kernel")) {
WANT_VALUE();
if (VIR_STRDUP(def->os.kernel, val) < 0)
goto error;
} else if (STREQ(arg, "-bios")) {
WANT_VALUE();
if (VIR_ALLOC(def->os.loader) < 0 ||
VIR_STRDUP(def->os.loader->path, val) < 0)
goto error;
} else if (STREQ(arg, "-initrd")) {
WANT_VALUE();
if (VIR_STRDUP(def->os.initrd, val) < 0)
goto error;
} else if (STREQ(arg, "-append")) {
WANT_VALUE();
if (VIR_STRDUP(def->os.cmdline, val) < 0)
goto error;
} else if (STREQ(arg, "-dtb")) {
WANT_VALUE();
if (VIR_STRDUP(def->os.dtb, val) < 0)
goto error;
} else if (STREQ(arg, "-boot")) {
const char *token = NULL;
WANT_VALUE();
if (!strchr(val, ',')) {
qemuParseCommandLineBootDevs(def, val);
} else {
token = val;
while (token && *token) {
if (STRPREFIX(token, "order=")) {
token += strlen("order=");
qemuParseCommandLineBootDevs(def, token);
} else if (STRPREFIX(token, "menu=on")) {
def->os.bootmenu = 1;
} else if (STRPREFIX(token, "reboot-timeout=")) {
int num;
char *endptr;
if (virStrToLong_i(token + strlen("reboot-timeout="),
&endptr, 10, &num) < 0 ||
(*endptr != '\0' && endptr != strchr(token, ','))) {
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
_("cannot parse reboot-timeout value"));
goto error;
}
if (num > 65535)
num = 65535;
else if (num < -1)
num = -1;
def->os.bios.rt_delay = num;
def->os.bios.rt_set = true;
}
token = strchr(token, ',');
/* This incrementation has to be done here in order to make it
* possible to pass the token pointer properly into the loop */
if (token)
token++;
}
}
} else if (STREQ(arg, "-name")) {
char *process;
WANT_VALUE();
process = strstr(val, ",process=");
if (process == NULL) {
if (VIR_STRDUP(def->name, val) < 0)
goto error;
} else {
if (VIR_STRNDUP(def->name, val, process - val) < 0)
goto error;
}
if (STREQ(def->name, ""))
VIR_FREE(def->name);
} else if (STREQ(arg, "-M") ||
STREQ(arg, "-machine")) {
char *param;
size_t j = 0;
/* -machine [type=]name[,prop[=value][,...]]
* Set os.machine only if first parameter lacks '=' or
* contains explicit type='...' */
WANT_VALUE();
if (!(list = virStringSplit(val, ",", 0)))
goto error;
param = list[0];
if (STRPREFIX(param, "type="))
param += strlen("type=");
if (!strchr(param, '=')) {
if (VIR_STRDUP(def->os.machine, param) < 0)
goto error;
j++;
}
/* handle all remaining "-machine" parameters */
while ((param = list[j++])) {
if (STRPREFIX(param, "dump-guest-core=")) {
param += strlen("dump-guest-core=");
def->mem.dump_core = virTristateSwitchTypeFromString(param);
if (def->mem.dump_core <= 0)
def->mem.dump_core = VIR_TRISTATE_SWITCH_ABSENT;
} else if (STRPREFIX(param, "mem-merge=off")) {
def->mem.nosharepages = true;
} else if (STRPREFIX(param, "accel=kvm")) {
def->virtType = VIR_DOMAIN_VIRT_KVM;
def->features[VIR_DOMAIN_FEATURE_PAE] = VIR_TRISTATE_SWITCH_ON;
} else if (STRPREFIX(param, "aes-key-wrap=")) {
if (STREQ(arg, "-M")) {
virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
_("aes-key-wrap is not supported with "
"this QEMU binary"));
goto error;
}
param += strlen("aes-key-wrap=");
if (!def->keywrap && VIR_ALLOC(def->keywrap) < 0)
goto error;
def->keywrap->aes = virTristateSwitchTypeFromString(param);
if (def->keywrap->aes < 0)
def->keywrap->aes = VIR_TRISTATE_SWITCH_ABSENT;
} else if (STRPREFIX(param, "dea-key-wrap=")) {
if (STREQ(arg, "-M")) {
virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
_("dea-key-wrap is not supported with "
"this QEMU binary"));
goto error;
}
param += strlen("dea-key-wrap=");
if (!def->keywrap && VIR_ALLOC(def->keywrap) < 0)
goto error;
def->keywrap->dea = virTristateSwitchTypeFromString(param);
if (def->keywrap->dea < 0)
def->keywrap->dea = VIR_TRISTATE_SWITCH_ABSENT;
}
}
virStringFreeList(list);
list = NULL;
} else if (STREQ(arg, "-serial")) {
WANT_VALUE();
if (STRNEQ(val, "none")) {
virDomainChrDefPtr chr;
if (!(chr = virDomainChrDefNew()))
goto error;
if (qemuParseCommandLineChr(&chr->source, val) < 0) {
virDomainChrDefFree(chr);
goto error;
}
chr->deviceType = VIR_DOMAIN_CHR_DEVICE_TYPE_SERIAL;
chr->target.port = def->nserials;
if (VIR_APPEND_ELEMENT(def->serials, def->nserials, chr) < 0) {
virDomainChrDefFree(chr);
goto error;
}
}
} else if (STREQ(arg, "-parallel")) {
WANT_VALUE();
if (STRNEQ(val, "none")) {
virDomainChrDefPtr chr;
if (!(chr = virDomainChrDefNew()))
goto error;
if (qemuParseCommandLineChr(&chr->source, val) < 0) {
virDomainChrDefFree(chr);
goto error;
}
chr->deviceType = VIR_DOMAIN_CHR_DEVICE_TYPE_PARALLEL;
chr->target.port = def->nparallels;
if (VIR_APPEND_ELEMENT(def->parallels, def->nparallels, chr) < 0) {
virDomainChrDefFree(chr);
goto error;
}
}
} else if (STREQ(arg, "-usbdevice")) {
WANT_VALUE();
if (STREQ(val, "tablet") ||
STREQ(val, "mouse") ||
STREQ(val, "keyboard")) {
virDomainInputDefPtr input;
if (VIR_ALLOC(input) < 0)
goto error;
input->bus = VIR_DOMAIN_INPUT_BUS_USB;
if (STREQ(val, "tablet"))
input->type = VIR_DOMAIN_INPUT_TYPE_TABLET;
else if (STREQ(val, "mouse"))
input->type = VIR_DOMAIN_INPUT_TYPE_MOUSE;
else
input->type = VIR_DOMAIN_INPUT_TYPE_KBD;
if (VIR_APPEND_ELEMENT(def->inputs, def->ninputs, input) < 0) {
virDomainInputDefFree(input);
goto error;
}
} else if (STRPREFIX(val, "disk:")) {
if (!(disk = virDomainDiskDefNew(xmlopt)))
goto error;
if (VIR_STRDUP(disk->src->path, val + strlen("disk:")) < 0)
goto error;
if (STRPREFIX(disk->src->path, "/dev/"))
disk->src->type = VIR_STORAGE_TYPE_BLOCK;
else
disk->src->type = VIR_STORAGE_TYPE_FILE;
disk->device = VIR_DOMAIN_DISK_DEVICE_DISK;
disk->bus = VIR_DOMAIN_DISK_BUS_USB;
disk->removable = VIR_TRISTATE_SWITCH_ABSENT;
if (VIR_STRDUP(disk->dst, "sda") < 0)
goto error;
if (VIR_APPEND_ELEMENT(def->disks, def->ndisks, disk) < 0)
goto error;
} else {
virDomainHostdevDefPtr hostdev;
if (!(hostdev = qemuParseCommandLineUSB(val)))
goto error;
if (VIR_APPEND_ELEMENT(def->hostdevs, def->nhostdevs, hostdev) < 0) {
virDomainHostdevDefFree(hostdev);
goto error;
}
}
} else if (STREQ(arg, "-net")) {
WANT_VALUE();
if (!STRPREFIX(val, "nic") && STRNEQ(val, "none")) {
virDomainNetDefPtr net;
if (!(net = qemuParseCommandLineNet(xmlopt, val, nnics, nics)))
goto error;
if (VIR_APPEND_ELEMENT(def->nets, def->nnets, net) < 0) {
virDomainNetDefFree(net);
goto error;
}
}
} else if (STREQ(arg, "-drive")) {
WANT_VALUE();
if (!(disk = qemuParseCommandLineDisk(xmlopt, val, def,
nvirtiodisk,
ceph_args != NULL)))
goto error;
if (disk->bus == VIR_DOMAIN_DISK_BUS_VIRTIO)
nvirtiodisk++;
if (VIR_APPEND_ELEMENT(def->disks, def->ndisks, disk) < 0)
goto error;
} else if (STREQ(arg, "-pcidevice")) {
virDomainHostdevDefPtr hostdev;
WANT_VALUE();
if (!(hostdev = qemuParseCommandLinePCI(val)))
goto error;
if (VIR_APPEND_ELEMENT(def->hostdevs, def->nhostdevs, hostdev) < 0) {
virDomainHostdevDefFree(hostdev);
goto error;
}
} else if (STREQ(arg, "-soundhw")) {
const char *start;
WANT_VALUE();
start = val;
while (start) {
const char *tmp = strchr(start, ',');
int type = -1;
if (STRPREFIX(start, "pcspk")) {
type = VIR_DOMAIN_SOUND_MODEL_PCSPK;
} else if (STRPREFIX(start, "sb16")) {
type = VIR_DOMAIN_SOUND_MODEL_SB16;
} else if (STRPREFIX(start, "es1370")) {
type = VIR_DOMAIN_SOUND_MODEL_ES1370;
} else if (STRPREFIX(start, "ac97")) {
type = VIR_DOMAIN_SOUND_MODEL_AC97;
} else if (STRPREFIX(start, "hda")) {
type = VIR_DOMAIN_SOUND_MODEL_ICH6;
}
if (type != -1) {
virDomainSoundDefPtr snd;
if (VIR_ALLOC(snd) < 0)
goto error;
snd->model = type;
if (VIR_APPEND_ELEMENT(def->sounds, def->nsounds, snd) < 0) {
VIR_FREE(snd);
goto error;
}
}
start = tmp ? tmp + 1 : NULL;
}
} else if (STREQ(arg, "-watchdog")) {
WANT_VALUE();
int model = virDomainWatchdogModelTypeFromString(val);
if (model != -1) {
virDomainWatchdogDefPtr wd;
if (VIR_ALLOC(wd) < 0)
goto error;
wd->model = model;
wd->action = VIR_DOMAIN_WATCHDOG_ACTION_RESET;
def->watchdog = wd;
}
} else if (STREQ(arg, "-watchdog-action") && def->watchdog) {
WANT_VALUE();
int action = virDomainWatchdogActionTypeFromString(val);
if (action != -1)
def->watchdog->action = action;
} else if (STREQ(arg, "-bootloader")) {
WANT_VALUE();
if (VIR_STRDUP(def->os.bootloader, val) < 0)
goto error;
} else if (STREQ(arg, "-vmwarevga")) {
video = VIR_DOMAIN_VIDEO_TYPE_VMVGA;
} else if (STREQ(arg, "-std-vga")) {
video = VIR_DOMAIN_VIDEO_TYPE_VGA;
} else if (STREQ(arg, "-vga")) {
WANT_VALUE();
if (STRNEQ(val, "none")) {
video = qemuVideoTypeFromString(val);
if (video < 0) {
virReportError(VIR_ERR_INTERNAL_ERROR,
_("unknown video adapter type '%s'"), val);
goto error;
}
}
} else if (STREQ(arg, "-cpu")) {
WANT_VALUE();
if (qemuParseCommandLineCPU(def, val) < 0)
goto error;
} else if (STREQ(arg, "-domid")) {
WANT_VALUE();
/* ignore, generted on the fly */
} else if (STREQ(arg, "-usb")) {
virDomainControllerDefPtr ctldef;
if (VIR_ALLOC(ctldef) < 0)
goto error;
ctldef->type = VIR_DOMAIN_CONTROLLER_TYPE_USB;
ctldef->idx = 0;
ctldef->model = -1;
if (virDomainControllerInsert(def, ctldef) < 0) {
VIR_FREE(ctldef);
goto error;
}
} else if (STREQ(arg, "-pidfile")) {
WANT_VALUE();
if (pidfile)
if (VIR_STRDUP(*pidfile, val) < 0)
goto error;
} else if (STREQ(arg, "-incoming")) {
WANT_VALUE();
/* ignore, used via restore/migrate APIs */
} else if (STREQ(arg, "-monitor")) {
WANT_VALUE();
if (monConfig) {
virDomainChrSourceDefPtr chr;
if (VIR_ALLOC(chr) < 0)
goto error;
if (qemuParseCommandLineChr(chr, val) < 0) {
virDomainChrSourceDefFree(chr);
goto error;
}
*monConfig = chr;
}
} else if (STREQ(arg, "-global") &&
STRPREFIX(progargv[i + 1], "PIIX4_PM.disable_s3=")) {
/* We want to parse only the known "-global" parameters,
* so the ones that we don't know are still added to the
* namespace */
WANT_VALUE();
val += strlen("PIIX4_PM.disable_s3=");
if (STREQ(val, "0")) {
def->pm.s3 = VIR_TRISTATE_BOOL_YES;
} else if (STREQ(val, "1")) {
def->pm.s3 = VIR_TRISTATE_BOOL_NO;
} else {
virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
_("invalid value for disable_s3 parameter: "
"'%s'"), val);
goto error;
}
} else if (STREQ(arg, "-global") &&
STRPREFIX(progargv[i + 1], "PIIX4_PM.disable_s4=")) {
WANT_VALUE();
val += strlen("PIIX4_PM.disable_s4=");
if (STREQ(val, "0")) {
def->pm.s4 = VIR_TRISTATE_BOOL_YES;
} else if (STREQ(val, "1")) {
def->pm.s4 = VIR_TRISTATE_BOOL_NO;
} else {
virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
_("invalid value for disable_s4 parameter: "
"'%s'"), val);
goto error;
}
} else if (STREQ(arg, "-global") &&
STRPREFIX(progargv[i + 1], "spapr-nvram.reg=")) {
WANT_VALUE();
if (VIR_ALLOC(def->nvram) < 0)
goto error;
def->nvram->info.type = VIR_DOMAIN_DEVICE_ADDRESS_TYPE_SPAPRVIO;
def->nvram->info.addr.spaprvio.has_reg = true;
val += strlen("spapr-nvram.reg=");
if (virStrToLong_ull(val, NULL, 16,
&def->nvram->info.addr.spaprvio.reg) < 0) {
virReportError(VIR_ERR_INTERNAL_ERROR,
_("cannot parse nvram's address '%s'"), val);
goto error;
}
} else if (STREQ(arg, "-S") ||
STREQ(arg, "-nodefaults") ||
STREQ(arg, "-nodefconfig")) {
/* ignore, always added by libvirt */
} else if (STREQ(arg, "-device") && progargv[1 + 1]) {
const char *opts = progargv[i + 1];
/* NB: we can't do WANT_VALUE until we're sure that we
* recognize the device, otherwise the !argRecognized
* logic below will be messed up
*/
if (STRPREFIX(opts, "virtio-balloon")) {
WANT_VALUE();
if (VIR_ALLOC(def->memballoon) < 0)
goto error;
def->memballoon->model = VIR_DOMAIN_MEMBALLOON_MODEL_VIRTIO;
} else {
/* add in new -device's here */
argRecognized = false;
}
} else {
argRecognized = false;
}
if (!argRecognized) {
char *tmp = NULL;
/* something we can't yet parse. Add it to the qemu namespace
* cmdline/environment advanced options and hope for the best
*/
VIR_WARN("unknown QEMU argument '%s', adding to the qemu namespace",
arg);
if (VIR_STRDUP(tmp, arg) < 0 ||
VIR_APPEND_ELEMENT(cmd->args, cmd->num_args, tmp) < 0) {
VIR_FREE(tmp);
goto error;
}
}
}
#undef WANT_VALUE
if (def->ndisks > 0 && ceph_args) {
char *hosts, *port, *saveptr = NULL, *token;
virDomainDiskDefPtr first_rbd_disk = NULL;
for (i = 0; i < def->ndisks; i++) {
if (def->disks[i]->src->type == VIR_STORAGE_TYPE_NETWORK &&
def->disks[i]->src->protocol == VIR_STORAGE_NET_PROTOCOL_RBD) {
first_rbd_disk = def->disks[i];
break;
}
}
if (!first_rbd_disk) {
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
_("CEPH_ARGS was set without an rbd disk"));
goto error;
}
/* CEPH_ARGS should be: -m host1[:port1][,host2[:port2]]... */
if (!STRPREFIX(ceph_args, "-m ")) {
virReportError(VIR_ERR_INTERNAL_ERROR,
_("could not parse CEPH_ARGS '%s'"), ceph_args);
goto error;
}
if (VIR_STRDUP(hosts, strchr(ceph_args, ' ') + 1) < 0)
goto error;
first_rbd_disk->src->nhosts = 0;
token = strtok_r(hosts, ",", &saveptr);
while (token != NULL) {
if (VIR_REALLOC_N(first_rbd_disk->src->hosts,
first_rbd_disk->src->nhosts + 1) < 0) {
VIR_FREE(hosts);
goto error;
}
port = strchr(token, ':');
if (port) {
*port++ = '\0';
if (VIR_STRDUP(port, port) < 0) {
VIR_FREE(hosts);
goto error;
}
}
first_rbd_disk->src->hosts[first_rbd_disk->src->nhosts].port = port;
if (VIR_STRDUP(first_rbd_disk->src->hosts[first_rbd_disk->src->nhosts].name,
token) < 0) {
VIR_FREE(hosts);
goto error;
}
first_rbd_disk->src->hosts[first_rbd_disk->src->nhosts].transport = VIR_STORAGE_NET_HOST_TRANS_TCP;
first_rbd_disk->src->hosts[first_rbd_disk->src->nhosts].socket = NULL;
first_rbd_disk->src->nhosts++;
token = strtok_r(NULL, ",", &saveptr);
}
VIR_FREE(hosts);
if (first_rbd_disk->src->nhosts == 0) {
virReportError(VIR_ERR_INTERNAL_ERROR,
_("found no rbd hosts in CEPH_ARGS '%s'"), ceph_args);
goto error;
}
}
if (!def->os.machine) {
virCapsDomainDataPtr capsdata;
if (!(capsdata = virCapabilitiesDomainDataLookup(caps, def->os.type,
def->os.arch, def->virtType, NULL, NULL)))
goto error;
if (VIR_STRDUP(def->os.machine, capsdata->machinetype) < 0) {
VIR_FREE(capsdata);
goto error;
}
VIR_FREE(capsdata);
}
if (!nographics && (def->ngraphics == 0 || have_sdl)) {
virDomainGraphicsDefPtr sdl;
const char *display = qemuFindEnv(progenv, "DISPLAY");
const char *xauth = qemuFindEnv(progenv, "XAUTHORITY");
if (VIR_ALLOC(sdl) < 0)
goto error;
sdl->type = VIR_DOMAIN_GRAPHICS_TYPE_SDL;
sdl->data.sdl.fullscreen = fullscreen;
if (VIR_STRDUP(sdl->data.sdl.display, display) < 0) {
VIR_FREE(sdl);
goto error;
}
if (VIR_STRDUP(sdl->data.sdl.xauth, xauth) < 0) {
VIR_FREE(sdl);
goto error;
}
if (VIR_APPEND_ELEMENT(def->graphics, def->ngraphics, sdl) < 0) {
virDomainGraphicsDefFree(sdl);
goto error;
}
}
if (def->ngraphics) {
virDomainVideoDefPtr vid;
if (VIR_ALLOC(vid) < 0)
goto error;
if (def->virtType == VIR_DOMAIN_VIRT_XEN)
vid->type = VIR_DOMAIN_VIDEO_TYPE_XEN;
else
vid->type = video;
vid->vram = virDomainVideoDefaultRAM(def, vid->type);
if (vid->type == VIR_DOMAIN_VIDEO_TYPE_QXL) {
vid->ram = virDomainVideoDefaultRAM(def, vid->type);
vid->vgamem = QEMU_QXL_VGAMEM_DEFAULT;
} else {
vid->ram = 0;
vid->vgamem = 0;
}
vid->heads = 1;
if (VIR_APPEND_ELEMENT(def->videos, def->nvideos, vid) < 0) {
virDomainVideoDefFree(vid);
goto error;
}
}
/*
* having a balloon is the default, define one with type="none" to avoid it
*/
if (!def->memballoon) {
virDomainMemballoonDefPtr memballoon;
if (VIR_ALLOC(memballoon) < 0)
goto error;
memballoon->model = VIR_DOMAIN_MEMBALLOON_MODEL_NONE;
def->memballoon = memballoon;
}
VIR_FREE(nics);
if (virDomainDefPostParse(def, caps, VIR_DOMAIN_DEF_PARSE_ABI_UPDATE,
xmlopt) < 0)
goto error;
if (cmd->num_args || cmd->num_env) {
def->ns = *virDomainXMLOptionGetNamespace(xmlopt);
def->namespaceData = cmd;
}
else
qemuDomainCmdlineDefFree(cmd);
return def;
error:
virDomainDiskDefFree(disk);
qemuDomainCmdlineDefFree(cmd);
virDomainDefFree(def);
virStringFreeList(list);
VIR_FREE(nics);
if (monConfig) {
virDomainChrSourceDefFree(*monConfig);
*monConfig = NULL;
}
if (pidfile)
VIR_FREE(*pidfile);
return NULL;
}
virDomainDefPtr qemuParseCommandLineString(virCapsPtr caps,
virDomainXMLOptionPtr xmlopt,
const char *args,
char **pidfile,
virDomainChrSourceDefPtr *monConfig,
bool *monJSON)
{
char **progenv = NULL;
char **progargv = NULL;
virDomainDefPtr def = NULL;
if (qemuStringToArgvEnv(args, &progenv, &progargv) < 0)
goto cleanup;
def = qemuParseCommandLine(caps, xmlopt, progenv, progargv,
pidfile, monConfig, monJSON);
cleanup:
virStringFreeList(progargv);
virStringFreeList(progenv);
return def;
}
static int qemuParseProcFileStrings(int pid_value,
const char *name,
char ***list)
{
char *path = NULL;
int ret = -1;
char *data = NULL;
ssize_t len;
char *tmp;
size_t nstr = 0;
char **str = NULL;
if (virAsprintf(&path, "/proc/%d/%s", pid_value, name) < 0)
goto cleanup;
if ((len = virFileReadAll(path, 1024*128, &data)) < 0)
goto cleanup;
tmp = data;
while (tmp < (data + len)) {
if (VIR_EXPAND_N(str, nstr, 1) < 0)
goto cleanup;
if (VIR_STRDUP(str[nstr-1], tmp) < 0)
goto cleanup;
/* Skip arg */
tmp += strlen(tmp);
/* Skip \0 separator */
tmp++;
}
if (VIR_EXPAND_N(str, nstr, 1) < 0)
goto cleanup;
str[nstr-1] = NULL;
ret = nstr-1;
*list = str;
cleanup:
if (ret < 0)
virStringFreeList(str);
VIR_FREE(data);
VIR_FREE(path);
return ret;
}
virDomainDefPtr qemuParseCommandLinePid(virCapsPtr caps,
virDomainXMLOptionPtr xmlopt,
pid_t pid,
char **pidfile,
virDomainChrSourceDefPtr *monConfig,
bool *monJSON)
{
virDomainDefPtr def = NULL;
char **progargv = NULL;
char **progenv = NULL;
char *exepath = NULL;
char *emulator;
/* The parser requires /proc/pid, which only exists on platforms
* like Linux where pid_t fits in int. */
if ((int) pid != pid ||
qemuParseProcFileStrings(pid, "cmdline", &progargv) < 0 ||
qemuParseProcFileStrings(pid, "environ", &progenv) < 0)
goto cleanup;
if (!(def = qemuParseCommandLine(caps, xmlopt, progenv, progargv,
pidfile, monConfig, monJSON)))
goto cleanup;
if (virAsprintf(&exepath, "/proc/%d/exe", (int) pid) < 0)
goto cleanup;
if (virFileResolveLink(exepath, &emulator) < 0) {
virReportSystemError(errno,
_("Unable to resolve %s for pid %u"),
exepath, (int) pid);
goto cleanup;
}
VIR_FREE(def->emulator);
def->emulator = emulator;
cleanup:
VIR_FREE(exepath);
virStringFreeList(progargv);
virStringFreeList(progenv);
return def;
}