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 QIONetListener *ret; 29 30 ret = QIO_NET_LISTENER(object_new(TYPE_QIO_NET_LISTENER)); 31 32 return ret; 33 } 34 35 void 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 43 static 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 66 int 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 110 void 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_tag = g_renew(gulong, listener->io_tag, listener->nsioc + 1); 122 listener->sioc[listener->nsioc] = sioc; 123 listener->io_tag[listener->nsioc] = 0; 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_tag[listener->nsioc] = qio_channel_add_watch( 131 QIO_CHANNEL(listener->sioc[listener->nsioc]), G_IO_IN, 132 qio_net_listener_channel_func, 133 listener, (GDestroyNotify)object_unref); 134 } 135 136 listener->nsioc++; 137 } 138 139 140 void qio_net_listener_set_client_func(QIONetListener *listener, 141 QIONetListenerClientFunc func, 142 gpointer data, 143 GDestroyNotify notify) 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_tag[i]) { 156 g_source_remove(listener->io_tag[i]); 157 listener->io_tag[i] = 0; 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_tag[i] = qio_channel_add_watch( 165 QIO_CHANNEL(listener->sioc[i]), G_IO_IN, 166 qio_net_listener_channel_func, 167 listener, (GDestroyNotify)object_unref); 168 } 169 } 170 } 171 172 173 struct QIONetListenerClientWaitData { 174 QIOChannelSocket *sioc; 175 GMainLoop *loop; 176 }; 177 178 179 static gboolean qio_net_listener_wait_client_func(QIOChannel *ioc, 180 GIOCondition condition, 181 gpointer opaque) 182 { 183 struct QIONetListenerClientWaitData *data = opaque; 184 QIOChannelSocket *sioc; 185 186 sioc = qio_channel_socket_accept(QIO_CHANNEL_SOCKET(ioc), 187 NULL); 188 if (!sioc) { 189 return TRUE; 190 } 191 192 if (data->sioc) { 193 object_unref(OBJECT(sioc)); 194 } else { 195 data->sioc = sioc; 196 g_main_loop_quit(data->loop); 197 } 198 199 return TRUE; 200 } 201 202 QIOChannelSocket *qio_net_listener_wait_client(QIONetListener *listener) 203 { 204 GMainContext *ctxt = g_main_context_new(); 205 GMainLoop *loop = g_main_loop_new(ctxt, TRUE); 206 GSource **sources; 207 struct QIONetListenerClientWaitData data = { 208 .sioc = NULL, 209 .loop = loop 210 }; 211 size_t i; 212 213 for (i = 0; i < listener->nsioc; i++) { 214 if (listener->io_tag[i]) { 215 g_source_remove(listener->io_tag[i]); 216 listener->io_tag[i] = 0; 217 } 218 } 219 220 sources = g_new0(GSource *, listener->nsioc); 221 for (i = 0; i < listener->nsioc; i++) { 222 sources[i] = qio_channel_create_watch(QIO_CHANNEL(listener->sioc[i]), 223 G_IO_IN); 224 225 g_source_set_callback(sources[i], 226 (GSourceFunc)qio_net_listener_wait_client_func, 227 &data, 228 NULL); 229 g_source_attach(sources[i], ctxt); 230 } 231 232 g_main_loop_run(loop); 233 234 for (i = 0; i < listener->nsioc; i++) { 235 g_source_unref(sources[i]); 236 } 237 g_free(sources); 238 g_main_loop_unref(loop); 239 g_main_context_unref(ctxt); 240 241 if (listener->io_func != NULL) { 242 for (i = 0; i < listener->nsioc; i++) { 243 object_ref(OBJECT(listener)); 244 listener->io_tag[i] = qio_channel_add_watch( 245 QIO_CHANNEL(listener->sioc[i]), G_IO_IN, 246 qio_net_listener_channel_func, 247 listener, (GDestroyNotify)object_unref); 248 } 249 } 250 251 return data.sioc; 252 } 253 254 void qio_net_listener_disconnect(QIONetListener *listener) 255 { 256 size_t i; 257 258 if (!listener->connected) { 259 return; 260 } 261 262 for (i = 0; i < listener->nsioc; i++) { 263 if (listener->io_tag[i]) { 264 g_source_remove(listener->io_tag[i]); 265 listener->io_tag[i] = 0; 266 } 267 qio_channel_close(QIO_CHANNEL(listener->sioc[i]), NULL); 268 } 269 listener->connected = false; 270 } 271 272 273 bool qio_net_listener_is_connected(QIONetListener *listener) 274 { 275 return listener->connected; 276 } 277 278 static void qio_net_listener_finalize(Object *obj) 279 { 280 QIONetListener *listener = QIO_NET_LISTENER(obj); 281 size_t i; 282 283 qio_net_listener_disconnect(listener); 284 285 for (i = 0; i < listener->nsioc; i++) { 286 object_unref(OBJECT(listener->sioc[i])); 287 } 288 g_free(listener->io_tag); 289 g_free(listener->sioc); 290 g_free(listener->name); 291 } 292 293 static const TypeInfo qio_net_listener_info = { 294 .parent = TYPE_OBJECT, 295 .name = TYPE_QIO_NET_LISTENER, 296 .instance_size = sizeof(QIONetListener), 297 .instance_finalize = qio_net_listener_finalize, 298 .class_size = sizeof(QIONetListenerClass), 299 }; 300 301 302 static void qio_net_listener_register_types(void) 303 { 304 type_register_static(&qio_net_listener_info); 305 } 306 307 308 type_init(qio_net_listener_register_types); 309