1/***********************************************************************
2*
3* event.c
4*
5* Abstraction of select call into "event-handling" to make programming
6* easier.
7*
8* Copyright (C) 2001 Roaring Penguin Software Inc.
9*
10* This program may be distributed according to the terms of the GNU
11* General Public License, version 2 or (at your option) any later version.
12*
13* LIC: GPL
14*
15***********************************************************************/
16
17static char const RCSID[] =
18"$Id$";
19
20#include "event.h"
21#include <stdlib.h>
22#include <errno.h>
23
24static void DestroySelector(EventSelector *es);
25static void DestroyHandler(EventHandler *eh);
26static void DoPendingChanges(EventSelector *es);
27
28/**********************************************************************
29* %FUNCTION: Event_CreateSelector
30* %ARGUMENTS:
31*  None
32* %RETURNS:
33*  A newly-allocated EventSelector, or NULL if out of memory.
34* %DESCRIPTION:
35*  Creates a new EventSelector.
36***********************************************************************/
37EventSelector *
38Event_CreateSelector(void)
39{
40    EventSelector *es = malloc(sizeof(EventSelector));
41    if (!es) return NULL;
42    es->handlers = NULL;
43    es->nestLevel = 0;
44    es->destroyPending = 0;
45    es->opsPending = 0;
46    EVENT_DEBUG(("CreateSelector() -> %p\n", (void *) es));
47    return es;
48}
49
50/**********************************************************************
51* %FUNCTION: Event_DestroySelector
52* %ARGUMENTS:
53*  es -- EventSelector to destroy
54* %RETURNS:
55*  Nothing
56* %DESCRIPTION:
57*  Destroys an EventSelector.  Destruction may be delayed if we
58*  are in the HandleEvent function.
59***********************************************************************/
60void
61Event_DestroySelector(EventSelector *es)
62{
63    if (es->nestLevel) {
64	es->destroyPending = 1;
65	es->opsPending = 1;
66	return;
67    }
68    DestroySelector(es);
69}
70
71/**********************************************************************
72* %FUNCTION: Event_HandleEvent
73* %ARGUMENTS:
74*  es -- EventSelector
75* %RETURNS:
76*  0 if OK, non-zero on error.  errno is set appropriately.
77* %DESCRIPTION:
78*  Handles a single event (uses select() to wait for an event.)
79***********************************************************************/
80int
81Event_HandleEvent(EventSelector *es)
82{
83    fd_set readfds, writefds;
84    fd_set *rd, *wr;
85    unsigned int flags;
86
87    struct timeval abs_timeout, now;
88
89    struct timeval timeout;
90    struct timeval *tm;
91    EventHandler *eh;
92
93    int r = 0;
94    int errno_save = 0;
95    int foundTimeoutEvent = 0;
96    int foundReadEvent = 0;
97    int foundWriteEvent = 0;
98    int maxfd = -1;
99    int pastDue;
100
101    /* Avoid compiler warning */
102    abs_timeout.tv_sec = 0;
103    abs_timeout.tv_usec = 0;
104
105    EVENT_DEBUG(("Enter Event_HandleEvent(es=%p)\n", (void *) es));
106
107    /* Build the select sets */
108    FD_ZERO(&readfds);
109    FD_ZERO(&writefds);
110
111    eh = es->handlers;
112    for (eh=es->handlers; eh; eh=eh->next) {
113	if (eh->flags & EVENT_FLAG_DELETED) continue;
114	if (eh->flags & EVENT_FLAG_READABLE) {
115	    foundReadEvent = 1;
116	    FD_SET(eh->fd, &readfds);
117	    if (eh->fd > maxfd) maxfd = eh->fd;
118	}
119	if (eh->flags & EVENT_FLAG_WRITEABLE) {
120	    foundWriteEvent = 1;
121	    FD_SET(eh->fd, &writefds);
122	    if (eh->fd > maxfd) maxfd = eh->fd;
123	}
124	if (eh->flags & EVENT_TIMER_BITS) {
125	    if (!foundTimeoutEvent) {
126		abs_timeout = eh->tmout;
127		foundTimeoutEvent = 1;
128	    } else {
129		if (eh->tmout.tv_sec < abs_timeout.tv_sec ||
130		    (eh->tmout.tv_sec == abs_timeout.tv_sec &&
131		     eh->tmout.tv_usec < abs_timeout.tv_usec)) {
132		    abs_timeout = eh->tmout;
133		}
134	    }
135	}
136    }
137    if (foundReadEvent) {
138	rd = &readfds;
139    } else {
140	rd = NULL;
141    }
142    if (foundWriteEvent) {
143	wr = &writefds;
144    } else {
145	wr = NULL;
146    }
147
148    if (foundTimeoutEvent) {
149	gettimeofday(&now, NULL);
150	/* Convert absolute timeout to relative timeout for select */
151	timeout.tv_usec = abs_timeout.tv_usec - now.tv_usec;
152	timeout.tv_sec = abs_timeout.tv_sec - now.tv_sec;
153	if (timeout.tv_usec < 0) {
154	    timeout.tv_usec += 1000000;
155	    timeout.tv_sec--;
156	}
157	if (timeout.tv_sec < 0 ||
158	    (timeout.tv_sec == 0 && timeout.tv_usec < 0)) {
159	    timeout.tv_sec = 0;
160	    timeout.tv_usec = 0;
161	}
162	tm = &timeout;
163    } else {
164	tm = NULL;
165    }
166
167    if (foundReadEvent || foundWriteEvent || foundTimeoutEvent) {
168	for(;;) {
169	    r = select(maxfd+1, rd, wr, NULL, tm);
170	    if (r < 0) {
171		if (errno == EINTR) continue;
172	    }
173	    break;
174	}
175    }
176
177    if (foundTimeoutEvent) gettimeofday(&now, NULL);
178    errno_save = errno;
179    es->nestLevel++;
180
181    if (r >= 0) {
182	/* Call handlers */
183	for (eh=es->handlers; eh; eh=eh->next) {
184
185	    /* Pending delete for this handler?  Ignore it */
186	    if (eh->flags & EVENT_FLAG_DELETED) continue;
187
188	    flags = 0;
189	    if ((eh->flags & EVENT_FLAG_READABLE) &&
190		FD_ISSET(eh->fd, &readfds)) {
191		flags |= EVENT_FLAG_READABLE;
192	    }
193	    if ((eh->flags & EVENT_FLAG_WRITEABLE) &&
194		FD_ISSET(eh->fd, &writefds)) {
195		flags |= EVENT_FLAG_WRITEABLE;
196	    }
197	    if (eh->flags & EVENT_TIMER_BITS) {
198		pastDue = (eh->tmout.tv_sec < now.tv_sec ||
199			   (eh->tmout.tv_sec == now.tv_sec &&
200			    eh->tmout.tv_usec <= now.tv_usec));
201		if (pastDue) {
202		    flags |= EVENT_TIMER_BITS;
203		    if (eh->flags & EVENT_FLAG_TIMER) {
204			/* Timer events are only called once */
205			es->opsPending = 1;
206			eh->flags |= EVENT_FLAG_DELETED;
207		    }
208		}
209	    }
210	    /* Do callback */
211	    if (flags) {
212		EVENT_DEBUG(("Enter callback: eh=%p flags=%u\n", eh, flags));
213		eh->fn(es, eh->fd, flags, eh->data);
214		EVENT_DEBUG(("Leave callback: eh=%p flags=%u\n", eh, flags));
215	    }
216	}
217    }
218
219    es->nestLevel--;
220
221    if (!es->nestLevel && es->opsPending) {
222	DoPendingChanges(es);
223    }
224    errno = errno_save;
225    return r;
226}
227
228/**********************************************************************
229* %FUNCTION: Event_AddHandler
230* %ARGUMENTS:
231*  es -- event selector
232*  fd -- file descriptor to watch
233*  flags -- combination of EVENT_FLAG_READABLE and EVENT_FLAG_WRITEABLE
234*  fn -- callback function to call when event is triggered
235*  data -- extra data to pass to callback function
236* %RETURNS:
237*  A newly-allocated EventHandler, or NULL.
238***********************************************************************/
239EventHandler *
240Event_AddHandler(EventSelector *es,
241		 int fd,
242		 unsigned int flags,
243		 EventCallbackFunc fn,
244		 void *data)
245{
246    EventHandler *eh;
247
248    /* Specifically disable timer and deleted flags */
249    flags &= (~(EVENT_TIMER_BITS | EVENT_FLAG_DELETED));
250
251    /* Bad file descriptor */
252    if (fd < 0) {
253	errno = EBADF;
254	return NULL;
255    }
256
257    eh = malloc(sizeof(EventHandler));
258    if (!eh) return NULL;
259    eh->fd = fd;
260    eh->flags = flags;
261    eh->tmout.tv_usec = 0;
262    eh->tmout.tv_sec = 0;
263    eh->fn = fn;
264    eh->data = data;
265
266    /* Add immediately.  This is safe even if we are in a handler. */
267    eh->next = es->handlers;
268    es->handlers = eh;
269
270    EVENT_DEBUG(("Event_AddHandler(es=%p, fd=%d, flags=%u) -> %p\n", es, fd, flags, eh));
271    return eh;
272}
273
274/**********************************************************************
275* %FUNCTION: Event_AddHandlerWithTimeout
276* %ARGUMENTS:
277*  es -- event selector
278*  fd -- file descriptor to watch
279*  flags -- combination of EVENT_FLAG_READABLE and EVENT_FLAG_WRITEABLE
280*  t -- Timeout after which to call handler, even if not readable/writable.
281*       If t.tv_sec < 0, calls normal Event_AddHandler with no timeout.
282*  fn -- callback function to call when event is triggered
283*  data -- extra data to pass to callback function
284* %RETURNS:
285*  A newly-allocated EventHandler, or NULL.
286***********************************************************************/
287EventHandler *
288Event_AddHandlerWithTimeout(EventSelector *es,
289			    int fd,
290			    unsigned int flags,
291			    struct timeval t,
292			    EventCallbackFunc fn,
293			    void *data)
294{
295    EventHandler *eh;
296    struct timeval now;
297
298    /* If timeout is negative, just do normal non-timing-out event */
299    if (t.tv_sec < 0 || t.tv_usec < 0) {
300	return Event_AddHandler(es, fd, flags, fn, data);
301    }
302
303    /* Specifically disable timer and deleted flags */
304    flags &= (~(EVENT_FLAG_TIMER | EVENT_FLAG_DELETED));
305    flags |= EVENT_FLAG_TIMEOUT;
306
307    /* Bad file descriptor? */
308    if (fd < 0) {
309	errno = EBADF;
310	return NULL;
311    }
312
313    /* Bad timeout? */
314    if (t.tv_usec >= 1000000) {
315	errno = EINVAL;
316	return NULL;
317    }
318
319    eh = malloc(sizeof(EventHandler));
320    if (!eh) return NULL;
321
322    /* Convert time interval to absolute time */
323    gettimeofday(&now, NULL);
324
325    t.tv_sec += now.tv_sec;
326    t.tv_usec += now.tv_usec;
327    if (t.tv_usec >= 1000000) {
328	t.tv_usec -= 1000000;
329	t.tv_sec++;
330    }
331
332    eh->fd = fd;
333    eh->flags = flags;
334    eh->tmout = t;
335    eh->fn = fn;
336    eh->data = data;
337
338    /* Add immediately.  This is safe even if we are in a handler. */
339    eh->next = es->handlers;
340    es->handlers = eh;
341
342    EVENT_DEBUG(("Event_AddHandlerWithTimeout(es=%p, fd=%d, flags=%u, t=%d/%d) -> %p\n", es, fd, flags, t.tv_sec, t.tv_usec, eh));
343    return eh;
344}
345
346
347/**********************************************************************
348* %FUNCTION: Event_AddTimerHandler
349* %ARGUMENTS:
350*  es -- event selector
351*  t -- time interval after which to trigger event
352*  fn -- callback function to call when event is triggered
353*  data -- extra data to pass to callback function
354* %RETURNS:
355*  A newly-allocated EventHandler, or NULL.
356***********************************************************************/
357EventHandler *
358Event_AddTimerHandler(EventSelector *es,
359		      struct timeval t,
360		      EventCallbackFunc fn,
361		      void *data)
362{
363    EventHandler *eh;
364    struct timeval now;
365
366    /* Check time interval for validity */
367    if (t.tv_sec < 0 || t.tv_usec < 0 || t.tv_usec >= 1000000) {
368	errno = EINVAL;
369	return NULL;
370    }
371
372    eh = malloc(sizeof(EventHandler));
373    if (!eh) return NULL;
374
375    /* Convert time interval to absolute time */
376    gettimeofday(&now, NULL);
377
378    t.tv_sec += now.tv_sec;
379    t.tv_usec += now.tv_usec;
380    if (t.tv_usec >= 1000000) {
381	t.tv_usec -= 1000000;
382	t.tv_sec++;
383    }
384
385    eh->fd = -1;
386    eh->flags = EVENT_FLAG_TIMER;
387    eh->tmout = t;
388    eh->fn = fn;
389    eh->data = data;
390
391    /* Add immediately.  This is safe even if we are in a handler. */
392    eh->next = es->handlers;
393    es->handlers = eh;
394
395    EVENT_DEBUG(("Event_AddTimerHandler(es=%p, t=%d/%d) -> %p\n", es, t.tv_sec,t.tv_usec, eh));
396    return eh;
397}
398
399/**********************************************************************
400* %FUNCTION: Event_DelHandler
401* %ARGUMENTS:
402*  es -- event selector
403*  eh -- event handler
404* %RETURNS:
405*  0 if OK, non-zero if there is an error
406* %DESCRIPTION:
407*  Deletes the event handler eh
408***********************************************************************/
409int
410Event_DelHandler(EventSelector *es,
411		 EventHandler *eh)
412{
413    /* Scan the handlers list */
414    EventHandler *cur, *prev;
415    EVENT_DEBUG(("Event_DelHandler(es=%p, eh=%p)\n", es, eh));
416    for (cur=es->handlers, prev=NULL; cur; prev=cur, cur=cur->next) {
417	if (cur == eh) {
418	    if (es->nestLevel) {
419		eh->flags |= EVENT_FLAG_DELETED;
420		es->opsPending = 1;
421		return 0;
422	    } else {
423		if (prev) prev->next = cur->next;
424		else      es->handlers = cur->next;
425
426		DestroyHandler(cur);
427		return 0;
428	    }
429	}
430    }
431
432    /* Handler not found */
433    return 1;
434}
435
436/**********************************************************************
437* %FUNCTION: DestroySelector
438* %ARGUMENTS:
439*  es -- an event selector
440* %RETURNS:
441*  Nothing
442* %DESCRIPTION:
443*  Destroys selector and all associated handles.
444***********************************************************************/
445void
446DestroySelector(EventSelector *es)
447{
448    EventHandler *cur, *next;
449    for (cur=es->handlers; cur; cur=next) {
450	next = cur->next;
451	DestroyHandler(cur);
452    }
453
454    free(es);
455}
456
457/**********************************************************************
458* %FUNCTION: DestroyHandler
459* %ARGUMENTS:
460*  eh -- an event handler
461* %RETURNS:
462*  Nothing
463* %DESCRIPTION:
464*  Destroys handler
465***********************************************************************/
466void
467DestroyHandler(EventHandler *eh)
468{
469    EVENT_DEBUG(("DestroyHandler(eh=%p)\n", eh));
470    free(eh);
471}
472
473/**********************************************************************
474* %FUNCTION: DoPendingChanges
475* %ARGUMENTS:
476*  es -- an event selector
477* %RETURNS:
478*  Nothing
479* %DESCRIPTION:
480*  Makes all pending insertions and deletions happen.
481***********************************************************************/
482void
483DoPendingChanges(EventSelector *es)
484{
485    EventHandler *cur, *prev, *next;
486
487    es->opsPending = 0;
488
489    /* If selector is to be deleted, do it and skip everything else */
490    if (es->destroyPending) {
491	DestroySelector(es);
492	return;
493    }
494
495    /* Do deletions */
496    cur = es->handlers;
497    prev = NULL;
498    while(cur) {
499	if (!(cur->flags & EVENT_FLAG_DELETED)) {
500	    prev = cur;
501	    cur = cur->next;
502	    continue;
503	}
504
505	/* Unlink from list */
506	if (prev) {
507	    prev->next = cur->next;
508	} else {
509	    es->handlers = cur->next;
510	}
511	next = cur->next;
512	DestroyHandler(cur);
513	cur = next;
514    }
515}
516
517/**********************************************************************
518* %FUNCTION: Event_GetCallback
519* %ARGUMENTS:
520*  eh -- the event handler
521* %RETURNS:
522*  The callback function
523***********************************************************************/
524EventCallbackFunc
525Event_GetCallback(EventHandler *eh)
526{
527    return eh->fn;
528}
529
530/**********************************************************************
531* %FUNCTION: Event_GetData
532* %ARGUMENTS:
533*  eh -- the event handler
534* %RETURNS:
535*  The "data" field.
536***********************************************************************/
537void *
538Event_GetData(EventHandler *eh)
539{
540    return eh->data;
541}
542
543/**********************************************************************
544* %FUNCTION: Event_SetCallbackAndData
545* %ARGUMENTS:
546*  eh -- the event handler
547*  fn -- new callback function
548*  data -- new data value
549* %RETURNS:
550*  Nothing
551* %DESCRIPTION:
552*  Sets the callback function and data fields.
553***********************************************************************/
554void
555Event_SetCallbackAndData(EventHandler *eh,
556			 EventCallbackFunc fn,
557			 void *data)
558{
559    eh->fn = fn;
560    eh->data = data;
561}
562
563#ifdef DEBUG_EVENT
564#include <stdarg.h>
565#include <stdio.h>
566FILE *Event_DebugFP = NULL;
567/**********************************************************************
568* %FUNCTION: Event_DebugMsg
569* %ARGUMENTS:
570*  fmt, ... -- format string
571* %RETURNS:
572*  Nothing
573* %DESCRIPTION:
574*  Writes a debug message to the debug file.
575***********************************************************************/
576void
577Event_DebugMsg(char const *fmt, ...)
578{
579    va_list ap;
580    struct timeval now;
581
582    if (!Event_DebugFP) return;
583
584    gettimeofday(&now, NULL);
585
586    fprintf(Event_DebugFP, "%03d.%03d ", (int) now.tv_sec % 1000,
587	    (int) now.tv_usec / 1000);
588
589    va_start(ap, fmt);
590    vfprintf(Event_DebugFP, fmt, ap);
591    va_end(ap);
592    fflush(Event_DebugFP);
593}
594
595#endif
596
597/**********************************************************************
598* %FUNCTION: Event_EnableDebugging
599* %ARGUMENTS:
600*  fname -- name of file to log debug messages to
601* %RETURNS:
602*  1 if debugging was enabled; 0 otherwise.
603***********************************************************************/
604int
605Event_EnableDebugging(char const *fname)
606{
607#ifndef DEBUG_EVENT
608    return 0;
609#else
610    Event_DebugFP = fopen(fname, "w");
611    return (Event_DebugFP != NULL);
612#endif
613}
614
615/**********************************************************************
616* %FUNCTION: Event_ChangeTimeout
617* %ARGUMENTS:
618*  h -- event handler
619*  t -- new timeout
620* %RETURNS:
621*  Nothing
622* %DESCRIPTION:
623*  Changes timeout of event handler to be "t" seconds in the future.
624***********************************************************************/
625void
626Event_ChangeTimeout(EventHandler *h, struct timeval t)
627{
628    struct timeval now;
629
630    /* Check time interval for validity */
631    if (t.tv_sec < 0 || t.tv_usec < 0 || t.tv_usec >= 1000000) {
632	return;
633    }
634    /* Convert time interval to absolute time */
635    gettimeofday(&now, NULL);
636
637    t.tv_sec += now.tv_sec;
638    t.tv_usec += now.tv_usec;
639    if (t.tv_usec >= 1000000) {
640	t.tv_usec -= 1000000;
641	t.tv_sec++;
642    }
643
644    h->tmout = t;
645}
646