mirror of
https://gitlab.com/libvirt/libvirt.git
synced 2024-11-02 11:21:12 +00:00
690 lines
18 KiB
C
690 lines
18 KiB
C
/*
|
|
* xmlrpc.c: XML-RPC protocol handler for libvir library
|
|
*
|
|
* Copyright (C) 2006 IBM, Corp.
|
|
*
|
|
* See COPYING.LIB for the License of this software
|
|
*
|
|
* Anthony Liguori <aliguori@us.ibm.com>
|
|
*/
|
|
|
|
#include <config.h>
|
|
|
|
#include "xmlrpc.h"
|
|
#include "internal.h"
|
|
#include "memory.h"
|
|
|
|
#include <libxml/nanohttp.h>
|
|
|
|
#include <string.h>
|
|
#include <errno.h>
|
|
|
|
/* TODO
|
|
1) Lots of error checking
|
|
2) xmlRpcValueToSexpr
|
|
*/
|
|
|
|
static xmlNodePtr xmlFirstElement(xmlNodePtr node);
|
|
static xmlNodePtr xmlNextElement(xmlNodePtr node);
|
|
|
|
struct _xmlRpcContext
|
|
{
|
|
char *uri;
|
|
int faultCode;
|
|
char *faultMessage;
|
|
};
|
|
|
|
static void xmlRpcError(virErrorNumber error, const char *info, int value)
|
|
{
|
|
const char *errmsg;
|
|
|
|
if (error == VIR_ERR_OK)
|
|
return;
|
|
|
|
errmsg = __virErrorMsg(error, info);
|
|
__virRaiseError(NULL, NULL, NULL, VIR_FROM_RPC, error, VIR_ERR_ERROR,
|
|
errmsg, info, NULL, value, 0, errmsg, info, value);
|
|
}
|
|
|
|
static xmlRpcValuePtr xmlRpcValueNew(xmlRpcValueType type)
|
|
{
|
|
xmlRpcValuePtr ret = NULL;
|
|
if (VIR_ALLOC(ret) < 0)
|
|
xmlRpcError(VIR_ERR_NO_MEMORY, _("allocate value"), sizeof(*ret));
|
|
else
|
|
ret->kind = type;
|
|
return ret;
|
|
}
|
|
|
|
static char *xmlGetText(xmlNodePtr node)
|
|
{
|
|
for (node = node->children; node; node = node->next)
|
|
if (node->type == XML_TEXT_NODE) {
|
|
char *x = strdup((const char *)node->content);
|
|
if (!x)
|
|
xmlRpcError(VIR_ERR_NO_MEMORY, _("copying node content"),
|
|
strlen((const char *)node->content));
|
|
return x;
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
static xmlNodePtr xmlFirstElement(xmlNodePtr node)
|
|
{
|
|
for (node = node->children; node; node = node->next)
|
|
if (node->type == XML_ELEMENT_NODE)
|
|
break;
|
|
return node;
|
|
}
|
|
|
|
static xmlNodePtr xmlNextElement(xmlNodePtr node)
|
|
{
|
|
for (node = node->next; node; node = node->next)
|
|
if (node->type == XML_ELEMENT_NODE)
|
|
break;
|
|
return node;
|
|
}
|
|
|
|
static xmlRpcValuePtr xmlRpcValueUnmarshalDateTime(xmlNodePtr node ATTRIBUTE_UNUSED)
|
|
{
|
|
/* we don't need this */
|
|
TODO
|
|
return NULL;
|
|
}
|
|
|
|
static xmlRpcValuePtr xmlRpcValueUnmarshalString(xmlNodePtr node)
|
|
{
|
|
xmlRpcValuePtr ret = xmlRpcValueNew(XML_RPC_STRING);
|
|
|
|
if (ret)
|
|
ret->value.string = xmlGetText(node);
|
|
return ret;
|
|
}
|
|
|
|
static xmlRpcValuePtr xmlRpcValueUnmarshalBase64(xmlNodePtr node ATTRIBUTE_UNUSED)
|
|
{
|
|
/* we don't need this */
|
|
TODO
|
|
return NULL;
|
|
}
|
|
|
|
static xmlRpcValuePtr xmlRpcValueUnmarshalInteger(xmlNodePtr node)
|
|
{
|
|
xmlRpcValuePtr ret = xmlRpcValueNew(XML_RPC_INTEGER);
|
|
char *value = xmlGetText(node);
|
|
|
|
if (ret && value)
|
|
ret->value.integer = atoi(value);
|
|
VIR_FREE(value);
|
|
return ret;
|
|
}
|
|
|
|
static xmlRpcValuePtr xmlRpcValueUnmarshalBoolean(xmlNodePtr node)
|
|
{
|
|
xmlRpcValuePtr ret = xmlRpcValueNew(XML_RPC_BOOLEAN);
|
|
char *value = xmlGetText(node);
|
|
|
|
if (!ret)
|
|
return NULL;
|
|
if (value && atoi(value))
|
|
ret->value.boolean = true;
|
|
else
|
|
ret->value.boolean = false;
|
|
VIR_FREE(value);
|
|
return ret;
|
|
}
|
|
|
|
static xmlRpcValuePtr xmlRpcValueUnmarshalDouble(xmlNodePtr node)
|
|
{
|
|
xmlRpcValuePtr ret = xmlRpcValueNew(XML_RPC_DOUBLE);
|
|
char *value = xmlGetText(node);
|
|
|
|
if (ret && value)
|
|
ret->value.real = atof(value);
|
|
VIR_FREE(value);
|
|
return ret;
|
|
}
|
|
|
|
static xmlRpcValuePtr xmlRpcValueUnmarshalArray(xmlNodePtr node)
|
|
{
|
|
xmlRpcValuePtr ret = xmlRpcValueNew(XML_RPC_ARRAY);
|
|
xmlNodePtr cur;
|
|
int n_elements = 0;
|
|
xmlRpcValuePtr *elems;
|
|
|
|
if (!ret)
|
|
return NULL;
|
|
|
|
for (cur = xmlFirstElement(node); cur; cur = xmlNextElement(cur))
|
|
n_elements += 1;
|
|
|
|
if (VIR_ALLOC_N(elems, n_elements) < 0) {
|
|
xmlRpcError(VIR_ERR_NO_MEMORY, _("allocate value array"),
|
|
n_elements * sizeof(*elems));
|
|
VIR_FREE(ret);
|
|
return NULL;
|
|
}
|
|
n_elements = 0;
|
|
for (cur = xmlFirstElement(node); cur; cur = xmlNextElement(cur)) {
|
|
elems[n_elements] = xmlRpcValueUnmarshal(cur);
|
|
n_elements += 1;
|
|
}
|
|
|
|
ret->value.array.elements = elems;
|
|
ret->value.array.n_elements = n_elements;
|
|
|
|
return ret;
|
|
}
|
|
|
|
static xmlRpcValueDictElementPtr xmlRpcValueUnmarshalDictElement(xmlNodePtr node)
|
|
{
|
|
xmlRpcValueDictElementPtr ret;
|
|
xmlNodePtr cur;
|
|
|
|
if (VIR_ALLOC(ret) < 0) {
|
|
xmlRpcError(VIR_ERR_NO_MEMORY, _("allocate dict"), sizeof(*ret));
|
|
return NULL;
|
|
}
|
|
memset(ret, 0, sizeof(*ret));
|
|
|
|
for (cur = xmlFirstElement(node); cur; cur = xmlNextElement(cur)) {
|
|
if (xmlStrEqual(cur->name, BAD_CAST "name")) {
|
|
ret->name = xmlGetText(cur);
|
|
} else if (xmlStrEqual(cur->name, BAD_CAST "value")) {
|
|
ret->value = xmlRpcValueUnmarshal(cur);
|
|
} else {
|
|
xmlRpcError(VIR_ERR_XML_ERROR, _("unexpected dict node"), 0);
|
|
VIR_FREE(ret->name);
|
|
if (ret->value)
|
|
xmlRpcValueFree(ret->value);
|
|
VIR_FREE(ret);
|
|
return NULL;
|
|
}
|
|
}
|
|
|
|
ret->next = NULL;
|
|
|
|
return ret;
|
|
}
|
|
|
|
static xmlRpcValuePtr xmlRpcValueUnmarshalDict(xmlNodePtr node)
|
|
{
|
|
xmlRpcValueDictElementPtr root = NULL, *elem = &root;
|
|
xmlRpcValuePtr ret = xmlRpcValueNew(XML_RPC_STRUCT);
|
|
xmlNodePtr cur;
|
|
|
|
if (!ret)
|
|
return NULL;
|
|
|
|
ret->value.dict.root = root;
|
|
|
|
for (cur = xmlFirstElement(node); cur; cur = xmlNextElement(cur)) {
|
|
*elem = xmlRpcValueUnmarshalDictElement(cur);
|
|
if (*elem==NULL) {
|
|
xmlRpcValueFree(ret);
|
|
return NULL;
|
|
}
|
|
elem = &(*elem)->next;
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
xmlRpcValuePtr xmlRpcValueUnmarshal(xmlNodePtr node)
|
|
{
|
|
xmlNodePtr n;
|
|
xmlRpcValuePtr ret = NULL;
|
|
|
|
if (xmlStrEqual(node->name, BAD_CAST "value")) {
|
|
n = xmlFirstElement(node);
|
|
if (n == NULL) {
|
|
ret = xmlRpcValueUnmarshalString(node);
|
|
} else {
|
|
ret = xmlRpcValueUnmarshal(n);
|
|
}
|
|
} else if (xmlStrEqual(node->name, BAD_CAST "dateTime.iso8601")) {
|
|
ret = xmlRpcValueUnmarshalDateTime(node);
|
|
} else if (xmlStrEqual(node->name, BAD_CAST "string")) {
|
|
ret = xmlRpcValueUnmarshalString(node);
|
|
} else if (xmlStrEqual(node->name, BAD_CAST "base64")) {
|
|
ret = xmlRpcValueUnmarshalBase64(node);
|
|
} else if (xmlStrEqual(node->name, BAD_CAST "i4") ||
|
|
xmlStrEqual(node->name, BAD_CAST "int")) {
|
|
ret = xmlRpcValueUnmarshalInteger(node);
|
|
} else if (xmlStrEqual(node->name, BAD_CAST "boolean")) {
|
|
ret = xmlRpcValueUnmarshalBoolean(node);
|
|
} else if (xmlStrEqual(node->name, BAD_CAST "double")) {
|
|
ret = xmlRpcValueUnmarshalDouble(node);
|
|
} else if (xmlStrEqual(node->name, BAD_CAST "array")) {
|
|
ret = xmlRpcValueUnmarshal(xmlFirstElement(node));
|
|
} else if (xmlStrEqual(node->name, BAD_CAST "data")) {
|
|
ret = xmlRpcValueUnmarshalArray(node);
|
|
} else if (xmlStrEqual(node->name, BAD_CAST "struct")) {
|
|
ret = xmlRpcValueUnmarshalDict(node);
|
|
} else if (xmlStrEqual(node->name, BAD_CAST "nil")) {
|
|
ret = xmlRpcValueNew(XML_RPC_NIL);
|
|
} else {
|
|
xmlRpcError(VIR_ERR_XML_ERROR, _("unexpected value node"), 0);
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
void xmlRpcValueFree(xmlRpcValuePtr value)
|
|
{
|
|
int i;
|
|
xmlRpcValueDictElementPtr cur, next;
|
|
|
|
if (value == NULL)
|
|
return;
|
|
|
|
switch (value->kind) {
|
|
case XML_RPC_ARRAY:
|
|
for (i = 0; i < value->value.array.n_elements; i++)
|
|
xmlRpcValueFree(value->value.array.elements[i]);
|
|
VIR_FREE(value->value.array.elements);
|
|
break;
|
|
case XML_RPC_STRUCT:
|
|
next = value->value.dict.root;
|
|
while (next) {
|
|
cur = next;
|
|
next = next->next;
|
|
VIR_FREE(cur->name);
|
|
xmlRpcValueFree(cur->value);
|
|
VIR_FREE(cur);
|
|
}
|
|
break;
|
|
case XML_RPC_STRING:
|
|
VIR_FREE(value->value.string);
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
|
|
VIR_FREE(value);
|
|
}
|
|
|
|
void xmlRpcValueMarshal(xmlRpcValuePtr value, virBufferPtr buf, int indent)
|
|
{
|
|
int i;
|
|
xmlRpcValueDictElement *elem;
|
|
|
|
virBufferVSprintf(buf, "%*s<value>", indent, "");
|
|
switch (value->kind) {
|
|
case XML_RPC_ARRAY:
|
|
virBufferStrcat(buf, "<array><data>\n", NULL);
|
|
for (i = 0; i < value->value.array.n_elements; i++)
|
|
xmlRpcValueMarshal(value->value.array.elements[i], buf, indent+2);
|
|
virBufferVSprintf(buf, "%*s</data></array>", indent, "");
|
|
break;
|
|
case XML_RPC_STRUCT:
|
|
virBufferStrcat(buf, "<struct>\n", NULL);
|
|
indent += 2;
|
|
for (elem = value->value.dict.root; elem; elem = elem->next) {
|
|
virBufferVSprintf(buf, "%*s<member>\n", indent, "");
|
|
virBufferVSprintf(buf, "%*s<name>%s</name>\n",
|
|
indent + 2, "", elem->name);
|
|
xmlRpcValueMarshal(elem->value, buf, indent + 2);
|
|
virBufferVSprintf(buf, "%*s</member>\n", indent, "");
|
|
}
|
|
indent -= 2;
|
|
virBufferVSprintf(buf, "%*s</struct>", indent, "");
|
|
break;
|
|
case XML_RPC_INTEGER:
|
|
virBufferVSprintf(buf, "<int>%d</int>", value->value.integer);
|
|
break;
|
|
case XML_RPC_DOUBLE:
|
|
virBufferVSprintf(buf, "<double>%f</double>", value->value.real);
|
|
break;
|
|
case XML_RPC_BOOLEAN:
|
|
if (value->value.boolean)
|
|
i = 1;
|
|
else
|
|
i = 0;
|
|
virBufferVSprintf(buf, "<boolean>%d</boolean>", i);
|
|
break;
|
|
case XML_RPC_DATE_TIME:
|
|
/* FIXME */
|
|
TODO
|
|
break;
|
|
case XML_RPC_BASE64:
|
|
/* FIXME */
|
|
TODO
|
|
break;
|
|
case XML_RPC_STRING:
|
|
virBufferStrcat(buf,
|
|
"<string>", value->value.string, "</string>", NULL);
|
|
break;
|
|
case XML_RPC_NIL:
|
|
virBufferStrcat(buf, "<nil> </nil>", NULL);
|
|
break;
|
|
}
|
|
virBufferStrcat(buf, "</value>\n", NULL);
|
|
}
|
|
|
|
void xmlRpcMarshalRequest(const char *request,
|
|
virBufferPtr buf,
|
|
int argc, xmlRpcValuePtr *argv)
|
|
{
|
|
int i;
|
|
|
|
virBufferStrcat(buf,
|
|
"<?xml version=\"1.0\"?>\n"
|
|
"<methodCall>\n"
|
|
" <methodName>", request, "</methodName>\n"
|
|
" <params>\n", NULL);
|
|
for (i = 0; i < argc; i++) {
|
|
virBufferStrcat(buf,
|
|
" <param>\n", NULL);
|
|
xmlRpcValueMarshal(argv[i], buf, 6);
|
|
virBufferStrcat(buf,
|
|
" </param>\n", NULL);
|
|
}
|
|
virBufferStrcat(buf,
|
|
" </params>\n"
|
|
"</methodCall>\n", NULL);
|
|
}
|
|
|
|
xmlRpcValuePtr xmlRpcUnmarshalResponse(xmlNodePtr node, bool *is_fault)
|
|
{
|
|
if (!node)
|
|
return NULL;
|
|
|
|
if (!xmlStrEqual(node->name, BAD_CAST "methodResponse"))
|
|
return NULL;
|
|
|
|
node = xmlFirstElement(node);
|
|
if (xmlStrEqual(node->name, BAD_CAST "params")) {
|
|
node = xmlFirstElement(node);
|
|
|
|
if (!xmlStrEqual(node->name, BAD_CAST "param"))
|
|
return NULL;
|
|
|
|
*is_fault = false;
|
|
return xmlRpcValueUnmarshal(xmlFirstElement(node));
|
|
} else if (xmlStrEqual(node->name, BAD_CAST "fault")) {
|
|
*is_fault = true;
|
|
return xmlRpcValueUnmarshal(xmlFirstElement(node));
|
|
} else
|
|
return NULL;
|
|
}
|
|
|
|
static char *xmlRpcCallRaw(const char *url, const char *request)
|
|
{
|
|
void *cxt;
|
|
char *contentType = (char *) "text/xml";
|
|
int len, ret, serrno;
|
|
char *response = NULL;
|
|
|
|
cxt = xmlNanoHTTPMethod(url,
|
|
"POST",
|
|
request,
|
|
&contentType,
|
|
NULL,
|
|
strlen(request));
|
|
|
|
if (cxt == NULL) {
|
|
xmlRpcError(VIR_ERR_POST_FAILED, _("send request"), 0);
|
|
goto error;
|
|
}
|
|
|
|
if (contentType && STRNEQ(contentType, "text/xml")) {
|
|
errno = EINVAL;
|
|
xmlRpcError(VIR_ERR_POST_FAILED, _("unexpected mime type"), 0);
|
|
goto error;
|
|
}
|
|
|
|
len = xmlNanoHTTPContentLength(cxt);
|
|
if (VIR_ALLOC_N(response, len + 1) < 0) {
|
|
xmlRpcError(VIR_ERR_NO_MEMORY, _("allocate response"), len);
|
|
goto error;
|
|
}
|
|
ret = xmlNanoHTTPRead(cxt, response, len);
|
|
if (ret != len) {
|
|
errno = EINVAL;
|
|
VIR_FREE(response);
|
|
response = NULL;
|
|
xmlRpcError(VIR_ERR_POST_FAILED, _("read response"), 0);
|
|
}
|
|
|
|
response[len] = 0;
|
|
|
|
error:
|
|
serrno = errno;
|
|
if (cxt) {
|
|
xmlNanoHTTPClose(cxt);
|
|
VIR_FREE(contentType);
|
|
}
|
|
errno = serrno;
|
|
|
|
return response;
|
|
}
|
|
|
|
static char **xmlRpcStringArray(xmlRpcValuePtr value)
|
|
{
|
|
char **ret, *ptr;
|
|
int i;
|
|
size_t size = 0;
|
|
|
|
if (value->kind != XML_RPC_ARRAY)
|
|
return NULL;
|
|
|
|
size = sizeof(char *) * (value->value.array.n_elements + 1);
|
|
|
|
for (i = 0; i < value->value.array.n_elements; i++)
|
|
if (value->value.array.elements[i]->kind == XML_RPC_STRING)
|
|
size += strlen(value->value.array.elements[i]->value.string) + 1;
|
|
|
|
if (VIR_ALLOC_N(ptr, size) < 0) {
|
|
xmlRpcError(VIR_ERR_NO_MEMORY, _("allocate string array"), size);
|
|
return NULL;
|
|
}
|
|
ret = (char **)ptr;
|
|
ptr += sizeof(char *) * (value->value.array.n_elements + 1);
|
|
|
|
for (i = 0; i < value->value.array.n_elements; i++) {
|
|
if (value->value.array.elements[i]->kind == XML_RPC_STRING) {
|
|
char *s = value->value.array.elements[i]->value.string;
|
|
strcpy(ptr, s);
|
|
ret[i] = ptr;
|
|
ptr += strlen(s) + 1;
|
|
} else
|
|
ret[i] = (char *) "";
|
|
}
|
|
|
|
ret[i] = NULL;
|
|
|
|
return ret;
|
|
}
|
|
|
|
xmlRpcValuePtr *
|
|
xmlRpcArgvNew(const char *fmt, va_list ap, int *argc)
|
|
{
|
|
xmlRpcValuePtr *argv;
|
|
const char *ptr;
|
|
int i;
|
|
|
|
*argc = strlen(fmt);
|
|
if (VIR_ALLOC_N(argv, *argc) < 0) {
|
|
xmlRpcError(VIR_ERR_NO_MEMORY, _("read response"), sizeof(*argv) * *argc);
|
|
return NULL;
|
|
}
|
|
i = 0;
|
|
for (ptr = fmt; *ptr; ptr++) {
|
|
switch (*ptr) {
|
|
case 'i':
|
|
if ((argv[i] = xmlRpcValueNew(XML_RPC_INTEGER)))
|
|
argv[i]->value.integer = va_arg(ap, int32_t);
|
|
break;
|
|
case 'f':
|
|
if ((argv[i] = xmlRpcValueNew(XML_RPC_DOUBLE)))
|
|
argv[i]->value.real = va_arg(ap, double);
|
|
break;
|
|
case 'b':
|
|
if ((argv[i] = xmlRpcValueNew(XML_RPC_BOOLEAN)))
|
|
argv[i]->value.boolean = va_arg(ap, int);
|
|
break;
|
|
case 's':
|
|
if ((argv[i] = xmlRpcValueNew(XML_RPC_STRING)))
|
|
argv[i]->value.string = strdup(va_arg(ap, const char *));
|
|
break;
|
|
default:
|
|
argv[i] = NULL;
|
|
break;
|
|
}
|
|
if (argv[i]==NULL) {
|
|
xmlRpcArgvFree(i, argv);
|
|
return NULL;
|
|
}
|
|
i++;
|
|
}
|
|
return argv;
|
|
}
|
|
|
|
void
|
|
xmlRpcArgvFree(int argc, xmlRpcValuePtr *argv)
|
|
{
|
|
int i;
|
|
if (!argv)
|
|
return;
|
|
for (i = 0; i < argc; i++)
|
|
xmlRpcValueFree(argv[i]);
|
|
|
|
VIR_FREE(argv);
|
|
}
|
|
|
|
int xmlRpcCall(xmlRpcContextPtr context, const char *method,
|
|
const char *retfmt, const char *fmt, ...)
|
|
{
|
|
va_list ap;
|
|
int argc;
|
|
xmlRpcValuePtr *argv;
|
|
virBuffer buf = VIR_BUFFER_INITIALIZER;
|
|
char *ret;
|
|
xmlDocPtr xml;
|
|
xmlNodePtr node;
|
|
bool fault;
|
|
xmlRpcValuePtr value;
|
|
void *retval = NULL;
|
|
char *content;
|
|
|
|
va_start(ap, fmt);
|
|
|
|
if (retfmt && *retfmt)
|
|
retval = va_arg(ap, void *);
|
|
|
|
if (!(argv = xmlRpcArgvNew(fmt, ap, &argc)))
|
|
return -1;
|
|
|
|
va_end(ap);
|
|
|
|
xmlRpcMarshalRequest(method, &buf, argc, argv);
|
|
|
|
xmlRpcArgvFree(argc, argv);
|
|
|
|
if (virBufferError(&buf))
|
|
return -1;
|
|
|
|
content = virBufferContentAndReset(&buf);
|
|
ret = xmlRpcCallRaw(context->uri, content);
|
|
VIR_FREE(content);
|
|
|
|
if (!ret)
|
|
return -1;
|
|
|
|
xml = xmlReadDoc((const xmlChar *)ret, "response.xml", NULL,
|
|
XML_PARSE_NOENT | XML_PARSE_NONET |
|
|
XML_PARSE_NOERROR | XML_PARSE_NOWARNING);
|
|
VIR_FREE(ret);
|
|
|
|
if (xml == NULL) {
|
|
errno = EINVAL;
|
|
xmlRpcError(VIR_ERR_XML_ERROR, _("parse server response failed"), 0);
|
|
return -1;
|
|
}
|
|
|
|
node = xmlDocGetRootElement(xml);
|
|
|
|
value = xmlRpcUnmarshalResponse(node, &fault);
|
|
|
|
if (!fault) {
|
|
switch (*retfmt) {
|
|
case 'i':
|
|
if (value->kind == XML_RPC_INTEGER)
|
|
*(int32_t *)retval = value->value.integer;
|
|
break;
|
|
case 'b':
|
|
if (value->kind == XML_RPC_BOOLEAN)
|
|
*(bool *)retval = value->value.boolean;
|
|
break;
|
|
case 'f':
|
|
if (value->kind == XML_RPC_DOUBLE)
|
|
*(double *)retval = value->value.real;
|
|
break;
|
|
case 's':
|
|
if (value->kind == XML_RPC_STRING)
|
|
*(char **)retval = strdup(value->value.string);
|
|
break;
|
|
case 'S':
|
|
*(char ***)retval = xmlRpcStringArray(value);
|
|
break;
|
|
case 'V':
|
|
*(xmlRpcValuePtr *)retval = value;
|
|
value = NULL;
|
|
break;
|
|
default:
|
|
printf("not supported yet\n");
|
|
break;
|
|
}
|
|
}
|
|
|
|
xmlFreeDoc(xml);
|
|
|
|
if (fault) {
|
|
/* FIXME we need generic dict routines */
|
|
/* FIXME we need faultMessage propagate to libvirt error API */
|
|
context->faultCode = value->value.dict.root->value->value.integer;
|
|
context->faultMessage = strdup(value->value.dict.root->next->value->value.string);
|
|
xmlRpcValueFree(value);
|
|
errno = EFAULT;
|
|
return -1;
|
|
}
|
|
|
|
xmlRpcValueFree(value);
|
|
|
|
return 0;
|
|
}
|
|
|
|
xmlRpcContextPtr xmlRpcContextNew(const char *uri)
|
|
{
|
|
xmlRpcContextPtr ret;
|
|
|
|
if (VIR_ALLOC(ret) < 0) {
|
|
xmlRpcError(VIR_ERR_NO_MEMORY, _("allocate new context"), sizeof(*ret));
|
|
} else {
|
|
ret->uri = strdup(uri);
|
|
ret->faultMessage = NULL;
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
void xmlRpcContextFree(xmlRpcContextPtr context)
|
|
{
|
|
if (context) {
|
|
VIR_FREE(context->uri);
|
|
VIR_FREE(context->faultMessage);
|
|
VIR_FREE(context);
|
|
}
|
|
}
|
|
|
|
int xmlRpcContextFaultCode(xmlRpcContextPtr context)
|
|
{
|
|
return context->faultCode;
|
|
}
|
|
|
|
const char *xmlRpcContextFaultMessage(xmlRpcContextPtr context)
|
|
{
|
|
return context->faultMessage;
|
|
}
|