Summary
When rbw is configured to use pinentry-tty and the user presses Ctrl+C during a passphrase prompt, pinentry-tty is not killed and continues to hold /dev/tty, competing with the shell for keystrokes until the retry limit is exhausted.
Environment
- rbw version: 1.15.0
- OS: Arch Linux, Hyprland
- pinentry-tty version: pinentry-tty (pinentry) 1.3.2
- rbw config:
pinentry = "pinentry-tty"
Steps to reproduce
- Configure rbw to use pinentry-tty:
rbw config set pinentry pinentry-tty
- Run
rbw get <any entry>
- When the
Master Password: prompt appears, press Ctrl+C
Expected behavior
Ctrl+C should cancel the pinentry prompt and return cleanly to the shell, as it does when using gpg-agent 2.4+ with the same pinentry-tty binary.
Actual behavior
pinentry-tty continues running as an orphan process attached to /dev/tty. The shell reclaims the foreground but keystrokes are raced between the shell and pinentry. Subsequent keystrokes register as password attempts (visible in the "attempt 2/3", "attempt 3/3" output), and only after the retry limit is exhausted does pinentry exit.
ps aux during the prompt confirms pinentry-tty has no controlling terminal (TTY column shows ?), meaning it is fully detached from the foreground process group and unreachable via terminal signals:
cfurber 71363 0.0 0.0 21872 7296 ? SL 15:25 0:00 pinentry-tty --timeout 0 --ttyname /dev/pts/0 --display :0
Root cause
When the user presses Ctrl+C, SIGINT is delivered to the foreground process group. The rbw client process is in that group and receives the signal, but rbw-agent is a background daemon in a different process group and does not. Since rbw-agent spawned pinentry-tty, and rbw-agent never receives or handles the SIGINT, it never sends a cancel/SIGINT to the pinentry child process.
This is confirmed by comparison with gpg-agent 2.4+, which explicitly propagates cancellation to pinentry when its client is interrupted (fix: GnuPG T4585 / T2011). The same pinentry-tty binary works correctly with gpg-agent — Ctrl+C exits cleanly:
$ echo "test" | gpg --symmetric -
Enter passphrase
Passphrase:
gpg: signal Interrupt caught ... exiting
Workaround
Ctrl+D cancels the prompt correctly since it sends EOF directly on the tty fd rather than via process group signal delivery.
A shell wrapper around the rbw binary also works. The key is keeping rbw in the foreground (no &) so it stays in the foreground process group and tty context is preserved. The trap then explicitly kills pinentry when SIGINT arrives:
#!/bin/bash
trap "pkill -x pinentry-tty 2>/dev/null" INT TERM
/usr/bin/rbw "$@"
Suggested fix
rbw-agent should monitor for client disconnection/cancellation and send SIGINT (or the Assuan CANCEL command) to any in-progress pinentry process when the client that initiated the request is interrupted. This is the behavior implemented in gpg-agent 2.4 (GnuPG commits addressing T2011 and T4585).
Summary
When
rbwis configured to usepinentry-ttyand the user presses Ctrl+C during a passphrase prompt,pinentry-ttyis not killed and continues to hold/dev/tty, competing with the shell for keystrokes until the retry limit is exhausted.Environment
pinentry = "pinentry-tty"Steps to reproduce
rbw config set pinentry pinentry-ttyrbw get <any entry>Master Password:prompt appears, press Ctrl+CExpected behavior
Ctrl+C should cancel the pinentry prompt and return cleanly to the shell, as it does when using gpg-agent 2.4+ with the same
pinentry-ttybinary.Actual behavior
pinentry-ttycontinues running as an orphan process attached to/dev/tty. The shell reclaims the foreground but keystrokes are raced between the shell and pinentry. Subsequent keystrokes register as password attempts (visible in the "attempt 2/3", "attempt 3/3" output), and only after the retry limit is exhausted does pinentry exit.ps auxduring the prompt confirms pinentry-tty has no controlling terminal (TTY column shows?), meaning it is fully detached from the foreground process group and unreachable via terminal signals:Root cause
When the user presses Ctrl+C, SIGINT is delivered to the foreground process group. The
rbwclient process is in that group and receives the signal, butrbw-agentis a background daemon in a different process group and does not. Sincerbw-agentspawnedpinentry-tty, andrbw-agentnever receives or handles the SIGINT, it never sends a cancel/SIGINT to the pinentry child process.This is confirmed by comparison with
gpg-agent 2.4+, which explicitly propagates cancellation to pinentry when its client is interrupted (fix: GnuPG T4585 / T2011). The samepinentry-ttybinary works correctly with gpg-agent — Ctrl+C exits cleanly:Workaround
Ctrl+D cancels the prompt correctly since it sends EOF directly on the tty fd rather than via process group signal delivery.
A shell wrapper around the
rbwbinary also works. The key is keeping rbw in the foreground (no&) so it stays in the foreground process group and tty context is preserved. The trap then explicitly kills pinentry when SIGINT arrives:Suggested fix
rbw-agentshould monitor for client disconnection/cancellation and send SIGINT (or the AssuanCANCELcommand) to any in-progress pinentry process when the client that initiated the request is interrupted. This is the behavior implemented in gpg-agent 2.4 (GnuPG commits addressing T2011 and T4585).