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