aboutsummaryrefslogtreecommitdiffstats
path: root/tools/libxen/src/xen_common.c
diff options
context:
space:
mode:
Diffstat (limited to 'tools/libxen/src/xen_common.c')
-rw-r--r--tools/libxen/src/xen_common.c1363
1 files changed, 1363 insertions, 0 deletions
diff --git a/tools/libxen/src/xen_common.c b/tools/libxen/src/xen_common.c
new file mode 100644
index 0000000000..bd370bb257
--- /dev/null
+++ b/tools/libxen/src/xen_common.c
@@ -0,0 +1,1363 @@
+/*
+ * Copyright (c) 2006 XenSource, 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, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include <assert.h>
+#include <stdarg.h>
+#include <stddef.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <libxml/parser.h>
+#include <libxml/tree.h>
+#include <libxml/xmlsave.h>
+#include <libxml/xmlstring.h>
+#include <libxml/xpath.h>
+
+#include "xen_common.h"
+#include "xen_internal.h"
+#include "xen_int_float_map.h"
+#include "xen_string_string_map.h"
+
+
+static xmlXPathCompExprPtr responsePath = NULL;
+static xmlXPathCompExprPtr faultPath = NULL;
+
+
+typedef struct
+{
+ size_t size;
+ void *contents[];
+} arbitrary_map;
+
+
+typedef struct
+{
+ void *handle;
+} arbitrary_record;
+
+
+typedef struct
+{
+ bool is_record;
+ union
+ {
+ char *handle;
+ arbitrary_record *record;
+ } u;
+} arbitrary_record_opt;
+
+
+static char *
+make_body(const char *, abstract_value [], int);
+
+static void
+parse_result(xen_session *, const char *, const abstract_type *, void *);
+
+static void
+add_value(xmlNode *, const char *, const char *);
+static void
+add_param(xmlNode *, const char *, const char *);
+
+static xmlNode *
+add_param_struct(xmlNode *);
+static xmlNode *
+add_struct_array(xmlNode *, const char *);
+static void
+add_struct_member(xmlNode *, const char *, const char *, const char *);
+static void
+add_unnamed_value(xmlNode *, const char *, const char *, const char *);
+
+static void
+add_struct_value(const struct abstract_type *, void *,
+ void (*)(xmlNode *, const char *, const char *,
+ const char *),
+ const char *, xmlNode *);
+
+static void
+call_raw(xen_session *, const char *, abstract_value [], int,
+ const abstract_type *, void *);
+
+static void
+parse_structmap_value(xen_session *, xmlNode *, const abstract_type *,
+ void *);
+
+static size_t size_of_member(const abstract_type *);
+
+
+void
+xen_init(void)
+{
+ responsePath =
+ xmlXPathCompile(
+ BAD_CAST(
+ "/methodResponse/params/param/value/struct/member/value"));
+ faultPath =
+ xmlXPathCompile(
+ BAD_CAST("/methodResponse/fault/value/struct/member/value"));
+}
+
+
+void
+xen_fini(void)
+{
+ xmlXPathFreeCompExpr(responsePath);
+ xmlXPathFreeCompExpr(faultPath);
+ responsePath = NULL;
+ faultPath = NULL;
+}
+
+
+xen_session *
+xen_session_login_with_password(xen_call_func call_func, void *handle,
+ const char *uname, const char *pwd)
+{
+ abstract_value params[] =
+ {
+ { .type = &abstract_type_string,
+ .u.string_val = uname },
+ { .type = &abstract_type_string,
+ .u.string_val = pwd }
+ };
+
+ xen_session *session = malloc(sizeof(xen_session));
+ session->call_func = call_func;
+ session->handle = handle;
+ session->session_id = NULL;
+ session->ok = true;
+ session->error_description = NULL;
+ session->error_description_count = 0;
+
+ call_raw(session, "session.login_with_password", params, 2,
+ &abstract_type_string, &session->session_id);
+
+ return session;
+}
+
+
+void
+xen_session_logout(xen_session *session)
+{
+ abstract_value params[] =
+ {
+ };
+ xen_call_(session, "session.logout", params, 0, NULL, NULL);
+
+ if (session->error_description != NULL)
+ {
+ for (int i = 0; i < session->error_description_count; i++)
+ {
+ free(session->error_description[i]);
+ }
+ free(session->error_description);
+ }
+
+ free((char *)session->session_id);
+ free(session);
+}
+
+
+int
+xen_session_get_this_host(xen_session *session, xen_host *result)
+{
+ abstract_value params[] =
+ {
+ };
+
+ xen_call_(session, "session.get_this_host", params, 0,
+ &abstract_type_string, result);
+ return session->ok;
+}
+
+
+#define X "%02x"
+#define UUID_FORMAT X X X X "-" X X "-" X X "-" X X "-" X X X X X X
+
+
+bool
+xen_uuid_string_to_bytes(char *uuid, char **bytes)
+{
+ unsigned int buf[16];
+
+ *bytes = NULL;
+
+ if (strlen(uuid) != 36)
+ return false;
+
+ if (16 != sscanf(uuid, UUID_FORMAT,
+ buf + 0, buf + 1, buf + 2, buf + 3,
+ buf + 4, buf + 5,
+ buf + 6, buf + 7,
+ buf + 8, buf + 9,
+ buf + 10, buf + 11, buf + 12, buf + 13, buf + 14,
+ buf + 15))
+ {
+ return false;
+ }
+
+ *bytes = malloc(16);
+ if (*bytes == NULL)
+ return false;
+
+ for (int i = 0; i < 16; i++) {
+ (*bytes)[i] = (char)buf[i];
+ }
+
+ return true;
+}
+
+
+bool
+xen_uuid_bytes_to_string(char *bytes, char **uuid)
+{
+ *uuid = malloc(37);
+ if (*uuid == NULL)
+ return false;
+
+ sprintf(*uuid, UUID_FORMAT,
+ bytes[0], bytes[1], bytes[2], bytes[3],
+ bytes[4], bytes[5],
+ bytes[6], bytes[7],
+ bytes[8], bytes[9],
+ bytes[10], bytes[11], bytes[12], bytes[13], bytes[14], bytes[15]);
+
+ return true;
+}
+
+
+#undef UUID_FORMAT
+#undef X
+
+
+void
+xen_uuid_free(char *uuid)
+{
+ free(uuid);
+}
+
+
+void
+xen_uuid_bytes_free(char *bytes)
+{
+ free(bytes);
+}
+
+
+/**
+ * @param value A pointer to the correct location as per the given
+ * result_type. Will be populated if the call succeeds. In that case, and if
+ * value is a char **, the char * itself must be freed by the caller.
+ */
+void
+xen_call_(xen_session *s, const char *method_name,
+ abstract_value params[], int param_count,
+ const abstract_type *result_type, void *value)
+{
+ if (!s->ok)
+ {
+ return;
+ }
+
+ abstract_value *full_params =
+ malloc(sizeof(abstract_value) * (param_count + 1));
+
+ full_params[0].type = &abstract_type_string;
+ full_params[0].u.string_val = s->session_id;
+
+ memcpy(full_params + 1, params, param_count * sizeof(abstract_value));
+
+ call_raw(s, method_name, full_params, param_count + 1, result_type,
+ value);
+
+ free(full_params);
+}
+
+
+static bool
+bufferAdd(const void *data, size_t len, void *buffer)
+{
+ return 0 == xmlBufferAdd((xmlBufferPtr)buffer, data, len);
+}
+
+
+static void
+call_raw(xen_session *s, const char *method_name,
+ abstract_value params[], int param_count,
+ const abstract_type *result_type, void *value)
+{
+ xmlBufferPtr buffer = xmlBufferCreate();
+ char *body = make_body(method_name, params, param_count);
+ int error_code =
+ s->call_func(body, strlen(body), s->handle, buffer, &bufferAdd);
+ free(body);
+ if (error_code)
+ {
+ char **strings = malloc(2 * sizeof(char *));
+
+ strings[0] = xen_strdup_("TRANSPORT_FAULT");
+ strings[1] = malloc(20);
+ snprintf(strings[1], 20, "%d", error_code);
+
+ s->ok = false;
+ s->error_description = strings;
+ s->error_description_count = 2;
+ }
+ else
+ {
+ parse_result(s, (char *)xmlBufferContent(buffer), result_type, value);
+ }
+ xmlBufferFree(buffer);
+}
+
+
+static void server_error(xen_session *session, const char *error_string)
+{
+ if (!session->ok)
+ {
+ /* Don't wipe out the earlier error message with this one. */
+ return;
+ }
+
+ char **strings = malloc(2 * sizeof(char *));
+
+ strings[0] = xen_strdup_("SERVER_FAULT");
+ strings[1] = xen_strdup_(error_string);
+
+ session->ok = false;
+ session->error_description = strings;
+ session->error_description_count = 2;
+}
+
+
+static void server_error_2(xen_session *session, const char *error_string,
+ const char *param)
+{
+ if (!session->ok)
+ {
+ /* Don't wipe out the earlier error message with this one. */
+ return;
+ }
+
+ char **strings = malloc(3 * sizeof(char *));
+
+ strings[0] = xen_strdup_("SERVER_FAULT_2");
+ strings[1] = xen_strdup_(error_string);
+ strings[2] = xen_strdup_(param);
+
+ session->ok = false;
+ session->error_description = strings;
+ session->error_description_count = 3;
+}
+
+
+static bool is_container_node(xmlNode *n, char *type)
+{
+ return
+ n->type == XML_ELEMENT_NODE &&
+ 0 == strcmp((char *)n->name, type) &&
+ n->children != NULL &&
+ n->children == n->last &&
+ n->children->type == XML_ELEMENT_NODE;
+}
+
+
+/**
+ * @return The contents of the given value, or NULL if this is not a node with
+ * the given type. If not NULL, the result must be freed with xmlFree().
+ */
+static xmlChar *string_from_value(xmlNode *n, char *type)
+{
+ return
+ is_container_node(n, "value") &&
+ 0 == strcmp((char *)n->children->name, type) ?
+ (n->children->children == NULL ?
+ xmlStrdup(BAD_CAST("")) :
+ xmlNodeGetContent(n->children->children)) :
+ NULL;
+}
+
+
+/**
+ * Find the name node that is a child of the given one, and return its
+ * contents, or NULL if this has no such node. If not NULL, the result must
+ * be freed with xmlFree().
+ */
+static xmlChar *string_from_name(xmlNode *n)
+{
+ xmlNode *cur = n->children;
+
+ while (cur != NULL)
+ {
+ if (0 == strcmp((char *)cur->name, "name"))
+ {
+ return xmlNodeGetContent(cur);
+ }
+ cur = cur->next;
+ }
+
+ return NULL;
+}
+
+
+static int count_children(xmlNode *n, const char *name)
+{
+ int result = 0;
+ xmlNode *cur = n->children;
+
+ while (cur != NULL)
+ {
+ if (0 == strcmp((char *)cur->name, name))
+ {
+ result++;
+ }
+ cur = cur->next;
+ }
+
+ return result;
+}
+
+
+static void destring(xen_session *s, xmlChar *name, const abstract_type *type,
+ void *value)
+{
+ switch (type->typename)
+ {
+ case STRING:
+ *((char **)value) = xen_strdup_((const char *)name);
+ break;
+
+ case INT:
+ *((int64_t *)value) = atoll((const char *)name);
+ break;
+
+ case FLOAT:
+ *((double *)value) = atof((const char *)name);
+ break;
+
+ default:
+ server_error(s, "Invalid Map key type");
+ }
+}
+
+
+/**
+ * result_type : STRING => value : char **, the char * is yours.
+ * result_type : ENUM => value : int *
+ * result_type : INT => value : int64_t *
+ * result_type : FLOAT => value : double *
+ * result_type : BOOL => value : bool *
+ * result_type : SET => value : arbitrary_set **, the set is yours.
+ * result_type : MAP => value : arbitrary_map **, the map is yours.
+ * result_type : OPT => value : arbitrary_record_opt **,
+ * the record is yours, the handle is filled.
+ * result_type : STRUCT => value : void **, the void * is yours.
+ */
+static void parse_into(xen_session *s, xmlNode *value_node,
+ const abstract_type *result_type, void *value,
+ int slot)
+{
+ if (result_type == NULL)
+ {
+ xmlChar *string = string_from_value(value_node, "string");
+ if (string == NULL || strcmp((char *)string, ""))
+ {
+ server_error(s,
+ "Expected Void from the server, but didn't get it");
+ }
+ else
+ {
+ free(string);
+ }
+
+ return;
+ }
+
+ switch (result_type->typename)
+ {
+ case STRING:
+ {
+ xmlChar *string = string_from_value(value_node, "string");
+ if (string == NULL)
+ {
+ server_error(
+ s, "Expected a String from the server, but didn't get one");
+ }
+ else
+ {
+ ((char **)value)[slot] = xen_strdup_((const char *)string);
+ free(string);
+ }
+ }
+ break;
+
+ case ENUM:
+ {
+ xmlChar *string = string_from_value(value_node, "string");
+ if (string == NULL)
+ {
+ server_error(
+ s, "Expected an Enum from the server, but didn't get one");
+ }
+ else
+ {
+ ((int *)value)[slot] =
+ result_type->enum_demarshaller(s, (const char *)string);
+ free(string);
+ }
+ }
+ break;
+
+ case INT:
+ {
+ xmlChar *string = string_from_value(value_node, "string");
+ if (string == NULL)
+ {
+ server_error(
+ s, "Expected an Int from the server, but didn't get one");
+ }
+ else
+ {
+ ((int64_t *)value)[slot] = (int64_t)atoll((char *)string);
+ free(string);
+ }
+ }
+ break;
+
+ case FLOAT:
+ {
+ xmlChar *string = string_from_value(value_node, "double");
+ if (string == NULL)
+ {
+ server_error(
+ s, "Expected a Float from the server, but didn't get one");
+ }
+ else
+ {
+ ((double *)value)[slot] = atof((char *)string);
+ free(string);
+ }
+ }
+ break;
+
+ case BOOL:
+ {
+ xmlChar *string = string_from_value(value_node, "boolean");
+ if (string == NULL)
+ {
+ server_error(
+ s, "Expected a Bool from the server, but didn't get one");
+ }
+ else
+ {
+ ((bool *)value)[slot] = (0 == strcmp((char *)string, "1"));
+ free(string);
+ }
+ }
+ break;
+
+ case SET:
+ {
+ if (!is_container_node(value_node, "value") ||
+ !is_container_node(value_node->children, "array"))
+ {
+ server_error(s,
+ "Expected Set from the server, but didn't get it");
+ }
+ else
+ {
+ xmlNode *data_node = value_node->children->children;
+ int n = count_children(data_node, "value");
+
+ const abstract_type *member_type = result_type->child;
+ size_t member_size = size_of_member(member_type);
+
+ arbitrary_set *set =
+ calloc(1, sizeof(arbitrary_set) + member_size * n);
+ set->size = n;
+ int i = 0;
+ xmlNode *cur = data_node->children;
+
+ while (cur != NULL)
+ {
+ if (0 == strcmp((char *)cur->name, "value"))
+ {
+ parse_into(s, cur, member_type, set->contents, i);
+ i++;
+ }
+ cur = cur->next;
+ }
+
+ ((arbitrary_set **)value)[slot] = set;
+ }
+ }
+ break;
+
+ case MAP:
+ {
+ if (!is_container_node(value_node, "value") ||
+ value_node->children->type != XML_ELEMENT_NODE ||
+ 0 != strcmp((char *)value_node->children->name, "struct") ||
+ value_node->children->children == NULL)
+ {
+ server_error(s,
+ "Expected Map from the server, but didn't get it");
+ }
+ else
+ {
+ xmlNode *struct_node = value_node->children;
+ int n = count_children(struct_node, "member");
+
+ size_t struct_size = result_type->struct_size;
+
+ const struct struct_member *key_member = result_type->members;
+ const struct struct_member *val_member = result_type->members + 1;
+
+ arbitrary_map *map =
+ calloc(1, sizeof(arbitrary_map) + struct_size * n);
+ map->size = n;
+ int i = 0;
+ xmlNode *cur = struct_node->children;
+
+ while (cur != NULL)
+ {
+ if (0 == strcmp((char *)cur->name, "member"))
+ {
+ if (cur->children == NULL || cur->last == cur->children)
+ {
+ server_error(s, "Malformed Map");
+ free(map);
+ return;
+ }
+
+ xmlChar *name = string_from_name(cur);
+ if (name == NULL)
+ {
+ server_error(s, "Malformed Map");
+ free(map);
+ return;
+ }
+
+ destring(s, name, key_member->type,
+ ((void *)(map + 1)) +
+ (i * struct_size) +
+ key_member->offset);
+ xmlFree(name);
+ if (!s->ok)
+ {
+ free(map);
+ return;
+ }
+
+ parse_structmap_value(s, cur, val_member->type,
+ ((void *)(map + 1)) +
+ (i * struct_size) +
+ val_member->offset);
+ if (!s->ok)
+ {
+ free(map);
+ return;
+ }
+ i++;
+ }
+ cur = cur->next;
+ }
+
+ ((arbitrary_map **)value)[slot] = map;
+ }
+ }
+ break;
+
+ case STRUCT:
+ {
+ if (!is_container_node(value_node, "value") ||
+ value_node->children->type != XML_ELEMENT_NODE ||
+ 0 != strcmp((char *)value_node->children->name, "struct") ||
+ value_node->children->children == NULL)
+ {
+ server_error(s,
+ "Expected Map from the server, but didn't get it");
+ }
+ else
+ {
+ xmlNode *struct_node = value_node->children;
+
+ void *result = calloc(1, result_type->struct_size);
+ xmlNode *cur = struct_node->children;
+
+ size_t member_count = result_type->member_count;
+
+ const struct_member **checklist =
+ malloc(sizeof(const struct_member *) * member_count);
+ int seen_count = 0;
+
+ while (cur != NULL)
+ {
+ if (0 == strcmp((char *)cur->name, "member"))
+ {
+ if (cur->children == NULL || cur->last == cur->children)
+ {
+ server_error(s, "Malformed Struct");
+ free(result);
+ free(checklist);
+ return;
+ }
+
+ xmlChar *name = string_from_name(cur);
+ if (name == NULL)
+ {
+ server_error(s, "Malformed Struct");
+ free(result);
+ free(checklist);
+ return;
+ }
+
+ for (size_t i = 0; i < member_count; i++)
+ {
+ const struct_member *mem = result_type->members + i;
+
+ if (0 == strcmp((char *)name, mem->key))
+ {
+ parse_structmap_value(s, cur, mem->type,
+ result + mem->offset);
+ checklist[seen_count] = mem;
+ seen_count++;
+ break;
+ }
+ }
+
+ /* Note that we're skipping unknown fields implicitly.
+ This means that we'll be forward compatible with
+ new servers. */
+
+ xmlFree(name);
+
+ if (!s->ok)
+ {
+ free(result);
+ free(checklist);
+ return;
+ }
+ }
+ cur = cur->next;
+ }
+
+ /* Check that we've filled all fields. */
+ for (size_t i = 0; i < member_count; i++)
+ {
+ const struct_member *mem = result_type->members + i;
+ int j;
+
+ for (j = 0; j < seen_count; j++)
+ {
+ if (checklist[j] == mem)
+ {
+ break;
+ }
+ }
+
+ if (j == seen_count)
+ {
+ server_error_2(s,
+ "Struct did not contain expected field",
+ mem->key);
+ free(result);
+ free(checklist);
+ return;
+ }
+ }
+
+ free(checklist);
+ ((void **)value)[slot] = result;
+ }
+ }
+ break;
+
+ case REF:
+ {
+ arbitrary_record_opt *record_opt =
+ calloc(1, sizeof(arbitrary_record_opt));
+
+ record_opt->is_record = false;
+ parse_into(s, value_node, &abstract_type_string,
+ &(record_opt->u.handle), 0);
+
+ ((arbitrary_record_opt **)value)[slot] = record_opt;
+ }
+ break;
+
+ default:
+ assert(false);
+ }
+}
+
+
+static size_t size_of_member(const abstract_type *type)
+{
+ switch (type->typename)
+ {
+ case STRING:
+ return sizeof(char *);
+
+/*
+ case INT:
+ return sizeof(int64_t);
+
+ case FLOAT:
+ return sizeof(double);
+
+ case BOOL:
+ return sizeof(bool);
+*/
+ case ENUM:
+ return sizeof(int);
+
+ case REF:
+ return sizeof(arbitrary_record_opt *);
+
+ default:
+ assert(false);
+ }
+}
+
+
+static void parse_structmap_value(xen_session *s, xmlNode *n,
+ const abstract_type *type, void *value)
+{
+ xmlNode *cur = n->children;
+
+ while (cur != NULL)
+ {
+ if (0 == strcmp((char *)cur->name, "value"))
+ {
+ parse_into(s, cur, type, value, 0);
+ return;
+ }
+ cur = cur->next;
+ }
+
+ server_error(s, "Missing value in Map/Struct");
+}
+
+
+static void parse_fault(xen_session *session, xmlXPathContextPtr xpathCtx)
+{
+ xmlXPathObjectPtr xpathObj = xmlXPathCompiledEval(faultPath, xpathCtx);
+ if (xpathObj == NULL)
+ {
+ server_error(session, "Method response is neither result nor fault");
+ return;
+ }
+
+ if (xpathObj->type != XPATH_NODESET ||
+ xpathObj->nodesetval->nodeNr != 2)
+ {
+ xmlXPathFreeObject(xpathObj);
+ server_error(session, "Method response is neither result nor fault");
+ return;
+ }
+
+ xmlNode *fault_node0 = xpathObj->nodesetval->nodeTab[0];
+ xmlNode *fault_node1 = xpathObj->nodesetval->nodeTab[1];
+
+ xmlChar *fault_code_str = string_from_value(fault_node0, "int");
+ if (fault_code_str == NULL)
+ {
+ fault_code_str = string_from_value(fault_node0, "i4");
+ }
+ if (fault_code_str == NULL)
+ {
+ xmlXPathFreeObject(xpathObj);
+ server_error(session, "Fault code is malformed");
+ return;
+ }
+
+ xmlChar *fault_string_str = string_from_value(fault_node1, "string");
+ if (fault_string_str == NULL)
+ {
+ xmlFree(fault_code_str);
+ xmlXPathFreeObject(xpathObj);
+ server_error(session, "Fault string is malformed");
+ return;
+ }
+
+ char **strings = malloc(3 * sizeof(char *));
+
+ strings[0] = xen_strdup_("FAULT");
+ strings[1] = xen_strdup_((char *)fault_code_str);
+ strings[2] = xen_strdup_((char *)fault_string_str);
+
+ session->ok = false;
+ session->error_description = strings;
+ session->error_description_count = 3;
+
+ xmlFree(fault_code_str);
+ xmlFree(fault_string_str);
+ xmlXPathFreeObject(xpathObj);
+}
+
+
+static void parse_failure(xen_session *session, xmlNode *node)
+{
+ abstract_type error_description_type =
+ { .typename = SET,
+ .child = &abstract_type_string };
+ arbitrary_set *error_descriptions;
+
+ parse_into(session, node, &error_description_type, &error_descriptions,
+ 0);
+
+ if (session->ok)
+ {
+ session->ok = false;
+
+ char **c = (char **)error_descriptions->contents;
+ int n = error_descriptions->size;
+
+ char **strings = malloc(3 * sizeof(char *));
+ for (int i = 0; i < n; i++)
+ {
+ strings[i] = xen_strdup_(c[i]);
+ }
+
+ session->error_description_count = n;
+ session->error_description = strings;
+ }
+
+ free(error_descriptions);
+}
+
+
+/**
+ * Parameters as for xen_call_() above.
+ */
+static void parse_result(xen_session *session, const char *result,
+ const abstract_type *result_type, void *value)
+{
+ xmlDocPtr doc =
+ xmlReadMemory(result, strlen(result), "", NULL, XML_PARSE_NONET);
+
+ if (doc == NULL)
+ {
+ server_error(session, "Couldn't parse the server response");
+ return;
+ }
+
+ xmlXPathContextPtr xpathCtx = xmlXPathNewContext(doc);
+ if (xpathCtx == NULL)
+ {
+ xmlFreeDoc(doc);
+ server_error(session, "Couldn't create XPath context");
+ return;
+ }
+
+ xmlXPathObjectPtr xpathObj =
+ xmlXPathCompiledEval(responsePath, xpathCtx);
+ if (xpathObj == NULL)
+ {
+ parse_fault(session, xpathCtx);
+
+ xmlXPathFreeContext(xpathCtx);
+ xmlFreeDoc(doc);
+ return;
+ }
+
+ if (xpathObj->type != XPATH_NODESET ||
+ xpathObj->nodesetval->nodeNr != 2)
+ {
+ parse_fault(session, xpathCtx);
+
+ xmlXPathFreeObject(xpathObj);
+ xmlXPathFreeContext(xpathCtx);
+ xmlFreeDoc(doc);
+ return;
+ }
+
+ xmlNode *node0 = xpathObj->nodesetval->nodeTab[0];
+ xmlNode *node1 = xpathObj->nodesetval->nodeTab[1];
+
+ xmlChar *status_code = string_from_value(node0, "string");
+ if (status_code == NULL)
+ {
+ xmlXPathFreeObject(xpathObj);
+ xmlXPathFreeContext(xpathCtx);
+ xmlFreeDoc(doc);
+ server_error(session, "Server response does not have a Status");
+ return;
+ }
+
+ if (strcmp((char *)status_code, "Success"))
+ {
+ parse_failure(session, node1);
+
+ xmlFree(status_code);
+ xmlXPathFreeObject(xpathObj);
+ xmlXPathFreeContext(xpathCtx);
+ xmlFreeDoc(doc);
+ return;
+ }
+
+ parse_into(session, node1, result_type, value, 0);
+
+ xmlFree(status_code);
+ xmlXPathFreeObject(xpathObj);
+ xmlXPathFreeContext(xpathCtx);
+ xmlFreeDoc(doc);
+}
+
+
+static char *
+make_body(const char *method_name, abstract_value params[], int param_count)
+{
+ char buf[20];
+
+ xmlDocPtr doc = xmlNewDoc(BAD_CAST "1.0");
+ xmlNode *methodCall = xmlNewNode(NULL, BAD_CAST "methodCall");
+ xmlDocSetRootElement(doc, methodCall);
+
+ xmlNewChild(methodCall, NULL, BAD_CAST "methodName",
+ BAD_CAST method_name);
+
+ xmlNode *params_node =
+ xmlNewChild(methodCall, NULL, BAD_CAST "params", NULL);
+
+ for (int p = 0; p < param_count; p++)
+ {
+ abstract_value *v = params + p;
+ switch (v->type->typename)
+ {
+ case STRING:
+ add_param(params_node, "string", v->u.string_val);
+ break;
+
+ case INT:
+ snprintf(buf, sizeof(buf), "%"PRId64, v->u.int_val);
+ add_param(params_node, "string", buf);
+ break;
+
+ case FLOAT:
+ snprintf(buf, sizeof(buf), "%lf", v->u.float_val);
+ add_param(params_node, "double", buf);
+ break;
+
+ case BOOL:
+ add_param(params_node, "boolean", v->u.bool_val ? "1" : "0");
+ break;
+
+ case VOID:
+ add_param(params_node, "string", "");
+ break;
+
+ case ENUM:
+ add_param(params_node, "string",
+ v->type->enum_marshaller(v->u.enum_val));
+ break;
+
+ case STRUCT:
+ {
+ size_t member_count = v->type->member_count;
+
+ xmlNode *struct_node = add_param_struct(params_node);
+
+ for (size_t i = 0; i < member_count; i++)
+ {
+ const struct struct_member *mem = v->type->members + i;
+ const char *key = mem->key;
+ void *struct_value = v->u.struct_val;
+
+ add_struct_value(mem->type, struct_value + mem->offset,
+ add_struct_member, key, struct_node);
+ }
+ }
+ break;
+
+ default:
+ assert(false);
+ }
+ }
+
+ xmlBufferPtr buffer = xmlBufferCreate();
+ xmlSaveCtxtPtr save_ctxt =
+ xmlSaveToBuffer(buffer, NULL, XML_SAVE_NO_XHTML);
+
+ if (xmlSaveDoc(save_ctxt, doc) == -1)
+ {
+ return NULL;
+ }
+
+ xmlFreeDoc(doc);
+ xmlSaveClose(save_ctxt);
+ xmlChar *content = xmlStrdup(xmlBufferContent(buffer));
+ xmlBufferFree(buffer);
+ return (char *)content;
+}
+
+
+static void
+add_struct_value(const struct abstract_type *type, void *value,
+ void (*adder)(xmlNode *node, const char *key,
+ const char *type, const char *val),
+ const char *key, xmlNode *node)
+{
+ char buf[20];
+
+ switch (type->typename)
+ {
+ case REF:
+ {
+ arbitrary_record_opt *val = *(arbitrary_record_opt **)value;
+ if (val != NULL)
+ {
+ if (val->is_record)
+ {
+ adder(node, key, "string", val->u.record->handle);
+ }
+ else
+ {
+ adder(node, key, "string", val->u.handle);
+ }
+ }
+ }
+ break;
+
+ case STRING:
+ {
+ char *val = *(char **)value;
+ if (val != NULL)
+ {
+ adder(node, key, "string", val);
+ }
+ }
+ break;
+
+ case INT:
+ {
+ int64_t val = *(int64_t *)value;
+ snprintf(buf, sizeof(buf), "%"PRId64, val);
+ adder(node, key, "string", buf);
+ }
+ break;
+
+ case FLOAT:
+ {
+ double val = *(double *)value;
+ snprintf(buf, sizeof(buf), "%lf", val);
+ adder(node, key, "double", buf);
+ }
+ break;
+
+ case BOOL:
+ {
+ bool val = *(bool *)value;
+ adder(node, key, "boolean", val ? "1" : "0");
+ }
+ break;
+
+ case ENUM:
+ {
+ int val = *(int *)value;
+ adder(node, key, "string", type->enum_marshaller(val));
+ }
+ break;
+
+ case SET:
+ {
+ const struct abstract_type *member_type = type->child;
+ size_t member_size = size_of_member(member_type);
+ arbitrary_set *set_val = *(arbitrary_set **)value;
+
+ if (set_val != NULL)
+ {
+ xmlNode *data_node = add_struct_array(node, key);
+
+ for (size_t i = 0; i < set_val->size; i++)
+ {
+ void *member_value = set_val->contents + (i * member_size);
+ add_struct_value(member_type, member_value,
+ add_unnamed_value, NULL, data_node);
+ }
+ }
+ }
+ break;
+
+ case STRUCT:
+ case MAP:
+ {
+ /* XXX Nested structures aren't supported yet, but
+ fortunately we don't need them, because we don't have
+ any "deep create" calls. This will need to be
+ fixed. We don't need maps either. */
+ }
+ break;
+
+ default:
+ assert(false);
+ }
+}
+
+
+static xmlNode *
+add_container(xmlNode *parent, const char *name)
+{
+ return xmlNewChild(parent, NULL, BAD_CAST name, NULL);
+}
+
+
+static void
+add_param(xmlNode *params_node, const char *type, const char *value)
+{
+ xmlNode *param_node = add_container(params_node, "param");
+ add_value(param_node, type, value);
+}
+
+
+static void
+add_value(xmlNode *parent, const char *type, const char *value)
+{
+ xmlNode *value_node = add_container(parent, "value");
+ xmlNewChild(value_node, NULL, BAD_CAST type, BAD_CAST value);
+}
+
+
+static void
+add_unnamed_value(xmlNode *parent, const char *name, const char *type,
+ const char *value)
+{
+ (void)name;
+ add_value(parent, type, value);
+}
+
+
+static xmlNode *
+add_param_struct(xmlNode *params_node)
+{
+ xmlNode *param_node = add_container(params_node, "param");
+ xmlNode *value_node = add_container(param_node, "value");
+
+ return xmlNewChild(value_node, NULL, BAD_CAST "struct", NULL);
+}
+
+
+static void
+add_struct_member(xmlNode *struct_node, const char *name, const char *type,
+ const char *value)
+{
+ xmlNode *member_node = add_container(struct_node, "member");
+
+ xmlNewChild(member_node, NULL, BAD_CAST "name", BAD_CAST name);
+
+ add_value(member_node, type, value);
+}
+
+
+static xmlNode *
+add_struct_array(xmlNode *struct_node, const char *name)
+{
+ xmlNode *member_node = add_container(struct_node, "member");
+
+ xmlNewChild(member_node, NULL, BAD_CAST "name", BAD_CAST name);
+
+ xmlNode *value_node = add_container(member_node, "value");
+ xmlNode *array_node = add_container(value_node, "array");
+
+ return add_container(array_node, "data");
+
+}
+
+
+int xen_enum_lookup_(xen_session *session, const char *str,
+ const char **lookup_table, int n)
+{
+ if (str != NULL)
+ {
+ for (int i = 0; i < n; i++)
+ {
+ if (0 == strcmp(str, lookup_table[i]))
+ {
+ return i;
+ }
+ }
+ }
+
+ server_error_2(session, "Bad enum string", str);
+ return 0;
+}
+
+
+char *
+xen_strdup_(const char *in)
+{
+ char *result = malloc(strlen(in) + 1);
+ strcpy(result, in);
+ return result;
+}
+
+
+const abstract_type abstract_type_string = { .typename = STRING };
+const abstract_type abstract_type_int = { .typename = INT };
+const abstract_type abstract_type_float = { .typename = FLOAT };
+const abstract_type abstract_type_bool = { .typename = BOOL };
+const abstract_type abstract_type_datetime = { .typename = DATETIME };
+const abstract_type abstract_type_ref = { .typename = REF };
+
+const abstract_type abstract_type_string_set =
+ {
+ .typename = SET,
+ .child = &abstract_type_string
+ };
+
+const abstract_type abstract_type_ref_set =
+ {
+ .typename = SET,
+ .child = &abstract_type_ref
+ };
+
+static const struct struct_member string_string_members[] =
+{
+ {
+ .type = &abstract_type_string,
+ .offset = offsetof(xen_string_string_map_contents, key)
+ },
+ {
+ .type = &abstract_type_string,
+ .offset = offsetof(xen_string_string_map_contents, val)
+ }
+};
+const abstract_type abstract_type_string_string_map =
+ {
+ .typename = MAP,
+ .struct_size = sizeof(xen_string_string_map_contents),
+ .members = string_string_members
+ };
+
+static struct struct_member int_float_members[] =
+{
+ {
+ .type = &abstract_type_int,
+ .offset = offsetof(xen_int_float_map_contents, key)
+ },
+ {
+ .type = &abstract_type_float,
+ .offset = offsetof(xen_int_float_map_contents, val)
+ }
+};
+const abstract_type abstract_type_int_float_map =
+ {
+ .typename = MAP,
+ .struct_size = sizeof(xen_int_float_map_contents),
+ .members = int_float_members
+ };