diff --git a/.gitignore b/.gitignore
index b9f8c3fdbb3..daf9e513019 100644
--- a/.gitignore
+++ b/.gitignore
@@ -33,6 +33,10 @@ cmd/*/*.[1-9]
# auto-generated systemd units
data/systemd/*.service
+
+# auto-generated dbus services
+data/dbus/*.service
+
data/info
# test-driver
diff --git a/cmd/snap/cmd_userd.go b/cmd/snap/cmd_userd.go
new file mode 100644
index 00000000000..17b75c5ab6f
--- /dev/null
+++ b/cmd/snap/cmd_userd.go
@@ -0,0 +1,74 @@
+// -*- Mode: Go; indent-tabs-mode: t -*-
+
+/*
+ * Copyright (C) 2017 Canonical Ltd
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 3 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ *
+ */
+
+package main
+
+import (
+ "fmt"
+ "os"
+ "os/signal"
+ "syscall"
+
+ "github.com/jessevdk/go-flags"
+
+ "github.com/snapcore/snapd/i18n"
+ "github.com/snapcore/snapd/userd"
+)
+
+type cmdUserd struct {
+ userd userd.Userd
+}
+
+var shortUserdHelp = i18n.G("Start the userd service")
+var longUserdHelp = i18n.G("The userd command starts the snap user session service.")
+
+func init() {
+ cmd := addCommand("userd",
+ shortAbortHelp,
+ longAbortHelp,
+ func() flags.Commander {
+ return &cmdUserd{}
+ },
+ nil,
+ []argDesc{},
+ )
+ cmd.hidden = true
+}
+
+func (x *cmdUserd) Execute(args []string) error {
+ if len(args) > 0 {
+ return ErrExtraArgs
+ }
+
+ if err := x.userd.Init(); err != nil {
+ return err
+ }
+ x.userd.Start()
+
+ ch := make(chan os.Signal)
+ signal.Notify(ch, syscall.SIGINT, syscall.SIGTERM, syscall.SIGUSR1)
+ select {
+ case sig := <-ch:
+ fmt.Fprintf(Stdout, "Exiting on %s.\n", sig)
+ case <-x.userd.Dying():
+ // something called Stop()
+ }
+
+ return x.userd.Stop()
+}
diff --git a/cmd/snap/cmd_userd_test.go b/cmd/snap/cmd_userd_test.go
new file mode 100644
index 00000000000..ec134a205e5
--- /dev/null
+++ b/cmd/snap/cmd_userd_test.go
@@ -0,0 +1,70 @@
+// -*- Mode: Go; indent-tabs-mode: t -*-
+
+/*
+ * Copyright (C) 2016 Canonical Ltd
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 3 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ *
+ */
+
+package main_test
+
+import (
+ "os"
+ "syscall"
+ "time"
+
+ . "gopkg.in/check.v1"
+
+ snap "github.com/snapcore/snapd/cmd/snap"
+ "github.com/snapcore/snapd/testutil"
+)
+
+type userdSuite struct {
+ BaseSnapSuite
+ testutil.DBusTest
+}
+
+var _ = Suite(&userdSuite{})
+
+func (s *userdSuite) TestUserdBadCommandline(c *C) {
+ _, err := snap.Parser().ParseArgs([]string{"userd", "extra-arg"})
+ c.Assert(err, ErrorMatches, "too many arguments for command")
+}
+
+func (s *userdSuite) TestUserd(c *C) {
+ go func() {
+ defer func() {
+ me, err := os.FindProcess(os.Getpid())
+ c.Assert(err, IsNil)
+ me.Signal(syscall.SIGUSR1)
+ }()
+
+ needle := "io.snapcraft.Launcher"
+ for i := 0; i < 10; i++ {
+ for _, objName := range s.SessionBus.Names() {
+ if objName == needle {
+ return
+ }
+ time.Sleep(1 * time.Second)
+ }
+
+ }
+ c.Fatalf("%s does not appeared on the bus", needle)
+ }()
+
+ rest, err := snap.Parser().ParseArgs([]string{"userd"})
+ c.Assert(err, IsNil)
+ c.Check(rest, DeepEquals, []string{})
+ c.Check(s.Stdout(), Equals, "Exiting on user defined signal 1.\n")
+}
diff --git a/data/Makefile b/data/Makefile
new file mode 100644
index 00000000000..caece8c0c3e
--- /dev/null
+++ b/data/Makefile
@@ -0,0 +1,3 @@
+all install clean:
+ $(MAKE) -C systemd $@
+ $(MAKE) -C dbus $@
diff --git a/data/dbus/Makefile b/data/dbus/Makefile
new file mode 100644
index 00000000000..66cc14e8648
--- /dev/null
+++ b/data/dbus/Makefile
@@ -0,0 +1,31 @@
+#
+# Copyright (C) 2017 Canonical Ltd
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License version 3 as
+# published by the Free Software Foundation.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see .
+
+BINDIR := /usr/bin
+DBUSSERVICESDIR := /usr/share/dbus-1/services
+
+SERVICES_GENERATED := $(patsubst %.service.in,%.service,$(wildcard *.service.in))
+SERVICES := ${SERVICES_GENERATED}
+
+%.service: %.service.in
+ cat $< | sed 's:@bindir@:${BINDIR}:g' | cat > $@
+
+all: ${SERVICES}
+
+install: ${SERVICES}
+ install -D -m 0644 -t ${DESTDIR}/${DBUSSERVICESDIR} $^
+
+clean:
+ rm -f ${SERVICES_GENERATED}
diff --git a/data/dbus/io.snapcraft.Launcher.service.in b/data/dbus/io.snapcraft.Launcher.service.in
new file mode 100644
index 00000000000..cd65b0aa0e6
--- /dev/null
+++ b/data/dbus/io.snapcraft.Launcher.service.in
@@ -0,0 +1,3 @@
+[D-BUS Service]
+Name=io.snapcraft.Launcher
+Exec=@bindir@/snap userd
diff --git a/interfaces/builtin/unity7.go b/interfaces/builtin/unity7.go
index 11408aec459..fc7cbe7de89 100644
--- a/interfaces/builtin/unity7.go
+++ b/interfaces/builtin/unity7.go
@@ -86,12 +86,23 @@ const unity7ConnectedPlugAppArmor = `
/usr/bin/xdg-open ixr,
/usr/share/applications/{,*} r,
/usr/bin/dbus-send ixr,
+
+# This allow access to the first version of the snapd-xdg-open
+# version which was shipped outside of snapd
dbus (send)
bus=session
path=/
interface=com.canonical.SafeLauncher
member=OpenURL
peer=(label=unconfined),
+# ... and this allows access to the new xdg-open service which
+# is now part of snapd itself.
+dbus (send)
+ bus=session
+ path=/io/snapcraft/Launcher
+ interface=io.snapcraft.Launcher
+ member=OpenURL
+ peer=(label=unconfined),
# input methods (ibus)
# subset of ibus abstraction
@@ -303,6 +314,11 @@ dbus (send)
bus=session
interface=com.canonical.SafeLauncher.OpenURL
peer=(label=unconfined),
+# new url helper (part of snap userd)
+dbus (send)
+ bus=session
+ interface=io.snapcraft.Launcher.OpenURL
+ peer=(label=unconfined),
# dbusmenu
dbus (send)
diff --git a/packaging/fedora/snapd.spec b/packaging/fedora/snapd.spec
index 7fbd5416001..d520f3fc913 100644
--- a/packaging/fedora/snapd.spec
+++ b/packaging/fedora/snapd.spec
@@ -380,7 +380,7 @@ autoreconf --force --install --verbose
popd
# Build systemd units
-pushd ./data/systemd
+pushd ./data/
make BINDIR="%{_bindir}" LIBEXECDIR="%{_libexecdir}" \
SYSTEMDSYSTEMUNITDIR="%{_unitdir}" \
SNAP_MOUNT_DIR="%{_sharedstatedir}/snapd/snap" \
@@ -441,7 +441,7 @@ rm -fv %{buildroot}%{_bindir}/ubuntu-core-launcher
popd
# Install all systemd units
-pushd ./data/systemd
+pushd ./data/
%make_install SYSTEMDSYSTEMUNITDIR="%{_unitdir}" BINDIR="%{_bindir}" LIBEXECDIR="%{_libexecdir}"
# Remove snappy core specific units
rm -fv %{buildroot}%{_unitdir}/snapd.system-shutdown.service
@@ -560,6 +560,7 @@ popd
%ghost %dir %{_sharedstatedir}/snapd/snap/bin
%dir %{_localstatedir}/snap
%ghost %{_sharedstatedir}/snapd/state.json
+%{_datadir}/dbus-1/services/io.snapcraft.Launcher.service
%files -n snap-confine
%doc cmd/snap-confine/PORTING
diff --git a/packaging/opensuse-42.2/snapd.spec b/packaging/opensuse-42.2/snapd.spec
index aef3bc923fc..a0d45a09832 100644
--- a/packaging/opensuse-42.2/snapd.spec
+++ b/packaging/opensuse-42.2/snapd.spec
@@ -206,7 +206,7 @@ install -d %buildroot/snap/bin
install -m 644 -D packaging/opensuse-42.2/permissions %buildroot/%{_sysconfdir}/permissions.d/snapd
install -m 644 -D packaging/opensuse-42.2/permissions.paranoid %buildroot/%{_sysconfdir}/permissions.d/snapd.paranoid
# Install the systemd units
-make -C data/systemd install DESTDIR=%{buildroot} SYSTEMDSYSTEMUNITDIR=%{_unitdir}
+make -C data install DESTDIR=%{buildroot} SYSTEMDSYSTEMUNITDIR=%{_unitdir}
for s in snapd.autoimport.service snapd.system-shutdown.service snap-repair.timer snap-repair.service snapd.core-fixup.service; do
rm -f %buildroot/%{_unitdir}/$s
done
@@ -291,6 +291,7 @@ esac
%{_libexecdir}/snapd/complete.sh
%{_libexecdir}/snapd/etelpmoc.sh
%{_mandir}/man1/snap.1.gz
+/usr/share/dbus-1/services/io.snapcraft.Launcher.service
%changelog
diff --git a/packaging/ubuntu-14.04/control b/packaging/ubuntu-14.04/control
index 59360aa68f4..a545367ca5c 100644
--- a/packaging/ubuntu-14.04/control
+++ b/packaging/ubuntu-14.04/control
@@ -7,6 +7,7 @@ Build-Depends: autoconf,
autotools-dev,
bash-completion,
debhelper (>= 9),
+ dbus,
dh-apparmor,
dh-autoreconf,
dh-golang (>=1.7),
@@ -118,3 +119,11 @@ Section: oldlibs
Pre-Depends: dpkg (>= 1.15.7.2)
Description: Transitional package for snap-confine
This is a transitional dummy package. It can safely be removed.
+
+Package: snapd-xdg-open
+Architecture: any
+Depends: snapd (= ${binary:Version}), ${misc:Depends}
+Section: oldlibs
+Pre-Depends: dpkg (>= 1.15.7.2)
+Description: Transitional package for snapd-xdg-open
+ This is a transitional dummy package. It can safely be removed.
diff --git a/packaging/ubuntu-14.04/rules b/packaging/ubuntu-14.04/rules
index 58f0f34045c..d435da890f7 100755
--- a/packaging/ubuntu-14.04/rules
+++ b/packaging/ubuntu-14.04/rules
@@ -166,6 +166,7 @@ override_dh_install:
# and now the normal install rules
install --mode=0644 debian/snapd.system-shutdown.service debian/snapd/$(SYSTEMD_UNITS_DESTDIR)
$(MAKE) -C cmd install DESTDIR=$(CURDIR)/debian/tmp
+
dh_install
override_dh_auto_install: snap.8
diff --git a/packaging/ubuntu-16.04/control b/packaging/ubuntu-16.04/control
index 95a2984cb69..493ecfa77a2 100644
--- a/packaging/ubuntu-16.04/control
+++ b/packaging/ubuntu-16.04/control
@@ -7,6 +7,7 @@ Build-Depends: autoconf,
autotools-dev,
bash-completion,
debhelper (>= 9),
+ dbus,
dh-apparmor,
dh-autoreconf,
dh-golang (>=1.7),
@@ -64,8 +65,8 @@ Depends: adduser,
systemd,
${misc:Depends},
${shlibs:Depends}
-Replaces: ubuntu-snappy (<< 1.9), ubuntu-snappy-cli (<< 1.9), snap-confine (<< 2.23), ubuntu-core-launcher (<< 2.22)
-Breaks: ubuntu-snappy (<< 1.9), ubuntu-snappy-cli (<< 1.9), snap-confine (<< 2.23), ubuntu-core-launcher (<< 2.22)
+Replaces: ubuntu-snappy (<< 1.9), ubuntu-snappy-cli (<< 1.9), snap-confine (<< 2.23), ubuntu-core-launcher (<< 2.22), snapd-xdg-open (<< 0.0.0)
+Breaks: ubuntu-snappy (<< 1.9), ubuntu-snappy-cli (<< 1.9), snap-confine (<< 2.23), ubuntu-core-launcher (<< 2.22), snapd-xdg-open (<< 0.0.0)
Conflicts: snap (<< 2013-11-29-1ubuntu1)
Built-Using: ${Built-Using} ${misc:Built-Using}
Description: Tool to interact with Ubuntu Core Snappy.
@@ -112,3 +113,11 @@ Section: oldlibs
Pre-Depends: dpkg (>= 1.15.7.2)
Description: Transitional package for snapd
This is a transitional dummy package. It can safely be removed.
+
+Package: snapd-xdg-open
+Architecture: any
+Depends: snapd (= ${binary:Version}), ${misc:Depends}
+Section: oldlibs
+Pre-Depends: dpkg (>= 1.15.7.2)
+Description: Transitional package for snapd-xdg-open
+ This is a transitional dummy package. It can safely be removed.
diff --git a/packaging/ubuntu-16.04/rules b/packaging/ubuntu-16.04/rules
index 56fc487b8b5..70387c6bb43 100755
--- a/packaging/ubuntu-16.04/rules
+++ b/packaging/ubuntu-16.04/rules
@@ -119,8 +119,8 @@ override_dh_auto_build:
cd cmd && ( ./configure --prefix=/usr --libexecdir=/usr/lib/snapd $(VENDOR_ARGS))
$(MAKE) -C cmd all
- # Generate the real systemd units out of the available templates
- $(MAKE) -C data/systemd all
+ # Generate the real systemd/dbus config files
+ $(MAKE) -C data all
override_dh_auto_test:
dh_auto_test -- $(GCCGOFLAGS)
@@ -210,10 +210,11 @@ override_dh_install:
cp -R share/locale debian/snapd/usr/share; \
fi
- # install snapd's systemd units, done here instead of
- # debian/snapd.install because the ubuntu/14.04 release
- # branch adds/changes bits here
- $(MAKE) -C data/systemd install DESTDIR=$(CURDIR)/debian/snapd/ SYSTEMDSYSTEMUNITDIR=$(SYSTEMD_UNITS_DESTDIR)
+ # install snapd's systemd units / upstart jobs, done
+ # here instead of debian/snapd.install because the
+ # ubuntu/14.04 release branch adds/changes bits here
+ $(MAKE) -C data install DESTDIR=$(CURDIR)/debian/snapd/ \
+ SYSTEMDSYSTEMUNITDIR=$(SYSTEMD_UNITS_DESTDIR)
$(MAKE) -C cmd install DESTDIR=$(CURDIR)/debian/tmp
diff --git a/spread.yaml b/spread.yaml
index 1af9ae9a87c..aa6b6fd2c43 100644
--- a/spread.yaml
+++ b/spread.yaml
@@ -441,6 +441,7 @@ suites:
fi
. $TESTSLIB/pkgdb.sh
distro_purge_package snapd
+ distro_purge_package snapd-xdg-open || true
tests/unit/:
summary: Suite to run unit tests (non-go and different go runtimes)
diff --git a/tests/main/interfaces-dbus/task.yaml b/tests/main/interfaces-dbus/task.yaml
index cd35e6c62a9..d8bff82b42c 100644
--- a/tests/main/interfaces-dbus/task.yaml
+++ b/tests/main/interfaces-dbus/task.yaml
@@ -17,6 +17,8 @@ prepare: |
. "$TESTSLIB/dirs.sh"
. "$TESTSLIB/pkgdb.sh"
+ distro_install_package dbus-x11
+
echo "Give a snap declaring a dbus slot in installed"
snap install --edge test-snapd-dbus-provider
@@ -54,6 +56,7 @@ restore: |
else
systemctl stop dbus-provider
fi
+ distro_purge_package dbus-x11
execute: |
CONNECTED_PATTERN="test-snapd-dbus-provider:dbus-test +test-snapd-dbus-consumer"
diff --git a/tests/main/snap-userd/task.yaml b/tests/main/snap-userd/task.yaml
new file mode 100644
index 00000000000..9b126a96a02
--- /dev/null
+++ b/tests/main/snap-userd/task.yaml
@@ -0,0 +1,117 @@
+summary: Ensure snap userd allows opening a URL via xdg-open
+
+systems:
+ # Not supposed to work on Ubuntu Core systems as we don't have
+ # a user session environment there
+ - -ubuntu-core-*
+
+environment:
+ DISPLAY: :0
+
+restore: |
+ . "$TESTSLIB/pkgdb.sh"
+ rm -f dbus.env
+ if [[ "$SPREAD_SYSTEM" == ubuntu-14.04-* ]]; then
+ stop test-snap-userd || true
+ rm -f /etc/init/test-snap-userd.conf
+ else
+ systemctl stop --signal=KILL test-snap-userd.scope || true
+ fi
+ umount -f /usr/bin/xdg-open || true
+ umount -f /snap/core/current/usr/bin/xdg-open || true
+ distro_purge_package dbus-x11 xdg-utils
+
+execute: |
+ . "$TESTSLIB/pkgdb.sh"
+ . "$TESTSLIB/dirs.sh"
+
+ # Install necessary pacakges to get dbus-launch helper
+ distro_install_package dbus-x11 xdg-utils
+
+ dbus-launch > dbus.env
+ export $(cat dbus.env | xargs)
+
+ # helper that returns true when io.snapcraft.Launcher.OpenURL
+ # responds
+ ping_launcher() {
+ dbus-send --session \
+ --dest=io.snapcraft.Launcher \
+ --type=method_call \
+ --print-reply \
+ / \
+ org.freedesktop.DBus.Peer.Ping
+ }
+
+ if [[ "$SPREAD_SYSTEM" == ubuntu-14.04-* ]]; then
+ cat << EOF > /etc/init/test-snap-userd.conf
+ env DISPLAY="$DISPLAY"
+ env DBUS_SESSION_BUS_ADDRESS="$DBUS_SESSION_BUS_ADDRESS"
+ env DBUS_SESSION_BUS_PID="$DBUS_SESSION_BUS_PID"
+ kill timeout 5
+ exec /usr/bin/snap userd
+ EOF
+ initctl reload-configuration
+ start test-snap-userd
+ while ! ping_launcher ; do
+ sleep .1
+ done
+ else
+ systemd-run \
+ --scope \
+ --unit=test-snap-userd \
+ --no-block \
+ --setenv=DISPLAY="$DISPLAY" \
+ --setenv=DBUS_SESSION_BUS_ADDRESS="$DBUS_SESSION_BUS_ADDRESS" \
+ --setenv=DBUS_SESSION_BUS_PID="$DBUS_SESSION_BUS_PID" \
+ /bin/sh -c '/usr/bin/snap userd &'
+ while ! ping_launcher ; do
+ sleep .1
+ done
+ fi
+
+ # Create a small helper which will tell us if snap passes
+ # the URL correctly to the right handler
+ cat << 'EOF' > /tmp/xdg-open
+ #!/bin/sh
+ echo "$@" > /tmp/xdg-open-output
+ EOF
+ chmod +x /tmp/xdg-open
+ mount --bind /tmp/xdg-open /usr/bin/xdg-open
+
+ # Until necessary changes landed in the core snap we need
+ # to create a custom one which points to the new service
+ # name io.snapcraft.Launcher where the current core
+ # snap still uses com.canonical.Launcher.
+ cat << 'EOF' > /tmp/xdg-open-core
+ #!/bin/sh
+ dbus-send --print-reply --session --dest=io.snapcraft.Launcher /io/snapcraft/Launcher io.snapcraft.Launcher.OpenURL string:"$1"
+ EOF
+ chmod +x /tmp/xdg-open-core
+ mount --bind /tmp/xdg-open-core /snap/core/current/usr/bin/xdg-open
+
+ ensure_xdg_open_output() {
+ rm -f /tmp/xdg-open-output
+ export DBUS_SESSION_BUS_ADDRESS=$DBUS_SESSION_BUS_ADDRESS
+ /snap/core/current/usr/bin/xdg-open $1
+ test -e /tmp/xdg-open-output
+ test "$(cat /tmp/xdg-open-output)" = $1
+ }
+
+ # Ensure http, https and mailto work
+ ensure_xdg_open_output "https://snapcraft.io"
+ ensure_xdg_open_output "http://snapcraft.io"
+ ensure_xdg_open_output "mailto:talk@snapcraft.io"
+
+ # Ensure other schemes are not passed through
+ rm /tmp/xdg-open-output
+ ! $SNAP_MOUNT_DIR/core/current/usr/bin/xdg-open ftp://snapcraft.io
+ test ! -e /tmp/xdg-open-output
+ ! $SNAP_MOUNT_DIR/core/current/usr/bin/xdg-open aabbcc
+ test ! -e /tmp/xdg-open-output
+
+ if [[ "$SPREAD_SYSTEM" == ubuntu-14.04-* ]]; then
+ stop test-snap-userd
+ else
+ systemctl list-units --type=scope # debug
+ systemctl stop --signal=KILL test-snap-userd.scope || true
+ fi
diff --git a/tests/upgrade/snapd-xdg-open/task.yaml b/tests/upgrade/snapd-xdg-open/task.yaml
new file mode 100644
index 00000000000..0f2d948de03
--- /dev/null
+++ b/tests/upgrade/snapd-xdg-open/task.yaml
@@ -0,0 +1,38 @@
+summary: Verify snapd-xdg-open package is properly replaced with the snapd one
+description: |
+ snapd-xdg-open was formerly provided by the snapd-xdg-open package
+ and is now part of the snapd package. This test case verifies that
+ the snapd-xdg-open package from the archive is properly replaced.
+# Test case only applies to Ubuntu as that is the only distribution where
+# we had a snapd-xdg-open package ever.
+systems: [-ubuntu-core-*, -debian-*, -ubuntu-14.04-*]
+restore: |
+ . "$TESTSLIB/pkgdb.sh"
+ if [ "$REMOTE_STORE" = staging ]; then
+ echo "skip upgrade tests while talking to the staging store"
+ exit 0
+ fi
+ distro_purge_package snapd-xdg-open || true
+execute: |
+ . "$TESTSLIB/pkgdb.sh"
+
+ # Original version of snapd-xdg-open in 16.04 which was not
+ # part of the snapd source package.
+ ver=0.0.0~16.04
+ if ! distro_install_package snapd-xdg-open=$ver; then
+ # version of snapd-xdg-open in 17.04,17.10
+ ver=0.0.0
+ if ! distro_install_package snapd-xdg-open=$ver; then
+ echo "SKIP: cannot find snapd-xdg-open, skipping test"
+ exit 0
+ fi
+ fi
+
+ prevsnapdxdgver=$(dpkg-query --showformat='${Version}' --show snapd-xdg-open)
+
+ # allow-downgrades prevents errors when new versions hit the archive, for instance,
+ # trying to install 2.11ubuntu1 over 2.11+0.16.04
+ distro_install_local_package --allow-downgrades $GOHOME/snapd*.deb
+
+ snapdxdgver=$(dpkg-query --showformat='${Version}' --show snapd-xdg-open)
+ [ "$snapdxdgver" != "$prevsnapdxdgver" ]
diff --git a/testutil/dbustest.go b/testutil/dbustest.go
new file mode 100644
index 00000000000..4f7b5f580d0
--- /dev/null
+++ b/testutil/dbustest.go
@@ -0,0 +1,69 @@
+// -*- Mode: Go; indent-tabs-mode: t -*-
+
+/*
+ * Copyright (C) 2015 Canonical Ltd
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 3 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ *
+ */
+
+package testutil
+
+import (
+ "fmt"
+ "os"
+ "os/exec"
+
+ "github.com/godbus/dbus"
+
+ . "gopkg.in/check.v1"
+)
+
+// DBusTest provides a separate dbus session bus for running tests
+type DBusTest struct {
+ tmpdir string
+ dbusDaemon *exec.Cmd
+ oldSessionBusEnv string
+
+ // the dbus.Conn to the session bus that tests can use
+ SessionBus *dbus.Conn
+}
+
+func (s *DBusTest) SetUpSuite(c *C) {
+ if _, err := exec.LookPath("dbus-daemon"); err != nil {
+ c.Skip(fmt.Sprintf("cannot run test without dbus-daemon: %s", err))
+ return
+ }
+ if _, err := exec.LookPath("dbus-launch"); err != nil {
+ c.Skip(fmt.Sprintf("cannot run test without dbus-launch: %s", err))
+ return
+ }
+
+ s.tmpdir = c.MkDir()
+ s.dbusDaemon = exec.Command("dbus-daemon", "--session", fmt.Sprintf("--address=unix:%s/user_bus_socket", s.tmpdir))
+ err := s.dbusDaemon.Start()
+ c.Assert(err, IsNil)
+ s.oldSessionBusEnv = os.Getenv("DBUS_SESSION_BUS_ADDRESS")
+
+ s.SessionBus, err = dbus.SessionBus()
+ c.Assert(err, IsNil)
+}
+
+func (s *DBusTest) TearDownSuite(c *C) {
+ os.Setenv("DBUS_SESSION_BUS_ADDRESS", s.oldSessionBusEnv)
+ if s.dbusDaemon != nil && s.dbusDaemon.Process != nil {
+ err := s.dbusDaemon.Process.Kill()
+ c.Assert(err, IsNil)
+ }
+
+}
diff --git a/userd/launcher.go b/userd/launcher.go
new file mode 100644
index 00000000000..fc427e87bf9
--- /dev/null
+++ b/userd/launcher.go
@@ -0,0 +1,88 @@
+// -*- Mode: Go; indent-tabs-mode: t -*-
+
+/*
+ * Copyright (C) 2017 Canonical Ltd
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 3 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ *
+ */
+
+package userd
+
+import (
+ "fmt"
+ "net/url"
+ "os/exec"
+
+ "github.com/godbus/dbus"
+ "github.com/snapcore/snapd/strutil"
+)
+
+const launcherIntrospectionXML = `
+
+
+
+
+
+
+
+
+
+
+
+`
+
+var (
+ allowedURLSchemes = []string{"http", "https", "mailto"}
+)
+
+// Launcher implements the 'io.snapcraft.Launcher' DBus interface.
+type Launcher struct{}
+
+// Name returns the name of the interface this object implements
+func (s *Launcher) Name() string {
+ return "io.snapcraft.Launcher"
+}
+
+// IntrospectionData gives the XML formatted introspection description
+// of the DBus service.
+func (s *Launcher) IntrospectionData() string {
+ return launcherIntrospectionXML
+}
+
+func makeAccessDeniedError(err error) *dbus.Error {
+ return &dbus.Error{
+ Name: "org.freedesktop.DBus.Error.AccessDenied",
+ Body: []interface{}{err.Error()},
+ }
+}
+
+// OpenURL implements the 'OpenURL' method of the 'com.canonical.Launcher'
+// DBus interface. Before the provided url is passed to xdg-open the scheme is
+// validated against a list of allowed schemes. All other schemes are denied.
+func (s *Launcher) OpenURL(addr string) *dbus.Error {
+ u, err := url.Parse(addr)
+ if err != nil {
+ return &dbus.ErrMsgInvalidArg
+ }
+
+ if !strutil.ListContains(allowedURLSchemes, u.Scheme) {
+ return makeAccessDeniedError(fmt.Errorf("Supplied URL scheme %q is not allowed", u.Scheme))
+ }
+
+ if err = exec.Command("xdg-open", addr).Run(); err != nil {
+ return dbus.MakeFailedError(fmt.Errorf("cannot open supplied URL"))
+ }
+
+ return nil
+}
diff --git a/userd/launcher_test.go b/userd/launcher_test.go
new file mode 100644
index 00000000000..acc17424312
--- /dev/null
+++ b/userd/launcher_test.go
@@ -0,0 +1,81 @@
+// -*- Mode: Go; indent-tabs-mode: t -*-
+
+/*
+ * Copyright (C) 2017 Canonical Ltd
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 3 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ *
+ */
+
+package userd_test
+
+import (
+ "github.com/godbus/dbus"
+
+ . "gopkg.in/check.v1"
+
+ "github.com/snapcore/snapd/testutil"
+ "github.com/snapcore/snapd/userd"
+)
+
+type launcherSuite struct {
+ launcher *userd.Launcher
+
+ mockXdgOpen *testutil.MockCmd
+}
+
+var _ = Suite(&launcherSuite{})
+
+func (s *launcherSuite) SetUpTest(c *C) {
+ s.launcher = &userd.Launcher{}
+ s.mockXdgOpen = testutil.MockCommand(c, "xdg-open", "")
+}
+
+func (s *launcherSuite) TearDownTest(c *C) {
+ s.mockXdgOpen.Restore()
+}
+
+func (s *launcherSuite) TestOpenURLWithNotAllowedScheme(c *C) {
+ for _, t := range []struct {
+ url string
+ errMatcher string
+ }{
+ {"tel://049112233445566", "Supplied URL scheme \"tel\" is not allowed"},
+ {"aabbccdd0011", "Supplied URL scheme \"\" is not allowed"},
+ {"invälid:%url", dbus.ErrMsgInvalidArg.Error()},
+ } {
+ err := s.launcher.OpenURL(t.url)
+ c.Assert(err, ErrorMatches, t.errMatcher)
+ c.Assert(s.mockXdgOpen.Calls(), IsNil)
+ }
+}
+
+func (s *launcherSuite) TestOpenURLWithAllowedSchemeHappy(c *C) {
+ for _, schema := range []string{"http", "https", "mailto"} {
+ err := s.launcher.OpenURL(schema + "://snapcraft.io")
+ c.Assert(err, IsNil)
+ c.Assert(s.mockXdgOpen.Calls(), DeepEquals, [][]string{
+ {"xdg-open", schema + "://snapcraft.io"},
+ })
+ s.mockXdgOpen.ForgetCalls()
+ }
+}
+
+func (s *launcherSuite) TestOpenURLWithFailingXdgOpen(c *C) {
+ cmd := testutil.MockCommand(c, "xdg-open", "false")
+ defer cmd.Restore()
+
+ err := s.launcher.OpenURL("https://snapcraft.io")
+ c.Assert(err, NotNil)
+ c.Assert(err, ErrorMatches, "cannot open supplied URL")
+}
diff --git a/userd/userd.go b/userd/userd.go
new file mode 100644
index 00000000000..d17018e6269
--- /dev/null
+++ b/userd/userd.go
@@ -0,0 +1,109 @@
+// -*- Mode: Go; indent-tabs-mode: t -*-
+
+/*
+ * Copyright (C) 2017 Canonical Ltd
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 3 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ *
+ */
+
+package userd
+
+import (
+ "bytes"
+ "fmt"
+
+ "github.com/godbus/dbus"
+ "github.com/godbus/dbus/introspect"
+ "gopkg.in/tomb.v2"
+)
+
+const (
+ busName = "io.snapcraft.Launcher"
+ basePath = "/io/snapcraft/Launcher"
+)
+
+type dbusInterface interface {
+ Name() string
+ IntrospectionData() string
+}
+
+type Userd struct {
+ tomb tomb.Tomb
+ conn *dbus.Conn
+ dbusIfaces []dbusInterface
+}
+
+func (ud *Userd) createAndExportInterfaces() {
+ ud.dbusIfaces = []dbusInterface{&Launcher{}}
+
+ var buffer bytes.Buffer
+ buffer.WriteString("")
+
+ for _, iface := range ud.dbusIfaces {
+ ud.conn.Export(iface, basePath, iface.Name())
+ buffer.WriteString(iface.IntrospectionData())
+ }
+
+ buffer.WriteString(introspect.IntrospectDataString)
+ buffer.WriteString("")
+
+ ud.conn.Export(introspect.Introspectable(buffer.String()), basePath, "org.freedesktop.DBus.Introspectable")
+}
+
+func (ud *Userd) Init() error {
+ var err error
+
+ ud.conn, err = dbus.SessionBus()
+ if err != nil {
+ return err
+ }
+
+ reply, err := ud.conn.RequestName(busName, dbus.NameFlagDoNotQueue)
+ if err != nil {
+ return err
+ }
+
+ if reply != dbus.RequestNameReplyPrimaryOwner {
+ err = fmt.Errorf("cannot obtain bus name '%s'", busName)
+ return err
+ }
+
+ ud.createAndExportInterfaces()
+ return nil
+}
+
+func (ud *Userd) Start() {
+ ud.tomb.Go(func() error {
+ // Listen to keep our thread up and running. All DBus bits
+ // are running in the background
+ select {
+ case <-ud.tomb.Dying():
+ ud.conn.Close()
+ }
+ err := ud.tomb.Err()
+ if err != nil && err != tomb.ErrStillAlive {
+ return err
+ }
+ return nil
+ })
+}
+
+func (ud *Userd) Stop() error {
+ ud.tomb.Kill(nil)
+ return ud.tomb.Wait()
+}
+
+func (ud *Userd) Dying() <-chan struct{} {
+ return ud.tomb.Dying()
+}
diff --git a/vendor/vendor.json b/vendor/vendor.json
index 66a46b98fa2..11ccfbc65e7 100644
--- a/vendor/vendor.json
+++ b/vendor/vendor.json
@@ -13,10 +13,16 @@
"revisionTime": "2016-11-14T12:22:54Z"
},
{
- "checksumSHA1": "LIpi0bDVAl3e0Xza8gohpKkH0+I=",
+ "checksumSHA1": "h77tT8kVh8x/J5ikkZReONPUjU0=",
"path": "github.com/godbus/dbus",
- "revision": "bd29ed602e2cf4207ebcabcd530259169e4289ba",
- "revisionTime": "2017-07-07T17:46:28Z"
+ "revision": "97646858c46433e4afb3432ad28c12e968efa298",
+ "revisionTime": "2017-08-22T15:24:03Z"
+ },
+ {
+ "checksumSHA1": "NrP46FPoALgKz3FY6puL3syMAAI=",
+ "path": "github.com/godbus/dbus/introspect",
+ "revision": "97646858c46433e4afb3432ad28c12e968efa298",
+ "revisionTime": "2017-08-22T15:24:03Z"
},
{
"checksumSHA1": "iIUYZyoanCQQTUaWsu8b+iOSPt4=",