Start the agent on demand if option --enable-standard socket has been
authorWerner Koch <wk@gnupg.org>
Tue, 4 May 2010 09:56:42 +0000 (09:56 +0000)
committerWerner Koch <wk@gnupg.org>
Tue, 4 May 2010 09:56:42 +0000 (09:56 +0000)
enabled.

ChangeLog
NEWS
agent/ChangeLog
agent/gpg-agent.c
common/ChangeLog
common/asshelp.c
common/exechelp.c
configure.ac
doc/gpg-agent.texi

index 1121a88..c27654a 100644 (file)
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,7 @@
+2010-05-04  Werner Koch  <wk@g10code.com>
+
+       * configure.ac: Add option --enable-standard-socket.
+
 2010-03-09  Werner Koch  <wk@g10code.com>
 
        Release 2.0.15.
diff --git a/NEWS b/NEWS
index 7730b5b..40053d1 100644 (file)
--- a/NEWS
+++ b/NEWS
@@ -1,6 +1,14 @@
 Noteworthy changes in version 2.0.16 (unreleased)
 -------------------------------------------------
 
+ * If the agent's --use-standard-socket option is active, all tools
+   try to start and daemonize the agent on the fly.  In the past this
+   was only supported on W32; on non-W32 systems the new configure
+   option --use-standard-socket may now be used to use this feature by
+   default.
+
+ * Minor bug fixes.
+
 
 Noteworthy changes in version 2.0.15 (2010-03-09)
 -------------------------------------------------
index 0bca557..e604ae4 100644 (file)
@@ -1,3 +1,7 @@
+2010-05-04  Werner Koch  <wk@g10code.com>
+
+       * gpg-agent.c (main): Add command --use-standard-socket-p.
+
 2010-05-03  Werner Koch  <wk@g10code.com>
 
        * gpg-agent.c (check_own_socket_thread): Do not release SOCKNAME
index eec2ca1..57d61b3 100644 (file)
@@ -1,6 +1,6 @@
 /* gpg-agent.c  -  The GnuPG Agent
  * Copyright (C) 2000, 2001, 2002, 2003, 2004, 2005,
- *               2006, 2007, 2009 Free Software Foundation, Inc.
+ *               2006, 2007, 2009, 2010 Free Software Foundation, Inc.
  *
  * This file is part of GnuPG.
  *
@@ -60,6 +60,7 @@ enum cmd_and_opt_values
   oNoVerbose = 500,
   aGPGConfList,
   aGPGConfTest,
+  aUseStandardSocketP,
   oOptions,
   oDebug,
   oDebugAll,
@@ -114,6 +115,7 @@ static ARGPARSE_OPTS opts[] = {
 
   { aGPGConfList, "gpgconf-list", 256, "@" },
   { aGPGConfTest, "gpgconf-test", 256, "@" },
+  { aUseStandardSocketP, "use-standard-socket-p", 256, "@" }, 
   
   { 301, NULL, 0, N_("@Options:\n ") },
 
@@ -628,7 +630,7 @@ main (int argc, char **argv )
 
   /* Set default options.  */
   parse_rereadable_options (NULL, 0); /* Reset them to default values. */
-#ifdef HAVE_W32_SYSTEM
+#ifdef USE_STANDARD_SOCKET
   use_standard_socket = 1;  /* Under Windows we always use a standard
                                socket.  */
 #endif
@@ -748,6 +750,7 @@ main (int argc, char **argv )
         {
         case aGPGConfList: gpgconf_list = 1; break;
         case aGPGConfTest: gpgconf_list = 2; break;
+        case aUseStandardSocketP: gpgconf_list = 3; break;
         case oBatch: opt.batch=1; break;
 
         case oDebugWait: debug_wait = pargs.r.ret_int; break;
@@ -858,6 +861,8 @@ main (int argc, char **argv )
       log_debug ("... okay\n");
     }
   
+  if (gpgconf_list == 3)
+    agent_exit (!use_standard_socket);
   if (gpgconf_list == 2)
     agent_exit (0);
   if (gpgconf_list)
index 5ca04c0..1fb20d7 100644 (file)
@@ -1,3 +1,13 @@
+2010-05-03  Werner Koch  <wk@g10code.com>
+
+       * asshelp.c (lock_agent_spawning, unlock_agent_spawning): New.
+       (start_new_gpg_agent): Test for configured standard socket and
+       try to fire up the agent in this case.
+
+       * exechelp.c (gnupg_spawn_process_detached): Do not reuse PID for
+       the second fork.
+       (gnupg_wait_process): Do not log a message if EXITCODE is given.
+
 2010-03-17  Werner Koch  <wk@g10code.com>
 
        * asshelp.c (start_new_gpg_agent) [W32]: Use a named mutex to
index 615bb8d..3dc4b22 100644 (file)
@@ -1,5 +1,5 @@
 /* asshelp.c - Helper functions for Assuan
- * Copyright (C) 2002, 2004, 2007, 2009 Free Software Foundation, Inc.
+ * Copyright (C) 2002, 2004, 2007, 2009, 2010 Free Software Foundation, Inc.
  *
  * This file is part of GnuPG.
  *
@@ -27,6 +27,7 @@
 #include <locale.h>
 #endif
 
+#define JNLIB_NEED_LOG_LOGV
 #include "i18n.h"
 #include "util.h"
 #include "exechelp.h"
 #include "status.h" 
 #include "asshelp.h"
 
+/* The type we use for lock_agent_spawning.  */
+#ifdef HAVE_W32_SYSTEM
+# define lock_agent_t HANDLE
+#else
+# define lock_agent_t DOTLOCK
+#endif
+
+
 static gpg_error_t
 send_one_option (assuan_context_t ctx, gpg_err_source_t errsource,
                  const char *name, const char *value, int use_putenv)
@@ -70,7 +79,9 @@ send_pinentry_environment (assuan_context_t ctx,
 
 {
   gpg_error_t err = 0;
+#if defined(HAVE_SETLOCALE)
   char *old_lc = NULL; 
+#endif
   char *dft_lc = NULL;
   const char *dft_ttyname;
   int iterator;
@@ -158,6 +169,77 @@ send_pinentry_environment (assuan_context_t ctx,
 }
 
 
+/* Lock the agent spawning process.  The caller needs to provide the
+   address of a variable to store the lock information.  */
+static gpg_error_t
+lock_agent_spawning (lock_agent_t *lock, const char *homedir)
+{
+#ifdef HAVE_W32_SYSTEM
+  int waitrc;
+
+  (void)homedir; /* Not required. */
+
+  *lock = CreateMutex (NULL, FALSE, "GnuPG_spawn_agent_sentinel");
+  if (!*lock)
+    {
+      log_error ("failed to create the spawn_agent mutex: %s\n",
+                 w32_strerror (-1));
+      return gpg_error (GPG_ERR_GENERAL);
+    }
+
+  waitrc = WaitForSingleObject (*lock, 5000);
+  if (waitrc == WAIT_OBJECT_0)
+    return 0;
+
+  if (waitrc == WAIT_TIMEOUT)
+    log_info ("error waiting for the spawn_agent mutex: timeout\n");
+  else
+    log_info ("error waiting for the spawn_agent mutex: "
+              "(code=%d) %s\n", waitrc, w32_strerror (-1));
+  return gpg_error (GPG_ERR_GENERAL);
+#else /*!HAVE_W32_SYSTEM*/
+  char *fname;
+
+  *lock = NULL;
+
+  fname = make_filename (homedir, "gnupg_spawn_agent_sentinel", NULL);
+  if (!fname)
+    return gpg_error_from_syserror ();
+
+  *lock = create_dotlock (fname);
+  xfree (fname);
+  if (!*lock)
+    return gpg_error_from_syserror ();
+
+  /* FIXME: We should use a timeout of 5000 here - however
+     make_dotlock does not yet support values other than -1 and 0.  */
+  if (make_dotlock (*lock, -1))
+    return gpg_error_from_syserror ();
+
+  return 0;
+#endif /*!HAVE_W32_SYSTEM*/
+}
+
+
+/* Unlock the spawning process.  */
+static void
+unlock_agent_spawning (lock_agent_t *lock)
+{
+  if (*lock)
+    {
+#ifdef HAVE_W32_SYSTEM
+      if (!ReleaseMutex (*lock))
+        log_error ("failed to release the spawn_agent mutex: %s\n",
+                   w32_strerror (-1));
+      CloseHandle (*lock);
+#else /*!HAVE_W32_SYSTEM*/
+      destroy_dotlock (*lock);
+#endif /*!HAVE_W32_SYSTEM*/
+      *lock = NULL;
+    }
+}
+
+
 /* Try to connect to the agent via socket or fork it off and work by
    pipes.  Handle the server's initial greeting.  Returns a new assuan
    context at R_CTX or an error code. */
@@ -177,17 +259,17 @@ start_new_gpg_agent (assuan_context_t *r_ctx,
      of the pipe based server for the lifetime of the process.  */
   static int force_pipe_server = 0;
 
-  gpg_error_t rc = 0;
+  gpg_error_t err = 0;
   char *infostr, *p;
   assuan_context_t ctx;
 
   *r_ctx = NULL;
 
-  rc = assuan_new (&ctx);
-  if (rc)
+  err = assuan_new (&ctx);
+  if (err)
     {
-      log_error ("error allocating assuan context: %s\n", gpg_strerror (rc));
-      return rc;
+      log_error ("error allocating assuan context: %s\n", gpg_strerror (err));
+      return err;
     }
 
  restart:
@@ -195,13 +277,16 @@ start_new_gpg_agent (assuan_context_t *r_ctx,
   if (!infostr || !*infostr)
     {
       char *sockname;
+      const char *argv[3];
+      pid_t pid;
+      int excode;
 
       /* First check whether we can connect at the standard
          socket.  */
       sockname = make_filename (homedir, "S.gpg-agent", NULL);
-      rc = assuan_socket_connect (ctx, sockname, 0, 0);
+      err = assuan_socket_connect (ctx, sockname, 0, 0);
 
-      if (rc)
+      if (err)
         {
           /* With no success start a new server.  */
           if (verbose)
@@ -224,102 +309,86 @@ start_new_gpg_agent (assuan_context_t *r_ctx,
           if (!agent_program || !*agent_program)
             agent_program = gnupg_module_name (GNUPG_MODULE_NAME_AGENT);
 
-#ifdef HAVE_W32_SYSTEM
-          {
-            /* Under Windows we start the server in daemon mode.  This
-               is because the default is to use the standard socket
-               and thus there is no need for the GPG_AGENT_INFO
-               envvar.  This is possible as we don't have a real unix
-               domain socket but use a plain file and thus there is no
-               need to care about non-local file systems.  We use a
-               named mutex to interlock the spawning.  There is just
-               one problem with that: If gpg-agent needs more than 3
-               seconds to come up and listen on the socket we might
-               still spawn another agent.  However this is no serious
-               problem because an agent detects this and handles it.
-               Thus the mutex merely helps to save resources in the
-               most common cases.  */
-            const char *argv[3];
-            HANDLE mutex;
-            int waitrc;
-
-            argv[0] = "--daemon";
-            argv[1] = "--use-standard-socket"; 
-            argv[2] = NULL;  
-
-            mutex = CreateMutex (NULL, FALSE, "GnuPG_spawn_agent_sentinel");
-            if (!mutex)
-              {
-                log_error ("failed to create the spawn_agent mutex: %s\n",
-                           w32_strerror (-1));
-                rc = gpg_error (GPG_ERR_GENERAL);
-              }
-            else if ((waitrc = WaitForSingleObject (mutex, 5000))
-                     == WAIT_OBJECT_0)
-              {
-                rc = assuan_socket_connect (&ctx, sockname, 0);
-                if (rc)
-                  {
-                    /* Still not available.  */
-                    rc = gnupg_spawn_process_detached (agent_program,
-                                                       argv, NULL);
-                    if (rc)
-                      log_debug ("failed to start agent `%s': %s\n",
-                                 agent_program, gpg_strerror (rc));
-                    else
-                      {
-                        /* Give the agent some time to prepare itself. */
-                        gnupg_sleep (3);
-                        /* Now try again to connect the agent.  */
-                        rc = assuan_socket_connect (&ctx, sockname, 0);
-                      }
-                  }
-                if (!ReleaseMutex (mutex))
-                  log_error ("failed to release the spawn_agent mutex: %s\n",
-                             w32_strerror (-1));
-              }
-            else if (waitrc == WAIT_TIMEOUT)
-              {
-                log_info ("error waiting for the spawn_agent mutex: timeout\n");
-                rc = gpg_error (GPG_ERR_GENERAL);
-              }
-            else
-              {
-                log_debug ("error waiting for the spawn_agent mutex: "
-                           "(code=%d) %s\n", waitrc, w32_strerror (-1));
-                rc = gpg_error (GPG_ERR_GENERAL);
-              }
-            
-            if (mutex)
-              CloseHandle (mutex);
-        }
-#else /*!HAVE_W32_SYSTEM*/
-          {
-            const char *pgmname;
-            const char *argv[3];
-            int no_close_list[3];
-            int i;
-
-            if ( !(pgmname = strrchr (agent_program, '/')))
-              pgmname = agent_program;
-            else
-              pgmname++;
-            
-            argv[0] = pgmname;
-            argv[1] = "--server";
-            argv[2] = NULL;
-            
-            i=0;
-            if (log_get_fd () != -1)
-              no_close_list[i++] = assuan_fd_from_posix_fd (log_get_fd ());
-            no_close_list[i++] = assuan_fd_from_posix_fd (fileno (stderr));
-            no_close_list[i] = -1;
-            
-            /* Connect to the agent and perform initial handshaking. */
-            rc = assuan_pipe_connect (ctx, agent_program, argv,
-                                      no_close_list, NULL, NULL, 0);
-          }
-#endif /*!HAVE_W32_SYSTEM*/
+          argv[0] = "--use-standard-socket-p"; 
+          argv[1] = NULL;  
+          err = gnupg_spawn_process_fd (agent_program, argv, -1, -1, -1, &pid);
+          if (err)
+            log_debug ("starting `%s' for testing failed: %s\n",
+                       agent_program, gpg_strerror (err));
+          else if ((err = gnupg_wait_process (agent_program, pid, &excode)))
+            {
+              if (excode == -1)
+                log_debug ("running `%s' for testing failed: %s\n",
+                           agent_program, gpg_strerror (err));
+            }          
+
+          if (!err && !excode)
+            {
+              /* If the agent has been configured for use with a
+                 standard socket, an environment variable is not
+                 required and thus we we can savely start the agent
+                 here.  */
+              lock_agent_t lock;
+
+              argv[0] = "--daemon";
+              argv[1] = "--use-standard-socket"; 
+              argv[2] = NULL;  
+
+              if (!(err = lock_agent_spawning (&lock, homedir))
+                  && assuan_socket_connect (ctx, sockname, 0, 0))
+                {
+                  err = gnupg_spawn_process_detached (agent_program, argv,NULL);
+                  if (err)
+                    log_error ("failed to start agent `%s': %s\n",
+                               agent_program, gpg_strerror (err));
+                  else
+                    {
+                      int i;
+
+                      if (verbose)
+                        log_info (_("waiting %d seconds for the agent "
+                                    "to come up\n"), 5);
+                      for (i=0; i < 5; i++)
+                        {
+                          gnupg_sleep (1);
+                          err = assuan_socket_connect (ctx, sockname, 0, 0);
+                          if (!err)
+                            break;
+                        }
+                    }
+                }
+
+              unlock_agent_spawning (&lock);
+            }
+          else
+            {
+              /* If using the standard socket is not the default we
+                 start the agent as a pipe server which gives us most
+                 of the required features except for passphrase
+                 caching etc.  */
+              const char *pgmname;
+              int no_close_list[3];
+              int i;
+              
+              if ( !(pgmname = strrchr (agent_program, '/')))
+                pgmname = agent_program;
+              else
+                pgmname++;
+              
+              argv[0] = pgmname;
+              argv[1] = "--server";
+              argv[2] = NULL;
+              
+              i=0;
+              if (log_get_fd () != -1)
+                no_close_list[i++] = assuan_fd_from_posix_fd (log_get_fd ());
+              no_close_list[i++] = assuan_fd_from_posix_fd (fileno (stderr));
+              no_close_list[i] = -1;
+              
+              /* Connect to the agent and perform initial handshaking. */
+              err = assuan_pipe_connect (ctx, agent_program, argv,
+                                         no_close_list, NULL, NULL, 0);
+            }
         }
       xfree (sockname);
     }
@@ -350,9 +419,9 @@ start_new_gpg_agent (assuan_context_t *r_ctx,
           goto restart;
         }
 
-      rc = assuan_socket_connect (ctx, infostr, pid, 0);
+      err = assuan_socket_connect (ctx, infostr, pid, 0);
       xfree (infostr);
-      if (gpg_err_code (rc) == GPG_ERR_ASS_CONNECT_FAILED)
+      if (gpg_err_code (err) == GPG_ERR_ASS_CONNECT_FAILED)
         {
           log_info (_("can't connect to the agent - trying fall back\n"));
           force_pipe_server = 1;
@@ -360,9 +429,9 @@ start_new_gpg_agent (assuan_context_t *r_ctx,
         }
     }
 
-  if (rc)
+  if (err)
     {
-      log_error ("can't connect to the agent: %s\n", gpg_strerror (rc));
+      log_error ("can't connect to the agent: %s\n", gpg_strerror (err));
       assuan_release (ctx);
       return gpg_error (GPG_ERR_NO_AGENT);
     }
@@ -370,16 +439,16 @@ start_new_gpg_agent (assuan_context_t *r_ctx,
   if (debug)
     log_debug ("connection to agent established\n");
 
-  rc = assuan_transact (ctx, "RESET",
+  err = assuan_transact (ctx, "RESET",
                         NULL, NULL, NULL, NULL, NULL, NULL);
-  if (!rc)
-    rc = send_pinentry_environment (ctx, errsource,
+  if (!err)
+    err = send_pinentry_environment (ctx, errsource,
                                     opt_lc_ctype, opt_lc_messages,
                                     session_env);
-  if (rc)
+  if (err)
     {
       assuan_release (ctx);
-      return rc;
+      return err;
     }
 
   *r_ctx = ctx;
index a5e25fd..cd9ba7b 100644 (file)
@@ -872,9 +872,11 @@ gnupg_wait_process (const char *pgmname, pid_t pid, int *exitcode)
     }
   else if (WIFEXITED (status) && WEXITSTATUS (status))
     {
-      log_error (_("error running `%s': exit status %d\n"), pgmname,
-                 WEXITSTATUS (status));
-      if (exitcode)
+      
+      if (!exitcode)
+        log_error (_("error running `%s': exit status %d\n"), pgmname,
+                   WEXITSTATUS (status));
+      else
         *exitcode = WEXITSTATUS (status);
       ec = GPG_ERR_GENERAL;
     }
@@ -1002,13 +1004,15 @@ gnupg_spawn_process_detached (const char *pgmname, const char *argv[],
     }
   if (!pid)
     {
+      pid_t pid2; 
+
       gcry_control (GCRYCTL_TERM_SECMEM);
       if (setsid() == -1 || chdir ("/"))
         _exit (1);
-      pid = fork (); /* Double fork to let init takes over the new child. */
-      if (pid == (pid_t)(-1))
+      pid2 = fork (); /* Double fork to let init takes over the new child. */
+      if (pid2 == (pid_t)(-1))
         _exit (1);
-      if (pid)
+      if (pid2)
         _exit (0);  /* Let the parent exit immediately. */
 
       if (envp)
index d7e18f1..4fd3e4c 100644 (file)
@@ -75,6 +75,7 @@ use_bzip2=yes
 use_exec=yes
 disable_keyserver_path=no
 use_ccid_driver=yes
+use_standard_socket=no
 
 GNUPG_BUILD_PROGRAM(gpg, yes)
 GNUPG_BUILD_PROGRAM(gpgsm, yes)
@@ -251,17 +252,17 @@ if test "$use_exec" = yes ; then
        [enable email keyserver interface only]),
       try_mailto=$enableval, try_mailto=no)
     AC_MSG_RESULT($try_mailto)
-    fi
+  fi
 
-    AC_MSG_CHECKING([whether keyserver exec-path is enabled])
-    AC_ARG_ENABLE(keyserver-path,
+  AC_MSG_CHECKING([whether keyserver exec-path is enabled])
+  AC_ARG_ENABLE(keyserver-path,
       AC_HELP_STRING([--disable-keyserver-path],
-        [disable the exec-path option for keyserver helpers]),
-      [if test "$enableval" = no ; then
-         disable_keyserver_path=yes
-      fi],enableval=yes)
-    AC_MSG_RESULT($enableval)
-  fi
+           [disable the exec-path option for keyserver helpers]),
+           [if test "$enableval" = no ; then
+              disable_keyserver_path=yes
+           fi],enableval=yes)
+  AC_MSG_RESULT($enableval)
+fi
 
 
 #
@@ -586,6 +587,30 @@ if test "$disable_keyserver_path" = yes; then
               [Defined to disable exec-path for keyserver helpers])
 fi
 
+#
+# Allows enabling the use of a standard socket by default This is
+# gpg-agent's option --[no-]use-standard-socket.  For Windows we force
+# the use of this.
+#
+AC_MSG_CHECKING([whether to use a standard socket by default])
+AC_ARG_ENABLE(standard-socket,
+              AC_HELP_STRING([--enable-standard-socket],
+                             [use a standard socket for the agent by default]),
+              use_standard_socket=$enableval)
+tmp=""
+if test "$use_standard_socket" != yes; then
+  if test "$have_w32_system" = yes; then
+    use_standard_socket=yes
+    tmp=" (forced)"
+  fi
+fi
+AC_MSG_RESULT($use_standard_socket$tmp)
+if test "$use_standard_socket" = yes; then
+  AC_DEFINE(USE_STANDARD_SOCKET,1,
+            [Use a standard socket for the agent by default])
+fi
+
+
 # (These need to go after AC_PROG_CC so that $EXEEXT is defined)
 AC_DEFINE_UNQUOTED(EXEEXT,"$EXEEXT",[The executable file extension, if any])
 
index 344f412..33332a4 100644 (file)
@@ -435,7 +435,11 @@ a random socket below a temporary directory.  Tools connecting to
 environment variable @var{GPG_AGENT_INFO} and then fall back to this
 socket.  This option may not be used if the home directory is mounted as
 a remote file system.  Note, that @option{--use-standard-socket} is the
-default on Windows systems.
+default on Windows systems.  The default may be changed at build time.
+It is possible to test at runtime whether the agent has been configured
+for use with the standard socket by issuing the command
+@command{gpg-agent --use-standard-socket-p} which returns success if the
+standard socket option has been enabled.
 
 
 @item --display @var{string}