Split Personality Snaps
by Alan Pope on 25 June 2020
Broadly speaking, most snaps in the Snap Store fall into one of two categories, desktop applications and server daemons. The graphical applications such as Chromium and Spotify use desktop files, which ensure they can be opened on demand by any user via a menu or launcher. The server applications such as NextCloud and AdGuard-Home typically have systemd units, which control their automatic (background) startup.
Taking an existing desktop application and converting it to an always-running appliance leads to some interesting engineering challenges. Applications and games tend to have expectations for what programs and services are accessible at runtime, which need mitigating. Application confinement in snaps on Ubuntu Core means some assumptions about file and device access may no longer apply.
We will typically need to stand-up a configuration in which the application believes it’s running in a standard desktop environment. The application will also need the startup automated in an appliance setting, but launched on demand when in a desktop environment.
We can be quite creative with snaps and build a “split personality” snap that can run both as a desktop application and as an appliance!
Building Blocks
Ubuntu Core doesn’t ship with Xorg or PulseAudio out of the box. This isn’t a problem if the appliance doesn’t require a connected monitor, nor makes any sounds, as is the case with Plex Media Server and Mosquitto but this can be a problem with applications which require access to the display or sound hardware.
Unlike multi-user desktop environments, appliances tend to only require one local system user under which services automatically start. On a desktop system, the logged-in user launches applications on demand, whereas with an appliance, a system user launches the appliance on boot.
Our snap needs to service the needs of running on a desktop environment where Xorg and PulseAudio exist, and on an Ubuntu Core system where they don’t. It also needs to be capable of being manually launched in a window, but also auto-start full-screen on an appliance.
Multiple Personality Order
Let’s have a look at ScummVM, where we’ve done this work already.
ScummVM is a program that allows you to run a variety of classic graphical point-and-click adventure games like The Secret of Monkey Island, provided you already have their data files. This makes it a great asset in the hands of old-game fans.
It’s published as a snap in the Snap Store, and runs on many different desktop Linux distributions. We thought it might be fun to make a “ScummVM Appliance” using this snap on top of Ubuntu Core. The goal is to create a single purpose device, which boots directly to the game selection screen, and functions like a ScummVM console.
As Ubuntu Core has no Xorg, we need to use mir-kiosk to provide a Wayland-compatible display stack to drive an external monitor. Ubuntu Core also ships without PulseAudio, so we’ll get no in-game sound. This is resolved by installing the pulseaudio snap. Keyboards & mice will just work as expected.
To enable the snap to function both on-demand on a desktop, and launch automatically on an appliance, we need to create two ‘apps’ stanzas in the snapcraft.yaml file that is used to build the ScummVM snap.
apps:
scummvm:
command: desktop-launch $SNAP/bin/wayland-if-possible.sh $SNAP/bin/scummvm-launch.sh
daemon:
command: daemon-start.sh $SNAP/bin/scummvm-launch.sh -f
daemon: simple
The daemon: simple
directive means the specified command will launch automatically on start.
This command includes logic that detects whether the program is running on an Ubuntu Core appliance, and if it is, it will launch using Mir as the Wayland display system. If the logic determines it’s not running on an Ubuntu Core appliance, the script exits immediately.
#!/bin/sh
if [ "$(id -u)" = "0" ] && [ "$(snapctl get daemon)" = "false" ]
then
# If not configured to run as a daemon we have to stop here
# (There's no "snapctl disable …")
snapctl stop $SNAP_NAME.daemon
exit 0
fi
mkdir -p "$XDG_RUNTIME_DIR" -m 700
if [ -z "${WAYLAND_DISPLAY}" ]
then WAYLAND_DISPLAY=wayland-0
fi
real_wayland=$(dirname "$XDG_RUNTIME_DIR")/${WAYLAND_DISPLAY}
while [ ! -O "${real_wayland}" ]; do echo waiting for Wayland socket; sleep 4; done
ln -sf "${real_wayland}" "$XDG_RUNTIME_DIR"
exec "$@"
That’s essentially all we need! One single yaml and a smattering of scripting is all that’s required to make a dual-personality snap that can run on-demand in desktop contexts, and automatically when run as an appliance.
The ScummVM snap is built for both x86 and arm based CPUs, we could create a simple self-updating boot-to-game device based on a low-cost device like a Raspberry Pi. Plugin a keyboard, screen and mouse, and we can be gaming like it’s 1990 all over again.
If you have an idea for something we could turn into an official Ubuntu Appliance, please join the discourse and post a suggestion in the proposed new appliances category.