Currently snapd includes support for user daemons and dbus activation that is currently gated behind experimental feature flags. I would like to see those flags turned on by default. For reference, these flags enable the following:
experimental.user-daemonsadds a new app property
daemon-scope. When set to
user, this causes the daemon to be installed to run under user session instances of systemd rather than the pid 1 system instance.
experimental.dbus-activationadds a new app property
activates-on, which contains a list of slot names referencing
dbusinterface slots a daemon should activate on. If set, the daemon’s start up will be delayed until any of the given service names are used on the relevant bus.
Both flags can be confined to support D-Bus session bus activatable services. But each flag can be useful on its own. The
user-daemons flag on its own enables user session timers and services. The
dbus-activation flag on its own enables activatable system bus services.
Below are the things I believe need to be done before these features can be enabled. If anyone has anything needed to complete it, that would be great.
user-daemons feature flag enables
snap.yaml syntax like the following:
apps: myservice: command: bin/myservice daemon: simple daemon-scope: user
daemon-scope property can take the values
system as the default providing the current behaviour. An app that sets
daemon-scope: user will instead be registered with the user session instance(s) of systemd. All properties used to configure system daemons can also be used to configure user session daemons too. This means things like socket activation and timers will also work for user session daemons.
When snapd installs, upgrades, or removes a snap containing a user daemon, it contacts the snap session agent for each running user session to ask that they start or stop the associated daemons. At present, that is the extent to how much snapd controls user daemons at present. Snapd does provide other ways of interacting with system daemons, which have not been updated though:
At the moment, snapd records the enabled state of all system daemons before an upgrade, and restores that state after the upgrade. It should do the same for user daemons.
snap serviceslists the status of system daemons. We can easily enumerate user daemons too and determine their global enabled/disabled state. We should be able to determine the active state of user daemons via the session agent, but as there could be 0…n instances of the daemon it might not be cleanly “active” or “inactive”.
snap stopcan be used to start and stop system daemons, and optionally enable or disable the service. This could be extended to cover user daemons, but it opens the question of whether they should affect all instances of the daemon or just that for the current user. Maybe we want to support both, but via an option for the command?
If we do allow starting/stopping an individual user’s instance of a user daemon, should unprivileged users be able to call these commands? We can certainly detect the calling process’s user ID within the REST API, but does exposing those endpoints to unprivileged users have any unwanted side effects?
If we allow unprivileged users to control their own instance of the daemon, does that mean we need to track per-user enabled state of user daemons? (currently we only care about the global enabled state).
snapctlequivalents of all of the above that can be called by code running in the context of a snap, and limited to daemons belonging to the snap. I believe the same concerns are relevant here.
I think that covers all the missing features. It’s possible that we decide not to implement some of these features for user daemons, or decide that they shouldn’t block us from turning on the feature. I think (1) probably needs doing though.
dbus-activation is the less risky one to enable by default. Here is an example of the
snap.yaml syntax for a D-Bus activated service:
slots: dbus-slot: interface: dbus bus: system name: org.example.Foo apps: myservice: command: bin/myservice daemon: simple activates-on: [dbus-slot]
Compared with a similar
snap.yaml without the
activates-on property, the behaviour is changed as follows:
- The systemd service generated for
myserviceis no longer wanted by
multi-user.target, so will not be started on boot.
/var/lib/snapd/dbus-1/system-services/org.example.Foo.servicefile will be written out allow dbus-daemon to start the service. The service activation file includes a
SystemdServicekey so that dbus-daemon will start the corresponding systemd service. The
AssumedAppArmorLabelkey is also set so that communication that depends on the label (e.g. mediated method calls between two snaps) can activate the daemon.
The snap has no more access to the system, since without
activates-on, it could have the daemon start on boot and request the given D-Bus name. The main change is that the snap can now delay initialisation until (or even if) the service is used. If the service implements some kind of “exit on idle” policy, it may even be able to remain stopped most of the time.
And since the activated daemon is still run under systemd, snapd can control it the same as any other daemon provided by a snap.
I’d welcome feedback from developers who would like to experiment with these features. You can enable them on your system by executing the following commands:
snap set system experimental.user-daemons=true snap set system experimental.dbus-activation=true
The new syntax is not supported by Snapcraft yet (there are PR #3129 and PR #3425, but they probably won’t be merged until the features are enabled), but they can still be used via the
passthrough: feature described here:
I’d welcome any feedback, including where things do not behave as you’d expect or additional issues you believe should be addressed before the features are enabled.