/* * Copyright (c) 2006-2007 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 */ #define _XOPEN_SOURCE #include #include #include #include #include #include #include #include #include #include #include #include #include "xen/api/xen_common.h" #include "xen/api/xen_host.h" #include "xen_internal.h" #include "xen/api/xen_int_float_map.h" #include "xen/api/xen_int_int_map.h" #include "xen/api/xen_int_string_set_map.h" #include "xen/api/xen_string_string_map.h" /* * Whether to ignore missing structure entries. This is not something we * want to do, once the API has stabilised, as it indicates that the server is * broken, but at the moment, complaining is just slowing development down. */ #define PERMISSIVE 1 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_param_array(xmlNode *); static xmlNode * add_struct_array(xmlNode *, const char *); static xmlNode * add_nested_struct(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 xmlNode * add_container(xmlNode *parent, const char *name); 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 *); static const char * get_val_as_string(const struct abstract_type *, void *, char *, size_t); 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; } void xen_session_record_free(xen_session_record *record) { if (record == NULL) { return; } free(record->uuid); xen_host_record_opt_free(record->this_host); free(record->this_user); free(record); } 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); } void xen_session_clear_error(xen_session *session) { if (session->error_description != NULL) { for (int i = 0; i < session->error_description_count; i++) { free(session->error_description[i]); } free(session->error_description); } session->error_description = NULL; session->error_description_count = 0; session->ok = true; } bool xen_session_get_uuid(xen_session *session, char **result, xen_session *self_session) { abstract_value params[] = { { .type = &abstract_type_string, .u.string_val = self_session->session_id } }; xen_call_(session, "session.get_uuid", params, 1, &abstract_type_string, result); return session->ok; } bool xen_session_get_this_host(xen_session *session, xen_host *result, xen_session *self_session) { abstract_value params[] = { { .type = &abstract_type_string, .u.string_val = self_session->session_id } }; xen_call_(session, "session.get_this_host", params, 1, &abstract_type_string, result); return session->ok; } bool xen_session_get_this_user(xen_session *session, char **result, xen_session *self_session) { abstract_value params[] = { { .type = &abstract_type_string, .u.string_val = self_session->session_id } }; xen_call_(session, "session.get_this_user", params, 1, &abstract_type_string, result); return session->ok; } bool xen_session_get_last_active(xen_session *session, time_t *result, xen_session *self_session) { abstract_value params[] = { { .type = &abstract_type_string, .u.string_val = self_session->session_id } }; xen_call_(session, "session.get_last_active", params, 1, &abstract_type_datetime, result); return session->ok; } static const struct_member xen_session_record_struct_members[] = { { .key = "uuid", .type = &abstract_type_string, .offset = offsetof(xen_session_record, uuid) }, { .key = "this_host", .type = &abstract_type_ref, .offset = offsetof(xen_session_record, this_host) }, { .key = "this_user", .type = &abstract_type_string, .offset = offsetof(xen_session_record, this_user) }, { .key = "last_active", .type = &abstract_type_datetime, .offset = offsetof(xen_session_record, last_active) }, }; const abstract_type xen_session_record_abstract_type_ = { .typename = STRUCT, .struct_size = sizeof(xen_session_record), .member_count = sizeof(xen_session_record_struct_members) / sizeof(struct_member), .members = xen_session_record_struct_members }; bool xen_session_get_record(xen_session *session, xen_session_record **result, xen_session *self_session) { abstract_value param_values[] = { { .type = &abstract_type_string, .u.string_val = self_session->session_id } }; abstract_type result_type = xen_session_record_abstract_type_; *result = NULL; XEN_CALL_("session.get_record"); 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) { abstract_value *full_params; if (!s->ok) { return; } 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) { char **strings; if (!session->ok) { /* Don't wipe out the earlier error message with this one. */ return; } 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) { char **strings; if (!session->ok) { /* Don't wipe out the earlier error message with this one. */ return; } 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_node(xmlNode *n, char *type) { return n->type == XML_ELEMENT_NODE && 0 == strcmp((char *)n->name, type); } static bool is_container_node(xmlNode *n, char *type) { return is_node(n, 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) { /* XYZ is normal, but the XML-RPC spec also allows XYZ where XYZ is to be interpreted as a string. */ if (is_container_node(n, "value") && 0 == strcmp((char *)n->children->name, type)) { return n->children->children == NULL ? xmlStrdup(BAD_CAST("")) : xmlNodeGetContent(n->children->children); } else if (0 == strcmp(type, "string") && is_node(n, "value")) { return n->children == NULL ? xmlStrdup(BAD_CAST("")) : xmlNodeGetContent(n->children); } else { return 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 : DATETIME => value : time_t * * 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) { #if PERMISSIVE fprintf(stderr, "Expected an Enum from the server, but didn't get one\n"); ((int *)value)[slot] = 0; #else server_error( s, "Expected an Enum from the server, but didn't get one"); #endif } 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) { #if PERMISSIVE fprintf(stderr, "Expected a Float from the server, but didn't get one\n"); ((double *)value)[slot] = 0.0; #else server_error( s, "Expected a Float from the server, but didn't get one"); #endif } else { ((double *)value)[slot] = atof((char *)string); free(string); } } break; case BOOL: { xmlChar *string = string_from_value(value_node, "boolean"); if (string == NULL) { #if PERMISSIVE fprintf(stderr, "Expected a Bool from the server, but didn't get one\n"); ((bool *)value)[slot] = false; #else server_error( s, "Expected a Bool from the server, but didn't get one"); #endif } else { ((bool *)value)[slot] = (0 == strcmp((char *)string, "1")); free(string); } } break; case DATETIME: { xmlChar *string = string_from_value(value_node, "dateTime.iso8601"); if (string == NULL) { server_error( s, "Expected an DateTime from the server but didn't get one"); } else { struct tm tm; memset(&tm, 0, sizeof(tm)); strptime((char *)string, "%Y%m%dT%H:%M:%S", &tm); ((time_t *)value)[slot] = (time_t)mktime(&tm); 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 { arbitrary_set *set; xmlNode *cur, *data_node = value_node->children->children; int i, n = count_children(data_node, "value"); const abstract_type *member_type = result_type->child; size_t member_size = size_of_member(member_type); set = calloc(1, sizeof(arbitrary_set) + member_size * n); set->size = n; i = 0; 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")) { server_error(s, "Expected Map from the server, but didn't get it"); } else { arbitrary_map *map; xmlNode *cur, *struct_node = value_node->children; int i, 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; map = calloc(1, sizeof(arbitrary_map) + struct_size * n); map->size = n; i = 0; cur = struct_node->children; while (cur != NULL) { if (0 == strcmp((char *)cur->name, "member")) { xmlChar *name; if (cur->children == NULL || cur->last == cur->children) { server_error(s, "Malformed Map"); free(map); return; } 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")) { xmlChar *name; if (cur->children == NULL || cur->last == cur->children) { server_error(s, "Malformed Struct"); free(result); free(checklist); return; } 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) { #if PERMISSIVE fprintf(stderr, "Struct did not contain expected field %s.\n", mem->key); #else server_error_2(s, "Struct did not contain expected field", mem->key); free(result); free(checklist); return; #endif } } 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 *); case STRUCT: return type->struct_size; 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) { xmlNode *fault_node0, *fault_node1; xmlChar *fault_code_str, *fault_string_str; char **strings; 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; } fault_node0 = xpathObj->nodesetval->nodeTab[0]; fault_node1 = xpathObj->nodesetval->nodeTab[1]; 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; } 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; } 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) { char **c, **strings; int n; session->ok = false; c = (char **)error_descriptions->contents; n = error_descriptions->size; strings = malloc(n * sizeof(char *)); for (int i = 0; i < n; i++) { strings[i] = 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); xmlXPathContextPtr xpathCtx; xmlXPathObjectPtr xpathObj; xmlNode *node0, *node1; xmlChar *status_code; if (doc == NULL) { server_error(session, "Couldn't parse the server response"); return; } xpathCtx = xmlXPathNewContext(doc); if (xpathCtx == NULL) { xmlFreeDoc(doc); server_error(session, "Couldn't create XPath context"); return; } 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; } node0 = xpathObj->nodesetval->nodeTab[0]; node1 = xpathObj->nodesetval->nodeTab[1]; 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 void make_body_add_type(enum abstract_typename typename, abstract_value *v, xmlNode *params_node) { char buf[20]; switch (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 SET: { const struct abstract_type *member_type = v->type->child; arbitrary_set *set_val = v->u.struct_val; abstract_value v; xmlNode *data_node = add_param_array(params_node); for (size_t i = 0; i < set_val->size; i++) { switch (member_type->typename) { case STRING: v.u.string_val = (char *)set_val->contents[i]; make_body_add_type(member_type->typename, &v, data_node); break; default: assert(false); } } } 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; case MAP: { const struct struct_member *member = v->type->members; arbitrary_map *map_val = v->u.struct_val; xmlNode *param_node = add_param_struct(params_node); for (size_t i = 0; i < map_val->size; i++) { enum abstract_typename typename_key = member[0].type->typename; enum abstract_typename typename_val = member[1].type->typename; int offset_key = member[0].offset; int offset_val = member[1].offset; int struct_size = v->type->struct_size; switch (typename_key) { case STRING: { char **addr = (void *)(map_val + 1) + (i * struct_size) + offset_key; char *key = *addr; switch (typename_val) { case STRING: { char *val; addr = (void *)(map_val + 1) + (i * struct_size) + offset_val; val = *addr; add_struct_member(param_node, key, "string", val); break; } default: assert(false); } break; } default: assert(false); } } } break; default: assert(false); } } static char * make_body(const char *method_name, abstract_value params[], int param_count) { xmlDocPtr doc = xmlNewDoc(BAD_CAST "1.0"); xmlNode *params_node, *methodCall = xmlNewNode(NULL, BAD_CAST "methodCall"); xmlBufferPtr buffer; xmlSaveCtxtPtr save_ctxt; xmlChar *content; xmlDocSetRootElement(doc, methodCall); xmlNewChild(methodCall, NULL, BAD_CAST "methodName", BAD_CAST method_name); params_node = xmlNewChild(methodCall, NULL, BAD_CAST "params", NULL); for (int p = 0; p < param_count; p++) { abstract_value *v = params + p; make_body_add_type(v->type->typename, v, params_node); } buffer = xmlBufferCreate(); save_ctxt = xmlSaveToBuffer(buffer, NULL, XML_SAVE_NO_XHTML); if (xmlSaveDoc(save_ctxt, doc) == -1) { return NULL; } xmlFreeDoc(doc); xmlSaveClose(save_ctxt); 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: case STRING: case INT: case ENUM: { const char *val_as_string = get_val_as_string(type, value, buf, sizeof(buf)); adder(node, key, "string", val_as_string); } 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 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 = (char *)set_val->contents + (i * member_size); add_struct_value(member_type, member_value, add_unnamed_value, NULL, data_node); } } } break; case STRUCT: { assert(false); /* 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. */ } break; case MAP: { size_t member_size = type->struct_size; const struct abstract_type *l_type = type->members[0].type; const struct abstract_type *r_type = type->members[1].type; int l_offset = type->members[0].offset; int r_offset = type->members[1].offset; arbitrary_map *map_val = *(arbitrary_map **)value; if (map_val != NULL) { xmlNode *struct_node = add_nested_struct(node, key); for (size_t i = 0; i < map_val->size; i++) { void *contents = (void *)map_val->contents; void *l_value = contents + (i * member_size) + l_offset; void *r_value = contents + (i * member_size) + r_offset; const char *l_value_as_string = get_val_as_string(l_type, l_value, buf, sizeof(buf)); add_struct_value(r_type, r_value, add_struct_member, l_value_as_string, struct_node); } } } break; default: assert(false); } } static const char * get_val_as_string(const struct abstract_type *type, void *value, char *buf, size_t bufsize) { switch (type->typename) { case REF: { arbitrary_record_opt *val = *(arbitrary_record_opt **)value; if (val != NULL) { if (val->is_record) { return val->u.record->handle; } else { return val->u.handle; } } else { return NULL; } } break; case STRING: { return *(char **)value; } break; case INT: { int64_t val = *(int64_t *)value; snprintf(buf, bufsize, "%"PRId64, val); return buf; } break; case ENUM: { int val = *(int *)value; return type->enum_marshaller(val); } 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 xmlNode * add_param_array(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 "array", 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"); xmlNode *value_node, *array_node; xmlNewChild(member_node, NULL, BAD_CAST "name", BAD_CAST name); value_node = add_container(member_node, "value"); array_node = add_container(value_node, "array"); return add_container(array_node, "data"); } static xmlNode * add_nested_struct(xmlNode *struct_node, const char *name) { xmlNode *member_node = add_container(struct_node, "member"); xmlNode *value_node; xmlNewChild(member_node, NULL, BAD_CAST "name", BAD_CAST name); value_node = add_container(member_node, "value"); return add_container(value_node, "struct"); } 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 }; static struct struct_member int_int_members[] = { { .type = &abstract_type_int, .offset = offsetof(xen_int_int_map_contents, key) }, { .type = &abstract_type_int, .offset = offsetof(xen_int_int_map_contents, val) } }; const abstract_type abstract_type_int_int_map = { .typename = MAP, .struct_size = sizeof(xen_int_int_map_contents), .members = int_int_members }; static struct struct_member int_string_set_members[] = { { .type = &abstract_type_int, .offset = offsetof(xen_int_string_set_map_contents, key) }, { .type = &abstract_type_string_set, .offset = offsetof(xen_int_string_set_map_contents, val) } }; const abstract_type abstract_type_int_string_set_map = { .typename = MAP, .struct_size = sizeof(xen_int_string_set_map_contents), .members = int_string_set_members };