From patchwork Wed Nov 3 15:28:10 2010 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Michael Roth X-Patchwork-Id: 70026 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Received: from lists.gnu.org (lists.gnu.org [199.232.76.165]) (using TLSv1 with cipher DHE-RSA-AES256-SHA (256/256 bits)) (Client did not present a certificate) by ozlabs.org (Postfix) with ESMTPS id DFF3D1007D2 for ; Thu, 4 Nov 2010 02:46:06 +1100 (EST) Received: from localhost ([127.0.0.1]:40108 helo=lists.gnu.org) by lists.gnu.org with esmtp (Exim 4.43) id 1PDfXF-0004DC-2s for incoming@patchwork.ozlabs.org; Wed, 03 Nov 2010 11:46:01 -0400 Received: from [140.186.70.92] (port=51833 helo=eggs.gnu.org) by lists.gnu.org with esmtp (Exim 4.43) id 1PDfGz-0001Vg-45 for qemu-devel@nongnu.org; Wed, 03 Nov 2010 11:29:15 -0400 Received: from Debian-exim by eggs.gnu.org with spam-scanned (Exim 4.71) (envelope-from ) id 1PDfGs-00077M-D6 for qemu-devel@nongnu.org; Wed, 03 Nov 2010 11:29:12 -0400 Received: from e7.ny.us.ibm.com ([32.97.182.137]:57391) by eggs.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1PDfGs-00077I-8b for qemu-devel@nongnu.org; Wed, 03 Nov 2010 11:29:06 -0400 Received: from d01relay07.pok.ibm.com (d01relay07.pok.ibm.com [9.56.227.147]) by e7.ny.us.ibm.com (8.14.4/8.13.1) with ESMTP id oA3FCjw6008873 for ; Wed, 3 Nov 2010 11:12:45 -0400 Received: from d01av01.pok.ibm.com (d01av01.pok.ibm.com [9.56.224.215]) by d01relay07.pok.ibm.com (8.13.8/8.13.8/NCO v10.0) with ESMTP id oA3FSxPH753820 for ; Wed, 3 Nov 2010 11:28:59 -0400 Received: from d01av01.pok.ibm.com (loopback [127.0.0.1]) by d01av01.pok.ibm.com (8.14.4/8.13.1/NCO v10.0 AVout) with ESMTP id oA3FSw0o008934 for ; Wed, 3 Nov 2010 11:28:58 -0400 Received: from localhost.localdomain (sig-9-76-99-21.mts.ibm.com [9.76.99.21]) by d01av01.pok.ibm.com (8.14.4/8.13.1/NCO v10.0 AVin) with ESMTP id oA3FSVDK005744; Wed, 3 Nov 2010 11:28:57 -0400 From: Michael Roth To: qemu-devel@nongnu.org Date: Wed, 3 Nov 2010 10:28:10 -0500 Message-Id: <1288798090-7127-16-git-send-email-mdroth@linux.vnet.ibm.com> X-Mailer: git-send-email 1.7.0.4 In-Reply-To: <1288798090-7127-1-git-send-email-mdroth@linux.vnet.ibm.com> References: <1288798090-7127-1-git-send-email-mdroth@linux.vnet.ibm.com> X-detected-operating-system: by eggs.gnu.org: GNU/Linux 2.6, seldom 2.4 (older, 4) Cc: abeekhof@redhat.com, agl@linux.vnet.ibm.com, mdroth@linux.vnet.ibm.com, aliguori@linux.vnet.ibm.com Subject: [Qemu-devel] [RFC][RESEND][PATCH v1 15/15] virtproxy: qemu-vp, main logic X-BeenThere: qemu-devel@nongnu.org X-Mailman-Version: 2.1.5 Precedence: list List-Id: qemu-devel.nongnu.org List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Sender: qemu-devel-bounces+incoming=patchwork.ozlabs.org@nongnu.org Errors-To: qemu-devel-bounces+incoming=patchwork.ozlabs.org@nongnu.org Signed-off-by: Michael Roth --- Makefile | 2 +- qemu-vp.c | 469 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++- 2 files changed, 469 insertions(+), 2 deletions(-) diff --git a/Makefile b/Makefile index 53b58d2..2dd64a3 100644 --- a/Makefile +++ b/Makefile @@ -135,7 +135,7 @@ qemu-nbd$(EXESUF): qemu-nbd.o qemu-tool.o qemu-error.o $(trace-obj-y) $(block-ob qemu-io$(EXESUF): qemu-io.o cmd.o qemu-tool.o qemu-error.o $(trace-obj-y) $(block-obj-y) $(qobject-obj-y) $(version-obj-y) -qemu-vp$(EXESUF): qemu-vp.o virtproxy.o qemu-tool.o qemu-error.o qemu-sockets.c $(trace-obj-y) $(block-obj-y) $(qobject-obj-y) $(version-obj-y) +qemu-vp$(EXESUF): qemu-vp.o virtproxy.o qemu-tool.o qemu-error.o qemu-sockets.c $(block-obj-y) $(qobject-obj-y) qemu-img-cmds.h: $(SRC_PATH)/qemu-img-cmds.hx $(call quiet-command,sh $(SRC_PATH)/hxtool -h < $< > $@," GEN $@") diff --git a/qemu-vp.c b/qemu-vp.c index 5075cdc..0cc0e67 100644 --- a/qemu-vp.c +++ b/qemu-vp.c @@ -9,10 +9,54 @@ * This work is licensed under the terms of the GNU GPL, version 2 or later. * See the COPYING file in the top-level directory. * + * QEMU System Emulator + * + * Copyright (c) 2003-2008 Fabrice Bellard + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. */ +#include +#include +#include "qemu-option.h" +#include "qemu_socket.h" #include "virtproxy.h" +static bool verbose_enabled = 0; +#define DEBUG_ENABLED + +#ifdef DEBUG_ENABLED +#define DEBUG(msg, ...) do { \ + fprintf(stderr, "%s:%s():L%d: " msg "\n", \ + __FILE__, __FUNCTION__, __LINE__, ## __VA_ARGS__); \ +} while(0) +#else +#define DEBUG(msg, ...) do {} while (0) +#endif + +#define INFO(msg, ...) do { \ + if (!verbose_enabled) { \ + break; \ + } \ + warnx(msg, ## __VA_ARGS__); \ +} while(0) + /* mirror qemu I/O-related code for standalone daemon */ typedef struct IOHandlerRecord { int fd; @@ -98,7 +142,7 @@ static void main_loop_wait(int nonblocking) fd_set rfds, wfds, xfds; int ret, nfds; struct timeval tv; - int timeout = 1000; + int timeout = 100000; if (nonblocking) { timeout = 0; @@ -149,3 +193,426 @@ static void main_loop_wait(int nonblocking) } } } + +#define VP_ARG_LEN 256 + +static QemuOptsList vp_opts = { + .name = "vpargs", + .head = QTAILQ_HEAD_INITIALIZER(vp_opts.head), + .desc = { + { + .name = "service_id", + .type = QEMU_OPT_STRING, + },{ + .name = "channel_method", + .type = QEMU_OPT_STRING, + },{ + .name = "index", + .type = QEMU_OPT_NUMBER, + },{ + .name = "path", + .type = QEMU_OPT_STRING, + },{ + .name = "host", + .type = QEMU_OPT_STRING, + },{ + .name = "port", + .type = QEMU_OPT_STRING, + },{ + .name = "ipv4", + .type = QEMU_OPT_BOOL, + },{ + .name = "ipv6", + .type = QEMU_OPT_BOOL, + }, + { /* end if list */ } + }, +}; + +typedef struct VPData { + QemuOpts *opts; + void *opaque; + QTAILQ_ENTRY(VPData) next; +} VPData; + +static QTAILQ_HEAD(, VPData) iforwards; +static QTAILQ_HEAD(, VPData) oforwards; +static QTAILQ_HEAD(, VPData) channels; + +static void usage(const char *cmd) +{ + printf( +"Usage: %s -c [-c ... ] [-i ...] " +"[-o ...]\n" +"QEMU virt-proxy communication channel\n" +"\n" +" -c, --channel channel options of the form:\n" +" ::[:channel_id]\n" +" -o, --oforward oforward options of the form:\n" +" ::[:channel_id]\n" +" -i, --iforward iforward options of the form:\n" +" ::[:channel_id]\n" +" -v, --verbose display extra debugging information\n" +" -h, --help display this help and exit\n" +"\n" +" channels are used to establish a data connection between 2 end-points in\n" +" the host or the guest (connection method specified by ).\n" +" oforwards specify a socket to listen for new connections on, outgoing\n" +" data from which is tagged with before being sent over the\n" +" channel. iforwards specify a socket to route incoming data/connections\n" +" with a specific to. The positional parameters for\n" +" channels/iforwards/oforwards are:\n" +"\n" +" : one of unix-connect, unix-listen, tcp-connect, tcp-listen,\n" +" virtserial-open\n" +" : path of unix socket or virtserial port, or IP of host, to\n" +" connect/bind to\n" +" : port to bind/connect to, or '-' if addr is a path\n" +" : an identifier used to properly route connections to the\n" +" corresponding host or guest daemon socket.\n" +" : numerical id to identify what channel to use for an iforward\n" +" or oforward. (default is 0)\n" +"\n" +"Report bugs to \n" + , cmd); +} + +static int vp_parse(QemuOpts *opts, const char *str, bool is_channel) +{ + /* TODO: use VP_SERVICE_ID_LEN, bring it into virtproxy.h */ + char service_id[32]; + char channel_method[32]; + char index[10]; + char *addr; + char port[33]; + int pos, ret; + + if (is_channel == false) { + /* parse service id */ + ret = sscanf(str,"%32[^:]:%n",service_id,&pos); + if (ret != 1) { + warn("error parsing service id"); + return -1; + } + qemu_opt_set(opts, "service_id", service_id); + } else { + /* parse connection type */ + ret = sscanf(str,"%32[^:]:%n",channel_method,&pos); + if (ret != 1) { + warn("error parsing channel method"); + return -1; + } + qemu_opt_set(opts, "channel_method", channel_method); + } + str += pos; + pos = 0; + + /* parse path/addr and port */ + if (str[0] == '[') { + /* ipv6 formatted */ + ret = sscanf(str,"[%a[^]:]]:%32[^:]%n",&addr,port,&pos); + qemu_opt_set(opts, "ipv6", "on"); + } else { + ret = sscanf(str,"%a[^:]:%32[^:]%n",&addr,port,&pos); + qemu_opt_set(opts, "ipv4", "on"); + } + + if (ret != 2) { + warnx("error parsing path/addr/port"); + return -1; + } else if (port[0] == '-') { + /* no port given, assume unix path */ + qemu_opt_set(opts, "path", addr); + } else { + qemu_opt_set(opts, "host", addr); + qemu_opt_set(opts, "port", port); + qemu_free(addr); + } + str += pos; + pos = 0; + + if (str[0] == ':') { + /* parse optional index parameter */ + ret = sscanf(str,":%10[^:]%n",index,&pos); + } else { + qemu_opt_set(opts, "index", "0"); + return 0; + } + + if (ret != 1) { + warnx("error parsing index"); + return -1; + } else { + qemu_opt_set(opts, "index", index); + } + str += pos; + pos = 0; + + return 0; +} + +static VPDriver *get_channel_drv(int index) { + VPData *data; + VPDriver *drv; + int cindex; + + QTAILQ_FOREACH(data, &channels, next) { + cindex = qemu_opt_get_number(data->opts, "index", 0); + if (cindex == index) { + drv = data->opaque; + return drv; + } + } + + return NULL; +} + +static int init_channels(void) { + VPDriver *drv; + VPData *channel_data; + const char *channel_method, *path; + int fd, ret; + bool listen; + + if (QTAILQ_EMPTY(&channels)) { + warnx("no channel specified"); + return -1; + } + + channel_data = QTAILQ_FIRST(&channels); + + /* TODO: add this support, optional idx param for -i/-o/-c + * args should suffice + */ + if (QTAILQ_NEXT(channel_data, next) != NULL) { + warnx("multiple channels not currently supported, defaulting to first"); + } + + INFO("initializing channel..."); + if (verbose_enabled) { + qemu_opts_print(channel_data->opts, NULL); + } + + channel_method = qemu_opt_get(channel_data->opts, "channel_method"); + + if (strcmp(channel_method, "tcp-listen") == 0) { + fd = inet_listen_opts(channel_data->opts, 0); + listen = true; + } else if (strcmp(channel_method, "tcp-connect") == 0) { + fd = inet_connect_opts(channel_data->opts); + listen = false; + } else if (strcmp(channel_method, "unix-listen") == 0) { + fd = unix_listen_opts(channel_data->opts); + listen = true; + } else if (strcmp(channel_method, "unix-connect") == 0) { + fd = unix_connect_opts(channel_data->opts); + listen = false; + } else if (strcmp(channel_method, "virtserial-open") == 0) { + path = qemu_opt_get(channel_data->opts, "path"); + fd = qemu_open(path, O_RDWR); + ret = fcntl(fd, F_GETFL); + ret = fcntl(fd, F_SETFL, ret | O_ASYNC); + if (ret < 0) { + warn("error setting flags for fd"); + return -1; + } + listen = false; + } else { + warnx("invalid channel type: %s", channel_method); + return -1; + } + + if (fd == -1) { + warn("error opening connection"); + return -1; + } + + drv = vp_new(fd, listen); + channel_data->opaque = drv; + + return 0; +} + +static int init_oforwards(void) { + VPDriver *drv; + VPData *oforward_data; + int index, ret, fd; + const char *service_id; + + QTAILQ_FOREACH(oforward_data, &oforwards, next) { + INFO("initializing oforward..."); + if (verbose_enabled) { + qemu_opts_print(oforward_data->opts, NULL); + } + + index = qemu_opt_get_number(oforward_data->opts, "index", 0); + drv = get_channel_drv(index); + if (drv == NULL) { + warnx("unable to find channel with index: %d", index); + return -1; + } + + if (qemu_opt_get(oforward_data->opts, "host") != NULL) { + fd = inet_listen_opts(oforward_data->opts, 0); + } else if (qemu_opt_get(oforward_data->opts, "path") != NULL) { + fd = unix_listen_opts(oforward_data->opts); + } else { + warnx("unable to find listening socket host/addr info"); + return -1; + } + + if (fd == -1) { + warnx("failed to create FD"); + return -1; + } + + service_id = qemu_opt_get(oforward_data->opts, "service_id"); + + if (service_id == NULL) { + warnx("no service_id specified"); + return -1; + } + + ret = vp_set_oforward(drv, fd, service_id); + } + + return 0; +} + +static int init_iforwards(void) { + VPDriver *drv; + VPData *iforward_data; + int index, ret; + const char *service_id, *addr, *port; + bool ipv6; + + QTAILQ_FOREACH(iforward_data, &iforwards, next) { + INFO("initializing iforward..."); + if (verbose_enabled) { + qemu_opts_print(iforward_data->opts, NULL); + } + + index = qemu_opt_get_number(iforward_data->opts, "index", 0); + drv = get_channel_drv(index); + if (drv == NULL) { + warnx("unable to find channel with index: %d", index); + return -1; + } + + service_id = qemu_opt_get(iforward_data->opts, "service_id"); + if (service_id == NULL) { + warnx("no service_id specified"); + return -1; + } + + addr = qemu_opt_get(iforward_data->opts, "path"); + port = NULL; + + if (addr == NULL) { + /* map service to a network socket instead */ + addr = qemu_opt_get(iforward_data->opts, "host"); + port = qemu_opt_get(iforward_data->opts, "port"); + } + + ipv6 = qemu_opt_get_bool(iforward_data->opts, "ipv6", 0) ? + true : false; + + ret = vp_set_iforward(drv, service_id, addr, port, ipv6); + if (ret != 0) { + warnx("error adding iforward"); + return -1; + } + } + + return 0; +} + +int main(int argc, char **argv) +{ + const char *sopt = "hVvi:o:c:"; + struct option lopt[] = { + { "help", 0, NULL, 'h' }, + { "version", 0, NULL, 'V' }, + { "verbose", 0, NULL, 'v' }, + { "iforward", 0, NULL, 'i' }, + { "oforward", 0, NULL, 'o' }, + { "channel", 0, NULL, 'c' }, + { NULL, 0, NULL, 0 } + }; + int opt_ind = 0, ch, ret; + QTAILQ_INIT(&iforwards); + QTAILQ_INIT(&oforwards); + QTAILQ_INIT(&channels); + + while ((ch = getopt_long(argc, argv, sopt, lopt, &opt_ind)) != -1) { + QemuOpts *opts; + VPData *data; + switch (ch) { + case 'i': + opts = qemu_opts_create(&vp_opts, NULL, 0); + ret = vp_parse(opts, optarg, 0); + if (ret) { + errx(EXIT_FAILURE, "error parsing arg: %s", optarg); + } + data = qemu_mallocz(sizeof(VPData)); + data->opts = opts; + QTAILQ_INSERT_TAIL(&iforwards, data, next); + break; + case 'o': + opts = qemu_opts_create(&vp_opts, NULL, 0); + ret = vp_parse(opts, optarg, 0); + if (ret) { + errx(EXIT_FAILURE, "error parsing arg: %s", optarg); + } + data = qemu_mallocz(sizeof(VPData)); + data->opts = opts; + QTAILQ_INSERT_TAIL(&oforwards, data, next); + break; + case 'c': + opts = qemu_opts_create(&vp_opts, NULL, 0); + ret = vp_parse(opts, optarg, 1); + if (ret) { + errx(EXIT_FAILURE, "error parsing arg: %s", optarg); + } + data = qemu_mallocz(sizeof(VPData)); + data->opts = opts; + QTAILQ_INSERT_TAIL(&channels, data, next); + break; + case 'v': + verbose_enabled = 1; + break; + case 'h': + usage(argv[0]); + return 0; + case '?': + errx(EXIT_FAILURE, "Try '%s --help' for more information.", + argv[0]); + } + } + + ret = init_channels(); + if (ret) { + errx(EXIT_FAILURE, "error initializing communication channel"); + } + + ret = init_oforwards(); + if (ret) { + errx(EXIT_FAILURE, + "error initializing forwarders for outgoing connections"); + } + + ret = init_iforwards(); + if (ret) { + errx(EXIT_FAILURE, + "error initializing service mappings for incoming connections"); + } + + /* main i/o loop */ + for (;;) { + DEBUG("entering main_loop_wait()"); + main_loop_wait(0); + DEBUG("left main_loop_wait()"); + } + + return 0; +}