[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index] [Xen-devel] Re: [PATCH V2] libxl, Introduce a QMP client
On Thu, 9 Jun 2011, Anthony Perard wrote: > From: Anthony PERARD <anthony.perard@xxxxxxxxxx> > > QMP stands for QEMU Monitor Protocol and it is used to query information > from QEMU or to control QEMU. > > This implementation will ask QEMU the list of chardevice and store the > path to serial0 in xenstored. So we will be able to use xl console with > QEMU upstream. > > In order to connect to the QMP server, a socket file is created in > /var/run/xen/qmp-$(domid). > > Signed-off-by: Anthony PERARD <anthony.perard@xxxxxxxxxx> > --- > > Change since the first version: > - Introduction of libxl_run_dir_path(), should maybe be in another patch. > - Add a new static function qmp_synchronous_send that waits the answer from > the server. > - QMP is now used only inside libxl, so only one command will be sent > through > the socket and after the connection is closed. This leave the socket free > to be used to send another command. > > > > Config.mk | 1 + > config/StdGNU.mk | 2 + > tools/libxl/Makefile | 4 + > tools/libxl/libxl.h | 1 + > tools/libxl/libxl_create.c | 4 + > tools/libxl/libxl_dm.c | 6 + > tools/libxl/libxl_paths.c | 4 + > tools/libxl/libxl_qmp.c | 930 > ++++++++++++++++++++++++++++++++++++++++++++ > tools/libxl/libxl_qmp.h | 27 ++ > 9 files changed, 979 insertions(+), 0 deletions(-) > create mode 100644 tools/libxl/libxl_qmp.c > create mode 100644 tools/libxl/libxl_qmp.h > > diff --git a/Config.mk b/Config.mk > index aa681ae..8b11cd8 100644 > --- a/Config.mk > +++ b/Config.mk > @@ -133,6 +133,7 @@ define buildmakevars2file-closure > echo "XEN_CONFIG_DIR=\"$(XEN_CONFIG_DIR)\"" >> $(1).tmp; \ > echo "XEN_SCRIPT_DIR=\"$(XEN_SCRIPT_DIR)\"" >> $(1).tmp; \ > echo "XEN_LOCK_DIR=\"$(XEN_LOCK_DIR)\"" >> $(1).tmp; \ > + echo "XEN_RUN_DIR=\"$(XEN_RUN_DIR)\"" >> $(1).tmp; \ > if ! cmp $(1).tmp $(1); then mv -f $(1).tmp $(1); fi > endef > > diff --git a/config/StdGNU.mk b/config/StdGNU.mk > index 25aeb4d..68fa226 100644 > --- a/config/StdGNU.mk > +++ b/config/StdGNU.mk > @@ -52,9 +52,11 @@ PRIVATE_BINDIR = $(PRIVATE_PREFIX)/bin > ifeq ($(PREFIX),/usr) > CONFIG_DIR = /etc > XEN_LOCK_DIR = /var/lock > +XEN_RUN_DIR = /var/run/xen > else > CONFIG_DIR = $(PREFIX)/etc > XEN_LOCK_DIR = $(PREFIX)/var/lock > +XEN_RUN_DIR = $(PREFIX)/var/run/xen > endif > > SYSCONFIG_DIR = $(CONFIG_DIR)/$(CONFIG_LEAF_DIR) > diff --git a/tools/libxl/Makefile b/tools/libxl/Makefile > index 538cd16..df01243 100644 > --- a/tools/libxl/Makefile > +++ b/tools/libxl/Makefile > @@ -32,6 +32,9 @@ endif > LIBXL_OBJS-$(CONFIG_X86) += libxl_cpuid.o > LIBXL_OBJS-$(CONFIG_IA64) += libxl_nocpuid.o > > +LIBXL_OBJS-y += libxl_qmp.o > +LIBXL_LIBS += -lyajl > + > LIBXL_OBJS = flexarray.o libxl.o libxl_create.o libxl_dm.o libxl_pci.o \ > libxl_dom.o libxl_exec.o libxl_xshelp.o > libxl_device.o \ > libxl_internal.o libxl_utils.o libxl_uuid.o > $(LIBXL_OBJS-y) > @@ -115,6 +118,7 @@ install: all > $(INSTALL_DIR) $(DESTDIR)$(LIBDIR) > $(INSTALL_DIR) $(DESTDIR)$(INCLUDEDIR) > $(INSTALL_DIR) $(DESTDIR)$(BASH_COMPLETION_DIR) > + $(INSTALL_DIR) $(DESTDIR)$(XEN_RUN_DIR) > $(INSTALL_PROG) xl $(DESTDIR)$(SBINDIR) > $(INSTALL_PROG) libxenlight.so.$(MAJOR).$(MINOR) $(DESTDIR)$(LIBDIR) > ln -sf libxenlight.so.$(MAJOR).$(MINOR) > $(DESTDIR)$(LIBDIR)/libxenlight.so.$(MAJOR) > diff --git a/tools/libxl/libxl.h b/tools/libxl/libxl.h > index 95a7ba3..accc4d5 100644 > --- a/tools/libxl/libxl.h > +++ b/tools/libxl/libxl.h > @@ -510,6 +510,7 @@ const char *libxl_xenfirmwaredir_path(void); > const char *libxl_xen_config_dir_path(void); > const char *libxl_xen_script_dir_path(void); > const char *libxl_lock_dir_path(void); > +const char *libxl_run_dir_path(void); > > #endif /* LIBXL_H */ > > diff --git a/tools/libxl/libxl_create.c b/tools/libxl/libxl_create.c > index 62294b2..c88be07 100644 > --- a/tools/libxl/libxl_create.c > +++ b/tools/libxl/libxl_create.c > @@ -30,6 +30,7 @@ > #include "libxl_utils.h" > #include "libxl_internal.h" > #include "flexarray.h" > +#include "libxl_qmp.h" > > void libxl_domain_config_destroy(libxl_domain_config *d_config) > { > @@ -511,6 +512,9 @@ static int do_domain_create(libxl__gc *gc, > libxl_domain_config *d_config, > } > > if (dm_starting) { > + if (dm_info->device_model_version == > LIBXL_DEVICE_MODEL_VERSION_QEMU_XEN) { > + libxl__qmp_get_serial_console_path(ctx, domid); > + } > ret = libxl__confirm_device_model_startup(gc, dm_starting); > if (ret < 0) { > LIBXL__LOG(ctx, LIBXL__LOG_ERROR, > diff --git a/tools/libxl/libxl_dm.c b/tools/libxl/libxl_dm.c > index 47a51c8..f9c0d3d 100644 > --- a/tools/libxl/libxl_dm.c > +++ b/tools/libxl/libxl_dm.c > @@ -245,6 +245,12 @@ static char ** > libxl__build_device_model_args_new(libxl__gc *gc, > flexarray_vappend(dm_args, dm, > "-xen-domid", libxl__sprintf(gc, "%d", info->domid), > NULL); > > + flexarray_append(dm_args, "-qmp"); > + flexarray_append(dm_args, > + libxl__sprintf(gc, "unix:%s/qmp-%d,server,nowait", > + libxl_run_dir_path(), > + info->domid)); > + > if (info->type == LIBXL_DOMAIN_TYPE_PV) { > flexarray_append(dm_args, "-xen-attach"); > } > diff --git a/tools/libxl/libxl_paths.c b/tools/libxl/libxl_paths.c > index 9c2bd06..ec940e3 100644 > --- a/tools/libxl/libxl_paths.c > +++ b/tools/libxl/libxl_paths.c > @@ -64,3 +64,7 @@ const char *libxl_lock_dir_path(void) > { > return XEN_LOCK_DIR; > } > +const char *libxl_run_dir_path(void) > +{ > + return XEN_RUN_DIR; > +} > diff --git a/tools/libxl/libxl_qmp.c b/tools/libxl/libxl_qmp.c > new file mode 100644 > index 0000000..68c3957 > --- /dev/null > +++ b/tools/libxl/libxl_qmp.c > @@ -0,0 +1,930 @@ > +/* > + * Copyright (C) 2011 Citrix Ltd. > + * Author Anthony PERARD <anthony.perard@xxxxxxxxxx> > + * > + * This program 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; version 2.1 only. with the special > + * exception on linking described in file LICENSE. > + * > + * This program 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. > + */ > + > +/* > + * This file implement a client for QMP (QEMU Monitor Protocol). For the > + * Specification, see in the QEMU repository. > + */ > + > +#include <stdio.h> > +#include <stdlib.h> > +#include <string.h> > +#include <unistd.h> > + > +#include <sys/un.h> > +#include <sys/queue.h> > + > +#include <yajl/yajl_parse.h> > +#include <yajl/yajl_gen.h> > + > +#include "libxl_internal.h" > +#include "flexarray.h" > +#include "libxl_qmp.h" > + > +/* #define DEBUG_ANSWER */ > +/* #define DEBUG_RECEIVE */ > + > +/* > + * json_object types > + */ > + > +typedef struct json_object json_object; > +typedef struct json_map_node json_map_node; > +typedef enum node_type node_type_e; > + > +enum node_type { > + JSON_ERROR, > + JSON_NULL, > + JSON_BOOL, > + JSON_INTEGER, > + JSON_DOUBLE, > + JSON_STRING, > + JSON_MAP, > + JSON_ARRAY > +}; > + > +struct json_object { > + node_type_e type; > + union { > + bool boolean; > + long integer; > + double floating; > + const char *string; > + /* List of json_object */ > + flexarray_t *array; > + /* List of json_map_node */ > + flexarray_t *map; > + } u; > + json_object *parent; > +}; > + > +struct json_map_node { > + const char *map_key; > + json_object *obj; > +}; > + > +/* > + * QMP types & constant > + */ > + > +typedef int (*qmp_callback_t)(libxl__qmp_handler *qmp, const json_object > *tree); > + > +typedef enum message_type { > + QMP_ANY, > + QMP_QMP, > + QMP_RETURN, > + QMP_ERROR, > + QMP_EVENT > +} message_type_e; > + > +struct { > + const char *name; > + message_type_e type; > +} member_name_to_message_type[] = { > + { "QMP", QMP_QMP }, > + { "return", QMP_RETURN }, > + { "error", QMP_ERROR }, > + { "event", QMP_EVENT }, > + { "", QMP_ANY }, > +}; > + > +typedef struct callback_id_pair { > + int id; > + qmp_callback_t callback; > + SIMPLEQ_ENTRY(callback_id_pair) next; > +} callback_id_pair; > + > +struct libxl__qmp_handler { > + struct sockaddr_un addr; > + int qmp_fd; > + bool connected; > + /* this will be used by the synchronous send, so we know > + * when we can stop and close the socket > + */ > + int wait_for_id; > + > + unsigned char *buffer; > + yajl_handle hand; > + > + json_object *head; > + json_object *current; > + > + libxl_ctx *ctx; > + uint32_t domid; > + > + int last_id_used; > + SIMPLEQ_HEAD(callback_list, callback_id_pair) callback_list; > +#ifdef DEBUG_ANSWER > + yajl_gen g; > +#endif > +}; > + > +static int qmp_send(libxl__qmp_handler *qmp, const char *cmd, qmp_callback_t > callback); > +static int qmp_synchronous_send(libxl__qmp_handler *qmp, const char *cmd, > qmp_callback_t callback); > + > +static const size_t QMP_RECEIVE_BUFFER_SIZE = 65536; > + > +/* > + * json_object functions > + */ > + > +static int json_object_append_to(libxl__qmp_handler *qmp, json_object *obj, > json_object *dst) > +{ > + if (!dst) > + return -1; > + > + switch (dst->type) { > + case JSON_MAP: { > + json_map_node *last; > + > + flexarray_get(dst->u.map, dst->u.map->count - 1, (void**)&last); > + last->obj = obj; > + break; > + } > + case JSON_ARRAY: > + flexarray_append(dst->u.array, obj); > + break; > + default: > + LIBXL__LOG(qmp->ctx, LIBXL__LOG_ERROR, > + "error, try append an object to a %i\n", dst->type); > + return -1; > + } > + > + obj->parent = dst; > + return 0; > +} > + > +static void json_object_free(libxl__qmp_handler *qmp, json_object *obj) > +{ > + int index = 0; > + > + if (obj == NULL) > + return; > + switch (obj->type) { > + case JSON_ERROR: > + case JSON_NULL: > + case JSON_BOOL: > + case JSON_INTEGER: > + case JSON_DOUBLE: > + break; > + case JSON_STRING: > + free((void*)obj->u.string); > + break; > + case JSON_MAP: { > + json_map_node *node = NULL; > + > + for (index = 0; index < obj->u.map->count; index++) { > + if (flexarray_get(obj->u.map, index, (void**)&node) != 0) > + break; > + json_object_free(qmp, node->obj); > + free((void*)node->map_key); > + free(node); > + node = NULL; > + } > + flexarray_free(obj->u.map); > + break; > + } > + case JSON_ARRAY:{ > + json_object *node = NULL; > + break; > + > + for (index = 0; index < obj->u.array->count; index++) { > + if (flexarray_get(obj->u.array, index, (void**)&node) != 0) > + break; > + json_object_free(qmp, node); > + node = NULL; > + } > + flexarray_free(obj->u.array); > + break; > + } > + } > + free(obj); > +} > + > +static inline const char *json_object_get_string(const json_object *o) > +{ > + if (o && o->type == JSON_STRING) { > + return o->u.string; > + } > + return NULL; > +} > + > +static const json_object *json_object_map_get(const char *key, const > json_object *o) > +{ > + flexarray_t *maps = NULL; > + int index = 0; > + > + if (o && o->type == JSON_MAP) { > + json_map_node *node = NULL; > + > + maps = o->u.map; > + for (index = 0; index < maps->count; index++) { > + if (flexarray_get(maps, index, (void**)&node) != 0) > + break; > + if (strcmp(key, node->map_key) == 0) { > + return node->obj; > + } > + } > + } > + return NULL; > +} > + > +/* > + * JSON callbacks > + */ > + > +static int json_callback_null(void *opaque) > +{ > + libxl__qmp_handler *qmp = opaque; > + json_object *obj; > + > +#ifdef DEBUG_ANSWER > + yajl_gen_null(qmp->g); > +#endif > + > + obj = calloc(1, sizeof (json_object)); > + obj->type = JSON_NULL; > + json_object_append_to(qmp, obj, qmp->current); > + > + return 1; > +} > + > +static int json_callback_boolean(void *opaque, int boolean) > +{ > + libxl__qmp_handler *qmp = opaque; > + json_object *obj; > + > +#ifdef DEBUG_ANSWER > + yajl_gen_bool(qmp->g, boolean); > +#endif > + > + obj = calloc(1, sizeof (json_object)); > + obj->type = JSON_BOOL; > + obj->u.boolean = boolean; > + json_object_append_to(qmp, obj, qmp->current); > + > + return 1; > +} > + > +static int json_callback_integer(void *opaque, long value) > +{ > + libxl__qmp_handler *qmp = opaque; > + json_object *obj; > + > +#ifdef DEBUG_ANSWER > + yajl_gen_integer(qmp->g, value); > +#endif > + > + obj = calloc(1, sizeof (json_object)); > + obj->type = JSON_INTEGER; > + obj->u.integer = value; > + json_object_append_to(qmp, obj, qmp->current); > + > + return 1; > +} > + > +static int json_callback_double(void *opaque, double value) > +{ > + libxl__qmp_handler *qmp = opaque; > + json_object *obj; > + > +#ifdef DEBUG_ANSWER > + yajl_gen_double(qmp->g, value); > +#endif > + > + obj = calloc(1, sizeof (json_object)); > + obj->type = JSON_DOUBLE; > + obj->u.floating = value; > + json_object_append_to(qmp, obj, qmp->current); > + > + return 1; > +} > + > +static int json_callback_string(void *opaque, const unsigned char *str, > + unsigned int len) > +{ > + libxl__qmp_handler *qmp = opaque; > + char *t = malloc(len + 1); > + json_object *obj = NULL; > + > +#ifdef DEBUG_ANSWER > + yajl_gen_string(qmp->g, str, len); > +#endif > + > + strncpy(t, (const char *) str, len); > + t[len] = 0; > + > + obj = calloc(1, sizeof (json_object)); > + obj->type = JSON_STRING; > + obj->u.string = t; > + > + json_object_append_to(qmp, obj, qmp->current); > + > + return 1; > +} > + > +static int json_callback_map_key(void *opaque, const unsigned char *str, > + unsigned int len) > +{ > + libxl__qmp_handler *qmp = opaque; > + char *t = malloc(len + 1); > + json_object *obj = qmp->current; > + > +#ifdef DEBUG_ANSWER > + yajl_gen_string(qmp->g, str, len); > +#endif > + > + strncpy(t, (const char *) str, len); > + t[len] = 0; > + > + if (obj->type == JSON_MAP) { > + json_map_node *node = malloc(sizeof (json_map_node)); > + > + node->map_key = t; > + node->obj = NULL; > + > + flexarray_append(obj->u.map, node); > + } else { > + return 0; > + } > + > + return 1; > +} > + > +static int json_callback_start_map(void *opaque) > +{ > + libxl__qmp_handler *qmp = opaque; > + json_object *obj = NULL; > + > +#ifdef DEBUG_ANSWER > + yajl_gen_map_open(qmp->g); > +#endif > + > + obj = calloc(1, sizeof (json_object)); > + if (qmp->head == NULL) { > + qmp->head = obj; > + } > + > + obj->type = JSON_MAP; > + obj->u.map = flexarray_make(1, 1); > + > + json_object_append_to(qmp, obj, qmp->current); > + > + obj->parent = qmp->current; > + qmp->current = obj; > + > + return 1; > +} > + > +static int json_callback_end_map(void *opaque) > +{ > + libxl__qmp_handler *qmp = opaque; > + > +#ifdef DEBUG_ANSWER > + yajl_gen_map_close(qmp->g); > +#endif > + > + if (qmp->current) { > + qmp->current = qmp->current->parent; > + } else { > + LIBXL__LOG(qmp->ctx, LIBXL__LOG_ERROR, "no parents for a > json_object"); > + return 0; > + } > + > + return 1; > +} > + > +static int json_callback_start_array(void *opaque) > +{ > + libxl__qmp_handler *qmp = opaque; > + json_object *obj = NULL; > + > +#ifdef DEBUG_ANSWER > + yajl_gen_array_open(qmp->g); > +#endif > + > + obj = calloc(1, sizeof (json_object)); > + if (qmp->head == NULL) { > + qmp->head = obj; > + } > + obj->type = JSON_ARRAY; > + obj->u.array = flexarray_make(1, 1); > + json_object_append_to(qmp, obj, qmp->current); > + qmp->current = obj; > + > + return 1; > +} > + > +static int json_callback_end_array(void *opaque) > +{ > + libxl__qmp_handler *qmp = opaque; > + > +#ifdef DEBUG_ANSWER > + yajl_gen_array_close(qmp->g); > +#endif > + > + if (qmp->current) { > + qmp->current = qmp->current->parent; > + } else { > + LIBXL__LOG(qmp->ctx, LIBXL__LOG_ERROR, "no parents for a > json_object"); > + return 0; > + } > + > + return 1; > +} > + > +static yajl_callbacks callbacks = { > + json_callback_null, > + json_callback_boolean, > + json_callback_integer, > + json_callback_double, > + NULL, > + json_callback_string, > + json_callback_start_map, > + json_callback_map_key, > + json_callback_end_map, > + json_callback_start_array, > + json_callback_end_array > +}; > + > +/* > + * QMP callbacks functions > + */ > + > +static const char *get_serial0_chardev(libxl__qmp_handler *qmp, const > json_object *tree) > +{ > + const json_object *ret = NULL; > + const json_object *obj = NULL; > + const json_object *label = NULL; > + const char *s = NULL; > + flexarray_t *array = NULL; > + int index = 0; > + > + ret = json_object_map_get("return", tree); > + > + if (!ret || ret->type != JSON_ARRAY) { > + return NULL; > + } > + array = ret->u.array; > + for (index = 0; index < array->count; index++) { > + if (flexarray_get(array, index, (void**)&obj) != 0) > + break; > + label = json_object_map_get("label", obj); > + s = json_object_get_string(label); > + > + /* TODO Could replace serial0 by serial and get all serial ttys, if > sevral */ > + if (s && strcmp("serial0", s) == 0) { > + const json_object *filename = NULL; > + filename = json_object_map_get("filename", obj); > + return json_object_get_string(filename); > + } > + }; > + > + return NULL; > +} > + > +static int register_serial0_chardev_callback(libxl__qmp_handler *qmp, const > json_object *tree) > +{ > + libxl__gc gc = LIBXL_INIT_GC(qmp->ctx); > + char *path = NULL; > + const char *chardev = NULL; > + int ret = 0; > + > + chardev = get_serial0_chardev(qmp, tree); > + if (!(chardev && strncmp("pty:", chardev, 4) == 0)) { > + return 1; > + } > + > + path = libxl__xs_get_dompath(&gc, qmp->domid); > + path = libxl__sprintf(&gc, "%s/serial/%d/tty", path, 0); > + > + LIBXL__LOG(qmp->ctx, LIBXL__LOG_DEBUG, > + "qmp serial0: %s (%s)", chardev + 4, path); > + ret = libxl__xs_write(&gc, XBT_NULL, path, "%s", chardev + 4); > + libxl__free_all(&gc); > + return ret; > +} > + > +static int qmp_capabilities_callback(libxl__qmp_handler *qmp, const > json_object *tree) > +{ > + /* The purpuse of this function is to ask to QEMU every information > needed > + * by xl. */ > + int ret = 0; > + > + qmp->connected = true; > + > + return ret; > +} > + > +/* > + * QMP commands > + */ > + > +static int enable_qmp_capabilities(libxl__qmp_handler *qmp) > +{ > + return qmp_send(qmp, "qmp_capabilities", qmp_capabilities_callback); > +} > + > +/* > + * Helpers > + */ > + > +static message_type_e qmp_response_type(libxl__qmp_handler *qmp, json_object > *resp) > +{ > + if (resp && resp->type == JSON_MAP) { > + flexarray_t *maps = NULL; > + json_map_node *node = NULL; > + int index = 0; > + > + maps = resp->u.map; > + for (index = 0; index < maps->count; index++) { > + int i = 0; > + if (flexarray_get(maps, index, (void**)&node) != 0) > + break; > + for (i = 0; member_name_to_message_type[i].type != QMP_ANY; i++) > { > + if (strcmp(member_name_to_message_type[i].name, > node->map_key) == 0) { > + return member_name_to_message_type[i].type; > + } > + } > + } > + } > + > + return QMP_ANY; > +} > + > +static callback_id_pair *qmp_get_callback_from_id(libxl__qmp_handler *qmp, > json_object *o) > +{ > + const json_object *id_object = json_object_map_get("id", o); > + int id = -1; > + callback_id_pair *pp = NULL; > + > + if (id_object && id_object->type == JSON_INTEGER) { > + id = id_object->u.integer; > + > + SIMPLEQ_FOREACH(pp, &qmp->callback_list, next) { > + if (pp->id == id) { > + return pp; > + } > + } > + } > + return NULL; > +} > + > +static void qmp_handle_error_response(libxl__qmp_handler *qmp, json_object > *resp) > +{ > + callback_id_pair *pp = qmp_get_callback_from_id(qmp, resp); > + const json_object *error = json_object_map_get("error", resp); > + const char *msg = json_object_get_string(json_object_map_get("desc", > error)); > + > + if (pp) { > + if (pp->id == qmp->wait_for_id) { > + /* tell that the id have been processed */ > + qmp->wait_for_id = 0; > + } > + SIMPLEQ_REMOVE(&qmp->callback_list, pp, callback_id_pair, next); > + free(pp); > + } > + > + LIBXL__LOG(qmp->ctx, LIBXL__LOG_ERROR, > + "receive an error message from QMP server: %s", > + msg); > +} > + > +static int qmp_handle_response(libxl__qmp_handler *qmp, json_object *resp) > +{ > + message_type_e type = QMP_ANY; > + > + type = qmp_response_type(qmp, resp); > + LIBXL__LOG(qmp->ctx, LIBXL__LOG_DEBUG, "message type: %i", type); > + > + switch (type) { > + case QMP_QMP: > + /* On the greeting message from the server, enable qmp capabilities > */ > + enable_qmp_capabilities(qmp); > + break; > + case QMP_RETURN: { > + callback_id_pair *pp = qmp_get_callback_from_id(qmp, resp); > + > + if (pp) { > + pp->callback(qmp, resp); > + if (pp->id == qmp->wait_for_id) { > + /* tell that the id have been processed */ > + qmp->wait_for_id = 0; > + } > + SIMPLEQ_REMOVE(&qmp->callback_list, pp, callback_id_pair, next); > + free(pp); > + } > + break; > + } > + case QMP_ERROR: > + qmp_handle_error_response(qmp, resp); > + break; > + case QMP_EVENT: > + break; > + case QMP_ANY: > + return -1; > + } > + return 0; > +} > + > +/* > + * Handler functions > + */ > + > +static int qmp_connect(libxl__qmp_handler *qmp, const char *qmp_socket_path, > + int timeout) > +{ > + int ret; > + int i = 0; > + > + qmp->qmp_fd = socket(AF_UNIX, SOCK_STREAM, 0); > + if (qmp->qmp_fd < 0) { > + return -1; > + } > + > + memset(&qmp->addr, 0, sizeof (&qmp->addr)); > + qmp->addr.sun_family = AF_UNIX; > + strncpy(qmp->addr.sun_path, qmp_socket_path, sizeof > (qmp->addr.sun_path)); > + > + do { > + ret = connect(qmp->qmp_fd, (struct sockaddr *) &qmp->addr, > + sizeof (qmp->addr)); > + if (ret == 0) > + break; > + if (errno == ENOENT || errno == ECONNREFUSED) { > + /* ENOENT : Socket may not have shown up yet > + * ECONNREFUSED : Leftover socket hasn't been removed yet */ > + continue; > + } > + return -1; > + } while ((++i <= timeout * 5) && (usleep(.2 * 1000000) <= 0)); > + > + if (ret == -1) > + return -1; > + > + return 0; > +} > + > +static void qmp_close(libxl__qmp_handler *qmp) > +{ > + callback_id_pair *pp = NULL; > + callback_id_pair *tmp = NULL; > +#ifdef DEBUG_ANSWER > + if (qmp->g) > + yajl_gen_free(qmp->g); > +#endif > + if (qmp->hand) > + yajl_free(qmp->hand); > + close(qmp->qmp_fd); > + SIMPLEQ_FOREACH(pp, &qmp->callback_list, next) { > + if (tmp) > + free(tmp); > + tmp = pp; > + } > + if (tmp) > + free(tmp); > +} > + > +static int qmp_next(libxl__qmp_handler *qmp) > +{ > + yajl_status status; > + ssize_t rd; > + ssize_t bytes_parsed = 0; > + > + /* read the socket */ > + rd = read(qmp->qmp_fd, qmp->buffer, QMP_RECEIVE_BUFFER_SIZE); > + if (rd <= 0) { > + /* either an error, or nothing */ > + return rd; > + } > +#ifdef DEBUG_RECEIVE > + qmp->buffer[rd] = 0; > + LIBXL__LOG(qmp->ctx, LIBXL__LOG_DEBUG, "received: '%s'", qmp->buffer); > +#endif > + > + while (bytes_parsed < rd) { > +#ifdef DEBUG_ANSWER > + if (qmp->g == NULL) { > + yajl_gen_config conf = { 1, " " }; > + qmp->g = yajl_gen_alloc(&conf, NULL); > + } > +#endif > + /* parse the input */ > + if (qmp->hand == NULL) { > + /* allow comments */ > + yajl_parser_config cfg = { 1, 1 }; > + qmp->hand = yajl_alloc(&callbacks, &cfg, NULL, qmp); > + } > + status = yajl_parse(qmp->hand, qmp->buffer + bytes_parsed, rd - > bytes_parsed); > + bytes_parsed += yajl_get_bytes_consumed(qmp->hand); > + > + /* handle the answer */ > + if (status != yajl_status_ok && status != > yajl_status_insufficient_data) { > + unsigned char *str = yajl_get_error(qmp->hand, 1, qmp->buffer, > rd); > + > + LIBXL__LOG(qmp->ctx, LIBXL__LOG_ERROR, "yajl error: %s", str); > + yajl_free_error(qmp->hand, str); > + } > + > + if (status == yajl_status_ok) { > +#ifdef DEBUG_ANSWER > + const unsigned char *buf = NULL; > + unsigned int len = 0; > + > + yajl_gen_get_buf(qmp->g, &buf, &len); > + LIBXL__LOG(qmp->ctx, LIBXL__LOG_DEBUG, "response:\n%s", buf); > + yajl_gen_free(qmp->g); > + qmp->g = NULL; > +#endif > + > + qmp_handle_response(qmp, qmp->head); > + > + json_object_free(qmp, qmp->head); > + qmp->head = NULL; > + qmp->current = NULL; > + > + yajl_free(qmp->hand); > + qmp->hand = NULL; > + } > + /* skip the CRLF of the end of a command */ > + while (bytes_parsed < rd && (qmp->buffer[bytes_parsed] == '\r' > + || qmp->buffer[bytes_parsed] == '\n')) { > + bytes_parsed++; > + } > + } > + return 1; > +} > + > +static int qmp_send(libxl__qmp_handler *qmp, const char *cmd, qmp_callback_t > callback) > +{ > + yajl_gen_config conf = { 0, NULL }; > + const unsigned char *buf; > + const char *ex = "execute"; > + unsigned int len = 0; > + yajl_gen_status s; > + yajl_gen hand; > + > + hand = yajl_gen_alloc(&conf, NULL); > + if (!hand) { > + return -1; > + } > + > + yajl_gen_map_open(hand); > + yajl_gen_string(hand, (const unsigned char *)ex, strlen(ex)); > + yajl_gen_string(hand, (const unsigned char *)cmd, strlen(cmd)); > + yajl_gen_string(hand, (const unsigned char *)"id", 2); > + yajl_gen_integer(hand, ++qmp->last_id_used); > + yajl_gen_map_close(hand); > + > + s = yajl_gen_get_buf(hand, &buf, &len); > + > + if (s) { > + LIBXL__LOG(qmp->ctx, LIBXL__LOG_ERROR, > + "could not generate a qmp command"); > + return -1; > + } > + > + if (callback) { > + callback_id_pair *elm = malloc(sizeof (callback_id_pair)); > + elm->id = qmp->last_id_used; > + elm->callback = callback; > + SIMPLEQ_INSERT_TAIL(&qmp->callback_list, elm, next); > + } > + > + LIBXL__LOG(qmp->ctx, LIBXL__LOG_DEBUG, "next qmp command: '%s'", buf); > + > + write(qmp->qmp_fd, buf, len); > + write(qmp->qmp_fd, "\r\n", 2); > + yajl_gen_free(hand); > + > + return qmp->last_id_used; > +} > + > +static int qmp_synchronous_send(libxl__qmp_handler *qmp, const char *cmd, > qmp_callback_t callback) > +{ > + int id = 0; > + int ret = 0; > + > + id = qmp_send(qmp, cmd, callback); > + if (id <= 0) { > + return -1; > + } > + qmp->wait_for_id = id; > + > + while (qmp->wait_for_id == id) { > + if ((ret = qmp_next(qmp)) < 0) > + return ret; > + } > + return 0; > +} Another thing: we should select on the qmp file descriptor in this loop so that we can have a timeout instead of blocking forever. _______________________________________________ Xen-devel mailing list Xen-devel@xxxxxxxxxxxxxxxxxxx http://lists.xensource.com/xen-devel
|
![]() |
Lists.xenproject.org is hosted with RackSpace, monitoring our |