agent: Kludge to allow disabling of the extra sockets.
[gnupg.git] / agent / gpg-agent.c
index fe310f4..d3f203b 100644 (file)
@@ -1,6 +1,6 @@
 /* gpg-agent.c  -  The GnuPG Agent
  * Copyright (C) 2000-2007, 2009-2010 Free Software Foundation, Inc.
- * Copyright (C) 2000-2014 Werner Koch
+ * Copyright (C) 2000-2016 Werner Koch
  *
  * This file is part of GnuPG.
  *
 #ifdef HAVE_SIGNAL_H
 # include <signal.h>
 #endif
+#ifdef HAVE_INOTIFY_INIT
+# include <sys/inotify.h>
+#endif /*HAVE_INOTIFY_INIT*/
 #include <npth.h>
 
-#define JNLIB_NEED_LOG_LOGV
-#define JNLIB_NEED_AFLOCAL
+#define GNUPG_COMMON_NEED_AFLOCAL
 #include "agent.h"
 #include <assuan.h> /* Malloc hooks  and socket wrappers. */
 
@@ -59,7 +61,6 @@
 #include "gc-opt-flags.h"
 #include "exechelp.h"
 #include "asshelp.h"
-#include "openpgpdefs.h"  /* for PUBKEY_ALGO_ECDSA, PUBKEY_ALGO_ECDH */
 #include "../common/init.h"
 
 
@@ -80,6 +81,7 @@ enum cmd_and_opt_values
   oDebugLevel,
   oDebugWait,
   oDebugQuickRandom,
+  oDebugPinentry,
   oNoGreeting,
   oNoOptions,
   oHomedir,
@@ -92,6 +94,8 @@ enum cmd_and_opt_values
 
   oPinentryProgram,
   oPinentryTouchFile,
+  oPinentryInvisibleChar,
+  oPinentryTimeout,
   oDisplay,
   oTTYname,
   oTTYtype,
@@ -112,6 +116,7 @@ enum cmd_and_opt_values
   oUseStandardSocket,
   oNoUseStandardSocket,
   oExtraSocket,
+  oBrowserSocket,
   oFakedSystemTime,
 
   oIgnoreCacheForSigning,
@@ -119,6 +124,9 @@ enum cmd_and_opt_values
   oNoAllowMarkTrusted,
   oAllowPresetPassphrase,
   oAllowLoopbackPinentry,
+  oNoAllowLoopbackPinentry,
+  oNoAllowExternalCache,
+  oAllowEmacsPinentry,
   oKeepTTY,
   oKeepDISPLAY,
   oSSHSupport,
@@ -150,11 +158,12 @@ static ARGPARSE_OPTS opts[] = {
   ARGPARSE_s_n (oCsh,    "csh",       N_("csh-style command output")),
   ARGPARSE_s_s (oOptions, "options", N_("|FILE|read options from FILE")),
 
-  ARGPARSE_s_u (oDebug,             "debug",       "@"),
+  ARGPARSE_s_s (oDebug,             "debug",       "@"),
   ARGPARSE_s_n (oDebugAll,   "debug-all",   "@"),
   ARGPARSE_s_s (oDebugLevel, "debug-level", "@"),
   ARGPARSE_s_i (oDebugWait,"  debug-wait",  "@"),
   ARGPARSE_s_n (oDebugQuickRandom, "debug-quick-random", "@"),
+  ARGPARSE_s_n (oDebugPinentry, "debug-pinentry", "@"),
 
   ARGPARSE_s_n (oNoDetach,  "no-detach", N_("do not detach from the console")),
   ARGPARSE_s_n (oNoGrab,    "no-grab",   N_("do not grab keyboard and mouse")),
@@ -162,11 +171,19 @@ static ARGPARSE_OPTS opts[] = {
   ARGPARSE_s_s (oPinentryProgram, "pinentry-program",
                 /* */             N_("|PGM|use PGM as the PIN-Entry program")),
   ARGPARSE_s_s (oPinentryTouchFile, "pinentry-touch-file", "@"),
+  ARGPARSE_s_s (oPinentryInvisibleChar, "pinentry-invisible-char", "@"),
+  ARGPARSE_s_u (oPinentryTimeout, "pinentry-timeout", "@"),
   ARGPARSE_s_s (oScdaemonProgram, "scdaemon-program",
                 /* */             N_("|PGM|use PGM as the SCdaemon program") ),
   ARGPARSE_s_n (oDisableScdaemon, "disable-scdaemon",
                 /* */             N_("do not use the SCdaemon") ),
   ARGPARSE_s_n (oDisableCheckOwnSocket, "disable-check-own-socket", "@"),
+
+  ARGPARSE_s_s (oExtraSocket, "extra-socket",
+                /* */       N_("|NAME|accept some commands via NAME")),
+
+  ARGPARSE_s_s (oBrowserSocket, "browser-socket", "@"),
+
   ARGPARSE_s_s (oFakedSystemTime, "faked-system-time", "@"),
 
   ARGPARSE_s_n (oBatch,      "batch",        "@"),
@@ -199,13 +216,19 @@ static ARGPARSE_OPTS opts[] = {
 
   ARGPARSE_s_n (oIgnoreCacheForSigning, "ignore-cache-for-signing",
                 /* */    N_("do not use the PIN cache when signing")),
+  ARGPARSE_s_n (oNoAllowExternalCache,  "no-allow-external-cache",
+                /* */    N_("disallow the use of an external password cache")),
   ARGPARSE_s_n (oNoAllowMarkTrusted, "no-allow-mark-trusted",
                 /* */    N_("disallow clients to mark keys as \"trusted\"")),
   ARGPARSE_s_n (oAllowMarkTrusted,   "allow-mark-trusted", "@"),
   ARGPARSE_s_n (oAllowPresetPassphrase, "allow-preset-passphrase",
                 /* */                    N_("allow presetting passphrase")),
-  ARGPARSE_s_n (oAllowLoopbackPinentry, "allow-loopback-pinentry",
-                                   N_("allow caller to override the pinentry")),
+  ARGPARSE_s_n (oNoAllowLoopbackPinentry, "no-allow-loopback-pinentry",
+                                N_("disallow caller to override the pinentry")),
+  ARGPARSE_s_n (oAllowLoopbackPinentry, "allow-loopback-pinentry", "@"),
+  ARGPARSE_s_n (oAllowEmacsPinentry,  "allow-emacs-pinentry",
+                /* */    N_("allow passphrase to be prompted through Emacs")),
+
   ARGPARSE_s_n (oSSHSupport,   "enable-ssh-support", N_("enable ssh support")),
   ARGPARSE_s_n (oPuttySupport, "enable-putty-support",
 #ifdef HAVE_W32_SYSTEM
@@ -214,7 +237,6 @@ static ARGPARSE_OPTS opts[] = {
                 /* */           "@"
 #endif
                 ),
-  ARGPARSE_s_s (oExtraSocket, "extra-socket", "@"),
 
   /* Dummy options for backward compatibility.  */
   ARGPARSE_o_s (oWriteEnvFile, "write-env-file", "@"),
@@ -225,6 +247,22 @@ static ARGPARSE_OPTS opts[] = {
 };
 
 
+/* 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"     },
+    { 77, NULL } /* 77 := Do not exit on "help" or "?".  */
+  };
+
+
+
 #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 */
@@ -250,6 +288,9 @@ static ARGPARSE_OPTS opts[] = {
 #endif
 
 
+/* Flag indicating that the ssh-agent subsystem has been enabled.  */
+static int ssh_support;
+
 #ifdef HAVE_W32_SYSTEM
 /* Flag indicating that support for Putty has been enabled.  */
 static int putty_support;
@@ -293,6 +334,10 @@ static char *redir_socket_name;
 static char *socket_name_extra;
 static char *redir_socket_name_extra;
 
+/* Name of the optional browser socket used for native gpg-agent requests.  */
+static char *socket_name_browser;
+static char *redir_socket_name_browser;
+
 /* Name of the communication socket used for ssh-agent-emulation.  */
 static char *socket_name_ssh;
 static char *redir_socket_name_ssh;
@@ -301,6 +346,7 @@ static char *redir_socket_name_ssh;
    POSIX systems). */
 static assuan_sock_nonce_t socket_nonce;
 static assuan_sock_nonce_t socket_nonce_extra;
+static assuan_sock_nonce_t socket_nonce_browser;
 static assuan_sock_nonce_t socket_nonce_ssh;
 
 
@@ -330,22 +376,52 @@ static pid_t parent_pid = (pid_t)(-1);
 /* Number of active connections.  */
 static int active_connections;
 
+/* This object is used to dispatch progress messages from Libgcrypt to
+ * the right thread.  Given that we won't have at max a few dozen
+ * connections at the same time using a linked list is the easiest way
+ * to handle this. */
+struct progress_dispatch_s
+{
+  struct progress_dispatch_s *next;
+  /* The control object of the connection.  If this is NULL no
+   * connection is associated with this item and it is free for reuse
+   * by new connections.  */
+  ctrl_t ctrl;
+
+  /* The thread id of (npth_self) of the connection.  */
+  npth_t tid;
+
+  /* The callback set by the connection.  This is similar to the
+   * Libgcrypt callback but with the control object passed as the
+   * first argument.  */
+  void (*cb)(ctrl_t ctrl,
+             const char *what, int printchar,
+             int current, int total);
+};
+struct progress_dispatch_s *progress_dispatch_list;
+
+
+
 \f
 /*
    Local prototypes.
  */
 
 static char *create_socket_name (char *standard_name, int with_homedir);
-static gnupg_fd_t create_server_socket (char *name, int primary,
+static gnupg_fd_t create_server_socket (char *name, int primary, int cygwin,
                                         char **r_redir_name,
                                         assuan_sock_nonce_t *nonce);
 static void create_directories (void);
 
+static void agent_libgcrypt_progress_cb (void *data, const char *what,
+                                         int printchar,
+                                         int current, int total);
 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_extra,
+                                gnupg_fd_t listen_fd_browser,
                                 gnupg_fd_t listen_fd_ssh);
 static void check_own_socket (void);
 static int check_for_running_agent (int silent);
@@ -435,11 +511,11 @@ set_debug (void)
   else if (!strcmp (debug_level, "none") || (numok && numlvl < 1))
     opt.debug = 0;
   else if (!strcmp (debug_level, "basic") || (numok && numlvl <= 2))
-    opt.debug = DBG_ASSUAN_VALUE;
+    opt.debug = DBG_IPC_VALUE;
   else if (!strcmp (debug_level, "advanced") || (numok && numlvl <= 5))
-    opt.debug = DBG_ASSUAN_VALUE|DBG_COMMAND_VALUE;
+    opt.debug = DBG_IPC_VALUE|DBG_COMMAND_VALUE;
   else if (!strcmp (debug_level, "expert") || (numok && numlvl <= 8))
-    opt.debug = (DBG_ASSUAN_VALUE|DBG_COMMAND_VALUE
+    opt.debug = (DBG_IPC_VALUE|DBG_COMMAND_VALUE
                  |DBG_CACHE_VALUE);
   else if (!strcmp (debug_level, "guru") || numok)
     {
@@ -470,15 +546,7 @@ set_debug (void)
   gcry_control (GCRYCTL_SET_VERBOSITY, (int)opt.verbose);
 
   if (opt.debug)
-    log_info ("enabled debug flags:%s%s%s%s%s%s%s%s\n",
-              (opt.debug & DBG_COMMAND_VALUE)? " command":"",
-              (opt.debug & DBG_MPI_VALUE    )? " mpi":"",
-              (opt.debug & DBG_CRYPTO_VALUE )? " crypto":"",
-              (opt.debug & DBG_MEMORY_VALUE )? " memory":"",
-              (opt.debug & DBG_CACHE_VALUE  )? " cache":"",
-              (opt.debug & DBG_MEMSTAT_VALUE)? " memstat":"",
-              (opt.debug & DBG_HASHING_VALUE)? " hashing":"",
-              (opt.debug & DBG_ASSUAN_VALUE )? " assuan":"");
+    parse_debug_flag (NULL, &opt.debug, debug_flags);
 }
 
 
@@ -489,19 +557,10 @@ remove_socket (char *name, char *redir_name)
 {
   if (name && *name)
     {
-      char *p;
-
       if (redir_name)
         name = redir_name;
 
       gnupg_remove (name);
-      p = strrchr (name, '/');
-      if (p)
-       {
-         *p = 0;
-         rmdir (name);
-         *p = '/';
-       }
       *name = 0;
     }
 }
@@ -521,6 +580,8 @@ cleanup (void)
   remove_socket (socket_name, redir_socket_name);
   if (opt.extra_socket > 1)
     remove_socket (socket_name_extra, redir_socket_name_extra);
+  if (opt.browser_socket > 1)
+    remove_socket (socket_name_browser, redir_socket_name_browser);
   remove_socket (socket_name_ssh, redir_socket_name_ssh);
 }
 
@@ -540,8 +601,12 @@ parse_rereadable_options (ARGPARSE_ARGS *pargs, int reread)
       opt.verbose = 0;
       opt.debug = 0;
       opt.no_grab = 0;
+      opt.debug_pinentry = 0;
       opt.pinentry_program = NULL;
       opt.pinentry_touch_file = NULL;
+      xfree (opt.pinentry_invisible_char);
+      opt.pinentry_invisible_char = NULL;
+      opt.pinentry_timeout = 0;
       opt.scdaemon_program = NULL;
       opt.def_cache_ttl = DEFAULT_CACHE_TTL;
       opt.def_cache_ttl_ssh = DEFAULT_CACHE_TTL_SSH;
@@ -555,6 +620,9 @@ parse_rereadable_options (ARGPARSE_ARGS *pargs, int reread)
       opt.enable_passhrase_history = 0;
       opt.ignore_cache_for_signing = 0;
       opt.allow_mark_trusted = 1;
+      opt.allow_external_cache = 1;
+      opt.allow_loopback_pinentry = 1;
+      opt.allow_emacs_pinentry = 0;
       opt.disable_scdaemon = 0;
       disable_check_own_socket = 0;
       return 1;
@@ -565,9 +633,12 @@ parse_rereadable_options (ARGPARSE_ARGS *pargs, int reread)
     case oQuiet: opt.quiet = 1; break;
     case oVerbose: opt.verbose++; break;
 
-    case oDebug: opt.debug |= pargs->r.ret_ulong; break;
+    case oDebug:
+      parse_debug_flag (pargs->r.ret_str, &opt.debug, debug_flags);
+      break;
     case oDebugAll: opt.debug = ~0; break;
     case oDebugLevel: debug_level = pargs->r.ret_str; break;
+    case oDebugPinentry: opt.debug_pinentry = 1; break;
 
     case oLogFile:
       if (!reread)
@@ -585,6 +656,11 @@ parse_rereadable_options (ARGPARSE_ARGS *pargs, int reread)
 
     case oPinentryProgram: opt.pinentry_program = pargs->r.ret_str; break;
     case oPinentryTouchFile: opt.pinentry_touch_file = pargs->r.ret_str; break;
+    case oPinentryInvisibleChar:
+      xfree (opt.pinentry_invisible_char);
+      opt.pinentry_invisible_char = xtrystrdup (pargs->r.ret_str); break;
+      break;
+    case oPinentryTimeout: opt.pinentry_timeout = pargs->r.ret_ulong; break;
     case oScdaemonProgram: opt.scdaemon_program = pargs->r.ret_str; break;
     case oDisableScdaemon: opt.disable_scdaemon = 1; break;
     case oDisableCheckOwnSocket: disable_check_own_socket = 1; break;
@@ -619,6 +695,13 @@ parse_rereadable_options (ARGPARSE_ARGS *pargs, int reread)
     case oAllowPresetPassphrase: opt.allow_preset_passphrase = 1; break;
 
     case oAllowLoopbackPinentry: opt.allow_loopback_pinentry = 1; break;
+    case oNoAllowLoopbackPinentry: opt.allow_loopback_pinentry = 0; break;
+
+    case oNoAllowExternalCache: opt.allow_external_cache = 0;
+      break;
+
+    case oAllowEmacsPinentry: opt.allow_emacs_pinentry = 1;
+      break;
 
     default:
       return 0; /* not handled */
@@ -632,12 +715,6 @@ parse_rereadable_options (ARGPARSE_ARGS *pargs, int reread)
 static void
 finalize_rereadable_options (void)
 {
-  /* It would be too surprising if the max-cache-ttl is lower than the
-     default-cache-ttl - thus we silently correct that.  */
-  if (opt.def_cache_ttl > opt.max_cache_ttl)
-    opt.max_cache_ttl = opt.def_cache_ttl;
-  if (opt.def_cache_ttl_ssh > opt.max_cache_ttl_ssh)
-    opt.max_cache_ttl_ssh = opt.def_cache_ttl_ssh;
 }
 
 
@@ -665,6 +742,8 @@ main (int argc, char **argv )
   gpg_error_t err;
   struct assuan_malloc_hooks malloc_hooks;
 
+  early_system_init ();
+
   /* Before we do anything else we save the list of currently open
      file descriptors and the signal mask.  This info is required to
      do the exec call properly. */
@@ -680,7 +759,7 @@ main (int argc, char **argv )
   /* 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_NAME, JNLIB_LOG_WITH_PREFIX|JNLIB_LOG_WITH_PID);
+  log_set_prefix (GPG_AGENT_NAME, GPGRT_LOG_WITH_PREFIX|GPGRT_LOG_WITH_PID);
 
   /* Make sure that our subsystems are ready.  */
   i18n_init ();
@@ -688,14 +767,6 @@ main (int argc, char **argv )
 
   npth_init ();
 
-  /* 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( _("%s is too old (need %s, have %s)\n"), "libgcrypt",
-                 NEED_LIBGCRYPT_VERSION, gcry_check_version (NULL) );
-    }
-
   malloc_hooks.malloc = gcry_malloc;
   malloc_hooks.realloc = gcry_realloc;
   malloc_hooks.free = gcry_free;
@@ -703,10 +774,11 @@ main (int argc, char **argv )
   assuan_set_gpg_err_source (GPG_ERR_SOURCE_DEFAULT);
   assuan_set_system_hooks (ASSUAN_SYSTEM_NPTH);
   assuan_sock_init ();
-  setup_libassuan_logging (&opt.debug);
+  setup_libassuan_logging (&opt.debug, NULL);
 
   setup_libgcrypt_logging ();
   gcry_control (GCRYCTL_USE_SECURE_RNDPOOL);
+  gcry_set_progress_handler (agent_libgcrypt_progress_cb, NULL);
 
   disable_core_dumps ();
 
@@ -717,8 +789,6 @@ main (int argc, char **argv )
   if (shell && strlen (shell) >= 3 && !strcmp (shell+strlen (shell)-3, "csh") )
     csh_style = 1;
 
-  opt.homedir = default_homedir ();
-
   /* Record some of the original environment strings. */
   {
     const char *s;
@@ -774,7 +844,7 @@ 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);
        else if (pargs.r_opt == oDebugQuickRandom)
           {
             gcry_control (GCRYCTL_ENABLE_QUICK_RANDOM, 0);
@@ -791,8 +861,8 @@ main (int argc, char **argv )
   */
 
   if (default_config)
-    configname = make_filename (opt.homedir, GPG_AGENT_NAME EXTSEP_S "conf",
-                                NULL );
+    configname = make_filename (gnupg_homedir (),
+                                GPG_AGENT_NAME EXTSEP_S "conf", NULL);
 
   argc = orig_argc;
   argv = orig_argv;
@@ -857,7 +927,7 @@ main (int argc, char **argv )
         case oNoGreeting: /* Dummy option.  */ 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;
@@ -874,8 +944,10 @@ main (int argc, char **argv )
         case oXauthority: default_xauthority = xstrdup (pargs.r.ret_str);
           break;
 
-        case oUseStandardSocket:   /* dummy */ break;
-        case oNoUseStandardSocket: /* dummy */ break;
+        case oUseStandardSocket:
+        case oNoUseStandardSocket:
+          obsolete_option (configname, configlineno, "use-standard-socket");
+          break;
 
         case oFakedSystemTime:
           {
@@ -889,11 +961,12 @@ main (int argc, char **argv )
         case oKeepTTY: opt.keep_tty = 1; break;
         case oKeepDISPLAY: opt.keep_display = 1; break;
 
-       case oSSHSupport:  opt.ssh_support = 1; break;
+       case oSSHSupport:
+          ssh_support = 1;
+          break;
         case oPuttySupport:
 #        ifdef HAVE_W32_SYSTEM
           putty_support = 1;
-          opt.ssh_support = 1;
 #        endif
           break;
 
@@ -902,11 +975,18 @@ main (int argc, char **argv )
           socket_name_extra = pargs.r.ret_str;
           break;
 
+        case oBrowserSocket:
+          opt.browser_socket = 1;  /* (1 = points into argv)  */
+          socket_name_browser = pargs.r.ret_str;
+          break;
+
         case oDebugQuickRandom:
           /* Only used by the first stage command line parser.  */
           break;
 
-        case oWriteEnvFile: /* dummy */ break;
+        case oWriteEnvFile:
+          obsolete_option (configname, configlineno, "write-env-file");
+          break;
 
         default : pargs.err = configfp? 1:2; break;
        }
@@ -932,9 +1012,6 @@ main (int argc, char **argv )
 
   finalize_rereadable_options ();
 
-  /* Turn the homedir into an absolute one. */
-  opt.homedir = make_absfilename (opt.homedir, NULL);
-
   /* Print a warning if an argument looks like an option.  */
   if (!opt.quiet && !(pargs.flags & ARGPARSE_FLAG_STOP_SEEN))
     {
@@ -968,6 +1045,34 @@ main (int argc, char **argv )
       agent_exit (0);
     }
 
+  if (! opt.extra_socket)
+    {
+      opt.extra_socket = 1;  /* (1 = points into r/o section)  */
+      socket_name_extra = GPG_AGENT_EXTRA_SOCK_NAME;
+    }
+  else if (socket_name_extra
+           && (!strcmp (socket_name_extra, "none")
+               || !strcmp (socket_name_extra, "/dev/null")))
+    {
+      /* User requested not to create this socket.  */
+      opt.extra_socket = 0;
+      socket_name_extra = NULL;
+    }
+
+  if (! opt.browser_socket)
+    {
+      opt.browser_socket = 1;  /* (1 = points into r/o section)  */
+      socket_name_browser = GPG_AGENT_BROWSER_SOCK_NAME;
+    }
+  else if (socket_name_browser
+           && (!strcmp (socket_name_browser, "none")
+               || !strcmp (socket_name_browser, "/dev/null")))
+    {
+      /* User requested not to create this socket.  */
+      opt.browser_socket = 0;
+      socket_name_browser = NULL;
+    }
+
   set_debug ();
 
   if (atexit (cleanup))
@@ -1007,8 +1112,8 @@ main (int argc, char **argv )
       char *filename_esc;
 
       /* List options and default values in the GPG Conf format.  */
-      filename = make_filename (opt.homedir, GPG_AGENT_NAME EXTSEP_S "conf",
-                                NULL );
+      filename = make_filename (gnupg_homedir (),
+                                GPG_AGENT_NAME EXTSEP_S "conf", NULL);
       filename_esc = percent_escape (filename, NULL);
 
       es_printf ("%s-%s.conf:%lu:\"%s\n",
@@ -1051,17 +1156,22 @@ main (int argc, char **argv )
               GC_OPT_FLAG_NONE|GC_OPT_FLAG_RUNTIME);
       es_printf ("ignore-cache-for-signing:%lu:\n",
               GC_OPT_FLAG_NONE|GC_OPT_FLAG_RUNTIME);
+      es_printf ("no-allow-external-cache:%lu:\n",
+              GC_OPT_FLAG_NONE|GC_OPT_FLAG_RUNTIME);
       es_printf ("no-allow-mark-trusted:%lu:\n",
               GC_OPT_FLAG_NONE|GC_OPT_FLAG_RUNTIME);
       es_printf ("disable-scdaemon:%lu:\n",
               GC_OPT_FLAG_NONE|GC_OPT_FLAG_RUNTIME);
+      es_printf ("enable-ssh-support:%lu:\n", GC_OPT_FLAG_NONE);
 #ifdef HAVE_W32_SYSTEM
       es_printf ("enable-putty-support:%lu:\n", GC_OPT_FLAG_NONE);
-#else
-      es_printf ("enable-ssh-support:%lu:\n", GC_OPT_FLAG_NONE);
 #endif
-      es_printf ("allow-loopback-pinentry:%lu:\n",
+      es_printf ("no-allow-loopback-pinentry:%lu:\n",
+              GC_OPT_FLAG_NONE|GC_OPT_FLAG_RUNTIME);
+      es_printf ("allow-emacs-pinentry:%lu:\n",
                  GC_OPT_FLAG_NONE|GC_OPT_FLAG_RUNTIME);
+      es_printf ("pinentry-timeout:%lu:0:\n",
+                 GC_OPT_FLAG_DEFAULT|GC_OPT_FLAG_RUNTIME);
 
       agent_exit (0);
     }
@@ -1070,9 +1180,9 @@ main (int argc, char **argv )
   if (logfile)
     {
       log_set_file (logfile);
-      log_set_prefix (NULL, (JNLIB_LOG_WITH_PREFIX
-                             |JNLIB_LOG_WITH_TIME
-                             |JNLIB_LOG_WITH_PID));
+      log_set_prefix (NULL, (GPGRT_LOG_WITH_PREFIX
+                             | GPGRT_LOG_WITH_TIME
+                             | GPGRT_LOG_WITH_PID));
       current_logfile = xstrdup (logfile);
     }
 
@@ -1114,8 +1224,11 @@ main (int argc, char **argv )
     { /* Regular server mode */
       gnupg_fd_t fd;
       gnupg_fd_t fd_extra = GNUPG_INVALID_FD;
+      gnupg_fd_t fd_browser = GNUPG_INVALID_FD;
       gnupg_fd_t fd_ssh = GNUPG_INVALID_FD;
+#ifndef HAVE_W32_SYSTEM
       pid_t pid;
+#endif
 
       /* Remove the DISPLAY variable so that a pinentry does not
          default to a specific display.  There is still a default
@@ -1128,28 +1241,39 @@ main (int argc, char **argv )
         gnupg_unsetenv ("DISPLAY");
 #endif
 
+      /* Remove the INSIDE_EMACS variable so that a pinentry does not
+         always try to interact with Emacs.  The variable is set when
+         a client requested this using an OPTION command.  */
+      gnupg_unsetenv ("INSIDE_EMACS");
+
       /* Create the sockets.  */
       socket_name = create_socket_name (GPG_AGENT_SOCK_NAME, 1);
-      fd = create_server_socket (socket_name, 1,
+      fd = create_server_socket (socket_name, 1, 0,
                                  &redir_socket_name, &socket_nonce);
 
       if (opt.extra_socket)
         {
           socket_name_extra = create_socket_name (socket_name_extra, 0);
           opt.extra_socket = 2; /* Indicate that it has been malloced.  */
-          fd_extra = create_server_socket (socket_name_extra, 0,
+          fd_extra = create_server_socket (socket_name_extra, 0, 0,
                                            &redir_socket_name_extra,
                                            &socket_nonce_extra);
         }
 
-      if (opt.ssh_support)
+      if (opt.browser_socket)
         {
-          socket_name_ssh = create_socket_name (GPG_AGENT_SSH_SOCK_NAME, 1);
-          fd_ssh = create_server_socket (socket_name_ssh, 0,
-                                         &redir_socket_name_ssh,
-                                         &socket_nonce_ssh);
+          socket_name_browser = create_socket_name (socket_name_browser, 0);
+          opt.browser_socket = 2; /* Indicate that it has been malloced.  */
+          fd_browser = create_server_socket (socket_name_browser, 0, 0,
+                                             &redir_socket_name_browser,
+                                             &socket_nonce_browser);
         }
 
+      socket_name_ssh = create_socket_name (GPG_AGENT_SSH_SOCK_NAME, 1);
+      fd_ssh = create_server_socket (socket_name_ssh, 0, 1,
+                                     &redir_socket_name_ssh,
+                                     &socket_nonce_ssh);
+
       /* 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. */
@@ -1160,7 +1284,6 @@ main (int argc, char **argv )
 #ifdef HAVE_W32_SYSTEM
       (void)csh_style;
       (void)nodetach;
-      pid = getpid ();
 #else /*!HAVE_W32_SYSTEM*/
       pid = fork ();
       if (pid == (pid_t)-1)
@@ -1191,7 +1314,7 @@ main (int argc, char **argv )
 #endif /*HAVE_SIGPROCMASK*/
 
           /* Create the SSH info string if enabled. */
-         if (opt.ssh_support)
+         if (ssh_support)
            {
              if (asprintf (&infostr_ssh_sock, "SSH_AUTH_SOCK=%s",
                            socket_name_ssh) < 0)
@@ -1213,13 +1336,14 @@ main (int argc, char **argv )
                                the child should do this from now on */
          if (opt.extra_socket)
            *socket_name_extra = 0;
-         if (opt.ssh_support)
-           *socket_name_ssh = 0;
+         if (opt.browser_socket)
+           *socket_name_browser = 0;
+          *socket_name_ssh = 0;
 
           if (argc)
             { /* Run the program given on the commandline.  */
-              if (opt.ssh_support && (putenv (infostr_ssh_sock)
-                                      || putenv (infostr_ssh_valid)))
+              if (ssh_support && (putenv (infostr_ssh_sock)
+                                  || putenv (infostr_ssh_valid)))
                 {
                   log_error ("failed to set environment: %s\n",
                              strerror (errno) );
@@ -1245,7 +1369,7 @@ main (int argc, char **argv )
                  shell's eval to set it */
               if (csh_style)
                 {
-                 if (opt.ssh_support)
+                 if (ssh_support)
                    {
                      *strchr (infostr_ssh_sock, '=') = ' ';
                      es_printf ("setenv %s;\n", infostr_ssh_sock);
@@ -1253,13 +1377,13 @@ main (int argc, char **argv )
                 }
               else
                 {
-                 if (opt.ssh_support)
+                 if (ssh_support)
                    {
                      es_printf ("%s; export SSH_AUTH_SOCK;\n",
                                  infostr_ssh_sock);
                    }
                 }
-             if (opt.ssh_support)
+             if (ssh_support)
                {
                  xfree (infostr_ssh_sock);
                  xfree (infostr_ssh_valid);
@@ -1302,7 +1426,7 @@ main (int argc, char **argv )
             }
 
           log_get_prefix (&oldflags);
-          log_set_prefix (NULL, oldflags | JNLIB_LOG_RUN_DETACHED);
+          log_set_prefix (NULL, oldflags | GPGRT_LOG_RUN_DETACHED);
           opt.running_detached = 1;
         }
 
@@ -1323,7 +1447,7 @@ main (int argc, char **argv )
 #endif /*!HAVE_W32_SYSTEM*/
 
       log_info ("%s %s started\n", strusage(11), strusage(13) );
-      handle_connections (fd, fd_extra, fd_ssh);
+      handle_connections (fd, fd_extra, fd_browser, fd_ssh);
       assuan_sock_close (fd);
     }
 
@@ -1359,6 +1483,88 @@ agent_exit (int rc)
 }
 
 
+/* This is our callback function for gcrypt progress messages.  It is
+   set once at startup and dispatches progress messages to the
+   corresponding threads of the agent.  */
+static void
+agent_libgcrypt_progress_cb (void *data, const char *what, int printchar,
+                             int current, int total)
+{
+  struct progress_dispatch_s *dispatch;
+  npth_t mytid = npth_self ();
+
+  (void)data;
+
+  for (dispatch = progress_dispatch_list; dispatch; dispatch = dispatch->next)
+    if (dispatch->ctrl && dispatch->tid == mytid)
+      break;
+  if (dispatch && dispatch->cb)
+    dispatch->cb (dispatch->ctrl, what, printchar, current, total);
+}
+
+
+/* If a progress dispatcher callback has been associated with the
+ * current connection unregister it.  */
+static void
+unregister_progress_cb (void)
+{
+  struct progress_dispatch_s *dispatch;
+  npth_t mytid = npth_self ();
+
+  for (dispatch = progress_dispatch_list; dispatch; dispatch = dispatch->next)
+    if (dispatch->ctrl && dispatch->tid == mytid)
+      break;
+  if (dispatch)
+    {
+      dispatch->ctrl = NULL;
+      dispatch->cb = NULL;
+    }
+}
+
+
+/* Setup a progress callback CB for the current connection.  Using a
+ * CB of NULL disables the callback.  */
+void
+agent_set_progress_cb (void (*cb)(ctrl_t ctrl, const char *what,
+                                  int printchar, int current, int total),
+                       ctrl_t ctrl)
+{
+  struct progress_dispatch_s *dispatch, *firstfree;
+  npth_t mytid = npth_self ();
+
+  firstfree = NULL;
+  for (dispatch = progress_dispatch_list; dispatch; dispatch = dispatch->next)
+    {
+      if (dispatch->ctrl && dispatch->tid == mytid)
+        break;
+      if (!dispatch->ctrl && !firstfree)
+        firstfree = dispatch;
+    }
+  if (!dispatch) /* None allocated: Reuse or allocate a new one.  */
+    {
+      if (firstfree)
+        {
+          dispatch = firstfree;
+        }
+      else if ((dispatch = xtrycalloc (1, sizeof *dispatch)))
+        {
+          dispatch->next = progress_dispatch_list;
+          progress_dispatch_list = dispatch;
+        }
+      else
+        {
+          log_error ("error allocating new progress dispatcher slot: %s\n",
+                     gpg_strerror (gpg_error_from_syserror ()));
+          return;
+        }
+      dispatch->ctrl = ctrl;
+      dispatch->tid = mytid;
+    }
+
+  dispatch->cb = cb;
+}
+
+
 /* Each thread has its own local variables conveyed by a control
    structure usually identified by an argument named CTRL.  This
    function is called immediately after allocating the control
@@ -1395,6 +1601,7 @@ agent_init_default_ctrl (ctrl_t ctrl)
 static void
 agent_deinit_default_ctrl (ctrl_t ctrl)
 {
+  unregister_progress_cb ();
   session_env_release (ctrl->session_env);
 
   if (ctrl->lc_ctype)
@@ -1406,7 +1613,7 @@ agent_deinit_default_ctrl (ctrl_t ctrl)
 
 /* Because the ssh protocol does not send us information about the
    current TTY setting, we use this function to use those from startup
-   or those explictly set.  This is also used for the restricted mode
+   or those explicitly set.  This is also used for the restricted mode
    where we ignore requests to change the environment.  */
 gpg_error_t
 agent_copy_startup_env (ctrl_t ctrl)
@@ -1505,6 +1712,14 @@ get_agent_ssh_socket_name (void)
 }
 
 
+/* Return the number of active connections. */
+int
+get_agent_active_connection_count (void)
+{
+  return active_connections;
+}
+
+
 /* Under W32, this function returns the handle of the scdaemon
    notification event.  Calling it the first time creates that
    event.  */
@@ -1561,7 +1776,7 @@ create_socket_name (char *standard_name, int with_homedir)
   char *name;
 
   if (with_homedir)
-    name = make_filename (opt.homedir, standard_name, NULL);
+    name = make_filename (gnupg_socketdir (), standard_name, NULL);
   else
     name = make_filename (standard_name, NULL);
   if (strchr (name, PATHSEP_C))
@@ -1579,9 +1794,10 @@ create_socket_name (char *standard_name, int with_homedir)
    function needs to be used for the regular socket first (indicated
    by PRIMARY) and only then for the extra and the ssh sockets.  If
    the socket has been redirected the name of the real socket is
-   stored as a malloced string at R_REDIR_NAME.  */
+   stored as a malloced string at R_REDIR_NAME.  If CYGWIN is set a
+   Cygwin compatible socket is created (Windows only). */
 static gnupg_fd_t
-create_server_socket (char *name, int primary,
+create_server_socket (char *name, int primary, int cygwin,
                       char **r_redir_name, assuan_sock_nonce_t *nonce)
 {
   struct sockaddr *addr;
@@ -1601,10 +1817,12 @@ create_server_socket (char *name, int primary,
       agent_exit (2);
     }
 
+  if (cygwin)
+    assuan_sock_set_flag (fd, "cygwin", 1);
+
   unaddr = xmalloc (sizeof *unaddr);
   addr = (struct sockaddr*)unaddr;
 
-#if ASSUAN_VERSION_NUMBER >= 0x020104 /* >= 2.1.4 */
   {
     int redirected;
 
@@ -1625,17 +1843,6 @@ create_server_socket (char *name, int primary,
           log_info ("redirecting socket '%s' to '%s'\n", name, *r_redir_name);
       }
   }
-#else /* Assuan < 2.1.4 */
-  memset (unaddr, 0, sizeof *unaddr);
-  unaddr->sun_family = AF_UNIX;
-  if (strlen (name) + 1 >= sizeof (unaddr->sun_path))
-    {
-      log_error (_("socket name '%s' is too long\n"), name);
-      *name = 0; /* Inhibit removal of the socket by cleanup(). */
-      agent_exit (2);
-    }
-  strcpy (unaddr->sun_path, name);
-#endif /* Assuan < 2.1.4 */
 
   len = SUN_LEN (unaddr);
   rc = assuan_sock_bind (fd, addr, len);
@@ -1658,7 +1865,7 @@ create_server_socket (char *name, int primary,
          server is not yet operational; this would lead to a hang.  */
       if (primary && !check_for_running_agent (1))
         {
-          log_set_prefix (NULL, JNLIB_LOG_WITH_PREFIX);
+          log_set_prefix (NULL, GPGRT_LOG_WITH_PREFIX);
           log_set_file (NULL);
           log_error (_("a gpg-agent is already running - "
                        "not starting a new one\n"));
@@ -1684,6 +1891,10 @@ create_server_socket (char *name, int primary,
       agent_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"), strerror (errno));
@@ -1717,9 +1928,13 @@ create_private_keys_directory (const char *home)
       else if (!opt.quiet)
         log_info (_("directory '%s' created\n"), fname);
     }
+  if (gnupg_chmod (fname, "-rwx"))
+    log_error (_("can't set permissions of '%s': %s\n"),
+               fname, strerror (errno));
   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
@@ -1733,7 +1948,7 @@ create_directories (void)
   const char *defhome = standard_homedir ();
   char *home;
 
-  home = make_filename (opt.homedir, NULL);
+  home = make_filename (gnupg_homedir (), NULL);
   if ( stat (home, &statbuf) )
     {
       if (errno == ENOENT)
@@ -1825,9 +2040,14 @@ agent_sighup_action (void)
 {
   log_info ("SIGHUP received - "
             "re-reading configuration and flushing cache\n");
+
   agent_flush_cache ();
   reread_configuration ();
   agent_reload_trustlist ();
+  /* We flush the module name cache so that after installing a
+     "pinentry" binary that one can be used in case the
+     "pinentry-basic" fallback was in use.  */
+  gnupg_module_name_flush_some ();
 }
 
 
@@ -1945,7 +2165,7 @@ putty_message_proc (HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam)
   if (!cds->cbData || mapfile[cds->cbData - 1])
     return 0;  /* Ignore empty and non-properly terminated strings.  */
 
-  if (DBG_ASSUAN)
+  if (DBG_IPC)
     {
       npth_protect ();
       log_debug ("ssh map file '%s'", mapfile);
@@ -1953,7 +2173,7 @@ putty_message_proc (HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam)
     }
 
   maphd = OpenFileMapping (FILE_MAP_ALL_ACCESS, FALSE, mapfile);
-  if (DBG_ASSUAN)
+  if (DBG_IPC)
     {
       npth_protect ();
       log_debug ("ssh map handle %p\n", maphd);
@@ -1982,7 +2202,7 @@ putty_message_proc (HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam)
       goto leave;
     }
 
-  if (DBG_ASSUAN)
+  if (DBG_IPC)
     {
       char *sidstr;
 
@@ -2003,7 +2223,7 @@ putty_message_proc (HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam)
     }
 
   data = MapViewOfFile (maphd, FILE_MAP_ALL_ACCESS, 0, 0, 0);
-  if (DBG_ASSUAN)
+  if (DBG_IPC)
     log_debug ("ssh IPC buffer at %p\n", data);
   if (!data)
     goto leave;
@@ -2106,27 +2326,22 @@ putty_message_thread (void *arg)
 
 
 static void *
-start_connection_thread (ctrl_t ctrl)
+do_start_connection_thread (ctrl_t ctrl)
 {
-  if (check_nonce (ctrl, &socket_nonce))
-    {
-      log_error ("handler 0x%lx nonce check FAILED\n",
-                 (unsigned long) npth_self());
-      return NULL;
-    }
-
+  active_connections++;
   agent_init_default_ctrl (ctrl);
-  if (opt.verbose)
+  if (opt.verbose && !DBG_IPC)
     log_info (_("handler 0x%lx for fd %d started\n"),
               (unsigned long) npth_self(), FD2INT(ctrl->thread_startup.fd));
 
   start_command_handler (ctrl, GNUPG_INVALID_FD, ctrl->thread_startup.fd);
-  if (opt.verbose)
+  if (opt.verbose && !DBG_IPC)
     log_info (_("handler 0x%lx for fd %d terminated\n"),
               (unsigned long) npth_self(), FD2INT(ctrl->thread_startup.fd));
 
   agent_deinit_default_ctrl (ctrl);
   xfree (ctrl);
+  active_connections--;
   return NULL;
 }
 
@@ -2137,7 +2352,14 @@ start_connection_thread_std (void *arg)
 {
   ctrl_t ctrl = arg;
 
-  return start_connection_thread (ctrl);
+  if (check_nonce (ctrl, &socket_nonce))
+    {
+      log_error ("handler 0x%lx nonce check FAILED\n",
+                 (unsigned long) npth_self());
+      return NULL;
+    }
+
+  return do_start_connection_thread (ctrl);
 }
 
 
@@ -2147,8 +2369,33 @@ start_connection_thread_extra (void *arg)
 {
   ctrl_t ctrl = arg;
 
+  if (check_nonce (ctrl, &socket_nonce_extra))
+    {
+      log_error ("handler 0x%lx nonce check FAILED\n",
+                 (unsigned long) npth_self());
+      return NULL;
+    }
+
   ctrl->restricted = 1;
-  return start_connection_thread (ctrl);
+  return do_start_connection_thread (ctrl);
+}
+
+
+/* This is the browser socket connection thread's main function.  */
+static void *
+start_connection_thread_browser (void *arg)
+{
+  ctrl_t ctrl = arg;
+
+  if (check_nonce (ctrl, &socket_nonce_browser))
+    {
+      log_error ("handler 0x%lx nonce check FAILED\n",
+                 (unsigned long) npth_self());
+      return NULL;
+    }
+
+  ctrl->restricted = 2;
+  return do_start_connection_thread (ctrl);
 }
 
 
@@ -2161,6 +2408,7 @@ start_connection_thread_ssh (void *arg)
   if (check_nonce (ctrl, &socket_nonce_ssh))
     return NULL;
 
+  active_connections++;
   agent_init_default_ctrl (ctrl);
   if (opt.verbose)
     log_info (_("ssh handler 0x%lx for fd %d started\n"),
@@ -2173,15 +2421,42 @@ start_connection_thread_ssh (void *arg)
 
   agent_deinit_default_ctrl (ctrl);
   xfree (ctrl);
+  active_connections--;
   return NULL;
 }
 
 
+#ifdef HAVE_INOTIFY_INIT
+/* Read an inotify event and return true if it matches NAME.  */
+static int
+my_inotify_is_name (int fd, const char *name)
+{
+  union {
+    struct inotify_event ev;
+    char _buf[sizeof (struct inotify_event) + 100 + 1];
+  } buf;
+  int n;
+
+  n = npth_read (fd, &buf, sizeof buf);
+  if (n < sizeof (struct inotify_event))
+    return 0;
+  if (buf.ev.len < strlen (name)+1)
+    return 0;
+  if (strcmp (buf.ev.name, name))
+    return 0; /* Not the desired file.  */
+
+  return 1; /* Found.  */
+}
+#endif /*HAVE_INOTIFY_INIT*/
+
+
+
 /* Connection handler loop.  Wait for connection requests and spawn a
    thread after accepting a connection.  */
 static void
 handle_connections (gnupg_fd_t listen_fd,
                     gnupg_fd_t listen_fd_extra,
+                    gnupg_fd_t listen_fd_browser,
                     gnupg_fd_t listen_fd_ssh)
 {
   npth_attr_t tattr;
@@ -2199,14 +2474,18 @@ handle_connections (gnupg_fd_t listen_fd,
   HANDLE events[2];
   unsigned int events_set;
 #endif
+#ifdef HAVE_INOTIFY_INIT
+  int my_inotify_fd;
+#endif /*HAVE_INOTIFY_INIT*/
   struct {
     const char *name;
     void *(*func) (void *arg);
     gnupg_fd_t l_fd;
   } listentbl[] = {
-    { "std",  start_connection_thread_std   },
-    { "extra",start_connection_thread_extra },
-    { "ssh",  start_connection_thread_ssh   }
+    { "std",     start_connection_thread_std   },
+    { "extra",   start_connection_thread_extra },
+    { "browser", start_connection_thread_browser },
+    { "ssh",    start_connection_thread_ssh   }
   };
 
 
@@ -2235,6 +2514,28 @@ handle_connections (gnupg_fd_t listen_fd,
 # endif
 #endif
 
+#ifdef HAVE_INOTIFY_INIT
+  if (disable_check_own_socket)
+    my_inotify_fd = -1;
+  else if ((my_inotify_fd = inotify_init ()) == -1)
+    log_info ("error enabling fast daemon termination: %s\n",
+              strerror (errno));
+  else
+    {
+      /* We need to watch the directory for the file because there
+       * won't be an IN_DELETE_SELF for a socket file.  */
+      char *slash = strrchr (socket_name, '/');
+      log_assert (slash && slash[1]);
+      *slash = 0;
+      if (inotify_add_watch (my_inotify_fd, socket_name, IN_DELETE) == -1)
+        {
+          close (my_inotify_fd);
+          my_inotify_fd = -1;
+        }
+      *slash = '/';
+    }
+#endif /*HAVE_INOTIFY_INIT*/
+
   /* On Windows we need to fire up a separate thread to listen for
      requests from Putty (an SSH client), so we can replace Putty's
      Pageant (its ssh-agent implementation). */
@@ -2264,16 +2565,31 @@ handle_connections (gnupg_fd_t listen_fd,
       if (FD2INT (listen_fd_extra) > nfd)
         nfd = FD2INT (listen_fd_extra);
     }
+  if (listen_fd_browser != GNUPG_INVALID_FD)
+    {
+      FD_SET ( FD2INT(listen_fd_browser), &fdset);
+      if (FD2INT (listen_fd_browser) > nfd)
+        nfd = FD2INT (listen_fd_browser);
+    }
   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);
     }
+#ifdef HAVE_INOTIFY_INIT
+  if (my_inotify_fd != -1)
+    {
+      FD_SET (my_inotify_fd, &fdset);
+      if (my_inotify_fd > nfd)
+        nfd = my_inotify_fd;
+    }
+#endif /*HAVE_INOTIFY_INIT*/
 
   listentbl[0].l_fd = listen_fd;
   listentbl[1].l_fd = listen_fd_extra;
-  listentbl[2].l_fd = listen_fd_ssh;
+  listentbl[2].l_fd = listen_fd_browser;
+  listentbl[3].l_fd = listen_fd_ssh;
 
   npth_clock_gettime (&abstime);
   abstime.tv_sec += TIMERTICK_INTERVAL;
@@ -2343,6 +2659,15 @@ handle_connections (gnupg_fd_t listen_fd,
           ctrl_t ctrl;
           npth_t thread;
 
+#ifdef HAVE_INOTIFY_INIT
+          if (my_inotify_fd != -1 && FD_ISSET (my_inotify_fd, &read_fdset)
+              && my_inotify_is_name (my_inotify_fd, GPG_AGENT_SOCK_NAME))
+            {
+              shutdown_pending = 1;
+              log_info ("socket file has been removed - shutting down\n");
+            }
+#endif /*HAVE_INOTIFY_INIT*/
+
           for (idx=0; idx < DIM(listentbl); idx++)
             {
               if (listentbl[idx].l_fd == GNUPG_INVALID_FD)
@@ -2389,6 +2714,10 @@ handle_connections (gnupg_fd_t listen_fd,
         }
     }
 
+#ifdef HAVE_INOTIFY_INIT
+  if (my_inotify_fd != -1)
+    close (my_inotify_fd);
+#endif /*HAVE_INOTIFY_INIT*/
   cleanup ();
   log_info (_("%s %s stopped\n"), strusage(11), strusage(13));
   npth_attr_destroy (&tattr);
@@ -2426,6 +2755,7 @@ check_own_socket_thread (void *arg)
       log_error ("can't allocate assuan context: %s\n", gpg_strerror (rc));
       goto leave;
     }
+  assuan_set_flag (ctx, ASSUAN_NO_LOGGING, 1);
 
   rc = assuan_socket_connect (ctx, sockname, (pid_t)(-1), 0);
   if (rc)
@@ -2493,7 +2823,7 @@ check_own_socket (void)
   if (check_own_socket_running || shutdown_pending)
     return;  /* Still running or already shutting down.  */
 
-  sockname = make_filename (opt.homedir, GPG_AGENT_SOCK_NAME, NULL);
+  sockname = make_filename_try (gnupg_socketdir (), GPG_AGENT_SOCK_NAME, NULL);
   if (!sockname)
     return; /* Out of memory.  */
 
@@ -2519,7 +2849,9 @@ check_for_running_agent (int silent)
   char *sockname;
   assuan_context_t ctx = NULL;
 
-  sockname = make_filename (opt.homedir, GPG_AGENT_SOCK_NAME, NULL);
+  sockname = make_filename_try (gnupg_socketdir (), GPG_AGENT_SOCK_NAME, NULL);
+  if (!sockname)
+    return gpg_error_from_syserror ();
 
   err = assuan_new (&ctx);
   if (!err)