1
0
mirror of git://git.gnupg.org/gnupg.git synced 2024-11-10 21:38:50 +01:00

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> 2010-03-09 Werner Koch <wk@g10code.com>
Release 2.0.15. Release 2.0.15.

8
NEWS
View File

@ -1,6 +1,14 @@
Noteworthy changes in version 2.0.16 (unreleased) 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) 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> 2010-05-03 Werner Koch <wk@g10code.com>
* gpg-agent.c (check_own_socket_thread): Do not release SOCKNAME * gpg-agent.c (check_own_socket_thread): Do not release SOCKNAME

View File

@ -1,6 +1,6 @@
/* gpg-agent.c - The GnuPG Agent /* gpg-agent.c - The GnuPG Agent
* Copyright (C) 2000, 2001, 2002, 2003, 2004, 2005, * 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. * This file is part of GnuPG.
* *
@ -60,6 +60,7 @@ enum cmd_and_opt_values
oNoVerbose = 500, oNoVerbose = 500,
aGPGConfList, aGPGConfList,
aGPGConfTest, aGPGConfTest,
aUseStandardSocketP,
oOptions, oOptions,
oDebug, oDebug,
oDebugAll, oDebugAll,
@ -114,6 +115,7 @@ static ARGPARSE_OPTS opts[] = {
{ aGPGConfList, "gpgconf-list", 256, "@" }, { aGPGConfList, "gpgconf-list", 256, "@" },
{ aGPGConfTest, "gpgconf-test", 256, "@" }, { aGPGConfTest, "gpgconf-test", 256, "@" },
{ aUseStandardSocketP, "use-standard-socket-p", 256, "@" },
{ 301, NULL, 0, N_("@Options:\n ") }, { 301, NULL, 0, N_("@Options:\n ") },
@ -628,7 +630,7 @@ main (int argc, char **argv )
/* Set default options. */ /* Set default options. */
parse_rereadable_options (NULL, 0); /* Reset them to default values. */ 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 use_standard_socket = 1; /* Under Windows we always use a standard
socket. */ socket. */
#endif #endif
@ -748,6 +750,7 @@ main (int argc, char **argv )
{ {
case aGPGConfList: gpgconf_list = 1; break; case aGPGConfList: gpgconf_list = 1; break;
case aGPGConfTest: gpgconf_list = 2; break; case aGPGConfTest: gpgconf_list = 2; break;
case aUseStandardSocketP: gpgconf_list = 3; break;
case oBatch: opt.batch=1; break; case oBatch: opt.batch=1; break;
case oDebugWait: debug_wait = pargs.r.ret_int; break; case oDebugWait: debug_wait = pargs.r.ret_int; break;
@ -858,6 +861,8 @@ main (int argc, char **argv )
log_debug ("... okay\n"); log_debug ("... okay\n");
} }
if (gpgconf_list == 3)
agent_exit (!use_standard_socket);
if (gpgconf_list == 2) if (gpgconf_list == 2)
agent_exit (0); agent_exit (0);
if (gpgconf_list) 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> 2010-03-17 Werner Koch <wk@g10code.com>
* asshelp.c (start_new_gpg_agent) [W32]: Use a named mutex to * asshelp.c (start_new_gpg_agent) [W32]: Use a named mutex to

View File

@ -1,5 +1,5 @@
/* asshelp.c - Helper functions for Assuan /* 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. * This file is part of GnuPG.
* *
@ -27,6 +27,7 @@
#include <locale.h> #include <locale.h>
#endif #endif
#define JNLIB_NEED_LOG_LOGV
#include "i18n.h" #include "i18n.h"
#include "util.h" #include "util.h"
#include "exechelp.h" #include "exechelp.h"
@ -34,6 +35,14 @@
#include "status.h" #include "status.h"
#include "asshelp.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 static gpg_error_t
send_one_option (assuan_context_t ctx, gpg_err_source_t errsource, send_one_option (assuan_context_t ctx, gpg_err_source_t errsource,
const char *name, const char *value, int use_putenv) 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; gpg_error_t err = 0;
#if defined(HAVE_SETLOCALE)
char *old_lc = NULL; char *old_lc = NULL;
#endif
char *dft_lc = NULL; char *dft_lc = NULL;
const char *dft_ttyname; const char *dft_ttyname;
int iterator; 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 /* 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 pipes. Handle the server's initial greeting. Returns a new assuan
context at R_CTX or an error code. */ 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. */ of the pipe based server for the lifetime of the process. */
static int force_pipe_server = 0; static int force_pipe_server = 0;
gpg_error_t rc = 0; gpg_error_t err = 0;
char *infostr, *p; char *infostr, *p;
assuan_context_t ctx; assuan_context_t ctx;
*r_ctx = NULL; *r_ctx = NULL;
rc = assuan_new (&ctx); err = assuan_new (&ctx);
if (rc) if (err)
{ {
log_error ("error allocating assuan context: %s\n", gpg_strerror (rc)); log_error ("error allocating assuan context: %s\n", gpg_strerror (err));
return rc; return err;
} }
restart: restart:
@ -195,13 +277,16 @@ start_new_gpg_agent (assuan_context_t *r_ctx,
if (!infostr || !*infostr) if (!infostr || !*infostr)
{ {
char *sockname; char *sockname;
const char *argv[3];
pid_t pid;
int excode;
/* First check whether we can connect at the standard /* First check whether we can connect at the standard
socket. */ socket. */
sockname = make_filename (homedir, "S.gpg-agent", NULL); 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. */ /* With no success start a new server. */
if (verbose) if (verbose)
@ -224,79 +309,64 @@ start_new_gpg_agent (assuan_context_t *r_ctx,
if (!agent_program || !*agent_program) if (!agent_program || !*agent_program)
agent_program = gnupg_module_name (GNUPG_MODULE_NAME_AGENT); agent_program = gnupg_module_name (GNUPG_MODULE_NAME_AGENT);
#ifdef 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)))
{ {
/* Under Windows we start the server in daemon mode. This if (excode == -1)
is because the default is to use the standard socket log_debug ("running `%s' for testing failed: %s\n",
and thus there is no need for the GPG_AGENT_INFO agent_program, gpg_strerror (err));
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 if (!err && !excode)
named mutex to interlock the spawning. There is just {
one problem with that: If gpg-agent needs more than 3 /* If the agent has been configured for use with a
seconds to come up and listen on the socket we might standard socket, an environment variable is not
still spawn another agent. However this is no serious required and thus we we can savely start the agent
problem because an agent detects this and handles it. here. */
Thus the mutex merely helps to save resources in the lock_agent_t lock;
most common cases. */
const char *argv[3];
HANDLE mutex;
int waitrc;
argv[0] = "--daemon"; argv[0] = "--daemon";
argv[1] = "--use-standard-socket"; argv[1] = "--use-standard-socket";
argv[2] = NULL; argv[2] = NULL;
mutex = CreateMutex (NULL, FALSE, "GnuPG_spawn_agent_sentinel"); if (!(err = lock_agent_spawning (&lock, homedir))
if (!mutex) && assuan_socket_connect (ctx, sockname, 0, 0))
{ {
log_error ("failed to create the spawn_agent mutex: %s\n", err = gnupg_spawn_process_detached (agent_program, argv,NULL);
w32_strerror (-1)); if (err)
rc = gpg_error (GPG_ERR_GENERAL); log_error ("failed to start agent `%s': %s\n",
} agent_program, gpg_strerror (err));
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 else
{ {
/* Give the agent some time to prepare itself. */ int i;
gnupg_sleep (3);
/* Now try again to connect the agent. */ if (verbose)
rc = assuan_socket_connect (&ctx, sockname, 0); log_info (_("waiting %d seconds for the agent "
} "to come up\n"), 5);
} for (i=0; i < 5; i++)
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"); gnupg_sleep (1);
rc = gpg_error (GPG_ERR_GENERAL); err = assuan_socket_connect (ctx, sockname, 0, 0);
if (!err)
break;
}
} }
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) unlock_agent_spawning (&lock);
CloseHandle (mutex);
} }
#else /*!HAVE_W32_SYSTEM*/ 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; const char *pgmname;
const char *argv[3];
int no_close_list[3]; int no_close_list[3];
int i; int i;
@ -316,10 +386,9 @@ start_new_gpg_agent (assuan_context_t *r_ctx,
no_close_list[i] = -1; no_close_list[i] = -1;
/* Connect to the agent and perform initial handshaking. */ /* Connect to the agent and perform initial handshaking. */
rc = assuan_pipe_connect (ctx, agent_program, argv, err = assuan_pipe_connect (ctx, agent_program, argv,
no_close_list, NULL, NULL, 0); no_close_list, NULL, NULL, 0);
} }
#endif /*!HAVE_W32_SYSTEM*/
} }
xfree (sockname); xfree (sockname);
} }
@ -350,9 +419,9 @@ start_new_gpg_agent (assuan_context_t *r_ctx,
goto restart; goto restart;
} }
rc = assuan_socket_connect (ctx, infostr, pid, 0); err = assuan_socket_connect (ctx, infostr, pid, 0);
xfree (infostr); 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")); log_info (_("can't connect to the agent - trying fall back\n"));
force_pipe_server = 1; 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); assuan_release (ctx);
return gpg_error (GPG_ERR_NO_AGENT); return gpg_error (GPG_ERR_NO_AGENT);
} }
@ -370,16 +439,16 @@ start_new_gpg_agent (assuan_context_t *r_ctx,
if (debug) if (debug)
log_debug ("connection to agent established\n"); log_debug ("connection to agent established\n");
rc = assuan_transact (ctx, "RESET", err = assuan_transact (ctx, "RESET",
NULL, NULL, NULL, NULL, NULL, NULL); NULL, NULL, NULL, NULL, NULL, NULL);
if (!rc) if (!err)
rc = send_pinentry_environment (ctx, errsource, err = send_pinentry_environment (ctx, errsource,
opt_lc_ctype, opt_lc_messages, opt_lc_ctype, opt_lc_messages,
session_env); session_env);
if (rc) if (err)
{ {
assuan_release (ctx); assuan_release (ctx);
return rc; return err;
} }
*r_ctx = ctx; *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)) else if (WIFEXITED (status) && WEXITSTATUS (status))
{ {
if (!exitcode)
log_error (_("error running `%s': exit status %d\n"), pgmname, log_error (_("error running `%s': exit status %d\n"), pgmname,
WEXITSTATUS (status)); WEXITSTATUS (status));
if (exitcode) else
*exitcode = WEXITSTATUS (status); *exitcode = WEXITSTATUS (status);
ec = GPG_ERR_GENERAL; ec = GPG_ERR_GENERAL;
} }
@ -1002,13 +1004,15 @@ gnupg_spawn_process_detached (const char *pgmname, const char *argv[],
} }
if (!pid) if (!pid)
{ {
pid_t pid2;
gcry_control (GCRYCTL_TERM_SECMEM); gcry_control (GCRYCTL_TERM_SECMEM);
if (setsid() == -1 || chdir ("/")) if (setsid() == -1 || chdir ("/"))
_exit (1); _exit (1);
pid = fork (); /* Double fork to let init takes over the new child. */ pid2 = fork (); /* Double fork to let init takes over the new child. */
if (pid == (pid_t)(-1)) if (pid2 == (pid_t)(-1))
_exit (1); _exit (1);
if (pid) if (pid2)
_exit (0); /* Let the parent exit immediately. */ _exit (0); /* Let the parent exit immediately. */
if (envp) if (envp)

View File

@ -75,6 +75,7 @@ use_bzip2=yes
use_exec=yes use_exec=yes
disable_keyserver_path=no disable_keyserver_path=no
use_ccid_driver=yes use_ccid_driver=yes
use_standard_socket=no
GNUPG_BUILD_PROGRAM(gpg, yes) GNUPG_BUILD_PROGRAM(gpg, yes)
GNUPG_BUILD_PROGRAM(gpgsm, yes) GNUPG_BUILD_PROGRAM(gpgsm, yes)
@ -586,6 +587,30 @@ if test "$disable_keyserver_path" = yes; then
[Defined to disable exec-path for keyserver helpers]) [Defined to disable exec-path for keyserver helpers])
fi 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) # (These need to go after AC_PROG_CC so that $EXEEXT is defined)
AC_DEFINE_UNQUOTED(EXEEXT,"$EXEEXT",[The executable file extension, if any]) 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 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 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 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} @item --display @var{string}