libdns: Hack to skip negation term.
[gnupg.git] / dirmngr / ldap-wrapper.c
index fa5bf3c..b313848 100644 (file)
@@ -15,7 +15,7 @@
  * GNU General Public License for more details.
  *
  * You should have received a copy of the GNU General Public License
- * along with this program; if not, see <http://www.gnu.org/licenses/>.
+ * along with this program; if not, see <https://www.gnu.org/licenses/>.
  */
 
 /*
@@ -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);
 }
@@ -163,7 +160,7 @@ destroy_wrapper (struct wrapper_context_s *ctx)
 
 /* Print the content of LINE to thye log stream but make sure to only
    print complete lines.  Using NULL for LINE will flush any pending
-   output.  LINE may be modified by this fucntion. */
+   output.  LINE may be modified by this function. */
 static void
 print_log_line (struct wrapper_context_s *ctx, char *line)
 {
@@ -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,20 +225,18 @@ 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. */
     {
       if (n < 0)
         log_error (_("error reading log from ldap wrapper %d: %s\n"),
-                   ctx->pid, strerror (errno));
+                   (int)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,62 @@ 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;
+  fd_set fdset;
+  int ret;
+  time_t exptime;
 
   (void)dummy;
 
+  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)
+      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.  */
+         npth_clock_gettime (&abstime);
+         abstime.tv_sec += TIMERTICK_INTERVAL;
        }
+      npth_timersub (&abstime, &curtime, &timeout);
 
+      FD_ZERO (&fdset);
+      nfds = -1;
       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);
+              FD_SET (ctx->log_fd, &fdset);
+              if (ctx->log_fd > nfds)
+                nfds = ctx->log_fd;
             }
         }
+      nfds++;
 
-      /* 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);
+      /* 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, &fdset, NULL, NULL, &timeout, NULL);
+      if (ret == -1)
+       {
+          if (errno != EINTR)
+            {
+              log_error (_("npth_select failed: %s - waiting 1s\n"),
+                         strerror (errno));
+              npth_sleep (1);
+            }
+          continue;
+       }
 
-      current_time = time (NULL);
-      if (current_time > INACTIVITY_TIMEOUT)
-        current_time -= INACTIVITY_TIMEOUT;
+      /* 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,11 +321,13 @@ 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, &fdset))
             {
               if (read_log_data (ctx))
-                any_action = 1;
+                {
+                  SAFE_CLOSE (ctx->log_fd);
+                  any_action = 1;
+                }
             }
 
           /* Check whether the process is still running.  */
@@ -334,7 +335,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 +369,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 +377,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 +385,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 +403,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 +427,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 +459,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 +474,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 +519,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
@@ -533,8 +545,8 @@ reader_callback (void *cb_value, char *buffer, size_t count,  size_t *nread)
   if (!buffer && !count && !nread)
     return -1; /* Rewind is not supported. */
 
-  /* If we ever encountered a read error don't allow to continue and
-     possible overwrite the last error cause.  Bail out also if the
+  /* If we ever encountered a read error, don't continue (we don't want to
+     possibly overwrite the last error cause).  Bail out also if the
      file descriptor has been closed. */
   if (ctx->fd_error || ctx->fd == -1)
     {
@@ -542,60 +554,76 @@ 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
+/* Fork and exec the LDAP wrapper and return a new libksba reader
    object at READER.  ARGV is a NULL terminated list of arguments for
    the wrapper.  The function returns 0 on success or an error code.
 
@@ -626,8 +654,9 @@ ldap_wrapper (ctrl_t ctrl, ksba_reader_t *reader, const char *argv[])
      only viable solutions are either to have another thread
      responsible for logging the messages or to add an option to the
      wrapper module to do the logging on its own.  Given that we anyway
-     need a way to rip the child process and this is best done using a
-     general ripping thread, that thread can do the logging too. */
+     need a way to reap the child process and this is best done using a
+     general reaping thread, that thread can do the logging too. */
+  ldap_wrapper_launch_thread ();
 
   *reader = NULL;
 
@@ -667,10 +696,10 @@ ldap_wrapper (ctrl_t ctrl, ksba_reader_t *reader, const char *argv[])
       return err;
     }
 
-  err = gnupg_create_inbound_pipe (outpipe);
+  err = gnupg_create_inbound_pipe (outpipe, NULL, 0);
   if (!err)
     {
-      err = gnupg_create_inbound_pipe (errpipe);
+      err = gnupg_create_inbound_pipe (errpipe, NULL, 0);
       if (err)
         {
           close (outpipe[0]);
@@ -702,12 +731,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);