diff options
Diffstat (limited to 'tools/libxen/src/xen_common.c')
-rw-r--r-- | tools/libxen/src/xen_common.c | 1363 |
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 + }; |