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