Enhanced last patch.
[gnupg.git] / agent / gpg-agent.c
index 675f2be..6cb0840 100644 (file)
@@ -1,11 +1,12 @@
 /* gpg-agent.c  -  The GnuPG Agent
- *     Copyright (C) 2000, 2001, 2002, 2003 Free Software Foundation, Inc.
+ * Copyright (C) 2000, 2001, 2002, 2003, 2004, 2005,
+ *               2006, 2007, 2009 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
+ * the Free Software Foundation; either version 3 of the License, or
  * (at your option) any later version.
  *
  * GnuPG is distributed in the hope that it will be useful,
@@ -14,8 +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, write to the Free Software
- * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
+ * along with this program; if not, see <http://www.gnu.org/licenses/>.
  */
 
 #include <config.h>
 #include <assert.h>
 #include <time.h>
 #include <fcntl.h>
-#include <sys/socket.h>
-#include <sys/un.h>
+#include <sys/stat.h>
+#ifndef HAVE_W32_SYSTEM
+# include <sys/socket.h>
+# include <sys/un.h>
+#endif /*!HAVE_W32_SYSTEM*/
 #include <unistd.h>
 #include <signal.h>
-#ifdef USE_GNU_PTH
-# include <pth.h>
-#endif
+#include <pth.h>
 
 #define JNLIB_NEED_LOG_LOGV
 #include "agent.h"
-#include <assuan.h> /* malloc hooks */
+#include <assuan.h> /* Malloc hooks  and socket wrappers. */
 
 #include "i18n.h"
+#include "mkdtemp.h" /* Gnulib replacement. */
 #include "sysutils.h"
+#include "setenv.h"
+#include "gc-opt-flags.h"
 
 
 enum cmd_and_opt_values 
@@ -51,11 +55,14 @@ enum cmd_and_opt_values
   oQuiet         = 'q',
   oSh            = 's',
   oVerbose       = 'v',
-  
+
   oNoVerbose = 500,
+  aGPGConfList,
+  aGPGConfTest,
   oOptions,
   oDebug,
   oDebugAll,
+  oDebugLevel,
   oDebugWait,
   oNoGreeting,
   oNoOptions,
@@ -68,24 +75,44 @@ enum cmd_and_opt_values
   oBatch,
 
   oPinentryProgram,
+  oPinentryTouchFile,
   oDisplay,
   oTTYname,
   oTTYtype,
   oLCctype,
   oLCmessages,
+  oXauthority,
   oScdaemonProgram,
   oDefCacheTTL,
-  oDisablePth,
+  oDefCacheTTLSSH,
+  oMaxCacheTTL,
+  oMaxCacheTTLSSH,
+  oEnforcePassphraseConstraints,
+  oMinPassphraseLen,
+  oMinPassphraseNonalpha,
+  oCheckPassphrasePattern,
+  oMaxPassphraseDays,
+  oEnablePassphraseHistory,
+  oUseStandardSocket,
+  oNoUseStandardSocket,
+  oFakedSystemTime,
 
   oIgnoreCacheForSigning,
+  oAllowMarkTrusted,
+  oAllowPresetPassphrase,
   oKeepTTY,
   oKeepDISPLAY,
-
-aTest };
+  oSSHSupport,
+  oDisableScdaemon,
+  oWriteEnvFile
+};
 
 
 
 static ARGPARSE_OPTS opts[] = {
+
+  { aGPGConfList, "gpgconf-list", 256, "@" },
+  { aGPGConfTest, "gpgconf-test", 256, "@" },
   
   { 301, NULL, 0, N_("@Options:\n ") },
 
@@ -95,45 +122,104 @@ static ARGPARSE_OPTS opts[] = {
   { oQuiet,    "quiet",     0, N_("be somewhat more quiet") },
   { oSh,       "sh",        0, N_("sh-style command output") },
   { oCsh,      "csh",       0, N_("csh-style command output") },
-  { oOptions, "options"  , 2, N_("read options from file")},
-  { oDebug,    "debug"     ,4|16, N_("set debugging flags")},
-  { oDebugAll, "debug-all" ,0, N_("enable full debugging")},
+  { oOptions, "options"  , 2, N_("|FILE|read options from FILE")},
+  { oDebug,    "debug"     ,4|16, "@"},
+  { oDebugAll, "debug-all"     ,0, "@"},
+  { oDebugLevel, "debug-level" ,2, "@"},
   { oDebugWait,"debug-wait",1, "@"},
   { oNoDetach, "no-detach" ,0, N_("do not detach from the console")},
   { oNoGrab, "no-grab"     ,0, N_("do not grab keyboard and mouse")},
   { oLogFile, "log-file"   ,2, N_("use a log file for the server")},
-  { oDisablePth, "disable-pth", 0, N_("do not allow multiple connections")},
-
-  { oPinentryProgram, "pinentry-program", 2 , "path to PIN Entry program" },
-  { oDisplay,    "display",     2, "set the display" },
-  { oTTYname,    "ttyname",     2, "set the tty terminal node name" },
-  { oTTYtype,    "ttytype",     2, "set the tty terminal type" },
-  { oLCctype,    "lc-ctype",    2, "set the tty LC_CTYPE value" },
-  { oLCmessages, "lc-messages", 2, "set the tty LC_MESSAGES value" },
+  { oUseStandardSocket, "use-standard-socket", 0,
+                      N_("use a standard location for the socket")},
+  { oNoUseStandardSocket, "no-use-standard-socket", 0, "@"},
+  { oPinentryProgram, "pinentry-program", 2 ,
+                               N_("|PGM|use PGM as the PIN-Entry program") },
+  { oPinentryTouchFile, "pinentry-touch-file", 2 , "@" },
+  { oScdaemonProgram, "scdaemon-program", 2 ,
+                               N_("|PGM|use PGM as the SCdaemon program") },
+  { oDisableScdaemon, "disable-scdaemon", 0, N_("do not use the SCdaemon") },
+  { oFakedSystemTime, "faked-system-time", 2, "@" }, /* (epoch time) */
+
+  { oBatch,      "batch",       0, "@" },
+  { oHomedir,    "homedir",     2, "@"},   
+
+  { oDisplay,    "display",     2, "@" },
+  { oTTYname,    "ttyname",     2, "@" },
+  { oTTYtype,    "ttytype",     2, "@" },
+  { oLCctype,    "lc-ctype",    2, "@" },
+  { oLCmessages, "lc-messages", 2, "@" },
+  { oXauthority, "xauthority",  2, "@" },
+  { oKeepTTY,    "keep-tty",    0,  N_("ignore requests to change the TTY")},
+  { oKeepDISPLAY, "keep-display",
+                          0, N_("ignore requests to change the X display")},
 
-  { oScdaemonProgram, "scdaemon-program", 2 , "path to SCdaemon program" },
   { oDefCacheTTL, "default-cache-ttl", 4,
-                                 "|N|expire cached PINs after N seconds"},
+                               N_("|N|expire cached PINs after N seconds")},
+  { oDefCacheTTLSSH, "default-cache-ttl-ssh", 4, "@" },
+  { oMaxCacheTTL, "max-cache-ttl", 4, "@" },
+  { oMaxCacheTTLSSH, "max-cache-ttl-ssh", 4, "@" },
+
+  { oEnforcePassphraseConstraints, "enforce-passphrase-constraints", 0, "@"},
+  { oMinPassphraseLen, "min-passphrase-len", 4, "@" },
+  { oMinPassphraseNonalpha, "min-passphrase-nonalpha", 4, "@" },
+  { oCheckPassphrasePattern, "check-passphrase-pattern", 2, "@" },
+  { oMaxPassphraseDays, "max-passphrase-days", 4, "@" },
+  { oEnablePassphraseHistory, "enable-passphrase-history", 0, "@" },
+
   { oIgnoreCacheForSigning, "ignore-cache-for-signing", 0,
-                                 "do not use the PIN cache when signing"},
-  { oKeepTTY, "keep-tty", 0,  N_("ignore requests to change the TTY")},
-  { oKeepDISPLAY, "keep-display",
-                          0, N_("ignore requests to change the X display")},
+                               N_("do not use the PIN cache when signing")},
+  { oAllowMarkTrusted, "allow-mark-trusted", 0,
+                             N_("allow clients to mark keys as \"trusted\"")},
+  { oAllowPresetPassphrase, "allow-preset-passphrase", 0,
+                             N_("allow presetting passphrase")},
+  { oSSHSupport, "enable-ssh-support", 0, N_("enable ssh-agent emulation") },
+  { oWriteEnvFile, "write-env-file", 2|8,
+            N_("|FILE|write environment settings also to FILE")},
   {0}
 };
 
 
-static volatile int caught_fatal_sig = 0;
+#define DEFAULT_CACHE_TTL     (10*60)  /* 10 minutes */
+#define DEFAULT_CACHE_TTL_SSH (30*60)  /* 30 minutes */
+#define MAX_CACHE_TTL         (120*60) /* 2 hours */
+#define MAX_CACHE_TTL_SSH     (120*60) /* 2 hours */
+#define MIN_PASSPHRASE_LEN    (8)      
+#define MIN_PASSPHRASE_NONALPHA (1)      
+#define MAX_PASSPHRASE_DAYS   (0)
+
+/* The timer tick used for housekeeping stuff.  For Windows we use a
+   longer period as the SetWaitableTimer seems to signal earlier than
+   the 2 seconds.  */
+#ifdef HAVE_W32_SYSTEM
+#define TIMERTICK_INTERVAL    (4)
+#else
+#define TIMERTICK_INTERVAL    (2)    /* Seconds.  */
+#endif
 
-/* flag to indicate that a shutdown was requested */
+/* Flag to indicate that a shutdown was requested.  */
 static int shutdown_pending;
 
+/* Counter for the currently running own socket checks.  */
+static int check_own_socket_running;
+
+/* True if we are listening on the standard socket.  */
+static int use_standard_socket;
 
 /* It is possible that we are currently running under setuid permissions */
 static int maybe_setuid = 1;
 
-/* Name of the communication socket */
-static char socket_name[128];
+/* Name of the communication socket used for native gpg-agent requests.  */
+static char *socket_name;
+
+/* Name of the communication socket used for ssh-agent-emulation.  */
+static char *socket_name_ssh;
+
+/* We need to keep track of the server's nonces (these are dummies for
+   POSIX systems). */
+static assuan_sock_nonce_t socket_nonce;
+static assuan_sock_nonce_t socket_nonce_ssh;
+
 
 /* Default values for options passed to the pinentry. */
 static char *default_display;
@@ -141,23 +227,86 @@ static char *default_ttyname;
 static char *default_ttytype;
 static char *default_lc_ctype;
 static char *default_lc_messages;
+static char *default_xauthority;
 
 /* Name of a config file, which will be reread on a HUP if it is not NULL. */
 static char *config_filename;
 
+/* Helper to implement --debug-level */
+static const char *debug_level;
+
+/* Keep track of the current log file so that we can avoid updating
+   the log file after a SIGHUP if it didn't changed. Malloced. */
+static char *current_logfile;
+
+/* The handle_tick() function may test whether a parent is still
+   running.  We record the PID of the parent here or -1 if it should be
+   watched. */
+static pid_t parent_pid = (pid_t)(-1);
 
-/* Local prototypes. */
+\f
+/*
+   Local prototypes. 
+ */
+
+static char *create_socket_name (char *standard_name, char *template);
+static gnupg_fd_t create_server_socket (char *name, int is_ssh, 
+                                        assuan_sock_nonce_t *nonce);
 static void create_directories (void);
-#ifdef USE_GNU_PTH
-static void handle_connections (int listen_fd);
+
+static void agent_init_default_ctrl (ctrl_t ctrl);
+static void agent_deinit_default_ctrl (ctrl_t ctrl);
+
+static void handle_connections (gnupg_fd_t listen_fd,
+                                gnupg_fd_t listen_fd_ssh);
+static void check_own_socket (void);
+static int check_for_running_agent (int silent, int mode);
+
+/* Pth wrapper function definitions. */
+GCRY_THREAD_OPTION_PTH_IMPL;
+static int fixed_gcry_pth_init (void)
+{
+  return pth_self ()? 0 : (pth_init () == FALSE) ? errno : 0;
+}
+
+
+#ifndef PTH_HAVE_PTH_THREAD_ID
+static unsigned long pth_thread_id (void)
+{
+  return (unsigned long)pth_self ();
+}
 #endif
 
 
+\f
+/*
+   Functions. 
+ */
+
+static char *
+make_libversion (const char *libname, const char *(*getfnc)(const char*))
+{
+  const char *s;
+  char *result;
+  
+  if (maybe_setuid)
+    {
+      gcry_control (GCRYCTL_INIT_SECMEM, 0, 0);  /* Drop setuid. */
+      maybe_setuid = 0;
+    }
+  s = getfnc (NULL);
+  result = xmalloc (strlen (libname) + 1 + strlen (s) + 1);
+  strcpy (stpcpy (stpcpy (result, libname), " "), s);
+  return result;
+}
+
 
 static const char *
 my_strusage (int level)
 {
+  static char *ver_gcry;
   const char *p;
+
   switch (level)
     {
     case 11: p = "gpg-agent (GnuPG)";
@@ -166,6 +315,12 @@ my_strusage (int level)
     case 17: p = PRINTABLE_OS_NAME; break;
     case 19: p = _("Please report bugs to <" PACKAGE_BUGREPORT ">.\n");
       break;
+    case 20:
+      if (!ver_gcry)
+        ver_gcry = make_libversion ("libgcrypt", gcry_check_version);
+      p = ver_gcry;
+      break;
+
     case 1:
     case 40: p =  _("Usage: gpg-agent [options] (-h for help)");
       break;
@@ -180,91 +335,84 @@ my_strusage (int level)
 
 
 
+/* Setup the debugging.  With the global variable DEBUG_LEVEL set to NULL
+   only the active debug flags are propagated to the subsystems.  With
+   DEBUG_LEVEL set, a specific set of debug flags is set; thus overriding
+   all flags already set. Note that we don't fail here, because it is
+   important to keep gpg-agent running even after re-reading the
+   options due to a SIGHUP. */
 static void
-i18n_init (void)
-{
-#ifdef USE_SIMPLE_GETTEXT
-    set_gettext_file( PACKAGE );
-#else
-#ifdef ENABLE_NLS
-    setlocale (LC_ALL, "");
-    bindtextdomain (PACKAGE, LOCALEDIR);
-    textdomain (PACKAGE);
-#endif
-#endif
-}
-
-
-
-/* Used by gcry for logging */
-static void
-my_gcry_logger (void *dummy, int level, const char *fmt, va_list arg_ptr)
+set_debug (void)
 {
-  /* translate the log levels */
-  switch (level)
+  if (!debug_level)
+    ;
+  else if (!strcmp (debug_level, "none"))
+    opt.debug = 0;
+  else if (!strcmp (debug_level, "basic"))
+    opt.debug = DBG_ASSUAN_VALUE;
+  else if (!strcmp (debug_level, "advanced"))
+    opt.debug = DBG_ASSUAN_VALUE|DBG_COMMAND_VALUE;
+  else if (!strcmp (debug_level, "expert"))
+    opt.debug = (DBG_ASSUAN_VALUE|DBG_COMMAND_VALUE
+                 |DBG_CACHE_VALUE);
+  else if (!strcmp (debug_level, "guru"))
+    opt.debug = ~0;
+  else
     {
-    case GCRY_LOG_CONT: level = JNLIB_LOG_CONT; break;
-    case GCRY_LOG_INFO: level = JNLIB_LOG_INFO; break;
-    case GCRY_LOG_WARN: level = JNLIB_LOG_WARN; break;
-    case GCRY_LOG_ERROR:level = JNLIB_LOG_ERROR; break;
-    case GCRY_LOG_FATAL:level = JNLIB_LOG_FATAL; break;
-    case GCRY_LOG_BUG:  level = JNLIB_LOG_BUG; break;
-    case GCRY_LOG_DEBUG:level = JNLIB_LOG_DEBUG; break;
-    default:            level = JNLIB_LOG_ERROR; break;  
+      log_error (_("invalid debug-level `%s' given\n"), debug_level);
+      opt.debug = 0; /* Reset debugging, so that prior debug
+                        statements won't have an undesired effect. */
     }
-  log_logv (level, fmt, arg_ptr);
-}
 
+  if (opt.debug && !opt.verbose)
+    opt.verbose = 1;
+  if (opt.debug && opt.quiet)
+    opt.quiet = 0;
+
+  if (opt.debug & DBG_MPI_VALUE)
+    gcry_control (GCRYCTL_SET_DEBUG_FLAGS, 2);
+  if (opt.debug & DBG_CRYPTO_VALUE )
+    gcry_control (GCRYCTL_SET_DEBUG_FLAGS, 1);
+  gcry_control (GCRYCTL_SET_VERBOSITY, (int)opt.verbose);
+}
 
+/* Helper for cleanup to remove one socket with NAME.  */
 static void
-cleanup (void)
+remove_socket (char *name)
 {
-  if (*socket_name)
+  if (name && *name)
     {
       char *p;
 
-      remove (socket_name);
-      p = strrchr (socket_name, '/');
+      remove (name);
+      p = strrchr (name, '/');
       if (p)
-        {
-          *p = 0;
-          rmdir (socket_name);
-          *p = '/';
-        }
-      *socket_name = 0;
+       {
+         *p = 0;
+         rmdir (name);
+         *p = '/';
+       }
+      *name = 0;
     }
-}
-
+}  
 
-static RETSIGTYPE
-cleanup_sh (int sig)
+static void
+cleanup (void)
 {
-  if (caught_fatal_sig)
-    raise (sig);
-  caught_fatal_sig = 1;
-
-  /* gcry_control( GCRYCTL_TERM_SECMEM );*/
-  cleanup ();
-
-#ifndef HAVE_DOSISH_SYSTEM
-  {    /* reset action to default action and raise signal again */
-    struct sigaction nact;
-    nact.sa_handler = SIG_DFL;
-    sigemptyset( &nact.sa_mask );
-    nact.sa_flags = 0;
-    sigaction( sig, &nact, NULL);
-  }
-#endif
-  raise( sig );
+  remove_socket (socket_name);
+  remove_socket (socket_name_ssh);
 }
 
 
+
 /* Handle options which are allowed to be reset after program start.
    Return true when the current option in PARGS could be handled and
    false if not.  As a special feature, passing a value of NULL for
-   PARGS, resets the options to the default. */
+   PARGS, resets the options to the default.  REREAD should be set
+   true if it is not the initial option parsing. */
 static int
-parse_rereadable_options (ARGPARSE_ARGS *pargs)
+parse_rereadable_options (ARGPARSE_ARGS *pargs, int reread)
 {
   if (!pargs)
     { /* reset mode */
@@ -273,9 +421,21 @@ parse_rereadable_options (ARGPARSE_ARGS *pargs)
       opt.debug = 0;
       opt.no_grab = 0;
       opt.pinentry_program = NULL;
+      opt.pinentry_touch_file = NULL;
       opt.scdaemon_program = NULL;
-      opt.def_cache_ttl = 10*60; /* default to 10 minutes */
+      opt.def_cache_ttl = DEFAULT_CACHE_TTL;
+      opt.def_cache_ttl_ssh = DEFAULT_CACHE_TTL_SSH;
+      opt.max_cache_ttl = MAX_CACHE_TTL;
+      opt.max_cache_ttl_ssh = MAX_CACHE_TTL_SSH;
+      opt.enforce_passphrase_constraints = 0;
+      opt.min_passphrase_len = MIN_PASSPHRASE_LEN;
+      opt.min_passphrase_nonalpha = MIN_PASSPHRASE_NONALPHA;
+      opt.check_passphrase_pattern = NULL;
+      opt.max_passphrase_days = MAX_PASSPHRASE_DAYS;
+      opt.enable_passhrase_history = 0;
       opt.ignore_cache_for_signing = 0;
+      opt.allow_mark_trusted = 0;
+      opt.disable_scdaemon = 0;
       return 1;
     }
 
@@ -286,23 +446,64 @@ parse_rereadable_options (ARGPARSE_ARGS *pargs)
 
     case oDebug: opt.debug |= pargs->r.ret_ulong; break;
     case oDebugAll: opt.debug = ~0; break;
+    case oDebugLevel: debug_level = pargs->r.ret_str; break;
+
+    case oLogFile:
+      if (!reread)
+        return 0; /* not handeld */
+      if (!current_logfile || !pargs->r.ret_str
+          || strcmp (current_logfile, pargs->r.ret_str))
+        {
+          log_set_file (pargs->r.ret_str);
+          xfree (current_logfile);
+          current_logfile = xtrystrdup (pargs->r.ret_str);
+        }
+      break;
 
     case oNoGrab: opt.no_grab = 1; break;
       
     case oPinentryProgram: opt.pinentry_program = pargs->r.ret_str; break;
+    case oPinentryTouchFile: opt.pinentry_touch_file = pargs->r.ret_str; break;
     case oScdaemonProgram: opt.scdaemon_program = pargs->r.ret_str; break;
+    case oDisableScdaemon: opt.disable_scdaemon = 1; break;
 
     case oDefCacheTTL: opt.def_cache_ttl = pargs->r.ret_ulong; break;
+    case oDefCacheTTLSSH: opt.def_cache_ttl_ssh = pargs->r.ret_ulong; break;
+    case oMaxCacheTTL: opt.max_cache_ttl = pargs->r.ret_ulong; break;
+    case oMaxCacheTTLSSH: opt.max_cache_ttl_ssh = pargs->r.ret_ulong; break;
       
+    case oEnforcePassphraseConstraints: 
+      opt.enforce_passphrase_constraints=1;
+      break;
+    case oMinPassphraseLen: opt.min_passphrase_len = pargs->r.ret_ulong; break;
+    case oMinPassphraseNonalpha: 
+      opt.min_passphrase_nonalpha = pargs->r.ret_ulong;
+      break;
+    case oCheckPassphrasePattern:
+      opt.check_passphrase_pattern = pargs->r.ret_str;
+      break;
+    case oMaxPassphraseDays:
+      opt.max_passphrase_days = pargs->r.ret_ulong; 
+      break;
+    case oEnablePassphraseHistory:
+      opt.enable_passhrase_history = 1;
+      break;
+
     case oIgnoreCacheForSigning: opt.ignore_cache_for_signing = 1; break;
 
+    case oAllowMarkTrusted: opt.allow_mark_trusted = 1; break;
+
+    case oAllowPresetPassphrase: opt.allow_preset_passphrase = 1; break;
+
     default:
       return 0; /* not handled */
     }
+
   return 1; /* handled */
 }
 
 
+/* The main entry point.  */
 int
 main (int argc, char **argv )
 {
@@ -324,55 +525,90 @@ main (int argc, char **argv )
   int csh_style = 0;
   char *logfile = NULL;
   int debug_wait = 0;
-  int disable_pth = 0;
+  int gpgconf_list = 0;
+  gpg_error_t err;
+  const char *env_file_name = NULL;
+
 
   set_strusage (my_strusage);
   gcry_control (GCRYCTL_SUSPEND_SECMEM_WARN);
   /* Please note that we may running SUID(ROOT), so be very CAREFUL
      when adding any stuff between here and the call to INIT_SECMEM()
      somewhere after the option parsing */
-  log_set_prefix ("gpg-agent", 1|4); 
+  log_set_prefix ("gpg-agent", JNLIB_LOG_WITH_PREFIX|JNLIB_LOG_WITH_PID); 
+
+  /* Make sure that our subsystems are ready.  */
   i18n_init ();
+  init_common_subsystems ();
 
-  /* We need to initialize Pth before libgcrypt, because the libgcrypt
-     initialization done by gcry_check_version internally sets up its
-     mutex system.  Note that one must not link against pth if
-     USE_GNU_PTH is not defined. */
-#ifdef USE_GNU_PTH
-  if (!pth_init ())
+
+  /* Libgcrypt requires us to register the threading model first.
+     Note that this will also do the pth_init. */
+  gcry_threads_pth.init = fixed_gcry_pth_init;
+  err = gcry_control (GCRYCTL_SET_THREAD_CBS, &gcry_threads_pth);
+  if (err)
     {
-      log_error ("failed to initialize the Pth library\n");
-      exit (1);
+      log_fatal ("can't register GNU Pth with Libgcrypt: %s\n",
+                 gpg_strerror (err));
     }
-#endif /*USE_GNU_PTH*/
 
-  /* check that the libraries are suitable.  Do it here because
-     the option parsing may need services of the library */
+
+  /* Check that the libraries are suitable.  Do it here because
+     the option parsing may need services of the library. */
   if (!gcry_check_version (NEED_LIBGCRYPT_VERSION) )
     {
-      log_fatal( _("libgcrypt is too old (need %s, have %s)\n"),
+      log_fatal( _("%s is too old (need %s, have %s)\n"), "libgcrypt",
                  NEED_LIBGCRYPT_VERSION, gcry_check_version (NULL) );
     }
 
   assuan_set_malloc_hooks (gcry_malloc, gcry_realloc, gcry_free);
+  assuan_set_assuan_log_stream (log_get_stream ());
+  assuan_set_assuan_log_prefix (log_get_prefix (NULL));
+  assuan_set_assuan_err_source (GPG_ERR_SOURCE_DEFAULT);
 
-  gcry_set_log_handler (my_gcry_logger, NULL);
+  setup_libgcrypt_logging ();
   gcry_control (GCRYCTL_USE_SECURE_RNDPOOL);
 
   may_coredump = disable_core_dumps ();
 
-  parse_rereadable_options (NULL); /* Reset them to default values. */
-
+  /* Set default options.  */
+  parse_rereadable_options (NULL, 0); /* Reset them to default values. */
+#ifdef HAVE_W32_SYSTEM
+  use_standard_socket = 1;  /* Under Windows we always use a standard
+                               socket.  */
+#endif
+  
   shell = getenv ("SHELL");
   if (shell && strlen (shell) >= 3 && !strcmp (shell+strlen (shell)-3, "csh") )
     csh_style = 1;
-  
-  opt.homedir = getenv("GNUPGHOME");
-  if (!opt.homedir || !*opt.homedir)
-    opt.homedir = GNUPG_DEFAULT_HOMEDIR;
 
-
-  /* check whether we have a config file on the commandline */
+  opt.homedir = default_homedir ();
+
+  /* Record some of the original environment strings. */
+  opt.startup_display = getenv ("DISPLAY");
+  if (opt.startup_display)
+    opt.startup_display = xstrdup (opt.startup_display);
+  opt.startup_ttyname = ttyname (0);
+  if (opt.startup_ttyname)
+    opt.startup_ttyname = xstrdup (opt.startup_ttyname);
+  opt.startup_ttytype = getenv ("TERM");
+  if (opt.startup_ttytype)
+    opt.startup_ttytype = xstrdup (opt.startup_ttytype);
+  /* Fixme: Better use the locale function here.  */
+  opt.startup_lc_ctype = getenv ("LC_CTYPE");
+  if (opt.startup_lc_ctype) 
+    opt.startup_lc_ctype = xstrdup (opt.startup_lc_ctype);
+  opt.startup_lc_messages = getenv ("LC_MESSAGES");
+  if (opt.startup_lc_messages)
+    opt.startup_lc_messages = xstrdup (opt.startup_lc_messages);
+  opt.startup_xauthority = getenv ("XAUTHORITY");
+  if (opt.startup_xauthority)
+    opt.startup_xauthority = xstrdup (opt.startup_xauthority);
+  opt.startup_pinentry_user_data = getenv ("PINENTRY_USER_DATA");
+  if (opt.startup_pinentry_user_data)
+    opt.startup_pinentry_user_data = xstrdup (opt.startup_pinentry_user_data);
+
+  /* Check whether we have a config file on the commandline */
   orig_argc = argc;
   orig_argv = argv;
   pargs.argc = &argc;
@@ -394,15 +630,14 @@ main (int argc, char **argv )
           opt.homedir = pargs.r.ret_str;
     }
 
-  /* initialize the secure memory. */
-  gcry_control (GCRYCTL_INIT_SECMEM, 16384, 0);
+  /* Initialize the secure memory. */
+  gcry_control (GCRYCTL_INIT_SECMEM, 32768, 0);
   maybe_setuid = 0;
 
   /* 
      Now we are now working under our real uid 
   */
 
-
   if (default_config)
     configname = make_filename (opt.homedir, "gpg-agent.conf", NULL );
   
@@ -440,10 +675,12 @@ main (int argc, char **argv )
 
   while (optfile_parse( configfp, configname, &configlineno, &pargs, opts) )
     {
-      if (parse_rereadable_options (&pargs))
+      if (parse_rereadable_options (&pargs, 0))
         continue; /* Already handled */
       switch (pargs.r_opt)
         {
+        case aGPGConfList: gpgconf_list = 1; break;
+        case aGPGConfTest: gpgconf_list = 2; break;
         case oBatch: opt.batch=1; break;
 
         case oDebugWait: debug_wait = pargs.r.ret_int; break;
@@ -467,17 +704,38 @@ main (int argc, char **argv )
         case oSh: csh_style = 0; break;
         case oServer: pipe_server = 1; break;
         case oDaemon: is_daemon = 1; break;
-        case oDisablePth: disable_pth = 1; break;
 
         case oDisplay: default_display = xstrdup (pargs.r.ret_str); break;
         case oTTYname: default_ttyname = xstrdup (pargs.r.ret_str); break;
         case oTTYtype: default_ttytype = xstrdup (pargs.r.ret_str); break;
         case oLCctype: default_lc_ctype = xstrdup (pargs.r.ret_str); break;
-        case oLCmessages: default_lc_messages = xstrdup (pargs.r.ret_str); break;
+        case oLCmessages: default_lc_messages = xstrdup (pargs.r.ret_str);
+        case oXauthority: default_xauthority = xstrdup (pargs.r.ret_str);
+          break;
+
+        case oUseStandardSocket: use_standard_socket = 1; break;
+        case oNoUseStandardSocket: use_standard_socket = 0; break;
+
+        case oFakedSystemTime:
+          {
+            time_t faked_time = isotime2epoch (pargs.r.ret_str); 
+            if (faked_time == (time_t)(-1))
+              faked_time = (time_t)strtoul (pargs.r.ret_str, NULL, 10);
+            gnupg_set_time (faked_time, 0);
+          }
+          break;
 
         case oKeepTTY: opt.keep_tty = 1; break;
         case oKeepDISPLAY: opt.keep_display = 1; break;
 
+       case oSSHSupport:  opt.ssh_support = 1; break;
+        case oWriteEnvFile:
+          if (pargs.r_type)
+            env_file_name = pargs.r.ret_str;
+          else
+            env_file_name = make_filename ("~/.gpg-agent-info", NULL);
+          break;
+
         default : pargs.err = configfp? 1:2; break;
        }
     }
@@ -504,9 +762,12 @@ main (int argc, char **argv )
       fprintf (stderr, "%s\n", strusage(15) );
     }
 #ifdef IS_DEVELOPMENT_VERSION
-  log_info ("NOTE: this is a development version!\n");
+  /* We don't want to print it here because gpg-agent is useful of its
+     own and quite matured.  */
+  /*log_info ("NOTE: this is a development version!\n");*/
 #endif
 
+  set_debug ();
   
   if (atexit (cleanup))
     {
@@ -515,36 +776,109 @@ main (int argc, char **argv )
       exit (1);
     }
 
+  initialize_module_call_pinentry ();
+  initialize_module_call_scd ();
+  initialize_module_trustlist ();
+  
+  /* Try to create missing directories. */
   create_directories ();
 
   if (debug_wait && pipe_server)
     {
       log_debug ("waiting for debugger - my pid is %u .....\n",
                  (unsigned int)getpid());
-      sleep (debug_wait);
+      gnupg_sleep (debug_wait);
       log_debug ("... okay\n");
     }
+  
+  if (gpgconf_list == 2)
+    agent_exit (0);
+  if (gpgconf_list)
+    {
+      char *filename;
+      char *filename_esc;
+
+      /* List options and default values in the GPG Conf format.  */
+      filename = make_filename (opt.homedir, "gpg-agent.conf", NULL );
+      filename_esc = percent_escape (filename, NULL);
+
+      printf ("gpgconf-gpg-agent.conf:%lu:\"%s\n",
+              GC_OPT_FLAG_DEFAULT, filename_esc);
+      xfree (filename);
+      xfree (filename_esc);
+
+      printf ("verbose:%lu:\n"
+              "quiet:%lu:\n"
+              "debug-level:%lu:\"none:\n"
+              "log-file:%lu:\n",
+              GC_OPT_FLAG_NONE|GC_OPT_FLAG_RUNTIME,
+              GC_OPT_FLAG_NONE|GC_OPT_FLAG_RUNTIME,
+              GC_OPT_FLAG_DEFAULT|GC_OPT_FLAG_RUNTIME,
+              GC_OPT_FLAG_NONE|GC_OPT_FLAG_RUNTIME );
+      printf ("default-cache-ttl:%lu:%d:\n",
+              GC_OPT_FLAG_DEFAULT|GC_OPT_FLAG_RUNTIME, DEFAULT_CACHE_TTL );
+      printf ("default-cache-ttl-ssh:%lu:%d:\n",
+              GC_OPT_FLAG_DEFAULT|GC_OPT_FLAG_RUNTIME, DEFAULT_CACHE_TTL_SSH );
+      printf ("max-cache-ttl:%lu:%d:\n",
+              GC_OPT_FLAG_DEFAULT|GC_OPT_FLAG_RUNTIME, MAX_CACHE_TTL );
+      printf ("max-cache-ttl-ssh:%lu:%d:\n",
+              GC_OPT_FLAG_DEFAULT|GC_OPT_FLAG_RUNTIME, MAX_CACHE_TTL_SSH );
+      printf ("enforce-passphrase-constraints:%lu:\n", 
+              GC_OPT_FLAG_NONE|GC_OPT_FLAG_RUNTIME);
+      printf ("min-passphrase-len:%lu:%d:\n",
+              GC_OPT_FLAG_DEFAULT|GC_OPT_FLAG_RUNTIME, MIN_PASSPHRASE_LEN );
+      printf ("min-passphrase-nonalpha:%lu:%d:\n",
+              GC_OPT_FLAG_DEFAULT|GC_OPT_FLAG_RUNTIME, 
+              MIN_PASSPHRASE_NONALPHA);
+      printf ("check-passphrase-pattern:%lu:\n",
+              GC_OPT_FLAG_DEFAULT|GC_OPT_FLAG_RUNTIME);
+      printf ("max-passphrase-days:%lu:%d:\n",
+              GC_OPT_FLAG_DEFAULT|GC_OPT_FLAG_RUNTIME, 
+              MAX_PASSPHRASE_DAYS);
+      printf ("enable-passphrase-history:%lu:\n", 
+              GC_OPT_FLAG_NONE|GC_OPT_FLAG_RUNTIME);
+      printf ("no-grab:%lu:\n", 
+              GC_OPT_FLAG_NONE|GC_OPT_FLAG_RUNTIME);
+      printf ("ignore-cache-for-signing:%lu:\n",
+              GC_OPT_FLAG_NONE|GC_OPT_FLAG_RUNTIME);
+      printf ("allow-mark-trusted:%lu:\n",
+              GC_OPT_FLAG_NONE|GC_OPT_FLAG_RUNTIME);
+      printf ("disable-scdaemon:%lu:\n",
+              GC_OPT_FLAG_NONE|GC_OPT_FLAG_RUNTIME);
 
+      agent_exit (0);
+    }
+
+  /* If this has been called without any options, we merely check
+     whether an agent is already running.  We do this here so that we
+     don't clobber a logfile but print it directly to stderr. */
   if (!pipe_server && !is_daemon)
-    log_info (_("please use the option `--daemon'"
-                " to run the program in the background\n"));
+    {
+      log_set_prefix (NULL, JNLIB_LOG_WITH_PREFIX); 
+      check_for_running_agent (0, 0);
+      agent_exit (0);
+    }
   
 #ifdef ENABLE_NLS
-  /* gpg-agent usdually does not ooutput any messages becuase it runs
-     in the background.  For log files it is acceptable to have
-     messages always encoded in utf-8.  We switch here to utf-8, so
-     that commands like --help still give native messages.  It is far
-     easier to swicthnonly once instead of for every message and it
-     actually helps when more then one thread is active (avoids
-     required an extra copy step). */
-    bind_textdomain_codeset (PACKAGE, "UTF-8");
+  /* gpg-agent usually does not output any messages because it runs in
+     the background.  For log files it is acceptable to have messages
+     always encoded in utf-8.  We switch here to utf-8, so that
+     commands like --help still give native messages.  It is far
+     easier to switch only once instead of for every message and it
+     actually helps when more then one thread is active (avoids an
+     extra copy step). */
+    bind_textdomain_codeset (PACKAGE_GT, "UTF-8");
 #endif
 
-  /* now start with logging to a file if this is desired */
+  /* Now start with logging to a file if this is desired. */
   if (logfile)
     {
       log_set_file (logfile);
-      log_set_prefix (NULL, 1|2|4);
+      log_set_prefix (NULL, (JNLIB_LOG_WITH_PREFIX
+                             |JNLIB_LOG_WITH_TIME
+                             |JNLIB_LOG_WITH_PID));
+      current_logfile = xstrdup (logfile);
+      assuan_set_assuan_log_stream (log_get_stream ());
     }
 
   /* Make sure that we have a default ttyname. */
@@ -553,88 +887,68 @@ main (int argc, char **argv )
   if (!default_ttytype && getenv ("TERM"))
     default_ttytype = xstrdup (getenv ("TERM"));
 
+
   if (pipe_server)
-    { /* this is the simple pipe based server */
-      start_command_handler (-1, -1);
+    { 
+      /* This is the simple pipe based server */
+      ctrl_t ctrl;
+
+      ctrl = xtrycalloc (1, sizeof *ctrl);
+      if (!ctrl)
+        {
+          log_error ("error allocating connection control data: %s\n",
+                     strerror (errno) );
+          agent_exit (1);
+        }
+      agent_init_default_ctrl (ctrl);
+      start_command_handler (ctrl, GNUPG_INVALID_FD, GNUPG_INVALID_FD);
+      agent_deinit_default_ctrl (ctrl);
+      xfree (ctrl);
     }
   else if (!is_daemon)
-    ;
+    ; /* NOTREACHED */
   else
-    { /* regular server mode */
-      int fd;
+    { /* Regular server mode */
+      gnupg_fd_t fd;
+      gnupg_fd_t fd_ssh;
       pid_t pid;
-      int len;
-      struct sockaddr_un serv_addr;
-      char *p;
 
       /* Remove the DISPLAY variable so that a pinentry does not
          default to a specific display.  There is still a default
-         display when gpg-agent weas started using --display or a
-         client requested this using an OPTION command. */
-      if (!opt.keep_display)
+         display when gpg-agent was started using --display or a
+         client requested this using an OPTION command.  Note, that we
+         don't do this when running in reverse daemon mode (i.e. when
+         exec the program given as arguments). */
+#ifndef HAVE_W32_SYSTEM
+      if (!opt.keep_display && !argc)
         unsetenv ("DISPLAY");
+#endif
 
-      *socket_name = 0;
-      snprintf (socket_name, DIM(socket_name)-1,
-                "/tmp/gpg-XXXXXX/S.gpg-agent");
-      socket_name[DIM(socket_name)-1] = 0;
-      p = strrchr (socket_name, '/');
-      if (!p)
-        BUG ();
-      *p = 0;;
-      if (!mkdtemp(socket_name))
-        {
-          log_error ("can't create directory `%s': %s\n",
-                    socket_name, strerror(errno) );
-          exit (1);
-        }
-      *p = '/';
-
-      if (strchr (socket_name, ':') )
-        {
-          log_error ("colons are not allowed in the socket name\n");
-          exit (1);
-        }
-      if (strlen (socket_name)+1 >= sizeof serv_addr.sun_path ) 
-        {
-          log_error ("name of socket too long\n");
-          exit (1);
-        }
-   
-
-      fd = socket (AF_UNIX, SOCK_STREAM, 0);
-      if (fd == -1)
-        {
-          log_error ("can't create socket: %s\n", strerror(errno) );
-          exit (1);
-        }
-
-      memset (&serv_addr, 0, sizeof serv_addr);
-      serv_addr.sun_family = AF_UNIX;
-      strcpy (serv_addr.sun_path, socket_name);
-      len = (offsetof (struct sockaddr_un, sun_path)
-             + strlen(serv_addr.sun_path) + 1);
 
-      if (bind (fd, (struct sockaddr*)&serv_addr, len) == -1)
-        {
-          log_error ("error binding socket to `%s': %s\n",
-                     serv_addr.sun_path, strerror (errno) );
-          close (fd);
-          exit (1);
-        }
-  
-      if (listen (fd, 5 ) == -1)
-        {
-          log_error ("listen() failed: %s\n", strerror (errno));
-          close (fd);
-          exit (1);
-        }
+      /* Create the sockets.  */
+      socket_name = create_socket_name 
+        ("S.gpg-agent", "/tmp/gpg-XXXXXX/S.gpg-agent");
+      if (opt.ssh_support)
+       socket_name_ssh = create_socket_name 
+          ("S.gpg-agent.ssh", "/tmp/gpg-XXXXXX/S.gpg-agent.ssh");
 
-      if (opt.verbose)
-        log_info ("listening on socket `%s'\n", socket_name );
+      fd = create_server_socket (socket_name, 0, &socket_nonce);
+      if (opt.ssh_support)
+       fd_ssh = create_server_socket (socket_name_ssh, 1, &socket_nonce_ssh);
+      else
+       fd_ssh = GNUPG_INVALID_FD;
 
+      /* If we are going to exec a program in the parent, we record
+         the PID, so that the child may check whether the program is
+         still alive. */
+      if (argc)
+        parent_pid = getpid ();
 
       fflush (NULL);
+#ifdef HAVE_W32_SYSTEM
+      pid = getpid ();
+      printf ("set GPG_AGENT_INFO=%s;%lu;1\n", socket_name, (ulong)pid);
+#else /*!HAVE_W32_SYSTEM*/
       pid = fork ();
       if (pid == (pid_t)-1) 
         {
@@ -642,12 +956,12 @@ main (int argc, char **argv )
           exit (1);
         }
       else if (pid) 
-        { /* we are the parent */
-          char *infostr;
+        { /* We are the parent */
+          char *infostr, *infostr_ssh_sock, *infostr_ssh_pid;
           
           close (fd);
           
-          /* create the info string: <name>:<pid>:<protocol_version> */
+          /* Create the info string: <name>:<pid>:<protocol_version> */
           if (asprintf (&infostr, "GPG_AGENT_INFO=%s:%lu:1",
                         socket_name, (ulong)pid ) < 0)
             {
@@ -655,10 +969,55 @@ main (int argc, char **argv )
               kill (pid, SIGTERM);
               exit (1);
             }
-          *socket_name = 0; /* don't let cleanup() remove the socket -
+         if (opt.ssh_support)
+           {
+             if (asprintf (&infostr_ssh_sock, "SSH_AUTH_SOCK=%s",
+                           socket_name_ssh) < 0)
+               {
+                 log_error ("out of core\n");
+                 kill (pid, SIGTERM);
+                 exit (1);
+               }
+             if (asprintf (&infostr_ssh_pid, "SSH_AGENT_PID=%u",
+                           pid) < 0)
+               {
+                 log_error ("out of core\n");
+                 kill (pid, SIGTERM);
+                 exit (1);
+               }
+           }
+
+          *socket_name = 0; /* Don't let cleanup() remove the socket -
                                the child should do this from now on */
+         if (opt.ssh_support)
+           *socket_name_ssh = 0;
+
+          if (env_file_name)
+            {
+              FILE *fp;
+              
+              fp = fopen (env_file_name, "w");
+              if (!fp)
+                log_error (_("error creating `%s': %s\n"),
+                             env_file_name, strerror (errno));
+              else
+                {
+                  fputs (infostr, fp);
+                  putc ('\n', fp);
+                  if (opt.ssh_support)
+                    {
+                      fputs (infostr_ssh_sock, fp);
+                      putc ('\n', fp);
+                      fputs (infostr_ssh_pid, fp);
+                      putc ('\n', fp);
+                    }
+                  fclose (fp);
+                }
+            }
+
+
           if (argc) 
-            { /* run the program given on the commandline */
+            { /* Run the program given on the commandline.  */
               if (putenv (infostr))
                 {
                   log_error ("failed to set environment: %s\n",
@@ -666,6 +1025,20 @@ main (int argc, char **argv )
                   kill (pid, SIGTERM );
                   exit (1);
                 }
+              if (opt.ssh_support && putenv (infostr_ssh_sock))
+                {
+                  log_error ("failed to set environment: %s\n",
+                             strerror (errno) );
+                  kill (pid, SIGTERM );
+                  exit (1);
+                }
+              if (opt.ssh_support && putenv (infostr_ssh_pid))
+                {
+                  log_error ("failed to set environment: %s\n",
+                             strerror (errno) );
+                  kill (pid, SIGTERM );
+                  exit (1);
+                }
               execvp (argv[0], argv);
               log_error ("failed to run the command: %s\n", strerror (errno));
               kill (pid, SIGTERM);
@@ -673,36 +1046,64 @@ main (int argc, char **argv )
             }
           else
             {
-              /* print the environment string, so that the caller can use
+              /* Print the environment string, so that the caller can use
                  shell's eval to set it */
               if (csh_style)
                 {
                   *strchr (infostr, '=') = ' ';
-                  printf ( "setenv %s\n", infostr);
+                  printf ("setenv %s\n", infostr);
+                 if (opt.ssh_support)
+                   {
+                     *strchr (infostr_ssh_sock, '=') = ' ';
+                     printf ("setenv %s\n", infostr_ssh_sock);
+                     *strchr (infostr_ssh_pid, '=') = ' ';
+                     printf ("setenv %s\n", infostr_ssh_pid);
+                   }
                 }
               else
                 {
                   printf ( "%s; export GPG_AGENT_INFO;\n", infostr);
+                 if (opt.ssh_support)
+                   {
+                     printf ("%s; export SSH_AUTH_SOCK;\n", infostr_ssh_sock);
+                     printf ("%s; export SSH_AGENT_PID;\n", infostr_ssh_pid);
+                   }
                 }
-              free (infostr);
+              xfree (infostr); 
+             if (opt.ssh_support)
+               {
+                 xfree (infostr_ssh_sock);
+                 xfree (infostr_ssh_pid);
+               }
               exit (0); 
             }
-          /*NEVER REACHED*/
-        } /* end parent */
-      
+          /*NOTREACHED*/
+        } /* End parent */
 
-      /* this is the child */
+      /* 
+         This is the child
+       */
 
-      /* detach from tty and put process into a new session */
+      /* Detach from tty and put process into a new session */
       if (!nodetach )
         { 
           int i;
+          unsigned int oldflags;
 
-          /* close stdin, stdout and stderr unless it is the log stream */
+          /* Close stdin, stdout and stderr unless it is the log stream */
           for (i=0; i <= 2; i++) 
             {
-              if ( log_get_fd () != i)
-                close (i);
+              if (!log_test_fd (i) && i != fd )
+                {
+                  if ( ! close (i)
+                       && open ("/dev/null", i? O_WRONLY : O_RDONLY) == -1)
+                    {
+                      log_error ("failed to open `%s': %s\n",
+                                 "/dev/null", strerror (errno));
+                      cleanup ();
+                      exit (1);
+                    }
+                }
             }
           if (setsid() == -1)
             {
@@ -710,6 +1111,9 @@ main (int argc, char **argv )
               cleanup ();
               exit (1);
             }
+
+          log_get_prefix (&oldflags);
+          log_set_prefix (NULL, oldflags | JNLIB_LOG_RUN_DETACHED);
           opt.running_detached = 1;
         }
 
@@ -719,46 +1123,24 @@ main (int argc, char **argv )
           exit (1);
         }
 
-
-#ifdef USE_GNU_PTH
-      if (!disable_pth)
-        {
-         struct sigaction sa;
-
-         sa.sa_handler = SIG_IGN;
-         sigemptyset (&sa.sa_mask);
-         sa.sa_flags = 0;
-         sigaction (SIGPIPE, &sa, NULL);
-          handle_connections (fd);
-        }
-      else
-#endif /*!USE_GNU_PTH*/
-      /* setup signals */
-        {
-          struct sigaction oact, nact;
-          
-          nact.sa_handler = cleanup_sh;
-          sigemptyset (&nact.sa_mask);
-          nact.sa_flags = 0;
-          
-          sigaction (SIGHUP, NULL, &oact);
-          if (oact.sa_handler != SIG_IGN)
-            sigaction (SIGHUP, &nact, NULL);
-          sigaction( SIGTERM, NULL, &oact );
-          if (oact.sa_handler != SIG_IGN)
-            sigaction (SIGTERM, &nact, NULL);
-          nact.sa_handler = SIG_IGN;
-          sigaction (SIGPIPE, &nact, NULL);
-          sigaction (SIGINT, &nact, NULL);
-
-          start_command_handler (fd, -1);
-        }
-      close (fd);
+      {
+        struct sigaction sa;
+        
+        sa.sa_handler = SIG_IGN;
+        sigemptyset (&sa.sa_mask);
+        sa.sa_flags = 0;
+        sigaction (SIGPIPE, &sa, NULL);
+      }
+#endif /*!HAVE_W32_SYSTEM*/
+
+      handle_connections (fd, opt.ssh_support ? fd_ssh : GNUPG_INVALID_FD);
+      assuan_sock_close (fd);
     }
   
   return 0;
 }
 
+
 void
 agent_exit (int rc)
 {
@@ -778,35 +1160,63 @@ agent_exit (int rc)
   exit (rc);
 }
 
-
-void
-agent_init_default_ctrl (struct server_control_s *ctrl)
+static void
+agent_init_default_ctrl (ctrl_t ctrl)
 {
   /* Note we ignore malloc errors because we can't do much about it
      and the request will fail anyway shortly after this
      initialization. */
   if (ctrl->display)
-    free (ctrl->display);
-  ctrl->display = default_display? strdup (default_display) : NULL;
+    xfree (ctrl->display);
+  ctrl->display = default_display? xtrystrdup (default_display) : NULL;
 
   if (ctrl->ttyname)
-    free (ctrl->ttyname);
-  ctrl->ttyname = default_ttyname? strdup (default_ttyname) : NULL;
+    xfree (ctrl->ttyname);
+  ctrl->ttyname = default_ttyname? xtrystrdup (default_ttyname) : NULL;
 
   if (ctrl->ttytype)
-    free (ctrl->ttytype);
-  ctrl->ttytype = default_ttytype? strdup (default_ttytype) : NULL;
+    xfree (ctrl->ttytype);
+  ctrl->ttytype = default_ttytype? xtrystrdup (default_ttytype) : NULL;
 
   if (ctrl->lc_ctype)
-    free (ctrl->lc_ctype);
-  ctrl->lc_ctype = default_lc_ctype? strdup (default_lc_ctype) : NULL;
+    xfree (ctrl->lc_ctype);
+  ctrl->lc_ctype = default_lc_ctype? xtrystrdup (default_lc_ctype) : NULL;
 
   if (ctrl->lc_messages)
-    free (ctrl->lc_messages);
-  ctrl->lc_messages = default_lc_messages? strdup (default_lc_messages) : NULL;
+    xfree (ctrl->lc_messages);
+  ctrl->lc_messages = default_lc_messages? xtrystrdup (default_lc_messages)
+                                    /**/ : NULL;
+
+  if (ctrl->xauthority)
+    xfree (ctrl->xauthority);
+  ctrl->xauthority = default_xauthority? xtrystrdup (default_xauthority)
+                                   /**/: NULL;
+
+  if (ctrl->pinentry_user_data)
+    xfree (ctrl->pinentry_user_data);
+  ctrl->pinentry_user_data = NULL;
 }
 
 
+static void
+agent_deinit_default_ctrl (ctrl_t ctrl)
+{
+  if (ctrl->display)
+    xfree (ctrl->display);
+  if (ctrl->ttyname)
+    xfree (ctrl->ttyname);
+  if (ctrl->ttytype)
+    xfree (ctrl->ttytype);
+  if (ctrl->lc_ctype)
+    xfree (ctrl->lc_ctype);
+  if (ctrl->lc_messages)
+    xfree (ctrl->lc_messages);
+  if (ctrl->xauthority)
+    xfree (ctrl->xauthority);
+  if (ctrl->pinentry_user_data)
+    xfree (ctrl->pinentry_user_data);
+}
+
 /* Reread parts of the configuration.  Note, that this function is
    obviously not thread-safe and should only be called from the PTH
    signal handler. 
@@ -835,7 +1245,7 @@ reread_configuration (void)
       return;
     }
 
-  parse_rereadable_options (NULL); /* Start from the default values. */
+  parse_rereadable_options (NULL, 1); /* Start from the default values. */
 
   memset (&pargs, 0, sizeof pargs);
   dummy = 0;
@@ -846,12 +1256,212 @@ reread_configuration (void)
       if (pargs.r_opt < -1)
         pargs.err = 1; /* Print a warning. */
       else /* Try to parse this option - ignore unchangeable ones. */
-        parse_rereadable_options (&pargs);
+        parse_rereadable_options (&pargs, 1);
     }
   fclose (fp);
+  set_debug ();
+}
+
+
+/* Return the file name of the socket we are using for native
+   requests.  */
+const char *
+get_agent_socket_name (void)
+{
+  const char *s = socket_name;
+
+  return (s && *s)? s : NULL;
+}
+
+/* Return the file name of the socket we are using for SSH
+   requests.  */
+const char *
+get_agent_ssh_socket_name (void)
+{
+  const char *s = socket_name_ssh;
+
+  return (s && *s)? s : NULL;
+}
+
+
+/* Under W32, this function returns the handle of the scdaemon
+   notification event.  Calling it the first time creates that
+   event.  */
+#ifdef HAVE_W32_SYSTEM
+void *
+get_agent_scd_notify_event (void)
+{
+  static HANDLE the_event;
+
+  if (!the_event)
+    {
+      HANDLE h, h2;
+      SECURITY_ATTRIBUTES sa = { sizeof (SECURITY_ATTRIBUTES), NULL, TRUE};
+
+      /* We need to use manual reset evet object due to the way our
+         w32-pth wait function works: If we would use an automatic
+         reset event we are not able to figure out which handle has
+         been signaled because at the time we single out the signaled
+         handles using WFSO the event has already been reset due to
+         the WFMO.  */
+      h = CreateEvent (&sa, TRUE, FALSE, NULL);
+      if (!h)
+        log_error ("can't create scd notify event: %s\n", w32_strerror (-1) );
+      else if (!DuplicateHandle (GetCurrentProcess(), h,
+                                 GetCurrentProcess(), &h2,
+                                 EVENT_MODIFY_STATE|SYNCHRONIZE, TRUE, 0)) 
+        {
+          log_error ("setting syncronize for scd notify event failed: %s\n",
+                     w32_strerror (-1) );
+          CloseHandle (h);
+        }
+      else
+        {
+          CloseHandle (h);
+          the_event = h2;
+        }
+    }
+
+  log_debug  ("returning notify handle %p\n", the_event);
+  return the_event;
 }
+#endif /*HAVE_W32_SYSTEM*/
 
 
+
+/* Create a name for the socket.  With USE_STANDARD_SOCKET given as
+   true using STANDARD_NAME in the home directory or if given as
+   false from the mkdir type name TEMPLATE.  In the latter case a
+   unique name in a unique new directory will be created.  In both
+   cases check for valid characters as well as against a maximum
+   allowed length for a unix domain socket is done.  The function
+   terminates the process in case of an error.  Returns: Pointer to an
+   allocated string with the absolute name of the socket used.  */
+static char *
+create_socket_name (char *standard_name, char *template)
+{
+  char *name, *p;
+
+  if (use_standard_socket)
+    name = make_filename (opt.homedir, standard_name, NULL);
+  else
+    {
+      name = xstrdup (template);
+      p = strrchr (name, '/');
+      if (!p)
+       BUG ();
+      *p = 0;
+      if (!mkdtemp (name))
+       {
+         log_error (_("can't create directory `%s': %s\n"),
+                    name, strerror (errno));
+         agent_exit (2);
+       }
+      *p = '/';
+    }
+
+  if (strchr (name, PATHSEP_C))
+    {
+      log_error (("`%s' are not allowed in the socket name\n"), PATHSEP_S);
+      agent_exit (2);
+    }
+  if (strlen (name) + 1 >= DIMof (struct sockaddr_un, sun_path) )
+    {
+      log_error (_("name of socket too long\n"));
+      agent_exit (2);
+    }
+  return name;
+}
+
+
+
+/* Create a Unix domain socket with NAME.  Returns the file descriptor
+   or terminates the process in case of an error.  Not that this
+   function needs to be used for the regular socket first and only
+   then for the ssh socket.  */
+static gnupg_fd_t
+create_server_socket (char *name, int is_ssh, assuan_sock_nonce_t *nonce)
+{
+  struct sockaddr_un *serv_addr;
+  socklen_t len;
+  gnupg_fd_t fd;
+  int rc;
+
+  fd = assuan_sock_new (AF_UNIX, SOCK_STREAM, 0);
+  if (fd == ASSUAN_INVALID_FD)
+    {
+      log_error (_("can't create socket: %s\n"), strerror (errno));
+      agent_exit (2);
+    }
+
+  serv_addr = xmalloc (sizeof (*serv_addr)); 
+  memset (serv_addr, 0, sizeof *serv_addr);
+  serv_addr->sun_family = AF_UNIX;
+  if (strlen (name) + 1 >= sizeof (serv_addr->sun_path))
+    {
+      log_error (_("socket name `%s' is too long\n"), name);
+      agent_exit (2);
+    }
+  strcpy (serv_addr->sun_path, name);
+  len = (offsetof (struct sockaddr_un, sun_path)
+        + strlen (serv_addr->sun_path) + 1);
+
+  rc = assuan_sock_bind (fd, (struct sockaddr*) serv_addr, len);
+  if (use_standard_socket && rc == -1 && errno == EADDRINUSE)
+    {
+      /* Check whether a gpg-agent is already running on the standard
+         socket.  We do this test only if this is not the ssh socket.
+         For ssh we assume that a test for gpg-agent has already been
+         done and reuse the requested ssh socket.  Testing the
+         ssh-socket is not possible because at this point, though we
+         know the new Assuan socket, the Assuan server and thus the
+         ssh-agent server is not yet operational.  This would lead to
+         a hang.  */
+      if (!is_ssh && !check_for_running_agent (1, 1))
+        {
+          log_error (_("a gpg-agent is already running - "
+                       "not starting a new one\n"));
+          *name = 0; /* Inhibit removal of the socket by cleanup(). */
+          assuan_sock_close (fd);
+          agent_exit (2);
+        }
+      remove (name);
+      rc = assuan_sock_bind (fd, (struct sockaddr*) serv_addr, len);
+    }
+  if (rc != -1 
+      && (rc=assuan_sock_get_nonce ((struct sockaddr*)serv_addr, len, nonce)))
+    log_error (_("error getting nonce for the socket\n"));
+  if (rc == -1)
+    {
+      /* We use gpg_strerror here because it allows us to get strings
+         for some W32 socket error codes.  */
+      log_error (_("error binding socket to `%s': %s\n"),
+                serv_addr->sun_path, 
+                 gpg_strerror (gpg_error_from_errno (errno)));
+      
+      assuan_sock_close (fd);
+      if (use_standard_socket)
+        *name = 0; /* Inhibit removal of the socket by cleanup(). */
+      agent_exit (2);
+    }
+
+  if (listen (FD2INT(fd), 5 ) == -1)
+    {
+      log_error (_("listen() failed: %s\n"), strerror (errno));
+      assuan_sock_close (fd);
+      agent_exit (2);
+    }
+          
+  if (opt.verbose)
+    log_info (_("listening on socket `%s'\n"), serv_addr->sun_path);
+
+  return fd;
+}
+
+
+/* Check that the directory for storing the private keys exists and
+   create it if not.  This function won't fail as it is only a
+   convenience function and not strictly necessary.  */
 static void
 create_private_keys_directory (const char *home)
 {
@@ -861,9 +1471,15 @@ create_private_keys_directory (const char *home)
   fname = make_filename (home, GNUPG_PRIVATE_KEYS_DIR, NULL);
   if (stat (fname, &statbuf) && errno == ENOENT)
     {
+#ifdef HAVE_W32_SYSTEM  /*FIXME: Setup proper permissions.  */
+      if (!CreateDirectory (fname, NULL))
+        log_error (_("can't create directory `%s': %s\n"),
+                   fname, w32_strerror (-1) );
+#else
       if (mkdir (fname, S_IRUSR|S_IWUSR|S_IXUSR ))
         log_error (_("can't create directory `%s': %s\n"),
-                   fname,      strerror(errno) );
+                   fname, strerror (errno) );
+#endif
       else if (!opt.quiet)
         log_info (_("directory `%s' created\n"), fname);
     }
@@ -880,24 +1496,35 @@ static void
 create_directories (void)
 {
   struct stat statbuf;
-  const char *defhome = GNUPG_DEFAULT_HOMEDIR;
+  const char *defhome = standard_homedir ();
   char *home;
 
-  home  = make_filename (opt.homedir, NULL);
+  home = make_filename (opt.homedir, NULL);
   if ( stat (home, &statbuf) )
     {
       if (errno == ENOENT)
         {
-          if ( (*defhome == '~'
+          if (
+#ifdef HAVE_W32_SYSTEM
+              ( !compare_filenames (home, defhome) )
+#else
+              (*defhome == '~'
                 && (strlen (home) >= strlen (defhome+1)
                     && !strcmp (home + strlen(home)
                                 - strlen (defhome+1), defhome+1)))
                || (*defhome != '~' && !strcmp (home, defhome) )
+#endif
                )
             {
+#ifdef HAVE_W32_SYSTEM
+              if (!CreateDirectory (home, NULL))
+                log_error (_("can't create directory `%s': %s\n"),
+                           home, w32_strerror (-1) );
+#else
               if (mkdir (home, S_IRUSR|S_IWUSR|S_IXUSR ))
                 log_error (_("can't create directory `%s': %s\n"),
-                           home, strerror(errno) );
+                           home, strerror (errno) );
+#endif
               else 
                 {
                   if (!opt.quiet)
@@ -907,11 +1534,11 @@ create_directories (void)
             }
         }
       else
-        log_error ("error stat-ing `%s': %s\n", home, strerror (errno));
+        log_error (_("stat() failed for `%s': %s\n"), home, strerror (errno));
     }
   else if ( !S_ISDIR(statbuf.st_mode))
     {
-      log_error ("can't use `%s' as home directory\n", home);
+      log_error (_("can't use `%s' as home directory\n"), home);
     }
   else /* exists and is a directory. */
     {
@@ -922,29 +1549,87 @@ create_directories (void)
 
 
 
-#ifdef USE_GNU_PTH
+/* This is the worker for the ticker.  It is called every few seconds
+   and may only do fast operations. */
+static void
+handle_tick (void)
+{
+  static time_t last_minute;
+
+  if (!last_minute)
+    last_minute = time (NULL);
+
+  /* Check whether the scdaemon has died and cleanup in this case. */
+  agent_scd_check_aliveness ();
+
+  /* If we are running as a child of another process, check whether
+     the parent is still alive and shutdown if not. */
+#ifndef HAVE_W32_SYSTEM
+  if (parent_pid != (pid_t)(-1))
+    {
+      if (kill (parent_pid, 0))
+        {
+          shutdown_pending = 2;
+          log_info ("parent process died - shutting down\n");
+          log_info ("%s %s stopped\n", strusage(11), strusage(13) );
+          cleanup ();
+          agent_exit (0);
+        }
+    }
+#endif /*HAVE_W32_SYSTEM*/
+  
+  /* Code to be run every minute.  */
+  if (last_minute + 60 <= time (NULL))
+    {
+      check_own_socket ();
+      last_minute = time (NULL);
+    }
+
+}
+
+
+/* A global function which allows us to call the reload stuff from
+   other places too.  This is only used when build for W32.  */
+void
+agent_sighup_action (void)
+{
+  log_info ("SIGHUP received - "
+            "re-reading configuration and flushing cache\n");
+  agent_flush_cache ();
+  reread_configuration ();
+  agent_reload_trustlist ();
+}
+
+
+static void
+agent_sigusr2_action (void)
+{
+  if (opt.verbose)
+    log_info ("SIGUSR2 received - updating card event counter\n");
+  /* Nothing to check right now.  We only increment a counter.  */
+  bump_card_eventcounter ();
+}
+
+
 static void
 handle_signal (int signo)
 {
   switch (signo)
     {
+#ifndef HAVE_W32_SYSTEM
     case SIGHUP:
-      log_info ("SIGHUP received - "
-                "re-reading configuration and flushing cache\n");
-      agent_flush_cache ();
-      reread_configuration ();
+      agent_sighup_action ();
       break;
       
     case SIGUSR1:
-      if (opt.verbose < 5)
-        opt.verbose++;
-      log_info ("SIGUSR1 received - verbosity set to %d\n", opt.verbose);
+      log_info ("SIGUSR1 received - printing internal information:\n");
+      pth_ctrl (PTH_CTRL_DUMPSTATE, log_get_stream ());
+      agent_query_dump_state ();
+      agent_scd_dump_state ();
       break;
-
+      
     case SIGUSR2:
-      if (opt.verbose)
-        opt.verbose--;
-      log_info ("SIGUSR2 received - verbosity set to %d\n", opt.verbose );
+      agent_sigusr2_action ();
       break;
 
     case SIGTERM:
@@ -969,95 +1654,502 @@ handle_signal (int signo)
       cleanup ();
       agent_exit (0);
       break;
-
+#endif
     default:
       log_info ("signal %d received - no action defined\n", signo);
     }
 }
 
 
+/* Check the nonce on a new connection.  This is a NOP unless we we
+   are using our Unix domain socket emulation under Windows.  */
+static int 
+check_nonce (ctrl_t ctrl, assuan_sock_nonce_t *nonce)
+{
+  if (assuan_sock_check_nonce (ctrl->thread_startup.fd, nonce))
+    {
+      log_info (_("error reading nonce on fd %d: %s\n"), 
+                FD2INT(ctrl->thread_startup.fd), strerror (errno));
+      assuan_sock_close (ctrl->thread_startup.fd);
+      xfree (ctrl);
+      return -1;
+    }
+  else
+    return 0;
+}
+
+
+/* This is the standard connection thread's main function.  */
 static void *
 start_connection_thread (void *arg)
 {
-  int fd = (int)arg;
+  ctrl_t ctrl = arg;
+
+  if (check_nonce (ctrl, &socket_nonce))
+    return NULL;
+
+  agent_init_default_ctrl (ctrl);
+  if (opt.verbose)
+    log_info (_("handler 0x%lx for fd %d started\n"), 
+              pth_thread_id (), FD2INT(ctrl->thread_startup.fd));
+
+  start_command_handler (ctrl, GNUPG_INVALID_FD, ctrl->thread_startup.fd);
+  if (opt.verbose)
+    log_info (_("handler 0x%lx for fd %d terminated\n"), 
+              pth_thread_id (), FD2INT(ctrl->thread_startup.fd));
+  
+  agent_deinit_default_ctrl (ctrl);
+  xfree (ctrl);
+  return NULL;
+}
+
+
+/* This is the ssh connection thread's main function.  */
+static void *
+start_connection_thread_ssh (void *arg)
+{
+  ctrl_t ctrl = arg;
 
+  if (check_nonce (ctrl, &socket_nonce_ssh))
+    return NULL;
+
+  agent_init_default_ctrl (ctrl);
   if (opt.verbose)
-    log_info ("handler for fd %d started\n", fd);
-  start_command_handler (-1, fd);
+    log_info (_("ssh handler 0x%lx for fd %d started\n"),
+              pth_thread_id (), FD2INT(ctrl->thread_startup.fd));
+
+  start_command_handler_ssh (ctrl, ctrl->thread_startup.fd);
   if (opt.verbose)
-    log_info ("handler for fd %d terminated\n", fd);
+    log_info (_("ssh handler 0x%lx for fd %d terminated\n"),
+              pth_thread_id (), FD2INT(ctrl->thread_startup.fd));
   
+  agent_deinit_default_ctrl (ctrl);
+  xfree (ctrl);
   return NULL;
 }
 
 
+/* Connection handler loop.  Wait for connection requests and spawn a
+   thread after accepting a connection.  */
 static void
-handle_connections (int listen_fd)
+handle_connections (gnupg_fd_t listen_fd, gnupg_fd_t listen_fd_ssh)
 {
   pth_attr_t tattr;
-  pth_event_t ev;
+  pth_event_t ev, time_ev;
   sigset_t sigs;
   int signo;
   struct sockaddr_un paddr;
-  socklen_t plen = sizeof( paddr );
-  int fd;
+  socklen_t plen;
+  fd_set fdset, read_fdset;
+  int ret;
+  gnupg_fd_t fd;
+  int nfd;
 
   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");
-
+  pth_attr_set (tattr, PTH_ATTR_STACK_SIZE, 256*1024);
+
+#ifndef HAVE_W32_SYSTEM /* fixme */
+  /* Make sure that the signals we are going to handle are not blocked
+     and create an event object for them.  We also set the default
+     action to ignore because we use an Pth event to get notified
+     about signals.  This avoids that the default action is taken in
+     case soemthing goes wrong within Pth.  The problem might also be
+     a Pth bug.  */
   sigemptyset (&sigs );
-  sigaddset (&sigs, SIGHUP);
-  sigaddset (&sigs, SIGUSR1);
-  sigaddset (&sigs, SIGUSR2);
-  sigaddset (&sigs, SIGINT);
-  sigaddset (&sigs, SIGTERM);
+  {
+    static const int mysigs[] = { SIGHUP, SIGUSR1, SIGUSR2, SIGINT, SIGTERM };
+    struct sigaction sa;
+    int i;
+
+    for (i=0; i < DIM (mysigs); i++)
+      {
+        sigemptyset (&sa.sa_mask);
+        sa.sa_handler = SIG_IGN;
+        sa.sa_flags = 0;
+        sigaction (mysigs[i], &sa, NULL);
+        
+        sigaddset (&sigs, mysigs[i]);
+      }
+  }
+
+  pth_sigmask (SIG_UNBLOCK, &sigs, NULL);
+  ev = pth_event (PTH_EVENT_SIGS, &sigs, &signo);
+#else
+# ifdef PTH_EVENT_HANDLE
+  sigs = 0;
+  ev = pth_event (PTH_EVENT_HANDLE, get_agent_scd_notify_event ());
+  signo = 0;
+# else
+  /* Use a dummy event. */
+  sigs = 0;
   ev = pth_event (PTH_EVENT_SIGS, &sigs, &signo);
+# endif
+#endif
+  time_ev = NULL;
+
+  FD_ZERO (&fdset);
+  FD_SET (FD2INT (listen_fd), &fdset);
+  nfd = FD2INT (listen_fd);
+  if (listen_fd_ssh != GNUPG_INVALID_FD)
+    {
+      FD_SET ( FD2INT(listen_fd_ssh), &fdset);
+      if (FD2INT (listen_fd_ssh) > nfd)
+        nfd = FD2INT (listen_fd_ssh);
+    }
 
   for (;;)
     {
+      /* Make sure that our signals are not blocked.  */
+      pth_sigmask (SIG_UNBLOCK, &sigs, NULL);
+
+      /* Shutdown test.  */
       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;
+          /* Do not accept new connections but keep on running the
+             loop to cope with the timer events.  */
+          FD_ZERO (&fdset);
        }
 
-      fd = pth_accept_ev (listen_fd, (struct sockaddr *)&paddr, &plen, ev);
-      if (fd == -1)
+      /* Create a timeout event if needed.  To help with power saving
+         we syncronize the ticks to the next full second.  */
+      if (!time_ev)
         {
-#ifdef PTH_STATUS_OCCURRED     /* This is Pth 2 */
-          if (pth_event_status (ev) == PTH_STATUS_OCCURRED)
+          pth_time_t nexttick;
+
+          nexttick = pth_timeout (TIMERTICK_INTERVAL, 0);
+          if (nexttick.tv_usec > 10)  /* Use a 10 usec threshhold.  */
+            {
+              nexttick.tv_sec++;
+              nexttick.tv_usec = 0;
+            }
+          time_ev = pth_event (PTH_EVENT_TIME, nexttick);
+        }
+
+      /* 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;
+
+      if (time_ev)
+        pth_event_concat (ev, time_ev, NULL);
+      ret = pth_select_ev (nfd+1, &read_fdset, NULL, NULL, NULL, ev);
+      if (time_ev)
+        pth_event_isolate (time_ev);
+
+      if (ret == -1)
+       {
+          if (pth_event_occurred (ev)
+              || (time_ev && pth_event_occurred (time_ev)))
+            {
+              if (pth_event_occurred (ev))
+                {
+#if defined(HAVE_W32_SYSTEM) && defined(PTH_EVENT_HANDLE)
+                  agent_sigusr2_action ();
 #else
-          if (pth_event_occurred (ev))
+                  handle_signal (signo);
 #endif
-            {
-              handle_signal (signo);
+                }
+              if (time_ev && pth_event_occurred (time_ev))
+                {
+                  pth_event_free (time_ev, PTH_FREE_ALL);
+                  time_ev = NULL;
+                  handle_tick ();
+                }
               continue;
-           }
-          log_error ("accept failed: %s - waiting 1s\n", strerror (errno));
-          pth_sleep(1);
+            }
+          log_error (_("pth_select failed: %s - waiting 1s\n"),
+                     strerror (errno));
+          pth_sleep (1);
           continue;
        }
 
-      if (!pth_spawn (tattr, start_connection_thread, (void*)fd))
+      if (pth_event_occurred (ev))
         {
-          log_error ("error spawning connection handler: %s\n",
-                     strerror (errno) );
-          close (fd);
+#if defined(HAVE_W32_SYSTEM) && defined(PTH_EVENT_HANDLE)
+          agent_sigusr2_action ();
+#else
+          handle_signal (signo);
+#endif
+        }
+
+      if (time_ev && pth_event_occurred (time_ev))
+        {
+          pth_event_free (time_ev, PTH_FREE_ALL);
+          time_ev = NULL;
+          handle_tick ();
+        }
+
+
+      /* We now might create new threads and because we don't want any
+         signals (as we are handling them here) to be delivered to a
+         new thread.  Thus we need to block those signals. */
+      pth_sigmask (SIG_BLOCK, &sigs, NULL);
+
+      if (!shutdown_pending && FD_ISSET (FD2INT (listen_fd), &read_fdset))
+       {
+          ctrl_t ctrl;
+
+          plen = sizeof paddr;
+         fd = INT2FD (pth_accept (FD2INT(listen_fd),
+                                   (struct sockaddr *)&paddr, &plen));
+         if (fd == GNUPG_INVALID_FD)
+           {
+             log_error ("accept failed: %s\n", strerror (errno));
+           }
+          else if ( !(ctrl = xtrycalloc (1, sizeof *ctrl)) )
+            {
+              log_error ("error allocating connection control data: %s\n",
+                         strerror (errno) );
+              assuan_sock_close (fd);
+            }
+          else 
+            {
+              char threadname[50];
+
+              snprintf (threadname, sizeof threadname-1,
+                        "conn fd=%d (gpg)", FD2INT(fd));
+              threadname[sizeof threadname -1] = 0;
+              pth_attr_set (tattr, PTH_ATTR_NAME, threadname);
+              ctrl->thread_startup.fd = fd;
+              if (!pth_spawn (tattr, start_connection_thread, ctrl))
+                {
+                  log_error ("error spawning connection handler: %s\n",
+                             strerror (errno) );
+                  assuan_sock_close (fd);
+                  xfree (ctrl);
+                }
+            }
+          fd = GNUPG_INVALID_FD;
+       }
+
+      if (!shutdown_pending && listen_fd_ssh != GNUPG_INVALID_FD 
+          && FD_ISSET ( FD2INT (listen_fd_ssh), &read_fdset))
+       {
+          ctrl_t ctrl;
+
+          plen = sizeof paddr;
+         fd = INT2FD(pth_accept (FD2INT(listen_fd_ssh),
+                                  (struct sockaddr *)&paddr, &plen));
+         if (fd == GNUPG_INVALID_FD)
+           {
+             log_error ("accept failed for ssh: %s\n", strerror (errno));
+           }
+          else if ( !(ctrl = xtrycalloc (1, sizeof *ctrl)) )
+            {
+              log_error ("error allocating connection control data: %s\n",
+                         strerror (errno) );
+              assuan_sock_close (fd);
+            }
+          else
+            {
+              char threadname[50];
+
+              agent_init_default_ctrl (ctrl);
+              snprintf (threadname, sizeof threadname-1,
+                        "conn fd=%d (ssh)", FD2INT(fd));
+              threadname[sizeof threadname -1] = 0;
+              pth_attr_set (tattr, PTH_ATTR_NAME, threadname);
+              ctrl->thread_startup.fd = fd;
+              if (!pth_spawn (tattr, start_connection_thread_ssh, ctrl) )
+                {
+                  log_error ("error spawning ssh connection handler: %s\n",
+                             strerror (errno) );
+                  assuan_sock_close (fd);
+                  xfree (ctrl);
+                }
+            }
+          fd = GNUPG_INVALID_FD;
        }
     }
 
   pth_event_free (ev, PTH_FREE_ALL);
+  if (time_ev)
+    pth_event_free (time_ev, PTH_FREE_ALL);
   cleanup ();
-  log_info ("%s %s stopped\n", strusage(11), strusage(13));
+  log_info (_("%s %s stopped\n"), strusage(11), strusage(13));
+}
+
+
+
+/* Helper for check_own_socket.  */
+static int
+check_own_socket_pid_cb (void *opaque, const void *buffer, size_t length)
+{
+  membuf_t *mb = opaque;
+  put_membuf (mb, buffer, length);
+  return 0;
+}
+
+
+/* The thread running the actual check.  We need to run this in a
+   separate thread so that check_own_thread can be called from the
+   timer tick.  */
+static void *
+check_own_socket_thread (void *arg)
+{
+  int rc;
+  char *sockname = arg;
+  assuan_context_t ctx;
+  membuf_t mb;
+  char *buffer;
+
+  check_own_socket_running++;
+
+  rc = assuan_socket_connect (&ctx, sockname, (pid_t)(-1));
+  xfree (sockname);
+  if (rc)
+    {
+      log_error ("can't connect my own socket: %s\n", gpg_strerror (rc));
+      goto leave;
+    }
+  init_membuf (&mb, 100);
+  rc = assuan_transact (ctx, "GETINFO pid", check_own_socket_pid_cb, &mb,
+                        NULL, NULL, NULL, NULL);
+  put_membuf (&mb, "", 1);
+  buffer = get_membuf (&mb, NULL);
+  if (rc || !buffer)
+    {
+      log_error ("sending command \"%s\" to my own socket failed: %s\n", 
+                 "GETINFO pid", gpg_strerror (rc));
+      rc = 1;
+    }
+  else if ( (pid_t)strtoul (buffer, NULL, 10) != getpid ())
+    {
+      log_error ("socket is now serviced by another server\n");
+      rc = 1;
+    }
+  else if (opt.verbose > 1)
+    log_error ("socket is still served by this server\n");
+    
+  xfree (buffer);
+  assuan_disconnect (ctx);
+
+ leave:
+  if (rc)
+    {
+      /* We may not remove the socket as it is now in use by another
+         server.  Setting the name to empty does this.  */
+      if (socket_name)
+        *socket_name = 0;
+      if (socket_name_ssh)
+        *socket_name_ssh = 0;
+      shutdown_pending = 2;
+      log_info ("this process is useless - shutting down\n");
+    }
+  check_own_socket_running--;
+  return NULL;
+}
+
+
+/* Check whether we are still listening on our own socket.  In case
+   another gpg-agent process started after us has taken ownership of
+   our socket, we woul linger around without any real taks.  Thus we
+   better check once in a while whether we are really needed.  */
+static void
+check_own_socket (void)
+{
+  char *sockname;
+  pth_attr_t tattr;
+
+  if (!use_standard_socket)
+    return; /* This check makes only sense in standard socket mode.  */
+
+  if (check_own_socket_running || shutdown_pending)
+    return;  /* Still running or already shutting down.  */
+
+  sockname = make_filename (opt.homedir, "S.gpg-agent", NULL);
+  if (!sockname)
+    return; /* Out of memory.  */
+
+  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, "check-own-socket");
+
+  if (!pth_spawn (tattr, check_own_socket_thread, sockname))
+      log_error ("error spawning check_own_socket_thread: %s\n",
+                 strerror (errno) );
+  pth_attr_destroy (tattr);
+}
+
+
+
+/* Figure out whether an agent is available and running. Prints an
+   error if not.  If SILENT is true, no messages are printed.  Usually
+   started with MODE 0.  Returns 0 if the agent is running. */
+static int
+check_for_running_agent (int silent, int mode)
+{
+  int rc;
+  char *infostr, *p;
+  assuan_context_t ctx;
+  int prot, pid;
+
+  if (!mode)
+    {
+      infostr = getenv ("GPG_AGENT_INFO");
+      if (!infostr || !*infostr)
+        {
+          if (!check_for_running_agent (silent, 1))
+            return 0; /* Okay, its running on the standard socket. */
+          if (!silent)
+            log_error (_("no gpg-agent running in this session\n"));
+          return -1;
+        }
+
+      infostr = xstrdup (infostr);
+      if ( !(p = strchr (infostr, PATHSEP_C)) || p == infostr)
+        {
+          xfree (infostr);
+          if (!check_for_running_agent (silent, 1))
+            return 0; /* Okay, its running on the standard socket. */
+          if (!silent)
+            log_error (_("malformed GPG_AGENT_INFO environment variable\n"));
+          return -1;
+        }
+
+      *p++ = 0;
+      pid = atoi (p);
+      while (*p && *p != PATHSEP_C)
+        p++;
+      prot = *p? atoi (p+1) : 0;
+      if (prot != 1)
+        {
+          xfree (infostr);
+          if (!silent)
+            log_error (_("gpg-agent protocol version %d is not supported\n"),
+                       prot);
+          if (!check_for_running_agent (silent, 1))
+            return 0; /* Okay, its running on the standard socket. */
+          return -1;
+        }
+    }
+  else /* MODE != 0 */
+    {
+      infostr = make_filename (opt.homedir, "S.gpg-agent", NULL);
+      pid = (pid_t)(-1);
+    }
+
+
+  rc = assuan_socket_connect (&ctx, infostr, pid);
+  xfree (infostr);
+  if (rc)
+    {
+      if (!mode && !check_for_running_agent (silent, 1))
+        return 0; /* Okay, its running on the standard socket. */
+
+      if (!mode && !silent)
+        log_error ("can't connect to the agent: %s\n", gpg_strerror (rc));
+      return -1;
+    }
+
+  if (!opt.quiet && !silent)
+    log_info ("gpg-agent running and available\n");
+
+  assuan_disconnect (ctx);
+  return 0;
 }
-#endif /*USE_GNU_PTH*/