qemu/io/net-listener.c
<<
>>
Prefs
   1/*
   2 * QEMU network listener
   3 *
   4 * Copyright (c) 2016-2017 Red Hat, Inc.
   5 *
   6 * This program is free software; you can redistribute it and/or modify
   7 * it under the terms of the GNU General Public License as published by
   8 * the Free Software Foundation; either version 2 of the License, or
   9 * (at your option) any later version.
  10 *
  11 * This program is distributed in the hope that it will be useful,
  12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
  13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  14 * GNU General Public License for more details.
  15 *
  16 * You should have received a copy of the GNU General Public License along
  17 * with this program; if not, see <http://www.gnu.org/licenses/>.
  18 *
  19 */
  20
  21#include "qemu/osdep.h"
  22#include "io/net-listener.h"
  23#include "io/dns-resolver.h"
  24#include "qapi/error.h"
  25#include "qemu/module.h"
  26
  27QIONetListener *qio_net_listener_new(void)
  28{
  29    return QIO_NET_LISTENER(object_new(TYPE_QIO_NET_LISTENER));
  30}
  31
  32void qio_net_listener_set_name(QIONetListener *listener,
  33                               const char *name)
  34{
  35    g_free(listener->name);
  36    listener->name = g_strdup(name);
  37}
  38
  39
  40static gboolean qio_net_listener_channel_func(QIOChannel *ioc,
  41                                              GIOCondition condition,
  42                                              gpointer opaque)
  43{
  44    QIONetListener *listener = QIO_NET_LISTENER(opaque);
  45    QIOChannelSocket *sioc;
  46
  47    sioc = qio_channel_socket_accept(QIO_CHANNEL_SOCKET(ioc),
  48                                     NULL);
  49    if (!sioc) {
  50        return TRUE;
  51    }
  52
  53    if (listener->io_func) {
  54        listener->io_func(listener, sioc, listener->io_data);
  55    }
  56
  57    object_unref(OBJECT(sioc));
  58
  59    return TRUE;
  60}
  61
  62
  63int qio_net_listener_open_sync(QIONetListener *listener,
  64                               SocketAddress *addr,
  65                               int num,
  66                               Error **errp)
  67{
  68    QIODNSResolver *resolver = qio_dns_resolver_get_instance();
  69    SocketAddress **resaddrs;
  70    size_t nresaddrs;
  71    size_t i;
  72    Error *err = NULL;
  73    bool success = false;
  74
  75    if (qio_dns_resolver_lookup_sync(resolver,
  76                                     addr,
  77                                     &nresaddrs,
  78                                     &resaddrs,
  79                                     errp) < 0) {
  80        return -1;
  81    }
  82
  83    for (i = 0; i < nresaddrs; i++) {
  84        QIOChannelSocket *sioc = qio_channel_socket_new();
  85
  86        if (qio_channel_socket_listen_sync(sioc, resaddrs[i], num,
  87                                           err ? NULL : &err) == 0) {
  88            success = true;
  89
  90            qio_net_listener_add(listener, sioc);
  91        }
  92
  93        qapi_free_SocketAddress(resaddrs[i]);
  94        object_unref(OBJECT(sioc));
  95    }
  96    g_free(resaddrs);
  97
  98    if (success) {
  99        error_free(err);
 100        return 0;
 101    } else {
 102        error_propagate(errp, err);
 103        return -1;
 104    }
 105}
 106
 107
 108void qio_net_listener_add(QIONetListener *listener,
 109                          QIOChannelSocket *sioc)
 110{
 111    if (listener->name) {
 112        char *name = g_strdup_printf("%s-listen", listener->name);
 113        qio_channel_set_name(QIO_CHANNEL(sioc), name);
 114        g_free(name);
 115    }
 116
 117    listener->sioc = g_renew(QIOChannelSocket *, listener->sioc,
 118                             listener->nsioc + 1);
 119    listener->io_source = g_renew(typeof(listener->io_source[0]),
 120                                  listener->io_source,
 121                                  listener->nsioc + 1);
 122    listener->sioc[listener->nsioc] = sioc;
 123    listener->io_source[listener->nsioc] = NULL;
 124
 125    object_ref(OBJECT(sioc));
 126    listener->connected = true;
 127
 128    if (listener->io_func != NULL) {
 129        object_ref(OBJECT(listener));
 130        listener->io_source[listener->nsioc] = qio_channel_add_watch_source(
 131            QIO_CHANNEL(listener->sioc[listener->nsioc]), G_IO_IN,
 132            qio_net_listener_channel_func,
 133            listener, (GDestroyNotify)object_unref, NULL);
 134    }
 135
 136    listener->nsioc++;
 137}
 138
 139
 140void qio_net_listener_set_client_func_full(QIONetListener *listener,
 141                                           QIONetListenerClientFunc func,
 142                                           gpointer data,
 143                                           GDestroyNotify notify,
 144                                           GMainContext *context)
 145{
 146    size_t i;
 147
 148    if (listener->io_notify) {
 149        listener->io_notify(listener->io_data);
 150    }
 151    listener->io_func = func;
 152    listener->io_data = data;
 153    listener->io_notify = notify;
 154
 155    for (i = 0; i < listener->nsioc; i++) {
 156        if (listener->io_source[i]) {
 157            g_source_destroy(listener->io_source[i]);
 158            g_source_unref(listener->io_source[i]);
 159            listener->io_source[i] = NULL;
 160        }
 161    }
 162
 163    if (listener->io_func != NULL) {
 164        for (i = 0; i < listener->nsioc; i++) {
 165            object_ref(OBJECT(listener));
 166            listener->io_source[i] = qio_channel_add_watch_source(
 167                QIO_CHANNEL(listener->sioc[i]), G_IO_IN,
 168                qio_net_listener_channel_func,
 169                listener, (GDestroyNotify)object_unref, context);
 170        }
 171    }
 172}
 173
 174void qio_net_listener_set_client_func(QIONetListener *listener,
 175                                      QIONetListenerClientFunc func,
 176                                      gpointer data,
 177                                      GDestroyNotify notify)
 178{
 179    qio_net_listener_set_client_func_full(listener, func, data,
 180                                          notify, NULL);
 181}
 182
 183struct QIONetListenerClientWaitData {
 184    QIOChannelSocket *sioc;
 185    GMainLoop *loop;
 186};
 187
 188
 189static gboolean qio_net_listener_wait_client_func(QIOChannel *ioc,
 190                                                  GIOCondition condition,
 191                                                  gpointer opaque)
 192{
 193    struct QIONetListenerClientWaitData *data = opaque;
 194    QIOChannelSocket *sioc;
 195
 196    sioc = qio_channel_socket_accept(QIO_CHANNEL_SOCKET(ioc),
 197                                     NULL);
 198    if (!sioc) {
 199        return TRUE;
 200    }
 201
 202    if (data->sioc) {
 203        object_unref(OBJECT(sioc));
 204    } else {
 205        data->sioc = sioc;
 206        g_main_loop_quit(data->loop);
 207    }
 208
 209    return TRUE;
 210}
 211
 212QIOChannelSocket *qio_net_listener_wait_client(QIONetListener *listener)
 213{
 214    GMainContext *ctxt = g_main_context_new();
 215    GMainLoop *loop = g_main_loop_new(ctxt, TRUE);
 216    GSource **sources;
 217    struct QIONetListenerClientWaitData data = {
 218        .sioc = NULL,
 219        .loop = loop
 220    };
 221    size_t i;
 222
 223    for (i = 0; i < listener->nsioc; i++) {
 224        if (listener->io_source[i]) {
 225            g_source_destroy(listener->io_source[i]);
 226            g_source_unref(listener->io_source[i]);
 227            listener->io_source[i] = NULL;
 228        }
 229    }
 230
 231    sources = g_new0(GSource *, listener->nsioc);
 232    for (i = 0; i < listener->nsioc; i++) {
 233        sources[i] = qio_channel_create_watch(QIO_CHANNEL(listener->sioc[i]),
 234                                              G_IO_IN);
 235
 236        g_source_set_callback(sources[i],
 237                              (GSourceFunc)qio_net_listener_wait_client_func,
 238                              &data,
 239                              NULL);
 240        g_source_attach(sources[i], ctxt);
 241    }
 242
 243    g_main_loop_run(loop);
 244
 245    for (i = 0; i < listener->nsioc; i++) {
 246        g_source_unref(sources[i]);
 247    }
 248    g_free(sources);
 249    g_main_loop_unref(loop);
 250    g_main_context_unref(ctxt);
 251
 252    if (listener->io_func != NULL) {
 253        for (i = 0; i < listener->nsioc; i++) {
 254            object_ref(OBJECT(listener));
 255            listener->io_source[i] = qio_channel_add_watch_source(
 256                QIO_CHANNEL(listener->sioc[i]), G_IO_IN,
 257                qio_net_listener_channel_func,
 258                listener, (GDestroyNotify)object_unref, NULL);
 259        }
 260    }
 261
 262    return data.sioc;
 263}
 264
 265void qio_net_listener_disconnect(QIONetListener *listener)
 266{
 267    size_t i;
 268
 269    if (!listener->connected) {
 270        return;
 271    }
 272
 273    for (i = 0; i < listener->nsioc; i++) {
 274        if (listener->io_source[i]) {
 275            g_source_destroy(listener->io_source[i]);
 276            g_source_unref(listener->io_source[i]);
 277            listener->io_source[i] = NULL;
 278        }
 279        qio_channel_close(QIO_CHANNEL(listener->sioc[i]), NULL);
 280    }
 281    listener->connected = false;
 282}
 283
 284
 285bool qio_net_listener_is_connected(QIONetListener *listener)
 286{
 287    return listener->connected;
 288}
 289
 290static void qio_net_listener_finalize(Object *obj)
 291{
 292    QIONetListener *listener = QIO_NET_LISTENER(obj);
 293    size_t i;
 294
 295    if (listener->io_notify) {
 296        listener->io_notify(listener->io_data);
 297    }
 298    qio_net_listener_disconnect(listener);
 299
 300    for (i = 0; i < listener->nsioc; i++) {
 301        object_unref(OBJECT(listener->sioc[i]));
 302    }
 303    g_free(listener->io_source);
 304    g_free(listener->sioc);
 305    g_free(listener->name);
 306}
 307
 308static const TypeInfo qio_net_listener_info = {
 309    .parent = TYPE_OBJECT,
 310    .name = TYPE_QIO_NET_LISTENER,
 311    .instance_size = sizeof(QIONetListener),
 312    .instance_finalize = qio_net_listener_finalize,
 313};
 314
 315
 316static void qio_net_listener_register_types(void)
 317{
 318    type_register_static(&qio_net_listener_info);
 319}
 320
 321
 322type_init(qio_net_listener_register_types);
 323