static int qemuAgentShutdownTestMonitorHandler(qemuMonitorTestPtr test, qemuMonitorTestItemPtr item, const char *cmdstr) { struct qemuAgentShutdownTestData *data; virJSONValuePtr val = NULL; virJSONValuePtr args; const char *cmdname; const char *mode; int ret = -1; data = qemuMonitorTestItemGetPrivateData(item); if (!(val = virJSONValueFromString(cmdstr))) return -1; if (!(cmdname = virJSONValueObjectGetString(val, "execute"))) { ret = qemuMonitorReportError(test, "Missing command name in %s", cmdstr); goto cleanup; } if (STRNEQ(cmdname, "guest-shutdown")) { ret = qemuMonitorTestAddInvalidCommandResponse(test, "guest-shutdown", cmdname); goto cleanup; } if (!(args = virJSONValueObjectGet(val, "arguments"))) { ret = qemuMonitorReportError(test, "Missing arguments section"); goto cleanup; } if (!(mode = virJSONValueObjectGetString(args, "mode"))) { ret = qemuMonitorReportError(test, "Missing shutdown mode"); goto cleanup; } if (STRNEQ(mode, data->mode)) { ret = qemuMonitorReportError(test, "expected shutdown mode '%s' got '%s'", data->mode, mode); goto cleanup; } /* now don't reply but return a qemu agent event */ qemuAgentNotifyEvent(qemuMonitorTestGetAgent(test), data->event); ret = 0; cleanup: virJSONValueFree(val); return ret; }
static int qemuAgentIOProcessEvent(qemuAgentPtr mon, virJSONValuePtr obj) { const char *type; VIR_DEBUG("mon=%p obj=%p", mon, obj); type = virJSONValueObjectGetString(obj, "event"); if (!type) { VIR_WARN("missing event type in message"); errno = EINVAL; return -1; } /* for (i = 0 ; i < ARRAY_CARDINALITY(eventHandlers) ; i++) { if (STREQ(eventHandlers[i].type, type)) { virJSONValuePtr data = virJSONValueObjectGet(obj, "data"); VIR_DEBUG("handle %s handler=%p data=%p", type, eventHandlers[i].handler, data); (eventHandlers[i].handler)(mon, data); break; } } */ return 0; }
static int testQEMUSchemaValidateBuiltin(virJSONValuePtr obj, virJSONValuePtr root, virBufferPtr debug) { const char *t = virJSONValueObjectGetString(root, "json-type"); const char *s = NULL; bool b = false; int ret = -1; if (STREQ_NULLABLE(t, "value")) { s = "{any}"; ret = 0; goto cleanup; } switch (virJSONValueGetType(obj)) { case VIR_JSON_TYPE_STRING: if (STRNEQ_NULLABLE(t, "string")) goto cleanup; s = virJSONValueGetString(obj); break; case VIR_JSON_TYPE_NUMBER: if (STRNEQ_NULLABLE(t, "int") && STRNEQ_NULLABLE(t, "number")) goto cleanup; s = "{number}"; break; case VIR_JSON_TYPE_BOOLEAN: if (STRNEQ_NULLABLE(t, "boolean")) goto cleanup; virJSONValueGetBoolean(obj, &b); if (b) s = "true"; else s = "false"; break; case VIR_JSON_TYPE_NULL: if (STRNEQ_NULLABLE(t, "null")) goto cleanup; break; case VIR_JSON_TYPE_OBJECT: case VIR_JSON_TYPE_ARRAY: goto cleanup; } ret = 0; cleanup: if (ret == 0) virBufferAsprintf(debug, "'%s': OK", s); else virBufferAsprintf(debug, "ERROR: expected type '%s', actual type %d", t, virJSONValueGetType(obj)); return ret; }
static int parallelsGetBridgedNetInfo(virNetworkDefPtr def, virJSONValuePtr jobj) { const char *ifname; char *bridgeLink = NULL; char *bridgePath = NULL; char *bridgeAddressPath = NULL; char *bridgeAddress = NULL; int len = 0; int ret = -1; if (!(ifname = virJSONValueObjectGetString(jobj, "Bound To"))) { parallelsParseError(); goto cleanup; } if (virAsprintf(&bridgeLink, SYSFS_NET_DIR "%s/brport/bridge", ifname) < 0) goto cleanup; if (virFileResolveLink(bridgeLink, &bridgePath) < 0) { virReportSystemError(errno, _("cannot read link '%s'"), bridgeLink); goto cleanup; } if (VIR_STRDUP(def->bridge, last_component(bridgePath)) < 0) goto cleanup; if (virAsprintf(&bridgeAddressPath, SYSFS_NET_DIR "%s/brport/bridge/address", ifname) < 0) goto cleanup; if ((len = virFileReadAll(bridgeAddressPath, 18, &bridgeAddress)) < 0) { virReportError(VIR_ERR_INTERNAL_ERROR, _("Error reading file '%s'"), bridgeAddressPath); goto cleanup; } if (len < VIR_MAC_STRING_BUFLEN) { virReportError(VIR_ERR_INTERNAL_ERROR, _("Error reading MAC from '%s'"), bridgeAddressPath); } bridgeAddress[VIR_MAC_STRING_BUFLEN - 1] = '\0'; if (virMacAddrParse(bridgeAddress, &def->mac) < 0) { virReportError(VIR_ERR_INTERNAL_ERROR, _("Can't parse MAC '%s'"), bridgeAddress); goto cleanup; } def->mac_specified = 1; ret = 0; cleanup: VIR_FREE(bridgeLink); VIR_FREE(bridgePath); VIR_FREE(bridgeAddress); VIR_FREE(bridgeAddressPath); return ret; }
/* Ignoring OOM in this method, since we're already reporting * a more important error * * XXX see qerror.h for different klasses & fill out useful params */ static const char * qemuAgentStringifyError(virJSONValuePtr error) { const char *klass = virJSONValueObjectGetString(error, "class"); const char *detail = NULL; /* The QMP 'desc' field is usually sufficient for our generic * error reporting needs. */ if (klass) detail = virJSONValueObjectGetString(error, "desc"); if (!detail) detail = qemuAgentStringifyErrorClass(klass); return detail; }
static const char * qemuAgentCommandName(virJSONValuePtr cmd) { const char *name = virJSONValueObjectGetString(cmd, "execute"); if (name) return name; else return "<unknown>"; }
static virLogHandlerLogFilePtr virLogHandlerLogFilePostExecRestart(virLogHandlerPtr handler, virJSONValuePtr object) { virLogHandlerLogFilePtr file; const char *path; if (VIR_ALLOC(file) < 0) return NULL; handler->inhibitor(true, handler->opaque); if ((path = virJSONValueObjectGetString(object, "path")) == NULL) { virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("Missing file path in JSON document")); goto error; } if ((file->file = virRotatingFileWriterNew(path, DEFAULT_FILE_SIZE, DEFAULT_MAX_BACKUP, false, DEFAULT_MODE)) == NULL) goto error; if (virJSONValueObjectGetNumberInt(object, "pipefd", &file->pipefd) < 0) { virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("Missing file pipefd in JSON document")); goto error; } if (virSetInherit(file->pipefd, false) < 0) { virReportSystemError(errno, "%s", _("Cannot enable close-on-exec flag")); goto error; } return file; error: handler->inhibitor(false, handler->opaque); virLogHandlerLogFileFree(file); return NULL; }
static virJSONValuePtr testQEMUSchemaStealObjectMemberByName(const char *name, virJSONValuePtr members) { virJSONValuePtr member; virJSONValuePtr ret = NULL; size_t i; for (i = 0; i < virJSONValueArraySize(members); i++) { member = virJSONValueArrayGet(members, i); if (STREQ_NULLABLE(name, virJSONValueObjectGetString(member, "name"))) { ret = virJSONValueArraySteal(members, i); break; } } return ret; }
/* * Processes a single line, looking for a matching expected * item to reply with, else replies with an error */ static int qemuMonitorTestProcessCommandJSON(qemuMonitorTestPtr test, const char *cmdstr) { virJSONValuePtr val; const char *cmdname; int ret = -1; if (!(val = virJSONValueFromString(cmdstr))) return -1; if (!(cmdname = virJSONValueObjectGetString(val, "execute"))) { virReportError(VIR_ERR_INTERNAL_ERROR, "Missing command name in %s", cmdstr); goto cleanup; } if (test->nitems == 0 || STRNEQ(test->items[0]->command_name, cmdname)) { ret = qemuMonitorTestAddReponse(test, "{ \"error\": " " { \"desc\": \"Unexpected command\", " " \"class\": \"UnexpectedCommand\" } }"); } else { ret = qemuMonitorTestAddReponse(test, test->items[0]->response); qemuMonitorTestItemFree(test->items[0]); if (test->nitems == 1) { VIR_FREE(test->items); test->nitems = 0; } else { memmove(test->items, test->items + 1, sizeof(test->items[0]) * (test->nitems - 1)); VIR_SHRINK_N(test->items, test->nitems, 1); } } cleanup: virJSONValueFree(val); return ret; }
static int testQEMUSchemaValidateObjectMember(const char *key, virJSONValuePtr value, void *opaque) { struct testQEMUSchemaValidateObjectMemberData *data = opaque; virJSONValuePtr keymember = NULL; const char *keytype; virJSONValuePtr keyschema = NULL; int ret = -1; virBufferStrcat(data->debug, key, ": ", NULL); /* lookup 'member' entry for key */ if (!(keymember = testQEMUSchemaStealObjectMemberByName(key, data->rootmembers))) { virBufferAddLit(data->debug, "ERROR: attribute not in schema"); goto cleanup; } /* lookup schema entry for keytype */ if (!(keytype = virJSONValueObjectGetString(keymember, "type")) || !(keyschema = virHashLookup(data->schema, keytype))) { virBufferAsprintf(data->debug, "ERROR: can't find schema for type '%s'", NULLSTR(keytype)); ret = -2; goto cleanup; } /* recurse */ ret = testQEMUSchemaValidateRecurse(value, keyschema, data->schema, data->debug); cleanup: virBufferAddLit(data->debug, "\n"); virJSONValueFree(keymember); return ret; }
virNetServerPtr virNetServerNewPostExecRestart(virJSONValuePtr object, virNetServerClientPrivNew clientPrivNew, virNetServerClientPrivNewPostExecRestart clientPrivNewPostExecRestart, virNetServerClientPrivPreExecRestart clientPrivPreExecRestart, virFreeCallback clientPrivFree, void *clientPrivOpaque) { virNetServerPtr srv = NULL; virJSONValuePtr clients; virJSONValuePtr services; size_t i; int n; unsigned int min_workers; unsigned int max_workers; unsigned int priority_workers; unsigned int max_clients; unsigned int max_anonymous_clients; unsigned int keepaliveInterval; unsigned int keepaliveCount; bool keepaliveRequired; const char *mdnsGroupName = NULL; if (virJSONValueObjectGetNumberUint(object, "min_workers", &min_workers) < 0) { virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("Missing min_workers data in JSON document")); goto error; } if (virJSONValueObjectGetNumberUint(object, "max_workers", &max_workers) < 0) { virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("Missing max_workers data in JSON document")); goto error; } if (virJSONValueObjectGetNumberUint(object, "priority_workers", &priority_workers) < 0) { virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("Missing priority_workers data in JSON document")); goto error; } if (virJSONValueObjectGetNumberUint(object, "max_clients", &max_clients) < 0) { virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("Missing max_clients data in JSON document")); goto error; } if (virJSONValueObjectHasKey(object, "max_anonymous_clients")) { if (virJSONValueObjectGetNumberUint(object, "max_anonymous_clients", &max_anonymous_clients) < 0) { virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("Malformed max_anonymous_clients data in JSON document")); goto error; } } else { max_anonymous_clients = max_clients; } if (virJSONValueObjectGetNumberUint(object, "keepaliveInterval", &keepaliveInterval) < 0) { virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("Missing keepaliveInterval data in JSON document")); goto error; } if (virJSONValueObjectGetNumberUint(object, "keepaliveCount", &keepaliveCount) < 0) { virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("Missing keepaliveCount data in JSON document")); goto error; } if (virJSONValueObjectGetBoolean(object, "keepaliveRequired", &keepaliveRequired) < 0) { virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("Missing keepaliveRequired data in JSON document")); goto error; } if (virJSONValueObjectHasKey(object, "mdnsGroupName") && (!(mdnsGroupName = virJSONValueObjectGetString(object, "mdnsGroupName")))) { virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("Malformed mdnsGroupName data in JSON document")); goto error; } if (!(srv = virNetServerNew(min_workers, max_clients, priority_workers, max_clients, max_anonymous_clients, keepaliveInterval, keepaliveCount, keepaliveRequired, mdnsGroupName, clientPrivNew, clientPrivPreExecRestart, clientPrivFree, clientPrivOpaque))) goto error; if (!(services = virJSONValueObjectGet(object, "services"))) { virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("Missing services data in JSON document")); goto error; } n = virJSONValueArraySize(services); if (n < 0) { virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("Malformed services data in JSON document")); goto error; } for (i = 0; i < n; i++) { virNetServerServicePtr service; virJSONValuePtr child = virJSONValueArrayGet(services, i); if (!child) { virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("Missing service data in JSON document")); goto error; } if (!(service = virNetServerServiceNewPostExecRestart(child))) goto error; /* XXX mdns entry names ? */ if (virNetServerAddService(srv, service, NULL) < 0) { virObjectUnref(service); goto error; } } if (!(clients = virJSONValueObjectGet(object, "clients"))) { virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("Missing clients data in JSON document")); goto error; } n = virJSONValueArraySize(clients); if (n < 0) { virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("Malformed clients data in JSON document")); goto error; } for (i = 0; i < n; i++) { virNetServerClientPtr client; virJSONValuePtr child = virJSONValueArrayGet(clients, i); if (!child) { virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("Missing client data in JSON document")); goto error; } if (!(client = virNetServerClientNewPostExecRestart(child, clientPrivNewPostExecRestart, clientPrivPreExecRestart, clientPrivFree, clientPrivOpaque))) goto error; if (virNetServerAddClient(srv, client) < 0) { virObjectUnref(client); goto error; } virObjectUnref(client); } return srv; error: virObjectUnref(srv); return NULL; }
static int testJSONLookup(const void *data) { const struct testInfo *info = data; virJSONValuePtr json; virJSONValuePtr value = NULL; char *result = NULL; int rc; int number; const char *str; int ret = -1; json = virJSONValueFromString(info->doc); if (!json) { VIR_TEST_VERBOSE("Fail to parse %s\n", info->doc); ret = -1; goto cleanup; } value = virJSONValueObjectGetObject(json, "a"); if (value) { if (!info->pass) { VIR_TEST_VERBOSE("lookup for 'a' in '%s' should have failed\n", info->doc); goto cleanup; } else { result = virJSONValueToString(value, false); if (STRNEQ_NULLABLE(result, "{}")) { VIR_TEST_VERBOSE("lookup for 'a' in '%s' found '%s' but " "should have found '{}'\n", info->doc, NULLSTR(result)); goto cleanup; } VIR_FREE(result); } } else if (info->pass) { VIR_TEST_VERBOSE("lookup for 'a' in '%s' should have succeeded\n", info->doc); goto cleanup; } number = 2; rc = virJSONValueObjectGetNumberInt(json, "b", &number); if (rc == 0) { if (!info->pass) { VIR_TEST_VERBOSE("lookup for 'b' in '%s' should have failed\n", info->doc); goto cleanup; } else if (number != 1) { VIR_TEST_VERBOSE("lookup for 'b' in '%s' found %d but " "should have found 1\n", info->doc, number); goto cleanup; } } else if (info->pass) { VIR_TEST_VERBOSE("lookup for 'b' in '%s' should have succeeded\n", info->doc); goto cleanup; } str = virJSONValueObjectGetString(json, "c"); if (str) { if (!info->pass) { VIR_TEST_VERBOSE("lookup for 'c' in '%s' should have failed\n", info->doc); goto cleanup; } else if (STRNEQ(str, "str")) { VIR_TEST_VERBOSE("lookup for 'c' in '%s' found '%s' but " "should have found 'str'\n", info->doc, str); goto cleanup; } } else if (info->pass) { VIR_TEST_VERBOSE("lookup for 'c' in '%s' should have succeeded\n", info->doc); goto cleanup; } value = virJSONValueObjectGetArray(json, "d"); if (value) { if (!info->pass) { VIR_TEST_VERBOSE("lookup for 'd' in '%s' should have failed\n", info->doc); goto cleanup; } else { result = virJSONValueToString(value, false); if (STRNEQ_NULLABLE(result, "[]")) { VIR_TEST_VERBOSE("lookup for 'd' in '%s' found '%s' but " "should have found '[]'\n", info->doc, NULLSTR(result)); goto cleanup; } VIR_FREE(result); } } else if (info->pass) { VIR_TEST_VERBOSE("lookup for 'd' in '%s' should have succeeded\n", info->doc); goto cleanup; } ret = 0; cleanup: virJSONValueFree(json); VIR_FREE(result); return ret; }
static virNetworkObjPtr parallelsLoadNetwork(parallelsConnPtr privconn, virJSONValuePtr jobj) { virNetworkObjPtr net; virNetworkDefPtr def; const char *tmp; /* MD5_DIGEST_SIZE = VIR_UUID_BUFLEN = 16 */ unsigned char md5[MD5_DIGEST_SIZE]; if (VIR_ALLOC(def) < 0) goto no_memory; if (!(tmp = virJSONValueObjectGetString(jobj, "Network ID"))) { parallelsParseError(); goto cleanup; } if (!(def->name = strdup(tmp))) goto no_memory; /* Network names are unique in Parallels Cloud Server, so we can make * an UUID from it */ md5_buffer(tmp, strlen(tmp), md5); memcpy(def->uuid, md5, VIR_UUID_BUFLEN); def->uuid_specified = 1; if (!(tmp = virJSONValueObjectGetString(jobj, "Type"))) { parallelsParseError(); goto cleanup; } if (STREQ(tmp, "bridged")) { def->forward.type = VIR_NETWORK_FORWARD_BRIDGE; if (parallelsGetBridgedNetInfo(def, jobj) < 0) goto cleanup; } else if (STREQ(tmp, "host-only")) { def->forward.type = VIR_NETWORK_FORWARD_NONE; if (parallelsGetHostOnlyNetInfo(def, def->name) < 0) goto cleanup; } else { parallelsParseError(); goto cleanup; } if (!(net = virNetworkAssignDef(&privconn->networks, def, false))) { virNetworkDefFree(def); goto cleanup; } net->active = 1; net->persistent = 1; net->autostart = 1; virNetworkObjUnlock(net); return net; no_memory: virReportOOMError(); cleanup: virNetworkDefFree(def); return NULL; }
static int parallelsGetHostOnlyNetInfo(virNetworkDefPtr def, const char *name) { const char *tmp; virJSONValuePtr jobj = NULL, jobj2; int ret = -1; if (VIR_EXPAND_N(def->ips, def->nips, 1) < 0) { virReportOOMError(); goto cleanup; } jobj = parallelsParseOutput("prlsrvctl", "net", "info", "-j", name, NULL); if (!jobj) { parallelsParseError(); goto cleanup; } if (!(jobj2 = virJSONValueObjectGet(jobj, "Parallels adapter"))) { parallelsParseError(); goto cleanup; } if (!(def->ips[0].family = strdup("ipv4"))) { virReportOOMError(); goto cleanup; }; if (!(tmp = virJSONValueObjectGetString(jobj2, "IP address"))) { parallelsParseError(); goto cleanup; } if (virSocketAddrParseIPv4(&def->ips[0].address, tmp) < 0) { parallelsParseError(); goto cleanup; } if (!(tmp = virJSONValueObjectGetString(jobj2, "Subnet mask"))) { parallelsParseError(); goto cleanup; } if (virSocketAddrParseIPv4(&def->ips[0].netmask, tmp) < 0) { parallelsParseError(); goto cleanup; } if (!(jobj2 = virJSONValueObjectGet(jobj, "DHCPv4 server"))) { parallelsParseError(); goto cleanup; } if (VIR_EXPAND_N(def->ips[0].ranges, def->ips[0].nranges, 1) < 0) { virReportOOMError(); goto cleanup; } if (!(tmp = virJSONValueObjectGetString(jobj2, "IP scope start address"))) { parallelsParseError(); goto cleanup; } if (virSocketAddrParseIPv4(&def->ips[0].ranges[0].start, tmp) < 0) { parallelsParseError(); goto cleanup; } if (!(tmp = virJSONValueObjectGetString(jobj2, "IP scope end address"))) { parallelsParseError(); goto cleanup; } if (virSocketAddrParseIPv4(&def->ips[0].ranges[0].end, tmp) < 0) { parallelsParseError(); goto cleanup; } ret = 0; cleanup: virJSONValueFree(jobj); return ret; }
static int parallelsLoadNetwork(parallelsConnPtr privconn, virJSONValuePtr jobj) { int ret = -1; virNetworkObjPtr net = NULL; virNetworkDefPtr def; const char *tmp; /* MD5_DIGEST_SIZE = VIR_UUID_BUFLEN = 16 */ unsigned char md5[MD5_DIGEST_SIZE]; if (VIR_ALLOC(def) < 0) goto cleanup; if (!(tmp = virJSONValueObjectGetString(jobj, "Network ID"))) { parallelsParseError(); goto cleanup; } if (VIR_STRDUP(def->name, tmp) < 0) goto cleanup; /* Network names are unique in Parallels Cloud Server, so we can make * a UUID from it */ md5_buffer(tmp, strlen(tmp), md5); memcpy(def->uuid, md5, VIR_UUID_BUFLEN); def->uuid_specified = 1; if (!(tmp = virJSONValueObjectGetString(jobj, "Type"))) { parallelsParseError(); goto cleanup; } if (STREQ(tmp, PARALLELS_BRIDGED_NETWORK_TYPE)) { def->forward.type = VIR_NETWORK_FORWARD_BRIDGE; if (parallelsGetBridgedNetInfo(def, jobj) < 0) { /* Only mandatory networks are required to be configured completely */ if (STRNEQ(def->name, PARALLELS_REQUIRED_BRIDGED_NETWORK)) ret = 0; goto cleanup; } } else if (STREQ(tmp, PARALLELS_HOSTONLY_NETWORK_TYPE)) { def->forward.type = VIR_NETWORK_FORWARD_NONE; if (parallelsGetHostOnlyNetInfo(def, def->name) < 0) { /* Only mandatory networks are required to be configured completely */ if (STRNEQ(def->name, PARALLELS_REQUIRED_HOSTONLY_NETWORK)) ret = 0; goto cleanup; } } else { parallelsParseError(); goto cleanup; } if (!(net = virNetworkAssignDef(privconn->networks, def, 0))) goto cleanup; def = NULL; net->active = 1; net->autostart = 1; ret = 0; cleanup: virNetworkObjEndAPI(&net); virNetworkDefFree(def); return ret; }
/** * testQEMUSchemaValidateObjectMergeVariant: * * Merges schema of variant @variantname in @root into @root and removes the * 'variants' array from @root. */ static int testQEMUSchemaValidateObjectMergeVariant(virJSONValuePtr root, const char *variantfield, const char *variantname, virHashTablePtr schema, virBufferPtr debug) { size_t i; virJSONValuePtr variants = NULL; virJSONValuePtr variant; virJSONValuePtr variantschema; virJSONValuePtr variantschemamembers; virJSONValuePtr rootmembers; const char *varianttype = NULL; int ret = -1; if (!(variants = virJSONValueObjectStealArray(root, "variants"))) { virBufferAddLit(debug, "ERROR: missing 'variants' in schema\n"); return -2; } for (i = 0; i < virJSONValueArraySize(variants); i++) { variant = virJSONValueArrayGet(variants, i); if (STREQ_NULLABLE(variantname, virJSONValueObjectGetString(variant, "case"))) { varianttype = virJSONValueObjectGetString(variant, "type"); break; } } if (!varianttype) { virBufferAsprintf(debug, "ERROR: variant '%s' for discriminator '%s' not found\n", variantname, variantfield); goto cleanup; } if (!(variantschema = virHashLookup(schema, varianttype)) || !(variantschemamembers = virJSONValueObjectGetArray(variantschema, "members"))) { virBufferAsprintf(debug, "ERROR: missing schema or schema members for variant '%s'(%s)\n", variantname, varianttype); ret = -2; goto cleanup; } rootmembers = virJSONValueObjectGetArray(root, "members"); if (virJSONValueArrayForeachSteal(variantschemamembers, testQEMUSchemaValidateObjectMergeVariantMember, rootmembers) < 0) { ret = -2; goto cleanup; } ret = 0; cleanup: virJSONValueFree(variants); return ret; }