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