[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


 


Rackspace

Lists.xenproject.org is hosted with RackSpace, monitoring our
servers 24x7x365 and backed by RackSpace's Fanatical Support®.