Sharing happens at the filesystem level, which means anything that can be expressed as a file can be shared. This includes executables, libraries and data files, but also sockets.
Sharing content
By default, when multiple directories are shared from a producer snap, or when multiple slots are connected to a single plug, the shared content is merged under the target path of the consuming path’s plug definition. This behaviour can be modified with the source attribute.
Read, write and target should start with either $SNAP, $SNAP_DATA or $SNAP_COMMON to refer to the designated directory. See Environment variables for details on where these point to on the filesystem.
Paths declared in a slot’s source attribute can also reference $SNAP_INSTANCE_NAME, but only within a path that starts with $SNAP_DATA or $SNAP_COMMON. This is useful for sharing a separate, per-instance directory with consumers of a parallel-installed producer snap. See the Parallel installed content providers section below. Requires snapd 2.79+.
The content identifier specified by the consuming snap (plug) must match the content attribute of the producer snap (slot).
At a very basic level, the content interface enables one directory, file or socket to appear in a place where another snap can access it.
Each example below involve two snaps: the first provides some content (using a content slot) while the second consumes that content (using a content plug).
In all of the cases we see a small set of attributes defined on the particular interface:
the producer declares which path can be read, using either the read attribute for read-only, or the write attribute for both read and write permissions
the consumer uses the target attribute to define where the content should become available at runtime.
both the producer and consumer use an arbitrary content attribute to describe the content. This attribute must match on both sides for the connection to happen.
Using source
The source attribute presents one or more sub-directories, shared from a slot to a plug, beneath the plug’s target path. Adding the source attribute ensures that sub-directories, shared from one or more producer snaps, are presented separately to the consumer snap beneath its target path.
When multiple slots are connected to the same plug and they share directories with the same name, those directories are given unique names with the following pattern: <directory>, <directory>-2, <directory>-3, <directory>-x. The first connection keeps the original directory name from the slot; subsequent connections with the same name get the suffixed form.
With the following example, directories from the producer snap are shared in corresponding directories beneath the consumer snap’s target path:
producer/snapcraft.yaml:
slots:
<slot_name>:
interface: content
content: my-binaries
source:
read:
- $SNAP/bin # binaries to share
- $SNAP/libs # libraries to share
consumer/snapcraft.yaml:
plugs:
<plug_name>:
interface: content
content: my-binaries
target: $SNAP/connected-content
Using the above configuration, the consumer snap could implement a part to run an executable from the following path:
$SNAP/connected-content/bin/<executable-name>
While the libraries are accessible at:
$SNAP/connected-content/libs/<library-name>
When more than one slot, providing similarly named sources, is connected to the same plug, the plug directory for the new connection will be incremented:
$SNAP/connected-content/bin-2/<executable-name>
Directory names are preserved after a reboot.
Parallel installed content providers
Requires snapd 2.79+.
When a slot’s source attribute declares paths starting with $SNAP_DATA or $SNAP_COMMON and with $SNAP_INSTANCE_NAME as the final path element, it allows a consumer snap to get a separate directory for each instance of the parallel-installed producer snap.
producer/snapcraft.yaml:
slots:
<slot_name>:
interface: content
content: assets
source:
read:
- $SNAP_DATA/assets/$SNAP_INSTANCE_NAME
consumer/snapcraft.yaml:
plugs:
<plug_name>:
interface: content
content: assets
target: $SNAP_DATA/assets
If multiple instances of producer snap such as my-producer, my-producer_inst1 and my-producer_inst2 are all connected to the consumer’s plug, the consumer sees each producer’s content under its own instance name:
$SNAP_DATA/assets/my-producer/...
$SNAP_DATA/assets/my-producer_inst1/...
$SNAP_DATA/assets/my-producer_inst2/...
Since $SNAP_INSTANCE_NAME is only known at content provider snap installation time, it can only be used when sharing data from writable locations, such as $SNAP_DATA or $SNAP_COMMON.
Using the read and write attributes
Without the source attribute, content from the producer snap is mounted at the exact path indicated in the consumer snap:
producer/snapcraft.yaml:
slots:
<slot_name>:
interface: content
content: my-binaries
read:
- $SNAP/bin
consumer/snapcraft.yaml:
plugs:
<plug_name>:
interface: content
content: my-binaries
target: $SNAP/usr/local/bin
Using the above configuration, the consumer snap can access executables using the following path:
$SNAP/usr/local/bin/<executable-name>
When more than one slot is connected to the same plug, the plug directory for the new connection will be incremented using the same mechanism as with the source attribute described above (<directory>, <directory>-2, <directory>-3, <directory>-x).
It is recommended to only expose a single read path using this configuration.
Read-only content sharing
Read-only content sharing is ideal for executables, libraries and files related to global graphical themes or images.
Sharing an executable
When the following two interfaces are connected, the consumer snap can invoke executables from $SNAP/usr/local/bin:
producer/snapcraft.yaml:
slots:
<slot_name>:
interface: content
content: my-binaries
read:
- $SNAP/bin
consumer/snapcraft.yaml:
plugs:
<plug_name>:
interface: content
content: my-binaries
target: $SNAP/usr/local/bin
The directory can be added to PATH in the wrapper script, if desired, and the directory can also be inspected by any applications that wish to check if the extra executables are available (they can then fail gracefully).
Sharing a C-level library
A consumer snap can link to libraries shared by a producer snap, using the read/write attributes:
producer/snapcraft.yaml:
slots:
old-libraries:
interface: content
content: lib0-1604
read:
- $SNAP/lib
consumer/snapcraft.yaml:
plugs:
old-libraries:
interface: content
content: lib0-1604
target: $SNAP/extra-libs
After connecting the interface, the consumer snap can link to libraries from $SNAP/extra-libs. The directory can be added to LD_LIBRARY_PATH in the wrapper script if desired.
The value of the content attribute can be anything, but it is good practice to follow the form nameAPI-BUILDENV to remind slot consumers of the API level and build tools used. This naming convention is also required when sharing content between snap publishers.
In the above example:
API and BUILDENV can be anything that is meaningful to the provider and consumers. For example, the GNOME content snap uses gnome-3-26-1604 to denote the full GNOME 3.26 platform libraries and supporting files built on Ubuntu 16.04 LTS.
Sharing icons or themes
A consumer snap can link to themes shared by a producer snap, which exposes multiple directories using the source attribute:
producer/snapcraft.yaml:
slots:
icon-themes:
interface: content
content: icon-themes
source:
read:
- $SNAP/share/icons/Yaru-MATE-dark
- $SNAP/share/icons/Yaru-MATE-light
consumer/snapcraft.yaml:
plugs:
icon-themes:
interface: content
content: icon-themes
target: $SNAP/themes
After connecting the interface, the consumer snap can observe all themes under $SNAP/themes, and the directories would be exposed as:
$SNAP/themes/Yaru-MATE-dark
$SNAP/themes/Yaru-MATE-light
Content identifier obligations
The content identifier attribute identifies a mostly-immutable compatibility contract (API/ABI or similar) between the snap providing the corresponding content and the snaps consuming it.
The providing snap must preserve backward compatibility for the content provided under a given identifier.
Equally, updates to consuming snaps cannot strongly depend on changes of the identified content from updates to the providing snaps.
There is no support in snapd to synchronise updates between consuming and providing snaps. Compatibility breaking changes need to happen under a different content identifier.
Technical details
The content interface is implemented via an interplay between two systems:
AppArmor and bind mounts.
By default, the AppArmor sandbox allows writes to $SNAP_DATA and reads from $SNAP (see Environment variables for details).
The content interface takes advantage of this feature to map data from other locations to either $SNAP or $SNAP_DATA.
A bind mount is then created to link $SNAP in one snap (e.g. from /snap/my-snap/1234/content) to an empty directory in the other snap (e.g., to /snap/my-other-snap/4321/incoming-content). Locations need to be selected carefully because a bind mount could potentially permit mounts over important locations, such as $SNAP_DATA, preventing access to a snap’s own writable space.
The same can be done for particular files, if desired, but it requires a pair of interfaces for each file and is more cumbersome.