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 26 QIONetListener *qio_net_listener_new(void) 27 { 28 return QIO_NET_LISTENER(object_new(TYPE_QIO_NET_LISTENER)); 29 } 30 31 void 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 39 static 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 62 int 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 106 void 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 138 void 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 172 void 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 181 struct QIONetListenerClientWaitData { 182 QIOChannelSocket *sioc; 183 GMainLoop *loop; 184 }; 185 186 187 static 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 210 QIOChannelSocket *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 263 void 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 283 bool qio_net_listener_is_connected(QIONetListener *listener) 284 { 285 return listener->connected; 286 } 287 288 static 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 303 static 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 312 static void qio_net_listener_register_types(void) 313 { 314 type_register_static(&qio_net_listener_info); 315 } 316 317 318 type_init(qio_net_listener_register_types); 319