# Snapd packaging

Snapd upstream project provides reference packaging for various distributions
that are part of its CI process (Ubuntu, Debian, Fedora, openSUSE, Arch etc).
The reference packaging is provided under the top level [packaging
directory](/packaging) in snapd source tree.

It is recommended to stay as close as possible to what is in the upstream
repository. Snapd developers keep the packaging updated as new features or
dependencies are introduced. At the same time, we are happy to accept patches
and contributions related to packaging. If you find that the reference packaging
is done incorrectly, or does not meet established conventions for your
distribution, feel free to open a pull-request.

The guide focuses on packaging of snapd for use with a classic Linux
distribution, such as Ubuntu, Fedora, Arch Linux, and **not** an immutable
system like Ubuntu Core.

## Dependencies

Majority of snapd is written in Go, but some smaller bits are written in C. See
the [go.mod](/go.mod) file for the minimum required version of Go. While for
building the C code any reasonably recent version of GCC or Clang will suffice.

Other high level build and runtime dependencies are:
- Linux kernel >= 4.4 (or otherwise reasonably recent)
- systemd
- autotools, pkg-config
- libc static
- libudev
- libseccomp
- libcap
- libapparmor (if using AppArmor)
- libselinux (if using SELinux)
- gnupg2
- python3-docutils
- squashfs

## Source tarballs

Snapd [release assets](https://github.com/canonical/snapd/releases/latest)
consist of 3 source tarballs:

- `snapd_<ver>.tar.xz` (e.g. `snapd_2.72.tar.xz`) - a unified snapshot, which
  includes the snapd source tree plus all vendored Go dependencies
- `snapd_<ver>.no-vendor.tar.xz` - snapshot without any Go dependencies
- `snapd_<ver>.only-vendor.tar.xz` - snapshot of all Go dependencies only

Refer to the packaging policy of your distribution to decide which source
tarball is appropriate for your use case.

## Configuration and build

The build process of snapd is split in a couple of parts:
- set snapd version
- configure and build the C code
- configure and build the Go code
- generate any other data files

The build version is set by invoking `mkversion.sh` script like so:

``` sh
./mkversion.sh <snapd-version>
# e.g. 2.72.1 with Arch build tag
./mkversion.sh 2.72.1-arch1
```

### C

All of the C code is contained within `cmd` directory. It is configured and
built using autotools. The high level process for building the code is:

``` sh
cd cmd
autoreconf -i -f
./configure <options>
```

Most relevant `configure` options:
- `--enable-apparmor` - enable AppArmor support
- `--enable-selinux` - enable limited SELinux support
- `--libexecdir` - set the location of where snapd internal tools are located,
  typically /usr/lib/snapd (or /usr/libexec/snapd)
- `--with-snap-mount-dir` - expected snap mount directory, typically
  /var/lib/snapd/snap (or /snap)

### Go

It is recommended to use `packaging/snapd.mk` helper for simplifying the build
of Go code which will invoke `go build` with appropriate options and build tags.

The helper requires a bit of configuration, see [openSUSE RPM
spec](/packaging/opensuse/snapd.spec) for examples of usage. It expects to find a
`snapd.defines.mk` configuration file within the build directory. The file
itself it directly included by `snapd.mk` and so it must follow Make syntax. An
example snippet which generates the configuration file, extracted from openSUSE
RPM spec, is provided below:

``` make
# This file is generated by openSUSE's snapd.spec

###
# Directory variables.
###
# Desired installation prefix, usually /usr
prefix = %{_prefix}
# Desired installation path for system wide binaries, usually /usr/bin
bindir = %{_bindir}
# ...and /usr/sbin
sbindir = %{_sbindir}
# Desired location of snapd internal tools, one of /usr/lib/ or /usr/libexec/
libexecdir = %{_libexecdir}
# Location of manpages, typically /usr/share/man
mandir = %{_mandir}
# Location of data files, typically /usr/share
datadir = %{_datadir}
# Location of local state, typically /var/lib
localstatedir = %{_localstatedir}
# Location of shared state, typically /var
sharedstatedir = %{_sharedstatedir}
# Systemd system units directory, usually /usr/lib/systemd/system
unitdir = %{_unitdir}
# Build directory, depends on the build environment
builddir = %{_builddir}

###
# Build configuration
###
# Enable or disable Ubuntu Core specific pieces, 0|1
with_core_bits = 0
# Expect alternative snap mount directory, 0|1
with_alt_snap_mount_dir = 1
# Enable AppArmor support, 0|1
with_apparmor = 1
# Enable use of PIE when building static binaries, 0|1
# Ensure support in your distribution toolchain.
with_static_pie = $build_with_static_pie
# Use vendored dependencies, 0|1
with_vendor = 1
# Pass any additional flags to `go build`
EXTRA_GO_BUILD_FLAGS = -v -x
# Pass any additional flags for `go tool link` in go build invocation
EXTRA_GO_LDFLAGS = -compressdwarf=false
# Enable test keys
# NOTE this option is relevant only in builds for snapd CI
with_testkeys = 0
```

The helper is invoked like so:

```sh
make -f packaging/snapd.mk \
    SNAPD_DEFINES_DIR=$PWD \
    install  # or: build|check|clean|all
```

Where `SNAPD_DEFINES_DIR` is set to the path of the directory containing
`snapd.defines.mk` file.

### Other build artifacts

The top level `data` directory contains additional files that need to be
configured and built. Those are systemd units, completion support, SELinux
policy module, environment tweaks and similar. The build and installation is
carried out like so, passing a number of overrides for the desired filesystem
locations (snippet extracted from [Fedora
packaging](/packaging/fedora/snapd.spec)):

``` sh
pushd ./data
make BINDIR="%{_bindir}" LIBEXECDIR="%{_libexecdir}" DATADIR="%{_datadir}" \
     SYSTEMDSYSTEMUNITDIR="%{_unitdir}" \
     USE_CANONICAL_SNAP_MOUNT_DIR=false \
     USE_ALT_SNAP_MOUNT_DIR=true \
     SNAPD_ENVIRONMENT_FILE="%{_sysconfdir}/sysconfig/snapd"

# or
make install BINDIR=...
popd
```

It is essential to pass the same set of Make variable overrides during build and
installation.

### Notes on building

The following binaries are provided by the native host package, but need to be
executed within the snap sandbox, and as such they need to be built statically:
- snap-exec
- snap-update-ns
- snapctl
- snapd-gdbserver-shim

The reference packaging contains relevant snippets for confirming that the
binaries listed above are indeed built statically. Packagers are encouraged to
copy the applicable snippets as needed.

## Installation

### snap-confine

The `snap-confine` binary is expected to have the file capabilities listed in
[`cmd/snap-confine/snap-confine.v2-only.caps`](/cmd/snap-confine/snap-confine.v2-only.caps).
set on it once the package has been installed.

The [`cmd/snap-confine/snap-confine.caps`](/cmd/snap-confine/snap-confine.caps)
file lists the same set of capabilities, but additionally includes
`cap_setuid,cap_setgid`, which is a workaround for kernel cgroup v1 issues and
relevant only if expecting that the snapd package will be used within a
container running on a host kernel that is booted with cgroup v1 support.
Packaging is encouraged to **not** ship or otherwise use this file, unless
deemed necessary for the use case.

### Systemd units

The main systemd units are `snapd.service`, the snapd daemon, and
`snapd.socket`, a socket unit which is responsible for creating the two sockets
over which clients communicate with snapd: /run/snapd.sock (for `snap` command),
and /run/snapd-snap.sock (for calls from within the sandbox using `snapctl`
tool).

On systems with AppArmor enabled, `snapd.apparmor.service` needs to be installed
as well. It is a dedicated unit which invokes `snapd-apparmor` internal tool,
that is responsible for loading AppArmor profiles of installed snaps.

The table provides a summary of all systemd units that are relevant for classic
distribution packaging:

| Unit                          | Kind   | Purpose                                                               |
|-------------------------------|--------|-----------------------------------------------------------------------|
| `snapd.service`               | system | Main snapd service                                                    |
| `snapd.socket`                | system | Snapd sockets unit, activates `snapd.service`                         |
| `snapd.apparmor.service`      | system | Loads snap AppArmor profiles                                          |
| `snapd.mounts.target`         | system | Synchronization target right after snap mounts have completed         |
| `snapd.mounts-pre.target`     | system | Synchronization target for mounting snap units                        |
| `snapd.seeded.service`        | system | Service waiting for snapd initialization to complete                  |
| `snapd.session-agent.service` | user   | User session agent                                                    |
| `snapd.session-agent.socket`  | user   | User session socket for communication with snapd, activates the agent |

It is recommended to enable at least `snapd.socket` and
`snapd.session-agent.socket` units, however this should be done in accordance
with a policy established by a given distribution.

### Directories

The `snapd` daemon will create relevant state directories as needed (see the
reference packaging for a list of actual locations), however some directories
are expected to be created by the packaging, those are:

- /usr/lib/snapd or /usr/libexec/snapd - snapd internal tools directory
- /var/lib/snapd/snap or /snap - snap mount directory

Consult your distribution policies to decide which locations are appropriate.

Historically, the internal tools and snap mount directories needed to be
hardcoded in snapd, however starting with 2.71, `snapd` will probe and use the
locations created by distribution packaging.

Support for classic snaps (such as code editors, compilers etc.) requires /snap
to be present, either as directory, or as a symbolic link pointing to
/var/lib/snapd/snap. The users are free to create a symbolic link themselves and
the packaging should account for that.

### Support scripts

Snapd provides support scripts that can be used during package installation:

#### `snap-mgmt`

This script is expected to be invoked during package removal, in a pre-remove
step, like so:

``` sh
${libexecdir}/snapd/snap-mgmt --purge
```

Its purpose is to ensure that all snaps and their data is removed from system wide locations.

#### `snap-mgmt-selinux`

A support script which can be invoked from the snapd SELinux policy subpackage.
Its purpose is to patch snap mount units to use a dedicated SELinux context for
all snap mounts. The script is only relevant if there is possibility that the
users, on a SELinux enabled system, may be updating from snapd older than 2.39.
