@@ -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
new file mode 100644
@@ -0,0 +1,355 @@
+/*
+ * 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"
+#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);
new file mode 100644
@@ -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
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=<id>,path=<socket_path>,listen=<on/off> 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 <b.reynal@virtualopensystems.com> --- 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