From: "Dr. David Alan Gilbert" <dgilbert@redhat.com>
To: "Daniel P. Berrange" <berrange@redhat.com>
Cc: qemu-devel@nongnu.org, "Eric Blake" <eblake@redhat.com>,
"Paolo Bonzini" <pbonzini@redhat.com>,
"Kevin Wolf" <kwolf@redhat.com>, "Max Reitz" <mreitz@redhat.com>,
"Marc-André Lureau" <marcandre.lureau@redhat.com>,
"Juan Quintela" <quintela@redhat.com>,
"Gerd Hoffmann" <kraxel@redhat.com>,
qemu-block@nongnu.org
Subject: Re: [Qemu-devel] [PATCH 2/8] io: introduce a network socket listener API
Date: Fri, 11 Aug 2017 13:26:00 +0100 [thread overview]
Message-ID: <20170811122559.GB2076@work-vm> (raw)
In-Reply-To: <20170810160451.32723-3-berrange@redhat.com>
* Daniel P. Berrange (berrange@redhat.com) wrote:
> The existing QIOChannelSocket class provides the ability to
> listen on a single socket at a time. This patch introduces
> a QIONetListener class that provides a higher level API
> concept around listening for network services, allowing
> for listening on multiple sockets.
What protects against a connection on more than one of the sockets?
Dave
> Signed-off-by: Daniel P. Berrange <berrange@redhat.com>
> ---
> include/io/net-listener.h | 174 +++++++++++++++++++++++++
> io/Makefile.objs | 1 +
> io/net-listener.c | 315 ++++++++++++++++++++++++++++++++++++++++++++++
> 3 files changed, 490 insertions(+)
> create mode 100644 include/io/net-listener.h
> create mode 100644 io/net-listener.c
>
> diff --git a/include/io/net-listener.h b/include/io/net-listener.h
> new file mode 100644
> index 0000000000..0ac5c9cc72
> --- /dev/null
> +++ b/include/io/net-listener.h
> @@ -0,0 +1,174 @@
> +/*
> + * QEMU I/O network listener
> + *
> + * Copyright (c) 2016 Red Hat, Inc.
> + *
> + * This library is free software; you can redistribute it and/or
> + * modify it under the terms of the GNU Lesser General Public
> + * License as published by the Free Software Foundation; either
> + * version 2 of the License, or (at your option) any later version.
> + *
> + * This library is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
> + * Lesser General Public License for more details.
> + *
> + * You should have received a copy of the GNU Lesser General Public
> + * License along with this library; if not, see <http://www.gnu.org/licenses/>.
> + *
> + */
> +
> +#ifndef QIO_NET_LISTENER_H
> +#define QIO_NET_LISTENER_H
> +
> +#include "io/channel-socket.h"
> +
> +#define TYPE_QIO_NET_LISTENER "qio-net-listener"
> +#define QIO_NET_LISTENER(obj) \
> + OBJECT_CHECK(QIONetListener, (obj), TYPE_QIO_NET_LISTENER)
> +#define QIO_NET_LISTENER_CLASS(klass) \
> + OBJECT_CLASS_CHECK(QIONetListenerClass, klass, TYPE_QIO_NET_LISTENER)
> +#define QIO_NET_LISTENER_GET_CLASS(obj) \
> + OBJECT_GET_CLASS(QIONetListenerClass, obj, TYPE_QIO_NET_LISTENER)
> +
> +typedef struct QIONetListener QIONetListener;
> +typedef struct QIONetListenerClass QIONetListenerClass;
> +
> +typedef void (*QIONetListenerClientFunc)(QIONetListener *listener,
> + QIOChannelSocket *sioc,
> + gpointer data);
> +
> +/**
> + * QIONetListener:
> + *
> + * The QIONetListener object encapsulates the management of a
> + * listening socket. It is able to listen on multiple sockets
> + * concurrently, to deal with the scenario where IPv4 / IPv6
> + * needs separate sockets, or there is a need to listen on a
> + * subset of interface IP addresses, instead of the wildcard
> + * address.
> + */
> +struct QIONetListener {
> + Object parent;
> +
> + char *name;
> + QIOChannelSocket **sioc;
> + gulong *io_tag;
> + size_t nsioc;
> +
> + gboolean disconnected;
> +
> + QIONetListenerClientFunc io_func;
> + gpointer io_data;
> + GDestroyNotify io_notify;
> +};
> +
> +struct QIONetListenerClass {
> + ObjectClass parent;
> +};
> +
> +
> +/**
> + * qio_net_listener_new:
> + *
> + * Create a new network listener service, which is not
> + * listening on any sockets initially.
> + *
> + * Returns: the new listener
> + */
> +QIONetListener *qio_net_listener_new(void);
> +
> +
> +/**
> + * qio_net_listener_set_name:
> + * @listener: the network listener object
> + * @name: the listener name
> + *
> + * Set the name of the listener. This is used as a debugging
> + * aid, to set names on any GSource instances associated
> + * with the listener
> + */
> +void qio_net_listener_set_name(QIONetListener *listener,
> + const char *name);
> +
> +/**
> + * qio_net_listener_open_sync:
> + * @listener: the network listener object
> + * @addr: the address to listen on
> + * @errp: pointer to a NULL initialized error object
> + *
> + * Synchronously open a listening connection on all
> + * addresses associated with @addr. This method may
> + * also be invoked multiple times, in order to have a
> + * single listener on multiple distinct addresses.
> + */
> +int qio_net_listener_open_sync(QIONetListener *listener,
> + SocketAddress *addr,
> + Error **errp);
> +
> +/**
> + * qio_net_listener_add:
> + * @listener: the network listener object
> + * @sioc: the socket I/O channel
> + *
> + * Associate a listening socket I/O channel with the
> + * listener. The listener will acquire an new reference
> + * on @sioc, so the caller should release its own reference
> + * if it no longer requires the object.
> + */
> +void qio_net_listener_add(QIONetListener *listener,
> + QIOChannelSocket *sioc);
> +
> +/**
> + * qio_net_listener_set_client_func:
> + * @listener: the network listener object
> + * @func: the callback function
> + * @data: opaque data to pass to @func
> + * @notify: callback to free @data
> + *
> + * Register @func to be invoked whenever a new client
> + * connects to the listener. @func will be invoked
> + * passing in the QIOChannelSocket instance for the
> + * client.
> + */
> +void qio_net_listener_set_client_func(QIONetListener *listener,
> + QIONetListenerClientFunc func,
> + gpointer data,
> + GDestroyNotify notify);
> +
> +/**
> + * qio_net_listener_wait_client:
> + * @listener: the network listener object
> + *
> + * Block execution of the caller until a new client arrives
> + * on one of the listening sockets. If there was previously
> + * a callback registered with qio_net_listener_set_client_func
> + * it will be temporarily disabled, and re-enabled afterwards.
> + *
> + * Returns: the new client socket
> + */
> +QIOChannelSocket *qio_net_listener_wait_client(QIONetListener *listener);
> +
> +
> +/**
> + * qio_net_listener_disconnect:
> + * @listener: the network listener object
> + *
> + * Disconnect the listener, removing all I/O callback
> + * watches and closing the socket channels.
> + */
> +void qio_net_listener_disconnect(QIONetListener *listener);
> +
> +
> +/**
> + * qio_net_listener_is_disconnected:
> + * @listener: the network listener object
> + *
> + * Determine if the listener is connected to any socket
> + * channels
> + *
> + * Returns: TRUE if connected, FALSE otherwise
> + */
> +gboolean qio_net_listener_is_disconnected(QIONetListener *listener);
> +
> +#endif /* QIO_NET_LISTENER_H */
> diff --git a/io/Makefile.objs b/io/Makefile.objs
> index 12983cca79..9a20fce4ed 100644
> --- a/io/Makefile.objs
> +++ b/io/Makefile.objs
> @@ -8,4 +8,5 @@ io-obj-y += channel-watch.o
> io-obj-y += channel-websock.o
> io-obj-y += channel-util.o
> io-obj-y += dns-resolver.o
> +io-obj-y += net-listener.o
> io-obj-y += task.o
> diff --git a/io/net-listener.c b/io/net-listener.c
> new file mode 100644
> index 0000000000..065429f6fb
> --- /dev/null
> +++ b/io/net-listener.c
> @@ -0,0 +1,315 @@
> +/*
> + * QEMU network listener
> + *
> + * Copyright (c) 2016 Red Hat, Inc.
> + *
> + * This library is free software; you can redistribute it and/or
> + * modify it under the terms of the GNU Lesser General Public
> + * License as published by the Free Software Foundation; either
> + * version 2 of the License, or (at your option) any later version.
> + *
> + * This library is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
> + * Lesser General Public License for more details.
> + *
> + * You should have received a copy of the GNU Lesser General Public
> + * License along with this library; if not, see <http://www.gnu.org/licenses/>.
> + *
> + */
> +
> +#include "qemu/osdep.h"
> +#include "io/net-listener.h"
> +#include "io/dns-resolver.h"
> +#include "qapi/error.h"
> +
> +QIONetListener *qio_net_listener_new(void)
> +{
> + QIONetListener *ret;
> +
> + ret = QIO_NET_LISTENER(object_new(TYPE_QIO_NET_LISTENER));
> +
> + return ret;
> +}
> +
> +void qio_net_listener_set_name(QIONetListener *listener,
> + const char *name)
> +{
> + g_free(listener->name);
> + listener->name = g_strdup(name);
> +}
> +
> +
> +static gboolean qio_net_listener_channel_func(QIOChannel *ioc,
> + GIOCondition condition,
> + gpointer opaque)
> +{
> + QIONetListener *listener = QIO_NET_LISTENER(opaque);
> + QIOChannelSocket *sioc;
> +
> + sioc = qio_channel_socket_accept(QIO_CHANNEL_SOCKET(ioc),
> + NULL);
> + if (!sioc) {
> + return TRUE;
> + }
> +
> + if (listener->io_func) {
> + listener->io_func(listener, sioc, listener->io_data);
> + }
> +
> + object_unref(OBJECT(sioc));
> +
> + return TRUE;
> +}
> +
> +
> +int qio_net_listener_open_sync(QIONetListener *listener,
> + SocketAddress *addr,
> + Error **errp)
> +{
> + QIODNSResolver *resolver = qio_dns_resolver_get_instance();
> + SocketAddress **resaddrs;
> + size_t nresaddrs;
> + size_t i;
> + Error *err = NULL;
> + bool success = false;
> +
> + if (qio_dns_resolver_lookup_sync(resolver,
> + addr,
> + &nresaddrs,
> + &resaddrs,
> + errp) < 0) {
> + return -1;
> + }
> +
> + for (i = 0; i < nresaddrs; i++) {
> + QIOChannelSocket *sioc = qio_channel_socket_new();
> +
> + if (qio_channel_socket_listen_sync(sioc, resaddrs[i],
> + err ? NULL : &err) == 0) {
> + success = true;
> + }
> +
> + qio_net_listener_add(listener, sioc);
> +
> + qapi_free_SocketAddress(resaddrs[i]);
> + object_unref(OBJECT(sioc));
> + }
> + g_free(resaddrs);
> +
> + if (success) {
> + error_free(err);
> + return 0;
> + } else {
> + error_propagate(errp, err);
> + return -1;
> + }
> +}
> +
> +
> +void qio_net_listener_add(QIONetListener *listener,
> + QIOChannelSocket *sioc)
> +{
> + if (listener->name) {
> + char *name = g_strdup_printf("%s-listen", listener->name);
> + qio_channel_set_name(QIO_CHANNEL(sioc), name);
> + g_free(name);
> + }
> +
> + listener->sioc = g_renew(QIOChannelSocket *, listener->sioc,
> + listener->nsioc + 1);
> + listener->io_tag = g_renew(gulong, listener->io_tag, listener->nsioc + 1);
> + listener->sioc[listener->nsioc] = sioc;
> + listener->io_tag[listener->nsioc] = 0;
> +
> + object_ref(OBJECT(sioc));
> + listener->disconnected = FALSE;
> +
> + if (listener->io_func != NULL) {
> + object_ref(OBJECT(listener));
> + listener->io_tag[listener->nsioc] = qio_channel_add_watch(
> + QIO_CHANNEL(listener->sioc[listener->nsioc]), G_IO_IN,
> + qio_net_listener_channel_func,
> + listener, (GDestroyNotify)object_unref);
> + }
> +
> + listener->nsioc++;
> +}
> +
> +
> +void qio_net_listener_set_client_func(QIONetListener *listener,
> + QIONetListenerClientFunc func,
> + gpointer data,
> + GDestroyNotify notify)
> +{
> + size_t i;
> +
> + if (listener->io_notify) {
> + listener->io_notify(listener->io_data);
> + }
> + listener->io_func = func;
> + listener->io_data = data;
> + listener->io_notify = notify;
> +
> + for (i = 0; i < listener->nsioc; i++) {
> + if (listener->io_tag[i]) {
> + g_source_remove(listener->io_tag[i]);
> + listener->io_tag[i] = 0;
> + }
> + }
> +
> + if (listener->io_func != NULL) {
> + for (i = 0; i < listener->nsioc; i++) {
> + object_ref(OBJECT(listener));
> + listener->io_tag[i] = qio_channel_add_watch(
> + QIO_CHANNEL(listener->sioc[i]), G_IO_IN,
> + qio_net_listener_channel_func,
> + listener, (GDestroyNotify)object_unref);
> + }
> + }
> +}
> +
> +
> +struct QIONetListenerClientWaitData {
> + QIOChannelSocket *sioc;
> + GMainLoop *loop;
> +};
> +
> +
> +static gboolean qio_net_listener_wait_client_func(QIOChannel *ioc,
> + GIOCondition condition,
> + gpointer opaque)
> +{
> + struct QIONetListenerClientWaitData *data = opaque;
> + QIOChannelSocket *sioc;
> +
> + sioc = qio_channel_socket_accept(QIO_CHANNEL_SOCKET(ioc),
> + NULL);
> + if (!sioc) {
> + return TRUE;
> + }
> +
> + if (data->sioc) {
> + object_unref(OBJECT(sioc));
> + } else {
> + data->sioc = sioc;
> + g_main_loop_quit(data->loop);
> + }
> +
> + return TRUE;
> +}
> +
> +QIOChannelSocket *qio_net_listener_wait_client(QIONetListener *listener)
> +{
> + GMainContext *ctxt = g_main_context_new();
> + GMainLoop *loop = g_main_loop_new(ctxt, TRUE);
> + GSource **sources;
> + struct QIONetListenerClientWaitData data = {
> + .sioc = NULL,
> + .loop = loop
> + };
> + size_t i;
> +
> + for (i = 0; i < listener->nsioc; i++) {
> + if (listener->io_tag[i]) {
> + g_source_remove(listener->io_tag[i]);
> + listener->io_tag[i] = 0;
> + }
> + }
> +
> + sources = g_new0(GSource *, listener->nsioc);
> + for (i = 0; i < listener->nsioc; i++) {
> + sources[i] = qio_channel_create_watch(QIO_CHANNEL(listener->sioc[i]),
> + G_IO_IN);
> +
> + g_source_set_callback(sources[i],
> + (GSourceFunc)qio_net_listener_wait_client_func,
> + &data,
> + NULL);
> + g_source_attach(sources[i], ctxt);
> + }
> +
> + g_main_loop_run(loop);
> +
> + for (i = 0; i < listener->nsioc; i++) {
> + g_source_unref(sources[i]);
> + }
> + g_main_loop_unref(loop);
> + g_main_context_unref(ctxt);
> +
> + if (listener->io_func != NULL) {
> + for (i = 0; i < listener->nsioc; i++) {
> + object_ref(OBJECT(listener));
> + listener->io_tag[i] = qio_channel_add_watch(
> + QIO_CHANNEL(listener->sioc[i]), G_IO_IN,
> + qio_net_listener_channel_func,
> + listener, (GDestroyNotify)object_unref);
> + }
> + }
> +
> + return data.sioc;
> +}
> +
> +void qio_net_listener_disconnect(QIONetListener *listener)
> +{
> + size_t i;
> +
> + if (listener->disconnected) {
> + return;
> + }
> +
> + for (i = 0; i < listener->nsioc; i++) {
> + if (listener->io_tag[i]) {
> + g_source_remove(listener->io_tag[i]);
> + listener->io_tag[i] = 0;
> + }
> + qio_channel_close(QIO_CHANNEL(listener->sioc[i]), NULL);
> + }
> + listener->disconnected = TRUE;
> +}
> +
> +
> +gboolean qio_net_listener_is_disconnected(QIONetListener *listener)
> +{
> + return listener->disconnected;
> +}
> +
> +static void qio_net_listener_init(Object *obj)
> +{
> + QIONetListener *listener = QIO_NET_LISTENER(obj);
> +
> + listener->disconnected = TRUE;
> +}
> +
> +static void qio_net_listener_finalize(Object *obj)
> +{
> + QIONetListener *listener = QIO_NET_LISTENER(obj);
> + size_t i;
> +
> + qio_net_listener_disconnect(listener);
> +
> + for (i = 0; i < listener->nsioc; i++) {
> + object_unref(OBJECT(listener->sioc[i]));
> + }
> + g_free(listener->io_tag);
> + g_free(listener->sioc);
> + g_free(listener->name);
> +}
> +
> +static const TypeInfo qio_net_listener_info = {
> + .parent = TYPE_OBJECT,
> + .name = TYPE_QIO_NET_LISTENER,
> + .instance_size = sizeof(QIONetListener),
> + .instance_finalize = qio_net_listener_finalize,
> + .instance_init = qio_net_listener_init,
> + .class_size = sizeof(QIONetListenerClass),
> +};
> +
> +
> +static void qio_net_listener_register_types(void)
> +{
> + type_register_static(&qio_net_listener_info);
> +}
> +
> +
> +type_init(qio_net_listener_register_types);
> --
> 2.13.3
>
--
Dr. David Alan Gilbert / dgilbert@redhat.com / Manchester, UK
next prev parent reply other threads:[~2017-08-11 12:26 UTC|newest]
Thread overview: 22+ messages / expand[flat|nested] mbox.gz Atom feed top
2017-08-10 16:04 [Qemu-devel] [PATCH 0/8] Enable full IPv4/IPv6 dual stack support Daniel P. Berrange
2017-08-10 16:04 ` [Qemu-devel] [PATCH 1/8] tests: add functional test validating ipv4/ipv6 address flag handling Daniel P. Berrange
2017-08-10 16:04 ` [Qemu-devel] [PATCH 2/8] io: introduce a network socket listener API Daniel P. Berrange
2017-08-10 18:12 ` Eric Blake
2017-08-11 9:15 ` Daniel P. Berrange
2017-08-11 10:18 ` Daniel P. Berrange
2017-08-11 12:26 ` Dr. David Alan Gilbert [this message]
2017-08-11 12:29 ` Daniel P. Berrange
2017-08-11 12:39 ` Dr. David Alan Gilbert
2017-08-11 12:48 ` Daniel P. Berrange
2017-08-10 16:04 ` [Qemu-devel] [PATCH 3/8] blockdev: convert internal NBD server to QIONetListener Daniel P. Berrange
2017-08-10 18:15 ` Eric Blake
2017-08-10 16:04 ` [Qemu-devel] [PATCH 4/8] blockdev: convert qemu-nbd " Daniel P. Berrange
2017-08-10 18:27 ` Eric Blake
2017-08-10 16:04 ` [Qemu-devel] [PATCH 5/8] migration: convert socket " Daniel P. Berrange
2017-08-10 16:04 ` [Qemu-devel] [PATCH 6/8] chardev: convert the " Daniel P. Berrange
2017-08-10 16:04 ` [Qemu-devel] [PATCH 7/8] ui: convert VNC " Daniel P. Berrange
2017-08-10 16:04 ` [Qemu-devel] [PATCH 8/8] sockets: fix parsing of ipv4/ipv6 opts in parse_socket_addr Daniel P. Berrange
2017-08-10 18:35 ` Eric Blake
2017-08-11 9:22 ` Daniel P. Berrange
2017-08-10 16:33 ` [Qemu-devel] [PATCH 0/8] Enable full IPv4/IPv6 dual stack support no-reply
2017-08-10 16:33 ` no-reply
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=20170811122559.GB2076@work-vm \
--to=dgilbert@redhat.com \
--cc=berrange@redhat.com \
--cc=eblake@redhat.com \
--cc=kraxel@redhat.com \
--cc=kwolf@redhat.com \
--cc=marcandre.lureau@redhat.com \
--cc=mreitz@redhat.com \
--cc=pbonzini@redhat.com \
--cc=qemu-block@nongnu.org \
--cc=qemu-devel@nongnu.org \
--cc=quintela@redhat.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 a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).