Virtualising X11 GUI, printers and sound on Linux

I recently had a requirement to do some testing involving a GUI, a printer and sound devices on Linux. Would be simple with a laptop and a real printer, but in this case the work was being done on a remote data center in the cloud. So how do you do these sorts of tests when you haven't got the hardware in front of you?


1. A virtual GUI

The X Window System, version 11 ("X11" for short) is the graphical subsystem used on most UNIX systems that handles the display

1.1 SSH tunnelling

If you're running a GUI on your local Linux system (Or a comparable X11 server on something else!) then you can forward GUI traffic over an ssh connection. It's not particularly quick but it gives you an option. You can set up the forwarding from your local system with:

ssh -X user@remotehost
This will automatically define a DISPLAY environment variable on your ssh session with a value such as "localhost:10.0" which will tunnel the X11 traffic back to your local server. in If it doesn't work, ensure that sshd_config (Often found in /etc/ssh) on your remote host contains this line:
X11Forwarding Yes

1.2 Xvfb

Xvfb (X11 Virtual Frame Buffer) as the name suggests lets you create an X11 "server" that doesn't really exist as a screen, only in RAM. It looks exactly like a real X11 server to applications so they can write to it and interact with it themselves, which is perfect for GUI test automation when you don't need to interact manually with the things that are produced. 

2. VNC

For some purposes it is still necessary to have a human check out what's happening and to do some interactions with the GUI. The ssh method mentioned before is usually sluggish unless on your LAN, and you can't disconnet/reconnect to it. The simplest solution (which many people reading this are likely already familiar with) is VNC which but it creates a virtual X11 display like Xvfb an allows you to remotely connect to it so you can interact with the screen. This gives you something similar to remote desktop connections on Windows. To interact sensibly with it you will need to run a window manager to allow you to move windows around - this can be a full desktop environment like GNome or something far more lightweight like twm which I use. I would recommend configuring twm to adjust the middle click on the background (window management operations) to right click so you can perform operations if the middle click doesn't go through. You can also add a "close" (delete) button to the windows which twm does not include by default

There are multiple versions of the VNC client and server around such as realvnc, tightvnc, tigervnc but they all work fundamentally the same way - install the package, run the server (likely linked as "vncserver") - it'll prompt you for a password which can be changed later with "vncpasswd" and then you connect using a "vncviewer" on your other machine. You can have multiple servers up at once - perhaps for different users. When you start the server it'll say something like:

New 'X' desktop is somehostname:1

At which point you connect to it with vncviewer somehostname:1 - the ports that VNC uses by default are 5900+display number so :1 will be on port 5901 etc. You can shut down the server using

vncserver -kill :1

While this is all very well, it's worth bearing in mind that VNC traffic is NOT encrypted by default. Also it's questionable whether you want your desktop to be directly accessible over the internet like that. Once you've created a vncserver you can use an ssh tunnel to connect to it. For example for a vncserver running as :1 and therefore on TCP port 5901 would would use:

ssh -C -L 5901:localhost:5901 vncserverhost

KEEP THIS SHELL OPEN! Then in another terminal you can run:

vncviewer localhost:1

The VNC traffic will then magically be redirected to the remote machine via your ssh tunnel as long as the ssh session you've just created remains active. The -C in the ssh command tells it to compress the traffic which may provide some performance boosts or it may not, depending on your network.

2.1 Setting up a window manager

It's all very well connecting to a nice empty X11 screen on a VNC server but to do anything with it you'll probably want a window manager. While you can install somethign large like a GNome/xfce environment that's usually overkill. I would suggest going fully back to basics and using the lightweight (by modern standards) twm window manager. And if you haven't already got it you should install xterm to be able to give you a terminal window inside your VNC session.

apt install twm xterm
Once you have both of them installed, set your DISPLAY environment variable to match your VNC display (e.g. :1) and start both of them
    export DISPLAY=:1
    export LANG=C
    twm &
    xterm &
twm is basic but functional. The LANG=C may not be needed but if twm aborts on startup complaing about problems with fonts that should resolve it. To perform operations on windows like killing and resizing you can click the middle mouse button on the background (i.e. not in a window) ... However depending on your setup the middle mouse button may not get transferred through to the remote system. If this is the case I recommend remapping it. To do this, take a copy of the master twm configuration file into your home directory from wherever it is on your system and edit it:
cp /etc/X11/twm/system.twmrc ~/.twmrc &
You can now replace "Button2" with "Button3" in the line that says

 Button2 = : root : f.menu "windowops"

For convenince you can also add a Delete (close) button to the twm titlebar by adding this line to your .twmrc file. Thanks to my colleague Simon for this one:

RightTitleButton "xlogo11" = f.delete

Done.

3. printing with cups-pdf 

For the purposes of testing printer output, the best option is to install cups-pdf. This creates a virtual printer that can be used and will write to a file in $HOME/PDF on your machine. The cups-bsd is optional, but allows you to interact with cups using the traditional BSD UNIX lpr and lpq command. Note that if you install cups-bsd later, you'll need to set the default printer again using the last command here in order to pick it up

apt install printer-driver-cups-pdf cups-bsd
perl -p -i -e 's/^#?Label.*$/Label 2/' /etc/cups/cups-pdf.conf
service cups start
lpadmin -p defaultpdf -v cups-pdf:/ -E -P /usr/share/ppd/cups-pdf/CUPS-PDF_noopt.ppd
lpadmin -d defaultpdf

4. Virtual sound via ALSA

This one is a little more tricky, but it is doable. Firstly you need to install the appropriate packages in order to have some basic tools to read and write from your audio devices. The modprobe command makes it take effect immediate, the adjustment to alsa-aloop.conf makes it take effect on subsequent reboots too
apt install alsa-utils    
modprobe snd_aloop
echo snd_aloop > /etc/modules-load.d/alsa-aloop.conf
The alsa-utils package providers you with the "aplay" and "arecord" commands. If you use "aplay -l" it will show you a list of audio devices on the machine, and with the snd-aloop module installed. In general you need the virtual card (probably zero if you're on a server but aplay -l will confirm, and device 0, subdevice 0 for playback (0,0,0) and device 1 subdevice 0 for recording (0,1,0). For example this should play the contents of a wav file in the background, then capture it back in to test that it is playing properly. If you get an error about sample formats or other settings just adjust S16_LE or the other options to one that it says are available:
aplay myaudiofile.wav &
arecord -D hw:0,1,0 -f S16_LE -c 2 -r 48000 capture.wav
If you want to get a bit more clever, you could start the aplay command as above, then from your local Linux system do something like this:
ssh server_with_dummy_audio arecord -D hw:0,1,0 -f S16_LE -c 2 -r 48000 | aplay
and you should hear the sound being "played" on the server coming out of your local machine's speakers.
Note that I have occasionally found that the whole thing stops working. I'm not sure why but if you get the same try killing the pulseaudio process that you are running - that will generally reset everything
If you want to be extra clever you can avoid storing audio files on your server with something like this to play an MP3 on your local system via the dummy device on the server while listing back with the previous command:
mpg123 -s somefile.mp3 | ssh server_with_dummy_audio arecord -D hw:0,1,0 -f S16_LE -c 2 -r 48000 | aplay
While this is clearly a colossal waste of bandwidth sending raw audio data across the network and then back again to play (It's about 1Mb every 3 seconds) it's there as an option should you need it :-)

Comments

Popular posts from this blog

macOS - first experiences from a Linux user perspective

Antisocial Networking: List of Top Tips

Customer service: contacting banks via the internet - how hard can it be?