This issue stems from the fundamental disconnect between how gpg-agent (v2.0.19) and gpg (v1.4.13) detect agent availability:
# gpg-agent checks socket connection
socket(PF_FILE, SOCK_STREAM, 0) = 5
connect(5, {sa_family=AF_FILE, sun_path="/home/user/.gnupg/S.gpg-agent"}, 32) = 0
# gpg only checks environment variables
infostr = getenv("GPG_AGENT_INFO")
if (!infostr || !*infostr) {
log_error(_("gpg-agent is not available in this session\n"));
return SPWQ_NO_AGENT;
}
When working with mixed gpg/gpg-agent versions, you'll encounter these scenarios:
- Agent running in Terminal A but invisible to Terminal B
- Stale GPG_AGENT_INFO environment variables
- Silent failures during batch processing
Here's a bash function that properly tests agent connectivity:
function ensure_gpg_agent() {
# First check socket connectivity
local socket_path="${GNUPGHOME:-$HOME/.gnupg}/S.gpg-agent"
if [[ -S "$socket_path" ]]; then
local test_response
test_response=$(echo "GETINFO pid" | nc -U "$socket_path" 2>/dev/null)
[[ "$test_response" =~ ^OK ]] && return 0
fi
# Fallback to environment if socket fails
if [[ -n "$GPG_AGENT_INFO" ]]; then
local port=${GPG_AGENT_INFO##*:}
[[ "$port" =~ ^[0-9]+$ ]] && \
echo "GETINFO pid" | nc localhost "$port" | grep -q ^OK && return 0
fi
# Start new agent if all checks fail
eval "$(gpg-agent --daemon)"
export GPG_AGENT_INFO
return $?
}
For legacy systems where you must use gpg 1.4.x:
# Force environment update
find /tmp -maxdepth 1 -name 'gpg-agent*' -type f -mmin -5 \
-exec cat {} \; | grep GPG_AGENT_INFO | source /dev/stdin
# Alternative using gpgconf
if command -v gpgconf &>/dev/null; then
export GPG_AGENT_INFO=$(gpgconf --list-dirs agent-socket)
gpgconf --launch gpg-agent
fi
For critical batch processing systems, implement this comprehensive check:
function verify_gpg_connectivity() {
local timeout=2
local test_msg="GETINFO version"
# Try socket first
local socket="${GNUPGHOME:-$HOME/.gnupg}/S.gpg-agent"
if [[ -S "$socket" ]]; then
if echo "$test_msg" | timeout "$timeout" socat - UNIX-CONNECT:"$socket" | grep -q ^OK; then
return 0
fi
fi
# Try TCP next
if [[ "$GPG_AGENT_INFO" =~ :([0-9]+)$ ]]; then
local port="${BASH_REMATCH[1]}"
if echo "$test_msg" | timeout "$timeout" nc localhost "$port" | grep -q ^OK; then
return 0
fi
fi
# Final fallback
if ! pgrep -x gpg-agent &>/dev/null; then
eval "$(gpg-agent --daemon)"
return $?
fi
return 1
}
This handles all edge cases including stale sockets, zombie processes, and network connectivity issues.
When issues persist, use these diagnostic commands:
# Check active sockets
ls -la ${GNUPGHOME:-$HOME/.gnupg}/S.gpg-agent*
# Verify agent responsiveness
echo "GETINFO pid" | socat - UNIX-CONNECT:${GNUPGHOME:-$HOME/.gnupg}/S.gpg-agent
# Environment inspection
gpgconf --list-dirs
gpg-connect-agent --dirmngr 'getinfo homeserver' /bye
When working with GPG encryption in shell scripts, you might encounter this frustrating scenario where gpg-agent
reports an existing instance while gpg
fails to recognize it. This typically occurs when:
# Terminal 1
$ eval $(gpg-agent --daemon)
# Terminal 2 (new session)
$ eval $(gpg-agent --daemon)
gpg-agent[21927]: a gpg-agent is already running - not starting a new one
$ gpg -d file.asc
gpg: gpg-agent is not available in this session
The root cause lies in how each component detects the agent:
- gpg-agent: Checks for active socket connections at
~/.gnupg/S.gpg-agent
- gpg: Relies solely on the
GPG_AGENT_INFO
environment variable
This becomes problematic when working across different terminal sessions where environment variables aren't shared.
Here are three robust approaches to handle this situation:
Method 1: Socket-based Verification
This method directly checks the agent socket:
# Check for active gpg-agent socket
if [ -S "${HOME}/.gnupg/S.gpg-agent" ]; then
# Test socket communication
if gpg-connect-agent --quiet /bye 2>/dev/null; then
echo "gpg-agent is available"
else
# Socket exists but agent isn't responding
rm -f "${HOME}/.gnupg/S.gpg-agent"
eval $(gpg-agent --daemon)
fi
else
eval $(gpg-agent --daemon)
fi
Method 2: Environment Variable Management
For scripts that need to work across sessions:
# Store agent info in a known location
AGENT_ENV="${HOME}/.gpg-agent-info"
if [ -f "${AGENT_ENV}" ]; then
. "${AGENT_ENV}"
if ! kill -0 $(echo ${GPG_AGENT_INFO} | cut -d: -f2) 2>/dev/null; then
# PID is invalid, start new agent
eval $(gpg-agent --daemon --write-env-file "${AGENT_ENV}")
fi
else
eval $(gpg-agent --daemon --write-env-file "${AGENT_ENV}")
fi
Method 3: Modern GPG 2.1+ Approach
For newer GPG versions that use the standard socket path:
# For GPG 2.1+ with automatic socket detection
export GPG_TTY=$(tty)
if ! gpg-connect-agent --quiet /bye 2>/dev/null; then
gpgconf --launch gpg-agent
fi
When dealing with both old and new GPG versions:
function ensure_gpg_agent() {
# Try modern approach first
if gpg-connect-agent --quiet /bye 2>/dev/null; then
return 0
fi
# Fallback to legacy method
if [ -S "${HOME}/.gnupg/S.gpg-agent" ]; then
GPG_AGENT_INFO=$(find "${HOME}/.gnupg" -name 'S.gpg-agent' -printf '%p:%l:1')
export GPG_AGENT_INFO
return 0
fi
# Start new agent
eval $(gpg-agent --daemon)
return $?
}
- Always verify agent connectivity before batch operations
- Handle both socket and environment variable cases
- Implement proper cleanup in error cases
- Consider using
gpgconf
for modern GPG installations - Log agent status changes for debugging
Here's a complete solution combining these approaches:
#!/bin/bash
# Initialize GPG agent with comprehensive checks
init_gpg_agent() {
# Modern GPG 2.1+ detection
if command -v gpgconf &>/dev/null; then
export GPG_TTY=$(tty)
gpgconf --launch gpg-agent 2>/dev/null
return $?
fi
# Legacy GPG detection
local agent_socket="${HOME}/.gnupg/S.gpg-agent"
local agent_env="${HOME}/.gpg-agent-info"
# Check existing socket
if [ -S "${agent_socket}" ]; then
if gpg-connect-agent --quiet /bye 2>/dev/null; then
return 0
fi
rm -f "${agent_socket}"
fi
# Check existing env file
if [ -f "${agent_env}" ]; then
. "${agent_env}" &>/dev/null
if [ -n "${GPG_AGENT_INFO}" ]; then
local pid=$(echo "${GPG_AGENT_INFO}" | cut -d: -f2)
if kill -0 "${pid}" 2>/dev/null; then
return 0
fi
fi
fi
# Start new agent
eval $(gpg-agent --daemon --write-env-file "${agent_env}")
return $?
}
# Usage example
if ! init_gpg_agent; then
echo "Failed to initialize gpg-agent" >&2
exit 1
fi
# Proceed with GPG operations
gpg --batch --decrypt "${encrypted_file}"