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