Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

interfaces: support D-Bus activation #6258

Closed
wants to merge 51 commits into from
Closed
Show file tree
Hide file tree
Changes from 42 commits
Commits
Show all changes
51 commits
Select commit Hold shift + click to select a range
f9209c2
interfaces/dbus: install conf files to point at D-Bus service activation
jhenstridge Dec 3, 2018
2324695
packaging: make sure extra /var/lib/snapd subdirs exist
jhenstridge Dec 3, 2018
70ad190
interfaces/dbus: add AddService method to dbus.Specification
jhenstridge Dec 3, 2018
ca5a1a5
interfaces/dbus: install and remove registered service activation files
jhenstridge Dec 3, 2018
4b4fab4
interfaces/builtin: add an "activatable" attribute to the dbus interface
jhenstridge Dec 4, 2018
651d584
tests: add dbus service test snap and spread test
mvo5 Dec 4, 2018
447c0f4
tests/lib/snaps: update test snaps for attribute name change
jhenstridge Dec 4, 2018
ed3c4ac
interfaces/dbus: don't try to copy the dbus config files if they're n…
jhenstridge Dec 5, 2018
be08f4f
tests: update dbus-activated test to only test the service, rather th…
jhenstridge Dec 5, 2018
74ad61f
tests: split dbus activation spread test into three
jhenstridge Dec 6, 2018
6f01882
interfaces/dbus: ensure we remove stale service activation files on u…
jhenstridge Dec 6, 2018
b4f85d6
wrappers: don't enable "daemon: dbus" services to start during boot
jhenstridge Dec 8, 2018
f192269
packaging: install compat symlinks for dbus config on Ubuntu 14.04
jhenstridge Dec 8, 2018
c1337f2
interfaces: include the "User" key in system service activation files
jhenstridge Dec 8, 2018
4d64766
tests: expect "daemon: dbus" service to be initially inactive
jhenstridge Dec 10, 2018
8ef5358
tests: use a client snap in dbus-activation tests to support core sys…
jhenstridge Dec 11, 2018
0392c81
interfaces: add the AssumedAppArmorLabel key to D-Bus .service files
jhenstridge Dec 15, 2018
e7fb17c
interfaces: require that activatable system bus names be attached to …
jhenstridge Dec 18, 2018
3be7f06
interfaces: alter the error message for when we find a conflicting
jhenstridge Dec 18, 2018
e3615af
interfaces: correctly distinguish between missing D-Bus service files
jhenstridge Dec 18, 2018
7a933c2
tests: skip system bus activation test on Trusty.
jhenstridge Dec 19, 2018
9b47622
tests: add a test for a D-Bus session bus managed by systemd
jhenstridge Dec 20, 2018
1d89564
tests: run dbus-activation-session-systemd as a regular user
jhenstridge Dec 21, 2018
c090c3c
snap: add activate-on attribute to apps, and remove bus-name.
jhenstridge Feb 21, 2019
c29c5db
interfaces: make dbus interface use activate-on attribute from app.
jhenstridge Feb 21, 2019
bba40c3
wrappers: generate BusName from last entry in activate-on stanza
jhenstridge Feb 22, 2019
7d2edb0
tests: update test-snapd-dbus-service snap to use activate-on
jhenstridge Mar 19, 2019
3f958aa
snap, interfaces, wrappers: rename "activate-on" attribute to "activa…
jhenstridge Apr 1, 2020
7eeca84
features: add a dbus-activation feature flag
jhenstridge Apr 1, 2020
8e9d600
overlord/snapstate: check the dbus-activation feature flag when insta…
jhenstridge Apr 2, 2020
ff3ee18
tests: update test snap to new snap.yaml syntax for dbus activation
jhenstridge Apr 2, 2020
10f73a5
tests: use user.sh fixture for dbus-activation-session-systemd test
jhenstridge Apr 2, 2020
8f02916
tests: fix shellcheck errors
jhenstridge Apr 8, 2020
3494675
data, packaging: rename D-Bus configuration fragments to match snapd.…
jhenstridge Apr 15, 2020
7e878e6
wrappers: copy D-Bus configuration from snapd snap on core18/core20
jhenstridge Apr 15, 2020
d52872e
cmd/snap-preseed: remove D-Bus activation files on reset
jhenstridge Apr 17, 2020
81bea96
tests: use session-tool in dbus-activation-session-systemd spread test
jhenstridge May 7, 2020
6642c75
interfaces: require that dbus activatable apps be daemons
jhenstridge May 12, 2020
5c74fd0
tests: switch test-snapd-dbus-service snap to use a user daemon
jhenstridge May 13, 2020
07304db
tests: treat the systemd case as the normal one for user sessions
jhenstridge May 13, 2020
31df3de
tests: remove generated D-Bus config fragments, similar to PR #8655
jhenstridge May 15, 2020
eefbc10
snap-mgmt: remove dbus activation files on purge
jhenstridge May 15, 2020
1c5533b
Merge remote-tracking branch 'upstream/master' into dbus-activation
jhenstridge May 28, 2020
9c307da
interfaces: improvements to error messages
jhenstridge Jun 2, 2020
f2f3485
snap: make AppInfo.ActivatesOn a slice of SlotInfos
jhenstridge Jun 2, 2020
d9f934e
snap: add more validation of slots listed in activates-on
jhenstridge Jun 3, 2020
44ea334
overlord/snapstate: refuse to install snaps with conflicting activata…
jhenstridge Jun 5, 2020
dc3a065
Merge remote-tracking branch 'upstream/master' into dbus-activation
jhenstridge Jun 15, 2020
61dbfe8
Merge remote-tracking branch 'upstream/master' into dbus-activation
jhenstridge Jul 6, 2020
c8283a7
Merge remote-tracking branch 'upstream/master' into dbus-activation
jhenstridge Jul 23, 2020
90d6838
Merge remote-tracking branch 'upstream/master' into dbus-activation
jhenstridge Nov 24, 2020
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
6 changes: 6 additions & 0 deletions cmd/snap-mgmt/snap-mgmt.sh.in
Expand Up @@ -119,6 +119,10 @@ purge() {
rm -f "/etc/systemd/system/multi-user.target.wants/$unit"
done

# dbus activation configuration
rm -f /etc/dbus-1/session.d/snapd.session-services.conf
rm -f /etc/dbus-1/system.d/snapd.system-services.conf

echo "Discarding preserved snap namespaces"
# opportunistic as those might not be actually mounted
if [ -d /run/snapd/ns ]; then
Expand All @@ -143,6 +147,8 @@ purge() {
rm -rf /var/snap

echo "Removing leftover snap shared state data"
rm -rf /var/lib/snapd/dbus/services/*
rm -rf /var/lib/snapd/dbus/system-services/*
rm -rf /var/lib/snapd/desktop/applications/*
rm -rf /var/lib/snapd/seccomp/bpf/*
rm -rf /var/lib/snapd/device/*
Expand Down
2 changes: 2 additions & 0 deletions cmd/snap-preseed/reset.go
Expand Up @@ -70,6 +70,8 @@ func resetPreseededChroot(preseedChroot string) error {
filepath.Join(dirs.SnapCacheDir, "*"),
filepath.Join(apparmor_sandbox.CacheDir, "*"),
filepath.Join(dirs.SnapDesktopFilesDir, "*"),
filepath.Join(dirs.SnapDBusSessionServicesDir, "*"),
filepath.Join(dirs.SnapDBusSystemServicesDir, "*"),
}

for _, gl := range globs {
Expand Down
13 changes: 11 additions & 2 deletions data/dbus/Makefile
Expand Up @@ -14,7 +14,8 @@
# along with this program. If not, see <http://www.gnu.org/licenses/>.

BINDIR := /usr/bin
DBUSSERVICESDIR := /usr/share/dbus-1/services
DBUSDIR = /usr/share/dbus-1
DBUSSERVICESDIR := ${DBUSDIR}/services

SERVICES_GENERATED := $(patsubst %.service.in,%.service,$(wildcard *.service.in))
SERVICES := ${SERVICES_GENERATED}
Expand All @@ -24,10 +25,18 @@ SERVICES := ${SERVICES_GENERATED}

all: ${SERVICES}

install: ${SERVICES}
install:: ${SERVICES}
# NOTE: old (e.g. 14.04) GNU coreutils doesn't -D with -t
install -d -m 0755 ${DESTDIR}/${DBUSSERVICESDIR}
install -m 0644 -t ${DESTDIR}/${DBUSSERVICESDIR} $^

install:: snapd.session-services.conf
install -d -m 0755 ${DESTDIR}/${DBUSDIR}/session.d
install -m 0644 -t ${DESTDIR}/${DBUSDIR}/session.d $^

install:: snapd.system-services.conf
install -d -m 0755 ${DESTDIR}/${DBUSDIR}/system.d
install -m 0644 -t ${DESTDIR}/${DBUSDIR}/system.d $^

clean:
rm -f ${SERVICES_GENERATED}
4 changes: 4 additions & 0 deletions data/dbus/snapd.session-services.conf
@@ -0,0 +1,4 @@
<!-- keep in sync with interfaces/dbus/backend.go:setupHostDBusConf() -->
<busconfig>
<servicedir>/var/lib/snapd/dbus/services/</servicedir>
</busconfig>
4 changes: 4 additions & 0 deletions data/dbus/snapd.system-services.conf
@@ -0,0 +1,4 @@
<!-- keep in sync with interfaces/dbus/backend.go:setupHostDBusConf() -->
<busconfig>
<servicedir>/var/lib/snapd/dbus/system-services/</servicedir>
</busconfig>
11 changes: 10 additions & 1 deletion dirs/dirs.go
Expand Up @@ -91,7 +91,11 @@ var (
SnapSystemdConfDir string
SnapDesktopFilesDir string
SnapDesktopIconsDir string
SnapBusPolicyDir string

SnapBusPolicyDir string
SnapBusSessionPolicyDir string
SnapDBusSessionServicesDir string
SnapDBusSystemServicesDir string

SnapModeenvFile string

Expand Down Expand Up @@ -288,6 +292,10 @@ func SetRootDir(rootdir string) {
// freedesktop.org specifications
SnapDesktopFilesDir = filepath.Join(rootdir, snappyDir, "desktop", "applications")
SnapDesktopIconsDir = filepath.Join(rootdir, snappyDir, "desktop", "icons")
// Use 'dbus/services' and `dbus/system-services' to mirror
// '/usr/share/dbus-1' hierarchy.
SnapDBusSessionServicesDir = filepath.Join(rootdir, snappyDir, "dbus", "services")
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

will there be dbus-2 ? should this be dbus-1? or no chance of that being an issue?

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this was some forward thinking by DBus authors. There might be dbus-2 eventually or never, depending on how things evolve.

SnapDBusSystemServicesDir = filepath.Join(rootdir, snappyDir, "dbus", "system-services")
SnapRunDir = filepath.Join(rootdir, "/run/snapd")
SnapRunNsDir = filepath.Join(SnapRunDir, "/ns")
SnapRunLockDir = filepath.Join(SnapRunDir, "/lock")
Expand Down Expand Up @@ -332,6 +340,7 @@ func SetRootDir(rootdir string) {
SnapUserServicesDir = filepath.Join(rootdir, "/etc/systemd/user")
SnapSystemdConfDir = SnapSystemdConfDirUnder(rootdir)
SnapBusPolicyDir = filepath.Join(rootdir, "/etc/dbus-1/system.d")
SnapBusSessionPolicyDir = filepath.Join(rootdir, "/etc/dbus-1/session.d")

CloudMetaDataFile = filepath.Join(rootdir, "/var/lib/cloud/seed/nocloud-net/meta-data")
CloudInstanceDataFile = filepath.Join(rootdir, "/run/cloud-init/instance-data.json")
Expand Down
5 changes: 4 additions & 1 deletion features/features.go
Expand Up @@ -49,6 +49,8 @@ const (
RobustMountNamespaceUpdates
// UserDaemons controls availability of user mode service support.
UserDaemons
// DbusActivation controls whether snaps daemons can be activated via D-Bus
DbusActivation
// lastFeature is the final known feature, it is only used for testing.
lastFeature
)
Expand All @@ -75,7 +77,8 @@ var featureNames = map[SnapdFeature]string{
ClassicPreservesXdgRuntimeDir: "classic-preserves-xdg-runtime-dir",
RobustMountNamespaceUpdates: "robust-mount-namespace-updates",

UserDaemons: "user-daemons",
UserDaemons: "user-daemons",
DbusActivation: "dbus-activation",
}

// featuresEnabledWhenUnset contains a set of features that are enabled when not explicitly configured.
Expand Down
3 changes: 3 additions & 0 deletions features/features_test.go
Expand Up @@ -48,6 +48,7 @@ func (*featureSuite) TestName(c *C) {
c.Check(features.ClassicPreservesXdgRuntimeDir.String(), Equals, "classic-preserves-xdg-runtime-dir")
c.Check(features.RobustMountNamespaceUpdates.String(), Equals, "robust-mount-namespace-updates")
c.Check(features.UserDaemons.String(), Equals, "user-daemons")
c.Check(features.DbusActivation.String(), Equals, "dbus-activation")
c.Check(func() { _ = features.SnapdFeature(1000).String() }, PanicMatches, "unknown feature flag code 1000")
}

Expand All @@ -70,6 +71,7 @@ func (*featureSuite) TestIsExported(c *C) {
c.Check(features.RefreshAppAwareness.IsExported(), Equals, true)
c.Check(features.ClassicPreservesXdgRuntimeDir.IsExported(), Equals, true)
c.Check(features.UserDaemons.IsExported(), Equals, false)
c.Check(features.DbusActivation.IsExported(), Equals, false)
}

func (*featureSuite) TestIsEnabled(c *C) {
Expand Down Expand Up @@ -101,6 +103,7 @@ func (*featureSuite) TestIsEnabledWhenUnset(c *C) {
c.Check(features.ClassicPreservesXdgRuntimeDir.IsEnabledWhenUnset(), Equals, false)
c.Check(features.RobustMountNamespaceUpdates.IsEnabledWhenUnset(), Equals, true)
c.Check(features.UserDaemons.IsEnabledWhenUnset(), Equals, false)
c.Check(features.DbusActivation.IsEnabledWhenUnset(), Equals, false)
}

func (*featureSuite) TestControlFile(c *C) {
Expand Down
74 changes: 62 additions & 12 deletions interfaces/builtin/dbus.go
Expand Up @@ -22,6 +22,7 @@ package builtin
import (
"bytes"
"fmt"
"os"
"regexp"
"strings"

Expand Down Expand Up @@ -231,9 +232,8 @@ func (iface *dbusInterface) StaticInfo() interfaces.StaticInfo {
var isInvalidSnappyBusName = regexp.MustCompile("-[0-9]+$").MatchString

// Obtain yaml-specified bus well-known name
func (iface *dbusInterface) getAttribs(attribs interfaces.Attrer) (string, string, error) {
func (iface *dbusInterface) getAttribs(attribs interfaces.Attrer) (bus, name string, err error) {
// bus attribute
var bus string
if err := attribs.Attr("bus", &bus); err != nil {
return "", "", fmt.Errorf("cannot find attribute 'bus'")
}
Expand All @@ -243,13 +243,11 @@ func (iface *dbusInterface) getAttribs(attribs interfaces.Attrer) (string, strin
}

// name attribute
var name string
if err := attribs.Attr("name", &name); err != nil {
return "", "", fmt.Errorf("cannot find attribute 'name'")
}

err := interfaces.ValidateDBusBusName(name)
if err != nil {
if err = interfaces.ValidateDBusBusName(name); err != nil {
return "", "", err
}

Expand Down Expand Up @@ -350,13 +348,24 @@ func (iface *dbusInterface) DBusPermanentSlot(spec *dbus.Specification, slot *sn
}

// only system services need bus policy
if bus != "system" {
return nil
if bus == "system" {
old := "###DBUS_NAME###"
new := name
spec.AddSnippet(strings.Replace(dbusPermanentSlotDBus, old, new, -1))
}

old := "###DBUS_NAME###"
new := name
spec.AddSnippet(strings.Replace(dbusPermanentSlotDBus, old, new, -1))
// handle activatable services
for _, app := range slot.Apps {
for _, slotName := range app.ActivatesOn {
if slotName == slot.Name {
err = spec.AddService(bus, name, app)
if err != nil {
return err
}
break
}
}
}
return nil
}

Expand Down Expand Up @@ -428,8 +437,49 @@ func (iface *dbusInterface) BeforePreparePlug(plug *snap.PlugInfo) error {
}

func (iface *dbusInterface) BeforePrepareSlot(slot *snap.SlotInfo) error {
_, _, err := iface.getAttribs(slot)
return err
bus, name, err := iface.getAttribs(slot)
if err != nil {
return err
}

var apps []*snap.AppInfo
for _, app := range slot.Apps {
for _, slotName := range app.ActivatesOn {
if slotName == slot.Name {
apps = append(apps, app)
break
}
}
}
if len(apps) > 1 {
return fmt.Errorf("cannot add activatable dbus service slot to multiple apps")
}
if len(apps) == 1 {
app := apps[0]
if !app.IsService() {
return fmt.Errorf("only daemons can be activatable D-Bus services")
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This made me realize that the proper spelling is indeed D-Bus and not DBus. We could later do a pass and make the tree consistent.

}
switch app.DaemonScope {
case snap.SystemDaemon:
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should all of this not be done by validation in the snap package instead?

if bus != "system" {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should this be a constant in dbusutil somewhere? Might avoid some chance of typos.

return fmt.Errorf("system daemons can only activate from the D-Bus system bus")
}
case snap.UserDaemon:
if bus != "session" {
return fmt.Errorf("user daemons can only activate from the D-Bus session bus")
}
}
if owner, err := dbus.BusNameOwner(bus, name); err != nil {
if !os.IsNotExist(err) {
return err
}
} else if owner == "" {
return fmt.Errorf("bus name %q is owned by a non-snap application", name)
} else if owner != slot.Snap.InstanceName() {
return fmt.Errorf("bus name %q is already owned by snap %q", name, owner)
}
}
return nil
}

func (iface *dbusInterface) AutoConnect(*snap.PlugInfo, *snap.SlotInfo) bool {
Expand Down