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
21 #include "libxl_osdeps.h" /* must come before any other headers */
22
23 #include <sys/un.h>
24
25 #include <yajl/yajl_gen.h>
26
27 #include "_libxl_list.h"
28 #include "libxl_internal.h"
29
30 /* #define DEBUG_RECEIVED */
31
32 #ifdef DEBUG_RECEIVED
33 # define DEBUG_REPORT_RECEIVED(dom, buf, len) \
34 LOGD(DEBUG, dom, "received: '%.*s'", len, buf)
35 #else
36 # define DEBUG_REPORT_RECEIVED(dom, buf, len) ((void)0)
37 #endif
38
39 /*
40 * QMP types & constant
41 */
42
43 #define QMP_RECEIVE_BUFFER_SIZE 4096
44 #define PCI_PT_QDEV_ID "pci-pt-%02x_%02x.%01x"
45
46 typedef int (*qmp_callback_t)(libxl__qmp_handler *qmp,
47 const libxl__json_object *tree,
48 void *opaque);
49
50 typedef struct qmp_request_context {
51 int rc;
52 } qmp_request_context;
53
54 typedef struct callback_id_pair {
55 int id;
56 qmp_callback_t callback;
57 void *opaque;
58 qmp_request_context *context;
59 LIBXL_STAILQ_ENTRY(struct callback_id_pair) next;
60 } callback_id_pair;
61
62 struct libxl__qmp_handler {
63 struct sockaddr_un addr;
64 int qmp_fd;
65 bool connected;
66 time_t timeout;
67 /* wait_for_id will be used by the synchronous send function */
68 int wait_for_id;
69
70 char buffer[QMP_RECEIVE_BUFFER_SIZE + 1];
71 libxl__yajl_ctx *yajl_ctx;
72
73 libxl_ctx *ctx;
74 uint32_t domid;
75
76 int last_id_used;
77 LIBXL_STAILQ_HEAD(callback_list, callback_id_pair) callback_list;
78 };
79
80 static int qmp_send(libxl__qmp_handler *qmp,
81 const char *cmd, libxl__json_object *args,
82 qmp_callback_t callback, void *opaque,
83 qmp_request_context *context);
84
85 static const int QMP_SOCKET_CONNECT_TIMEOUT = 5;
86
87 /*
88 * QMP callbacks functions
89 */
90
store_serial_port_info(libxl__qmp_handler * qmp,const char * chardev,int port)91 static int store_serial_port_info(libxl__qmp_handler *qmp,
92 const char *chardev,
93 int port)
94 {
95 GC_INIT(qmp->ctx);
96 char *path = NULL;
97 int ret = 0;
98
99 if (!(chardev && strncmp("pty:", chardev, 4) == 0)) {
100 return 0;
101 }
102
103 path = libxl__xs_get_dompath(gc, qmp->domid);
104 path = GCSPRINTF("%s/serial/%d/tty", path, port);
105
106 ret = libxl__xs_printf(gc, XBT_NULL, path, "%s", chardev + 4);
107
108 GC_FREE;
109 return ret;
110 }
111
register_serials_chardev_callback(libxl__qmp_handler * qmp,const libxl__json_object * o,void * unused)112 static int register_serials_chardev_callback(libxl__qmp_handler *qmp,
113 const libxl__json_object *o,
114 void *unused)
115 {
116 const libxl__json_object *obj = NULL;
117 const libxl__json_object *label = NULL;
118 const char *s = NULL;
119 int i = 0;
120 const char *chardev = NULL;
121 int ret = 0;
122
123 for (i = 0; (obj = libxl__json_array_get(o, i)); i++) {
124 if (!libxl__json_object_is_map(obj))
125 continue;
126 label = libxl__json_map_get("label", obj, JSON_STRING);
127 s = libxl__json_object_get_string(label);
128
129 if (s && strncmp("serial", s, strlen("serial")) == 0) {
130 const libxl__json_object *filename = NULL;
131 char *endptr = NULL;
132 int port_number;
133
134 filename = libxl__json_map_get("filename", obj, JSON_STRING);
135 chardev = libxl__json_object_get_string(filename);
136
137 s += strlen("serial");
138 port_number = strtol(s, &endptr, 10);
139 if (*s == 0 || *endptr != 0) {
140 LIBXL__LOGD(qmp->ctx, LIBXL__LOG_ERROR, qmp->domid,
141 "Invalid serial port number: %s", s);
142 return -1;
143 }
144 ret = store_serial_port_info(qmp, chardev, port_number);
145 if (ret) {
146 LIBXL__LOGD_ERRNO(qmp->ctx, LIBXL__LOG_ERROR, qmp->domid,
147 "Failed to store serial port information"
148 " in xenstore");
149 return ret;
150 }
151 }
152 };
153
154 return ret;
155 }
156
qmp_write_domain_console_item(libxl__gc * gc,int domid,const char * item,const char * value)157 static int qmp_write_domain_console_item(libxl__gc *gc, int domid,
158 const char *item, const char *value)
159 {
160 char *path;
161
162 path = libxl__xs_get_dompath(gc, domid);
163 path = GCSPRINTF("%s/console/%s", path, item);
164
165 return libxl__xs_printf(gc, XBT_NULL, path, "%s", value);
166 }
167
qmp_register_vnc_callback(libxl__qmp_handler * qmp,const libxl__json_object * o,void * unused)168 static int qmp_register_vnc_callback(libxl__qmp_handler *qmp,
169 const libxl__json_object *o,
170 void *unused)
171 {
172 GC_INIT(qmp->ctx);
173 const libxl__json_object *obj;
174 const char *addr, *port;
175 int rc = -1;
176
177 if (!libxl__json_object_is_map(o)) {
178 goto out;
179 }
180
181 obj = libxl__json_map_get("enabled", o, JSON_BOOL);
182 if (!obj || !libxl__json_object_get_bool(obj)) {
183 rc = 0;
184 goto out;
185 }
186
187 obj = libxl__json_map_get("host", o, JSON_STRING);
188 addr = libxl__json_object_get_string(obj);
189 obj = libxl__json_map_get("service", o, JSON_STRING);
190 port = libxl__json_object_get_string(obj);
191
192 if (!addr || !port) {
193 LOGD(ERROR, qmp->domid, "Failed to retreive VNC connect information.");
194 goto out;
195 }
196
197 rc = qmp_write_domain_console_item(gc, qmp->domid, "vnc-listen", addr);
198 if (!rc)
199 rc = qmp_write_domain_console_item(gc, qmp->domid, "vnc-port", port);
200
201 out:
202 GC_FREE;
203 return rc;
204 }
205
qmp_capabilities_callback(libxl__qmp_handler * qmp,const libxl__json_object * o,void * unused)206 static int qmp_capabilities_callback(libxl__qmp_handler *qmp,
207 const libxl__json_object *o, void *unused)
208 {
209 qmp->connected = true;
210
211 return 0;
212 }
213
214 /*
215 * QMP commands
216 */
217
enable_qmp_capabilities(libxl__qmp_handler * qmp)218 static int enable_qmp_capabilities(libxl__qmp_handler *qmp)
219 {
220 return qmp_send(qmp, "qmp_capabilities", NULL,
221 qmp_capabilities_callback, NULL, NULL);
222 }
223
224 /*
225 * Helpers
226 */
227
qmp_response_type(libxl__qmp_handler * qmp,const libxl__json_object * o)228 static libxl__qmp_message_type qmp_response_type(libxl__qmp_handler *qmp,
229 const libxl__json_object *o)
230 {
231 libxl__qmp_message_type type;
232 libxl__json_map_node *node = NULL;
233 int i = 0;
234
235 for (i = 0; (node = libxl__json_map_node_get(o, i)); i++) {
236 if (libxl__qmp_message_type_from_string(node->map_key, &type) == 0)
237 return type;
238 }
239
240 return LIBXL__QMP_MESSAGE_TYPE_INVALID;
241 }
242
qmp_get_callback_from_id(libxl__qmp_handler * qmp,const libxl__json_object * o)243 static callback_id_pair *qmp_get_callback_from_id(libxl__qmp_handler *qmp,
244 const libxl__json_object *o)
245 {
246 const libxl__json_object *id_object = libxl__json_map_get("id", o,
247 JSON_INTEGER);
248 int id = -1;
249 callback_id_pair *pp = NULL;
250
251 if (id_object) {
252 id = libxl__json_object_get_integer(id_object);
253
254 LIBXL_STAILQ_FOREACH(pp, &qmp->callback_list, next) {
255 if (pp->id == id) {
256 return pp;
257 }
258 }
259 }
260 return NULL;
261 }
262
qmp_handle_error_response(libxl__gc * gc,libxl__qmp_handler * qmp,const libxl__json_object * resp)263 static void qmp_handle_error_response(libxl__gc *gc, libxl__qmp_handler *qmp,
264 const libxl__json_object *resp)
265 {
266 callback_id_pair *pp = qmp_get_callback_from_id(qmp, resp);
267
268 resp = libxl__json_map_get("error", resp, JSON_MAP);
269 resp = libxl__json_map_get("desc", resp, JSON_STRING);
270
271 if (pp) {
272 if (pp->callback) {
273 int rc = pp->callback(qmp, NULL, pp->opaque);
274 if (pp->context) {
275 pp->context->rc = rc;
276 }
277 }
278 if (pp->id == qmp->wait_for_id) {
279 /* tell that the id have been processed */
280 qmp->wait_for_id = 0;
281 }
282 LIBXL_STAILQ_REMOVE(&qmp->callback_list, pp, callback_id_pair, next);
283 free(pp);
284 }
285
286 LOGD(ERROR, qmp->domid, "received an error message from QMP server: %s",
287 libxl__json_object_get_string(resp));
288 }
289
qmp_handle_response(libxl__gc * gc,libxl__qmp_handler * qmp,const libxl__json_object * resp)290 static int qmp_handle_response(libxl__gc *gc, libxl__qmp_handler *qmp,
291 const libxl__json_object *resp)
292 {
293 libxl__qmp_message_type type = LIBXL__QMP_MESSAGE_TYPE_INVALID;
294
295 type = qmp_response_type(qmp, resp);
296 LOGD(DEBUG, qmp->domid, "message type: %s", libxl__qmp_message_type_to_string(type));
297
298 switch (type) {
299 case LIBXL__QMP_MESSAGE_TYPE_QMP:
300 /* On the greeting message from the server, enable QMP capabilities */
301 return enable_qmp_capabilities(qmp);
302 case LIBXL__QMP_MESSAGE_TYPE_RETURN: {
303 callback_id_pair *pp = qmp_get_callback_from_id(qmp, resp);
304
305 if (pp) {
306 if (pp->callback) {
307 int rc = pp->callback(qmp,
308 libxl__json_map_get("return", resp, JSON_ANY),
309 pp->opaque);
310 if (pp->context) {
311 pp->context->rc = rc;
312 }
313 }
314 if (pp->id == qmp->wait_for_id) {
315 /* tell that the id have been processed */
316 qmp->wait_for_id = 0;
317 }
318 LIBXL_STAILQ_REMOVE(&qmp->callback_list, pp, callback_id_pair,
319 next);
320 free(pp);
321 }
322 return 0;
323 }
324 case LIBXL__QMP_MESSAGE_TYPE_ERROR:
325 qmp_handle_error_response(gc, qmp, resp);
326 return -1;
327 case LIBXL__QMP_MESSAGE_TYPE_EVENT:
328 return 0;
329 case LIBXL__QMP_MESSAGE_TYPE_INVALID:
330 return -1;
331 }
332 return 0;
333 }
334
335 /*
336 * Handler functions
337 */
338
qmp_init_handler(libxl__gc * gc,uint32_t domid)339 static libxl__qmp_handler *qmp_init_handler(libxl__gc *gc, uint32_t domid)
340 {
341 libxl__qmp_handler *qmp = NULL;
342
343 qmp = calloc(1, sizeof (libxl__qmp_handler));
344 if (qmp == NULL) {
345 LOGED(ERROR, domid, "Failed to allocate qmp_handler");
346 return NULL;
347 }
348 qmp->ctx = CTX;
349 qmp->domid = domid;
350 qmp->timeout = 5;
351
352 LIBXL_STAILQ_INIT(&qmp->callback_list);
353
354 return qmp;
355 }
356
qmp_open(libxl__qmp_handler * qmp,const char * qmp_socket_path,int timeout)357 static int qmp_open(libxl__qmp_handler *qmp, const char *qmp_socket_path,
358 int timeout)
359 {
360 int ret = -1;
361 int i = 0;
362
363 qmp->qmp_fd = socket(AF_UNIX, SOCK_STREAM, 0);
364 if (qmp->qmp_fd < 0) {
365 goto out;
366 }
367 ret = libxl_fd_set_nonblock(qmp->ctx, qmp->qmp_fd, 1);
368 if (ret) {
369 ret = -1;
370 goto out;
371 }
372 ret = libxl_fd_set_cloexec(qmp->ctx, qmp->qmp_fd, 1);
373 if (ret) {
374 ret = -1;
375 goto out;
376 }
377
378 if (sizeof (qmp->addr.sun_path) <= strlen(qmp_socket_path)) {
379 ret = -1;
380 goto out;
381 }
382 memset(&qmp->addr, 0, sizeof (qmp->addr));
383 qmp->addr.sun_family = AF_UNIX;
384 strncpy(qmp->addr.sun_path, qmp_socket_path,
385 sizeof (qmp->addr.sun_path)-1);
386
387 do {
388 ret = connect(qmp->qmp_fd, (struct sockaddr *) &qmp->addr,
389 sizeof (qmp->addr));
390 if (ret == 0)
391 break;
392 if (errno == ENOENT || errno == ECONNREFUSED) {
393 /* ENOENT : Socket may not have shown up yet
394 * ECONNREFUSED : Leftover socket hasn't been removed yet */
395 continue;
396 }
397 ret = -1;
398 goto out;
399 } while ((++i / 5 <= timeout) && (usleep(200 * 1000) <= 0));
400
401 out:
402 if (ret == -1 && qmp->qmp_fd > -1) close(qmp->qmp_fd);
403
404 return ret;
405 }
406
qmp_close(libxl__qmp_handler * qmp)407 static void qmp_close(libxl__qmp_handler *qmp)
408 {
409 callback_id_pair *pp = NULL;
410 callback_id_pair *tmp = NULL;
411
412 close(qmp->qmp_fd);
413 LIBXL_STAILQ_FOREACH(pp, &qmp->callback_list, next) {
414 free(tmp);
415 tmp = pp;
416 }
417 free(tmp);
418 }
419
qmp_next(libxl__gc * gc,libxl__qmp_handler * qmp)420 static int qmp_next(libxl__gc *gc, libxl__qmp_handler *qmp)
421 {
422 ssize_t rd;
423 char *s = NULL;
424 char *s_end = NULL;
425
426 char *incomplete = NULL;
427 size_t incomplete_size = 0;
428 int rc = 0;
429
430 do {
431 fd_set rfds;
432 int ret = 0;
433 struct timeval timeout = {
434 .tv_sec = qmp->timeout,
435 .tv_usec = 0,
436 };
437
438 FD_ZERO(&rfds);
439 FD_SET(qmp->qmp_fd, &rfds);
440
441 ret = select(qmp->qmp_fd + 1, &rfds, NULL, NULL, &timeout);
442 if (ret == 0) {
443 LOGD(ERROR, qmp->domid, "timeout");
444 return -1;
445 } else if (ret < 0) {
446 if (errno == EINTR)
447 continue;
448 LOGED(ERROR, qmp->domid, "Select error");
449 return -1;
450 }
451
452 rd = read(qmp->qmp_fd, qmp->buffer, QMP_RECEIVE_BUFFER_SIZE);
453 if (rd == 0) {
454 LOGD(ERROR, qmp->domid, "Unexpected end of socket");
455 return -1;
456 } else if (rd < 0) {
457 LOGED(ERROR, qmp->domid, "Socket read error");
458 return rd;
459 }
460 qmp->buffer[rd] = '\0';
461
462 DEBUG_REPORT_RECEIVED(qmp->domid, qmp->buffer, rd);
463
464 do {
465 char *end = NULL;
466 if (incomplete) {
467 size_t current_pos = s - incomplete;
468 incomplete = libxl__realloc(gc, incomplete,
469 incomplete_size + rd + 1);
470 strncat(incomplete + incomplete_size, qmp->buffer, rd);
471 s = incomplete + current_pos;
472 incomplete_size += rd;
473 s_end = incomplete + incomplete_size;
474 } else {
475 incomplete = libxl__strndup(gc, qmp->buffer, rd);
476 incomplete_size = rd;
477 s = incomplete;
478 s_end = s + rd;
479 rd = 0;
480 }
481
482 end = strstr(s, "\r\n");
483 if (end) {
484 libxl__json_object *o = NULL;
485
486 *end = '\0';
487
488 o = libxl__json_parse(gc, s);
489
490 if (o) {
491 rc = qmp_handle_response(gc, qmp, o);
492 } else {
493 LOGD(ERROR, qmp->domid, "Parse error of : %s", s);
494 return -1;
495 }
496
497 s = end + 2;
498 } else {
499 break;
500 }
501 } while (s < s_end);
502 } while (s < s_end);
503
504 return rc;
505 }
506
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)507 static char *qmp_send_prepare(libxl__gc *gc, libxl__qmp_handler *qmp,
508 const char *cmd, libxl__json_object *args,
509 qmp_callback_t callback, void *opaque,
510 qmp_request_context *context)
511 {
512 const unsigned char *buf = NULL;
513 char *ret = NULL;
514 libxl_yajl_length len = 0;
515 yajl_gen_status s;
516 yajl_gen hand;
517 callback_id_pair *elm = NULL;
518
519 hand = libxl_yajl_gen_alloc(NULL);
520
521 if (!hand) {
522 return NULL;
523 }
524
525 yajl_gen_map_open(hand);
526 libxl__yajl_gen_asciiz(hand, "execute");
527 libxl__yajl_gen_asciiz(hand, cmd);
528 libxl__yajl_gen_asciiz(hand, "id");
529 yajl_gen_integer(hand, ++qmp->last_id_used);
530 if (args) {
531 libxl__yajl_gen_asciiz(hand, "arguments");
532 libxl__json_object_to_yajl_gen(gc, hand, args);
533 }
534 yajl_gen_map_close(hand);
535
536 s = yajl_gen_get_buf(hand, &buf, &len);
537
538 if (s) {
539 LOGD(ERROR, qmp->domid, "Failed to generate a qmp command");
540 goto out;
541 }
542
543 elm = malloc(sizeof (callback_id_pair));
544 if (elm == NULL) {
545 LOGED(ERROR, qmp->domid, "Failed to allocate a QMP callback");
546 goto out;
547 }
548 elm->id = qmp->last_id_used;
549 elm->callback = callback;
550 elm->opaque = opaque;
551 elm->context = context;
552 LIBXL_STAILQ_INSERT_TAIL(&qmp->callback_list, elm, next);
553
554 ret = libxl__strndup(gc, (const char*)buf, len);
555
556 LOGD(DEBUG, qmp->domid, "next qmp command: '%s'", buf);
557
558 out:
559 yajl_gen_free(hand);
560 return ret;
561 }
562
qmp_send(libxl__qmp_handler * qmp,const char * cmd,libxl__json_object * args,qmp_callback_t callback,void * opaque,qmp_request_context * context)563 static int qmp_send(libxl__qmp_handler *qmp,
564 const char *cmd, libxl__json_object *args,
565 qmp_callback_t callback, void *opaque,
566 qmp_request_context *context)
567 {
568 char *buf = NULL;
569 int rc = -1;
570 GC_INIT(qmp->ctx);
571
572 buf = qmp_send_prepare(gc, qmp, cmd, args, callback, opaque, context);
573
574 if (buf == NULL) {
575 goto out;
576 }
577
578 if (libxl_write_exactly(qmp->ctx, qmp->qmp_fd, buf, strlen(buf),
579 "QMP command", "QMP socket"))
580 goto out;
581 if (libxl_write_exactly(qmp->ctx, qmp->qmp_fd, "\r\n", 2,
582 "CRLF", "QMP socket"))
583 goto out;
584
585 rc = qmp->last_id_used;
586 out:
587 GC_FREE;
588 return rc;
589 }
590
qmp_synchronous_send(libxl__qmp_handler * qmp,const char * cmd,libxl__json_object * args,qmp_callback_t callback,void * opaque,int ask_timeout)591 static int qmp_synchronous_send(libxl__qmp_handler *qmp, const char *cmd,
592 libxl__json_object *args,
593 qmp_callback_t callback, void *opaque,
594 int ask_timeout)
595 {
596 int id = 0;
597 int ret = 0;
598 GC_INIT(qmp->ctx);
599 qmp_request_context context = { .rc = 0 };
600
601 id = qmp_send(qmp, cmd, args, callback, opaque, &context);
602 if (id <= 0) {
603 return -1;
604 }
605 qmp->wait_for_id = id;
606
607 while (qmp->wait_for_id == id) {
608 if ((ret = qmp_next(gc, qmp)) < 0) {
609 break;
610 }
611 }
612
613 if (qmp->wait_for_id != id && ret == 0) {
614 ret = context.rc;
615 }
616
617 GC_FREE;
618
619 return ret;
620 }
621
qmp_free_handler(libxl__qmp_handler * qmp)622 static void qmp_free_handler(libxl__qmp_handler *qmp)
623 {
624 free(qmp);
625 }
626
627 /*
628 * QMP Parameters Helpers
629 */
qmp_parameters_common_add(libxl__gc * gc,libxl__json_object ** param,const char * name,libxl__json_object * obj)630 static void qmp_parameters_common_add(libxl__gc *gc,
631 libxl__json_object **param,
632 const char *name,
633 libxl__json_object *obj)
634 {
635 libxl__json_map_node *arg = NULL;
636
637 if (!*param) {
638 *param = libxl__json_object_alloc(gc, JSON_MAP);
639 }
640
641 GCNEW(arg);
642
643 arg->map_key = libxl__strdup(gc, name);
644 arg->obj = obj;
645
646 flexarray_append((*param)->u.map, arg);
647 }
648
qmp_parameters_add_string(libxl__gc * gc,libxl__json_object ** param,const char * name,const char * argument)649 static void qmp_parameters_add_string(libxl__gc *gc,
650 libxl__json_object **param,
651 const char *name, const char *argument)
652 {
653 libxl__json_object *obj;
654
655 obj = libxl__json_object_alloc(gc, JSON_STRING);
656 obj->u.string = libxl__strdup(gc, argument);
657
658 qmp_parameters_common_add(gc, param, name, obj);
659 }
660
qmp_parameters_add_bool(libxl__gc * gc,libxl__json_object ** param,const char * name,bool b)661 static void qmp_parameters_add_bool(libxl__gc *gc,
662 libxl__json_object **param,
663 const char *name, bool b)
664 {
665 libxl__json_object *obj;
666
667 obj = libxl__json_object_alloc(gc, JSON_BOOL);
668 obj->u.b = b;
669 qmp_parameters_common_add(gc, param, name, obj);
670 }
671
qmp_parameters_add_integer(libxl__gc * gc,libxl__json_object ** param,const char * name,const int i)672 static void qmp_parameters_add_integer(libxl__gc *gc,
673 libxl__json_object **param,
674 const char *name, const int i)
675 {
676 libxl__json_object *obj;
677
678 obj = libxl__json_object_alloc(gc, JSON_INTEGER);
679 obj->u.i = i;
680
681 qmp_parameters_common_add(gc, param, name, obj);
682 }
683
684 #define QMP_PARAMETERS_SPRINTF(args, name, format, ...) \
685 qmp_parameters_add_string(gc, args, name, GCSPRINTF(format, __VA_ARGS__))
686
687 /*
688 * API
689 */
690
libxl__qmp_initialize(libxl__gc * gc,uint32_t domid)691 libxl__qmp_handler *libxl__qmp_initialize(libxl__gc *gc, uint32_t domid)
692 {
693 int ret = 0;
694 libxl__qmp_handler *qmp = NULL;
695 char *qmp_socket;
696
697 qmp = qmp_init_handler(gc, domid);
698 if (!qmp) return NULL;
699
700 qmp_socket = GCSPRINTF("%s/qmp-libxl-%d", libxl__run_dir_path(), domid);
701 if ((ret = qmp_open(qmp, qmp_socket, QMP_SOCKET_CONNECT_TIMEOUT)) < 0) {
702 LOGED(ERROR, domid, "Connection error");
703 qmp_free_handler(qmp);
704 return NULL;
705 }
706
707 LOGD(DEBUG, domid, "connected to %s", qmp_socket);
708
709 /* Wait for the response to qmp_capabilities */
710 while (!qmp->connected) {
711 if ((ret = qmp_next(gc, qmp)) < 0) {
712 break;
713 }
714 }
715
716 if (!qmp->connected) {
717 LOGD(ERROR, domid, "Failed to connect to QMP");
718 libxl__qmp_close(qmp);
719 return NULL;
720 }
721 return qmp;
722 }
723
libxl__qmp_close(libxl__qmp_handler * qmp)724 void libxl__qmp_close(libxl__qmp_handler *qmp)
725 {
726 if (!qmp)
727 return;
728 qmp_close(qmp);
729 qmp_free_handler(qmp);
730 }
731
libxl__qmp_cleanup(libxl__gc * gc,uint32_t domid)732 void libxl__qmp_cleanup(libxl__gc *gc, uint32_t domid)
733 {
734 char *qmp_socket;
735
736 qmp_socket = GCSPRINTF("%s/qmp-libxl-%d", libxl__run_dir_path(), domid);
737 if (unlink(qmp_socket) == -1) {
738 if (errno != ENOENT) {
739 LOGED(ERROR, domid, "Failed to remove QMP socket file %s", qmp_socket);
740 }
741 }
742
743 qmp_socket = GCSPRINTF("%s/qmp-libxenstat-%d", libxl__run_dir_path(), domid);
744 if (unlink(qmp_socket) == -1) {
745 if (errno != ENOENT) {
746 LOGED(ERROR, domid, "Failed to remove QMP socket file %s", qmp_socket);
747 }
748 }
749 }
750
libxl__qmp_query_serial(libxl__qmp_handler * qmp)751 int libxl__qmp_query_serial(libxl__qmp_handler *qmp)
752 {
753 return qmp_synchronous_send(qmp, "query-chardev", NULL,
754 register_serials_chardev_callback,
755 NULL, qmp->timeout);
756 }
757
qmp_query_vnc(libxl__qmp_handler * qmp)758 static int qmp_query_vnc(libxl__qmp_handler *qmp)
759 {
760 return qmp_synchronous_send(qmp, "query-vnc", NULL,
761 qmp_register_vnc_callback,
762 NULL, qmp->timeout);
763 }
764
pci_add_callback(libxl__qmp_handler * qmp,const libxl__json_object * response,void * opaque)765 static int pci_add_callback(libxl__qmp_handler *qmp,
766 const libxl__json_object *response, void *opaque)
767 {
768 libxl_device_pci *pcidev = opaque;
769 const libxl__json_object *bus = NULL;
770 GC_INIT(qmp->ctx);
771 int i, j, rc = -1;
772 char *asked_id = GCSPRINTF(PCI_PT_QDEV_ID,
773 pcidev->bus, pcidev->dev, pcidev->func);
774
775 for (i = 0; (bus = libxl__json_array_get(response, i)); i++) {
776 const libxl__json_object *devices = NULL;
777 const libxl__json_object *device = NULL;
778 const libxl__json_object *o = NULL;
779 const char *id = NULL;
780
781 devices = libxl__json_map_get("devices", bus, JSON_ARRAY);
782
783 for (j = 0; (device = libxl__json_array_get(devices, j)); j++) {
784 o = libxl__json_map_get("qdev_id", device, JSON_STRING);
785 id = libxl__json_object_get_string(o);
786
787 if (id && strcmp(asked_id, id) == 0) {
788 int dev_slot, dev_func;
789
790 o = libxl__json_map_get("slot", device, JSON_INTEGER);
791 if (!o)
792 goto out;
793 dev_slot = libxl__json_object_get_integer(o);
794 o = libxl__json_map_get("function", device, JSON_INTEGER);
795 if (!o)
796 goto out;
797 dev_func = libxl__json_object_get_integer(o);
798
799 pcidev->vdevfn = PCI_DEVFN(dev_slot, dev_func);
800
801 rc = 0;
802 goto out;
803 }
804 }
805 }
806
807
808 out:
809 GC_FREE;
810 return rc;
811 }
812
qmp_run_command(libxl__gc * gc,int domid,const char * cmd,libxl__json_object * args,qmp_callback_t callback,void * opaque)813 static int qmp_run_command(libxl__gc *gc, int domid,
814 const char *cmd, libxl__json_object *args,
815 qmp_callback_t callback, void *opaque)
816 {
817 libxl__qmp_handler *qmp = NULL;
818 int rc = 0;
819
820 qmp = libxl__qmp_initialize(gc, domid);
821 if (!qmp)
822 return ERROR_FAIL;
823
824 rc = qmp_synchronous_send(qmp, cmd, args, callback, opaque, qmp->timeout);
825
826 libxl__qmp_close(qmp);
827 return rc;
828 }
829
libxl__qmp_run_command_flexarray(libxl__gc * gc,int domid,const char * cmd,flexarray_t * array)830 int libxl__qmp_run_command_flexarray(libxl__gc *gc, int domid,
831 const char *cmd, flexarray_t *array)
832 {
833 libxl__json_object *args = NULL;
834 int i;
835 void *name, *value;
836
837 for (i = 0; i < array->count; i += 2) {
838 flexarray_get(array, i, &name);
839 flexarray_get(array, i + 1, &value);
840 qmp_parameters_add_string(gc, &args, (char *)name, (char *)value);
841 }
842
843 return qmp_run_command(gc, domid, cmd, args, NULL, NULL);
844 }
845
libxl__qmp_pci_add(libxl__gc * gc,int domid,libxl_device_pci * pcidev)846 int libxl__qmp_pci_add(libxl__gc *gc, int domid, libxl_device_pci *pcidev)
847 {
848 libxl__qmp_handler *qmp = NULL;
849 libxl__json_object *args = NULL;
850 char *hostaddr = NULL;
851 int rc = 0;
852
853 qmp = libxl__qmp_initialize(gc, domid);
854 if (!qmp)
855 return -1;
856
857 hostaddr = GCSPRINTF("%04x:%02x:%02x.%01x", pcidev->domain,
858 pcidev->bus, pcidev->dev, pcidev->func);
859 if (!hostaddr)
860 return -1;
861
862 qmp_parameters_add_string(gc, &args, "driver", "xen-pci-passthrough");
863 QMP_PARAMETERS_SPRINTF(&args, "id", PCI_PT_QDEV_ID,
864 pcidev->bus, pcidev->dev, pcidev->func);
865 qmp_parameters_add_string(gc, &args, "hostaddr", hostaddr);
866 if (pcidev->vdevfn) {
867 QMP_PARAMETERS_SPRINTF(&args, "addr", "%x.%x",
868 PCI_SLOT(pcidev->vdevfn), PCI_FUNC(pcidev->vdevfn));
869 }
870 /*
871 * Version of QEMU prior to the XSA-131 fix did not support this
872 * property and were effectively always in permissive mode. The
873 * fix for XSA-131 switched the default to be restricted by
874 * default and added the permissive property.
875 *
876 * Therefore in order to support both old and new QEMU we only set
877 * the permissive flag if it is true. Users of older QEMU have no
878 * reason to set the flag so this is ok.
879 */
880 if (pcidev->permissive)
881 qmp_parameters_add_bool(gc, &args, "permissive", true);
882
883 rc = qmp_synchronous_send(qmp, "device_add", args,
884 NULL, NULL, qmp->timeout);
885 if (rc == 0) {
886 rc = qmp_synchronous_send(qmp, "query-pci", NULL,
887 pci_add_callback, pcidev, qmp->timeout);
888 }
889
890 libxl__qmp_close(qmp);
891 return rc;
892 }
893
qmp_device_del(libxl__gc * gc,int domid,char * id)894 static int qmp_device_del(libxl__gc *gc, int domid, char *id)
895 {
896 libxl__json_object *args = NULL;
897
898 qmp_parameters_add_string(gc, &args, "id", id);
899 return qmp_run_command(gc, domid, "device_del", args, NULL, NULL);
900 }
901
libxl__qmp_pci_del(libxl__gc * gc,int domid,libxl_device_pci * pcidev)902 int libxl__qmp_pci_del(libxl__gc *gc, int domid, libxl_device_pci *pcidev)
903 {
904 char *id = NULL;
905
906 id = GCSPRINTF(PCI_PT_QDEV_ID, pcidev->bus, pcidev->dev, pcidev->func);
907
908 return qmp_device_del(gc, domid, id);
909 }
910
libxl__qmp_system_wakeup(libxl__gc * gc,int domid)911 int libxl__qmp_system_wakeup(libxl__gc *gc, int domid)
912 {
913 return qmp_run_command(gc, domid, "system_wakeup", NULL, NULL, NULL);
914 }
915
libxl__qmp_save(libxl__gc * gc,int domid,const char * filename)916 int libxl__qmp_save(libxl__gc *gc, int domid, const char *filename)
917 {
918 libxl__json_object *args = NULL;
919
920 qmp_parameters_add_string(gc, &args, "filename", (char *)filename);
921 return qmp_run_command(gc, domid, "xen-save-devices-state", args,
922 NULL, NULL);
923 }
924
libxl__qmp_restore(libxl__gc * gc,int domid,const char * state_file)925 int libxl__qmp_restore(libxl__gc *gc, int domid, const char *state_file)
926 {
927 libxl__json_object *args = NULL;
928
929 qmp_parameters_add_string(gc, &args, "filename", state_file);
930
931 return qmp_run_command(gc, domid, "xen-load-devices-state", args,
932 NULL, NULL);
933 }
934
qmp_change(libxl__gc * gc,libxl__qmp_handler * qmp,char * device,char * target,char * arg)935 static int qmp_change(libxl__gc *gc, libxl__qmp_handler *qmp,
936 char *device, char *target, char *arg)
937 {
938 libxl__json_object *args = NULL;
939 int rc = 0;
940
941 qmp_parameters_add_string(gc, &args, "device", device);
942 qmp_parameters_add_string(gc, &args, "target", target);
943 if (arg) {
944 qmp_parameters_add_string(gc, &args, "arg", arg);
945 }
946
947 rc = qmp_synchronous_send(qmp, "change", args,
948 NULL, NULL, qmp->timeout);
949
950 return rc;
951 }
952
libxl__qmp_stop(libxl__gc * gc,int domid)953 int libxl__qmp_stop(libxl__gc *gc, int domid)
954 {
955 return qmp_run_command(gc, domid, "stop", NULL, NULL, NULL);
956 }
957
libxl__qmp_resume(libxl__gc * gc,int domid)958 int libxl__qmp_resume(libxl__gc *gc, int domid)
959 {
960 return qmp_run_command(gc, domid, "cont", NULL, NULL, NULL);
961 }
962
libxl__qmp_set_global_dirty_log(libxl__gc * gc,int domid,bool enable)963 int libxl__qmp_set_global_dirty_log(libxl__gc *gc, int domid, bool enable)
964 {
965 libxl__json_object *args = NULL;
966
967 qmp_parameters_add_bool(gc, &args, "enable", enable);
968
969 return qmp_run_command(gc, domid, "xen-set-global-dirty-log", args,
970 NULL, NULL);
971 }
972
libxl__qmp_insert_cdrom(libxl__gc * gc,int domid,const libxl_device_disk * disk)973 int libxl__qmp_insert_cdrom(libxl__gc *gc, int domid,
974 const libxl_device_disk *disk)
975 {
976 libxl__json_object *args = NULL;
977 int dev_number = libxl__device_disk_dev_number(disk->vdev, NULL, NULL);
978
979 QMP_PARAMETERS_SPRINTF(&args, "device", "ide-%i", dev_number);
980
981 if (disk->format == LIBXL_DISK_FORMAT_EMPTY) {
982 return qmp_run_command(gc, domid, "eject", args, NULL, NULL);
983 } else {
984 qmp_parameters_add_string(gc, &args, "target", disk->pdev_path);
985 return qmp_run_command(gc, domid, "change", args, NULL, NULL);
986 }
987 }
988
libxl__qmp_cpu_add(libxl__gc * gc,int domid,int idx)989 int libxl__qmp_cpu_add(libxl__gc *gc, int domid, int idx)
990 {
991 libxl__json_object *args = NULL;
992
993 qmp_parameters_add_integer(gc, &args, "id", idx);
994
995 return qmp_run_command(gc, domid, "cpu-add", args, NULL, NULL);
996 }
997
query_cpus_callback(libxl__qmp_handler * qmp,const libxl__json_object * response,void * opaque)998 static int query_cpus_callback(libxl__qmp_handler *qmp,
999 const libxl__json_object *response,
1000 void *opaque)
1001 {
1002 libxl_bitmap *map = opaque;
1003 unsigned int i;
1004 const libxl__json_object *cpu = NULL;
1005 int rc;
1006 GC_INIT(qmp->ctx);
1007
1008 libxl_bitmap_set_none(map);
1009 for (i = 0; (cpu = libxl__json_array_get(response, i)); i++) {
1010 unsigned int idx;
1011 const libxl__json_object *o;
1012
1013 o = libxl__json_map_get("CPU", cpu, JSON_INTEGER);
1014 if (!o) {
1015 LOGD(ERROR, qmp->domid, "Failed to retrieve CPU index.");
1016 rc = ERROR_FAIL;
1017 goto out;
1018 }
1019
1020 idx = libxl__json_object_get_integer(o);
1021 libxl_bitmap_set(map, idx);
1022 }
1023
1024 rc = 0;
1025 out:
1026 GC_FREE;
1027 return rc;
1028 }
1029
libxl__qmp_query_cpus(libxl__gc * gc,int domid,libxl_bitmap * map)1030 int libxl__qmp_query_cpus(libxl__gc *gc, int domid, libxl_bitmap *map)
1031 {
1032 return qmp_run_command(gc, domid, "query-cpus", NULL,
1033 query_cpus_callback, map);
1034 }
1035
libxl__qmp_nbd_server_start(libxl__gc * gc,int domid,const char * host,const char * port)1036 int libxl__qmp_nbd_server_start(libxl__gc *gc, int domid,
1037 const char *host, const char *port)
1038 {
1039 libxl__json_object *args = NULL;
1040 libxl__json_object *addr = NULL;
1041 libxl__json_object *data = NULL;
1042
1043 /* 'addr': {
1044 * 'type': 'inet',
1045 * 'data': {
1046 * 'host': '$nbd_host',
1047 * 'port': '$nbd_port'
1048 * }
1049 * }
1050 */
1051 qmp_parameters_add_string(gc, &data, "host", host);
1052 qmp_parameters_add_string(gc, &data, "port", port);
1053
1054 qmp_parameters_add_string(gc, &addr, "type", "inet");
1055 qmp_parameters_common_add(gc, &addr, "data", data);
1056
1057 qmp_parameters_common_add(gc, &args, "addr", addr);
1058
1059 return qmp_run_command(gc, domid, "nbd-server-start", args, NULL, NULL);
1060 }
1061
libxl__qmp_nbd_server_add(libxl__gc * gc,int domid,const char * disk)1062 int libxl__qmp_nbd_server_add(libxl__gc *gc, int domid, const char *disk)
1063 {
1064 libxl__json_object *args = NULL;
1065
1066 qmp_parameters_add_string(gc, &args, "device", disk);
1067 qmp_parameters_add_bool(gc, &args, "writable", true);
1068
1069 return qmp_run_command(gc, domid, "nbd-server-add", args, NULL, NULL);
1070 }
1071
libxl__qmp_start_replication(libxl__gc * gc,int domid,bool primary)1072 int libxl__qmp_start_replication(libxl__gc *gc, int domid, bool primary)
1073 {
1074 libxl__json_object *args = NULL;
1075
1076 qmp_parameters_add_bool(gc, &args, "enable", true);
1077 qmp_parameters_add_bool(gc, &args, "primary", primary);
1078
1079 return qmp_run_command(gc, domid, "xen-set-replication", args, NULL, NULL);
1080 }
1081
libxl__qmp_query_xen_replication_status(libxl__gc * gc,int domid)1082 int libxl__qmp_query_xen_replication_status(libxl__gc *gc, int domid)
1083 {
1084 return qmp_run_command(gc, domid, "query-xen-replication-status", NULL,
1085 NULL, NULL);
1086 }
1087
libxl__qmp_colo_do_checkpoint(libxl__gc * gc,int domid)1088 int libxl__qmp_colo_do_checkpoint(libxl__gc *gc, int domid)
1089 {
1090 return qmp_run_command(gc, domid, "xen-colo-do-checkpoint",
1091 NULL, NULL, NULL);
1092 }
1093
libxl__qmp_stop_replication(libxl__gc * gc,int domid,bool primary)1094 int libxl__qmp_stop_replication(libxl__gc *gc, int domid, bool primary)
1095 {
1096 libxl__json_object *args = NULL;
1097
1098 qmp_parameters_add_bool(gc, &args, "enable", false);
1099 qmp_parameters_add_bool(gc, &args, "primary", primary);
1100
1101 return qmp_run_command(gc, domid, "xen-set-replication", args, NULL, NULL);
1102 }
1103
libxl__qmp_nbd_server_stop(libxl__gc * gc,int domid)1104 int libxl__qmp_nbd_server_stop(libxl__gc *gc, int domid)
1105 {
1106 return qmp_run_command(gc, domid, "nbd-server-stop", NULL, NULL, NULL);
1107 }
1108
libxl__qmp_x_blockdev_change(libxl__gc * gc,int domid,const char * parent,const char * child,const char * node)1109 int libxl__qmp_x_blockdev_change(libxl__gc *gc, int domid, const char *parent,
1110 const char *child, const char *node)
1111 {
1112 libxl__json_object *args = NULL;
1113
1114 qmp_parameters_add_string(gc, &args, "parent", parent);
1115 if (child)
1116 qmp_parameters_add_string(gc, &args, "child", child);
1117 if (node)
1118 qmp_parameters_add_string(gc, &args, "node", node);
1119
1120 return qmp_run_command(gc, domid, "x-blockdev-change", args, NULL, NULL);
1121 }
1122
hmp_callback(libxl__qmp_handler * qmp,const libxl__json_object * response,void * opaque)1123 static int hmp_callback(libxl__qmp_handler *qmp,
1124 const libxl__json_object *response,
1125 void *opaque)
1126 {
1127 char **output = opaque;
1128 GC_INIT(qmp->ctx);
1129 int rc;
1130
1131 rc = 0;
1132 if (!output)
1133 goto out;
1134
1135 *output = NULL;
1136
1137 if (libxl__json_object_is_string(response)) {
1138 *output = libxl__strdup(NOGC, libxl__json_object_get_string(response));
1139 goto out;
1140 }
1141
1142 LOG(ERROR, "Response has unexpected format");
1143 rc = ERROR_FAIL;
1144
1145 out:
1146 GC_FREE;
1147 return rc;
1148 }
1149
libxl__qmp_hmp(libxl__gc * gc,int domid,const char * command_line,char ** output)1150 int libxl__qmp_hmp(libxl__gc *gc, int domid, const char *command_line,
1151 char **output)
1152 {
1153 libxl__json_object *args = NULL;
1154
1155 qmp_parameters_add_string(gc, &args, "command-line", command_line);
1156
1157 return qmp_run_command(gc, domid, "human-monitor-command", args,
1158 hmp_callback, output);
1159 }
1160
libxl_qemu_monitor_command(libxl_ctx * ctx,uint32_t domid,const char * command_line,char ** output)1161 int libxl_qemu_monitor_command(libxl_ctx *ctx, uint32_t domid,
1162 const char *command_line, char **output)
1163 {
1164 GC_INIT(ctx);
1165 int rc;
1166
1167 rc = libxl__qmp_hmp(gc, domid, command_line, output);
1168
1169 GC_FREE;
1170 return rc;
1171 }
1172
libxl__qmp_initializations(libxl__gc * gc,uint32_t domid,const libxl_domain_config * guest_config)1173 int libxl__qmp_initializations(libxl__gc *gc, uint32_t domid,
1174 const libxl_domain_config *guest_config)
1175 {
1176 const libxl_vnc_info *vnc = libxl__dm_vnc(guest_config);
1177 libxl__qmp_handler *qmp = NULL;
1178 int ret = 0;
1179
1180 qmp = libxl__qmp_initialize(gc, domid);
1181 if (!qmp)
1182 return -1;
1183 ret = libxl__qmp_query_serial(qmp);
1184 if (!ret && vnc && vnc->passwd) {
1185 ret = qmp_change(gc, qmp, "vnc", "password", vnc->passwd);
1186 qmp_write_domain_console_item(gc, domid, "vnc-pass", vnc->passwd);
1187 }
1188 if (!ret) {
1189 ret = qmp_query_vnc(qmp);
1190 }
1191 libxl__qmp_close(qmp);
1192 return ret;
1193 }
1194
1195 /*
1196 * Local variables:
1197 * mode: C
1198 * c-basic-offset: 4
1199 * indent-tabs-mode: nil
1200 * End:
1201 */
1202