2004-05-10 Michael Poole <mdpoole@troilus.org>
[ircu2.10.12-pk.git] / ircd / ircd_events.c
1 /*
2  * IRC - Internet Relay Chat, ircd/ircd_events.c
3  * Copyright (C) 2001 Kevin L. Mitchell <klmitch@mit.edu>
4  *
5  * This program is free software; you can redistribute it and/or modify
6  * it under the terms of the GNU General Public License as published by
7  * the Free Software Foundation; either version 1, or (at your option)
8  * any later version.
9  *
10  * This program 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
13  * GNU General Public License for more details.
14  *
15  * You should have received a copy of the GNU General Public License
16  * along with this program; if not, write to the Free Software
17  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
18  *
19  * $Id$
20  */
21 #include "config.h"
22
23 #include "ircd_events.h"
24
25 #include "ircd.h"
26 #include "ircd_alloc.h"
27 #include "ircd_log.h"
28 #include "ircd_snprintf.h"
29 #include "s_debug.h"
30
31 #include <assert.h>
32 #include <signal.h>
33 #include <stdlib.h>
34 #include <unistd.h>
35
36 #define SIGS_PER_SOCK   10      /* number of signals to process per socket
37                                    readable event */
38
39 #ifdef USE_KQUEUE
40 extern struct Engine engine_kqueue;
41 #define ENGINE_KQUEUE   &engine_kqueue,
42 #else
43 #define ENGINE_KQUEUE
44 #endif /* USE_KQUEUE */
45
46 #ifdef USE_DEVPOLL
47 extern struct Engine engine_devpoll;
48 #define ENGINE_DEVPOLL  &engine_devpoll,
49 #else
50 #define ENGINE_DEVPOLL
51 #endif /* USE_DEVPOLL */
52
53 #ifdef USE_EPOLL
54 extern struct Engine engine_epoll;
55 #define ENGINE_EPOLL &engine_epoll,
56 #else
57 #define ENGINE_EPOLL
58 #endif /* USE_EPOLL */
59
60 #ifdef USE_POLL
61 extern struct Engine engine_poll;
62 #define ENGINE_FALLBACK &engine_poll,
63 #else
64 extern struct Engine engine_select;
65 #define ENGINE_FALLBACK &engine_select,
66 #endif /* USE_POLL */
67
68 /* list of engines to try */
69 static const struct Engine *evEngines[] = {
70   ENGINE_KQUEUE
71   ENGINE_EPOLL
72   ENGINE_DEVPOLL
73   ENGINE_FALLBACK
74   0
75 };
76
77 /* signal routines pipe data */
78 static struct {
79   int           fd;     /* signal routine's fd */
80   struct Socket sock;   /* and its struct Socket */
81 } sigInfo = { -1 };
82
83 /* All the thread info */
84 static struct {
85   struct Generators    gens;            /* List of all generators */
86   struct Event*        events_free;     /* struct Event free list */
87   unsigned int         events_alloc;    /* count of allocated struct Events */
88   const struct Engine* engine;          /* core engine being used */
89 #ifdef IRCD_THREADED
90   struct GenHeader*    genq_head;       /* head of generator event queue */
91   struct GenHeader*    genq_tail;       /* tail of generator event queue */
92   unsigned int         genq_count;      /* count of generators on queue */
93 #endif
94 } evInfo = {
95   { 0, 0, 0 },
96   0, 0, 0
97 #ifdef IRCD_THREADED
98   , 0, 0, 0
99 #endif
100 };
101
102 /* Initialize a struct GenHeader */
103 static void
104 gen_init(struct GenHeader* gen, EventCallBack call, void* data,
105          struct GenHeader* next, struct GenHeader** prev_p)
106 {
107   assert(0 != gen);
108
109   gen->gh_next = next;
110   gen->gh_prev_p = prev_p;
111 #ifdef IRCD_THREADED
112   gen->gh_qnext = 0;
113   gen->gh_qprev_p = 0;
114   gen->gh_head = 0;
115   gen->gh_tail = 0;
116 #endif
117   gen->gh_flags = GEN_ACTIVE;
118   gen->gh_ref = 0;
119   gen->gh_call = call;
120   gen->gh_data = data;
121   gen->gh_engdata.ed_int = 0;
122
123   if (prev_p) { /* Going to link into list? */
124     if (next) /* do so */
125       next->gh_prev_p = &gen->gh_next;
126     *prev_p = gen;
127   }
128 }
129
130 /* Execute an event; optimizations should inline this */
131 static void
132 event_execute(struct Event* event)
133 {
134   assert(0 != event);
135   assert(0 == event->ev_prev_p); /* must be off queue first */
136   assert(event->ev_gen.gen_header->gh_flags & GEN_ACTIVE);
137
138   if (event->ev_type == ET_DESTROY) /* turn off active flag *before* destroy */
139     event->ev_gen.gen_header->gh_flags &= ~GEN_ACTIVE;
140   if (event->ev_type == ET_ERROR) /* turn on error flag before callback */
141     event->ev_gen.gen_header->gh_flags |= GEN_ERROR;
142
143   (*event->ev_gen.gen_header->gh_call)(event); /* execute the event */
144
145   /* The logic here is very careful; if the event was an ET_DESTROY,
146    * then we must assume the generator is now invalid; fortunately, we
147    * don't need to do anything to it if so.  Otherwise, we decrement
148    * the reference count; if reference count goes to zero, AND we need
149    * to destroy the generator, THEN we generate a DESTROY event.
150    */
151   if (event->ev_type != ET_DESTROY)
152     gen_ref_dec(event->ev_gen.gen_header);
153
154   event->ev_gen.gen_header = 0; /* clear event data */
155   event->ev_type = ET_DESTROY;
156
157   event->ev_next = evInfo.events_free; /* add to free list */
158   evInfo.events_free = event;
159 }
160
161 #ifndef IRCD_THREADED
162 /* we synchronously execute the event when not threaded */
163 #define event_add(event)        \
164 do {                                                                          \
165   struct Event* _ev = (event);                                                \
166   _ev->ev_next = 0;                                                           \
167   _ev->ev_prev_p = 0;                                                         \
168   event_execute(_ev);                                                         \
169 } while (0)
170
171 #else
172 /* add an event to the work queue */
173 /* This is just a placeholder; don't expect ircd to be threaded soon */
174 /* There should be locks all over the place in here */
175 static void
176 event_add(struct Event* event)
177 {
178   struct GenHeader* gen;
179
180   assert(0 != event);
181
182   gen = event->ev_gen.gen_header;
183
184   /* First, place event on generator's event queue */
185   event->ev_next = 0;
186   if (gen->gh_head) {
187     assert(0 != gen->gh_tail);
188
189     event->ev_prev_p = &gen->gh_tail->ev_next;
190     gen->gh_tail->ev_next = event;
191     gen->gh_tail = event;
192   } else { /* queue was empty */
193     assert(0 == gen->gh_tail);
194
195     event->ev_prev_p = &gen->gh_head;
196     gen->gh_head = event;
197     gen->gh_tail = event;
198   }
199
200   /* Now, if the generator isn't on the queue yet... */
201   if (!gen->gh_qprev_p) {
202     gen->gh_qnext = 0;
203     if (evInfo.genq_head) {
204       assert(0 != evInfo.genq_tail);
205
206       gen->gh_qprev_p = &evInfo.genq_tail->gh_qnext;
207       evInfo.genq_tail->gh_qnext = gen;
208       evInfo.genq_tail = gen;
209     } else { /* queue was empty */
210       assert(0 == evInfo.genq_tail);
211
212       gen->gh_qprev_p = &evInfo.genq_head;
213       evInfo.genq_head = gen;
214       evInfo.genq_tail = gen;
215     }
216
217     /* We'd also have to signal the work crew here */
218   }
219 }
220 #endif /* IRCD_THREADED */
221
222 /* Place a timer in the correct spot on the queue */
223 static void
224 timer_enqueue(struct Timer* timer)
225 {
226   struct Timer** ptr_p;
227
228   assert(0 != timer);
229   assert(0 == timer->t_header.gh_prev_p); /* not already on queue */
230   assert(timer->t_header.gh_flags & GEN_ACTIVE); /* timer is active */
231
232   /* Calculate expire time */
233   switch (timer->t_type) {
234   case TT_ABSOLUTE: /* no need to consider it relative */
235     timer->t_expire = timer->t_value;
236     break;
237
238   case TT_RELATIVE: case TT_PERIODIC: /* relative timer */
239     timer->t_expire = timer->t_value + CurrentTime;
240     break;
241   }
242
243   /* Find a slot to insert timer */
244   for (ptr_p = &evInfo.gens.g_timer; ;
245        ptr_p = (struct Timer**) &(*ptr_p)->t_header.gh_next)
246     if (!*ptr_p || timer->t_expire < (*ptr_p)->t_expire)
247       break;
248
249   /* link it in the right place */
250   timer->t_header.gh_next = (struct GenHeader*) *ptr_p;
251   timer->t_header.gh_prev_p = (struct GenHeader**) ptr_p;
252   if (*ptr_p)
253     (*ptr_p)->t_header.gh_prev_p = &timer->t_header.gh_next;
254   *ptr_p = timer;
255 }
256
257 /* signal handler for writing signal notification to pipe */
258 static void
259 signal_handler(int sig)
260 {
261   unsigned char c;
262
263   assert(sigInfo.fd >= 0);
264
265   c = (unsigned char) sig; /* only write 1 byte to identify sig */
266
267   write(sigInfo.fd, &c, 1);
268 }
269
270 /* callback for signal "socket" events */
271 static void
272 signal_callback(struct Event* event)
273 {
274   unsigned char sigstr[SIGS_PER_SOCK];
275   int sig, n_sigs, i;
276   struct Signal* ptr;
277
278   assert(event->ev_type == ET_READ); /* readable events only */
279
280   n_sigs = read(event->ev_gen.gen_socket->s_fd, sigstr, sizeof(sigstr));
281
282   for (i = 0; i < n_sigs; i++) {
283     sig = (int) sigstr[i]; /* get signal */
284
285     for (ptr = evInfo.gens.g_signal; ptr;
286          ptr = (struct Signal*) ptr->sig_header.gh_next)
287       if (ptr->sig_signal == sig) /* find its descriptor... */
288         break;
289
290     if (ptr)
291       event_generate(ET_SIGNAL, ptr, sig); /* generate signal event */
292   }
293 }
294
295 /* Remove something from its queue */
296 void
297 gen_dequeue(void* arg)
298 {
299   struct GenHeader* gen = (struct GenHeader*) arg;
300
301   if (gen->gh_next) /* clip it out of the list */
302     gen->gh_next->gh_prev_p = gen->gh_prev_p;
303   if (gen->gh_prev_p)
304     *gen->gh_prev_p = gen->gh_next;
305
306   gen->gh_next = 0; /* mark that it's not in the list anymore */
307   gen->gh_prev_p = 0;
308 }
309
310 /* Initializes the event system */
311 void
312 event_init(int max_sockets)
313 {
314   int i, p[2];
315
316   for (i = 0; evEngines[i]; i++) { /* look for an engine... */
317     assert(0 != evEngines[i]->eng_name);
318     assert(0 != evEngines[i]->eng_init);
319
320     if ((*evEngines[i]->eng_init)(max_sockets))
321       break; /* Found an engine that'll work */
322   }
323
324   assert(0 != evEngines[i]);
325
326   evInfo.engine = evEngines[i]; /* save engine */
327
328   if (!evInfo.engine->eng_signal) { /* engine can't do signals */
329     if (pipe(p)) {
330       log_write(LS_SYSTEM, L_CRIT, 0, "Failed to open signal pipe");
331       exit(8);
332     }
333
334     sigInfo.fd = p[1]; /* write end of pipe */
335     socket_add(&sigInfo.sock, signal_callback, 0, SS_NOTSOCK,
336                SOCK_EVENT_READABLE, p[0]); /* read end of pipe */
337   }
338 }
339
340 /* Do the event loop */
341 void
342 event_loop(void)
343 {
344   assert(0 != evInfo.engine);
345   assert(0 != evInfo.engine->eng_loop);
346
347   (*evInfo.engine->eng_loop)(&evInfo.gens);
348 }
349
350 /* Generate an event and add it to the queue (or execute it) */
351 void
352 event_generate(enum EventType type, void* arg, int data)
353 {
354   struct Event* ptr;
355   struct GenHeader* gen = (struct GenHeader*) arg;
356
357   assert(0 != gen);
358
359   /* don't create events (other than ET_DESTROY) for destroyed generators */
360   if (type != ET_DESTROY && (gen->gh_flags & GEN_DESTROY))
361     return;
362
363   Debug((DEBUG_LIST, "Generating event type %s for generator %p (%s)",
364          event_to_name(type), gen, gen_flags(gen->gh_flags)));
365
366   if ((ptr = evInfo.events_free))
367     evInfo.events_free = ptr->ev_next; /* pop one off the freelist */
368   else { /* allocate another structure */
369     ptr = (struct Event*) MyMalloc(sizeof(struct Event));
370     evInfo.events_alloc++; /* count of allocated events */
371   }
372
373   ptr->ev_type = type; /* Record event type */
374   ptr->ev_data = data;
375
376   ptr->ev_gen.gen_header = (struct GenHeader*) gen;
377   ptr->ev_gen.gen_header->gh_ref++;
378
379   event_add(ptr); /* add event to queue */
380 }
381
382 #if 0
383 /* Try to verify the timer list */
384 void
385 timer_verify(void)
386 {
387   struct Timer* ptr;
388   struct Timer** ptr_p = &evInfo.gens.g_timer;
389   time_t lasttime = 0;
390
391   for (ptr = evInfo.gens.g_timer; ptr;
392        ptr = (struct Timer*) ptr->t_header.gh_next) {
393     /* verify timer is supposed to be in the list */
394     assert(ptr->t_header.gh_prev_p);
395     /* verify timer is correctly ordered */
396     assert((struct Timer**) ptr->t_header.gh_prev_p == ptr_p);
397     /* verify timer is active */
398     assert(ptr->t_header.gh_flags & GEN_ACTIVE);
399     /* verify timer ordering is correct */
400     assert(lasttime <= ptr->t_expire);
401
402     lasttime = ptr->t_expire; /* store time for ordering check */
403     ptr_p = (struct Timer**) &ptr->t_header.gh_next; /* store prev pointer */
404   }
405 }
406 #endif
407
408 /* Initialize a timer structure */
409 struct Timer*
410 timer_init(struct Timer* timer)
411 {
412   gen_init((struct GenHeader*) timer, 0, 0, 0, 0);
413
414   timer->t_header.gh_flags = 0; /* turn off active flag */
415
416   return timer; /* convenience return */
417 }
418
419 /* Add a timer to be processed */
420 void
421 timer_add(struct Timer* timer, EventCallBack call, void* data,
422           enum TimerType type, time_t value)
423 {
424   assert(0 != timer);
425   assert(0 != call);
426
427   Debug((DEBUG_LIST, "Adding timer %p; time out %Tu (type %s)", timer, value,
428          timer_to_name(type)));
429
430   /* initialize a timer... */
431   timer->t_header.gh_flags |= GEN_ACTIVE;
432   if (timer->t_header.gh_flags & GEN_MARKED)
433     timer->t_header.gh_flags |= GEN_READD;
434
435   timer->t_header.gh_ref = 0;
436   timer->t_header.gh_call = call;
437   timer->t_header.gh_data = data;
438
439   timer->t_type = type;
440   timer->t_value = value;
441   timer->t_expire = 0;
442
443   if (!(timer->t_header.gh_flags & GEN_MARKED))
444     timer_enqueue(timer); /* and enqueue it */
445 }
446
447 /* Remove a timer from the processing queue */
448 void
449 timer_del(struct Timer* timer)
450 {
451   assert(0 != timer);
452
453   timer->t_header.gh_flags &= ~GEN_READD;
454
455   if (timer->t_header.gh_flags & GEN_MARKED)
456     return; /* timer is being used */
457
458   Debug((DEBUG_LIST, "Deleting timer %p (type %s)", timer,
459          timer_to_name(timer->t_type)));
460
461   gen_dequeue(timer);
462   event_generate(ET_DESTROY, timer, 0);
463 }
464
465 /* Change the time a timer expires */
466 void
467 timer_chg(struct Timer* timer, enum TimerType type, time_t value)
468 {
469   assert(0 != timer);
470   assert(0 != value);
471   assert(TT_PERIODIC != timer->t_type);
472   assert(TT_PERIODIC != type);
473
474   Debug((DEBUG_LIST, "Changing timer %p from type %s timeout %Tu to type %s "
475          "timeout %Tu", timer, timer_to_name(timer->t_type), timer->t_value,
476          timer_to_name(type), value));
477
478   gen_dequeue(timer); /* remove the timer from the queue */
479
480   timer->t_type = type; /* Set the new type and value */
481   timer->t_value = value;
482   timer->t_expire = 0;
483
484   timer_enqueue(timer); /* re-queue the timer */
485 }
486
487 /* Execute all expired timers */
488 void
489 timer_run(void)
490 {
491   struct Timer* ptr;
492
493   /* go through queue... */
494   while ((ptr = evInfo.gens.g_timer)) {
495     if (CurrentTime < ptr->t_expire)
496       break; /* processed all pending timers */
497
498     gen_dequeue(ptr); /* must dequeue timer here */
499     ptr->t_header.gh_flags |= (GEN_MARKED |
500                                (ptr->t_type == TT_PERIODIC ? GEN_READD : 0));
501
502     event_generate(ET_EXPIRE, ptr, 0); /* generate expire event */
503
504     ptr->t_header.gh_flags &= ~GEN_MARKED;
505
506     if (!(ptr->t_header.gh_flags & GEN_READD)) {
507       Debug((DEBUG_LIST, "Destroying timer %p", ptr));
508       event_generate(ET_DESTROY, ptr, 0);
509     } else {
510       Debug((DEBUG_LIST, "Re-enqueuing timer %p", ptr));
511       timer_enqueue(ptr); /* re-queue timer */
512       ptr->t_header.gh_flags &= ~GEN_READD;
513     }
514   }
515 }
516
517 /* Adds a signal to the event callback system */
518 void
519 signal_add(struct Signal* signal, EventCallBack call, void* data, int sig)
520 {
521   struct sigaction act;
522
523   assert(0 != signal);
524   assert(0 != call);
525   assert(0 != evInfo.engine);
526
527   /* set up struct */
528   gen_init((struct GenHeader*) signal, call, data,
529            (struct GenHeader*) evInfo.gens.g_signal,
530            (struct GenHeader**) &evInfo.gens.g_signal);
531
532   signal->sig_signal = sig;
533
534   if (evInfo.engine->eng_signal)
535     (*evInfo.engine->eng_signal)(signal); /* tell engine */
536   else {
537     act.sa_handler = signal_handler; /* set up signal handler */
538     act.sa_flags = 0;
539     sigemptyset(&act.sa_mask);
540     sigaction(sig, &act, 0);
541   }
542 }
543
544 /* Adds a socket to the event system */
545 int
546 socket_add(struct Socket* sock, EventCallBack call, void* data,
547            enum SocketState state, unsigned int events, int fd)
548 {
549   assert(0 != sock);
550   assert(0 != call);
551   assert(fd >= 0);
552   assert(0 != evInfo.engine);
553   assert(0 != evInfo.engine->eng_add);
554
555   /* set up struct */
556   gen_init((struct GenHeader*) sock, call, data,
557            (struct GenHeader*) evInfo.gens.g_socket,
558            (struct GenHeader**) &evInfo.gens.g_socket);
559
560   sock->s_state = state;
561   sock->s_events = events & SOCK_EVENT_MASK;
562   sock->s_fd = fd;
563
564   return (*evInfo.engine->eng_add)(sock); /* tell engine about it */
565 }
566
567 /* deletes (or marks for deletion) a socket */
568 void
569 socket_del(struct Socket* sock)
570 {
571   assert(0 != sock);
572   assert(!(sock->s_header.gh_flags & GEN_DESTROY));
573   assert(0 != evInfo.engine);
574   assert(0 != evInfo.engine->eng_closing);
575
576   /* tell engine socket is going away */
577   (*evInfo.engine->eng_closing)(sock);
578
579   sock->s_header.gh_flags |= GEN_DESTROY;
580
581   if (!sock->s_header.gh_ref) { /* not in use; destroy right now */
582     gen_dequeue(sock);
583     event_generate(ET_DESTROY, sock, 0);
584   }
585 }
586
587 /* Sets the socket state to something else */
588 void
589 socket_state(struct Socket* sock, enum SocketState state)
590 {
591   assert(0 != sock);
592   assert(0 != evInfo.engine);
593   assert(0 != evInfo.engine->eng_state);
594
595   /* assertions for invalid socket state transitions */
596   assert(sock->s_state != state); /* not changing states ?! */
597   assert(sock->s_state != SS_LISTENING); /* listening socket to...?! */
598   assert(sock->s_state != SS_CONNECTED); /* connected socket to...?! */
599   /* connecting socket now connected */
600   assert(sock->s_state != SS_CONNECTING || state == SS_CONNECTED);
601   /* unconnected datagram socket now connected */
602   assert(sock->s_state != SS_DATAGRAM || state == SS_CONNECTDG);
603   /* connected datagram socket now unconnected */
604   assert(sock->s_state != SS_CONNECTDG || state == SS_DATAGRAM);
605
606   /* Don't continue if an error occurred or the socket got destroyed */
607   if (sock->s_header.gh_flags & (GEN_DESTROY | GEN_ERROR))
608     return;
609
610   /* tell engine we're changing socket state */
611   (*evInfo.engine->eng_state)(sock, state);
612
613   sock->s_state = state; /* set new state */
614 }
615
616 /* sets the events a socket's interested in */
617 void
618 socket_events(struct Socket* sock, unsigned int events)
619 {
620   unsigned int new_events = 0;
621
622   assert(0 != sock);
623   assert(0 != evInfo.engine);
624   assert(0 != evInfo.engine->eng_events);
625
626   /* Don't continue if an error occurred or the socket got destroyed */
627   if (sock->s_header.gh_flags & (GEN_DESTROY | GEN_ERROR))
628     return;
629
630   switch (events & SOCK_ACTION_MASK) {
631   case SOCK_ACTION_SET: /* set events to given set */
632     new_events = events & SOCK_EVENT_MASK;
633     break;
634
635   case SOCK_ACTION_ADD: /* add some events */
636     new_events = sock->s_events | (events & SOCK_EVENT_MASK);
637     break;
638
639   case SOCK_ACTION_DEL: /* remove some events */
640     new_events = sock->s_events & ~(events & SOCK_EVENT_MASK);
641     break;
642   }
643
644   if (sock->s_events == new_events)
645     return; /* no changes have been made */
646
647   /* tell engine about event mask change */
648   (*evInfo.engine->eng_events)(sock, new_events);
649
650   sock->s_events = new_events; /* set new events */
651 }
652
653 /* Returns an engine's name for informational purposes */
654 const char*
655 engine_name(void)
656 {
657   assert(0 != evInfo.engine);
658   assert(0 != evInfo.engine->eng_name);
659
660   return evInfo.engine->eng_name;
661 }
662
663 #ifdef DEBUGMODE
664 /* These routines pretty-print names for states and types for debug printing */
665
666 #define NS(TYPE) \
667 struct {        \
668   char *name;   \
669   TYPE value;   \
670 }
671
672 #define NM(name)        { #name, name }
673
674 #define NE              { 0 }
675
676 const char*
677 state_to_name(enum SocketState state)
678 {
679   int i;
680   NS(enum SocketState) map[] = {
681     NM(SS_CONNECTING),
682     NM(SS_LISTENING),
683     NM(SS_CONNECTED),
684     NM(SS_DATAGRAM),
685     NM(SS_CONNECTDG),
686     NM(SS_NOTSOCK),
687     NE
688   };
689
690   for (i = 0; map[i].name; i++)
691     if (map[i].value == state)
692       return map[i].name;
693
694   return "Undefined socket state";
695 }
696
697 const char*
698 timer_to_name(enum TimerType type)
699 {
700   int i;
701   NS(enum TimerType) map[] = {
702     NM(TT_ABSOLUTE),
703     NM(TT_RELATIVE),
704     NM(TT_PERIODIC),
705     NE
706   };
707
708   for (i = 0; map[i].name; i++)
709     if (map[i].value == type)
710       return map[i].name;
711
712   return "Undefined timer type";
713 }
714
715 const char*
716 event_to_name(enum EventType type)
717 {
718   int i;
719   NS(enum EventType) map[] = {
720     NM(ET_READ),
721     NM(ET_WRITE),
722     NM(ET_ACCEPT),
723     NM(ET_CONNECT),
724     NM(ET_EOF),
725     NM(ET_ERROR),
726     NM(ET_SIGNAL),
727     NM(ET_EXPIRE),
728     NM(ET_DESTROY),
729     NE
730   };
731
732   for (i = 0; map[i].name; i++)
733     if (map[i].value == type)
734       return map[i].name;
735
736   return "Undefined event type";
737 }
738
739 const char*
740 gen_flags(unsigned int flags)
741 {
742   int i, loc = 0;
743   static char buf[256];
744   NS(unsigned int) map[] = {
745     NM(GEN_DESTROY),
746     NM(GEN_MARKED),
747     NM(GEN_ACTIVE),
748     NM(GEN_READD),
749     NM(GEN_ERROR),
750     NE
751   };
752
753   buf[0] = '\0';
754
755   for (i = 0; map[i].name; i++)
756     if (map[i].value & flags) {
757       if (loc != 0)
758         buf[loc++] = ' ';
759       loc += ircd_snprintf(0, buf + loc, sizeof(buf) - loc, "%s", map[i].name);
760       if (loc >= sizeof(buf))
761         return buf; /* overflow case */
762     }
763
764   return buf;
765 }
766
767 const char*
768 sock_flags(unsigned int flags)
769 {
770   int i, loc = 0;
771   static char buf[256];
772   NS(unsigned int) map[] = {
773     NM(SOCK_EVENT_READABLE),
774     NM(SOCK_EVENT_WRITABLE),
775     NM(SOCK_ACTION_SET),
776     NM(SOCK_ACTION_ADD),
777     NM(SOCK_ACTION_DEL),
778     NE
779   };
780
781   buf[0] = '\0';
782
783   for (i = 0; map[i].name; i++)
784     if (map[i].value & flags) {
785       if (loc != 0)
786         buf[loc++] = ' ';
787       loc += ircd_snprintf(0, buf + loc, sizeof(buf) - loc, "%s", map[i].name);
788       if (loc >= sizeof(buf))
789         return buf; /* overflow case */
790     }
791
792   return buf;
793 }
794
795 #endif /* DEBUGMODE */