From patchwork Fri Mar 18 09:12:46 2016 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Baptiste Reynal X-Patchwork-Id: 8617341 Return-Path: X-Original-To: patchwork-qemu-devel@patchwork.kernel.org Delivered-To: patchwork-parsemail@patchwork1.web.kernel.org Received: from mail.kernel.org (mail.kernel.org [198.145.29.136]) by patchwork1.web.kernel.org (Postfix) with ESMTP id E7DA29F44D for ; Fri, 18 Mar 2016 09:13:18 +0000 (UTC) Received: from mail.kernel.org (localhost [127.0.0.1]) by mail.kernel.org (Postfix) with ESMTP id 5BA4F2034F for ; Fri, 18 Mar 2016 09:13:17 +0000 (UTC) Received: from lists.gnu.org (lists.gnu.org [208.118.235.17]) (using TLSv1 with cipher AES256-SHA (256/256 bits)) (No client certificate requested) by mail.kernel.org (Postfix) with ESMTPS id A2F562034B for ; Fri, 18 Mar 2016 09:13:15 +0000 (UTC) Received: from localhost ([::1]:42355 helo=lists.gnu.org) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1agqT4-0001wI-G0 for patchwork-qemu-devel@patchwork.kernel.org; Fri, 18 Mar 2016 05:13:14 -0400 Received: from eggs.gnu.org ([2001:4830:134:3::10]:41982) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1agqSt-0001rR-Fd for qemu-devel@nongnu.org; Fri, 18 Mar 2016 05:13:05 -0400 Received: from Debian-exim by eggs.gnu.org with spam-scanned (Exim 4.71) (envelope-from ) id 1agqSq-0003gO-2s for qemu-devel@nongnu.org; Fri, 18 Mar 2016 05:13:03 -0400 Received: from mail-wm0-x241.google.com ([2a00:1450:400c:c09::241]:33534) by eggs.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1agqSp-0003g1-Ie for qemu-devel@nongnu.org; Fri, 18 Mar 2016 05:12:59 -0400 Received: by mail-wm0-x241.google.com with SMTP id x188so4456498wmg.0 for ; Fri, 18 Mar 2016 02:12:59 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=virtualopensystems-com.20150623.gappssmtp.com; s=20150623; h=from:to:cc:subject:date:message-id; bh=oiZD0kAt563LohPTJkiPIK0jOpJP9tmTApvZOEczvV8=; b=C+MvJRf3xabFI2XmuXN2QR2nr5fbwJN9sPS1gXZye3nGsUBCvtyX5c96vbZgiChfbw AANMswAuP557fxEY2RpMcACHfFf+1dN62ShKYrXfP/s3PXBSJraQ2dzfrsYA83NwEoR6 7EInWgG0SE5Gzjrytt6fWsOXGlg9rn6jAQCOu840vkwO1F7AlRelCoEopevDlKCr1As/ r5rNtHt/tUWxzbNlpxWeLkoAZzZsPt1xbj0ireGSHaQRBjlnrzzO9EmItmHI0YtbrQHv 2IjcBHbC0GaEHLjqRoBbEoEGqC+4eMNZ5Pv60R7q85hwsh9+OAEMNBs5WtQ+tvvyz/m2 dCWg== 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; bh=oiZD0kAt563LohPTJkiPIK0jOpJP9tmTApvZOEczvV8=; b=Bkr5cb7ARjBM8RzuXc0Dufc0F8+neC1VgGo5B6IdcsGAzzA6s9QZ+iQp+RFffJmkjS 3bIngvFxGUxKpn4iEj/A7JAk45IJfR8wZb4S74Or3Sp4S1lXsTsXYkFmHIzGdWhW1w51 9s9c6orAenE9nrvA1EqTNzRfxha/rPQaFUW4F9ER/9U+3bJtMTSDsbNXyei+Mhycl1BX PSilBvqBbrsjCTNSMIzofpyHU7DczQldv0REN7mpbrUvwN89oBSY++hTqhq3h52c2boR RIGXh18OTbyoqXbAZkf1+el9gpg4kFqiBpC/EqvQroiLLsC6PH1hS4/65+XIwzPWKhLn 9sfg== X-Gm-Message-State: AD7BkJLLEnB1EcxqP9vxiAok6a8HIfDltYEMKTYQLLc3F50AnAhyJhdhmoTQEu4xsenKuA== X-Received: by 10.194.121.194 with SMTP id lm2mr14651293wjb.71.1458292378708; Fri, 18 Mar 2016 02:12:58 -0700 (PDT) Received: from localhost (LMontsouris-657-1-4-224.w90-63.abo.wanadoo.fr. [90.63.239.224]) by smtp.gmail.com with ESMTPSA id x203sm11731152wmg.23.2016.03.18.02.12.57 (version=TLS1_2 cipher=ECDHE-RSA-AES128-GCM-SHA256 bits=128/128); Fri, 18 Mar 2016 02:12:58 -0700 (PDT) From: Baptiste Reynal To: qemu-devel@nongnu.org Date: Fri, 18 Mar 2016 10:12:46 +0100 Message-Id: <1458292366-13723-1-git-send-email-b.reynal@virtualopensystems.com> X-Mailer: git-send-email 2.7.3 X-detected-operating-system: by eggs.gnu.org: GNU/Linux 2.2.x-3.x [generic] X-Received-From: 2a00:1450:400c:c09::241 Cc: b.reynal@virtualopensystems.com, Jani.Kokkonen@huawei.com, tech@virtualopensystems.com, Claudio.Fontana@huawei.com Subject: [Qemu-devel] [PATCH 1/1] backend: multi-client-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+patchwork-qemu-devel=patchwork.kernel.org@nongnu.org Sender: qemu-devel-bounces+patchwork-qemu-devel=patchwork.kernel.org@nongnu.org X-Spam-Status: No, score=-6.8 required=5.0 tests=BAYES_00,DKIM_SIGNED, RCVD_IN_DNSWL_HI, T_DKIM_INVALID, UNPARSEABLE_RELAY autolearn=ham version=3.3.1 X-Spam-Checker-Version: SpamAssassin 3.3.1 (2010-03-16) on mail.kernel.org X-Virus-Scanned: ClamAV using ClamSMTP This patch introduces a new socket for QEMU, called multi-client-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. This patch is a follow-up to "[RFC PATCH 0/8] Towards an Heterogeneous QEMU": https://lists.gnu.org/archive/html/qemu-devel/2015-10/msg00171.html This work has been sponsored by Huawei Technologies Duesseldorf GmbH. Signed-off-by: Baptiste Reynal --- backends/Makefile.objs | 2 + backends/multi-socket.c | 355 ++++++++++++++++++++++++++++++++++++++++++++ include/qemu/multi-socket.h | 124 ++++++++++++++++ 3 files changed, 481 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..2cfbb50 --- /dev/null +++ b/backends/multi-socket.c @@ -0,0 +1,355 @@ +/* + * 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" +#include "qemu/error-report.h" + +typedef struct MSHandler MSHandler; +typedef struct MSRegHandler MSRegHandler; + +struct MSHandler { + char *name; + void (*read)(MSClient *client, const char *message, void *opaque); + void *opaque; + + QLIST_ENTRY(MSHandler) next; +}; + +struct MSRegHandler { + void (*reg)(MSClient *client, void *opaque); + void *opaque; + + QLIST_ENTRY(MSRegHandler) next; +}; + +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; + } + + 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; + } + } + error_report("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)) { + error_report("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