Lets get snappy! Snaps are a containerization system that makes it easy to package and distribute a complete set of dependencies and files needed for an application. As an example, several snaps are now available on the LimeNet store for the Lime Suite GUI, Pothos GUI, GQRX, and GNURadio companion. In this blog, I will cover basics about creating and installing snaps, and provide instructions for using the snaps on the LimeNet store.
Lets cover some basics for creating a snap package. At its most basic, a snap is described a single text file named “snapcraft.yaml”. In practice, a few other files usually accompany the .yaml file such as icons, desktop launchers, and scripts for setting up environment variables. Its also possible to include source code as well, although my preference is to to fetch the various sources from a repository like github.
To start with, we are going to need an Ubuntu PC with “snapcraft” and “snapd” installed. Snapcraft is a command line tool used for building snaps. It will also automatically download any additional dependencies needed by the snap. So all you will need from this point on is a text editor.
sudo apt-get install snapcraft snapd
In short, a “snapcraft.yaml” lists all of the dependencies needed to build and package an application, and all of the relevant executables and binaries that will be used. This can include GUI applications, server daemons, bash scripts, python, and more. Here is a real-world example that packages LimeSuite along with SoapySDR:
name: limesuite-example version: 2016.09 summary: LimeSuite Example description: Example package with LimeSuite confinement: strict apps: SoapySDRUtil: command: SoapySDRUtil plugs: [home] LimeUtil: command: LimeUtil plugs: [home] parts: soapysdr: plugin: cmake source: https://github.com/pothosware/SoapySDR.git limesuite: plugin: cmake source: https://github.com/myriadrf/LimeSuite.git after: [soapysdr] build-packages: - libsqlite3-dev - libi2c-dev - libusb-1.0-0-dev
- “after” allows us to order dependencies. SoapySDR must be built first in this case.
- “build-packages” specifies a list of dependencies that are found in the apt repository.
- “plugs” are part of the security features of snaps, but more on that in another section.
- Read more about the snapcraft yaml syntax.
- Read more about the syntax for snapcraft parts.
To build the snap, simply open a terminal and cd into the directory containing “snapcraft.yaml”. Now run:
After some time compiling, you should see a few build directories and a .snap file:
>>> ls limesuite-example_2016.09_amd64.snap parts prime setup snapcraft.yaml stage
Installing the snap
Now use the snap command to install the new .snap package:
sudo snap install --force-dangerous limesuite-example_2016.09_amd64.snap
Don’t worry about the “–force-dangerous” part, that’s just because the snap command does not know that the file is from a trusted source, even though you had just compiled it. The snap should now be installed — but where did it go?
>>> ls /snap/limesuite-example/ current x1
The “x1” directory contains the snap that you just installed. Look inside, you will see all of the various dependencies and installed files. Subsequent installs of limesuite-example would create an x2, x3, etc directory so you can always roll back to a previous version. And “current” is a symlink to the latest version of the snap that you just installed. In this case its just a symlink to the x1 directory.
You will be able to find the commands specified in the “app” section under the /snap/bin/ directory. Executables take the form “package.command”. So in this case expect two executables named “limesuite-example.SoapySDRUtil” and “limesuite-example.LimeUtil”. This may seem cumbersome, but it avoids name collisions with other snaps that may have overlapping applications. Also note that /snap/bin/ is in the PATH by default, so one only needs to type “package.command” at the prompt.
To help shed some more light on snaps and how they work, I will try to cover some other aspects that came up during packaging and development.
An installed snap does not simply get free-run of your system. Every command is confined by AppArmor which gives restrictive access based on a profile. To access the user’s home directory, graphics, network, devices, and other restricted elements; each “app” entry also specifies a list of interfaces in the “plugs”. See the complete interfaces list for existing options. In the limesuite-example, we specified “plugs: [home]“ because LimeSuite uses the home directory to store calibration data.
Snapcraft can also package graphical apps with a few extra additions to the .yaml file. Here is an example for GQRX. Note that not all of the yaml file is show:
apps: gqrx: command: desktop-launch gqrx plugs: [network, x11, pulseaudio, opengl, home] parts: gqrx: plugin: cmake source: https://github.com/csete/gqrx.git build-packages: [qtbase5-dev] after: [grosmosdr, desktop-qt5]
- The command uses “desktop-launch” before the actual executable
- The plugs also include “x11” and “opengl” for graphics access
- Notice the use of “desktop-qt5” for the after dependencies
- There is a similar “desktop-gtk3” for gtk-based apps
In addition, snaps can package menu launchers in setup/gui/app-name.desktop. The .desktop files follow the freedesktop.org specification and will appear in the menu of the window management environment like Unity or KDE.
Since snaps get installed to an arbitrary directory and have pretty strict access rules, its important to let your application know where its installed (so it can find resources) and where it can store its configuration files. Fortunately, a myriad of SNAP_ environment variables are set before executing the application just for this purpose.
However, most applications are not already looking for “SNAP_” environment variables but often specify their own. For example, SoapySDR uses SOAPY_SDR_ROOT to locate the installation directory, which it needs to locate its plugin modules. In this case, and many others, I found it necessary to create a simple adaptor script to set expected variables based on the SNAP_ variable names. Example:
#!/bin/sh export SOAPY_SDR_ROOT=$SNAP export APPDATA=$SNAP_USER_COMMON export GRC_BLOCKS_PATH=$SNAP/share/gnuradio/grc/blocks export VOLK_CONFIGPATH=$SNAP_USER_COMMON
This script is then installed by the snapcraft.yaml and called in the command right before the actual executable.
Snaps for LimeSDR
Snaps for Lime Suite GUI, Pothos GUI, GQRX, and GNURadio companion have been uploaded to the LimeNet store. In this section we will cover installing snaps from the store and using them with the LimeSDR. The source code used to build all of these snaps can be found in the myriadrf snapcraft sandbox. Anyone should be able to clone, build, modify these snaps, or take them as an example to package other applications altogether.
Setup Udev rules
Until hotplugging capabilities are added to snaps, its necessary to create udev rules for the LimeSDR. The udev rules consist of a simple one-line file that gives user (non-root) access to the LimeSDR. Run the following command to create a udev rule for the LimeSDR:
wget https://raw.githubusercontent.com/myriadrf/LimeSuite/master/udev-rules/64-limesuite.rules sudo mv 64-limesuite.rules /etc/udev/rules.d/ sudo udevadm control --reload-rules
The LimeNET store
To download and install snaps from the LimeNET store, we must set the store ID in the environment variables and restart the snapd service. The snap command line tool should be able to locate and install the new snap-based applications after this step.
#set the UBUNTU_STORE_ID in /etc/environment >>> sudo edit /etc/environment #add this line: UBUNTU_STORE_ID=LimeNET
And now restart the snapd service:
>>> sudo service snapd restart
The LimeNET store should now be accessible:
>>> snap find lime Name Version Developer Notes Summary limesdr-grc 184.108.40.206.0 goq - LimeSuite + GRC limesdr-pothos 0.4.1.0 goq - LimeSuite + Pothos apps limesdr-gqrx 2.6.1 goq - LimeSuite + GQRX limesuite 220.127.116.11 goq - LimeSuite GUI and command line utils
Now the first application be installed. Lets install the “limesuite” snap. This snap contains the hardware support GUI for the LimeSDR and a helpful command line application LimeUtil. First install the “limesuite” snap with the following command:
sudo snap install --devmode limesuite
Now its possible to see how snaps get installed on the system:
>>> ls /snap/bin/limesuite.* /snap/bin/limesuite.LimeSuiteGUI /snap/bin/limesuite.LimeUtil >>> ls -l /snap/limesuite/ total 0 drwxr-xr-x 10 root root 211 Oct 7 10:48 2 lrwxrwxrwx 1 root root 1 Oct 19 22:07 current -> 2
Running the applications:
Graphical apps like the LimeSuiteGUI can also be found in the menu of your window manager (KDE pictured):
And command line apps lime LimeUtil can be run if properly prefixed with the snap name:
>>>limesuite.LimeUtil --find * [USB 3.0 (LimeSDR-USB), media=USB, module=STREAM, addr=241:1204]
Notes about devmode
Without the hotplugging capabilities, which are still in development, AppArmor will block LimeSuite and libusb from communicating with the LimeSDR. Fortunately, we have a quick work around. Snaps can be installed with the “–devmode” option. This bypasses the security profile and can also be used to make development easier to experiment with snaps without considering the plugs and interfaces. But we should have a complete solution to this in the near future.
Now lets install the LimeSDR snap this GQRX. This snap includes LimeSuite, SoapySDR, and GQRX:
sudo snap install --devmode limesdr-gqrx
Since GQRX is a graphical application, it can also be launched from the menu:
Snaps are a complete container system for sharing an application and all of its dependencies. Snaps offer both security through signing and validation, as well as confinement rules between applications and the rest of the system. With a little upfront work from a developer, any application can be snapped, and uploaded to a store to share with millions. The LimeNET store will continue to grow and expand to include new SDR applications. And snaps for LimeSDR support are expected to get even easier as new hotplugging support enters the snap ecosystem.