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                               Error **errp)
  66{
  67    QIODNSResolver *resolver = qio_dns_resolver_get_instance();
  68    SocketAddress **resaddrs;
  69    size_t nresaddrs;
  70    size_t i;
  71    Error *err = NULL;
  72    bool success = false;
  73
  74    if (qio_dns_resolver_lookup_sync(resolver,
  75                                     addr,
  76                                     &nresaddrs,
  77                                     &resaddrs,
  78                                     errp) < 0) {
  79        return -1;
  80    }
  81
  82    for (i = 0; i < nresaddrs; i++) {
  83        QIOChannelSocket *sioc = qio_channel_socket_new();
  84
  85        if (qio_channel_socket_listen_sync(sioc, resaddrs[i],
  86                                           err ? NULL : &err) == 0) {
  87            success = true;
  88
  89            qio_net_listener_add(listener, sioc);
  90        }
  91
  92        qapi_free_SocketAddress(resaddrs[i]);
  93        object_unref(OBJECT(sioc));
  94    }
  95    g_free(resaddrs);
  96
  97    if (success) {
  98        error_free(err);
  99        return 0;
 100    } else {
 101        error_propagate(errp, err);
 102        return -1;
 103    }
 104}
 105
 106
 107void qio_net_listener_add(QIONetListener *listener,
 108                          QIOChannelSocket *sioc)
 109{
 110    if (listener->name) {
 111        char *name = g_strdup_printf("%s-listen", listener->name);
 112        qio_channel_set_name(QIO_CHANNEL(sioc), name);
 113        g_free(name);
 114    }
 115
 116    listener->sioc = g_renew(QIOChannelSocket *, listener->sioc,
 117                             listener->nsioc + 1);
 118    listener->io_source = g_renew(typeof(listener->io_source[0]),
 119                                  listener->io_source,
 120                                  listener->nsioc + 1);
 121    listener->sioc[listener->nsioc] = sioc;
 122    listener->io_source[listener->nsioc] = NULL;
 123
 124    object_ref(OBJECT(sioc));
 125    listener->connected = true;
 126
 127    if (listener->io_func != NULL) {
 128        object_ref(OBJECT(listener));
 129        listener->io_source[listener->nsioc] = qio_channel_add_watch_source(
 130            QIO_CHANNEL(listener->sioc[listener->nsioc]), G_IO_IN,
 131            qio_net_listener_channel_func,
 132            listener, (GDestroyNotify)object_unref, NULL);
 133    }
 134
 135    listener->nsioc++;
 136}
 137
 138
 139void qio_net_listener_set_client_func_full(QIONetListener *listener,
 140                                           QIONetListenerClientFunc func,
 141                                           gpointer data,
 142                                           GDestroyNotify notify,
 143                                           GMainContext *context)
 144{
 145    size_t i;
 146
 147    if (listener->io_notify) {
 148        listener->io_notify(listener->io_data);
 149    }
 150    listener->io_func = func;
 151    listener->io_data = data;
 152    listener->io_notify = notify;
 153
 154    for (i = 0; i < listener->nsioc; i++) {
 155        if (listener->io_source[i]) {
 156            g_source_destroy(listener->io_source[i]);
 157            g_source_unref(listener->io_source[i]);
 158            listener->io_source[i] = NULL;
 159        }
 160    }
 161
 162    if (listener->io_func != NULL) {
 163        for (i = 0; i < listener->nsioc; i++) {
 164            object_ref(OBJECT(listener));
 165            listener->io_source[i] = qio_channel_add_watch_source(
 166                QIO_CHANNEL(listener->sioc[i]), G_IO_IN,
 167                qio_net_listener_channel_func,
 168                listener, (GDestroyNotify)object_unref, context);
 169        }
 170    }
 171}
 172
 173void qio_net_listener_set_client_func(QIONetListener *listener,
 174                                      QIONetListenerClientFunc func,
 175                                      gpointer data,
 176                                      GDestroyNotify notify)
 177{
 178    qio_net_listener_set_client_func_full(listener, func, data,
 179                                          notify, NULL);
 180}
 181
 182struct QIONetListenerClientWaitData {
 183    QIOChannelSocket *sioc;
 184    GMainLoop *loop;
 185};
 186
 187
 188static gboolean qio_net_listener_wait_client_func(QIOChannel *ioc,
 189                                                  GIOCondition condition,
 190                                                  gpointer opaque)
 191{
 192    struct QIONetListenerClientWaitData *data = opaque;
 193    QIOChannelSocket *sioc;
 194
 195    sioc = qio_channel_socket_accept(QIO_CHANNEL_SOCKET(ioc),
 196                                     NULL);
 197    if (!sioc) {
 198        return TRUE;
 199    }
 200
 201    if (data->sioc) {
 202        object_unref(OBJECT(sioc));
 203    } else {
 204        data->sioc = sioc;
 205        g_main_loop_quit(data->loop);
 206    }
 207
 208    return TRUE;
 209}
 210
 211QIOChannelSocket *qio_net_listener_wait_client(QIONetListener *listener)
 212{
 213    GMainContext *ctxt = g_main_context_new();
 214    GMainLoop *loop = g_main_loop_new(ctxt, TRUE);
 215    GSource **sources;
 216    struct QIONetListenerClientWaitData data = {
 217        .sioc = NULL,
 218        .loop = loop
 219    };
 220    size_t i;
 221
 222    for (i = 0; i < listener->nsioc; i++) {
 223        if (listener->io_source[i]) {
 224            g_source_destroy(listener->io_source[i]);
 225            g_source_unref(listener->io_source[i]);
 226            listener->io_source[i] = NULL;
 227        }
 228    }
 229
 230    sources = g_new0(GSource *, listener->nsioc);
 231    for (i = 0; i < listener->nsioc; i++) {
 232        sources[i] = qio_channel_create_watch(QIO_CHANNEL(listener->sioc[i]),
 233                                              G_IO_IN);
 234
 235        g_source_set_callback(sources[i],
 236                              (GSourceFunc)qio_net_listener_wait_client_func,
 237                              &data,
 238                              NULL);
 239        g_source_attach(sources[i], ctxt);
 240    }
 241
 242    g_main_loop_run(loop);
 243
 244    for (i = 0; i < listener->nsioc; i++) {
 245        g_source_unref(sources[i]);
 246    }
 247    g_free(sources);
 248    g_main_loop_unref(loop);
 249    g_main_context_unref(ctxt);
 250
 251    if (listener->io_func != NULL) {
 252        for (i = 0; i < listener->nsioc; i++) {
 253            object_ref(OBJECT(listener));
 254            listener->io_source[i] = qio_channel_add_watch_source(
 255                QIO_CHANNEL(listener->sioc[i]), G_IO_IN,
 256                qio_net_listener_channel_func,
 257                listener, (GDestroyNotify)object_unref, NULL);
 258        }
 259    }
 260
 261    return data.sioc;
 262}
 263
 264void qio_net_listener_disconnect(QIONetListener *listener)
 265{
 266    size_t i;
 267
 268    if (!listener->connected) {
 269        return;
 270    }
 271
 272    for (i = 0; i < listener->nsioc; i++) {
 273        if (listener->io_source[i]) {
 274            g_source_destroy(listener->io_source[i]);
 275            g_source_unref(listener->io_source[i]);
 276            listener->io_source[i] = NULL;
 277        }
 278        qio_channel_close(QIO_CHANNEL(listener->sioc[i]), NULL);
 279    }
 280    listener->connected = false;
 281}
 282
 283
 284bool qio_net_listener_is_connected(QIONetListener *listener)
 285{
 286    return listener->connected;
 287}
 288
 289static void qio_net_listener_finalize(Object *obj)
 290{
 291    QIONetListener *listener = QIO_NET_LISTENER(obj);
 292    size_t i;
 293
 294    qio_net_listener_disconnect(listener);
 295
 296    for (i = 0; i < listener->nsioc; i++) {
 297        object_unref(OBJECT(listener->sioc[i]));
 298    }
 299    g_free(listener->io_source);
 300    g_free(listener->sioc);
 301    g_free(listener->name);
 302}
 303
 304static const TypeInfo qio_net_listener_info = {
 305    .parent = TYPE_OBJECT,
 306    .name = TYPE_QIO_NET_LISTENER,
 307    .instance_size = sizeof(QIONetListener),
 308    .instance_finalize = qio_net_listener_finalize,
 309    .class_size = sizeof(QIONetListenerClass),
 310};
 311
 312
 313static void qio_net_listener_register_types(void)
 314{
 315    type_register_static(&qio_net_listener_info);
 316}
 317
 318
 319type_init(qio_net_listener_register_types);
 320