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