qemu/io/net-listener.c
Eduardo Habkost c734cd40a1 qom: Remove ParentClassType argument from OBJECT_DECLARE_SIMPLE_TYPE
The requirement to specify the parent class type makes the macro
harder to use and easy to misuse (silent bugs can be introduced
if the wrong struct type is specified).

Simplify the macro by just not declaring any class struct,
allowing us to remove the class_size field from the TypeInfo
variables for those types.

Signed-off-by: Eduardo Habkost <ehabkost@redhat.com>
Reviewed-by: Philippe Mathieu-Daudé <philmd@redhat.com>
Message-Id: <20200916182519.415636-3-ehabkost@redhat.com>
Signed-off-by: Eduardo Habkost <ehabkost@redhat.com>
2020-09-18 14:12:32 -04:00

320 lines
9.0 KiB
C

/*
* QEMU network listener
*
* Copyright (c) 2016-2017 Red Hat, Inc.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; 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"
#include "qemu/module.h"
QIONetListener *qio_net_listener_new(void)
{
return QIO_NET_LISTENER(object_new(TYPE_QIO_NET_LISTENER));
}
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,
int num,
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], num,
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_source = g_renew(typeof(listener->io_source[0]),
listener->io_source,
listener->nsioc + 1);
listener->sioc[listener->nsioc] = sioc;
listener->io_source[listener->nsioc] = NULL;
object_ref(OBJECT(sioc));
listener->connected = true;
if (listener->io_func != NULL) {
object_ref(OBJECT(listener));
listener->io_source[listener->nsioc] = qio_channel_add_watch_source(
QIO_CHANNEL(listener->sioc[listener->nsioc]), G_IO_IN,
qio_net_listener_channel_func,
listener, (GDestroyNotify)object_unref, NULL);
}
listener->nsioc++;
}
void qio_net_listener_set_client_func_full(QIONetListener *listener,
QIONetListenerClientFunc func,
gpointer data,
GDestroyNotify notify,
GMainContext *context)
{
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_source[i]) {
g_source_destroy(listener->io_source[i]);
g_source_unref(listener->io_source[i]);
listener->io_source[i] = NULL;
}
}
if (listener->io_func != NULL) {
for (i = 0; i < listener->nsioc; i++) {
object_ref(OBJECT(listener));
listener->io_source[i] = qio_channel_add_watch_source(
QIO_CHANNEL(listener->sioc[i]), G_IO_IN,
qio_net_listener_channel_func,
listener, (GDestroyNotify)object_unref, context);
}
}
}
void qio_net_listener_set_client_func(QIONetListener *listener,
QIONetListenerClientFunc func,
gpointer data,
GDestroyNotify notify)
{
qio_net_listener_set_client_func_full(listener, func, data,
notify, NULL);
}
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_source[i]) {
g_source_destroy(listener->io_source[i]);
g_source_unref(listener->io_source[i]);
listener->io_source[i] = NULL;
}
}
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_free(sources);
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_source[i] = qio_channel_add_watch_source(
QIO_CHANNEL(listener->sioc[i]), G_IO_IN,
qio_net_listener_channel_func,
listener, (GDestroyNotify)object_unref, NULL);
}
}
return data.sioc;
}
void qio_net_listener_disconnect(QIONetListener *listener)
{
size_t i;
if (!listener->connected) {
return;
}
for (i = 0; i < listener->nsioc; i++) {
if (listener->io_source[i]) {
g_source_destroy(listener->io_source[i]);
g_source_unref(listener->io_source[i]);
listener->io_source[i] = NULL;
}
qio_channel_close(QIO_CHANNEL(listener->sioc[i]), NULL);
}
listener->connected = false;
}
bool qio_net_listener_is_connected(QIONetListener *listener)
{
return listener->connected;
}
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_source);
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,
};
static void qio_net_listener_register_types(void)
{
type_register_static(&qio_net_listener_info);
}
type_init(qio_net_listener_register_types);