xref: /openbmc/qemu/io/channel-watch.c (revision b4b9a0e3)
1 /*
2  * QEMU I/O channels watch helper APIs
3  *
4  * Copyright (c) 2015 Red Hat, Inc.
5  *
6  * This library is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU Lesser General Public
8  * License as published by the Free Software Foundation; either
9  * version 2.1 of the License, or (at your option) any later version.
10  *
11  * This library 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 GNU
14  * Lesser General Public License for more details.
15  *
16  * You should have received a copy of the GNU Lesser General Public
17  * License along with this library; if not, see <http://www.gnu.org/licenses/>.
18  *
19  */
20 
21 #include "qemu/osdep.h"
22 #include "io/channel-watch.h"
23 
24 typedef struct QIOChannelFDSource QIOChannelFDSource;
25 struct QIOChannelFDSource {
26     GSource parent;
27     GPollFD fd;
28     QIOChannel *ioc;
29     GIOCondition condition;
30 };
31 
32 
33 #ifdef CONFIG_WIN32
34 typedef struct QIOChannelSocketSource QIOChannelSocketSource;
35 struct QIOChannelSocketSource {
36     GSource parent;
37     GPollFD fd;
38     QIOChannel *ioc;
39     SOCKET socket;
40     int revents;
41     GIOCondition condition;
42 };
43 
44 #endif
45 
46 
47 typedef struct QIOChannelFDPairSource QIOChannelFDPairSource;
48 struct QIOChannelFDPairSource {
49     GSource parent;
50     GPollFD fdread;
51     GPollFD fdwrite;
52     QIOChannel *ioc;
53     GIOCondition condition;
54 };
55 
56 
57 static gboolean
58 qio_channel_fd_source_prepare(GSource *source G_GNUC_UNUSED,
59                               gint *timeout)
60 {
61     *timeout = -1;
62 
63     return FALSE;
64 }
65 
66 
67 static gboolean
68 qio_channel_fd_source_check(GSource *source)
69 {
70     QIOChannelFDSource *ssource = (QIOChannelFDSource *)source;
71 
72     return ssource->fd.revents & ssource->condition;
73 }
74 
75 
76 static gboolean
77 qio_channel_fd_source_dispatch(GSource *source,
78                                GSourceFunc callback,
79                                gpointer user_data)
80 {
81     QIOChannelFunc func = (QIOChannelFunc)callback;
82     QIOChannelFDSource *ssource = (QIOChannelFDSource *)source;
83 
84     return (*func)(ssource->ioc,
85                    ssource->fd.revents & ssource->condition,
86                    user_data);
87 }
88 
89 
90 static void
91 qio_channel_fd_source_finalize(GSource *source)
92 {
93     QIOChannelFDSource *ssource = (QIOChannelFDSource *)source;
94 
95     object_unref(OBJECT(ssource->ioc));
96 }
97 
98 
99 #ifdef CONFIG_WIN32
100 static gboolean
101 qio_channel_socket_source_prepare(GSource *source G_GNUC_UNUSED,
102                                   gint *timeout)
103 {
104     *timeout = -1;
105 
106     return FALSE;
107 }
108 
109 
110 /*
111  * NB, this impl only works when the socket is in non-blocking
112  * mode on Win32
113  */
114 static gboolean
115 qio_channel_socket_source_check(GSource *source)
116 {
117     static struct timeval tv0;
118 
119     QIOChannelSocketSource *ssource = (QIOChannelSocketSource *)source;
120     WSANETWORKEVENTS ev;
121     fd_set rfds, wfds, xfds;
122 
123     if (!ssource->condition) {
124         return 0;
125     }
126 
127     WSAEnumNetworkEvents(ssource->socket, ssource->ioc->event, &ev);
128 
129     FD_ZERO(&rfds);
130     FD_ZERO(&wfds);
131     FD_ZERO(&xfds);
132     if (ssource->condition & G_IO_IN) {
133         FD_SET((SOCKET)ssource->socket, &rfds);
134     }
135     if (ssource->condition & G_IO_OUT) {
136         FD_SET((SOCKET)ssource->socket, &wfds);
137     }
138     if (ssource->condition & G_IO_PRI) {
139         FD_SET((SOCKET)ssource->socket, &xfds);
140     }
141     ssource->revents = 0;
142     if (select(0, &rfds, &wfds, &xfds, &tv0) == 0) {
143         return 0;
144     }
145 
146     if (FD_ISSET(ssource->socket, &rfds)) {
147         ssource->revents |= G_IO_IN;
148     }
149     if (FD_ISSET(ssource->socket, &wfds)) {
150         ssource->revents |= G_IO_OUT;
151     }
152     if (FD_ISSET(ssource->socket, &xfds)) {
153         ssource->revents |= G_IO_PRI;
154     }
155 
156     return ssource->revents;
157 }
158 
159 
160 static gboolean
161 qio_channel_socket_source_dispatch(GSource *source,
162                                    GSourceFunc callback,
163                                    gpointer user_data)
164 {
165     QIOChannelFunc func = (QIOChannelFunc)callback;
166     QIOChannelSocketSource *ssource = (QIOChannelSocketSource *)source;
167 
168     return (*func)(ssource->ioc, ssource->revents, user_data);
169 }
170 
171 
172 static void
173 qio_channel_socket_source_finalize(GSource *source)
174 {
175     QIOChannelSocketSource *ssource = (QIOChannelSocketSource *)source;
176 
177     object_unref(OBJECT(ssource->ioc));
178 }
179 
180 
181 GSourceFuncs qio_channel_socket_source_funcs = {
182     qio_channel_socket_source_prepare,
183     qio_channel_socket_source_check,
184     qio_channel_socket_source_dispatch,
185     qio_channel_socket_source_finalize
186 };
187 #endif
188 
189 
190 static gboolean
191 qio_channel_fd_pair_source_prepare(GSource *source G_GNUC_UNUSED,
192                                    gint *timeout)
193 {
194     *timeout = -1;
195 
196     return FALSE;
197 }
198 
199 
200 static gboolean
201 qio_channel_fd_pair_source_check(GSource *source)
202 {
203     QIOChannelFDPairSource *ssource = (QIOChannelFDPairSource *)source;
204     GIOCondition poll_condition = ssource->fdread.revents |
205         ssource->fdwrite.revents;
206 
207     return poll_condition & ssource->condition;
208 }
209 
210 
211 static gboolean
212 qio_channel_fd_pair_source_dispatch(GSource *source,
213                                     GSourceFunc callback,
214                                     gpointer user_data)
215 {
216     QIOChannelFunc func = (QIOChannelFunc)callback;
217     QIOChannelFDPairSource *ssource = (QIOChannelFDPairSource *)source;
218     GIOCondition poll_condition = ssource->fdread.revents |
219         ssource->fdwrite.revents;
220 
221     return (*func)(ssource->ioc,
222                    poll_condition & ssource->condition,
223                    user_data);
224 }
225 
226 
227 static void
228 qio_channel_fd_pair_source_finalize(GSource *source)
229 {
230     QIOChannelFDPairSource *ssource = (QIOChannelFDPairSource *)source;
231 
232     object_unref(OBJECT(ssource->ioc));
233 }
234 
235 
236 GSourceFuncs qio_channel_fd_source_funcs = {
237     qio_channel_fd_source_prepare,
238     qio_channel_fd_source_check,
239     qio_channel_fd_source_dispatch,
240     qio_channel_fd_source_finalize
241 };
242 
243 
244 GSourceFuncs qio_channel_fd_pair_source_funcs = {
245     qio_channel_fd_pair_source_prepare,
246     qio_channel_fd_pair_source_check,
247     qio_channel_fd_pair_source_dispatch,
248     qio_channel_fd_pair_source_finalize
249 };
250 
251 
252 GSource *qio_channel_create_fd_watch(QIOChannel *ioc,
253                                      int fd,
254                                      GIOCondition condition)
255 {
256     GSource *source;
257     QIOChannelFDSource *ssource;
258 
259     source = g_source_new(&qio_channel_fd_source_funcs,
260                           sizeof(QIOChannelFDSource));
261     ssource = (QIOChannelFDSource *)source;
262 
263     ssource->ioc = ioc;
264     object_ref(OBJECT(ioc));
265 
266     ssource->condition = condition;
267 
268 #ifdef CONFIG_WIN32
269     ssource->fd.fd = (gint64)_get_osfhandle(fd);
270 #else
271     ssource->fd.fd = fd;
272 #endif
273     ssource->fd.events = condition;
274 
275     g_source_add_poll(source, &ssource->fd);
276 
277     return source;
278 }
279 
280 #ifdef CONFIG_WIN32
281 GSource *qio_channel_create_socket_watch(QIOChannel *ioc,
282                                          int socket,
283                                          GIOCondition condition)
284 {
285     GSource *source;
286     QIOChannelSocketSource *ssource;
287 
288 #ifdef WIN32
289     WSAEventSelect(socket, ioc->event,
290                    FD_READ | FD_ACCEPT | FD_CLOSE |
291                    FD_CONNECT | FD_WRITE | FD_OOB);
292 #endif
293 
294     source = g_source_new(&qio_channel_socket_source_funcs,
295                           sizeof(QIOChannelSocketSource));
296     ssource = (QIOChannelSocketSource *)source;
297 
298     ssource->ioc = ioc;
299     object_ref(OBJECT(ioc));
300 
301     ssource->condition = condition;
302     ssource->socket = socket;
303     ssource->revents = 0;
304 
305     ssource->fd.fd = (gintptr)ioc->event;
306     ssource->fd.events = G_IO_IN;
307 
308     g_source_add_poll(source, &ssource->fd);
309 
310     return source;
311 }
312 #else
313 GSource *qio_channel_create_socket_watch(QIOChannel *ioc,
314                                          int socket,
315                                          GIOCondition condition)
316 {
317     return qio_channel_create_fd_watch(ioc, socket, condition);
318 }
319 #endif
320 
321 GSource *qio_channel_create_fd_pair_watch(QIOChannel *ioc,
322                                           int fdread,
323                                           int fdwrite,
324                                           GIOCondition condition)
325 {
326     GSource *source;
327     QIOChannelFDPairSource *ssource;
328 
329     source = g_source_new(&qio_channel_fd_pair_source_funcs,
330                           sizeof(QIOChannelFDPairSource));
331     ssource = (QIOChannelFDPairSource *)source;
332 
333     ssource->ioc = ioc;
334     object_ref(OBJECT(ioc));
335 
336     ssource->condition = condition;
337 
338 #ifdef CONFIG_WIN32
339     ssource->fdread.fd = (gint64)_get_osfhandle(fdread);
340     ssource->fdwrite.fd = (gint64)_get_osfhandle(fdwrite);
341 #else
342     ssource->fdread.fd = fdread;
343     ssource->fdwrite.fd = fdwrite;
344 #endif
345 
346     ssource->fdread.events = condition & G_IO_IN;
347     ssource->fdwrite.events = condition & G_IO_OUT;
348 
349     g_source_add_poll(source, &ssource->fdread);
350     g_source_add_poll(source, &ssource->fdwrite);
351 
352     return source;
353 }
354