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 27 QIONetListener *qio_net_listener_new(void) 28 { 29 return QIO_NET_LISTENER(object_new(TYPE_QIO_NET_LISTENER)); 30 } 31 32 void 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 40 static 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 63 int 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], 1, 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 107 void 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 139 void 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 173 void 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 182 struct QIONetListenerClientWaitData { 183 QIOChannelSocket *sioc; 184 GMainLoop *loop; 185 }; 186 187 188 static 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 211 QIOChannelSocket *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 264 void 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 284 bool qio_net_listener_is_connected(QIONetListener *listener) 285 { 286 return listener->connected; 287 } 288 289 static 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 304 static 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 313 static void qio_net_listener_register_types(void) 314 { 315 type_register_static(&qio_net_listener_info); 316 } 317 318 319 type_init(qio_net_listener_register_types); 320