xref: /openbmc/qemu/io/net-listener.c (revision f0dbe427ec7c0dd81fc95c1d0d7174b79b6e6587)
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                                 int num,
66                                 Error **errp)
67  {
68      QIODNSResolver *resolver = qio_dns_resolver_get_instance();
69      SocketAddress **resaddrs;
70      size_t nresaddrs;
71      size_t i;
72      Error *err = NULL;
73      bool success = false;
74  
75      if (qio_dns_resolver_lookup_sync(resolver,
76                                       addr,
77                                       &nresaddrs,
78                                       &resaddrs,
79                                       errp) < 0) {
80          return -1;
81      }
82  
83      for (i = 0; i < nresaddrs; i++) {
84          QIOChannelSocket *sioc = qio_channel_socket_new();
85  
86          if (qio_channel_socket_listen_sync(sioc, resaddrs[i], num,
87                                             err ? NULL : &err) == 0) {
88              success = true;
89  
90              qio_net_listener_add(listener, sioc);
91          }
92  
93          qapi_free_SocketAddress(resaddrs[i]);
94          object_unref(OBJECT(sioc));
95      }
96      g_free(resaddrs);
97  
98      if (success) {
99          error_free(err);
100          return 0;
101      } else {
102          error_propagate(errp, err);
103          return -1;
104      }
105  }
106  
107  
108  void qio_net_listener_add(QIONetListener *listener,
109                            QIOChannelSocket *sioc)
110  {
111      if (listener->name) {
112          qio_channel_set_name(QIO_CHANNEL(sioc), listener->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      if (listener->io_notify) {
294          listener->io_notify(listener->io_data);
295      }
296      qio_net_listener_disconnect(listener);
297  
298      for (i = 0; i < listener->nsioc; i++) {
299          object_unref(OBJECT(listener->sioc[i]));
300      }
301      g_free(listener->io_source);
302      g_free(listener->sioc);
303      g_free(listener->name);
304  }
305  
306  static const TypeInfo qio_net_listener_info = {
307      .parent = TYPE_OBJECT,
308      .name = TYPE_QIO_NET_LISTENER,
309      .instance_size = sizeof(QIONetListener),
310      .instance_finalize = qio_net_listener_finalize,
311  };
312  
313  
314  static void qio_net_listener_register_types(void)
315  {
316      type_register_static(&qio_net_listener_info);
317  }
318  
319  
320  type_init(qio_net_listener_register_types);
321