xref: /openbmc/qemu/io/channel-watch.c (revision 7a5951f6)
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     QIOChannelSocketSource *ssource = (QIOChannelSocketSource *)source;
119     fd_set rfds, wfds, xfds;
120 
121     if (!ssource->condition) {
122         return 0;
123     }
124 
125     FD_ZERO(&rfds);
126     FD_ZERO(&wfds);
127     FD_ZERO(&xfds);
128     if (ssource->condition & G_IO_IN) {
129         FD_SET(ssource->socket, &rfds);
130     }
131     if (ssource->condition & G_IO_OUT) {
132         FD_SET(ssource->socket, &wfds);
133     }
134     if (ssource->condition & G_IO_PRI) {
135         FD_SET(ssource->socket, &xfds);
136     }
137     ssource->revents = 0;
138     if (select(0, &rfds, &wfds, &xfds, &tv0) == 0) {
139         return 0;
140     }
141 
142     if (FD_ISSET(ssource->socket, &rfds)) {
143         ssource->revents |= G_IO_IN;
144     }
145     if (FD_ISSET(ssource->socket, &wfds)) {
146         ssource->revents |= G_IO_OUT;
147     }
148     if (FD_ISSET(ssource->socket, &xfds)) {
149         ssource->revents |= G_IO_PRI;
150     }
151 
152     return ssource->revents;
153 }
154 
155 
156 static gboolean
157 qio_channel_socket_source_dispatch(GSource *source,
158                                    GSourceFunc callback,
159                                    gpointer user_data)
160 {
161     QIOChannelFunc func = (QIOChannelFunc)callback;
162     QIOChannelSocketSource *ssource = (QIOChannelSocketSource *)source;
163 
164     return (*func)(ssource->ioc, ssource->revents, user_data);
165 }
166 
167 
168 static void
169 qio_channel_socket_source_finalize(GSource *source)
170 {
171     QIOChannelSocketSource *ssource = (QIOChannelSocketSource *)source;
172 
173     object_unref(OBJECT(ssource->ioc));
174 }
175 
176 
177 GSourceFuncs qio_channel_socket_source_funcs = {
178     qio_channel_socket_source_prepare,
179     qio_channel_socket_source_check,
180     qio_channel_socket_source_dispatch,
181     qio_channel_socket_source_finalize
182 };
183 #endif
184 
185 
186 static gboolean
187 qio_channel_fd_pair_source_prepare(GSource *source G_GNUC_UNUSED,
188                                    gint *timeout)
189 {
190     *timeout = -1;
191 
192     return FALSE;
193 }
194 
195 
196 static gboolean
197 qio_channel_fd_pair_source_check(GSource *source)
198 {
199     QIOChannelFDPairSource *ssource = (QIOChannelFDPairSource *)source;
200     GIOCondition poll_condition = ssource->fdread.revents |
201         ssource->fdwrite.revents;
202 
203     return poll_condition & ssource->condition;
204 }
205 
206 
207 static gboolean
208 qio_channel_fd_pair_source_dispatch(GSource *source,
209                                     GSourceFunc callback,
210                                     gpointer user_data)
211 {
212     QIOChannelFunc func = (QIOChannelFunc)callback;
213     QIOChannelFDPairSource *ssource = (QIOChannelFDPairSource *)source;
214     GIOCondition poll_condition = ssource->fdread.revents |
215         ssource->fdwrite.revents;
216 
217     return (*func)(ssource->ioc,
218                    poll_condition & ssource->condition,
219                    user_data);
220 }
221 
222 
223 static void
224 qio_channel_fd_pair_source_finalize(GSource *source)
225 {
226     QIOChannelFDPairSource *ssource = (QIOChannelFDPairSource *)source;
227 
228     object_unref(OBJECT(ssource->ioc));
229 }
230 
231 
232 GSourceFuncs qio_channel_fd_source_funcs = {
233     qio_channel_fd_source_prepare,
234     qio_channel_fd_source_check,
235     qio_channel_fd_source_dispatch,
236     qio_channel_fd_source_finalize
237 };
238 
239 
240 GSourceFuncs qio_channel_fd_pair_source_funcs = {
241     qio_channel_fd_pair_source_prepare,
242     qio_channel_fd_pair_source_check,
243     qio_channel_fd_pair_source_dispatch,
244     qio_channel_fd_pair_source_finalize
245 };
246 
247 
248 GSource *qio_channel_create_fd_watch(QIOChannel *ioc,
249                                      int fd,
250                                      GIOCondition condition)
251 {
252     GSource *source;
253     QIOChannelFDSource *ssource;
254 
255     source = g_source_new(&qio_channel_fd_source_funcs,
256                           sizeof(QIOChannelFDSource));
257     ssource = (QIOChannelFDSource *)source;
258 
259     ssource->ioc = ioc;
260     object_ref(OBJECT(ioc));
261 
262     ssource->condition = condition;
263 
264 #ifdef CONFIG_WIN32
265     ssource->fd.fd = (gint64)_get_osfhandle(fd);
266 #else
267     ssource->fd.fd = fd;
268 #endif
269     ssource->fd.events = condition;
270 
271     g_source_add_poll(source, &ssource->fd);
272 
273     return source;
274 }
275 
276 #ifdef CONFIG_WIN32
277 GSource *qio_channel_create_socket_watch(QIOChannel *ioc,
278                                          int socket,
279                                          GIOCondition condition)
280 {
281     GSource *source;
282     QIOChannelSocketSource *ssource;
283 
284     WSAEventSelect(socket, ioc->event,
285                    FD_READ | FD_ACCEPT | FD_CLOSE |
286                    FD_CONNECT | FD_WRITE | FD_OOB);
287 
288     source = g_source_new(&qio_channel_socket_source_funcs,
289                           sizeof(QIOChannelSocketSource));
290     ssource = (QIOChannelSocketSource *)source;
291 
292     ssource->ioc = ioc;
293     object_ref(OBJECT(ioc));
294 
295     ssource->condition = condition;
296     ssource->socket = socket;
297     ssource->revents = 0;
298 
299     ssource->fd.fd = (gintptr)ioc->event;
300     ssource->fd.events = G_IO_IN;
301 
302     g_source_add_poll(source, &ssource->fd);
303 
304     return source;
305 }
306 #else
307 GSource *qio_channel_create_socket_watch(QIOChannel *ioc,
308                                          int socket,
309                                          GIOCondition condition)
310 {
311     return qio_channel_create_fd_watch(ioc, socket, condition);
312 }
313 #endif
314 
315 GSource *qio_channel_create_fd_pair_watch(QIOChannel *ioc,
316                                           int fdread,
317                                           int fdwrite,
318                                           GIOCondition condition)
319 {
320     GSource *source;
321     QIOChannelFDPairSource *ssource;
322 
323     source = g_source_new(&qio_channel_fd_pair_source_funcs,
324                           sizeof(QIOChannelFDPairSource));
325     ssource = (QIOChannelFDPairSource *)source;
326 
327     ssource->ioc = ioc;
328     object_ref(OBJECT(ioc));
329 
330     ssource->condition = condition;
331 
332 #ifdef CONFIG_WIN32
333     ssource->fdread.fd = (gint64)_get_osfhandle(fdread);
334     ssource->fdwrite.fd = (gint64)_get_osfhandle(fdwrite);
335 #else
336     ssource->fdread.fd = fdread;
337     ssource->fdwrite.fd = fdwrite;
338 #endif
339 
340     ssource->fdread.events = condition & G_IO_IN;
341     ssource->fdwrite.events = condition & G_IO_OUT;
342 
343     g_source_add_poll(source, &ssource->fdread);
344     g_source_add_poll(source, &ssource->fdwrite);
345 
346     return source;
347 }
348