sm: Fix certificate creation with key on card.
[gnupg.git] / dirmngr / dirmngr.c
index a32040e..5b9e7a8 100644 (file)
@@ -16,7 +16,9 @@
  * 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, see <http://www.gnu.org/licenses/>.
+ * along with this program; if not, see <https://www.gnu.org/licenses/>.
+ *
+ * SPDX-License-Identifier: GPL-3.0+
  */
 
 #include <config.h>
@@ -39,6 +41,9 @@
 #ifdef HAVE_SIGNAL_H
 # include <signal.h>
 #endif
+#ifdef HAVE_INOTIFY_INIT
+# include <sys/inotify.h>
+#endif /*HAVE_INOTIFY_INIT*/
 #include <npth.h>
 
 #include "dirmngr-err.h"
 #if USE_LDAP
 # include "ldapserver.h"
 #endif
-#include "asshelp.h"
+#include "../common/asshelp.h"
 #if USE_LDAP
 # include "ldap-wrapper.h"
 #endif
 #include "../common/init.h"
-#include "gc-opt-flags.h"
-
-/* The plain Windows version uses the windows service system.  For
-   example to start the service you may use "sc start dirmngr".
-   WindowsCE does not support this; the service system over there is
-   based on a single process with all services being DLLs - we can't
-   support this easily.  */
-#if defined(HAVE_W32_SYSTEM) && !defined(HAVE_W32CE_SYSTEM)
-# define USE_W32_SERVICE 1
-#endif
+#include "../common/gc-opt-flags.h"
+#include "dns-stuff.h"
+#include "http-common.h"
 
 #ifndef ENAMETOOLONG
 # define ENAMETOOLONG EINVAL
@@ -93,7 +91,7 @@ enum cmd_and_opt_values {
 
   aServer,
   aDaemon,
-  aService,
+  aSupervised,
   aListCRLs,
   aLoadCRL,
   aFetchCRL,
@@ -116,6 +114,8 @@ enum cmd_and_opt_values {
   oBatch,
   oDisableHTTP,
   oDisableLDAP,
+  oDisableIPv4,
+  oDisableIPv6,
   oIgnoreLDAPDP,
   oIgnoreHTTPDP,
   oIgnoreOCSPSvcUrl,
@@ -136,12 +136,22 @@ enum cmd_and_opt_values {
   oFakedSystemTime,
   oForce,
   oAllowOCSP,
+  oAllowVersionCheck,
   oSocketName,
   oLDAPWrapperProgram,
   oHTTPWrapperProgram,
   oIgnoreCertExtension,
   oUseTor,
+  oNoUseTor,
   oKeyServer,
+  oNameServer,
+  oDisableCheckOwnSocket,
+  oStandardResolver,
+  oRecursiveResolver,
+  oResolverTimeout,
+  oConnectTimeout,
+  oConnectQuickTimeout,
+  oListenBacklog,
   aTest
 };
 
@@ -153,8 +163,8 @@ static ARGPARSE_OPTS opts[] = {
 
   ARGPARSE_c (aServer,   "server",  N_("run in server mode (foreground)") ),
   ARGPARSE_c (aDaemon,   "daemon",  N_("run in daemon mode (background)") ),
-#ifdef USE_W32_SERVICE
-  ARGPARSE_c (aService,  "service", N_("run as windows service (background)")),
+#ifndef HAVE_W32_SYSTEM
+  ARGPARSE_c (aSupervised,  "supervised", N_("run in supervised mode")),
 #endif
   ARGPARSE_c (aListCRLs, "list-crls", N_("list the contents of the CRL cache")),
   ARGPARSE_c (aLoadCRL,  "load-crl",  N_("|FILE|load CRL from FILE into cache")),
@@ -179,6 +189,8 @@ static ARGPARSE_OPTS opts[] = {
   ARGPARSE_s_n (oBatch,    "batch",       N_("run without asking a user")),
   ARGPARSE_s_n (oForce,    "force",       N_("force loading of outdated CRLs")),
   ARGPARSE_s_n (oAllowOCSP, "allow-ocsp", N_("allow sending OCSP requests")),
+  ARGPARSE_s_n (oAllowVersionCheck, "allow-version-check",
+                N_("allow online software version check")),
   ARGPARSE_s_n (oDisableHTTP, "disable-http", N_("inhibit the use of HTTP")),
   ARGPARSE_s_n (oDisableLDAP, "disable-ldap", N_("inhibit the use of LDAP")),
   ARGPARSE_s_n (oIgnoreHTTPDP,"ignore-http-dp",
@@ -214,11 +226,16 @@ static ARGPARSE_OPTS opts[] = {
   ARGPARSE_s_i (oMaxReplies, "max-replies",
                 N_("|N|do not return more than N items in one query")),
 
+  ARGPARSE_s_s (oNameServer, "nameserver", "@"),
   ARGPARSE_s_s (oKeyServer, "keyserver", "@"),
   ARGPARSE_s_s (oHkpCaCert, "hkp-cacert",
                 N_("|FILE|use the CA certificates in FILE for HKP over TLS")),
 
-  ARGPARSE_s_n (oUseTor, "use-tor", N_("route all network traffic via TOR")),
+  ARGPARSE_s_n (oUseTor, "use-tor", N_("route all network traffic via Tor")),
+  ARGPARSE_s_n (oNoUseTor, "no-use-tor", "@"),
+
+  ARGPARSE_s_n (oDisableIPv4, "disable-ipv4", "@"),
+  ARGPARSE_s_n (oDisableIPv6, "disable-ipv6", "@"),
 
   ARGPARSE_s_s (oSocketName, "socket-name", "@"),  /* Only for debugging.  */
 
@@ -228,12 +245,19 @@ static ARGPARSE_OPTS opts[] = {
   ARGPARSE_s_i (oGnutlsDebug, "gnutls-debug", "@"),
   ARGPARSE_s_i (oGnutlsDebug, "tls-debug", "@"),
   ARGPARSE_s_i (oDebugWait, "debug-wait", "@"),
+  ARGPARSE_s_n (oDisableCheckOwnSocket, "disable-check-own-socket", "@"),
   ARGPARSE_s_n (oNoGreeting, "no-greeting", "@"),
   ARGPARSE_s_s (oHomedir, "homedir", "@"),
   ARGPARSE_s_s (oLDAPWrapperProgram, "ldap-wrapper-program", "@"),
   ARGPARSE_s_s (oHTTPWrapperProgram, "http-wrapper-program", "@"),
   ARGPARSE_s_n (oHonorHTTPProxy, "honor-http-proxy", "@"),
   ARGPARSE_s_s (oIgnoreCertExtension,"ignore-cert-extension", "@"),
+  ARGPARSE_s_n (oStandardResolver, "standard-resolver", "@"),
+  ARGPARSE_s_n (oRecursiveResolver, "recursive-resolver", "@"),
+  ARGPARSE_s_i (oResolverTimeout, "resolver-timeout", "@"),
+  ARGPARSE_s_i (oConnectTimeout, "connect-timeout", "@"),
+  ARGPARSE_s_i (oConnectQuickTimeout, "connect-quick-timeout", "@"),
+  ARGPARSE_s_i (oListenBacklog, "listen-backlog", "@"),
 
   ARGPARSE_group (302,N_("@\n(See the \"info\" manual for a complete listing "
                          "of all commands and options)\n")),
@@ -251,12 +275,18 @@ static struct debug_flags_s debug_flags [] =
     { DBG_MEMSTAT_VALUE, "memstat" },
     { DBG_HASHING_VALUE, "hashing" },
     { DBG_IPC_VALUE    , "ipc"     },
+    { DBG_DNS_VALUE    , "dns"     },
+    { DBG_NETWORK_VALUE, "network" },
     { DBG_LOOKUP_VALUE , "lookup"  },
+    { DBG_EXTPROG_VALUE, "extprog" },
     { 77, NULL } /* 77 := Do not exit on "help" or "?".  */
   };
 
 #define DEFAULT_MAX_REPLIES 10
-#define DEFAULT_LDAP_TIMEOUT 100 /* arbitrary large timeout */
+#define DEFAULT_LDAP_TIMEOUT 15  /* seconds */
+
+#define DEFAULT_CONNECT_TIMEOUT       (15*1000)  /* 15 seconds */
+#define DEFAULT_CONNECT_QUICK_TIMEOUT ( 2*1000)  /*  2 seconds */
 
 /* For the cleanup handler we need to keep track of the socket's name.  */
 static const char *socket_name;
@@ -268,6 +298,10 @@ static const char *redir_socket_name;
    POSIX systems). */
 static assuan_sock_nonce_t socket_nonce;
 
+/* Value for the listen() backlog argument.
+ * Change at runtime with --listen-backlog.  */
+static int listen_backlog = 64;
+
 /* Only if this flag has been set will we remove the socket file.  */
 static int cleanup_socket;
 
@@ -284,20 +318,35 @@ static int opt_gnutls_debug = -1;
 /* Flag indicating that a shutdown has been requested.  */
 static volatile int shutdown_pending;
 
+/* Flags to indicate that we shall not watch our own socket. */
+static int disable_check_own_socket;
+
+/* Flag to control the Tor mode.  */
+static enum
+  { TOR_MODE_AUTO = 0,  /* Switch to NO or YES         */
+    TOR_MODE_NEVER,     /* Never use Tor.              */
+    TOR_MODE_NO,        /* Do not use Tor              */
+    TOR_MODE_YES,       /* Use Tor                     */
+    TOR_MODE_FORCE      /* Force using Tor             */
+  } tor_mode;
+
+
 /* Counter for the active connections.  */
 static int active_connections;
 
-/* The timer tick used for housekeeping stuff.  For Windows we use a
-   longer period as the SetWaitableTimer seems to signal earlier than
-   the 2 seconds.  All values are in seconds. */
-#if defined(HAVE_W32CE_SYSTEM)
-# define TIMERTICK_INTERVAL         (60)
-#elif defined(HAVE_W32_SYSTEM)
-# define TIMERTICK_INTERVAL          (4)
-#else
-# define TIMERTICK_INTERVAL          (2)
-#endif
+/* This flag is set by any network access and used by the housekeeping
+ * thread to run background network tasks.  */
+static int network_activity_seen;
+
+/* A list of filenames registered with --hkp-cacert.  */
+static strlist_t hkp_cacert_filenames;
+
+
+/* The timer tick used for housekeeping stuff.  The second constant is used when a shutdown is pending.  */
+#define TIMERTICK_INTERVAL           (60)
+#define TIMERTICK_INTERVAL_SHUTDOWN  (4)
 
+/* How oft to run the housekeeping.  */
 #define HOUSEKEEPING_INTERVAL      (600)
 
 
@@ -317,7 +366,7 @@ union int_and_ptr_u
    local storage.  We use this in conjunction with the
    log_set_pid_suffix_cb feature.  */
 #ifndef HAVE_W32_SYSTEM
-static int my_tlskey_current_fd;
+static npth_key_t my_tlskey_current_fd;
 #endif
 
 /* Prototypes. */
@@ -326,6 +375,7 @@ static void cleanup (void);
 static ldap_server_t parse_ldapserver_file (const char* filename);
 #endif /*USE_LDAP*/
 static fingerprint_list_t parse_ocsp_signer (const char *string);
+static void netactivity_action (void);
 static void handle_connections (assuan_fd_t listen_fd);
 
 /* NPth wrapper function definitions. */
@@ -361,7 +411,7 @@ my_strusage( int level )
 
 /* Callback from libksba to hash a provided buffer.  Our current
    implementation does only allow SHA-1 for hashing. This may be
-   extended by mapping the name, testing for algorithm availibility
+   extended by mapping the name, testing for algorithm availability
    and adjust the length checks accordingly. */
 static gpg_error_t
 my_ksba_hash_buffer (void *arg, const char *oid,
@@ -466,6 +516,53 @@ set_debug (void)
 
 
 static void
+set_tor_mode (void)
+{
+  if (dirmngr_use_tor ())
+    {
+      /* Enable Tor mode and when called again force a new circuit
+       * (e.g. on SIGHUP).  */
+      enable_dns_tormode (1);
+      if (assuan_sock_set_flag (ASSUAN_INVALID_FD, "tor-mode", 1))
+        {
+          log_error ("error enabling Tor mode: %s\n", strerror (errno));
+          log_info ("(is your Libassuan recent enough?)\n");
+        }
+    }
+  else
+    disable_dns_tormode ();
+}
+
+
+/* Return true if Tor shall be used.  */
+int
+dirmngr_use_tor (void)
+{
+  if (tor_mode == TOR_MODE_AUTO)
+    {
+      /* Figure out whether Tor is running.  */
+      assuan_fd_t sock;
+
+      sock = assuan_sock_connect_byname (NULL, 0, 0, NULL, ASSUAN_SOCK_TOR);
+      if (sock == ASSUAN_INVALID_FD)
+        tor_mode = TOR_MODE_NO;
+      else
+        {
+          tor_mode = TOR_MODE_YES;
+          assuan_sock_close (sock);
+        }
+    }
+
+  if (tor_mode == TOR_MODE_FORCE)
+    return 2; /* Use Tor (using 2 to indicate force mode) */
+  else if (tor_mode == TOR_MODE_YES)
+    return 1; /* Use Tor */
+  else
+    return 0; /* Do not use Tor.  */
+}
+
+
+static void
 wrong_args (const char *text)
 {
   es_fprintf (es_stderr, _("usage: %s [options] "), DIRMNGR_NAME);
@@ -509,6 +606,7 @@ parse_rereadable_options (ARGPARSE_ARGS *pargs, int reread)
       opt.ignore_ldap_dp = 0;
       opt.ignore_ocsp_service_url = 0;
       opt.allow_ocsp = 0;
+      opt.allow_version_check = 0;
       opt.ocsp_responder = NULL;
       opt.ocsp_max_clock_skew = 10 * 60;      /* 10 minutes.  */
       opt.ocsp_max_period = 90 * 86400;       /* 90 days.  */
@@ -522,9 +620,16 @@ parse_rereadable_options (ARGPARSE_ARGS *pargs, int reread)
         }
       FREE_STRLIST (opt.ignored_cert_extensions);
       http_register_tls_ca (NULL);
-      xfree (opt.keyserver);
-      opt.keyserver = NULL;
-      /* Note: We do not allow resetting of opt.use_tor at runtime.  */
+      FREE_STRLIST (hkp_cacert_filenames);
+      FREE_STRLIST (opt.keyserver);
+      /* Note: We do not allow resetting of TOR_MODE_FORCE at runtime.  */
+      if (tor_mode != TOR_MODE_FORCE)
+        tor_mode = TOR_MODE_AUTO;
+      disable_check_own_socket = 0;
+      enable_standard_resolver (0);
+      set_dns_timeout (0);
+      opt.connect_timeout = 0;
+      opt.connect_quick_timeout = 0;
       return 1;
     }
 
@@ -551,6 +656,8 @@ parse_rereadable_options (ARGPARSE_ARGS *pargs, int reread)
         }
       break;
 
+    case oDisableCheckOwnSocket: disable_check_own_socket = 1; break;
+
     case oLDAPWrapperProgram:
       opt.ldap_wrapper_program = pargs->r.ret_str;
       break;
@@ -560,6 +667,8 @@ parse_rereadable_options (ARGPARSE_ARGS *pargs, int reread)
 
     case oDisableHTTP: opt.disable_http = 1; break;
     case oDisableLDAP: opt.disable_ldap = 1; break;
+    case oDisableIPv4: opt.disable_ipv4 = 1; break;
+    case oDisableIPv6: opt.disable_ipv6 = 1; break;
     case oHonorHTTPProxy: opt.honor_http_proxy = 1; break;
     case oHTTPProxy: opt.http_proxy = pargs->r.ret_str; break;
     case oLDAPProxy: opt.ldap_proxy = pargs->r.ret_str; break;
@@ -569,6 +678,7 @@ parse_rereadable_options (ARGPARSE_ARGS *pargs, int reread)
     case oIgnoreOCSPSvcUrl: opt.ignore_ocsp_service_url = 1; break;
 
     case oAllowOCSP: opt.allow_ocsp = 1; break;
+    case oAllowVersionCheck: opt.allow_version_check = 1; break;
     case oOCSPResponder: opt.ocsp_responder = pargs->r.ret_str; break;
     case oOCSPSigner:
       opt.ocsp_signer = parse_ocsp_signer (pargs->r.ret_str);
@@ -581,17 +691,14 @@ parse_rereadable_options (ARGPARSE_ARGS *pargs, int reread)
 
     case oHkpCaCert:
       {
+        /* We need to register the filenames with gnutls (http.c) and
+         * also for our own cert cache.  */
         char *tmpname;
 
-        /* Do tilde expansion and print a warning if the file can't be
-           accessed.  */
-        tmpname = make_absfilename_try (pargs->r.ret_str, NULL);
-        if (!tmpname || access (tmpname, F_OK))
-          log_info (_("can't access '%s': %s\n"),
-                    tmpname? tmpname : pargs->r.ret_str,
-                    gpg_strerror (gpg_error_from_syserror()));
-        else
-          http_register_tls_ca (tmpname);
+        /* Do tilde expansion and make path absolute.  */
+        tmpname = make_absfilename (pargs->r.ret_str, NULL);
+        http_register_tls_ca (tmpname);
+        add_to_strlist (&hkp_cacert_filenames, pargs->r.ret_str);
         xfree (tmpname);
       }
       break;
@@ -600,56 +707,65 @@ parse_rereadable_options (ARGPARSE_ARGS *pargs, int reread)
       add_to_strlist (&opt.ignored_cert_extensions, pargs->r.ret_str);
       break;
 
-    case oUseTor: opt.use_tor = 1; break;
+    case oUseTor:
+      tor_mode = TOR_MODE_FORCE;
+      break;
+    case oNoUseTor:
+      if (tor_mode != TOR_MODE_FORCE)
+        tor_mode = TOR_MODE_NEVER;
+      break;
+
+    case oStandardResolver: enable_standard_resolver (1); break;
+    case oRecursiveResolver: enable_recursive_resolver (1); break;
 
     case oKeyServer:
-      xfree (opt.keyserver);
-      opt.keyserver = *pargs->r.ret_str? xtrystrdup (pargs->r.ret_str) : NULL;
+      if (*pargs->r.ret_str)
+        add_to_strlist (&opt.keyserver, pargs->r.ret_str);
+      break;
+
+    case oNameServer:
+      set_dns_nameserver (pargs->r.ret_str);
+      break;
+
+    case oResolverTimeout:
+      set_dns_timeout (pargs->r.ret_int);
+      break;
+
+    case oConnectTimeout:
+      opt.connect_timeout = pargs->r.ret_ulong * 1000;
+      break;
+
+    case oConnectQuickTimeout:
+      opt.connect_quick_timeout = pargs->r.ret_ulong * 1000;
       break;
 
     default:
       return 0; /* Not handled. */
     }
 
+  set_dns_verbose (opt.verbose, !!DBG_DNS);
+  http_set_verbose (opt.verbose, !!DBG_NETWORK);
+  set_dns_disable_ipv4 (opt.disable_ipv4);
+  set_dns_disable_ipv6 (opt.disable_ipv6);
+
   return 1; /* Handled. */
 }
 
 
-#ifdef USE_W32_SERVICE
-/* The global status of our service.  */
-SERVICE_STATUS_HANDLE service_handle;
-SERVICE_STATUS service_status;
-
-DWORD WINAPI
-w32_service_control (DWORD control, DWORD event_type, LPVOID event_data,
-                    LPVOID context)
+/* This function is called after option parsing to adjust some values
+ * and call option setup functions.  */
+static void
+post_option_parsing (void)
 {
-  (void)event_type;
-  (void)event_data;
-  (void)context;
+  /* It would be too surpirsing if the quick timeout is larger than
+   * the standard value.  */
+  if (opt.connect_quick_timeout > opt.connect_timeout)
+    opt.connect_quick_timeout = opt.connect_timeout;
 
-  /* event_type and event_data are not used here.  */
-  switch (control)
-    {
-    case SERVICE_CONTROL_SHUTDOWN:
-      /* For shutdown we will try to force termination.  */
-      service_status.dwCurrentState = SERVICE_STOP_PENDING;
-      SetServiceStatus (service_handle, &service_status);
-      shutdown_pending = 3;
-      break;
-
-    case SERVICE_CONTROL_STOP:
-      service_status.dwCurrentState = SERVICE_STOP_PENDING;
-      SetServiceStatus (service_handle, &service_status);
-      shutdown_pending = 1;
-      break;
-
-    default:
-      break;
-    }
-  return 0;
+  set_debug ();
+  set_tor_mode ();
 }
-#endif /*USE_W32_SERVICE*/
+
 
 #ifndef HAVE_W32_SYSTEM
 static int
@@ -664,16 +780,45 @@ pid_suffix_callback (unsigned long *r_suffix)
 }
 #endif /*!HAVE_W32_SYSTEM*/
 
+#if HTTP_USE_NTBTLS
+static void
+my_ntbtls_log_handler (void *opaque, int level, const char *fmt, va_list argv)
+{
+  (void)opaque;
 
-#ifdef USE_W32_SERVICE
-# define main real_main
+  if (level == -1)
+    log_logv_prefix (GPGRT_LOGLVL_INFO, "ntbtls: ", fmt, argv);
+  else
+    {
+      char prefix[10+20];
+      snprintf (prefix, sizeof prefix, "ntbtls(%d): ", level);
+      log_logv_prefix (GPGRT_LOGLVL_DEBUG, prefix, fmt, argv);
+    }
+}
 #endif
+
+
+static void
+thread_init (void)
+{
+  npth_init ();
+  assuan_set_system_hooks (ASSUAN_SYSTEM_NPTH);
+  gpgrt_set_syscall_clamp (npth_unprotect, npth_protect);
+
+  /* Now with NPth running we can set the logging callback.  Our
+     windows implementation does not yet feature the NPth TLS
+     functions.  */
+#ifndef HAVE_W32_SYSTEM
+  if (npth_key_create (&my_tlskey_current_fd, NULL) == 0)
+    if (npth_setspecific (my_tlskey_current_fd, NULL) == 0)
+      log_set_pid_suffix_cb (pid_suffix_callback);
+#endif /*!HAVE_W32_SYSTEM*/
+}
+
+
 int
 main (int argc, char **argv)
 {
-#ifdef USE_W32_SERVICE
-# undef main
-#endif
   enum cmd_and_opt_values cmd = 0;
   ARGPARSE_ARGS pargs;
   int orig_argc;
@@ -694,51 +839,20 @@ main (int argc, char **argv)
 #endif /*USE_LDAP*/
   int debug_wait = 0;
   int rc;
-  int homedir_seen = 0;
   struct assuan_malloc_hooks malloc_hooks;
 
   early_system_init ();
-
-#ifdef USE_W32_SERVICE
-  /* The option will be set by main() below if we should run as a
-     system daemon.  */
-  if (opt.system_service)
-    {
-      service_handle
-       = RegisterServiceCtrlHandlerEx ("DirMngr",
-                                       &w32_service_control, NULL /*FIXME*/);
-      if (service_handle == 0)
-       log_error ("failed to register service control handler: ec=%d",
-                  (int) GetLastError ());
-      service_status.dwServiceType = SERVICE_WIN32_OWN_PROCESS;
-      service_status.dwCurrentState = SERVICE_START_PENDING;
-      service_status.dwControlsAccepted
-       = SERVICE_ACCEPT_STOP | SERVICE_ACCEPT_SHUTDOWN;
-      service_status.dwWin32ExitCode = NO_ERROR;
-      service_status.dwServiceSpecificExitCode = NO_ERROR;
-      service_status.dwCheckPoint = 0;
-      service_status.dwWaitHint = 10000; /* 10 seconds timeout.  */
-      SetServiceStatus (service_handle, &service_status);
-    }
-#endif /*USE_W32_SERVICE*/
-
   set_strusage (my_strusage);
-  log_set_prefix (DIRMNGR_NAME, 1|4);
+  log_set_prefix (DIRMNGR_NAME, GPGRT_LOG_WITH_PREFIX | GPGRT_LOG_WITH_PID);
 
   /* Make sure that our subsystems are ready.  */
   i18n_init ();
   init_common_subsystems (&argc, &argv);
 
-  npth_init ();
-
   gcry_control (GCRYCTL_DISABLE_SECMEM, 0);
 
  /* Check that the libraries are suitable.  Do it here because
     the option parsing may need services of the libraries. */
-
-  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) );
   if (!ksba_check_version (NEED_KSBA_VERSION) )
     log_fatal( _("%s is too old (need %s, have %s)\n"), "libksba",
                NEED_KSBA_VERSION, ksba_check_version (NULL) );
@@ -764,31 +878,27 @@ main (int argc, char **argv)
   assuan_set_malloc_hooks (&malloc_hooks);
   assuan_set_assuan_log_prefix (log_get_prefix (NULL));
   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, dirmngr_assuan_log_monitor);
 
   setup_libgcrypt_logging ();
 
+#if HTTP_USE_NTBTLS
+  ntbtls_set_log_handler (my_ntbtls_log_handler, NULL);
+#endif
+
   /* Setup defaults. */
   shell = getenv ("SHELL");
   if (shell && strlen (shell) >= 3 && !strcmp (shell+strlen (shell)-3, "csh") )
     csh_style = 1;
 
-  opt.homedir = default_homedir ();
-
-  /* Now with NPth running we can set the logging callback.  Our
-     windows implementation does not yet feature the NPth TLS
-     functions.  */
-#ifndef HAVE_W32_SYSTEM
-  if (npth_key_create (&my_tlskey_current_fd, NULL) == 0)
-    if (npth_setspecific (my_tlskey_current_fd, NULL) == 0)
-      log_set_pid_suffix_cb (pid_suffix_callback);
-#endif /*!HAVE_W32_SYSTEM*/
-
   /* Reset rereadable options to default values. */
   parse_rereadable_options (NULL, 0);
 
+  /* Default TCP timeouts.  */
+  opt.connect_timeout = DEFAULT_CONNECT_TIMEOUT;
+  opt.connect_quick_timeout = DEFAULT_CONNECT_QUICK_TIMEOUT;
+
   /* LDAP defaults.  */
   opt.add_new_ldapservers = 0;
   opt.ldaptimeout = DEFAULT_LDAP_TIMEOUT;
@@ -815,47 +925,13 @@ main (int argc, char **argv)
         default_config = 0; /* --no-options */
       else if (pargs.r_opt == oHomedir)
         {
-          opt.homedir = pargs.r.ret_str;
-          homedir_seen = 1;
+          gnupg_set_homedir (pargs.r.ret_str);
         }
-      else if (pargs.r_opt == aDaemon)
-        opt.system_daemon = 1;
-      else if (pargs.r_opt == aService)
-        {
-         /* Redundant.  The main function takes care of it.  */
-         opt.system_service = 1;
-         opt.system_daemon = 1;
-       }
-#ifdef HAVE_W32_SYSTEM
-      else if (pargs.r_opt == aGPGConfList || pargs.r_opt == aGPGConfTest)
-       /* We set this so we switch to the system configuration
-          directory below.  This is a crutch to solve the problem
-          that the user configuration is never used on Windows.  Also
-          see below at aGPGConfList.  */
-        opt.system_daemon = 1;
-#endif
-    }
-
-  /* If --daemon has been given on the command line but not --homedir,
-     we switch to /etc/gnupg as default home directory.  Note, that
-     this also overrides the GNUPGHOME environment variable.  */
-  if (opt.system_daemon && !homedir_seen)
-    {
-#ifdef HAVE_W32CE_SYSTEM
-      opt.homedir = DIRSEP_S "gnupg";
-#else
-      opt.homedir = gnupg_sysconfdir ();
-#endif
-      opt.homedir_cache = gnupg_cachedir ();
-      socket_name = dirmngr_sys_socket_name ();
     }
-  else if (dirmngr_user_socket_name ())
-    socket_name = dirmngr_user_socket_name ();
-  else
-    socket_name = dirmngr_sys_socket_name ();
 
+  socket_name = dirmngr_socket_name ();
   if (default_config)
-    configname = make_filename (opt.homedir, DIRMNGR_NAME".conf", NULL );
+    configname = make_filename (gnupg_homedir (), DIRMNGR_NAME".conf", NULL );
 
   argc = orig_argc;
   argv = orig_argv;
@@ -897,7 +973,7 @@ main (int argc, char **argv)
         {
         case aServer:
         case aDaemon:
-        case aService:
+        case aSupervised:
         case aShutdown:
         case aFlush:
        case aListCRLs:
@@ -949,6 +1025,10 @@ main (int argc, char **argv)
 
         case oSocketName: socket_name = pargs.r.ret_str; break;
 
+        case oListenBacklog:
+          listen_backlog = pargs.r.ret_int;
+          break;
+
         default : pargs.err = configfp? 1:2; break;
        }
     }
@@ -969,7 +1049,7 @@ main (int argc, char **argv)
     greeting = 0;
 
   if (!opt.homedir_cache)
-    opt.homedir_cache = opt.homedir;
+    opt.homedir_cache = xstrdup (gnupg_homedir ());
 
   if (greeting)
     {
@@ -982,14 +1062,6 @@ main (int argc, char **argv)
   log_info ("NOTE: this is a development version!\n");
 #endif
 
-  if (opt.use_tor)
-    {
-      log_info ("WARNING: ***************************************\n");
-      log_info ("WARNING: TOR mode (--use-tor) DOES NOT YET WORK!\n");
-      log_info ("WARNING: ***************************************\n");
-    }
-
-
   /* Print a warning if an argument looks like an option.  */
   if (!opt.quiet && !(pargs.flags & ARGPARSE_FLAG_STOP_SEEN))
     {
@@ -1000,7 +1072,8 @@ main (int argc, char **argv)
           log_info (_("Note: '%s' is not considered an option\n"), argv[i]);
     }
 
-  if (!access ("/etc/"DIRMNGR_NAME, F_OK) && !strncmp (opt.homedir, "/etc/", 5))
+  if (!access ("/etc/"DIRMNGR_NAME, F_OK)
+      && !strncmp (gnupg_homedir (), "/etc/", 5))
     log_info
       ("NOTE: DirMngr is now a proper part of %s.  The configuration and"
        " other directory names changed.  Please check that no other version"
@@ -1017,15 +1090,14 @@ main (int argc, char **argv)
       log_printf ("\n");
     }
 
-  set_debug ();
+  post_option_parsing ();
 
   /* Get LDAP server list from file. */
 #if USE_LDAP
   if (!ldapfile)
     {
-      ldapfile = make_filename (opt.homedir,
-                                opt.system_daemon?
-                                "ldapservers.conf":"dirmngr_ldapservers.conf",
+      ldapfile = make_filename (gnupg_homedir (),
+                                "dirmngr_ldapservers.conf",
                                 NULL);
       opt.ldapservers = parse_ldapserver_file (ldapfile);
       xfree (ldapfile);
@@ -1043,9 +1115,7 @@ main (int argc, char **argv)
 #endif
 
   /* Ready.  Now to our duties. */
-  if (!cmd && opt.system_service)
-    cmd = aDaemon;
-  else if (!cmd)
+  if (!cmd)
     cmd = aServer;
   rc = 0;
 
@@ -1058,7 +1128,7 @@ main (int argc, char **argv)
       if (logfile)
         {
           log_set_file (logfile);
-          log_set_prefix (NULL, 2|4);
+          log_set_prefix (NULL, GPGRT_LOG_WITH_TIME | GPGRT_LOG_WITH_PID);
         }
 
       if (debug_wait)
@@ -1069,15 +1139,52 @@ main (int argc, char **argv)
           log_debug ("... okay\n");
         }
 
-#if USE_LDAP
-      ldap_wrapper_launch_thread ();
-#endif /*USE_LDAP*/
 
-      cert_cache_init ();
+      thread_init ();
+      cert_cache_init (hkp_cacert_filenames);
       crl_cache_init ();
-      start_command_handler (ASSUAN_INVALID_FD);
+      ks_hkp_init ();
+      http_register_netactivity_cb (netactivity_action);
+      start_command_handler (ASSUAN_INVALID_FD, 0);
       shutdown_reaper ();
     }
+#ifndef HAVE_W32_SYSTEM
+  else if (cmd == aSupervised)
+    {
+      /* In supervised mode, we expect file descriptor 3 to be an
+         already opened, listening socket.
+
+         We will also not detach from the controlling process or close
+         stderr; the supervisor should handle all of that.  */
+      struct stat statbuf;
+      if (fstat (3, &statbuf) == -1 && errno == EBADF)
+        {
+          log_error ("file descriptor 3 must be validin --supervised mode\n");
+          dirmngr_exit (1);
+        }
+      socket_name = gnupg_get_socket_name (3);
+
+      /* Now start with logging to a file if this is desired. */
+      if (logfile)
+        {
+          log_set_file (logfile);
+          log_set_prefix (NULL, (GPGRT_LOG_WITH_PREFIX
+                                 |GPGRT_LOG_WITH_TIME
+                                 |GPGRT_LOG_WITH_PID));
+          current_logfile = xstrdup (logfile);
+        }
+      else
+        log_set_prefix (NULL, 0);
+
+      thread_init ();
+      cert_cache_init (hkp_cacert_filenames);
+      crl_cache_init ();
+      ks_hkp_init ();
+      http_register_netactivity_cb (netactivity_action);
+      handle_connections (3);
+      shutdown_reaper ();
+    }
+#endif /*HAVE_W32_SYSTEM*/
   else if (cmd == aDaemon)
     {
       assuan_fd_t fd;
@@ -1098,6 +1205,14 @@ main (int argc, char **argv)
           current_logfile = xstrdup (logfile);
         }
 
+      if (debug_wait)
+        {
+          log_debug ("waiting for debugger - my pid is %u .....\n",
+                     (unsigned int)getpid());
+          gnupg_sleep (debug_wait);
+          log_debug ("... okay\n");
+        }
+
 #ifndef HAVE_W32_SYSTEM
       if (strchr (socket_name, ':'))
         {
@@ -1113,7 +1228,6 @@ main (int argc, char **argv)
           dirmngr_exit (1);
         }
 
-#if ASSUAN_VERSION_NUMBER >= 0x020104 /* >= 2.1.4 */
       {
         int redirected;
 
@@ -1137,16 +1251,6 @@ main (int argc, char **argv)
                         socket_name, redir_socket_name);
           }
       }
-#else /* Assuan < 2.1.4 */
-      memset (&serv_addr, 0, sizeof serv_addr);
-      serv_addr.sun_family = AF_UNIX;
-      if (strlen (socket_name)+1 >= sizeof serv_addr.sun_path )
-        {
-          log_error (_("socket name '%s' is too long\n"), socket_name);
-          dirmngr_exit (1);
-        }
-      strcpy (serv_addr.sun_path, socket_name);
-#endif /* Assuan < 2.1.4 */
 
       len = SUN_LEN (&serv_addr);
 
@@ -1175,9 +1279,14 @@ main (int argc, char **argv)
         }
       cleanup_socket = 1;
 
-      if (listen (FD2INT (fd), 5) == -1)
+      if (gnupg_chmod (serv_addr.sun_path, "-rwx"))
+        log_error (_("can't set permissions of '%s': %s\n"),
+                   serv_addr.sun_path, strerror (errno));
+
+      if (listen (FD2INT (fd), listen_backlog) == -1)
         {
-          log_error (_("listen() failed: %s\n"), strerror (errno));
+          log_error ("listen(fd,%d) failed: %s\n",
+                     listen_backlog, strerror (errno));
           assuan_sock_close (fd);
           dirmngr_exit (1);
         }
@@ -1254,8 +1363,18 @@ main (int argc, char **argv)
           for (i=0; i <= 2; i++)
             {
               if (!log_test_fd (i) && i != fd )
-                close (i);
+                {
+                  if ( !close (i)
+                       && open ("/dev/null", i? O_WRONLY : O_RDONLY) == -1)
+                    {
+                      log_error ("failed to open '%s': %s\n",
+                                 "/dev/null", strerror (errno));
+                      cleanup ();
+                      dirmngr_exit (1);
+                    }
+                }
             }
+
           if (setsid() == -1)
             {
               log_error ("setsid() failed: %s\n", strerror(errno) );
@@ -1266,46 +1385,32 @@ main (int argc, char **argv)
           log_set_prefix (NULL, oldflags | GPGRT_LOG_RUN_DETACHED);
           opt.running_detached = 1;
 
-          if (chdir("/"))
+        }
+#endif
+
+      if (!nodetach )
+        {
+          if (gnupg_chdir (gnupg_daemon_rootdir ()))
             {
-              log_error ("chdir to / failed: %s\n", strerror (errno));
+              log_error ("chdir to '%s' failed: %s\n",
+                         gnupg_daemon_rootdir (), strerror (errno));
               dirmngr_exit (1);
             }
         }
-#endif
 
-#if USE_LDAP
-      ldap_wrapper_launch_thread ();
-#endif /*USE_LDAP*/
-
-      cert_cache_init ();
+      thread_init ();
+      cert_cache_init (hkp_cacert_filenames);
       crl_cache_init ();
-#ifdef USE_W32_SERVICE
-      if (opt.system_service)
-       {
-         service_status.dwCurrentState = SERVICE_RUNNING;
-         SetServiceStatus (service_handle, &service_status);
-       }
-#endif
+      ks_hkp_init ();
+      http_register_netactivity_cb (netactivity_action);
       handle_connections (fd);
-      assuan_sock_close (fd);
       shutdown_reaper ();
-#ifdef USE_W32_SERVICE
-      if (opt.system_service)
-       {
-         service_status.dwCurrentState = SERVICE_STOPPED;
-         SetServiceStatus (service_handle, &service_status);
-       }
-#endif
     }
   else if (cmd == aListCRLs)
     {
       /* Just list the CRL cache and exit. */
       if (argc)
         wrong_args ("--list-crls");
-#if USE_LDAP
-      ldap_wrapper_launch_thread ();
-#endif /*USE_LDAP*/
       crl_cache_init ();
       crl_cache_list (es_stdout);
     }
@@ -1316,11 +1421,10 @@ main (int argc, char **argv)
       memset (&ctrlbuf, 0, sizeof ctrlbuf);
       dirmngr_init_default_ctrl (&ctrlbuf);
 
-#if USE_LDAP
-      ldap_wrapper_launch_thread ();
-#endif /*USE_LDAP*/
-      cert_cache_init ();
+      thread_init ();
+      cert_cache_init (hkp_cacert_filenames);
       crl_cache_init ();
+      ks_hkp_init ();
       if (!argc)
         rc = crl_cache_load (&ctrlbuf, NULL);
       else
@@ -1341,11 +1445,10 @@ main (int argc, char **argv)
       memset (&ctrlbuf, 0, sizeof ctrlbuf);
       dirmngr_init_default_ctrl (&ctrlbuf);
 
-#if USE_LDAP
-      ldap_wrapper_launch_thread ();
-#endif /*USE_LDAP*/
-      cert_cache_init ();
+      thread_init ();
+      cert_cache_init (hkp_cacert_filenames);
       crl_cache_init ();
+      ks_hkp_init ();
       rc = crl_fetch (&ctrlbuf, argv[0], &reader);
       if (rc)
         log_error (_("fetching CRL from '%s' failed: %s\n"),
@@ -1375,19 +1478,10 @@ main (int argc, char **argv)
       char *filename;
       char *filename_esc;
 
-#ifdef HAVE_W32_SYSTEM
-      /* On Windows systems, dirmngr always runs as system daemon, and
-        the per-user configuration is never used.  So we short-cut
-        everything to use the global system configuration of dirmngr
-        above, and here we set the no change flag to make these
-        read-only.  */
-      flags |= GC_OPT_FLAG_NO_CHANGE;
-#endif
-
       /* First the configuration file.  This is not an option, but it
         is vital information for GPG Conf.  */
       if (!opt.config_filename)
-        opt.config_filename = make_filename (opt.homedir,
+        opt.config_filename = make_filename (gnupg_homedir (),
                                              "dirmngr.conf", NULL );
 
       filename = percent_escape (opt.config_filename, NULL);
@@ -1407,9 +1501,8 @@ main (int argc, char **argv)
          and having both of them is thus problematic.  --no-detach is
          also only usable on the command line.  --batch is unused.  */
 
-      filename = make_filename (opt.homedir,
-                                opt.system_daemon?
-                                "ldapservers.conf":"dirmngr_ldapservers.conf",
+      filename = make_filename (gnupg_homedir (),
+                                "dirmngr_ldapservers.conf",
                                 NULL);
       filename_esc = percent_escape (filename, NULL);
       es_printf ("ldapserverlist-file:%lu:\"%s\n", flags | GC_OPT_FLAG_DEFAULT,
@@ -1422,6 +1515,7 @@ main (int argc, char **argv)
       es_printf ("max-replies:%lu:%u\n",
               flags | GC_OPT_FLAG_DEFAULT, DEFAULT_MAX_REPLIES);
       es_printf ("allow-ocsp:%lu:\n", flags | GC_OPT_FLAG_NONE);
+      es_printf ("allow-version-check:%lu:\n", flags | GC_OPT_FLAG_NONE);
       es_printf ("ocsp-responder:%lu:\n", flags | GC_OPT_FLAG_NONE);
       es_printf ("ocsp-signer:%lu:\n", flags | GC_OPT_FLAG_NONE);
 
@@ -1442,50 +1536,20 @@ main (int argc, char **argv)
       es_printf ("ignore-ocsp-servic-url:%lu:\n", flags | GC_OPT_FLAG_NONE);
 
       es_printf ("use-tor:%lu:\n", flags | GC_OPT_FLAG_NONE);
-      es_printf ("keyserver:%lu:\n", flags | GC_OPT_FLAG_NONE);
-    }
-  cleanup ();
-  return !!rc;
-}
 
+      filename_esc = percent_escape (get_default_keyserver (0), NULL);
+      es_printf ("keyserver:%lu:\"%s:\n", flags | GC_OPT_FLAG_DEFAULT,
+                 filename_esc);
+      xfree (filename_esc);
 
-#ifdef USE_W32_SERVICE
-static void WINAPI
-call_real_main (DWORD argc, LPSTR *argv)
-{
-  real_main (argc, argv);
-}
-
-int
-main (int argc, char *argv[])
-{
-  int i;
-
-  /* Find out if we run in daemon mode or on the command line.  */
-  for (i = 1; i < argc; i++)
-    if (!strcmp (argv[i], "--service"))
-      {
-       opt.system_service = 1;
-       opt.system_daemon = 1;
-       break;
-      }
 
-  if (!opt.system_service)
-    return real_main (argc, argv);
-  else
-    {
-      SERVICE_TABLE_ENTRY DispatchTable [] =
-       {
-         { "DirMngr", &call_real_main },
-         { NULL, NULL }
-       };
-
-      if (!StartServiceCtrlDispatcher (DispatchTable))
-        return 1;
-      return 0;
+      es_printf ("nameserver:%lu:\n", flags | GC_OPT_FLAG_NONE);
+      es_printf ("resolver-timeout:%lu:%u\n",
+                 flags | GC_OPT_FLAG_DEFAULT, 0);
     }
+  cleanup ();
+  return !!rc;
 }
-#endif /*USE_W32_SERVICE*/
 
 
 static void
@@ -1493,6 +1557,7 @@ cleanup (void)
 {
   crl_cache_deinit ();
   cert_cache_deinit (1);
+  reload_dns_stuff (1);
 
 #if USE_LDAP
   ldapserver_list_free (opt.ldapservers);
@@ -1521,8 +1586,11 @@ dirmngr_exit (int rc)
 void
 dirmngr_init_default_ctrl (ctrl_t ctrl)
 {
+  ctrl->magic = SERVER_CONTROL_MAGIC;
   if (opt.http_proxy)
     ctrl->http_proxy = xstrdup (opt.http_proxy);
+  ctrl->http_no_crl = 1;
+  ctrl->timeout = opt.connect_timeout;
 }
 
 
@@ -1531,6 +1599,8 @@ dirmngr_deinit_default_ctrl (ctrl_t ctrl)
 {
   if (!ctrl)
     return;
+  ctrl->magic = 0xdeadbeef;
+
   xfree (ctrl->http_proxy);
   ctrl->http_proxy = NULL;
 }
@@ -1649,7 +1719,7 @@ parse_ocsp_signer (const char *string)
     {
       if (string[0] == '.' && string[1] == '/' )
         string += 2;
-      fname = make_filename (opt.homedir, string, NULL);
+      fname = make_filename (gnupg_homedir (), string, NULL);
     }
 
   fp = es_fopen (fname, "r");
@@ -1782,7 +1852,7 @@ reread_configuration (void)
     }
   fclose (fp);
 
-  set_debug ();
+  post_option_parsing ();
 }
 
 
@@ -1796,11 +1866,23 @@ dirmngr_sighup_action (void)
   reread_configuration ();
   cert_cache_deinit (0);
   crl_cache_deinit ();
-  cert_cache_init ();
+  cert_cache_init (hkp_cacert_filenames);
   crl_cache_init ();
+  reload_dns_stuff (0);
+  ks_hkp_reload ();
 }
 
 
+/* This function is called if some network activity was done.  At this
+ * point we know the we have a network and we can decide whether to
+ * run scheduled background tasks soon.  The function should return
+ * quickly and only trigger actions for another thread. */
+static void
+netactivity_action (void)
+{
+  network_activity_seen = 1;
+}
+
 
 /* The signal handler. */
 #ifndef HAVE_W32_SYSTEM
@@ -1815,6 +1897,7 @@ handle_signal (int signo)
 
     case SIGUSR1:
       cert_cache_print_stats ();
+      domaininfo_print_stats ();
       break;
 
     case SIGUSR2:
@@ -1857,6 +1940,7 @@ housekeeping_thread (void *arg)
 {
   static int sentinel;
   time_t curtime;
+  struct server_control_s ctrlbuf;
 
   (void)arg;
 
@@ -1867,12 +1951,26 @@ housekeeping_thread (void *arg)
       return NULL;
     }
   sentinel++;
-  if (opt.verbose)
+  if (opt.verbose > 1)
     log_info ("starting housekeeping\n");
 
+  memset (&ctrlbuf, 0, sizeof ctrlbuf);
+  dirmngr_init_default_ctrl (&ctrlbuf);
+
   ks_hkp_housekeeping (curtime);
+  if (network_activity_seen)
+    {
+      network_activity_seen = 0;
+      if (opt.allow_version_check)
+        dirmngr_load_swdb (&ctrlbuf, 0);
+      workqueue_run_global_tasks (&ctrlbuf, 1);
+    }
+  else
+    workqueue_run_global_tasks (&ctrlbuf, 0);
 
-  if (opt.verbose)
+  dirmngr_deinit_default_ctrl (&ctrlbuf);
+
+  if (opt.verbose > 1)
     log_info ("ready with housekeeping\n");
   sentinel--;
   return NULL;
@@ -1910,19 +2008,7 @@ time_for_housekeeping_p (time_t curtime)
 static void
 handle_tick (void)
 {
-  /* Under Windows we don't use signals and need a way for the loop to
-     check for the shutdown flag.  */
-#ifdef HAVE_W32_SYSTEM
-  if (shutdown_pending)
-    log_info (_("SIGTERM received - shutting down ...\n"));
-  if (shutdown_pending > 2)
-    {
-      log_info (_("shutdown forced\n"));
-      log_info ("%s %s stopped\n", strusage(11), strusage(13) );
-      cleanup ();
-      dirmngr_exit (0);
-    }
-#endif /*HAVE_W32_SYSTEM*/
+  struct stat statbuf;
 
   if (time_for_housekeeping_p (gnupg_get_time ()))
     {
@@ -1943,6 +2029,14 @@ handle_tick (void)
           npth_attr_destroy (&tattr);
         }
     }
+
+  /* Check whether the homedir is still available.  */
+  if (!shutdown_pending
+      && stat (gnupg_homedir (), &statbuf) && errno == ENOENT)
+    {
+      shutdown_pending = 1;
+      log_info ("homedir has been removed - shutting down\n");
+    }
 }
 
 
@@ -1963,10 +2057,12 @@ check_nonce (assuan_fd_t fd, assuan_sock_nonce_t *nonce)
 }
 
 
-/* Helper to call a connection's main fucntion. */
+/* Helper to call a connection's main function. */
 static void *
 start_connection_thread (void *arg)
 {
+  static unsigned int last_session_id;
+  unsigned int session_id;
   union int_and_ptr_u argval;
   gnupg_fd_t fd;
 
@@ -1988,12 +2084,17 @@ start_connection_thread (void *arg)
   if (opt.verbose)
     log_info (_("handler for fd %d started\n"), FD2INT (fd));
 
-  start_command_handler (fd);
+  session_id = ++last_session_id;
+  if (!session_id)
+    session_id = ++last_session_id;
+  start_command_handler (fd, session_id);
 
   if (opt.verbose)
     log_info (_("handler for fd %d terminated\n"), FD2INT (fd));
   active_connections--;
 
+  workqueue_run_post_session_tasks (session_id);
+
 #ifndef HAVE_W32_SYSTEM
   argval.afd = ASSUAN_INVALID_FD;
   npth_setspecific (my_tlskey_current_fd, argval.aptr);
@@ -2003,7 +2104,37 @@ start_connection_thread (void *arg)
 }
 
 
-/* Main loop in daemon mode. */
+#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;
+  const char *s;
+
+  s = strrchr (name, '/');
+  if (s && s[1])
+    name = s + 1;
+
+  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*/
+
+
+/* Main loop in daemon mode.  Note that LISTEN_FD will be owned by
+ * this function. */
 static void
 handle_connections (assuan_fd_t listen_fd)
 {
@@ -2013,13 +2144,13 @@ handle_connections (assuan_fd_t listen_fd)
 #endif
   struct sockaddr_un paddr;
   socklen_t plen = sizeof( paddr );
-  gnupg_fd_t fd;
   int nfd, ret;
   fd_set fdset, read_fdset;
   struct timespec abstime;
   struct timespec curtime;
   struct timespec timeout;
   int saved_errno;
+  int my_inotify_fd = -1;
 
   npth_attr_init (&tattr);
   npth_attr_setdetachstate (&tattr, NPTH_CREATE_DETACHED);
@@ -2034,12 +2165,41 @@ handle_connections (assuan_fd_t listen_fd)
   npth_sigev_fini ();
 #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*/
+
+
   /* Setup the fdset.  It has only one member.  This is because we use
      pth_select instead of pth_accept to properly sync timeouts with
      to full second.  */
   FD_ZERO (&fdset);
   FD_SET (FD2INT (listen_fd), &fdset);
   nfd = FD2INT (listen_fd);
+  if (my_inotify_fd != -1)
+    {
+      FD_SET (my_inotify_fd, &fdset);
+      if (my_inotify_fd > nfd)
+        nfd = my_inotify_fd;
+    }
 
   npth_clock_gettime (&abstime);
   abstime.tv_sec += TIMERTICK_INTERVAL;
@@ -2054,8 +2214,21 @@ handle_connections (assuan_fd_t listen_fd)
             break; /* ready */
 
           /* Do not accept new connections but keep on running the
-             loop to cope with the timer events.  */
+           * loop to cope with the timer events.
+           *
+           * Note that we do not close the listening socket because a
+           * client trying to connect to that socket would instead
+           * restart a new dirmngr instance - which is unlikely the
+           * intention of a shutdown. */
+          /* assuan_sock_close (listen_fd); */
+          /* listen_fd = -1; */
           FD_ZERO (&fdset);
+          nfd = -1;
+          if (my_inotify_fd != -1)
+            {
+              FD_SET (my_inotify_fd, &fdset);
+              nfd = my_inotify_fd;
+            }
        }
 
       /* Take a copy of the fdset.  */
@@ -2064,15 +2237,19 @@ handle_connections (assuan_fd_t listen_fd)
       npth_clock_gettime (&curtime);
       if (!(npth_timercmp (&curtime, &abstime, <)))
        {
-         /* Timeout.  */
+         /* Timeout.  When a shutdown is pending we use a shorter
+           * interval to handle the shutdown more quickly.  */
          handle_tick ();
          npth_clock_gettime (&abstime);
-         abstime.tv_sec += TIMERTICK_INTERVAL;
+         abstime.tv_sec += (shutdown_pending
+                             ? TIMERTICK_INTERVAL_SHUTDOWN
+                             : TIMERTICK_INTERVAL);
        }
       npth_timersub (&abstime, &curtime, &timeout);
 
 #ifndef HAVE_W32_SYSTEM
-      ret = npth_pselect (nfd+1, &read_fdset, NULL, NULL, &timeout, npth_sigev_sigmask());
+      ret = npth_pselect (nfd+1, &read_fdset, NULL, NULL, &timeout,
+                          npth_sigev_sigmask());
       saved_errno = errno;
 
       while (npth_sigev_get_pending(&signo))
@@ -2091,12 +2268,31 @@ handle_connections (assuan_fd_t listen_fd)
        }
 
       if (ret <= 0)
-       /* Interrupt or timeout.  Will be handled when calculating the
-          next timeout.  */
-       continue;
+        {
+          /* Interrupt or timeout.  Will be handled when calculating the
+             next timeout.  */
+          continue;
+        }
+
+      if (shutdown_pending)
+        {
+          /* Do not anymore accept connections.  */
+          continue;
+        }
 
-      if (!shutdown_pending && FD_ISSET (FD2INT (listen_fd), &read_fdset))
+#ifdef HAVE_INOTIFY_INIT
+      if (my_inotify_fd != -1 && FD_ISSET (my_inotify_fd, &read_fdset)
+          && my_inotify_is_name (my_inotify_fd, socket_name))
+        {
+          shutdown_pending = 1;
+          log_info ("socket file has been removed - shutting down\n");
+        }
+#endif /*HAVE_INOTIFY_INIT*/
+
+      if (FD_ISSET (FD2INT (listen_fd), &read_fdset))
        {
+          gnupg_fd_t fd;
+
           plen = sizeof paddr;
          fd = INT2FD (npth_accept (FD2INT(listen_fd),
                                    (struct sockaddr *)&paddr, &plen));
@@ -2112,9 +2308,8 @@ handle_connections (assuan_fd_t listen_fd)
 
               memset (&argval, 0, sizeof argval);
               argval.afd = fd;
-              snprintf (threadname, sizeof threadname-1,
+              snprintf (threadname, sizeof threadname,
                         "conn fd=%d", FD2INT(fd));
-              threadname[sizeof threadname -1] = 0;
 
               ret = npth_create (&thread, &tattr,
                                  start_connection_thread, argval.aptr);
@@ -2126,11 +2321,25 @@ handle_connections (assuan_fd_t listen_fd)
                 }
              npth_setname_np (thread, threadname);
             }
-          fd = GNUPG_INVALID_FD;
        }
     }
 
+#ifdef HAVE_INOTIFY_INIT
+  if (my_inotify_fd != -1)
+    close (my_inotify_fd);
+#endif /*HAVE_INOTIFY_INIT*/
   npth_attr_destroy (&tattr);
+  if (listen_fd != GNUPG_INVALID_FD)
+    assuan_sock_close (listen_fd);
   cleanup ();
   log_info ("%s %s stopped\n", strusage(11), strusage(13));
 }
+
+const char*
+dirmngr_get_current_socket_name (void)
+{
+  if (socket_name)
+    return socket_name;
+  else
+    return dirmngr_socket_name ();
+}