virtagent: Makefile fixups
[qemu/mdroth.git] / qemu-vp.c
blob0e8d876d6031d14ad8f573d8a7d6cc3849bb14c1
1 /*
2 * virt-proxy - host/guest communication daemon
4 * Copyright IBM Corp. 2010
6 * Authors:
7 * Michael Roth <mdroth@linux.vnet.ibm.com>
9 * This work is licensed under the terms of the GNU GPL, version 2 or later.
10 * See the COPYING file in the top-level directory.
12 * QEMU System Emulator
14 * Copyright (c) 2003-2008 Fabrice Bellard
16 * Permission is hereby granted, free of charge, to any person obtaining a copy
17 * of this software and associated documentation files (the "Software"), to deal
18 * in the Software without restriction, including without limitation the rights
19 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
20 * copies of the Software, and to permit persons to whom the Software is
21 * furnished to do so, subject to the following conditions:
23 * The above copyright notice and this permission notice shall be included in
24 * all copies or substantial portions of the Software.
26 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
27 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
28 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
29 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
30 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
31 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
32 * THE SOFTWARE.
35 #include <getopt.h>
36 #include <err.h>
37 #include "qemu-option.h"
38 #include "qemu_socket.h"
39 #include "virtproxy.h"
40 #include "virtagent.h"
41 #include "virtagent-daemon.h"
43 static bool verbose_enabled = 0;
44 #define DEBUG_ENABLED
46 #ifdef DEBUG_ENABLED
47 #define DEBUG(msg, ...) do { \
48 fprintf(stderr, "%s:%s():L%d: " msg "\n", \
49 __FILE__, __FUNCTION__, __LINE__, ## __VA_ARGS__); \
50 } while(0)
51 #else
52 #define DEBUG(msg, ...) do {} while (0)
53 #endif
55 #define INFO(msg, ...) do { \
56 if (!verbose_enabled) { \
57 break; \
58 } \
59 warnx(msg, ## __VA_ARGS__); \
60 } while(0)
62 /* mirror qemu I/O-related code for standalone daemon */
63 typedef struct IOHandlerRecord {
64 int fd;
65 IOCanReadHandler *fd_read_poll;
66 IOHandler *fd_read;
67 IOHandler *fd_write;
68 int deleted;
69 void *opaque;
70 /* temporary data */
71 struct pollfd *ufd;
72 QLIST_ENTRY(IOHandlerRecord) next;
73 } IOHandlerRecord;
75 static QLIST_HEAD(, IOHandlerRecord) io_handlers =
76 QLIST_HEAD_INITIALIZER(io_handlers);
78 int vp_set_fd_handler2(int fd,
79 IOCanReadHandler *fd_read_poll,
80 IOHandler *fd_read,
81 IOHandler *fd_write,
82 void *opaque)
84 IOHandlerRecord *ioh;
86 if (!fd_read && !fd_write) {
87 QLIST_FOREACH(ioh, &io_handlers, next) {
88 if (ioh->fd == fd) {
89 ioh->deleted = 1;
90 break;
93 } else {
94 QLIST_FOREACH(ioh, &io_handlers, next) {
95 if (ioh->fd == fd)
96 goto found;
98 ioh = qemu_mallocz(sizeof(IOHandlerRecord));
99 QLIST_INSERT_HEAD(&io_handlers, ioh, next);
100 found:
101 ioh->fd = fd;
102 ioh->fd_read_poll = fd_read_poll;
103 ioh->fd_read = fd_read;
104 ioh->fd_write = fd_write;
105 ioh->opaque = opaque;
106 ioh->deleted = 0;
108 return 0;
111 int vp_set_fd_handler(int fd,
112 IOHandler *fd_read,
113 IOHandler *fd_write,
114 void *opaque)
116 return vp_set_fd_handler2(fd, NULL, fd_read, fd_write, opaque);
119 int vp_send_all(int fd, const void *buf, int len1)
121 int ret, len;
123 len = len1;
124 while (len > 0) {
125 ret = write(fd, buf, len);
126 if (ret < 0) {
127 if (errno != EINTR && errno != EAGAIN) {
128 warn("write() failed");
129 return -1;
131 } else if (ret == 0) {
132 break;
133 } else {
134 buf += ret;
135 len -= ret;
138 return len1 - len;
141 /* qemu_chr_read doesn't get used in guest so noop our wrapper function */
142 void vp_chr_read(CharDriverState *s, uint8_t *buf, int len)
144 return;
147 static void main_loop_wait(int nonblocking)
149 IOHandlerRecord *ioh;
150 fd_set rfds, wfds, xfds;
151 int ret, nfds;
152 struct timeval tv;
153 int timeout = 100000;
155 if (nonblocking) {
156 timeout = 0;
159 /* poll any events */
160 nfds = -1;
161 FD_ZERO(&rfds);
162 FD_ZERO(&wfds);
163 FD_ZERO(&xfds);
164 QLIST_FOREACH(ioh, &io_handlers, next) {
165 if (ioh->deleted)
166 continue;
167 if (ioh->fd_read &&
168 (!ioh->fd_read_poll ||
169 ioh->fd_read_poll(ioh->opaque) != 0)) {
170 FD_SET(ioh->fd, &rfds);
171 if (ioh->fd > nfds)
172 nfds = ioh->fd;
174 if (ioh->fd_write) {
175 FD_SET(ioh->fd, &wfds);
176 if (ioh->fd > nfds)
177 nfds = ioh->fd;
181 tv.tv_sec = timeout / 1000;
182 tv.tv_usec = (timeout % 1000) * 1000;
184 ret = select(nfds + 1, &rfds, &wfds, &xfds, &tv);
186 if (ret > 0) {
187 IOHandlerRecord *pioh;
189 QLIST_FOREACH_SAFE(ioh, &io_handlers, next, pioh) {
190 if (ioh->deleted) {
191 QLIST_REMOVE(ioh, next);
192 qemu_free(ioh);
193 continue;
195 if (ioh->fd_read && FD_ISSET(ioh->fd, &rfds)) {
196 ioh->fd_read(ioh->opaque);
198 if (ioh->fd_write && FD_ISSET(ioh->fd, &wfds)) {
199 ioh->fd_write(ioh->opaque);
205 #define VP_ARG_LEN 256
207 static QemuOptsList vp_opts = {
208 .name = "vpargs",
209 .head = QTAILQ_HEAD_INITIALIZER(vp_opts.head),
210 .desc = {
212 .name = "service_id",
213 .type = QEMU_OPT_STRING,
215 .name = "channel_method",
216 .type = QEMU_OPT_STRING,
218 .name = "index",
219 .type = QEMU_OPT_NUMBER,
221 .name = "path",
222 .type = QEMU_OPT_STRING,
224 .name = "host",
225 .type = QEMU_OPT_STRING,
227 .name = "port",
228 .type = QEMU_OPT_STRING,
230 .name = "ipv4",
231 .type = QEMU_OPT_BOOL,
233 .name = "ipv6",
234 .type = QEMU_OPT_BOOL,
236 { /* end if list */ }
240 typedef struct VPData {
241 QemuOpts *opts;
242 void *opaque;
243 QTAILQ_ENTRY(VPData) next;
244 } VPData;
246 static QTAILQ_HEAD(, VPData) iforwards;
247 static QTAILQ_HEAD(, VPData) oforwards;
248 static QTAILQ_HEAD(, VPData) channels;
250 static void usage(const char *cmd)
252 printf(
253 "Usage: %s -c <channel_opts> [-c ... ] [-i <iforward_opts> ...] "
254 "[-o <oforward_opts> ...]\n"
255 "QEMU virt-proxy communication channel\n"
256 "\n"
257 " -c, --channel channel options of the form:\n"
258 " <method>:<addr>:<port>[:channel_id]\n"
259 " -g, --guest-agent guest rpc server, options of the form:\n"
260 " [channel_id]\n"
261 " -o, --oforward oforward options of the form:\n"
262 " <service_id>:<addr>:<port>[:channel_id]\n"
263 " -i, --iforward iforward options of the form:\n"
264 " <service_id>:<addr>:<port>[:channel_id]\n"
265 " -v, --verbose display extra debugging information\n"
266 " -h, --help display this help and exit\n"
267 "\n"
268 " channels are used to establish a data connection between 2 end-points in\n"
269 " the host or the guest (connection method specified by <method>).\n"
270 " oforwards specify a socket to listen for new connections on, outgoing\n"
271 " data from which is tagged with <service_id> before being sent over the\n"
272 " channel. iforwards specify a socket to route incoming data/connections\n"
273 " with a specific <service_id> to. The positional parameters for\n"
274 " channels/iforwards/oforwards are:\n"
275 "\n"
276 " <method>: one of unix-connect, unix-listen, tcp-connect, tcp-listen,\n"
277 " virtserial-open\n"
278 " <addr>: path of unix socket or virtserial port, or IP of host, to\n"
279 " connect/bind to\n"
280 " <port>: port to bind/connect to, or '-' if addr is a path\n"
281 " <service_id>: an identifier used to properly route connections to the\n"
282 " corresponding host or guest daemon socket.\n"
283 " <channel_id>: numerical id to identify what channel to use for an iforward\n"
284 " or oforward. (default is 0)\n"
285 "\n"
286 "Report bugs to <mdroth@linux.vnet.ibm.com>\n"
287 , cmd);
290 static int vp_parse(QemuOpts *opts, const char *str, bool is_channel)
292 /* TODO: use VP_SERVICE_ID_LEN, bring it into virtproxy.h */
293 char service_id[32];
294 char channel_method[32];
295 char index[10];
296 char *addr;
297 char port[33];
298 int pos, ret;
300 if (is_channel == false) {
301 /* parse service id */
302 ret = sscanf(str,"%32[^:]:%n",service_id,&pos);
303 if (ret != 1) {
304 warn("error parsing service id");
305 return -1;
307 qemu_opt_set(opts, "service_id", service_id);
308 } else {
309 /* parse connection type */
310 ret = sscanf(str,"%32[^:]:%n",channel_method,&pos);
311 if (ret != 1) {
312 warn("error parsing channel method");
313 return -1;
315 qemu_opt_set(opts, "channel_method", channel_method);
317 str += pos;
318 pos = 0;
320 /* parse path/addr and port */
321 if (str[0] == '[') {
322 /* ipv6 formatted */
323 ret = sscanf(str,"[%a[^]:]]:%32[^:]%n",&addr,port,&pos);
324 qemu_opt_set(opts, "ipv6", "on");
325 } else {
326 ret = sscanf(str,"%a[^:]:%32[^:]%n",&addr,port,&pos);
327 qemu_opt_set(opts, "ipv4", "on");
330 if (ret != 2) {
331 warnx("error parsing path/addr/port");
332 return -1;
333 } else if (port[0] == '-') {
334 /* no port given, assume unix path */
335 qemu_opt_set(opts, "path", addr);
336 } else {
337 qemu_opt_set(opts, "host", addr);
338 qemu_opt_set(opts, "port", port);
339 qemu_free(addr);
341 str += pos;
342 pos = 0;
344 if (str[0] == ':') {
345 /* parse optional index parameter */
346 ret = sscanf(str,":%10[^:]%n",index,&pos);
347 } else {
348 qemu_opt_set(opts, "index", "0");
349 return 0;
352 if (ret != 1) {
353 warnx("error parsing index");
354 return -1;
355 } else {
356 qemu_opt_set(opts, "index", index);
358 str += pos;
359 pos = 0;
361 return 0;
364 static VPDriver *get_channel_drv(int index) {
365 VPData *data;
366 VPDriver *drv;
367 int cindex;
369 QTAILQ_FOREACH(data, &channels, next) {
370 cindex = qemu_opt_get_number(data->opts, "index", 0);
371 if (cindex == index) {
372 drv = data->opaque;
373 return drv;
377 return NULL;
380 static int init_channels(void) {
381 VPDriver *drv;
382 VPData *channel_data;
383 const char *channel_method, *path;
384 int fd, ret;
385 bool listen;
387 if (QTAILQ_EMPTY(&channels)) {
388 warnx("no channel specified");
389 return -1;
392 channel_data = QTAILQ_FIRST(&channels);
394 /* TODO: add this support, optional idx param for -i/-o/-c
395 * args should suffice
397 if (QTAILQ_NEXT(channel_data, next) != NULL) {
398 warnx("multiple channels not currently supported, defaulting to first");
401 INFO("initializing channel...");
402 if (verbose_enabled) {
403 qemu_opts_print(channel_data->opts, NULL);
406 channel_method = qemu_opt_get(channel_data->opts, "channel_method");
408 if (strcmp(channel_method, "tcp-listen") == 0) {
409 fd = inet_listen_opts(channel_data->opts, 0);
410 listen = true;
411 } else if (strcmp(channel_method, "tcp-connect") == 0) {
412 fd = inet_connect_opts(channel_data->opts);
413 listen = false;
414 } else if (strcmp(channel_method, "unix-listen") == 0) {
415 fd = unix_listen_opts(channel_data->opts);
416 listen = true;
417 } else if (strcmp(channel_method, "unix-connect") == 0) {
418 fd = unix_connect_opts(channel_data->opts);
419 listen = false;
420 } else if (strcmp(channel_method, "virtserial-open") == 0) {
421 path = qemu_opt_get(channel_data->opts, "path");
422 fd = qemu_open(path, O_RDWR);
423 ret = fcntl(fd, F_GETFL);
424 ret = fcntl(fd, F_SETFL, ret | O_ASYNC);
425 if (ret < 0) {
426 warn("error setting flags for fd");
427 return -1;
429 listen = false;
430 } else {
431 warnx("invalid channel type: %s", channel_method);
432 return -1;
435 if (fd == -1) {
436 warn("error opening connection");
437 return -1;
440 drv = vp_new(VP_CTX_FD, NULL, fd, listen);
441 channel_data->opaque = drv;
443 return 0;
446 static int init_oforwards(void) {
447 VPDriver *drv;
448 VPData *oforward_data;
449 int index, ret, fd;
450 const char *service_id;
452 QTAILQ_FOREACH(oforward_data, &oforwards, next) {
453 INFO("initializing oforward...");
454 if (verbose_enabled) {
455 qemu_opts_print(oforward_data->opts, NULL);
458 index = qemu_opt_get_number(oforward_data->opts, "index", 0);
459 drv = get_channel_drv(index);
460 if (drv == NULL) {
461 warnx("unable to find channel with index: %d", index);
462 return -1;
465 if (qemu_opt_get(oforward_data->opts, "host") != NULL) {
466 fd = inet_listen_opts(oforward_data->opts, 0);
467 } else if (qemu_opt_get(oforward_data->opts, "path") != NULL) {
468 fd = unix_listen_opts(oforward_data->opts);
469 } else {
470 warnx("unable to find listening socket host/addr info");
471 return -1;
474 if (fd == -1) {
475 warnx("failed to create FD");
476 return -1;
479 service_id = qemu_opt_get(oforward_data->opts, "service_id");
481 if (service_id == NULL) {
482 warnx("no service_id specified");
483 return -1;
486 ret = vp_set_oforward(drv, fd, service_id);
489 return 0;
492 static int init_iforwards(void) {
493 VPDriver *drv;
494 VPData *iforward_data;
495 int index, ret;
496 const char *service_id, *addr, *port;
497 bool ipv6;
499 QTAILQ_FOREACH(iforward_data, &iforwards, next) {
500 INFO("initializing iforward...");
501 if (verbose_enabled) {
502 qemu_opts_print(iforward_data->opts, NULL);
505 index = qemu_opt_get_number(iforward_data->opts, "index", 0);
506 drv = get_channel_drv(index);
507 if (drv == NULL) {
508 warnx("unable to find channel with index: %d", index);
509 return -1;
512 service_id = qemu_opt_get(iforward_data->opts, "service_id");
513 if (service_id == NULL) {
514 warnx("no service_id specified");
515 return -1;
518 addr = qemu_opt_get(iforward_data->opts, "path");
519 port = NULL;
521 if (addr == NULL) {
522 /* map service to a network socket instead */
523 addr = qemu_opt_get(iforward_data->opts, "host");
524 port = qemu_opt_get(iforward_data->opts, "port");
527 ipv6 = qemu_opt_get_bool(iforward_data->opts, "ipv6", 0) ?
528 true : false;
530 ret = vp_set_iforward(drv, service_id, addr, port, ipv6);
531 if (ret != 0) {
532 warnx("error adding iforward");
533 return -1;
537 return 0;
540 static int init_agent(const VPData *agent_iforward) {
541 QemuOpts *opts = agent_iforward->opts;
542 VPDriver *drv;
543 int ret, index;
545 INFO("initializing agent...");
546 if (verbose_enabled) {
547 qemu_opts_print(opts, NULL);
550 index = qemu_opt_get_number(agent_iforward->opts, "index", 0);
551 drv = get_channel_drv(index);
552 if (drv == NULL) {
553 warnx("unable to find channel with index: %d", index);
554 goto err;
557 /* outbound RPCs */
558 ret = va_client_init(drv, false);
559 if (ret) {
560 warnx("error starting RPC server");
561 goto err;
564 /* start guest RPC server */
565 ret = va_server_init(drv, false);
566 if (ret != 0) {
567 warnx("error starting RPC server");
568 goto err;
571 return 0;
573 err:
574 return -1;
577 int main(int argc, char **argv)
579 const char *sopt = "hVvi:o:c:g::p::";
580 struct option lopt[] = {
581 { "help", 0, NULL, 'h' },
582 { "version", 0, NULL, 'V' },
583 { "verbose", 0, NULL, 'v' },
584 { "host-agent", 0, NULL, 'p' },
585 { "guest-agent", 0, NULL, 'g' },
586 { "iforward", 0, NULL, 'i' },
587 { "oforward", 0, NULL, 'o' },
588 { "channel", 0, NULL, 'c' },
589 { NULL, 0, NULL, 0 }
591 int opt_ind = 0, ch, ret;
592 QTAILQ_INIT(&iforwards);
593 QTAILQ_INIT(&oforwards);
594 QTAILQ_INIT(&channels);
595 VPData *guest_agent_iforward = NULL;
597 while ((ch = getopt_long(argc, argv, sopt, lopt, &opt_ind)) != -1) {
598 QemuOpts *opts;
599 VPData *data;
600 char optarg_tmp[VP_ARG_LEN];
601 switch (ch) {
602 case 'i':
603 opts = qemu_opts_create(&vp_opts, NULL, 0);
604 ret = vp_parse(opts, optarg, 0);
605 if (ret) {
606 errx(EXIT_FAILURE, "error parsing arg: %s", optarg);
608 data = qemu_mallocz(sizeof(VPData));
609 data->opts = opts;
610 QTAILQ_INSERT_TAIL(&iforwards, data, next);
611 break;
612 case 'o':
613 opts = qemu_opts_create(&vp_opts, NULL, 0);
614 ret = vp_parse(opts, optarg, 0);
615 if (ret) {
616 errx(EXIT_FAILURE, "error parsing arg: %s", optarg);
618 data = qemu_mallocz(sizeof(VPData));
619 data->opts = opts;
620 QTAILQ_INSERT_TAIL(&oforwards, data, next);
621 break;
622 case 'c':
623 opts = qemu_opts_create(&vp_opts, NULL, 0);
624 ret = vp_parse(opts, optarg, 1);
625 if (ret) {
626 errx(EXIT_FAILURE, "error parsing arg: %s", optarg);
628 data = qemu_mallocz(sizeof(VPData));
629 data->opts = opts;
630 QTAILQ_INSERT_TAIL(&channels, data, next);
631 break;
632 case 'g':
633 /* create pre-baked iforward for guest agent */
634 if (guest_agent_iforward) {
635 errx(EXIT_FAILURE, "only one --guest-agent argument allowed");
637 opts = qemu_opts_create(&vp_opts, NULL, 0);
638 if (optarg == 0) {
639 sprintf(optarg_tmp, "%s:%s:-", GUEST_AGENT_SERVICE_ID,
640 GUEST_AGENT_PATH);
641 } else {
642 sprintf(optarg_tmp, "%s:%s:-:%d", GUEST_AGENT_SERVICE_ID,
643 GUEST_AGENT_PATH, atoi(optarg));
645 ret = vp_parse(opts, optarg_tmp, 0);
646 if (ret) {
647 errx(EXIT_FAILURE, "error parsing arg: %s", optarg);
649 data = qemu_mallocz(sizeof(VPData));
650 data->opts = opts;
651 QTAILQ_INSERT_TAIL(&iforwards, data, next);
652 guest_agent_iforward = data;
653 break;
654 case 'v':
655 verbose_enabled = 1;
656 break;
657 case 'h':
658 usage(argv[0]);
659 return 0;
660 case '?':
661 errx(EXIT_FAILURE, "Try '%s --help' for more information.",
662 argv[0]);
666 ret = init_channels();
667 if (ret) {
668 errx(EXIT_FAILURE, "error initializing communication channel");
671 ret = init_oforwards();
672 if (ret) {
673 errx(EXIT_FAILURE,
674 "error initializing forwarders for outgoing connections");
677 ret = init_iforwards();
678 if (ret) {
679 errx(EXIT_FAILURE,
680 "error initializing service mappings for incoming connections");
684 if (guest_agent_iforward) {
685 ret = init_agent(guest_agent_iforward);
686 if (ret) {
687 errx(EXIT_FAILURE,
688 "error initializing guest agent");
690 /* tell the host the agent is running */
691 //va_send_hello();
694 /* main i/o loop */
695 for (;;) {
696 DEBUG("entering main_loop_wait()");
697 main_loop_wait(0);
698 DEBUG("left main_loop_wait()");
701 return 0;