auto start the agent if --use-standard-socket is in use.
authorWerner Koch <wk@gnupg.org>
Mon, 3 May 2010 15:23:10 +0000 (15:23 +0000)
committerWerner Koch <wk@gnupg.org>
Mon, 3 May 2010 15:23:10 +0000 (15:23 +0000)
ChangeLog
agent/ChangeLog
agent/gpg-agent.c
common/ChangeLog
common/asshelp.c
common/exechelp-posix.c
configure.ac
doc/gpg-agent.texi

index 9641bf0..4865dd7 100644 (file)
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,8 @@
+2010-04-30  Werner Koch  <wk@g10code.com>
+
+       * configure.ac: Add option --enable-standard-socket.
+       (USE_STANDARD_SOCKET): ac_define it.
+
 2010-04-14  Werner Koch  <wk@g10code.com>
 
        * Makefile.am (keyserver) [W32CE]: Do not build for now.
index 176f4fa..830b4c2 100644 (file)
@@ -1,3 +1,12 @@
+2010-05-03  Werner Koch  <wk@g10code.com>
+
+       * gpg-agent.c (check_own_socket_thread): Do not release SOCKNAME
+       too early.
+
+2010-04-30  Werner Koch  <wk@g10code.com>
+
+       * gpg-agent.c (main): Add command --use-standard-socket-p.
+
 2010-04-26  Werner Koch  <wk@g10code.com>
 
        * gpg-agent.c (create_server_socket) [W32]: Also check for EEXIST.
index e30adb4..89027b5 100644 (file)
@@ -62,6 +62,7 @@ enum cmd_and_opt_values
   oNoVerbose = 500,
   aGPGConfList,
   aGPGConfTest,
+  aUseStandardSocketP,
   oOptions,
   oDebug,
   oDebugAll,
@@ -98,6 +99,7 @@ enum cmd_and_opt_values
   oEnablePassphraseHistory,
   oUseStandardSocket,
   oNoUseStandardSocket,
+  oStandardSocketP,
   oFakedSystemTime,
 
   oIgnoreCacheForSigning,
@@ -116,6 +118,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 ") },
 
@@ -748,6 +751,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,9 +862,11 @@ main (int argc, char **argv )
       log_debug ("... okay\n");
     }
   
-  if (gpgconf_list == 2)
+  if (gpgconf_list == 3)
+    agent_exit (!use_standard_socket);
+  else if (gpgconf_list == 2)
     agent_exit (0);
-  if (gpgconf_list)
+  else if (gpgconf_list)
     {
       char *filename;
       char *filename_esc;
@@ -2097,7 +2103,6 @@ check_own_socket_thread (void *arg)
   check_own_socket_running++;
 
   rc = assuan_new (&ctx);
-  xfree (sockname);
   if (rc)
     {
       log_error ("can't allocate assuan context: %s\n", gpg_strerror (rc));
@@ -2133,6 +2138,7 @@ check_own_socket_thread (void *arg)
   xfree (buffer);
 
  leave:
+  xfree (sockname);
   if (ctx)
     assuan_release (ctx);
   if (rc)
@@ -2153,7 +2159,7 @@ check_own_socket_thread (void *arg)
 
 /* Check whether we are still listening on our own socket.  In case
    another gpg-agent process started after us has taken ownership of
-   our socket, we woul linger around without any real taks.  Thus we
+   our socket, we woulf linger around without any real task.  Thus we
    better check once in a while whether we are really needed.  */
 static void
 check_own_socket (void)
index e9e3e50..14ca0ef 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-posix.c (gnupg_wait_process): Do not log a message if
+       EXITCODE is given.
+       (gnupg_spawn_process_detached): Do not reuse PID for the second fork.
+
 2010-04-26  Werner Koch  <wk@g10code.com>
 
        * utf8conv.c (load_libiconv) [W32CE]: No libiconv warning
index d07adf7..95c7747 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.
  *
 #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_t
+#endif
+
 
 /* A bitfield that specifies the assuan categories to log.  This is
    identical to the default log handler of libassuan.  We need to do
@@ -209,6 +216,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. */
@@ -228,17 +306,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:
@@ -246,13 +324,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)
@@ -275,59 +356,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. */
-            const char *argv[3];
-
-            argv[0] = "--daemon";
-            argv[1] = "--use-standard-socket"; 
-            argv[2] = NULL;  
-
-            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, 0);
-              }
-          }
-#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);
     }
@@ -358,9 +466,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;
@@ -368,9 +476,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);
     }
@@ -378,16 +486,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 7b94ee7..aaf6287 100644 (file)
@@ -410,7 +410,8 @@ gnupg_spawn_process_fd (const char *pgmname, const char *argv[],
    diagnostics. Returns 0 if the process succeeded, GPG_ERR_GENERAL
    for any failures of the spawned program or other error codes.  If
    EXITCODE is not NULL the exit code of the process is stored at this
-   address or -1 if it could not be retrieved. */
+   address or -1 if it could not be retrieved and no error message is
+   logged.  */
 gpg_error_t
 gnupg_wait_process (const char *pgmname, pid_t pid, int *exitcode)
 {
@@ -443,9 +444,10 @@ 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;
     }
@@ -497,13 +499,16 @@ 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 take 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 337f22f..280e408 100644 (file)
@@ -79,6 +79,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)
@@ -266,17 +267,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
+  AC_MSG_RESULT($enableval)
+fi
 
 
 #
@@ -625,6 +626,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 the 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..bd47eb1 100644 (file)
@@ -435,8 +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}
 @itemx --ttyname @var{string}