net-listener.c (9285B)
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);