I'm struggling with some issues while scripting gpg with bash on a Debian 6.0.6 box. I have a script that does a batch of operations and wants to make sure that a gpg-agent is available before it attempts to proceed.

Since gpg-agent will take no action and return success if launched when already running, ensuring the agent is present is as simple as:

eval $(gpg-agent --daemon)

gpg-agent start, or will report:

gpg-agent[21927]: a gpg-agent is already running - not starting a new one

and return 0 (success) if already running.

The problem arises when an agent is already running in another session. gpg-agent says that it's already running ... but gpg its self then claims that it's unavailable.

$ gpg-agent --version
gpg-agent (GnuPG) 2.0.19
libgcrypt 1.5.0
$ gpg --version
gpg (GnuPG) 1.4.13

$ eval $(gpg-agent --daemon)
gpg-agent[21927]: a gpg-agent is already running - not starting a new one
$ gpg -d demo-file.asc
gpg: gpg-agent is not available in this session

This leaves me frustrated and confused. It appears that gpg-agent is detecting the agent a different way to gpg its self. Worse, gpg offers no way to ask if the agent is available in a scriptable way, much as it likes to silently ignore recipients with unusable keys and still return success, so it's very hard to detect this problem before beginning the batch. I don't want to get into parsing gpg's output for i18n reasons among others.

You can reproduce this by ensuring you don't have a gpg-agent running or have GPG_AGENT_INFO set, then in one terminal running eval $(gpg-agent --daemon) and in another terminal running the above. You'll note that gpg-agent says it's already running, but gpg fails to connect to the agent.


UPDATE: gpg-agent detects another agent by looking for a socket file in a well-known location and writing to it to test for aliveness, a per this strace:

socket(PF_FILE, SOCK_STREAM, 0)         = 5
connect(5, {sa_family=AF_FILE, sun_path="/home/craig/.gnupg/S.gpg-agent"}, 32) = 0
fcntl(5, F_GETFL)                       = 0x2 (flags O_RDWR)
fcntl(5, F_GETFL)                       = 0x2 (flags O_RDWR)
select(6, [5], NULL, NULL, {0, 0})      = 1 (in [5], left {0, 0})
read(5, "OK Pleased to meet you, process "..., 1002) = 38
mmap(NULL, 8192, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7f41a3e61000
write(2, "gpg-agent: gpg-agent running and"..., 43gpg-agent: gpg-agent running and available
) = 43

while GnuPG seems to look only at the environment, ignoring the well-known socket location. In common/simple-pwquery.c:

/* Try to open a connection to the agent, send all options and return
   the file descriptor for the connection.  Return -1 in case of
   error. */
static int
agent_open (int *rfd)
  int rc;
  int fd;
  char *infostr, *p;
  struct sockaddr_un client_addr;
  size_t len;
  int prot;
  char line[200];
  int nread;

  *rfd = -1;
  infostr = getenv ( "GPG_AGENT_INFO" );
  if ( !infostr || !*infostr )
    infostr = default_gpg_agent_info;
  if ( !infostr || !*infostr )
      log_error (_("gpg-agent is not available in this session\n"));
      return SPWQ_NO_AGENT;
    /* blah blah blah truncated blah */

I don't really want to kill the agent just to make sure I can start it again, and there's no standard place where the user's agent might write an environment file. Worse, I can't even test for the presence of GPG_AGENT_INFO in the environment since that could refer to a stale (dead) agent that's since been replaced ... and neither gpg nor gpg-agent provide a command line option to ping the agent and return true if it's ok.


4 Answers 4

  1. You can check the exit code of gpg-connect-agent /bye
  2. You can check whether the socket given in $GPG_AGENT_INFO exists. That should be enough but you can also check with fuser or lsof whether the process given in $GPG_AGENT_INFO is the one that has opened the socket. And if you want to be really exhaustive you can also check whether /proc/$PID/exe is a link to /usr/bin/gpg-agent (or whatever).
  • Unfortunately, neither of these resolve the issue. (1) correctly determines if gpg-agent is running, but it doesn't test the same way gpg its self does, so it may succeed when gpg subsequently fails to connect to the agent. The same is true of (2) in that the agent may be running, but GPG_AGENT_INFO not set in the current session, and there's no apparent way to ask the gpg-agent command for the GPG_AGENT_INFO of an already-running agent. Feb 21, 2013 at 23:33

The running gpg agent's major version is 2. You should invoke gpg2 rather than gpg as answered here: https://unix.stackexchange.com/questions/231386/how-to-make-gpg-find-gpg-agent


So far the best workaround I have is the following hideous mess:

if ! test -v GPG_AGENT_INFO; then
    if gpg-agent 2>/dev/null; then
        if test -e /tmp/.gpg-agent-$USER/env; then
            . /tmp/.gpg-agent-$USER/env
        elif test -e ~/.gpg-agent-info; then
            . ~/.gpg-agent-info
            echo 'A gpg agent is running, but we cannot find its socket info because'
            echo 'the GPG_AGENT_INFO env var is not set and gpg agent info has not been'
            echo 'written to any expected location. Cannot continue. Please report this'
            echo 'issue for investigation.'
            exit 5
        mkdir /tmp/.gpg-agent-$USER
        chmod 700 /tmp/.gpg-agent-$USER
        gpg-agent --daemon --write-env-file /tmp/.gpg-agent-$USER/env
        . /tmp/.gpg-agent-$USER/env
    # The env file doesn't include an export statement
    export GPG_AGENT_INFO
    if ! gpg-agent 2>/dev/null; then
        echo 'GPG_AGENT_INFO is set, but cannot connect to the agent.'
        echo 'Unsure how to proceed, so aborting execution. Please report this'
        echo 'issue for investigation.'
        exit 5

This will check for GPG_AGENT_INFO in the environment and if it's set, make sure gpg-agent is actually running. (I'm not yet sure how this interacts with other gpg-agent implementations like GNOME's agent). If the agent info is set but the agent is not running it doesn't know how to cope and gives up.

If the agent info isn't set it checks to see if the agent is running. If it is, it looks for the env info in a couple of well known locations and if it fails to find it, gives up.

If the agent isn't running and the agent info is unset, it starts an agent, writes the env file to a private location, and proceeds.

To say that I'm unhappy with this horrible, user-hostile and unreliable hack is an understatement.

It's very surprising that gpg, a security/crypto tool, will ignore arguments and proceed. --use-agent should be a fatal error if an agent is not running, at least optionally, much as specifying -r with an invalid recipient should be an error rather than ignored. The fact that gpg finds its agent a different way to the gpg-agent command is bewildering.

  • As usual it can be made even messier... :-) If GPG_AGENT_INFO is not (or wrongly) set and you know the PID (e.g. by pgrep gpg-agent) then ypu may do this to find the socket: lsof -n -p $PID | grep S.gpg-agent$ | awk '{print $NF}' Feb 22, 2013 at 1:34
  • 1
    @HaukeLaging ... if it's really gpg-agent not say gnome-keyring-daemon at work. 'cos it wasn't already horrible enough :S . I'm amazed that it's all such an inconsistent mess. Feb 22, 2013 at 2:34
  • ! test -v GPG_AGENT_INFO doesn't work on Mac OS X. You'll need to use something like [ -z ${GPG_AGENT_INFO+x} ] instead.
    – dwlz
    Jul 27, 2016 at 22:14

On my Ubuntu system gpg-agent is configured to write its environment file to ~/.gnupg/gpg-agent-info-$(hostname) (which is done by /etc/X11/Xsession.d/90gpg-agent). If your system doesn't do this you could modify the way the agent is started to write an environment file in a well known location which can later be sourced. For example:

$ gpg-agent --daemon --write-env-file="$HOME/.gnupg/gpg-agent-info"
$ source ~/.gnupg/gpg-agent-info
  • Yeah, the trouble is that I'm scripting tools that need to be portable; I can't really rely on distro-specific details. gpg-agent won't write an env file if an agent is already running and I can't determine where any env file might already be. If gpg-agent --write-env-file would query the currently running agent and write an env file, that'd be just fine, but it doesn't. Feb 21, 2013 at 23:30
  • 2
    NB: gpg-agent[2333]: WARNING: "--write-env-file" is an obsolete option - it has no effect Apr 4, 2016 at 12:33

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.