When I was looking to jump from my Apple lifestyle to a more bare-bones, Linux-centric world, I started casting about for replacements for my mainstays of macOS / iOS computing. The first thing on the replacement short-list: 1password.
Thankfully, there is already a decent password manager for UNIX: pass. It uses GPG (via
gpgme) and a handful of POSIX and near-POSIX utilities to manage an encrypted hierarchy of secrets, similar in spirit to Vault, LastPass, and 1password.
It even has clipboard integration!
So, I dockerized it. And quickly ran afoul of some peculiarities in X11, with respect to clipboard functionality.
My mental model of the ubiquitous clipboard goes a little something like this:
- The operating system maintains a buffer, in kernel-space (or perhaps in user-space, under control of a daemon).
- Copy means (quite literally) copying bytes into that buffer, from the copying application.
- Paste means to copy those bytes out of the clipboard buffer into the pasting application.
Indeed, I have always thought of the "clipboard" as an actual thing, that is separate from the clients. And in macOS / Windows, that seems to be precisely the case.
Not so, with X11.
X11 distributes the job of clipboarding to all involved applications. What I mentally call "the clipboard" is nothing more than a convention of naming and differentiating what X11 calls selections.
Here's what happens, under X11, when you copy something:
- Copying Application: Hey, X11 server!
- X11 Server: Yo. what's up?
- C: I just told my user I copied stuff to the clipboard.
- X: LOL. Got it. I'll let the copiers know.
That is, the copying application never sends the data to the X11 server for safe-keeping. Instead, it informs the X11 server that it now owns the clipboard, and that anyone who wants to paste should talk to the application (semi-)directly.
Here's how paste works:
- Pasting Application: Hey, X11 server!
- X11 Server: what's shakin'?
- P: My user just asked to paste from the clipboard.
- X: Cool. Looks like you need to talk to window 0x12345.
- P: Got it.
- P: Hey, Window 0x12345!
- Copying Application: Go ahead caller, I'm listening.
- P: Send me your clipboard data as UTF-8 text.
- C: Sure thing. Here you go!
(I've elided some of the details; mainly that the pasting application and the copying application never communicate directly, but rather coordinate through asynchronous messages sent to the X11 server. The upshot is that we end up "communicating" peer to peer to get at the clipboard data.)
So, what happens if copying application goes away between the "copy" and the "paste"?
The clipboard disappears.
This completely shatters my mental model of the clipboard as a neutral, independent buffer that gets written to by copy operations, and read from by paste operations.
To the problem at-hand: the
pass utility lets you copy secret data to "the clipboard" (skeptical air quotes), and expires it after 45 seconds. Here's what really happens:
passretrieves the current contents of the clipboard (as a UTF-8 string) and holds onto it.
passdecrypts the secret and marks that data as the X11 clipboard selection.
passforks off a subshell in the background that sleeps for 45 seconds and then re-constitutes the original clipboard contents by re-marking the old data as the current selection.
This is all done using
xclip, which works around X11's reliance on the continued existence of the clipboard "owner" by:
- forking into the background.
- exiting whenever anyone else copies to it.
Awesome. Works great in normal mode, where
pass is just executed under a long-running PID 1 (which will tend to the orphaned
xclip process until the machine shuts down).
Works less than great when I try to dockerize
In that case, Docker sets up a new cgroup environment, with its own PID namespace, with
pass playing the role of PID 1. When
xclip forks off to be "long-lived" (against, skeptical air quotes), we re-inherit it as a child process, and then summarily exit. At this point, Docker kills off all of the remaining child processes, and our clipboard vanishes into thin air.
That's why I wrote xclipd.
It's a small daemon that runs on the host and doggedly watches the X11 CLIPBOARD selection, immediately requesting copied data and then asserting control of the clipboard. This reifies clipboard management, bring it in-line with my neutral 3rd party mental model of clipboards.
If you really want to dig into how X11's clipboard management works, and you don't mind a little bit of C, check out https://www.uninformativ.de/blog/postings/2017-04-02/0/POSTING-en.html