/* * 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 * . */ #include #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 virJSONValuePtr virQEMUQAPISchemaTraverse(const char *baseName, char **query, virHashTablePtr schema) { virJSONValuePtr base; const char *metatype; const char *querystr; char modifier; while (1) { if (!(base = virHashLookup(schema, baseName))) return NULL; if (!*query) return base; if (!(metatype = virJSONValueObjectGetString(base, "meta-type"))) return NULL; /* flatten arrays by default */ if (STREQ(metatype, "array")) { if (!(baseName = virJSONValueObjectGetString(base, "element-type"))) return NULL; continue; } else if (STREQ(metatype, "object")) { querystr = *query; modifier = **query; if (!c_isalpha(modifier)) querystr++; if (modifier == '+') baseName = virQEMUQAPISchemaObjectGetType("variants", querystr, "case", base); else baseName = virQEMUQAPISchemaObjectGetType("members", querystr, "name", base); if (!baseName) return NULL; } else if (STREQ(metatype, "command") || STREQ(metatype, "event")) { if (!(baseName = virJSONValueObjectGetString(base, *query))) return NULL; } else { /* alternates, basic types and enums can't be entered */ return NULL; } query++; } return base; } /** * 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 * * 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 * +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 0 on success (including if the requested schema was not found) and * fills @entry appropriately. On failure returns -1 and sets an appropriate * error message. */ int virQEMUQAPISchemaPathGet(const char *query, virHashTablePtr schema, virJSONValuePtr *entry) { char **elems = NULL; *entry = NULL; if (!(elems = virStringSplit(query, "/", 0))) return -1; if (!*elems) { virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("malformed query string")); virStringListFree(elems); return -1; } *entry = virQEMUQAPISchemaTraverse(*elems, elems + 1, schema); virStringListFree(elems); return 0; } bool virQEMUQAPISchemaPathExists(const char *query, virHashTablePtr schema) { virJSONValuePtr entry; if (virQEMUQAPISchemaPathGet(query, schema, &entry)) return false; return !!entry; } 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) { virHashTablePtr schema; virHashTablePtr ret = NULL; if (!(schema = virHashCreate(512, virJSONValueHashFree))) goto cleanup; if (virJSONValueArrayForeachSteal(schemareply, virQEMUQAPISchemaEntryProcess, schema) < 0) goto cleanup; VIR_STEAL_PTR(ret, schema); cleanup: virJSONValueFree(schemareply); virHashFree(schema); return ret; }