]>
jfr.im git - irc/quakenet/snircd.git/blob - ircd/ircd_events.c
2 * IRC - Internet Relay Chat, ircd/ircd_events.c
3 * Copyright (C) 2001 Kevin L. Mitchell <klmitch@mit.edu>
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)
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.
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.
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 $
25 #include "ircd_events.h"
28 #include "ircd_alloc.h"
30 #include "ircd_snprintf.h"
33 /* #include <assert.h> -- Now using assert in ircd_log.h */
38 #define SIGS_PER_SOCK 10 /**< number of signals to process per socket
42 extern struct Engine engine_kqueue
;
43 #define ENGINE_KQUEUE &engine_kqueue,
45 /** Address of kqueue engine (if used). */
47 #endif /* USE_KQUEUE */
50 extern struct Engine engine_devpoll
;
51 #define ENGINE_DEVPOLL &engine_devpoll,
53 /** Address of /dev/poll engine (if used). */
54 #define ENGINE_DEVPOLL
55 #endif /* USE_DEVPOLL */
58 extern struct Engine engine_epoll
;
59 #define ENGINE_EPOLL &engine_epoll,
61 /** Address of epoll engine (if used). */
63 #endif /* USE_EPOLL */
66 extern struct Engine engine_poll
;
67 /** Address of fallback (poll) engine. */
68 #define ENGINE_FALLBACK &engine_poll,
70 extern struct Engine engine_select
;
71 /** Address of fallback (select) engine. */
72 #define ENGINE_FALLBACK &engine_select,
75 /** list of engines to try */
76 static const struct Engine
*evEngines
[] = {
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).
89 int fd
; /**< signal routine's fd */
90 struct Socket sock
; /**< and its struct Socket */
93 /** All the thread info */
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 */
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 */
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.
120 gen_init(struct GenHeader
* gen
, EventCallBack call
, void* data
,
121 struct GenHeader
* next
, struct GenHeader
** prev_p
)
126 gen
->gh_prev_p
= prev_p
;
133 gen
->gh_flags
= GEN_ACTIVE
;
137 gen
->gh_engdata
.ed_int
= 0;
139 if (prev_p
) { /* Going to link into list? */
140 if (next
) /* do so */
141 next
->gh_prev_p
= &gen
->gh_next
;
146 /** Execute an event.
147 * Optimizations should inline this.
148 * @param[in] event Event to execute.
151 event_execute(struct Event
* event
)
154 assert(0 == event
->ev_prev_p
); /* must be off queue first */
155 assert(event
->ev_gen
.gen_header
->gh_flags
& GEN_ACTIVE
);
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
;
162 (*event
->ev_gen
.gen_header
->gh_call
)(event
); /* execute the event */
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.
170 if (event
->ev_type
!= ET_DESTROY
)
171 gen_ref_dec(event
->ev_gen
.gen_header
);
173 event
->ev_gen
.gen_header
= 0; /* clear event data */
174 event
->ev_type
= ET_DESTROY
;
176 event
->ev_next
= evInfo
.events_free
; /* add to free list */
177 evInfo
.events_free
= event
;
180 #ifndef IRCD_THREADED
181 /** we synchronously execute the event when not threaded */
182 #define event_add(event) \
184 struct Event* _ev = (event); \
186 _ev->ev_prev_p = 0; \
187 event_execute(_ev); \
191 /** Add an event to the work queue.
192 * @param[in] event Event to enqueue.
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 */
197 event_add(struct Event
* event
)
199 struct GenHeader
* gen
;
203 gen
= event
->ev_gen
.gen_header
;
205 /* First, place event on generator's event queue */
208 assert(0 != gen
->gh_tail
);
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
);
216 event
->ev_prev_p
= &gen
->gh_head
;
217 gen
->gh_head
= event
;
218 gen
->gh_tail
= event
;
221 /* Now, if the generator isn't on the queue yet... */
222 if (!gen
->gh_qprev_p
) {
224 if (evInfo
.genq_head
) {
225 assert(0 != evInfo
.genq_tail
);
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
);
233 gen
->gh_qprev_p
= &evInfo
.genq_head
;
234 evInfo
.genq_head
= gen
;
235 evInfo
.genq_tail
= gen
;
238 /* We'd also have to signal the work crew here */
241 #endif /* IRCD_THREADED */
243 /** Place a timer in the correct spot on the queue.
244 * @param[in] timer Timer to enqueue.
247 timer_enqueue(struct Timer
* timer
)
249 struct Timer
** ptr_p
;
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 */
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
;
261 case TT_RELATIVE
: case TT_PERIODIC
: /* relative timer */
262 timer
->t_expire
= timer
->t_value
+ CurrentTime
;
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
)
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
;
276 (*ptr_p
)->t_header
.gh_prev_p
= &timer
->t_header
.gh_next
;
280 /** &Signal handler for writing signal notification to pipe.
281 * @param[in] sig Signal number that just happened.
284 signal_handler(int sig
)
288 assert(sigInfo
.fd
>= 0);
290 c
= (unsigned char) sig
; /* only write 1 byte to identify sig */
292 write(sigInfo
.fd
, &c
, 1);
295 /** Callback for signal "socket" (really pipe) events.
296 * @param[in] event Event activity descriptor.
299 signal_callback(struct Event
* event
)
301 unsigned char sigstr
[SIGS_PER_SOCK
];
305 assert(event
->ev_type
== ET_READ
); /* readable events only */
307 n_sigs
= read(event
->ev_gen
.gen_socket
->s_fd
, sigstr
, sizeof(sigstr
));
309 for (i
= 0; i
< n_sigs
; i
++) {
310 sig
= (int) sigstr
[i
]; /* get signal */
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... */
318 event_generate(ET_SIGNAL
, ptr
, sig
); /* generate signal event */
322 /** Remove a generator from its queue.
323 * @param[in] arg Pointer to a GenHeader to dequeue.
326 gen_dequeue(void* arg
)
328 struct GenHeader
* gen
= (struct GenHeader
*) arg
;
330 if (gen
->gh_next
) /* clip it out of the list */
331 gen
->gh_next
->gh_prev_p
= gen
->gh_prev_p
;
333 *gen
->gh_prev_p
= gen
->gh_next
;
335 gen
->gh_next
= 0; /* mark that it's not in the list anymore */
339 /** Initializes the event system.
340 * @param[in] max_sockets Maximum number of sockets to support.
343 event_init(int max_sockets
)
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
);
351 if ((*evEngines
[i
]->eng_init
)(max_sockets
))
352 break; /* Found an engine that'll work */
355 assert(0 != evEngines
[i
]);
357 evInfo
.engine
= evEngines
[i
]; /* save engine */
359 if (!evInfo
.engine
->eng_signal
) { /* engine can't do signals */
361 log_write(LS_SYSTEM
, L_CRIT
, 0, "Failed to open signal pipe");
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 */
371 /** Do the event loop. */
375 assert(0 != evInfo
.engine
);
376 assert(0 != evInfo
.engine
->eng_loop
);
378 (*evInfo
.engine
->eng_loop
)(&evInfo
.gens
);
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.
387 event_generate(enum EventType type
, void* arg
, int data
)
390 struct GenHeader
* gen
= (struct GenHeader
*) arg
;
394 /* don't create events (other than ET_DESTROY) for destroyed generators */
395 if (type
!= ET_DESTROY
&& (gen
->gh_flags
& GEN_DESTROY
))
398 Debug((DEBUG_LIST
, "Generating event type %s for generator %p (%s)",
399 event_to_name(type
), gen
, gen_flags(gen
->gh_flags
)));
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 */
408 ptr
->ev_type
= type
; /* Record event type */
411 ptr
->ev_gen
.gen_header
= (struct GenHeader
*) gen
;
412 ptr
->ev_gen
.gen_header
->gh_ref
++;
414 event_add(ptr
); /* add event to queue */
418 /* Try to verify the timer list */
423 struct Timer
** ptr_p
= &evInfo
.gens
.g_timer
;
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
);
437 lasttime
= ptr
->t_expire
; /* store time for ordering check */
438 ptr_p
= (struct Timer
**) &ptr
->t_header
.gh_next
; /* store prev pointer */
443 /** Initialize a timer structure.
444 * @param[in,out] timer Timer to initialize.
445 * @return The pointer \a timer.
448 timer_init(struct Timer
* timer
)
450 gen_init((struct GenHeader
*) timer
, 0, 0, 0, 0);
452 timer
->t_header
.gh_flags
= 0; /* turn off active flag */
454 return timer
; /* convenience return */
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).
465 timer_add(struct Timer
* timer
, EventCallBack call
, void* data
,
466 enum TimerType type
, time_t value
)
471 Debug((DEBUG_LIST
, "Adding timer %p; time out %Tu (type %s)", timer
, value
,
472 timer_to_name(type
)));
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
;
479 timer
->t_header
.gh_ref
= 0;
480 timer
->t_header
.gh_call
= call
;
481 timer
->t_header
.gh_data
= data
;
483 timer
->t_type
= type
;
484 timer
->t_value
= value
;
487 if (!(timer
->t_header
.gh_flags
& GEN_MARKED
))
488 timer_enqueue(timer
); /* and enqueue it */
491 /** Remove a timer from the processing queue.
492 * @param[in] timer Timer to remove.
495 timer_del(struct Timer
* timer
)
499 timer
->t_header
.gh_flags
&= ~GEN_READD
;
501 if (timer
->t_header
.gh_flags
& GEN_MARKED
)
502 return; /* timer is being used */
504 Debug((DEBUG_LIST
, "Deleting timer %p (type %s)", timer
,
505 timer_to_name(timer
->t_type
)));
508 event_generate(ET_DESTROY
, timer
, 0);
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.
517 timer_chg(struct Timer
* timer
, enum TimerType type
, time_t value
)
521 assert(TT_PERIODIC
!= timer
->t_type
);
522 assert(TT_PERIODIC
!= type
);
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
));
528 timer
->t_type
= type
; /* Set the new type and value */
529 timer
->t_value
= value
;
532 /* If the timer expiration callback tries to change the timer
533 * expiration, flag the timer but do not dequeue it yet.
535 if (timer
->t_header
.gh_flags
& GEN_MARKED
)
537 timer
->t_header
.gh_flags
|= GEN_READD
;
540 gen_dequeue(timer
); /* remove the timer from the queue */
541 timer_enqueue(timer
); /* re-queue the timer */
544 /** Execute all expired timers. */
550 /* go through queue... */
551 while ((ptr
= evInfo
.gens
.g_timer
)) {
552 if (CurrentTime
< ptr
->t_expire
)
553 break; /* processed all pending timers */
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));
559 event_generate(ET_EXPIRE
, ptr
, 0); /* generate expire event */
561 ptr
->t_header
.gh_flags
&= ~GEN_MARKED
;
563 if (!(ptr
->t_header
.gh_flags
& GEN_READD
)) {
564 Debug((DEBUG_LIST
, "Destroying timer %p", ptr
));
565 event_generate(ET_DESTROY
, ptr
, 0);
567 Debug((DEBUG_LIST
, "Re-enqueuing timer %p", ptr
));
568 timer_enqueue(ptr
); /* re-queue timer */
569 ptr
->t_header
.gh_flags
&= ~GEN_READD
;
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.
581 signal_add(struct Signal
* signal
, EventCallBack call
, void* data
, int sig
)
583 struct sigaction act
;
587 assert(0 != evInfo
.engine
);
590 gen_init((struct GenHeader
*) signal
, call
, data
,
591 (struct GenHeader
*) evInfo
.gens
.g_signal
,
592 (struct GenHeader
**) &evInfo
.gens
.g_signal
);
594 signal
->sig_signal
= sig
;
596 if (evInfo
.engine
->eng_signal
)
597 (*evInfo
.engine
->eng_signal
)(signal
); /* tell engine */
599 act
.sa_handler
= signal_handler
; /* set up signal handler */
601 sigemptyset(&act
.sa_mask
);
602 sigaction(sig
, &act
, 0);
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.
616 socket_add(struct Socket
* sock
, EventCallBack call
, void* data
,
617 enum SocketState state
, unsigned int events
, int fd
)
622 assert(0 != evInfo
.engine
);
623 assert(0 != evInfo
.engine
->eng_add
);
626 gen_init((struct GenHeader
*) sock
, call
, data
,
627 (struct GenHeader
*) evInfo
.gens
.g_socket
,
628 (struct GenHeader
**) &evInfo
.gens
.g_socket
);
630 sock
->s_state
= state
;
631 sock
->s_events
= events
& SOCK_EVENT_MASK
;
634 return (*evInfo
.engine
->eng_add
)(sock
); /* tell engine about it */
637 /** Deletes (or marks for deletion) a socket generator.
638 * @param[in] sock Event generator to clear.
641 socket_del(struct Socket
* sock
)
644 assert(!(sock
->s_header
.gh_flags
& GEN_DESTROY
));
645 assert(0 != evInfo
.engine
);
646 assert(0 != evInfo
.engine
->eng_closing
);
648 /* tell engine socket is going away */
649 (*evInfo
.engine
->eng_closing
)(sock
);
651 sock
->s_header
.gh_flags
|= GEN_DESTROY
;
653 if (!sock
->s_header
.gh_ref
) { /* not in use; destroy right now */
655 event_generate(ET_DESTROY
, sock
, 0);
659 /** Sets the socket state to something else.
660 * @param[in] sock Socket generator to update.
661 * @param[in] state New socket state.
664 socket_state(struct Socket
* sock
, enum SocketState state
)
667 assert(0 != evInfo
.engine
);
668 assert(0 != evInfo
.engine
->eng_state
);
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
);
681 /* Don't continue if an error occurred or the socket got destroyed */
682 if (sock
->s_header
.gh_flags
& (GEN_DESTROY
| GEN_ERROR
))
685 /* tell engine we're changing socket state */
686 (*evInfo
.engine
->eng_state
)(sock
, state
);
688 sock
->s_state
= state
; /* set new state */
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.
696 socket_events(struct Socket
* sock
, unsigned int events
)
698 unsigned int new_events
= 0;
701 assert(0 != evInfo
.engine
);
702 assert(0 != evInfo
.engine
->eng_events
);
704 /* Don't continue if an error occurred or the socket got destroyed */
705 if (sock
->s_header
.gh_flags
& (GEN_DESTROY
| GEN_ERROR
))
708 switch (events
& SOCK_ACTION_MASK
) {
709 case SOCK_ACTION_SET
: /* set events to given set */
710 new_events
= events
& SOCK_EVENT_MASK
;
713 case SOCK_ACTION_ADD
: /* add some events */
714 new_events
= sock
->s_events
| (events
& SOCK_EVENT_MASK
);
717 case SOCK_ACTION_DEL
: /* remove some events */
718 new_events
= sock
->s_events
& ~(events
& SOCK_EVENT_MASK
);
722 if (sock
->s_events
== new_events
)
723 return; /* no changes have been made */
725 /* tell engine about event mask change */
726 (*evInfo
.engine
->eng_events
)(sock
, new_events
);
728 sock
->s_events
= new_events
; /* set new events */
731 /** Returns the current engine's name for informational purposes.
732 * @return Pointer to a static buffer containing the engine name.
737 assert(0 != evInfo
.engine
);
738 assert(0 != evInfo
.engine
->eng_name
);
740 return evInfo
.engine
->eng_name
;
744 /* These routines pretty-print names for states and types for debug printing */
746 /** Declares a struct variable containing name(s) and value(s) of \a TYPE. */
753 /** Declares an element initialize for an NS() struct. */
754 #define NM(name) { #name, name }
756 /** Declares end of an NS() struct array. */
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".
764 state_to_name(enum SocketState state
)
767 NS(enum SocketState
) map
[] = {
777 for (i
= 0; map
[i
].name
; i
++)
778 if (map
[i
].value
== state
)
781 return "Undefined socket state";
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".
789 timer_to_name(enum TimerType type
)
792 NS(enum TimerType
) map
[] = {
799 for (i
= 0; map
[i
].name
; i
++)
800 if (map
[i
].value
== type
)
803 return "Undefined timer type";
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".
811 event_to_name(enum EventType type
)
814 NS(enum EventType
) map
[] = {
827 for (i
= 0; map
[i
].name
; i
++)
828 if (map
[i
].value
== type
)
831 return "Undefined event type";
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.
839 gen_flags(unsigned int flags
)
842 static char buf
[256];
843 NS(unsigned int) map
[] = {
854 for (i
= 0; map
[i
].name
; i
++)
855 if (map
[i
].value
& flags
) {
858 loc
+= ircd_snprintf(0, buf
+ loc
, sizeof(buf
) - loc
, "%s", map
[i
].name
);
859 if (loc
>= sizeof(buf
))
860 return buf
; /* overflow case */
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.
871 sock_flags(unsigned int flags
)
874 static char buf
[256];
875 NS(unsigned int) map
[] = {
876 NM(SOCK_EVENT_READABLE
),
877 NM(SOCK_EVENT_WRITABLE
),
886 for (i
= 0; map
[i
].name
; i
++)
887 if (map
[i
].value
& flags
) {
890 loc
+= ircd_snprintf(0, buf
+ loc
, sizeof(buf
) - loc
, "%s", map
[i
].name
);
891 if (loc
>= sizeof(buf
))
892 return buf
; /* overflow case */
898 #endif /* DEBUGMODE */