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