All of lore.kernel.org
 help / color / mirror / Atom feed
From: Christian Pinto <c.pinto@virtualopensystems.com>
To: qemu-devel@nongnu.org
Cc: Baptiste Reynal <b.reynal@virtualopensystems.com>,
	Jani.Kokkonen@huawei.com, tech@virtualopensystems.com,
	Claudio.Fontana@huawei.com,
	Christian Pinto <c.pinto@virtualopensystems.com>
Subject: [Qemu-devel] [RFC PATCH 1/8] backend: multi-socket
Date: Tue, 29 Sep 2015 15:57:32 +0200	[thread overview]
Message-ID: <1443535059-26010-2-git-send-email-c.pinto@virtualopensystems.com> (raw)
In-Reply-To: <1443535059-26010-1-git-send-email-c.pinto@virtualopensystems.com>

From: Baptiste Reynal <b.reynal@virtualopensystems.com>

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=<id>,path=<socket_path>,listen=<on/off>

If listen is set, the socket will act as a listener and register new
clients.

Signed-off-by: Baptiste Reynal <b.reynal@virtualopensystems.com>
---
 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 <b.reynal@virtualopensystems.com>
+ *
+ * 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 <b.reynal@virtualopensystems.com>
+ *
+ * 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
-- 
1.9.1

  reply	other threads:[~2015-09-29 14:00 UTC|newest]

Thread overview: 19+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2015-09-29 13:57 [Qemu-devel] [RFC PATCH 0/8] Towards an Heterogeneous QEMU Christian Pinto
2015-09-29 13:57 ` Christian Pinto [this message]
2015-09-29 13:57 ` [Qemu-devel] [RFC PATCH 2/8] backend: shared memory backend Christian Pinto
2015-09-29 13:57 ` [Qemu-devel] [RFC PATCH 3/8] migration: add shared migration type Christian Pinto
2015-09-29 13:57 ` [Qemu-devel] [RFC PATCH 4/8] hw/misc: IDM Device Christian Pinto
2015-09-29 13:57 ` [Qemu-devel] [RFC PATCH 5/8] hw/arm: sysbus-fdt Christian Pinto
2015-09-29 13:57 ` [Qemu-devel] [RFC PATCH 6/8] qemu: slave machine flag Christian Pinto
2015-09-29 13:57 ` [Qemu-devel] [RFC PATCH 7/8] hw/arm: boot Christian Pinto
2015-09-29 13:57 ` [Qemu-devel] [RFC PATCH 8/8] qemu: numa Christian Pinto
2015-10-01 16:26 ` [Qemu-devel] [RFC PATCH 0/8] Towards an Heterogeneous QEMU Peter Crosthwaite
2015-10-05 15:50   ` Christian Pinto
2015-10-07 15:48     ` Peter Crosthwaite
2015-10-22  9:21       ` Christian Pinto
2015-10-25 21:38         ` Peter Crosthwaite
2015-10-26 17:12           ` mar.krzeminski
2015-10-26 17:42             ` Peter Crosthwaite
2015-10-27 10:30           ` Christian Pinto
2015-11-13  7:02             ` Peter Crosthwaite
2015-12-12 10:19               ` Christian Pinto

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=1443535059-26010-2-git-send-email-c.pinto@virtualopensystems.com \
    --to=c.pinto@virtualopensystems.com \
    --cc=Claudio.Fontana@huawei.com \
    --cc=Jani.Kokkonen@huawei.com \
    --cc=b.reynal@virtualopensystems.com \
    --cc=qemu-devel@nongnu.org \
    --cc=tech@virtualopensystems.com \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.