diff --git a/ChangeLog b/ChangeLog index 1121a886f..c27654a13 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,7 @@ +2010-05-04 Werner Koch + + * configure.ac: Add option --enable-standard-socket. + 2010-03-09 Werner Koch Release 2.0.15. diff --git a/NEWS b/NEWS index 7730b5b12..40053d174 100644 --- 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) ------------------------------------------------- diff --git a/agent/ChangeLog b/agent/ChangeLog index 0bca557c9..e604ae46e 100644 --- a/agent/ChangeLog +++ b/agent/ChangeLog @@ -1,3 +1,7 @@ +2010-05-04 Werner Koch + + * gpg-agent.c (main): Add command --use-standard-socket-p. + 2010-05-03 Werner Koch * gpg-agent.c (check_own_socket_thread): Do not release SOCKNAME diff --git a/agent/gpg-agent.c b/agent/gpg-agent.c index eec2ca1e0..57d61b379 100644 --- a/agent/gpg-agent.c +++ b/agent/gpg-agent.c @@ -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) diff --git a/common/ChangeLog b/common/ChangeLog index 5ca04c07d..1fb20d769 100644 --- a/common/ChangeLog +++ b/common/ChangeLog @@ -1,3 +1,13 @@ +2010-05-03 Werner Koch + + * 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 * asshelp.c (start_new_gpg_agent) [W32]: Use a named mutex to diff --git a/common/asshelp.c b/common/asshelp.c index 615bb8d3b..3dc4b223b 100644 --- a/common/asshelp.c +++ b/common/asshelp.c @@ -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 #endif +#define JNLIB_NEED_LOG_LOGV #include "i18n.h" #include "util.h" #include "exechelp.h" @@ -34,6 +35,14 @@ #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] = "--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)); + } - argv[0] = "--daemon"; - argv[1] = "--use-standard-socket"; - argv[2] = NULL; + 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; - 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; + argv[0] = "--daemon"; + argv[1] = "--use-standard-socket"; + argv[2] = NULL; - 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*/ + 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; diff --git a/common/exechelp.c b/common/exechelp.c index a5e25fd5d..cd9ba7b40 100644 --- a/common/exechelp.c +++ b/common/exechelp.c @@ -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) diff --git a/configure.ac b/configure.ac index d7e18f198..4fd3e4c3f 100644 --- a/configure.ac +++ b/configure.ac @@ -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,18 +252,18 @@ if test "$use_exec" = yes ; then [enable email keyserver interface only]), try_mailto=$enableval, try_mailto=no) AC_MSG_RESULT($try_mailto) - fi - - 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_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 + # # Check for the key/uid cache size. This can't be zero, but can be @@ -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]) diff --git a/doc/gpg-agent.texi b/doc/gpg-agent.texi index 344f41250..33332a468 100644 --- a/doc/gpg-agent.texi +++ b/doc/gpg-agent.texi @@ -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}