/* * 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 #include #include #include #include #include #include #include #include #include #include #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 };