[libvirt] [PATCH v2 0/3] Check return status for virDomainGraphicsListenSetAddress

v1: http://www.redhat.com/archives/libvir-list/2016-February/msg00428.html Adjustments since v1: * Implement suggestions - created qemuParseCommandLineVnc to handle the alloc/free of the 'vnc'... Note: Difference in refactored code is to use of "cleanup" instead of "error" and "ret = -1" initializer. * Modify the virDomainGraphicsListenSetAddress, to have one call instead of two... adjusting the 'val' based on whether find '[' in first char * Split out the qemuParseCommandLine{String|Pid} functions into their own module along with all the collateral damage that causes. Pared down the list of #includes for new modules by build trial and error. John Ferlan (3): qemu: Introduce qemuParseCommandLineVnc qemu: Check return status for virDomainGraphicsListenSetAddress qemu: Split the command parsing routines into own module po/POTFILES.in | 1 + src/Makefile.am | 1 + src/qemu/qemu_command.c | 2703 ---------------------------------------- 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 | 2744 +++++++++++++++++++++++++++++++++++++++++ src/qemu/qemu_parse_command.h | 53 + tests/qemuargv2xmltest.c | 2 +- 10 files changed, 2807 insertions(+), 2732 deletions(-) create mode 100644 src/qemu/qemu_parse_command.c create mode 100644 src/qemu/qemu_parse_command.h -- 2.5.0

Refactor qemuParseCommandLine to pull out the "-vnc" argument parsing into its own helper function. Modify the code to use "cleanup" instead of "error" and use the standard return processing to indicate success or failure by using ret Signed-off-by: John Ferlan <jferlan@redhat.com> --- src/qemu/qemu_command.c | 255 +++++++++++++++++++++++++----------------------- 1 file changed, 132 insertions(+), 123 deletions(-) diff --git a/src/qemu/qemu_command.c b/src/qemu/qemu_command.c index d7f19f3..d09a063 100644 --- a/src/qemu/qemu_command.c +++ b/src/qemu/qemu_command.c @@ -11874,6 +11874,137 @@ qemuParseKeywords(const char *str, 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] == '[') + virDomainGraphicsListenSetAddress(vnc, 0, + val+1, tmp-(val+1), true); + else + virDomainGraphicsListenSetAddress(vnc, 0, + val, tmp-val, true); + if (!virDomainGraphicsListenGetAddress(vnc, 0)) + goto cleanup; + + if (*opts == ',') { + char *orig_opts; + + if (VIR_STRDUP(orig_opts, opts + 1) < 0) + goto cleanup; + opts = orig_opts; + + while (opts && *opts) { + char *nextopt = strchr(opts, ','); + if (nextopt) + *(nextopt++) = '\0'; + + if (STRPREFIX(opts, "websocket")) { + char *websocket = opts + strlen("websocket"); + if (*(websocket++) == '=' && + *websocket) { + /* If the websocket continues with + * '=<something>', we'll parse it */ + if (virStrToLong_i(websocket, + NULL, 0, + &vnc->data.vnc.websocket) < 0) { + virReportError(VIR_ERR_INTERNAL_ERROR, + _("cannot parse VNC " + "WebSocket port '%s'"), + websocket); + VIR_FREE(orig_opts); + goto cleanup; + } + } else { + /* Otherwise, we'll compute the port the same + * way QEMU does, by adding a 5700 to the + * display value. */ + vnc->data.vnc.websocket = + vnc->data.vnc.port + 5700; + } + } else if (STRPREFIX(opts, "share=")) { + char *sharePolicy = opts + strlen("share="); + if (sharePolicy && *sharePolicy) { + int policy = + virDomainGraphicsVNCSharePolicyTypeFromString(sharePolicy); + + if (policy < 0) { + virReportError(VIR_ERR_INTERNAL_ERROR, + _("unknown vnc display sharing policy '%s'"), + sharePolicy); + VIR_FREE(orig_opts); + goto cleanup; + } else { + vnc->data.vnc.sharePolicy = policy; + } + } else { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("missing vnc sharing policy")); + VIR_FREE(orig_opts); + goto cleanup; + } + } + + opts = nextopt; + } + VIR_FREE(orig_opts); + } + vnc->data.vnc.port += 5900; + vnc->data.vnc.autoport = false; + } + + if (VIR_APPEND_ELEMENT(def->graphics, def->ngraphics, vnc) < 0) + goto cleanup; + + ret = 0; + + cleanup: + virDomainGraphicsDefFree(vnc); + return ret; +} + + /* * Tries to parse new style QEMU -drive args. * @@ -13100,131 +13231,9 @@ qemuParseCommandLine(virCapsPtr qemuCaps, arg++; if (STREQ(arg, "-vnc")) { - virDomainGraphicsDefPtr vnc; - char *tmp; WANT_VALUE(); - if (VIR_ALLOC(vnc) < 0) + if (qemuParseCommandLineVnc(def, val) < 0) goto error; - 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) { - virDomainGraphicsDefFree(vnc); - goto error; - } - } 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) { - virDomainGraphicsDefFree(vnc); - virReportError(VIR_ERR_INTERNAL_ERROR, - _("missing VNC port number in '%s'"), val); - goto error; - } - port = tmp + strlen(sep); - if (virStrToLong_i(port, &opts, 10, - &vnc->data.vnc.port) < 0) { - virDomainGraphicsDefFree(vnc); - virReportError(VIR_ERR_INTERNAL_ERROR, - _("cannot parse VNC port '%s'"), port); - goto error; - } - if (val[0] == '[') - virDomainGraphicsListenSetAddress(vnc, 0, - val+1, tmp-(val+1), true); - else - virDomainGraphicsListenSetAddress(vnc, 0, - val, tmp-val, true); - if (!virDomainGraphicsListenGetAddress(vnc, 0)) { - virDomainGraphicsDefFree(vnc); - goto error; - } - - if (*opts == ',') { - char *orig_opts; - - if (VIR_STRDUP(orig_opts, opts + 1) < 0) { - virDomainGraphicsDefFree(vnc); - goto error; - } - opts = orig_opts; - - while (opts && *opts) { - char *nextopt = strchr(opts, ','); - if (nextopt) - *(nextopt++) = '\0'; - - if (STRPREFIX(opts, "websocket")) { - char *websocket = opts + strlen("websocket"); - if (*(websocket++) == '=' && - *websocket) { - /* If the websocket continues with - * '=<something>', we'll parse it */ - if (virStrToLong_i(websocket, - NULL, 0, - &vnc->data.vnc.websocket) < 0) { - virReportError(VIR_ERR_INTERNAL_ERROR, - _("cannot parse VNC " - "WebSocket port '%s'"), - websocket); - virDomainGraphicsDefFree(vnc); - VIR_FREE(orig_opts); - goto error; - } - } 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); - virDomainGraphicsDefFree(vnc); - VIR_FREE(orig_opts); - goto error; - } else { - vnc->data.vnc.sharePolicy = policy; - } - } else { - virReportError(VIR_ERR_INTERNAL_ERROR, "%s", - _("missing vnc sharing policy")); - virDomainGraphicsDefFree(vnc); - VIR_FREE(orig_opts); - goto error; - } - } - - 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) { - virDomainGraphicsDefFree(vnc); - goto error; - } } else if (STREQ(arg, "-sdl")) { have_sdl = true; } else if (STREQ(arg, "-m")) { -- 2.5.0

Recent refactors in the vbox code to check the return status for the function tipped Coverity's scales of justice for any functions that do not check status - such as this one. While I'm at it, since the call is essentially the same other than whether starting from val or val+1 when val[0] = '[', just adjust the val pointer by one and have one call instead of two. Signed-off-by: John Ferlan <jferlan@redhat.com> --- src/qemu/qemu_command.c | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/src/qemu/qemu_command.c b/src/qemu/qemu_command.c index d09a063..9d530b6 100644 --- a/src/qemu/qemu_command.c +++ b/src/qemu/qemu_command.c @@ -11920,11 +11920,9 @@ qemuParseCommandLineVnc(virDomainDefPtr def, goto cleanup; } if (val[0] == '[') - virDomainGraphicsListenSetAddress(vnc, 0, - val+1, tmp-(val+1), true); - else - virDomainGraphicsListenSetAddress(vnc, 0, - val, tmp-val, true); + val++; + if (virDomainGraphicsListenSetAddress(vnc, 0, val, tmp-val, true) < 0) + goto cleanup; if (!virDomainGraphicsListenGetAddress(vnc, 0)) goto cleanup; -- 2.5.0

On 02/10/2016 08:05 AM, John Ferlan wrote:
Recent refactors in the vbox code to check the return status for the function tipped Coverity's scales of justice for any functions that do not check status - such as this one.
While I'm at it, since the call is essentially the same other than whether starting from val or val+1 when val[0] = '[', just adjust the val pointer by one and have one call instead of two.
Signed-off-by: John Ferlan <jferlan@redhat.com> --- src/qemu/qemu_command.c | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-)
diff --git a/src/qemu/qemu_command.c b/src/qemu/qemu_command.c index d09a063..9d530b6 100644 --- a/src/qemu/qemu_command.c +++ b/src/qemu/qemu_command.c @@ -11920,11 +11920,9 @@ qemuParseCommandLineVnc(virDomainDefPtr def, goto cleanup; } if (val[0] == '[') - virDomainGraphicsListenSetAddress(vnc, 0, - val+1, tmp-(val+1), true); - else - virDomainGraphicsListenSetAddress(vnc, 0, - val, tmp-val, true); + val++; + if (virDomainGraphicsListenSetAddress(vnc, 0, val, tmp-val, true) < 0) + goto cleanup; if (!virDomainGraphicsListenGetAddress(vnc, 0)) goto cleanup;
Jan was correct when he suggested that the above call to virDomainGraphicsListenGetAddress() is now redundant - it is only there to force an early return if there was a failure in setting the address, and you're now checking for that already.

On 02/10/2016 11:55 AM, Laine Stump wrote:
On 02/10/2016 08:05 AM, John Ferlan wrote:
Recent refactors in the vbox code to check the return status for the function tipped Coverity's scales of justice for any functions that do not check status - such as this one.
While I'm at it, since the call is essentially the same other than whether starting from val or val+1 when val[0] = '[', just adjust the val pointer by one and have one call instead of two.
Signed-off-by: John Ferlan <jferlan@redhat.com> --- src/qemu/qemu_command.c | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-)
diff --git a/src/qemu/qemu_command.c b/src/qemu/qemu_command.c index d09a063..9d530b6 100644 --- a/src/qemu/qemu_command.c +++ b/src/qemu/qemu_command.c @@ -11920,11 +11920,9 @@ qemuParseCommandLineVnc(virDomainDefPtr def, goto cleanup; } if (val[0] == '[') - virDomainGraphicsListenSetAddress(vnc, 0, - val+1, tmp-(val+1), true); - else - virDomainGraphicsListenSetAddress(vnc, 0, - val, tmp-val, true); + val++; + if (virDomainGraphicsListenSetAddress(vnc, 0, val, tmp-val, true) < 0) + goto cleanup; if (!virDomainGraphicsListenGetAddress(vnc, 0)) goto cleanup;
Jan was correct when he suggested that the above call to virDomainGraphicsListenGetAddress() is now redundant - it is only there to force an early return if there was a failure in setting the address, and you're now checking for that already.
Old age myopia... I was focused on just the SetListAddress return value not being checked... I'll remove the redundant ListGetAddress call. John

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. Signed-off-by: John Ferlan <jferlan@redhat.com> --- po/POTFILES.in | 1 + src/Makefile.am | 1 + src/qemu/qemu_command.c | 2710 ---------------------------------------- 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 | 2744 +++++++++++++++++++++++++++++++++++++++++ src/qemu/qemu_parse_command.h | 53 + tests/qemuargv2xmltest.c | 2 +- 10 files changed, 2807 insertions(+), 2739 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 82e8d3e..4d82a8f 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 a4aef0f..f857e59 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 9d530b6..7a8ae73 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", @@ -3028,233 +3025,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,2483 +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 (!virDomainGraphicsListenGetAddress(vnc, 0)) - goto cleanup; - - if (*opts == ',') { - char *orig_opts; - - if (VIR_STRDUP(orig_opts, opts + 1) < 0) - goto cleanup; - opts = orig_opts; - - while (opts && *opts) { - char *nextopt = strchr(opts, ','); - if (nextopt) - *(nextopt++) = '\0'; - - if (STRPREFIX(opts, "websocket")) { - char *websocket = opts + strlen("websocket"); - if (*(websocket++) == '=' && - *websocket) { - /* If the websocket continues with - * '=<something>', we'll parse it */ - if (virStrToLong_i(websocket, - NULL, 0, - &vnc->data.vnc.websocket) < 0) { - virReportError(VIR_ERR_INTERNAL_ERROR, - _("cannot parse VNC " - "WebSocket port '%s'"), - websocket); - VIR_FREE(orig_opts); - goto cleanup; - } - } else { - /* Otherwise, we'll compute the port the same - * way QEMU does, by adding a 5700 to the - * display value. */ - vnc->data.vnc.websocket = - vnc->data.vnc.port + 5700; - } - } else if (STRPREFIX(opts, "share=")) { - char *sharePolicy = opts + strlen("share="); - if (sharePolicy && *sharePolicy) { - int policy = - virDomainGraphicsVNCSharePolicyTypeFromString(sharePolicy); - - if (policy < 0) { - virReportError(VIR_ERR_INTERNAL_ERROR, - _("unknown vnc display sharing policy '%s'"), - sharePolicy); - VIR_FREE(orig_opts); - goto cleanup; - } else { - vnc->data.vnc.sharePolicy = policy; - } - } else { - virReportError(VIR_ERR_INTERNAL_ERROR, "%s", - _("missing vnc sharing policy")); - VIR_FREE(orig_opts); - goto cleanup; - } - } - - opts = nextopt; - } - VIR_FREE(orig_opts); - } - vnc->data.vnc.port += 5900; - vnc->data.vnc.autoport = false; - } - - if (VIR_APPEND_ELEMENT(def->graphics, def->ngraphics, vnc) < 0) - goto cleanup; - - ret = 0; - - cleanup: - virDomainGraphicsDefFree(vnc); - return ret; -} - - -/* - * Tries to parse new style QEMU -drive args. - * - * eg -drive file=/dev/HostVG/VirtData1,if=ide,index=1 - * - * Will fail if not using the 'index' keyword - */ -static virDomainDiskDefPtr -qemuParseCommandLineDisk(virDomainXMLOptionPtr xmlopt, - const char *val, - virDomainDefPtr dom, - int nvirtiodisk, - bool old_style_ceph_args) -{ - virDomainDiskDefPtr def = NULL; - char **keywords; - char **values; - int nkeywords; - size_t i; - int idx = -1; - int busid = -1; - int unitid = -1; - - if (qemuParseKeywords(val, - &keywords, - &values, - &nkeywords, - 0) < 0) - return NULL; - - if (VIR_ALLOC(def) < 0) - goto cleanup; - if (VIR_ALLOC(def->src) < 0) - goto error; - - if ((ARCH_IS_PPC64(dom->os.arch) && - dom->os.machine && STRPREFIX(dom->os.machine, "pseries"))) - def->bus = VIR_DOMAIN_DISK_BUS_SCSI; - else - def->bus = VIR_DOMAIN_DISK_BUS_IDE; - def->device = VIR_DOMAIN_DISK_DEVICE_DISK; - def->src->type = VIR_STORAGE_TYPE_FILE; - - for (i = 0; i < nkeywords; i++) { - if (STREQ(keywords[i], "file")) { - if (values[i] && STRNEQ(values[i], "")) { - def->src->path = values[i]; - values[i] = NULL; - if (STRPREFIX(def->src->path, "/dev/")) - def->src->type = VIR_STORAGE_TYPE_BLOCK; - else if (STRPREFIX(def->src->path, "nbd:") || - STRPREFIX(def->src->path, "nbd+")) { - def->src->type = VIR_STORAGE_TYPE_NETWORK; - def->src->protocol = VIR_STORAGE_NET_PROTOCOL_NBD; - - if (qemuParseNBDString(def) < 0) - goto error; - } else if (STRPREFIX(def->src->path, "rbd:")) { - char *p = def->src->path; - - def->src->type = VIR_STORAGE_TYPE_NETWORK; - def->src->protocol = VIR_STORAGE_NET_PROTOCOL_RBD; - if (VIR_STRDUP(def->src->path, p + strlen("rbd:")) < 0) - goto error; - /* old-style CEPH_ARGS env variable is parsed later */ - if (!old_style_ceph_args && qemuParseRBDString(def) < 0) { - VIR_FREE(p); - goto error; - } - - VIR_FREE(p); - } else if (STRPREFIX(def->src->path, "gluster:") || - STRPREFIX(def->src->path, "gluster+")) { - def->src->type = VIR_STORAGE_TYPE_NETWORK; - def->src->protocol = VIR_STORAGE_NET_PROTOCOL_GLUSTER; - - if (qemuParseGlusterString(def) < 0) - goto error; - } else if (STRPREFIX(def->src->path, "iscsi:")) { - def->src->type = VIR_STORAGE_TYPE_NETWORK; - def->src->protocol = VIR_STORAGE_NET_PROTOCOL_ISCSI; - - if (qemuParseISCSIString(def) < 0) - goto error; - } else if (STRPREFIX(def->src->path, "sheepdog:")) { - char *p = def->src->path; - char *port, *vdi; - - def->src->type = VIR_STORAGE_TYPE_NETWORK; - def->src->protocol = VIR_STORAGE_NET_PROTOCOL_SHEEPDOG; - if (VIR_STRDUP(def->src->path, p + strlen("sheepdog:")) < 0) - goto error; - VIR_FREE(p); - - /* def->src->path must be [vdiname] or [host]:[port]:[vdiname] */ - port = strchr(def->src->path, ':'); - if (port) { - *port = '\0'; - vdi = strchr(port + 1, ':'); - if (!vdi) { - *port = ':'; - virReportError(VIR_ERR_INTERNAL_ERROR, - _("cannot parse sheepdog filename '%s'"), - def->src->path); - goto error; - } - port++; - *vdi++ = '\0'; - if (VIR_ALLOC(def->src->hosts) < 0) - goto error; - def->src->nhosts = 1; - def->src->hosts->name = def->src->path; - if (VIR_STRDUP(def->src->hosts->port, port) < 0) - goto error; - def->src->hosts->transport = VIR_STORAGE_NET_HOST_TRANS_TCP; - def->src->hosts->socket = NULL; - if (VIR_STRDUP(def->src->path, vdi) < 0) - goto error; - } - } else { - def->src->type = VIR_STORAGE_TYPE_FILE; - } - } else { - def->src->type = VIR_STORAGE_TYPE_FILE; - } - } else if (STREQ(keywords[i], "if")) { - if (STREQ(values[i], "ide")) { - def->bus = VIR_DOMAIN_DISK_BUS_IDE; - if ((ARCH_IS_PPC64(dom->os.arch) && - dom->os.machine && STRPREFIX(dom->os.machine, "pseries"))) { - virReportError(VIR_ERR_INTERNAL_ERROR, - _("pseries systems do not support ide devices '%s'"), val); - goto error; - } - } else if (STREQ(values[i], "scsi")) { - def->bus = VIR_DOMAIN_DISK_BUS_SCSI; - } else if (STREQ(values[i], "floppy")) { - def->bus = VIR_DOMAIN_DISK_BUS_FDC; - def->device = VIR_DOMAIN_DISK_DEVICE_FLOPPY; - } else if (STREQ(values[i], "virtio")) { - def->bus = VIR_DOMAIN_DISK_BUS_VIRTIO; - } else if (STREQ(values[i], "xen")) { - def->bus = VIR_DOMAIN_DISK_BUS_XEN; - } else if (STREQ(values[i], "sd")) { - def->bus = VIR_DOMAIN_DISK_BUS_SD; - } - } else if (STREQ(keywords[i], "media")) { - if (STREQ(values[i], "cdrom")) { - def->device = VIR_DOMAIN_DISK_DEVICE_CDROM; - def->src->readonly = true; - } else if (STREQ(values[i], "floppy")) { - def->device = VIR_DOMAIN_DISK_DEVICE_FLOPPY; - } - } else if (STREQ(keywords[i], "format")) { - if (VIR_STRDUP(def->src->driverName, "qemu") < 0) - goto error; - def->src->format = virStorageFileFormatTypeFromString(values[i]); - } else if (STREQ(keywords[i], "cache")) { - if (STREQ(values[i], "off") || - STREQ(values[i], "none")) - def->cachemode = VIR_DOMAIN_DISK_CACHE_DISABLE; - else if (STREQ(values[i], "writeback") || - STREQ(values[i], "on")) - def->cachemode = VIR_DOMAIN_DISK_CACHE_WRITEBACK; - else if (STREQ(values[i], "writethrough")) - def->cachemode = VIR_DOMAIN_DISK_CACHE_WRITETHRU; - else if (STREQ(values[i], "directsync")) - def->cachemode = VIR_DOMAIN_DISK_CACHE_DIRECTSYNC; - else if (STREQ(values[i], "unsafe")) - def->cachemode = VIR_DOMAIN_DISK_CACHE_UNSAFE; - } else if (STREQ(keywords[i], "werror")) { - if (STREQ(values[i], "stop")) - def->error_policy = VIR_DOMAIN_DISK_ERROR_POLICY_STOP; - else if (STREQ(values[i], "report")) - def->error_policy = VIR_DOMAIN_DISK_ERROR_POLICY_REPORT; - else if (STREQ(values[i], "ignore")) - def->error_policy = VIR_DOMAIN_DISK_ERROR_POLICY_IGNORE; - else if (STREQ(values[i], "enospc")) - def->error_policy = VIR_DOMAIN_DISK_ERROR_POLICY_ENOSPACE; - } else if (STREQ(keywords[i], "rerror")) { - if (STREQ(values[i], "stop")) - def->rerror_policy = VIR_DOMAIN_DISK_ERROR_POLICY_STOP; - else if (STREQ(values[i], "report")) - def->rerror_policy = VIR_DOMAIN_DISK_ERROR_POLICY_REPORT; - else if (STREQ(values[i], "ignore")) - def->rerror_policy = VIR_DOMAIN_DISK_ERROR_POLICY_IGNORE; - } else if (STREQ(keywords[i], "index")) { - if (virStrToLong_i(values[i], NULL, 10, &idx) < 0) { - virReportError(VIR_ERR_INTERNAL_ERROR, - _("cannot parse drive index '%s'"), val); - goto error; - } - } else if (STREQ(keywords[i], "bus")) { - if (virStrToLong_i(values[i], NULL, 10, &busid) < 0) { - virReportError(VIR_ERR_INTERNAL_ERROR, - _("cannot parse drive bus '%s'"), val); - goto error; - } - } else if (STREQ(keywords[i], "unit")) { - if (virStrToLong_i(values[i], NULL, 10, &unitid) < 0) { - virReportError(VIR_ERR_INTERNAL_ERROR, - _("cannot parse drive unit '%s'"), val); - goto error; - } - } else if (STREQ(keywords[i], "readonly")) { - if ((values[i] == NULL) || STREQ(values[i], "on")) - def->src->readonly = true; - } else if (STREQ(keywords[i], "aio")) { - if ((def->iomode = virDomainDiskIoTypeFromString(values[i])) < 0) { - virReportError(VIR_ERR_INTERNAL_ERROR, - _("cannot parse io mode '%s'"), values[i]); - goto error; - } - } else if (STREQ(keywords[i], "cyls")) { - if (virStrToLong_ui(values[i], NULL, 10, - &(def->geometry.cylinders)) < 0) { - virDomainDiskDefFree(def); - def = NULL; - virReportError(VIR_ERR_INTERNAL_ERROR, - _("cannot parse cylinders value'%s'"), - values[i]); - goto error; - } - } else if (STREQ(keywords[i], "heads")) { - if (virStrToLong_ui(values[i], NULL, 10, - &(def->geometry.heads)) < 0) { - virDomainDiskDefFree(def); - def = NULL; - virReportError(VIR_ERR_INTERNAL_ERROR, - _("cannot parse heads value'%s'"), - values[i]); - goto error; - } - } else if (STREQ(keywords[i], "secs")) { - if (virStrToLong_ui(values[i], NULL, 10, - &(def->geometry.sectors)) < 0) { - virDomainDiskDefFree(def); - def = NULL; - virReportError(VIR_ERR_INTERNAL_ERROR, - _("cannot parse sectors value'%s'"), - values[i]); - goto error; - } - } else if (STREQ(keywords[i], "trans")) { - def->geometry.trans = - virDomainDiskGeometryTransTypeFromString(values[i]); - if ((def->geometry.trans < VIR_DOMAIN_DISK_TRANS_DEFAULT) || - (def->geometry.trans >= VIR_DOMAIN_DISK_TRANS_LAST)) { - virDomainDiskDefFree(def); - def = NULL; - virReportError(VIR_ERR_INTERNAL_ERROR, - _("cannot parse translation value '%s'"), - values[i]); - goto error; - } - } - } - - if (def->rerror_policy == def->error_policy) - def->rerror_policy = 0; - - if (!def->src->path && - def->device == VIR_DOMAIN_DISK_DEVICE_DISK && - def->src->type != VIR_STORAGE_TYPE_NETWORK) { - virReportError(VIR_ERR_INTERNAL_ERROR, - _("missing file parameter in drive '%s'"), val); - goto error; - } - if (idx == -1 && - def->bus == VIR_DOMAIN_DISK_BUS_VIRTIO) - idx = nvirtiodisk; - - if (idx == -1 && - unitid == -1 && - busid == -1) { - virReportError(VIR_ERR_INTERNAL_ERROR, - _("missing index/unit/bus parameter in drive '%s'"), - val); - goto error; - } - - if (idx == -1) { - if (unitid == -1) - unitid = 0; - if (busid == -1) - busid = 0; - switch (def->bus) { - case VIR_DOMAIN_DISK_BUS_IDE: - idx = (busid * 2) + unitid; - break; - case VIR_DOMAIN_DISK_BUS_SCSI: - idx = (busid * 7) + unitid; - break; - default: - idx = unitid; - break; - } - } - - if (def->bus == VIR_DOMAIN_DISK_BUS_IDE) { - ignore_value(VIR_STRDUP(def->dst, "hda")); - } else if (def->bus == VIR_DOMAIN_DISK_BUS_SCSI || - def->bus == VIR_DOMAIN_DISK_BUS_SD) { - ignore_value(VIR_STRDUP(def->dst, "sda")); - } else if (def->bus == VIR_DOMAIN_DISK_BUS_VIRTIO) { - ignore_value(VIR_STRDUP(def->dst, "vda")); - } else if (def->bus == VIR_DOMAIN_DISK_BUS_XEN) { - ignore_value(VIR_STRDUP(def->dst, "xvda")); - } else if (def->bus == VIR_DOMAIN_DISK_BUS_FDC) { - ignore_value(VIR_STRDUP(def->dst, "fda")); - } else { - ignore_value(VIR_STRDUP(def->dst, "hda")); - } - - if (!def->dst) - goto error; - if (STREQ(def->dst, "xvda")) - def->dst[3] = 'a' + idx; - else - def->dst[2] = 'a' + idx; - - if (virDomainDiskDefAssignAddress(xmlopt, def, dom) < 0) { - virReportError(VIR_ERR_INTERNAL_ERROR, - _("invalid device name '%s'"), def->dst); - virDomainDiskDefFree(def); - def = NULL; - goto cleanup; - } - - cleanup: - for (i = 0; i < nkeywords; i++) { - VIR_FREE(keywords[i]); - VIR_FREE(values[i]); - } - VIR_FREE(keywords); - VIR_FREE(values); - return def; - - error: - virDomainDiskDefFree(def); - def = NULL; - goto cleanup; -} - -/* - * Tries to find a NIC definition matching a vlan we want - */ -static const char * -qemuFindNICForVLAN(int nnics, - const char **nics, - int wantvlan) -{ - size_t i; - for (i = 0; i < nnics; i++) { - int gotvlan; - const char *tmp = strstr(nics[i], "vlan="); - char *end; - if (!tmp) - continue; - - tmp += strlen("vlan="); - - if (virStrToLong_i(tmp, &end, 10, &gotvlan) < 0) { - virReportError(VIR_ERR_INTERNAL_ERROR, - _("cannot parse NIC vlan in '%s'"), nics[i]); - return NULL; - } - - if (gotvlan == wantvlan) - return nics[i]; - } - - if (wantvlan == 0 && nnics > 0) - return nics[0]; - - virReportError(VIR_ERR_INTERNAL_ERROR, - _("cannot find NIC definition for vlan %d"), wantvlan); - return NULL; -} - - -/* - * Tries to parse a QEMU -net backend argument. Gets given - * a list of all known -net frontend arguments to try and - * match up against. Horribly complicated stuff - */ -static virDomainNetDefPtr -qemuParseCommandLineNet(virDomainXMLOptionPtr xmlopt, - const char *val, - int nnics, - const char **nics) -{ - virDomainNetDefPtr def = NULL; - char **keywords = NULL; - char **values = NULL; - int nkeywords; - const char *nic; - int wantvlan = 0; - const char *tmp; - bool genmac = true; - size_t i; - - tmp = strchr(val, ','); - - if (tmp) { - if (qemuParseKeywords(tmp+1, - &keywords, - &values, - &nkeywords, - 0) < 0) - return NULL; - } else { - nkeywords = 0; - } - - if (VIR_ALLOC(def) < 0) - goto cleanup; - - /* 'tap' could turn into libvirt type=ethernet, type=bridge or - * type=network, but we can't tell, so use the generic config */ - if (STRPREFIX(val, "tap,")) - def->type = VIR_DOMAIN_NET_TYPE_ETHERNET; - else if (STRPREFIX(val, "socket")) - def->type = VIR_DOMAIN_NET_TYPE_CLIENT; - else if (STRPREFIX(val, "user")) - def->type = VIR_DOMAIN_NET_TYPE_USER; - else - def->type = VIR_DOMAIN_NET_TYPE_ETHERNET; - - for (i = 0; i < nkeywords; i++) { - if (STREQ(keywords[i], "vlan")) { - if (virStrToLong_i(values[i], NULL, 10, &wantvlan) < 0) { - virReportError(VIR_ERR_INTERNAL_ERROR, - _("cannot parse vlan in '%s'"), val); - virDomainNetDefFree(def); - def = NULL; - goto cleanup; - } - } else if (def->type == VIR_DOMAIN_NET_TYPE_ETHERNET && - STREQ(keywords[i], "script") && STRNEQ(values[i], "")) { - def->script = values[i]; - values[i] = NULL; - } else if (def->type == VIR_DOMAIN_NET_TYPE_ETHERNET && - STREQ(keywords[i], "ifname")) { - def->ifname = values[i]; - values[i] = NULL; - } - } - - - /* Done parsing the nic backend. Now to try and find corresponding - * frontend, based off vlan number. NB this assumes a 1-1 mapping - */ - - nic = qemuFindNICForVLAN(nnics, nics, wantvlan); - if (!nic) { - virDomainNetDefFree(def); - def = NULL; - goto cleanup; - } - - if (!STRPREFIX(nic, "nic")) { - virReportError(VIR_ERR_INTERNAL_ERROR, - _("cannot parse NIC definition '%s'"), nic); - virDomainNetDefFree(def); - def = NULL; - goto cleanup; - } - - for (i = 0; i < nkeywords; i++) { - VIR_FREE(keywords[i]); - VIR_FREE(values[i]); - } - VIR_FREE(keywords); - VIR_FREE(values); - - if (STRPREFIX(nic, "nic,")) { - if (qemuParseKeywords(nic + strlen("nic,"), - &keywords, - &values, - &nkeywords, - 0) < 0) { - virDomainNetDefFree(def); - def = NULL; - goto cleanup; - } - } else { - nkeywords = 0; - } - - for (i = 0; i < nkeywords; i++) { - if (STREQ(keywords[i], "macaddr")) { - genmac = false; - if (virMacAddrParse(values[i], &def->mac) < 0) { - virReportError(VIR_ERR_INTERNAL_ERROR, - _("unable to parse mac address '%s'"), - values[i]); - virDomainNetDefFree(def); - def = NULL; - goto cleanup; - } - } else if (STREQ(keywords[i], "model")) { - def->model = values[i]; - values[i] = NULL; - } else if (STREQ(keywords[i], "vhost")) { - if ((values[i] == NULL) || STREQ(values[i], "on")) { - def->driver.virtio.name = VIR_DOMAIN_NET_BACKEND_TYPE_VHOST; - } else if (STREQ(keywords[i], "off")) { - def->driver.virtio.name = VIR_DOMAIN_NET_BACKEND_TYPE_QEMU; - } - } else if (STREQ(keywords[i], "sndbuf") && values[i]) { - if (virStrToLong_ul(values[i], NULL, 10, &def->tune.sndbuf) < 0) { - virReportError(VIR_ERR_INTERNAL_ERROR, - _("cannot parse sndbuf size in '%s'"), val); - virDomainNetDefFree(def); - def = NULL; - goto cleanup; - } - def->tune.sndbuf_specified = true; - } - } - - if (genmac) - virDomainNetGenerateMAC(xmlopt, &def->mac); - - cleanup: - for (i = 0; i < nkeywords; i++) { - VIR_FREE(keywords[i]); - VIR_FREE(values[i]); - } - VIR_FREE(keywords); - VIR_FREE(values); - return def; -} - - -/* - * Tries to parse a QEMU PCI device - */ -static virDomainHostdevDefPtr -qemuParseCommandLinePCI(const char *val) -{ - int bus = 0, slot = 0, func = 0; - const char *start; - char *end; - virDomainHostdevDefPtr def = virDomainHostdevDefAlloc(); - - if (!def) - goto error; - - if (!STRPREFIX(val, "host=")) { - virReportError(VIR_ERR_INTERNAL_ERROR, - _("unknown PCI device syntax '%s'"), val); - goto error; - } - - start = val + strlen("host="); - if (virStrToLong_i(start, &end, 16, &bus) < 0 || *end != ':') { - virReportError(VIR_ERR_INTERNAL_ERROR, - _("cannot extract PCI device bus '%s'"), val); - goto error; - } - start = end + 1; - if (virStrToLong_i(start, &end, 16, &slot) < 0 || *end != '.') { - virReportError(VIR_ERR_INTERNAL_ERROR, - _("cannot extract PCI device slot '%s'"), val); - goto error; - } - start = end + 1; - if (virStrToLong_i(start, NULL, 16, &func) < 0) { - virReportError(VIR_ERR_INTERNAL_ERROR, - _("cannot extract PCI device function '%s'"), val); - goto error; - } - - def->mode = VIR_DOMAIN_HOSTDEV_MODE_SUBSYS; - def->managed = true; - def->source.subsys.type = VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_PCI; - def->source.subsys.u.pci.addr.bus = bus; - def->source.subsys.u.pci.addr.slot = slot; - def->source.subsys.u.pci.addr.function = func; - return def; - - error: - virDomainHostdevDefFree(def); - return NULL; -} - - -/* - * Tries to parse a QEMU USB device - */ -static virDomainHostdevDefPtr -qemuParseCommandLineUSB(const char *val) -{ - virDomainHostdevDefPtr def = virDomainHostdevDefAlloc(); - virDomainHostdevSubsysUSBPtr usbsrc; - int first = 0, second = 0; - const char *start; - char *end; - - if (!def) - goto error; - usbsrc = &def->source.subsys.u.usb; - - if (!STRPREFIX(val, "host:")) { - virReportError(VIR_ERR_INTERNAL_ERROR, - _("unknown USB device syntax '%s'"), val); - goto error; - } - - start = val + strlen("host:"); - if (strchr(start, ':')) { - if (virStrToLong_i(start, &end, 16, &first) < 0 || *end != ':') { - virReportError(VIR_ERR_INTERNAL_ERROR, - _("cannot extract USB device vendor '%s'"), val); - goto error; - } - start = end + 1; - if (virStrToLong_i(start, NULL, 16, &second) < 0) { - virReportError(VIR_ERR_INTERNAL_ERROR, - _("cannot extract USB device product '%s'"), val); - goto error; - } - } else { - if (virStrToLong_i(start, &end, 10, &first) < 0 || *end != '.') { - virReportError(VIR_ERR_INTERNAL_ERROR, - _("cannot extract USB device bus '%s'"), val); - goto error; - } - start = end + 1; - if (virStrToLong_i(start, NULL, 10, &second) < 0) { - virReportError(VIR_ERR_INTERNAL_ERROR, - _("cannot extract USB device address '%s'"), val); - goto error; - } - } - - def->mode = VIR_DOMAIN_HOSTDEV_MODE_SUBSYS; - def->managed = false; - def->source.subsys.type = VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_USB; - if (*end == '.') { - usbsrc->bus = first; - usbsrc->device = second; - } else { - usbsrc->vendor = first; - usbsrc->product = second; - } - return def; - - error: - virDomainHostdevDefFree(def); - return NULL; -} - - -/* - * Tries to parse a QEMU serial/parallel device - */ -static int -qemuParseCommandLineChr(virDomainChrSourceDefPtr source, - const char *val) -{ - if (STREQ(val, "null")) { - source->type = VIR_DOMAIN_CHR_TYPE_NULL; - } else if (STREQ(val, "vc")) { - source->type = VIR_DOMAIN_CHR_TYPE_VC; - } else if (STREQ(val, "pty")) { - source->type = VIR_DOMAIN_CHR_TYPE_PTY; - } else if (STRPREFIX(val, "file:")) { - source->type = VIR_DOMAIN_CHR_TYPE_FILE; - if (VIR_STRDUP(source->data.file.path, val + strlen("file:")) < 0) - goto error; - } else if (STRPREFIX(val, "pipe:")) { - source->type = VIR_DOMAIN_CHR_TYPE_PIPE; - if (VIR_STRDUP(source->data.file.path, val + strlen("pipe:")) < 0) - goto error; - } else if (STREQ(val, "stdio")) { - source->type = VIR_DOMAIN_CHR_TYPE_STDIO; - } else if (STRPREFIX(val, "udp:")) { - const char *svc1, *host2, *svc2; - source->type = VIR_DOMAIN_CHR_TYPE_UDP; - val += strlen("udp:"); - svc1 = strchr(val, ':'); - host2 = svc1 ? strchr(svc1, '@') : NULL; - svc2 = host2 ? strchr(host2, ':') : NULL; - - if (svc1 && svc1 != val && - VIR_STRNDUP(source->data.udp.connectHost, val, svc1 - val) < 0) - goto error; - - if (svc1) { - svc1++; - if (VIR_STRNDUP(source->data.udp.connectService, svc1, - host2 ? host2 - svc1 : strlen(svc1)) < 0) - goto error; - } - - if (host2) { - host2++; - if (svc2 && svc2 != host2 && - VIR_STRNDUP(source->data.udp.bindHost, host2, svc2 - host2) < 0) - goto error; - } - - if (svc2) { - svc2++; - if (STRNEQ(svc2, "0")) { - if (VIR_STRDUP(source->data.udp.bindService, svc2) < 0) - goto error; - } - } - } else if (STRPREFIX(val, "tcp:") || - STRPREFIX(val, "telnet:")) { - const char *opt, *svc; - source->type = VIR_DOMAIN_CHR_TYPE_TCP; - if (STRPREFIX(val, "tcp:")) { - val += strlen("tcp:"); - } else { - val += strlen("telnet:"); - source->data.tcp.protocol = VIR_DOMAIN_CHR_TCP_PROTOCOL_TELNET; - } - svc = strchr(val, ':'); - if (!svc) { - virReportError(VIR_ERR_INTERNAL_ERROR, - _("cannot find port number in character device %s"), val); - goto error; - } - opt = strchr(svc, ','); - if (opt && strstr(opt, "server")) - source->data.tcp.listen = true; - - if (VIR_STRNDUP(source->data.tcp.host, val, svc - val) < 0) - goto error; - svc++; - if (VIR_STRNDUP(source->data.tcp.service, svc, opt ? opt - svc : -1) < 0) - goto error; - } else if (STRPREFIX(val, "unix:")) { - const char *opt; - val += strlen("unix:"); - opt = strchr(val, ','); - source->type = VIR_DOMAIN_CHR_TYPE_UNIX; - if (VIR_STRNDUP(source->data.nix.path, val, opt ? opt - val : -1) < 0) - goto error; - - } else if (STRPREFIX(val, "/dev")) { - source->type = VIR_DOMAIN_CHR_TYPE_DEV; - if (VIR_STRDUP(source->data.file.path, val) < 0) - goto error; - } else { - virReportError(VIR_ERR_INTERNAL_ERROR, - _("unknown character device syntax %s"), val); - goto error; - } - - return 0; - - error: - return -1; -} - - -static virCPUDefPtr -qemuInitGuestCPU(virDomainDefPtr dom) -{ - if (!dom->cpu) { - virCPUDefPtr cpu; - - if (VIR_ALLOC(cpu) < 0) - return NULL; - - cpu->type = VIR_CPU_TYPE_GUEST; - cpu->match = VIR_CPU_MATCH_EXACT; - dom->cpu = cpu; - } - - return dom->cpu; -} - - -static int -qemuParseCommandLineCPU(virDomainDefPtr dom, - const char *val) -{ - virCPUDefPtr cpu = NULL; - char **tokens; - char **hv_tokens = NULL; - char *model = NULL; - int ret = -1; - size_t i; - - if (!(tokens = virStringSplit(val, ",", 0))) - goto cleanup; - - if (tokens[0] == NULL) - goto syntax; - - for (i = 0; tokens[i] != NULL; i++) { - if (*tokens[i] == '\0') - goto syntax; - - if (i == 0) { - if (VIR_STRDUP(model, tokens[i]) < 0) - goto cleanup; - - if (STRNEQ(model, "qemu32") && STRNEQ(model, "qemu64")) { - if (!(cpu = qemuInitGuestCPU(dom))) - goto cleanup; - - cpu->model = model; - model = NULL; - } - } else if (*tokens[i] == '+' || *tokens[i] == '-') { - const char *feature = tokens[i] + 1; /* '+' or '-' */ - int policy; - - if (*tokens[i] == '+') - policy = VIR_CPU_FEATURE_REQUIRE; - else - policy = VIR_CPU_FEATURE_DISABLE; - - if (*feature == '\0') - goto syntax; - - if (dom->os.arch != VIR_ARCH_X86_64 && - dom->os.arch != VIR_ARCH_I686) { - virReportError(VIR_ERR_INTERNAL_ERROR, - _("%s platform doesn't support CPU features'"), - virArchToString(dom->os.arch)); - goto cleanup; - } - - if (STREQ(feature, "kvmclock")) { - bool present = (policy == VIR_CPU_FEATURE_REQUIRE); - size_t j; - - for (j = 0; j < dom->clock.ntimers; j++) { - if (dom->clock.timers[j]->name == VIR_DOMAIN_TIMER_NAME_KVMCLOCK) - break; - } - - if (j == dom->clock.ntimers) { - virDomainTimerDefPtr timer; - if (VIR_ALLOC(timer) < 0 || - VIR_APPEND_ELEMENT_COPY(dom->clock.timers, - dom->clock.ntimers, timer) < 0) { - VIR_FREE(timer); - goto cleanup; - } - timer->name = VIR_DOMAIN_TIMER_NAME_KVMCLOCK; - timer->present = present; - timer->tickpolicy = -1; - timer->track = -1; - timer->mode = -1; - } else if (dom->clock.timers[j]->present != -1 && - dom->clock.timers[j]->present != present) { - virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s", - _("conflicting occurrences of kvmclock feature")); - goto cleanup; - } - } else if (STREQ(feature, "kvm_pv_eoi")) { - if (policy == VIR_CPU_FEATURE_REQUIRE) - dom->apic_eoi = VIR_TRISTATE_SWITCH_ON; - else - dom->apic_eoi = VIR_TRISTATE_SWITCH_OFF; - } else { - if (!cpu) { - if (!(cpu = qemuInitGuestCPU(dom))) - goto cleanup; - - cpu->model = model; - model = NULL; - } - - if (virCPUDefAddFeature(cpu, feature, policy) < 0) - goto cleanup; - } - } else if (STREQ(tokens[i], "hv_crash")) { - size_t j; - for (j = 0; j < dom->npanics; j++) { - if (dom->panics[j]->model == VIR_DOMAIN_PANIC_MODEL_HYPERV) - break; - } - - if (j == dom->npanics) { - virDomainPanicDefPtr panic; - if (VIR_ALLOC(panic) < 0 || - VIR_APPEND_ELEMENT_COPY(dom->panics, - dom->npanics, panic) < 0) { - VIR_FREE(panic); - goto cleanup; - } - panic->model = VIR_DOMAIN_PANIC_MODEL_HYPERV; - } - } else if (STRPREFIX(tokens[i], "hv_")) { - const char *token = tokens[i] + 3; /* "hv_" */ - const char *feature, *value; - int f; - - if (*token == '\0') - goto syntax; - - if (!(hv_tokens = virStringSplit(token, "=", 2))) - goto cleanup; - - feature = hv_tokens[0]; - value = hv_tokens[1]; - - if (*feature == '\0') - goto syntax; - - dom->features[VIR_DOMAIN_FEATURE_HYPERV] = VIR_TRISTATE_SWITCH_ON; - - if ((f = virDomainHypervTypeFromString(feature)) < 0) { - virReportError(VIR_ERR_CONFIG_UNSUPPORTED, - _("unsupported HyperV Enlightenment feature " - "'%s'"), feature); - goto cleanup; - } - - switch ((virDomainHyperv) f) { - case VIR_DOMAIN_HYPERV_RELAXED: - case VIR_DOMAIN_HYPERV_VAPIC: - if (value) { - virReportError(VIR_ERR_CONFIG_UNSUPPORTED, - _("HyperV feature '%s' should not " - "have a value"), feature); - goto cleanup; - } - dom->hyperv_features[f] = VIR_TRISTATE_SWITCH_ON; - break; - - case VIR_DOMAIN_HYPERV_SPINLOCKS: - dom->hyperv_features[f] = VIR_TRISTATE_SWITCH_ON; - if (!value) { - virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s", - _("missing HyperV spinlock retry count")); - goto cleanup; - } - - if (virStrToLong_ui(value, NULL, 0, &dom->hyperv_spinlocks) < 0) { - virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s", - _("cannot parse HyperV spinlock retry count")); - goto cleanup; - } - - if (dom->hyperv_spinlocks < 0xFFF) - dom->hyperv_spinlocks = 0xFFF; - break; - - case VIR_DOMAIN_HYPERV_LAST: - break; - } - virStringFreeList(hv_tokens); - hv_tokens = NULL; - } else if (STREQ(tokens[i], "kvm=off")) { - dom->features[VIR_DOMAIN_FEATURE_KVM] = VIR_TRISTATE_SWITCH_ON; - dom->kvm_features[VIR_DOMAIN_KVM_HIDDEN] = VIR_TRISTATE_SWITCH_ON; - } - } - - if (dom->os.arch == VIR_ARCH_X86_64) { - bool is_32bit = false; - if (cpu) { - virCPUDataPtr cpuData = NULL; - - if (cpuEncode(VIR_ARCH_X86_64, cpu, NULL, &cpuData, - NULL, NULL, NULL, NULL) < 0) - goto cleanup; - - is_32bit = (cpuHasFeature(cpuData, "lm") != 1); - cpuDataFree(cpuData); - } else if (model) { - is_32bit = STREQ(model, "qemu32"); - } - - if (is_32bit) - dom->os.arch = VIR_ARCH_I686; - } - - ret = 0; - - cleanup: - VIR_FREE(model); - virStringFreeList(tokens); - virStringFreeList(hv_tokens); - return ret; - - syntax: - virReportError(VIR_ERR_INTERNAL_ERROR, - _("unknown CPU syntax '%s'"), val); - goto cleanup; -} - - -static int -qemuParseCommandLineSmp(virDomainDefPtr dom, - const char *val) -{ - unsigned int sockets = 0; - unsigned int cores = 0; - unsigned int threads = 0; - unsigned int maxcpus = 0; - unsigned int vcpus = 0; - size_t i; - int nkws; - char **kws; - char **vals; - int n; - char *end; - int ret; - - if (qemuParseKeywords(val, &kws, &vals, &nkws, 1) < 0) - return -1; - - for (i = 0; i < nkws; i++) { - if (vals[i] == NULL) { - if (i > 0 || - virStrToLong_ui(kws[i], &end, 10, &vcpus) < 0 || *end != '\0') - goto syntax; - } else { - if (virStrToLong_i(vals[i], &end, 10, &n) < 0 || *end != '\0') - goto syntax; - if (STREQ(kws[i], "sockets")) - sockets = n; - else if (STREQ(kws[i], "cores")) - cores = n; - else if (STREQ(kws[i], "threads")) - threads = n; - else if (STREQ(kws[i], "maxcpus")) - maxcpus = n; - else - goto syntax; - } - } - - if (maxcpus == 0) - maxcpus = vcpus; - - if (virDomainDefSetVcpusMax(dom, maxcpus) < 0) - goto error; - - if (virDomainDefSetVcpus(dom, vcpus) < 0) - goto error; - - if (sockets && cores && threads) { - virCPUDefPtr cpu; - - if (!(cpu = qemuInitGuestCPU(dom))) - goto error; - cpu->sockets = sockets; - cpu->cores = cores; - cpu->threads = threads; - } else if (sockets || cores || threads) { - goto syntax; - } - - ret = 0; - - cleanup: - for (i = 0; i < nkws; i++) { - VIR_FREE(kws[i]); - VIR_FREE(vals[i]); - } - VIR_FREE(kws); - VIR_FREE(vals); - - return ret; - - syntax: - virReportError(VIR_ERR_INTERNAL_ERROR, - _("cannot parse CPU topology '%s'"), val); - error: - ret = -1; - goto cleanup; -} - - -static void -qemuParseCommandLineBootDevs(virDomainDefPtr def, const char *str) -{ - int n, b = 0; - - for (n = 0; str[n] && b < VIR_DOMAIN_BOOT_LAST; n++) { - if (str[n] == 'a') - def->os.bootDevs[b++] = VIR_DOMAIN_BOOT_FLOPPY; - else if (str[n] == 'c') - def->os.bootDevs[b++] = VIR_DOMAIN_BOOT_DISK; - else if (str[n] == 'd') - def->os.bootDevs[b++] = VIR_DOMAIN_BOOT_CDROM; - else if (str[n] == 'n') - def->os.bootDevs[b++] = VIR_DOMAIN_BOOT_NET; - else if (str[n] == ',') - break; - } - def->os.nBootDevs = b; -} - - -/* - * Analyse the env and argv settings and reconstruct a - * virDomainDefPtr representing these settings as closely - * as is practical. This is not an exact science.... - */ -static virDomainDefPtr -qemuParseCommandLine(virCapsPtr 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 53bfda5..f549aa5 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 c4fa860..495c76b 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 fe2f527..2bbc724 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 24a8865..d2b641f 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 0000000..36aa828 --- /dev/null +++ b/src/qemu/qemu_parse_command.c @@ -0,0 +1,2744 @@ +/* + * qemu_parse_command.c: QEMU command parser + * + * Copyright (C) 2006-2016 Red Hat, Inc. + * Copyright (C) 2006 Daniel P. Berrange + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library. If not, see + * <http://www.gnu.org/licenses/>. + * + * Author: Daniel P. Berrange <berrange@redhat.com> + */ + +#include <config.h> + +#include "qemu_command.h" +#include "qemu_parse_command.h" +#include "dirname.h" +#include "viralloc.h" +#include "virlog.h" +#include "virstring.h" +#include "c-ctype.h" +#include "secret_conf.h" + +#define VIR_FROM_THIS VIR_FROM_QEMU + +VIR_LOG_INIT("qemu.qemu_parse_command"); + + +static int +qemuParseRBDString(virDomainDiskDefPtr disk) +{ + char *source = disk->src->path; + int ret; + + disk->src->path = NULL; + + ret = virStorageSourceParseRBDColonString(source, disk->src); + + VIR_FREE(source); + return ret; +} + + +static int +qemuParseDriveURIString(virDomainDiskDefPtr def, virURIPtr uri, + const char *scheme) +{ + int ret = -1; + char *transp = NULL; + char *sock = NULL; + char *volimg = NULL; + char *secret = NULL; + virStorageAuthDefPtr authdef = NULL; + + if (VIR_ALLOC(def->src->hosts) < 0) + goto error; + + transp = strchr(uri->scheme, '+'); + if (transp) + *transp++ = 0; + + if (STRNEQ(uri->scheme, scheme)) { + virReportError(VIR_ERR_INTERNAL_ERROR, + _("Invalid transport/scheme '%s'"), uri->scheme); + goto error; + } + + if (!transp) { + def->src->hosts->transport = VIR_STORAGE_NET_HOST_TRANS_TCP; + } else { + def->src->hosts->transport = virStorageNetHostTransportTypeFromString(transp); + if (def->src->hosts->transport < 0) { + virReportError(VIR_ERR_INTERNAL_ERROR, + _("Invalid %s transport type '%s'"), scheme, transp); + goto error; + } + } + def->src->nhosts = 0; /* set to 1 once everything succeeds */ + + if (def->src->hosts->transport != VIR_STORAGE_NET_HOST_TRANS_UNIX) { + if (VIR_STRDUP(def->src->hosts->name, uri->server) < 0) + goto error; + + if (virAsprintf(&def->src->hosts->port, "%d", uri->port) < 0) + goto error; + } else { + def->src->hosts->name = NULL; + def->src->hosts->port = 0; + if (uri->query) { + if (STRPREFIX(uri->query, "socket=")) { + sock = strchr(uri->query, '=') + 1; + if (VIR_STRDUP(def->src->hosts->socket, sock) < 0) + goto error; + } else { + virReportError(VIR_ERR_INTERNAL_ERROR, + _("Invalid query parameter '%s'"), uri->query); + goto error; + } + } + } + if (uri->path) { + volimg = uri->path + 1; /* skip the prefix slash */ + VIR_FREE(def->src->path); + if (VIR_STRDUP(def->src->path, volimg) < 0) + goto error; + } else { + VIR_FREE(def->src->path); + } + + if (uri->user) { + const char *secrettype; + /* formulate authdef for disk->src->auth */ + if (VIR_ALLOC(authdef) < 0) + goto error; + + secret = strchr(uri->user, ':'); + if (secret) + *secret = '\0'; + + if (VIR_STRDUP(authdef->username, uri->user) < 0) + goto error; + if (STREQ(scheme, "iscsi")) { + secrettype = + virSecretUsageTypeToString(VIR_SECRET_USAGE_TYPE_ISCSI); + if (VIR_STRDUP(authdef->secrettype, secrettype) < 0) + goto error; + } + def->src->auth = authdef; + authdef = NULL; + + /* Cannot formulate a secretType (eg, usage or uuid) given + * what is provided. + */ + } + + def->src->nhosts = 1; + ret = 0; + + cleanup: + virURIFree(uri); + + return ret; + + error: + virStorageNetHostDefClear(def->src->hosts); + VIR_FREE(def->src->hosts); + virStorageAuthDefFree(authdef); + goto cleanup; +} + +static int +qemuParseGlusterString(virDomainDiskDefPtr def) +{ + virURIPtr uri = NULL; + + if (!(uri = virURIParse(def->src->path))) + return -1; + + return qemuParseDriveURIString(def, uri, "gluster"); +} + +static int +qemuParseISCSIString(virDomainDiskDefPtr def) +{ + virURIPtr uri = NULL; + char *slash; + unsigned lun; + + if (!(uri = virURIParse(def->src->path))) + return -1; + + if (uri->path && + (slash = strchr(uri->path + 1, '/')) != NULL) { + + if (slash[1] == '\0') { + *slash = '\0'; + } else if (virStrToLong_ui(slash + 1, NULL, 10, &lun) == -1) { + virReportError(VIR_ERR_INTERNAL_ERROR, + _("invalid name '%s' for iSCSI disk"), + def->src->path); + virURIFree(uri); + return -1; + } + } + + return qemuParseDriveURIString(def, uri, "iscsi"); +} + +static int +qemuParseNBDString(virDomainDiskDefPtr disk) +{ + virStorageNetHostDefPtr h = NULL; + char *host, *port; + char *src; + + virURIPtr uri = NULL; + + if (strstr(disk->src->path, "://")) { + if (!(uri = virURIParse(disk->src->path))) + return -1; + return qemuParseDriveURIString(disk, uri, "nbd"); + } + + if (VIR_ALLOC(h) < 0) + goto error; + + host = disk->src->path + strlen("nbd:"); + if (STRPREFIX(host, "unix:/")) { + src = strchr(host + strlen("unix:"), ':'); + if (src) + *src++ = '\0'; + + h->transport = VIR_STORAGE_NET_HOST_TRANS_UNIX; + if (VIR_STRDUP(h->socket, host + strlen("unix:")) < 0) + goto error; + } else { + port = strchr(host, ':'); + if (!port) { + virReportError(VIR_ERR_INTERNAL_ERROR, + _("cannot parse nbd filename '%s'"), disk->src->path); + goto error; + } + + *port++ = '\0'; + if (VIR_STRDUP(h->name, host) < 0) + goto error; + + src = strchr(port, ':'); + if (src) + *src++ = '\0'; + + if (VIR_STRDUP(h->port, port) < 0) + goto error; + } + + if (src && STRPREFIX(src, "exportname=")) { + if (VIR_STRDUP(src, strchr(src, '=') + 1) < 0) + goto error; + } else { + src = NULL; + } + + VIR_FREE(disk->src->path); + disk->src->path = src; + disk->src->nhosts = 1; + disk->src->hosts = h; + return 0; + + error: + virStorageNetHostDefClear(h); + VIR_FREE(h); + return -1; +} + + +/* + * This method takes a string representing a QEMU command line ARGV set + * optionally prefixed by a list of environment variables. It then tries + * to split it up into a NULL terminated list of env & argv, splitting + * on space + */ +static int qemuStringToArgvEnv(const char *args, + char ***retenv, + char ***retargv) +{ + char **arglist = NULL; + size_t argcount = 0; + size_t argalloc = 0; + size_t envend; + size_t i; + const char *curr = args; + const char *start; + char **progenv = NULL; + char **progargv = NULL; + + /* Iterate over string, splitting on sequences of ' ' */ + while (curr && *curr != '\0') { + char *arg; + const char *next; + + start = curr; + /* accept a space in CEPH_ARGS */ + if (STRPREFIX(curr, "CEPH_ARGS=-m ")) + start += strlen("CEPH_ARGS=-m "); + if (*start == '\'') { + if (start == curr) + curr++; + next = strchr(start + 1, '\''); + } else if (*start == '"') { + if (start == curr) + curr++; + next = strchr(start + 1, '"'); + } else { + next = strchr(start, ' '); + } + if (!next) + next = strchr(curr, '\n'); + + if (VIR_STRNDUP(arg, curr, next ? next - curr : -1) < 0) + goto error; + + if (next && (*next == '\'' || *next == '"')) + next++; + + if (VIR_RESIZE_N(arglist, argalloc, argcount, 2) < 0) { + VIR_FREE(arg); + goto error; + } + + arglist[argcount++] = arg; + arglist[argcount] = NULL; + + while (next && c_isspace(*next)) + next++; + + curr = next; + } + + /* Iterate over list of args, finding first arg not containing + * the '=' character (eg, skip over env vars FOO=bar) */ + for (envend = 0; ((envend < argcount) && + (strchr(arglist[envend], '=') != NULL)); + envend++) + ; /* nada */ + + /* Copy the list of env vars */ + if (envend > 0) { + if (VIR_REALLOC_N(progenv, envend+1) < 0) + goto error; + for (i = 0; i < envend; i++) + progenv[i] = arglist[i]; + progenv[i] = NULL; + } + + /* Copy the list of argv */ + if (VIR_REALLOC_N(progargv, argcount-envend + 1) < 0) + goto error; + for (i = envend; i < argcount; i++) + progargv[i-envend] = arglist[i]; + progargv[i-envend] = NULL; + + VIR_FREE(arglist); + + *retenv = progenv; + *retargv = progargv; + + return 0; + + error: + VIR_FREE(progenv); + VIR_FREE(progargv); + virStringFreeList(arglist); + return -1; +} + + +/* + * Search for a named env variable, and return the value part + */ +static const char *qemuFindEnv(char **progenv, + const char *name) +{ + size_t i; + int len = strlen(name); + + for (i = 0; progenv && progenv[i]; i++) { + if (STREQLEN(progenv[i], name, len) && + progenv[i][len] == '=') + return progenv[i] + len + 1; + } + return NULL; +} + +/* + * Takes a string containing a set of key=value,key=value,key... + * parameters and splits them up, returning two arrays with + * the individual keys and values. If allowEmptyValue is nonzero, + * the "=value" part is optional and if a key with no value is found, + * NULL is be placed into corresponding place in retvalues. + */ +int +qemuParseKeywords(const char *str, + char ***retkeywords, + char ***retvalues, + int *retnkeywords, + int allowEmptyValue) +{ + int keywordCount = 0; + int keywordAlloc = 0; + char **keywords = NULL; + char **values = NULL; + const char *start = str; + const char *end; + size_t i; + + *retkeywords = NULL; + *retvalues = NULL; + *retnkeywords = 0; + end = start + strlen(str); + + while (start) { + const char *separator; + const char *endmark; + char *keyword; + char *value = NULL; + + endmark = start; + do { + /* Qemu accepts ',,' as an escape for a literal comma; + * skip past those here while searching for the end of the + * value, then strip them down below */ + endmark = strchr(endmark, ','); + } while (endmark && endmark[1] == ',' && (endmark += 2)); + if (!endmark) + endmark = end; + if (!(separator = strchr(start, '='))) + separator = end; + + if (separator >= endmark) { + if (!allowEmptyValue) { + virReportError(VIR_ERR_INTERNAL_ERROR, + _("malformed keyword arguments in '%s'"), str); + goto error; + } + separator = endmark; + } + + if (VIR_STRNDUP(keyword, start, separator - start) < 0) + goto error; + + if (separator < endmark) { + separator++; + if (VIR_STRNDUP(value, separator, endmark - separator) < 0) { + VIR_FREE(keyword); + goto error; + } + if (strchr(value, ',')) { + char *p = strchr(value, ',') + 1; + char *q = p + 1; + while (*q) { + if (*q == ',') + q++; + *p++ = *q++; + } + *p = '\0'; + } + } + + if (keywordAlloc == keywordCount) { + if (VIR_REALLOC_N(keywords, keywordAlloc + 10) < 0 || + VIR_REALLOC_N(values, keywordAlloc + 10) < 0) { + VIR_FREE(keyword); + VIR_FREE(value); + goto error; + } + keywordAlloc += 10; + } + + keywords[keywordCount] = keyword; + values[keywordCount] = value; + keywordCount++; + + start = endmark < end ? endmark + 1 : NULL; + } + + *retkeywords = keywords; + *retvalues = values; + *retnkeywords = keywordCount; + return 0; + + error: + for (i = 0; i < keywordCount; i++) { + VIR_FREE(keywords[i]); + VIR_FREE(values[i]); + } + VIR_FREE(keywords); + VIR_FREE(values); + return -1; +} + + +/* qemuParseCommandLineVnc + * + * Tries to parse the various "-vnc ..." argument formats. + */ +static int +qemuParseCommandLineVnc(virDomainDefPtr def, + const char *val) +{ + int ret = -1; + virDomainGraphicsDefPtr vnc = NULL; + char *tmp; + + if (VIR_ALLOC(vnc) < 0) + goto cleanup; + vnc->type = VIR_DOMAIN_GRAPHICS_TYPE_VNC; + + if (STRPREFIX(val, "unix:")) { + /* -vnc unix:/some/big/path */ + if (VIR_STRDUP(vnc->data.vnc.socket, val + 5) < 0) + goto cleanup; + } else { + /* + * -vnc 127.0.0.1:4 + * -vnc [2001:1:2:3:4:5:1234:1234]:4 + * -vnc some.host.name:4 + */ + char *opts; + char *port; + const char *sep = ":"; + if (val[0] == '[') + sep = "]:"; + tmp = strstr(val, sep); + if (!tmp) { + virReportError(VIR_ERR_INTERNAL_ERROR, + _("missing VNC port number in '%s'"), val); + goto cleanup; + } + port = tmp + strlen(sep); + if (virStrToLong_i(port, &opts, 10, + &vnc->data.vnc.port) < 0) { + virReportError(VIR_ERR_INTERNAL_ERROR, + _("cannot parse VNC port '%s'"), port); + goto cleanup; + } + if (val[0] == '[') + val++; + if (virDomainGraphicsListenSetAddress(vnc, 0, val, tmp-val, true) < 0) + goto cleanup; + if (!virDomainGraphicsListenGetAddress(vnc, 0)) + goto cleanup; + + if (*opts == ',') { + char *orig_opts; + + if (VIR_STRDUP(orig_opts, opts + 1) < 0) + goto cleanup; + opts = orig_opts; + + while (opts && *opts) { + char *nextopt = strchr(opts, ','); + if (nextopt) + *(nextopt++) = '\0'; + + if (STRPREFIX(opts, "websocket")) { + char *websocket = opts + strlen("websocket"); + if (*(websocket++) == '=' && + *websocket) { + /* If the websocket continues with + * '=<something>', we'll parse it */ + if (virStrToLong_i(websocket, + NULL, 0, + &vnc->data.vnc.websocket) < 0) { + virReportError(VIR_ERR_INTERNAL_ERROR, + _("cannot parse VNC " + "WebSocket port '%s'"), + websocket); + VIR_FREE(orig_opts); + goto cleanup; + } + } else { + /* Otherwise, we'll compute the port the same + * way QEMU does, by adding a 5700 to the + * display value. */ + vnc->data.vnc.websocket = + vnc->data.vnc.port + 5700; + } + } else if (STRPREFIX(opts, "share=")) { + char *sharePolicy = opts + strlen("share="); + if (sharePolicy && *sharePolicy) { + int policy = + virDomainGraphicsVNCSharePolicyTypeFromString(sharePolicy); + + if (policy < 0) { + virReportError(VIR_ERR_INTERNAL_ERROR, + _("unknown vnc display sharing policy '%s'"), + sharePolicy); + VIR_FREE(orig_opts); + goto cleanup; + } else { + vnc->data.vnc.sharePolicy = policy; + } + } else { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("missing vnc sharing policy")); + VIR_FREE(orig_opts); + goto cleanup; + } + } + + opts = nextopt; + } + VIR_FREE(orig_opts); + } + vnc->data.vnc.port += 5900; + vnc->data.vnc.autoport = false; + } + + if (VIR_APPEND_ELEMENT(def->graphics, def->ngraphics, vnc) < 0) + goto cleanup; + + ret = 0; + + cleanup: + virDomainGraphicsDefFree(vnc); + return ret; +} + + +/* + * Tries to parse new style QEMU -drive args. + * + * eg -drive file=/dev/HostVG/VirtData1,if=ide,index=1 + * + * Will fail if not using the 'index' keyword + */ +static virDomainDiskDefPtr +qemuParseCommandLineDisk(virDomainXMLOptionPtr xmlopt, + const char *val, + virDomainDefPtr dom, + int nvirtiodisk, + bool old_style_ceph_args) +{ + virDomainDiskDefPtr def = NULL; + char **keywords; + char **values; + int nkeywords; + size_t i; + int idx = -1; + int busid = -1; + int unitid = -1; + + if (qemuParseKeywords(val, + &keywords, + &values, + &nkeywords, + 0) < 0) + return NULL; + + if (VIR_ALLOC(def) < 0) + goto cleanup; + if (VIR_ALLOC(def->src) < 0) + goto error; + + if ((ARCH_IS_PPC64(dom->os.arch) && + dom->os.machine && STRPREFIX(dom->os.machine, "pseries"))) + def->bus = VIR_DOMAIN_DISK_BUS_SCSI; + else + def->bus = VIR_DOMAIN_DISK_BUS_IDE; + def->device = VIR_DOMAIN_DISK_DEVICE_DISK; + def->src->type = VIR_STORAGE_TYPE_FILE; + + for (i = 0; i < nkeywords; i++) { + if (STREQ(keywords[i], "file")) { + if (values[i] && STRNEQ(values[i], "")) { + def->src->path = values[i]; + values[i] = NULL; + if (STRPREFIX(def->src->path, "/dev/")) + def->src->type = VIR_STORAGE_TYPE_BLOCK; + else if (STRPREFIX(def->src->path, "nbd:") || + STRPREFIX(def->src->path, "nbd+")) { + def->src->type = VIR_STORAGE_TYPE_NETWORK; + def->src->protocol = VIR_STORAGE_NET_PROTOCOL_NBD; + + if (qemuParseNBDString(def) < 0) + goto error; + } else if (STRPREFIX(def->src->path, "rbd:")) { + char *p = def->src->path; + + def->src->type = VIR_STORAGE_TYPE_NETWORK; + def->src->protocol = VIR_STORAGE_NET_PROTOCOL_RBD; + if (VIR_STRDUP(def->src->path, p + strlen("rbd:")) < 0) + goto error; + /* old-style CEPH_ARGS env variable is parsed later */ + if (!old_style_ceph_args && qemuParseRBDString(def) < 0) { + VIR_FREE(p); + goto error; + } + + VIR_FREE(p); + } else if (STRPREFIX(def->src->path, "gluster:") || + STRPREFIX(def->src->path, "gluster+")) { + def->src->type = VIR_STORAGE_TYPE_NETWORK; + def->src->protocol = VIR_STORAGE_NET_PROTOCOL_GLUSTER; + + if (qemuParseGlusterString(def) < 0) + goto error; + } else if (STRPREFIX(def->src->path, "iscsi:")) { + def->src->type = VIR_STORAGE_TYPE_NETWORK; + def->src->protocol = VIR_STORAGE_NET_PROTOCOL_ISCSI; + + if (qemuParseISCSIString(def) < 0) + goto error; + } else if (STRPREFIX(def->src->path, "sheepdog:")) { + char *p = def->src->path; + char *port, *vdi; + + def->src->type = VIR_STORAGE_TYPE_NETWORK; + def->src->protocol = VIR_STORAGE_NET_PROTOCOL_SHEEPDOG; + if (VIR_STRDUP(def->src->path, p + strlen("sheepdog:")) < 0) + goto error; + VIR_FREE(p); + + /* def->src->path must be [vdiname] or [host]:[port]:[vdiname] */ + port = strchr(def->src->path, ':'); + if (port) { + *port = '\0'; + vdi = strchr(port + 1, ':'); + if (!vdi) { + *port = ':'; + virReportError(VIR_ERR_INTERNAL_ERROR, + _("cannot parse sheepdog filename '%s'"), + def->src->path); + goto error; + } + port++; + *vdi++ = '\0'; + if (VIR_ALLOC(def->src->hosts) < 0) + goto error; + def->src->nhosts = 1; + def->src->hosts->name = def->src->path; + if (VIR_STRDUP(def->src->hosts->port, port) < 0) + goto error; + def->src->hosts->transport = VIR_STORAGE_NET_HOST_TRANS_TCP; + def->src->hosts->socket = NULL; + if (VIR_STRDUP(def->src->path, vdi) < 0) + goto error; + } + } else { + def->src->type = VIR_STORAGE_TYPE_FILE; + } + } else { + def->src->type = VIR_STORAGE_TYPE_FILE; + } + } else if (STREQ(keywords[i], "if")) { + if (STREQ(values[i], "ide")) { + def->bus = VIR_DOMAIN_DISK_BUS_IDE; + if ((ARCH_IS_PPC64(dom->os.arch) && + dom->os.machine && STRPREFIX(dom->os.machine, "pseries"))) { + virReportError(VIR_ERR_INTERNAL_ERROR, + _("pseries systems do not support ide devices '%s'"), val); + goto error; + } + } else if (STREQ(values[i], "scsi")) { + def->bus = VIR_DOMAIN_DISK_BUS_SCSI; + } else if (STREQ(values[i], "floppy")) { + def->bus = VIR_DOMAIN_DISK_BUS_FDC; + def->device = VIR_DOMAIN_DISK_DEVICE_FLOPPY; + } else if (STREQ(values[i], "virtio")) { + def->bus = VIR_DOMAIN_DISK_BUS_VIRTIO; + } else if (STREQ(values[i], "xen")) { + def->bus = VIR_DOMAIN_DISK_BUS_XEN; + } else if (STREQ(values[i], "sd")) { + def->bus = VIR_DOMAIN_DISK_BUS_SD; + } + } else if (STREQ(keywords[i], "media")) { + if (STREQ(values[i], "cdrom")) { + def->device = VIR_DOMAIN_DISK_DEVICE_CDROM; + def->src->readonly = true; + } else if (STREQ(values[i], "floppy")) { + def->device = VIR_DOMAIN_DISK_DEVICE_FLOPPY; + } + } else if (STREQ(keywords[i], "format")) { + if (VIR_STRDUP(def->src->driverName, "qemu") < 0) + goto error; + def->src->format = virStorageFileFormatTypeFromString(values[i]); + } else if (STREQ(keywords[i], "cache")) { + if (STREQ(values[i], "off") || + STREQ(values[i], "none")) + def->cachemode = VIR_DOMAIN_DISK_CACHE_DISABLE; + else if (STREQ(values[i], "writeback") || + STREQ(values[i], "on")) + def->cachemode = VIR_DOMAIN_DISK_CACHE_WRITEBACK; + else if (STREQ(values[i], "writethrough")) + def->cachemode = VIR_DOMAIN_DISK_CACHE_WRITETHRU; + else if (STREQ(values[i], "directsync")) + def->cachemode = VIR_DOMAIN_DISK_CACHE_DIRECTSYNC; + else if (STREQ(values[i], "unsafe")) + def->cachemode = VIR_DOMAIN_DISK_CACHE_UNSAFE; + } else if (STREQ(keywords[i], "werror")) { + if (STREQ(values[i], "stop")) + def->error_policy = VIR_DOMAIN_DISK_ERROR_POLICY_STOP; + else if (STREQ(values[i], "report")) + def->error_policy = VIR_DOMAIN_DISK_ERROR_POLICY_REPORT; + else if (STREQ(values[i], "ignore")) + def->error_policy = VIR_DOMAIN_DISK_ERROR_POLICY_IGNORE; + else if (STREQ(values[i], "enospc")) + def->error_policy = VIR_DOMAIN_DISK_ERROR_POLICY_ENOSPACE; + } else if (STREQ(keywords[i], "rerror")) { + if (STREQ(values[i], "stop")) + def->rerror_policy = VIR_DOMAIN_DISK_ERROR_POLICY_STOP; + else if (STREQ(values[i], "report")) + def->rerror_policy = VIR_DOMAIN_DISK_ERROR_POLICY_REPORT; + else if (STREQ(values[i], "ignore")) + def->rerror_policy = VIR_DOMAIN_DISK_ERROR_POLICY_IGNORE; + } else if (STREQ(keywords[i], "index")) { + if (virStrToLong_i(values[i], NULL, 10, &idx) < 0) { + virReportError(VIR_ERR_INTERNAL_ERROR, + _("cannot parse drive index '%s'"), val); + goto error; + } + } else if (STREQ(keywords[i], "bus")) { + if (virStrToLong_i(values[i], NULL, 10, &busid) < 0) { + virReportError(VIR_ERR_INTERNAL_ERROR, + _("cannot parse drive bus '%s'"), val); + goto error; + } + } else if (STREQ(keywords[i], "unit")) { + if (virStrToLong_i(values[i], NULL, 10, &unitid) < 0) { + virReportError(VIR_ERR_INTERNAL_ERROR, + _("cannot parse drive unit '%s'"), val); + goto error; + } + } else if (STREQ(keywords[i], "readonly")) { + if ((values[i] == NULL) || STREQ(values[i], "on")) + def->src->readonly = true; + } else if (STREQ(keywords[i], "aio")) { + if ((def->iomode = virDomainDiskIoTypeFromString(values[i])) < 0) { + virReportError(VIR_ERR_INTERNAL_ERROR, + _("cannot parse io mode '%s'"), values[i]); + goto error; + } + } else if (STREQ(keywords[i], "cyls")) { + if (virStrToLong_ui(values[i], NULL, 10, + &(def->geometry.cylinders)) < 0) { + virDomainDiskDefFree(def); + def = NULL; + virReportError(VIR_ERR_INTERNAL_ERROR, + _("cannot parse cylinders value'%s'"), + values[i]); + goto error; + } + } else if (STREQ(keywords[i], "heads")) { + if (virStrToLong_ui(values[i], NULL, 10, + &(def->geometry.heads)) < 0) { + virDomainDiskDefFree(def); + def = NULL; + virReportError(VIR_ERR_INTERNAL_ERROR, + _("cannot parse heads value'%s'"), + values[i]); + goto error; + } + } else if (STREQ(keywords[i], "secs")) { + if (virStrToLong_ui(values[i], NULL, 10, + &(def->geometry.sectors)) < 0) { + virDomainDiskDefFree(def); + def = NULL; + virReportError(VIR_ERR_INTERNAL_ERROR, + _("cannot parse sectors value'%s'"), + values[i]); + goto error; + } + } else if (STREQ(keywords[i], "trans")) { + def->geometry.trans = + virDomainDiskGeometryTransTypeFromString(values[i]); + if ((def->geometry.trans < VIR_DOMAIN_DISK_TRANS_DEFAULT) || + (def->geometry.trans >= VIR_DOMAIN_DISK_TRANS_LAST)) { + virDomainDiskDefFree(def); + def = NULL; + virReportError(VIR_ERR_INTERNAL_ERROR, + _("cannot parse translation value '%s'"), + values[i]); + goto error; + } + } + } + + if (def->rerror_policy == def->error_policy) + def->rerror_policy = 0; + + if (!def->src->path && + def->device == VIR_DOMAIN_DISK_DEVICE_DISK && + def->src->type != VIR_STORAGE_TYPE_NETWORK) { + virReportError(VIR_ERR_INTERNAL_ERROR, + _("missing file parameter in drive '%s'"), val); + goto error; + } + if (idx == -1 && + def->bus == VIR_DOMAIN_DISK_BUS_VIRTIO) + idx = nvirtiodisk; + + if (idx == -1 && + unitid == -1 && + busid == -1) { + virReportError(VIR_ERR_INTERNAL_ERROR, + _("missing index/unit/bus parameter in drive '%s'"), + val); + goto error; + } + + if (idx == -1) { + if (unitid == -1) + unitid = 0; + if (busid == -1) + busid = 0; + switch (def->bus) { + case VIR_DOMAIN_DISK_BUS_IDE: + idx = (busid * 2) + unitid; + break; + case VIR_DOMAIN_DISK_BUS_SCSI: + idx = (busid * 7) + unitid; + break; + default: + idx = unitid; + break; + } + } + + if (def->bus == VIR_DOMAIN_DISK_BUS_IDE) { + ignore_value(VIR_STRDUP(def->dst, "hda")); + } else if (def->bus == VIR_DOMAIN_DISK_BUS_SCSI || + def->bus == VIR_DOMAIN_DISK_BUS_SD) { + ignore_value(VIR_STRDUP(def->dst, "sda")); + } else if (def->bus == VIR_DOMAIN_DISK_BUS_VIRTIO) { + ignore_value(VIR_STRDUP(def->dst, "vda")); + } else if (def->bus == VIR_DOMAIN_DISK_BUS_XEN) { + ignore_value(VIR_STRDUP(def->dst, "xvda")); + } else if (def->bus == VIR_DOMAIN_DISK_BUS_FDC) { + ignore_value(VIR_STRDUP(def->dst, "fda")); + } else { + ignore_value(VIR_STRDUP(def->dst, "hda")); + } + + if (!def->dst) + goto error; + if (STREQ(def->dst, "xvda")) + def->dst[3] = 'a' + idx; + else + def->dst[2] = 'a' + idx; + + if (virDomainDiskDefAssignAddress(xmlopt, def, dom) < 0) { + virReportError(VIR_ERR_INTERNAL_ERROR, + _("invalid device name '%s'"), def->dst); + virDomainDiskDefFree(def); + def = NULL; + goto cleanup; + } + + cleanup: + for (i = 0; i < nkeywords; i++) { + VIR_FREE(keywords[i]); + VIR_FREE(values[i]); + } + VIR_FREE(keywords); + VIR_FREE(values); + return def; + + error: + virDomainDiskDefFree(def); + def = NULL; + goto cleanup; +} + +/* + * Tries to find a NIC definition matching a vlan we want + */ +static const char * +qemuFindNICForVLAN(int nnics, + const char **nics, + int wantvlan) +{ + size_t i; + for (i = 0; i < nnics; i++) { + int gotvlan; + const char *tmp = strstr(nics[i], "vlan="); + char *end; + if (!tmp) + continue; + + tmp += strlen("vlan="); + + if (virStrToLong_i(tmp, &end, 10, &gotvlan) < 0) { + virReportError(VIR_ERR_INTERNAL_ERROR, + _("cannot parse NIC vlan in '%s'"), nics[i]); + return NULL; + } + + if (gotvlan == wantvlan) + return nics[i]; + } + + if (wantvlan == 0 && nnics > 0) + return nics[0]; + + virReportError(VIR_ERR_INTERNAL_ERROR, + _("cannot find NIC definition for vlan %d"), wantvlan); + return NULL; +} + + +/* + * Tries to parse a QEMU -net backend argument. Gets given + * a list of all known -net frontend arguments to try and + * match up against. Horribly complicated stuff + */ +static virDomainNetDefPtr +qemuParseCommandLineNet(virDomainXMLOptionPtr xmlopt, + const char *val, + int nnics, + const char **nics) +{ + virDomainNetDefPtr def = NULL; + char **keywords = NULL; + char **values = NULL; + int nkeywords; + const char *nic; + int wantvlan = 0; + const char *tmp; + bool genmac = true; + size_t i; + + tmp = strchr(val, ','); + + if (tmp) { + if (qemuParseKeywords(tmp+1, + &keywords, + &values, + &nkeywords, + 0) < 0) + return NULL; + } else { + nkeywords = 0; + } + + if (VIR_ALLOC(def) < 0) + goto cleanup; + + /* 'tap' could turn into libvirt type=ethernet, type=bridge or + * type=network, but we can't tell, so use the generic config */ + if (STRPREFIX(val, "tap,")) + def->type = VIR_DOMAIN_NET_TYPE_ETHERNET; + else if (STRPREFIX(val, "socket")) + def->type = VIR_DOMAIN_NET_TYPE_CLIENT; + else if (STRPREFIX(val, "user")) + def->type = VIR_DOMAIN_NET_TYPE_USER; + else + def->type = VIR_DOMAIN_NET_TYPE_ETHERNET; + + for (i = 0; i < nkeywords; i++) { + if (STREQ(keywords[i], "vlan")) { + if (virStrToLong_i(values[i], NULL, 10, &wantvlan) < 0) { + virReportError(VIR_ERR_INTERNAL_ERROR, + _("cannot parse vlan in '%s'"), val); + virDomainNetDefFree(def); + def = NULL; + goto cleanup; + } + } else if (def->type == VIR_DOMAIN_NET_TYPE_ETHERNET && + STREQ(keywords[i], "script") && STRNEQ(values[i], "")) { + def->script = values[i]; + values[i] = NULL; + } else if (def->type == VIR_DOMAIN_NET_TYPE_ETHERNET && + STREQ(keywords[i], "ifname")) { + def->ifname = values[i]; + values[i] = NULL; + } + } + + + /* Done parsing the nic backend. Now to try and find corresponding + * frontend, based off vlan number. NB this assumes a 1-1 mapping + */ + + nic = qemuFindNICForVLAN(nnics, nics, wantvlan); + if (!nic) { + virDomainNetDefFree(def); + def = NULL; + goto cleanup; + } + + if (!STRPREFIX(nic, "nic")) { + virReportError(VIR_ERR_INTERNAL_ERROR, + _("cannot parse NIC definition '%s'"), nic); + virDomainNetDefFree(def); + def = NULL; + goto cleanup; + } + + for (i = 0; i < nkeywords; i++) { + VIR_FREE(keywords[i]); + VIR_FREE(values[i]); + } + VIR_FREE(keywords); + VIR_FREE(values); + + if (STRPREFIX(nic, "nic,")) { + if (qemuParseKeywords(nic + strlen("nic,"), + &keywords, + &values, + &nkeywords, + 0) < 0) { + virDomainNetDefFree(def); + def = NULL; + goto cleanup; + } + } else { + nkeywords = 0; + } + + for (i = 0; i < nkeywords; i++) { + if (STREQ(keywords[i], "macaddr")) { + genmac = false; + if (virMacAddrParse(values[i], &def->mac) < 0) { + virReportError(VIR_ERR_INTERNAL_ERROR, + _("unable to parse mac address '%s'"), + values[i]); + virDomainNetDefFree(def); + def = NULL; + goto cleanup; + } + } else if (STREQ(keywords[i], "model")) { + def->model = values[i]; + values[i] = NULL; + } else if (STREQ(keywords[i], "vhost")) { + if ((values[i] == NULL) || STREQ(values[i], "on")) { + def->driver.virtio.name = VIR_DOMAIN_NET_BACKEND_TYPE_VHOST; + } else if (STREQ(keywords[i], "off")) { + def->driver.virtio.name = VIR_DOMAIN_NET_BACKEND_TYPE_QEMU; + } + } else if (STREQ(keywords[i], "sndbuf") && values[i]) { + if (virStrToLong_ul(values[i], NULL, 10, &def->tune.sndbuf) < 0) { + virReportError(VIR_ERR_INTERNAL_ERROR, + _("cannot parse sndbuf size in '%s'"), val); + virDomainNetDefFree(def); + def = NULL; + goto cleanup; + } + def->tune.sndbuf_specified = true; + } + } + + if (genmac) + virDomainNetGenerateMAC(xmlopt, &def->mac); + + cleanup: + for (i = 0; i < nkeywords; i++) { + VIR_FREE(keywords[i]); + VIR_FREE(values[i]); + } + VIR_FREE(keywords); + VIR_FREE(values); + return def; +} + + +/* + * Tries to parse a QEMU PCI device + */ +static virDomainHostdevDefPtr +qemuParseCommandLinePCI(const char *val) +{ + int bus = 0, slot = 0, func = 0; + const char *start; + char *end; + virDomainHostdevDefPtr def = virDomainHostdevDefAlloc(); + + if (!def) + goto error; + + if (!STRPREFIX(val, "host=")) { + virReportError(VIR_ERR_INTERNAL_ERROR, + _("unknown PCI device syntax '%s'"), val); + goto error; + } + + start = val + strlen("host="); + if (virStrToLong_i(start, &end, 16, &bus) < 0 || *end != ':') { + virReportError(VIR_ERR_INTERNAL_ERROR, + _("cannot extract PCI device bus '%s'"), val); + goto error; + } + start = end + 1; + if (virStrToLong_i(start, &end, 16, &slot) < 0 || *end != '.') { + virReportError(VIR_ERR_INTERNAL_ERROR, + _("cannot extract PCI device slot '%s'"), val); + goto error; + } + start = end + 1; + if (virStrToLong_i(start, NULL, 16, &func) < 0) { + virReportError(VIR_ERR_INTERNAL_ERROR, + _("cannot extract PCI device function '%s'"), val); + goto error; + } + + def->mode = VIR_DOMAIN_HOSTDEV_MODE_SUBSYS; + def->managed = true; + def->source.subsys.type = VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_PCI; + def->source.subsys.u.pci.addr.bus = bus; + def->source.subsys.u.pci.addr.slot = slot; + def->source.subsys.u.pci.addr.function = func; + return def; + + error: + virDomainHostdevDefFree(def); + return NULL; +} + + +/* + * Tries to parse a QEMU USB device + */ +static virDomainHostdevDefPtr +qemuParseCommandLineUSB(const char *val) +{ + virDomainHostdevDefPtr def = virDomainHostdevDefAlloc(); + virDomainHostdevSubsysUSBPtr usbsrc; + int first = 0, second = 0; + const char *start; + char *end; + + if (!def) + goto error; + usbsrc = &def->source.subsys.u.usb; + + if (!STRPREFIX(val, "host:")) { + virReportError(VIR_ERR_INTERNAL_ERROR, + _("unknown USB device syntax '%s'"), val); + goto error; + } + + start = val + strlen("host:"); + if (strchr(start, ':')) { + if (virStrToLong_i(start, &end, 16, &first) < 0 || *end != ':') { + virReportError(VIR_ERR_INTERNAL_ERROR, + _("cannot extract USB device vendor '%s'"), val); + goto error; + } + start = end + 1; + if (virStrToLong_i(start, NULL, 16, &second) < 0) { + virReportError(VIR_ERR_INTERNAL_ERROR, + _("cannot extract USB device product '%s'"), val); + goto error; + } + } else { + if (virStrToLong_i(start, &end, 10, &first) < 0 || *end != '.') { + virReportError(VIR_ERR_INTERNAL_ERROR, + _("cannot extract USB device bus '%s'"), val); + goto error; + } + start = end + 1; + if (virStrToLong_i(start, NULL, 10, &second) < 0) { + virReportError(VIR_ERR_INTERNAL_ERROR, + _("cannot extract USB device address '%s'"), val); + goto error; + } + } + + def->mode = VIR_DOMAIN_HOSTDEV_MODE_SUBSYS; + def->managed = false; + def->source.subsys.type = VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_USB; + if (*end == '.') { + usbsrc->bus = first; + usbsrc->device = second; + } else { + usbsrc->vendor = first; + usbsrc->product = second; + } + return def; + + error: + virDomainHostdevDefFree(def); + return NULL; +} + + +/* + * Tries to parse a QEMU serial/parallel device + */ +static int +qemuParseCommandLineChr(virDomainChrSourceDefPtr source, + const char *val) +{ + if (STREQ(val, "null")) { + source->type = VIR_DOMAIN_CHR_TYPE_NULL; + } else if (STREQ(val, "vc")) { + source->type = VIR_DOMAIN_CHR_TYPE_VC; + } else if (STREQ(val, "pty")) { + source->type = VIR_DOMAIN_CHR_TYPE_PTY; + } else if (STRPREFIX(val, "file:")) { + source->type = VIR_DOMAIN_CHR_TYPE_FILE; + if (VIR_STRDUP(source->data.file.path, val + strlen("file:")) < 0) + goto error; + } else if (STRPREFIX(val, "pipe:")) { + source->type = VIR_DOMAIN_CHR_TYPE_PIPE; + if (VIR_STRDUP(source->data.file.path, val + strlen("pipe:")) < 0) + goto error; + } else if (STREQ(val, "stdio")) { + source->type = VIR_DOMAIN_CHR_TYPE_STDIO; + } else if (STRPREFIX(val, "udp:")) { + const char *svc1, *host2, *svc2; + source->type = VIR_DOMAIN_CHR_TYPE_UDP; + val += strlen("udp:"); + svc1 = strchr(val, ':'); + host2 = svc1 ? strchr(svc1, '@') : NULL; + svc2 = host2 ? strchr(host2, ':') : NULL; + + if (svc1 && svc1 != val && + VIR_STRNDUP(source->data.udp.connectHost, val, svc1 - val) < 0) + goto error; + + if (svc1) { + svc1++; + if (VIR_STRNDUP(source->data.udp.connectService, svc1, + host2 ? host2 - svc1 : strlen(svc1)) < 0) + goto error; + } + + if (host2) { + host2++; + if (svc2 && svc2 != host2 && + VIR_STRNDUP(source->data.udp.bindHost, host2, svc2 - host2) < 0) + goto error; + } + + if (svc2) { + svc2++; + if (STRNEQ(svc2, "0")) { + if (VIR_STRDUP(source->data.udp.bindService, svc2) < 0) + goto error; + } + } + } else if (STRPREFIX(val, "tcp:") || + STRPREFIX(val, "telnet:")) { + const char *opt, *svc; + source->type = VIR_DOMAIN_CHR_TYPE_TCP; + if (STRPREFIX(val, "tcp:")) { + val += strlen("tcp:"); + } else { + val += strlen("telnet:"); + source->data.tcp.protocol = VIR_DOMAIN_CHR_TCP_PROTOCOL_TELNET; + } + svc = strchr(val, ':'); + if (!svc) { + virReportError(VIR_ERR_INTERNAL_ERROR, + _("cannot find port number in character device %s"), val); + goto error; + } + opt = strchr(svc, ','); + if (opt && strstr(opt, "server")) + source->data.tcp.listen = true; + + if (VIR_STRNDUP(source->data.tcp.host, val, svc - val) < 0) + goto error; + svc++; + if (VIR_STRNDUP(source->data.tcp.service, svc, opt ? opt - svc : -1) < 0) + goto error; + } else if (STRPREFIX(val, "unix:")) { + const char *opt; + val += strlen("unix:"); + opt = strchr(val, ','); + source->type = VIR_DOMAIN_CHR_TYPE_UNIX; + if (VIR_STRNDUP(source->data.nix.path, val, opt ? opt - val : -1) < 0) + goto error; + + } else if (STRPREFIX(val, "/dev")) { + source->type = VIR_DOMAIN_CHR_TYPE_DEV; + if (VIR_STRDUP(source->data.file.path, val) < 0) + goto error; + } else { + virReportError(VIR_ERR_INTERNAL_ERROR, + _("unknown character device syntax %s"), val); + goto error; + } + + return 0; + + error: + return -1; +} + + +static virCPUDefPtr +qemuInitGuestCPU(virDomainDefPtr dom) +{ + if (!dom->cpu) { + virCPUDefPtr cpu; + + if (VIR_ALLOC(cpu) < 0) + return NULL; + + cpu->type = VIR_CPU_TYPE_GUEST; + cpu->match = VIR_CPU_MATCH_EXACT; + dom->cpu = cpu; + } + + return dom->cpu; +} + + +static int +qemuParseCommandLineCPU(virDomainDefPtr dom, + const char *val) +{ + virCPUDefPtr cpu = NULL; + char **tokens; + char **hv_tokens = NULL; + char *model = NULL; + int ret = -1; + size_t i; + + if (!(tokens = virStringSplit(val, ",", 0))) + goto cleanup; + + if (tokens[0] == NULL) + goto syntax; + + for (i = 0; tokens[i] != NULL; i++) { + if (*tokens[i] == '\0') + goto syntax; + + if (i == 0) { + if (VIR_STRDUP(model, tokens[i]) < 0) + goto cleanup; + + if (STRNEQ(model, "qemu32") && STRNEQ(model, "qemu64")) { + if (!(cpu = qemuInitGuestCPU(dom))) + goto cleanup; + + cpu->model = model; + model = NULL; + } + } else if (*tokens[i] == '+' || *tokens[i] == '-') { + const char *feature = tokens[i] + 1; /* '+' or '-' */ + int policy; + + if (*tokens[i] == '+') + policy = VIR_CPU_FEATURE_REQUIRE; + else + policy = VIR_CPU_FEATURE_DISABLE; + + if (*feature == '\0') + goto syntax; + + if (dom->os.arch != VIR_ARCH_X86_64 && + dom->os.arch != VIR_ARCH_I686) { + virReportError(VIR_ERR_INTERNAL_ERROR, + _("%s platform doesn't support CPU features'"), + virArchToString(dom->os.arch)); + goto cleanup; + } + + if (STREQ(feature, "kvmclock")) { + bool present = (policy == VIR_CPU_FEATURE_REQUIRE); + size_t j; + + for (j = 0; j < dom->clock.ntimers; j++) { + if (dom->clock.timers[j]->name == VIR_DOMAIN_TIMER_NAME_KVMCLOCK) + break; + } + + if (j == dom->clock.ntimers) { + virDomainTimerDefPtr timer; + if (VIR_ALLOC(timer) < 0 || + VIR_APPEND_ELEMENT_COPY(dom->clock.timers, + dom->clock.ntimers, timer) < 0) { + VIR_FREE(timer); + goto cleanup; + } + timer->name = VIR_DOMAIN_TIMER_NAME_KVMCLOCK; + timer->present = present; + timer->tickpolicy = -1; + timer->track = -1; + timer->mode = -1; + } else if (dom->clock.timers[j]->present != -1 && + dom->clock.timers[j]->present != present) { + virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s", + _("conflicting occurrences of kvmclock feature")); + goto cleanup; + } + } else if (STREQ(feature, "kvm_pv_eoi")) { + if (policy == VIR_CPU_FEATURE_REQUIRE) + dom->apic_eoi = VIR_TRISTATE_SWITCH_ON; + else + dom->apic_eoi = VIR_TRISTATE_SWITCH_OFF; + } else { + if (!cpu) { + if (!(cpu = qemuInitGuestCPU(dom))) + goto cleanup; + + cpu->model = model; + model = NULL; + } + + if (virCPUDefAddFeature(cpu, feature, policy) < 0) + goto cleanup; + } + } else if (STREQ(tokens[i], "hv_crash")) { + size_t j; + for (j = 0; j < dom->npanics; j++) { + if (dom->panics[j]->model == VIR_DOMAIN_PANIC_MODEL_HYPERV) + break; + } + + if (j == dom->npanics) { + virDomainPanicDefPtr panic; + if (VIR_ALLOC(panic) < 0 || + VIR_APPEND_ELEMENT_COPY(dom->panics, + dom->npanics, panic) < 0) { + VIR_FREE(panic); + goto cleanup; + } + panic->model = VIR_DOMAIN_PANIC_MODEL_HYPERV; + } + } else if (STRPREFIX(tokens[i], "hv_")) { + const char *token = tokens[i] + 3; /* "hv_" */ + const char *feature, *value; + int f; + + if (*token == '\0') + goto syntax; + + if (!(hv_tokens = virStringSplit(token, "=", 2))) + goto cleanup; + + feature = hv_tokens[0]; + value = hv_tokens[1]; + + if (*feature == '\0') + goto syntax; + + dom->features[VIR_DOMAIN_FEATURE_HYPERV] = VIR_TRISTATE_SWITCH_ON; + + if ((f = virDomainHypervTypeFromString(feature)) < 0) { + virReportError(VIR_ERR_CONFIG_UNSUPPORTED, + _("unsupported HyperV Enlightenment feature " + "'%s'"), feature); + goto cleanup; + } + + switch ((virDomainHyperv) f) { + case VIR_DOMAIN_HYPERV_RELAXED: + case VIR_DOMAIN_HYPERV_VAPIC: + if (value) { + virReportError(VIR_ERR_CONFIG_UNSUPPORTED, + _("HyperV feature '%s' should not " + "have a value"), feature); + goto cleanup; + } + dom->hyperv_features[f] = VIR_TRISTATE_SWITCH_ON; + break; + + case VIR_DOMAIN_HYPERV_SPINLOCKS: + dom->hyperv_features[f] = VIR_TRISTATE_SWITCH_ON; + if (!value) { + virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s", + _("missing HyperV spinlock retry count")); + goto cleanup; + } + + if (virStrToLong_ui(value, NULL, 0, &dom->hyperv_spinlocks) < 0) { + virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s", + _("cannot parse HyperV spinlock retry count")); + goto cleanup; + } + + if (dom->hyperv_spinlocks < 0xFFF) + dom->hyperv_spinlocks = 0xFFF; + break; + + case VIR_DOMAIN_HYPERV_LAST: + break; + } + virStringFreeList(hv_tokens); + hv_tokens = NULL; + } else if (STREQ(tokens[i], "kvm=off")) { + dom->features[VIR_DOMAIN_FEATURE_KVM] = VIR_TRISTATE_SWITCH_ON; + dom->kvm_features[VIR_DOMAIN_KVM_HIDDEN] = VIR_TRISTATE_SWITCH_ON; + } + } + + if (dom->os.arch == VIR_ARCH_X86_64) { + bool is_32bit = false; + if (cpu) { + virCPUDataPtr cpuData = NULL; + + if (cpuEncode(VIR_ARCH_X86_64, cpu, NULL, &cpuData, + NULL, NULL, NULL, NULL) < 0) + goto cleanup; + + is_32bit = (cpuHasFeature(cpuData, "lm") != 1); + cpuDataFree(cpuData); + } else if (model) { + is_32bit = STREQ(model, "qemu32"); + } + + if (is_32bit) + dom->os.arch = VIR_ARCH_I686; + } + + ret = 0; + + cleanup: + VIR_FREE(model); + virStringFreeList(tokens); + virStringFreeList(hv_tokens); + return ret; + + syntax: + virReportError(VIR_ERR_INTERNAL_ERROR, + _("unknown CPU syntax '%s'"), val); + goto cleanup; +} + + +static int +qemuParseCommandLineSmp(virDomainDefPtr dom, + const char *val) +{ + unsigned int sockets = 0; + unsigned int cores = 0; + unsigned int threads = 0; + unsigned int maxcpus = 0; + unsigned int vcpus = 0; + size_t i; + int nkws; + char **kws; + char **vals; + int n; + char *end; + int ret; + + if (qemuParseKeywords(val, &kws, &vals, &nkws, 1) < 0) + return -1; + + for (i = 0; i < nkws; i++) { + if (vals[i] == NULL) { + if (i > 0 || + virStrToLong_ui(kws[i], &end, 10, &vcpus) < 0 || *end != '\0') + goto syntax; + } else { + if (virStrToLong_i(vals[i], &end, 10, &n) < 0 || *end != '\0') + goto syntax; + if (STREQ(kws[i], "sockets")) + sockets = n; + else if (STREQ(kws[i], "cores")) + cores = n; + else if (STREQ(kws[i], "threads")) + threads = n; + else if (STREQ(kws[i], "maxcpus")) + maxcpus = n; + else + goto syntax; + } + } + + if (maxcpus == 0) + maxcpus = vcpus; + + if (virDomainDefSetVcpusMax(dom, maxcpus) < 0) + goto error; + + if (virDomainDefSetVcpus(dom, vcpus) < 0) + goto error; + + if (sockets && cores && threads) { + virCPUDefPtr cpu; + + if (!(cpu = qemuInitGuestCPU(dom))) + goto error; + cpu->sockets = sockets; + cpu->cores = cores; + cpu->threads = threads; + } else if (sockets || cores || threads) { + goto syntax; + } + + ret = 0; + + cleanup: + for (i = 0; i < nkws; i++) { + VIR_FREE(kws[i]); + VIR_FREE(vals[i]); + } + VIR_FREE(kws); + VIR_FREE(vals); + + return ret; + + syntax: + virReportError(VIR_ERR_INTERNAL_ERROR, + _("cannot parse CPU topology '%s'"), val); + error: + ret = -1; + goto cleanup; +} + + +static void +qemuParseCommandLineBootDevs(virDomainDefPtr def, const char *str) +{ + int n, b = 0; + + for (n = 0; str[n] && b < VIR_DOMAIN_BOOT_LAST; n++) { + if (str[n] == 'a') + def->os.bootDevs[b++] = VIR_DOMAIN_BOOT_FLOPPY; + else if (str[n] == 'c') + def->os.bootDevs[b++] = VIR_DOMAIN_BOOT_DISK; + else if (str[n] == 'd') + def->os.bootDevs[b++] = VIR_DOMAIN_BOOT_CDROM; + else if (str[n] == 'n') + def->os.bootDevs[b++] = VIR_DOMAIN_BOOT_NET; + else if (str[n] == ',') + break; + } + def->os.nBootDevs = b; +} + + +/* + * Analyse the env and argv settings and reconstruct a + * virDomainDefPtr representing these settings as closely + * as is practical. This is not an exact science.... + */ +static virDomainDefPtr +qemuParseCommandLine(virCapsPtr 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 0000000..a743189 --- /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 + * <http://www.gnu.org/licenses/>. + * + * Author: Daniel P. Berrange <berrange@redhat.com> + */ + +#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 ac06bed..6671be4 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" -- 2.5.0

On Wed, Feb 10, 2016 at 08:05:53AM -0500, John Ferlan wrote:
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.
Signed-off-by: John Ferlan <jferlan@redhat.com> --- po/POTFILES.in | 1 + src/Makefile.am | 1 + src/qemu/qemu_command.c | 2710 ---------------------------------------- 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 | 2744 +++++++++++++++++++++++++++++++++++++++++ src/qemu/qemu_parse_command.h | 53 + tests/qemuargv2xmltest.c | 2 +- 10 files changed, 2807 insertions(+), 2739 deletions(-) create mode 100644 src/qemu/qemu_parse_command.c create mode 100644 src/qemu/qemu_parse_command.h
diff --git a/src/qemu/qemu_command.h b/src/qemu/qemu_command.h index 53bfda5..f549aa5 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
If you leave this definition here you don't need to include qemu_parse_command.h in qemu_domain.c. Jan

On 02/10/2016 08:47 AM, Ján Tomko wrote:
On Wed, Feb 10, 2016 at 08:05:53AM -0500, John Ferlan wrote:
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.
Signed-off-by: John Ferlan <jferlan@redhat.com> --- po/POTFILES.in | 1 + src/Makefile.am | 1 + src/qemu/qemu_command.c | 2710 ---------------------------------------- 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 | 2744 +++++++++++++++++++++++++++++++++++++++++ src/qemu/qemu_parse_command.h | 53 + tests/qemuargv2xmltest.c | 2 +- 10 files changed, 2807 insertions(+), 2739 deletions(-) create mode 100644 src/qemu/qemu_parse_command.c create mode 100644 src/qemu/qemu_parse_command.h
diff --git a/src/qemu/qemu_command.h b/src/qemu/qemu_command.h index 53bfda5..f549aa5 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
If you leave this definition here you don't need to include qemu_parse_command.h in qemu_domain.c.
Yeah, but it's not used in qemu_command.c, it's used in qemu_command_parse.c though. That's why I moved it... John

On Wed, Feb 10, 2016 at 08:05:50AM -0500, John Ferlan wrote:
v1: http://www.redhat.com/archives/libvir-list/2016-February/msg00428.html
Adjustments since v1:
* Implement suggestions - created qemuParseCommandLineVnc to handle the alloc/free of the 'vnc'... Note: Difference in refactored code is to use of "cleanup" instead of "error" and "ret = -1" initializer.
* Modify the virDomainGraphicsListenSetAddress, to have one call instead of two... adjusting the 'val' based on whether find '[' in first char
* Split out the qemuParseCommandLine{String|Pid} functions into their own module along with all the collateral damage that causes. Pared down the list of #includes for new modules by build trial and error.
John Ferlan (3): qemu: Introduce qemuParseCommandLineVnc qemu: Check return status for virDomainGraphicsListenSetAddress qemu: Split the command parsing routines into own module
po/POTFILES.in | 1 + src/Makefile.am | 1 + src/qemu/qemu_command.c | 2703 ---------------------------------------- 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 | 2744 +++++++++++++++++++++++++++++++++++++++++ src/qemu/qemu_parse_command.h | 53 + tests/qemuargv2xmltest.c | 2 +- 10 files changed, 2807 insertions(+), 2732 deletions(-) create mode 100644 src/qemu/qemu_parse_command.c create mode 100644 src/qemu/qemu_parse_command.h
ACK series Jan
participants (3)
-
John Ferlan
-
Ján Tomko
-
Laine Stump