2016-05-31 21:35:14 +00:00
|
|
|
/*
|
|
|
|
* virqemu.c: utilities for working with qemu and its tools
|
|
|
|
*
|
|
|
|
* Copyright (C) 2016 Red Hat, Inc.
|
|
|
|
*
|
|
|
|
* This library is free software; you can redistribute it and/or
|
|
|
|
* modify it under the terms of the GNU Lesser General Public
|
|
|
|
* License as published by the Free Software Foundation; either
|
|
|
|
* version 2.1 of the License, or (at your option) any later version.
|
|
|
|
*
|
|
|
|
* This library is distributed in the hope that it will be useful,
|
|
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
|
|
* Lesser General Public License for more details.
|
|
|
|
*
|
|
|
|
* You should have received a copy of the GNU Lesser General Public
|
|
|
|
* License along with this library. If not, see
|
|
|
|
* <http://www.gnu.org/licenses/>.
|
|
|
|
*
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
|
|
#include <config.h>
|
|
|
|
|
|
|
|
#include "virbuffer.h"
|
|
|
|
#include "virerror.h"
|
|
|
|
#include "virlog.h"
|
|
|
|
#include "virqemu.h"
|
2016-07-22 15:19:28 +00:00
|
|
|
#include "virstring.h"
|
|
|
|
#include "viralloc.h"
|
2016-05-31 21:35:14 +00:00
|
|
|
|
|
|
|
#define VIR_FROM_THIS VIR_FROM_NONE
|
|
|
|
|
|
|
|
VIR_LOG_INIT("util.qemu");
|
|
|
|
|
2016-07-22 15:19:28 +00:00
|
|
|
struct virQEMUCommandLineJSONIteratorData {
|
|
|
|
const char *prefix;
|
|
|
|
virBufferPtr buf;
|
2020-05-14 07:41:48 +00:00
|
|
|
const char *skipKey;
|
2016-07-22 15:50:03 +00:00
|
|
|
virQEMUBuildCommandLineJSONArrayFormatFunc arrayFunc;
|
2016-07-22 15:19:28 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
static int
|
|
|
|
virQEMUBuildCommandLineJSONRecurse(const char *key,
|
2016-12-14 14:25:31 +00:00
|
|
|
virJSONValuePtr value,
|
2016-07-22 15:19:28 +00:00
|
|
|
virBufferPtr buf,
|
2020-05-14 07:41:48 +00:00
|
|
|
const char *skipKey,
|
2016-07-22 15:50:03 +00:00
|
|
|
virQEMUBuildCommandLineJSONArrayFormatFunc arrayFunc,
|
2016-07-22 15:19:28 +00:00
|
|
|
bool nested);
|
|
|
|
|
2016-07-22 15:50:03 +00:00
|
|
|
|
|
|
|
|
|
|
|
int
|
|
|
|
virQEMUBuildCommandLineJSONArrayBitmap(const char *key,
|
2016-12-14 14:25:31 +00:00
|
|
|
virJSONValuePtr array,
|
2020-05-14 07:41:48 +00:00
|
|
|
virBufferPtr buf,
|
2021-02-16 12:36:15 +00:00
|
|
|
const char *skipKey G_GNUC_UNUSED)
|
2016-07-22 15:50:03 +00:00
|
|
|
{
|
|
|
|
ssize_t pos = -1;
|
|
|
|
ssize_t end;
|
2019-10-15 12:47:50 +00:00
|
|
|
g_autoptr(virBitmap) bitmap = NULL;
|
2016-07-22 15:50:03 +00:00
|
|
|
|
|
|
|
if (virJSONValueGetArrayAsBitmap(array, &bitmap) < 0)
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
while ((pos = virBitmapNextSetBit(bitmap, pos)) > -1) {
|
|
|
|
if ((end = virBitmapNextClearBit(bitmap, pos)) < 0)
|
|
|
|
end = virBitmapLastSetBit(bitmap) + 1;
|
|
|
|
|
|
|
|
if (end - 1 > pos) {
|
2016-07-25 12:59:19 +00:00
|
|
|
virBufferAsprintf(buf, "%s=%zd-%zd,", key, pos, end - 1);
|
2016-07-22 15:50:03 +00:00
|
|
|
pos = end;
|
|
|
|
} else {
|
2016-07-25 12:59:19 +00:00
|
|
|
virBufferAsprintf(buf, "%s=%zd,", key, pos);
|
2016-07-22 15:50:03 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2016-07-25 17:47:40 +00:00
|
|
|
int
|
|
|
|
virQEMUBuildCommandLineJSONArrayNumbered(const char *key,
|
2016-12-14 14:25:31 +00:00
|
|
|
virJSONValuePtr array,
|
2020-05-14 07:41:48 +00:00
|
|
|
virBufferPtr buf,
|
2021-02-16 12:36:15 +00:00
|
|
|
const char *skipKey)
|
2016-07-25 17:47:40 +00:00
|
|
|
{
|
2016-12-14 14:25:31 +00:00
|
|
|
virJSONValuePtr member;
|
2016-07-25 17:47:40 +00:00
|
|
|
size_t i;
|
|
|
|
|
2016-10-10 20:08:39 +00:00
|
|
|
for (i = 0; i < virJSONValueArraySize(array); i++) {
|
2019-10-15 13:16:31 +00:00
|
|
|
g_autofree char *prefix = NULL;
|
2016-07-25 17:47:40 +00:00
|
|
|
|
2020-08-03 15:27:58 +00:00
|
|
|
member = virJSONValueArrayGet((virJSONValuePtr) array, i);
|
2019-10-22 13:26:14 +00:00
|
|
|
prefix = g_strdup_printf("%s.%zu", key, i);
|
2016-07-25 17:47:40 +00:00
|
|
|
|
2021-02-16 12:36:15 +00:00
|
|
|
if (virQEMUBuildCommandLineJSONRecurse(prefix, member, buf, skipKey,
|
2016-07-25 17:47:40 +00:00
|
|
|
virQEMUBuildCommandLineJSONArrayNumbered,
|
|
|
|
true) < 0)
|
2018-07-28 18:01:46 +00:00
|
|
|
return 0;
|
2016-07-25 17:47:40 +00:00
|
|
|
}
|
|
|
|
|
2018-07-28 18:01:46 +00:00
|
|
|
return 0;
|
2016-07-25 17:47:40 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2020-05-15 08:59:40 +00:00
|
|
|
/**
|
2020-07-09 04:42:21 +00:00
|
|
|
* This array converter is for quirky cases where the QMP schema mandates an
|
2020-05-15 08:59:40 +00:00
|
|
|
* array of objects with only one attribute 'str' which needs to be formatted as
|
|
|
|
* repeated key-value pairs without the 'str' being printed:
|
|
|
|
*
|
|
|
|
* 'guestfwd': [
|
|
|
|
* { "str": "tcp:10.0.2.1:4600-chardev:charchannel0" },
|
|
|
|
* { "str": "...."},
|
|
|
|
* ]
|
|
|
|
*
|
|
|
|
* guestfwd=tcp:10.0.2.1:4600-chardev:charchannel0,guestfwd=...
|
|
|
|
*/
|
|
|
|
static int
|
|
|
|
virQEMUBuildCommandLineJSONArrayObjectsStr(const char *key,
|
|
|
|
virJSONValuePtr array,
|
|
|
|
virBufferPtr buf,
|
2021-02-16 12:36:15 +00:00
|
|
|
const char *skipKey G_GNUC_UNUSED)
|
2020-05-15 08:59:40 +00:00
|
|
|
{
|
|
|
|
g_auto(virBuffer) tmp = VIR_BUFFER_INITIALIZER;
|
|
|
|
size_t i;
|
|
|
|
|
|
|
|
for (i = 0; i < virJSONValueArraySize(array); i++) {
|
|
|
|
virJSONValuePtr member = virJSONValueArrayGet(array, i);
|
|
|
|
const char *str = virJSONValueObjectGetString(member, "str");
|
|
|
|
|
|
|
|
if (!str)
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
virBufferAsprintf(&tmp, "%s=%s,", key, str);
|
|
|
|
}
|
|
|
|
|
|
|
|
virBufferAddBuffer(buf, &tmp);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2016-07-22 15:19:28 +00:00
|
|
|
/* internal iterator to handle nested object formatting */
|
|
|
|
static int
|
|
|
|
virQEMUBuildCommandLineJSONIterate(const char *key,
|
2016-12-14 14:25:31 +00:00
|
|
|
virJSONValuePtr value,
|
2016-07-22 15:19:28 +00:00
|
|
|
void *opaque)
|
|
|
|
{
|
|
|
|
struct virQEMUCommandLineJSONIteratorData *data = opaque;
|
|
|
|
|
2020-05-14 07:41:48 +00:00
|
|
|
if (STREQ_NULLABLE(key, data->skipKey))
|
|
|
|
return 0;
|
|
|
|
|
2016-07-22 15:19:28 +00:00
|
|
|
if (data->prefix) {
|
2019-10-15 13:16:31 +00:00
|
|
|
g_autofree char *tmpkey = NULL;
|
2018-07-28 18:01:46 +00:00
|
|
|
|
2019-10-22 13:26:14 +00:00
|
|
|
tmpkey = g_strdup_printf("%s.%s", data->prefix, key);
|
2016-07-22 15:19:28 +00:00
|
|
|
|
2018-07-28 18:01:46 +00:00
|
|
|
return virQEMUBuildCommandLineJSONRecurse(tmpkey, value, data->buf,
|
2021-02-16 12:36:15 +00:00
|
|
|
data->skipKey,
|
2018-09-19 08:38:14 +00:00
|
|
|
data->arrayFunc, false);
|
2016-07-22 15:19:28 +00:00
|
|
|
} else {
|
2018-07-28 18:01:46 +00:00
|
|
|
return virQEMUBuildCommandLineJSONRecurse(key, value, data->buf,
|
2021-02-16 12:36:15 +00:00
|
|
|
data->skipKey,
|
2018-09-19 08:38:14 +00:00
|
|
|
data->arrayFunc, false);
|
2016-07-22 15:19:28 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-05-31 21:35:14 +00:00
|
|
|
|
|
|
|
static int
|
2016-07-22 13:54:57 +00:00
|
|
|
virQEMUBuildCommandLineJSONRecurse(const char *key,
|
2016-12-14 14:25:31 +00:00
|
|
|
virJSONValuePtr value,
|
2016-07-22 13:54:57 +00:00
|
|
|
virBufferPtr buf,
|
2020-05-14 07:41:48 +00:00
|
|
|
const char *skipKey,
|
2016-07-22 15:50:03 +00:00
|
|
|
virQEMUBuildCommandLineJSONArrayFormatFunc arrayFunc,
|
2016-07-22 13:54:57 +00:00
|
|
|
bool nested)
|
2016-05-31 21:35:14 +00:00
|
|
|
{
|
2021-02-16 12:36:15 +00:00
|
|
|
struct virQEMUCommandLineJSONIteratorData data = { key, buf, skipKey, arrayFunc };
|
2018-03-29 18:30:05 +00:00
|
|
|
virJSONType type = virJSONValueGetType(value);
|
2016-05-31 21:35:14 +00:00
|
|
|
virJSONValuePtr elem;
|
2018-03-29 18:41:07 +00:00
|
|
|
bool tmp;
|
2016-05-31 21:35:14 +00:00
|
|
|
size_t i;
|
|
|
|
|
2018-03-29 18:30:05 +00:00
|
|
|
if (!key && type != VIR_JSON_TYPE_OBJECT) {
|
2016-07-22 15:19:28 +00:00
|
|
|
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
|
|
|
|
_("only JSON objects can be top level"));
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
2018-03-29 18:30:05 +00:00
|
|
|
switch (type) {
|
2016-05-31 21:35:14 +00:00
|
|
|
case VIR_JSON_TYPE_STRING:
|
2016-07-25 12:59:19 +00:00
|
|
|
virBufferAsprintf(buf, "%s=", key);
|
2018-03-29 18:41:07 +00:00
|
|
|
virQEMUBuildBufferEscapeComma(buf, virJSONValueGetString(value));
|
2016-07-25 12:59:19 +00:00
|
|
|
virBufferAddLit(buf, ",");
|
2016-05-31 21:35:14 +00:00
|
|
|
break;
|
|
|
|
|
|
|
|
case VIR_JSON_TYPE_NUMBER:
|
2018-03-29 18:41:07 +00:00
|
|
|
virBufferAsprintf(buf, "%s=%s,", key, virJSONValueGetNumberString(value));
|
2016-05-31 21:35:14 +00:00
|
|
|
break;
|
|
|
|
|
|
|
|
case VIR_JSON_TYPE_BOOLEAN:
|
2018-03-29 18:41:07 +00:00
|
|
|
virJSONValueGetBoolean(value, &tmp);
|
2021-02-16 12:36:15 +00:00
|
|
|
if (tmp)
|
|
|
|
virBufferAsprintf(buf, "%s=on,", key);
|
|
|
|
else
|
|
|
|
virBufferAsprintf(buf, "%s=off,", key);
|
2016-05-31 21:35:14 +00:00
|
|
|
break;
|
|
|
|
|
|
|
|
case VIR_JSON_TYPE_ARRAY:
|
|
|
|
if (nested) {
|
|
|
|
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
|
2016-07-22 13:54:57 +00:00
|
|
|
_("nested JSON array to commandline conversion is "
|
|
|
|
"not supported"));
|
2016-05-31 21:35:14 +00:00
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
2021-02-16 12:36:15 +00:00
|
|
|
if (!arrayFunc || arrayFunc(key, value, buf, skipKey) < 0) {
|
2016-05-31 21:35:14 +00:00
|
|
|
/* fallback, treat the array as a non-bitmap, adding the key
|
|
|
|
* for each member */
|
|
|
|
for (i = 0; i < virJSONValueArraySize(value); i++) {
|
|
|
|
elem = virJSONValueArrayGet((virJSONValuePtr)value, i);
|
|
|
|
|
|
|
|
/* recurse to avoid duplicating code */
|
2020-05-14 07:41:48 +00:00
|
|
|
if (virQEMUBuildCommandLineJSONRecurse(key, elem, buf, skipKey,
|
2021-02-16 12:36:15 +00:00
|
|
|
arrayFunc, true) < 0)
|
2016-05-31 21:35:14 +00:00
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
|
|
case VIR_JSON_TYPE_OBJECT:
|
2016-07-22 15:19:28 +00:00
|
|
|
if (virJSONValueObjectForeachKeyValue(value,
|
|
|
|
virQEMUBuildCommandLineJSONIterate,
|
|
|
|
&data) < 0)
|
|
|
|
return -1;
|
|
|
|
break;
|
|
|
|
|
2016-05-31 21:35:14 +00:00
|
|
|
case VIR_JSON_TYPE_NULL:
|
|
|
|
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
|
2016-07-22 15:19:28 +00:00
|
|
|
_("NULL JSON type can't be converted to commandline"));
|
2016-05-31 21:35:14 +00:00
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2016-07-22 13:54:57 +00:00
|
|
|
/**
|
|
|
|
* virQEMUBuildCommandLineJSON:
|
|
|
|
* @value: json object containing the value
|
|
|
|
* @buf: otuput buffer
|
2020-05-14 07:41:48 +00:00
|
|
|
* @skipKey: name of key that will be handled separately by caller
|
2016-07-22 15:50:03 +00:00
|
|
|
* @arrayFunc: array formatter function to allow for different syntax
|
2016-07-22 13:54:57 +00:00
|
|
|
*
|
|
|
|
* Formats JSON value object into command line parameters suitable for use with
|
|
|
|
* qemu.
|
|
|
|
*
|
|
|
|
* Returns 0 on success -1 on error.
|
|
|
|
*/
|
|
|
|
int
|
2016-12-14 14:25:31 +00:00
|
|
|
virQEMUBuildCommandLineJSON(virJSONValuePtr value,
|
2016-07-22 15:50:03 +00:00
|
|
|
virBufferPtr buf,
|
2020-05-14 07:41:48 +00:00
|
|
|
const char *skipKey,
|
2016-07-22 15:50:03 +00:00
|
|
|
virQEMUBuildCommandLineJSONArrayFormatFunc array)
|
2016-07-22 13:54:57 +00:00
|
|
|
{
|
2021-02-16 12:36:15 +00:00
|
|
|
if (virQEMUBuildCommandLineJSONRecurse(NULL, value, buf, skipKey, array, false) < 0)
|
2016-07-25 12:59:19 +00:00
|
|
|
return -1;
|
|
|
|
|
2020-02-02 19:26:38 +00:00
|
|
|
virBufferTrim(buf, ",");
|
2016-07-25 12:59:19 +00:00
|
|
|
|
|
|
|
return 0;
|
2016-05-31 21:35:14 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2020-05-14 08:10:16 +00:00
|
|
|
/**
|
|
|
|
* virQEMUBuildNetdevCommandlineFromJSON:
|
|
|
|
* @props: JSON properties describing a netdev
|
2020-05-15 12:24:21 +00:00
|
|
|
* @rawjson: don't transform to commandline args, but just stringify json
|
2020-05-14 08:10:16 +00:00
|
|
|
*
|
|
|
|
* Converts @props into arguments for -netdev including all the quirks and
|
|
|
|
* differences between the monitor and command line syntax.
|
2020-05-15 12:24:21 +00:00
|
|
|
*
|
|
|
|
* @rawjson is meant for testing of the schema in the xml2argvtest
|
2020-05-14 08:10:16 +00:00
|
|
|
*/
|
|
|
|
char *
|
2020-05-15 12:24:21 +00:00
|
|
|
virQEMUBuildNetdevCommandlineFromJSON(virJSONValuePtr props,
|
|
|
|
bool rawjson)
|
2020-05-14 08:10:16 +00:00
|
|
|
{
|
|
|
|
const char *type = virJSONValueObjectGetString(props, "type");
|
|
|
|
g_auto(virBuffer) buf = VIR_BUFFER_INITIALIZER;
|
|
|
|
|
2020-05-15 12:24:21 +00:00
|
|
|
if (rawjson)
|
|
|
|
return virJSONValueToString(props, false);
|
|
|
|
|
2020-05-14 08:10:16 +00:00
|
|
|
virBufferAsprintf(&buf, "%s,", type);
|
|
|
|
|
2021-02-16 12:36:15 +00:00
|
|
|
if (virQEMUBuildCommandLineJSON(props, &buf, "type",
|
2020-05-15 08:59:40 +00:00
|
|
|
virQEMUBuildCommandLineJSONArrayObjectsStr) < 0)
|
2020-05-14 08:10:16 +00:00
|
|
|
return NULL;
|
|
|
|
|
|
|
|
return virBufferContentAndReset(&buf);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2016-07-25 17:51:18 +00:00
|
|
|
char *
|
2016-12-14 14:25:31 +00:00
|
|
|
virQEMUBuildDriveCommandlineFromJSON(virJSONValuePtr srcdef)
|
2016-07-25 17:51:18 +00:00
|
|
|
{
|
2020-07-03 02:30:20 +00:00
|
|
|
g_auto(virBuffer) buf = VIR_BUFFER_INITIALIZER;
|
2016-07-25 17:51:18 +00:00
|
|
|
|
2021-02-16 12:36:15 +00:00
|
|
|
if (virQEMUBuildCommandLineJSON(srcdef, &buf, NULL,
|
2016-07-25 17:51:18 +00:00
|
|
|
virQEMUBuildCommandLineJSONArrayNumbered) < 0)
|
2020-07-03 03:20:00 +00:00
|
|
|
return NULL;
|
2016-07-25 17:51:18 +00:00
|
|
|
|
2020-07-03 03:20:00 +00:00
|
|
|
return virBufferContentAndReset(&buf);
|
2016-07-25 17:51:18 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2016-07-15 11:02:05 +00:00
|
|
|
/**
|
|
|
|
* virQEMUBuildBufferEscapeComma:
|
|
|
|
* @buf: buffer to append the escaped string
|
|
|
|
* @str: the string to escape
|
|
|
|
*
|
|
|
|
* qemu requires that any values passed on the command line which contain
|
|
|
|
* a ',' must escape it using an extra ',' as the escape character
|
|
|
|
*/
|
|
|
|
void
|
|
|
|
virQEMUBuildBufferEscapeComma(virBufferPtr buf, const char *str)
|
|
|
|
{
|
|
|
|
virBufferEscape(buf, ',', ",", "%s", str);
|
|
|
|
}
|