xref: /openbmc/qemu/io/net-listener.c (revision 4e2d8bf6)
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