1
0
Fork 0
mirror of git://git.gnupg.org/gnupg.git synced 2025-07-02 22:46:30 +02: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,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)