A Non-Intrusive GPG Pinentry

If you use GPG, you are probably familiar with the situation where suddenly the pinentry program pops up and interrupts whatever you’re doing. Maybe you’re in the middle of a game, maybe you are deeply engrossed in tracking down a bug, whatever. That pinentry is intrusive; it interrupts your thoughts and your actions, demanding immediate attention.

I was not surprised to find questions online asking how to make it non-intrusive; I was surprised that there was no solution offered. Eventually, after walking me into a nest of Biters one too many times, I hacked together a solution.

Caveat lector, as usual. For me, all this affects is the background syncing of my email and calendar, which is fairly infrequent in any case. It blocks pinentry until you tell it to go ahead and show you the dialog. You can check its state by calling it with --bar; it’ll return !! if it needs attention, and X if it doesn’t. This can be used to put the status in a bar, like i3bar or polybar. If your bar allows interactions, you can have it call the script with --yes on click, which will unblock the script and call your pinentry GUI.

Hopefully this helps other people fix a curious omission in the whole GPG pinentry workflow.

#!/bin/sh
# A non-intrusive GPG pinentry.
# The purpose of this script is to prevent GPG's pinentry from popping up and
# interrupting the user. It does this by blocking on a lockfile; running it
# with an argument removes the lockfile and unblocks the script, which can
# then call the pinentry GUI program.
#
# HOW TO USE
# 1. `cp THISSCRIPT ~/.local/bin/pinentry-preexec`
# 2. `chmod +x ~/.local/bin/pinentry-preexec`
# 3. Edit ~/.gnupg/gpg-agent.conf and change (or add) the line:
#    `pinentry-program ${HOME}/.local/bin/pinentry-preexec`
# 4. Optionally, if you're using a WM bar, call this script with
#    the `--bar` argument to get the status of the agent, and set
#    the action command to `--yes`. An example for polybar is at the
#    end of this file.
# 5. Reload your gpg agent:
#    `gpg-connect-agent reloadagent /bye`
# 6. Test it! Do something to trigger pinentry.

PINENTRY=/usr/bin/pinentry-rofi
LOCKDIR="${HOME}/.local/share/pinentry-preexec"
LOCKFILE="${LOCKDIR}/lock"
UNBLOCKED="X"
BLOCKED="!!"

test -d "$LOCKDIR" || 
	mkdir "$LOCKDIR"

test "$1" == "--bar" &&
	( test -f "$LOCKFILE" &&
	  echo -n "$BLOCKED" ||
	  echo -n "$UNBLOCKED" ) &&
	exit 0

test "$1" == "--yes" &&
	rm -f "$LOCKFILE" &&
	exit 0

test -f "$LOCKFILE" ||
	touch "$LOCKFILE"

while [[ -f "$LOCKFILE" ]]; do
	sleep 1
done

rm -f "$LOCKFILE"
exec $PINENTRY "$@"

###############################################################################
# Polybar configuration example
###############################################################################
# 
# # Create a module...
# 
# [module/pinentry]
# type = custom/script
# interval = 1
# label-padding = 1
# exec = ${env:HOME}/.local/bin/pinentry-preexec --bar
# click-left = ${env:HOME}/.local/bin/pinentry-preexec --yes
#
# # ... and add the module to your bar
#
# modules-right = alsa fm pinentry powermenu