mount does now work in server and standalone mode.
authorWerner Koch <wk@gnupg.org>
Wed, 14 Oct 2009 17:06:10 +0000 (17:06 +0000)
committerWerner Koch <wk@gnupg.org>
Wed, 14 Oct 2009 17:06:10 +0000 (17:06 +0000)
Implemented a signal handler.

g13/Makefile.am
g13/be-encfs.c
g13/create.c
g13/g13.c
g13/g13.h
g13/runner.c
g13/runner.h
g13/server.c [new file with mode: 0644]
g13/server.h [new file with mode: 0644]

index 2108f56..e65684c 100644 (file)
@@ -30,6 +30,7 @@ g13_SOURCES = \
        g13.c g13.h \
        keyblob.h \
        utils.c utils.h \
        g13.c g13.h \
        keyblob.h \
        utils.c utils.h \
+       server.c server.h \
        create.c create.h \
        mount.c mount.h \
        call-gpg.c call-gpg.h \
        create.c create.h \
        mount.c mount.h \
        call-gpg.c call-gpg.h \
index 0f7ec73..de3209a 100644 (file)
@@ -262,7 +262,8 @@ run_encfs_tool (ctrl_t ctrl, enum encfs_cmds cmd,
   pgmname = "/usr/bin/encfs";
   idx = 0;
   argv[idx++] = "-f";
   pgmname = "/usr/bin/encfs";
   idx = 0;
   argv[idx++] = "-f";
-  argv[idx++] = "-v";
+  if (opt.verbose)
+    argv[idx++] = "-v";
   argv[idx++] = "--stdinpass";
   argv[idx++] = "--annotate";
   argv[idx++] = rawdir;
   argv[idx++] = "--stdinpass";
   argv[idx++] = "--annotate";
   argv[idx++] = rawdir;
index 7f3a349..0279c33 100644 (file)
@@ -39,7 +39,7 @@
 /* Create a new blob with all the session keys and other meta
    information which are to be stored encrypted in the crypto
    container header.  On success the malloced blob is stored at R_BLOB
 /* Create a new blob with all the session keys and other meta
    information which are to be stored encrypted in the crypto
    container header.  On success the malloced blob is stored at R_BLOB
-   and its length at R_BLOBLEN.  On error en error ocde is returned
+   and its length at R_BLOBLEN.  On error an error code is returned
    and (R_BLOB,R_BLOBLEN) are set to (NULL,0). 
 
    The format of this blob is a sequence of tag-length-value tuples.
    and (R_BLOB,R_BLOBLEN) are set to (NULL,0). 
 
    The format of this blob is a sequence of tag-length-value tuples.
index 6b4d05a..2886894 100644 (file)
--- a/g13/g13.c
+++ b/g13/g13.c
@@ -36,8 +36,9 @@
 #include "sysutils.h"
 #include "gc-opt-flags.h"
 #include "keyblob.h"
 #include "sysutils.h"
 #include "gc-opt-flags.h"
 #include "keyblob.h"
-#include "./runner.h"
-#include "./create.h"
+#include "server.h"
+#include "runner.h"
+#include "create.h"
 #include "./mount.h"
 
 
 #include "./mount.h"
 
 
@@ -51,6 +52,7 @@ enum cmd_and_opt_values {
   aCreate,
   aMount,
   aUmount,
   aCreate,
   aMount,
   aUmount,
+  aServer,
 
   oOptions,
   oDebug,
 
   oOptions,
   oDebug,
@@ -102,6 +104,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 (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", "@"),
 
   ARGPARSE_c (aGPGConfList, "gpgconf-list", "@"),
   ARGPARSE_c (aGPGConfTest, "gpgconf-test", "@"),
@@ -168,6 +171,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;
 
 /* Global variable to keep an error count. */
 int g13_errors_seen = 0;
 
@@ -178,11 +185,23 @@ static int maybe_setuid = 1;
 static const char *debug_level;
 static unsigned int debug_value;
 
 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 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. */
 GCRY_THREAD_OPTION_PTH_IMPL;
 static int fixed_gcry_pth_init (void)
 /* Begin Pth wrapper functions. */
 GCRY_THREAD_OPTION_PTH_IMPL;
 static int fixed_gcry_pth_init (void)
@@ -475,6 +494,7 @@ main ( int argc, char **argv)
           nokeysetup = 1;
           break;
 
           nokeysetup = 1;
           break;
 
+        case aServer:
         case aMount:
         case aUmount:
           nokeysetup = 1;
         case aMount:
         case aUmount:
           nokeysetup = 1;
@@ -591,9 +611,9 @@ main ( int argc, char **argv)
   
   if (greeting)
     {
   
   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)
     }
 
   if (may_coredump && !opt.quiet)
@@ -680,24 +700,25 @@ main ( int argc, char **argv)
          configuration file is valid.  */
       break;
 
          configuration file is valid.  */
       break;
 
+    case aServer:
+      {
+        start_idle_task ();
+        err = g13_server (&ctrl);
+        if (err)
+          log_error ("server exited with error: %s <%s>\n",
+                     gpg_strerror (err), gpg_strsource (err));
+      }
+      break;
+
     case aCreate: /* Create a new container. */
       {
         if (argc != 1) 
           wrong_args ("--create filename");
     case aCreate: /* Create a new container. */
       {
         if (argc != 1) 
           wrong_args ("--create filename");
+        start_idle_task ();
         err = g13_create_container (&ctrl, argv[0]);
         if (err)
           log_error ("error creating a new container: %s <%s>\n",
                      gpg_strerror (err), gpg_strsource (err));
         err = g13_create_container (&ctrl, argv[0]);
         if (err)
           log_error ("error creating a new container: %s <%s>\n",
                      gpg_strerror (err), gpg_strsource (err));
-        else
-          {
-            unsigned int n;
-
-            while ((n = runner_get_threads ()))
-              {
-                log_info ("number of running threads: %u\n", n);
-                pth_sleep (5);
-              }
-          }
       }
       break;
 
       }
       break;
 
@@ -705,20 +726,11 @@ main ( int argc, char **argv)
       {
         if (argc != 1 && argc != 2 ) 
           wrong_args ("--mount filename [mountpoint]");
       {
         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));
         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));
-        else
-          {
-            unsigned int n;
-
-            while ((n = runner_get_threads ()))
-              {
-                log_info ("number of running threads: %u\n", n);
-                pth_sleep (5);
-              }
-          }
       }
       break;
 
       }
       break;
 
@@ -727,6 +739,9 @@ main ( int argc, char **argv)
       break;
     }
 
       break;
     }
 
+  if (!err)
+    join_idle_task ();
+
   /* Print the audit result if needed.  */
   if (auditlog && auditfp)
     {
   /* Print the audit result if needed.  */
   if (auditlog && auditfp)
     {
@@ -741,6 +756,7 @@ main ( int argc, char **argv)
   return 8; /*NOTREACHED*/
 }
 
   return 8; /*NOTREACHED*/
 }
 
+
 /* Note: This function is used by signal handlers!. */
 static void
 emergency_cleanup (void)
 /* Note: This function is used by signal handlers!. */
 static void
 emergency_cleanup (void)
@@ -766,6 +782,7 @@ g13_exit (int rc)
 }
 
 
 }
 
 
+/* Store defaults into the per-connection CTRL object.  */
 void
 g13_init_default_ctrl (struct server_control_s *ctrl)
 {
 void
 g13_init_default_ctrl (struct server_control_s *ctrl)
 {
@@ -773,84 +790,179 @@ g13_init_default_ctrl (struct server_control_s *ctrl)
 }
 
 
 }
 
 
-/* static void */
-/* daemonize (int nodetach) */
-/* { */
-/*   gnupg_fd_t fd; */
-/*   gnupg_fd_t fd_ssh; */
-/*   pid_t pid; */
-
-/*   fflush (NULL); */
-/* #ifdef HAVE_W32_SYSTEM */
-/*   pid = getpid (); */
-/* #else /\*!HAVE_W32_SYSTEM*\/ */
-/*   pid = fork (); */
-/*   if (pid == (pid_t)-1)  */
-/*     { */
-/*       log_fatal ("fork failed: %s\n", strerror (errno) ); */
-/*       g13_exit (1); */
-/*     } */
-/*   else if (pid) /\* We are the parent *\/ */
-/*     {  */
-/*       /\* We need to clwanup our resources.  An gcry_atfork might be */
-/*          needed. *\/ */
-/*       exit (0);  */
-/*       /\*NOTREACHED*\/ */
-/*     } /\* End parent *\/ */
-
-/*   /\*  */
-/*      This is the child */
-/*   *\/ */
-
-/*   /\* Detach from tty and put process into a new session *\/ */
-/*   if (!nodetach ) */
-/*     {  */
-/*       int i; */
-/*       unsigned int oldflags; */
+/* 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");
+      pth_ctrl (PTH_CTRL_DUMPSTATE, log_get_stream ());
+      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*/
       
       
-/*       /\* 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 | JNLIB_LOG_RUN_DETACHED); */
-/*       opt.running_detached = 1; */
-/*     } */
+    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;
   
   
-/*   if (chdir("/")) */
-/*     { */
-/*       log_error ("chdir to / failed: %s\n", strerror (errno)); */
-/*       exit (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*\/ */
-
-/*   log_info ("%s %s started\n", strusage(11), strusage(13) ); */
-/*   handle_something (fd, opt.ssh_support ? fd_ssh : GNUPG_INVALID_FD); */
-/* } */
+  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 ()));
+    }
+}
+
index 5740e58..0bfc42e 100644 (file)
--- a/g13/g13.h
+++ b/g13/g13.h
@@ -30,6 +30,7 @@
 #include "../common/status.h"
 #include "../common/estream.h"
 #include "../common/audit.h"
 #include "../common/status.h"
 #include "../common/estream.h"
 #include "../common/audit.h"
+#include "../common/session-env.h"
 
 /* A large struct named "opt" to keep global flags.  */
 struct 
 
 /* A large struct named "opt" to keep global flags.  */
 struct 
@@ -58,6 +59,7 @@ struct
   char *lc_messages;
   char *xauthority;
   char *pinentry_user_data;
   char *lc_messages;
   char *xauthority;
   char *pinentry_user_data;
+  session_env_t session_env;
 
   /* Name of the output file - FIXME: what is this?  */
   const char *outfile;
 
   /* Name of the output file - FIXME: what is this?  */
   const char *outfile;
index d88b69b..27d0370 100644 (file)
@@ -40,9 +40,12 @@ struct runner_s
 
   int spawned;  /* True if runner_spawn has been called.  */
   pth_t threadid; /* The TID of the runner thread.  */
 
   int spawned;  /* True if runner_spawn has been called.  */
   pth_t threadid; /* The TID of the runner thread.  */
+  runner_t next_running; /* Builds a list of all running threads.  */
+  int canceled;     /* Set if a cancel has already been send once.  */
 
   int cancel_flag;  /* If set the thread should terminate itself.  */
 
 
   int cancel_flag;  /* If set the thread should terminate itself.  */
 
+
   /* We use a reference counter to know when it is safe to remove the
      object.  Lackiong an explicit ref fucntion this counter will take
      only these two values:
   /* We use a reference counter to know when it is safe to remove the
      object.  Lackiong an explicit ref fucntion this counter will take
      only these two values:
@@ -66,8 +69,9 @@ struct runner_s
 };
 
 
 };
 
 
-/* Avariabale to track the number of active runner threads.  */
-static unsigned int thread_count;
+/* The head of the list of all running threads.  */
+static runner_t running_threads;
+
 
 
 \f
 
 
 \f
@@ -114,7 +118,12 @@ check_already_spawned (runner_t runner, const char *funcname)
 unsigned int
 runner_get_threads (void)
 {
 unsigned int
 runner_get_threads (void)
 {
-  return thread_count;
+  unsigned int n = 0;
+  runner_t r;
+
+  for (r = running_threads; r; r = r->next_running)
+    n++;
+  return n;
 }
 
 
 }
 
 
@@ -240,7 +249,7 @@ static void *
 runner_thread (void *arg)
 {
   runner_t runner = arg;
 runner_thread (void *arg)
 {
   runner_t runner = arg;
-  gpg_error_t err;
+  gpg_error_t err = 0;
 
   log_debug ("starting runner thread\n");
   /* If a status_fp is available, the thread's main task is to read
 
   log_debug ("starting runner thread\n");
   /* If a status_fp is available, the thread's main task is to read
@@ -257,7 +266,6 @@ runner_thread (void *arg)
       estream_t fp = runner->status_fp;
 
       pos = 0;
       estream_t fp = runner->status_fp;
 
       pos = 0;
-      err = 0;
       cont_line = 0;
       while (!err && !runner->cancel_flag && (c=es_getc (fp)) != EOF)
         {
       cont_line = 0;
       while (!err && !runner->cancel_flag && (c=es_getc (fp)) != EOF)
         {
@@ -322,9 +330,22 @@ runner_thread (void *arg)
 
   /* Get rid of the runner object (note: it is refcounted).  */
   log_debug ("runner thread releasing runner ...\n");
 
   /* Get rid of the runner object (note: it is refcounted).  */
   log_debug ("runner thread releasing runner ...\n");
+  {
+    runner_t r, rprev;
+    
+    for (r = running_threads, rprev = NULL; r; rprev = r, r = r->next_running)
+      if (r == runner)
+        {
+          if (!rprev)
+            running_threads = r->next_running;
+          else
+            rprev->next_running = r->next_running;
+          r->next_running = NULL;
+          break;
+        }
+  }
   runner_release (runner);
   log_debug ("runner thread runner released\n");
   runner_release (runner);
   log_debug ("runner thread runner released\n");
-  thread_count--;
   
   return NULL;
 }
   
   return NULL;
 }
@@ -374,14 +395,14 @@ runner_spawn (runner_t runner)
     }
   /* The scheduler has not yet kicked in, thus we can safely set the
      spawned flag and the tid.  */
     }
   /* The scheduler has not yet kicked in, thus we can safely set the
      spawned flag and the tid.  */
-  thread_count++;
   runner->spawned = 1;
   runner->threadid = tid;
   runner->spawned = 1;
   runner->threadid = tid;
+  runner->next_running = running_threads;
+  running_threads = runner;
+
   pth_attr_destroy (tattr);
 
   /* The runner thread is now runnable.  */
   pth_attr_destroy (tattr);
 
   /* The runner thread is now runnable.  */
-  
-  
 
   return 0;
 }
 
   return 0;
 }
@@ -391,8 +412,10 @@ runner_spawn (runner_t runner)
 void
 runner_cancel (runner_t runner)
 {
 void
 runner_cancel (runner_t runner)
 {
+  /* Warning: runner_cancel_all has knowledge of this code.  */
   if (runner->spawned)
     {
   if (runner->spawned)
     {
+      runner->canceled = 1;  /* Mark that we canceled this one already.  */
       /* FIXME: This does only work if the thread emits status lines.  We
          need to change the trhead to wait on an event.  */
       runner->cancel_flag = 1;
       /* FIXME: This does only work if the thread emits status lines.  We
          need to change the trhead to wait on an event.  */
       runner->cancel_flag = 1;
@@ -402,6 +425,25 @@ runner_cancel (runner_t runner)
 }
 
 
 }
 
 
+/* Cancel all runner threads.  */
+void
+runner_cancel_all (void)
+{
+  runner_t r;
+
+  do 
+    {
+      for (r = running_threads; r; r = r->next_running)
+        if (r->spawned && !r->canceled)
+          {
+            runner_cancel (r);
+            break;
+          }
+    }
+  while (r);
+}
+
+
 /* Send a line of data down to the engine.  This line may not contain
    a binary Nul or a LF character.  This function is used by the
    engine's handler.  */
 /* Send a line of data down to the engine.  This line may not contain
    a binary Nul or a LF character.  This function is used by the
    engine's handler.  */
index 0152f22..88ebc7d 100644 (file)
@@ -57,6 +57,9 @@ gpg_error_t runner_spawn (runner_t runner);
 /* Cancel a runner.  */
 void runner_cancel (runner_t runner);
 
 /* Cancel a runner.  */
 void runner_cancel (runner_t runner);
 
+/* Cancel all runner.  */
+void runner_cancel_all (void);
+
 /* Send data back to the engine.  This function is used by the
    engine's handler.  */
 gpg_error_t runner_send_line (runner_t runner,
 /* Send data back to the engine.  This function is used by the
    engine's handler.  */
 gpg_error_t runner_send_line (runner_t runner,
diff --git a/g13/server.c b/g13/server.c
new file mode 100644 (file)
index 0000000..bdf214f
--- /dev/null
@@ -0,0 +1,709 @@
+/* server.c - The G13 Assuan server
+ * Copyright (C) 2009 Free Software Foundation, Inc.
+ *
+ * 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 <http://www.gnu.org/licenses/>.
+ */
+
+#include <config.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <stdarg.h>
+#include <errno.h>
+#include <assert.h>
+
+#include "g13.h"
+#include <assuan.h>
+#include "i18n.h"
+#include "keyblob.h"
+#include "./server.h"
+#include "./mount.h"
+
+
+/* Local data for this server module.  A pointer to this is stored in
+   the CTRL object of each connection.  */
+struct server_local_s 
+{
+  /* The Assuan contect we are working on.  */
+  assuan_context_t assuan_ctx;
+
+  char *mountpoint;  /* Malloced current mountpoint.  */
+
+};
+
+
+/* Cookie definition for assuan data line output.  */
+static ssize_t data_line_cookie_write (void *cookie,
+                                       const void *buffer, size_t size);
+static int data_line_cookie_close (void *cookie);
+static es_cookie_io_functions_t data_line_cookie_functions =
+  {
+    NULL,
+    data_line_cookie_write,
+    NULL,
+    data_line_cookie_close
+  };
+
+
+/* The filepointer for status message used in non-server mode.  */
+/* static FILE *statusfp;  FIXME; */
+
+
+
+\f
+static int command_has_option (const char *cmd, const char *cmdopt);
+
+
+
+\f
+#define set_error(e,t) assuan_set_error (ctx, gpg_error (e), (t))
+
+
+
+\f
+/* Skip over options.  
+   Blanks after the options are also removed.  */
+static char *
+skip_options (const char *line)
+{
+  while (spacep (line))
+    line++;
+  while ( *line == '-' && line[1] == '-' )
+    {
+      while (*line && !spacep (line))
+        line++;
+      while (spacep (line))
+        line++;
+    }
+  return (char*)line;
+}
+
+
+/* Check whether the option NAME appears in LINE.  */
+static int
+has_option (const char *line, const char *name)
+{
+  const char *s;
+  int n = strlen (name);
+
+  s = strstr (line, name);
+  if (s && s >= skip_options (line))
+    return 0;
+  return (s && (s == line || spacep (s-1)) && (!s[n] || spacep (s+n)));
+}
+
+
+/* A write handler used by es_fopencookie to write Assuan data
+   lines.  */
+static ssize_t
+data_line_cookie_write (void *cookie, const void *buffer, size_t size)
+{
+  assuan_context_t ctx = cookie;
+
+  if (assuan_send_data (ctx, buffer, size))
+    {
+      errno = EIO;
+      return -1;
+    }
+
+  return size;
+}
+
+/* A close handler used by es_fopencookie to write Assuan data
+   lines.  */
+static int
+data_line_cookie_close (void *cookie)
+{
+  assuan_context_t ctx = cookie;
+
+  if (assuan_send_data (ctx, NULL, 0))
+    {
+      errno = EIO;
+      return -1;
+    }
+
+  return 0;
+}
+
+
+/* The handler for Assuan 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, "putenv"))
+    {
+      /* Change the session's environment to be used for the
+         Pinentry.  Valid values are:
+          <NAME>            Delete envvar NAME
+          <KEY>=            Set envvar NAME to the empty string
+          <KEY>=<VALUE>     Set envvar NAME to VALUE
+      */
+      err = session_env_putenv (opt.session_env, value);
+    }
+  else if (!strcmp (key, "display"))
+    {
+      err = session_env_setenv (opt.session_env, "DISPLAY", value);
+    }
+  else if (!strcmp (key, "ttyname"))
+    {
+      err = session_env_setenv (opt.session_env, "GPG_TTY", value);
+    }
+  else if (!strcmp (key, "ttytype"))
+    {
+      err = session_env_setenv (opt.session_env, "TERM", value);
+    }
+  else if (!strcmp (key, "lc-ctype"))
+    {
+      xfree (opt.lc_ctype);
+      opt.lc_ctype = xtrystrdup (value);
+      if (!opt.lc_ctype)
+        err = gpg_error_from_syserror ();
+    }
+  else if (!strcmp (key, "lc-messages"))
+    {
+      xfree (opt.lc_messages);
+      opt.lc_messages = xtrystrdup (value);
+      if (!opt.lc_messages)
+        err = gpg_error_from_syserror ();
+    }
+  else if (!strcmp (key, "xauthority"))
+    {
+      err = session_env_setenv (opt.session_env, "XAUTHORITY", value);
+    }
+  else if (!strcmp (key, "pinentry-user-data"))
+    {
+      err = session_env_setenv (opt.session_env, "PINENTRY_USER_DATA", value);
+    }
+  else if (!strcmp (key, "enable-audit-log"))
+    {
+      /* This is not yet used.  */
+      /* int i = *value? atoi (value) : 0; */
+      /* ctrl->server_local->enable_audit_log = i; */
+    }
+  else if (!strcmp (key, "allow-pinentry-notify"))
+    {
+      ; /* We always allow it.  */
+    }
+  else
+    err = gpg_error (GPG_ERR_UNKNOWN_OPTION);
+
+  return err;
+}
+
+
+/* The handler for an Assuan RESET command.  */
+static void
+reset_notify (assuan_context_t ctx)
+{
+  ctrl_t ctrl = assuan_get_pointer (ctx);
+
+  xfree (ctrl->server_local->mountpoint);
+  ctrl->server_local->mountpoint = NULL;
+
+  assuan_close_input_fd (ctx);
+  assuan_close_output_fd (ctx);
+}
+
+
+/* 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;
+}
+
+
+
+/* RECIPIENT <userID>
+
+   FIXME - description. 
+   All RECIPIENT commands are cumulative until a RESET or an
+   successful CREATE command.
+ */
+static gpg_error_t
+cmd_recipient (assuan_context_t ctx, char *line)
+{
+  ctrl_t ctrl = assuan_get_pointer (ctx);
+  gpg_error_t err;
+
+  (void)ctrl;
+  err = gpg_error (GPG_ERR_NOT_IMPLEMENTED);
+  /* err = gpgsm_add_to_certlist (ctrl, line, 0, */
+  /*                              &ctrl->server_local->recplist, 0); */
+  /* if (err) */
+  /*   { */
+  /*     gpgsm_status2 (ctrl, STATUS_INV_RECP, */
+  /*                    get_inv_recpsgnr_code (rc), line, NULL); */
+  /*   } */
+
+  return leave_cmd (ctx, err);
+}
+
+
+/* SIGNER <userID>
+
+   FIXME - description. 
+ */
+static gpg_error_t
+cmd_signer (assuan_context_t ctx, char *line)
+{
+  ctrl_t ctrl = assuan_get_pointer (ctx);
+  gpg_error_t err;
+
+  (void)ctrl;
+
+  err = gpg_error (GPG_ERR_NOT_IMPLEMENTED);
+  return leave_cmd (ctx, err);
+}
+
+
+/* SETMOUNTPOINT [options] [<dirname>]
+
+   Set DIRNAME as the new mount point for future operations.  
+ */
+static gpg_error_t
+cmd_setmountpoint (assuan_context_t ctx, char *line)
+{
+  ctrl_t ctrl = assuan_get_pointer (ctx);
+  gpg_error_t err = 0;
+  char *p, *pend;
+  size_t len;
+
+  line = skip_options (line);
+  for (p=line; *p && !spacep (p); p++)
+    ;
+  pend = p;
+  while (spacep(p))
+    p++;
+  if (*p)
+    {
+      err = gpg_error (GPG_ERR_ASS_SYNTAX);
+      goto leave;
+    }
+  *pend = 0;
+
+  /* Unescape the line and check for embedded Nul bytes.  */
+  len = percent_plus_unescape_inplace (line, 0);
+  line[len] = 0;
+  if (memchr (line, 0, len))
+    {
+      err = gpg_error (GPG_ERR_INV_NAME);
+      goto leave;
+    }
+
+  xfree (ctrl->server_local->mountpoint);
+  if (!len)  /* Reset mountpoint.  */
+    ctrl->server_local->mountpoint = NULL;
+  else
+    {
+      ctrl->server_local->mountpoint = xtrystrdup (line);
+      if (!ctrl->server_local->mountpoint)
+        err = gpg_error_from_syserror ();
+    }
+
+  if (!err)
+    log_debug ("mountpoint is now `%s'\n", 
+               ctrl->server_local->mountpoint
+               ? ctrl->server_local->mountpoint: "[none]");
+
+ leave:
+  return leave_cmd (ctx, err);
+}
+
+
+/* MOUNT [options] <containername>
+
+   Mount CONTAINERNAME onto the current mount point.  CONTAINERNAME is
+   the name of a file in the g13 format and must be percent-plus
+   escaped to allow for arbitrary names.  The mount poiunt must have
+   been set already.
+
+
+   A reason why we use a separate command for the mount point is to
+   allow for longer filenames (an assuan command line is limited to
+   ~1000 byte. 
+ */
+static gpg_error_t
+cmd_mount (assuan_context_t ctx, char *line)
+{
+  ctrl_t ctrl = assuan_get_pointer (ctx);
+  gpg_error_t err = 0;
+  char *p, *pend;
+  size_t len;
+
+  line = skip_options (line);
+  for (p=line; *p && !spacep (p); p++)
+    ;
+  pend = p;
+  while (spacep(p))
+    p++;
+  if (*p || pend == line)
+    {
+      err = gpg_error (GPG_ERR_ASS_SYNTAX);
+      goto leave;
+    }
+  *pend = 0;
+
+  /* Unescape the line and check for embedded Nul bytes.  */
+  len = percent_plus_unescape_inplace (line, 0);
+  line[len] = 0;
+  if (!len || memchr (line, 0, len))
+    {
+      err = gpg_error (GPG_ERR_INV_NAME);
+      goto leave;
+    }
+    
+  /* Perform the mount.  */
+  err = g13_mount_container (ctrl, line, ctrl->server_local->mountpoint);
+
+ leave:
+  return leave_cmd (ctx, err);
+}
+
+
+/* GETINFO <what>
+
+   Multipurpose function to return a variety of information.
+   Supported values for WHAT are:
+
+     version     - Return the version of the program.
+     pid         - Return the process id of the server.
+     cmd_has_option CMD OPT
+                 - Returns OK if the command CMD implements the option OPT.
+
+ */
+static gpg_error_t
+cmd_getinfo (assuan_context_t ctx, char *line)
+{
+  gpg_error_t err = 0;
+
+  if (!strcmp (line, "version"))
+    {
+      const char *s = PACKAGE_VERSION;
+      err = assuan_send_data (ctx, s, strlen (s));
+    }
+  else if (!strcmp (line, "pid"))
+    {
+      char numbuf[50];
+
+      snprintf (numbuf, sizeof numbuf, "%lu", (unsigned long)getpid ());
+      err = assuan_send_data (ctx, numbuf, strlen (numbuf));
+    }
+  else if (!strncmp (line, "cmd_has_option", 14)
+           && (line[14] == ' ' || line[14] == '\t' || !line[14]))
+    {
+      char *cmd, *cmdopt;
+      line += 14;
+      while (*line == ' ' || *line == '\t')
+        line++;
+      if (!*line)
+        err = gpg_error (GPG_ERR_MISSING_VALUE);
+      else
+        {
+          cmd = line;
+          while (*line && (*line != ' ' && *line != '\t'))
+            line++;
+          if (!*line)
+            err = gpg_error (GPG_ERR_MISSING_VALUE);
+          else
+            {
+              *line++ = 0;
+              while (*line == ' ' || *line == '\t')
+                line++;
+              if (!*line)
+                err = gpg_error (GPG_ERR_MISSING_VALUE);
+              else
+                {
+                  cmdopt = line;
+                  if (!command_has_option (cmd, cmdopt))
+                    err = gpg_error (GPG_ERR_GENERAL);
+                }
+            }
+        }
+    }
+  else
+    err = set_error (GPG_ERR_ASS_PARAMETER, "unknown value for WHAT");
+
+  return leave_cmd (ctx, err);
+}
+
+
+\f
+/* Return true if the command CMD implements the option CMDOPT.  */
+static int
+command_has_option (const char *cmd, const char *cmdopt)
+{
+  (void)cmd;
+  (void)cmdopt;
+      
+  return 0;
+}
+
+
+/* Tell the Assuan library about our commands.  */
+static int
+register_commands (assuan_context_t ctx)
+{
+  static struct {
+    const char *name;
+    gpg_error_t (*handler)(assuan_context_t, char *line);
+  } table[] =  {
+    { "RECIPIENT",     cmd_recipient },
+    { "SIGNER",        cmd_signer },
+    { "MOUNT",         cmd_mount },
+    { "SETMOUNTPOINT", cmd_setmountpoint },
+    { "INPUT",         NULL }, 
+    { "OUTPUT",        NULL }, 
+    { "GETINFO",       cmd_getinfo },
+    { NULL }
+  };
+  gpg_error_t err;
+  int i;
+
+  for (i=0; table[i].name; i++)
+    {
+      err = assuan_register_command (ctx, table[i].name, table[i].handler);
+      if (err)
+        return err;
+    } 
+  return 0;
+}
+
+
+/* Startup the server. DEFAULT_RECPLIST is the list of recipients as
+   set from the command line or config file.  We only require those
+   marked as encrypt-to. */
+gpg_error_t
+g13_server (ctrl_t ctrl)
+{
+  gpg_error_t err;
+  int filedes[2];
+  assuan_context_t ctx = NULL;
+  static const char hello[] = ("GNU Privacy Guard's G13 server "
+                               PACKAGE_VERSION " ready");
+
+  /* We use a pipe based server so that we can work from scripts.
+     assuan_init_pipe_server will automagically detect when we are
+     called with a socketpair and ignore FIELDES in this case. */
+  filedes[0] = 0;
+  filedes[1] = 1;
+  err = assuan_new (&ctx);
+  if (err)
+    {
+      log_error ("failed to allocate an Assuan context: %s\n",
+                 gpg_strerror (err));
+      goto leave;
+    }
+
+  err = assuan_init_pipe_server (ctx, filedes);
+  if (err)
+    {
+      log_error ("failed to initialize the server: %s\n", gpg_strerror (err));
+      goto leave;
+    }
+
+  err = register_commands (ctx);
+  if (err)
+    {
+      log_error ("failed to the register commands with Assuan: %s\n",
+                 gpg_strerror (err));
+      goto leave;
+    }
+
+  assuan_set_pointer (ctx, ctrl);
+
+  if (opt.verbose || opt.debug)
+    {
+      char *tmp = NULL;
+      const char *s1 = getenv ("GPG_AGENT_INFO");
+
+      tmp = xtryasprintf ("Home: %s\n"
+                          "Config: %s\n"
+                          "AgentInfo: %s\n"
+                          "%s",
+                          opt.homedir,
+                          opt.config_filename,
+                          s1?s1:"[not set]",
+                          hello);
+      if (tmp)
+        {
+          assuan_set_hello_line (ctx, tmp);
+          xfree (tmp);
+        }
+    }
+  else
+    assuan_set_hello_line (ctx, hello);
+
+  assuan_register_reset_notify (ctx, reset_notify);
+  assuan_register_option_handler (ctx, option_handler);
+
+  ctrl->server_local = xtrycalloc (1, sizeof *ctrl->server_local);
+  if (!ctrl->server_local)
+    {
+      err = gpg_error_from_syserror ();
+      goto leave;
+    }
+  ctrl->server_local->assuan_ctx = ctx;
+
+  if (DBG_ASSUAN)
+    assuan_set_log_stream (ctx, log_get_stream ());
+
+  while ( !(err = assuan_accept (ctx)) )
+    {
+      err = assuan_process (ctx);
+      if (err)
+        log_info ("Assuan processing failed: %s\n", gpg_strerror (err));
+    }
+  if (err == -1)
+    err = 0;
+  else
+    log_info ("Assuan accept problem: %s\n", gpg_strerror (err));
+  
+ leave:
+  if (ctrl->server_local)
+    {
+      xfree (ctrl->server_local);
+      ctrl->server_local = NULL;
+    }
+
+  assuan_release (ctx);
+  return err;
+}
+
+
+
+/* gpg_error_t */
+/* gpgsm_status2 (ctrl_t ctrl, int no, ...) */
+/* { */
+/*   gpg_error_t err = 0; */
+/*   va_list arg_ptr; */
+/*   const char *text; */
+
+/*   va_start (arg_ptr, no); */
+
+/*   if (ctrl->no_server && ctrl->status_fd == -1) */
+/*     ; /\* No status wanted. *\/ */
+/*   else if (ctrl->no_server) */
+/*     { */
+/*       if (!statusfp) */
+/*         { */
+/*           if (ctrl->status_fd == 1) */
+/*             statusfp = stdout; */
+/*           else if (ctrl->status_fd == 2) */
+/*             statusfp = stderr; */
+/*           else */
+/*             statusfp = fdopen (ctrl->status_fd, "w"); */
+      
+/*           if (!statusfp) */
+/*             { */
+/*               log_fatal ("can't open fd %d for status output: %s\n", */
+/*                          ctrl->status_fd, strerror(errno)); */
+/*             } */
+/*         } */
+      
+/*       fputs ("[GNUPG:] ", statusfp); */
+/*       fputs (get_status_string (no), statusfp); */
+    
+/*       while ( (text = va_arg (arg_ptr, const char*) )) */
+/*         { */
+/*           putc ( ' ', statusfp ); */
+/*           for (; *text; text++)  */
+/*             { */
+/*               if (*text == '\n') */
+/*                 fputs ( "\\n", statusfp ); */
+/*               else if (*text == '\r') */
+/*                 fputs ( "\\r", statusfp ); */
+/*               else  */
+/*                 putc ( *(const byte *)text,  statusfp ); */
+/*             } */
+/*         } */
+/*       putc ('\n', statusfp); */
+/*       fflush (statusfp); */
+/*     } */
+/*   else  */
+/*     { */
+/*       assuan_context_t ctx = ctrl->server_local->assuan_ctx; */
+/*       char buf[950], *p; */
+/*       size_t n; */
+
+/*       p = buf;  */
+/*       n = 0; */
+/*       while ( (text = va_arg (arg_ptr, const char *)) ) */
+/*         { */
+/*           if (n) */
+/*             { */
+/*               *p++ = ' '; */
+/*               n++; */
+/*             } */
+/*           for ( ; *text && n < DIM (buf)-2; n++) */
+/*             *p++ = *text++; */
+/*         } */
+/*       *p = 0; */
+/*       err = assuan_write_status (ctx, get_status_string (no), buf); */
+/*     } */
+
+/*   va_end (arg_ptr); */
+/*   return err; */
+/* } */
+
+/* gpg_error_t */
+/* gpgsm_status (ctrl_t ctrl, int no, const char *text) */
+/* { */
+/*   return gpgsm_status2 (ctrl, no, text, NULL); */
+/* } */
+
+/* gpg_error_t */
+/* gpgsm_status_with_err_code (ctrl_t ctrl, int no, const char *text, */
+/*                             gpg_err_code_t ec) */
+/* { */
+/*   char buf[30]; */
+
+/*   sprintf (buf, "%u", (unsigned int)ec); */
+/*   if (text) */
+/*     return gpgsm_status2 (ctrl, no, text, buf, NULL); */
+/*   else */
+/*     return gpgsm_status2 (ctrl, no, buf, NULL); */
+/* } */
+
+
+/* Helper to notify the client about Pinentry events.  Returns an gpg
+   error code. */
+gpg_error_t
+g13_proxy_pinentry_notify (ctrl_t ctrl, const unsigned char *line)
+{
+  if (!ctrl || !ctrl->server_local)
+    return 0;
+  return assuan_inquire (ctrl->server_local->assuan_ctx, line, NULL, NULL, 0);
+}
+
+
+
+
diff --git a/g13/server.h b/g13/server.h
new file mode 100644 (file)
index 0000000..9b6eb98
--- /dev/null
@@ -0,0 +1,29 @@
+/* server.h - The G13 Assuan server
+ * Copyright (C) 2009 Free Software Foundation, Inc.
+ *
+ * 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 <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef G13_SERVER_H
+#define G13_SERVER_H
+
+
+gpg_error_t g13_server (ctrl_t ctrl);
+
+gpg_error_t g13_proxy_pinentry_notify (ctrl_t ctrl, const unsigned char *line);
+
+#endif /*G13_SERVER_H*/
+