* no-pth.c, Makefile.am: Removed.
authorWerner Koch <wk@gnupg.org>
Thu, 23 May 2002 09:07:45 +0000 (09:07 +0000)
committerWerner Koch <wk@gnupg.org>
Thu, 23 May 2002 09:07:45 +0000 (09:07 +0000)
* call-scd.c: Seirialized all scdaeom access when using Pth.

* cache.c: Made the cache Pth-thread-safe.
(agent_unlock_cache_entry): New.
* findkey.c (unprotect): Unlock the returned cache value.
* command.c (cmd_get_passphrase): Ditto.

* gpg-agent.c (main): Register pth_read/write with Assuan.

12 files changed:
agent/ChangeLog
agent/Makefile.am
agent/agent.h
agent/cache.c
agent/call-scd.c
agent/command.c
agent/findkey.c
agent/gpg-agent.c
agent/query.c
common/ChangeLog
common/Makefile.am
common/no-pth.c [deleted file]

index fb358f4..14fbcc1 100644 (file)
@@ -1,3 +1,25 @@
+2002-05-23  Werner Koch  <wk@gnupg.org>
+
+       * call-scd.c: Seirialized all scdaeom access when using Pth.
+
+       * cache.c: Made the cache Pth-thread-safe.
+       (agent_unlock_cache_entry): New.
+       * findkey.c (unprotect): Unlock the returned cache value.
+       * command.c (cmd_get_passphrase): Ditto.
+
+       * gpg-agent.c (main): Register pth_read/write with Assuan.
+
+2002-05-22  Werner Koch  <wk@gnupg.org>
+
+       * query.c: Serialized all pinentry access when using Pth.
+
+       * gpg-agent.c (handle_signal,start_connection_thread)
+       (handle_connections): New
+       (main): Use the new Pth stuff to allow concurrent connections.
+       * command.c (start_command_handler): Add new arg FD so that the
+       fucntion can also be used for an already connected socket.
+       * Makefile.am: Link with Pth.
+
 2002-05-14  Werner Koch  <wk@gnupg.org>
 
        * cache.c (housekeeping, agent_put_cache): Use our time() wrapper.
index 5716519..6680dcc 100644 (file)
@@ -21,7 +21,7 @@
 bin_PROGRAMS = gpg-agent
 noinst_PROGRAMS = protect-tool
 
-AM_CPPFLAGS = -I$(top_srcdir)/common $(LIBGCRYPT_CFLAGS)
+AM_CPPFLAGS = -I$(top_srcdir)/common $(LIBGCRYPT_CFLAGS) $(PTH_CFLAGS)
 LDFLAGS = @LDFLAGS@ 
 
 gpg_agent_SOURCES = \
@@ -43,7 +43,7 @@ gpg_agent_SOURCES = \
 
 
 gpg_agent_LDADD = ../jnlib/libjnlib.a ../assuan/libassuan.a  \
-               ../common/libcommon.a $(LIBGCRYPT_LIBS)
+               ../common/libcommon.a $(LIBGCRYPT_LIBS) $(PTH_LIBS)
 
 protect_tool_SOURCES = \
        protect-tool.c \
index 4dad518..44b3832 100644 (file)
@@ -104,7 +104,7 @@ void agent_exit (int rc); /* also implemented in other tools */
 const char *trans (const char *text);
 
 /*-- command.c --*/
-void start_command_handler (int);
+void start_command_handler (int, int);
 
 /*-- findkey.c --*/
 int agent_write_private_key (const unsigned char *grip,
@@ -124,8 +124,8 @@ int agent_get_confirmation (const char *desc, const char *ok,
 
 /*-- cache.c --*/
 int agent_put_cache (const char *key, const char *data, int ttl);
-const char *agent_get_cache (const char *key);
-
+const char *agent_get_cache (const char *key, void **cache_id);
+void agent_unlock_cache_entry (void **cache_id);
 
 
 /*-- pksign.c --*/
index ed9c8cf..4b18ad3 100644 (file)
@@ -40,6 +40,7 @@ struct cache_item_s {
   time_t created;
   time_t accessed;
   int  ttl;  /* max. lifetime given in seonds */
+  int lockcount;
   struct secret_data_s *pw;
   char key[1];
 };
@@ -87,7 +88,7 @@ housekeeping (void)
   /* first expire the actual data */
   for (r=thecache; r; r = r->next)
     {
-      if (r->pw && r->accessed + r->ttl < current)
+      if (!r->lockcount && r->pw && r->accessed + r->ttl < current)
         {
           if (DBG_CACHE)
             log_debug ("  expired `%s' (%ds after last access)\n",
@@ -99,10 +100,10 @@ housekeeping (void)
     }
 
   /* second, make sure that we also remove them based on the created stamp so
-     that the used has to enter it from time to time.  We do this every hour */
+     that the user has to enter it from time to time.  We do this every hour */
   for (r=thecache; r; r = r->next)
     {
-      if (r->pw && r->created + 60*60 < current)
+      if (!r->lockcount && r->pw && r->created + 60*60 < current)
         {
           if (DBG_CACHE)
             log_debug ("  expired `%s' (1h after creation)\n", r->key);
@@ -118,15 +119,27 @@ housekeeping (void)
     {
       if (!r->pw && r->accessed + 60*30 < current)
         {
-          ITEM r2 = r->next;
-          if (DBG_CACHE)
-            log_debug ("  removed `%s' (slot not used for 30m)\n", r->key);
-          xfree (r);
-          if (!rprev)
-            thecache = r2;
+          if (r->lockcount)
+            {
+              log_error ("can't remove unused cache entry `%s' due to"
+                         " lockcount=%d\n",
+                         r->key, r->lockcount);
+              r->accessed += 60*10; /* next error message in 10 minutes */
+              rprev = r;
+              r = r->next;
+            }
           else
-            rprev->next = r2;
-          r = r2;
+            {
+              ITEM r2 = r->next;
+              if (DBG_CACHE)
+                log_debug ("  removed `%s' (slot not used for 30m)\n", r->key);
+              xfree (r);
+              if (!rprev)
+                thecache = r2;
+              else
+                rprev->next = r2;
+              r = r2;
+            }
         }
       else
         {
@@ -158,7 +171,7 @@ agent_put_cache (const char *key, const char *data, int ttl)
 
   for (r=thecache; r; r = r->next)
     {
-      if ( !strcmp (r->key, key))
+      if (!r->lockcount && !strcmp (r->key, key))
         break;
     }
   if (r)
@@ -206,34 +219,67 @@ agent_put_cache (const char *key, const char *data, int ttl)
 
 /* Try to find an item in the cache */
 const char *
-agent_get_cache (const char *key)
+agent_get_cache (const char *key, void **cache_id)
 {
   ITEM r;
-  int count = 0;
 
   if (DBG_CACHE)
     log_debug ("agent_get_cache `%s'...\n", key);
   housekeeping ();
 
-  /* FIXME: Returning pointers is not thread safe - add a reference
-     counter */
-  for (r=thecache; r; r = r->next, count++)
+  /* first try to find one with no locks - this is an updated cache
+     entry: We might have entries with a lockcount and without a
+     lockcount. */
+  for (r=thecache; r; r = r->next)
     {
-      if (r->pw && !strcmp (r->key, key))
+      if (!r->lockcount && r->pw && !strcmp (r->key, key))
         {
           /* put_cache does only put strings into the cache, so we
              don't need the lengths */
           r->accessed = gnupg_get_time ();
           if (DBG_CACHE)
             log_debug ("... hit\n");
+          r->lockcount++;
+          *cache_id = r;
+          return r->pw->data;
+        }
+    }
+  /* again, but this time get even one with a lockcount set */
+  for (r=thecache; r; r = r->next)
+    {
+      if (r->pw && !strcmp (r->key, key))
+        {
+          r->accessed = gnupg_get_time ();
+          if (DBG_CACHE)
+            log_debug ("... hit (locked)\n");
+          r->lockcount++;
+          *cache_id = r;
           return r->pw->data;
         }
     }
   if (DBG_CACHE)
     log_debug ("... miss\n");
 
+  *cache_id = NULL;
   return NULL;
 }
 
 
+void
+agent_unlock_cache_entry (void **cache_id)
+{
+  ITEM r;
 
+  for (r=thecache; r; r = r->next)
+    {
+      if (r == *cache_id)
+        {
+          if (!r->lockcount)
+            log_error ("trying to unlock non-locked cache entry `%s'\n",
+                       r->key);
+          else
+            r->lockcount--;
+          return;
+        }
+    }
+}
index f618912..8b79e81 100644 (file)
  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
  */
 
+/* Fixme: For now we have serialized all access to the scdaemon which
+   make sense becuase the scdaemon can't handle concurrent connections
+   right now.  We should however keep a list of connections and lock
+   just that connection - it migth make sense to implemtn parts of
+   this in Assuan.*/
+
 #include <config.h>
 #include <errno.h>
 #include <stdio.h>
@@ -27,6 +33,9 @@
 #include <assert.h>
 #include <unistd.h>
 #include <sys/stat.h>
+#ifdef USE_GNU_PTH
+# include <pth.h>
+#endif
 
 #include "agent.h"
 #include "../assuan/assuan.h"
@@ -38,6 +47,9 @@
 #endif
 
 static ASSUAN_CONTEXT scd_ctx = NULL;
+#ifdef USE_GNU_PTH
+static pth_mutex_t scd_lock = PTH_MUTEX_INIT;
+#endif
 
 /* callback parameter for learn card */
 struct learn_parm_s {
@@ -60,7 +72,7 @@ struct membuf {
 
 
 \f
-/* A simple implemnation of a dynamic buffer.  Use init_membuf() to
+/* A simple implementation of a dynamic buffer.  Use init_membuf() to
    create a buffer, put_membuf to append bytes and get_membuf to
    release and return the buffer.  Allocation errors are detected but
    only returned at the final get_membuf(), this helps not to clutter
@@ -122,6 +134,20 @@ get_membuf (struct membuf *mb, size_t *len)
 
 
 \f
+static int 
+unlock_scd (int rc)
+{
+#ifdef USE_GNU_PTH
+  if (!pth_mutex_release (&scd_lock))
+    {
+      log_error ("failed to release the SCD lock\n");
+      if (!rc)
+        rc = GNUPG_Internal_Error;
+    }
+#endif
+  return rc;
+}
+
 /* Fork off the SCdaemon if this has not already been done */
 static int
 start_scd (void)
@@ -131,6 +157,14 @@ start_scd (void)
   ASSUAN_CONTEXT ctx;
   const char *argv[3];
 
+#ifdef USE_GNU_PTH
+  if (!pth_mutex_acquire (&scd_lock, 0, NULL))
+    {
+      log_error ("failed to acquire the SCD lock\n");
+      return GNUPG_Internal_Error;
+    }
+#endif
+
   if (scd_ctx)
     return 0; /* No need to serialize things because the agent is
                  expected to tun as a single-thread (or may be in
@@ -142,7 +176,7 @@ start_scd (void)
   if (fflush (NULL))
     {
       log_error ("error flushing pending output: %s\n", strerror (errno));
-      return seterr (Write_Error);
+      return unlock_scd (seterr (Write_Error));
     }
 
   /* FIXME: change the default location of the program */
@@ -163,7 +197,7 @@ start_scd (void)
     {
       log_error ("can't connect to the SCdaemon: %s\n",
                  assuan_strerror (rc));
-      return seterr (No_Scdaemon);
+      return unlock_scd (seterr (No_Scdaemon));
     }
   scd_ctx = ctx;
   
@@ -218,9 +252,9 @@ agent_card_learn (void (*kpinfo_cb)(void*, const char *), void *kpinfo_cb_arg)
                         NULL, NULL, NULL, NULL,
                         learn_status_cb, &parm);
   if (rc)
-    return map_assuan_err (rc);
+    return unlock_scd (map_assuan_err (rc));
 
-  return 0;
+  return unlock_scd (0);
 }
 
 
@@ -274,7 +308,7 @@ agent_card_serialno (char **r_serialno)
      this is really SCdaemon's duty */
   rc = assuan_transact (scd_ctx, "RESET", NULL, NULL, NULL, NULL, NULL, NULL);
   if (rc)
-    return map_assuan_err (rc);
+    return unlock_scd (map_assuan_err (rc));
 
   rc = assuan_transact (scd_ctx, "SERIALNO",
                         NULL, NULL, NULL, NULL,
@@ -282,10 +316,10 @@ agent_card_serialno (char **r_serialno)
   if (rc)
     {
       xfree (serialno);
-      return map_assuan_err (rc);
+      return unlock_scd (map_assuan_err (rc));
     }
   *r_serialno = serialno;
-  return 0;
+  return unlock_scd (0);
 }
 
 \f
@@ -354,7 +388,7 @@ agent_card_pksign (const char *keyid,
     return rc;
 
   if (indatalen*2 + 50 > DIM(line))
-    return seterr (General_Error);
+    return unlock_scd (seterr (General_Error));
 
   sprintf (line, "SETDATA ");
   p = line + strlen (line);
@@ -362,7 +396,7 @@ agent_card_pksign (const char *keyid,
     sprintf (p, "%02X", indata[i]);
   rc = assuan_transact (scd_ctx, line, NULL, NULL, NULL, NULL, NULL, NULL);
   if (rc)
-    return map_assuan_err (rc);
+    return unlock_scd (map_assuan_err (rc));
 
   init_membuf (&data, 1024);
   inqparm.ctx = scd_ctx;
@@ -377,7 +411,7 @@ agent_card_pksign (const char *keyid,
   if (rc)
     {
       xfree (get_membuf (&data, &len));
-      return map_assuan_err (rc);
+      return unlock_scd (map_assuan_err (rc));
     }
   sigbuf = get_membuf (&data, &sigbuflen);
 
@@ -388,7 +422,7 @@ agent_card_pksign (const char *keyid,
   if (!*r_buf)
     {
       xfree (*r_buf);
-      return GNUPG_Out_Of_Core;
+      return unlock_scd (GNUPG_Out_Of_Core);
     }
   p = stpcpy (*r_buf, "(7:sig-val(3:rsa(1:s" );
   sprintf (p, "%u:", (unsigned int)sigbuflen);
@@ -399,7 +433,7 @@ agent_card_pksign (const char *keyid,
   xfree (sigbuf);
 
   assert (gcry_sexp_canon_len (*r_buf, *r_buflen, NULL, NULL));
-  return 0;
+  return unlock_scd (0);
 }
 
 /* Decipher INDATA using the current card. Note that the returned value is */
@@ -423,7 +457,7 @@ agent_card_pkdecrypt (const char *keyid,
 
   /* FIXME: use secure memory where appropriate */
   if (indatalen*2 + 50 > DIM(line))
-    return seterr (General_Error);
+    return unlock_scd (seterr (General_Error));
 
   sprintf (line, "SETDATA ");
   p = line + strlen (line);
@@ -431,7 +465,7 @@ agent_card_pkdecrypt (const char *keyid,
     sprintf (p, "%02X", indata[i]);
   rc = assuan_transact (scd_ctx, line, NULL, NULL, NULL, NULL, NULL, NULL);
   if (rc)
-    return map_assuan_err (rc);
+    return unlock_scd (map_assuan_err (rc));
 
   init_membuf (&data, 1024);
   inqparm.ctx = scd_ctx;
@@ -446,13 +480,13 @@ agent_card_pkdecrypt (const char *keyid,
   if (rc)
     {
       xfree (get_membuf (&data, &len));
-      return map_assuan_err (rc);
+      return unlock_scd (map_assuan_err (rc));
     }
   *r_buf = get_membuf (&data, r_buflen);
   if (!*r_buf)
-    return GNUPG_Out_Of_Core;
+    return unlock_scd (GNUPG_Out_Of_Core);
 
-  return 0;
+  return unlock_scd (0);
 }
 
 
@@ -481,13 +515,13 @@ agent_card_readcert (const char *id, char **r_buf, size_t *r_buflen)
   if (rc)
     {
       xfree (get_membuf (&data, &len));
-      return map_assuan_err (rc);
+      return unlock_scd (map_assuan_err (rc));
     }
   *r_buf = get_membuf (&data, r_buflen);
   if (!*r_buf)
-    return GNUPG_Out_Of_Core;
+    return unlock_scd (GNUPG_Out_Of_Core);
 
-  return 0;
+  return unlock_scd (0);
 }
 
 
@@ -517,19 +551,19 @@ agent_card_readkey (const char *id, unsigned char **r_buf)
   if (rc)
     {
       xfree (get_membuf (&data, &len));
-      return map_assuan_err (rc);
+      return unlock_scd (map_assuan_err (rc));
     }
   *r_buf = get_membuf (&data, &buflen);
   if (!*r_buf)
-    return GNUPG_Out_Of_Core;
+    return unlock_scd (GNUPG_Out_Of_Core);
 
   if (!gcry_sexp_canon_len (*r_buf, buflen, NULL, NULL))
     {
       xfree (*r_buf); *r_buf = NULL;
-      return GNUPG_Invalid_Value;
+      return unlock_scd (GNUPG_Invalid_Value);
     }
 
-  return 0;
+  return unlock_scd (0);
 }
 
 
index e6f34fd..387eef4 100644 (file)
@@ -370,6 +370,7 @@ cmd_get_passphrase (ASSUAN_CONTEXT ctx, char *line)
   char *response;
   char *cacheid = NULL, *desc = NULL, *prompt = NULL, *errtext = NULL;
   char *p;
+  void *cache_marker;
 
   /* parse the stuff */
   for (p=line; *p == ' '; p++)
@@ -417,17 +418,18 @@ cmd_get_passphrase (ASSUAN_CONTEXT ctx, char *line)
     desc = NULL;
 
   /* Note: we store the hexified versions in the cache. */
-  pw = cacheid ? agent_get_cache (cacheid) : NULL;
+  pw = cacheid ? agent_get_cache (cacheid, &cache_marker) : NULL;
   if (pw)
     {
       assuan_begin_confidential (ctx);
       rc = assuan_set_okay_line (ctx, pw);
+      agent_unlock_cache_entry (&cache_marker);
     }
   else
     {
-      /* Note, that we only need to repalce the + characters and
-         should leave the other escaping in place becuase the escaped
-         sting is send verbatim to the pinentry which does the
+      /* Note, that we only need to replace the + characters and
+         should leave the other escaping in place because the escaped
+         string is send verbatim to the pinentry which does the
          unescaping (but not the + replacing) */
       if (errtext)
         plus_to_blank (errtext);
@@ -593,19 +595,18 @@ register_commands (ASSUAN_CONTEXT ctx)
 }
 
 
-/* Startup the server.  If LISTEN_FD is given as -1, this is a simple
+/* Startup the server.  If LISTEN_FD and FD is given as -1, this is a simple
    piper server, otherwise it is a regular server */
 void
-start_command_handler (int listen_fd)
+start_command_handler (int listen_fd, int fd)
 {
   int rc;
   ASSUAN_CONTEXT ctx;
   struct server_control_s ctrl;
 
   memset (&ctrl, 0, sizeof ctrl);
-
   
-  if (listen_fd == -1)
+  if (listen_fd == -1 && fd == -1)
     {
       int filedes[2];
 
@@ -613,10 +614,14 @@ start_command_handler (int listen_fd)
       filedes[1] = 1;
       rc = assuan_init_pipe_server (&ctx, filedes);
     }
-  else
+  else if (listen_fd != -1)
     {
       rc = assuan_init_socket_server (&ctx, listen_fd);
     }
+  else 
+    {
+      rc = assuan_init_connected_socket_server (&ctx, fd);
+    }
   if (rc)
     {
       log_error ("failed to initialize the server: %s\n",
@@ -664,5 +669,3 @@ start_command_handler (int listen_fd)
   assuan_deinit_server (ctx);
 }
 
-
-
index 662a56f..1a222ba 100644 (file)
@@ -106,10 +106,12 @@ unprotect (unsigned char **keybuf, const unsigned char *grip)
   /* first try to get it from the cache - if there is none or we can't
      unprotect it, we fall back to ask the user */
   {
-    const char *pw = agent_get_cache (hexgrip);
+    void *cache_marker;
+    const char *pw = agent_get_cache (hexgrip, &cache_marker);
     if (pw)
       {
         rc = agent_unprotect (*keybuf, pw, &result, &resultlen);
+        agent_unlock_cache_entry (&cache_marker);
         if (!rc)
           {
             xfree (*keybuf);
index c39c8c4..ff1deec 100644 (file)
@@ -33,6 +33,9 @@
 #include <sys/un.h>
 #include <unistd.h>
 #include <signal.h>
+#ifdef USE_GNU_PTH
+# include <pth.h>
+#endif
 
 #include <gcrypt.h>
 
@@ -113,7 +116,13 @@ static ARGPARSE_OPTS opts[] = {
 };
 
 
+#ifndef USE_GNU_PTH
 static volatile int caught_fatal_sig = 0;
+#endif /*!USE_GNU_PTH*/
+
+/* flag to indicate that a shutdown was requested */
+static int shutdown_pending;
+
 
 /* It is possible that we are currently running under setuid permissions */
 static int maybe_setuid = 1;
@@ -122,6 +131,11 @@ static int maybe_setuid = 1;
 static char socket_name[128];
 
 
+#ifdef USE_GNU_PTH
+static void handle_connections (int listen_fd);
+#endif
+
+
 
 static const char *
 my_strusage (int level)
@@ -205,6 +219,7 @@ cleanup (void)
 }
 
 
+#ifndef USE_GNU_PTH
 static RETSIGTYPE
 cleanup_sh (int sig)
 {
@@ -226,6 +241,7 @@ cleanup_sh (int sig)
 #endif
   raise( sig );
 }
+#endif /*!USE_GNU_PTH*/
 
 int
 main (int argc, char **argv )
@@ -265,6 +281,9 @@ main (int argc, char **argv )
     }
 
   assuan_set_malloc_hooks (gcry_malloc, gcry_realloc, gcry_free);
+#ifdef USE_GNU_PTH
+  assuan_set_io_func (pth_read, pth_write);
+#endif
   gcry_set_log_handler (my_gcry_logger, NULL);
   gcry_control (GCRYCTL_USE_SECURE_RNDPOOL);
 
@@ -447,7 +466,7 @@ main (int argc, char **argv )
 
   if (pipe_server)
     { /* this is the simple pipe based server */
-      start_command_handler (-1);
+      start_command_handler (-1, -1);
     }
   else
     { /* regular server mode */
@@ -574,6 +593,7 @@ main (int argc, char **argv )
           /*NEVER REACHED*/
         } /* end parent */
       
+
       /* this is the child */
 
       /* detach from tty and put process into a new session */
@@ -592,6 +612,22 @@ main (int argc, char **argv )
             }
         }
 
+      if (chdir("/"))
+        {
+          log_error ("chdir to / failed: %s\n", strerror (errno));
+          exit (1);
+        }
+
+
+#ifdef USE_GNU_PTH
+      if (!pth_init ())
+        {
+          log_error ("failed to initialize the Pth library\n");
+          exit (1);
+        }
+      signal (SIGPIPE, SIG_IGN);
+      handle_connections (fd);
+#else /*!USE_GNU_PTH*/
       /* setup signals */
       {
         struct sigaction oact, nact;
@@ -610,15 +646,8 @@ main (int argc, char **argv )
         sigaction (SIGPIPE, &nact, NULL);
         sigaction (SIGINT, &nact, NULL);
       }
-
-      if (chdir("/"))
-        {
-          log_error ("chdir to / failed: %s\n", strerror (errno));
-          exit (1);
-        }
-
-      start_command_handler (fd);
-
+      start_command_handler (fd, -1);
+#endif /*!USE_GNU_PTH*/
       close (fd);
     }
   
@@ -628,10 +657,7 @@ main (int argc, char **argv )
 void
 agent_exit (int rc)
 {
-  #if 0
-#warning no update_random_seed_file
-  update_random_seed_file();
-  #endif
+  /*FIXME: update_random_seed_file();*/
 #if 0
   /* at this time a bit annoying */
   if (opt.debug & DBG_MEMSTAT_VALUE)
@@ -647,3 +673,144 @@ agent_exit (int rc)
   exit (rc);
 }
 
+
+static void
+reread_configuration (void)
+{
+  /* FIXME: Move parts of the option parsing to here. */
+}
+
+
+#ifdef USE_GNU_PTH
+static void
+handle_signal (int signo)
+{
+  switch (signo)
+    {
+    case SIGHUP:
+      log_info ("SIGHUP received - re-reading configuration\n");
+      reread_configuration ();
+      break;
+      
+    case SIGUSR1:
+      if (opt.verbose < 5)
+        opt.verbose++;
+      log_info ("SIGUSR1 received - verbosity set to %d\n", opt.verbose);
+      break;
+
+    case SIGUSR2:
+      if (opt.verbose)
+        opt.verbose--;
+      log_info ("SIGUSR2 received - verbosity set to %d\n", opt.verbose );
+      break;
+
+    case SIGTERM:
+      if (!shutdown_pending)
+        log_info ("SIGTERM received - shutting down ...\n");
+      else
+        log_info ("SIGTERM received - still %ld running threads\n",
+                  pth_ctrl( PTH_CTRL_GETTHREADS ));
+      shutdown_pending++;
+      if (shutdown_pending > 2)
+        {
+          log_info ("shutdown forced\n");
+          log_info ("%s %s stopped\n", strusage(11), strusage(13) );
+          cleanup ();
+          agent_exit (0);
+       }
+       break;
+        
+    case SIGINT:
+      log_info ("SIGINT received - immediate shutdown\n");
+      log_info( "%s %s stopped\n", strusage(11), strusage(13));
+      cleanup ();
+      agent_exit (0);
+      break;
+
+    default:
+      log_info ("signal %d received - no action defined\n", signo);
+    }
+}
+
+
+static void *
+start_connection_thread (void *arg)
+{
+  int fd = (int)arg;
+
+  if (opt.verbose)
+    log_info ("handler for fd %d started\n", fd);
+  start_command_handler (-1, fd);
+  if (opt.verbose)
+    log_info ("handler for fd %d terminated\n", fd);
+  
+  return NULL;
+}
+
+
+static void
+handle_connections (int listen_fd)
+{
+  pth_attr_t tattr;
+  pth_event_t ev;
+  sigset_t sigs;
+  int signo;
+  struct sockaddr_un paddr;
+  socklen_t plen = sizeof( paddr );
+  int fd;
+
+  tattr = pth_attr_new();
+  pth_attr_set (tattr, PTH_ATTR_JOINABLE, 0);
+  pth_attr_set (tattr, PTH_ATTR_STACK_SIZE, 32*1024);
+  pth_attr_set (tattr, PTH_ATTR_NAME, "gpg-agent");
+
+  sigemptyset (&sigs );
+  sigaddset (&sigs, SIGHUP);
+  sigaddset (&sigs, SIGUSR1);
+  sigaddset (&sigs, SIGUSR2);
+  sigaddset (&sigs, SIGINT);
+  sigaddset (&sigs, SIGTERM);
+  ev = pth_event (PTH_EVENT_SIGS, &sigs, &signo);
+
+  for (;;)
+    {
+      if (shutdown_pending)
+        {
+          if (pth_ctrl (PTH_CTRL_GETTHREADS) == 1)
+            break; /* ready */
+
+          /* Do not accept anymore connections and wait for existing
+             connections to terminate */
+          signo = 0;
+          pth_wait (ev);
+          if (pth_event_occurred (ev) && signo)
+            handle_signal (signo);
+          continue;
+       }
+
+      fd = pth_accept_ev (listen_fd, (struct sockaddr *)&paddr, &plen, ev);
+      if (fd == -1)
+        {
+          if (pth_event_occurred (ev))
+            {
+              handle_signal (signo);
+              continue;
+           }
+          log_error ("accept failed: %s - waiting 1s\n", strerror (errno));
+          pth_sleep(1);
+          continue;
+       }
+
+      if (!pth_spawn (tattr, start_connection_thread, (void*)fd))
+        {
+          log_error ("error spawning connection handler: %s\n",
+                     strerror (errno) );
+          close (fd);
+       }
+    }
+
+  pth_event_free (ev, PTH_FREE_ALL);
+  cleanup ();
+  log_info ("%s %s stopped\n", strusage(11), strusage(13));
+}
+#endif /*USE_GNU_PTH*/
index 7a20b14..af513b8 100644 (file)
@@ -27,6 +27,9 @@
 #include <assert.h>
 #include <unistd.h>
 #include <sys/stat.h>
+#ifdef USE_GNU_PTH
+# include <pth.h>
+#endif
 
 #include "agent.h"
 #include "../assuan/assuan.h"
@@ -38,6 +41,9 @@
 #endif
 
 static ASSUAN_CONTEXT entry_ctx = NULL;
+#ifdef USE_GNU_PTH
+static pth_mutex_t entry_lock = PTH_MUTEX_INIT;
+#endif
 
 /* data to be passed to our callbacks */
 struct entry_parm_s {
@@ -49,7 +55,24 @@ struct entry_parm_s {
 
 
 \f
-/* Fork off the pin entry if this has not already been done */
+static int 
+unlock_pinentry (int rc)
+{
+#ifdef USE_GNU_PTH
+  if (!pth_mutex_release (&entry_lock))
+    {
+      log_error ("failed to release the entry lock\n");
+      if (!rc)
+        rc = GNUPG_Internal_Error;
+    }
+#endif
+  return rc;
+}
+
+/* Fork off the pin entry if this has not already been done.  Note,
+   that this function must always be used to aquire the lock for the
+   pinentry - we will serialize _all_ pinentry calls.
+ */
 static int
 start_pinentry (void)
 {
@@ -58,10 +81,16 @@ start_pinentry (void)
   ASSUAN_CONTEXT ctx;
   const char *argv[5];
 
+#ifdef USE_GNU_PTH
+  if (!pth_mutex_acquire (&entry_lock, 0, NULL))
+    {
+      log_error ("failed to acquire the entry lock\n");
+      return GNUPG_Internal_Error;
+    }
+#endif
+
   if (entry_ctx)
-    return 0; /* No need to serialize things becuase the agent is
-                 expected to tun as a single-thread (or may be in
-                 future using libpth) */
+    return 0; 
 
   if (opt.verbose)
     log_info ("no running PIN Entry - starting it\n");
@@ -69,7 +98,7 @@ start_pinentry (void)
   if (fflush (NULL))
     {
       log_error ("error flushing pending output: %s\n", strerror (errno));
-      return seterr (Write_Error);
+      return unlock_pinentry (seterr (Write_Error));
     }
 
   /* FIXME: change the default location of the program */
@@ -80,6 +109,7 @@ start_pinentry (void)
   else
     pgmname++;
 
+  /* FIXME: We must do this thread specific */
   argv[0] = pgmname;
   if (opt.display)
     {
@@ -96,7 +126,7 @@ start_pinentry (void)
     {
       log_error ("can't connect to the PIN entry module: %s\n",
                  assuan_strerror (rc));
-      return seterr (No_PIN_Entry);
+      return unlock_pinentry (seterr (No_PIN_Entry));
     }
   entry_ctx = ctx;
 
@@ -107,47 +137,47 @@ start_pinentry (void)
                         opt.no_grab? "OPTION no-grab":"OPTION grab",
                         NULL, NULL, NULL, NULL, NULL, NULL);
   if (rc)
-    return map_assuan_err (rc);
+    return unlock_pinentry (map_assuan_err (rc));
   if (opt.ttyname)
     {
       char *optstr;
       if (asprintf (&optstr, "OPTION ttyname=%s", opt.ttyname) < 0 )
-       return GNUPG_Out_Of_Core;
+       return unlock_pinentry (GNUPG_Out_Of_Core);
       rc = assuan_transact (entry_ctx, optstr, NULL, NULL, NULL, NULL, NULL,
                            NULL);
       free (optstr);
       if (rc)
-       return map_assuan_err (rc);
+       return unlock_pinentry (map_assuan_err (rc));
     }
   if (opt.ttytype)
     {
       char *optstr;
       if (asprintf (&optstr, "OPTION ttytype=%s", opt.ttytype) < 0 )
-       return GNUPG_Out_Of_Core;
+       return unlock_pinentry (GNUPG_Out_Of_Core);
       rc = assuan_transact (entry_ctx, optstr, NULL, NULL, NULL, NULL, NULL,
                            NULL);
       if (rc)
-       return map_assuan_err (rc);
+       return unlock_pinentry (map_assuan_err (rc));
     }
   if (opt.lc_ctype)
     {
       char *optstr;
       if (asprintf (&optstr, "OPTION lc-ctype=%s", opt.lc_ctype) < 0 )
-       return GNUPG_Out_Of_Core;
+       return unlock_pinentry (GNUPG_Out_Of_Core);
       rc = assuan_transact (entry_ctx, optstr, NULL, NULL, NULL, NULL, NULL,
                            NULL);
       if (rc)
-       return map_assuan_err (rc);
+       return unlock_pinentry (map_assuan_err (rc));
     }
   if (opt.lc_messages)
     {
       char *optstr;
       if (asprintf (&optstr, "OPTION lc-messages=%s", opt.lc_messages) < 0 )
-       return GNUPG_Out_Of_Core;
+       return unlock_pinentry (GNUPG_Out_Of_Core);
       rc = assuan_transact (entry_ctx, optstr, NULL, NULL, NULL, NULL, NULL,
                            NULL);
       if (rc)
-       return map_assuan_err (rc);
+       return unlock_pinentry (map_assuan_err (rc));
     }
   return 0;
 }
@@ -213,14 +243,14 @@ agent_askpin (const char *desc_text, const char *start_err_text,
   line[DIM(line)-1] = 0;
   rc = assuan_transact (entry_ctx, line, NULL, NULL, NULL, NULL, NULL, NULL);
   if (rc)
-    return map_assuan_err (rc);
+    return unlock_pinentry (map_assuan_err (rc));
 
   rc = assuan_transact (entry_ctx,
                         pininfo->min_digits? "SETPROMPT PIN:"
                                            : "SETPROMPT Passphrase:",
                         NULL, NULL, NULL, NULL, NULL, NULL);
   if (rc)
-    return map_assuan_err (rc);
+    return unlock_pinentry (map_assuan_err (rc));
 
   for (;pininfo->failed_tries < pininfo->max_tries; pininfo->failed_tries++)
     {
@@ -242,7 +272,7 @@ agent_askpin (const char *desc_text, const char *start_err_text,
           line[DIM(line)-1] = 0;
           rc = assuan_transact (entry_ctx, line, NULL, NULL, NULL, NULL, NULL, NULL);
           if (rc)
-            return map_assuan_err (rc);
+            return unlock_pinentry (map_assuan_err (rc));
           errtext = NULL;
         }
       
@@ -251,9 +281,9 @@ agent_askpin (const char *desc_text, const char *start_err_text,
         errtext = pininfo->min_digits? trans ("PIN too long")
                                      : trans ("Passphrase too long");
       else if (rc)
-        return map_assuan_err (rc);
+        return unlock_pinentry (map_assuan_err (rc));
       if (!errtext && !pininfo->min_digits)
-        return 0; /* okay, got a passphrase */
+        return unlock_pinentry (0); /* okay, got a passphrase */
       if (!errtext && !all_digitsp (pininfo->pin))
         errtext = trans ("Invalid characters in PIN");
       if (!errtext && pininfo->max_digits
@@ -264,10 +294,11 @@ agent_askpin (const char *desc_text, const char *start_err_text,
         errtext = trans ("PIN too short");
 
       if (!errtext)
-        return 0; /* okay, got a PIN */
+        return unlock_pinentry (0); /* okay, got a PIN */
     }
 
-  return pininfo->min_digits? GNUPG_Bad_PIN : GNUPG_Bad_Passphrase;
+  return unlock_pinentry (pininfo->min_digits? GNUPG_Bad_PIN
+                          : GNUPG_Bad_Passphrase);
 }
 
 
@@ -301,13 +332,13 @@ agent_get_passphrase (char **retpass, const char *desc, const char *prompt,
   line[DIM(line)-1] = 0;
   rc = assuan_transact (entry_ctx, line, NULL, NULL, NULL, NULL, NULL, NULL);
   if (rc)
-    return map_assuan_err (rc);
+    return unlock_pinentry (map_assuan_err (rc));
 
   snprintf (line, DIM(line)-1, "SETPROMPT %s", prompt? prompt : "Passphrase");
   line[DIM(line)-1] = 0;
   rc = assuan_transact (entry_ctx, line, NULL, NULL, NULL, NULL, NULL, NULL);
   if (rc)
-    return map_assuan_err (rc);
+    return unlock_pinentry (map_assuan_err (rc));
 
   if (errtext)
     {
@@ -315,28 +346,28 @@ agent_get_passphrase (char **retpass, const char *desc, const char *prompt,
       line[DIM(line)-1] = 0;
       rc = assuan_transact (entry_ctx, line, NULL, NULL, NULL, NULL, NULL, NULL);
       if (rc)
-        return map_assuan_err (rc);
+        return unlock_pinentry (map_assuan_err (rc));
     }
 
   memset (&parm, 0, sizeof parm);
   parm.size = ASSUAN_LINELENGTH/2 - 5;
   parm.buffer = gcry_malloc_secure (parm.size+10);
   if (!parm.buffer)
-    return seterr (Out_Of_Core);
+    return unlock_pinentry (seterr (Out_Of_Core));
 
   assuan_begin_confidential (entry_ctx);
   rc = assuan_transact (entry_ctx, "GETPIN", getpin_cb, &parm, NULL, NULL, NULL, NULL);
   if (rc)
     {
       xfree (parm.buffer);
-      return map_assuan_err (rc);
+      return unlock_pinentry (map_assuan_err (rc));
     }
   
   hexstring = gcry_malloc_secure (strlen (parm.buffer)*2+1);
   if (!hexstring)
     {
       xfree (parm.buffer);
-      return seterr (Out_Of_Core);
+      return unlock_pinentry (seterr (Out_Of_Core));
     }
 
   for (i=0, p=parm.buffer; *p; p++, i += 2)
@@ -344,7 +375,7 @@ agent_get_passphrase (char **retpass, const char *desc, const char *prompt,
   
   xfree (parm.buffer);
   *retpass = hexstring;
-  return 0;
+  return unlock_pinentry (0);
 }
 
 
@@ -370,7 +401,7 @@ agent_get_confirmation (const char *desc, const char *ok, const char *cancel)
   line[DIM(line)-1] = 0;
   rc = assuan_transact (entry_ctx, line, NULL, NULL, NULL, NULL, NULL, NULL);
   if (rc)
-    return map_assuan_err (rc);
+    return unlock_pinentry (map_assuan_err (rc));
 
   if (ok)
     {
@@ -378,7 +409,7 @@ agent_get_confirmation (const char *desc, const char *ok, const char *cancel)
       line[DIM(line)-1] = 0;
       rc = assuan_transact (entry_ctx, line, NULL, NULL, NULL, NULL, NULL, NULL);
       if (rc)
-        return map_assuan_err (rc);
+        return unlock_pinentry (map_assuan_err (rc));
     }
   if (cancel)
     {
@@ -386,11 +417,11 @@ agent_get_confirmation (const char *desc, const char *ok, const char *cancel)
       line[DIM(line)-1] = 0;
       rc = assuan_transact (entry_ctx, line, NULL, NULL, NULL, NULL, NULL, NULL);
       if (rc)
-        return map_assuan_err (rc);
+        return unlock_pinentry (map_assuan_err (rc));
     }
 
   rc = assuan_transact (entry_ctx, "CONFIRM", NULL, NULL, NULL, NULL, NULL, NULL);
-  return map_assuan_err (rc);
+  return unlock_pinentry (map_assuan_err (rc));
 }
 
 
index 7e273d8..c9c435c 100644 (file)
@@ -1,3 +1,7 @@
+2002-05-23  Werner Koch  <wk@gnupg.org>
+
+       * no-pth.c, Makefile.am: Removed.
+
 2002-05-22  Werner Koch  <wk@gnupg.org>
 
        * mkdtemp.c: Replaced byte by unsigned char because it is no longer
index 85dcfe2..d118e04 100644 (file)
@@ -32,7 +32,6 @@ libcommon_a_SOURCES = \
        errors.c errors.h \
        maperror.c \
        sysutils.c sysutils.h \
-       no-pth.c \
        cryptmiss.c \
        gettime.c 
 
diff --git a/common/no-pth.c b/common/no-pth.c
deleted file mode 100644 (file)
index 5eaaa3f..0000000
+++ /dev/null
@@ -1,54 +0,0 @@
-/* no-pth.c - stubs to avoid linking against PTH
- * Copyright (C) 2002 Free Software Foundation, Inc.
- *
- * This file is part of GnuPG.
- *
- * GnuPG is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * GnuPG is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * 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, write to the Free Software
- * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
- */
-
-#include <config.h>
-#ifdef USE_GNU_PTH /*we need the stubs only in this case */
-
-#include <stdio.h>
-#include <stdlib.h>
-#include <unistd.h>
-#include <pth.h>
-
-#if PTH_SYSCALL_SOFT
-# error this file cannot be used with PTH syscall divertion 
-#endif
-
-
-ssize_t  
-pth_read (int a, void *b , size_t c)
-{
-  return read (a, b, c);
-}
-
-ssize_t
-pth_write (int a, const void *b, size_t c)
-{
-  return write (a, b, c);
-}
-
-int
-pth_accept (int a, struct sockaddr *b, socklen_t *c) 
-{
-  return accept (a, b, c);
-}
-
-
-
-#endif /*USE_GNU_PTH*/