ssh: Improve key lookup for many keys.
[gnupg.git] / dirmngr / ldap-wrapper.c
index fa5bf3c..f2aaf59 100644 (file)
@@ -23,7 +23,7 @@
 
    1. On some systems the LDAP library uses (indirectly) pthreads and
       that is not compatible with PTh.
-  
+
    2. It is huge library in particular if TLS comes into play.  So
       problems with unfreed memory might turn up and we don't want
       this in a long running daemon.
@@ -35,7 +35,7 @@
       process to commit suicide or have our own housekepping function
       kill it after some time.  The latter also allows proper
       cancellation of a query at any point of time.
-      
+
    4. Given that we are going out to the network and usually get back
       a long response, the fork/exec overhead is acceptable.
 
@@ -55,7 +55,7 @@
 #include <unistd.h>
 #include <fcntl.h>
 #include <time.h>
-#include <pth.h>
+#include <npth.h>
 
 #include "dirmngr.h"
 #include "exechelp.h"
@@ -82,7 +82,7 @@
 
 #define INACTIVITY_TIMEOUT (opt.ldaptimeout + 60*5)  /* seconds */
 
-
+#define TIMERTICK_INTERVAL 2
 
 /* To keep track of the LDAP wrapper state we use this structure.  */
 struct wrapper_context_s
@@ -96,7 +96,6 @@ struct wrapper_context_s
   gpg_error_t fd_error; /* Set to the gpg_error of the last read error
                            if any.  */
   int log_fd;   /* Connected with stderr of the ldap wrapper.  */
-  pth_event_t log_ev;
   ctrl_t ctrl;  /* Connection data. */
   int ready;    /* Internally used to mark to be removed contexts. */
   ksba_reader_t reader; /* The ksba reader object or NULL. */
@@ -117,8 +116,8 @@ static struct wrapper_context_s *wrapper_list;
 static int shutting_down;
 
 /* Close the pth file descriptor FD and set it to -1.  */
-#define SAFE_PTH_CLOSE(fd) \
-  do { int _fd = fd; if (_fd != -1) { pth_close (_fd); fd = -1;} } while (0)
+#define SAFE_CLOSE(fd) \
+  do { int _fd = fd; if (_fd != -1) { close (_fd); fd = -1;} } while (0)
 
 
 
@@ -129,7 +128,7 @@ read_buffer (ksba_reader_t reader, unsigned char *buffer, size_t count)
 {
   gpg_error_t err;
   size_t nread;
-  
+
   while (count)
     {
       err = ksba_reader_read (reader, buffer, count, &nread);
@@ -144,7 +143,7 @@ read_buffer (ksba_reader_t reader, unsigned char *buffer, size_t count)
 
 /* Release the wrapper context and kill a running wrapper process. */
 static void
-destroy_wrapper (struct wrapper_context_s *ctx) 
+destroy_wrapper (struct wrapper_context_s *ctx)
 {
   if (ctx->pid != (pid_t)(-1))
     {
@@ -152,10 +151,8 @@ destroy_wrapper (struct wrapper_context_s *ctx)
       gnupg_release_process (ctx->pid);
     }
   ksba_reader_release (ctx->reader);
-  SAFE_PTH_CLOSE (ctx->fd);
-  SAFE_PTH_CLOSE (ctx->log_fd);
-  if (ctx->log_ev)
-    pth_event_free (ctx->log_ev, PTH_FREE_THIS);
+  SAFE_CLOSE (ctx->fd);
+  SAFE_CLOSE (ctx->log_fd);
   xfree (ctx->line);
   xfree (ctx);
 }
@@ -175,18 +172,18 @@ print_log_line (struct wrapper_context_s *ctx, char *line)
       if (ctx->line && ctx->linelen)
         {
 
-          log_info ("%s\n", ctx->line); 
+          log_info ("%s\n", ctx->line);
           ctx->linelen = 0;
         }
       return;
     }
-  
+
   while ((s = strchr (line, '\n')))
     {
       *s = 0;
       if (ctx->line && ctx->linelen)
         {
-          log_info ("%s", ctx->line); 
+          log_info ("%s", ctx->line);
           ctx->linelen = 0;
           log_printf ("%s\n", line);
         }
@@ -228,9 +225,9 @@ read_log_data (struct wrapper_context_s *ctx)
   int n;
   char line[256];
 
-  /* We must use the pth_read function for pipes, always.  */
-  do 
-    n = pth_read (ctx->log_fd, line, sizeof line - 1);
+  /* We must use the npth_read function for pipes, always.  */
+  do
+    n = npth_read (ctx->log_fd, line, sizeof line - 1);
   while (n < 0 && errno == EINTR);
 
   if (n <= 0) /* EOF or error. */
@@ -239,9 +236,7 @@ read_log_data (struct wrapper_context_s *ctx)
         log_error (_("error reading log from ldap wrapper %d: %s\n"),
                    ctx->pid, strerror (errno));
       print_log_line (ctx, NULL);
-      SAFE_PTH_CLOSE (ctx->log_fd);
-      pth_event_free (ctx->log_ev, PTH_FREE_THIS);
-      ctx->log_ev = NULL;
+      SAFE_CLOSE (ctx->log_fd);
       return 1;
     }
 
@@ -261,58 +256,72 @@ ldap_wrapper_thread (void *dummy)
   int nfds;
   struct wrapper_context_s *ctx;
   struct wrapper_context_s *ctx_prev;
-  time_t current_time;
+  struct timespec abstime;
+  struct timespec curtime;
+  struct timespec timeout;
+  int saved_errno;
+  fd_set fdset, read_fdset;
+  int ret;
+  time_t exptime;
 
   (void)dummy;
 
+  FD_ZERO (&fdset);
+  nfds = -1;
+  for (ctx = wrapper_list; ctx; ctx = ctx->next)
+    {
+      if (ctx->log_fd != -1)
+       {
+         FD_SET (ctx->log_fd, &fdset);
+         if (ctx->log_fd > nfds)
+           nfds = ctx->log_fd;
+       }
+    }
+  nfds++;
+
+  npth_clock_gettime (&abstime);
+  abstime.tv_sec += TIMERTICK_INTERVAL;
+
   for (;;)
     {
-      pth_event_t timeout_ev;
       int any_action = 0;
-      pth_time_t nexttick;
 
-      /* We timeout the pth_wait every 2 seconds.  To help with power
-         saving we syncronize the timeouts to the next full second.  */
-      nexttick = pth_timeout (2, 0);
-      if (nexttick.tv_usec > 10)  /* Use a 10 usec threshhold.  */
-        {
-          nexttick.tv_sec++;
-          nexttick.tv_usec = 0;
-        }
-      timeout_ev = pth_event (PTH_EVENT_TIME, nexttick);
-      if (! timeout_ev)
+      /* POSIX says that fd_set should be implemented as a structure,
+         thus a simple assignment is fine to copy the entire set.  */
+      read_fdset = fdset;
+
+      npth_clock_gettime (&curtime);
+      if (!(npth_timercmp (&curtime, &abstime, <)))
        {
-          log_error (_("pth_event failed: %s\n"), strerror (errno));
-          pth_sleep (10);
-         continue;
+         /* Inactivity is checked below.  Nothing else to do.  */
+         // handle_tick ();
+         npth_clock_gettime (&abstime);
+         abstime.tv_sec += TIMERTICK_INTERVAL;
        }
+      npth_timersub (&abstime, &curtime, &timeout);
 
-      for (ctx = wrapper_list; ctx; ctx = ctx->next)
-        {
-          if (ctx->log_fd != -1)
-            {
-             pth_event_isolate (ctx->log_ev);
-             pth_event_concat (timeout_ev, ctx->log_ev, NULL);
-            }
-        }
+      /* FIXME: For Windows, we have to use a reader thread on the
+        pipe that signals an event (and a npth_select_ev variant).  */
+      ret = npth_pselect (nfds + 1, &read_fdset, NULL, NULL, &timeout, NULL);
+      saved_errno = errno;
 
-      /* Note that the read FDs are actually handles.  Thus, we can
-        not use pth_select, but have to use pth_wait.  */
-      nfds = pth_wait (timeout_ev);
-      if (nfds < 0)
-        {
-          pth_event_free (timeout_ev, PTH_FREE_THIS);
-          log_error (_("pth_wait failed: %s\n"), strerror (errno));
-          pth_sleep (10);
-         continue;
-        }
-      if (pth_event_status (timeout_ev) == PTH_STATUS_OCCURRED)
-       nfds--;
-      pth_event_free (timeout_ev, PTH_FREE_THIS);
+      if (ret == -1 && saved_errno != EINTR)
+       {
+          log_error (_("npth_select failed: %s - waiting 1s\n"),
+                     strerror (saved_errno));
+          npth_sleep (1);
+          continue;
+       }
 
-      current_time = time (NULL);
-      if (current_time > INACTIVITY_TIMEOUT)
-        current_time -= INACTIVITY_TIMEOUT;
+      if (ret <= 0)
+       /* Interrupt or timeout.  Will be handled when calculating the
+          next timeout.  */
+       continue;
+
+      /* All timestamps before exptime should be considered expired.  */
+      exptime = time (NULL);
+      if (exptime > INACTIVITY_TIMEOUT)
+        exptime -= INACTIVITY_TIMEOUT;
 
       /* Note that there is no need to lock the list because we always
          add entries at the head (with a pending event status) and
@@ -322,8 +331,7 @@ ldap_wrapper_thread (void *dummy)
       for (ctx = wrapper_list; ctx; ctx = ctx->next)
         {
           /* Check whether there is any logging to be done. */
-          if (nfds && ctx->log_fd != -1
-             && pth_event_status (ctx->log_ev) == PTH_STATUS_OCCURRED)
+          if (nfds && ctx->log_fd != -1 && FD_ISSET (ctx->log_fd, &read_fdset))
             {
               if (read_log_data (ctx))
                 any_action = 1;
@@ -334,7 +342,7 @@ ldap_wrapper_thread (void *dummy)
             {
               gpg_error_t err;
              int status;
-              
+
              err = gnupg_wait_process ("[dirmngr_ldap]", ctx->pid, 0,
                                         &status);
               if (!err)
@@ -368,7 +376,7 @@ ldap_wrapper_thread (void *dummy)
 
           /* Check whether we should terminate the process. */
           if (ctx->pid != (pid_t)(-1)
-              && ctx->stamp != (time_t)(-1) && ctx->stamp < current_time)
+              && ctx->stamp != (time_t)(-1) && ctx->stamp < exptime)
             {
               gnupg_kill_process (ctx->pid);
               ctx->stamp = (time_t)(-1);
@@ -376,7 +384,7 @@ ldap_wrapper_thread (void *dummy)
                         (int)ctx->pid);
               /* We need to close the log fd because the cleanup loop
                  waits for it.  */
-              SAFE_PTH_CLOSE (ctx->log_fd);
+              SAFE_CLOSE (ctx->log_fd);
               any_action = 1;
             }
         }
@@ -384,15 +392,15 @@ ldap_wrapper_thread (void *dummy)
       /* If something has been printed to the log file or we got an
          EOF from a wrapper, we now print the list of active
          wrappers.  */
-      if (any_action && DBG_LOOKUP) 
+      if (any_action && DBG_LOOKUP)
         {
           log_info ("ldap worker stati:\n");
           for (ctx = wrapper_list; ctx; ctx = ctx->next)
             log_info ("  c=%p pid=%d/%d rdr=%p ctrl=%p/%d la=%lu rdy=%d\n",
-                      ctx, 
+                      ctx,
                       (int)ctx->pid, (int)ctx->printable_pid,
                       ctx->reader,
-                      ctx->ctrl, ctx->ctrl? ctx->ctrl->refcount:0, 
+                      ctx->ctrl, ctx->ctrl? ctx->ctrl->refcount:0,
                       (unsigned long)ctx->stamp, ctx->ready);
         }
 
@@ -402,7 +410,7 @@ ldap_wrapper_thread (void *dummy)
          is not anymore in use or we are in shutdown state.  */
      again:
       for (ctx_prev=NULL, ctx=wrapper_list; ctx; ctx_prev=ctx, ctx=ctx->next)
-        if (ctx->ready 
+        if (ctx->ready
             && ((ctx->log_fd == -1 && !ctx->reader) || shutting_down))
           {
             if (ctx_prev)
@@ -426,24 +434,26 @@ void
 ldap_wrapper_launch_thread (void)
 {
   static int done;
-  pth_attr_t tattr;
+  npth_attr_t tattr;
+  npth_t thread;
+  int err;
 
   if (done)
     return;
   done = 1;
 
-  tattr = pth_attr_new();
-  pth_attr_set (tattr, PTH_ATTR_JOINABLE, 0);
-  pth_attr_set (tattr, PTH_ATTR_STACK_SIZE, 256*1024);
-  pth_attr_set (tattr, PTH_ATTR_NAME, "ldap-reaper");
+  npth_attr_init (&tattr);
+  npth_attr_setdetachstate (&tattr, NPTH_CREATE_DETACHED);
 
-  if (!pth_spawn (tattr, ldap_wrapper_thread, NULL))
+  err = npth_create (&thread, &tattr, ldap_wrapper_thread, NULL);
+  if (err)
     {
       log_error (_("error spawning ldap wrapper reaper thread: %s\n"),
-                 strerror (errno) );
+                 strerror (err) );
       dirmngr_exit (1);
     }
-  pth_attr_destroy (tattr);
+  npth_setname_np (thread, "ldap-reaper");
+  npth_attr_destroy (&tattr);
 }
 
 
@@ -456,8 +466,9 @@ void
 ldap_wrapper_wait_connections ()
 {
   shutting_down = 1;
+  /* FIXME: This is a busy wait.  */
   while (wrapper_list)
-    pth_yield (NULL);
+    npth_usleep (200);
 }
 
 
@@ -470,19 +481,19 @@ ldap_wrapper_release_context (ksba_reader_t reader)
 
   if (!reader )
     return;
-    
+
   for (ctx=wrapper_list; ctx; ctx=ctx->next)
     if (ctx->reader == reader)
       {
         if (DBG_LOOKUP)
           log_info ("releasing ldap worker c=%p pid=%d/%d rdr=%p ctrl=%p/%d\n",
-                    ctx, 
+                    ctx,
                     (int)ctx->pid, (int)ctx->printable_pid,
                     ctx->reader,
                     ctx->ctrl, ctx->ctrl? ctx->ctrl->refcount:0);
 
         ctx->reader = NULL;
-        SAFE_PTH_CLOSE (ctx->fd);
+        SAFE_CLOSE (ctx->fd);
         if (ctx->ctrl)
           {
             ctx->ctrl->refcount--;
@@ -515,14 +526,22 @@ ldap_wrapper_connection_cleanup (ctrl_t ctrl)
       }
 }
 
+
 /* This is the callback used by the ldap wrapper to feed the ksba
    reader with the wrappers stdout.  See the description of
    ksba_reader_set_cb for details.  */
-static int 
+static int
 reader_callback (void *cb_value, char *buffer, size_t count,  size_t *nread)
 {
   struct wrapper_context_s *ctx = cb_value;
   size_t nleft = count;
+  int nfds;
+  struct timespec abstime;
+  struct timespec curtime;
+  struct timespec timeout;
+  int saved_errno;
+  fd_set fdset, read_fdset;
+  int ret;
 
   /* FIXME: We might want to add some internal buffering because the
      ksba code does not do any buffering for itself (because a ksba
@@ -542,57 +561,73 @@ reader_callback (void *cb_value, char *buffer, size_t count,  size_t *nread)
       return -1;
     }
 
+  FD_ZERO (&fdset);
+  FD_SET (ctx->fd, &fdset);
+  nfds = ctx->fd + 1;
+
+  npth_clock_gettime (&abstime);
+  abstime.tv_sec += TIMERTICK_INTERVAL;
+
   while (nleft > 0)
     {
       int n;
-      pth_event_t evt;
       gpg_error_t err;
 
-      evt = pth_event (PTH_EVENT_TIME, pth_timeout (1, 0));
-      n = pth_read_ev (ctx->fd, buffer, nleft, evt);
-      if (n < 0 && evt && pth_event_occurred (evt))
-        {
-          n = 0;
-          err = dirmngr_tick (ctx->ctrl);
+      npth_clock_gettime (&curtime);
+      if (!(npth_timercmp (&curtime, &abstime, <)))
+       {
+         err = dirmngr_tick (ctx->ctrl);
           if (err)
             {
               ctx->fd_error = err;
-              SAFE_PTH_CLOSE (ctx->fd);
-              if (evt)
-                pth_event_free (evt, PTH_FREE_THIS);
+              SAFE_CLOSE (ctx->fd);
               return -1;
             }
+         npth_clock_gettime (&abstime);
+         abstime.tv_sec += TIMERTICK_INTERVAL;
+       }
+      npth_timersub (&abstime, &curtime, &timeout);
 
+      read_fdset = fdset;
+      ret = npth_pselect (nfds, &read_fdset, NULL, NULL, &timeout, NULL);
+      saved_errno = errno;
+
+      if (ret == -1 && saved_errno != EINTR)
+       {
+          ctx->fd_error = gpg_error_from_errno (errno);
+          SAFE_CLOSE (ctx->fd);
+          return -1;
         }
-      else if (n < 0)
+      if (ret <= 0)
+       /* Timeout.  Will be handled when calculating the next timeout.  */
+       continue;
+
+      /* This should not block now that select returned with a file
+        descriptor.  So it shouldn't be necessary to use npth_read
+        (and it is slightly dangerous in the sense that a concurrent
+        thread might (accidentially?) change the status of ctx->fd
+        before we read.  FIXME: Set ctx->fd to nonblocking?  */
+      n = read (ctx->fd, buffer, nleft);
+      if (n < 0)
         {
           ctx->fd_error = gpg_error_from_errno (errno);
-          SAFE_PTH_CLOSE (ctx->fd);
-          if (evt)
-            pth_event_free (evt, PTH_FREE_THIS);
+          SAFE_CLOSE (ctx->fd);
           return -1;
         }
       else if (!n)
         {
           if (nleft == count)
-            {
-              if (evt)
-                pth_event_free (evt, PTH_FREE_THIS);
-              return -1; /* EOF. */
-            }
-          break; 
+           return -1; /* EOF. */
+          break;
         }
       nleft -= n;
       buffer += n;
-      if (evt)
-        pth_event_free (evt, PTH_FREE_THIS);
       if (n > 0 && ctx->stamp != (time_t)(-1))
         ctx->stamp = time (NULL);
     }
   *nread = count - nleft;
 
   return 0;
-
 }
 
 /* Fork and exec the LDAP wrapper and returns a new libksba reader
@@ -702,12 +737,6 @@ ldap_wrapper (ctrl_t ctrl, ksba_reader_t *reader, const char *argv[])
   ctx->printable_pid = (int) pid;
   ctx->fd = outpipe[0];
   ctx->log_fd = errpipe[0];
-  ctx->log_ev = pth_event (PTH_EVENT_FD | PTH_UNTIL_FD_READABLE, ctx->log_fd);
-  if (! ctx->log_ev)
-    {
-      xfree (ctx);
-      return gpg_error_from_syserror ();
-    }
   ctx->ctrl = ctrl;
   ctrl->refcount++;
   ctx->stamp = time (NULL);