1 /*
2 * linc-source.c: This file is part of the linc library.
3 *
4 * Authors:
5 * Owen Taylor (owen@redhat.com)
6 * Michael Meeks (michael@ximian.com)
7 * Tor Lillqvist (tml@novell.com)
8 *
9 * Copyright 1998, 2001, 2005, Red Hat, Inc., Ximian, Inc., Novell, Inc.
10 */
11
12 #include <config.h>
13 #include <sys/types.h>
14 #include <sys/stat.h>
15 #include <stdio.h>
16 #ifdef HAVE_UNISTD_H
17 # include <unistd.h>
18 #endif
19 #include <errno.h>
20 #include <string.h>
21 #include <fcntl.h>
22
23 #include <glib.h>
24
25 #include "linc-compat.h"
26 #include "linc-private.h"
27
28 #if defined (G_OS_WIN32) && defined (CONNECTION_DEBUG)
29
30 static char *
fd_mask(int mask)31 fd_mask (int mask)
32 {
33 static char buf[100];
34 int checked_bits = 0;
35 char *bufp = buf;
36
37 if (mask == 0)
38 return "";
39
40 #define BIT(n) checked_bits |= FD_##n; if (mask & FD_##n) bufp += sprintf (bufp, "%s" #n, (bufp>buf ? "|" : ""))
41
42 BIT (READ);
43 BIT (WRITE);
44 BIT (OOB);
45 BIT (ACCEPT);
46 BIT (CONNECT);
47 BIT (CLOSE);
48 BIT (QOS);
49 BIT (GROUP_QOS);
50 BIT (ROUTING_INTERFACE_CHANGE);
51 BIT (ADDRESS_LIST_CHANGE);
52
53 #undef BIT
54
55 if ((mask & ~checked_bits) != 0)
56 bufp += sprintf (bufp, "|%#x", mask & ~checked_bits);
57
58 return buf;
59 }
60
61 #endif
62
63 static gboolean
link_source_prepare(GSource * source,gint * timeout)64 link_source_prepare (GSource *source,
65 gint *timeout)
66 {
67 #ifdef G_OS_WIN32
68 LinkUnixWatch *watch = (LinkUnixWatch *) source;
69 int event_mask = 0;
70
71 if (watch->condition & G_IO_IN)
72 event_mask |= (FD_READ | FD_ACCEPT);
73 if (watch->condition & G_IO_OUT)
74 event_mask |= (FD_WRITE | FD_CONNECT);
75 if (watch->condition & G_IO_HUP)
76 event_mask |= FD_CLOSE;
77
78 if (watch->link_watch != NULL &&
79 watch->link_watch->last_polled_source != watch) {
80 watch->link_watch->last_polled_source = watch;
81 watch->event_mask = 0;
82 }
83
84 if (watch->event_mask != event_mask) {
85 #ifdef CONNECTION_DEBUG
86 d_printf ("prepare: WSAEventSelect(%d, %#x, {%s})\n",
87 watch->socket, watch->pollfd.fd, fd_mask (event_mask));
88 #endif
89 if (WSAEventSelect (watch->socket, (HANDLE) watch->pollfd.fd,
90 event_mask) == SOCKET_ERROR)
91 d_printf ("WSAEventSelect() failed: %s\n",
92 link_strerror (WSAGetLastError ()));
93 else
94 watch->event_mask = event_mask;
95 }
96 #endif
97
98 *timeout = -1;
99
100 return FALSE;
101 }
102
103 static gboolean
link_source_check(GSource * source)104 link_source_check (GSource *source)
105 {
106 LinkUnixWatch *watch = (LinkUnixWatch *)source;
107
108 #ifdef G_OS_WIN32
109 WSANETWORKEVENTS events;
110
111 d_printf ("check: sock=%d handle=%#x revents=%x condition=%x ",
112 watch->socket, watch->pollfd.fd,
113 watch->pollfd.revents, watch->condition);
114
115 if (WSAEnumNetworkEvents (watch->socket,
116 /* (HANDLE) watch->pollfd.fd, */ 0,
117 &events) == SOCKET_ERROR)
118 d_printf ("\nWSAEnumNetworkEvents failed: %s\n",
119 link_strerror (WSAGetLastError ()));
120 else {
121 #ifdef CONNECTION_DEBUG
122 d_printf ("events={%s}\n", fd_mask (events.lNetworkEvents));
123 #endif
124 if (watch->pollfd.revents != 0 &&
125 events.lNetworkEvents == 0) {
126 watch->event_mask = 0;
127 d_printf ("check: WSAEventSelect(%d, %#x, {})\n",
128 watch->socket, watch->pollfd.fd);
129 WSAEventSelect (watch->socket, (HANDLE) watch->pollfd.fd, 0);
130 d_printf ("check: ResetEvent(%#x)\n",
131 watch->pollfd.fd);
132 ResetEvent ((HANDLE) watch->pollfd.fd);
133 }
134 watch->pollfd.revents = 0;
135 if (events.lNetworkEvents & (FD_READ | FD_ACCEPT))
136 watch->pollfd.revents |= G_IO_IN;
137 if (events.lNetworkEvents & (FD_WRITE | FD_CONNECT))
138 watch->pollfd.revents |= G_IO_OUT;
139 if (events.lNetworkEvents & (FD_CLOSE))
140 watch->pollfd.revents |= G_IO_HUP;
141 }
142
143 if (!watch->write_would_have_blocked && (watch->event_mask & FD_WRITE))
144 watch->pollfd.revents |= G_IO_OUT; /* This sucks but... */
145 #endif
146
147 return watch->pollfd.revents & watch->condition;
148 }
149
150 static gboolean
link_source_dispatch(GSource * source,GSourceFunc callback,gpointer user_data)151 link_source_dispatch (GSource *source,
152 GSourceFunc callback,
153 gpointer user_data)
154
155 {
156 GIOFunc func;
157 LinkUnixWatch *watch = (LinkUnixWatch *) source;
158
159 if (!callback)
160 g_error ("No callback");
161
162 func = (GIOFunc) callback;
163
164 return (*func) (watch->channel,
165 watch->pollfd.revents & watch->condition,
166 user_data);
167 }
168
169 static void
link_source_finalize(GSource * source)170 link_source_finalize (GSource *source)
171 {
172 d_printf ("Finalize source %p\n", source);
173 }
174
175 static GSourceFuncs link_source_watch_funcs = {
176 link_source_prepare,
177 link_source_check,
178 link_source_dispatch,
179 link_source_finalize
180 };
181
182 /**
183 * link_source_set_condition:
184 * @source: a source created with #link_source_create_watch
185 * @condition: a new condition.
186 *
187 * This sets a new IO condition on an existing
188 * source very rapidly.
189 **/
190 void
link_source_set_condition(GSource * source,GIOCondition condition)191 link_source_set_condition (GSource *source,
192 GIOCondition condition)
193 {
194 LinkUnixWatch *watch = (LinkUnixWatch *) source;
195
196 if (watch) {
197 watch->pollfd.events = condition;
198 watch->condition = condition;
199 }
200 }
201
202 /**
203 * link_source_create_watch:
204 * @context: context to add to (or NULL for default)
205 * @fd: file descriptor to poll on
206 * @opt_channel: channel, handed to the callback (can be NULL)
207 * @condition: IO condition eg. G_IO_IN|G_IO_PRI
208 * @func: callback when condition is met
209 * @user_data: callback closure.
210 *
211 * This adds a new source to the specified context.
212 *
213 * Return value: the source handle so you can remove it later.
214 **/
215 GSource *
link_source_create_watch(GMainContext * context,int fd,GIOChannel * opt_channel,GIOCondition condition,GIOFunc func,gpointer user_data)216 link_source_create_watch (GMainContext *context,
217 int fd,
218 GIOChannel *opt_channel,
219 GIOCondition condition,
220 GIOFunc func,
221 gpointer user_data)
222 {
223 GSource *source;
224 LinkUnixWatch *watch;
225
226 source = g_source_new (&link_source_watch_funcs,
227 sizeof (LinkUnixWatch));
228 watch = (LinkUnixWatch *) source;
229
230 #ifdef G_OS_WIN32
231 watch->pollfd.fd = (int) WSACreateEvent ();
232 d_printf ("WSACreateEvent(): for socket %d: %#x\n", fd, watch->pollfd.fd);
233 watch->link_watch = NULL;
234 watch->socket = fd;
235 watch->event_mask = 0;
236 watch->write_would_have_blocked = FALSE;
237 #else
238 watch->pollfd.fd = fd;
239 #endif
240 watch->channel = opt_channel;
241 watch->condition = condition;
242 watch->callback = func;
243 watch->user_data = user_data;
244
245 link_source_set_condition (source, condition);
246
247 g_source_set_can_recurse (source, TRUE);
248 g_source_add_poll (source, &watch->pollfd);
249
250 g_source_set_callback (source, (GSourceFunc) func,
251 user_data, NULL);
252 g_source_attach (source, context);
253
254 return source;
255 }
256
257 static GSource *
link_source_create_watch_for_watch(LinkWatch * watch,GMainContext * context,int fd,GIOChannel * opt_channel,GIOCondition condition,GIOFunc func,gpointer user_data)258 link_source_create_watch_for_watch (LinkWatch *watch,
259 GMainContext *context,
260 int fd,
261 GIOChannel *opt_channel,
262 GIOCondition condition,
263 GIOFunc func,
264 gpointer user_data)
265 {
266 GSource *retval =
267 link_source_create_watch (context, fd, opt_channel,
268 condition, func, user_data);
269
270 #ifdef G_OS_WIN32
271 ((LinkUnixWatch *) retval)->link_watch = watch;
272 #endif
273
274 return retval;
275 }
276
277 #ifdef G_OS_WIN32
278
279 void
link_win32_watch_set_write_wouldblock(LinkWatch * w,gboolean flag)280 link_win32_watch_set_write_wouldblock (LinkWatch *w,
281 gboolean flag)
282 {
283 if (w->link_source)
284 ((LinkUnixWatch *)w->link_source)->write_would_have_blocked = flag;
285 if (w->main_source)
286 ((LinkUnixWatch *)w->main_source)->write_would_have_blocked = flag;
287 }
288
289 #endif
290
291 LinkWatch *
link_io_add_watch_fd(int fd,GIOCondition condition,GIOFunc func,gpointer user_data)292 link_io_add_watch_fd (int fd,
293 GIOCondition condition,
294 GIOFunc func,
295 gpointer user_data)
296 {
297 LinkWatch *w;
298 GMainContext *thread_ctx;
299
300 w = g_new0 (LinkWatch, 1);
301
302 if ((thread_ctx = link_thread_io_context ())) {
303 /* Have a dedicated I/O worker thread */
304 w->link_source = link_source_create_watch_for_watch
305 (w, thread_ctx, fd, NULL, condition, func, user_data);
306
307 } else {
308 /* Have an inferior and hook into the glib context */
309
310 /* Link loop */
311 w->link_source = link_source_create_watch_for_watch
312 (w, link_main_get_context (), fd, NULL,
313 condition, func, user_data);
314
315 w->main_source = link_source_create_watch_for_watch
316 (w, NULL, fd, NULL,
317 condition, func, user_data);
318 }
319
320 return w;
321 }
322
323 static void
link_watch_unlisten(LinkWatch * w)324 link_watch_unlisten (LinkWatch *w)
325 {
326 if (w->main_source) {
327 link_source_set_condition (w->main_source, 0);
328 #ifdef G_OS_WIN32
329 d_printf ("CloseHandle(%#x)\n",
330 ((LinkUnixWatch *) w->main_source)->pollfd.fd);
331 if (!CloseHandle ((HANDLE) ((LinkUnixWatch *) w->main_source)->pollfd.fd))
332 d_printf ("CloseHandle failed: %ld\n", GetLastError ());
333 #endif
334 g_source_destroy (w->main_source);
335 g_source_unref (w->main_source);
336 w->main_source = NULL;
337 }
338
339 if (w->link_source) {
340 link_source_set_condition (w->link_source, 0);
341 #ifdef G_OS_WIN32
342 d_printf ("CloseHandle(%#x)\n",
343 ((LinkUnixWatch *) w->link_source)->pollfd.fd);
344 if (!CloseHandle ((HANDLE) ((LinkUnixWatch *) w->link_source)->pollfd.fd))
345 d_printf ("CloseHandle failed: %ld\n", GetLastError ());
346 #endif
347 g_source_destroy (w->link_source);
348 g_source_unref (w->link_source);
349 w->link_source = NULL;
350 }
351 #ifdef G_OS_WIN32
352 w->last_polled_source = NULL;
353 #endif
354 }
355
356 void
link_io_remove_watch(LinkWatch * w)357 link_io_remove_watch (LinkWatch *w)
358 {
359 if (!w)
360 return;
361
362 link_watch_unlisten (w);
363 g_free (w);
364 }
365
366 void
link_watch_set_condition(LinkWatch * w,GIOCondition condition)367 link_watch_set_condition (LinkWatch *w,
368 GIOCondition condition)
369 {
370 if (w) {
371 link_source_set_condition (
372 w->link_source, condition);
373
374 link_source_set_condition (
375 w->main_source, condition);
376 }
377 }
378
379 /*
380 * Migrates the source to/from the main thread.
381 */
382 void
link_watch_move_io(LinkWatch * w,gboolean to_io_thread)383 link_watch_move_io (LinkWatch *w,
384 gboolean to_io_thread)
385 {
386 LinkUnixWatch w_cpy;
387
388 if (!w)
389 return;
390
391 g_assert (to_io_thread); /* FIXME */
392
393 w_cpy = *(LinkUnixWatch *)w->link_source;
394
395 link_watch_unlisten (w);
396
397 w->link_source = link_source_create_watch_for_watch
398 (w, link_thread_io_context (),
399 #ifdef G_OS_WIN32
400 w_cpy.socket,
401 #else
402 w_cpy.pollfd.fd,
403 #endif
404 w_cpy.channel, w_cpy.condition,
405 w_cpy.callback, w_cpy.user_data);
406 }
407