[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[Xen-devel] [PATCH V5 3/3] libxl, Introduce a QMP client



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>
---
 tools/libxl/Makefile         |    3 +
 tools/libxl/libxl.c          |    2 +
 tools/libxl/libxl_create.c   |    3 +
 tools/libxl/libxl_dm.c       |    9 +
 tools/libxl/libxl_internal.h |   18 +
 tools/libxl/libxl_qmp.c      | 1116 ++++++++++++++++++++++++++++++++++++++++++
 6 files changed, 1151 insertions(+), 0 deletions(-)
 create mode 100644 tools/libxl/libxl_qmp.c

diff --git a/tools/libxl/Makefile b/tools/libxl/Makefile
index f148ad3..86ba7b0 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)
diff --git a/tools/libxl/libxl.c b/tools/libxl/libxl.c
index c21cfe7..e09d7a7 100644
--- a/tools/libxl/libxl.c
+++ b/tools/libxl/libxl.c
@@ -757,6 +757,8 @@ int libxl_domain_destroy(libxl_ctx *ctx, uint32_t domid, 
int force)
     if (dm_present) {
         if (libxl__destroy_device_model(&gc, domid) < 0)
             LIBXL__LOG(ctx, LIBXL__LOG_ERROR, "libxl__destroy_device_model 
failed for %d", domid);
+
+        libxl__qmp_cleanup(&gc, domid);
     }
     if (libxl__devices_destroy(&gc, domid, force) < 0)
         LIBXL__LOG(ctx, LIBXL__LOG_ERROR, "libxl_destroy_devices failed for 
%d", domid);
diff --git a/tools/libxl/libxl_create.c b/tools/libxl/libxl_create.c
index 08d6539..06a4e42 100644
--- a/tools/libxl/libxl_create.c
+++ b/tools/libxl/libxl_create.c
@@ -522,6 +522,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_initializations(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 76d72a5..fa11370 100644
--- a/tools/libxl/libxl_dm.c
+++ b/tools/libxl/libxl_dm.c
@@ -249,6 +249,15 @@ 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, "-chardev");
+    flexarray_append(dm_args,
+                     libxl__sprintf(gc, 
"socket,id=libxl-cmd,path=%s/qmp-%d,server,nowait",
+                                    libxl_run_dir_path(),
+                                    info->domid));
+
+    flexarray_append(dm_args, "-mon");
+    flexarray_append(dm_args, "chardev=libxl-cmd,mode=control");
+
     if (info->type == LIBXL_DOMAIN_TYPE_PV) {
         flexarray_append(dm_args, "-xen-attach");
     }
diff --git a/tools/libxl/libxl_internal.h b/tools/libxl/libxl_internal.h
index af35861..d91cee4 100644
--- a/tools/libxl/libxl_internal.h
+++ b/tools/libxl/libxl_internal.h
@@ -379,4 +379,22 @@ _hidden int libxl__e820_alloc(libxl_ctx *ctx, uint32_t 
domid, libxl_domain_confi
 #define STRINGIFY(x) #x
 #define TOSTRING(x) STRINGIFY(x)
 
+/* from libxl_qmp */
+typedef struct libxl__qmp_handler libxl__qmp_handler;
+
+/* Initialise and connect to the QMP socket.
+ *   Return an handler or NULL if there is an error
+ */
+_hidden libxl__qmp_handler *libxl__qmp_initialize(libxl_ctx *ctx,
+                                                  uint32_t domid);
+/* ask to QEMU the serial port information and store it in xenstore. */
+_hidden int libxl__qmp_query_serial(libxl__qmp_handler *qmp);
+/* close and free the QMP handler */
+_hidden void libxl__qmp_close(libxl__qmp_handler *qmp);
+/* remove the socket file, if the file has already been removed, nothing 
happen */
+_hidden void libxl__qmp_cleanup(libxl__gc *gc, uint32_t domid);
+
+/* this helper calls qmp_initialize, query_serial and qmp_close */
+_hidden int libxl__qmp_initializations(libxl_ctx *ctx, uint32_t domid);
+
 #endif
diff --git a/tools/libxl/libxl_qmp.c b/tools/libxl/libxl_qmp.c
new file mode 100644
index 0000000..6b1fdc6
--- /dev/null
+++ b/tools/libxl/libxl_qmp.c
@@ -0,0 +1,1116 @@
+/*
+ * 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"
+
+/* #define DEBUG_ANSWER */
+/* #define DEBUG_RECEIVED */
+
+#ifdef DEBUG_ANSWER
+#  define DEBUG_GEN(h, type)              yajl_gen_##type(h)
+#  define DEBUG_GEN_VALUE(h, type, value) yajl_gen_##type(h, value)
+#  define DEBUG_GEN_STRING(h, str, n)     yajl_gen_string(h, str, n)
+#else
+#  define DEBUG_GEN(h, type)                  (void)0
+#  define DEBUG_GEN_VALUE(h, type, value)     (void)0
+#  define DEBUG_GEN_STRING(h, value, lenght)  (void)0
+#endif
+
+/*
+ * json_object types
+ */
+
+typedef enum {
+    JSON_ERROR,
+    JSON_NULL,
+    JSON_TRUE,
+    JSON_FALSE,
+    JSON_INTEGER,
+    JSON_DOUBLE,
+    JSON_STRING,
+    JSON_MAP,
+    JSON_ARRAY,
+    JSON_ANY
+} node_type;
+
+typedef struct json_object {
+    node_type type;
+    union {
+        long i;
+        double d;
+        const char *string;
+        /* List of json_object */
+        flexarray_t *array;
+        /* List of json_map_node */
+        flexarray_t *map;
+    } u;
+    struct json_object *parent;
+} json_object;
+
+typedef struct {
+    const char *map_key;
+    json_object *obj;
+} json_map_node;
+
+/*
+ * QMP types & constant
+ */
+
+#define QMP_RECEIVE_BUFFER_SIZE 4096
+
+typedef int (*qmp_callback_t)(libxl__qmp_handler *qmp,
+                              const json_object *tree);
+
+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;
+    time_t timeout;
+    /* wait_for_id will be used by the synchronous send function */
+    int wait_for_id;
+
+    unsigned char buffer[QMP_RECEIVE_BUFFER_SIZE];
+    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 const int QMP_SOCKET_CONNECT_TIMEOUT = 5;
+
+/*
+ * json_object helper functions
+ */
+
+static inline bool json_object_is_string(const json_object *o)
+{
+    return o != NULL && o->type == JSON_STRING;
+}
+
+static inline bool json_object_is_integer(const json_object *o)
+{
+    return o != NULL && o->type == JSON_INTEGER;
+}
+
+static inline bool json_object_is_map(const json_object *o)
+{
+    return o != NULL && o->type == JSON_MAP;
+}
+
+static inline bool json_object_is_array(const json_object *o)
+{
+    return o != NULL && o->type == JSON_ARRAY;
+}
+
+
+static inline const char *json_object_get_string(const json_object *o)
+{
+    if (json_object_is_string(o))
+        return o->u.string;
+    else
+        return NULL;
+}
+
+static inline flexarray_t *json_object_get_map(const json_object *o)
+{
+    if (json_object_is_map(o))
+        return o->u.map;
+    else
+        return NULL;
+}
+
+static inline flexarray_t *json_object_get_array(const json_object *o)
+{
+    if (json_object_is_array(o))
+        return o->u.array;
+    else
+        return NULL;
+}
+
+static inline long json_object_get_integer(const json_object *o)
+{
+    if (json_object_is_integer(o))
+        return o->u.i;
+    else
+        return -1;
+}
+
+
+static json_object *json_object_alloc(libxl__qmp_handler *qmp,
+                                      node_type type)
+{
+    json_object *obj;
+
+    obj = calloc(1, sizeof (json_object));
+    if (obj == NULL) {
+        LIBXL__LOG_ERRNO(qmp->ctx, LIBXL__LOG_ERROR,
+                         "Failed to allocate a json_object");
+        return NULL;
+    }
+
+    obj->type = type;
+
+    if (type == JSON_MAP || type == JSON_ARRAY) {
+        flexarray_t *array = flexarray_make(1, 1);
+        if (array == NULL) {
+            LIBXL__LOG_ERRNO(qmp->ctx, LIBXL__LOG_ERROR,
+                             "Failed to allocate a flexarray");
+            free(obj);
+            return NULL;
+        }
+        if (type == JSON_MAP)
+            obj->u.map = array;
+        else
+            obj->u.array = array;
+    }
+
+    return obj;
+}
+
+static int json_object_append_to(libxl__qmp_handler *qmp,
+                                 json_object *obj, json_object *dst)
+{
+    if (!dst) {
+        LIBXL__LOG(qmp->ctx, LIBXL__LOG_ERROR,
+                   "No parent json object to fill");
+        return -1;
+    }
+
+    switch (dst->type) {
+    case JSON_MAP: {
+        json_map_node *last;
+
+        if (dst->u.map->count == 0) {
+            LIBXL__LOG(qmp->ctx, LIBXL__LOG_ERROR,
+                       "Try to add a value to an empty map (with no key)");
+            return -1;
+        }
+        flexarray_get(dst->u.map, dst->u.map->count - 1, (void**)&last);
+        last->obj = obj;
+        break;
+    }
+    case JSON_ARRAY:
+        if (flexarray_append(dst->u.array, obj) == 2) {
+            LIBXL__LOG_ERRNO(qmp->ctx, LIBXL__LOG_ERROR,
+                             "Failed to grow a flexarray");
+            return -1;
+        }
+        break;
+    default:
+        LIBXL__LOG(qmp->ctx, LIBXL__LOG_ERROR,
+                   "Try append an object is not a map/array (%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_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;
+    }
+    default:
+        break;
+    }
+    free(obj);
+}
+
+static const json_object *json_object_get(const char *key,
+                                          const json_object *o,
+                                          node_type expected_type)
+{
+    flexarray_t *maps = NULL;
+    int index = 0;
+
+    if (json_object_is_map(o)) {
+        json_map_node *node = NULL;
+
+        maps = o->u.map;
+        for (index = 0; index < maps->count; index++) {
+            if (flexarray_get(maps, index, (void**)&node) != 0)
+                return NULL;
+            if (strcmp(key, node->map_key) == 0) {
+                if (expected_type == JSON_ANY
+                    || (node->obj && node->obj->type == expected_type)) {
+                    return node->obj;
+                } else {
+                    return NULL;
+                }
+            }
+        }
+    }
+    return NULL;
+}
+
+/*
+ * JSON callbacks
+ */
+
+static int json_callback_null(void *opaque)
+{
+    libxl__qmp_handler *qmp = opaque;
+    json_object *obj;
+
+    DEBUG_GEN(qmp->g, null);
+
+    if ((obj = json_object_alloc(qmp, JSON_NULL)) == NULL)
+        return 0;
+
+    if (json_object_append_to(qmp, obj, qmp->current) == -1) {
+        json_object_free(qmp, obj);
+        return 0;
+    }
+
+    return 1;
+}
+
+static int json_callback_boolean(void *opaque, int boolean)
+{
+    libxl__qmp_handler *qmp = opaque;
+    json_object *obj;
+
+    DEBUG_GEN_VALUE(qmp->g, bool, boolean);
+
+    if ((obj = json_object_alloc(qmp,
+                                 boolean ? JSON_TRUE : JSON_FALSE)) == NULL)
+        return 0;
+
+    if (json_object_append_to(qmp, obj, qmp->current) == -1) {
+        json_object_free(qmp, obj);
+        return 0;
+    }
+
+    return 1;
+}
+
+static int json_callback_integer(void *opaque, long value)
+{
+    libxl__qmp_handler *qmp = opaque;
+    json_object *obj;
+
+    DEBUG_GEN_VALUE(qmp->g, integer, value);
+
+    if ((obj = json_object_alloc(qmp, JSON_INTEGER)) == NULL)
+        return 0;
+    obj->u.i = value;
+
+    if (json_object_append_to(qmp, obj, qmp->current) == -1) {
+        json_object_free(qmp, obj);
+        return 0;
+    }
+
+    return 1;
+}
+
+static int json_callback_double(void *opaque, double value)
+{
+    libxl__qmp_handler *qmp = opaque;
+    json_object *obj;
+
+    DEBUG_GEN_VALUE(qmp->g, double, value);
+
+    if ((obj = json_object_alloc(qmp, JSON_DOUBLE)) == NULL)
+        return 0;
+    obj->u.d = value;
+
+    if (json_object_append_to(qmp, obj, qmp->current) == -1) {
+        json_object_free(qmp, obj);
+        return 0;
+    }
+
+    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;
+
+    if (t == NULL) {
+        LIBXL__LOG_ERRNO(qmp->ctx, LIBXL__LOG_ERROR, "Failed to allocate");
+        return 0;
+    }
+
+    DEBUG_GEN_STRING(qmp->g, str, len);
+
+    strncpy(t, (const char *) str, len);
+    t[len] = 0;
+
+    if ((obj = json_object_alloc(qmp, JSON_STRING)) == NULL) {
+        free(t);
+        return 0;
+    }
+    obj->u.string = t;
+
+    if (json_object_append_to(qmp, obj, qmp->current) == -1) {
+        json_object_free(qmp, obj);
+        return 0;
+    }
+
+    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;
+
+    if (t == NULL) {
+        LIBXL__LOG_ERRNO(qmp->ctx, LIBXL__LOG_ERROR, "Failed to allocate");
+        return 0;
+    }
+
+    DEBUG_GEN_STRING(qmp->g, str, len);
+
+    strncpy(t, (const char *) str, len);
+    t[len] = 0;
+
+    if (json_object_is_map(obj)) {
+        json_map_node *node = malloc(sizeof (json_map_node));
+        if (node == NULL) {
+            LIBXL__LOG_ERRNO(qmp->ctx, LIBXL__LOG_ERROR,
+                             "Failed to allocate");
+            return 0;
+        }
+
+        node->map_key = t;
+        node->obj = NULL;
+
+        if (flexarray_append(obj->u.map, node) == 2) {
+            LIBXL__LOG_ERRNO(qmp->ctx, LIBXL__LOG_ERROR,
+                             "Failed to grow a flexarray");
+            return 0;
+        }
+    } else {
+        LIBXL__LOG(qmp->ctx, LIBXL__LOG_ERROR,
+                   "Current json object is not a map");
+        return 0;
+    }
+
+    return 1;
+}
+
+static int json_callback_start_map(void *opaque)
+{
+    libxl__qmp_handler *qmp = opaque;
+    json_object *obj = NULL;
+
+    DEBUG_GEN(qmp->g, map_open);
+
+    if ((obj = json_object_alloc(qmp, JSON_MAP)) == NULL)
+        return 0;
+
+    if (qmp->current) {
+        if (json_object_append_to(qmp, obj, qmp->current) == -1) {
+            json_object_free(qmp, obj);
+            return 0;
+        }
+    }
+
+    qmp->current = obj;
+    if (qmp->head == NULL) {
+        qmp->head = obj;
+    }
+
+    return 1;
+}
+
+static int json_callback_end_map(void *opaque)
+{
+    libxl__qmp_handler *qmp = opaque;
+
+    DEBUG_GEN(qmp->g, map_close);
+
+    if (qmp->current) {
+        qmp->current = qmp->current->parent;
+    } else {
+        LIBXL__LOG(qmp->ctx, LIBXL__LOG_ERROR,
+                   "No current json_object, cannot use his parent.");
+        return 0;
+    }
+
+    return 1;
+}
+
+static int json_callback_start_array(void *opaque)
+{
+    libxl__qmp_handler *qmp = opaque;
+    json_object *obj = NULL;
+
+    DEBUG_GEN(qmp->g, array_open);
+
+    if ((obj = json_object_alloc(qmp, JSON_ARRAY)) == NULL)
+        return 0;
+
+    if (qmp->current) {
+        if (json_object_append_to(qmp, obj, qmp->current) == -1) {
+            json_object_free(qmp, obj);
+            return 0;
+        }
+    }
+
+    qmp->current = obj;
+    if (qmp->head == NULL) {
+        qmp->head = obj;
+    }
+
+    return 1;
+}
+
+static int json_callback_end_array(void *opaque)
+{
+    libxl__qmp_handler *qmp = opaque;
+
+    DEBUG_GEN(qmp->g, array_close);
+
+    if (qmp->current) {
+        qmp->current = qmp->current->parent;
+    } else {
+        LIBXL__LOG(qmp->ctx, LIBXL__LOG_ERROR,
+                   "No current json_object, cannot use his parent.");
+        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 int store_serial_port_info(libxl__qmp_handler *qmp,
+                                  const char *chardev,
+                                  int port)
+{
+    libxl__gc gc = LIBXL_INIT_GC(qmp->ctx);
+    char *path = NULL;
+    int ret = 0;
+
+    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, port);
+
+    ret = libxl__xs_write(&gc, XBT_NULL, path, "%s", chardev + 4);
+
+    libxl__free_all(&gc);
+    return ret;
+}
+
+static int register_serials_chardev_callback(libxl__qmp_handler *qmp,
+                                             const json_object *o)
+{
+    const json_object *obj = NULL;
+    const json_object *label = NULL;
+    const char *s = NULL;
+    flexarray_t *array = NULL;
+    int index = 0;
+    const char *chardev = NULL;
+    int ret = 0;
+
+    if ((array = json_object_get_array(o)) == NULL) {
+        return -1;
+    }
+
+    for (index = 0; index < array->count; index++) {
+        if (flexarray_get(array, index, (void**)&obj) != 0)
+            break;
+        label = json_object_get("label", obj, JSON_STRING);
+        s = json_object_get_string(label);
+
+        if (s && strncmp("serial", s, strlen("serial")) == 0) {
+            const json_object *filename = NULL;
+            char *endptr = NULL;
+            int port_number;
+
+            filename = json_object_get("filename", obj, JSON_STRING);
+            chardev = json_object_get_string(filename);
+
+            s += strlen("serial");
+            port_number = strtol(s, &endptr, 10);
+            if (*s == 0 || *endptr != 0) {
+                LIBXL__LOG(qmp->ctx, LIBXL__LOG_ERROR,
+                           "Invalid serial port number: %s", s);
+                return -1;
+            }
+            ret = store_serial_port_info(qmp, chardev, port_number);
+            if (ret) {
+                LIBXL__LOG_ERRNO(qmp->ctx, LIBXL__LOG_ERROR,
+                                 "Failed to store serial port information"
+                                 " in xenstore");
+                return ret;
+            }
+        }
+    };
+
+    return ret;
+}
+
+static int qmp_capabilities_callback(libxl__qmp_handler *qmp,
+                                     const json_object *o)
+{
+    qmp->connected = true;
+
+    return 0;
+}
+
+/*
+ * QMP commands
+ */
+
+static int enable_qmp_capabilities(libxl__qmp_handler *qmp)
+{
+    return qmp_send(qmp, "qmp_capabilities", qmp_capabilities_callback);
+}
+
+/*
+ * Helpers
+ */
+
+static libxl__qmp_message_type qmp_response_type(libxl__qmp_handler *qmp,
+                                                 const json_object *o)
+{
+    flexarray_t *maps = json_object_get_map(o);
+    libxl__qmp_message_type type;
+
+    if (maps) {
+        json_map_node *node = NULL;
+        int index = 0;
+
+        for (index = 0; index < maps->count; index++) {
+            if (flexarray_get(maps, index, (void**)&node) != 0)
+                break;
+            if (libxl__qmp_message_type_from_string(node->map_key, &type) == 0)
+                return type;
+        }
+    }
+
+    return LIBXL__QMP_MESSAGE_TYPE_INVALID;
+}
+
+static callback_id_pair *qmp_get_callback_from_id(libxl__qmp_handler *qmp,
+                                                  const json_object *o)
+{
+    const json_object *id_object = json_object_get("id", o, JSON_INTEGER);
+    int id = -1;
+    callback_id_pair *pp = NULL;
+
+    if (id_object) {
+        id = json_object_get_integer(id_object);
+
+        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,
+                                      const json_object *resp)
+{
+    callback_id_pair *pp = qmp_get_callback_from_id(qmp, resp);
+
+    resp = json_object_get("error", resp, JSON_MAP);
+    resp = json_object_get("desc", resp, JSON_STRING);
+
+    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,
+               "received an error message from QMP server: %s",
+               json_object_get_string(resp));
+}
+
+static int qmp_handle_response(libxl__qmp_handler *qmp,
+                               const json_object *resp)
+{
+    libxl__qmp_message_type type = LIBXL__QMP_MESSAGE_TYPE_INVALID;
+
+    type = qmp_response_type(qmp, resp);
+    LIBXL__LOG(qmp->ctx, LIBXL__LOG_DEBUG,
+               "message type: %s", libxl__qmp_message_type_to_string(type));
+
+    switch (type) {
+    case LIBXL__QMP_MESSAGE_TYPE_QMP:
+        /* On the greeting message from the server, enable QMP capabilities */
+        enable_qmp_capabilities(qmp);
+        break;
+    case LIBXL__QMP_MESSAGE_TYPE_RETURN: {
+        callback_id_pair *pp = qmp_get_callback_from_id(qmp, resp);
+
+        if (pp) {
+            pp->callback(qmp, json_object_get("return", resp, JSON_ANY));
+            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 LIBXL__QMP_MESSAGE_TYPE_ERROR:
+        qmp_handle_error_response(qmp, resp);
+        break;
+    case LIBXL__QMP_MESSAGE_TYPE_EVENT:
+        break;
+    case LIBXL__QMP_MESSAGE_TYPE_INVALID:
+        return -1;
+    }
+    return 0;
+}
+
+static inline yajl_gen_status yajl_gen_asciiz(yajl_gen hand, const char *str)
+{
+    return yajl_gen_string(hand, (const unsigned char *)str, strlen(str));
+}
+
+/*
+ * Handler functions
+ */
+
+static libxl__qmp_handler *qmp_init_handler(libxl_ctx *ctx, uint32_t domid)
+{
+    libxl__qmp_handler *qmp = NULL;
+
+    qmp = calloc(1, sizeof (libxl__qmp_handler));
+    if (qmp == NULL) {
+        LIBXL__LOG_ERRNO(ctx, LIBXL__LOG_ERROR,
+                         "Failed to allocate qmp_handler");
+        return NULL;
+    }
+    qmp->ctx = ctx;
+    qmp->domid = domid;
+    qmp->timeout = 5;
+
+    SIMPLEQ_INIT(&qmp->callback_list);
+
+    return qmp;
+}
+
+static int qmp_open(libxl__qmp_handler *qmp, const char *qmp_socket_path,
+                       int timeout)
+{
+    int ret;
+    int i = 0;
+
+    qmp->qmp_fd = socket(AF_UNIX, SOCK_STREAM | SOCK_NONBLOCK, 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 / 5 <= timeout) && (usleep(200 * 1000) <= 0));
+
+    return ret;
+}
+
+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 */
+    while (1) {
+        fd_set rfds;
+        int ret = 0;
+        struct timeval timeout = {
+            .tv_sec = qmp->timeout,
+            .tv_usec = 0,
+        };
+
+        FD_ZERO(&rfds);
+        FD_SET(qmp->qmp_fd, &rfds);
+
+        ret = select(qmp->qmp_fd + 1, &rfds, NULL, NULL, &timeout);
+        if (ret > 0) {
+            rd = read(qmp->qmp_fd, qmp->buffer, QMP_RECEIVE_BUFFER_SIZE);
+            if (rd > 0) {
+                break;
+            } else if (rd < 0) {
+                LIBXL__LOG_ERRNO(qmp->ctx, LIBXL__LOG_ERROR,
+                                 "Socket read error");
+                return rd;
+            }
+        } else if (ret == 0) {
+            LIBXL__LOG(qmp->ctx, LIBXL__LOG_ERROR, "timeout");
+            return -1;
+        } else if (ret < 0) {
+            LIBXL__LOG_ERRNO(qmp->ctx, LIBXL__LOG_ERROR, "Select error");
+            return -1;
+        }
+    }
+#ifdef DEBUG_RECEIVED
+    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);
+            return -1;
+        }
+
+        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, this avoid doing an extra
+         * turn just for them */
+        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;
+    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_asciiz(hand, "execute");
+    yajl_gen_asciiz(hand, cmd);
+    yajl_gen_asciiz(hand, "id");
+    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,
+                   "Failed to generate a qmp command");
+        return -1;
+    }
+
+    if (callback) {
+        callback_id_pair *elm = malloc(sizeof (callback_id_pair));
+        if (elm == NULL) {
+            LIBXL__LOG_ERRNO(qmp->ctx, LIBXL__LOG_ERROR,
+                             "Failed to allocate a QMP callback");
+            yajl_gen_free(hand);
+            return -1;
+        }
+        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);
+
+    if (libxl_write_exactly(qmp->ctx, qmp->qmp_fd, buf, len,
+                            "QMP command", "QMP socket"))
+        goto error;
+    if (libxl_write_exactly(qmp->ctx, qmp->qmp_fd, "\r\n", 2,
+                            "CRLF", "QMP socket"))
+        goto error;
+
+    yajl_gen_free(hand);
+
+    return qmp->last_id_used;
+
+error:
+    yajl_gen_free(hand);
+    return -1;
+}
+
+static int qmp_synchronous_send(libxl__qmp_handler *qmp, const char *cmd,
+                                qmp_callback_t callback, int ask_timeout)
+{
+    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;
+}
+
+static void qmp_free_handler(libxl__qmp_handler *qmp)
+{
+    free(qmp);
+}
+
+/*
+ * API
+ */
+
+libxl__qmp_handler *libxl__qmp_initialize(libxl_ctx *ctx, uint32_t domid)
+{
+    int ret = 0;
+    libxl__qmp_handler *qmp = NULL;
+    char *qmp_socket;
+    libxl__gc gc = LIBXL_INIT_GC(ctx);
+
+    qmp = qmp_init_handler(ctx, domid);
+
+    qmp_socket = libxl__sprintf(&gc, "%s/qmp-%d",
+                                libxl_run_dir_path(), domid);
+    if ((ret = qmp_open(qmp, qmp_socket, QMP_SOCKET_CONNECT_TIMEOUT)) < 0) {
+        LIBXL__LOG_ERRNO(ctx, LIBXL__LOG_ERROR, "Connection error");
+        qmp_free_handler(qmp);
+        return NULL;
+    }
+
+    LIBXL__LOG(qmp->ctx, LIBXL__LOG_DEBUG, "connected to %s", qmp_socket);
+
+    /* Wait for the response to qmp_capabilities */
+    while (!qmp->connected) {
+        if ((ret = qmp_next(qmp)) < 0) {
+            break;
+        }
+    }
+
+    libxl__free_all(&gc);
+    return qmp;
+}
+
+void libxl__qmp_close(libxl__qmp_handler *qmp)
+{
+    if (!qmp)
+        return;
+    qmp_close(qmp);
+    qmp_free_handler(qmp);
+}
+
+void libxl__qmp_cleanup(libxl__gc *gc, uint32_t domid)
+{
+    libxl_ctx *ctx = libxl__gc_owner(gc);
+    char *qmp_socket;
+
+    qmp_socket = libxl__sprintf(gc,
+                                "%s/qmp-%d", libxl_run_dir_path(), domid);
+    if (unlink(qmp_socket) == -1) {
+        if (errno != ENOENT) {
+            LIBXL__LOG_ERRNO(ctx, LIBXL__LOG_ERROR,
+                             "Failed to remove QMP socket file %s",
+                             qmp_socket);
+        }
+    }
+}
+
+int libxl__qmp_query_serial(libxl__qmp_handler *qmp)
+{
+    return qmp_synchronous_send(qmp, "query-chardev",
+                                register_serials_chardev_callback,
+                                qmp->timeout);
+}
+
+int libxl__qmp_initializations(libxl_ctx *ctx, uint32_t domid)
+{
+    libxl__qmp_handler *qmp = NULL;
+    int ret = 0;
+
+    qmp = libxl__qmp_initialize(ctx, domid);
+    if (!qmp)
+        return -1;
+    if (qmp->connected) {
+        ret = libxl__qmp_query_serial(qmp);
+    }
+    libxl__qmp_close(qmp);
+    return ret;
+}
-- 
1.7.2.5


_______________________________________________
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®.