Files
xen/tools/libxen/src/xen_common.c
T
Ewan Mellor 6492f2ffbf Added documentation, C bindings, and a test for VM_metrics.{start_time,
VCPUs_CPU,VCPUs_params}.

Changed VM_metrics.start_time to be a DateTime, not an integer.

Signed-off-by: Ewan Mellor <ewan@xensource.com>
2007-04-09 17:09:21 +01:00

1719 lines
44 KiB
C

/*
* 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 <assert.h>
#include <stdarg.h>
#include <stddef.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.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_host.h"
#include "xen_internal.h"
#include "xen_int_float_map.h"
#include "xen_int_int_map.h"
#include "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_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);
}
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)
{
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_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)
{
/*
<value><type>XYZ</type></value> is normal, but the XML-RPC spec also
allows <value>XYZ</value> 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
{
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"))
{
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)
{
#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)
{
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(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);
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 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_struct(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 *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;
make_body_add_type(v->type->typename, v, params_node);
}
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:
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 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");
}
static xmlNode *
add_nested_struct(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");
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
};