From d860b2f537fda94b77e464005820e5f40334ad5c Mon Sep 17 00:00:00 2001 From: John Ferlan Date: Wed, 10 Feb 2016 07:33:47 -0500 Subject: [PATCH] qemu: Split the command parsing routines into own module Extract out the qemuParseCommandLine{String|Pid} into their own separate module - taking with it all the various static functions. Causes a ripple effect with a few other modules to include the new qemu_parse_command.h. Narrowed down the list of #include's in the split out module to those that are necessary for build. --- po/POTFILES.in | 1 + src/Makefile.am | 1 + src/qemu/qemu_command.c | 2708 -------------------------------- src/qemu/qemu_command.h | 28 +- src/qemu/qemu_domain.c | 3 +- src/qemu/qemu_driver.c | 1 + src/qemu/qemu_monitor_json.c | 3 +- src/qemu/qemu_parse_command.c | 2742 +++++++++++++++++++++++++++++++++ src/qemu/qemu_parse_command.h | 53 + tests/qemuargv2xmltest.c | 2 +- 10 files changed, 2805 insertions(+), 2737 deletions(-) create mode 100644 src/qemu/qemu_parse_command.c create mode 100644 src/qemu/qemu_parse_command.h diff --git a/po/POTFILES.in b/po/POTFILES.in index 82e8d3e814..4d82a8f2e5 100644 --- a/po/POTFILES.in +++ b/po/POTFILES.in @@ -132,6 +132,7 @@ src/qemu/qemu_migration.c src/qemu/qemu_monitor.c src/qemu/qemu_monitor_json.c src/qemu/qemu_monitor_text.c +src/qemu/qemu_parse_command.c src/qemu/qemu_process.c src/remote/remote_client_bodies.h src/remote/remote_driver.c diff --git a/src/Makefile.am b/src/Makefile.am index a4aef0f12d..f857e59c12 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -794,6 +794,7 @@ QEMU_DRIVER_SOURCES = \ qemu/qemu_blockjob.c qemu/qemu_blockjob.h \ qemu/qemu_capabilities.c qemu/qemu_capabilities.h \ qemu/qemu_command.c qemu/qemu_command.h \ + qemu/qemu_parse_command.c qemu/qemu_parse_command.h \ qemu/qemu_domain.c qemu/qemu_domain.h \ qemu/qemu_cgroup.c qemu/qemu_cgroup.h \ qemu/qemu_hostdev.c qemu/qemu_hostdev.h \ diff --git a/src/qemu/qemu_command.c b/src/qemu/qemu_command.c index a723e2aed8..7a8ae7322b 100644 --- a/src/qemu/qemu_command.c +++ b/src/qemu/qemu_command.c @@ -39,7 +39,6 @@ #include "virstring.h" #include "virtime.h" #include "viruuid.h" -#include "c-ctype.h" #include "domain_nwfilter.h" #include "domain_addr.h" #include "domain_audit.h" @@ -95,8 +94,6 @@ VIR_ENUM_IMPL(qemuDiskCacheV2, VIR_DOMAIN_DISK_CACHE_LAST, "directsync", "unsafe"); -VIR_ENUM_DECL(qemuVideo) - VIR_ENUM_IMPL(qemuVideo, VIR_DOMAIN_VIDEO_TYPE_LAST, "std", "cirrus", @@ -3027,233 +3024,6 @@ qemuGetSecretString(virConnectPtr conn, } -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; -} - - static int qemuNetworkDriveGetPort(int protocol, const char *port) @@ -11647,2481 +11417,3 @@ qemuBuildChrDeviceStr(char **deviceStr, return ret; } - - -/* - * 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 - * '=', 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 qemuCaps, - 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 = qemuCaps->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(qemuCaps, 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 (virDomainDefAddImplicitControllers(def) < 0) - goto error; - - if (virDomainDefPostParse(def, qemuCaps, 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 qemuCaps, - 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(qemuCaps, 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 qemuCaps, - 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(qemuCaps, 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; -} diff --git a/src/qemu/qemu_command.h b/src/qemu/qemu_command.h index 53bfda57d6..f549aa5390 100644 --- a/src/qemu/qemu_command.h +++ b/src/qemu/qemu_command.h @@ -1,7 +1,7 @@ /* * qemu_command.h: QEMU command generation * - * Copyright (C) 2006-2015 Red Hat, Inc. + * Copyright (C) 2006-2016 Red Hat, Inc. * Copyright (C) 2006 Daniel P. Berrange * * This library is free software; you can redistribute it and/or @@ -54,7 +54,7 @@ # define QEMU_MIGRATION_PORT_MIN 49152 # define QEMU_MIGRATION_PORT_MAX 49215 -# define QEMU_QXL_VGAMEM_DEFAULT 16 * 1024 +VIR_ENUM_DECL(qemuVideo) typedef struct _qemuBuildCommandLineCallbacks qemuBuildCommandLineCallbacks; typedef qemuBuildCommandLineCallbacks *qemuBuildCommandLineCallbacksPtr; @@ -245,23 +245,6 @@ int qemuOpenVhostNet(virDomainDefPtr def, int qemuNetworkPrepareDevices(virDomainDefPtr def); -/* - * NB: def->name can be NULL upon return and the caller - * *must* decide how to fill in a name in this case - */ -virDomainDefPtr qemuParseCommandLineString(virCapsPtr qemuCaps, - virDomainXMLOptionPtr xmlopt, - const char *args, - char **pidfile, - virDomainChrSourceDefPtr *monConfig, - bool *monJSON); -virDomainDefPtr qemuParseCommandLinePid(virCapsPtr qemuCaps, - virDomainXMLOptionPtr xmlopt, - pid_t pid, - char **pidfile, - virDomainChrSourceDefPtr *monConfig, - bool *monJSON); - int qemuDomainAssignAddresses(virDomainDefPtr def, virQEMUCapsPtr qemuCaps, virDomainObjPtr obj) @@ -291,13 +274,6 @@ int qemuAssignDeviceChrAlias(virDomainDefPtr def, ssize_t idx); int qemuAssignDeviceRNGAlias(virDomainRNGDefPtr rng, size_t idx); -int -qemuParseKeywords(const char *str, - char ***retkeywords, - char ***retvalues, - int *retnkeywords, - int allowEmptyValue); - int qemuGetDriveSourceString(virStorageSourcePtr src, virConnectPtr conn, char **source); diff --git a/src/qemu/qemu_domain.c b/src/qemu/qemu_domain.c index c4fa860556..495c76b3c0 100644 --- a/src/qemu/qemu_domain.c +++ b/src/qemu/qemu_domain.c @@ -1,7 +1,7 @@ /* * qemu_domain.c: QEMU domain private state * - * Copyright (C) 2006-2015 Red Hat, Inc. + * Copyright (C) 2006-2016 Red Hat, Inc. * Copyright (C) 2006 Daniel P. Berrange * * This library is free software; you can redistribute it and/or @@ -25,6 +25,7 @@ #include "qemu_domain.h" #include "qemu_command.h" +#include "qemu_parse_command.h" #include "qemu_capabilities.h" #include "qemu_migration.h" #include "viralloc.h" diff --git a/src/qemu/qemu_driver.c b/src/qemu/qemu_driver.c index fe2f527446..2bbc7242bc 100644 --- a/src/qemu/qemu_driver.c +++ b/src/qemu/qemu_driver.c @@ -49,6 +49,7 @@ #include "qemu_conf.h" #include "qemu_capabilities.h" #include "qemu_command.h" +#include "qemu_parse_command.h" #include "qemu_cgroup.h" #include "qemu_hostdev.h" #include "qemu_hotplug.h" diff --git a/src/qemu/qemu_monitor_json.c b/src/qemu/qemu_monitor_json.c index 24a8865b5a..d2b641fc67 100644 --- a/src/qemu/qemu_monitor_json.c +++ b/src/qemu/qemu_monitor_json.c @@ -1,7 +1,7 @@ /* * qemu_monitor_json.c: interaction with QEMU monitor console * - * Copyright (C) 2006-2015 Red Hat, Inc. + * Copyright (C) 2006-2016 Red Hat, Inc. * Copyright (C) 2006 Daniel P. Berrange * * This library is free software; you can redistribute it and/or @@ -34,6 +34,7 @@ #include "qemu_monitor_text.h" #include "qemu_monitor_json.h" #include "qemu_command.h" +#include "qemu_parse_command.h" #include "qemu_capabilities.h" #include "viralloc.h" #include "virlog.h" diff --git a/src/qemu/qemu_parse_command.c b/src/qemu/qemu_parse_command.c new file mode 100644 index 0000000000..ca59f9177d --- /dev/null +++ b/src/qemu/qemu_parse_command.c @@ -0,0 +1,2742 @@ +/* + * 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 + * . + * + * Author: Daniel P. Berrange + */ + +#include + +#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 + * '=', 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 qemuCaps, + 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 = qemuCaps->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(qemuCaps, 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 (virDomainDefAddImplicitControllers(def) < 0) + goto error; + + if (virDomainDefPostParse(def, qemuCaps, 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 qemuCaps, + 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(qemuCaps, 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 qemuCaps, + 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(qemuCaps, 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; +} diff --git a/src/qemu/qemu_parse_command.h b/src/qemu/qemu_parse_command.h new file mode 100644 index 0000000000..a7431893ec --- /dev/null +++ b/src/qemu/qemu_parse_command.h @@ -0,0 +1,53 @@ +/* + * qemu_parse_command.h: 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 + * . + * + * Author: Daniel P. Berrange + */ + +#ifndef __QEMU_PARSE_COMMAND_H__ +# define __QEMU_PARSE_COMMAND_H__ + +# define QEMU_QXL_VGAMEM_DEFAULT 16 * 1024 + +/* + * NB: def->name can be NULL upon return and the caller + * *must* decide how to fill in a name in this case + */ +virDomainDefPtr qemuParseCommandLineString(virCapsPtr qemuCaps, + virDomainXMLOptionPtr xmlopt, + const char *args, + char **pidfile, + virDomainChrSourceDefPtr *monConfig, + bool *monJSON); +virDomainDefPtr qemuParseCommandLinePid(virCapsPtr qemuCaps, + virDomainXMLOptionPtr xmlopt, + pid_t pid, + char **pidfile, + virDomainChrSourceDefPtr *monConfig, + bool *monJSON); + +int +qemuParseKeywords(const char *str, + char ***retkeywords, + char ***retvalues, + int *retnkeywords, + int allowEmptyValue); + +#endif /* __QEMU_PARSE_COMMAND_H__*/ diff --git a/tests/qemuargv2xmltest.c b/tests/qemuargv2xmltest.c index ac06bede9a..6671be4f9b 100644 --- a/tests/qemuargv2xmltest.c +++ b/tests/qemuargv2xmltest.c @@ -13,7 +13,7 @@ #ifdef WITH_QEMU # include "internal.h" -# include "qemu/qemu_command.h" +# include "qemu/qemu_parse_command.h" # include "testutilsqemu.h" # include "virstring.h"