1 /*
2     GSK - a library to write servers
3     Copyright (C) 1999-2000 Dave Benson
4 
5     This library is free software; you can redistribute it and/or
6     modify it under the terms of the GNU Lesser General Public
7     License as published by the Free Software Foundation; either
8     version 2 of the License, or (at your option) any later version.
9 
10     This library is distributed in the hope that it will be useful,
11     but WITHOUT ANY WARRANTY; without even the implied warranty of
12     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13     Lesser General Public License for more details.
14 
15     You should have received a copy of the GNU Lesser General Public
16     License along with this library; if not, write to the Free Software
17     Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307  USA
18 
19     Contact:
20         daveb@ffem.org <Dave Benson>
21 */
22 
23 #include "gskmainloopkqueue.h"
24 #include "config.h"
25 static GObjectClass *parent_class = NULL;
26 
27 #if HAVE_KQUEUE
28 #include <signal.h>
29 #include <errno.h>
30 
31 /* Include headers for kqueue(), kevent() and struct kevent. */
32 #include <sys/types.h>
33 #include <sys/event.h>
34 #include <sys/time.h>
35 #endif
36 
37 #include <unistd.h>    /* needed for close() */
38 #include <string.h>
39 
40 /* --- GskMainLoop methods --- */
41 
42 static gboolean
gsk_main_loop_kqueue_setup(GskMainLoop * main_loop)43 gsk_main_loop_kqueue_setup (GskMainLoop *main_loop)
44 {
45   GskMainLoopKqueue *main_loop_kqueue = GSK_MAIN_LOOP_KQUEUE (main_loop);
46 #if HAVE_KQUEUE
47   main_loop_kqueue->kernel_queue_id = kqueue ();
48   return (main_loop_kqueue->kernel_queue_id >= 0);
49 #else
50   (void) main_loop_kqueue;
51   return FALSE;
52 #endif
53 }
54 
55 #if HAVE_KQUEUE
56 static void
kqueue_flush_pending_changes(GskMainLoopKqueue * main_loop_kqueue)57 kqueue_flush_pending_changes (GskMainLoopKqueue *main_loop_kqueue)
58 {
59   if (main_loop_kqueue->num_updates > 0)
60     {
61       struct timespec timeout = { 0, 0 };
62       kevent (main_loop_kqueue->kernel_queue_id,
63               (const struct kevent *) main_loop_kqueue->kevent_array,
64 	      main_loop_kqueue->num_updates,
65 	      NULL, 0,
66               &timeout);
67       main_loop_kqueue->num_updates = 0;
68     }
69 }
70 
71 static inline void
change_signal(int signo,gboolean ignore)72 change_signal (int signo, gboolean ignore)
73 {
74   struct sigaction action;
75   memset (&action, 0, sizeof (action));
76   action.sa_handler = ignore ? SIG_IGN : SIG_DFL;
77   sigaction (signo, &action, NULL);
78 }
79 
do_nothing(int signo)80 static void do_nothing (int signo)
81 {
82 }
83 
84 static inline void
almost_ignore_sigchld(void)85 almost_ignore_sigchld (void)
86 {
87   struct sigaction action;
88   memset (&action, 0, sizeof (action));
89   action.sa_handler = do_nothing;
90   sigaction (SIGCHLD, &action, NULL);
91 }
92 
93 static inline struct kevent *
get_next_update(GskMainLoopKqueue * main_loop_kqueue)94 get_next_update ( GskMainLoopKqueue *main_loop_kqueue)
95 {
96   if (main_loop_kqueue->num_updates == main_loop_kqueue->max_updates)
97     {
98       if (main_loop_kqueue->max_updates == 0)
99         main_loop_kqueue->max_updates = 128;
100       else
101         main_loop_kqueue->max_updates *= 2;
102       main_loop_kqueue->kevent_array
103 	= g_realloc (main_loop_kqueue->kevent_array,
104 		     sizeof (struct kevent) * main_loop_kqueue->max_updates);
105     }
106   return ((struct kevent *) (main_loop_kqueue->kevent_array))
107         + main_loop_kqueue->num_updates;
108 }
109 
110 
111 static void
gsk_main_loop_kqueue_change(GskMainLoop * main_loop,GskMainLoopChange * change)112 gsk_main_loop_kqueue_change (GskMainLoop       *main_loop,
113                              GskMainLoopChange *change)
114 {
115   GskMainLoopKqueue *main_loop_kqueue = GSK_MAIN_LOOP_KQUEUE (main_loop);
116   if (main_loop_kqueue->num_updates + 1 >= main_loop_kqueue->max_updates)
117     {
118       if (main_loop_kqueue->max_updates == 0)
119         main_loop_kqueue->max_updates = 128;
120       else
121         main_loop_kqueue->max_updates *= 2;
122       main_loop_kqueue->kevent_array
123 	= g_realloc (main_loop_kqueue->kevent_array,
124 		     sizeof (struct kevent) * main_loop_kqueue->max_updates);
125     }
126   switch (change->type)
127     {
128       static gboolean has_ignored_sigchld = FALSE;
129       struct kevent *event;
130     case GSK_MAIN_LOOP_EVENT_PROCESS:
131       if (change->data.process.did_exit)
132         return;
133       event = get_next_update (main_loop_kqueue);
134       event->ident = change->data.process.pid;
135       event->filter = EVFILT_PROC;
136       event->flags = change->data.process.add ? (EV_ADD|EV_ONESHOT|EV_CLEAR) : EV_DELETE;
137       event->fflags = NOTE_EXIT;
138       event->data = 0;
139       event->udata = NULL;
140       main_loop_kqueue->num_updates++;
141       if (!has_ignored_sigchld)
142         {
143           almost_ignore_sigchld ();
144           has_ignored_sigchld = TRUE;
145         }
146       break;
147     case GSK_MAIN_LOOP_EVENT_SIGNAL:
148       event = get_next_update (main_loop_kqueue);
149       event->ident = change->data.signal.number;
150       event->filter = EVFILT_SIGNAL;
151       event->flags = change->data.signal.add ? EV_ADD : EV_DELETE;
152       event->fflags = 0;
153       event->data = 0;
154       event->udata = NULL;
155       change_signal (change->data.signal.number, change->data.signal.add);
156       main_loop_kqueue->num_updates++;
157       kqueue_flush_pending_changes (main_loop_kqueue);
158       break;
159     case GSK_MAIN_LOOP_EVENT_IO:
160       if ((change->data.io.old_events ^ change->data.io.events) & G_IO_IN)
161 	{
162           event = get_next_update (main_loop_kqueue);
163 	  event->ident = change->data.io.fd;
164 	  event->filter = EVFILT_READ;
165 	  event->flags = (change->data.io.events & G_IO_IN) ? EV_ADD : EV_DELETE;
166 	  event->fflags = 0;
167 	  event->data = 0;
168 	  event->udata = NULL;
169 	  main_loop_kqueue->num_updates++;
170 	}
171       if ((change->data.io.old_events ^ change->data.io.events) & G_IO_OUT)
172 	{
173           event = get_next_update (main_loop_kqueue);
174 	  event->ident = change->data.io.fd;
175 	  event->filter = EVFILT_WRITE;
176 	  event->flags = (change->data.io.events & G_IO_OUT) ? EV_ADD : EV_DELETE;
177 	  event->fflags = 0;
178 	  event->data = 0;
179 	  event->udata = NULL;
180 	  main_loop_kqueue->num_updates++;
181 	}
182       kqueue_flush_pending_changes (main_loop_kqueue);
183       break;
184     }
185 }
186 
187 static guint
gsk_main_loop_kqueue_poll(GskMainLoop * main_loop,guint max_events_out,GskMainLoopEvent * events,gint timeout_milli)188 gsk_main_loop_kqueue_poll (GskMainLoop       *main_loop,
189                            guint              max_events_out,
190                            GskMainLoopEvent  *events,
191                            gint               timeout_milli)
192 {
193   struct kevent *out = alloca (sizeof (struct kevent) * max_events_out);
194   GskMainLoopKqueue *main_loop_kqueue = GSK_MAIN_LOOP_KQUEUE (main_loop);
195   gint kevent_rv;
196   guint i;
197   guint rv = 0;
198   struct timespec timeout;
199   struct timespec *p_timeout;
200   if (timeout_milli < 0)
201     {
202       p_timeout = NULL;
203     }
204   else
205     {
206       timeout.tv_sec = timeout_milli / 1000;
207       timeout.tv_nsec = timeout_milli % 1000 * 1000 * 1000;
208       p_timeout = &timeout;
209     }
210 retry_kevent:
211   kevent_rv = kevent (main_loop_kqueue->kernel_queue_id,
212 		      main_loop_kqueue->kevent_array,
213 		      main_loop_kqueue->num_updates,
214 		      out,
215 		      max_events_out,
216 		      p_timeout);
217 
218   if (kevent_rv < 0)
219     {
220       if (gsk_errno_is_ignorable (errno))
221         goto retry_kevent;
222       g_warning ("error running kevent: %s", g_strerror (errno));
223       return 0;
224     }
225 
226   main_loop_kqueue->num_updates = 0;
227 
228   for (i = 0; i < kevent_rv; i++)
229     {
230       switch (out[i].filter)
231         {
232         case EVFILT_READ:
233           events[rv].type = GSK_MAIN_LOOP_EVENT_IO;
234           events[rv].data.io.events = G_IO_IN;
235           events[rv].data.io.fd = out[i].ident;
236           rv++;
237           break;
238 
239         case EVFILT_WRITE:
240           events[rv].type = GSK_MAIN_LOOP_EVENT_IO;
241           events[rv].data.io.events = G_IO_OUT;
242           events[rv].data.io.fd = out[i].ident;
243           rv++;
244           break;
245 
246         case EVFILT_SIGNAL:
247           events[rv].type = GSK_MAIN_LOOP_EVENT_SIGNAL;
248           events[rv].data.signal = out[i].ident;
249           rv++;
250           break;
251 
252         case EVFILT_PROC:
253           if (out[i].fflags == NOTE_EXIT)
254             {
255               events[rv].type = GSK_MAIN_LOOP_EVENT_PROCESS;
256               events[rv].data.process_wait_info.pid = out[i].ident;
257               if (gsk_main_loop_do_waitpid (out[i].ident,
258                                          &events[rv].data.process_wait_info))
259                 rv++;
260             }
261           break;
262 
263         default:
264           g_warning ("unexpected type of event from kevent (%d)",
265                      out[i].filter);
266         }
267     }
268   return rv;
269 }
270 #endif
271 
272 static void
gsk_main_loop_kqueue_finalize(GObject * object)273 gsk_main_loop_kqueue_finalize(GObject *object)
274 {
275   GskMainLoopKqueue *kqueue = GSK_MAIN_LOOP_KQUEUE (object);
276   gsk_main_loop_destroy_all_sources (GSK_MAIN_LOOP (object));
277   if (kqueue->kernel_queue_id >= 0)
278     close (kqueue->kernel_queue_id);
279   (*parent_class->finalize) (object);
280 }
281 
282 /* --- class methods --- */
283 static void
gsk_main_loop_kqueue_init(GskMainLoopKqueue * kqueue)284 gsk_main_loop_kqueue_init (GskMainLoopKqueue *kqueue)
285 {
286   kqueue->kernel_queue_id = -1;
287 }
288 
289 static void
gsk_main_loop_kqueue_class_init(GskMainLoopClass * main_loop_class)290 gsk_main_loop_kqueue_class_init (GskMainLoopClass *main_loop_class)
291 {
292   GObjectClass *object_class = G_OBJECT_CLASS (main_loop_class);
293   parent_class = g_type_class_peek_parent (main_loop_class);
294   main_loop_class->setup = gsk_main_loop_kqueue_setup;
295 #if HAVE_KQUEUE
296   main_loop_class->poll = gsk_main_loop_kqueue_poll;
297   main_loop_class->change = gsk_main_loop_kqueue_change;
298 #endif
299   object_class->finalize = gsk_main_loop_kqueue_finalize;
300 }
301 
302 GType
gsk_main_loop_kqueue_get_type()303 gsk_main_loop_kqueue_get_type()
304 {
305   static GType main_loop_kqueue_type = 0;
306   if (!main_loop_kqueue_type)
307     {
308       static const GTypeInfo main_loop_kqueue_info =
309       {
310 	sizeof(GskMainLoopKqueueClass),
311 	(GBaseInitFunc) NULL,
312 	(GBaseFinalizeFunc) NULL,
313 	(GClassInitFunc) gsk_main_loop_kqueue_class_init,
314 	NULL,		/* class_finalize */
315 	NULL,		/* class_data */
316 	sizeof (GskMainLoopKqueue),
317 	0,		/* n_preallocs */
318 	(GInstanceInitFunc) gsk_main_loop_kqueue_init,
319 	NULL		/* value_table */
320       };
321       GType parent = GSK_TYPE_MAIN_LOOP;
322       main_loop_kqueue_type = g_type_register_static (parent,
323                                                   "GskMainLoopKqueue",
324 						  &main_loop_kqueue_info, 0);
325     }
326   return main_loop_kqueue_type;
327 }
328