From mboxrd@z Thu Jan 1 00:00:00 1970 Received: from eggs.gnu.org ([140.186.70.92]:42524) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1QlAEQ-0004TX-N5 for qemu-devel@nongnu.org; Sun, 24 Jul 2011 21:45:20 -0400 Received: from Debian-exim by eggs.gnu.org with spam-scanned (Exim 4.71) (envelope-from ) id 1QlAEK-0005a7-2P for qemu-devel@nongnu.org; Sun, 24 Jul 2011 21:45:18 -0400 Received: from e7.ny.us.ibm.com ([32.97.182.137]:60625) by eggs.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1QlAEJ-0005Zi-A1 for qemu-devel@nongnu.org; Sun, 24 Jul 2011 21:45:11 -0400 Received: from d01relay04.pok.ibm.com (d01relay04.pok.ibm.com [9.56.227.236]) by e7.ny.us.ibm.com (8.14.4/8.13.1) with ESMTP id p6P1JabB022725 for ; Sun, 24 Jul 2011 21:19:36 -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 p6P1jAYt135642 for ; Sun, 24 Jul 2011 21:45:10 -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 p6P1jAZF008088 for ; Sun, 24 Jul 2011 21:45:10 -0400 From: Anthony Liguori Date: Sun, 24 Jul 2011 20:44:51 -0500 Message-Id: <1311558293-5855-20-git-send-email-aliguori@us.ibm.com> In-Reply-To: <1311558293-5855-1-git-send-email-aliguori@us.ibm.com> References: <1311558293-5855-1-git-send-email-aliguori@us.ibm.com> Subject: [Qemu-devel] [PATCH 19/21] qom-chrdrv: add Socket base class List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , To: qemu-devel@nongnu.org Cc: Anthony Liguori This introduces two classed, SocketCharDriver and SocketServer. SocketCharDriver implements the generic logic for all socket based transports. SocketServer implements the necessary functionality for server-based socket transports. Unlike the previous socket CharDriverState, there is no magic overloading based on passed options. You have to explicitly create types of socket CharDrivers for TCP or Unix domain sockets. Signed-off-by: Anthony Liguori --- chrdrv/Makefile | 1 + chrdrv/Qconfig | 7 ++ chrdrv/socketchr.c | 250 ++++++++++++++++++++++++++++++++++++++++++++++ include/qemu/socketchr.h | 109 ++++++++++++++++++++ 4 files changed, 367 insertions(+), 0 deletions(-) create mode 100644 chrdrv/socketchr.c create mode 100644 include/qemu/socketchr.h diff --git a/chrdrv/Makefile b/chrdrv/Makefile index 6cfdd27..84bff48 100644 --- a/chrdrv/Makefile +++ b/chrdrv/Makefile @@ -1,2 +1,3 @@ chrdrv-obj-$(CONFIG_CHRDRV) := chrdrv.o chrdrv-obj-$(CONFIG_CHRDRV_MEM) += memchr.o +chrdrv-obj-$(CONFIG_CHRDRV_SOCKET) += socketchr.o diff --git a/chrdrv/Qconfig b/chrdrv/Qconfig index f3f939e..417c063 100644 --- a/chrdrv/Qconfig +++ b/chrdrv/Qconfig @@ -10,3 +10,10 @@ config CHRDRV_MEM depends on CHRDRV help Character device that stores all written data to a memory buffer. + +config CHRDRV_SOCKET + bool "Socket character driver" + default y + depends on CHRDRV + help + Character driver that implements a socket based transport. diff --git a/chrdrv/socketchr.c b/chrdrv/socketchr.c new file mode 100644 index 0000000..941792c --- /dev/null +++ b/chrdrv/socketchr.c @@ -0,0 +1,250 @@ +#include "qemu/socketchr.h" +#include "qemu_socket.h" + +static int socket_chr_write(CharDriver *chr, const uint8_t *buf, int len) +{ + SocketCharDriver *s = SOCKET_CHAR_DRIVER(chr); + + if (!s->connected) { + return len; + } + + return send_all(s->fd, buf, len); +} + +static int socket_chr_get_msgfd(CharDriver *chr) +{ + SocketCharDriver *s = SOCKET_CHAR_DRIVER(chr); + int fd = s->msgfd; + s->msgfd = -1; + return fd; +} + +#ifndef _WIN32 +static void unix_process_msgfd(CharDriver *chr, struct msghdr *msg) +{ + SocketCharDriver *s = SOCKET_CHAR_DRIVER(chr); + struct cmsghdr *cmsg; + + for (cmsg = CMSG_FIRSTHDR(msg); cmsg; cmsg = CMSG_NXTHDR(msg, cmsg)) { + int fd; + + if (cmsg->cmsg_len != CMSG_LEN(sizeof(int)) || + cmsg->cmsg_level != SOL_SOCKET || + cmsg->cmsg_type != SCM_RIGHTS) { + continue; + } + + fd = *((int *)CMSG_DATA(cmsg)); + if (fd < 0) { + continue; + } + + if (s->msgfd != -1) { + close(s->msgfd); + } + s->msgfd = fd; + } +} + +static ssize_t socket_chr_recv(CharDriver *chr, char *buf, size_t len) +{ + SocketCharDriver *s = SOCKET_CHAR_DRIVER(chr); + struct msghdr msg = { NULL, }; + struct iovec iov[1]; + union { + struct cmsghdr cmsg; + char control[CMSG_SPACE(sizeof(int))]; + } msg_control; + ssize_t ret; + + iov[0].iov_base = buf; + iov[0].iov_len = len; + + msg.msg_iov = iov; + msg.msg_iovlen = 1; + msg.msg_control = &msg_control; + msg.msg_controllen = sizeof(msg_control); + + ret = recvmsg(s->fd, &msg, 0); + if (ret > 0) { + unix_process_msgfd(chr, &msg); + } + + return ret; +} +#else +static ssize_t socket_chr_recv(CharDriver *chr, char *buf, size_t len) +{ + SocketCharDriver *s = SOCKET_CHAR_DRIVER(chr); + return recv(s->fd, buf, len, 0); +} +#endif + +#define READ_BUF_LEN 1024 + +static void socket_chr_try_read(void *opaque) +{ + SocketCharDriver *s = opaque; + uint8_t buf[READ_BUF_LEN]; + int len, size; + int max_size; + + /* Hopefully no drivers rely on can_read anymore.. */ + max_size = char_driver_can_read(CHAR_DRIVER(s)); + if (max_size <= 0) { + return; + } + + len = sizeof(buf); + if (len > max_size) { + len = max_size; + } + size = socket_chr_recv(CHAR_DRIVER(s), (void *)buf, len); + if (size == 0) { + /* connection closed */ + s->connected = 0; + qemu_set_fd_handler(s->fd, NULL, NULL, NULL); + closesocket(s->fd); + s->fd = -1; + char_driver_event(CHAR_DRIVER(s), CHR_EVENT_CLOSED); + // fixme call something that socketserver can hook + } else if (size > 0) { + char_driver_read(CHAR_DRIVER(s), buf, size); + } +} + +void socket_chr_connect(SocketCharDriver *s) +{ + s->connected = 1; + qemu_set_fd_handler(s->fd, socket_chr_try_read, NULL, s); +// FIXME qemu_chr_generic_open(chr); +} + +static void socket_chr_close(CharDriver *chr) +{ + SocketCharDriver *s = SOCKET_CHAR_DRIVER(chr); + + if (s->fd >= 0) { + qemu_set_fd_handler(s->fd, NULL, NULL, NULL); + closesocket(s->fd); + } + char_driver_event(chr, CHR_EVENT_CLOSED); +} + +static void socket_chr_init(TypeInstance *inst) +{ + SocketCharDriver *s = SOCKET_CHAR_DRIVER(inst); + + s->fd = -1; +} + +static void socket_chr_class_init(TypeClass *class) +{ + CharDriverClass *cdc = CHAR_DRIVER_CLASS(class); + + cdc->write = socket_chr_write; + cdc->close = socket_chr_close; + cdc->get_msgfd = socket_chr_get_msgfd; +} + +static TypeInfo socket_chr_info = { + .name = TYPE_SOCKET_CHAR_DRIVER, + .parent = TYPE_CHAR_DRIVER, + .instance_size = sizeof(SocketCharDriver), + .class_init = socket_chr_class_init, + .instance_init = socket_chr_init, + .abstract = true, +}; + +/* Socket Server */ + +static void socket_server_accept(void *opaque) +{ + SocketServer *s = SOCKET_SERVER(opaque); + SocketCharDriver *scd = SOCKET_CHAR_DRIVER(s); + SocketServerClass *ssc = SOCKET_SERVER_GET_CLASS(s); + int fd; + + fd = ssc->accept(s); + socket_set_nonblock(fd); + scd->fd = fd; + qemu_set_fd_handler(s->listen_fd, NULL, NULL, NULL); + socket_chr_connect(scd); +} + +static void socket_server_open(CharDriver *chr, Error **errp) +{ + SocketServer *s = SOCKET_SERVER(chr); + + socket_server_rebind(s); +} + +bool socket_server_get_wait(SocketServer *s, Error **errp) +{ + return s->wait; +} + +void socket_server_set_wait(SocketServer *s, bool value, Error **errp) +{ + s->wait = value; +} + +void socket_server_rebind(SocketServer *s) +{ + SocketServerClass *ssc = SOCKET_SERVER_GET_CLASS(s); + + /* Don't immediately rebind if the backend isn't realized */ + if (!plug_get_realized(PLUG(s), NULL)) { + return; + } + + if (s->listen_fd != -1) { + qemu_set_fd_handler(s->listen_fd, NULL, NULL, NULL); + close(s->listen_fd); + } + + s->listen_fd = ssc->make_listen_socket(s); + if (!s->wait) { + socket_set_nonblock(s->listen_fd); + qemu_set_fd_handler(s->listen_fd, socket_server_accept, NULL, s); + } else { + socket_server_accept(s); + } +} + +static void socket_server_init(TypeInstance *inst) +{ + SocketServer *s = SOCKET_SERVER(inst); + + s->listen_fd = -1; + + plug_add_property_bool(PLUG(s), "wait", + (PlugPropertyGetterBool *)socket_server_get_wait, + (PlugPropertySetterBool *)socket_server_set_wait, + PROP_F_READWRITE); +} + +static void socket_server_class_init(TypeClass *class) +{ + CharDriverClass *cdc = CHAR_DRIVER_CLASS(class); + cdc->open = socket_server_open; +} + +static TypeInfo socket_server_info = { + .name = TYPE_SOCKET_SERVER, + .parent = TYPE_SOCKET_CHAR_DRIVER, + .instance_size = sizeof(SocketServer), + .class_size = sizeof(SocketServerClass), + .class_init = socket_server_class_init, + .instance_init = socket_server_init, + .abstract = true, +}; + +static void register_backends(void) +{ + type_register_static(&socket_chr_info); + type_register_static(&socket_server_info); +} + +device_init(register_backends); diff --git a/include/qemu/socketchr.h b/include/qemu/socketchr.h new file mode 100644 index 0000000..2beb888 --- /dev/null +++ b/include/qemu/socketchr.h @@ -0,0 +1,109 @@ +#ifndef CHR_SOCKET_H +#define CHR_SOCKET_H + +#include "qemu/chrdrv.h" + +/** + * @SocketCharDriver: + * + * Base class for any @CharDriver that uses a socket as a transport. + */ +typedef struct SocketCharDriver +{ + CharDriver parent; + + /* Private */ + + /** + * @connected whether the transport is connected + */ + int connected; + + /** + * @fd the data socket fd + */ + int fd; + + /** + * @msgfd the received file descriptor from the transport + */ + int msgfd; +} SocketCharDriver; + +typedef struct SocketCharDriverClass +{ + CharDriverClass parent_class; +} SocketCharDriverClass; + +#define TYPE_SOCKET_CHAR_DRIVER "socket-char-driver" +#define SOCKET_CHAR_DRIVER(obj) \ + TYPE_CHECK(SocketCharDriver, obj, TYPE_SOCKET_CHAR_DRIVER) + +void socket_chr_connect(SocketCharDriver *s); + +/** + * @SocketServer: + * + * The base class for any socket based transport that accepts connections. + */ +typedef struct SocketServer +{ + SocketCharDriver parent; + + /* protected */ + + /** + * @wait if true, then immediate block until a client connects + */ + bool wait; + + /** + * @listen_fd the fd of the socket to accept connections on + */ + int listen_fd; +} SocketServer; + +typedef struct SocketServerClass +{ + SocketCharDriverClass parent_class; + + /* Protected */ + + /** + * @make_listen_socket + * + * This function should create a socket and bind it such that its + * suitable for listening. + */ + int (*make_listen_socket)(SocketServer *obj); + + /** + * @accept + * + * This function should accept a connection on @listen_fd and return + * a socket representing that connection. + */ + int (*accept)(SocketServer *obj); +} SocketServerClass; + +#define TYPE_SOCKET_SERVER "socket-server" +#define SOCKET_SERVER(obj) \ + TYPE_CHECK(SocketServer, obj, TYPE_SOCKET_SERVER) +#define SOCKET_SERVER_CLASS(class) \ + TYPE_CLASS_CHECK(SocketServerClass, class, TYPE_SOCKET_SERVER) +#define SOCKET_SERVER_GET_CLASS(obj) \ + TYPE_GET_CLASS(SocketServerClass, obj, TYPE_SOCKET_SERVER) + +bool socket_server_get_wait(SocketServer *s, Error **errp); +void socket_server_set_wait(SocketServer *s, bool value, Error **errp); + +/** + * @socket_server_rebind: + * + * Rebinds the server by closing and reopening the listen_fd. This is only + * meant to be used by sub classes implementations. + */ +void socket_server_rebind(SocketServer *s); + +#endif + -- 1.7.4.1