Mark component descriptions for translation.
[gnupg.git] / g13 / g13.c
index d6a3167..180b0d9 100644 (file)
--- a/g13/g13.c
+++ b/g13/g13.c
 #include "i18n.h"
 #include "sysutils.h"
 #include "gc-opt-flags.h"
-#include "create.h"
+#include "asshelp.h"
 #include "keyblob.h"
+#include "server.h"
+#include "runner.h"
+#include "create.h"
+#include "mount.h"
+#include "mountinfo.h"
 
 
 enum cmd_and_opt_values {
   aNull = 0,
   oQuiet       = 'q',
   oVerbose     = 'v',
+  oRecipient   = 'r',
 
   aGPGConfList  = 500,
   aGPGConfTest,
   aCreate,
   aMount,
   aUmount,
+  aServer,
 
   oOptions,
   oDebug,
@@ -84,8 +91,7 @@ enum cmd_and_opt_values {
   oHomedir,
   oWithColons,
   oDryRun,
-
-  oRecipient,
+  oNoDetach,
 
   oNoRandomSeedFile,
   oFakedSystemTime
@@ -99,6 +105,7 @@ 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 (aServer, "server", N_("Run in server mode")),
 
   ARGPARSE_c (aGPGConfList, "gpgconf-list", "@"),
   ARGPARSE_c (aGPGConfTest, "gpgconf-test", "@"),
@@ -111,12 +118,11 @@ static ARGPARSE_OPTS opts[] = {
   ARGPARSE_s_n (oVerbose, "verbose", N_("verbose")),
   ARGPARSE_s_n (oQuiet,        "quiet",  N_("be somewhat more quiet")),
   ARGPARSE_s_n (oNoTTY, "no-tty", N_("don't use the terminal at all")),
+  ARGPARSE_s_n (oNoDetach, "no-detach", N_("do not detach from the console")),
   ARGPARSE_s_s (oLogFile, "log-file",  N_("|FILE|write log output to FILE")),
   ARGPARSE_s_n (oNoLogFile, "no-log-file", "@"),
   ARGPARSE_s_i (oLoggerFD, "logger-fd", "@"),
 
-  ARGPARSE_s_s (oAuditLog, "audit-log",
-                N_("|FILE|write an audit log to FILE")),
   ARGPARSE_s_n (oDryRun, "dry-run", N_("do not make any changes")),
 
   ARGPARSE_s_s (oOptions, "options", N_("|FILE|read options from FILE")),
@@ -142,10 +148,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", "@"),
@@ -164,6 +170,10 @@ static ARGPARSE_OPTS opts[] = {
 };
 
 
+/* The timer tick interval used by the idle task.  */
+#define TIMERTICK_INTERVAL_SEC     (1)
+
+
 /* Global variable to keep an error count. */
 int g13_errors_seen = 0;
 
@@ -174,12 +184,26 @@ static int maybe_setuid = 1;
 static const char *debug_level;
 static unsigned int debug_value;
 
+/* Flag to indicate that a shutdown was requested.  */
+static int shutdown_pending;
+
+/* The thread id of the idle task.  */
+static pth_t idle_task_tid;
+
+
+\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)
 {
@@ -211,7 +235,7 @@ my_strusage( int level )
 
     case 31: p = "\nHome: "; break;
     case 32: p = opt.homedir; break;
-     
+
     default: p = NULL; break;
     }
   return p;
@@ -235,18 +259,25 @@ 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"))
+  else if (!strcmp (debug_level, "basic") || (numok && numlvl <= 2))
     opt.debug = DBG_ASSUAN_VALUE|DBG_MOUNT_VALUE;
-  else if (!strcmp (debug_level, "advanced"))
+  else if (!strcmp (debug_level, "advanced") || (numok && numlvl <= 5))
     opt.debug = DBG_ASSUAN_VALUE|DBG_MOUNT_VALUE;
-  else if (!strcmp (debug_level, "expert"))
+  else if (!strcmp (debug_level, "expert") || (numok && numlvl <= 8))
     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, "guru") || numok)
+    {
+      opt.debug = ~0;
+      /* if (numok) */
+      /*   opt.debug &= ~(DBG_HASHING_VALUE); */
+    }
   else
     {
       log_error (_("invalid debug-level `%s' given\n"), debug_level);
@@ -263,8 +294,16 @@ 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)
+    log_info ("enabled debug flags:%s%s%s%s%s\n",
+              (opt.debug & DBG_MOUNT_VALUE  )? " mount":"",
+              (opt.debug & DBG_CRYPTO_VALUE )? " crypto":"",
+              (opt.debug & DBG_MEMORY_VALUE )? " memory":"",
+              (opt.debug & DBG_MEMSTAT_VALUE)? " memstat":"",
+              (opt.debug & DBG_ASSUAN_VALUE )? " assuan":"");
 }
+
 
 
 static void
@@ -274,7 +313,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);
@@ -284,30 +323,6 @@ 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)
 {
@@ -324,15 +339,14 @@ main ( int argc, char **argv)
   int no_more_options = 0;
   int default_config =1;
   char *logfile = NULL;
-  char *auditlog = NULL;
   int greeting = 0;
   int nogreeting = 0;
   int debug_wait = 0;
   int use_random_seed = 1;
+  int nodetach = 0;
   int nokeysetup = 0;
   enum cmd_and_opt_values cmd = 0;
   struct server_control_s ctrl;
-  estream_t auditfp = NULL;
   strlist_t recipients = NULL;
 
   /*mtrace();*/
@@ -345,7 +359,7 @@ main ( int argc, char **argv)
 
   /* Make sure that our subsystems are ready.  */
   i18n_init ();
-  init_common_subsystems ();
+  init_common_subsystems (&argc, &argv);
 
   /* Libgcrypt requires us to register the threading model first.
      Note that this will also do the pth_init. */
@@ -359,18 +373,23 @@ main ( int argc, char **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", 
+    log_fatal (_("%s is too old (need %s, have %s)\n"), "libgcrypt",
                NEED_LIBGCRYPT_VERSION, gcry_check_version (NULL) );
 
   /* 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.session_env = session_env_new ();
+  if (!opt.session_env)
+    log_fatal ("error allocating session environment block: %s\n",
+               strerror (errno));
+
   opt.homedir = default_homedir ();
 
   /* First check whether we have a config file on the commandline.  */
@@ -399,8 +418,8 @@ main ( int argc, char **argv)
   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. */
@@ -412,20 +431,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);
+  setup_libassuan_logging (&opt.debug);
 
   /* 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);
-  
+
   argc        = orig_argc;
   argv        = orig_argv;
   pargs.argc  = &argc;
@@ -444,9 +465,9 @@ main ( int argc, char **argv)
               if (parse_debug)
                 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);
             }
@@ -457,19 +478,20 @@ main ( int argc, char **argv)
         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;
           break;
 
+        case aServer:
         case aMount:
         case aUmount:
           nokeysetup = 1;
@@ -495,9 +517,9 @@ 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 oAuditLog: auditlog = pargs.r.ret_str; break;
+        case oNoDetach: nodetach = 1; break;
 
         case oDebug: debug_value |= pargs.r.ret_ulong; break;
         case oDebugAll: debug_value = ~0; break;
@@ -532,10 +554,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);
@@ -551,8 +573,8 @@ main ( int argc, char **argv)
           break;
 
 
-        default: 
-          pargs.err = configfp? ARGPARSE_PRINT_WARNING:ARGPARSE_PRINT_ERROR; 
+        default:
+          pargs.err = configfp? ARGPARSE_PRINT_WARNING:ARGPARSE_PRINT_ERROR;
           break;
        }
     }
@@ -581,12 +603,12 @@ main ( int argc, char **argv)
 
   if (nogreeting)
     greeting = 0;
-  
+
   if (greeting)
     {
-      fprintf(stderr, "%s %s; %s\n",
-              strusage(11), strusage(13), strusage(14) );
-      fprintf(stderr, "%s\n", strusage(15) );
+      fprintf (stderr, "%s %s; %s\n",
+               strusage(11), strusage(13), strusage(14) );
+      fprintf (stderr, "%s\n", strusage(15) );
     }
 
   if (may_coredump && !opt.quiet)
@@ -625,36 +647,38 @@ main ( int argc, char **argv)
   /* 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);
       gcry_control (GCRYCTL_SET_RANDOM_SEED_FILE, p);
       xfree(p);
     }
-  
+
   /* Store given filename into FNAME. */
   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))
+        if (check_encryption_key ())
           failed = 1;
       if (failed)
         g13_exit (1);
     }
-  
+#endif /*0*/
+
   /* Dispatch command.  */
   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);
 
@@ -673,36 +697,59 @@ main ( int argc, char **argv)
          configuration file is valid.  */
       break;
 
+    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++;
+      }
+      break;
+
     case aCreate: /* Create a new container. */
       {
-        if (argc != 1) 
+        if (argc != 1)
           wrong_args ("--create filename");
-        err = create_new_container (&ctrl, argv[0]);
+        start_idle_task ();
+        err = g13_create_container (&ctrl, argv[0], recipients);
         if (err)
           log_error ("error creating a new container: %s <%s>\n",
                      gpg_strerror (err), gpg_strsource (err));
+        else
+          shutdown_pending++;
+      }
+      break;
+
+    case aMount: /* Mount a container. */
+      {
+        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",
+                     *argv, gpg_strerror (err), gpg_strsource (err));
       }
       break;
 
     default:
-        log_error (_("invalid command (there is no implicit command)\n"));
-       break;
+      log_error (_("invalid command (there is no implicit command)\n"));
+      break;
     }
 
-  /* Print the audit result if needed.  */
-  if (auditlog && auditfp)
-    {
-      /* audit_print_result (ctrl.audit, auditfp, 0); */
-      /* audit_release (ctrl.audit); */
-      ctrl.audit = NULL;
-      es_fclose (auditfp);
-    }
-  
+  if (!err)
+    join_idle_task ();
+
   /* Cleanup.  */
   g13_exit (0);
   return 8; /*NOTREACHED*/
 }
 
+
 /* Note: This function is used by signal handlers!. */
 static void
 emergency_cleanup (void)
@@ -728,6 +775,7 @@ g13_exit (int rc)
 }
 
 
+/* Store defaults into the per-connection CTRL object.  */
 void
 g13_init_default_ctrl (struct server_control_s *ctrl)
 {
@@ -735,3 +783,181 @@ g13_init_default_ctrl (struct server_control_s *ctrl)
 }
 
 
+/* 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
+   restricted in what it may do.  */
+static void
+handle_signal (int signo)
+{
+  switch (signo)
+    {
+#ifndef HAVE_W32_SYSTEM
+    case SIGHUP:
+      log_info ("SIGHUP received - re-reading configuration\n");
+      /* Fixme:  Not yet implemented.  */
+      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 ()); */
+      mountinfo_dump_all ();
+      break;
+
+    case SIGUSR2:
+      log_info ("SIGUSR2 received - no action defined\n");
+      break;
+
+    case SIGTERM:
+      if (!shutdown_pending)
+        log_info ("SIGTERM received - shutting down ...\n");
+      else
+        log_info ("SIGTERM received - still %u runners active\n",
+                  runner_get_threads ());
+      shutdown_pending++;
+      if (shutdown_pending > 2)
+        {
+          log_info ("shutdown forced\n");
+          log_info ("%s %s stopped\n", strusage(11), strusage(13) );
+          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);
+    }
+}
+
+
+/* This ticker function is called about every TIMERTICK_INTERVAL_SEC
+   seconds. */
+static void
+handle_tick (void)
+{
+  /* log_debug ("TICK\n"); */
+}
+
+
+/* The idle task.  We use a separate thread to do idle stuff and to
+   catch signals.  */
+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.  */
+
+  (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;
+#endif
+  ev = pth_event (PTH_EVENT_SIGS, &sigs, &signo);
+
+  /* The time event neds to computed n tghe fly.  */
+  time_ev = NULL;
+
+  for (;;)
+    {
+      /* The shutdown flag allows us to terminate the idle task.  */
+      if (shutdown_pending)
+        {
+          runner_cancel_all ();
+
+          if (!runner_get_threads ())
+            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;
+
+          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);
+        }
+
+      pth_event_concat (ev, time_ev, NULL);
+      pth_wait (ev);
+      pth_event_isolate (time_ev);
+
+      if (pth_event_occurred (ev))
+        {
+          handle_signal (signo);
+        }
+
+      if (time_ev && pth_event_occurred (time_ev))
+        {
+          pth_event_free (time_ev, PTH_FREE_ALL);
+          time_ev = NULL;
+          handle_tick ();
+        }
+    }
+
+  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;
+}
+
+
+/* Start the idle task.   */
+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)
+    {
+      log_fatal ("error starting idle task: %s\n",
+                 gpg_strerror (gpg_error_from_syserror ()));
+      return; /*NOTREACHED*/
+    }
+  idle_task_tid = tid;
+  pth_attr_destroy (tattr);
+}
+
+
+/* Wait for the idle task to finish.  */
+static void
+join_idle_task (void)
+{
+  if (idle_task_tid)
+    {
+      if (!pth_join (idle_task_tid, NULL))
+        log_error ("waiting for idle task thread failed: %s\n",
+                   gpg_strerror (gpg_error_from_syserror ()));
+    }
+}