kbx: Add framework for a public key daemon.
authorWerner Koch <wk@gnupg.org>
Wed, 5 Sep 2018 15:00:17 +0000 (17:00 +0200)
committerWerner Koch <wk@gnupg.org>
Wed, 5 Sep 2018 15:00:17 +0000 (17:00 +0200)
* kbx/keyboxd.c: New.
* kbx/keyboxd.h: New.
* kbx/kbxserver.c: New.
* kbx/keyboxd-w32info.rc: New.
* kbx/Makefile.am (EXTRA_DIST): Add new rc file.
(resource_objs): Ditto.
(libexec_PROGRAMS): New.
(common_libs, commonpth_libs): New.
(kbxutil_LDADD): Use here.
(keyboxd_SOURCES): New.
(keyboxd_CFLAGS): New.
(keyboxd_LDADD): New.
(keyboxd_LDFLAGS): New.
(keyboxd_DEPENDENCIES): new.
($(PROGRAMS)): Extend.

Signed-off-by: Werner Koch <wk@gnupg.org>
configure.ac
kbx/Makefile.am
kbx/kbxserver.c [new file with mode: 0644]
kbx/keyboxd-w32info.rc [new file with mode: 0644]
kbx/keyboxd.c [new file with mode: 0644]
kbx/keyboxd.h [new file with mode: 0644]

index 78a03c4..9f78259 100644 (file)
@@ -56,7 +56,7 @@ AC_DEFINE_UNQUOTED(GNUPG_SWDB_TAG, "gnupg24", [swdb tag for this branch])
 NEED_GPG_ERROR_VERSION=1.29
 
 NEED_LIBGCRYPT_API=1
-NEED_LIBGCRYPT_VERSION=1.7.0
+NEED_LIBGCRYPT_VERSION=1.8.0
 
 NEED_LIBASSUAN_API=2
 NEED_LIBASSUAN_VERSION=2.5.0
@@ -505,6 +505,7 @@ AH_BOTTOM([
 #define GNUPG_DEFAULT_HOMEDIR "~/.gnupg"
 #endif
 #define GNUPG_PRIVATE_KEYS_DIR  "private-keys-v1.d"
+#define GNUPG_PUBLIC_KEYS_DIR   "public-keys.d"
 #define GNUPG_OPENPGP_REVOC_DIR "openpgp-revocs.d"
 
 #define GNUPG_DEF_COPYRIGHT_LINE \
@@ -1882,6 +1883,8 @@ AC_DEFINE_UNQUOTED(DIRMNGR_INFO_NAME, "DIRMNGR_INFO",
                    [The name of the dirmngr info envvar])
 AC_DEFINE_UNQUOTED(SCDAEMON_SOCK_NAME, "S.scdaemon",
                    [The name of the SCdaemon socket])
+AC_DEFINE_UNQUOTED(KEYBOXD_SOCK_NAME, "S.keyboxd",
+                   [The name of the keyboxd socket])
 AC_DEFINE_UNQUOTED(DIRMNGR_SOCK_NAME, "S.dirmngr",
                    [The name of the dirmngr socket])
 AC_DEFINE_UNQUOTED(DIRMNGR_DEFAULT_KEYSERVER,
index 8fca24a..51cabbf 100644 (file)
 
 ## Process this file with automake to produce Makefile.in
 
-EXTRA_DIST = mkerrors
+EXTRA_DIST = mkerrors keyboxd-w32info.rc
 
 AM_CPPFLAGS =
 
 include $(top_srcdir)/am/cmacros.am
+if HAVE_W32_SYSTEM
+resource_objs += keyboxd-w32info.o
+endif
 
 AM_CFLAGS = $(LIBGCRYPT_CFLAGS) $(KSBA_CFLAGS)
 
 noinst_LIBRARIES = libkeybox.a libkeybox509.a
 bin_PROGRAMS = kbxutil
+libexec_PROGRAMS = keyboxd
 
 if HAVE_W32CE_SYSTEM
 extra_libs =  $(LIBASSUAN_LIBS)
@@ -35,6 +39,9 @@ else
 extra_libs =
 endif
 
+common_libs = $(libcommon)
+commonpth_libs = $(libcommonpth)
+
 common_sources = \
        keybox.h keybox-defs.h keybox-search-desc.h \
        keybox-util.c \
@@ -59,9 +66,26 @@ libkeybox509_a_CFLAGS = $(AM_CFLAGS) -DKEYBOX_WITH_X509=1
 # to do it this way.
 kbxutil_SOURCES = kbxutil.c $(common_sources)
 kbxutil_CFLAGS = $(AM_CFLAGS) -DKEYBOX_WITH_X509=1
-kbxutil_LDADD   = ../common/libcommon.a \
+kbxutil_LDADD   = $(common_libs) \
                   $(KSBA_LIBS) $(LIBGCRYPT_LIBS) $(extra_libs) \
                   $(GPG_ERROR_LIBS) $(LIBINTL) $(LIBICONV) $(W32SOCKLIBS) \
                  $(NETLIBS)
 
-$(PROGRAMS) : ../common/libcommon.a
+
+keyboxd_SOURCES = \
+       keyboxd.c keyboxd.h \
+       kbxserver.c
+
+keyboxd_CFLAGS = $(AM_CFLAGS) $(LIBASSUAN_CFLAGS) $(NPTH_CFLAGS) \
+               $(INCICONV)
+keyboxd_LDADD = $(commonpth_libs) \
+                $(LIBGCRYPT_LIBS) $(LIBASSUAN_LIBS) $(NPTH_LIBS) \
+               $(GPG_ERROR_LIBS) $(LIBINTL) $(NETLIBS) $(LIBICONV) \
+               $(resource_objs)
+keyboxd_LDFLAGS = $(extra_bin_ldflags)
+keyboxd_DEPENDENCIES = $(resource_objs)
+
+
+# Make sure that all libs are build before we use them.  This is
+# important for things like make -j2.
+$(PROGRAMS): $(common_libs) $(commonpth_libs)
diff --git a/kbx/kbxserver.c b/kbx/kbxserver.c
new file mode 100644 (file)
index 0000000..59fcb64
--- /dev/null
@@ -0,0 +1,447 @@
+/* kbxserver.c - Handle Assuan commands send to the keyboxd
+ * Copyright (C) 2018 g10 Code GmbH
+ *
+ * This file is part of GnuPG.
+ *
+ * GnuPG is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * GnuPG is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * 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 <https://www.gnu.org/licenses/>.
+ * SPDX-License-Identifier: GPL-3.0+
+ */
+
+#include <config.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <stddef.h>
+#include <string.h>
+#include <assert.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <unistd.h>
+#include <errno.h>
+
+#include "keyboxd.h"
+#include <assuan.h>
+
+#include "../common/i18n.h"
+#include "../common/server-help.h"
+
+
+
+
+#define PARM_ERROR(t) assuan_set_error (ctx, \
+                                        gpg_error (GPG_ERR_ASS_PARAMETER), (t))
+#define set_error(e,t) (ctx ? assuan_set_error (ctx, gpg_error (e), (t)) \
+                        /**/: gpg_error (e))
+
+
+
+/* Control structure per connection. */
+struct server_local_s
+{
+  /* Data used to associate an Assuan context with local server data */
+  assuan_context_t assuan_ctx;
+
+  /* The session id (a counter).  */
+  unsigned int session_id;
+
+  /* If this flag is set to true this process will be terminated after
+   * the end of this session.  */
+  int stopme;
+
+  /* If the first both flags are set the assuan logging of data lines
+   * is suppressed.  The count variable is used to show the number of
+   * non-logged bytes.  */
+  size_t inhibit_data_logging_count;
+  unsigned int inhibit_data_logging : 1;
+  unsigned int inhibit_data_logging_now : 1;
+
+  /* Dummy option.  */
+  int foo;
+};
+
+
+
+\f
+/* Helper to print a message while leaving a command.  */
+static gpg_error_t
+leave_cmd (assuan_context_t ctx, gpg_error_t err)
+{
+  if (err)
+    {
+      const char *name = assuan_get_command_name (ctx);
+      if (!name)
+        name = "?";
+      if (gpg_err_source (err) == GPG_ERR_SOURCE_DEFAULT)
+        log_error ("command '%s' failed: %s\n", name,
+                   gpg_strerror (err));
+      else
+        log_error ("command '%s' failed: %s <%s>\n", name,
+                   gpg_strerror (err), gpg_strsource (err));
+    }
+  return err;
+}
+
+
+\f
+/* Handle OPTION commands. */
+static gpg_error_t
+option_handler (assuan_context_t ctx, const char *key, const char *value)
+{
+  ctrl_t ctrl = assuan_get_pointer (ctx);
+  gpg_error_t err = 0;
+
+  if (!strcmp (key, "foo"))
+    {
+      ctrl->server_local->foo = 1;
+    }
+  else if (!strcmp (key, "lc-messages"))
+    {
+      if (ctrl->lc_messages)
+        xfree (ctrl->lc_messages);
+      ctrl->lc_messages = xtrystrdup (value);
+      if (!ctrl->lc_messages)
+        return out_of_core ();
+    }
+  else
+    err = gpg_error (GPG_ERR_UNKNOWN_OPTION);
+
+  return err;
+}
+
+
+\f
+static const char hlp_foo[] =
+  "FOO <user_id>\n"
+  "\n"
+  "Bla bla\n"
+  "more bla.";
+static gpg_error_t
+cmd_foo (assuan_context_t ctx, char *line)
+{
+  ctrl_t ctrl = assuan_get_pointer (ctx);
+  gpg_error_t err;
+
+  (void)ctrl;
+  (void)line;
+
+  err = gpg_error (GPG_ERR_NOT_IMPLEMENTED);
+
+  return leave_cmd (ctx, err);
+}
+
+
+\f
+static const char hlp_getinfo[] =
+  "GETINFO <what>\n"
+  "\n"
+  "Multi purpose command to return certain information.  \n"
+  "Supported values of WHAT are:\n"
+  "\n"
+  "version     - Return the version of the program.\n"
+  "pid         - Return the process id of the server.\n"
+  "socket_name - Return the name of the socket.\n"
+  "session_id  - Return the current session_id.\n"
+  "getenv NAME - Return value of envvar NAME\n";
+static gpg_error_t
+cmd_getinfo (assuan_context_t ctx, char *line)
+{
+  ctrl_t ctrl = assuan_get_pointer (ctx);
+  gpg_error_t err;
+  char numbuf[50];
+
+  if (!strcmp (line, "version"))
+    {
+      const char *s = VERSION;
+      err = assuan_send_data (ctx, s, strlen (s));
+    }
+  else if (!strcmp (line, "pid"))
+    {
+      snprintf (numbuf, sizeof numbuf, "%lu", (unsigned long)getpid ());
+      err = assuan_send_data (ctx, numbuf, strlen (numbuf));
+    }
+  else if (!strcmp (line, "socket_name"))
+    {
+      const char *s = get_kbxd_socket_name ();
+      if (!s)
+        s = "[none]";
+      err = assuan_send_data (ctx, s, strlen (s));
+    }
+  else if (!strcmp (line, "session_id"))
+    {
+      snprintf (numbuf, sizeof numbuf, "%u", ctrl->server_local->session_id);
+      err = assuan_send_data (ctx, numbuf, strlen (numbuf));
+    }
+  else if (!strncmp (line, "getenv", 6)
+           && (line[6] == ' ' || line[6] == '\t' || !line[6]))
+    {
+      line += 6;
+      while (*line == ' ' || *line == '\t')
+        line++;
+      if (!*line)
+        err = gpg_error (GPG_ERR_MISSING_VALUE);
+      else
+        {
+          const char *s = getenv (line);
+          if (!s)
+            err = set_error (GPG_ERR_NOT_FOUND, "No such envvar");
+          else
+            err = assuan_send_data (ctx, s, strlen (s));
+        }
+    }
+  else
+    err = set_error (GPG_ERR_ASS_PARAMETER, "unknown value for WHAT");
+
+  return leave_cmd (ctx, err);
+}
+
+
+\f
+static const char hlp_killkeyboxd[] =
+  "KILLKEYBOXD\n"
+  "\n"
+  "This command allows a user - given sufficient permissions -\n"
+  "to kill this keyboxd process.\n";
+static gpg_error_t
+cmd_killkeyboxd (assuan_context_t ctx, char *line)
+{
+  ctrl_t ctrl = assuan_get_pointer (ctx);
+
+  (void)line;
+
+  ctrl->server_local->stopme = 1;
+  assuan_set_flag (ctx, ASSUAN_FORCE_CLOSE, 1);
+  return gpg_error (GPG_ERR_EOF);
+}
+
+
+static const char hlp_reloadkeyboxd[] =
+  "RELOADKEYBOXD\n"
+  "\n"
+  "This command is an alternative to SIGHUP\n"
+  "to reload the configuration.";
+static gpg_error_t
+cmd_reloadkeyboxd (assuan_context_t ctx, char *line)
+{
+  (void)ctx;
+  (void)line;
+
+  kbxd_sighup_action ();
+  return 0;
+}
+
+
+\f
+/* Tell the assuan library about our commands. */
+static int
+register_commands (assuan_context_t ctx)
+{
+  static struct {
+    const char *name;
+    assuan_handler_t handler;
+    const char * const help;
+  } table[] = {
+    { "FOO",        cmd_foo,        hlp_foo },
+    { "GETINFO",    cmd_getinfo,    hlp_getinfo },
+    { "KILLKEYBOXD",cmd_killkeyboxd,hlp_killkeyboxd },
+    { "RELOADKEYBOXD",cmd_reloadkeyboxd,hlp_reloadkeyboxd },
+    { NULL, NULL }
+  };
+  int i, j, rc;
+
+  for (i=j=0; table[i].name; i++)
+    {
+      rc = assuan_register_command (ctx, table[i].name, table[i].handler,
+                                    table[i].help);
+      if (rc)
+        return rc;
+    }
+  return 0;
+}
+
+
+/* Note that we do not reset the list of configured keyservers.  */
+static gpg_error_t
+reset_notify (assuan_context_t ctx, char *line)
+{
+  ctrl_t ctrl = assuan_get_pointer (ctx);
+
+  (void)line;
+  (void)ctrl;
+
+  return 0;
+}
+
+
+/* This function is called by our assuan log handler to test whether a
+ * log message shall really be printed.  The function must return
+ * false to inhibit the logging of MSG.  CAT gives the requested log
+ * category.  MSG might be NULL. */
+int
+kbxd_assuan_log_monitor (assuan_context_t ctx, unsigned int cat,
+                         const char *msg)
+{
+  ctrl_t ctrl = assuan_get_pointer (ctx);
+
+  (void)cat;
+  (void)msg;
+
+  if (!ctrl || !ctrl->server_local)
+    return 1; /* Can't decide - allow logging.  */
+
+  if (!ctrl->server_local->inhibit_data_logging)
+    return 1; /* Not requested - allow logging.  */
+
+  /* Disallow logging if *_now is true.  */
+  return !ctrl->server_local->inhibit_data_logging_now;
+}
+
+
+/* Return the assuan contxt from the local server info in CTRL.  */
+static assuan_context_t
+get_assuan_ctx_from_ctrl (ctrl_t ctrl)
+{
+  if (!ctrl || !ctrl->server_local)
+    return NULL;
+  return ctrl->server_local->assuan_ctx;
+}
+
+
+/* Startup the server and run the main command loop.  With FD = -1,
+ * use stdin/stdout.  SESSION_ID is either 0 or a unique number
+ * identifying a session. */
+void
+kbxd_start_command_handler (ctrl_t ctrl, gnupg_fd_t fd, unsigned int session_id)
+{
+  static const char hello[] = "Keyboxd " VERSION " at your service";
+  static char *hello_line;
+  int rc;
+  assuan_context_t ctx;
+
+  ctrl->server_local = xtrycalloc (1, sizeof *ctrl->server_local);
+  if (!ctrl->server_local)
+    {
+      log_error (_("can't allocate control structure: %s\n"),
+                 gpg_strerror (gpg_error_from_syserror ()));
+      xfree (ctrl);
+      return;
+    }
+
+  rc = assuan_new (&ctx);
+  if (rc)
+    {
+      log_error (_("failed to allocate assuan context: %s\n"),
+                gpg_strerror (rc));
+      kbxd_exit (2);
+    }
+
+  if (fd == GNUPG_INVALID_FD)
+    {
+      assuan_fd_t filedes[2];
+
+      filedes[0] = assuan_fdopen (0);
+      filedes[1] = assuan_fdopen (1);
+      rc = assuan_init_pipe_server (ctx, filedes);
+    }
+  else
+    {
+      rc = assuan_init_socket_server (ctx, fd, ASSUAN_SOCKET_SERVER_ACCEPTED);
+    }
+
+  if (rc)
+    {
+      assuan_release (ctx);
+      log_error (_("failed to initialize the server: %s\n"),
+                 gpg_strerror (rc));
+      kbxd_exit (2);
+    }
+
+  rc = register_commands (ctx);
+  if (rc)
+    {
+      log_error (_("failed to the register commands with Assuan: %s\n"),
+                 gpg_strerror(rc));
+      kbxd_exit (2);
+    }
+
+
+  if (!hello_line)
+    {
+      hello_line = xtryasprintf
+        ("Home: %s\n"
+         "Config: %s\n"
+         "%s",
+         gnupg_homedir (),
+         /*opt.config_filename? opt.config_filename :*/ "[none]",
+         hello);
+    }
+
+  ctrl->server_local->assuan_ctx = ctx;
+  assuan_set_pointer (ctx, ctrl);
+
+  assuan_set_hello_line (ctx, hello_line);
+  assuan_register_option_handler (ctx, option_handler);
+  assuan_register_reset_notify (ctx, reset_notify);
+
+  ctrl->server_local->session_id = session_id;
+
+  /* The next call enable the use of status_printf.  */
+  set_assuan_context_func (get_assuan_ctx_from_ctrl);
+
+  for (;;)
+    {
+      rc = assuan_accept (ctx);
+      if (rc == -1)
+        break;
+      if (rc)
+        {
+          log_info (_("Assuan accept problem: %s\n"), gpg_strerror (rc));
+          break;
+        }
+
+#ifndef HAVE_W32_SYSTEM
+      if (opt.verbose)
+        {
+         assuan_peercred_t peercred;
+
+          if (!assuan_get_peercred (ctx, &peercred))
+            log_info ("connection from process %ld (%ld:%ld)\n",
+                      (long)peercred->pid, (long)peercred->uid,
+                     (long)peercred->gid);
+        }
+#endif
+
+      rc = assuan_process (ctx);
+      if (rc)
+        {
+          log_info (_("Assuan processing failed: %s\n"), gpg_strerror (rc));
+          continue;
+        }
+    }
+
+
+  set_assuan_context_func (NULL);
+  ctrl->server_local->assuan_ctx = NULL;
+  assuan_release (ctx);
+
+  if (ctrl->server_local->stopme)
+    kbxd_exit (0);
+
+  if (ctrl->refcount)
+    log_error ("oops: connection control structure still referenced (%d)\n",
+               ctrl->refcount);
+  else
+    {
+      xfree (ctrl->server_local);
+      ctrl->server_local = NULL;
+    }
+}
diff --git a/kbx/keyboxd-w32info.rc b/kbx/keyboxd-w32info.rc
new file mode 100644 (file)
index 0000000..4217474
--- /dev/null
@@ -0,0 +1,50 @@
+/* keyboxd-w32info.rc                                        -*- c -*-
+ * Copyright (C) 2018 g10 Code GmbH
+ *
+ * This file is free software; as a special exception the author gives
+ * unlimited permission to copy and/or distribute it, with or without
+ * modifications, as long as this notice is preserved.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY, to the extent permitted by law; without even the
+ * implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ */
+
+#include "afxres.h"
+#include "../common/w32info-rc.h"
+
+1 ICON "../common/gnupg.ico"
+
+1 VERSIONINFO
+  FILEVERSION    W32INFO_VI_FILEVERSION
+  PRODUCTVERSION W32INFO_VI_PRODUCTVERSION
+  FILEFLAGSMASK  0x3fL
+#ifdef _DEBUG
+  FILEFLAGS      0x01L    /* VS_FF_DEBUG (0x1)*/
+#else
+  FILEFLAGS      0x00L
+#endif
+  FILEOS         0x40004L /* VOS_NT (0x40000) | VOS__WINDOWS32 (0x4)  */
+  FILETYPE       0x1L     /* VFT_APP (0x1)  */
+  FILESUBTYPE    0x0L     /* VFT2_UNKNOWN   */
+  BEGIN
+    BLOCK "StringFileInfo"
+    BEGIN
+        BLOCK "040904b0"  /* US English (0409), Unicode (04b0) */
+        BEGIN
+            VALUE "FileDescription", L"GnuPG\x2019s public key daemon\0"
+            VALUE "InternalName", "keyboxd\0"
+            VALUE "OriginalFilename", "keyboxd.exe\0"
+            VALUE "ProductName",    W32INFO_PRODUCTNAME
+            VALUE "ProductVersion", W32INFO_PRODUCTVERSION
+            VALUE "CompanyName", W32INFO_COMPANYNAME
+            VALUE "FileVersion", W32INFO_FILEVERSION
+            VALUE "LegalCopyright", W32INFO_LEGALCOPYRIGHT
+            VALUE "Comments",    W32INFO_COMMENTS
+        END
+    END
+    BLOCK "VarFileInfo"
+    BEGIN
+      VALUE "Translation", 0x409, 0x4b0
+    END
+  END
diff --git a/kbx/keyboxd.c b/kbx/keyboxd.c
new file mode 100644 (file)
index 0000000..28232b3
--- /dev/null
@@ -0,0 +1,1817 @@
+/* keyboxd.c  -  The GnuPG Keybox Daemon
+ * Copyright (C) 2000-2007, 2009-2010 Free Software Foundation, Inc.
+ * Copyright (C) 2000-2018 Werner Koch
+ *
+ * This file is part of GnuPG.
+ *
+ * GnuPG is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * GnuPG is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * 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 <https://www.gnu.org/licenses/>.
+ * SPDX-License-Identifier: GPL-3.0+
+ */
+
+#include <config.h>
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <stddef.h>
+#include <stdarg.h>
+#include <string.h>
+#include <errno.h>
+#include <assert.h>
+#include <time.h>
+#include <fcntl.h>
+#include <sys/stat.h>
+#ifdef HAVE_W32_SYSTEM
+# ifndef WINVER
+#  define WINVER 0x0500  /* Same as in common/sysutils.c */
+# endif
+# include <winsock2.h>
+#else /*!HAVE_W32_SYSTEM*/
+# include <sys/socket.h>
+# include <sys/un.h>
+#endif /*!HAVE_W32_SYSTEM*/
+#include <unistd.h>
+#ifdef HAVE_SIGNAL_H
+# include <signal.h>
+#endif
+#include <npth.h>
+
+#define GNUPG_COMMON_NEED_AFLOCAL
+#include "keyboxd.h"
+#include <assuan.h> /* Malloc hooks and socket wrappers. */
+
+#include "../common/i18n.h"
+#include "../common/sysutils.h"
+#include "../common/asshelp.h"
+#include "../common/init.h"
+#include "../common/gc-opt-flags.h"
+#include "../common/exechelp.h"
+
+
+enum cmd_and_opt_values
+  {
+    aNull = 0,
+    oQuiet       = 'q',
+    oVerbose     = 'v',
+
+    oNoVerbose = 500,
+    aGPGConfList,
+    aGPGConfTest,
+    oOptions,
+    oDebug,
+    oDebugAll,
+    oDebugWait,
+    oNoGreeting,
+    oNoOptions,
+    oHomedir,
+    oNoDetach,
+    oLogFile,
+    oServer,
+    oDaemon,
+    oBatch,
+    oFakedSystemTime,
+    oListenBacklog,
+    oDisableCheckOwnSocket,
+
+    oDummy
+  };
+
+
+static ARGPARSE_OPTS opts[] = {
+  ARGPARSE_c (aGPGConfList, "gpgconf-list", "@"),
+  ARGPARSE_c (aGPGConfTest, "gpgconf-test", "@"),
+
+  ARGPARSE_group (301, N_("@Options:\n ")),
+
+  ARGPARSE_s_n (oDaemon,  "daemon", N_("run in daemon mode (background)")),
+  ARGPARSE_s_n (oServer,  "server", N_("run in server mode (foreground)")),
+  ARGPARSE_s_n (oVerbose, "verbose", N_("verbose")),
+  ARGPARSE_s_n (oQuiet,          "quiet",     N_("be somewhat more quiet")),
+  ARGPARSE_s_s (oOptions, "options", N_("|FILE|read options from FILE")),
+
+  ARGPARSE_s_s (oDebug,            "debug",      "@"),
+  ARGPARSE_s_n (oDebugAll,  "debug-all",  "@"),
+  ARGPARSE_s_i (oDebugWait, "debug-wait", "@"),
+
+  ARGPARSE_s_n (oDisableCheckOwnSocket, "disable-check-own-socket", "@"),
+  ARGPARSE_s_n (oNoDetach,  "no-detach", N_("do not detach from the console")),
+  ARGPARSE_s_s (oLogFile,   "log-file",  N_("use a log file for the server")),
+  ARGPARSE_s_s (oFakedSystemTime, "faked-system-time", "@"),
+
+  ARGPARSE_s_n (oBatch,      "batch",        "@"),
+  ARGPARSE_s_s (oHomedir,    "homedir",      "@"),
+
+  ARGPARSE_s_i (oListenBacklog, "listen-backlog", "@"),
+
+  ARGPARSE_end () /* End of list */
+};
+
+
+/* The list of supported debug flags.  */
+static struct debug_flags_s debug_flags [] =
+  {
+    { 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 "?".  */
+  };
+
+/* The timer tick used for housekeeping stuff.  Note that on Windows
+ * we use a SetWaitableTimer seems to signal earlier than about 2
+ * seconds.  Thus we use 4 seconds on all platforms except for
+ * Windowsce.  CHECK_OWN_SOCKET_INTERVAL defines how often we check
+ * our own socket in standard socket mode.  If that value is 0 we
+ * don't check at all.  All values are in seconds. */
+#if defined(HAVE_W32CE_SYSTEM)
+# define TIMERTICK_INTERVAL         (60)
+# define CHECK_OWN_SOCKET_INTERVAL   (0)  /* Never */
+#else
+# define TIMERTICK_INTERVAL          (4)
+# define CHECK_OWN_SOCKET_INTERVAL  (60)
+#endif
+
+/* The list of open file descriptors at startup.  Note that this list
+ * has been allocated using the standard malloc.  */
+#ifndef HAVE_W32_SYSTEM
+static int *startup_fd_list;
+#endif
+
+/* The signal mask at startup and a flag telling whether it is valid.  */
+#ifdef HAVE_SIGPROCMASK
+static sigset_t startup_signal_mask;
+static int startup_signal_mask_valid;
+#endif
+
+/* Flag to indicate that a shutdown was requested.  */
+static int shutdown_pending;
+
+/* Counter for the currently running own socket checks.  */
+static int check_own_socket_running;
+
+/* Flag to indicate that we shall not watch our own socket. */
+static int disable_check_own_socket;
+
+/* Flag to inhibit socket removal in cleanup.  */
+static int inhibit_socket_removal;
+
+/* Name of the communication socket used for client requests.  */
+static char *socket_name;
+
+/* We need to keep track of the server's nonces (these are dummies for
+ * POSIX systems). */
+static assuan_sock_nonce_t socket_nonce;
+
+/* Value for the listen() backlog argument.  We use the same value for
+ * all sockets - 64 is on current Linux half of the default maximum.
+ * Let's try this as default.  Change at runtime with --listen-backlog.  */
+static int listen_backlog = 64;
+
+/* Name of a config file, which will be reread on a HUP if it is not NULL. */
+static char *config_filename;
+
+/* Keep track of the current log file so that we can avoid updating
+ * the log file after a SIGHUP if it didn't changed.  Malloced. */
+static char *current_logfile;
+
+/* This flag is true if the inotify mechanism for detecting the
+ * removal of the homedir is active.  This flag is used to disable the
+ * alternative but portable stat based check.  */
+static int have_homedir_inotify;
+
+/* Depending on how keyboxd was started, the homedir inotify watch may
+ * not be reliable.  This flag is set if we assume that inotify works
+ * reliable.  */
+static int reliable_homedir_inotify;
+
+/* 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 will have at max only a few dozen
+ * connections at a 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 cygwin,
+                                        assuan_sock_nonce_t *nonce);
+static void create_directories (void);
+
+static void kbxd_libgcrypt_progress_cb (void *data, const char *what,
+                                        int printchar,
+                                        int current, int total);
+static void kbxd_init_default_ctrl (ctrl_t ctrl);
+static void kbxd_deinit_default_ctrl (ctrl_t ctrl);
+
+static void handle_connections (gnupg_fd_t listen_fd);
+static void check_own_socket (void);
+static int check_for_running_kbxd (int silent);
+
+/* Pth wrapper function definitions. */
+ASSUAN_SYSTEM_NPTH_IMPL;
+
+\f
+/*
+ * Functions.
+ */
+
+/* Allocate a string describing a library version by calling a GETFNC.
+ * This function is expected to be called only once.  GETFNC is
+ * expected to have a semantic like gcry_check_version ().  */
+static char *
+make_libversion (const char *libname, const char *(*getfnc)(const char*))
+{
+  return xstrconcat (libname, " ", getfnc (NULL), NULL);
+}
+
+
+/* Return strings describing this program.  The case values are
+ * described in common/argparse.c:strusage.  The values here override
+ * the default values given by strusage.  */
+static const char *
+my_strusage (int level)
+{
+  static char *ver_gcry;
+  const char *p;
+
+  switch (level)
+    {
+    case 11: p = "keyboxd (@GNUPG@)";
+      break;
+    case 13: p = VERSION; break;
+    case 17: p = PRINTABLE_OS_NAME; break;
+      /* TRANSLATORS: @EMAIL@ will get replaced by the actual bug
+         reporting address.  This is so that we can change the
+         reporting address without breaking the translations.  */
+    case 19: p = _("Please report bugs to <@EMAIL@>.\n"); break;
+
+    case 20:
+      if (!ver_gcry)
+        ver_gcry = make_libversion ("libgcrypt", gcry_check_version);
+      p = ver_gcry;
+      break;
+
+    case 1:
+    case 40: p =  _("Usage: keyboxd [options] (-h for help)");
+      break;
+    case 41: p =  _("Syntax: keyboxd [options] [command [args]]\n"
+                    "Public key management for @GNUPG@\n");
+    break;
+
+    default: p = NULL;
+    }
+  return p;
+}
+
+
+
+/* Setup the debugging.  Note that we don't fail here, because it is
+ * important to keep keyboxd running even after re-reading the options
+ * due to a SIGHUP. */
+static void
+set_debug (void)
+{
+  if (opt.debug && !opt.verbose)
+    opt.verbose = 1;
+  if (opt.debug && opt.quiet)
+    opt.quiet = 0;
+
+  if (opt.debug & DBG_MPI_VALUE)
+    gcry_control (GCRYCTL_SET_DEBUG_FLAGS, 2);
+  if (opt.debug & DBG_CRYPTO_VALUE )
+    gcry_control (GCRYCTL_SET_DEBUG_FLAGS, 1);
+  gcry_control (GCRYCTL_SET_VERBOSITY, (int)opt.verbose);
+
+  if (opt.debug)
+    parse_debug_flag (NULL, &opt.debug, debug_flags);
+}
+
+
+/* Helper for cleanup to remove one socket with NAME.  */
+static void
+remove_socket (char *name)
+{
+  if (name && *name)
+    {
+      gnupg_remove (name);
+      *name = 0;
+    }
+}
+
+
+/* Cleanup code for this program.  This is either called has an atexit
+   handler or directly.  */
+static void
+cleanup (void)
+{
+  static int done;
+
+  if (done)
+    return;
+  done = 1;
+  if (!inhibit_socket_removal)
+    remove_socket (socket_name);
+}
+
+
+/* Handle options which are allowed to be reset after program start.
+ * Return true when the current option in PARGS could be handled and
+ * false if not.  As a special feature, passing a value of NULL for
+ * PARGS, resets the options to the default.  REREAD should be set
+ * true if it is not the initial option parsing. */
+static int
+parse_rereadable_options (ARGPARSE_ARGS *pargs, int reread)
+{
+  if (!pargs)
+    { /* reset mode */
+      opt.quiet = 0;
+      opt.verbose = 0;
+      opt.debug = 0;
+      disable_check_own_socket = 0;
+      return 1;
+    }
+
+  switch (pargs->r_opt)
+    {
+    case oQuiet: opt.quiet = 1; break;
+    case oVerbose: opt.verbose++; break;
+
+    case oDebug:
+      parse_debug_flag (pargs->r.ret_str, &opt.debug, debug_flags);
+      break;
+    case oDebugAll: opt.debug = ~0; break;
+
+    case oLogFile:
+      if (!reread)
+        return 0; /* not handeld */
+      if (!current_logfile || !pargs->r.ret_str
+          || strcmp (current_logfile, pargs->r.ret_str))
+        {
+          log_set_file (pargs->r.ret_str);
+          xfree (current_logfile);
+          current_logfile = xtrystrdup (pargs->r.ret_str);
+        }
+      break;
+
+    case oDisableCheckOwnSocket: disable_check_own_socket = 1; break;
+
+    default:
+      return 0; /* not handled */
+    }
+
+  return 1; /* handled */
+}
+
+
+/* Fixup some options after all have been processed.  */
+static void
+finalize_rereadable_options (void)
+{
+}
+
+
+static void
+thread_init_once (void)
+{
+  static int npth_initialized = 0;
+
+  if (!npth_initialized)
+    {
+      npth_initialized++;
+      npth_init ();
+    }
+  gpgrt_set_syscall_clamp (npth_unprotect, npth_protect);
+  /* Now that we have set the syscall clamp we need to tell Libgcrypt
+   * that it should get them from libgpg-error.  Note that Libgcrypt
+   * has already been initialized but at that point nPth was not
+   * initialized and thus Libgcrypt could not set its system call
+   * clamp.  */
+  gcry_control (GCRYCTL_REINIT_SYSCALL_CLAMP, 0, 0);
+}
+
+
+static void
+initialize_modules (void)
+{
+  thread_init_once ();
+  assuan_set_system_hooks (ASSUAN_SYSTEM_NPTH);
+}
+
+
+/* The main entry point.  */
+int
+main (int argc, char **argv )
+{
+  ARGPARSE_ARGS pargs;
+  int orig_argc;
+  char **orig_argv;
+  FILE *configfp = NULL;
+  char *configname = NULL;
+  unsigned configlineno;
+  int parse_debug = 0;
+  int default_config =1;
+  int pipe_server = 0;
+  int is_daemon = 0;
+  int nodetach = 0;
+  char *logfile = NULL;
+  int gpgconf_list = 0;
+  int debug_wait = 0;
+  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.  We don't need it on Windows.  */
+#ifndef HAVE_W32_SYSTEM
+  startup_fd_list = get_all_open_fds ();
+#endif /*!HAVE_W32_SYSTEM*/
+#ifdef HAVE_SIGPROCMASK
+  if (!sigprocmask (SIG_UNBLOCK, NULL, &startup_signal_mask))
+    startup_signal_mask_valid = 1;
+#endif /*HAVE_SIGPROCMASK*/
+
+  /* Set program name etc.  */
+  set_strusage (my_strusage);
+  log_set_prefix ("keyboxd", GPGRT_LOG_WITH_PREFIX|GPGRT_LOG_WITH_PID);
+
+  /* Make sure that our subsystems are ready.  */
+  i18n_init ();
+  init_common_subsystems (&argc, &argv);
+  gcry_control (GCRYCTL_DISABLE_SECMEM, 0);
+
+  malloc_hooks.malloc = gcry_malloc;
+  malloc_hooks.realloc = gcry_realloc;
+  malloc_hooks.free = gcry_free;
+  assuan_set_malloc_hooks (&malloc_hooks);
+  assuan_set_gpg_err_source (GPG_ERR_SOURCE_DEFAULT);
+  assuan_sock_init ();
+  assuan_sock_set_system_hooks (ASSUAN_SYSTEM_NPTH);
+  setup_libassuan_logging (&opt.debug, NULL);
+
+  setup_libgcrypt_logging ();
+  gcry_set_progress_handler (kbxd_libgcrypt_progress_cb, NULL);
+
+  /* Set default options.  */
+  parse_rereadable_options (NULL, 0); /* Reset them to default values. */
+
+  /* Check whether we have a config file on the commandline */
+  orig_argc = argc;
+  orig_argv = argv;
+  pargs.argc = &argc;
+  pargs.argv = &argv;
+  pargs.flags= 1|(1<<6);  /* do not remove the args, ignore version */
+  while (arg_parse( &pargs, opts))
+    {
+      if (pargs.r_opt == oDebug || pargs.r_opt == oDebugAll)
+        parse_debug++;
+      else if (pargs.r_opt == oOptions)
+        { /* Yes, a config file was given so we do not try the default
+          * one.  Instead we read the config file when it is
+          * encountered during main parsing of the command line.  */
+          default_config = 0;
+       }
+       else if (pargs.r_opt == oNoOptions)
+          default_config = 0; /* --no-options */
+       else if (pargs.r_opt == oHomedir)
+          gnupg_set_homedir (pargs.r.ret_str);
+    }
+
+  if (default_config)
+    configname = make_filename (gnupg_homedir (),
+                                "keyboxd" EXTSEP_S "conf", NULL);
+
+  argc = orig_argc;
+  argv = orig_argv;
+  pargs.argc = &argc;
+  pargs.argv = &argv;
+  pargs.flags=  1;  /* do not remove the args */
+ next_pass:
+  if (configname)
+    {
+      configlineno = 0;
+      configfp = fopen (configname, "r");
+      if (!configfp)
+        {
+          if (default_config)
+            {
+              if (parse_debug)
+                log_info (_("Note: no default option file '%s'\n"), configname);
+              /* Save the default confif file name so that
+               * reread_configuration is able to test whether the
+               * config file has been created in the meantime.  */
+              xfree (config_filename);
+              config_filename = configname;
+              configname = NULL;
+           }
+          else
+            {
+              log_error (_("option file '%s': %s\n"),
+                         configname, strerror (errno));
+              exit (2);
+           }
+          xfree (configname);
+          configname = NULL;
+       }
+      if (parse_debug && configname )
+        log_info (_("reading options from '%s'\n"), configname);
+      default_config = 0;
+    }
+
+  while (optfile_parse (configfp, configname, &configlineno, &pargs, opts))
+    {
+      if (parse_rereadable_options (&pargs, 0))
+        continue; /* Already handled */
+      switch (pargs.r_opt)
+        {
+        case aGPGConfList: gpgconf_list = 1; break;
+        case aGPGConfTest: gpgconf_list = 2; break;
+        case oBatch: opt.batch=1; break;
+        case oDebugWait: debug_wait = pargs.r.ret_int; break;
+        case oOptions:
+          /* Config files may not be nested (silently ignore them). */
+          if (!configfp)
+            {
+              xfree (configname);
+              configname = xstrdup (pargs.r.ret_str);
+              goto next_pass;
+           }
+          break;
+        case oNoGreeting: /* Dummy option.  */ break;
+        case oNoVerbose: opt.verbose = 0; break;
+        case oNoOptions: break; /* no-options */
+        case oHomedir: gnupg_set_homedir (pargs.r.ret_str); break;
+        case oNoDetach: nodetach = 1; break;
+        case oLogFile: logfile = pargs.r.ret_str; break;
+        case oServer: pipe_server = 1; break;
+        case oDaemon: is_daemon = 1; break;
+        case oFakedSystemTime:
+          {
+            time_t faked_time = isotime2epoch (pargs.r.ret_str);
+            if (faked_time == (time_t)(-1))
+              faked_time = (time_t)strtoul (pargs.r.ret_str, NULL, 10);
+            gnupg_set_time (faked_time, 0);
+          }
+          break;
+
+        case oListenBacklog:
+          listen_backlog = pargs.r.ret_int;
+          break;
+
+        default : pargs.err = configfp? 1:2; break;
+       }
+    }
+  if (configfp)
+    {
+      fclose (configfp);
+      configfp = NULL;
+      /* Keep a copy of the name so that it can be read on SIGHUP. */
+      if (config_filename != configname)
+        {
+          xfree (config_filename);
+          config_filename = configname;
+        }
+      configname = NULL;
+      goto next_pass;
+    }
+
+  xfree (configname);
+  configname = NULL;
+  if (log_get_errorcount(0))
+    exit (2);
+
+  finalize_rereadable_options ();
+
+  /* Print a warning if an argument looks like an option.  */
+  if (!opt.quiet && !(pargs.flags & ARGPARSE_FLAG_STOP_SEEN))
+    {
+      int i;
+
+      for (i=0; i < argc; i++)
+        if (argv[i][0] == '-' && argv[i][1] == '-')
+          log_info (_("Note: '%s' is not considered an option\n"), argv[i]);
+    }
+
+#ifdef ENABLE_NLS
+  /* keyboxd usually does not output any messages because it runs in
+   * the background.  For log files it is acceptable to have messages
+   * always encoded in utf-8.  We switch here to utf-8, so that
+   * commands like --help still give native messages.  It is far
+   * easier to switch only once instead of for every message and it
+   * actually helps when more then one thread is active (avoids an
+   * extra copy step). */
+  bind_textdomain_codeset (PACKAGE_GT, "UTF-8");
+#endif
+
+  if (!pipe_server && !is_daemon && !gpgconf_list)
+    {
+     /* We have been called without any command and thus we merely
+      * check whether an instance of us is already running.  We do
+      * this right here so that we don't clobber a logfile with this
+      * check but print the status directly to stderr.  */
+      opt.debug = 0;
+      set_debug ();
+      check_for_running_kbxd (0);
+      kbxd_exit (0);
+    }
+
+  set_debug ();
+
+  if (atexit (cleanup))
+    {
+      log_error ("atexit failed\n");
+      cleanup ();
+      exit (1);
+    }
+
+  /* Try to create missing directories. */
+  create_directories ();
+
+  if (debug_wait && pipe_server)
+    {
+      thread_init_once ();
+      log_debug ("waiting for debugger - my pid is %u .....\n",
+                 (unsigned int)getpid());
+      gnupg_sleep (debug_wait);
+      log_debug ("... okay\n");
+    }
+
+  if (gpgconf_list == 2)
+    kbxd_exit (0);
+  else if (gpgconf_list)
+    {
+      char *filename;
+      char *filename_esc;
+
+      /* List options and default values in the gpgconf format.  */
+      filename = make_filename (gnupg_homedir (),
+                                "keyboxd" EXTSEP_S "conf", NULL);
+      filename_esc = percent_escape (filename, NULL);
+
+      es_printf ("%s-%s.conf:%lu:\"%s\n",
+                 GPGCONF_NAME, "keyboxd", GC_OPT_FLAG_DEFAULT, filename_esc);
+      xfree (filename);
+      xfree (filename_esc);
+
+      es_printf ("verbose:%lu:\n"
+                 "quiet:%lu:\n"
+                 "log-file:%lu:\n",
+                 GC_OPT_FLAG_NONE|GC_OPT_FLAG_RUNTIME,
+                 GC_OPT_FLAG_NONE|GC_OPT_FLAG_RUNTIME,
+                 GC_OPT_FLAG_NONE|GC_OPT_FLAG_RUNTIME );
+
+      kbxd_exit (0);
+    }
+
+  /* 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);
+    }
+
+  if (pipe_server)
+    {
+      /* This is the simple pipe based server */
+      ctrl_t ctrl;
+
+      initialize_modules ();
+
+      ctrl = xtrycalloc (1, sizeof *ctrl);
+      if (!ctrl)
+        {
+          log_error ("error allocating connection control data: %s\n",
+                     strerror (errno) );
+          kbxd_exit (1);
+        }
+      kbxd_init_default_ctrl (ctrl);
+      kbxd_start_command_handler (ctrl, GNUPG_INVALID_FD, 0);
+      kbxd_deinit_default_ctrl (ctrl);
+      xfree (ctrl);
+    }
+  else if (!is_daemon)
+    ; /* NOTREACHED */
+  else
+    { /* Regular daemon mode.  */
+      gnupg_fd_t fd;
+#ifndef HAVE_W32_SYSTEM
+      pid_t pid;
+#endif
+
+      /* Create the sockets.  */
+      socket_name = create_socket_name (KEYBOXD_SOCK_NAME, 1);
+      fd = create_server_socket (socket_name, 0, &socket_nonce);
+
+      fflush (NULL);
+
+#ifdef HAVE_W32_SYSTEM
+
+      (void)nodetach;
+      initialize_modules ();
+
+#else /*!HAVE_W32_SYSTEM*/
+
+      pid = fork ();
+      if (pid == (pid_t)-1)
+        {
+          log_fatal ("fork failed: %s\n", strerror (errno) );
+          exit (1);
+        }
+      else if (pid)
+        { /* We are the parent */
+
+          /* Close the socket FD. */
+          close (fd);
+
+          /* The signal mask might not be correct right now and thus
+           * we restore it.  That is not strictly necessary but some
+           * programs falsely assume a cleared signal mask.  */
+
+#ifdef HAVE_SIGPROCMASK
+          if (startup_signal_mask_valid)
+            {
+              if (sigprocmask (SIG_SETMASK, &startup_signal_mask, NULL))
+                log_error ("error restoring signal mask: %s\n",
+                           strerror (errno));
+            }
+          else
+            log_info ("no saved signal mask\n");
+#endif /*HAVE_SIGPROCMASK*/
+
+          *socket_name = 0; /* Don't let cleanup() remove the socket -
+                               the child should do this from now on */
+
+          exit (0);
+          /*NOTREACHED*/
+        } /* End parent */
+
+      /*
+       * This is the child
+       */
+
+      initialize_modules ();
+
+      /* Detach from tty and put process into a new session */
+      if (!nodetach)
+        {
+          int i;
+          unsigned int oldflags;
+
+          /* Close stdin, stdout and stderr unless it is the log stream */
+          for (i=0; i <= 2; i++)
+            {
+              if (!log_test_fd (i) && i != fd )
+                {
+                  if ( ! close (i)
+                       && open ("/dev/null", i? O_WRONLY : O_RDONLY) == -1)
+                    {
+                      log_error ("failed to open '%s': %s\n",
+                                 "/dev/null", strerror (errno));
+                      cleanup ();
+                      exit (1);
+                    }
+                }
+            }
+          if (setsid() == -1)
+            {
+              log_error ("setsid() failed: %s\n", strerror(errno) );
+              cleanup ();
+              exit (1);
+            }
+
+          log_get_prefix (&oldflags);
+          log_set_prefix (NULL, oldflags | GPGRT_LOG_RUN_DETACHED);
+          opt.running_detached = 1;
+
+          /* Because we don't support running a program on the command
+           * line we can assume that the inotify things works and thus
+           * we can avoid the regular stat calls.  */
+          reliable_homedir_inotify = 1;
+        }
+
+      {
+        struct sigaction sa;
+
+        sa.sa_handler = SIG_IGN;
+        sigemptyset (&sa.sa_mask);
+        sa.sa_flags = 0;
+        sigaction (SIGPIPE, &sa, NULL);
+      }
+#endif /*!HAVE_W32_SYSTEM*/
+
+      if (gnupg_chdir (gnupg_daemon_rootdir ()))
+        {
+          log_error ("chdir to '%s' failed: %s\n",
+                     gnupg_daemon_rootdir (), strerror (errno));
+          exit (1);
+        }
+
+      log_info ("%s %s started\n", strusage(11), strusage(13) );
+      handle_connections (fd);
+      assuan_sock_close (fd);
+    }
+
+  return 0;
+}
+
+
+/* Exit entry point.  This function should be called instead of a
+   plain exit.  */
+void
+kbxd_exit (int rc)
+{
+  /* As usual we run our cleanup handler.  */
+  cleanup ();
+
+  /* at this time a bit annoying */
+  if ((opt.debug & DBG_MEMSTAT_VALUE))
+    gcry_control (GCRYCTL_DUMP_MEMORY_STATS );
+  rc = rc? rc : log_get_errorcount(0)? 2 : 0;
+  exit (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 ours.  */
+static void
+kbxd_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
+kbxd_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
+ * structure.  Its purpose is to setup the default values for that
+ * structure.  Note that some values may have already been set.  */
+static void
+kbxd_init_default_ctrl (ctrl_t ctrl)
+{
+  ctrl->magic = SERVER_CONTROL_MAGIC;
+}
+
+
+/* Release all resources allocated by default in the control
+   structure.  This is the counterpart to kbxd_init_default_ctrl.  */
+static void
+kbxd_deinit_default_ctrl (ctrl_t ctrl)
+{
+  if (!ctrl)
+    return;
+  ctrl->magic = 0xdeadbeef;
+  unregister_progress_cb ();
+  xfree (ctrl->lc_messages);
+}
+
+
+/* Reread parts of the configuration.  Note, that this function is
+ * obviously not thread-safe and should only be called from the PTH
+ * signal handler.
+ *
+ * Fixme: Due to the way the argument parsing works, we create a
+ * memory leak here for all string type arguments.  There is currently
+ * no clean way to tell whether the memory for the argument has been
+ * allocated or points into the process' original arguments.  Unless
+ * we have a mechanism to tell this, we need to live on with this. */
+static void
+reread_configuration (void)
+{
+  ARGPARSE_ARGS pargs;
+  FILE *fp;
+  unsigned int configlineno = 0;
+  int dummy;
+
+  if (!config_filename)
+    return; /* No config file. */
+
+  fp = fopen (config_filename, "r");
+  if (!fp)
+    {
+      log_info (_("option file '%s': %s\n"),
+                config_filename, strerror(errno) );
+      return;
+    }
+
+  parse_rereadable_options (NULL, 1); /* Start from the default values. */
+
+  memset (&pargs, 0, sizeof pargs);
+  dummy = 0;
+  pargs.argc = &dummy;
+  pargs.flags = 1;  /* do not remove the args */
+  while (optfile_parse (fp, config_filename, &configlineno, &pargs, opts) )
+    {
+      if (pargs.r_opt < -1)
+        pargs.err = 1; /* Print a warning. */
+      else /* Try to parse this option - ignore unchangeable ones. */
+        parse_rereadable_options (&pargs, 1);
+    }
+  fclose (fp);
+  finalize_rereadable_options ();
+  set_debug ();
+}
+
+
+/* Return the file name of the socket we are using for requests.  */
+const char *
+get_kbxd_socket_name (void)
+{
+  const char *s = socket_name;
+
+  return (s && *s)? s : NULL;
+}
+
+
+/* Return the number of active connections. */
+int
+get_kbxd_active_connection_count (void)
+{
+  return active_connections;
+}
+
+
+/* Create a name for the socket in the home directory as using
+ * STANDARD_NAME.  We also check for valid characters as well as
+ * against a maximum allowed length for a Unix domain socket is done.
+ * The function terminates the process in case of an error.  The
+ * function returns a pointer to an allocated string with the absolute
+ * name of the socket used.  */
+static char *
+create_socket_name (char *standard_name, int with_homedir)
+{
+  char *name;
+
+  if (with_homedir)
+    name = make_filename (gnupg_socketdir (), standard_name, NULL);
+  else
+    name = make_filename (standard_name, NULL);
+  if (strchr (name, PATHSEP_C))
+    {
+      log_error (("'%s' are not allowed in the socket name\n"), PATHSEP_S);
+      kbxd_exit (2);
+    }
+  return name;
+}
+
+
+
+/* Create a Unix domain socket with NAME.  Returns the file descriptor
+ * or terminates the process in case of an error.  If CYGWIN is set a
+ * Cygwin compatible socket is created (Windows only). */
+static gnupg_fd_t
+create_server_socket (char *name, int cygwin, assuan_sock_nonce_t *nonce)
+{
+  struct sockaddr *addr;
+  struct sockaddr_un *unaddr;
+  socklen_t len;
+  gnupg_fd_t fd;
+  int rc;
+
+  fd = assuan_sock_new (AF_UNIX, SOCK_STREAM, 0);
+  if (fd == ASSUAN_INVALID_FD)
+    {
+      log_error (_("can't create socket: %s\n"), strerror (errno));
+      *name = 0; /* Inhibit removal of the socket by cleanup(). */
+      kbxd_exit (2);
+    }
+
+  if (cygwin)
+    assuan_sock_set_flag (fd, "cygwin", 1);
+
+  unaddr = xmalloc (sizeof *unaddr);
+  addr = (struct sockaddr*)unaddr;
+
+  if (assuan_sock_set_sockaddr_un (name, addr, NULL))
+    {
+      if (errno == ENAMETOOLONG)
+        log_error (_("socket name '%s' is too long\n"), name);
+      else
+        log_error ("error preparing socket '%s': %s\n",
+                   name, gpg_strerror (gpg_error_from_syserror ()));
+      *name = 0; /* Inhibit removal of the socket by cleanup(). */
+      xfree (unaddr);
+      kbxd_exit (2);
+    }
+
+  len = SUN_LEN (unaddr);
+  rc = assuan_sock_bind (fd, addr, len);
+
+  /* Our error code mapping on W32CE returns EEXIST thus we also test
+     for this. */
+  if (rc == -1
+      && (errno == EADDRINUSE
+#ifdef HAVE_W32_SYSTEM
+          || errno == EEXIST
+#endif
+          ))
+    {
+      /* Check whether a keyboxd is already running.  */
+      if (!check_for_running_kbxd (1))
+        {
+          log_set_prefix (NULL, GPGRT_LOG_WITH_PREFIX);
+          log_set_file (NULL);
+          log_error (_("a keyboxd is already running - "
+                       "not starting a new one\n"));
+          *name = 0; /* Inhibit removal of the socket by cleanup(). */
+          assuan_sock_close (fd);
+          xfree (unaddr);
+          kbxd_exit (2);
+        }
+      gnupg_remove (unaddr->sun_path);
+      rc = assuan_sock_bind (fd, addr, len);
+    }
+  if (rc != -1 && (rc=assuan_sock_get_nonce (addr, len, nonce)))
+    log_error (_("error getting nonce for the socket\n"));
+  if (rc == -1)
+    {
+      /* We use gpg_strerror here because it allows us to get strings
+         for some W32 socket error codes.  */
+      log_error (_("error binding socket to '%s': %s\n"),
+                 unaddr->sun_path, gpg_strerror (gpg_error_from_syserror ()));
+
+      assuan_sock_close (fd);
+      *name = 0; /* Inhibit removal of the socket by cleanup(). */
+      xfree (unaddr);
+      kbxd_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), listen_backlog ) == -1)
+    {
+      log_error ("listen(fd,%d) failed: %s\n", listen_backlog, strerror (errno));
+      *name = 0; /* Inhibit removal of the socket by cleanup(). */
+      assuan_sock_close (fd);
+      xfree (unaddr);
+      kbxd_exit (2);
+    }
+
+  if (opt.verbose)
+    log_info (_("listening on socket '%s'\n"), unaddr->sun_path);
+
+  xfree (unaddr);
+  return fd;
+}
+
+
+/* Check that the directory for storing the public keys exists and
+ * create it if not.  This function won't fail as it is only a
+ * convenience function and not strictly necessary.  */
+static void
+create_public_keys_directory (const char *home)
+{
+  char *fname;
+  struct stat statbuf;
+
+  fname = make_filename (home, GNUPG_PUBLIC_KEYS_DIR, NULL);
+  if (stat (fname, &statbuf) && errno == ENOENT)
+    {
+      if (gnupg_mkdir (fname, "-rwxr-x"))
+        log_error (_("can't create directory '%s': %s\n"),
+                   fname, strerror (errno) );
+      else if (!opt.quiet)
+        log_info (_("directory '%s' created\n"), fname);
+    }
+  if (gnupg_chmod (fname, "-rwxr-x"))
+    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
+ * with HOME, we compare only the suffix if we see that the default
+ * homedir does start with a tilde.  We don't stop here in case of
+ * problems because other functions will throw an error anyway.*/
+static void
+create_directories (void)
+{
+  struct stat statbuf;
+  const char *defhome = standard_homedir ();
+  char *home;
+
+  home = make_filename (gnupg_homedir (), NULL);
+  if (stat (home, &statbuf))
+    {
+      if (errno == ENOENT)
+        {
+          if (
+#ifdef HAVE_W32_SYSTEM
+              ( !compare_filenames (home, defhome) )
+#else
+              (*defhome == '~'
+                && (strlen (home) >= strlen (defhome+1)
+                    && !strcmp (home + strlen(home)
+                                - strlen (defhome+1), defhome+1)))
+               || (*defhome != '~' && !strcmp (home, defhome) )
+#endif
+               )
+            {
+              if (gnupg_mkdir (home, "-rwx"))
+                log_error (_("can't create directory '%s': %s\n"),
+                           home, strerror (errno) );
+              else
+                {
+                  if (!opt.quiet)
+                    log_info (_("directory '%s' created\n"), home);
+                }
+            }
+        }
+      else
+        log_error (_("stat() failed for '%s': %s\n"), home, strerror (errno));
+    }
+  else if ( !S_ISDIR(statbuf.st_mode))
+    {
+      log_error (_("can't use '%s' as home directory\n"), home);
+    }
+  else /* exists and is a directory. */
+    {
+      create_public_keys_directory (home);
+    }
+  xfree (home);
+}
+
+
+
+/* This is the worker for the ticker.  It is called every few seconds
+ * and may only do fast operations. */
+static void
+handle_tick (void)
+{
+  static time_t last_minute;
+  struct stat statbuf;
+
+  if (!last_minute)
+    last_minute = time (NULL);
+
+  /* Code to be run from time to time.  */
+#if CHECK_OWN_SOCKET_INTERVAL > 0
+  if (last_minute + CHECK_OWN_SOCKET_INTERVAL <= time (NULL))
+    {
+      check_own_socket ();
+      last_minute = time (NULL);
+    }
+#endif
+
+
+  /* Check whether the homedir is still available.  */
+  if (!shutdown_pending
+      && (!have_homedir_inotify || !reliable_homedir_inotify)
+      && stat (gnupg_homedir (), &statbuf) && errno == ENOENT)
+    {
+      shutdown_pending = 1;
+      log_info ("homedir has been removed - shutting down\n");
+    }
+}
+
+
+/* A global function which allows us to call the reload stuff from
+ * other places too.  This is only used when build for W32.  */
+void
+kbxd_sighup_action (void)
+{
+  log_info ("SIGHUP received - "
+            "re-reading configuration and flushing cache\n");
+
+  reread_configuration ();
+}
+
+
+/* A helper function to handle SIGUSR2.  */
+static void
+kbxd_sigusr2_action (void)
+{
+  if (opt.verbose)
+    log_info ("SIGUSR2 received - no action\n");
+  /* Nothing to do right now.  */
+}
+
+
+#ifndef HAVE_W32_SYSTEM
+/* The signal handler for this program.  It is expected to be run in
+ * its own thread and not in the context of a signal handler.  */
+static void
+handle_signal (int signo)
+{
+  switch (signo)
+    {
+    case SIGHUP:
+      kbxd_sighup_action ();
+      break;
+
+    case SIGUSR1:
+      log_info ("SIGUSR1 received - printing internal information:\n");
+      /* Fixme: We need to see how to integrate pth dumping into our
+         logging system.  */
+      /* pth_ctrl (PTH_CTRL_DUMPSTATE, log_get_stream ()); */
+      break;
+
+    case SIGUSR2:
+      kbxd_sigusr2_action ();
+      break;
+
+    case SIGTERM:
+      if (!shutdown_pending)
+        log_info ("SIGTERM received - shutting down ...\n");
+      else
+        log_info ("SIGTERM received - still %i open connections\n",
+                 active_connections);
+      shutdown_pending++;
+      if (shutdown_pending > 2)
+        {
+          log_info ("shutdown forced\n");
+          log_info ("%s %s stopped\n", strusage(11), strusage(13) );
+          cleanup ();
+          kbxd_exit (0);
+       }
+      break;
+
+    case SIGINT:
+      log_info ("SIGINT received - immediate shutdown\n");
+      log_info( "%s %s stopped\n", strusage(11), strusage(13));
+      cleanup ();
+      kbxd_exit (0);
+      break;
+
+    default:
+      log_info ("signal %d received - no action defined\n", signo);
+    }
+}
+#endif
+
+/* Check the nonce on a new connection.  This is a NOP unless we
+   are using our Unix domain socket emulation under Windows.  */
+static int
+check_nonce (ctrl_t ctrl, assuan_sock_nonce_t *nonce)
+{
+  if (assuan_sock_check_nonce (ctrl->thread_startup.fd, nonce))
+    {
+      log_info (_("error reading nonce on fd %d: %s\n"),
+                FD2INT(ctrl->thread_startup.fd), strerror (errno));
+      assuan_sock_close (ctrl->thread_startup.fd);
+      xfree (ctrl);
+      return -1;
+    }
+  else
+    return 0;
+}
+
+
+static void *
+do_start_connection_thread (ctrl_t ctrl)
+{
+  static unsigned int last_session_id;
+  unsigned int session_id;
+
+  active_connections++;
+  kbxd_init_default_ctrl (ctrl);
+  if (opt.verbose && !DBG_IPC)
+    log_info (_("handler 0x%lx for fd %d started\n"),
+              (unsigned long) npth_self(), FD2INT(ctrl->thread_startup.fd));
+
+  session_id = ++last_session_id;
+  if (!session_id)
+    session_id = ++last_session_id;
+  kbxd_start_command_handler (ctrl, ctrl->thread_startup.fd, session_id);
+  if (opt.verbose && !DBG_IPC)
+    log_info (_("handler 0x%lx for fd %d terminated\n"),
+              (unsigned long) npth_self(), FD2INT(ctrl->thread_startup.fd));
+
+  kbxd_deinit_default_ctrl (ctrl);
+  xfree (ctrl);
+  active_connections--;
+  return NULL;
+}
+
+
+/* This is the standard connection thread's main function.  */
+static void *
+start_connection_thread (void *arg)
+{
+  ctrl_t ctrl = arg;
+
+  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);
+}
+
+
+/* Connection handler loop.  Wait for connection requests and spawn a
+ * thread after accepting a connection.  */
+static void
+handle_connections (gnupg_fd_t listen_fd)
+{
+  gpg_error_t err;
+  npth_attr_t tattr;
+  struct sockaddr_un paddr;
+  socklen_t plen;
+  fd_set fdset, read_fdset;
+  int ret;
+  gnupg_fd_t fd;
+  int nfd;
+  int saved_errno;
+  struct timespec abstime;
+  struct timespec curtime;
+  struct timespec timeout;
+#ifdef HAVE_W32_SYSTEM
+  HANDLE events[2];
+  unsigned int events_set;
+#endif
+  int sock_inotify_fd = -1;
+  int home_inotify_fd = -1;
+  struct {
+    const char *name;
+    void *(*func) (void *arg);
+    gnupg_fd_t l_fd;
+  } listentbl[] = {
+    { "std",     start_connection_thread },
+  };
+
+
+  ret = npth_attr_init(&tattr);
+  if (ret)
+    log_fatal ("error allocating thread attributes: %s\n", strerror (ret));
+  npth_attr_setdetachstate (&tattr, NPTH_CREATE_DETACHED);
+
+#ifndef HAVE_W32_SYSTEM
+  npth_sigev_init ();
+  npth_sigev_add (SIGHUP);
+  npth_sigev_add (SIGUSR1);
+  npth_sigev_add (SIGUSR2);
+  npth_sigev_add (SIGINT);
+  npth_sigev_add (SIGTERM);
+  npth_sigev_fini ();
+#else
+# ifdef HAVE_W32CE_SYSTEM
+  /* Use a dummy event. */
+  sigs = 0;
+  ev = pth_event (PTH_EVENT_SIGS, &sigs, &signo);
+# else
+  events[0] = INVALID_HANDLE_VALUE;
+# endif
+#endif
+
+  if (disable_check_own_socket)
+    sock_inotify_fd = -1;
+  else if ((err = gnupg_inotify_watch_socket (&sock_inotify_fd, socket_name)))
+    {
+      if (gpg_err_code (err) != GPG_ERR_NOT_SUPPORTED)
+        log_info ("error enabling daemon termination by socket removal: %s\n",
+                  gpg_strerror (err));
+    }
+
+  if (disable_check_own_socket)
+    home_inotify_fd = -1;
+  else if ((err = gnupg_inotify_watch_delete_self (&home_inotify_fd,
+                                                   gnupg_homedir ())))
+    {
+      if (gpg_err_code (err) != GPG_ERR_NOT_SUPPORTED)
+        log_info ("error enabling daemon termination by homedir removal: %s\n",
+                  gpg_strerror (err));
+    }
+  else
+    have_homedir_inotify = 1;
+
+  FD_ZERO (&fdset);
+  FD_SET (FD2INT (listen_fd), &fdset);
+  nfd = FD2INT (listen_fd);
+  if (sock_inotify_fd != -1)
+    {
+      FD_SET (sock_inotify_fd, &fdset);
+      if (sock_inotify_fd > nfd)
+        nfd = sock_inotify_fd;
+    }
+  if (home_inotify_fd != -1)
+    {
+      FD_SET (home_inotify_fd, &fdset);
+      if (home_inotify_fd > nfd)
+        nfd = home_inotify_fd;
+    }
+
+  listentbl[0].l_fd = listen_fd;
+
+  npth_clock_gettime (&abstime);
+  abstime.tv_sec += TIMERTICK_INTERVAL;
+
+  for (;;)
+    {
+      /* Shutdown test.  */
+      if (shutdown_pending)
+        {
+          if (!active_connections)
+            break; /* ready */
+
+          /* Do not accept new connections but keep on running the
+           * 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 keyboxd instance - which is unlikely the
+           * intention of a shutdown. */
+          FD_ZERO (&fdset);
+          nfd = -1;
+          if (sock_inotify_fd != -1)
+            {
+              FD_SET (sock_inotify_fd, &fdset);
+              nfd = sock_inotify_fd;
+            }
+          if (home_inotify_fd != -1)
+            {
+              FD_SET (home_inotify_fd, &fdset);
+              if (home_inotify_fd > nfd)
+                nfd = home_inotify_fd;
+            }
+       }
+
+      read_fdset = fdset;
+
+      npth_clock_gettime (&curtime);
+      if (!(npth_timercmp (&curtime, &abstime, <)))
+       {
+         /* Timeout.  */
+         handle_tick ();
+         npth_clock_gettime (&abstime);
+         abstime.tv_sec += TIMERTICK_INTERVAL;
+       }
+      npth_timersub (&abstime, &curtime, &timeout);
+
+#ifndef HAVE_W32_SYSTEM
+      ret = npth_pselect (nfd+1, &read_fdset, NULL, NULL, &timeout,
+                          npth_sigev_sigmask ());
+      saved_errno = errno;
+
+      {
+        int signo;
+        while (npth_sigev_get_pending (&signo))
+          handle_signal (signo);
+      }
+#else
+      ret = npth_eselect (nfd+1, &read_fdset, NULL, NULL, &timeout,
+                          events, &events_set);
+      saved_errno = errno;
+
+      /* This is valid even if npth_eselect returns an error.  */
+      if ((events_set & 1))
+       kbxd_sigusr2_action ();
+#endif
+
+      if (ret == -1 && saved_errno != EINTR)
+       {
+          log_error (_("npth_pselect failed: %s - waiting 1s\n"),
+                     strerror (saved_errno));
+          npth_sleep (1);
+          continue;
+       }
+      if (ret <= 0)
+        {
+          /* Interrupt or timeout.  Will be handled when calculating the
+           * next timeout.  */
+          continue;
+        }
+
+      /* The inotify fds are set even when a shutdown is pending (see
+       * above).  So we must handle them in any case.  To avoid that
+       * they trigger a second time we close them immediately.  */
+      if (sock_inotify_fd != -1
+          && FD_ISSET (sock_inotify_fd, &read_fdset)
+          && gnupg_inotify_has_name (sock_inotify_fd, KEYBOXD_SOCK_NAME))
+        {
+          shutdown_pending = 1;
+          close (sock_inotify_fd);
+          sock_inotify_fd = -1;
+          log_info ("socket file has been removed - shutting down\n");
+        }
+
+      if (home_inotify_fd != -1
+          && FD_ISSET (home_inotify_fd, &read_fdset))
+        {
+          shutdown_pending = 1;
+          close (home_inotify_fd);
+          home_inotify_fd = -1;
+          log_info ("homedir has been removed - shutting down\n");
+        }
+
+      if (!shutdown_pending)
+        {
+          int idx;
+          ctrl_t ctrl;
+          npth_t thread;
+
+          for (idx=0; idx < DIM(listentbl); idx++)
+            {
+              if (listentbl[idx].l_fd == GNUPG_INVALID_FD)
+                continue;
+              if (!FD_ISSET (FD2INT (listentbl[idx].l_fd), &read_fdset))
+                continue;
+
+              plen = sizeof paddr;
+              fd = INT2FD (npth_accept (FD2INT(listentbl[idx].l_fd),
+                                        (struct sockaddr *)&paddr, &plen));
+              if (fd == GNUPG_INVALID_FD)
+                {
+                  log_error ("accept failed for %s: %s\n",
+                             listentbl[idx].name, strerror (errno));
+                }
+              else if ( !(ctrl = xtrycalloc (1, sizeof *ctrl)))
+                {
+                  log_error ("error allocating connection data for %s: %s\n",
+                             listentbl[idx].name, strerror (errno) );
+                  assuan_sock_close (fd);
+                }
+              else
+                {
+                  ctrl->thread_startup.fd = fd;
+                  ret = npth_create (&thread, &tattr,
+                                     listentbl[idx].func, ctrl);
+                  if (ret)
+                    {
+                      log_error ("error spawning connection handler for %s:"
+                                 " %s\n", listentbl[idx].name, strerror (ret));
+                      assuan_sock_close (fd);
+                      xfree (ctrl);
+                    }
+                }
+            }
+        }
+    }
+
+  if (sock_inotify_fd != -1)
+    close (sock_inotify_fd);
+  if (home_inotify_fd != -1)
+    close (home_inotify_fd);
+  cleanup ();
+  log_info (_("%s %s stopped\n"), strusage(11), strusage(13));
+  npth_attr_destroy (&tattr);
+}
+
+
+
+/* Helper for check_own_socket.  */
+static gpg_error_t
+check_own_socket_pid_cb (void *opaque, const void *buffer, size_t length)
+{
+  membuf_t *mb = opaque;
+  put_membuf (mb, buffer, length);
+  return 0;
+}
+
+
+/* The thread running the actual check.  We need to run this in a
+ * separate thread so that check_own_thread can be called from the
+ * timer tick.  */
+static void *
+check_own_socket_thread (void *arg)
+{
+  int rc;
+  char *sockname = arg;
+  assuan_context_t ctx = NULL;
+  membuf_t mb;
+  char *buffer;
+
+  check_own_socket_running++;
+
+  rc = assuan_new (&ctx);
+  if (rc)
+    {
+      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)
+    {
+      log_error ("can't connect my own socket: %s\n", gpg_strerror (rc));
+      goto leave;
+    }
+
+  init_membuf (&mb, 100);
+  rc = assuan_transact (ctx, "GETINFO pid", check_own_socket_pid_cb, &mb,
+                        NULL, NULL, NULL, NULL);
+  put_membuf (&mb, "", 1);
+  buffer = get_membuf (&mb, NULL);
+  if (rc || !buffer)
+    {
+      log_error ("sending command \"%s\" to my own socket failed: %s\n",
+                 "GETINFO pid", gpg_strerror (rc));
+      rc = 1;
+    }
+  else if ( (pid_t)strtoul (buffer, NULL, 10) != getpid ())
+    {
+      log_error ("socket is now serviced by another server\n");
+      rc = 1;
+    }
+  else if (opt.verbose > 1)
+    log_error ("socket is still served by this server\n");
+
+  xfree (buffer);
+
+ leave:
+  xfree (sockname);
+  if (ctx)
+    assuan_release (ctx);
+  if (rc)
+    {
+      /* We may not remove the socket as it is now in use by another
+       * server. */
+      inhibit_socket_removal = 1;
+      shutdown_pending = 2;
+      log_info ("this process is useless - shutting down\n");
+    }
+  check_own_socket_running--;
+  return NULL;
+}
+
+
+/* Check whether we are still listening on our own socket.  In case
+ * another keyboxd process started after us has taken ownership of our
+ * socket, we would linger around without any real task.  Thus we
+ * better check once in a while whether we are really needed.  */
+static void
+check_own_socket (void)
+{
+  char *sockname;
+  npth_t thread;
+  npth_attr_t tattr;
+  int err;
+
+  if (disable_check_own_socket)
+    return;
+
+  if (check_own_socket_running || shutdown_pending)
+    return;  /* Still running or already shutting down.  */
+
+  sockname = make_filename_try (gnupg_socketdir (), KEYBOXD_SOCK_NAME, NULL);
+  if (!sockname)
+    return; /* Out of memory.  */
+
+  err = npth_attr_init (&tattr);
+  if (err)
+    return;
+  npth_attr_setdetachstate (&tattr, NPTH_CREATE_DETACHED);
+  err = npth_create (&thread, &tattr, check_own_socket_thread, sockname);
+  if (err)
+    log_error ("error spawning check_own_socket_thread: %s\n", strerror (err));
+  npth_attr_destroy (&tattr);
+}
+
+
+
+/* Figure out whether a keyboxd is available and running.  Prints an
+ * error if not.  If SILENT is true, no messages are printed.  Returns
+ * 0 if the agent is running. */
+static int
+check_for_running_kbxd (int silent)
+{
+  gpg_error_t err;
+  char *sockname;
+  assuan_context_t ctx = NULL;
+
+  sockname = make_filename_try (gnupg_socketdir (), KEYBOXD_SOCK_NAME, NULL);
+  if (!sockname)
+    return gpg_error_from_syserror ();
+
+  err = assuan_new (&ctx);
+  if (!err)
+    err = assuan_socket_connect (ctx, sockname, (pid_t)(-1), 0);
+  xfree (sockname);
+  if (err)
+    {
+      if (!silent)
+        log_error (_("no keyboxd running in this session\n"));
+
+      if (ctx)
+       assuan_release (ctx);
+      return -1;
+    }
+
+  if (!opt.quiet && !silent)
+    log_info ("keyboxd running and available\n");
+
+  assuan_release (ctx);
+  return 0;
+}
diff --git a/kbx/keyboxd.h b/kbx/keyboxd.h
new file mode 100644 (file)
index 0000000..7719061
--- /dev/null
@@ -0,0 +1,137 @@
+/* keyboxd.h - Global definitions for keyboxd
+ * Copyright (C) 2018 Werner Koch
+ *
+ * This file is part of GnuPG.
+ *
+ * GnuPG is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * GnuPG is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * 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 <https://www.gnu.org/licenses/>.
+ */
+
+#ifndef KEYBOXD_H
+#define KEYBOXD_H
+
+#ifdef GPG_ERR_SOURCE_DEFAULT
+#error GPG_ERR_SOURCE_DEFAULT already defined
+#endif
+#define GPG_ERR_SOURCE_DEFAULT  GPG_ERR_SOURCE_KEYBOX
+#include <gpg-error.h>
+
+#include <gcrypt.h>
+#include "../common/util.h"
+#include "../common/membuf.h"
+#include "../common/sysutils.h" /* (gnupg_fd_t) */
+
+
+/* A large struct name "opt" to keep global flags */
+struct
+{
+  unsigned int debug;  /* Debug flags (DBG_foo_VALUE) */
+  int verbose;         /* Verbosity level */
+  int quiet;           /* Be as quiet as possible */
+  int dry_run;         /* Don't change any persistent data */
+  int batch;           /* Batch mode */
+
+  /* True if we are running detached from the tty. */
+  int running_detached;
+
+} opt;
+
+
+/* Bit values for the --debug option.  */
+#define DBG_MPI_VALUE    2     /* debug mpi details */
+#define DBG_CRYPTO_VALUE  4    /* debug low level crypto */
+#define DBG_MEMORY_VALUE  32   /* debug memory allocation stuff */
+#define DBG_CACHE_VALUE   64   /* debug the caching */
+#define DBG_MEMSTAT_VALUE 128  /* show memory statistics */
+#define DBG_HASHING_VALUE 512  /* debug hashing operations */
+#define DBG_IPC_VALUE     1024  /* Enable Assuan debugging.  */
+
+/* Test macros for the debug option.  */
+#define DBG_CRYPTO  (opt.debug & DBG_CRYPTO_VALUE)
+#define DBG_MEMORY  (opt.debug & DBG_MEMORY_VALUE)
+#define DBG_CACHE   (opt.debug & DBG_CACHE_VALUE)
+#define DBG_HASHING (opt.debug & DBG_HASHING_VALUE)
+#define DBG_IPC     (opt.debug & DBG_IPC_VALUE)
+
+/* Forward reference for local definitions in command.c.  */
+struct server_local_s;
+
+#if SIZEOF_UNSIGNED_LONG == 8
+# define SERVER_CONTROL_MAGIC 0x6b6579626f786420
+#else
+# define SERVER_CONTROL_MAGIC 0x6b627864
+#endif
+
+/* Collection of data per session (aka connection). */
+struct server_control_s
+{
+  unsigned long magic;/* Always has SERVER_CONTROL_MAGIC.  */
+  int refcount;       /* Count additional references to this object.  */
+
+  /* Private data used to fire up the connection thread.  We use this
+   * structure do avoid an extra allocation for only a few bytes while
+   * spawning a new connection thread.  */
+  struct {
+    gnupg_fd_t fd;
+  } thread_startup;
+
+  /* Private data of the server (kbxserver.c). */
+  struct server_local_s *server_local;
+
+  /* Environment settings for the connection.  */
+  char *lc_messages;
+
+  /* Miscellaneous info on the connection.  */
+  unsigned long client_pid;
+  int client_uid;
+
+};
+
+
+/* This is a special version of the usual _() gettext macro.  It
+ * assumes a server connection control variable with the name "ctrl"
+ * and uses that to translate a string according to the locale set for
+ * the connection.  The macro LunderscoreIMPL is used by i18n to
+ * actually define the inline function when needed.  */
+#if defined (ENABLE_NLS) || defined (USE_SIMPLE_GETTEXT)
+#define L_(a) keyboxd_Lunderscore (ctrl, (a))
+#define LunderscorePROTO                                            \
+  static inline const char *keyboxd_Lunderscore (ctrl_t ctrl,       \
+                                                 const char *string)  \
+    GNUPG_GCC_ATTR_FORMAT_ARG(2);
+#define LunderscoreIMPL                                         \
+  static inline const char *                                    \
+  keyboxd_Lunderscore (ctrl_t ctrl, const char *string)         \
+  {                                                             \
+    return ctrl? i18n_localegettext (ctrl->lc_messages, string) \
+      /*     */: gettext (string);                              \
+  }
+#else
+#define L_(a) (a)
+#endif
+
+
+/*-- keyboxd.c --*/
+void kbxd_exit (int rc) GPGRT_ATTR_NORETURN;
+void kbxd_set_progress_cb (void (*cb)(ctrl_t ctrl, const char *what,
+                                      int printchar, int current, int total),
+                           ctrl_t ctrl);
+const char *get_kbxd_socket_name (void);
+int get_kbxd_active_connection_count (void);
+void kbxd_sighup_action (void);
+
+
+/*-- kbxserver.c --*/
+void kbxd_start_command_handler (ctrl_t, gnupg_fd_t, unsigned int);
+
+#endif /*KEYBOXD_H*/