gpg: First rough implementation of keyboxd access for key lookup.
authorWerner Koch <wk@gnupg.org>
Tue, 10 Sep 2019 14:05:54 +0000 (16:05 +0200)
committerWerner Koch <wk@gnupg.org>
Tue, 10 Sep 2019 14:05:54 +0000 (16:05 +0200)
* g10/Makefile.am: Add nPth flags.
* g10/gpg.c: Include npth.h.
(gpg_deinit_default_ctrl): Deinit call-keyboxd local data.
(main): Init nPth.
* g10/keydb-private.h (struct keydb_handle_s): Add field 'kbl' and
remove the search result and the assuan context.
* g10/call-keyboxd.c (struct keyboxd_local_s): Add more fields.
(lock_datastream, unlock_datastream): New.
(gpg_keyboxd_deinit_session_data): Adjust for changed data structures.
(prepare_data_pipe): New.
(open_context): Return kbl instead of an Assuan context.  Init mutexes
etc.
(close_context): Merge into ...
(keydb_release): here.  Adjust for changed data structures.
(datastream_thread): New.
(keydb_get_keyblock): Implement datastream stuff.
(keydb_search): Ditto.

* common/asshelp.c (wait_for_sock): Add arg connect_flags.
(start_new_service): Set FDPASSING flag for the keyboxd.
--

This code as a lot of rough edges, in particular it relies on a well
behaving keyboxd.  We need to add code to shutdown the datastream
reader thread in case of errors and to properly get it up again.  We
also need to make really sure that both threads run in lockstep so
that the datastream thread is only active while we are sending a
command to the keyboxd.

We should also see whether we can depend nPth initialization on the
--use-keyboxd option to avoid any problems with nPth.

And we need to test on Windows.

Signed-off-by: Werner Koch <wk@gnupg.org>
common/asshelp.c
g10/Makefile.am
g10/call-keyboxd.c
g10/gpg.c
g10/keydb-private.h
g10/keydb.h

index a5724fa..174933a 100644 (file)
@@ -310,14 +310,15 @@ unlock_spawning (lock_spawn_t *lock, const char *name)
 }
 
 
-/* Helper to start a service.
- * SECS gives the number of seconds to wait.  SOCKNAME is the name of
- * the socket to connect.  VERBOSE is the usual verbose flag. CTX is
- * the assuan context.  DID_SUCCESS_MSG will be set to 1 if a success
- * messages has been printed.
+/* Helper to start a service.  SECS gives the number of seconds to
+ * wait.  SOCKNAME is the name of the socket to connect.  VERBOSE is
+ * the usual verbose flag.  CTX is the assuan context.  CONNECT_FLAGS
+ * are the assuan connect flags.  DID_SUCCESS_MSG will be set to 1 if
+ * a success messages has been printed.
  */
 static gpg_error_t
 wait_for_sock (int secs, int module_name_id, const char *sockname,
+               unsigned int connect_flags,
                int verbose, assuan_context_t ctx, int *did_success_msg)
 {
   gpg_error_t err = 0;
@@ -353,7 +354,7 @@ wait_for_sock (int secs, int module_name_id, const char *sockname,
         }
       gnupg_usleep (next_sleep_us);
       elapsed_us += next_sleep_us;
-      err = assuan_socket_connect (ctx, sockname, 0, 0);
+      err = assuan_socket_connect (ctx, sockname, 0, connect_flags);
       if (!err)
         {
           if (verbose)
@@ -403,6 +404,7 @@ start_new_service (assuan_context_t *r_ctx,
   const char *status_start_line;
   int no_service_err;
   int seconds_to_wait;
+  unsigned int connect_flags = 0;
   const char *argv[6];
 
   *r_ctx = NULL;
@@ -439,6 +441,7 @@ start_new_service (assuan_context_t *r_ctx,
       status_start_line = "starting_keyboxd ? 0 0";
       no_service_err = GPG_ERR_NO_KEYBOXD;
       seconds_to_wait = SECS_TO_WAIT_FOR_KEYBOXD;
+      connect_flags |= ASSUAN_SOCKET_CONNECT_FDPASSING;
       break;
     default:
       err = gpg_error (GPG_ERR_INV_ARG);
@@ -446,7 +449,7 @@ start_new_service (assuan_context_t *r_ctx,
       return err;
     }
 
-  err = assuan_socket_connect (ctx, sockname, 0, 0);
+  err = assuan_socket_connect (ctx, sockname, 0, connect_flags);
   if (err && autostart)
     {
       char *abs_homedir;
@@ -522,7 +525,7 @@ start_new_service (assuan_context_t *r_ctx,
       argv[i++] = NULL;
 
       if (!(err = lock_spawning (&lock, gnupg_homedir (), lock_name, verbose))
-          && assuan_socket_connect (ctx, sockname, 0, 0))
+          && assuan_socket_connect (ctx, sockname, 0, connect_flags))
         {
           err = gnupg_spawn_process_detached (program? program : program_name,
                                               argv, NULL);
@@ -532,7 +535,8 @@ start_new_service (assuan_context_t *r_ctx,
                        gpg_strerror (err));
           else
             err = wait_for_sock (seconds_to_wait, module_name_id,
-                                 sockname, verbose, ctx, &did_success_msg);
+                                 sockname, connect_flags,
+                                 verbose, ctx, &did_success_msg);
         }
 
       unlock_spawning (&lock, lock_name);
index 1404014..fd2cd21 100644 (file)
@@ -29,9 +29,9 @@ AM_CPPFLAGS =
 include $(top_srcdir)/am/cmacros.am
 
 AM_CFLAGS = $(SQLITE3_CFLAGS) $(LIBGCRYPT_CFLAGS) \
-            $(LIBASSUAN_CFLAGS) $(GPG_ERROR_CFLAGS)
+            $(LIBASSUAN_CFLAGS) $(NPTH_CFLAGS) $(GPG_ERROR_CFLAGS)
 
-needed_libs = ../kbx/libkeybox.a $(libcommon)
+needed_libs = ../kbx/libkeybox.a $(libcommonpth)
 
 # Because there are no program specific transform macros we need to
 # work around that to allow installing gpg as gpg2.
@@ -178,11 +178,11 @@ gpgv_SOURCES = gpgv.c           \
 LDADD =  $(needed_libs) ../common/libgpgrl.a \
          $(ZLIBS) $(LIBINTL) $(CAPLIBS) $(NETLIBS)
 gpg_LDADD = $(LDADD) $(SQLITE3_LIBS) $(LIBGCRYPT_LIBS) $(LIBREADLINE) \
-             $(LIBASSUAN_LIBS) $(GPG_ERROR_LIBS) \
+             $(LIBASSUAN_LIBS) $(NPTH_LIBS) $(GPG_ERROR_LIBS) \
             $(LIBICONV) $(resource_objs) $(extra_sys_libs)
 gpg_LDFLAGS = $(extra_bin_ldflags)
 gpgv_LDADD = $(LDADD) $(LIBGCRYPT_LIBS) \
-             $(LIBASSUAN_LIBS) $(GPG_ERROR_LIBS) \
+             $(LIBASSUAN_LIBS) $(NPTH_LIBS) $(GPG_ERROR_LIBS) \
              $(LIBICONV) $(resource_objs) $(extra_sys_libs)
 gpgv_LDFLAGS = $(extra_bin_ldflags)
 
@@ -197,17 +197,17 @@ t_rmd160_SOURCES = t-rmd160.c rmd160.c
 t_rmd160_LDADD = $(t_common_ldadd)
 t_keydb_SOURCES = t-keydb.c test-stubs.c $(common_source)
 t_keydb_LDADD = $(LDADD) $(LIBGCRYPT_LIBS) \
-              $(LIBASSUAN_LIBS) $(GPG_ERROR_LIBS) \
+              $(LIBASSUAN_LIBS) $(NPTH_LIBS) $(GPG_ERROR_LIBS) \
              $(LIBICONV) $(t_common_ldadd)
 t_keydb_get_keyblock_SOURCES = t-keydb-get-keyblock.c test-stubs.c \
              $(common_source)
 t_keydb_get_keyblock_LDADD = $(LDADD) $(LIBGCRYPT_LIBS) \
-              $(LIBASSUAN_LIBS) $(GPG_ERROR_LIBS) \
+              $(LIBASSUAN_LIBS) $(NPTH_LIBS) $(GPG_ERROR_LIBS) \
              $(LIBICONV) $(t_common_ldadd)
 t_stutter_SOURCES = t-stutter.c test-stubs.c \
              $(common_source)
 t_stutter_LDADD = $(LDADD) $(LIBGCRYPT_LIBS) \
-             $(LIBASSUAN_LIBS) $(GPG_ERROR_LIBS) \
+             $(LIBASSUAN_LIBS) $(NPTH_LIBS) $(GPG_ERROR_LIBS) \
              $(LIBICONV) $(t_common_ldadd)
 
 
index f05a9e8..97f84c0 100644 (file)
@@ -28,6 +28,7 @@
 #ifdef HAVE_LOCALE_H
 # include <locale.h>
 #endif
+#include <npth.h>
 
 #include "gpg.h"
 #include <assuan.h>
@@ -36,6 +37,8 @@
 #include "options.h"
 #include "../common/i18n.h"
 #include "../common/asshelp.h"
+#include "../common/host2net.h"
+#include "../common/exechelp.h"
 #include "../common/status.h"
 #include "keydb.h"
 
@@ -54,17 +57,63 @@ struct keyboxd_local_s
   /* The active Assuan context. */
   assuan_context_t ctx;
 
+  /* This object is used if fd-passing is used to convey the
+   * keyblocks.  */
+  struct {
+    /* NULL or a stream used to receive data.  */
+    estream_t fp;
+
+    /* Condition variable to sync the datastream with the command.  */
+    npth_mutex_t mutex;
+    npth_cond_t  cond;
+
+    /* The found keyblock or the parsing error.   */
+    kbnode_t found_keyblock;
+    gpg_error_t found_err;
+  } datastream;
+
+  /* I/O buffer with the last search result or NULL.  Used if
+   * D-lines are used to convey the keyblocks. */
+  iobuf_t search_result;
+
   /* This flag set while an operation is running on this context.  */
   unsigned int is_active : 1;
 
   /* This flag is set to record that the standard per session init has
    * been done.  */
   unsigned int per_session_init_done : 1;
+
+  /* Flag indicating that a search reset is required.  */
+  unsigned int need_search_reset : 1;
 };
 
 
+/* Local prototypes.  */
+static void *datastream_thread (void *arg);
+
+
 
 \f
+static void
+lock_datastream (keyboxd_local_t kbl)
+{
+  int rc = npth_mutex_lock (&kbl->datastream.mutex);
+  if (rc)
+    log_fatal ("%s: failed to acquire mutex: %s\n", __func__,
+               gpg_strerror (gpg_error_from_errno (rc)));
+}
+
+
+static void
+unlock_datastream (keyboxd_local_t kbl)
+{
+  int rc = npth_mutex_unlock (&kbl->datastream.mutex);
+  if (rc)
+    log_fatal ("%s: failed to release mutex: %s\n", __func__,
+               gpg_strerror (gpg_error_from_errno (rc)));
+}
+
+
 /* Deinitialize all session resources pertaining to the keyboxd.  */
 void
 gpg_keyboxd_deinit_session_data (ctrl_t ctrl)
@@ -77,7 +126,12 @@ gpg_keyboxd_deinit_session_data (ctrl_t ctrl)
       if (kbl->is_active)
         log_error ("oops: trying to cleanup an active keyboxd context\n");
       else
-        assuan_release (kbl->ctx);
+        {
+          es_fclose (kbl->datastream.fp);
+          kbl->datastream.fp = NULL;
+          assuan_release (kbl->ctx);
+          kbl->ctx = NULL;
+        }
       xfree (kbl);
     }
 }
@@ -165,18 +219,86 @@ create_new_context (ctrl_t ctrl, assuan_context_t *r_ctx)
 }
 
 
+
+/* Setup the pipe used for receiving data from the keyboxd.  Store the
+ * info on KBL.  */
+static gpg_error_t
+prepare_data_pipe (keyboxd_local_t kbl)
+{
+  gpg_error_t err;
+  int rc;
+  int inpipe[2];
+  estream_t infp;
+  npth_t thread;
+  npth_attr_t tattr;
+
+  err = gnupg_create_inbound_pipe (inpipe, &infp, 0);
+  if (err)
+    {
+      log_error ("error creating inbound pipe: %s\n", gpg_strerror (err));
+      return err;  /* That should not happen.  */
+    }
+
+  err = assuan_sendfd (kbl->ctx, INT2FD (inpipe[1]));
+  if (err)
+    {
+      log_error ("sending sending fd %d to keyboxd: %s <%s>\n",
+                 inpipe[1], gpg_strerror (err), gpg_strsource (err));
+      es_fclose (infp);
+      close (inpipe[1]);
+      return 0; /* Server may not support fd-passing.  */
+    }
+
+  err = assuan_transact (kbl->ctx, "OUTPUT FD",
+                         NULL, NULL, NULL, NULL, NULL, NULL);
+  if (err)
+    {
+      log_info ("keyboxd does not accept our fd: %s <%s>\n",
+                gpg_strerror (err), gpg_strsource (err));
+      es_fclose (infp);
+      return 0;
+    }
+
+  kbl->datastream.fp = infp;
+  kbl->datastream.found_keyblock = NULL;
+  kbl->datastream.found_err = 0;
+
+  rc = npth_attr_init (&tattr);
+  if (rc)
+    {
+      err = gpg_error_from_errno (rc);
+      log_error ("error preparing thread for keyboxd: %s\n",gpg_strerror (err));
+      es_fclose (infp);
+      kbl->datastream.fp = NULL;
+      return err;
+    }
+  npth_attr_setdetachstate (&tattr, NPTH_CREATE_DETACHED);
+  rc = npth_create (&thread, &tattr, datastream_thread, kbl);
+  if (rc)
+    {
+      err = gpg_error_from_errno (rc);
+      log_error ("error spawning thread for keyboxd: %s\n", gpg_strerror (err));
+      npth_attr_destroy (&tattr);
+      es_fclose (infp);
+      kbl->datastream.fp = NULL;
+      return err;
+    }
+
+  return 0;
+}
+
+
 /* Get a context for accessing keyboxd.  If no context is available a
- * new one is created and if necessary keyboxd is started.  On success
- * an assuan context is stored at R_CTX.  This context may only be
- * released by means of close_context.  Note that NULL is stored at
- * R_CTX on error.  */
+ * new one is created and if necessary keyboxd is started.  R_KBL
+ * receives a pointer to the local context object.  */
 static gpg_error_t
-open_context (ctrl_t ctrl, assuan_context_t *r_ctx)
+open_context (ctrl_t ctrl, keyboxd_local_t *r_kbl)
 {
   gpg_error_t err;
+  int rc;
   keyboxd_local_t kbl;
 
-  *r_ctx = NULL;
+  *r_kbl = NULL;
   for (;;)
     {
       for (kbl = ctrl->keyboxd_local; kbl && kbl->is_active; kbl = kbl->next)
@@ -189,12 +311,16 @@ open_context (ctrl_t ctrl, assuan_context_t *r_ctx)
           /* But first do the per session init if not yet done.  */
           if (!kbl->per_session_init_done)
             {
+              err = prepare_data_pipe (kbl);
+              if (err)
+                return err;
               kbl->per_session_init_done = 1;
             }
 
           kbl->is_active = 1;
+          kbl->need_search_reset = 1;
 
-          *r_ctx = kbl->ctx;
+          *r_kbl = kbl;
           return 0;
         }
 
@@ -202,43 +328,40 @@ open_context (ctrl_t ctrl, assuan_context_t *r_ctx)
       kbl = xtrycalloc (1, sizeof *kbl);
       if (!kbl)
         return gpg_error_from_syserror ();
+
+      rc = npth_mutex_init (&kbl->datastream.mutex, NULL);
+      if (rc)
+        {
+          err = gpg_error_from_errno (rc);
+          log_error ("error initializing mutex: %s\n", gpg_strerror (err));
+          xfree (kbl);
+          return err;
+        }
+      rc = npth_cond_init (&kbl->datastream.cond, NULL);
+      if (rc)
+        {
+          err = gpg_error_from_errno (rc);
+          log_error ("error initializing condition: %s\n", gpg_strerror (err));
+          npth_mutex_destroy (&kbl->datastream.mutex);
+          xfree (kbl);
+          return err;
+        }
+
       err = create_new_context (ctrl, &kbl->ctx);
       if (err)
         {
+          npth_cond_destroy (&kbl->datastream.cond);
+          npth_mutex_destroy (&kbl->datastream.mutex);
           xfree (kbl);
           return err;
         }
 
-      /* Although we are not yet using nPth in gpg we better prepare
-       * to be nPth thread safe.  Thus we add it to the list and
-       * retry; this is easier than to employ a lock.  */
+      /* For thread-saftey we add it to the list and retry; this is
+       * easier than to employ a lock.  */
       kbl->next = ctrl->keyboxd_local;
       ctrl->keyboxd_local = kbl;
     }
-}
-
-
-/* Close the assuan context CTX and return it to a pool of unused
- * contexts.  If CTX is NULL, the function does nothing.  */
-static void
-close_context (ctrl_t ctrl, assuan_context_t ctx)
-{
-  keyboxd_local_t kbl;
-
-  if (!ctx)
-    return;
-
-  for (kbl = ctrl->keyboxd_local; kbl; kbl = kbl->next)
-    {
-      if (kbl->ctx == ctx)
-        {
-          if (!kbl->is_active)
-            log_fatal ("closing inactive keyboxd context %p\n", ctx);
-          kbl->is_active = 0;
-          return;
-        }
-    }
-  log_fatal ("closing unknown keyboxd ctx %p\n", ctx);
+  /*NOTREACHED*/
 }
 
 
@@ -275,11 +398,8 @@ keydb_new (ctrl_t ctrl)
     }
   hd->use_keyboxd = 1;
   hd->ctrl = ctrl;
-  hd->need_search_reset = 1;
 
-  err = open_context (ctrl, &hd->assuan_context);
-  if (err)
-    goto leave;
+  err = open_context (ctrl, &hd->kbl);
 
  leave:
   if (err)
@@ -300,14 +420,25 @@ keydb_new (ctrl_t ctrl)
 void
 keydb_release (KEYDB_HANDLE hd)
 {
+  keyboxd_local_t kbl;
+
   if (!hd)
     return;
 
+  if (DBG_CLOCK)
+    log_clock ("keydb_release");
   if (!hd->use_keyboxd)
     internal_keydb_deinit (hd);
   else
     {
-      close_context (hd->ctrl, hd->assuan_context);
+      kbl = hd->kbl;
+      if (DBG_CLOCK)
+        log_clock ("close_context (found)");
+      if (!kbl->is_active)
+        log_fatal ("closing inactive keyboxd context %p\n", kbl);
+      kbl->is_active = 0;
+      hd->kbl = NULL;
+      hd->ctrl = NULL;
     }
   xfree (hd);
 }
@@ -465,6 +596,86 @@ keydb_get_keyblock_do_parse (iobuf_t iobuf, int pk_no, int uid_no,
 }
 
 
+/* The thread used to read from the data stream.  This is running as
+ * long as the connection and its datastream exists.  */
+static void *
+datastream_thread (void *arg)
+{
+  keyboxd_local_t kbl = arg;
+  gpg_error_t err;
+  int rc;
+  unsigned char lenbuf[4];
+  size_t nread, datalen;
+  iobuf_t iobuf;
+  int pk_no, uid_no;
+  kbnode_t keyblock, tmpkeyblock;
+
+
+  log_debug ("Datastream_thread started\n");
+  while (kbl->datastream.fp)
+    {
+      /* log_debug ("Datastream_thread waiting ...\n"); */
+      if (es_read (kbl->datastream.fp, lenbuf, 4, &nread))
+        {
+          err = gpg_error_from_syserror ();
+          if (gpg_err_code (err) == GPG_ERR_EAGAIN)
+            continue;
+          log_error ("error reading data length from keyboxd: %s\n",
+                     gpg_strerror (err));
+          gnupg_sleep (1);
+          continue;
+        }
+      if (nread != 4)
+        {
+          err = gpg_error (GPG_ERR_EIO);
+          log_error ("error reading data length from keyboxd: %s\n",
+                     "short read");
+          continue;
+        }
+
+      datalen = buf32_to_size_t (lenbuf);
+      /* log_debug ("keyboxd announced %zu bytes\n", datalen); */
+
+      iobuf = iobuf_esopen (kbl->datastream.fp, "rb", 1, datalen);
+      pk_no = uid_no = 0;  /* FIXME: Get this from the keyboxd.  */
+      err = keydb_get_keyblock_do_parse (iobuf, pk_no, uid_no, &keyblock);
+      iobuf_close (iobuf);
+      if (!err)
+        {
+          /* log_debug ("parsing datastream succeeded\n"); */
+
+          /* Thread-safe assignment to the result var:  */
+          tmpkeyblock = kbl->datastream.found_keyblock;
+          kbl->datastream.found_keyblock = keyblock;
+          release_kbnode (tmpkeyblock);
+      }
+      else
+        {
+          /* log_debug ("parsing datastream failed: %s <%s>\n", */
+          /*            gpg_strerror (err), gpg_strsource (err)); */
+          tmpkeyblock = kbl->datastream.found_keyblock;
+          kbl->datastream.found_keyblock = NULL;
+          kbl->datastream.found_err = err;
+          release_kbnode (tmpkeyblock);
+        }
+
+      /* Tell the main thread.  */
+      lock_datastream (kbl);
+      rc = npth_cond_signal (&kbl->datastream.cond);
+      if (rc)
+        {
+          err = gpg_error_from_errno (rc);
+          log_error ("%s: signaling condition failed: %s\n",
+                     __func__, gpg_strerror (err));
+        }
+      unlock_datastream (kbl);
+    }
+  log_debug ("Datastream_thread finished\n");
+
+  return NULL;
+}
+
+
 /* Return the keyblock last found by keydb_search() in *RET_KB.
  *
  * On success, the function returns 0 and the caller must free *RET_KB
@@ -494,20 +705,28 @@ keydb_get_keyblock (KEYDB_HANDLE hd, kbnode_t *ret_kb)
       goto leave;
     }
 
-  if (!hd->search_result)
+  if (hd->kbl->search_result)
+    {
+      pk_no = uid_no = 0;  /*FIXME: Get this from the keyboxd.  */
+      err = keydb_get_keyblock_do_parse (hd->kbl->search_result,
+                                         pk_no, uid_no, ret_kb);
+      /* In contrast to the old code we close the iobuf here and thus
+       * this function may be called only once to get a keyblock.  */
+      iobuf_close (hd->kbl->search_result);
+      hd->kbl->search_result = NULL;
+    }
+  else if (hd->kbl->datastream.found_keyblock)
+    {
+      *ret_kb = hd->kbl->datastream.found_keyblock;
+      hd->kbl->datastream.found_keyblock = NULL;
+      err = 0;
+    }
+  else
     {
       err = gpg_error (GPG_ERR_VALUE_NOT_FOUND);
       goto leave;
     }
 
-  pk_no = uid_no = 0;  /*FIXME: Get this from the keyboxd.  */
-  err = keydb_get_keyblock_do_parse (hd->search_result, pk_no, uid_no, ret_kb);
-  /* In contrast to the old code we close the iobuf here and thus this
-   * function may be called only once to get a keyblock.  */
-  iobuf_close (hd->search_result);
-  hd->search_result = NULL;
-
-
  leave:
   if (DBG_CLOCK)
     log_clock ("%s leave%s", __func__, err? " (failed)":"");
@@ -636,7 +855,7 @@ keydb_search_reset (KEYDB_HANDLE hd)
 
   /* All we need todo is to tell search that a reset is pending.  Noet
    * that keydb_new sets this flag as well.  */
-  hd->need_search_reset = 1;
+  hd->kbl->need_search_reset = 1;
   err = 0;
 
  leave:
@@ -697,14 +916,20 @@ keydb_search (KEYDB_HANDLE hd, KEYDB_SEARCH_DESC *desc,
       goto leave;
     }
 
-
-  if (hd->search_result)
+  /* Clear the result objects.  */
+  if (hd->kbl->search_result)
     {
-      iobuf_close (hd->search_result);
-      hd->search_result = NULL;
+      iobuf_close (hd->kbl->search_result);
+      hd->kbl->search_result = NULL;
+    }
+  if (hd->kbl->datastream.found_keyblock)
+    {
+      release_kbnode (hd->kbl->datastream.found_keyblock);
+      hd->kbl->datastream.found_keyblock = NULL;
     }
 
-  if (!hd->need_search_reset)
+  /* Check whether this is a NEXT search.  */
+  if (!hd->kbl->need_search_reset)
     {
       /* No reset requested thus continue the search.  The keyboxd
        * keeps the context of the search and thus the NEXT operates on
@@ -717,7 +942,7 @@ keydb_search (KEYDB_HANDLE hd, KEYDB_SEARCH_DESC *desc,
       goto do_search;
     }
 
-  hd->need_search_reset = 0;
+  hd->kbl->need_search_reset = 0;
 
   if (!ndesc)
     {
@@ -807,31 +1032,68 @@ keydb_search (KEYDB_HANDLE hd, KEYDB_SEARCH_DESC *desc,
     }
 
  do_search:
-  {
-    membuf_t data;
-    void *buffer;
-    size_t len;
-
-    init_membuf (&data, 8192);
-    err = assuan_transact (hd->assuan_context, line,
-                           put_membuf_cb, &data,
-                           NULL, NULL,
-                           NULL, NULL);
-    if (err)
-      {
-        xfree (get_membuf (&data, &len));
-        goto leave;
-      }
+  if (hd->kbl->datastream.fp)
+    {
+      /* log_debug ("Sending command '%s'\n", line); */
+      err = assuan_transact (hd->kbl->ctx, line,
+                             NULL, NULL,
+                             NULL, NULL,
+                             NULL, NULL);
+      if (err)
+        {
+          /* log_debug ("Finished command with error: %s\n", gpg_strerror (err)); */
+          /* Fixme: On unexpected errors we need a way to cancek the
+           * data stream.  Probly it will be best to closeand reopen
+           * it.  */
+        }
+      else
+        {
+          int rc;
 
-    buffer = get_membuf (&data, &len);
-    if (!buffer)
-      {
-        err = gpg_error_from_syserror ();
-        goto leave;
-      }
+          /* log_debug ("Finished command .. telling data stream\n"); */
+          lock_datastream (hd->kbl);
+          if (!hd->kbl->datastream.found_keyblock)
+            {
+              /* log_debug ("%s: waiting on datastream_cond ...\n", __func__); */
+              rc = npth_cond_wait (&hd->kbl->datastream.cond,
+                                   &hd->kbl->datastream.mutex);
+              /* log_debug ("%s: waiting on datastream.cond done\n", __func__); */
+              if (rc)
+                {
+                  err = gpg_error_from_errno (rc);
+                  log_error ("%s: waiting on condition failed: %s\n",
+                             __func__, gpg_strerror (err));
+                }
+            }
+          unlock_datastream (hd->kbl);
+        }
+    }
+  else /* Slower D-line version if fd-passing was not successful.  */
+    {
+      membuf_t data;
+      void *buffer;
+      size_t len;
+
+      init_membuf (&data, 8192);
+      err = assuan_transact (hd->kbl->ctx, line,
+                             put_membuf_cb, &data,
+                             NULL, NULL,
+                             NULL, NULL);
+      if (err)
+        {
+          xfree (get_membuf (&data, &len));
+          goto leave;
+        }
+
+      buffer = get_membuf (&data, &len);
+      if (!buffer)
+        {
+          err = gpg_error_from_syserror ();
+          goto leave;
+        }
 
-    /* Fixme: Avoid double allocation.  */
-    hd->search_result = iobuf_temp_with_content (buffer, len);
+      hd->kbl->search_result = iobuf_temp_with_content (buffer, len);
+      xfree (buffer);
   }
 
 
index 145796c..821d395 100644 (file)
--- a/g10/gpg.c
+++ b/g10/gpg.c
@@ -36,6 +36,7 @@
 # endif
 # include <windows.h>
 #endif
+#include <npth.h>
 
 #define INCLUDED_BY_MAIN_MODULE 1
 #include "gpg.h"
@@ -979,6 +980,9 @@ static void add_keyserver_url( const char *string, int which );
 static void emergency_cleanup (void);
 static void read_sessionkey_from_fd (int fd);
 
+/* NPth wrapper function definitions. */
+ASSUAN_SYSTEM_NPTH_IMPL;
+
 
 static char *
 make_libversion (const char *libname, const char *(*getfnc)(const char*))
@@ -2246,6 +2250,7 @@ gpg_deinit_default_ctrl (ctrl_t ctrl)
   gpg_dirmngr_deinit_session_data (ctrl);
 
   keydb_release (ctrl->cached_getkey_kdb);
+  gpg_keyboxd_deinit_session_data (ctrl);
 }
 
 
@@ -3736,6 +3741,11 @@ main (int argc, char **argv)
       }
 #endif
 
+    /* Init threading which is used by some helper functions.  */
+    npth_init ();
+    assuan_set_system_hooks (ASSUAN_SYSTEM_NPTH);
+    gpgrt_set_syscall_clamp (npth_unprotect, npth_protect);
+
     /* FIXME: We should use logging to a file only in server mode;
        however we have not yet implemetyed that.  Thus we try to get
        away with --batch as indication for logging to file
@@ -3743,7 +3753,9 @@ main (int argc, char **argv)
     if (logfile && opt.batch)
       {
         log_set_file (logfile);
-        log_set_prefix (NULL, GPGRT_LOG_WITH_PREFIX | GPGRT_LOG_WITH_TIME | GPGRT_LOG_WITH_PID);
+        log_set_prefix (NULL, (GPGRT_LOG_WITH_PREFIX
+                               | GPGRT_LOG_WITH_TIME
+                               | GPGRT_LOG_WITH_PID ));
       }
 
     if (opt.verbose > 2)
index c5315b9..efef822 100644 (file)
@@ -92,14 +92,9 @@ struct keydb_handle_s
   /* A shallow pointer with the CTRL used to create this handle.  */
   ctrl_t ctrl;
 
-  /* The context used to communicate with the keyboxd.  */
-  assuan_context_t assuan_context;
-
-  /* I/O buffer with the last search result or NULL.  */
-  iobuf_t search_result;
-
-  /* Flag indicating that a search reset is required.  */
-  unsigned int need_search_reset : 1;
+  /* Connection info which also keep the local state.  (This is points
+   * into the CTRL->keybox_local list.) */
+  keyboxd_local_t kbl;
 
   /* END USE_KEYBOXD */
 
index 25304ed..cc6f059 100644 (file)
@@ -165,6 +165,9 @@ is_in_klist (struct key_item *k, PKT_signature *sig)
 
 /*-- call-keyboxd.c --*/
 
+/* Release all open contexts to the keyboxd.  */
+void gpg_keyboxd_deinit_session_data (ctrl_t ctrl);
+
 /* Create a new database handle.  Returns NULL on error, sets ERRNO,
  * and prints an error diagnostic. */
 KEYDB_HANDLE keydb_new (ctrl_t ctrl);