From patchwork Tue Sep 29 13:57:32 2015 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Christian Pinto X-Patchwork-Id: 524445 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Received: from lists.gnu.org (lists.gnu.org [IPv6:2001:4830:134:3::11]) (using TLSv1 with cipher AES256-SHA (256/256 bits)) (No client certificate requested) by ozlabs.org (Postfix) with ESMTPS id 18E9F140D4D for ; Thu, 1 Oct 2015 01:13:17 +1000 (AEST) Received: from localhost ([::1]:59756 helo=lists.gnu.org) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1ZhJ4E-0001nO-SN for incoming@patchwork.ozlabs.org; Wed, 30 Sep 2015 11:13:14 -0400 Received: from eggs.gnu.org ([2001:4830:134:3::10]:34497) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1ZgvRu-0000TH-Ek for qemu-devel@nongnu.org; Tue, 29 Sep 2015 10:00:13 -0400 Received: from Debian-exim by eggs.gnu.org with spam-scanned (Exim 4.71) (envelope-from ) id 1ZgvRo-0002Mv-W8 for qemu-devel@nongnu.org; Tue, 29 Sep 2015 10:00:06 -0400 Received: from mail-wi0-f169.google.com ([209.85.212.169]:35805) by eggs.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1ZgvRo-0002MB-Nx for qemu-devel@nongnu.org; Tue, 29 Sep 2015 10:00:00 -0400 Received: by wicge5 with SMTP id ge5so152076682wic.0 for ; Tue, 29 Sep 2015 07:00:00 -0700 (PDT) X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20130820; h=x-gm-message-state:from:to:cc:subject:date:message-id:in-reply-to :references; bh=BsM0/IisX4RiyKkXAhy2DPUvT0pjFbC6myn4CpTayYk=; b=M9+dv5Rlk6RoumCTEg/dVAvNd5d2O3aVt366A2MDYEYozo0icTCQ/unO9TmQUYpvc+ eNJDpUqAntxyTEwhwVDnqFSZaXor+N6S3kBgmzDmoITFKSsFyd/kEMockgvH0VrJFspZ 02EvsID3Quar0A8ePfi+1WUWnRtT6jJBWe4exylDl1X7TcwQsorUt5Uo9iMGLpAgCsb4 5Kt7+mpka2xIpXwwNdyFXFaTxwM2IZZXXSplFE7iot7Kp9U8HUryfumoC4Ygqhu3U/nt OLfPrJrTWLE8UuG6FdVk+Bbt4wv2rGI5L3R3g2++3YQ18RArxEExGWbwHRiQjCdGntsX G4IQ== X-Gm-Message-State: ALoCoQnCS34R3Oqj0XlLy7OD8u2CzE+1fKF8bpmpSeY5Dd7NKeEXei2GUKyreYv9XjMjuoAYmXp1 X-Received: by 10.194.8.227 with SMTP id u3mr28551257wja.38.1443535200151; Tue, 29 Sep 2015 07:00:00 -0700 (PDT) Received: from localhost.localdomain ([151.67.6.207]) by smtp.googlemail.com with ESMTPSA id e6sm24077357wiy.3.2015.09.29.06.59.56 (version=TLSv1.2 cipher=ECDHE-RSA-AES128-SHA bits=128/128); Tue, 29 Sep 2015 06:59:58 -0700 (PDT) From: Christian Pinto To: qemu-devel@nongnu.org Date: Tue, 29 Sep 2015 15:57:32 +0200 Message-Id: <1443535059-26010-2-git-send-email-c.pinto@virtualopensystems.com> X-Mailer: git-send-email 1.9.1 In-Reply-To: <1443535059-26010-1-git-send-email-c.pinto@virtualopensystems.com> References: <1443535059-26010-1-git-send-email-c.pinto@virtualopensystems.com> X-detected-operating-system: by eggs.gnu.org: GNU/Linux 2.2.x-3.x [generic] [fuzzy] X-Received-From: 209.85.212.169 Cc: Baptiste Reynal , Jani.Kokkonen@huawei.com, tech@virtualopensystems.com, Claudio.Fontana@huawei.com, Christian Pinto Subject: [Qemu-devel] [RFC PATCH 1/8] backend: multi-socket 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 From: Baptiste Reynal This patch introduces a new socket for QEMU, called multi-socket. This socket allows multiple QEMU instances to communicate by sharing messages and file descriptors. A socket can be instantiated with the following parameters: -object multi-socket-backend,id=,path=,listen= If listen is set, the socket will act as a listener and register new clients. Signed-off-by: Baptiste Reynal --- backends/Makefile.objs | 2 + backends/multi-socket.c | 353 ++++++++++++++++++++++++++++++++++++++++++++ include/qemu/multi-socket.h | 124 ++++++++++++++++ 3 files changed, 479 insertions(+) create mode 100644 backends/multi-socket.c create mode 100644 include/qemu/multi-socket.h diff --git a/backends/Makefile.objs b/backends/Makefile.objs index 31a3a89..689eac3 100644 --- a/backends/Makefile.objs +++ b/backends/Makefile.objs @@ -9,3 +9,5 @@ common-obj-$(CONFIG_TPM) += tpm.o common-obj-y += hostmem.o hostmem-ram.o common-obj-$(CONFIG_LINUX) += hostmem-file.o + +common-obj-y += multi-socket.o diff --git a/backends/multi-socket.c b/backends/multi-socket.c new file mode 100644 index 0000000..36bee3a --- /dev/null +++ b/backends/multi-socket.c @@ -0,0 +1,353 @@ +/* + * QEMU Multi Client socket + * + * Copyright (C) 2015 - Virtual Open Systems + * + * Author: Baptiste Reynal + * + * This work is licensed under the terms of the GNU GPL, version 2. See + * the COPYING file in the top-level directory. + */ + +#include "qemu/multi-socket.h" + +typedef struct MSHandler { + char *name; + void (*read)(MSClient *client, const char *message, void *opaque); + void *opaque; + + QLIST_ENTRY (MSHandler) next; +} MSHandler; + +typedef struct MSRegHandler { + void (*reg)(MSClient *client, void *opaque); + void *opaque; + + QLIST_ENTRY (MSRegHandler) next; +} MSRegHandler; + +static void multi_socket_get_fds(MSClient *client, struct msghdr msg) +{ + struct cmsghdr *cmsg; + + /* process fds */ + for (cmsg = CMSG_FIRSTHDR(&msg); cmsg; cmsg = CMSG_NXTHDR(&msg, cmsg)) { + int fd_size; + + if (cmsg->cmsg_len < CMSG_LEN(sizeof(int)) || + cmsg->cmsg_level != SOL_SOCKET || + cmsg->cmsg_type != SCM_RIGHTS) { + continue; + } + + fd_size = cmsg->cmsg_len - CMSG_LEN(0); + + if (!fd_size) { + continue; + } + + if (client->rcvfds) { + g_free(client->rcvfds); + } + + client->rcvfds_num = fd_size / sizeof(int); + client->rcvfds = g_malloc(fd_size); + memcpy(client->rcvfds, CMSG_DATA(cmsg), fd_size); + } +} + +static gboolean +multi_socket_read_handler(GIOChannel *channel, GIOCondition cond, void *opaque) +{ + MSClient *client = (MSClient *) opaque; + MSBackend *backend = client->backend; + + char message[BUFFER_SIZE]; + struct MSHandler *h; + + struct msghdr msg = { NULL, }; + struct iovec iov[1]; + union { + struct cmsghdr cmsg; + char control[CMSG_SPACE(sizeof(int) * MAX_FDS)]; + } msg_control; + int flags = 0; + ssize_t ret; + + iov[0].iov_base = message; + iov[0].iov_len = BUFFER_SIZE; + + msg.msg_iov = iov; + msg.msg_iovlen = 1; + msg.msg_control = &msg_control; + msg.msg_controllen = sizeof(msg_control); + + ret = recvmsg(client->fd, &msg, flags); + + if (ret > 0) { + multi_socket_get_fds(client, msg); + + /* handler callback */ + QLIST_FOREACH (h, &backend->handlers, next) { + if (!strncmp(h->name, message, strlen(h->name))) { + h->read(client, message + strlen(h->name) + 1, h->opaque); + return TRUE; + } + } + printf("Unrecognized message: %s", message); + } + + return FALSE; +} + +void multi_socket_add_reg_handler(MSBackend *backend, + void (*reg)(MSClient *client, void *opaque), void *opaque) +{ + struct MSRegHandler *h; + + h = g_malloc(sizeof(struct MSRegHandler)); + + h->reg = reg; + h->opaque = opaque; + + QLIST_INSERT_HEAD(&backend->reg_handlers, h, next); +} + +void multi_socket_add_handler(MSBackend *backend, + const char *name, + void (*read)(MSClient *c, const char *message, void *opaque), + void *opaque) +{ + struct MSHandler *h; + + /* check that the handler name is not taken */ + QLIST_FOREACH(h, &backend->handlers, next) { + if (!strcmp(h->name, name)) { + printf("Handler %s already exists.", name); + return; + } + } + + h = g_malloc(sizeof(struct MSHandler)); + + h->name = g_strdup(name); + h->read = read; + h->opaque = opaque; + + QLIST_INSERT_HEAD (&backend->handlers, h, next); +} + +static void multi_socket_init_client(MSBackend *backend, + MSClient *client, int fd, GIOFunc handler) +{ + client->backend = backend; + client->fd = fd; + client->chan = g_io_channel_unix_new(fd); + client->tag = g_io_add_watch(client->chan, G_IO_IN, handler, client); + + g_io_channel_set_encoding(client->chan, NULL, NULL); + g_io_channel_set_buffered(client->chan, FALSE); +} + +int multi_socket_send_fds_to(MSClient *client, int *fds, int count, + const char *message, int size) +{ + struct msghdr msgh; + struct iovec iov; + int r; + + size_t fd_size = count * sizeof(int); + char control[CMSG_SPACE(fd_size)]; + struct cmsghdr *cmsg; + + memset(&msgh, 0, sizeof(msgh)); + memset(control, 0, sizeof(control)); + + /* set the payload */ + iov.iov_base = (uint8_t *) message; + iov.iov_len = size; + + msgh.msg_iov = &iov; + msgh.msg_iovlen = 1; + + msgh.msg_control = control; + msgh.msg_controllen = sizeof(control); + + cmsg = CMSG_FIRSTHDR(&msgh); + + cmsg->cmsg_len = CMSG_LEN(fd_size); + cmsg->cmsg_level = SOL_SOCKET; + cmsg->cmsg_type = SCM_RIGHTS; + memcpy(CMSG_DATA(cmsg), fds, fd_size); + + do { + r = sendmsg(client->fd, &msgh, 0); + } while (r < 0 && errno == EINTR); + + return r; +} + +int multi_socket_write_to(MSClient *client, const char *message, int size) +{ + return multi_socket_send_fds_to(client, 0, 0, message, size); +} + +int multi_socket_get_fds_num_from(MSClient *client) +{ + return client->rcvfds_num; +} + +int multi_socket_get_fds_from(MSClient *client, int *fds) +{ + memcpy(fds, client->rcvfds, client->rcvfds_num * sizeof(int)); + + return client->rcvfds_num; +} + +static void multi_socket_add_client(MSBackend *backend, int fd) +{ + MSClient *c = g_malloc(sizeof(MSClient)); + MSRegHandler *h; + + multi_socket_init_client(backend, c, fd, multi_socket_read_handler); + QLIST_FOREACH(h, &backend->reg_handlers, next) { + h->reg(c, h->opaque); + } + + QLIST_INSERT_HEAD(&backend->clients, c, next); +} + +static gboolean +multi_socket_accept(GIOChannel *channel, GIOCondition cond, void *opaque) +{ + MSClient *client = (MSClient *) opaque; + MSBackend *backend = client->backend; + + struct sockaddr_un uaddr; + socklen_t len; + int fd; + + len = sizeof(uaddr); + + fd = qemu_accept(backend->listener.fd, (struct sockaddr *) &uaddr, &len); + + if (fd > 0) { + multi_socket_add_client(backend, fd); + return true; + } else { + perror("Error creating socket."); + return false; + } +} + +static void multi_socket_init_socket(MSBackend *backend) +{ + int fd; + + backend->addr = g_new0(SocketAddress, 1); + backend->addr->kind = SOCKET_ADDRESS_KIND_UNIX; + backend->addr->q_unix = g_new0(UnixSocketAddress, 1); + /* TODO change name with real path */ + backend->addr->q_unix->path = g_strdup(backend->path); + + if (backend->listen) { + fd = socket_listen(backend->addr, NULL); + + if (fd < 0) { + perror("Error: Impossible to open socket."); + exit(-1); + } + + multi_socket_init_client(backend, &backend->listener, fd, + multi_socket_accept); + } else { + fd = socket_connect(backend->addr, NULL, NULL, NULL); + + if (fd < 0) { + perror("Error: Unavailable socket server."); + exit(-1); + } + + multi_socket_init_client(backend, &backend->listener, + fd, multi_socket_read_handler); + } +} + +static void multi_socket_backend_complete(UserCreatable *uc, Error **errp) +{ + MSBackend *backend = MULTI_SOCKET_BACKEND(uc); + + QLIST_INIT(&backend->clients); + QLIST_INIT(&backend->handlers); + + multi_socket_init_socket(backend); +} + +static void multi_socket_class_init(ObjectClass *oc, void *data) +{ + UserCreatableClass *ucc = USER_CREATABLE_CLASS(oc); + + ucc->complete = multi_socket_backend_complete; +} + +static bool multi_socket_backend_get_listen(Object *o, Error **errp) +{ + MSBackend *backend = MULTI_SOCKET_BACKEND(o); + + return backend->listen; +} + +static void multi_socket_backend_set_listen(Object *o, bool value, Error **errp) +{ + MSBackend *backend = MULTI_SOCKET_BACKEND(o); + + backend->listen = value; +} + +static char *multi_socket_get_path(Object *o, Error **errp) +{ + MSBackend *backend = MULTI_SOCKET_BACKEND(o); + + return g_strdup(backend->path); +} + +static void multi_socket_set_path(Object *o, const char *str, Error **errp) +{ + MSBackend *backend = MULTI_SOCKET_BACKEND(o); + + if (str == NULL || str[0] == 0) { + perror("Error: Socket path is empty."); + exit(-1); + } + + backend->path = g_strdup(str); +} + +static void multi_socket_instance_init(Object *o) +{ + object_property_add_bool(o, "listen", + multi_socket_backend_get_listen, + multi_socket_backend_set_listen, NULL); + object_property_add_str(o, "path", multi_socket_get_path, + multi_socket_set_path, NULL); +} + +static const TypeInfo multi_socket_backend_info = { + .name = TYPE_MULTI_SOCKET_BACKEND, + .parent = TYPE_OBJECT, + .class_size = sizeof(MSBackendClass), + .class_init = multi_socket_class_init, + .instance_size = sizeof(MSBackend), + .instance_init = multi_socket_instance_init, + .interfaces = (InterfaceInfo[]) { + { TYPE_USER_CREATABLE }, + { } + } +}; + +static void register_types(void) +{ + type_register_static(&multi_socket_backend_info); +} + +type_init(register_types); diff --git a/include/qemu/multi-socket.h b/include/qemu/multi-socket.h new file mode 100644 index 0000000..dee866a --- /dev/null +++ b/include/qemu/multi-socket.h @@ -0,0 +1,124 @@ +/* + * QEMU Multi Client socket + * + * Copyright (C) 2015 - Virtual Open Systems + * + * Author: Baptiste Reynal + * + * This work is licensed under the terms of the GNU GPL, version 2. See + * the COPYING file in the top-level directory. + */ +#ifndef QEMU_MS_H +#define QEMU_MS_H + +#include "qemu-common.h" +#include "qemu/queue.h" +#include "qemu/sockets.h" +#include "qom/object.h" +#include "qom/object_interfaces.h" + +#define TYPE_MULTI_SOCKET_BACKEND "multi-socket-backend" +#define MULTI_SOCKET_BACKEND(obj) \ + OBJECT_CHECK(MSBackend, (obj), TYPE_MULTI_SOCKET_BACKEND) +#define MULTI_SOCKET_BACKEND_GET_CLASS(obj) \ + OBJECT_GET_CLASS(MSBackendClass, (obj), TYPE_MULTI_SOCKET_BACKEND) +#define MULTI_SOCKET_BACKEND_CLASS(klass) \ + OBJECT_CLASS_CHECK(MSBackendClass, (klass), TYPE_MULTI_SOCKET_BACKEND) + +#define MAX_FDS 32 +#define BUFFER_SIZE 32 + +typedef struct MSBackend MSBackend; +typedef struct MSBackendClass MSBackendClass; +typedef struct MSClient MSClient; + +struct MSClient { + /* private */ + int fd; + MSBackend *backend; + GIOChannel *chan; + guint tag; + + int *rcvfds; + int rcvfds_num; + + QLIST_ENTRY(MSClient) next; +}; + +struct MSBackendClass { + /* private */ + ObjectClass parent_class; +}; + +struct MSBackend { + /* private */ + Object parent; + + /* protected */ + char *path; + SocketAddress *addr; + + QLIST_HEAD(clients_head, MSClient) clients; + QLIST_HEAD(reg_handlers_head, MSRegHandler) reg_handlers; + QLIST_HEAD(handlers_head, MSHandler) handlers; + + bool listen; + MSClient listener; +}; + +/* Callback method called each time a client is registered to the server. + * @backend: socket server + * @reg: callback function + * @opaque: optionnal data passed to register function + */ +void multi_socket_add_reg_handler(MSBackend *backend, + void (*reg)(MSClient *client, void *opaque), + void *opaque); + +/* Attach a handler to the socket. "read" function will be called if a string + * begining with "name" is received over the socket. Payload can be attached + * next to name and will be passed to the "read" function as "message" + * parameter. + * + * @backend: multi-client socket + * @name: name of the handler (should be unique for the socket) + * @read: callback function + * @opaque:optionnal datas passed to read function + */ +void multi_socket_add_handler(MSBackend *backend, const char *name, + void (*read)(MSClient *client, const char *message, void *opaque), + void *opaque); + +/* Send file descriptors over the socket. + * + * @client: client to whom send the message + * @fds: file descriptors array to send + * @count: size of the array + * @message: attached message + * @size: size of the message + */ +int multi_socket_send_fds_to(MSClient *client, int *fds, int count, + const char *message, int size); + +/* Send message over the socket + * + * @client: client to whom send the message + * @message: attached message + * @size: size of the message + */ +int multi_socket_write_to(MSClient *client, const char *message, int size); + +/* Get fds size received with the last message. + * + * @client: client who sent the message + */ +int multi_socket_get_fds_num_from(MSClient *client); + +/* Get the fds received with the last message. + * + * @client: client who sent the message + * @fds: int array to fill with the fds + */ +int multi_socket_get_fds_from(MSClient *client, int *fds); + +#endif