Fix spelling.
[gnupg.git] / g13 / g13.c
index 5838b13..0553c85 100644 (file)
--- a/g13/g13.c
+++ b/g13/g13.c
@@ -14,7 +14,7 @@
  * GNU General Public License for more details.
  *
  * You should have received a copy of the GNU General Public License
- * along with this program; if not, see <http://www.gnu.org/licenses/>.
+ * along with this program; if not, see <https://www.gnu.org/licenses/>.
  */
 
 #include <config.h>
@@ -25,7 +25,7 @@
 #include <ctype.h>
 #include <unistd.h>
 #include <fcntl.h>
-#include <pth.h>
+#include <npth.h>
 
 #include "g13.h"
 
 #include "i18n.h"
 #include "sysutils.h"
 #include "gc-opt-flags.h"
+#include "asshelp.h"
+#include "../common/init.h"
 #include "keyblob.h"
 #include "server.h"
 #include "runner.h"
 #include "create.h"
 #include "mount.h"
+#include "suspend.h"
 #include "mountinfo.h"
+#include "backend.h"
+#include "call-syshelp.h"
 
 
 enum cmd_and_opt_values {
   aNull = 0,
   oQuiet       = 'q',
   oVerbose     = 'v',
+  oRecipient   = 'r',
 
   aGPGConfList  = 500,
   aGPGConfTest,
   aCreate,
   aMount,
   aUmount,
+  aSuspend,
+  aResume,
   aServer,
+  aFindDevice,
 
   oOptions,
   oDebug,
@@ -70,6 +79,7 @@ enum cmd_and_opt_values {
 
   oAgentProgram,
   oGpgProgram,
+  oType,
 
   oDisplay,
   oTTYname,
@@ -91,8 +101,6 @@ enum cmd_and_opt_values {
   oDryRun,
   oNoDetach,
 
-  oRecipient,
-
   oNoRandomSeedFile,
   oFakedSystemTime
  };
@@ -105,7 +113,10 @@ static ARGPARSE_OPTS opts[] = {
   ARGPARSE_c (aCreate, "create", N_("Create a new file system container")),
   ARGPARSE_c (aMount,  "mount",  N_("Mount a file system container") ),
   ARGPARSE_c (aUmount, "umount", N_("Unmount a file system container") ),
+  ARGPARSE_c (aSuspend, "suspend", N_("Suspend a file system container") ),
+  ARGPARSE_c (aResume,  "resume",  N_("Resume a file system container") ),
   ARGPARSE_c (aServer, "server", N_("Run in server mode")),
+  ARGPARSE_c (aFindDevice, "find-device", "@"),
 
   ARGPARSE_c (aGPGConfList, "gpgconf-list", "@"),
   ARGPARSE_c (aGPGConfTest, "gpgconf-test", "@"),
@@ -113,6 +124,7 @@ static ARGPARSE_OPTS opts[] = {
   ARGPARSE_group (301, N_("@\nOptions:\n ")),
 
   ARGPARSE_s_s (oRecipient, "recipient", N_("|USER-ID|encrypt for USER-ID")),
+  ARGPARSE_s_s (oType, "type", N_("|NAME|use container format NAME")),
 
   ARGPARSE_s_s (oOutput, "output", N_("|FILE|write output to FILE")),
   ARGPARSE_s_n (oVerbose, "verbose", N_("verbose")),
@@ -127,7 +139,7 @@ static ARGPARSE_OPTS opts[] = {
 
   ARGPARSE_s_s (oOptions, "options", N_("|FILE|read options from FILE")),
 
-  ARGPARSE_p_u (oDebug, "debug", "@"),
+  ARGPARSE_s_s (oDebug, "debug", "@"),
   ARGPARSE_s_s (oDebugLevel, "debug-level",
                 N_("|LEVEL|set the debugging level to LEVEL")),
   ARGPARSE_s_n (oDebugAll, "debug-all", "@"),
@@ -148,10 +160,10 @@ static ARGPARSE_OPTS opts[] = {
 
   /* Hidden options. */
   ARGPARSE_s_n (oNoVerbose, "no-verbose", "@"),
-  ARGPARSE_s_n (oNoSecmemWarn, "no-secmem-warning", "@"), 
+  ARGPARSE_s_n (oNoSecmemWarn, "no-secmem-warning", "@"),
   ARGPARSE_s_n (oNoGreeting, "no-greeting", "@"),
   ARGPARSE_s_n (oNoOptions, "no-options", "@"),
-  ARGPARSE_s_s (oHomedir, "homedir", "@"),   
+  ARGPARSE_s_s (oHomedir, "homedir", "@"),
   ARGPARSE_s_s (oAgentProgram, "agent-program", "@"),
   ARGPARSE_s_s (oGpgProgram, "gpg-program", "@"),
   ARGPARSE_s_s (oDisplay,    "display", "@"),
@@ -170,12 +182,20 @@ static ARGPARSE_OPTS opts[] = {
 };
 
 
-/* The timer tick interval used by the idle task.  */
-#define TIMERTICK_INTERVAL_SEC     (1)
+/* The list of supported debug flags.  */
+static struct debug_flags_s debug_flags [] =
+  {
+    { DBG_MOUNT_VALUE  , "mount"  },
+    { DBG_CRYPTO_VALUE , "crypto"  },
+    { DBG_MEMORY_VALUE , "memory"  },
+    { DBG_MEMSTAT_VALUE, "memstat" },
+    { DBG_IPC_VALUE    , "ipc"     },
+    { 0, NULL }
+  };
 
 
-/* Global variable to keep an error count. */
-int g13_errors_seen = 0;
+/* The timer tick interval used by the idle task.  */
+#define TIMERTICK_INTERVAL_SEC     (1)
 
 /* It is possible that we are currently running under setuid permissions.  */
 static int maybe_setuid = 1;
@@ -188,30 +208,25 @@ static unsigned int debug_value;
 static int shutdown_pending;
 
 /* The thread id of the idle task.  */
-static pth_t idle_task_tid;
+static npth_t idle_task_thread;
+
+
+/* The container type as specified on the command line.  */
+static int cmdline_conttype;
 
 
 \f
 static void set_cmd (enum cmd_and_opt_values *ret_cmd,
                      enum cmd_and_opt_values new_cmd );
 
-static void emergency_cleanup (void);
 static void start_idle_task (void);
 static void join_idle_task (void);
 
-
 \f
-/* Begin Pth wrapper functions. */
-ASSUAN_SYSTEM_PTH_IMPL;
-
-GCRY_THREAD_OPTION_PTH_IMPL;
-static int fixed_gcry_pth_init (void)
-{
-  return pth_self ()? 0 : (pth_init () == FALSE) ? errno : 0;
-}
-/* End Pth wrapper functions. */
-
+/* Begin NPth wrapper functions. */
+ASSUAN_SYSTEM_NPTH_IMPL;
 
+\f
 static const char *
 my_strusage( int level )
 {
@@ -219,23 +234,23 @@ my_strusage( int level )
 
   switch (level)
     {
-    case 11: p = "g13 (GnuPG)";
+    case 11: p = "@G13@ (@GNUPG@)";
       break;
     case 13: p = VERSION; break;
     case 17: p = PRINTABLE_OS_NAME; break;
     case 19: p = _("Please report bugs to <" PACKAGE_BUGREPORT ">.\n");
       break;
     case 1:
-    case 40: p = _("Usage: g13 [options] [files] (-h for help)");
+    case 40: p = _("Usage: @G13@ [options] [files] (-h for help)");
       break;
     case 41:
-      p = _("Syntax: g13 [options] [files]\n"
+      p = _("Syntax: @G13@ [options] [files]\n"
             "Create, mount or unmount an encrypted file system container\n");
       break;
 
     case 31: p = "\nHome: "; break;
-    case 32: p = opt.homedir; break;
-     
+    case 32: p = gnupg_homedir (); break;
+
     default: p = NULL; break;
     }
   return p;
@@ -245,7 +260,7 @@ my_strusage( int level )
 static void
 wrong_args (const char *text)
 {
-  fputs (_("usage: g13 [options] "), stderr);
+  fprintf (stderr, _("usage: %s [options] "), G13_NAME);
   fputs (text, stderr);
   putc ('\n', stderr);
   g13_exit (2);
@@ -259,21 +274,28 @@ wrong_args (const char *text)
 static void
 set_debug (void)
 {
+  int numok = (debug_level && digitp (debug_level));
+  int numlvl = numok? atoi (debug_level) : 0;
+
   if (!debug_level)
     ;
-  else if (!strcmp (debug_level, "none"))
+  else if (!strcmp (debug_level, "none") || (numok && numlvl < 1))
     opt.debug = 0;
-  else if (!strcmp (debug_level, "basic"))
-    opt.debug = DBG_ASSUAN_VALUE|DBG_MOUNT_VALUE;
-  else if (!strcmp (debug_level, "advanced"))
-    opt.debug = DBG_ASSUAN_VALUE|DBG_MOUNT_VALUE;
-  else if (!strcmp (debug_level, "expert"))
-    opt.debug = (DBG_ASSUAN_VALUE|DBG_MOUNT_VALUE|DBG_CRYPTO_VALUE);
-  else if (!strcmp (debug_level, "guru"))
-    opt.debug = ~0;
+  else if (!strcmp (debug_level, "basic") || (numok && numlvl <= 2))
+    opt.debug = DBG_IPC_VALUE|DBG_MOUNT_VALUE;
+  else if (!strcmp (debug_level, "advanced") || (numok && numlvl <= 5))
+    opt.debug = DBG_IPC_VALUE|DBG_MOUNT_VALUE;
+  else if (!strcmp (debug_level, "expert") || (numok && numlvl <= 8))
+    opt.debug = (DBG_IPC_VALUE|DBG_MOUNT_VALUE|DBG_CRYPTO_VALUE);
+  else if (!strcmp (debug_level, "guru") || numok)
+    {
+      opt.debug = ~0;
+      /* if (numok) */
+      /*   opt.debug &= ~(DBG_HASHING_VALUE); */
+    }
   else
     {
-      log_error (_("invalid debug-level `%s' given\n"), debug_level);
+      log_error (_("invalid debug-level '%s' given\n"), debug_level);
       g13_exit(2);
     }
 
@@ -287,8 +309,11 @@ set_debug (void)
   if (opt.debug & DBG_CRYPTO_VALUE )
     gcry_control (GCRYCTL_SET_DEBUG_FLAGS, 1);
   gcry_control (GCRYCTL_SET_VERBOSITY, (int)opt.verbose);
+
+  if (opt.debug)
+    parse_debug_flag (NULL, &opt.debug, debug_flags);
 }
+
 
 
 static void
@@ -298,7 +323,7 @@ set_cmd (enum cmd_and_opt_values *ret_cmd, enum cmd_and_opt_values new_cmd)
 
   if (!cmd || cmd == new_cmd)
     cmd = new_cmd;
-  else 
+  else
     {
       log_error (_("conflicting commands\n"));
       g13_exit (2);
@@ -308,38 +333,14 @@ set_cmd (enum cmd_and_opt_values *ret_cmd, enum cmd_and_opt_values new_cmd)
 }
 
 
-/* Helper to add recipients to a list. */
-static int
-add_encryption_key (ctrl_t ctrl, const char *name,
-                    void /*FIXME*/ *keylist, int is_cms)
-{
-  /* FIXME: Decide whether to add a CMS or OpenPGP key and then add
-     the key to a list.  */
-  /* int rc = foo_add_to_certlist (ctrl, name, 0, recplist, is_encrypt_to); */
-  /* if (rc) */
-  /*   { */
-  /*     if (recp_required) */
-  /*       { */
-  /*         log_error ("can't encrypt to `%s': %s\n", name, gpg_strerror (rc)); */
-  /*         gpgsm_status2 (ctrl, STATUS_INV_RECP, */
-  /*                        get_inv_recpsgnr_code (rc), name, NULL); */
-  /*       } */
-  /*     else */
-  /*       log_info (_("NOTE: won't be able to encrypt to `%s': %s\n"), */
-  /*                 name, gpg_strerror (rc)); */
-  /*   } */
-  return 0; /* Key is good.  */
-}
-
-
 int
 main ( int argc, char **argv)
 {
   ARGPARSE_ARGS pargs;
   int orig_argc;
   char **orig_argv;
-  gpg_error_t err;
-  const char *fname;
+  gpg_error_t err = 0;
+  /* const char *fname; */
   int may_coredump;
   FILE *configfp = NULL;
   char *configname = NULL;
@@ -350,51 +351,42 @@ main ( int argc, char **argv)
   char *logfile = NULL;
   int greeting = 0;
   int nogreeting = 0;
-  int debug_wait = 0;
+  /* int debug_wait = 0; */
   int use_random_seed = 1;
-  int nodetach = 0;
-  int nokeysetup = 0;
+  /* int nodetach = 0; */
+  /* int nokeysetup = 0; */
   enum cmd_and_opt_values cmd = 0;
   struct server_control_s ctrl;
   strlist_t recipients = NULL;
 
   /*mtrace();*/
 
-  gnupg_reopen_std ("g13");
+  early_system_init ();
+  gnupg_reopen_std (G13_NAME);
   set_strusage (my_strusage);
   gcry_control (GCRYCTL_SUSPEND_SECMEM_WARN);
 
-  log_set_prefix ("g13", 1);
+  log_set_prefix (G13_NAME, GPGRT_LOG_WITH_PREFIX);
 
   /* Make sure that our subsystems are ready.  */
   i18n_init ();
-  init_common_subsystems ();
-
-  /* Libgcrypt requires us to register the threading model first.
-     Note that this will also do the pth_init. */
-  gcry_threads_pth.init = fixed_gcry_pth_init;
-  err = gcry_control (GCRYCTL_SET_THREAD_CBS, &gcry_threads_pth);
-  if (err)
-    {
-      log_fatal ("can't register GNU Pth with Libgcrypt: %s\n",
-                 gpg_strerror (err));
-    }
+  init_common_subsystems (&argc, &argv);
 
-  /* Check that the Libgcrypt is suitable.  */
-  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) );
+  npth_init ();
 
   /* Take extra care of the random pool.  */
   gcry_control (GCRYCTL_USE_SECURE_RNDPOOL);
 
   may_coredump = disable_core_dumps ();
-  
-  gnupg_init_signals (0, emergency_cleanup);
-  
-  create_dotlock (NULL); /* Register locking cleanup.  */
 
-  opt.homedir = default_homedir ();
+  g13_init_signals ();
+
+  dotlock_create (NULL, 0); /* Register locking cleanup.  */
+
+  opt.session_env = session_env_new ();
+  if (!opt.session_env)
+    log_fatal ("error allocating session environment block: %s\n",
+               strerror (errno));
 
   /* First check whether we have a config file on the commandline.  */
   orig_argc = argc;
@@ -415,15 +407,15 @@ main ( int argc, char **argv)
       else if (pargs.r_opt == oNoOptions)
         default_config = 0; /* --no-options */
       else if (pargs.r_opt == oHomedir)
-        opt.homedir = pargs.r.ret_str;
+        gnupg_set_homedir (pargs.r.ret_str);
     }
 
   /* Initialize the secure memory. */
   gcry_control (GCRYCTL_INIT_SECMEM, 16384, 0);
   maybe_setuid = 0;
 
-  /* 
-     Now we are now working under our real uid 
+  /*
+     Now we are now working under our real uid
   */
 
   /* Setup malloc hooks. */
@@ -435,21 +427,22 @@ main ( int argc, char **argv)
     malloc_hooks.free = gcry_free;
     assuan_set_malloc_hooks (&malloc_hooks);
   }
-  
+
   /* Prepare libassuan.  */
-  assuan_set_assuan_log_prefix (log_get_prefix (NULL));
   assuan_set_gpg_err_source (GPG_ERR_SOURCE_DEFAULT);
-  assuan_set_system_hooks (ASSUAN_SYSTEM_PTH);
-
+  assuan_set_system_hooks (ASSUAN_SYSTEM_NPTH);
+  setup_libassuan_logging (&opt.debug, NULL);
 
   /* Setup a default control structure for command line mode.  */
   memset (&ctrl, 0, sizeof ctrl);
   g13_init_default_ctrl (&ctrl);
+  ctrl.no_server = 1;
+  ctrl.status_fd = -1; /* No status output. */
 
   /* Set the default option file */
   if (default_config )
-    configname = make_filename (opt.homedir, "g13.conf", NULL);
-  
+    configname = make_filename (gnupg_homedir (), G13_NAME".conf", NULL);
+
   argc        = orig_argc;
   argv        = orig_argv;
   pargs.argc  = &argc;
@@ -466,11 +459,11 @@ main ( int argc, char **argv)
           if (default_config)
             {
               if (parse_debug)
-                log_info (_("NOTE: no default option file `%s'\n"), configname);
+                log_info (_("Note: no default option file '%s'\n"), configname);
             }
-          else 
+          else
             {
-              log_error (_("option file `%s': %s\n"), 
+              log_error (_("option file '%s': %s\n"),
                          configname, strerror(errno));
               g13_exit(2);
             }
@@ -478,27 +471,29 @@ main ( int argc, char **argv)
           configname = NULL;
         }
       if (parse_debug && configname)
-        log_info (_("reading options from `%s'\n"), configname);
+        log_info (_("reading options from '%s'\n"), configname);
       default_config = 0;
     }
-  
-  while (!no_more_options 
+
+  while (!no_more_options
          && optfile_parse (configfp, configname, &configlineno, &pargs, opts))
     {
       switch (pargs.r_opt)
         {
-       case aGPGConfList: 
-       case aGPGConfTest: 
+       case aGPGConfList:
+       case aGPGConfTest:
           set_cmd (&cmd, pargs.r_opt);
           nogreeting = 1;
-          nokeysetup = 1;
+          /* nokeysetup = 1; */
           break;
 
         case aServer:
         case aMount:
         case aUmount:
-          nokeysetup = 1;
+        case aSuspend:
+        case aResume:
         case aCreate:
+        case aFindDevice:
           set_cmd (&cmd, pargs.r_opt);
           break;
 
@@ -520,15 +515,21 @@ main ( int argc, char **argv)
           break;
 
         case oLogFile: logfile = pargs.r.ret_str; break;
-        case oNoLogFile: logfile = NULL; break;          
+        case oNoLogFile: logfile = NULL; break;
 
-        case oNoDetach: nodetach = 1; break;
+        case oNoDetach: /*nodetach = 1; */break;
 
-        case oDebug: debug_value |= pargs.r.ret_ulong; break;
+        case oDebug:
+          if (parse_debug_flag (pargs.r.ret_str, &opt.debug, debug_flags))
+            {
+              pargs.r_opt = ARGPARSE_INVALID_ARG;
+              pargs.err = ARGPARSE_PRINT_ERROR;
+            }
+            break;
         case oDebugAll: debug_value = ~0; break;
         case oDebugNone: debug_value = 0; break;
         case oDebugLevel: debug_level = pargs.r.ret_str; break;
-        case oDebugWait: debug_wait = pargs.r.ret_int; break;
+        case oDebugWait: /*debug_wait = pargs.r.ret_int; */break;
         case oDebugAllowCoreDump:
           may_coredump = enable_core_dumps ();
           break;
@@ -547,7 +548,7 @@ main ( int argc, char **argv)
            }
           break;
 
-        case oHomedir: opt.homedir = pargs.r.ret_str; break;
+        case oHomedir: gnupg_set_homedir (pargs.r.ret_str); break;
 
         case oAgentProgram: opt.agent_program = pargs.r.ret_str;  break;
         case oGpgProgram: opt.gpg_program = pargs.r.ret_str;  break;
@@ -557,10 +558,10 @@ main ( int argc, char **argv)
         case oLCctype: opt.lc_ctype = xstrdup (pargs.r.ret_str); break;
         case oLCmessages: opt.lc_messages = xstrdup (pargs.r.ret_str); break;
         case oXauthority: opt.xauthority = xstrdup (pargs.r.ret_str); break;
-          
+
         case oFakedSystemTime:
           {
-            time_t faked_time = isotime2epoch (pargs.r.ret_str); 
+            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);
@@ -575,13 +576,36 @@ main ( int argc, char **argv)
           add_to_strlist (&recipients, pargs.r.ret_str);
           break;
 
+        case oType:
+          if (!strcmp (pargs.r.ret_str, "help"))
+            {
+              be_parse_conttype_name (NULL);
+              g13_exit (0);
+            }
+          cmdline_conttype = be_parse_conttype_name (pargs.r.ret_str);
+          if (!cmdline_conttype)
+            {
+              pargs.r_opt = ARGPARSE_INVALID_ARG;
+              pargs.err = ARGPARSE_PRINT_ERROR;
+            }
+          break;
 
-        default: 
-          pargs.err = configfp? ARGPARSE_PRINT_WARNING:ARGPARSE_PRINT_ERROR; 
+        default:
+          pargs.err = configfp? ARGPARSE_PRINT_WARNING:ARGPARSE_PRINT_ERROR;
           break;
        }
     }
 
+  /* XXX Construct GPG arguments.  */
+  {
+    strlist_t last;
+    last = append_to_strlist (&opt.gpg_arguments, "-z");
+    last = append_to_strlist (&last, "0");
+    last = append_to_strlist (&last, "--trust-model");
+    last = append_to_strlist (&last, "always");
+    (void) last;
+  }
+
   if (configfp)
     {
       fclose (configfp);
@@ -595,7 +619,8 @@ main ( int argc, char **argv)
   configname = NULL;
 
   if (!opt.config_filename)
-    opt.config_filename = make_filename (opt.homedir, "g13.conf", NULL);
+    opt.config_filename = make_filename (gnupg_homedir (),
+                                         G13_NAME".conf", NULL);
 
   if (log_get_errorcount(0))
     g13_exit(2);
@@ -603,10 +628,12 @@ main ( int argc, char **argv)
   /* Now that we have the options parsed we need to update the default
      control structure.  */
   g13_init_default_ctrl (&ctrl);
+  ctrl.recipients = recipients;
+  recipients = NULL;
 
   if (nogreeting)
     greeting = 0;
-  
+
   if (greeting)
     {
       fprintf (stderr, "%s %s; %s\n",
@@ -617,10 +644,21 @@ main ( int argc, char **argv)
   if (may_coredump && !opt.quiet)
     log_info (_("WARNING: program may create a core file!\n"));
 
+  /* Print a warning if an argument looks like an option.  */
+  if (!opt.quiet && !(pargs.flags & ARGPARSE_FLAG_STOP_SEEN))
+    {
+      int i;
+
+      for (i=0; i < argc; i++)
+        if (argv[i][0] == '-' && argv[i][1] == '-')
+          log_info (_("Note: '%s' is not considered an option\n"), argv[i]);
+    }
+
+
   if (logfile)
     {
       log_set_file (logfile);
-      log_set_prefix (NULL, 1|2|4);
+      log_set_prefix (NULL, GPGRT_LOG_WITH_PREFIX | GPGRT_LOG_WITH_TIME | GPGRT_LOG_WITH_PID);
     }
 
   if (gnupg_faked_time_p ())
@@ -639,47 +677,45 @@ main ( int argc, char **argv)
   /* Setup the debug flags for all subsystems.  */
   set_debug ();
 
-  /* Install a regular exit handler to make real sure that the secure
-     memory gets wiped out.  */
-  if (atexit (emergency_cleanup))
-    {
-      log_error ("atexit failed\n");
-      g13_exit (2);
-    }
+  /* Install emergency cleanup handler.  */
+  g13_install_emergency_cleanup ();
 
   /* Terminate if we found any error until now.  */
   if (log_get_errorcount(0))
     g13_exit (2);
-  
+
   /* Set the standard GnuPG random seed file.  */
-  if (use_random_seed) 
+  if (use_random_seed)
     {
-      char *p = make_filename (opt.homedir, "random_seed", NULL);
+      char *p = make_filename (gnupg_homedir (), "random_seed", NULL);
       gcry_control (GCRYCTL_SET_RANDOM_SEED_FILE, p);
       xfree(p);
     }
-  
+
   /* Store given filename into FNAME. */
-  fname = argc? *argv : NULL;
+  /* fname = argc? *argv : NULL; */
 
   /* Parse all given encryption keys.  This does a lookup of the keys
      and stops if any of the given keys was not found. */
+#if 0 /* Currently not implemented.  */
   if (!nokeysetup)
     {
       strlist_t sl;
       int failed = 0;
-      
-      for (sl = recipients; sl; sl = sl->next)
-        if (add_encryption_key (&ctrl, sl->d, NULL /* FIXME*/, 0))
+
+      for (sl = ctrl->recipients; sl; sl = sl->next)
+        if (check_encryption_key ())
           failed = 1;
       if (failed)
         g13_exit (1);
     }
-  
+#endif /*0*/
+
   /* Dispatch command.  */
+  err = 0;
   switch (cmd)
     {
-    case aGPGConfList: 
+    case aGPGConfList:
       { /* List options and default values in the GPG Conf format.  */
        char *config_filename_esc = percent_escape (opt.config_filename, NULL);
 
@@ -701,18 +737,35 @@ main ( int argc, char **argv)
     case aServer:
       {
         start_idle_task ();
+        ctrl.no_server = 0;
         err = g13_server (&ctrl);
         if (err)
           log_error ("server exited with error: %s <%s>\n",
                      gpg_strerror (err), gpg_strsource (err));
         else
-          shutdown_pending++;
+          g13_request_shutdown ();
+      }
+      break;
+
+    case aFindDevice:
+      {
+        char *blockdev;
+
+        if (argc != 1)
+          wrong_args ("--find-device name");
+
+        err = call_syshelp_find_device (&ctrl, argv[0], &blockdev);
+        if (err)
+          log_error ("error finding device '%s': %s <%s>\n",
+                     argv[0], gpg_strerror (err), gpg_strsource (err));
+        else
+          puts (blockdev);
       }
       break;
 
     case aCreate: /* Create a new container. */
       {
-        if (argc != 1) 
+        if (argc != 1)
           wrong_args ("--create filename");
         start_idle_task ();
         err = g13_create_container (&ctrl, argv[0]);
@@ -720,18 +773,53 @@ main ( int argc, char **argv)
           log_error ("error creating a new container: %s <%s>\n",
                      gpg_strerror (err), gpg_strsource (err));
         else
-          shutdown_pending++;
+          g13_request_shutdown ();
       }
       break;
 
     case aMount: /* Mount a container. */
       {
-        if (argc != 1 && argc != 2 ) 
+        if (argc != 1 && argc != 2 )
           wrong_args ("--mount filename [mountpoint]");
         start_idle_task ();
         err = g13_mount_container (&ctrl, argv[0], argc == 2?argv[1]:NULL);
         if (err)
-          log_error ("error mounting container `%s': %s <%s>\n",
+          log_error ("error mounting container '%s': %s <%s>\n",
+                     *argv, gpg_strerror (err), gpg_strsource (err));
+      }
+      break;
+
+    case aUmount: /* Unmount a mounted container.  */
+      {
+        if (argc != 1)
+          wrong_args ("--umount filename");
+        err = g13_umount_container (&ctrl, argv[0], NULL);
+        if (err)
+          log_error ("error unmounting container '%s': %s <%s>\n",
+                     *argv, gpg_strerror (err), gpg_strsource (err));
+      }
+      break;
+
+    case aSuspend: /* Suspend a container. */
+      {
+        /* Fixme: Should we add a suspend all container option?  */
+        if (argc != 1)
+          wrong_args ("--suspend filename");
+        err = g13_suspend_container (&ctrl, argv[0]);
+        if (err)
+          log_error ("error suspending container '%s': %s <%s>\n",
+                     *argv, gpg_strerror (err), gpg_strsource (err));
+      }
+      break;
+
+    case aResume: /* Resume a suspended container. */
+      {
+        /* Fixme: Should we add a resume all container option?  */
+        if (argc != 1)
+          wrong_args ("--resume filename");
+        err = g13_resume_container (&ctrl, argv[0]);
+        if (err)
+          log_error ("error resuming container '%s': %s <%s>\n",
                      *argv, gpg_strerror (err), gpg_strsource (err));
       }
       break;
@@ -741,6 +829,8 @@ main ( int argc, char **argv)
       break;
     }
 
+  g13_deinit_default_ctrl (&ctrl);
+
   if (!err)
     join_idle_task ();
 
@@ -750,41 +840,34 @@ main ( int argc, char **argv)
 }
 
 
-/* Note: This function is used by signal handlers!. */
-static void
-emergency_cleanup (void)
+/* Store defaults into the per-connection CTRL object.  */
+void
+g13_init_default_ctrl (ctrl_t ctrl)
 {
-  gcry_control (GCRYCTL_TERM_SECMEM );
+  ctrl->conttype = cmdline_conttype? cmdline_conttype : CONTTYPE_ENCFS;
 }
 
 
+/* Release remaining resources allocated in the CTRL object.  */
 void
-g13_exit (int rc)
+g13_deinit_default_ctrl (ctrl_t ctrl)
 {
-  gcry_control (GCRYCTL_UPDATE_RANDOM_SEED_FILE);
-  if (opt.debug & DBG_MEMSTAT_VALUE)
-    {
-      gcry_control( GCRYCTL_DUMP_MEMORY_STATS );
-      gcry_control( GCRYCTL_DUMP_RANDOM_STATS );
-    }
-  if (opt.debug)
-    gcry_control (GCRYCTL_DUMP_SECMEM_STATS );
-  emergency_cleanup ();
-  rc = rc? rc : log_get_errorcount(0)? 2 : g13_errors_seen? 1 : 0;
-  exit (rc);
+  call_syshelp_release (ctrl);
+  FREE_STRLIST (ctrl->recipients);
 }
 
 
-/* Store defaults into the per-connection CTRL object.  */
+/* Request a shutdown.  This can be used when the process should
+ * finish instead of running the idle task.  */
 void
-g13_init_default_ctrl (struct server_control_s *ctrl)
+g13_request_shutdown (void)
 {
-  ctrl->conttype = CONTTYPE_ENCFS;
+  shutdown_pending++;
 }
 
 
 /* This function is called for each signal we catch.  It is run in the
-   main context or the one of a Pth thread and thus it is not
+   main context or the one of a NPth thread and thus it is not
    restricted in what it may do.  */
 static void
 handle_signal (int signo)
@@ -796,10 +879,12 @@ handle_signal (int signo)
       log_info ("SIGHUP received - re-reading configuration\n");
       /* Fixme:  Not yet implemented.  */
       break;
-      
+
     case SIGUSR1:
       log_info ("SIGUSR1 received - printing internal information:\n");
-      pth_ctrl (PTH_CTRL_DUMPSTATE, log_get_stream ());
+      /* Fixme: We need to see how to integrate pth dumping into our
+         logging system.  */
+      /* pth_ctrl (PTH_CTRL_DUMPSTATE, log_get_stream ()); */
       mountinfo_dump_all ();
       break;
 
@@ -821,14 +906,14 @@ handle_signal (int signo)
           g13_exit (0);
        }
       break;
-      
+
     case SIGINT:
       log_info ("SIGINT received - immediate shutdown\n");
       log_info( "%s %s stopped\n", strusage(11), strusage(13));
       g13_exit (0);
       break;
 #endif /*!HAVE_W32_SYSTEM*/
-      
+
     default:
       log_info ("signal %d received - no action defined\n", signo);
     }
@@ -849,29 +934,28 @@ handle_tick (void)
 static void *
 idle_task (void *dummy_arg)
 {
-  sigset_t sigs;       /* The set of signals we want to catch.  */
-  pth_event_t ev;      /* The main event to catch signals.  */
-  pth_event_t time_ev; /* The time event.  */
   int signo;           /* The number of a raised signal is stored here.  */
+  int saved_errno;
+  struct timespec abstime;
+  struct timespec curtime;
+  struct timespec timeout;
+  int ret;
 
   (void)dummy_arg;
 
   /* Create the event to catch the signals. */
 #ifndef HAVE_W32_SYSTEM
-  sigemptyset (&sigs );
-  sigaddset (&sigs, SIGHUP);
-  sigaddset (&sigs, SIGUSR1);
-  sigaddset (&sigs, SIGUSR2);
-  sigaddset (&sigs, SIGINT);
-  sigaddset (&sigs, SIGTERM);
-  pth_sigmask (SIG_UNBLOCK, &sigs, NULL);
-#else
-  sigs = 0;
+  npth_sigev_init ();
+  npth_sigev_add (SIGHUP);
+  npth_sigev_add (SIGUSR1);
+  npth_sigev_add (SIGUSR2);
+  npth_sigev_add (SIGINT);
+  npth_sigev_add (SIGTERM);
+  npth_sigev_fini ();
 #endif
-  ev = pth_event (PTH_EVENT_SIGS, &sigs, &signo);
 
-  /* The time event neds to computed n tghe fly.  */
-  time_ev = NULL;
+  npth_clock_gettime (&abstime);
+  abstime.tv_sec += TIMERTICK_INTERVAL_SEC;
 
   for (;;)
     {
@@ -884,41 +968,45 @@ idle_task (void *dummy_arg)
             break; /* ready */
        }
 
-      /* Create a timeout event if needed.  To help with power saving
-         we syncronize the ticks to the next full second.  */
-      if (!time_ev)
-        {
-          pth_time_t nexttick;
+      npth_clock_gettime (&curtime);
+      if (!(npth_timercmp (&curtime, &abstime, <)))
+       {
+         /* Timeout.  */
+         handle_tick ();
+         npth_clock_gettime (&abstime);
+         abstime.tv_sec += TIMERTICK_INTERVAL_SEC;
+       }
+      npth_timersub (&abstime, &curtime, &timeout);
 
-          nexttick = pth_timeout (TIMERTICK_INTERVAL_SEC, 0);
-          if (nexttick.tv_usec > 10)  /* Use a 10 usec threshhold.  */
-            {
-              nexttick.tv_sec++;
-              nexttick.tv_usec = 0;
-            }
-          time_ev = pth_event (PTH_EVENT_TIME, nexttick);
-        }
+#ifndef HAVE_W32_SYSTEM
+      ret = npth_pselect (0, NULL, NULL, NULL, &timeout, npth_sigev_sigmask());
+      saved_errno = errno;
 
-      pth_event_concat (ev, time_ev, NULL);
-      pth_wait (ev);
-      pth_event_isolate (time_ev);
+      while (npth_sigev_get_pending(&signo))
+       handle_signal (signo);
+#else
+      ret = npth_eselect (0, NULL, NULL, NULL, &timeout, NULL, NULL);
+      saved_errno = errno;
+#endif
 
-      if (pth_event_occurred (ev))
-        {
-          handle_signal (signo);
-        }
+      if (ret == -1 && saved_errno != EINTR)
+       {
+          log_error (_("npth_pselect failed: %s - waiting 1s\n"),
+                     strerror (saved_errno));
+          npth_sleep (1);
+          continue;
+       }
 
-      if (time_ev && pth_event_occurred (time_ev))
+      if (ret <= 0)
         {
-          pth_event_free (time_ev, PTH_FREE_ALL);
-          time_ev = NULL;
-          handle_tick ();
+          /* Interrupt or timeout.  Will be handled when calculating the
+             next timeout.  */
+          continue;
         }
+
+      /* Here one would add processing of file descriptors.  */
     }
 
-  pth_event_free (ev, PTH_FREE_ALL);
-  if (time_ev)
-    pth_event_free (time_ev, PTH_FREE_ALL);
   log_info (_("%s %s stopped\n"), strusage(11), strusage(13));
   return NULL;
 }
@@ -928,23 +1016,36 @@ idle_task (void *dummy_arg)
 static void
 start_idle_task (void)
 {
-  pth_attr_t tattr;
-  pth_t tid;
-  
-  tattr = pth_attr_new ();
-  pth_attr_set (tattr, PTH_ATTR_JOINABLE, 1);
-  pth_attr_set (tattr, PTH_ATTR_STACK_SIZE, 64*1024);
-  pth_attr_set (tattr, PTH_ATTR_NAME, "idle-task");
-  
-  tid = pth_spawn (tattr, idle_task, NULL);
-  if (!tid)
+  npth_attr_t tattr;
+  npth_t thread;
+  sigset_t sigs;       /* The set of signals we want to catch.  */
+  int err;
+
+#ifndef HAVE_W32_SYSTEM
+  /* These signals should always go to the idle task, so they need to
+     be blocked everywhere else.  We assume start_idle_task is called
+     from the main thread before any other threads are created.  */
+  sigemptyset (&sigs);
+  sigaddset (&sigs, SIGHUP);
+  sigaddset (&sigs, SIGUSR1);
+  sigaddset (&sigs, SIGUSR2);
+  sigaddset (&sigs, SIGINT);
+  sigaddset (&sigs, SIGTERM);
+  npth_sigmask (SIG_BLOCK, &sigs, NULL);
+#endif
+
+  npth_attr_init (&tattr);
+  npth_attr_setdetachstate (&tattr, NPTH_CREATE_JOINABLE);
+
+  err = npth_create (&thread, &tattr, idle_task, NULL);
+  if (err)
     {
-      log_fatal ("error starting idle task: %s\n", 
-                 gpg_strerror (gpg_error_from_syserror ()));
+      log_fatal ("error starting idle task: %s\n", strerror (err));
       return; /*NOTREACHED*/
     }
-  idle_task_tid = tid;
-  pth_attr_destroy (tattr);
+  npth_setname_np (thread, "idle-task");
+  idle_task_thread = thread;
+  npth_attr_destroy (&tattr);
 }
 
 
@@ -952,11 +1053,15 @@ start_idle_task (void)
 static void
 join_idle_task (void)
 {
-  if (idle_task_tid)
+  int err;
+
+  /* FIXME: This assumes that a valid pthread_t is non-null.  That is
+     not guaranteed.  */
+  if (idle_task_thread)
     {
-      if (!pth_join (idle_task_tid, NULL))
+      err = npth_join (idle_task_thread, NULL);
+      if (err)
         log_error ("waiting for idle task thread failed: %s\n",
-                   gpg_strerror (gpg_error_from_syserror ()));
+                   strerror (err));
     }
 }
-