The other day I was rebooting our development server. It has full disk encryption, and the password for it has to be specified at boot time, long before it has network access.
Even though the machine is in the same building, walking over there is obviously not an option. The machine has IPMI, like all modern machines do, so we can connect a virtual console over the local network. For that, we use the SuperMicro ipmiview tool.
Unfortunately, it’s a Java application that doesn’t play that well with the rest of my X window system. In particular: it doesn’t do pasting from the clipboard. The middle-mouse paste doesn’t work, the CTRL-V paste doesn’t, and the CTRL-SHIFT-V alternative doesn’t either!
That is no fun if the encrypted disk password looks something like
vzyxLyi8hsdQIM1zWUlz .. did I type the lowercase Z? Or?
No key available with this passphrase
vzyxLyi8hsdQIM1zWUlzZM14jZCk2iuOZ*3 .. drat.. too slow with the
You can see how that gets old real quick.
What if we could fake keypress events in the IPMI console window?
Turns out we can. Behold: xpaste.
Examining events in X
Events are constantly being passed around in the X window system. You can check which windows exist in your display, like this:
$ xwininfo -root -tree ... lots of windows
You can quickly find the window of your gnome-terminal with this trick:
$ cd `mktemp -d` /tmp/tmp.ej4mWwShfr$ xwininfo -root -tree | grep `pwd` -C4 0x1400264 (has no name): () 1855x1176+65+24 +65+24 1 child: 0x1400265 (has no name): () 1855x1176+0+0 +65+24 1 child: 0x2e0000a "walter@walter-desktop: /tmp/tmp.ej4mWwShfr": ("gnome-terminal-server" "Gnome-terminal") 1855x1176+0+0 +65+24 1 child: 0x2e0000b (has no name): () 1x1+-1+-1 +64+23 0x1403447 (has no name): () 1855x1176+65+24 +65+24 1 child:
Look, a window with my name on it ;-)
But that’s not where the keyboard events go. If you run
xev -id 0x2e0000a (or its child) there will be zero relevant events.
xev & sleep 10; kill $! and you can sniff events in your
terminal (or a test window, depending on which version of
have) for 10 seconds. It’ll show things like this:
KeyPress event, serial 37, synthetic NO, window 0x4400001, root 0x25d, subw 0x0, time 154332240, (58,131), root:(1658,183), state 0x0, keycode 38 (keysym 0x61, a), same_screen YES, XLookupString gives 1 bytes: (61) "a" XmbLookupString gives 1 bytes: (61) "a" XFilterEvent returns: False KeyRelease event, serial 37, synthetic NO, window 0x4400001, root 0x25d, subw 0x0, time 154332320, (58,131), root:(1658,183), state 0x0, keycode 38 (keysym 0x61, a), same_screen YES, XLookupString gives 1 bytes: (61) "a" XFilterEvent returns: False
Promising. That’s the press and release of the
Rolled into xpaste
Xpaste works by emitting keypress events as if the user is typing. This had already been implemented by small utility apps like crikey. Crikey sends key events into the window that currently has focus.
For our purposes we needed to improve it to make it choose the right window too. And, as seen above, picking the right window to send the events to is not necessarily trivial.
This problem was tackled by having xpaste listen for some kind of key — in this case the [ENTER]. If the user would press enter in that window, that window is the right one to paste into. It grabs the root window (the parent of all windows) and asks to get events that relate to the [ENTER] key. When the currently focused window gets the keypress, xpaste has determined the in which window ID the user wants the paste.
Combining the above, it looks like this — the translucent parts are after the enter keypress:
Yeah! It works! And it’s implemented in pure python.
Fetch xpaste from github or through
pip install xpaste.