auto start the agent if --use-standard-socket is in use.

This commit is contained in:
Werner Koch 2010-05-03 15:23:10 +00:00
parent 03d34be425
commit 8524ac000c
8 changed files with 256 additions and 85 deletions

View File

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

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

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

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

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.
*
@ -35,6 +35,13 @@
#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] = "--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;
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;
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);
}
@ -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;

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

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

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