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 | |
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 | |