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