2 * virt-proxy - host/guest communication daemon
4 * Copyright IBM Corp. 2010
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
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;
47 #define DEBUG(msg, ...) do { \
48 fprintf(stderr, "%s:%s():L%d: " msg "\n", \
49 __FILE__, __FUNCTION__, __LINE__, ## __VA_ARGS__); \
52 #define DEBUG(msg, ...) do {} while (0)
55 #define INFO(msg, ...) do { \
56 if (!verbose_enabled) { \
59 warnx(msg, ## __VA_ARGS__); \
62 /* mirror qemu I/O-related code for standalone daemon */
63 typedef struct IOHandlerRecord
{
65 IOCanReadHandler
*fd_read_poll
;
72 QLIST_ENTRY(IOHandlerRecord
) next
;
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
,
86 if (!fd_read
&& !fd_write
) {
87 QLIST_FOREACH(ioh
, &io_handlers
, next
) {
94 QLIST_FOREACH(ioh
, &io_handlers
, next
) {
98 ioh
= qemu_mallocz(sizeof(IOHandlerRecord
));
99 QLIST_INSERT_HEAD(&io_handlers
, ioh
, next
);
102 ioh
->fd_read_poll
= fd_read_poll
;
103 ioh
->fd_read
= fd_read
;
104 ioh
->fd_write
= fd_write
;
105 ioh
->opaque
= opaque
;
111 int vp_set_fd_handler(int fd
,
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
)
125 ret
= write(fd
, buf
, len
);
127 if (errno
!= EINTR
&& errno
!= EAGAIN
) {
128 warn("write() failed");
131 } else if (ret
== 0) {
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
)
147 static void main_loop_wait(int nonblocking
)
149 IOHandlerRecord
*ioh
;
150 fd_set rfds
, wfds
, xfds
;
153 int timeout
= 100000;
159 /* poll any events */
164 QLIST_FOREACH(ioh
, &io_handlers
, next
) {
168 (!ioh
->fd_read_poll
||
169 ioh
->fd_read_poll(ioh
->opaque
) != 0)) {
170 FD_SET(ioh
->fd
, &rfds
);
175 FD_SET(ioh
->fd
, &wfds
);
181 tv
.tv_sec
= timeout
/ 1000;
182 tv
.tv_usec
= (timeout
% 1000) * 1000;
184 ret
= select(nfds
+ 1, &rfds
, &wfds
, &xfds
, &tv
);
187 IOHandlerRecord
*pioh
;
189 QLIST_FOREACH_SAFE(ioh
, &io_handlers
, next
, pioh
) {
191 QLIST_REMOVE(ioh
, next
);
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
= {
209 .head
= QTAILQ_HEAD_INITIALIZER(vp_opts
.head
),
212 .name
= "service_id",
213 .type
= QEMU_OPT_STRING
,
215 .name
= "channel_method",
216 .type
= QEMU_OPT_STRING
,
219 .type
= QEMU_OPT_NUMBER
,
222 .type
= QEMU_OPT_STRING
,
225 .type
= QEMU_OPT_STRING
,
228 .type
= QEMU_OPT_STRING
,
231 .type
= QEMU_OPT_BOOL
,
234 .type
= QEMU_OPT_BOOL
,
236 { /* end if list */ }
240 typedef struct VPData
{
243 QTAILQ_ENTRY(VPData
) next
;
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
)
253 "Usage: %s -c <channel_opts> [-c ... ] [-i <iforward_opts> ...] "
254 "[-o <oforward_opts> ...]\n"
255 "QEMU virt-proxy communication channel\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"
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"
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"
276 " <method>: one of unix-connect, unix-listen, tcp-connect, tcp-listen,\n"
278 " <addr>: path of unix socket or virtserial port, or IP of host, 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"
286 "Report bugs to <mdroth@linux.vnet.ibm.com>\n"
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 */
294 char channel_method
[32];
300 if (is_channel
== false) {
301 /* parse service id */
302 ret
= sscanf(str
,"%32[^:]:%n",service_id
,&pos
);
304 warn("error parsing service id");
307 qemu_opt_set(opts
, "service_id", service_id
);
309 /* parse connection type */
310 ret
= sscanf(str
,"%32[^:]:%n",channel_method
,&pos
);
312 warn("error parsing channel method");
315 qemu_opt_set(opts
, "channel_method", channel_method
);
320 /* parse path/addr and port */
323 ret
= sscanf(str
,"[%a[^]:]]:%32[^:]%n",&addr
,port
,&pos
);
324 qemu_opt_set(opts
, "ipv6", "on");
326 ret
= sscanf(str
,"%a[^:]:%32[^:]%n",&addr
,port
,&pos
);
327 qemu_opt_set(opts
, "ipv4", "on");
331 warnx("error parsing path/addr/port");
333 } else if (port
[0] == '-') {
334 /* no port given, assume unix path */
335 qemu_opt_set(opts
, "path", addr
);
337 qemu_opt_set(opts
, "host", addr
);
338 qemu_opt_set(opts
, "port", port
);
345 /* parse optional index parameter */
346 ret
= sscanf(str
,":%10[^:]%n",index
,&pos
);
348 qemu_opt_set(opts
, "index", "0");
353 warnx("error parsing index");
356 qemu_opt_set(opts
, "index", index
);
364 static VPDriver
*get_channel_drv(int index
) {
369 QTAILQ_FOREACH(data
, &channels
, next
) {
370 cindex
= qemu_opt_get_number(data
->opts
, "index", 0);
371 if (cindex
== index
) {
380 static int init_channels(void) {
382 VPData
*channel_data
;
383 const char *channel_method
, *path
;
387 if (QTAILQ_EMPTY(&channels
)) {
388 warnx("no channel specified");
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);
411 } else if (strcmp(channel_method
, "tcp-connect") == 0) {
412 fd
= inet_connect_opts(channel_data
->opts
);
414 } else if (strcmp(channel_method
, "unix-listen") == 0) {
415 fd
= unix_listen_opts(channel_data
->opts
);
417 } else if (strcmp(channel_method
, "unix-connect") == 0) {
418 fd
= unix_connect_opts(channel_data
->opts
);
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
);
426 warn("error setting flags for fd");
431 warnx("invalid channel type: %s", channel_method
);
436 warn("error opening connection");
440 drv
= vp_new(VP_CTX_FD
, NULL
, fd
, listen
);
441 channel_data
->opaque
= drv
;
446 static int init_oforwards(void) {
448 VPData
*oforward_data
;
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
);
461 warnx("unable to find channel with index: %d", index
);
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
);
470 warnx("unable to find listening socket host/addr info");
475 warnx("failed to create FD");
479 service_id
= qemu_opt_get(oforward_data
->opts
, "service_id");
481 if (service_id
== NULL
) {
482 warnx("no service_id specified");
486 ret
= vp_set_oforward(drv
, fd
, service_id
);
492 static int init_iforwards(void) {
494 VPData
*iforward_data
;
496 const char *service_id
, *addr
, *port
;
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
);
508 warnx("unable to find channel with index: %d", index
);
512 service_id
= qemu_opt_get(iforward_data
->opts
, "service_id");
513 if (service_id
== NULL
) {
514 warnx("no service_id specified");
518 addr
= qemu_opt_get(iforward_data
->opts
, "path");
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) ?
530 ret
= vp_set_iforward(drv
, service_id
, addr
, port
, ipv6
);
532 warnx("error adding iforward");
540 static int init_agent(const VPData
*agent_iforward
) {
541 QemuOpts
*opts
= agent_iforward
->opts
;
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
);
553 warnx("unable to find channel with index: %d", index
);
558 ret
= va_client_init(drv
, false);
560 warnx("error starting RPC server");
564 /* start guest RPC server */
565 ret
= va_server_init(drv
, false);
567 warnx("error starting RPC server");
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' },
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) {
600 char optarg_tmp
[VP_ARG_LEN
];
603 opts
= qemu_opts_create(&vp_opts
, NULL
, 0);
604 ret
= vp_parse(opts
, optarg
, 0);
606 errx(EXIT_FAILURE
, "error parsing arg: %s", optarg
);
608 data
= qemu_mallocz(sizeof(VPData
));
610 QTAILQ_INSERT_TAIL(&iforwards
, data
, next
);
613 opts
= qemu_opts_create(&vp_opts
, NULL
, 0);
614 ret
= vp_parse(opts
, optarg
, 0);
616 errx(EXIT_FAILURE
, "error parsing arg: %s", optarg
);
618 data
= qemu_mallocz(sizeof(VPData
));
620 QTAILQ_INSERT_TAIL(&oforwards
, data
, next
);
623 opts
= qemu_opts_create(&vp_opts
, NULL
, 0);
624 ret
= vp_parse(opts
, optarg
, 1);
626 errx(EXIT_FAILURE
, "error parsing arg: %s", optarg
);
628 data
= qemu_mallocz(sizeof(VPData
));
630 QTAILQ_INSERT_TAIL(&channels
, data
, next
);
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);
639 sprintf(optarg_tmp
, "%s:%s:-", GUEST_AGENT_SERVICE_ID
,
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);
647 errx(EXIT_FAILURE
, "error parsing arg: %s", optarg
);
649 data
= qemu_mallocz(sizeof(VPData
));
651 QTAILQ_INSERT_TAIL(&iforwards
, data
, next
);
652 guest_agent_iforward
= data
;
661 errx(EXIT_FAILURE
, "Try '%s --help' for more information.",
666 ret
= init_channels();
668 errx(EXIT_FAILURE
, "error initializing communication channel");
671 ret
= init_oforwards();
674 "error initializing forwarders for outgoing connections");
677 ret
= init_iforwards();
680 "error initializing service mappings for incoming connections");
684 if (guest_agent_iforward
) {
685 ret
= init_agent(guest_agent_iforward
);
688 "error initializing guest agent");
690 /* tell the host the agent is running */
696 DEBUG("entering main_loop_wait()");
698 DEBUG("left main_loop_wait()");