Managing the X11 Clipboard — or — Area Hacker Finds One Weird Trick For Persistent Copy / Paste Behavior

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:

  1. The operating system maintains a buffer, in kernel-space (or perhaps in user-space, under control of a daemon).
  2. Copy means (quite literally) copying bytes into that buffer, from the copying application.
  3. 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:

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:

(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:

  1. pass retrieves the current contents of the clipboard (as a UTF-8 string) and holds onto it.
  2. pass decrypts the secret and marks that data as the X11 clipboard selection.
  3. pass forks 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:

  1. forking into the background.
  2. 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 pass itself.

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.

Further Reading

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

James (@iamjameshunt) works on the Internet, spends his weekends developing new and interesting bits of software and his nights trying to make sense of research papers.

Currently exploring Kubernetes, as both a floor wax and a dessert topping.