scd: Fix an action after card removal.
[gnupg.git] / scd / scdaemon.c
index 135f097..514e3c2 100644 (file)
@@ -1,11 +1,12 @@
 /* scdaemon.c  -  The GnuPG Smartcard Daemon
- *     Copyright (C) 2001, 2002, 2004 Free Software Foundation, Inc.
+ * Copyright (C) 2001-2002, 2004-2005, 2007-2009 Free Software Foundation, Inc.
+ * Copyright (C) 2001-2002, 2004-2005, 2007-2014 Werner Koch
  *
  * 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>
 #endif /*HAVE_W32_SYSTEM*/
 #include <unistd.h>
 #include <signal.h>
-#ifdef USE_GNU_PTH
-# include <pth.h>
-#endif
+#include <npth.h>
 
-#define JNLIB_NEED_LOG_LOGV
+#define GNUPG_COMMON_NEED_AFLOCAL
 #include "scdaemon.h"
 #include <ksba.h>
 #include <gcrypt.h>
 #include "i18n.h"
 #include "sysutils.h"
 #include "app-common.h"
-#ifdef HAVE_W32_SYSTEM
-#include "../jnlib/w32-afunix.h"
+#include "iso7816.h"
+#include "apdu.h"
+#include "ccid-driver.h"
+#include "gc-opt-flags.h"
+#include "asshelp.h"
+#include "../common/init.h"
+
+#ifndef ENAMETOOLONG
+# define ENAMETOOLONG EINVAL
 #endif
 
-
-enum cmd_and_opt_values 
+enum cmd_and_opt_values
 { aNull = 0,
   oCsh           = 'c',
   oQuiet         = 'q',
   oSh            = 's',
   oVerbose       = 'v',
-  
+
   oNoVerbose = 500,
   aGPGConfList,
+  aGPGConfTest,
   oOptions,
   oDebug,
   oDebugAll,
   oDebugLevel,
   oDebugWait,
-  oDebugSC,
+  oDebugAllowCoreDump,
+  oDebugCCIDDriver,
+  oDebugLogTid,
+  oDebugAssuanLogCats,
   oNoGreeting,
   oNoOptions,
   oHomedir,
@@ -76,75 +84,124 @@ enum cmd_and_opt_values
   oNoGrab,
   oLogFile,
   oServer,
+  oMultiServer,
   oDaemon,
   oBatch,
   oReaderPort,
+  oCardTimeout,
   octapiDriver,
   opcscDriver,
   oDisableCCID,
   oDisableOpenSC,
+  oDisablePinpad,
   oAllowAdmin,
   oDenyAdmin,
   oDisableApplication,
-
-aTest };
+  oEnablePinpadVarlen,
+  oDebugDisableTicker
+};
 
 
 
 static ARGPARSE_OPTS opts[] = {
-
-  { aGPGConfList, "gpgconf-list", 256, "@" },
-  
-  { 301, NULL, 0, N_("@Options:\n ") },
-
-  { oServer,   "server",     0, N_("run in server mode (foreground)") },
-  { oDaemon,   "daemon",     0, N_("run in daemon mode (background)") },
-  { oVerbose, "verbose",   0, N_("verbose") },
-  { 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, "@"},
-  { oDebugAll, "debug-all"     ,0, "@"},
-  { oDebugLevel, "debug-level" ,2, "@"},
-  { oDebugWait,"debug-wait",1, "@"},
-  { oDebugSC,  "debug-sc",  1, N_("|N|set OpenSC debug level to N")},
-  { oNoDetach, "no-detach" ,0, N_("do not detach from the console")},
-  { oLogFile,  "log-file"   ,2, N_("use a log file for the server")},
-  { oReaderPort, "reader-port", 2, N_("|N|connect to reader at port N")},
-  { octapiDriver, "ctapi-driver", 2, N_("|NAME|use NAME as ct-API driver")},
-  { opcscDriver, "pcsc-driver", 2, N_("|NAME|use NAME as PC/SC driver")},
-  { oDisableCCID, "disable-ccid", 0,
+  ARGPARSE_c (aGPGConfList, "gpgconf-list", "@"),
+  ARGPARSE_c (aGPGConfTest, "gpgconf-test", "@"),
+
+  ARGPARSE_group (301, N_("@Options:\n ")),
+
+  ARGPARSE_s_n (oServer,"server", N_("run in server mode (foreground)")),
+  ARGPARSE_s_n (oMultiServer, "multi-server",
+                N_("run in multi server mode (foreground)")),
+  ARGPARSE_s_n (oDaemon, "daemon", N_("run in daemon mode (background)")),
+  ARGPARSE_s_n (oVerbose, "verbose", N_("verbose")),
+  ARGPARSE_s_n (oQuiet,        "quiet", N_("be somewhat more quiet")),
+  ARGPARSE_s_n (oSh,   "sh", N_("sh-style command output")),
+  ARGPARSE_s_n (oCsh,  "csh", N_("csh-style command output")),
+  ARGPARSE_s_s (oOptions, "options", N_("|FILE|read options from FILE")),
+  ARGPARSE_s_s (oDebug,        "debug", "@"),
+  ARGPARSE_s_n (oDebugAll, "debug-all", "@"),
+  ARGPARSE_s_s (oDebugLevel, "debug-level" ,
+                N_("|LEVEL|set the debugging level to LEVEL")),
+  ARGPARSE_s_i (oDebugWait, "debug-wait", "@"),
+  ARGPARSE_s_n (oDebugAllowCoreDump, "debug-allow-core-dump", "@"),
+  ARGPARSE_s_n (oDebugCCIDDriver, "debug-ccid-driver", "@"),
+  ARGPARSE_s_n (oDebugDisableTicker, "debug-disable-ticker", "@"),
+  ARGPARSE_s_n (oDebugLogTid, "debug-log-tid", "@"),
+  ARGPARSE_p_u (oDebugAssuanLogCats, "debug-assuan-log-cats", "@"),
+  ARGPARSE_s_n (oNoDetach, "no-detach", N_("do not detach from the console")),
+  ARGPARSE_s_s (oLogFile,  "log-file", N_("|FILE|write a log to FILE")),
+  ARGPARSE_s_s (oReaderPort, "reader-port",
+                N_("|N|connect to reader at port N")),
+  ARGPARSE_s_s (octapiDriver, "ctapi-driver",
+                N_("|NAME|use NAME as ct-API driver")),
+  ARGPARSE_s_s (opcscDriver, "pcsc-driver",
+                N_("|NAME|use NAME as PC/SC driver")),
+  ARGPARSE_s_n (oDisableCCID, "disable-ccid",
 #ifdef HAVE_LIBUSB
                                     N_("do not use the internal CCID driver")
 #else
                                     "@"
 #endif
-                                         /* end --disable-ccid */},
-  { oDisableOpenSC, "disable-opensc", 0,
-#ifdef HAVE_OPENSC
-                                    N_("do not use the OpenSC layer")
-#else
-                                    "@"
-#endif
-                                         /* end --disable-opensc */},
-  { oAllowAdmin, "allow-admin", 0, N_("allow the use of admin card commands")},
-  { oDenyAdmin,  "deny-admin",  0, "@" },  
-  { oDisableApplication, "disable-application", 2, "@"},
-
-  {0}
+                /* end --disable-ccid */),
+  ARGPARSE_s_u (oCardTimeout, "card-timeout",
+                N_("|N|disconnect the card after N seconds of inactivity")),
+
+  ARGPARSE_s_n (oDisablePinpad, "disable-pinpad",
+                N_("do not use a reader's pinpad")),
+  ARGPARSE_ignore (300, "disable-keypad"),
+
+  ARGPARSE_s_n (oAllowAdmin, "allow-admin", "@"),
+  ARGPARSE_s_n (oDenyAdmin, "deny-admin",
+                N_("deny the use of admin card commands")),
+  ARGPARSE_s_s (oDisableApplication, "disable-application", "@"),
+  ARGPARSE_s_n (oEnablePinpadVarlen, "enable-pinpad-varlen",
+                N_("use variable length input for pinpad")),
+  ARGPARSE_s_s (oHomedir,    "homedir",      "@"),
+
+  ARGPARSE_end ()
 };
 
 
-/* The card dirver we use by default for PC/SC.  */
-#ifdef HAVE_W32_SYSTEM
+/* The list of supported debug flags.  */
+static struct debug_flags_s debug_flags [] =
+  {
+    { DBG_COMMAND_VALUE, "command"  },
+    { DBG_MPI_VALUE    , "mpi"     },
+    { DBG_CRYPTO_VALUE , "crypto"  },
+    { DBG_MEMORY_VALUE , "memory"  },
+    { DBG_CACHE_VALUE  , "cache"   },
+    { DBG_MEMSTAT_VALUE, "memstat" },
+    { DBG_HASHING_VALUE, "hashing" },
+    { DBG_IPC_VALUE    , "ipc"     },
+    { DBG_CARD_IO_VALUE, "cardio"  },
+    { DBG_READER_VALUE , "reader"  },
+    { 0, NULL }
+  };
+
+
+/* The card driver we use by default for PC/SC.  */
+#if defined(HAVE_W32_SYSTEM) || defined(__CYGWIN__)
 #define DEFAULT_PCSC_DRIVER "winscard.dll"
+#elif defined(__APPLE__)
+#define DEFAULT_PCSC_DRIVER "/System/Library/Frameworks/PCSC.framework/PCSC"
+#elif defined(__GLIBC__)
+#define DEFAULT_PCSC_DRIVER "libpcsclite.so.1"
 #else
 #define DEFAULT_PCSC_DRIVER "libpcsclite.so"
 #endif
 
+/* The timer tick used for housekeeping stuff.  We poll every 500ms to
+   let the user immediately know a status change.
 
-static volatile int caught_fatal_sig = 0;
+   This is not too good for power saving but given that there is no
+   easy way to block on card status changes it is the best we can do.
+   For PC/SC we could in theory use an extra thread to wait for status
+   changes but that requires a native thread because there is no way
+   to make the underlying PC/SC card change function block using a Npth
+   mechanism.  Given that a native thread could only be used under W32
+   we don't do that at all.  */
+#define TIMERTICK_INTERVAL_SEC     (0)
+#define TIMERTICK_INTERVAL_USEC    (500000)
 
 /* Flag to indicate that a shutdown was requested. */
 static int shutdown_pending;
@@ -152,80 +209,105 @@ static int shutdown_pending;
 /* It is possible that we are currently running under setuid permissions */
 static int maybe_setuid = 1;
 
+/* Flag telling whether we are running as a pipe server.  */
+static int pipe_server;
+
 /* Name of the communication socket */
-static char socket_name[128];
+static char *socket_name;
+/* Name of the redirected socket or NULL.  */
+static char *redir_socket_name;
 
+/* We need to keep track of the server's nonces (these are dummies for
+   POSIX systems). */
+static assuan_sock_nonce_t socket_nonce;
+
+/* Debug flag to disable the ticker.  The ticker is in fact not
+   disabled but it won't perform any ticker specific actions. */
+static int ticker_disabled;
+
+
+\f
+static char *create_socket_name (char *standard_name);
+static gnupg_fd_t create_server_socket (const char *name,
+                                        char **r_redir_name,
+                                        assuan_sock_nonce_t *nonce);
+
+static void *start_connection_thread (void *arg);
+static void handle_connections (int listen_fd);
 
-#ifndef HAVE_OPENSC
-#ifdef USE_GNU_PTH
-#ifndef HAVE_W32_SYSTEM
 /* Pth wrapper function definitions. */
-GCRY_THREAD_OPTION_PTH_IMPL;
-#endif
+ASSUAN_SYSTEM_NPTH_IMPL;
+
+static int active_connections;
+
+\f
+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 void *ticker_thread (void *arg);
-#endif /*USE_GNU_PTH*/
-#endif /*!HAVE_OPENSC*/
 
 static const char *
 my_strusage (int level)
 {
+  static char *ver_gcry, *ver_ksba;
   const char *p;
+
   switch (level)
     {
-    case 11: p = "scdaemon (GnuPG)";
+    case 11: p = "@SCDAEMON@ (@GNUPG@)";
       break;
     case 13: p = VERSION; break;
     case 17: p = PRINTABLE_OS_NAME; break;
-    case 19: p = _("Please report bugs to <" PACKAGE_BUGREPORT ">.\n");
+    case 19: p = _("Please report bugs to <@EMAIL@>.\n"); break;
+
+    case 20:
+      if (!ver_gcry)
+        ver_gcry = make_libversion ("libgcrypt", gcry_check_version);
+      p = ver_gcry;
+      break;
+    case 21:
+      if (!ver_ksba)
+        ver_ksba = make_libversion ("libksba", ksba_check_version);
+      p = ver_ksba;
       break;
     case 1:
-    case 40: p =  _("Usage: scdaemon [options] (-h for help)");
+    case 40: p =  _("Usage: @SCDAEMON@ [options] (-h for help)");
       break;
     case 41: p =  _("Syntax: scdaemon [options] [command [args]]\n"
-                    "Smartcard daemon for GnuPG\n");
+                    "Smartcard daemon for @GNUPG@\n");
     break;
-    
+
     default: p = NULL;
     }
   return p;
 }
 
 
-
-static void
-i18n_init (void)
+static int
+tid_log_callback (unsigned long *rvalue)
 {
-#ifdef USE_SIMPLE_GETTEXT
-    set_gettext_file( PACKAGE_GT );
-#else
-#ifdef ENABLE_NLS
-    setlocale (LC_ALL, "");
-    bindtextdomain (PACKAGE_GT, LOCALEDIR);
-    textdomain (PACKAGE_GT);
-#endif
-#endif
-}
-
+  int len = sizeof (*rvalue);
+  npth_t thread;
 
+  thread = npth_self ();
+  if (sizeof (thread) < len)
+    len = sizeof (thread);
+  memcpy (rvalue, &thread, len);
 
-/* Used by gcry for logging */
-static void
-my_gcry_logger (void *dummy, int level, const char *fmt, va_list arg_ptr)
-{
-  /* translate the log levels */
-  switch (level)
-    {
-    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_logv (level, fmt, arg_ptr);
+  return 2; /* Use use hex representation.  */
 }
 
 
@@ -236,22 +318,33 @@ my_gcry_logger (void *dummy, int level, const char *fmt, va_list arg_ptr)
 static void
 set_debug (const char *level)
 {
+  int numok = (level && digitp (level));
+  int numlvl = numok? atoi (level) : 0;
+
   if (!level)
     ;
-  else if (!strcmp (level, "none"))
+  else if (!strcmp (level, "none") || (numok && numlvl < 1))
     opt.debug = 0;
-  else if (!strcmp (level, "basic"))
-    opt.debug = DBG_ASSUAN_VALUE;
-  else if (!strcmp (level, "advanced"))
-    opt.debug = DBG_ASSUAN_VALUE|DBG_COMMAND_VALUE;
-  else if (!strcmp (level, "expert"))
-    opt.debug = (DBG_ASSUAN_VALUE|DBG_COMMAND_VALUE
+  else if (!strcmp (level, "basic") || (numok && numlvl <= 2))
+    opt.debug = DBG_IPC_VALUE;
+  else if (!strcmp (level, "advanced") || (numok && numlvl <= 5))
+    opt.debug = DBG_IPC_VALUE|DBG_COMMAND_VALUE;
+  else if (!strcmp (level, "expert") || (numok && numlvl <= 8))
+    opt.debug = (DBG_IPC_VALUE|DBG_COMMAND_VALUE
                  |DBG_CACHE_VALUE|DBG_CARD_IO_VALUE);
-  else if (!strcmp (level, "guru"))
-    opt.debug = ~0;
+  else if (!strcmp (level, "guru") || numok)
+    {
+      opt.debug = ~0;
+      /* Unless the "guru" string has been used we don't want to allow
+         hashing debugging.  The rationale is that people tend to
+         select the highest debug value and would then clutter their
+         disk with debug files which may reveal confidential data.  */
+      if (numok)
+        opt.debug &= ~(DBG_HASHING_VALUE);
+    }
   else
     {
-      log_error (_("invalid debug-level `%s' given\n"), level);
+      log_error (_("invalid debug-level '%s' given\n"), level);
       scd_exit(2);
     }
 
@@ -266,70 +359,45 @@ set_debug (const char *level)
   if (opt.debug & DBG_CRYPTO_VALUE )
     gcry_control (GCRYCTL_SET_DEBUG_FLAGS, 1);
   gcry_control (GCRYCTL_SET_VERBOSITY, (int)opt.verbose);
+
+  if (opt.debug)
+    parse_debug_flag (NULL, &opt.debug, debug_flags);
 }
+
 
 
 static void
 cleanup (void)
 {
-  if (*socket_name)
+  if (socket_name && *socket_name)
     {
-      char *p;
+      char *name;
 
-      remove (socket_name);
-      p = strrchr (socket_name, '/');
-      if (p)
-        {
-          *p = 0;
-          rmdir (socket_name);
-          *p = '/';
-        }
+      name = redir_socket_name? redir_socket_name : socket_name;
+
+      gnupg_remove (name);
       *socket_name = 0;
     }
 }
 
 
-static RETSIGTYPE
-cleanup_sh (int sig)
-{
-  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 );
-}
 
 int
 main (int argc, char **argv )
 {
   ARGPARSE_ARGS pargs;
   int orig_argc;
-  gpg_error_t err;
-  int may_coredump;
   char **orig_argv;
   FILE *configfp = NULL;
   char *configname = NULL;
   const char *shell;
-  unsigned configlineno;
+  unsigned int configlineno;
   int parse_debug = 0;
   const char *debug_level = NULL;
   int default_config =1;
   int greeting = 0;
   int nogreeting = 0;
-  int pipe_server = 0;
+  int multi_server = 0;
   int is_daemon = 0;
   int nodetach = 0;
   int csh_style = 0;
@@ -337,70 +405,50 @@ main (int argc, char **argv )
   int debug_wait = 0;
   int gpgconf_list = 0;
   const char *config_filename = NULL;
+  int allow_coredump = 0;
+  struct assuan_malloc_hooks malloc_hooks;
+  int res;
+  npth_t pipecon_handler;
 
+  early_system_init ();
   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 ("scdaemon", 1|4); 
-  /* Try to auto set the character set.  */
-  set_native_charset (NULL); 
+  log_set_prefix ("scdaemon", GPGRT_LOG_WITH_PREFIX | GPGRT_LOG_WITH_PID);
 
+  /* Make sure that our subsystems are ready.  */
   i18n_init ();
+  init_common_subsystems (&argc, &argv);
 
-  /* Libgcrypt requires us to register the threading model first.
-     Note that this will also do the pth_init. */
-#ifndef HAVE_OPENSC
-#ifdef USE_GNU_PTH
-# ifdef HAVE_W32_SYSTEM
-  pth_init ();
-# else /*!HAVE_W32_SYSTEM*/
-  err = gcry_control (GCRYCTL_SET_THREAD_CBS, &gcry_threads_pth);
-  if (err)
-    {
-      log_fatal ("can't register GNU Pth with Libgcrypt: %s\n",
-                 gpg_strerror (err));
-    }
-# endif /*!HAVE_W32_SYSTEM*/
-#endif /*USE_GNU_PTH*/
-#endif /*!HAVE_OPENSC*/
-
-  /* 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"),
-                 NEED_LIBGCRYPT_VERSION, gcry_check_version (NULL) );
-    }
+  npth_init ();
 
   ksba_set_malloc_hooks (gcry_malloc, gcry_realloc, gcry_free);
 
-  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));
+  malloc_hooks.malloc = gcry_malloc;
+  malloc_hooks.realloc = gcry_realloc;
+  malloc_hooks.free = gcry_free;
+  assuan_set_malloc_hooks (&malloc_hooks);
+  assuan_set_gpg_err_source (GPG_ERR_SOURCE_DEFAULT);
+  assuan_set_system_hooks (ASSUAN_SYSTEM_NPTH);
+  assuan_sock_init ();
+  setup_libassuan_logging (&opt.debug);
 
-  gcry_set_log_handler (my_gcry_logger, NULL);
+  setup_libgcrypt_logging ();
   gcry_control (GCRYCTL_USE_SECURE_RNDPOOL);
 
-  may_coredump = disable_core_dumps ();
+  disable_core_dumps ();
 
   /* Set default options. */
-  opt.pcsc_driver = DEFAULT_PCSC_DRIVER; 
-
+  opt.allow_admin = 1;
+  opt.pcsc_driver = DEFAULT_PCSC_DRIVER;
 
   shell = getenv ("SHELL");
   if (shell && strlen (shell) >= 3 && !strcmp (shell+strlen (shell)-3, "csh") )
     csh_style = 1;
-  
-  /* FIXME: Using this homedir option does only make sense when not
-     running as a system service.  We might want to check for this by
-     looking at the uid or ebtter use an explict option for this */
-  opt.homedir = getenv("GNUPGHOME");
-  if (!opt.homedir || !*opt.homedir)
-    opt.homedir = GNUPG_DEFAULT_HOMEDIR;
-
-  /* check whether we have a config file on the commandline */
+
+  /* Check whether we have a config file on the commandline */
   orig_argc = argc;
   orig_argv = argv;
   pargs.argc = &argc;
@@ -419,22 +467,23 @@ main (int argc, char **argv )
        else if (pargs.r_opt == oNoOptions)
           default_config = 0; /* --no-options */
        else if (pargs.r_opt == oHomedir)
-          opt.homedir = pargs.r.ret_str;
+          gnupg_set_homedir (pargs.r.ret_str);
     }
 
   /* initialize the secure memory. */
   gcry_control (GCRYCTL_INIT_SECMEM, 16384, 0);
   maybe_setuid = 0;
 
-  /* 
-     Now we are working under our real uid 
+  /*
+     Now we are working under our real uid
   */
 
 
   if (default_config)
-    configname = make_filename (opt.homedir, "scdaemon.conf", NULL );
+    configname = make_filename (gnupg_homedir (), SCDAEMON_NAME EXTSEP_S "conf",
+                                NULL );
+
 
-  
   argc = orig_argc;
   argv = orig_argv;
   pargs.argc = &argc;
@@ -450,20 +499,20 @@ main (int argc, char **argv )
           if (default_config)
             {
               if( parse_debug )
-                log_info (_("NOTE: no default option file `%s'\n"),
+                log_info (_("Note: no default option file '%s'\n"),
                           configname );
            }
           else
             {
-              log_error (_("option file `%s': %s\n"),
+              log_error (_("option file '%s': %s\n"),
                          configname, strerror(errno) );
               exit(2);
            }
-          xfree (configname); 
+          xfree (configname);
           configname = NULL;
        }
       if (parse_debug && configname )
-        log_info (_("reading options from `%s'\n"), configname );
+        log_info (_("reading options from '%s'\n"), configname );
       default_config = 0;
     }
 
@@ -472,15 +521,37 @@ main (int argc, char **argv )
       switch (pargs.r_opt)
         {
         case aGPGConfList: gpgconf_list = 1; break;
+        case aGPGConfTest: gpgconf_list = 2; break;
         case oQuiet: opt.quiet = 1; break;
         case oVerbose: opt.verbose++; break;
         case oBatch: opt.batch=1; break;
 
-        case oDebug: opt.debug |= pargs.r.ret_ulong; break;
+        case oDebug:
+          if (parse_debug_flag (pargs.r.ret_str, &opt.debug, debug_flags))
+            {
+              pargs.r_opt = ARGPARSE_INVALID_ARG;
+              pargs.err = ARGPARSE_PRINT_ERROR;
+            }
+          break;
         case oDebugAll: opt.debug = ~0; break;
         case oDebugLevel: debug_level = pargs.r.ret_str; break;
         case oDebugWait: debug_wait = pargs.r.ret_int; break;
-        case oDebugSC: opt.debug_sc = pargs.r.ret_int; break;
+        case oDebugAllowCoreDump:
+          enable_core_dumps ();
+          allow_coredump = 1;
+          break;
+        case oDebugCCIDDriver:
+#ifdef HAVE_LIBUSB
+          ccid_set_debug_level (ccid_set_debug_level (-1)+1);
+#endif /*HAVE_LIBUSB*/
+          break;
+        case oDebugDisableTicker: ticker_disabled = 1; break;
+        case oDebugLogTid:
+          log_set_pid_suffix_cb (tid_log_callback);
+          break;
+        case oDebugAssuanLogCats:
+          set_libassuan_log_cats (pargs.r.ret_ulong);
+          break;
 
         case oOptions:
           /* config files may not be nested (silently ignore them) */
@@ -494,28 +565,38 @@ main (int argc, char **argv )
         case oNoGreeting: nogreeting = 1; break;
         case oNoVerbose: opt.verbose = 0; break;
         case oNoOptions: break; /* no-options */
-        case oHomedir: opt.homedir = pargs.r.ret_str; break;
+        case oHomedir: gnupg_set_homedir (pargs.r.ret_str); break;
         case oNoDetach: nodetach = 1; break;
         case oLogFile: logfile = pargs.r.ret_str; break;
         case oCsh: csh_style = 1; break;
         case oSh: csh_style = 0; break;
         case oServer: pipe_server = 1; break;
+        case oMultiServer: pipe_server = 1; multi_server = 1; break;
         case oDaemon: is_daemon = 1; break;
 
         case oReaderPort: opt.reader_port = pargs.r.ret_str; break;
         case octapiDriver: opt.ctapi_driver = pargs.r.ret_str; break;
         case opcscDriver: opt.pcsc_driver = pargs.r.ret_str; break;
         case oDisableCCID: opt.disable_ccid = 1; break;
-        case oDisableOpenSC: opt.disable_opensc = 1; break;
+        case oDisableOpenSC: break;
+
+        case oDisablePinpad: opt.disable_pinpad = 1; break;
 
-        case oAllowAdmin: opt.allow_admin = 1; break;
+        case oAllowAdmin: /* Dummy because allow is now the default.  */
+          break;
         case oDenyAdmin: opt.allow_admin = 0; break;
 
+        case oCardTimeout: opt.card_timeout = pargs.r.ret_ulong; break;
+
         case oDisableApplication:
-          add_to_strlist (&opt.disabled_applications, pargs.r.ret_str); 
+          add_to_strlist (&opt.disabled_applications, pargs.r.ret_str);
           break;
 
-        default : pargs.err = configfp? 1:2; break;
+       case oEnablePinpadVarlen: opt.enable_pinpad_varlen = 1; break;
+
+        default:
+          pargs.err = configfp? ARGPARSE_PRINT_WARNING:ARGPARSE_PRINT_ERROR;
+          break;
        }
     }
   if (configfp)
@@ -536,15 +617,24 @@ main (int argc, char **argv )
 
   if (greeting)
     {
-      fprintf (stderr, "%s %s; %s\n",
-                 strusage(11), strusage(13), strusage(14) );
-      fprintf (stderr, "%s\n", strusage(15) );
+      es_fprintf (es_stderr, "%s %s; %s\n",
+                  strusage(11), strusage(13), strusage(14) );
+      es_fprintf (es_stderr, "%s\n", strusage(15) );
     }
 #ifdef IS_DEVELOPMENT_VERSION
   log_info ("NOTE: this is a development version!\n");
 #endif
 
+  /* Print a warning if an argument looks like an option.  */
+  if (!opt.quiet && !(pargs.flags & ARGPARSE_FLAG_STOP_SEEN))
+    {
+      int i;
+
+      for (i=0; i < argc; i++)
+        if (argv[i][0] == '-' && argv[i][1] == '-')
+          log_info (_("Note: '%s' is not considered an option\n"), argv[i]);
+    }
+
   if (atexit (cleanup))
     {
       log_error ("atexit failed\n");
@@ -554,199 +644,181 @@ main (int argc, char **argv )
 
   set_debug (debug_level);
 
-  if (debug_wait && pipe_server)
-    {
-      log_debug ("waiting for debugger - my pid is %u .....\n",
-                 (unsigned int)getpid());
-      sleep (debug_wait);
-      log_debug ("... okay\n");
-    }
-  
+  initialize_module_command ();
+
+  if (gpgconf_list == 2)
+    scd_exit (0);
   if (gpgconf_list)
     {
       /* List options and default values in the GPG Conf format.  */
+      char *filename = NULL;
+      char *filename_esc;
 
-      /* The following list is taken from gnupg/tools/gpgconf-comp.c.  */
-      /* Option flags.  YOU MUST NOT CHANGE THE NUMBERS OF THE EXISTING
-         FLAGS, AS THEY ARE PART OF THE EXTERNAL INTERFACE.  */
-#define GC_OPT_FLAG_NONE       0UL
-      /* The RUNTIME flag for an option indicates that the option can be
-         changed at runtime.  */
-#define GC_OPT_FLAG_RUNTIME    (1UL << 3)
-      /* The DEFAULT flag for an option indicates that the option has a
-         default value.  */
-#define GC_OPT_FLAG_DEFAULT    (1UL << 4)
-      /* The DEF_DESC flag for an option indicates that the option has a
-         default, which is described by the value of the default field.  */
-#define GC_OPT_FLAG_DEF_DESC   (1UL << 5)
-      /* The NO_ARG_DESC flag for an option indicates that the argument has
-         a default, which is described by the value of the ARGDEF field.  */
-#define GC_OPT_FLAG_NO_ARG_DESC        (1UL << 6)
-      if (!config_filename)
-        config_filename = make_filename (opt.homedir, "scdaemon.conf", NULL );
-
-      printf ("gpgconf-scdaemon.conf:%lu:\"%s\n",
-              GC_OPT_FLAG_DEFAULT, config_filename);
-        
-      printf ("verbose:%lu:\n"
-              "quiet:%lu:\n"
-              "debug-level:%lu:\"none:\n"
-              "log-file:%lu:\n",
-              GC_OPT_FLAG_NONE,
-              GC_OPT_FLAG_NONE,
-              GC_OPT_FLAG_DEFAULT,
-              GC_OPT_FLAG_NONE );
-
-      printf ("reader-port:%lu:\n", GC_OPT_FLAG_NONE );
-      printf ("ctapi-driver:%lu:\n", GC_OPT_FLAG_NONE );
-      printf ("pcsc-driver:%lu:\"%s:\n",
+      if (config_filename)
+       filename = xstrdup (config_filename);
+      else
+        filename = make_filename (gnupg_homedir (),
+                                  SCDAEMON_NAME EXTSEP_S "conf", NULL);
+      filename_esc = percent_escape (filename, NULL);
+
+      es_printf ("%s-%s.conf:%lu:\"%s\n",
+                 GPGCONF_NAME, SCDAEMON_NAME,
+                 GC_OPT_FLAG_DEFAULT, filename_esc);
+      xfree (filename_esc);
+      xfree (filename);
+
+      es_printf ("verbose:%lu:\n"
+                 "quiet:%lu:\n"
+                 "debug-level:%lu:\"none:\n"
+                 "log-file:%lu:\n",
+                 GC_OPT_FLAG_NONE,
+                 GC_OPT_FLAG_NONE,
+                 GC_OPT_FLAG_DEFAULT,
+                 GC_OPT_FLAG_NONE );
+
+      es_printf ("reader-port:%lu:\n", GC_OPT_FLAG_NONE );
+      es_printf ("ctapi-driver:%lu:\n", GC_OPT_FLAG_NONE );
+      es_printf ("pcsc-driver:%lu:\"%s:\n",
               GC_OPT_FLAG_DEFAULT, DEFAULT_PCSC_DRIVER );
 #ifdef HAVE_LIBUSB
-      printf ("disable-ccid:%lu:\n", GC_OPT_FLAG_NONE );
-#endif
-#ifdef HAVE_LIBUSB
-      printf ("disable-opensc:%lu:\n", GC_OPT_FLAG_NONE );
+      es_printf ("disable-ccid:%lu:\n", GC_OPT_FLAG_NONE );
 #endif
-      printf ("allow-admin:%lu:\n", GC_OPT_FLAG_NONE );
-
+      es_printf ("deny-admin:%lu:\n", GC_OPT_FLAG_NONE );
+      es_printf ("disable-pinpad:%lu:\n", GC_OPT_FLAG_NONE );
+      es_printf ("card-timeout:%lu:%d:\n", GC_OPT_FLAG_DEFAULT, 0);
+      es_printf ("enable-pinpad-varlen:%lu:\n", GC_OPT_FLAG_NONE );
 
       scd_exit (0);
     }
 
-  /* 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, GPGRT_LOG_WITH_PREFIX | GPGRT_LOG_WITH_TIME | GPGRT_LOG_WITH_PID);
     }
 
-
-  if (pipe_server)
-    { /* This is the simple pipe based server */
-#ifndef HAVE_OPENSC
-#ifdef USE_GNU_PTH
-      pth_attr_t tattr;
-      tattr = pth_attr_new();
-      pth_attr_set (tattr, PTH_ATTR_JOINABLE, 0);
-      pth_attr_set (tattr, PTH_ATTR_STACK_SIZE, 512*1024);
-      pth_attr_set (tattr, PTH_ATTR_NAME, "ticker");
-
-      if (!pth_spawn (tattr, ticker_thread, NULL))
-        {
-          log_error ("error spawning ticker thread: %s\n", strerror (errno));
-          scd_exit (2);
-        }
-#endif /*USE_GNU_PTH*/
-#endif /*!HAVE_OPENSC*/
-      scd_command_handler (-1);
-    }
-  else if (!is_daemon)
+  if (debug_wait && pipe_server)
     {
-      log_info (_("please use the option `--daemon'"
-                  " to run the program in the background\n"));
+      log_debug ("waiting for debugger - my pid is %u .....\n",
+                 (unsigned int)getpid());
+      gnupg_sleep (debug_wait);
+      log_debug ("... okay\n");
     }
-  else
-    { /* regular server mode */
-      int fd;
-      pid_t pid;
-      int i;
-      int len;
-      struct sockaddr_un serv_addr;
-      char *p;
 
-      /* fixme: if there is already a running gpg-agent we should
-         share the same directory - and vice versa */
-      *socket_name = 0;
-      snprintf (socket_name, DIM(socket_name)-1,
-                "/tmp/gpg-XXXXXX/S.scdaemon");
-      socket_name[DIM(socket_name)-1] = 0;
-      p = strrchr (socket_name, '/');
-      if (!p)
-        BUG ();
-      *p = 0;;
+  if (pipe_server)
+    {
+      /* This is the simple pipe based server */
+      ctrl_t ctrl;
+      npth_attr_t tattr;
+      int fd = -1;
 
 #ifndef HAVE_W32_SYSTEM
-      if (!mkdtemp(socket_name))
-        {
-          log_error ("can't create directory `%s': %s\n",
-                    socket_name, strerror(errno) );
-          exit (1);
-        }
+      {
+        struct sigaction sa;
+
+        sa.sa_handler = SIG_IGN;
+        sigemptyset (&sa.sa_mask);
+        sa.sa_flags = 0;
+        sigaction (SIGPIPE, &sa, NULL);
+      }
 #endif
-      *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 ) 
+      /* If --debug-allow-core-dump has been given we also need to
+         switch the working directory to a place where we can actually
+         write. */
+      if (allow_coredump)
         {
-          log_error ("name of socket to long\n");
-          exit (1);
+          if (chdir("/tmp"))
+            log_debug ("chdir to '/tmp' failed: %s\n", strerror (errno));
+          else
+            log_debug ("changed working directory to '/tmp'\n");
         }
-   
 
-#ifdef HAVE_W32_SYSTEM
-      fd = _w32_sock_new (AF_UNIX, SOCK_STREAM, 0);
-#else
-      fd = socket (AF_UNIX, SOCK_STREAM, 0);
-#endif
-      if (fd == -1)
+      /* In multi server mode we need to listen on an additional
+         socket.  Create that socket now before starting the handler
+         for the pipe connection.  This allows that handler to send
+         back the name of that socket. */
+      if (multi_server)
         {
-          log_error ("can't create socket: %s\n", strerror(errno) );
-          exit (1);
+          socket_name = create_socket_name (SCDAEMON_SOCK_NAME);
+          fd = FD2INT(create_server_socket (socket_name,
+                                            &redir_socket_name, &socket_nonce));
         }
 
-      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);
+      res = npth_attr_init (&tattr);
+      if (res)
+       {
+          log_error ("error allocating thread attributes: %s\n",
+                     strerror (res));
+          scd_exit (2);
+        }
+      npth_attr_setdetachstate (&tattr, NPTH_CREATE_DETACHED);
 
-      if (
-#ifdef HAVE_W32_SYSTEM
-          _w32_sock_bind
-#else
-          bind 
-#endif
-          (fd, (struct sockaddr*)&serv_addr, len) == -1)
+      ctrl = xtrycalloc (1, sizeof *ctrl);
+      if ( !ctrl )
         {
-          log_error ("error binding socket to `%s': %s\n",
-                     serv_addr.sun_path, strerror (errno) );
-          close (fd);
-          exit (1);
+          log_error ("error allocating connection control data: %s\n",
+                     strerror (errno) );
+          scd_exit (2);
         }
-  
-      if (listen (fd, 5 ) == -1)
+      ctrl->thread_startup.fd = GNUPG_INVALID_FD;
+      res = npth_create (&pipecon_handler, &tattr, start_connection_thread, ctrl);
+      if (res)
         {
-          log_error ("listen() failed: %s\n", strerror (errno));
-          close (fd);
-          exit (1);
+          log_error ("error spawning pipe connection handler: %s\n",
+                     strerror (res) );
+          xfree (ctrl);
+          scd_exit (2);
         }
+      npth_setname_np (pipecon_handler, "pipe-connection");
+      npth_attr_destroy (&tattr);
+
+      /* We run handle_connection to wait for the shutdown signal and
+         to run the ticker stuff.  */
+      handle_connections (fd);
+      if (fd != -1)
+        close (fd);
+    }
+  else if (!is_daemon)
+    {
+      log_info (_("please use the option '--daemon'"
+                  " to run the program in the background\n"));
+    }
+  else
+    { /* Regular server mode */
+      int fd;
+#ifndef HAVE_W32_SYSTEM
+      pid_t pid;
+      int i;
+#endif
 
-      if (opt.verbose)
-        log_info ("listening on socket `%s'\n", socket_name );
+      /* Create the socket.  */
+      socket_name = create_socket_name (SCDAEMON_SOCK_NAME);
+      fd = FD2INT (create_server_socket (socket_name,
+                                         &redir_socket_name, &socket_nonce));
 
 
       fflush (NULL);
-#ifndef HAVE_W32_SYSTEM
+#ifdef HAVE_W32_SYSTEM
+      (void)csh_style;
+      (void)nodetach;
+#else
       pid = fork ();
-      if (pid == (pid_t)-1) 
+      if (pid == (pid_t)-1)
         {
           log_fatal ("fork failed: %s\n", strerror (errno) );
           exit (1);
         }
-      else if (pid) 
+      else if (pid)
         { /* we are the parent */
           char *infostr;
-          
+
           close (fd);
-          
+
           /* create the info string: <name>:<pid>:<protocol_version> */
-          if (asprintf (&infostr, "SCDAEMON_INFO=%s:%lu:1",
-                        socket_name, (ulong)pid ) < 0)
+          if (gpgrt_asprintf (&infostr, "SCDAEMON_INFO=%s:%lu:1",
+                              socket_name, (ulong) pid) < 0)
             {
               log_error ("out of core\n");
               kill (pid, SIGTERM);
@@ -754,7 +826,7 @@ main (int argc, char **argv )
             }
           *socket_name = 0; /* don't let cleanup() remove the socket -
                                the child should do this from now on */
-          if (argc) 
+          if (argc)
             { /* run the program given on the commandline */
               if (putenv (infostr))
                 {
@@ -770,31 +842,32 @@ 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);
+                  es_printf ( "setenv %s;\n", infostr);
                 }
               else
                 {
-                  printf ( "%s; export SCDAEMON_INFO;\n", infostr);
+                  es_printf ( "%s; export SCDAEMON_INFO;\n", infostr);
                 }
-              free (infostr);
-              exit (0); 
+              xfree (infostr);
+              exit (0);
             }
           /* NOTREACHED */
         } /* end parent */
-      
-      /* this is the child */
 
-      /* detach from tty and put process into a new session */
+      /* This is the child. */
+
+      /* Detach from tty and put process into a new session. */
       if (!nodetach )
-        {  /* close stdin, stdout and stderr unless it is the log stream */
-          for (i=0; i <= 2; i++) 
+        {
+          /* Close stdin, stdout and stderr unless it is the log stream. */
+          for (i=0; i <= 2; i++)
             {
-              if ( log_get_fd () != i)
+              if ( log_test_fd (i) && i != fd)
                 close (i);
             }
           if (setsid() == -1)
@@ -805,23 +878,13 @@ main (int argc, char **argv )
             }
         }
 
-      /* 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);
+        struct sigaction sa;
+
+        sa.sa_handler = SIG_IGN;
+        sigemptyset (&sa.sa_mask);
+        sa.sa_flags = 0;
+        sigaction (SIGPIPE, &sa, NULL);
       }
 
       if (chdir("/"))
@@ -832,17 +895,18 @@ main (int argc, char **argv )
 
 #endif /*!HAVE_W32_SYSTEM*/
 
-      scd_command_handler (fd);
+      handle_connections (fd);
 
       close (fd);
     }
-  
+
   return 0;
 }
 
 void
 scd_exit (int rc)
 {
+  apdu_prepare_exit ();
 #if 0
 #warning no update_random_seed_file
   update_random_seed_file();
@@ -863,30 +927,52 @@ scd_exit (int rc)
 }
 
 
-void
-scd_init_default_ctrl (CTRL ctrl)
+static void
+scd_init_default_ctrl (ctrl_t ctrl)
+{
+  (void)ctrl;
+}
+
+static void
+scd_deinit_default_ctrl (ctrl_t ctrl)
 {
-  ctrl->reader_slot = -1;
+  if (!ctrl)
+    return;
+  xfree (ctrl->in_data.value);
+  ctrl->in_data.value = NULL;
+  ctrl->in_data.valuelen = 0;
 }
 
 
-#ifndef HAVE_OPENSC
-#ifdef USE_GNU_PTH
+/* Return the name of the socket to be used to connect to this
+   process.  If no socket is available, return NULL. */
+const char *
+scd_get_socket_name ()
+{
+  if (socket_name && *socket_name)
+    return socket_name;
+  return NULL;
+}
+
 
+#ifndef HAVE_W32_SYSTEM
 static void
 handle_signal (int signo)
 {
   switch (signo)
     {
-#ifndef HAVE_W32_SYSTEM
     case SIGHUP:
       log_info ("SIGHUP received - "
                 "re-reading configuration and resetting cards\n");
 /*       reread_configuration (); */
       break;
-      
+
     case SIGUSR1:
-      log_info ("SIGUSR1 received - no action defined\n");
+      log_info ("SIGUSR1 received - printing internal information:\n");
+      /* Fixme: We need to see how to integrate pth dumping into our
+         logging system.  */
+      /* pth_ctrl (PTH_CTRL_DUMPSTATE, log_get_stream ()); */
+      app_dump_state ();
       break;
 
     case SIGUSR2:
@@ -897,8 +983,8 @@ handle_signal (int signo)
       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 ));
+        log_info ("SIGTERM received - still %i running threads\n",
+                  active_connections);
       shutdown_pending++;
       if (shutdown_pending > 2)
         {
@@ -908,77 +994,319 @@ handle_signal (int signo)
           scd_exit (0);
        }
       break;
-        
+
     case SIGINT:
       log_info ("SIGINT received - immediate shutdown\n");
       log_info( "%s %s stopped\n", strusage(11), strusage(13));
       cleanup ();
       scd_exit (0);
       break;
-#endif /*!HAVE_W32_SYSTEM*/
 
     default:
       log_info ("signal %d received - no action defined\n", signo);
     }
 }
+#endif /*!HAVE_W32_SYSTEM*/
+
 
 static void
 handle_tick (void)
 {
-  scd_update_reader_status_file ();
+  if (!ticker_disabled)
+    scd_update_reader_status_file ();
+}
+
+
+/* Create a name for the socket.  We 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.
+   Retunrs: Pointer to an allcoated string with the absolute name of
+   the socket used.  */
+static char *
+create_socket_name (char *standard_name)
+{
+  char *name;
+
+  name = make_filename (gnupg_socketdir (), standard_name, NULL);
+  if (strchr (name, PATHSEP_C))
+    {
+      log_error (("'%s' are not allowed in the socket name\n"), PATHSEP_S);
+      scd_exit (2);
+    }
+  return name;
+}
+
+
+
+/* Create a Unix domain socket with NAME.  Returns the file descriptor
+   or terminates the process in case of an error.  If the socket has
+   been redirected the name of the real socket is stored as a malloced
+   string at R_REDIR_NAME. */
+static gnupg_fd_t
+create_server_socket (const char *name, char **r_redir_name,
+                      assuan_sock_nonce_t *nonce)
+{
+  struct sockaddr *addr;
+  struct sockaddr_un *unaddr;
+  socklen_t len;
+  gnupg_fd_t fd;
+  int rc;
+
+  xfree (*r_redir_name);
+  *r_redir_name = NULL;
+
+  fd = assuan_sock_new (AF_UNIX, SOCK_STREAM, 0);
+  if (fd == GNUPG_INVALID_FD)
+    {
+      log_error (_("can't create socket: %s\n"), strerror (errno));
+      scd_exit (2);
+    }
+
+  unaddr = xmalloc (sizeof (*unaddr));
+  addr = (struct sockaddr*)unaddr;
+
+  {
+    int redirected;
+
+    if (assuan_sock_set_sockaddr_un (name, addr, &redirected))
+      {
+        if (errno == ENAMETOOLONG)
+          log_error (_("socket name '%s' is too long\n"), name);
+        else
+          log_error ("error preparing socket '%s': %s\n",
+                     name, gpg_strerror (gpg_error_from_syserror ()));
+        scd_exit (2);
+      }
+    if (redirected)
+      {
+        *r_redir_name = xstrdup (unaddr->sun_path);
+        if (opt.verbose)
+          log_info ("redirecting socket '%s' to '%s'\n", name, *r_redir_name);
+      }
+  }
+
+  len = SUN_LEN (unaddr);
+
+  rc = assuan_sock_bind (fd, addr, len);
+  if (rc == -1 && errno == EADDRINUSE)
+    {
+      gnupg_remove (unaddr->sun_path);
+      rc = assuan_sock_bind (fd, addr, len);
+    }
+  if (rc != -1
+      && (rc=assuan_sock_get_nonce (addr, len, nonce)))
+    log_error (_("error getting nonce for the socket\n"));
+ if (rc == -1)
+    {
+      log_error (_("error binding socket to '%s': %s\n"),
+                unaddr->sun_path,
+                 gpg_strerror (gpg_error_from_syserror ()));
+      assuan_sock_close (fd);
+      scd_exit (2);
+    }
+
+  if (gnupg_chmod (unaddr->sun_path, "-rwx"))
+    log_error (_("can't set permissions of '%s': %s\n"),
+               unaddr->sun_path, strerror (errno));
+
+  if (listen (FD2INT(fd), 5 ) == -1)
+    {
+      log_error (_("listen() failed: %s\n"),
+                 gpg_strerror (gpg_error_from_syserror ()));
+      assuan_sock_close (fd);
+      scd_exit (2);
+    }
+
+  if (opt.verbose)
+    log_info (_("listening on socket '%s'\n"), unaddr->sun_path);
+
+  return fd;
 }
 
+
+
+/* This is the standard connection thread's main function.  */
 static void *
-ticker_thread (void *dummy_arg)
+start_connection_thread (void *arg)
+{
+  ctrl_t ctrl = arg;
+
+  if (ctrl->thread_startup.fd != GNUPG_INVALID_FD
+      && assuan_sock_check_nonce (ctrl->thread_startup.fd, &socket_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 NULL;
+    }
+
+  scd_init_default_ctrl (ctrl);
+  if (opt.verbose)
+    log_info (_("handler for fd %d started\n"),
+              FD2INT(ctrl->thread_startup.fd));
+
+  /* If this is a pipe server, we request a shutdown if the command
+     handler asked for it.  With the next ticker event and given that
+     no other connections are running the shutdown will then
+     happen.  */
+  if (scd_command_handler (ctrl, FD2INT(ctrl->thread_startup.fd))
+      && pipe_server)
+    shutdown_pending = 1;
+
+  if (opt.verbose)
+    log_info (_("handler for fd %d terminated\n"),
+              FD2INT (ctrl->thread_startup.fd));
+
+  scd_deinit_default_ctrl (ctrl);
+  xfree (ctrl);
+  return NULL;
+}
+
+
+/* Connection handler loop.  Wait for connection requests and spawn a
+   thread after accepting a connection.  LISTEN_FD is allowed to be -1
+   in which case this code will only do regular timeouts and handle
+   signals. */
+static void
+handle_connections (int listen_fd)
 {
-  pth_event_t sigs_ev, time_ev = NULL;
-  sigset_t sigs;
+  npth_attr_t tattr;
+  struct sockaddr_un paddr;
+  socklen_t plen;
+  fd_set fdset, read_fdset;
+  int ret;
+  int fd;
+  int nfd;
+  struct timespec abstime;
+  struct timespec curtime;
+  struct timespec timeout;
+  int saved_errno;
+#ifndef HAVE_W32_SYSTEM
   int signo;
+#endif
 
-#ifndef HAVE_W32_SYSTEM /* fixme */
-  sigemptyset (&sigs );
-  sigaddset (&sigs, SIGHUP);
-  sigaddset (&sigs, SIGUSR1);
-  sigaddset (&sigs, SIGUSR2);
-  sigaddset (&sigs, SIGINT);
-  sigaddset (&sigs, SIGTERM);
-  sigs_ev = pth_event (PTH_EVENT_SIGS, &sigs, &signo);
-#else
-  sigs_ev = NULL;
+  ret = npth_attr_init(&tattr);
+  /* FIXME: Check error.  */
+  npth_attr_setdetachstate (&tattr, NPTH_CREATE_DETACHED);
+
+#ifndef HAVE_W32_SYSTEM
+  npth_sigev_init ();
+  npth_sigev_add (SIGHUP);
+  npth_sigev_add (SIGUSR1);
+  npth_sigev_add (SIGUSR2);
+  npth_sigev_add (SIGINT);
+  npth_sigev_add (SIGTERM);
+  npth_sigev_fini ();
 #endif
-  
+
+  FD_ZERO (&fdset);
+  nfd = 0;
+  if (listen_fd != -1)
+    {
+      FD_SET (listen_fd, &fdset);
+      nfd = listen_fd;
+    }
+
+  npth_clock_gettime (&curtime);
+  timeout.tv_sec = TIMERTICK_INTERVAL_SEC;
+  timeout.tv_nsec = TIMERTICK_INTERVAL_USEC * 1000;
+  npth_timeradd (&curtime, &timeout, &abstime);
+  /* We only require abstime here.  The others will be reused.  */
+
   for (;;)
     {
-      if (!time_ev)
+      if (shutdown_pending)
         {
-          time_ev = pth_event (PTH_EVENT_TIME, pth_timeout (2, 0));
-          if (time_ev)
-            pth_event_concat (sigs_ev, time_ev, NULL);
-        }
+          if (active_connections == 0)
+            break; /* ready */
+
+          /* Do not accept anymore connections but wait for existing
+             connections to terminate. We do this by clearing out all
+             file descriptors to wait for, so that the select will be
+             used to just wait on a signal or timeout event. */
+          FD_ZERO (&fdset);
+          listen_fd = -1;
+       }
+
+      npth_clock_gettime (&curtime);
+      if (!(npth_timercmp (&curtime, &abstime, <)))
+       {
+         /* Timeout.  */
+         handle_tick ();
+         timeout.tv_sec = TIMERTICK_INTERVAL_SEC;
+         timeout.tv_nsec = TIMERTICK_INTERVAL_USEC * 1000;
+         npth_timeradd (&curtime, &timeout, &abstime);
+       }
+      npth_timersub (&abstime, &curtime, &timeout);
 
-      if (pth_wait (sigs_ev) < 1)
-        continue;
+      /* 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 (
-#ifdef PTH_STATUS_OCCURRED     /* This is Pth 2 */
-          pth_event_status (sigs_ev) == PTH_STATUS_OCCURRED
+#ifndef HAVE_W32_SYSTEM
+      ret = npth_pselect (nfd+1, &read_fdset, NULL, NULL, &timeout, npth_sigev_sigmask());
+      saved_errno = errno;
+
+      while (npth_sigev_get_pending(&signo))
+       handle_signal (signo);
 #else
-          pth_event_occurred (sigs_ev)
+      ret = npth_eselect (nfd+1, &read_fdset, NULL, NULL, &timeout, NULL, NULL);
+      saved_errno = errno;
 #endif
-          )
-        handle_signal (signo);
 
-      /* Always run the ticker. */
-      if (!shutdown_pending)
-        {
-          pth_event_isolate (sigs_ev);
-          pth_event_free (time_ev, PTH_FREE_ALL);
-          time_ev = NULL;
-          handle_tick ();
-        }
+      if (ret == -1 && saved_errno != EINTR)
+       {
+          log_error (_("npth_pselect failed: %s - waiting 1s\n"),
+                     strerror (saved_errno));
+          npth_sleep (1);
+         continue;
+       }
+
+      if (ret <= 0)
+       /* Timeout.  Will be handled when calculating the next timeout.  */
+       continue;
+
+      if (listen_fd != -1 && FD_ISSET (listen_fd, &read_fdset))
+       {
+          ctrl_t ctrl;
+
+          plen = sizeof paddr;
+         fd = npth_accept (listen_fd, (struct sockaddr *)&paddr, &plen);
+         if (fd == -1)
+           {
+             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) );
+              close (fd);
+            }
+          else
+            {
+              char threadname[50];
+             npth_t thread;
+
+              snprintf (threadname, sizeof threadname-1, "conn fd=%d", fd);
+              threadname[sizeof threadname -1] = 0;
+              ctrl->thread_startup.fd = INT2FD (fd);
+              ret = npth_create (&thread, &tattr, start_connection_thread, ctrl);
+             if (ret)
+                {
+                  log_error ("error spawning connection handler: %s\n",
+                             strerror (ret));
+                  xfree (ctrl);
+                  close (fd);
+                }
+              else
+               npth_setname_np (thread, threadname);
+            }
+          fd = -1;
+       }
     }
 
-  pth_event_free (sigs_ev, PTH_FREE_ALL);
+  cleanup ();
+  log_info (_("%s %s stopped\n"), strusage(11), strusage(13));
+  npth_attr_destroy (&tattr);
 }
-#endif /*USE_GNU_PTH*/
-#endif /*!HAVE_OPENSC*/