added gnutls backend and moved backend code into new files
[ircu2.10.12-pk.git] / ircd / ircd_events.c
index e59cd051f4cfd7590086cfde3aa76d5f4779554a..f21ecdbe29ca8c775f9e93da66e46b5dcccc28b3 100644 (file)
  * You should have received a copy of the GNU General Public License
  * along with this program; if not, write to the Free Software
  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
- *
- * $Id$
+ */
+/** @file
+ * @brief Implementation of event loop mid-layer.
+ * @version $Id$
  */
 #include "config.h"
 
 #include "ircd_snprintf.h"
 #include "s_debug.h"
 
-#include <assert.h>
+/* #include <assert.h> -- Now using assert in ircd_log.h */
 #include <signal.h>
 #include <stdlib.h>
 #include <unistd.h>
 
-#define SIGS_PER_SOCK  10      /* number of signals to process per socket
+#define SIGS_PER_SOCK  10      /**< number of signals to process per socket
                                   readable event */
 
 #ifdef USE_KQUEUE
 extern struct Engine engine_kqueue;
 #define ENGINE_KQUEUE  &engine_kqueue,
 #else
+/** Address of kqueue engine (if used). */
 #define ENGINE_KQUEUE
 #endif /* USE_KQUEUE */
 
@@ -47,41 +50,56 @@ extern struct Engine engine_kqueue;
 extern struct Engine engine_devpoll;
 #define ENGINE_DEVPOLL &engine_devpoll,
 #else
+/** Address of /dev/poll engine (if used). */
 #define ENGINE_DEVPOLL
 #endif /* USE_DEVPOLL */
 
+#ifdef USE_EPOLL
+extern struct Engine engine_epoll;
+#define ENGINE_EPOLL &engine_epoll,
+#else
+/** Address of epoll engine (if used). */
+#define ENGINE_EPOLL
+#endif /* USE_EPOLL */
+
 #ifdef USE_POLL
 extern struct Engine engine_poll;
+/** Address of fallback (poll) engine. */
 #define ENGINE_FALLBACK        &engine_poll,
 #else
 extern struct Engine engine_select;
+/** Address of fallback (select) engine. */
 #define ENGINE_FALLBACK        &engine_select,
 #endif /* USE_POLL */
 
-/* list of engines to try */
+/** list of engines to try */
 static const struct Engine *evEngines[] = {
   ENGINE_KQUEUE
+  ENGINE_EPOLL
   ENGINE_DEVPOLL
   ENGINE_FALLBACK
   0
 };
 
-/* signal routines pipe data */
+/** Signal routines pipe data.
+ * This is used if an engine does not implement signal handling itself
+ * (when Engine::eng_signal is NULL).
+ */
 static struct {
-  int          fd;     /* signal routine's fd */
-  struct Socket        sock;   /* and its struct Socket */
+  int          fd;     /**< signal routine's fd */
+  struct Socket        sock;   /**< and its struct Socket */
 } sigInfo = { -1 };
 
-/* All the thread info */
+/** All the thread info */
 static struct {
-  struct Generators    gens;           /* List of all generators */
-  struct Event*               events_free;     /* struct Event free list */
-  unsigned int        events_alloc;    /* count of allocated struct Events */
-  const struct Engine* engine;         /* core engine being used */
+  struct Generators    gens;           /**< List of all generators */
+  struct Event*               events_free;     /**< struct Event free list */
+  unsigned int        events_alloc;    /**< count of allocated struct Events */
+  const struct Engine* engine;         /**< core engine being used */
 #ifdef IRCD_THREADED
-  struct GenHeader*    genq_head;      /* head of generator event queue */
-  struct GenHeader*    genq_tail;      /* tail of generator event queue */
-  unsigned int        genq_count;      /* count of generators on queue */
+  struct GenHeader*    genq_head;      /**< head of generator event queue */
+  struct GenHeader*    genq_tail;      /**< tail of generator event queue */
+  unsigned int        genq_count;      /**< count of generators on queue */
 #endif
 } evInfo = {
   { 0, 0, 0 },
@@ -91,7 +109,13 @@ static struct {
 #endif
 };
 
-/* Initialize a struct GenHeader */
+/** Initialize a struct GenHeader.
+ * @param[in,out] gen GenHeader to initialize.
+ * @param[in] call Callback for generated events.
+ * @param[in] data User data pointer.
+ * @param[in] next Pointer to next generator.
+ * @param[in,out] prev_p Pointer to previous pointer for this list.
+ */
 static void
 gen_init(struct GenHeader* gen, EventCallBack call, void* data,
         struct GenHeader* next, struct GenHeader** prev_p)
@@ -119,7 +143,10 @@ gen_init(struct GenHeader* gen, EventCallBack call, void* data,
   }
 }
 
-/* Execute an event; optimizations should inline this */
+/** Execute an event.
+ * Optimizations should inline this.
+ * @param[in] event Event to execute.
+ */
 static void
 event_execute(struct Event* event)
 {
@@ -129,6 +156,8 @@ event_execute(struct Event* event)
 
   if (event->ev_type == ET_DESTROY) /* turn off active flag *before* destroy */
     event->ev_gen.gen_header->gh_flags &= ~GEN_ACTIVE;
+  if (event->ev_type == ET_ERROR) /* turn on error flag before callback */
+    event->ev_gen.gen_header->gh_flags |= GEN_ERROR;
 
   (*event->ev_gen.gen_header->gh_call)(event); /* execute the event */
 
@@ -149,7 +178,7 @@ event_execute(struct Event* event)
 }
 
 #ifndef IRCD_THREADED
-/* we synchronously execute the event when not threaded */
+/** we synchronously execute the event when not threaded */
 #define event_add(event)       \
 do {                                                                         \
   struct Event* _ev = (event);                                               \
@@ -159,7 +188,9 @@ do {                                                                              \
 } while (0)
 
 #else
-/* add an event to the work queue */
+/** Add an event to the work queue.
+ * @param[in] event Event to enqueue.
+ */
 /* This is just a placeholder; don't expect ircd to be threaded soon */
 /* There should be locks all over the place in here */
 static void
@@ -209,14 +240,17 @@ event_add(struct Event* event)
 }
 #endif /* IRCD_THREADED */
 
-/* Place a timer in the correct spot on the queue */
+/** Place a timer in the correct spot on the queue.
+ * @param[in] timer Timer to enqueue.
+ */
 static void
 timer_enqueue(struct Timer* timer)
 {
-  struct Timer** ptr_p;
+  struct GenHeader** ptr_p;
 
   assert(0 != timer);
   assert(0 == timer->t_header.gh_prev_p); /* not already on queue */
+  assert(timer->t_header.gh_flags & GEN_ACTIVE); /* timer is active */
 
   /* Calculate expire time */
   switch (timer->t_type) {
@@ -231,19 +265,21 @@ timer_enqueue(struct Timer* timer)
 
   /* Find a slot to insert timer */
   for (ptr_p = &evInfo.gens.g_timer; ;
-       ptr_p = (struct Timer**) &(*ptr_p)->t_header.gh_next)
-    if (!*ptr_p || timer->t_expire < (*ptr_p)->t_expire)
+       ptr_p = &(*ptr_p)->gh_next)
+    if (!*ptr_p || timer->t_expire < ((struct Timer*)*ptr_p)->t_expire)
       break;
 
   /* link it in the right place */
-  timer->t_header.gh_next = (struct GenHeader*) *ptr_p;
-  timer->t_header.gh_prev_p = (struct GenHeader**) ptr_p;
+  timer->t_header.gh_next = *ptr_p;
+  timer->t_header.gh_prev_p = ptr_p;
   if (*ptr_p)
-    (*ptr_p)->t_header.gh_prev_p = &timer->t_header.gh_next;
-  *ptr_p = timer;
+    (*ptr_p)->gh_prev_p = &timer->t_header.gh_next;
+  *ptr_p = &timer->t_header;
 }
 
-/* signal handler for writing signal notification to pipe */
+/** &Signal handler for writing signal notification to pipe.
+ * @param[in] sig Signal number that just happened.
+ */
 static void
 signal_handler(int sig)
 {
@@ -256,13 +292,15 @@ signal_handler(int sig)
   write(sigInfo.fd, &c, 1);
 }
 
-/* callback for signal "socket" events */
+/** Callback for signal "socket" (really pipe) events.
+ * @param[in] event Event activity descriptor.
+ */
 static void
 signal_callback(struct Event* event)
 {
   unsigned char sigstr[SIGS_PER_SOCK];
   int sig, n_sigs, i;
-  struct Signal* ptr;
+  struct GenHeader* ptr;
 
   assert(event->ev_type == ET_READ); /* readable events only */
 
@@ -272,8 +310,8 @@ signal_callback(struct Event* event)
     sig = (int) sigstr[i]; /* get signal */
 
     for (ptr = evInfo.gens.g_signal; ptr;
-        ptr = (struct Signal*) ptr->sig_header.gh_next)
-      if (ptr->sig_signal == sig) /* find its descriptor... */
+        ptr = ptr->gh_next)
+      if (((struct Signal*)ptr)->sig_signal == sig) /* find its descriptor... */
        break;
 
     if (ptr)
@@ -281,7 +319,9 @@ signal_callback(struct Event* event)
   }
 }
 
-/* Remove something from its queue */
+/** Remove a generator from its queue.
+ * @param[in] arg Pointer to a GenHeader to dequeue.
+ */
 void
 gen_dequeue(void* arg)
 {
@@ -296,7 +336,9 @@ gen_dequeue(void* arg)
   gen->gh_prev_p = 0;
 }
 
-/* Initializes the event system */
+/** Initializes the event system.
+ * @param[in] max_sockets Maximum number of sockets to support.
+ */
 void
 event_init(int max_sockets)
 {
@@ -326,7 +368,7 @@ event_init(int max_sockets)
   }
 }
 
-/* Do the event loop */
+/** Do the event loop. */
 void
 event_loop(void)
 {
@@ -336,7 +378,11 @@ event_loop(void)
   (*evInfo.engine->eng_loop)(&evInfo.gens);
 }
 
-/* Generate an event and add it to the queue (or execute it) */
+/** Generate an event and add it to the queue (or execute it).
+ * @param[in] type Type of event to generate.
+ * @param[in] arg Pointer to an event generator (GenHeader).
+ * @param[in] data Extra data for event.
+ */
 void
 event_generate(enum EventType type, void* arg, int data)
 {
@@ -368,7 +414,53 @@ event_generate(enum EventType type, void* arg, int data)
   event_add(ptr); /* add event to queue */
 }
 
-/* Add a timer to be processed */
+#if 0
+/* Try to verify the timer list */
+void
+timer_verify(void)
+{
+  struct Timer* ptr;
+  struct Timer** ptr_p = &evInfo.gens.g_timer;
+  time_t lasttime = 0;
+
+  for (ptr = evInfo.gens.g_timer; ptr;
+       ptr = (struct Timer*) ptr->t_header.gh_next) {
+    /* verify timer is supposed to be in the list */
+    assert(ptr->t_header.gh_prev_p);
+    /* verify timer is correctly ordered */
+    assert((struct Timer**) ptr->t_header.gh_prev_p == ptr_p);
+    /* verify timer is active */
+    assert(ptr->t_header.gh_flags & GEN_ACTIVE);
+    /* verify timer ordering is correct */
+    assert(lasttime <= ptr->t_expire);
+
+    lasttime = ptr->t_expire; /* store time for ordering check */
+    ptr_p = (struct Timer**) &ptr->t_header.gh_next; /* store prev pointer */
+  }
+}
+#endif
+
+/** Initialize a timer structure.
+ * @param[in,out] timer Timer to initialize.
+ * @return The pointer \a timer.
+ */
+struct Timer*
+timer_init(struct Timer* timer)
+{
+  gen_init(&timer->t_header, 0, 0, 0, 0);
+
+  timer->t_header.gh_flags = 0; /* turn off active flag */
+
+  return timer; /* convenience return */
+}
+
+/** Add a timer to be processed.
+ * @param[in] timer Timer to add.
+ * @param[in] call Callback for when the timer expires or is removed.
+ * @param[in] data User data pointer for the timer.
+ * @param[in] type Timer type.
+ * @param[in] value Timer expiration, duration or interval (per \a type).
+ */
 void
 timer_add(struct Timer* timer, EventCallBack call, void* data,
          enum TimerType type, time_t value)
@@ -380,36 +472,47 @@ timer_add(struct Timer* timer, EventCallBack call, void* data,
         timer_to_name(type)));
 
   /* initialize a timer... */
-  gen_init((struct GenHeader*) timer, call, data, 0, 0);
+  timer->t_header.gh_flags |= GEN_ACTIVE;
+  if (timer->t_header.gh_flags & GEN_MARKED)
+    timer->t_header.gh_flags |= GEN_READD;
+
+  timer->t_header.gh_ref = 0;
+  timer->t_header.gh_call = call;
+  timer->t_header.gh_data = data;
 
   timer->t_type = type;
   timer->t_value = value;
   timer->t_expire = 0;
 
-  timer_enqueue(timer); /* and enqueue it */
+  if (!(timer->t_header.gh_flags & GEN_MARKED))
+    timer_enqueue(timer); /* and enqueue it */
 }
 
-/* Remove a timer from the processing queue */
+/** Remove a timer from the processing queue.
+ * @param[in] timer Timer to remove.
+ */
 void
 timer_del(struct Timer* timer)
 {
   assert(0 != timer);
 
-  if (timer->t_header.gh_flags & GEN_MARKED)
-    return; /* timer already marked for destruction */
+  timer->t_header.gh_flags &= ~GEN_READD;
 
-  timer->t_header.gh_flags |= GEN_DESTROY;
+  if (timer->t_header.gh_flags & GEN_MARKED)
+    return; /* timer is being used */
 
   Debug((DEBUG_LIST, "Deleting timer %p (type %s)", timer,
         timer_to_name(timer->t_type)));
 
-  if (!timer->t_header.gh_ref) { /* not in use; destroy right now */
-    gen_dequeue(timer);
-    event_generate(ET_DESTROY, timer, 0);
-  }
+  gen_dequeue(timer);
+  event_generate(ET_DESTROY, timer, 0);
 }
 
-/* Change the time a timer expires */
+/** Change the time a timer expires.
+ * @param[in] timer Timer to update.
+ * @param[in] type New timer type.
+ * @param[in] value New timer expiration value.
+ */
 void
 timer_chg(struct Timer* timer, enum TimerType type, time_t value)
 {
@@ -422,46 +525,58 @@ timer_chg(struct Timer* timer, enum TimerType type, time_t value)
         "timeout %Tu", timer, timer_to_name(timer->t_type), timer->t_value,
         timer_to_name(type), value));
 
-  gen_dequeue(timer); /* remove the timer from the queue */
-
   timer->t_type = type; /* Set the new type and value */
   timer->t_value = value;
   timer->t_expire = 0;
 
+  /* If the timer expiration callback tries to change the timer
+   * expiration, flag the timer but do not dequeue it yet.
+   */
+  if (timer->t_header.gh_flags & GEN_MARKED)
+  {
+    timer->t_header.gh_flags |= GEN_READD;
+    return;
+  }
+  gen_dequeue(timer); /* remove the timer from the queue */
   timer_enqueue(timer); /* re-queue the timer */
 }
 
-/* Execute all expired timers */
+/** Execute all expired timers. */
 void
 timer_run(void)
 {
   struct Timer* ptr;
-  struct Timer* next = 0;
 
   /* go through queue... */
-  for (ptr = evInfo.gens.g_timer; ptr; ptr = next) {
-    next = (struct Timer*) ptr->t_header.gh_next;
+  while ((ptr = (struct Timer*)evInfo.gens.g_timer)) {
     if (CurrentTime < ptr->t_expire)
       break; /* processed all pending timers */
 
     gen_dequeue(ptr); /* must dequeue timer here */
-    if (ptr->t_type == TT_ABSOLUTE || ptr->t_type == TT_RELATIVE) {
-      Debug((DEBUG_LIST, "Marking timer %p for later destruction", ptr));
-      ptr->t_header.gh_flags |= GEN_MARKED; /* mark for destruction */
-    }
+    ptr->t_header.gh_flags |= (GEN_MARKED |
+                              (ptr->t_type == TT_PERIODIC ? GEN_READD : 0));
+
     event_generate(ET_EXPIRE, ptr, 0); /* generate expire event */
 
-    if (ptr->t_header.gh_flags & (GEN_MARKED | GEN_DESTROY)) {
+    ptr->t_header.gh_flags &= ~GEN_MARKED;
+
+    if (!(ptr->t_header.gh_flags & GEN_READD)) {
       Debug((DEBUG_LIST, "Destroying timer %p", ptr));
       event_generate(ET_DESTROY, ptr, 0);
-    } else if (ptr->t_type == TT_PERIODIC) {
-      Debug((DEBUG_LIST, "Re-enqueuing periodic timer %p", ptr));
-      timer_enqueue(ptr); /* re-queue periodic timer */
+    } else {
+      Debug((DEBUG_LIST, "Re-enqueuing timer %p", ptr));
+      timer_enqueue(ptr); /* re-queue timer */
+      ptr->t_header.gh_flags &= ~GEN_READD;
     }
   }
 }
 
-/* Adds a signal to the event callback system */
+/** Adds a signal to the event callback system.
+ * @param[in] signal Signal event generator to use.
+ * @param[in] call Callback function to use.
+ * @param[in] data User data pointer for generator.
+ * @param[in] sig Signal number to hook.
+ */
 void
 signal_add(struct Signal* signal, EventCallBack call, void* data, int sig)
 {
@@ -472,9 +587,9 @@ signal_add(struct Signal* signal, EventCallBack call, void* data, int sig)
   assert(0 != evInfo.engine);
 
   /* set up struct */
-  gen_init((struct GenHeader*) signal, call, data,
-          (struct GenHeader*) evInfo.gens.g_signal,
-          (struct GenHeader**) &evInfo.gens.g_signal);
+  gen_init(&signal->sig_header, call, data,
+          evInfo.gens.g_signal,
+          &evInfo.gens.g_signal);
 
   signal->sig_signal = sig;
 
@@ -488,7 +603,15 @@ signal_add(struct Signal* signal, EventCallBack call, void* data, int sig)
   }
 }
 
-/* Adds a socket to the event system */
+/** Adds a socket to the event system.
+ * @param[in] sock Socket event generator to use.
+ * @param[in] call Callback function to use.
+ * @param[in] data User data pointer for the generator.
+ * @param[in] state Current socket state.
+ * @param[in] events Event interest mask for connected or connectionless sockets.
+ * @param[in] fd &Socket file descriptor.
+ * @return Zero on error, non-zero on success.
+ */
 int
 socket_add(struct Socket* sock, EventCallBack call, void* data,
           enum SocketState state, unsigned int events, int fd)
@@ -500,9 +623,9 @@ socket_add(struct Socket* sock, EventCallBack call, void* data,
   assert(0 != evInfo.engine->eng_add);
 
   /* set up struct */
-  gen_init((struct GenHeader*) sock, call, data,
-          (struct GenHeader*) evInfo.gens.g_socket,
-          (struct GenHeader**) &evInfo.gens.g_socket);
+  gen_init(&sock->s_header, call, data,
+          evInfo.gens.g_socket,
+          &evInfo.gens.g_socket);
 
   sock->s_state = state;
   sock->s_events = events & SOCK_EVENT_MASK;
@@ -511,7 +634,9 @@ socket_add(struct Socket* sock, EventCallBack call, void* data,
   return (*evInfo.engine->eng_add)(sock); /* tell engine about it */
 }
 
-/* deletes (or marks for deletion) a socket */
+/** Deletes (or marks for deletion) a socket generator.
+ * @param[in] sock Event generator to clear.
+ */
 void
 socket_del(struct Socket* sock)
 {
@@ -531,7 +656,10 @@ socket_del(struct Socket* sock)
   }
 }
 
-/* Sets the socket state to something else */
+/** Sets the socket state to something else.
+ * @param[in] sock Socket generator to update.
+ * @param[in] state New socket state.
+ */
 void
 socket_state(struct Socket* sock, enum SocketState state)
 {
@@ -550,7 +678,8 @@ socket_state(struct Socket* sock, enum SocketState state)
   /* connected datagram socket now unconnected */
   assert(sock->s_state != SS_CONNECTDG || state == SS_DATAGRAM);
 
-  if (sock->s_header.gh_flags & GEN_DESTROY) /* socket's been destroyed */
+  /* Don't continue if an error occurred or the socket got destroyed */
+  if (sock->s_header.gh_flags & (GEN_DESTROY | GEN_ERROR))
     return;
 
   /* tell engine we're changing socket state */
@@ -559,7 +688,10 @@ socket_state(struct Socket* sock, enum SocketState state)
   sock->s_state = state; /* set new state */
 }
 
-/* sets the events a socket's interested in */
+/** Sets the events a socket's interested in.
+ * @param[in] sock Socket generator to update.
+ * @param[in] events New event interest mask.
+ */
 void
 socket_events(struct Socket* sock, unsigned int events)
 {
@@ -569,7 +701,8 @@ socket_events(struct Socket* sock, unsigned int events)
   assert(0 != evInfo.engine);
   assert(0 != evInfo.engine->eng_events);
 
-  if (sock->s_header.gh_flags & GEN_DESTROY) /* socket's been destroyed */
+  /* Don't continue if an error occurred or the socket got destroyed */
+  if (sock->s_header.gh_flags & (GEN_DESTROY | GEN_ERROR))
     return;
 
   switch (events & SOCK_ACTION_MASK) {
@@ -595,7 +728,9 @@ socket_events(struct Socket* sock, unsigned int events)
   sock->s_events = new_events; /* set new events */
 }
 
-/* Returns an engine's name for informational purposes */
+/** Returns the current engine's name for informational purposes.
+ * @return Pointer to a static buffer containing the engine name.
+ */
 const char*
 engine_name(void)
 {
@@ -608,16 +743,23 @@ engine_name(void)
 #ifdef DEBUGMODE
 /* These routines pretty-print names for states and types for debug printing */
 
+/** Declares a struct variable containing name(s) and value(s) of \a TYPE. */
 #define NS(TYPE) \
 struct {       \
   char *name;  \
   TYPE value;  \
 }
 
+/** Declares an element initialize for an NS() struct. */
 #define NM(name)       { #name, name }
 
-#define NE             { 0, 0 }
+/** Declares end of an NS() struct array. */
+#define NE             { 0 }
 
+/** Looks up name for a socket state.
+ * @param[in] state &Socket state to look up.
+ * @return Pointer to a static buffer containing the name, or "Undefined socket state".
+ */
 const char*
 state_to_name(enum SocketState state)
 {
@@ -639,6 +781,10 @@ state_to_name(enum SocketState state)
   return "Undefined socket state";
 }
 
+/** Looks up name for a timer type.
+ * @param[in] type &Timer type to look up.
+ * @return Pointer to a static buffer containing the name, or "Undefined timer type".
+ */
 const char*
 timer_to_name(enum TimerType type)
 {
@@ -657,6 +803,10 @@ timer_to_name(enum TimerType type)
   return "Undefined timer type";
 }
 
+/** Looks up name for an event type.
+ * @param[in] type &Event type to look up.
+ * @return Pointer to a static buffer containing the name, or "Undefined event type".
+ */
 const char*
 event_to_name(enum EventType type)
 {
@@ -681,6 +831,10 @@ event_to_name(enum EventType type)
   return "Undefined event type";
 }
 
+/** Constructs a string describing certain generator flags.
+ * @param[in] flags Bitwise combination of generator flags.
+ * @return Pointer to a static buffer containing the names of flags set in \a flags.
+ */
 const char*
 gen_flags(unsigned int flags)
 {
@@ -689,6 +843,9 @@ gen_flags(unsigned int flags)
   NS(unsigned int) map[] = {
     NM(GEN_DESTROY),
     NM(GEN_MARKED),
+    NM(GEN_ACTIVE),
+    NM(GEN_READD),
+    NM(GEN_ERROR),
     NE
   };
 
@@ -706,6 +863,10 @@ gen_flags(unsigned int flags)
   return buf;
 }
 
+/** Constructs a string describing certain socket flags.
+ * @param[in] flags Bitwise combination of socket flags.
+ * @return Pointer to a static buffer containing the names of flags set in \a flags.
+ */
 const char*
 sock_flags(unsigned int flags)
 {