diff --git a/overlord/snapstate/backend/link.go b/overlord/snapstate/backend/link.go
index fe2ff0b37ae..1d3acfb74fc 100644
--- a/overlord/snapstate/backend/link.go
+++ b/overlord/snapstate/backend/link.go
@@ -275,7 +275,7 @@ func removeGeneratedWrappers(s *snap.Info, firstInstallUndo bool, meter progress
func GenerateSnapdWrappers(s *snap.Info) error {
// snapd services are handled separately via an explicit helper
- return wrappers.AddSnapdSnapServices(s, progress.Null)
+ return wrappers.AddSnapdSnapServices(s, nil, progress.Null)
}
func removeGeneratedSnapdWrappers(s *snap.Info, firstInstall bool, meter progress.Meter) error {
diff --git a/systemd/emulation.go b/systemd/emulation.go
index 58f3b60a81b..2fce6bc6ee7 100644
--- a/systemd/emulation.go
+++ b/systemd/emulation.go
@@ -51,7 +51,7 @@ func (s *emulation) Backend() Backend {
}
func (s *emulation) DaemonReload() error {
- return ¬ImplementedError{"DaemonReload"}
+ return nil
}
func (s *emulation) DaemonReexec() error {
@@ -69,15 +69,15 @@ func (s *emulation) Disable(services []string) error {
}
func (s *emulation) Start(services []string) error {
- return ¬ImplementedError{"Start"}
+ return nil
}
func (s *emulation) StartNoBlock(services []string) error {
- return ¬ImplementedError{"StartNoBlock"}
+ return nil
}
func (s *emulation) Stop(services []string, timeout time.Duration) error {
- return ¬ImplementedError{"Stop"}
+ return nil
}
func (s *emulation) Kill(service, signal, who string) error {
@@ -85,7 +85,7 @@ func (s *emulation) Kill(service, signal, who string) error {
}
func (s *emulation) Restart(services []string, timeout time.Duration) error {
- return ¬ImplementedError{"Restart"}
+ return nil
}
func (s *emulation) ReloadOrRestart(service string) error {
diff --git a/wrappers/core18.go b/wrappers/core18.go
index 92b22c91f5f..f4f4f176ff4 100644
--- a/wrappers/core18.go
+++ b/wrappers/core18.go
@@ -64,7 +64,7 @@ func snapdUnitSkipStart(unitPath string) (skip bool, err error) {
return snapdSkipStart(content), nil
}
-func writeSnapdToolingMountUnit(sysd systemd.Systemd, prefix string) error {
+func writeSnapdToolingMountUnit(sysd systemd.Systemd, prefix string, opts *AddSnapdSnapServicesOptions) error {
// TODO: the following comment is wrong, we don't need RequiredBy=snapd here?
@@ -98,9 +98,11 @@ WantedBy=snapd.service
if err != nil {
return err
}
+
if err := sysd.DaemonReload(); err != nil {
return err
}
+
units := []string{SnapdToolingMountUnit}
if err := sysd.Enable(units); err != nil {
return err
@@ -136,9 +138,16 @@ func undoSnapdToolingMountUnit(sysd systemd.Systemd) error {
return os.Remove(mountUnitPath)
}
+type AddSnapdSnapServicesOptions struct {
+ // Preseeding is whether the system is currently being preseeded, in which
+ // case there is not a running systemd for EnsureSnapServicesOptions to
+ // issue commands like systemctl daemon-reload to.
+ Preseeding bool
+}
+
// AddSnapdSnapServices sets up the services based on a given snapd snap in the
// system.
-func AddSnapdSnapServices(s *snap.Info, inter interacter) error {
+func AddSnapdSnapServices(s *snap.Info, opts *AddSnapdSnapServicesOptions, inter interacter) error {
if snapType := s.Type(); snapType != snap.TypeSnapd {
return fmt.Errorf("internal error: adding explicit snapd services for snap %q type %q is unexpected", s.InstanceName(), snapType)
}
@@ -148,9 +157,18 @@ func AddSnapdSnapServices(s *snap.Info, inter interacter) error {
return nil
}
- sysd := systemd.New(systemd.SystemMode, inter)
+ if opts == nil {
+ opts = &AddSnapdSnapServicesOptions{}
+ }
- if err := writeSnapdToolingMountUnit(sysd, s.MountDir()); err != nil {
+ var sysd systemd.Systemd
+ if !opts.Preseeding {
+ sysd = systemd.New(systemd.SystemMode, inter)
+ } else {
+ sysd = systemd.NewEmulationMode("")
+ }
+
+ if err := writeSnapdToolingMountUnit(sysd, s.MountDir(), opts); err != nil {
return err
}
@@ -202,6 +220,7 @@ func AddSnapdSnapServices(s *snap.Info, inter interacter) error {
// nothing to do
return nil
}
+
// stop all removed units first
for _, unit := range removed {
serviceUnits := []string{unit}
@@ -231,49 +250,54 @@ func AddSnapdSnapServices(s *snap.Info, inter interacter) error {
// systemd version, where older versions (eg 229 in 16.04) would
// error out unless --force is passed, while new ones remove the
// symlink and create a new one.
- enabled, err := sysd.IsEnabled(unit)
- if err != nil {
- return err
- }
- if enabled {
- continue
+ if !opts.Preseeding {
+ enabled, err := sysd.IsEnabled(unit)
+ if err != nil {
+ return err
+ }
+ if enabled {
+ continue
+ }
}
if err := sysd.Enable([]string{unit}); err != nil {
return err
}
}
- for _, unit := range changed {
- // Some units (like the snapd.system-shutdown.service) cannot
- // be started. Others like "snapd.seeded.service" are started
- // as dependencies of snapd.service.
- if snapdSkipStart(snapdUnits[unit].(*osutil.MemoryFileState).Content) {
- continue
- }
- // Ensure to only restart if the unit was previously
- // active. This ensures we DTRT on firstboot and do
- // not stop e.g. snapd.socket because doing that
- // would mean that the snapd.seeded.service is also
- // stopped (independently of snapd.socket being
- // active) which confuses the boot order (the unit
- // exists before we are fully seeded).
- isActive, err := sysd.IsActive(unit)
- if err != nil {
- return err
- }
- serviceUnits := []string{unit}
- if isActive {
- // we can never restart the snapd.socket because
- // this will also bring down snapd itself
- if unit != "snapd.socket" {
- if err := sysd.Restart(serviceUnits, 5*time.Second); err != nil {
- return err
- }
+ if !opts.Preseeding {
+ for _, unit := range changed {
+ // Some units (like the snapd.system-shutdown.service) cannot
+ // be started. Others like "snapd.seeded.service" are started
+ // as dependencies of snapd.service.
+ if snapdSkipStart(snapdUnits[unit].(*osutil.MemoryFileState).Content) {
+ continue
}
- } else {
- if err := sysd.Start(serviceUnits); err != nil {
+ // Ensure to only restart if the unit was previously
+ // active. This ensures we DTRT on firstboot and do
+ // not stop e.g. snapd.socket because doing that
+ // would mean that the snapd.seeded.service is also
+ // stopped (independently of snapd.socket being
+ // active) which confuses the boot order (the unit
+ // exists before we are fully seeded).
+ isActive, err := sysd.IsActive(unit)
+ if err != nil {
return err
}
+
+ serviceUnits := []string{unit}
+ if isActive {
+ // we can never restart the snapd.socket because
+ // this will also bring down snapd itself
+ if unit != "snapd.socket" {
+ if err := sysd.Restart(serviceUnits, 5*time.Second); err != nil {
+ return err
+ }
+ }
+ } else {
+ if err := sysd.Start(serviceUnits); err != nil {
+ return err
+ }
+ }
}
}
@@ -407,6 +431,7 @@ func writeSnapdUserServicesOnCore(s *snap.Info, inter interacter) error {
return err
}
+ // TODO: use EmulationMode when preseeding (teach EmulationMode about user services)?
sysd := systemd.New(systemd.GlobalUserMode, inter)
serviceUnits, err := filepath.Glob(filepath.Join(s.MountDir(), "usr/lib/systemd/user/*.service"))
diff --git a/wrappers/core18_test.go b/wrappers/core18_test.go
index 650e8c0f45c..35bd43284c4 100644
--- a/wrappers/core18_test.go
+++ b/wrappers/core18_test.go
@@ -119,7 +119,7 @@ func (s *servicesTestSuite) TestAddSnapServicesForSnapdOnCore(c *C) {
info := makeMockSnapdSnap(c)
// add the snapd service
- err := wrappers.AddSnapdSnapServices(info, progress.Null)
+ err := wrappers.AddSnapdSnapServices(info, nil, progress.Null)
c.Assert(err, IsNil)
mountUnit := fmt.Sprintf(`[Unit]
@@ -219,13 +219,106 @@ WantedBy=snapd.service
})
}
+func (s *servicesTestSuite) TestAddSnapServicesForSnapdOnCorePreseeding(c *C) {
+ restore := release.MockOnClassic(false)
+ defer restore()
+
+ restore = release.MockReleaseInfo(&release.OS{ID: "ubuntu"})
+ defer restore()
+
+ // reset root dir
+ dirs.SetRootDir(s.tempdir)
+
+ info := makeMockSnapdSnap(c)
+ // add the snapd service
+ err := wrappers.AddSnapdSnapServices(info, &wrappers.AddSnapdSnapServicesOptions{Preseeding: true}, progress.Null)
+ c.Assert(err, IsNil)
+
+ mountUnit := fmt.Sprintf(`[Unit]
+Description=Make the snapd snap tooling available for the system
+Before=snapd.service
+
+[Mount]
+What=%s/snap/snapd/1/usr/lib/snapd
+Where=/usr/lib/snapd
+Type=none
+Options=bind
+
+[Install]
+WantedBy=snapd.service
+`, dirs.GlobalRootDir)
+ for _, entry := range [][]string{{
+ // check that snapd.service is created
+ filepath.Join(dirs.SnapServicesDir, "snapd.service"),
+ // and paths get re-written
+ fmt.Sprintf("[Unit]\n[Service]\nExecStart=%[1]s/snapd/1/usr/lib/snapd/snapd\n# X-Snapd-Snap: do-not-start\n[Unit]\nRequiresMountsFor=%[1]s/snapd/1\n", dirs.SnapMountDir),
+ }, {
+ // check that snapd.autoimport.service is created
+ filepath.Join(dirs.SnapServicesDir, "snapd.autoimport.service"),
+ // and paths get re-written
+ fmt.Sprintf("[Unit]\n[Service]\nExecStart=%[1]s/snapd/1/usr/bin/snap auto-import\n[Unit]\nRequiresMountsFor=%[1]s/snapd/1\n", dirs.SnapMountDir),
+ }, {
+ // check that snapd.system-shutdown.service is created
+ filepath.Join(dirs.SnapServicesDir, "snapd.system-shutdown.service"),
+ // and paths *do not* get re-written
+ "[Unit]\n[Service]\nExecStart=/bin/umount --everything\n# X-Snapd-Snap: do-not-start",
+ }, {
+ // check that usr-lib-snapd.mount is created
+ filepath.Join(dirs.SnapServicesDir, "usr-lib-snapd.mount"),
+ mountUnit,
+ }, {
+ // check that snapd.session-agent.service is created
+ filepath.Join(dirs.SnapUserServicesDir, "snapd.session-agent.service"),
+ // and paths get re-written
+ fmt.Sprintf("[Unit]\n[Service]\nExecStart=%[1]s/snapd/1/usr/bin/snap session-agent\n[Unit]\nRequiresMountsFor=%[1]s/snapd/1\n", dirs.SnapMountDir),
+ }, {
+ // check that snapd.session-agent.socket is created
+ filepath.Join(dirs.SnapUserServicesDir, "snapd.session-agent.socket"),
+ "[Unit]\n[Socket]\nListenStream=%t/snap-session.socket",
+ }, {
+ filepath.Join(dirs.SnapDBusSystemPolicyDir, "snapd.system-services.conf"),
+ "",
+ }, {
+ filepath.Join(dirs.SnapDBusSessionPolicyDir, "snapd.session-services.conf"),
+ "",
+ }, {
+ filepath.Join(dirs.SnapDBusSessionServicesDir, "io.snapcraft.Launcher.service"),
+ "[D-BUS Service]\nName=io.snapcraft.Launcher",
+ }, {
+ filepath.Join(dirs.SnapDBusSessionServicesDir, "io.snapcraft.Settings.service"),
+ "[D-BUS Service]\nName=io.snapcraft.Settings",
+ }, {
+ filepath.Join(dirs.SnapDBusSessionServicesDir, "io.snapcraft.SessionAgent.service"),
+ "[D-BUS Service]\nName=io.snapcraft.SessionAgent",
+ }} {
+ c.Check(entry[0], testutil.FileEquals, entry[1])
+ }
+
+ // Non-snapd D-Bus config is not copied
+ c.Check(filepath.Join(dirs.SnapDBusSystemPolicyDir, "io.netplan.Netplan.conf"), testutil.FileAbsent)
+
+ // check the systemctl calls
+ c.Check(s.sysdLog, DeepEquals, [][]string{
+ {"--root", s.tempdir, "enable", "usr-lib-snapd.mount"},
+ {"--root", s.tempdir, "enable", "snapd.autoimport.service"},
+ {"--root", s.tempdir, "enable", "snapd.service"},
+ {"--root", s.tempdir, "enable", "snapd.snap-repair.timer"},
+ {"--root", s.tempdir, "enable", "snapd.socket"},
+ {"--root", s.tempdir, "enable", "snapd.system-shutdown.service"},
+ {"--user", "--global", "disable", "snapd.session-agent.service"},
+ {"--user", "--global", "enable", "snapd.session-agent.service"},
+ {"--user", "--global", "disable", "snapd.session-agent.socket"},
+ {"--user", "--global", "enable", "snapd.session-agent.socket"},
+ })
+}
+
func (s *servicesTestSuite) TestAddSnapServicesForSnapdOnClassic(c *C) {
restore := release.MockOnClassic(true)
defer restore()
info := makeMockSnapdSnap(c)
// add the snapd service
- err := wrappers.AddSnapdSnapServices(info, progress.Null)
+ err := wrappers.AddSnapdSnapServices(info, nil, progress.Null)
c.Assert(err, IsNil)
// check that snapd services were *not* created
@@ -260,7 +353,7 @@ func (s *servicesTestSuite) TestAddSessionServicesWithReadOnlyFilesystem(c *C) {
defer restore()
// add the snapd service
- err := wrappers.AddSnapdSnapServices(info, progress.Null)
+ err := wrappers.AddSnapdSnapServices(info, nil, progress.Null)
// didn't fail despite of read-only SnapDBusSessionPolicyDir
c.Assert(err, IsNil)
@@ -286,7 +379,7 @@ func (s *servicesTestSuite) TestAddSnapdServicesWithNonSnapd(c *C) {
restore = release.MockReleaseInfo(&release.OS{ID: "ubuntu"})
defer restore()
- err := wrappers.AddSnapdSnapServices(info, progress.Null)
+ err := wrappers.AddSnapdSnapServices(info, nil, progress.Null)
c.Assert(err, ErrorMatches, `internal error: adding explicit snapd services for snap "foo" type "app" is unexpected`)
}