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