Low-brow clipboard integration with remote X

Sunday, 11 Feb 2024

This is another one of those small-lightbulb moments like this one was. The setup for this one is that I run some X programs on my home server for remote use from the machine I actually work on – but via VNC, where I never got clipboard integration working.1 I only rarely need to paste in X, so I’ve just been living with this… until the obvious recently occcured to me:

The low-brow but perfectly serviceable solution is to just use the clipboard commands of both systems, strung together with a pipe over a SSH connection. Of course this needs to be done manually to transfer the clipboard every single time I copy something on one side and want to paste it on the other – the low-brow bit. But it is far more convenient than pasting into terminals like I was doing before, to the point I won’t feel the lack any more.

Only… it didn’t quite work right.

I searched the web for a fix and unsurprisingly found that plenty of people have had the same idea:

# copy in X, run this, then paste locally
ssh server.home xclip -selection clipboard -out | pbcopy

(If you’re not using a Mac locally, just replace pbcopy (and pbpaste) with your system’s equivalent.)

The trouble is, I was looking for the other direction – and far from novel though the idea may be, I didn’t find a command written up anywhere. There turns out to be a minor trick to it (and maybe that’s why), which I ultimately had to figure out for myself:

# copy locally, run this, then paste in X
pbpaste | exec ssh server.home 'exec xclip -selection clipboard &> /dev/null'

Namely, this won’t work as desired without the “&> /dev/null” bit.

It will work, except without returning to the prompt. It just hangs. This comes down to the way that selections work in X: the program the selected content came from must grab that selection and then answer requests to paste it – no program, no paste. So the only way xclip can work is to stick around as a background process after putting something on the clipboard – until something else is copied, then it can exit. And because xclip doesn’t close stdout and stderr, ssh won’t know to quit any sooner than that, so it sits there waiting. To create a compound command that immediately returns to the prompt after shipping over the local clipboard contents, it is therefore necessary to close stdout and stderr on xclip explicitly.

With that, I have a solution I can happily live with.2

  1. This is after trying all the usual suggestions (like running vncconfig on the server). Presumably they didn’t work because I am using the Screen Sharing app that comes with MacOS, which apparently is not actually a VNC client but just uses that protocol for most of its functionality.

    The backstory to that is that I used to use Xpra for remote X because it makes that rather neat: individual windows on the server are displayed remotely as individual windows on the client, complete with native local windowing UI, so there is none of the ungainly window-in-window hassle and no need for an X window manager. The catch is that Xpra requires reasonably matching versions on server and client, and I have at times fallen well behind running the latest OS version on either side, which on a few occasions has made them tricky to align. A while ago I failed to find any working constellation at all, at which point I decided I was tired of doing that and would switch to something less bespoke. Now I no longer need to install anything on the Mac and have multiple highly compatible options on the server.

  2. Or maybe I’ll go back to Xpra, who knows…

Closing for trouble in the land of streams

Thursday, 8 Feb 2024

Recently I learned of a mistake I had been making without realizing it. Zygo:

Closing FD 2, without simultaneously opening something else in its place (like a new TTY, if you’re trying to shed a controlling TTY or drop the last references to your old namespace/chroot parent), is weird and occasionally dangerous.

It’s asking for trouble because libraries are chatty and they write noise to stderr and it’s nearly impossible to stop them. Libraries also like to make their own open file descriptors, and they tend not to notice when the thing they’ve opened/connected to and expect to exclusively control/have a private conversation with happens to also be stderr and full of noise from random library functions. Chaos follows when those patterns collide.

Jamie Zawinski:

XScreenSaver has had code in it since the beginning to re-open FDs 1 and 2 as /dev/null lest XOpenDisplay put the connection to the X server on FD 2 with hilarious results.

In other words, closing one of the standard streams might lead to flooding somewhere else. You must ensure drainage, even if only to the void.

How to eject external hard disks used for APFS TimeMachine backups (I’m not making this up)

Friday, 20 Jan 2023 [Monday, 12 Feb 2024]

Ever since upgrading to a recent Mac that came with the disk formatted with AFPS, a perennial irritation has been Time Machine. I use a USB hard drive for backups, which of course needs unplugging when I want to take the machine with me somewhere. There are long stretches of time when I don’t even think about this because it works just fine. And then there are the other stretches of time when this has been impossible: clicking the eject button in Finder does nothing for a few ponderous moments and then shows a force eject dialog. (And of course the command line tools and other methods all equally fail.)

I could of course forcibly eject the disk, as the dialog offers. And maybe I would, if it this was just a USB stick I was using to shuffle around some files. But doing this with my backup disk seems rather less clever. This disk I want to unmount in good order.

Unfortunately when this happens, there is no help for it: even closing all applications does not stop the mystery program from using it. So what is the program which is using the disk? The Spotlight indexer, it turns out.

$ sudo lsof +d /Volumes/TimeMachine\ XXXXXX/
mds     1234 root    5r   DIR   1,24      160    2 /Volumes/TimeMachine XXXXXX
mds     1234 root    6r   DIR   1,24      160    2 /Volumes/TimeMachine XXXXXX
mds     1234 root    7r   DIR   1,24      160    2 /Volumes/TimeMachine XXXXXX

How do you ask this to stop?

Beats me. I have not found any documented, official way of doing so. Not the Spotlight privacy settings, not removing the disk from the list of backup disks in the Time Machine settings, not the combination of those, no conceivable variation of using tmutil on the command line, not a number of other things – nothing. Even killall -HUP mds does not help: obviously the Spotlight service notices, and just respawns the processes.

And this state will persist for hours and days – literally. On one occasion, I wanted but didn’t need the machine with me, so I left it to its own devices out of curiosity. It took over 2 days before ejecting the Time Machine volume worked again.

For a purportedly portable computer, this is… you know… a bit of a showstopper.

So after suffering this issue long enough, I finally tried something stupid the other day – and whaddayaknow, it works:

$ sudo killall -HUP mds ; sudo umount /Volumes/TimeMachine\ XXXXXX/

This will not always work the first time, it may need a repeat or two. But sooner rather than later it does take. Evidently the mds process respawn is not so quick that it wouldn’t leave a window during which the disk can be unmounted properly.

And so I put the following in ~/bin/macos/unmount-despite-mds and made it executable:

if (( $# != 1 )) ; then echo "usage: $0 <mountpoint>" 1>&2 ; exit 1 ; fi
parent_dev=$( stat -f %d "$1"/.. ) || exit $?
while [[ -d $1 ]] && [[ "$( stat -qf %d "$1" )" != $parent_dev ]] && ! umount "$1" ; do
  killall -HUP mds
  lsof +d "$1"

Now I can invoke it at any time from the terminal like so:

$ sudo ~/bin/macos/unmount-despite-mds /Volumes/TimeMachine\ XXXXXX/

What this does is check whether the given path, if it is a mount point, fails to unmount. If so, it sends a signal to terminate the Spotlight indexer processes and immediately retries. In a loop.

Or to put it more colloquially, it machineguns Spotlight until umount can slip in under the suppressing fire and pull the disk out from under it.

This is not a solution. It is the bluntest of instruments. But this is what works. And as far as I have been able to find, this is the only thing that works.

Three cheers for Apple software quality, I guess.

Update: Sending HUP instead of TERM is a better idea. Both cause the mds processes to shut down, but TERM seems to leave Spotlight in a bad state: for some time after, some search results take longer to show up and search result order gets messed up, which painfully disrupts my muscle memory. With HUP I have not observed the same issue. I have added it to all the examples in the article.