From patchwork Mon Jul 25 01:44:52 2011 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Anthony Liguori X-Patchwork-Id: 106599 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Received: from lists.gnu.org (lists.gnu.org [140.186.70.17]) (using TLSv1 with cipher AES256-SHA (256/256 bits)) (Client did not present a certificate) by ozlabs.org (Postfix) with ESMTPS id 8060EB6F85 for ; Mon, 25 Jul 2011 12:43:19 +1000 (EST) Received: from localhost ([::1]:58444 helo=lists.gnu.org) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1QlAFh-0007HN-UD for incoming@patchwork.ozlabs.org; Sun, 24 Jul 2011 21:46:37 -0400 Received: from eggs.gnu.org ([140.186.70.92]:42537) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1QlAER-0004X5-Kx for qemu-devel@nongnu.org; Sun, 24 Jul 2011 21:45:21 -0400 Received: from Debian-exim by eggs.gnu.org with spam-scanned (Exim 4.71) (envelope-from ) id 1QlAEK-0005aP-T3 for qemu-devel@nongnu.org; Sun, 24 Jul 2011 21:45:19 -0400 Received: from e6.ny.us.ibm.com ([32.97.182.146]:44851) by eggs.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1QlAEK-0005Zx-Km for qemu-devel@nongnu.org; Sun, 24 Jul 2011 21:45:12 -0400 Received: from d01relay04.pok.ibm.com (d01relay04.pok.ibm.com [9.56.227.236]) by e6.ny.us.ibm.com (8.14.4/8.13.1) with ESMTP id p6P1L4Ts027349 for ; Sun, 24 Jul 2011 21:21:04 -0400 Received: from d01av04.pok.ibm.com (d01av04.pok.ibm.com [9.56.224.64]) by d01relay04.pok.ibm.com (8.13.8/8.13.8/NCO v10.0) with ESMTP id p6P1jBrb155410 for ; Sun, 24 Jul 2011 21:45:11 -0400 Received: from d01av04.pok.ibm.com (loopback [127.0.0.1]) by d01av04.pok.ibm.com (8.14.4/8.13.1/NCO v10.0 AVout) with ESMTP id p6P1jB06008131 for ; Sun, 24 Jul 2011 21:45:11 -0400 Received: from titi.austin.rr.com (sig-9-65-207-230.mts.ibm.com [9.65.207.230]) by d01av04.pok.ibm.com (8.14.4/8.13.1/NCO v10.0 AVin) with ESMTP id p6P1itL1006824; Sun, 24 Jul 2011 21:45:10 -0400 From: Anthony Liguori To: qemu-devel@nongnu.org Date: Sun, 24 Jul 2011 20:44:52 -0500 Message-Id: <1311558293-5855-21-git-send-email-aliguori@us.ibm.com> X-Mailer: git-send-email 1.7.4.1 In-Reply-To: <1311558293-5855-1-git-send-email-aliguori@us.ibm.com> References: <1311558293-5855-1-git-send-email-aliguori@us.ibm.com> X-detected-operating-system: by eggs.gnu.org: GNU/Linux 2.6, seldom 2.4 (older, 4) X-Received-From: 32.97.182.146 Cc: Anthony Liguori Subject: [Qemu-devel] [PATCH 20/21] qom-chrdrv: add TCPServer class X-BeenThere: qemu-devel@nongnu.org X-Mailman-Version: 2.1.14 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: qemu-devel-bounces+incoming=patchwork.ozlabs.org@nongnu.org Sender: qemu-devel-bounces+incoming=patchwork.ozlabs.org@nongnu.org This is roughly equivalent to -chardev socket,path=PATH,port=PORT,server=on Signed-off-by: Anthony Liguori --- chrdrv/Makefile | 1 + chrdrv/Qconfig | 7 ++ chrdrv/tcpsrv.c | 203 +++++++++++++++++++++++++++++++++++++++++++++++++ include/qemu/tcpsrv.h | 40 ++++++++++ 4 files changed, 251 insertions(+), 0 deletions(-) create mode 100644 chrdrv/tcpsrv.c create mode 100644 include/qemu/tcpsrv.h diff --git a/chrdrv/Makefile b/chrdrv/Makefile index 84bff48..50a3d50 100644 --- a/chrdrv/Makefile +++ b/chrdrv/Makefile @@ -1,3 +1,4 @@ chrdrv-obj-$(CONFIG_CHRDRV) := chrdrv.o chrdrv-obj-$(CONFIG_CHRDRV_MEM) += memchr.o chrdrv-obj-$(CONFIG_CHRDRV_SOCKET) += socketchr.o +chrdrv-obj-$(CONFIG_CHRDRV_TCP_SERVER) += tcpsrv.o diff --git a/chrdrv/Qconfig b/chrdrv/Qconfig index 417c063..ea22b50 100644 --- a/chrdrv/Qconfig +++ b/chrdrv/Qconfig @@ -17,3 +17,10 @@ config CHRDRV_SOCKET depends on CHRDRV help Character driver that implements a socket based transport. + +config CHRDRV_TCP_SERVER + bool "TCP server character driver" + default y + depends on CHRDRV_SOCKET + help + Character driver that implements a TCP server transport. diff --git a/chrdrv/tcpsrv.c b/chrdrv/tcpsrv.c new file mode 100644 index 0000000..bab94f0 --- /dev/null +++ b/chrdrv/tcpsrv.c @@ -0,0 +1,203 @@ +#include "qemu/tcpsrv.h" + +void tcp_server_initialize(TcpServer *obj, const char *id) +{ + type_initialize(obj, TYPE_TCP_SERVER, id); +} + +void tcp_server_finalize(TcpServer *obj) +{ + type_finalize(obj); +} + +const char *tcp_server_get_host(TcpServer *obj, Error **errp) +{ + return obj->host; +} + +void tcp_server_set_host(TcpServer *obj, const char *value, Error **errp) +{ + qemu_free(obj->host); + obj->host = qemu_strdup(value); + + socket_server_rebind(SOCKET_SERVER(obj)); +} + +const char *tcp_server_get_peername(TcpServer *obj, Error **errp) +{ + /* FIXME */ + return "w00t!"; +} + +const char *tcp_server_get_port(TcpServer *obj, Error **errp) +{ + return obj->port; +} + +void tcp_server_set_port(TcpServer *obj, const char *value, Error **errp) +{ + qemu_free(obj->port); + obj->port = qemu_strdup(value); + + socket_server_rebind(SOCKET_SERVER(obj)); +} + +bool tcp_server_get_ipv4(TcpServer *obj, Error **errp) +{ + return obj->ipv4; +} + +void tcp_server_set_ipv4(TcpServer *obj, bool value, Error **errp) +{ + obj->ipv4 = value; + socket_server_rebind(SOCKET_SERVER(obj)); +} + +bool tcp_server_get_ipv6(TcpServer *obj, Error **errp) +{ + return obj->ipv6; +} + +void tcp_server_set_ipv6(TcpServer *obj, bool value, Error **errp) +{ + obj->ipv6 = value; + socket_server_rebind(SOCKET_SERVER(obj)); +} + +static int tcp_server_make_listen_socket(SocketServer *ss) +{ + TcpServer *s = TCP_SERVER(ss); + struct addrinfo ai, *res, *e; + int ret; + int fd = -1; + + memset(&ai, 0, sizeof(ai)); + ai.ai_flags = AI_PASSIVE | AI_ADDRCONFIG; + ai.ai_family = PF_UNSPEC; + ai.ai_socktype = SOCK_STREAM; + + if (s->ipv4) { + ai.ai_family = PF_INET; + } + + if (s->ipv6) { + ai.ai_family = PF_INET6; + } + + ret = getaddrinfo(s->host, s->port, &ai, &res); + if (ret != 0) { + return -ret; + } + + for (e = res; e != NULL; e = e->ai_next) { + char uaddr[INET6_ADDRSTRLEN + 1]; + char uport[32 + 1]; + int on = 1; + int off = 0; + + getnameinfo((struct sockaddr *)e->ai_addr, e->ai_addrlen, + uaddr, INET6_ADDRSTRLEN, + uport, sizeof(uport) - 1, + NI_NUMERICHOST | NI_NUMERICSERV); + + fd = qemu_socket(e->ai_family, e->ai_socktype, e->ai_protocol); + if (fd == -1) { + continue; + } + + setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)); +#ifdef IPV6_V6ONLY + if (e->ai_family == PF_INET6) { + /* listen on both ipv4 and ipv6 */ + setsockopt(fd, IPPROTO_IPV6, IPV6_V6ONLY, &off, sizeof(off)); + } +#endif + + ret = bind(fd, e->ai_addr, e->ai_addrlen); + if (ret == 0) { + break; + } + + closesocket(fd); + fd = -1; + } + + listen(fd, 1); + + return fd; +} + +static int tcp_server_accept(SocketServer *ss) +{ + TcpServer *s = TCP_SERVER(ss); + int fd; + + do { + socklen_t addrlen = sizeof(s->peer); + fd = qemu_accept(ss->listen_fd, (struct sockaddr *)&s->peer, &addrlen); + } while (fd == -1 && errno == EINTR); + + return fd; +} + +static void tcp_server_init(TypeInstance *inst) +{ + TcpServer *s = TCP_SERVER(inst); + + plug_add_property_str(PLUG(s), "host", + (PlugPropertyGetterStr *)tcp_server_get_host, + (PlugPropertySetterStr *)tcp_server_set_host, + PROP_F_READWRITE); + + plug_add_property_str(PLUG(s), "port", + (PlugPropertyGetterStr *)tcp_server_get_port, + (PlugPropertySetterStr *)tcp_server_set_port, + PROP_F_READWRITE); + + plug_add_property_bool(PLUG(s), "ipv4", + (PlugPropertyGetterBool *)tcp_server_get_ipv4, + (PlugPropertySetterBool *)tcp_server_set_ipv4, + PROP_F_READWRITE); + + plug_add_property_bool(PLUG(s), "ipv6", + (PlugPropertyGetterBool *)tcp_server_get_ipv6, + (PlugPropertySetterBool *)tcp_server_set_ipv6, + PROP_F_READWRITE); + + plug_add_property_str(PLUG(s), "peername", + (PlugPropertyGetterStr *)tcp_server_get_peername, + NULL, + PROP_F_READ); +} + +static void tcp_server_fini(TypeInstance *inst) +{ + TcpServer *s = TCP_SERVER(inst); + + qemu_free(s->port); + qemu_free(s->host); +} + +static void tcp_server_class_init(TypeClass *class) +{ + SocketServerClass *ssc = SOCKET_SERVER_CLASS(class); + + ssc->accept = tcp_server_accept; + ssc->make_listen_socket = tcp_server_make_listen_socket; +} + +static TypeInfo tcp_server_type_info = { + .name = TYPE_TCP_SERVER, + .parent = TYPE_SOCKET_SERVER, + .instance_size = sizeof(TcpServer), + .instance_init = tcp_server_init, + .instance_finalize = tcp_server_fini, + .class_init = tcp_server_class_init, +}; + +static void register_backends(void) +{ + type_register_static(&tcp_server_type_info); +} + +device_init(register_backends); diff --git a/include/qemu/tcpsrv.h b/include/qemu/tcpsrv.h new file mode 100644 index 0000000..c398483 --- /dev/null +++ b/include/qemu/tcpsrv.h @@ -0,0 +1,40 @@ +#ifndef CHAR_DRIVER_TCP_H +#define CHAR_DRIVER_TCP_H + +#include "qemu/socketchr.h" +#include "qemu_socket.h" + +typedef struct TcpServer +{ + SocketServer parent; + + struct sockaddr_in peer; + + char *host; + char *port; + + bool ipv4; + bool ipv6; +} TcpServer; + +#define TYPE_TCP_SERVER "tcp-server" +#define TCP_SERVER(obj) TYPE_CHECK(TcpServer, obj, TYPE_TCP_SERVER) + +void tcp_server_initialize(TcpServer *obj, const char *id); +void tcp_server_finalize(TcpServer *obj); + +const char *tcp_server_get_host(TcpServer *obj, Error **errp); +void tcp_server_set_host(TcpServer *obj, const char *value, Error **errp); + +const char *tcp_server_get_port(TcpServer *obj, Error **errp); +void tcp_server_set_port(TcpServer *obj, const char *value, Error **errp); + +bool tcp_server_get_ipv4(TcpServer *obj, Error **errp); +void tcp_server_set_ipv4(TcpServer *obj, bool value, Error **errp); + +bool tcp_server_get_ipv6(TcpServer *obj, Error **errp); +void tcp_server_set_ipv6(TcpServer *obj, bool value, Error **errp); + +const char *tcp_server_get_peername(TcpServer *obj, Error **errp); + +#endif