What you’ll learn
In this tutorial we will create a snap of an X11 application to act as the graphical user interface for an IoT or kiosk device. For the introduction to this tutorial series and the Mir display server please visit here.
X11 is a legacy protocol, it is known to be insecure, so we need to take steps to ensure it is secured correctly. To do this we shall embed an intermediary X server based on Mir in the application snap and use snapd’s infrastructure to maintain security.
The combination of Snap, the Ubuntu Frame Wayland server and Ubuntu Core ensures the reliability and security of any graphical embedded device application.
This tutorial assumes you are familiar with the material in Make a Wayland-native Kiosk snap. In particular, techniques for debugging problems in your snap are not repeated here.
Depending on the toolkit your application is written in, it may work on the newer and more secure Wayland protocol. If so, the snapping process is simpler. To check, please read this guide.
What you’ll need
- An Ubuntu desktop running any current release of Ubuntu, or an Ubuntu Virtual Machine on another OS.
- Note: this tutorial assumes you are running an
amd64distribution on the device you’re using to follow this tutorial. There will be minor differences in some file names if you are running a different architecture.
- Note: this tutorial assumes you are running an
- A ‘Target Device’ from one of the following:
A device running Ubuntu Core 18.
This guide shows you how to set up a supported device. If there’s no supported image that fits your needs you can create your own core image.
- Using a Virtual Machine (VM) You don’t need to have a physical “Target Device”, you can follow the tutorial with Ubuntu Core in a VM. This guide shows you how to set up an Ubuntu Core VM.
- Using Ubuntu Classic You don’t have to use Ubuntu Core, you can use also a “Target Device” with Ubuntu Classic. Read this guide to understand how to run kiosk snaps on your desktop, as the particular details won’t be repeated here.
- A device running Ubuntu Core 18.
2. X11 on top of Wayland
We use Wayland as the primary display interface. We will use Mir to manage the display and support connections from Wayland clients and Snapd will confine the applications and enable Wayland protocol interactions through Mir, securely.
Wayland is relatively new in the Linux world however. Its predecessor - X11 - has been the dominant graphics technology for decades. As a result, not all toolkits have native support for Wayland - they only support X11.
To deal with the limitations of X11 in a secure fashion we will embed a tiny intermediary X11 server called “Xwayland” inside the application snap, which translates the X11 calls to Wayland ones, allowing the X11 application to talk Wayland - a far more secure protocol.
The Snap security framework then ensures this X11 server is private to the application snap.
Toolkits without Native support for Wayland
- Java apps
- Windows apps emulated under Wine
It may also be your application is written using X11 calls directly, in which case this guide is for you.
If your application is written using GTK3/4, Qt5 or SDL2, or another toolkit with native Wayland support, you should follow this guide.
3. Introducing glxgears and Mir support for X11
A large fraction of applications are still written for X11 - there are those written with Qt4 and Gtk2, but also Java, Mono or Wine-based. We can snap these for a kiosk just fine, we just need to add some extra bits to the snap.
We’ll take a trivial example to start with (glxgears). glxgears is again a handy snap to have, as it will help prove OpenGL is working for X11 apps inside Ubuntu Core.
Install Ubuntu Frame
We will do our initial development and testing on your Linux desktop, and for this you should install ubuntu-frame:
sudo snap install ubuntu-frame
To build snaps, you need to install snapcraft:
sudo snap install snapcraft --classic
and install Multipass:
sudo snap install multipass
5. Set up test & development environment
As you may be running a Wayland based desktop and we want to run your application against Ubuntu Frame, we need to set up a development environment for this. Open a terminal and type:
export WAYLAND_DISPLAY=wayland-99 ubuntu-frame&
This will open a “Mir-on-X” window with Ubuntu Frame running in it.
Why are we changing the
WAYLAND_DISPLAYenvironment variable? It’s because newer versions of Ubuntu use Wayland. If we run Ubuntu Frame without this, it will try to bind to the default,
wayland-0, which is already being used by your desktop. We assume that
wayland-99is definitely not in use. Unless you happen to be running 100 displays for some reason…
You should follow the tutorial using this terminal and this “Mir on X” window. If you close them, or open another terminal to work in remember to
export WAYLAND_DISPLAY=wayland-99 and, if necessary, restart Ubuntu Frame.
6. First Pass Snapping: Test on Desktop
For our first pass we will snap glxgears and run it on our Ubuntu desktop. This guide assumes you are familiar with creating snaps. If not, please read here first.
Create the snap directory by forking https://github.com/MirServer/mir_kiosk_x11-example
git clone https://github.com/MirServer/mir_kiosk_x11-example
Change to the new
Update the “snap/snapcraft.yaml” file with as follows…
Update the metadata:
@@ -1,7 +1,8 @@ -name: mir-kiosk-x11-example # YOUR SNAP NAME GOES HERE -version: '0.1' # YOUR SNAP VERSION GOES HERE -summary: example X11 kiosk # YOUR SUMMARY GOES HERE -description: example X11 kiosk # YOUR DESCRIPTION GOES HERE +name: mir-kiosk-x11-example +version: '0.1' +summary: example (glxgears) X11 kiosk, using mir-kiosk-x11 +description: | + example (glxgears) X11 kiosk, using mir-kiosk-x11
Add “glxgears” to the app command:
@@ -12,7 +13,7 @@ - command: usr/local/bin/x11_kiosk_launch ### YOUR COMMAND GOES HERE + command: usr/local/bin/x11_kiosk_launch glxgears
Add the packaging commands for glxgears:
- ### YOUR PART GOES HERE - your-part: + glxgears: plugin: nil + stage-packages: + - mesa-utils stage-snaps: [mir-kiosk-x11]
Here’s the full snapcraft.yaml for reference:
name: mir-kiosk-x11-example version: '0.1' summary: example (glxgears) X11 kiosk, using mir-kiosk-x11 description: | example (glxgears) X11 kiosk, using mir-kiosk-x11 base: core20 confinement: strict grade: devel apps: mir-kiosk-x11-example: # daemon: simple # restart-condition: always command-chain: - env-setup command: usr/local/bin/x11_kiosk_launch glxgears plugs: - opengl # For Mir - wayland # For Mir - network-bind # For Mir (to serve X11) architectures: - build-on: amd64 - build-on: arm64 - build-on: armhf parts: glxgears: plugin: nil stage-packages: - mesa-utils stage-snaps: [mir-kiosk-x11] layout: /usr/share/X11: bind: $SNAP/usr/share/X11 /usr/bin/xkbcomp: symlink: $SNAP/usr/bin/xkbcomp /usr/share/icons: bind: $SNAP/usr/share/icons /usr/share/fonts: bind: $SNAP/usr/share/fonts /etc/fonts: bind: $SNAP/etc/fonts
Create the snap by running
If your device uses the
aarch64architecture, you will need to add the
--use-lxdflag when running
You should be left with a “mir-kiosk-x11-example_0.1_amd64.snap” file.
Let’s test it!
sudo snap install --dangerous ./mir-kiosk-x11-example_0.1_amd64.snap snap connect mir-kiosk-x11-example:wayland ubuntu-frame:wayland mir-kiosk-x11-example
You should see a fullscreen gear animation in the Mir-on-X window.
7. Snapping to use on a device
Now make another update to your snapcraft.yaml file:
-# daemon: simple -# restart-condition: always + daemon: simple + restart-condition: always
This changes the snap from a simple command to a “daemon” which is how thing are usually run on devices. Check this builds locally before proceeding:
8. Building snaps for different architectures
Your device is likely not the same architecture as your desktop, so you need to build your snap for that architecture. You can do this using
Depending on the load on the builders this can take a few minutes to complete. Once it completes you should have an suitable
.snap for your device architecture. (We’ll assume that is “armhf” for these notes, but you can replace that with whatever suits your case best.)
9. Second Pass Snapping: Your Device
Open another terminal and ssh login to your device and from this login install the Ubuntu Frame snap.
snap install ubuntu-frame
It auto-starts, so now you should have a graduated grey screen.
Ubuntu Frame provides the graphical environment needed for running a graphical snap.
10. Deploy the snap on the device
Push the snap you built to your device using your device’s SSH username & IP address details:
scp mir-kiosk-x11-example_0.1_armhf.snap <user>@<ip-address>:~
We now have the .snap file on the device in its home directory. We need to install the snap, configure it to talk Wayland to mir-kiosk and run the application. In your ssh session to your device:
snap install --dangerous ./mir-kiosk-x11-example_0.1_armhf.snap
On your device, you should see the same graphical animations you saw earlier. It will continue to run until you run “
snap stop mir-kiosk-x11-example”
Your device is now a kiosk! Rebooting will restart Ubuntu Frame and mir-kiosk-x11-example automatically.
Should you wish to share this snap, the next step would be to push your snap to the Snap Store. And, once you are satisfied that the snap is working well from the store, you should change the grade to stable so that you can publish to the “stable” channel:
- grade: devel + grade: stable
Congratulations, you have created your first graphical snap of an X11 app for Ubuntu Core.