Start the agent on demand if option --enable-standard socket has been

enabled.
This commit is contained in:
Werner Koch 2010-05-04 09:56:42 +00:00
parent 830af45ca2
commit 7d0aa53f7f
9 changed files with 262 additions and 129 deletions

View File

@ -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.

8
NEWS
View File

@ -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)
-------------------------------------------------

View 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

View 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)

View 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

View 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"
@ -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;

View 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)

View 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,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])

View 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}