[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index] [Xen-changelog] [xen master] libxl_qmp: Implementation of libxl__ev_qmp_*
commit a40e753fa1fe2d63dda63badd3807af40e3f7656 Author: Anthony PERARD <anthony.perard@xxxxxxxxxx> AuthorDate: Thu Nov 8 17:38:19 2018 +0000 Commit: Wei Liu <wei.liu2@xxxxxxxxxx> CommitDate: Fri Jan 11 14:57:44 2019 +0000 libxl_qmp: Implementation of libxl__ev_qmp_* This patch implement the API libxl__ev_qmp documented in the previous patch, "libxl: Design of an async API to issue QMP commands to QEMU". Since this API is to interact with QEMU via the QMP protocol, it also implement a QMP client. The specification for the QEMU Machine Protocol (QMP) can be found in the QEMU repository at: https://git.qemu.org/?p=qemu.git;a=blob_plain;f=docs/interop/qmp-spec.txt Signed-off-by: Anthony PERARD <anthony.perard@xxxxxxxxxx> Acked-by: Ian Jackson <ian.jackson@xxxxxxxxxxxxx> [ wei: fix build ] Signed-off-by: Wei Liu <wei.liu2@xxxxxxxxxx> --- tools/libxl/libxl_internal.h | 34 ++ tools/libxl/libxl_qmp.c | 739 +++++++++++++++++++++++++++++++++++++++++++ tools/libxl/libxl_types.idl | 6 + 3 files changed, 779 insertions(+) diff --git a/tools/libxl/libxl_internal.h b/tools/libxl/libxl_internal.h index 5ad44c38dc..c3934e2d8d 100644 --- a/tools/libxl/libxl_internal.h +++ b/tools/libxl/libxl_internal.h @@ -418,6 +418,19 @@ _hidden int libxl__ev_qmp_send(libxl__gc *gc, libxl__ev_qmp *ev, const char *cmd, libxl__json_object *args); _hidden void libxl__ev_qmp_dispose(libxl__gc *gc, libxl__ev_qmp *ev); +typedef enum { + /* initial state */ + qmp_state_disconnected = 1, + /* connected to QMP socket, waiting for greeting message */ + qmp_state_connecting, + /* qmp_capabilities command sent, waiting for reply */ + qmp_state_capability_negotiation, + /* sending user's cmd and waiting for reply */ + qmp_state_waiting_reply, + /* ready to send commands */ + qmp_state_connected, +} libxl__qmp_state; + struct libxl__ev_qmp { /* caller should include this in their own struct */ /* caller must fill these in, and they must all remain valid */ @@ -425,6 +438,27 @@ struct libxl__ev_qmp { libxl_domid domid; libxl__ev_qmp_callback *callback; int payload_fd; /* set to send a fd with the command, -1 otherwise */ + + /* + * remaining fields are private to libxl_ev_qmp_* + */ + + libxl__carefd *cfd; + libxl__ev_fd efd; + libxl__qmp_state state; + int id; + int next_id; /* next id to use */ + /* receive buffer */ + char *rx_buf; + size_t rx_buf_size; /* current allocated size */ + size_t rx_buf_used; /* actual data in the buffer */ + /* sending buffer */ + char *tx_buf; + size_t tx_buf_len; /* tx_buf size */ + size_t tx_buf_off; /* already sent */ + /* The message to send when ready */ + char *msg; + int msg_id; }; diff --git a/tools/libxl/libxl_qmp.c b/tools/libxl/libxl_qmp.c index 73f2202b4f..4e21a504db 100644 --- a/tools/libxl/libxl_qmp.c +++ b/tools/libxl/libxl_qmp.c @@ -75,11 +75,18 @@ # define DEBUG_REPORT_RECEIVED(dom, buf, len) ((void)0) #endif +#ifdef DEBUG_QMP_CLIENT +# define LOG_QMP(f, ...) LOGD(DEBUG, ev->domid, f, ##__VA_ARGS__) +#else +# define LOG_QMP(f, ...) +#endif + /* * QMP types & constant */ #define QMP_RECEIVE_BUFFER_SIZE 4096 +#define QMP_MAX_SIZE_RX_BUF MB(1) #define PCI_PT_QDEV_ID "pci-pt-%02x_%02x.%01x" /* @@ -1307,6 +1314,738 @@ int libxl__qmp_initializations(libxl__gc *gc, uint32_t domid, return ret; } +/* ------------ Implementation of libxl__ev_qmp ---------------- */ + +/* + * Possible internal state compared to qmp_state: + * + * qmp_state External cfd efd id rx_buf* tx_buf* msg* + * disconnected Idle NULL Idle reset free free free + * connecting Active open IN reset used free set + * cap.neg Active open IN|OUT sent used cap_neg set + * cap.neg Active open IN sent used free set + * connected Connected open IN any used free free + * waiting_reply Active open IN|OUT sent used free set + * waiting_reply Active open IN|OUT sent used user's free + * waiting_reply Active open IN sent used free free + * broken[1] none[2] any Active any any any any + * + * [1] When an internal function return an error, it can leave ev_qmp in a + * `broken` state but only if the caller is another internal function. + * That `broken` needs to be cleaned up, e.i. transitionned to the + * `disconnected` state, before the control of ev_qmp is released outsides + * of ev_qmp implementation. + * + * [2] This internal state should not be visible externally, see [1]. + * + * Possible buffers states: + * - receiving buffer: + * free used + * rx_buf NULL NULL or allocated + * rx_buf_size 0 allocation size of `rx_buf` + * rx_buf_used 0 <= rx_buf_size, actual data in the buffer + * - transmitting buffer: + * free used + * tx_buf NULL contains data + * tx_buf_len 0 size of data + * tx_buf_off 0 <= tx_buf_len, data already sent + * - queued user command: + * free set + * msg NULL contains data + * msg_id 0 id assoctiated with the command in `msg` + * + * - Allowed internal state transition: + * disconnected -> connecting + * connection -> capability_negotiation + * capability_negotiation/connected -> waiting_reply + * waiting_reply -> connected + * any -> broken + * broken -> disconnected + * any -> disconnected + * + * The QEMU Machine Protocol (QMP) specification can be found in the QEMU + * repository: + * https://git.qemu.org/?p=qemu.git;a=blob_plain;f=docs/interop/qmp-spec.txt + */ + +/* prototypes */ + +static void qmp_ev_fd_callback(libxl__egc *egc, libxl__ev_fd *ev_fd, + int fd, short events, short revents); +static int qmp_ev_callback_writable(libxl__gc *gc, + libxl__ev_qmp *ev, int fd); +static int qmp_ev_callback_readable(libxl__egc *egc, + libxl__ev_qmp *ev, int fd); +static int qmp_ev_get_next_msg(libxl__egc *egc, libxl__ev_qmp *ev, + libxl__json_object **o_r); +static int qmp_ev_handle_message(libxl__egc *egc, + libxl__ev_qmp *ev, + const libxl__json_object *resp); + +/* helpers */ + +static void qmp_ev_ensure_reading_writing(libxl__gc *gc, libxl__ev_qmp *ev) + /* Update the state of `efd` to match the permited state + * on entry: !disconnected */ +{ + short events = POLLIN; + + if (ev->tx_buf) + events |= POLLOUT; + else if ((ev->state == qmp_state_waiting_reply) && ev->msg) + events |= POLLOUT; + + libxl__ev_fd_modify(gc, &ev->efd, events); +} + +static void qmp_ev_set_state(libxl__gc *gc, libxl__ev_qmp *ev, + libxl__qmp_state new_state) + /* on entry: !broken and !disconnected */ +{ + switch (new_state) { + case qmp_state_disconnected: + break; + case qmp_state_connecting: + assert(ev->state == qmp_state_disconnected); + break; + case qmp_state_capability_negotiation: + assert(ev->state == qmp_state_connecting); + break; + case qmp_state_waiting_reply: + assert(ev->state == qmp_state_capability_negotiation || + ev->state == qmp_state_connected); + break; + case qmp_state_connected: + assert(ev->state == qmp_state_waiting_reply); + break; + } + + ev->state = new_state; + + qmp_ev_ensure_reading_writing(gc, ev); +} + +static void qmp_ev_tx_buf_clear(libxl__ev_qmp *ev) +{ + ev->tx_buf = NULL; + ev->tx_buf_len = 0; + ev->tx_buf_off = 0; +} + +static int qmp_error_class_to_libxl_error_code(libxl__gc *gc, + const char *eclass) +{ + const libxl_enum_string_table *t = libxl_error_string_table; + const char skip[] = "QMP_"; + const size_t skipl = sizeof(skip) - 1; + + /* compare "QMP_GENERIC_ERROR" from libxl_error to "GenericError" + * generated by the QMP server */ + + for (; t->s; t++) { + const char *s = eclass; + const char *se = t->s; + if (strncasecmp(t->s, skip, skipl)) + continue; + se += skipl; + while (*s && *se) { + /* skip underscores */ + if (*se == '_') { + se++; + continue; + } + if (tolower(*s) != tolower(*se)) + break; + s++, se++; + } + if (!*s && !*se) + return t->v; + } + + LOG(ERROR, "Unknown QMP error class '%s'", eclass); + return ERROR_UNKNOWN_QMP_ERROR; +} + +/* Setup connection */ + +static int qmp_ev_connect(libxl__gc *gc, libxl__ev_qmp *ev) + /* disconnected -> connecting but with `msg` free + * on error: broken */ +{ + int fd; + int rc, r; + struct sockaddr_un un; + const char *qmp_socket_path; + + assert(ev->state == qmp_state_disconnected); + + qmp_socket_path = libxl__qemu_qmp_path(gc, ev->domid); + + LOGD(DEBUG, ev->domid, "Connecting to %s", qmp_socket_path); + + libxl__carefd_begin(); + fd = socket(AF_UNIX, SOCK_STREAM, 0); + ev->cfd = libxl__carefd_opened(CTX, fd); + if (!ev->cfd) { + LOGED(ERROR, ev->domid, "socket() failed"); + rc = ERROR_FAIL; + goto out; + } + rc = libxl_fd_set_nonblock(CTX, libxl__carefd_fd(ev->cfd), 1); + if (rc) + goto out; + + rc = libxl__prepare_sockaddr_un(gc, &un, qmp_socket_path, + "QMP socket"); + if (rc) + goto out; + + r = connect(libxl__carefd_fd(ev->cfd), + (struct sockaddr *) &un, sizeof(un)); + if (r && errno != EINPROGRESS) { + LOGED(ERROR, ev->domid, "Failed to connect to QMP socket %s", + qmp_socket_path); + rc = ERROR_FAIL; + goto out; + } + + rc = libxl__ev_fd_register(gc, &ev->efd, qmp_ev_fd_callback, + libxl__carefd_fd(ev->cfd), POLLIN); + if (rc) + goto out; + + qmp_ev_set_state(gc, ev, qmp_state_connecting); + + return 0; + +out: + return rc; +} + +/* QMP FD callbacks */ + +static void qmp_ev_fd_callback(libxl__egc *egc, libxl__ev_fd *ev_fd, + int fd, short events, short revents) + /* On entry, ev_fd is (of course) Active. The ev_qmp may be in any + * state where this is permitted. qmp_ev_fd_callback will do the work + * necessary to make progress, depending on the current state, and make + * the appropriate state transitions and callbacks. */ +{ + libxl__ev_qmp *ev = CONTAINER_OF(ev_fd, *ev, efd); + STATE_AO_GC(ev->ao); + int rc; + + if (revents & (POLLHUP|POLLERR)) { + int r; + int error_val = 0; + socklen_t opt_len = sizeof(error_val); + + r = getsockopt(fd, SOL_SOCKET, SO_ERROR, &error_val, &opt_len); + if (r) + LOGED(ERROR, ev->domid, "getsockopt failed"); + if (!r && error_val) { + errno = error_val; + LOGED(ERROR, ev->domid, "error on QMP socket"); + } else { + LOGD(ERROR, ev->domid, + "received POLLHUP|POLLERR from QMP socket"); + } + rc = ERROR_PROTOCOL_ERROR_QMP; + goto error; + } + + if (revents & ~(POLLIN|POLLOUT)) { + LOGD(ERROR, ev->domid, + "unexpected poll event 0x%x on QMP socket (expected POLLIN " + "and/or POLLOUT)", + revents); + rc = ERROR_FAIL; + goto error; + } + + if (revents & POLLOUT) { + rc = qmp_ev_callback_writable(gc, ev, fd); + if (rc) + goto error; + } + + if (revents & POLLIN) { + rc = qmp_ev_callback_readable(egc, ev, fd); + if (rc < 0) + goto error; + if (rc == 1) { + /* user callback has been called */ + return; + } + } + + return; + +error: + assert(rc); + + LOGD(ERROR, ev->domid, + "Error happened with the QMP connection to QEMU"); + + /* On error, deallocate all private ressources */ + libxl__ev_qmp_dispose(gc, ev); + + /* And tell libxl__ev_qmp user about the error */ + ev->callback(egc, ev, NULL, rc); /* must be last */ +} + +static int qmp_ev_callback_writable(libxl__gc *gc, + libxl__ev_qmp *ev, int fd) + /* on entry: !disconnected + * on return, one of these state transition: + * waiting_reply (with msg set) -> waiting_reply (with msg free) + * tx_buf set -> same state or tx_buf free + * on error: broken */ +{ + int rc; + ssize_t r; + + if (ev->state == qmp_state_waiting_reply) { + if (ev->msg) { + assert(!ev->tx_buf); + ev->tx_buf = ev->msg; + ev->tx_buf_len = strlen(ev->msg); + ev->tx_buf_off = 0; + ev->id = ev->msg_id; + ev->msg = NULL; + ev->msg_id = 0; + } + } + + assert(ev->tx_buf); + + LOG_QMP("sending: '%.*s'", (int)ev->tx_buf_len, ev->tx_buf); + + /* + * We will send a file descriptor associated with a command on the + * first byte of this command. + */ + if (ev->state == qmp_state_waiting_reply && + ev->payload_fd >= 0 && + ev->tx_buf_off == 0) { + + rc = libxl__sendmsg_fds(gc, fd, ev->tx_buf, 1, + 1, &ev->payload_fd, "QMP socket"); + /* Check for EWOULDBLOCK, and return to try again later */ + if (rc == ERROR_NOT_READY) + return 0; + if (rc) + return rc; + ev->tx_buf_off++; + } + + while (ev->tx_buf_off < ev->tx_buf_len) { + ssize_t max_write = ev->tx_buf_len - ev->tx_buf_off; + r = write(fd, ev->tx_buf + ev->tx_buf_off, max_write); + if (r < 0) { + if (errno == EINTR) + continue; + if (errno == EWOULDBLOCK) + break; + LOGED(ERROR, ev->domid, "failed to write to QMP socket"); + return ERROR_FAIL; + } + assert(r > 0 && r <= max_write); + ev->tx_buf_off += r; + } + + if (ev->tx_buf_off == ev->tx_buf_len) + qmp_ev_tx_buf_clear(ev); + + qmp_ev_ensure_reading_writing(gc, ev); + + return 0; +} + +static int qmp_ev_callback_readable(libxl__egc *egc, + libxl__ev_qmp *ev, int fd) + /* + * Return values: + * < 0 libxl error code + * 0 success + * 1 success, but a user callback has been called, + * `ev` should not be used anymore. + * + * This function will update the rx buffer and possibly update + * ev->state: + * connecting -> capability_negotiation + * capability_negotiation -> waiting_reply + * waiting_reply -> connected + * on error: broken + */ +{ + STATE_AO_GC(ev->ao); + int rc; + ssize_t r; + + while (1) { + while (1) { + libxl__json_object *o = NULL; + + /* parse rx buffer to find one json object */ + rc = qmp_ev_get_next_msg(egc, ev, &o); + if (rc == ERROR_NOTFOUND) + break; + else if (rc) + return rc; + + /* Must be last and return when the user callback is called */ + rc = qmp_ev_handle_message(egc, ev, o); + if (rc) + /* returns both rc values -ERROR_* and 1 */ + return rc; + } + + /* Check if the buffer still have space, or increase size */ + if (ev->rx_buf_size - ev->rx_buf_used < QMP_RECEIVE_BUFFER_SIZE) { + size_t newsize = ev->rx_buf_size * 2 + QMP_RECEIVE_BUFFER_SIZE; + + if (newsize > QMP_MAX_SIZE_RX_BUF) { + LOGD(ERROR, ev->domid, + "QMP receive buffer is too big (%zu > %lld)", + newsize, QMP_MAX_SIZE_RX_BUF); + return ERROR_BUFFERFULL; + } + ev->rx_buf_size = newsize; + ev->rx_buf = libxl__realloc(gc, ev->rx_buf, ev->rx_buf_size); + } + + r = read(fd, ev->rx_buf + ev->rx_buf_used, + ev->rx_buf_size - ev->rx_buf_used); + if (r < 0) { + if (errno == EINTR) + continue; + if (errno == EWOULDBLOCK) + break; + LOGED(ERROR, ev->domid, "error reading QMP socket"); + return ERROR_FAIL; + } + + if (r == 0) { + LOGD(ERROR, ev->domid, "Unexpected EOF on QMP socket"); + return ERROR_PROTOCOL_ERROR_QMP; + } + + LOG_QMP("received %ldB: '%.*s'", r, + (int)r, ev->rx_buf + ev->rx_buf_used); + + ev->rx_buf_used += r; + assert(ev->rx_buf_used <= ev->rx_buf_size); + } + + return 0; +} + +/* Handle messages received from QMP server */ + +static int qmp_ev_get_next_msg(libxl__egc *egc, libxl__ev_qmp *ev, + libxl__json_object **o_r) + /* Find a JSON object and store it in o_r. + * return ERROR_NOTFOUND if no object is found. + * + * !disconnected -> same state (with rx buffer updated) + */ +{ + STATE_AO_GC(ev->ao); + size_t len; + char *end = NULL; + const char eom[] = "\r\n"; + const size_t eoml = sizeof(eom) - 1; + libxl__json_object *o = NULL; + + if (!ev->rx_buf_used) + return ERROR_NOTFOUND; + + /* Search for the end of a QMP message: "\r\n" */ + end = memmem(ev->rx_buf, ev->rx_buf_used, eom, eoml); + if (!end) + return ERROR_NOTFOUND; + len = (end - ev->rx_buf) + eoml; + + LOG_QMP("parsing %luB: '%.*s'", len, (int)len, ev->rx_buf); + + /* Replace \r by \0 so that libxl__json_parse can use strlen */ + ev->rx_buf[len - eoml] = '\0'; + o = libxl__json_parse(gc, ev->rx_buf); + + if (!o) { + LOGD(ERROR, ev->domid, "Parse error"); + return ERROR_PROTOCOL_ERROR_QMP; + } + + ev->rx_buf_used -= len; + memmove(ev->rx_buf, ev->rx_buf + len, ev->rx_buf_used); + + LOG_QMP("JSON object received: %s", JSON(o)); + + *o_r = o; + + return 0; +} + +static int qmp_ev_parse_error_messages(libxl__egc *egc, + libxl__ev_qmp *ev, + const libxl__json_object *resp); + +static int qmp_ev_handle_message(libxl__egc *egc, + libxl__ev_qmp *ev, + const libxl__json_object *resp) + /* + * This function will handle every messages sent by the QMP server. + * Return values: + * < 0 libxl error code + * 0 success + * 1 success, but a user callback has been called, + * `ev` should not be used anymore. + * + * Possible state changes: + * connecting -> capability_negotiation + * capability_negotiation -> waiting_reply + * waiting_reply -> waiting_reply/connected + * + * on error: broken + */ +{ + STATE_AO_GC(ev->ao); + int id; + char *buf; + int rc = 0; + const libxl__json_object *o; + const libxl__json_object *response; + libxl__qmp_message_type type = qmp_response_type(resp); + + switch (type) { + case LIBXL__QMP_MESSAGE_TYPE_QMP: + /* greeting message */ + + if (ev->state != qmp_state_connecting) { + LOGD(ERROR, ev->domid, + "Unexpected greeting message received"); + return ERROR_PROTOCOL_ERROR_QMP; + } + + /* Prepare next message to send */ + assert(!ev->tx_buf); + ev->id = ev->next_id++; + buf = qmp_prepare_cmd(gc, "qmp_capabilities", NULL, ev->id); + if (!buf) { + LOGD(ERROR, ev->domid, + "Failed to generate qmp_capabilities command"); + return ERROR_FAIL; + } + ev->tx_buf = buf; + ev->tx_buf_len = strlen(buf); + ev->tx_buf_off = 0; + qmp_ev_set_state(gc, ev, qmp_state_capability_negotiation); + + return 0; + + case LIBXL__QMP_MESSAGE_TYPE_RETURN: + case LIBXL__QMP_MESSAGE_TYPE_ERROR: + /* + * Reply to a command (success/error) or server error + * + * In this cases, we are parsing two possibles responses: + * - success: + * { "return": json-value, "id": int } + * - error: + * { "error": { "class": string, "desc": string }, "id": int } + */ + + o = libxl__json_map_get("id", resp, JSON_INTEGER); + if (!o) { + /* + * If "id" isn't present, an error occur on the server before + * it has read the "id" provided by libxl. + * + * We deliberately squash all errors into + * ERROR_PROTOCOL_ERROR_QMP as qmp_ev_parse_error_messages may + * also return ERROR_QMP_* but those are reserved for errors + * return by the caller's command. + */ + qmp_ev_parse_error_messages(egc, ev, resp); + return ERROR_PROTOCOL_ERROR_QMP; + } + + id = libxl__json_object_get_integer(o); + + if (id != ev->id) { + LOGD(ERROR, ev->domid, + "Message from QEMU with unexpected id %d: %s", + id, JSON(resp)); + return ERROR_PROTOCOL_ERROR_QMP; + } + + switch (ev->state) { + case qmp_state_capability_negotiation: + if (type != LIBXL__QMP_MESSAGE_TYPE_RETURN) { + LOGD(ERROR, ev->domid, + "Error during capability negotiation: %s", + JSON(resp)); + return ERROR_PROTOCOL_ERROR_QMP; + } + qmp_ev_set_state(gc, ev, qmp_state_waiting_reply); + return 0; + case qmp_state_waiting_reply: + if (type == LIBXL__QMP_MESSAGE_TYPE_RETURN) { + response = libxl__json_map_get("return", resp, JSON_ANY); + rc = 0; + } else { + /* error message */ + response = NULL; + rc = qmp_ev_parse_error_messages(egc, ev, resp); + } + qmp_ev_set_state(gc, ev, qmp_state_connected); + ev->callback(egc, ev, response, rc); /* must be last */ + return 1; + default: + LOGD(ERROR, ev->domid, "Unexpected message: %s", JSON(resp)); + return ERROR_PROTOCOL_ERROR_QMP; + } + return 0; + + case LIBXL__QMP_MESSAGE_TYPE_EVENT: + /* Events are ignored */ + return 0; + + case LIBXL__QMP_MESSAGE_TYPE_INVALID: + LOGD(ERROR, ev->domid, "Unexpected message received: %s", + JSON(resp)); + return ERROR_PROTOCOL_ERROR_QMP; + + default: + abort(); + } + + return 0; +} + +static int qmp_ev_parse_error_messages(libxl__egc *egc, + libxl__ev_qmp *ev, + const libxl__json_object *resp) + /* no state change */ +{ + STATE_AO_GC(ev->ao); + int rc; + const char *s; + const libxl__json_object *o; + const libxl__json_object *err; + + /* + * { "error": { "class": string, "desc": string } } + */ + + err = libxl__json_map_get("error", resp, JSON_MAP); + + o = libxl__json_map_get("class", err, JSON_STRING); + if (!o) { + LOGD(ERROR, ev->domid, + "Protocol error: missing 'class' member in error message"); + return ERROR_PROTOCOL_ERROR_QMP; + } + s = libxl__json_object_get_string(o); + if (s) + rc = qmp_error_class_to_libxl_error_code(gc, s); + else + rc = ERROR_PROTOCOL_ERROR_QMP; + + o = libxl__json_map_get("desc", err, JSON_STRING); + if (!o) { + LOGD(ERROR, ev->domid, + "Protocol error: missing 'desc' member in error message"); + return ERROR_PROTOCOL_ERROR_QMP; + } + s = libxl__json_object_get_string(o); + if (s) + LOGD(ERROR, ev->domid, "%s", s); + else + LOGD(ERROR, ev->domid, "Received unexpected error: %s", + JSON(resp)); + return rc; +} + +/* + * libxl__ev_qmp_* + */ + +void libxl__ev_qmp_init(libxl__ev_qmp *ev) + /* disconnected -> disconnected */ +{ + /* Start with an message ID that is obviously generated by libxl + * "xlq\0" */ + ev->next_id = 0x786c7100; + + ev->cfd = NULL; + libxl__ev_fd_init(&ev->efd); + ev->state = qmp_state_disconnected; + ev->id = 0; + + ev->rx_buf = NULL; + ev->rx_buf_size = ev->rx_buf_used = 0; + qmp_ev_tx_buf_clear(ev); + + ev->msg = NULL; + ev->msg_id = 0; +} + +int libxl__ev_qmp_send(libxl__gc *unused_gc, libxl__ev_qmp *ev, + const char *cmd, libxl__json_object *args) + /* disconnected -> connecting + * connected -> waiting_reply (with msg set) + * on error: disconnected */ +{ + STATE_AO_GC(ev->ao); + int rc; + + LOGD(DEBUG, ev->domid, " ev %p, cmd '%s'", ev, cmd); + + assert(ev->state == qmp_state_disconnected || + ev->state == qmp_state_connected); + assert(cmd); + + /* Connect to QEMU if not already connected */ + if (ev->state == qmp_state_disconnected) { + rc = qmp_ev_connect(gc, ev); + if (rc) + goto error; + } + + /* Prepare user command */ + ev->msg_id = ev->next_id++; + ev->msg = qmp_prepare_cmd(gc, cmd, args, ev->msg_id); + if (!ev->msg) { + LOGD(ERROR, ev->domid, "Failed to generate caller's command %s", + cmd); + rc = ERROR_FAIL; + goto error; + } + if (ev->state == qmp_state_connected) { + qmp_ev_set_state(gc, ev, qmp_state_waiting_reply); + } + + return 0; + +error: + libxl__ev_qmp_dispose(gc, ev); + return rc; +} + +void libxl__ev_qmp_dispose(libxl__gc *gc, libxl__ev_qmp *ev) + /* * -> disconnected */ +{ + LOGD(DEBUG, ev->domid, " ev %p", ev); + + libxl__ev_fd_deregister(gc, &ev->efd); + libxl__carefd_close(ev->cfd); + + libxl__ev_qmp_init(ev); +} + /* * Local variables: * mode: C diff --git a/tools/libxl/libxl_types.idl b/tools/libxl/libxl_types.idl index 141c46e42a..212b00a677 100644 --- a/tools/libxl/libxl_types.idl +++ b/tools/libxl/libxl_types.idl @@ -69,6 +69,12 @@ libxl_error = Enumeration("error", [ (-23, "NOTFOUND"), (-24, "DOMAIN_DESTROYED"), # Target domain ceased to exist during op (-25, "FEATURE_REMOVED"), # For functionality that has been removed + (-26, "PROTOCOL_ERROR_QMP"), + (-27, "UNKNOWN_QMP_ERROR"), + (-28, "QMP_GENERIC_ERROR"), # unspecified qmp error + (-29, "QMP_COMMAND_NOT_FOUND"), # the requested command has not been found + (-30, "QMP_DEVICE_NOT_ACTIVE"), # a device has failed to be become active + (-31, "QMP_DEVICE_NOT_FOUND"), # the requested device has not been found ], value_namespace = "") libxl_domain_type = Enumeration("domain_type", [ -- generated by git-patchbot for /home/xen/git/xen.git#master _______________________________________________ Xen-changelog mailing list Xen-changelog@xxxxxxxxxxxxxxxxxxxx https://lists.xenproject.org/xen-changelog
|
Lists.xenproject.org is hosted with RackSpace, monitoring our |