libvirt/src/qemu/qemu_qapi.c

280 lines
8.2 KiB
C
Raw Normal View History

/*
* qemu_qapi.c: helper functions for QEMU QAPI schema handling
*
* 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 "qemu_qapi.h"
#include "viralloc.h"
#include "virstring.h"
#include "virerror.h"
#include "virlog.h"
#include "c-ctype.h"
#define VIR_FROM_THIS VIR_FROM_QEMU
VIR_LOG_INIT("qemu.qemu_qapi");
/**
* virQEMUQAPISchemaObjectGet:
* @field: name of the object containing the requested type
* @name: name of the requested type
* @namefield: name of the object property holding @name
* @elem: QAPI schema entry JSON object
*
* Helper that selects the type of a QMP schema object member or it's variant
* member. Returns the QMP entry on success or NULL on error.
*/
static virJSONValuePtr
virQEMUQAPISchemaObjectGet(const char *field,
const char *name,
const char *namefield,
virJSONValuePtr elem)
{
virJSONValuePtr arr;
virJSONValuePtr cur;
const char *curname;
size_t i;
if (!(arr = virJSONValueObjectGetArray(elem, field)))
return NULL;
for (i = 0; i < virJSONValueArraySize(arr); i++) {
if (!(cur = virJSONValueArrayGet(arr, i)) ||
!(curname = virJSONValueObjectGetString(cur, namefield)))
continue;
if (STREQ(name, curname))
return cur;
}
return NULL;
}
static const char *
virQEMUQAPISchemaTypeFromObject(virJSONValuePtr obj)
{
if (!obj)
return NULL;
return virJSONValueObjectGetString(obj, "type");
}
/**
* virQEMUQAPISchemaObjectGetType:
* @field: name of the object containing the requested type
* @name: name of the requested type
* @namefield: name of the object property holding @name
* @elem: QAPI schema entry JSON object
*
* Helper that selects the type of a QMP schema object member or it's variant
* member. Returns the type string on success or NULL on error.
*/
static const char *
virQEMUQAPISchemaObjectGetType(const char *field,
const char *name,
const char *namefield,
virJSONValuePtr elem)
{
virJSONValuePtr obj = virQEMUQAPISchemaObjectGet(field, name, namefield, elem);
return virQEMUQAPISchemaTypeFromObject(obj);
}
static int
virQEMUQAPISchemaTraverse(const char *baseName,
char **query,
virHashTablePtr schema,
virJSONValuePtr *type)
{
virJSONValuePtr curtype;
virJSONValuePtr obj;
const char *metatype;
const char *querytype = NULL;
const char *querystr;
char modifier;
if (!(curtype = virHashLookup(schema, baseName)))
return 0;
if (!*query) {
if (type)
*type = curtype;
return 1;
}
if (!(metatype = virJSONValueObjectGetString(curtype, "meta-type")))
return 0;
/* flatten arrays by default */
if (STREQ(metatype, "array")) {
if (!(querytype = virJSONValueObjectGetString(curtype, "element-type")))
return 0;
} else if (STREQ(metatype, "object")) {
querystr = *query;
modifier = **query;
if (!c_isalpha(modifier))
querystr++;
if (modifier == '+') {
querytype = virQEMUQAPISchemaObjectGetType("variants",
querystr,
"case", curtype);
} else {
obj = virQEMUQAPISchemaObjectGet("members", querystr,
"name", curtype);
if (modifier == '*' &&
!virJSONValueObjectHasKey(obj, "default"))
return 0;
querytype = virQEMUQAPISchemaTypeFromObject(obj);
}
query++;
} else if (STREQ(metatype, "command") ||
STREQ(metatype, "event")) {
if (!(querytype = virJSONValueObjectGetString(curtype, *query)))
return 0;
query++;
} else {
/* alternates, basic types and enums can't be entered */
return 0;
}
if (!querytype)
return 0;
return virQEMUQAPISchemaTraverse(querytype, query, schema, type);
}
/**
* virQEMUQAPISchemaPathGet:
* @query: string specifying the required data type (see below)
* @schema: hash table containing the schema data
* @entry: filled with the located schema object requested by @query (optional)
*
* Retrieves the requested schema entry specified by @query to @entry. The
* @query parameter has the following syntax which is very closely tied to the
* qemu schema syntax entries separated by slashes with a few special characters:
*
* "command_or_event/attribute/subattribute/+variant_discriminator/subattribute"
*
* command_or_event: name of the event or attribute to introspect
* attribute: selects whether arguments or return type should be introspected
* ("arg-type" or "ret-type" for commands, "arg-type" for events)
* subattribute: specifies member name of object types
* *subattribute: same as above but must be optional (has a property named
* 'default' field in the schema)
* +variant_discriminator: In the case of unionized objects, select a
* specific case to introspect.
*
* If the name of any (sub)attribute starts with non-alphabetical symbols it
* needs to be prefixed by a single space.
*
* Array types are automatically flattened to the singular type. Alternate
* types are currently not supported.
*
* The above types can be chained arbitrarily using slashes to construct any
* path into the schema tree.
*
* Returns 1 if @query was found in @schema filling @entry if non-NULL, 0 if
* @query was not found in @schema and -1 on other errors along with an appropriate
* error message.
*/
int
virQEMUQAPISchemaPathGet(const char *query,
virHashTablePtr schema,
virJSONValuePtr *entry)
{
VIR_AUTOSTRINGLIST elems = NULL;
if (entry)
*entry = NULL;
if (!(elems = virStringSplit(query, "/", 0)))
return -1;
if (!*elems) {
virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("malformed query string"));
return -1;
}
return virQEMUQAPISchemaTraverse(elems[0], elems + 1, schema, entry);
}
bool
virQEMUQAPISchemaPathExists(const char *query,
virHashTablePtr schema)
{
return virQEMUQAPISchemaPathGet(query, schema, NULL) == 1;
}
static int
virQEMUQAPISchemaEntryProcess(size_t pos ATTRIBUTE_UNUSED,
virJSONValuePtr item,
void *opaque)
{
const char *name;
virHashTablePtr schema = opaque;
if (!(name = virJSONValueObjectGetString(item, "name"))) {
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
_("malformed QMP schema"));
return -1;
}
if (virHashAddEntry(schema, name, item) < 0)
return -1;
return 0;
}
/**
* virQEMUQAPISchemaConvert:
* @schemareply: Schema data as returned by the qemu monitor
*
* Converts the schema into the hash-table used by the functions working with
* the schema. @schemareply is consumed and freed.
*/
virHashTablePtr
virQEMUQAPISchemaConvert(virJSONValuePtr schemareply)
{
VIR_AUTOPTR(virHashTable) schema = NULL;
VIR_AUTOPTR(virJSONValue) schemajson = schemareply;
if (!(schema = virHashCreate(512, virJSONValueHashFree)))
return NULL;
if (virJSONValueArrayForeachSteal(schemajson,
virQEMUQAPISchemaEntryProcess,
schema) < 0)
return NULL;
VIR_RETURN_PTR(schema);
}