1 /*
2 * Copyright (C) 2011 Citrix Ltd.
3 * Author Anthony PERARD <anthony.perard@citrix.com>
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU Lesser General Public License as published
7 * by the Free Software Foundation; version 2.1 only. with the special
8 * exception on linking described in file LICENSE.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU Lesser General Public License for more details.
14 */
15
16 /*
17 * This file implement a client for QMP (QEMU Monitor Protocol). For the
18 * Specification, see in the QEMU repository.
19 *
20 * WARNING - Do not trust QEMU when writing codes for new commands or when
21 * improving the client code.
22 */
23
24 /*
25 * Logic used to send command to QEMU
26 *
27 * qmp_open():
28 * Will open a socket and connect to QEMU.
29 *
30 * qmp_next():
31 * Will read data sent by QEMU and then call qmp_handle_response() once a
32 * complete QMP message is received.
33 * The function return on timeout/error or once every data received as been
34 * processed.
35 *
36 * qmp_handle_response()
37 * This process json messages received from QEMU and update different list and
38 * may call callback function.
39 * `libxl__qmp_handler.wait_for_id` is reset once a message with this ID is
40 * processed.
41 * `libxl__qmp_handler.callback_list`: list with ID of command sent and
42 * optional assotiated callback function. The return value of a callback is
43 * set in context.
44 *
45 * qmp_send():
46 * Simply prepare a QMP command and send it to QEMU.
47 * It also add a `struct callback_id_pair` on the
48 * `libxl__qmp_handler.callback_list` via qmp_send_prepare().
49 *
50 * qmp_synchronous_send():
51 * This function calls qmp_send(), then wait for QEMU to reply to the command.
52 * The wait is done by calling qmp_next() over and over again until either
53 * there is a response for the command or there is an error.
54 *
55 * An ID can be set for each QMP command, this is set into
56 * `libxl__qmp_handler.wait_for_id`. qmp_next will check every response's ID
57 * again this field and change the value of the field once the ID is found.
58 */
59
60 #include "libxl_osdeps.h" /* must come before any other headers */
61
62 #include <sys/un.h>
63
64 #include <yajl/yajl_gen.h>
65
66 #include "_libxl_list.h"
67 #include "libxl_internal.h"
68
69 /* #define DEBUG_RECEIVED */
70
71 #ifdef DEBUG_RECEIVED
72 # define DEBUG_REPORT_RECEIVED(dom, buf, len) \
73 LOGD(DEBUG, dom, "received: '%.*s'", len, buf)
74 #else
75 # define DEBUG_REPORT_RECEIVED(dom, buf, len) ((void)0)
76 #endif
77
78 #ifdef DEBUG_QMP_CLIENT
79 # define LOG_QMP(f, ...) LOGD(DEBUG, ev->domid, f, ##__VA_ARGS__)
80 #else
81 # define LOG_QMP(f, ...)
82 #endif
83
84 /*
85 * QMP types & constant
86 */
87
88 #define QMP_RECEIVE_BUFFER_SIZE 4096
89 #define QMP_MAX_SIZE_RX_BUF MB(1)
90
91 /*
92 * qmp_callback_t is call whenever a message from QMP contain the "id"
93 * associated with the callback.
94 * "tree" contain the JSON tree that is in "return" of a QMP message. If QMP
95 * sent an error message, "tree" will be NULL.
96 */
97 typedef int (*qmp_callback_t)(libxl__qmp_handler *qmp,
98 const libxl__json_object *tree,
99 void *opaque);
100
101 typedef struct qmp_request_context {
102 int rc;
103 } qmp_request_context;
104
105 typedef struct callback_id_pair {
106 int id;
107 qmp_callback_t callback;
108 void *opaque;
109 qmp_request_context *context;
110 LIBXL_STAILQ_ENTRY(struct callback_id_pair) next;
111 } callback_id_pair;
112
113 struct libxl__qmp_handler {
114 int qmp_fd;
115 bool connected;
116 time_t timeout;
117 /* wait_for_id will be used by the synchronous send function */
118 int wait_for_id;
119
120 char buffer[QMP_RECEIVE_BUFFER_SIZE + 1];
121
122 libxl_ctx *ctx;
123 uint32_t domid;
124
125 int last_id_used;
126 LIBXL_STAILQ_HEAD(callback_list, callback_id_pair) callback_list;
127 struct {
128 int major;
129 int minor;
130 int micro;
131 } version;
132 };
133
134 static int qmp_send(libxl__qmp_handler *qmp,
135 const char *cmd, libxl__json_object *args,
136 qmp_callback_t callback, void *opaque,
137 qmp_request_context *context);
138
139 static const int QMP_SOCKET_CONNECT_TIMEOUT = 5;
140
141 /*
142 * QMP callbacks functions
143 */
144
qmp_capabilities_callback(libxl__qmp_handler * qmp,const libxl__json_object * o,void * unused)145 static int qmp_capabilities_callback(libxl__qmp_handler *qmp,
146 const libxl__json_object *o, void *unused)
147 {
148 qmp->connected = true;
149
150 return 0;
151 }
152
153 /*
154 * QMP commands
155 */
156
enable_qmp_capabilities(libxl__qmp_handler * qmp)157 static int enable_qmp_capabilities(libxl__qmp_handler *qmp)
158 {
159 return qmp_send(qmp, "qmp_capabilities", NULL,
160 qmp_capabilities_callback, NULL, NULL);
161 }
162
163 /*
164 * Helpers
165 */
166
qmp_response_type(const libxl__json_object * o)167 static libxl__qmp_message_type qmp_response_type(const libxl__json_object *o)
168 {
169 libxl__qmp_message_type type;
170 libxl__json_map_node *node = NULL;
171 int i = 0;
172
173 for (i = 0; (node = libxl__json_map_node_get(o, i)); i++) {
174 if (libxl__qmp_message_type_from_string(node->map_key, &type) == 0)
175 return type;
176 }
177
178 return LIBXL__QMP_MESSAGE_TYPE_INVALID;
179 }
180
qmp_get_callback_from_id(libxl__qmp_handler * qmp,const libxl__json_object * o)181 static callback_id_pair *qmp_get_callback_from_id(libxl__qmp_handler *qmp,
182 const libxl__json_object *o)
183 {
184 const libxl__json_object *id_object = libxl__json_map_get("id", o,
185 JSON_INTEGER);
186 int id = -1;
187 callback_id_pair *pp = NULL;
188
189 if (id_object) {
190 id = libxl__json_object_get_integer(id_object);
191
192 LIBXL_STAILQ_FOREACH(pp, &qmp->callback_list, next) {
193 if (pp->id == id) {
194 return pp;
195 }
196 }
197 }
198 return NULL;
199 }
200
qmp_handle_error_response(libxl__gc * gc,libxl__qmp_handler * qmp,const libxl__json_object * resp)201 static void qmp_handle_error_response(libxl__gc *gc, libxl__qmp_handler *qmp,
202 const libxl__json_object *resp)
203 {
204 callback_id_pair *pp = qmp_get_callback_from_id(qmp, resp);
205
206 resp = libxl__json_map_get("error", resp, JSON_MAP);
207 resp = libxl__json_map_get("desc", resp, JSON_STRING);
208
209 if (pp) {
210 if (pp->callback) {
211 int rc = pp->callback(qmp, NULL, pp->opaque);
212 if (pp->context) {
213 pp->context->rc = rc;
214 }
215 }
216 if (pp->id == qmp->wait_for_id) {
217 /* tell that the id have been processed */
218 qmp->wait_for_id = 0;
219 }
220 LIBXL_STAILQ_REMOVE(&qmp->callback_list, pp, callback_id_pair, next);
221 free(pp);
222 }
223
224 LOGD(ERROR, qmp->domid, "received an error message from QMP server: %s",
225 libxl__json_object_get_string(resp));
226 }
227
qmp_handle_response(libxl__gc * gc,libxl__qmp_handler * qmp,const libxl__json_object * resp)228 static int qmp_handle_response(libxl__gc *gc, libxl__qmp_handler *qmp,
229 const libxl__json_object *resp)
230 {
231 libxl__qmp_message_type type = LIBXL__QMP_MESSAGE_TYPE_INVALID;
232
233 type = qmp_response_type(resp);
234 LOGD(DEBUG, qmp->domid, "message type: %s", libxl__qmp_message_type_to_string(type));
235
236 switch (type) {
237 case LIBXL__QMP_MESSAGE_TYPE_QMP: {
238 const libxl__json_object *o;
239 o = libxl__json_map_get("QMP", resp, JSON_MAP);
240 o = libxl__json_map_get("version", o, JSON_MAP);
241 o = libxl__json_map_get("qemu", o, JSON_MAP);
242 qmp->version.major = libxl__json_object_get_integer(
243 libxl__json_map_get("major", o, JSON_INTEGER));
244 qmp->version.minor = libxl__json_object_get_integer(
245 libxl__json_map_get("minor", o, JSON_INTEGER));
246 qmp->version.micro = libxl__json_object_get_integer(
247 libxl__json_map_get("micro", o, JSON_INTEGER));
248 LOGD(DEBUG, qmp->domid, "QEMU version: %d.%d.%d",
249 qmp->version.major, qmp->version.minor, qmp->version.micro);
250 /* On the greeting message from the server, enable QMP capabilities */
251 return enable_qmp_capabilities(qmp);
252 }
253 case LIBXL__QMP_MESSAGE_TYPE_RETURN: {
254 callback_id_pair *pp = qmp_get_callback_from_id(qmp, resp);
255
256 if (pp) {
257 if (pp->callback) {
258 int rc = pp->callback(qmp,
259 libxl__json_map_get("return", resp, JSON_ANY),
260 pp->opaque);
261 if (pp->context) {
262 pp->context->rc = rc;
263 }
264 }
265 if (pp->id == qmp->wait_for_id) {
266 /* tell that the id have been processed */
267 qmp->wait_for_id = 0;
268 }
269 LIBXL_STAILQ_REMOVE(&qmp->callback_list, pp, callback_id_pair,
270 next);
271 free(pp);
272 }
273 return 0;
274 }
275 case LIBXL__QMP_MESSAGE_TYPE_ERROR:
276 qmp_handle_error_response(gc, qmp, resp);
277 return -1;
278 case LIBXL__QMP_MESSAGE_TYPE_EVENT:
279 return 0;
280 case LIBXL__QMP_MESSAGE_TYPE_INVALID:
281 return -1;
282 }
283 return 0;
284 }
285
286 /*
287 * return values:
288 * < 0 if qemu's version < asked version
289 * = 0 if qemu's version == asked version
290 * > 0 if qemu's version > asked version
291 */
qmp_ev_qemu_compare_version(libxl__ev_qmp * ev,int major,int minor,int micro)292 static int qmp_ev_qemu_compare_version(libxl__ev_qmp *ev, int major,
293 int minor, int micro)
294 {
295 #define CHECK_VERSION(level) do { \
296 if (ev->qemu_version.level > (level)) return +1; \
297 if (ev->qemu_version.level < (level)) return -1; \
298 } while (0)
299
300 CHECK_VERSION(major);
301 CHECK_VERSION(minor);
302 CHECK_VERSION(micro);
303
304 #undef CHECK_VERSION
305
306 return 0;
307 }
308
309 /*
310 * Handler functions
311 */
312
qmp_init_handler(libxl__gc * gc,uint32_t domid)313 static libxl__qmp_handler *qmp_init_handler(libxl__gc *gc, uint32_t domid)
314 {
315 libxl__qmp_handler *qmp = NULL;
316
317 qmp = calloc(1, sizeof (libxl__qmp_handler));
318 if (qmp == NULL) {
319 LOGED(ERROR, domid, "Failed to allocate qmp_handler");
320 return NULL;
321 }
322 qmp->ctx = CTX;
323 qmp->domid = domid;
324 qmp->timeout = 5;
325
326 LIBXL_STAILQ_INIT(&qmp->callback_list);
327
328 return qmp;
329 }
330
qmp_open(libxl__qmp_handler * qmp,const char * qmp_socket_path,int timeout)331 static int qmp_open(libxl__qmp_handler *qmp, const char *qmp_socket_path,
332 int timeout)
333 {
334 GC_INIT(qmp->ctx);
335 int ret = -1;
336 int i = 0;
337 struct sockaddr_un addr;
338
339 qmp->qmp_fd = socket(AF_UNIX, SOCK_STREAM, 0);
340 if (qmp->qmp_fd < 0) {
341 goto out;
342 }
343 ret = libxl_fd_set_nonblock(qmp->ctx, qmp->qmp_fd, 1);
344 if (ret) {
345 ret = -1;
346 goto out;
347 }
348 ret = libxl_fd_set_cloexec(qmp->ctx, qmp->qmp_fd, 1);
349 if (ret) {
350 ret = -1;
351 goto out;
352 }
353
354 ret = libxl__prepare_sockaddr_un(gc, &addr, qmp_socket_path, "QMP socket");
355 if (ret)
356 goto out;
357
358 do {
359 ret = connect(qmp->qmp_fd, (struct sockaddr *) &addr, sizeof(addr));
360 if (ret == 0)
361 break;
362 if (errno == ENOENT || errno == ECONNREFUSED) {
363 /* ENOENT : Socket may not have shown up yet
364 * ECONNREFUSED : Leftover socket hasn't been removed yet */
365 continue;
366 }
367 ret = -1;
368 goto out;
369 } while ((++i / 5 <= timeout) && (usleep(200 * 1000) <= 0));
370
371 out:
372 if (ret == -1 && qmp->qmp_fd > -1) close(qmp->qmp_fd);
373
374 GC_FREE;
375 return ret;
376 }
377
qmp_close(libxl__qmp_handler * qmp)378 static void qmp_close(libxl__qmp_handler *qmp)
379 {
380 callback_id_pair *pp = NULL;
381 callback_id_pair *tmp = NULL;
382
383 close(qmp->qmp_fd);
384 LIBXL_STAILQ_FOREACH(pp, &qmp->callback_list, next) {
385 free(tmp);
386 tmp = pp;
387 }
388 free(tmp);
389 }
390
qmp_next(libxl__gc * gc,libxl__qmp_handler * qmp)391 static int qmp_next(libxl__gc *gc, libxl__qmp_handler *qmp)
392 {
393 ssize_t rd;
394 char *s = NULL;
395 char *s_end = NULL;
396
397 char *incomplete = NULL;
398 size_t incomplete_size = 0;
399 int rc = 0;
400
401 do {
402 fd_set rfds;
403 int ret = 0;
404 struct timeval timeout = {
405 .tv_sec = qmp->timeout,
406 .tv_usec = 0,
407 };
408
409 FD_ZERO(&rfds);
410 FD_SET(qmp->qmp_fd, &rfds);
411
412 ret = select(qmp->qmp_fd + 1, &rfds, NULL, NULL, &timeout);
413 if (ret == 0) {
414 LOGD(ERROR, qmp->domid, "timeout");
415 return -1;
416 } else if (ret < 0) {
417 if (errno == EINTR)
418 continue;
419 LOGED(ERROR, qmp->domid, "Select error");
420 return -1;
421 }
422
423 rd = read(qmp->qmp_fd, qmp->buffer, QMP_RECEIVE_BUFFER_SIZE);
424 if (rd == 0) {
425 LOGD(ERROR, qmp->domid, "Unexpected end of socket");
426 return -1;
427 } else if (rd < 0) {
428 LOGED(ERROR, qmp->domid, "Socket read error");
429 return rd;
430 }
431 qmp->buffer[rd] = '\0';
432
433 DEBUG_REPORT_RECEIVED(qmp->domid, qmp->buffer, (int)rd);
434
435 if (incomplete) {
436 size_t current_pos = s - incomplete;
437 incomplete = libxl__realloc(gc, incomplete,
438 incomplete_size + rd + 1);
439 strncat(incomplete + incomplete_size, qmp->buffer, rd);
440 s = incomplete + current_pos;
441 incomplete_size += rd;
442 s_end = incomplete + incomplete_size;
443 } else {
444 incomplete = libxl__strndup(gc, qmp->buffer, rd);
445 incomplete_size = rd;
446 s = incomplete;
447 s_end = s + rd;
448 rd = 0;
449 }
450
451 do {
452 char *end = NULL;
453
454 end = strstr(s, "\r\n");
455 if (end) {
456 libxl__json_object *o = NULL;
457
458 *end = '\0';
459
460 o = libxl__json_parse(gc, s);
461
462 if (o) {
463 rc = qmp_handle_response(gc, qmp, o);
464 } else {
465 LOGD(ERROR, qmp->domid, "Parse error of : %s", s);
466 return -1;
467 }
468
469 s = end + 2;
470 } else {
471 break;
472 }
473 } while (s < s_end);
474 } while (s < s_end);
475
476 return rc;
477 }
478
qmp_prepare_cmd(libxl__gc * gc,const char * cmd,const libxl__json_object * args,int id)479 static char *qmp_prepare_cmd(libxl__gc *gc, const char *cmd,
480 const libxl__json_object *args,
481 int id)
482 {
483 yajl_gen hand = NULL;
484 /* memory for 'buf' is owned by 'hand' */
485 const unsigned char *buf;
486 libxl_yajl_length len;
487 yajl_gen_status s;
488 char *ret = NULL;
489
490 hand = libxl_yajl_gen_alloc(NULL);
491
492 if (!hand) {
493 return NULL;
494 }
495
496 #if HAVE_YAJL_V2
497 /* Disable beautify for data sent to QEMU */
498 yajl_gen_config(hand, yajl_gen_beautify, 0);
499 #endif
500
501 yajl_gen_map_open(hand);
502 libxl__yajl_gen_asciiz(hand, "execute");
503 libxl__yajl_gen_asciiz(hand, cmd);
504 libxl__yajl_gen_asciiz(hand, "id");
505 yajl_gen_integer(hand, id);
506 if (args) {
507 libxl__yajl_gen_asciiz(hand, "arguments");
508 libxl__json_object_to_yajl_gen(gc, hand, args);
509 }
510 yajl_gen_map_close(hand);
511
512 s = yajl_gen_get_buf(hand, &buf, &len);
513
514 if (s != yajl_gen_status_ok)
515 goto out;
516
517 ret = libxl__sprintf(gc, "%*.*s\r\n", (int)len, (int)len, buf);
518
519 out:
520 yajl_gen_free(hand);
521 return ret;
522 }
523
qmp_send_prepare(libxl__gc * gc,libxl__qmp_handler * qmp,const char * cmd,libxl__json_object * args,qmp_callback_t callback,void * opaque,qmp_request_context * context)524 static char *qmp_send_prepare(libxl__gc *gc, libxl__qmp_handler *qmp,
525 const char *cmd, libxl__json_object *args,
526 qmp_callback_t callback, void *opaque,
527 qmp_request_context *context)
528 {
529 char *buf;
530 callback_id_pair *elm;
531
532 buf = qmp_prepare_cmd(gc, cmd, args, ++qmp->last_id_used);
533
534 if (!buf) {
535 LOGD(ERROR, qmp->domid, "Failed to generate a qmp command");
536 goto out;
537 }
538
539 elm = malloc(sizeof (callback_id_pair));
540 if (elm == NULL) {
541 LOGED(ERROR, qmp->domid, "Failed to allocate a QMP callback");
542 goto out;
543 }
544 elm->id = qmp->last_id_used;
545 elm->callback = callback;
546 elm->opaque = opaque;
547 elm->context = context;
548 LIBXL_STAILQ_INSERT_TAIL(&qmp->callback_list, elm, next);
549
550 LOGD(DEBUG, qmp->domid, "next qmp command: '%s'", buf);
551
552 out:
553 return buf;
554 }
555
qmp_send(libxl__qmp_handler * qmp,const char * cmd,libxl__json_object * args,qmp_callback_t callback,void * opaque,qmp_request_context * context)556 static int qmp_send(libxl__qmp_handler *qmp,
557 const char *cmd, libxl__json_object *args,
558 qmp_callback_t callback, void *opaque,
559 qmp_request_context *context)
560 {
561 char *buf = NULL;
562 int rc = -1;
563 GC_INIT(qmp->ctx);
564
565 buf = qmp_send_prepare(gc, qmp, cmd, args, callback, opaque, context);
566
567 if (buf == NULL) {
568 goto out;
569 }
570
571 if (libxl_write_exactly(qmp->ctx, qmp->qmp_fd, buf, strlen(buf),
572 "QMP command", "QMP socket"))
573 goto out;
574
575 rc = qmp->last_id_used;
576 out:
577 GC_FREE;
578 return rc;
579 }
580
qmp_synchronous_send(libxl__qmp_handler * qmp,const char * cmd,libxl__json_object * args,qmp_callback_t callback,void * opaque,int ask_timeout)581 static int qmp_synchronous_send(libxl__qmp_handler *qmp, const char *cmd,
582 libxl__json_object *args,
583 qmp_callback_t callback, void *opaque,
584 int ask_timeout)
585 {
586 int id = 0;
587 int ret = 0;
588 GC_INIT(qmp->ctx);
589 qmp_request_context context = { .rc = 0 };
590
591 id = qmp_send(qmp, cmd, args, callback, opaque, &context);
592 if (id <= 0) {
593 return ERROR_FAIL;
594 }
595 qmp->wait_for_id = id;
596
597 while (qmp->wait_for_id == id) {
598 if ((ret = qmp_next(gc, qmp)) < 0) {
599 break;
600 }
601 }
602
603 if (qmp->wait_for_id != id && ret == 0) {
604 ret = context.rc;
605 }
606
607 GC_FREE;
608
609 return ret;
610 }
611
qmp_free_handler(libxl__qmp_handler * qmp)612 static void qmp_free_handler(libxl__qmp_handler *qmp)
613 {
614 free(qmp);
615 }
616
617 /*
618 * QMP Parameters Helpers
619 */
qmp_parameters_common_add(libxl__gc * gc,libxl__json_object ** param,const char * name,libxl__json_object * obj)620 static void qmp_parameters_common_add(libxl__gc *gc,
621 libxl__json_object **param,
622 const char *name,
623 libxl__json_object *obj)
624 {
625 libxl__json_map_node *arg = NULL;
626
627 if (!*param) {
628 *param = libxl__json_object_alloc(gc, JSON_MAP);
629 }
630
631 GCNEW(arg);
632
633 arg->map_key = libxl__strdup(gc, name);
634 arg->obj = obj;
635
636 flexarray_append((*param)->u.map, arg);
637 }
638
libxl__qmp_param_add_string(libxl__gc * gc,libxl__json_object ** param,const char * name,const char * argument)639 void libxl__qmp_param_add_string(libxl__gc *gc,
640 libxl__json_object **param,
641 const char *name, const char *argument)
642 {
643 libxl__json_object *obj;
644
645 obj = libxl__json_object_alloc(gc, JSON_STRING);
646 obj->u.string = libxl__strdup(gc, argument);
647
648 qmp_parameters_common_add(gc, param, name, obj);
649 }
650
libxl__qmp_param_add_bool(libxl__gc * gc,libxl__json_object ** param,const char * name,bool b)651 void libxl__qmp_param_add_bool(libxl__gc *gc,
652 libxl__json_object **param,
653 const char *name, bool b)
654 {
655 libxl__json_object *obj;
656
657 obj = libxl__json_object_alloc(gc, JSON_BOOL);
658 obj->u.b = b;
659 qmp_parameters_common_add(gc, param, name, obj);
660 }
661
libxl__qmp_param_add_integer(libxl__gc * gc,libxl__json_object ** param,const char * name,const int i)662 void libxl__qmp_param_add_integer(libxl__gc *gc,
663 libxl__json_object **param,
664 const char *name, const int i)
665 {
666 libxl__json_object *obj;
667
668 obj = libxl__json_object_alloc(gc, JSON_INTEGER);
669 obj->u.i = i;
670
671 qmp_parameters_common_add(gc, param, name, obj);
672 }
673
674 /*
675 * API
676 */
677
libxl__qmp_initialize(libxl__gc * gc,uint32_t domid)678 libxl__qmp_handler *libxl__qmp_initialize(libxl__gc *gc, uint32_t domid)
679 {
680 int ret = 0;
681 libxl__qmp_handler *qmp = NULL;
682 char *qmp_socket;
683
684 qmp = qmp_init_handler(gc, domid);
685 if (!qmp) return NULL;
686
687 qmp_socket = GCSPRINTF("%s/qmp-libxl-%d", libxl__run_dir_path(), domid);
688 if ((ret = qmp_open(qmp, qmp_socket, QMP_SOCKET_CONNECT_TIMEOUT)) < 0) {
689 LOGED(ERROR, domid, "Connection error");
690 qmp_free_handler(qmp);
691 return NULL;
692 }
693
694 LOGD(DEBUG, domid, "connected to %s", qmp_socket);
695
696 /* Wait for the response to qmp_capabilities */
697 while (!qmp->connected) {
698 if ((ret = qmp_next(gc, qmp)) < 0) {
699 break;
700 }
701 }
702
703 if (!qmp->connected) {
704 LOGD(ERROR, domid, "Failed to connect to QMP");
705 libxl__qmp_close(qmp);
706 return NULL;
707 }
708 return qmp;
709 }
710
libxl__qmp_close(libxl__qmp_handler * qmp)711 void libxl__qmp_close(libxl__qmp_handler *qmp)
712 {
713 if (!qmp)
714 return;
715 qmp_close(qmp);
716 qmp_free_handler(qmp);
717 }
718
libxl__qmp_cleanup(libxl__gc * gc,uint32_t domid)719 void libxl__qmp_cleanup(libxl__gc *gc, uint32_t domid)
720 {
721 char *qmp_socket;
722
723 qmp_socket = GCSPRINTF("%s/qmp-libxl-%d", libxl__run_dir_path(), domid);
724 if (unlink(qmp_socket) == -1) {
725 if (errno != ENOENT) {
726 LOGED(ERROR, domid, "Failed to remove QMP socket file %s", qmp_socket);
727 }
728 }
729
730 qmp_socket = GCSPRINTF("%s/qmp-libxenstat-%d", libxl__run_dir_path(), domid);
731 if (unlink(qmp_socket) == -1) {
732 if (errno != ENOENT) {
733 LOGED(ERROR, domid, "Failed to remove QMP socket file %s", qmp_socket);
734 }
735 }
736 }
737
qmp_run_command(libxl__gc * gc,int domid,const char * cmd,libxl__json_object * args,qmp_callback_t callback,void * opaque)738 static int qmp_run_command(libxl__gc *gc, int domid,
739 const char *cmd, libxl__json_object *args,
740 qmp_callback_t callback, void *opaque)
741 {
742 libxl__qmp_handler *qmp = NULL;
743 int rc = 0;
744
745 qmp = libxl__qmp_initialize(gc, domid);
746 if (!qmp)
747 return ERROR_FAIL;
748
749 rc = qmp_synchronous_send(qmp, cmd, args, callback, opaque, qmp->timeout);
750
751 libxl__qmp_close(qmp);
752 return rc;
753 }
754
libxl__qmp_restore(libxl__gc * gc,int domid,const char * state_file)755 int libxl__qmp_restore(libxl__gc *gc, int domid, const char *state_file)
756 {
757 libxl__json_object *args = NULL;
758
759 libxl__qmp_param_add_string(gc, &args, "filename", state_file);
760
761 return qmp_run_command(gc, domid, "xen-load-devices-state", args,
762 NULL, NULL);
763 }
764
libxl__qmp_resume(libxl__gc * gc,int domid)765 int libxl__qmp_resume(libxl__gc *gc, int domid)
766 {
767 return qmp_run_command(gc, domid, "cont", NULL, NULL, NULL);
768 }
769
libxl__qmp_nbd_server_start(libxl__gc * gc,int domid,const char * host,const char * port)770 int libxl__qmp_nbd_server_start(libxl__gc *gc, int domid,
771 const char *host, const char *port)
772 {
773 libxl__json_object *args = NULL;
774 libxl__json_object *addr = NULL;
775 libxl__json_object *data = NULL;
776
777 /* 'addr': {
778 * 'type': 'inet',
779 * 'data': {
780 * 'host': '$nbd_host',
781 * 'port': '$nbd_port'
782 * }
783 * }
784 */
785 libxl__qmp_param_add_string(gc, &data, "host", host);
786 libxl__qmp_param_add_string(gc, &data, "port", port);
787
788 libxl__qmp_param_add_string(gc, &addr, "type", "inet");
789 qmp_parameters_common_add(gc, &addr, "data", data);
790
791 qmp_parameters_common_add(gc, &args, "addr", addr);
792
793 return qmp_run_command(gc, domid, "nbd-server-start", args, NULL, NULL);
794 }
795
libxl__qmp_nbd_server_add(libxl__gc * gc,int domid,const char * disk)796 int libxl__qmp_nbd_server_add(libxl__gc *gc, int domid, const char *disk)
797 {
798 libxl__json_object *args = NULL;
799
800 libxl__qmp_param_add_string(gc, &args, "device", disk);
801 libxl__qmp_param_add_bool(gc, &args, "writable", true);
802
803 return qmp_run_command(gc, domid, "nbd-server-add", args, NULL, NULL);
804 }
805
libxl__qmp_start_replication(libxl__gc * gc,int domid,bool primary)806 int libxl__qmp_start_replication(libxl__gc *gc, int domid, bool primary)
807 {
808 libxl__json_object *args = NULL;
809
810 libxl__qmp_param_add_bool(gc, &args, "enable", true);
811 libxl__qmp_param_add_bool(gc, &args, "primary", primary);
812
813 return qmp_run_command(gc, domid, "xen-set-replication", args, NULL, NULL);
814 }
815
libxl__qmp_query_xen_replication_status(libxl__gc * gc,int domid)816 int libxl__qmp_query_xen_replication_status(libxl__gc *gc, int domid)
817 {
818 return qmp_run_command(gc, domid, "query-xen-replication-status", NULL,
819 NULL, NULL);
820 }
821
libxl__qmp_colo_do_checkpoint(libxl__gc * gc,int domid)822 int libxl__qmp_colo_do_checkpoint(libxl__gc *gc, int domid)
823 {
824 return qmp_run_command(gc, domid, "xen-colo-do-checkpoint",
825 NULL, NULL, NULL);
826 }
827
libxl__qmp_stop_replication(libxl__gc * gc,int domid,bool primary)828 int libxl__qmp_stop_replication(libxl__gc *gc, int domid, bool primary)
829 {
830 libxl__json_object *args = NULL;
831
832 libxl__qmp_param_add_bool(gc, &args, "enable", false);
833 libxl__qmp_param_add_bool(gc, &args, "primary", primary);
834
835 return qmp_run_command(gc, domid, "xen-set-replication", args, NULL, NULL);
836 }
837
libxl__qmp_nbd_server_stop(libxl__gc * gc,int domid)838 int libxl__qmp_nbd_server_stop(libxl__gc *gc, int domid)
839 {
840 return qmp_run_command(gc, domid, "nbd-server-stop", NULL, NULL, NULL);
841 }
842
libxl__qmp_x_blockdev_change(libxl__gc * gc,int domid,const char * parent,const char * child,const char * node)843 int libxl__qmp_x_blockdev_change(libxl__gc *gc, int domid, const char *parent,
844 const char *child, const char *node)
845 {
846 libxl__json_object *args = NULL;
847
848 libxl__qmp_param_add_string(gc, &args, "parent", parent);
849 if (child)
850 libxl__qmp_param_add_string(gc, &args, "child", child);
851 if (node)
852 libxl__qmp_param_add_string(gc, &args, "node", node);
853
854 return qmp_run_command(gc, domid, "x-blockdev-change", args, NULL, NULL);
855 }
856
hmp_callback(libxl__qmp_handler * qmp,const libxl__json_object * response,void * opaque)857 static int hmp_callback(libxl__qmp_handler *qmp,
858 const libxl__json_object *response,
859 void *opaque)
860 {
861 char **output = opaque;
862 GC_INIT(qmp->ctx);
863 int rc;
864
865 rc = 0;
866 if (!output)
867 goto out;
868
869 *output = NULL;
870
871 if (libxl__json_object_is_string(response)) {
872 *output = libxl__strdup(NOGC, libxl__json_object_get_string(response));
873 goto out;
874 }
875
876 LOG(ERROR, "Response has unexpected format");
877 rc = ERROR_FAIL;
878
879 out:
880 GC_FREE;
881 return rc;
882 }
883
libxl__qmp_hmp(libxl__gc * gc,int domid,const char * command_line,char ** output)884 int libxl__qmp_hmp(libxl__gc *gc, int domid, const char *command_line,
885 char **output)
886 {
887 libxl__json_object *args = NULL;
888
889 libxl__qmp_param_add_string(gc, &args, "command-line", command_line);
890
891 return qmp_run_command(gc, domid, "human-monitor-command", args,
892 hmp_callback, output);
893 }
894
895
896 typedef struct {
897 libxl__ev_qmp qmp;
898 char **output; /* user pointer */
899 } qemu_monitor_command_state;
900
901 static void qemu_monitor_command_done(libxl__egc *, libxl__ev_qmp *,
902 const libxl__json_object *response,
903 int rc);
904
libxl_qemu_monitor_command(libxl_ctx * ctx,uint32_t domid,const char * command_line,char ** output,const libxl_asyncop_how * ao_how)905 int libxl_qemu_monitor_command(libxl_ctx *ctx, uint32_t domid,
906 const char *command_line, char **output,
907 const libxl_asyncop_how *ao_how)
908 {
909 AO_CREATE(ctx, domid, ao_how);
910 qemu_monitor_command_state *qmcs;
911 libxl__json_object *args = NULL;
912 int rc;
913
914 if (!output) {
915 rc = ERROR_INVAL;
916 goto out;
917 }
918
919 GCNEW(qmcs);
920 libxl__ev_qmp_init(&qmcs->qmp);
921 qmcs->qmp.ao = ao;
922 qmcs->qmp.domid = domid;
923 qmcs->qmp.payload_fd = -1;
924 qmcs->qmp.callback = qemu_monitor_command_done;
925 qmcs->output = output;
926 libxl__qmp_param_add_string(gc, &args, "command-line", command_line);
927 rc = libxl__ev_qmp_send(egc, &qmcs->qmp, "human-monitor-command", args);
928 out:
929 if (rc) return AO_CREATE_FAIL(rc);
930 return AO_INPROGRESS;
931 }
932
qemu_monitor_command_done(libxl__egc * egc,libxl__ev_qmp * qmp,const libxl__json_object * response,int rc)933 static void qemu_monitor_command_done(libxl__egc *egc, libxl__ev_qmp *qmp,
934 const libxl__json_object *response,
935 int rc)
936 {
937 STATE_AO_GC(qmp->ao);
938 qemu_monitor_command_state *qmcs = CONTAINER_OF(qmp, *qmcs, qmp);
939
940 if (rc) goto out;
941
942 if (!libxl__json_object_is_string(response)) {
943 rc = ERROR_QEMU_API;
944 LOGD(ERROR, qmp->domid, "Response has unexpected format");
945 goto out;
946 }
947
948 *(qmcs->output) =
949 libxl__strdup(NOGC, libxl__json_object_get_string(response));
950 rc = 0;
951
952 out:
953 libxl__ev_qmp_dispose(gc, qmp);
954 libxl__ao_complete(egc, ao, rc);
955 }
956
957 /*
958 * Functions using libxl__ev_qmp
959 */
960
961 static void dm_stopped(libxl__egc *egc, libxl__ev_qmp *ev,
962 const libxl__json_object *response, int rc);
963 static void dm_state_fd_ready(libxl__egc *egc, libxl__ev_qmp *ev,
964 const libxl__json_object *response, int rc);
965 static void dm_state_save_to_fdset(libxl__egc *egc, libxl__ev_qmp *ev, int fdset);
966 static void dm_state_saved(libxl__egc *egc, libxl__ev_qmp *ev,
967 const libxl__json_object *response, int rc);
968
969 /* calls dsps->callback_device_model_done when done */
libxl__qmp_suspend_save(libxl__egc * egc,libxl__domain_suspend_state * dsps)970 void libxl__qmp_suspend_save(libxl__egc *egc,
971 libxl__domain_suspend_state *dsps)
972 {
973 EGC_GC;
974 int rc;
975 libxl__ev_qmp *ev = &dsps->qmp;
976
977 ev->ao = dsps->ao;
978 ev->domid = dsps->domid;
979 ev->callback = dm_stopped;
980 ev->payload_fd = -1;
981
982 rc = libxl__ev_qmp_send(egc, ev, "stop", NULL);
983 if (rc)
984 goto error;
985
986 return;
987
988 error:
989 dsps->callback_device_model_done(egc, dsps, rc);
990 }
991
dm_stopped(libxl__egc * egc,libxl__ev_qmp * ev,const libxl__json_object * response,int rc)992 static void dm_stopped(libxl__egc *egc, libxl__ev_qmp *ev,
993 const libxl__json_object *response, int rc)
994 {
995 EGC_GC;
996 libxl__domain_suspend_state *dsps = CONTAINER_OF(ev, *dsps, qmp);
997 const char *const filename = dsps->dm_savefile;
998 uint32_t dm_domid = libxl_get_stubdom_id(CTX, dsps->domid);
999
1000 if (rc)
1001 goto error;
1002
1003 if (dm_domid) {
1004 /* see Linux stubdom interface in docs/stubdom.txt */
1005 dm_state_save_to_fdset(egc, ev, 1);
1006 return;
1007 }
1008
1009 ev->payload_fd = open(filename, O_WRONLY | O_CREAT, 0600);
1010 if (ev->payload_fd < 0) {
1011 LOGED(ERROR, ev->domid,
1012 "Failed to open file %s for QEMU", filename);
1013 rc = ERROR_FAIL;
1014 goto error;
1015 }
1016
1017 ev->callback = dm_state_fd_ready;
1018 rc = libxl__ev_qmp_send(egc, ev, "add-fd", NULL);
1019 if (rc)
1020 goto error;
1021
1022 return;
1023
1024 error:
1025 if (ev->payload_fd >= 0) {
1026 close(ev->payload_fd);
1027 libxl__remove_file(gc, filename);
1028 ev->payload_fd = -1;
1029 }
1030 dsps->callback_device_model_done(egc, dsps, rc);
1031 }
1032
dm_state_fd_ready(libxl__egc * egc,libxl__ev_qmp * ev,const libxl__json_object * response,int rc)1033 static void dm_state_fd_ready(libxl__egc *egc, libxl__ev_qmp *ev,
1034 const libxl__json_object *response, int rc)
1035 {
1036 EGC_GC;
1037 int fdset;
1038 const libxl__json_object *o;
1039 libxl__domain_suspend_state *dsps = CONTAINER_OF(ev, *dsps, qmp);
1040
1041 close(ev->payload_fd);
1042 ev->payload_fd = -1;
1043
1044 if (rc)
1045 goto error;
1046
1047 o = libxl__json_map_get("fdset-id", response, JSON_INTEGER);
1048 if (!o) {
1049 rc = ERROR_QEMU_API;
1050 goto error;
1051 }
1052 fdset = libxl__json_object_get_integer(o);
1053 dm_state_save_to_fdset(egc, ev, fdset);
1054 return;
1055
1056 error:
1057 assert(rc);
1058 libxl__remove_file(gc, dsps->dm_savefile);
1059 dsps->callback_device_model_done(egc, dsps, rc);
1060 }
1061
dm_state_save_to_fdset(libxl__egc * egc,libxl__ev_qmp * ev,int fdset)1062 static void dm_state_save_to_fdset(libxl__egc *egc, libxl__ev_qmp *ev, int fdset)
1063 {
1064 EGC_GC;
1065 int rc;
1066 libxl__json_object *args = NULL;
1067 libxl__domain_suspend_state *dsps = CONTAINER_OF(ev, *dsps, qmp);
1068
1069 ev->callback = dm_state_saved;
1070
1071 /* The `live` parameter was added to QEMU 2.11. It signals QEMU that
1072 * the save operation is for a live migration rather than for taking a
1073 * snapshot. */
1074 if (qmp_ev_qemu_compare_version(ev, 2, 11, 0) >= 0)
1075 libxl__qmp_param_add_bool(gc, &args, "live", dsps->live);
1076 QMP_PARAMETERS_SPRINTF(&args, "filename", "/dev/fdset/%d", fdset);
1077 rc = libxl__ev_qmp_send(egc, ev, "xen-save-devices-state", args);
1078 if (rc)
1079 goto error;
1080
1081 return;
1082
1083 error:
1084 assert(rc);
1085 if (!libxl_get_stubdom_id(CTX, dsps->domid))
1086 libxl__remove_file(gc, dsps->dm_savefile);
1087 dsps->callback_device_model_done(egc, dsps, rc);
1088 }
1089
dm_state_saved(libxl__egc * egc,libxl__ev_qmp * ev,const libxl__json_object * response,int rc)1090 static void dm_state_saved(libxl__egc *egc, libxl__ev_qmp *ev,
1091 const libxl__json_object *response, int rc)
1092 {
1093 EGC_GC;
1094 libxl__domain_suspend_state *dsps = CONTAINER_OF(ev, *dsps, qmp);
1095
1096 if (rc)
1097 libxl__remove_file(gc, dsps->dm_savefile);
1098
1099 dsps->callback_device_model_done(egc, dsps, rc);
1100 }
1101
1102
1103 /* ------------ Implementation of libxl__ev_qmp ---------------- */
1104
1105 /*
1106 * Possible internal state compared to qmp_state:
1107 *
1108 * qmp_state External cfd efd id rx_buf* tx_buf* msg* lock
1109 * disconnected Idle NULL Idle reset free free free Idle
1110 * waiting_lock Active open Idle reset used free set Active
1111 * connecting Active open IN reset used free set Acquired
1112 * cap.neg Active open IN|OUT sent used cap_neg set Acquired
1113 * cap.neg Active open IN sent used free set Acquired
1114 * connected Connected open IN any used free free Acquired
1115 * waiting_reply Active open IN|OUT sent used free set Acquired
1116 * waiting_reply Active open IN|OUT sent used user's free Acquired
1117 * waiting_reply Active open IN sent used free free Acquired
1118 * broken[1] none[2] any Active any any any any any
1119 *
1120 * [1] When an internal function return an error, it can leave ev_qmp in a
1121 * `broken` state but only if the caller is another internal function.
1122 * That `broken` needs to be cleaned up, e.i. transitionned to the
1123 * `disconnected` state, before the control of ev_qmp is released outsides
1124 * of ev_qmp implementation.
1125 *
1126 * [2] This internal state should not be visible externally, see [1].
1127 *
1128 * Possible buffers states:
1129 * - receiving buffer:
1130 * free used
1131 * rx_buf NULL NULL or allocated
1132 * rx_buf_size 0 allocation size of `rx_buf`
1133 * rx_buf_used 0 <= rx_buf_size, actual data in the buffer
1134 * - transmitting buffer:
1135 * free used
1136 * tx_buf NULL contains data
1137 * tx_buf_len 0 size of data
1138 * tx_buf_off 0 <= tx_buf_len, data already sent
1139 * - queued user command:
1140 * free set
1141 * msg NULL contains data
1142 * msg_id 0 id assoctiated with the command in `msg`
1143 *
1144 * - Allowed internal state transition:
1145 * disconnected -> waiting_lock
1146 * waiting_lock -> connecting
1147 * connection -> capability_negotiation
1148 * capability_negotiation/connected -> waiting_reply
1149 * waiting_reply -> connected
1150 * any -> broken
1151 * broken -> disconnected
1152 * any -> disconnected
1153 *
1154 * The QEMU Machine Protocol (QMP) specification can be found in the QEMU
1155 * repository:
1156 * https://git.qemu.org/?p=qemu.git;a=blob_plain;f=docs/interop/qmp-spec.txt
1157 */
1158
1159 /* prototypes */
1160
1161 static void qmp_ev_fd_callback(libxl__egc *egc, libxl__ev_fd *ev_fd,
1162 int fd, short events, short revents);
1163 static int qmp_ev_callback_writable(libxl__gc *gc,
1164 libxl__ev_qmp *ev, int fd);
1165 static int qmp_ev_callback_readable(libxl__egc *egc,
1166 libxl__ev_qmp *ev, int fd);
1167 static int qmp_ev_get_next_msg(libxl__egc *egc, libxl__ev_qmp *ev,
1168 libxl__json_object **o_r);
1169 static int qmp_ev_handle_message(libxl__egc *egc,
1170 libxl__ev_qmp *ev,
1171 const libxl__json_object *resp);
1172
1173 /* helpers */
1174
qmp_ev_ensure_reading_writing(libxl__gc * gc,libxl__ev_qmp * ev)1175 static void qmp_ev_ensure_reading_writing(libxl__gc *gc, libxl__ev_qmp *ev)
1176 /* Update the state of `efd` to match the permited state
1177 * on entry: !disconnected */
1178 {
1179 short events = POLLIN;
1180
1181 if (ev->state == qmp_state_waiting_lock)
1182 /* We can't modify the efd yet, as it isn't registered. */
1183 return;
1184
1185 if (ev->tx_buf)
1186 events |= POLLOUT;
1187 else if ((ev->state == qmp_state_waiting_reply) && ev->msg)
1188 events |= POLLOUT;
1189
1190 libxl__ev_fd_modify(gc, &ev->efd, events);
1191 }
1192
qmp_ev_set_state(libxl__gc * gc,libxl__ev_qmp * ev,libxl__qmp_state new_state)1193 static void qmp_ev_set_state(libxl__gc *gc, libxl__ev_qmp *ev,
1194 libxl__qmp_state new_state)
1195 /* on entry: !broken and !disconnected */
1196 {
1197 switch (new_state) {
1198 case qmp_state_disconnected:
1199 break;
1200 case qmp_state_waiting_lock:
1201 assert(ev->state == qmp_state_disconnected);
1202 break;
1203 case qmp_state_connecting:
1204 assert(ev->state == qmp_state_waiting_lock);
1205 break;
1206 case qmp_state_capability_negotiation:
1207 assert(ev->state == qmp_state_connecting);
1208 break;
1209 case qmp_state_waiting_reply:
1210 assert(ev->state == qmp_state_capability_negotiation ||
1211 ev->state == qmp_state_connected);
1212 break;
1213 case qmp_state_connected:
1214 assert(ev->state == qmp_state_waiting_reply);
1215 break;
1216 }
1217
1218 ev->state = new_state;
1219
1220 qmp_ev_ensure_reading_writing(gc, ev);
1221 }
1222
qmp_ev_tx_buf_clear(libxl__ev_qmp * ev)1223 static void qmp_ev_tx_buf_clear(libxl__ev_qmp *ev)
1224 {
1225 ev->tx_buf = NULL;
1226 ev->tx_buf_len = 0;
1227 ev->tx_buf_off = 0;
1228 }
1229
qmp_error_class_to_libxl_error_code(libxl__gc * gc,const char * eclass)1230 static int qmp_error_class_to_libxl_error_code(libxl__gc *gc,
1231 const char *eclass)
1232 {
1233 const libxl_enum_string_table *t = libxl_error_string_table;
1234 const char skip[] = "QMP_";
1235 const size_t skipl = sizeof(skip) - 1;
1236
1237 /* compare "QMP_GENERIC_ERROR" from libxl_error to "GenericError"
1238 * generated by the QMP server */
1239
1240 for (; t->s; t++) {
1241 const char *s = eclass;
1242 const char *se = t->s;
1243 if (strncasecmp(t->s, skip, skipl))
1244 continue;
1245 se += skipl;
1246 while (*s && *se) {
1247 /* skip underscores */
1248 if (*se == '_') {
1249 se++;
1250 continue;
1251 }
1252 if (tolower(*s) != tolower(*se))
1253 break;
1254 s++, se++;
1255 }
1256 if (!*s && !*se)
1257 return t->v;
1258 }
1259
1260 LOG(ERROR, "Unknown QMP error class '%s'", eclass);
1261 return ERROR_UNKNOWN_QMP_ERROR;
1262 }
1263
1264 /* Setup connection */
1265
1266 static void qmp_ev_lock_aquired(libxl__egc *, libxl__ev_slowlock *,
1267 int rc);
1268 static void lock_error_callback(libxl__egc *, libxl__ev_immediate *);
1269
qmp_ev_connect(libxl__egc * egc,libxl__ev_qmp * ev)1270 static int qmp_ev_connect(libxl__egc *egc, libxl__ev_qmp *ev)
1271 /* disconnected -> waiting_lock/connecting but with `msg` free
1272 * on error: broken */
1273 {
1274 EGC_GC;
1275 int fd;
1276 int rc;
1277
1278 /* Convenience aliases */
1279 libxl__ev_slowlock *lock = &ev->lock;
1280
1281 assert(ev->state == qmp_state_disconnected);
1282
1283 libxl__carefd_begin();
1284 fd = socket(AF_UNIX, SOCK_STREAM, 0);
1285 ev->cfd = libxl__carefd_opened(CTX, fd);
1286 if (!ev->cfd) {
1287 LOGED(ERROR, ev->domid, "socket() failed");
1288 rc = ERROR_FAIL;
1289 goto out;
1290 }
1291 rc = libxl_fd_set_nonblock(CTX, libxl__carefd_fd(ev->cfd), 1);
1292 if (rc)
1293 goto out;
1294
1295 qmp_ev_set_state(gc, ev, qmp_state_waiting_lock);
1296
1297 lock->ao = ev->ao;
1298 lock->domid = ev->domid;
1299 lock->callback = qmp_ev_lock_aquired;
1300 libxl__ev_slowlock_lock(egc, &ev->lock);
1301
1302 return 0;
1303
1304 out:
1305 return rc;
1306 }
1307
qmp_ev_lock_aquired(libxl__egc * egc,libxl__ev_slowlock * lock,int rc)1308 static void qmp_ev_lock_aquired(libxl__egc *egc, libxl__ev_slowlock *lock,
1309 int rc)
1310 /* waiting_lock (with `lock' Acquired) -> connecting
1311 * on error: broken */
1312 {
1313 libxl__ev_qmp *ev = CONTAINER_OF(lock, *ev, lock);
1314 EGC_GC;
1315 const char *qmp_socket_path;
1316 struct sockaddr_un un;
1317 int r;
1318
1319 if (rc) goto out;
1320
1321 qmp_socket_path = libxl__qemu_qmp_path(gc, ev->domid);
1322
1323 LOGD(DEBUG, ev->domid, "Connecting to %s", qmp_socket_path);
1324
1325 rc = libxl__prepare_sockaddr_un(gc, &un, qmp_socket_path,
1326 "QMP socket");
1327 if (rc)
1328 goto out;
1329
1330 r = connect(libxl__carefd_fd(ev->cfd),
1331 (struct sockaddr *) &un, sizeof(un));
1332 if (r && errno != EINPROGRESS) {
1333 LOGED(ERROR, ev->domid, "Failed to connect to QMP socket %s",
1334 qmp_socket_path);
1335 rc = ERROR_FAIL;
1336 goto out;
1337 }
1338
1339 rc = libxl__ev_fd_register(gc, &ev->efd, qmp_ev_fd_callback,
1340 libxl__carefd_fd(ev->cfd), POLLIN);
1341 if (rc)
1342 goto out;
1343
1344 qmp_ev_set_state(gc, ev, qmp_state_connecting);
1345
1346 return;
1347
1348 out:
1349 /* An error occurred and we need to let the caller know. At this
1350 * point, we can only do so via the callback. Unfortunately, the
1351 * callback of libxl__ev_slowlock_lock() might be called synchronously,
1352 * but libxl__ev_qmp_send() promise that it will not call the callback
1353 * synchronously. So we have to arrange to call the callback
1354 * asynchronously. */
1355 ev->rc = rc;
1356 ev->ei.callback = lock_error_callback;
1357 libxl__ev_immediate_register(egc, &ev->ei);
1358 }
1359
lock_error_callback(libxl__egc * egc,libxl__ev_immediate * ei)1360 static void lock_error_callback(libxl__egc *egc, libxl__ev_immediate *ei)
1361 /* broken -> disconnected */
1362 {
1363 EGC_GC;
1364 libxl__ev_qmp *ev = CONTAINER_OF(ei, *ev, ei);
1365
1366 int rc = ev->rc;
1367
1368 /* On error, deallocate all private resources */
1369 libxl__ev_qmp_dispose(gc, ev);
1370
1371 /* And tell libxl__ev_qmp user about the error */
1372 ev->callback(egc, ev, NULL, rc); /* must be last */
1373 }
1374
1375 /* QMP FD callbacks */
1376
qmp_ev_fd_callback(libxl__egc * egc,libxl__ev_fd * ev_fd,int fd,short events,short revents)1377 static void qmp_ev_fd_callback(libxl__egc *egc, libxl__ev_fd *ev_fd,
1378 int fd, short events, short revents)
1379 /* On entry, ev_fd is (of course) Active. The ev_qmp may be in any
1380 * state where this is permitted. qmp_ev_fd_callback will do the work
1381 * necessary to make progress, depending on the current state, and make
1382 * the appropriate state transitions and callbacks. */
1383 {
1384 libxl__ev_qmp *ev = CONTAINER_OF(ev_fd, *ev, efd);
1385 STATE_AO_GC(ev->ao);
1386 int rc;
1387
1388 if (revents & (POLLHUP|POLLERR)) {
1389 int r;
1390 int error_val = 0;
1391 socklen_t opt_len = sizeof(error_val);
1392
1393 r = getsockopt(fd, SOL_SOCKET, SO_ERROR, &error_val, &opt_len);
1394 if (r)
1395 LOGED(ERROR, ev->domid, "getsockopt failed");
1396 if (!r && error_val) {
1397 errno = error_val;
1398 LOGED(ERROR, ev->domid, "error on QMP socket");
1399 } else {
1400 LOGD(ERROR, ev->domid,
1401 "received POLLHUP|POLLERR from QMP socket");
1402 }
1403 rc = ERROR_PROTOCOL_ERROR_QMP;
1404 goto error;
1405 }
1406
1407 if (revents & ~(POLLIN|POLLOUT)) {
1408 LOGD(ERROR, ev->domid,
1409 "unexpected poll event 0x%x on QMP socket (expected POLLIN "
1410 "and/or POLLOUT)",
1411 revents);
1412 rc = ERROR_FAIL;
1413 goto error;
1414 }
1415
1416 if (revents & POLLOUT) {
1417 rc = qmp_ev_callback_writable(gc, ev, fd);
1418 if (rc)
1419 goto error;
1420 }
1421
1422 if (revents & POLLIN) {
1423 rc = qmp_ev_callback_readable(egc, ev, fd);
1424 if (rc < 0)
1425 goto error;
1426 if (rc == 1) {
1427 /* user callback has been called */
1428 return;
1429 }
1430 }
1431
1432 return;
1433
1434 error:
1435 assert(rc);
1436
1437 LOGD(ERROR, ev->domid,
1438 "Error happened with the QMP connection to QEMU");
1439
1440 /* On error, deallocate all private ressources */
1441 libxl__ev_qmp_dispose(gc, ev);
1442
1443 /* And tell libxl__ev_qmp user about the error */
1444 ev->callback(egc, ev, NULL, rc); /* must be last */
1445 }
1446
qmp_ev_callback_writable(libxl__gc * gc,libxl__ev_qmp * ev,int fd)1447 static int qmp_ev_callback_writable(libxl__gc *gc,
1448 libxl__ev_qmp *ev, int fd)
1449 /* on entry: !disconnected
1450 * on return, one of these state transition:
1451 * waiting_reply (with msg set) -> waiting_reply (with msg free)
1452 * tx_buf set -> same state or tx_buf free
1453 * on error: broken */
1454 {
1455 int rc;
1456 ssize_t r;
1457
1458 if (ev->state == qmp_state_waiting_reply) {
1459 if (ev->msg) {
1460 assert(!ev->tx_buf);
1461 ev->tx_buf = ev->msg;
1462 ev->tx_buf_len = strlen(ev->msg);
1463 ev->tx_buf_off = 0;
1464 ev->id = ev->msg_id;
1465 ev->msg = NULL;
1466 ev->msg_id = 0;
1467 }
1468 }
1469
1470 assert(ev->tx_buf);
1471
1472 LOG_QMP("sending: '%.*s'", (int)ev->tx_buf_len, ev->tx_buf);
1473
1474 /*
1475 * We will send a file descriptor associated with a command on the
1476 * first byte of this command.
1477 */
1478 if (ev->state == qmp_state_waiting_reply &&
1479 ev->payload_fd >= 0 &&
1480 ev->tx_buf_off == 0) {
1481
1482 rc = libxl__sendmsg_fds(gc, fd, ev->tx_buf[ev->tx_buf_off],
1483 1, &ev->payload_fd, "QMP socket");
1484 /* Check for EWOULDBLOCK, and return to try again later */
1485 if (rc == ERROR_NOT_READY)
1486 return 0;
1487 if (rc)
1488 return rc;
1489 ev->tx_buf_off++;
1490 }
1491
1492 while (ev->tx_buf_off < ev->tx_buf_len) {
1493 ssize_t max_write = ev->tx_buf_len - ev->tx_buf_off;
1494 r = write(fd, ev->tx_buf + ev->tx_buf_off, max_write);
1495 if (r < 0) {
1496 if (errno == EINTR)
1497 continue;
1498 if (errno == EWOULDBLOCK)
1499 break;
1500 LOGED(ERROR, ev->domid, "failed to write to QMP socket");
1501 return ERROR_FAIL;
1502 }
1503 assert(r > 0 && r <= max_write);
1504 ev->tx_buf_off += r;
1505 }
1506
1507 if (ev->tx_buf_off == ev->tx_buf_len)
1508 qmp_ev_tx_buf_clear(ev);
1509
1510 qmp_ev_ensure_reading_writing(gc, ev);
1511
1512 return 0;
1513 }
1514
qmp_ev_callback_readable(libxl__egc * egc,libxl__ev_qmp * ev,int fd)1515 static int qmp_ev_callback_readable(libxl__egc *egc,
1516 libxl__ev_qmp *ev, int fd)
1517 /*
1518 * Return values:
1519 * < 0 libxl error code
1520 * 0 success
1521 * 1 success, but a user callback has been called,
1522 * `ev` should not be used anymore.
1523 *
1524 * This function will update the rx buffer and possibly update
1525 * ev->state:
1526 * connecting -> capability_negotiation
1527 * capability_negotiation -> waiting_reply
1528 * waiting_reply -> connected
1529 * on error: broken
1530 */
1531 {
1532 STATE_AO_GC(ev->ao);
1533 int rc;
1534 ssize_t r;
1535
1536 while (1) {
1537 while (1) {
1538 libxl__json_object *o = NULL;
1539
1540 /* parse rx buffer to find one json object */
1541 rc = qmp_ev_get_next_msg(egc, ev, &o);
1542 if (rc == ERROR_NOTFOUND)
1543 break;
1544 else if (rc)
1545 return rc;
1546
1547 /* Must be last and return when the user callback is called */
1548 rc = qmp_ev_handle_message(egc, ev, o);
1549 if (rc)
1550 /* returns both rc values -ERROR_* and 1 */
1551 return rc;
1552 }
1553
1554 /* Check if the buffer still have space, or increase size */
1555 if (ev->rx_buf_size - ev->rx_buf_used < QMP_RECEIVE_BUFFER_SIZE) {
1556 size_t newsize = ev->rx_buf_size * 2 + QMP_RECEIVE_BUFFER_SIZE;
1557
1558 if (newsize > QMP_MAX_SIZE_RX_BUF) {
1559 LOGD(ERROR, ev->domid,
1560 "QMP receive buffer is too big (%zu > %lld)",
1561 newsize, QMP_MAX_SIZE_RX_BUF);
1562 return ERROR_BUFFERFULL;
1563 }
1564 ev->rx_buf_size = newsize;
1565 ev->rx_buf = libxl__realloc(gc, ev->rx_buf, ev->rx_buf_size);
1566 }
1567
1568 r = read(fd, ev->rx_buf + ev->rx_buf_used,
1569 ev->rx_buf_size - ev->rx_buf_used);
1570 if (r < 0) {
1571 if (errno == EINTR)
1572 continue;
1573 if (errno == EWOULDBLOCK)
1574 break;
1575 LOGED(ERROR, ev->domid, "error reading QMP socket");
1576 return ERROR_FAIL;
1577 }
1578
1579 if (r == 0) {
1580 LOGD(ERROR, ev->domid, "Unexpected EOF on QMP socket");
1581 return ERROR_PROTOCOL_ERROR_QMP;
1582 }
1583
1584 LOG_QMP("received %ldB: '%.*s'", r,
1585 (int)r, ev->rx_buf + ev->rx_buf_used);
1586
1587 ev->rx_buf_used += r;
1588 assert(ev->rx_buf_used <= ev->rx_buf_size);
1589 }
1590
1591 return 0;
1592 }
1593
1594 /* Handle messages received from QMP server */
1595
qmp_ev_get_next_msg(libxl__egc * egc,libxl__ev_qmp * ev,libxl__json_object ** o_r)1596 static int qmp_ev_get_next_msg(libxl__egc *egc, libxl__ev_qmp *ev,
1597 libxl__json_object **o_r)
1598 /* Find a JSON object and store it in o_r.
1599 * return ERROR_NOTFOUND if no object is found.
1600 *
1601 * !disconnected -> same state (with rx buffer updated)
1602 */
1603 {
1604 STATE_AO_GC(ev->ao);
1605 size_t len;
1606 char *end = NULL;
1607 const char eom[] = "\r\n";
1608 const size_t eoml = sizeof(eom) - 1;
1609 libxl__json_object *o = NULL;
1610
1611 if (!ev->rx_buf_used)
1612 return ERROR_NOTFOUND;
1613
1614 /* Search for the end of a QMP message: "\r\n" */
1615 end = memmem(ev->rx_buf, ev->rx_buf_used, eom, eoml);
1616 if (!end)
1617 return ERROR_NOTFOUND;
1618 len = (end - ev->rx_buf) + eoml;
1619
1620 LOG_QMP("parsing %luB: '%.*s'", len, (int)len, ev->rx_buf);
1621
1622 /* Replace \r by \0 so that libxl__json_parse can use strlen */
1623 ev->rx_buf[len - eoml] = '\0';
1624 o = libxl__json_parse(gc, ev->rx_buf);
1625
1626 if (!o) {
1627 LOGD(ERROR, ev->domid, "Parse error");
1628 return ERROR_PROTOCOL_ERROR_QMP;
1629 }
1630
1631 ev->rx_buf_used -= len;
1632 memmove(ev->rx_buf, ev->rx_buf + len, ev->rx_buf_used);
1633
1634 LOG_QMP("JSON object received: %s", JSON(o));
1635
1636 *o_r = o;
1637
1638 return 0;
1639 }
1640
1641 static int qmp_ev_parse_error_messages(libxl__egc *egc,
1642 libxl__ev_qmp *ev,
1643 const libxl__json_object *resp);
1644
qmp_ev_handle_message(libxl__egc * egc,libxl__ev_qmp * ev,const libxl__json_object * resp)1645 static int qmp_ev_handle_message(libxl__egc *egc,
1646 libxl__ev_qmp *ev,
1647 const libxl__json_object *resp)
1648 /*
1649 * This function will handle every messages sent by the QMP server.
1650 * Return values:
1651 * < 0 libxl error code
1652 * 0 success
1653 * 1 success, but a user callback has been called,
1654 * `ev` should not be used anymore.
1655 *
1656 * Possible state changes:
1657 * connecting -> capability_negotiation
1658 * capability_negotiation -> waiting_reply
1659 * waiting_reply -> waiting_reply/connected
1660 *
1661 * on error: broken
1662 */
1663 {
1664 STATE_AO_GC(ev->ao);
1665 int id;
1666 char *buf;
1667 int rc = 0;
1668 const libxl__json_object *o;
1669 const libxl__json_object *response;
1670 libxl__qmp_message_type type = qmp_response_type(resp);
1671
1672 switch (type) {
1673 case LIBXL__QMP_MESSAGE_TYPE_QMP:
1674 /* greeting message */
1675
1676 if (ev->state != qmp_state_connecting) {
1677 LOGD(ERROR, ev->domid,
1678 "Unexpected greeting message received");
1679 return ERROR_PROTOCOL_ERROR_QMP;
1680 }
1681
1682 /*
1683 * Store advertised QEMU version
1684 * { "QMP": { "version": {
1685 * "qemu": { "major": int, "minor": int, "micro": int } } } }
1686 */
1687 o = libxl__json_map_get("QMP", resp, JSON_MAP);
1688 o = libxl__json_map_get("version", o, JSON_MAP);
1689 o = libxl__json_map_get("qemu", o, JSON_MAP);
1690 #define GRAB_VERSION(level) do { \
1691 ev->qemu_version.level = libxl__json_object_get_integer( \
1692 libxl__json_map_get(#level, o, JSON_INTEGER)); \
1693 } while (0)
1694 GRAB_VERSION(major);
1695 GRAB_VERSION(minor);
1696 GRAB_VERSION(micro);
1697 #undef GRAB_VERSION
1698 LOGD(DEBUG, ev->domid, "QEMU version: %d.%d.%d",
1699 ev->qemu_version.major,
1700 ev->qemu_version.minor,
1701 ev->qemu_version.micro);
1702
1703 /* Prepare next message to send */
1704 assert(!ev->tx_buf);
1705 ev->id = ev->next_id++;
1706 buf = qmp_prepare_cmd(gc, "qmp_capabilities", NULL, ev->id);
1707 if (!buf) {
1708 LOGD(ERROR, ev->domid,
1709 "Failed to generate qmp_capabilities command");
1710 return ERROR_FAIL;
1711 }
1712 ev->tx_buf = buf;
1713 ev->tx_buf_len = strlen(buf);
1714 ev->tx_buf_off = 0;
1715 qmp_ev_set_state(gc, ev, qmp_state_capability_negotiation);
1716
1717 return 0;
1718
1719 case LIBXL__QMP_MESSAGE_TYPE_RETURN:
1720 case LIBXL__QMP_MESSAGE_TYPE_ERROR:
1721 /*
1722 * Reply to a command (success/error) or server error
1723 *
1724 * In this cases, we are parsing two possibles responses:
1725 * - success:
1726 * { "return": json-value, "id": int }
1727 * - error:
1728 * { "error": { "class": string, "desc": string }, "id": int }
1729 */
1730
1731 o = libxl__json_map_get("id", resp, JSON_INTEGER);
1732 if (!o) {
1733 /*
1734 * If "id" isn't present, an error occur on the server before
1735 * it has read the "id" provided by libxl.
1736 *
1737 * We deliberately squash all errors into
1738 * ERROR_PROTOCOL_ERROR_QMP as qmp_ev_parse_error_messages may
1739 * also return ERROR_QMP_* but those are reserved for errors
1740 * return by the caller's command.
1741 */
1742 qmp_ev_parse_error_messages(egc, ev, resp);
1743 return ERROR_PROTOCOL_ERROR_QMP;
1744 }
1745
1746 id = libxl__json_object_get_integer(o);
1747
1748 if (id != ev->id) {
1749 LOGD(ERROR, ev->domid,
1750 "Message from QEMU with unexpected id %d: %s",
1751 id, JSON(resp));
1752 return ERROR_PROTOCOL_ERROR_QMP;
1753 }
1754
1755 switch (ev->state) {
1756 case qmp_state_capability_negotiation:
1757 if (type != LIBXL__QMP_MESSAGE_TYPE_RETURN) {
1758 LOGD(ERROR, ev->domid,
1759 "Error during capability negotiation: %s",
1760 JSON(resp));
1761 return ERROR_PROTOCOL_ERROR_QMP;
1762 }
1763 qmp_ev_set_state(gc, ev, qmp_state_waiting_reply);
1764 return 0;
1765 case qmp_state_waiting_reply:
1766 if (type == LIBXL__QMP_MESSAGE_TYPE_RETURN) {
1767 response = libxl__json_map_get("return", resp, JSON_ANY);
1768 rc = 0;
1769 } else {
1770 /* error message */
1771 response = NULL;
1772 rc = qmp_ev_parse_error_messages(egc, ev, resp);
1773 }
1774 qmp_ev_set_state(gc, ev, qmp_state_connected);
1775 ev->callback(egc, ev, response, rc); /* must be last */
1776 return 1;
1777 default:
1778 LOGD(ERROR, ev->domid, "Unexpected message: %s", JSON(resp));
1779 return ERROR_PROTOCOL_ERROR_QMP;
1780 }
1781 return 0;
1782
1783 case LIBXL__QMP_MESSAGE_TYPE_EVENT:
1784 /* Events are ignored */
1785 return 0;
1786
1787 case LIBXL__QMP_MESSAGE_TYPE_INVALID:
1788 LOGD(ERROR, ev->domid, "Unexpected message received: %s",
1789 JSON(resp));
1790 return ERROR_PROTOCOL_ERROR_QMP;
1791
1792 default:
1793 abort();
1794 }
1795
1796 return 0;
1797 }
1798
qmp_ev_parse_error_messages(libxl__egc * egc,libxl__ev_qmp * ev,const libxl__json_object * resp)1799 static int qmp_ev_parse_error_messages(libxl__egc *egc,
1800 libxl__ev_qmp *ev,
1801 const libxl__json_object *resp)
1802 /* no state change */
1803 {
1804 STATE_AO_GC(ev->ao);
1805 int rc;
1806 const char *s;
1807 const libxl__json_object *o;
1808 const libxl__json_object *err;
1809
1810 /*
1811 * { "error": { "class": string, "desc": string } }
1812 */
1813
1814 err = libxl__json_map_get("error", resp, JSON_MAP);
1815
1816 o = libxl__json_map_get("class", err, JSON_STRING);
1817 if (!o) {
1818 LOGD(ERROR, ev->domid,
1819 "Protocol error: missing 'class' member in error message");
1820 return ERROR_PROTOCOL_ERROR_QMP;
1821 }
1822 s = libxl__json_object_get_string(o);
1823 if (s)
1824 rc = qmp_error_class_to_libxl_error_code(gc, s);
1825 else
1826 rc = ERROR_PROTOCOL_ERROR_QMP;
1827
1828 o = libxl__json_map_get("desc", err, JSON_STRING);
1829 if (!o) {
1830 LOGD(ERROR, ev->domid,
1831 "Protocol error: missing 'desc' member in error message");
1832 return ERROR_PROTOCOL_ERROR_QMP;
1833 }
1834 s = libxl__json_object_get_string(o);
1835 if (s)
1836 LOGD(ERROR, ev->domid, "%s", s);
1837 else
1838 LOGD(ERROR, ev->domid, "Received unexpected error: %s",
1839 JSON(resp));
1840 return rc;
1841 }
1842
1843 /*
1844 * libxl__ev_qmp_*
1845 */
1846
libxl__ev_qmp_init(libxl__ev_qmp * ev)1847 void libxl__ev_qmp_init(libxl__ev_qmp *ev)
1848 /* disconnected -> disconnected */
1849 {
1850 /* Start with an message ID that is obviously generated by libxl
1851 * "xlq\0" */
1852 ev->next_id = 0x786c7100;
1853
1854 ev->cfd = NULL;
1855 libxl__ev_fd_init(&ev->efd);
1856 ev->state = qmp_state_disconnected;
1857 ev->id = 0;
1858
1859 ev->rx_buf = NULL;
1860 ev->rx_buf_size = ev->rx_buf_used = 0;
1861 qmp_ev_tx_buf_clear(ev);
1862
1863 ev->msg = NULL;
1864 ev->msg_id = 0;
1865
1866 ev->qemu_version.major = -1;
1867 ev->qemu_version.minor = -1;
1868 ev->qemu_version.micro = -1;
1869
1870 libxl__ev_qmplock_init(&ev->lock);
1871 ev->rc = 0;
1872 }
1873
libxl__ev_qmp_send(libxl__egc * egc,libxl__ev_qmp * ev,const char * cmd,libxl__json_object * args)1874 int libxl__ev_qmp_send(libxl__egc *egc, libxl__ev_qmp *ev,
1875 const char *cmd, libxl__json_object *args)
1876 /* disconnected -> waiting_lock/connecting
1877 * connected -> waiting_reply (with msg set)
1878 * on error: disconnected */
1879 {
1880 STATE_AO_GC(ev->ao);
1881 int rc;
1882
1883 LOGD(DEBUG, ev->domid, " ev %p, cmd '%s'", ev, cmd);
1884
1885 assert(ev->state == qmp_state_disconnected ||
1886 ev->state == qmp_state_connected);
1887 assert(cmd);
1888
1889 /* Connect to QEMU if not already connected */
1890 if (ev->state == qmp_state_disconnected) {
1891 rc = qmp_ev_connect(egc, ev);
1892 if (rc)
1893 goto error;
1894 }
1895
1896 /* Prepare user command */
1897 ev->msg_id = ev->next_id++;
1898 ev->msg = qmp_prepare_cmd(gc, cmd, args, ev->msg_id);
1899 if (!ev->msg) {
1900 LOGD(ERROR, ev->domid, "Failed to generate caller's command %s",
1901 cmd);
1902 rc = ERROR_FAIL;
1903 goto error;
1904 }
1905 if (ev->state == qmp_state_connected) {
1906 qmp_ev_set_state(gc, ev, qmp_state_waiting_reply);
1907 }
1908
1909 return 0;
1910
1911 error:
1912 libxl__ev_qmp_dispose(gc, ev);
1913 return rc;
1914 }
1915
libxl__ev_qmp_dispose(libxl__gc * gc,libxl__ev_qmp * ev)1916 void libxl__ev_qmp_dispose(libxl__gc *gc, libxl__ev_qmp *ev)
1917 /* * -> disconnected */
1918 {
1919 LOGD(DEBUG, ev->domid, " ev %p", ev);
1920
1921 libxl__ev_fd_deregister(gc, &ev->efd);
1922 libxl__carefd_close(ev->cfd);
1923 libxl__ev_slowlock_dispose(gc, &ev->lock);
1924
1925 libxl__ev_qmp_init(ev);
1926 }
1927
1928 /*
1929 * Local variables:
1930 * mode: C
1931 * c-basic-offset: 4
1932 * indent-tabs-mode: nil
1933 * End:
1934 */
1935