Preparing a new release. Updated gettext
[gnupg.git] / agent / gpg-agent.c
index e397df9..4e3f0b5 100644 (file)
@@ -1,5 +1,5 @@
 /* gpg-agent.c  -  The GnuPG Agent
- *     Copyright (C) 2000, 2001, 2002 Free Software Foundation, Inc.
+ *     Copyright (C) 2000, 2001, 2002, 2003 Free Software Foundation, Inc.
  *
  * This file is part of GnuPG.
  *
@@ -30,6 +30,7 @@
 #include <time.h>
 #include <fcntl.h>
 #include <sys/socket.h>
+#include <sys/stat.h>
 #include <sys/un.h>
 #include <unistd.h>
 #include <signal.h>
 # include <pth.h>
 #endif
 
-#include <gcrypt.h>
-
 #define JNLIB_NEED_LOG_LOGV
 #include "agent.h"
-#include "../assuan/assuan.h" /* malloc hooks */
+#include <assuan.h> /* malloc hooks */
 
+#include "i18n.h"
 #include "sysutils.h"
 
 
-#define N_(a) a
-#define _(a) a
-
-
 enum cmd_and_opt_values 
 { aNull = 0,
   oCsh           = 'c',
   oQuiet         = 'q',
   oSh            = 's',
   oVerbose       = 'v',
-  
+
   oNoVerbose = 500,
+  aGPGConfList,
   oOptions,
   oDebug,
   oDebugAll,
+  oDebugLevel,
   oDebugWait,
   oNoGreeting,
   oNoOptions,
@@ -69,6 +67,7 @@ enum cmd_and_opt_values
   oNoGrab,
   oLogFile,
   oServer,
+  oDaemon,
   oBatch,
 
   oPinentryProgram,
@@ -78,47 +77,70 @@ enum cmd_and_opt_values
   oLCctype,
   oLCmessages,
   oScdaemonProgram,
+  oDisablePth,
   oDefCacheTTL,
+  oMaxCacheTTL,
+
+  oIgnoreCacheForSigning,
+  oAllowMarkTrusted,
+  oKeepTTY,
+  oKeepDISPLAY,
 
 aTest };
 
 
 
 static ARGPARSE_OPTS opts[] = {
+
+  { aGPGConfList, "gpgconf-list", 256, "@" },
   
   { 301, NULL, 0, N_("@Options:\n ") },
 
-  { oServer,   "server",     0, N_("run in server mode") },
-  { oVerbose, "verbose",   0, N_("verbose") },
+  { 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, 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 ,
+                               N_("|PGM|use PGM as the PIN-Entry program") },
+  { oScdaemonProgram, "scdaemon-program", 2 ,
+                               N_("|PGM|use PGM as the SCdaemon program") },
+
+  { oDisplay,    "display",     2, "@" },
+  { oTTYname,    "ttyname",     2, "@" },
+  { oTTYtype,    "ttytype",     2, "@" },
+  { oLCctype,    "lc-ctype",    2, "@" },
+  { oLCmessages, "lc-messages", 2, "@" },
+  { oKeepTTY, "keep-tty", 0,  N_("ignore requests to change the TTY")},
+  { oKeepDISPLAY, "keep-display",
+                          0, N_("ignore requests to change the X display")},
 
-  { 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" },
-
-  { 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")},
+  { oMaxCacheTTL, "max-cache-ttl", 4, "@" },
+  { oIgnoreCacheForSigning, "ignore-cache-for-signing", 0,
+                               N_("do not use the PIN cache when signing")},
+  { oAllowMarkTrusted, "allow-mark-trusted", 0,
+                             N_("allow clients to mark keys as \"trusted\"")},
   {0}
 };
 
 
-#ifndef USE_GNU_PTH
+#define DEFAULT_CACHE_TTL (10*60)  /* 10 minutes */
+#define MAX_CACHE_TTL     (120*60) /* 2 hours */
+
 static volatile int caught_fatal_sig = 0;
-#endif /*!USE_GNU_PTH*/
 
 /* flag to indicate that a shutdown was requested */
 static int shutdown_pending;
@@ -130,10 +152,33 @@ static int maybe_setuid = 1;
 /* Name of the communication socket */
 static char socket_name[128];
 
+/* Default values for options passed to the pinentry. */
+static char *default_display;
+static char *default_ttyname;
+static char *default_ttytype;
+static char *default_lc_ctype;
+static char *default_lc_messages;
 
+/* 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 afte a SIGHUP if id didn't changed. Malloced. */
+static char *current_logfile;
+
+/* Local prototypes. */
+static void create_directories (void);
 #ifdef USE_GNU_PTH
 static void handle_connections (int listen_fd);
-#endif
+
+/* Pth wrapper function definitions. */
+GCRY_THREAD_OPTION_PTH_IMPL;
+
+#endif /*USE_GNU_PTH*/
+static void check_for_running_agent (void);
 
 
 
@@ -167,12 +212,12 @@ static void
 i18n_init (void)
 {
 #ifdef USE_SIMPLE_GETTEXT
-    set_gettext_file( PACKAGE );
+    set_gettext_file( PACKAGE_GT );
 #else
 #ifdef ENABLE_NLS
-    /* gtk_set_locale (); HMMM: We have not yet called gtk_init */
-    bindtextdomain( PACKAGE, GNUPG_LOCALEDIR );
-    textdomain( PACKAGE );
+    setlocale (LC_ALL, "");
+    bindtextdomain (PACKAGE_GT, LOCALEDIR);
+    textdomain (PACKAGE_GT);
 #endif
 #endif
 }
@@ -199,6 +244,48 @@ my_gcry_logger (void *dummy, int level, const char *fmt, va_list arg_ptr)
 }
 
 
+/* Setup the debugging.  With a LEVEL of NULL only the active debug
+   flags are propagated to the subsystems.  With 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
+set_debug (void)
+{
+  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
+    {
+      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. */
+    }
+
+  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);
+}
+
 static void
 cleanup (void)
 {
@@ -219,7 +306,6 @@ cleanup (void)
 }
 
 
-#ifndef USE_GNU_PTH
 static RETSIGTYPE
 cleanup_sh (int sig)
 {
@@ -241,7 +327,69 @@ cleanup_sh (int sig)
 #endif
   raise( sig );
 }
-#endif /*!USE_GNU_PTH*/
+
+
+/* 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.  REREAD should be set
+   true if it is not the initial option parsing. */
+static int
+parse_rereadable_options (ARGPARSE_ARGS *pargs, int reread)
+{
+  if (!pargs)
+    { /* reset mode */
+      opt.quiet = 0;
+      opt.verbose = 0;
+      opt.debug = 0;
+      opt.no_grab = 0;
+      opt.pinentry_program = NULL;
+      opt.scdaemon_program = NULL;
+      opt.def_cache_ttl = DEFAULT_CACHE_TTL;
+      opt.max_cache_ttl = MAX_CACHE_TTL;
+      opt.ignore_cache_for_signing = 0;
+      opt.allow_mark_trusted = 0;
+      return 1;
+    }
+
+  switch (pargs->r_opt)
+    {
+    case oQuiet: opt.quiet = 1; break;
+    case oVerbose: opt.verbose++; break;
+
+    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 
+          && (!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 oScdaemonProgram: opt.scdaemon_program = pargs->r.ret_str; break;
+
+    case oDefCacheTTL: opt.def_cache_ttl = pargs->r.ret_ulong; break;
+    case oMaxCacheTTL: opt.max_cache_ttl = pargs->r.ret_ulong; break;
+      
+    case oIgnoreCacheForSigning: opt.ignore_cache_for_signing = 1; break;
+
+    case oAllowMarkTrusted: opt.allow_mark_trusted = 1; break;
+
+    default:
+      return 0; /* not handled */
+    }
+  return 1; /* handled */
+}
+
 
 int
 main (int argc, char **argv )
@@ -259,50 +407,64 @@ main (int argc, char **argv )
   int greeting = 0;
   int nogreeting = 0;
   int pipe_server = 0;
+  int is_daemon = 0;
   int nodetach = 0;
   int csh_style = 0;
   char *logfile = NULL;
   int debug_wait = 0;
+  int disable_pth = 0;
+  int gpgconf_list = 0;
+  gpg_error_t err;
 
   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); 
+
+  /* Try to auto set the character set.  */
+  set_native_charset (NULL); 
+
   i18n_init ();
 
-  /* check that the libraries are suitable.  Do it here because
-     the option parsing may need services of the library */
-  if (!gcry_check_version ( "1.1.5" ) )
+  /* Libgcrypt requires us to register the threading model first.
+     Note that this will also do the pth_init. */
+#ifdef USE_GNU_PTH
+  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 /*USE_GNU_PTH*/
+
+  /* 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"),
-                 "1.1.5", gcry_check_version (NULL) );
+                 NEED_LIBGCRYPT_VERSION, gcry_check_version (NULL) );
     }
 
   assuan_set_malloc_hooks (gcry_malloc, gcry_realloc, gcry_free);
-#ifdef USE_GNU_PTH
-  assuan_set_io_func (pth_read, pth_write);
-#endif
+  assuan_set_assuan_log_stream (log_get_stream ());
+  assuan_set_assuan_log_prefix (log_get_prefix (NULL));
+
   gcry_set_log_handler (my_gcry_logger, NULL);
   gcry_control (GCRYCTL_USE_SECURE_RNDPOOL);
 
   may_coredump = disable_core_dumps ();
 
+  parse_rereadable_options (NULL, 0); /* Reset them to default values. */
+
   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)
-    {
-#ifdef HAVE_DRIVE_LETTERS
-      opt.homedir = "c:/gnupg-test";
-#else
-      opt.homedir = "~/.gnupg-test";
-#endif
-    }
-  opt.def_cache_ttl = 10*60; /* default to 10 minutes */
+    opt.homedir = GNUPG_DEFAULT_HOMEDIR;
 
 
   /* check whether we have a config file on the commandline */
@@ -373,14 +535,13 @@ main (int argc, char **argv )
 
   while (optfile_parse( configfp, configname, &configlineno, &pargs, opts) )
     {
+      if (parse_rereadable_options (&pargs, 0))
+        continue; /* Already handled */
       switch (pargs.r_opt)
         {
-        case oQuiet: opt.quiet = 1; break;
-        case oVerbose: opt.verbose++; break;
+        case aGPGConfList: gpgconf_list = 1; break;
         case oBatch: opt.batch=1; break;
 
-        case oDebug: opt.debug |= pargs.r.ret_ulong; break;
-        case oDebugAll: opt.debug = ~0; break;
         case oDebugWait: debug_wait = pargs.r.ret_int; break;
 
         case oOptions:
@@ -397,20 +558,22 @@ main (int argc, char **argv )
         case oNoOptions: break; /* no-options */
         case oHomedir: opt.homedir = pargs.r.ret_str; break;
         case oNoDetach: nodetach = 1; break;
-        case oNoGrab: opt.no_grab = 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 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 oPinentryProgram: opt.pinentry_program = pargs.r.ret_str; break;
-        case oDisplay: opt.display = xstrdup (pargs.r.ret_str); break;
-        case oTTYname: opt.ttyname = xstrdup (pargs.r.ret_str); break;
-        case oTTYtype: opt.ttytype = xstrdup (pargs.r.ret_str); break;
-        case oLCctype: opt.lc_ctype = xstrdup (pargs.r.ret_str); break;
-        case oLCmessages: opt.lc_messages = xstrdup (pargs.r.ret_str); break;
-        case oScdaemonProgram: opt.scdaemon_program = pargs.r.ret_str; break;
-        case oDefCacheTTL: opt.def_cache_ttl = pargs.r.ret_ulong; break;
+        case oKeepTTY: opt.keep_tty = 1; break;
+        case oKeepDISPLAY: opt.keep_display = 1; break;
 
         default : pargs.err = configfp? 1:2; break;
        }
@@ -419,7 +582,8 @@ main (int argc, char **argv )
     {
       fclose( configfp );
       configfp = NULL;
-      xfree(configname);
+      /* Keep a copy of the name so that it can be read on SIGHUP. */
+      config_filename = configname;
       configname = NULL;
       goto next_pass;
     }
@@ -437,9 +601,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))
     {
@@ -448,6 +615,8 @@ main (int argc, char **argv )
       exit (1);
     }
 
+  create_directories ();
+
   if (debug_wait && pipe_server)
     {
       log_debug ("waiting for debugger - my pid is %u .....\n",
@@ -456,27 +625,112 @@ main (int argc, char **argv )
       log_debug ("... okay\n");
     }
   
-  /* now start with logging to a file if this is desired */
+  if (gpgconf_list)
+    {
+      char *filename;
+
+      /* List options and default values in the GPG Conf format.  */
+
+      /* 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)
+
+      filename = make_filename (opt.homedir, "gpg-agent.conf", NULL );
+      printf ("gpgconf-gpg-agent.conf:%lu:\"%s\n",
+              GC_OPT_FLAG_DEFAULT, filename);
+      xfree (filename);
+
+      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 ("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);
+
+      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_set_prefix (NULL, JNLIB_LOG_WITH_PREFIX); 
+      check_for_running_agent ();
+      agent_exit (0);
+    }
+  
+#ifdef ENABLE_NLS
+  /* 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. */
   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);
     }
 
+  /* Make sure that we have a default ttyname. */
+  if (!default_ttyname && ttyname (1))
+    default_ttyname = xstrdup (ttyname (1));
+  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);
     }
+  else if (!is_daemon)
+    ; /* NOTREACHED */
   else
-    { /* regular server mode */
+    { /* Regular server mode */
       int fd;
       pid_t pid;
-      int i;
       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)
+        unsetenv ("DISPLAY");
+
       *socket_name = 0;
       snprintf (socket_name, DIM(socket_name)-1,
                 "/tmp/gpg-XXXXXX/S.gpg-agent");
@@ -545,7 +799,7 @@ main (int argc, char **argv )
           exit (1);
         }
       else if (pid) 
-        { /* we are the parent */
+        { /* We are the parent */
           char *infostr;
           
           close (fd);
@@ -594,14 +848,20 @@ main (int argc, char **argv )
         } /* 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 )
-        {  /* close stdin, stdout and stderr unless it is the log stream */
+        { 
+          int i;
+          unsigned int oldflags;
+
+          /* 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) )
                 close (i);
             }
           if (setsid() == -1)
@@ -610,6 +870,10 @@ 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;
         }
 
       if (chdir("/"))
@@ -620,34 +884,38 @@ main (int argc, char **argv )
 
 
 #ifdef USE_GNU_PTH
-      if (!pth_init ())
+      if (!disable_pth)
         {
-          log_error ("failed to initialize the Pth library\n");
-          exit (1);
+         struct sigaction sa;
+
+         sa.sa_handler = SIG_IGN;
+         sigemptyset (&sa.sa_mask);
+         sa.sa_flags = 0;
+         sigaction (SIGPIPE, &sa, NULL);
+          handle_connections (fd);
         }
-      signal (SIGPIPE, SIG_IGN);
-      handle_connections (fd);
-#else /*!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);
+      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);
     }
   
@@ -674,13 +942,152 @@ agent_exit (int rc)
 }
 
 
+void
+agent_init_default_ctrl (struct server_control_s *ctrl)
+{
+  ctrl->connection_fd = -1;
+
+  /* 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;
+
+  if (ctrl->ttyname)
+    free (ctrl->ttyname);
+  ctrl->ttyname = default_ttyname? strdup (default_ttyname) : NULL;
+
+  if (ctrl->ttytype)
+    free (ctrl->ttytype);
+  ctrl->ttytype = default_ttytype? strdup (default_ttytype) : NULL;
+
+  if (ctrl->lc_ctype)
+    free (ctrl->lc_ctype);
+  ctrl->lc_ctype = default_lc_ctype? strdup (default_lc_ctype) : NULL;
+
+  if (ctrl->lc_messages)
+    free (ctrl->lc_messages);
+  ctrl->lc_messages = default_lc_messages? strdup (default_lc_messages) : NULL;
+}
+
+
+/* Reread parts of the configuration.  Note, that this function is
+   obviously not thread-safe and should only be called from the PTH
+   signal handler. 
+
+   Fixme: Due to the way the argument parsing works, we create a
+   memory leak here for all string type arguments.  There is currently
+   no clean way to tell whether the memory for the argument has been
+   allocated or points into the process' original arguments.  Unless
+   we have a mechanism to tell this, we need to live on with this. */
 static void
 reread_configuration (void)
 {
-  /* FIXME: Move parts of the option parsing to here. */
+  ARGPARSE_ARGS pargs;
+  FILE *fp;
+  unsigned int configlineno = 0;
+  int dummy;
+
+  if (!config_filename)
+    return; /* No config file. */
+
+  fp = fopen (config_filename, "r");
+  if (!fp)
+    {
+      log_error (_("option file `%s': %s\n"),
+                 config_filename, strerror(errno) );
+      return;
+    }
+
+  parse_rereadable_options (NULL, 1); /* Start from the default values. */
+
+  memset (&pargs, 0, sizeof pargs);
+  dummy = 0;
+  pargs.argc = &dummy;
+  pargs.flags = 1;  /* do not remove the args */
+  while (optfile_parse (fp, config_filename, &configlineno, &pargs, opts) )
+    {
+      if (pargs.r_opt < -1)
+        pargs.err = 1; /* Print a warning. */
+      else /* Try to parse this option - ignore unchangeable ones. */
+        parse_rereadable_options (&pargs, 1);
+    }
+  fclose (fp);
+  set_debug ();
+}
+
+
+static void
+create_private_keys_directory (const char *home)
+{
+  char *fname;
+  struct stat statbuf;
+
+  fname = make_filename (home, GNUPG_PRIVATE_KEYS_DIR, NULL);
+  if (stat (fname, &statbuf) && errno == ENOENT)
+    {
+      if (mkdir (fname, S_IRUSR|S_IWUSR|S_IXUSR ))
+        log_error (_("can't create directory `%s': %s\n"),
+                   fname,      strerror(errno) );
+      else if (!opt.quiet)
+        log_info (_("directory `%s' created\n"), fname);
+    }
+  xfree (fname);
+}
+
+/* Create the directory only if the supplied directory name is the
+   same as the default one.  This way we avoid to create arbitrary
+   directories when a non-default home directory is used.  To cope
+   with HOME, we compare only the suffix if we see that the default
+   homedir does start with a tilde.  We don't stop here in case of
+   problems because other functions will throw an error anyway.*/
+static void
+create_directories (void)
+{
+  struct stat statbuf;
+  const char *defhome = GNUPG_DEFAULT_HOMEDIR;
+  char *home;
+
+  home  = make_filename (opt.homedir, NULL);
+  if ( stat (home, &statbuf) )
+    {
+      if (errno == ENOENT)
+        {
+          if ( (*defhome == '~'
+                && (strlen (home) >= strlen (defhome+1)
+                    && !strcmp (home + strlen(home)
+                                - strlen (defhome+1), defhome+1)))
+               || (*defhome != '~' && !strcmp (home, defhome) )
+               )
+            {
+              if (mkdir (home, S_IRUSR|S_IWUSR|S_IXUSR ))
+                log_error (_("can't create directory `%s': %s\n"),
+                           home, strerror(errno) );
+              else 
+                {
+                  if (!opt.quiet)
+                    log_info (_("directory `%s' created\n"), home);
+                  create_private_keys_directory (home);
+                }
+            }
+        }
+      else
+        log_error ("error stat-ing `%s': %s\n", home, strerror (errno));
+    }
+  else if ( !S_ISDIR(statbuf.st_mode))
+    {
+      log_error ("can't use `%s' as home directory\n", home);
+    }
+  else /* exists and is a directory. */
+    {
+      create_private_keys_directory (home);
+    }
+  xfree (home);
 }
 
 
+
 #ifdef USE_GNU_PTH
 static void
 handle_signal (int signo)
@@ -688,20 +1095,19 @@ handle_signal (int signo)
   switch (signo)
     {
     case SIGHUP:
-      log_info ("SIGHUP received - re-reading configuration\n");
+      log_info ("SIGHUP received - "
+                "re-reading configuration and flushing cache\n");
+      agent_flush_cache ();
       reread_configuration ();
+      agent_reload_trustlist ();
       break;
       
     case SIGUSR1:
-      if (opt.verbose < 5)
-        opt.verbose++;
-      log_info ("SIGUSR1 received - verbosity set to %d\n", opt.verbose);
+      log_info ("SIGUSR1 received - no action defined\n");
       break;
-
+      
     case SIGUSR2:
-      if (opt.verbose)
-        opt.verbose--;
-      log_info ("SIGUSR2 received - verbosity set to %d\n", opt.verbose );
+      log_info ("SIGUSR2 received - checking smartcard status\n");
       break;
 
     case SIGTERM:
@@ -718,7 +1124,7 @@ handle_signal (int signo)
           cleanup ();
           agent_exit (0);
        }
-       break;
+      break;
         
     case SIGINT:
       log_info ("SIGINT received - immediate shutdown\n");
@@ -740,6 +1146,12 @@ start_connection_thread (void *arg)
 
   if (opt.verbose)
     log_info ("handler for fd %d started\n", fd);
+
+  /* FIXME: Move this housekeeping into a ticker function.  Calling it
+     for each connection should work but won't work anymore if our
+     cleints start to keep connections. */
+  agent_trustlist_housekeeping ();
+
   start_command_handler (-1, fd);
   if (opt.verbose)
     log_info ("handler for fd %d terminated\n", fd);
@@ -761,7 +1173,7 @@ handle_connections (int listen_fd)
 
   tattr = pth_attr_new();
   pth_attr_set (tattr, PTH_ATTR_JOINABLE, 0);
-  pth_attr_set (tattr, PTH_ATTR_STACK_SIZE, 32*1024);
+  pth_attr_set (tattr, PTH_ATTR_STACK_SIZE, 256*1024);
   pth_attr_set (tattr, PTH_ATTR_NAME, "gpg-agent");
 
   sigemptyset (&sigs );
@@ -791,7 +1203,11 @@ handle_connections (int listen_fd)
       fd = pth_accept_ev (listen_fd, (struct sockaddr *)&paddr, &plen, ev);
       if (fd == -1)
         {
+#ifdef PTH_STATUS_OCCURRED     /* This is Pth 2 */
+          if (pth_event_status (ev) == PTH_STATUS_OCCURRED)
+#else
           if (pth_event_occurred (ev))
+#endif
             {
               handle_signal (signo);
               continue;
@@ -814,3 +1230,56 @@ handle_connections (int listen_fd)
   log_info ("%s %s stopped\n", strusage(11), strusage(13));
 }
 #endif /*USE_GNU_PTH*/
+
+
+/* Figure out whether an agent is available and running. Prints an
+   error if not.  */
+static void
+check_for_running_agent ()
+{
+  int rc;
+  char *infostr, *p;
+  assuan_context_t ctx;
+  int prot, pid;
+
+  infostr = getenv ("GPG_AGENT_INFO");
+  if (!infostr || !*infostr)
+    {
+      log_error (_("no gpg-agent running in this session\n"));
+      return;
+    }
+
+  infostr = xstrdup (infostr);
+  if ( !(p = strchr (infostr, ':')) || p == infostr)
+    {
+      log_error (_("malformed GPG_AGENT_INFO environment variable\n"));
+      xfree (infostr);
+      return;
+    }
+
+  *p++ = 0;
+  pid = atoi (p);
+  while (*p && *p != ':')
+    p++;
+  prot = *p? atoi (p+1) : 0;
+  if (prot != 1)
+    {
+      log_error (_("gpg-agent protocol version %d is not supported\n"),
+                 prot);
+      xfree (infostr);
+      return;
+    }
+
+  rc = assuan_socket_connect (&ctx, infostr, pid);
+  xfree (infostr);
+  if (rc)
+    {
+      log_error ("can't connect to the agent: %s\n", assuan_strerror (rc));
+      return;
+    }
+
+  if (!opt.quiet)
+    log_info ("gpg-agent running and available\n");
+
+  assuan_disconnect (ctx);
+}