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 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
24typedef struct QIOChannelFDSource QIOChannelFDSource;
25struct QIOChannelFDSource {
26 GSource parent;
27 GPollFD fd;
28 QIOChannel *ioc;
29 GIOCondition condition;
30};
31
32
33#ifdef CONFIG_WIN32
34typedef struct QIOChannelSocketSource QIOChannelSocketSource;
35struct 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
47typedef struct QIOChannelFDPairSource QIOChannelFDPairSource;
48struct QIOChannelFDPairSource {
49 GSource parent;
50 GPollFD fdread;
51 GPollFD fdwrite;
52 QIOChannel *ioc;
53 GIOCondition condition;
54};
55
56
57static gboolean
58qio_channel_fd_source_prepare(GSource *source G_GNUC_UNUSED,
59 gint *timeout)
60{
61 *timeout = -1;
62
63 return FALSE;
64}
65
66
67static gboolean
68qio_channel_fd_source_check(GSource *source)
69{
70 QIOChannelFDSource *ssource = (QIOChannelFDSource *)source;
71
72 return ssource->fd.revents & ssource->condition;
73}
74
75
76static gboolean
77qio_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
90static void
91qio_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
100static gboolean
101qio_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 */
114static gboolean
115qio_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
160static gboolean
161qio_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
172static void
173qio_channel_socket_source_finalize(GSource *source)
174{
175 QIOChannelSocketSource *ssource = (QIOChannelSocketSource *)source;
176
177 object_unref(OBJECT(ssource->ioc));
178}
179
180
181GSourceFuncs 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
190static gboolean
191qio_channel_fd_pair_source_prepare(GSource *source G_GNUC_UNUSED,
192 gint *timeout)
193{
194 *timeout = -1;
195
196 return FALSE;
197}
198
199
200static gboolean
201qio_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
211static gboolean
212qio_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
227static void
228qio_channel_fd_pair_source_finalize(GSource *source)
229{
230 QIOChannelFDPairSource *ssource = (QIOChannelFDPairSource *)source;
231
232 object_unref(OBJECT(ssource->ioc));
233}
234
235
236GSourceFuncs 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
244GSourceFuncs 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
252GSource *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
281GSource *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
313GSource *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
321GSource *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