github.com/stolowski/snapd@v0.0.0-20210407085831-115137ce5a22/wrappers/core18.go (about)

     1  // -*- Mode: Go; indent-tabs-mode: t -*-
     2  
     3  /*
     4   * Copyright (C) 2018 Canonical Ltd
     5   *
     6   * This program is free software: you can redistribute it and/or modify
     7   * it under the terms of the GNU General Public License version 3 as
     8   * published by the Free Software Foundation.
     9   *
    10   * This program is distributed in the hope that it will be useful,
    11   * but WITHOUT ANY WARRANTY; without even the implied warranty of
    12   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    13   * GNU General Public License for more details.
    14   *
    15   * You should have received a copy of the GNU General Public License
    16   * along with this program.  If not, see <http://www.gnu.org/licenses/>.
    17   *
    18   */
    19  
    20  package wrappers
    21  
    22  import (
    23  	"bytes"
    24  	"fmt"
    25  	"io/ioutil"
    26  	"os"
    27  	"path/filepath"
    28  	"regexp"
    29  	"syscall"
    30  	"time"
    31  
    32  	"github.com/snapcore/snapd/dirs"
    33  	"github.com/snapcore/snapd/logger"
    34  	"github.com/snapcore/snapd/osutil"
    35  	"github.com/snapcore/snapd/release"
    36  	"github.com/snapcore/snapd/snap"
    37  	"github.com/snapcore/snapd/systemd"
    38  	"github.com/snapcore/snapd/timeout"
    39  )
    40  
    41  var snapdServiceStopTimeout = time.Duration(timeout.DefaultTimeout)
    42  
    43  // catches units that run /usr/bin/snap (with args), or things in /usr/lib/snapd/
    44  var execStartRe = regexp.MustCompile(`(?m)^ExecStart=(/usr/bin/snap\s+.*|/usr/lib/snapd/.*)$`)
    45  
    46  // snapdToolingMountUnit is the name of the mount unit that makes the
    47  const snapdToolingMountUnit = "usr-lib-snapd.mount"
    48  
    49  func snapdSkipStart(content []byte) bool {
    50  	return bytes.Contains(content, []byte("X-Snapd-Snap: do-not-start"))
    51  }
    52  
    53  // snapdUnitSkipStart returns true for units that should not be started
    54  // automatically
    55  func snapdUnitSkipStart(unitPath string) (skip bool, err error) {
    56  	content, err := ioutil.ReadFile(unitPath)
    57  	if err != nil {
    58  		if os.IsNotExist(err) {
    59  			// no point in starting units that do not exist
    60  			return true, nil
    61  		}
    62  		return false, err
    63  	}
    64  	return snapdSkipStart(content), nil
    65  }
    66  
    67  func writeSnapdToolingMountUnit(sysd systemd.Systemd, prefix string) error {
    68  	// Not using AddMountUnitFile() because we need
    69  	// "RequiredBy=snapd.service"
    70  	content := []byte(fmt.Sprintf(`[Unit]
    71  Description=Make the snapd snap tooling available for the system
    72  Before=snapd.service
    73  
    74  [Mount]
    75  What=%s/usr/lib/snapd
    76  Where=/usr/lib/snapd
    77  Type=none
    78  Options=bind
    79  
    80  [Install]
    81  WantedBy=snapd.service
    82  `, prefix))
    83  	fullPath := filepath.Join(dirs.SnapServicesDir, snapdToolingMountUnit)
    84  
    85  	err := osutil.EnsureFileState(fullPath,
    86  		&osutil.MemoryFileState{
    87  			Content: content,
    88  			Mode:    0644,
    89  		},
    90  	)
    91  	if err == osutil.ErrSameState {
    92  		return nil
    93  	}
    94  	if err != nil {
    95  		return err
    96  	}
    97  	if err := sysd.DaemonReload(); err != nil {
    98  		return err
    99  	}
   100  	if err := sysd.Enable(snapdToolingMountUnit); err != nil {
   101  		return err
   102  	}
   103  
   104  	if err := sysd.Restart(snapdToolingMountUnit, 5*time.Second); err != nil {
   105  		return err
   106  	}
   107  
   108  	return nil
   109  }
   110  
   111  func undoSnapdToolingMountUnit(sysd systemd.Systemd) error {
   112  	unit := "usr-lib-snapd.mount"
   113  	mountUnitPath := filepath.Join(dirs.SnapServicesDir, unit)
   114  
   115  	if !osutil.FileExists(mountUnitPath) {
   116  		return nil
   117  	}
   118  
   119  	if err := sysd.Disable(unit); err != nil {
   120  		return err
   121  	}
   122  	// XXX: it is ok to stop the mount unit, the failover handler
   123  	// executes snapd directly from the previous revision of snapd snap or
   124  	// the core snap, the handler is running directly from the mounted snapd snap
   125  	if err := sysd.Stop(unit, snapdServiceStopTimeout); err != nil {
   126  		return err
   127  	}
   128  	return os.Remove(mountUnitPath)
   129  }
   130  
   131  // AddSnapdSnapServices sets up the services based on a given snapd snap in the
   132  // system.
   133  func AddSnapdSnapServices(s *snap.Info, inter interacter) error {
   134  	if snapType := s.Type(); snapType != snap.TypeSnapd {
   135  		return fmt.Errorf("internal error: adding explicit snapd services for snap %q type %q is unexpected", s.InstanceName(), snapType)
   136  	}
   137  
   138  	// we never write snapd services on classic
   139  	if release.OnClassic {
   140  		return nil
   141  	}
   142  
   143  	sysd := systemd.New(systemd.SystemMode, inter)
   144  
   145  	if err := writeSnapdToolingMountUnit(sysd, s.MountDir()); err != nil {
   146  		return err
   147  	}
   148  
   149  	serviceUnits, err := filepath.Glob(filepath.Join(s.MountDir(), "lib/systemd/system/*.service"))
   150  	if err != nil {
   151  		return err
   152  	}
   153  	socketUnits, err := filepath.Glob(filepath.Join(s.MountDir(), "lib/systemd/system/*.socket"))
   154  	if err != nil {
   155  		return err
   156  	}
   157  	timerUnits, err := filepath.Glob(filepath.Join(s.MountDir(), "lib/systemd/system/*.timer"))
   158  	if err != nil {
   159  		return err
   160  	}
   161  	units := append(socketUnits, serviceUnits...)
   162  	units = append(units, timerUnits...)
   163  
   164  	snapdUnits := make(map[string]osutil.FileState, len(units)+1)
   165  	for _, unit := range units {
   166  		st, err := os.Stat(unit)
   167  		if err != nil {
   168  			return err
   169  		}
   170  		content, err := ioutil.ReadFile(unit)
   171  		if err != nil {
   172  			return err
   173  		}
   174  		if execStartRe.Match(content) {
   175  			content = execStartRe.ReplaceAll(content, []byte(fmt.Sprintf("ExecStart=%s$1", s.MountDir())))
   176  			// when the service executes a command from the snapd snap, make
   177  			// sure the exec path points to the mount dir, and that the
   178  			// mount happens before the unit is started
   179  			content = append(content, []byte(fmt.Sprintf("\n[Unit]\nRequiresMountsFor=%s\n", s.MountDir()))...)
   180  		}
   181  
   182  		snapdUnits[filepath.Base(unit)] = &osutil.MemoryFileState{
   183  			Content: content,
   184  			Mode:    st.Mode(),
   185  		}
   186  	}
   187  	globs := []string{"snapd.service", "snapd.socket", "snapd.*.service", "snapd.*.timer"}
   188  	changed, removed, err := osutil.EnsureDirStateGlobs(dirs.SnapServicesDir, globs, snapdUnits)
   189  	if err != nil {
   190  		// TODO: uhhhh, what do we do in this case?
   191  		return err
   192  	}
   193  	if (len(changed) + len(removed)) == 0 {
   194  		// nothing to do
   195  		return nil
   196  	}
   197  	// stop all removed units first
   198  	for _, unit := range removed {
   199  		if err := sysd.Stop(unit, 5*time.Second); err != nil {
   200  			logger.Noticef("failed to stop %q: %v", unit, err)
   201  		}
   202  		if err := sysd.Disable(unit); err != nil {
   203  			logger.Noticef("failed to disable %q: %v", unit, err)
   204  		}
   205  	}
   206  
   207  	// daemon-reload so that we get the new services
   208  	if len(changed) > 0 {
   209  		if err := sysd.DaemonReload(); err != nil {
   210  			return err
   211  		}
   212  	}
   213  
   214  	// enable/start all the new services
   215  	for _, unit := range changed {
   216  		// systemd looks at the logical units, even if 'enabled' service
   217  		// symlink points to /lib/systemd/system location, dropping an
   218  		// identically named service in /etc overrides the other unit,
   219  		// therefore it is sufficient to enable the new units only
   220  		//
   221  		// Calling sysd.Enable() unconditionally may fail depending on
   222  		// systemd version, where older versions (eg 229 in 16.04) would
   223  		// error out unless --force is passed, while new ones remove the
   224  		// symlink and create a new one.
   225  		enabled, err := sysd.IsEnabled(unit)
   226  		if err != nil {
   227  			return err
   228  		}
   229  		if enabled {
   230  			continue
   231  		}
   232  		if err := sysd.Enable(unit); err != nil {
   233  			return err
   234  		}
   235  	}
   236  
   237  	for _, unit := range changed {
   238  		// Some units (like the snapd.system-shutdown.service) cannot
   239  		// be started. Others like "snapd.seeded.service" are started
   240  		// as dependencies of snapd.service.
   241  		if snapdSkipStart(snapdUnits[unit].(*osutil.MemoryFileState).Content) {
   242  			continue
   243  		}
   244  		// Ensure to only restart if the unit was previously
   245  		// active. This ensures we DTRT on firstboot and do
   246  		// not stop e.g. snapd.socket because doing that
   247  		// would mean that the snapd.seeded.service is also
   248  		// stopped (independently of snapd.socket being
   249  		// active) which confuses the boot order (the unit
   250  		// exists before we are fully seeded).
   251  		isActive, err := sysd.IsActive(unit)
   252  		if err != nil {
   253  			return err
   254  		}
   255  		if isActive {
   256  			// we can never restart the snapd.socket because
   257  			// this will also bring down snapd itself
   258  			if unit != "snapd.socket" {
   259  				if err := sysd.Restart(unit, 5*time.Second); err != nil {
   260  					return err
   261  				}
   262  			}
   263  		} else {
   264  			if err := sysd.Start(unit); err != nil {
   265  				return err
   266  			}
   267  		}
   268  	}
   269  	// and finally start snapd.service (it will stop by itself and gets
   270  	// started by systemd then)
   271  	if err := sysd.Start("snapd.service"); err != nil {
   272  		return err
   273  	}
   274  	if err := sysd.StartNoBlock("snapd.seeded.service"); err != nil {
   275  		return err
   276  	}
   277  	// we cannot start snapd.autoimport in blocking mode because
   278  	// it has a "After=snapd.seeded.service" which means that on
   279  	// seeding a "systemctl start" that blocks would hang forever
   280  	// and we deadlock.
   281  	if err := sysd.StartNoBlock("snapd.autoimport.service"); err != nil {
   282  		return err
   283  	}
   284  
   285  	// Handle the user services
   286  	if err := writeSnapdUserServicesOnCore(s, inter); err != nil {
   287  		return err
   288  	}
   289  
   290  	// Handle D-Bus configuration
   291  	if err := writeSnapdDbusConfigOnCore(s); err != nil {
   292  		return err
   293  	}
   294  
   295  	if err := writeSnapdDbusActivationOnCore(s); err != nil {
   296  		return err
   297  	}
   298  
   299  	return nil
   300  }
   301  
   302  // undoSnapdUserServicesOnCore attempts to remove services that were deployed in
   303  // the filesystem as part of snapd snap installation. This should only be
   304  // executed as part of a controlled undo path.
   305  func undoSnapdServicesOnCore(s *snap.Info, sysd systemd.Systemd) error {
   306  	// list service, socket and timer units present in the snapd snap
   307  	serviceUnits, err := filepath.Glob(filepath.Join(s.MountDir(), "lib/systemd/system/*.service"))
   308  	if err != nil {
   309  		return err
   310  	}
   311  	socketUnits, err := filepath.Glob(filepath.Join(s.MountDir(), "lib/systemd/system/*.socket"))
   312  	if err != nil {
   313  		return err
   314  	}
   315  	timerUnits, err := filepath.Glob(filepath.Join(s.MountDir(), "lib/systemd/system/*.timer"))
   316  	if err != nil {
   317  		return err
   318  	}
   319  	units := append(socketUnits, serviceUnits...)
   320  	units = append(units, timerUnits...)
   321  
   322  	for _, snapdUnit := range units {
   323  		unit := filepath.Base(snapdUnit)
   324  		coreUnit := filepath.Join(dirs.GlobalRootDir, "lib/systemd/system", unit)
   325  		writtenUnitPath := filepath.Join(dirs.SnapServicesDir, unit)
   326  		if !osutil.FileExists(writtenUnitPath) {
   327  			continue
   328  		}
   329  		existsInCore := osutil.FileExists(coreUnit)
   330  
   331  		if !existsInCore {
   332  			// new unit that did not exist on core, disable and stop
   333  			if err := sysd.Disable(unit); err != nil {
   334  				logger.Noticef("failed to disable %q: %v", unit, err)
   335  			}
   336  			if err := sysd.Stop(unit, snapdServiceStopTimeout); err != nil {
   337  				return err
   338  			}
   339  		}
   340  		if err := os.Remove(writtenUnitPath); err != nil {
   341  			return err
   342  		}
   343  		if !existsInCore {
   344  			// nothing more to do here
   345  			continue
   346  		}
   347  
   348  		isEnabled, err := sysd.IsEnabled(unit)
   349  		if err != nil {
   350  			return err
   351  		}
   352  		if !isEnabled {
   353  			if err := sysd.Enable(unit); err != nil {
   354  				return err
   355  			}
   356  		}
   357  
   358  		if unit == "snapd.socket" {
   359  			// do not start the socket, snap failover handler will
   360  			// restart it
   361  			continue
   362  		}
   363  		skipStart, err := snapdUnitSkipStart(coreUnit)
   364  		if err != nil {
   365  			return err
   366  		}
   367  		if !skipStart {
   368  			// TODO: consider using sys.Restart() instead of is-active check
   369  			isActive, err := sysd.IsActive(unit)
   370  			if err != nil {
   371  				return err
   372  			}
   373  			if isActive {
   374  				if err := sysd.Restart(unit, snapdServiceStopTimeout); err != nil {
   375  					return err
   376  				}
   377  			} else {
   378  				if err := sysd.Start(unit); err != nil {
   379  					return err
   380  				}
   381  			}
   382  		}
   383  	}
   384  	return nil
   385  }
   386  
   387  func writeSnapdUserServicesOnCore(s *snap.Info, inter interacter) error {
   388  	// Ensure /etc/systemd/user exists
   389  	if err := os.MkdirAll(dirs.SnapUserServicesDir, 0755); err != nil {
   390  		return err
   391  	}
   392  
   393  	sysd := systemd.New(systemd.GlobalUserMode, inter)
   394  
   395  	serviceUnits, err := filepath.Glob(filepath.Join(s.MountDir(), "usr/lib/systemd/user/*.service"))
   396  	if err != nil {
   397  		return err
   398  	}
   399  	socketUnits, err := filepath.Glob(filepath.Join(s.MountDir(), "usr/lib/systemd/user/*.socket"))
   400  	if err != nil {
   401  		return err
   402  	}
   403  	units := append(serviceUnits, socketUnits...)
   404  
   405  	snapdUnits := make(map[string]osutil.FileState, len(units)+1)
   406  	for _, unit := range units {
   407  		st, err := os.Stat(unit)
   408  		if err != nil {
   409  			return err
   410  		}
   411  		content, err := ioutil.ReadFile(unit)
   412  		if err != nil {
   413  			return err
   414  		}
   415  		if execStartRe.Match(content) {
   416  			content = execStartRe.ReplaceAll(content, []byte(fmt.Sprintf("ExecStart=%s$1", s.MountDir())))
   417  			// when the service executes a command from the snapd snap, make
   418  			// sure the exec path points to the mount dir, and that the
   419  			// mount happens before the unit is started
   420  			content = append(content, []byte(fmt.Sprintf("\n[Unit]\nRequiresMountsFor=%s\n", s.MountDir()))...)
   421  		}
   422  
   423  		snapdUnits[filepath.Base(unit)] = &osutil.MemoryFileState{
   424  			Content: content,
   425  			Mode:    st.Mode(),
   426  		}
   427  	}
   428  	changed, removed, err := osutil.EnsureDirStateGlobs(dirs.SnapUserServicesDir, []string{"snapd.*.service", "snapd.*.socket"}, snapdUnits)
   429  	if err != nil {
   430  		// TODO: uhhhh, what do we do in this case?
   431  		return err
   432  	}
   433  	if (len(changed) + len(removed)) == 0 {
   434  		// nothing to do
   435  		return nil
   436  	}
   437  	// disable all removed units first
   438  	for _, unit := range removed {
   439  		if err := sysd.Disable(unit); err != nil {
   440  			logger.Noticef("failed to disable %q: %v", unit, err)
   441  		}
   442  	}
   443  
   444  	// enable/start all the new services
   445  	for _, unit := range changed {
   446  		if err := sysd.Disable(unit); err != nil {
   447  			logger.Noticef("failed to disable %q: %v", unit, err)
   448  		}
   449  		if err := sysd.Enable(unit); err != nil {
   450  			return err
   451  		}
   452  	}
   453  
   454  	return nil
   455  }
   456  
   457  // undoSnapdUserServicesOnCore attempts to remove user services that were
   458  // deployed in the filesystem as part of snapd snap installation. This should
   459  // only be executed as part of a controlled undo path.
   460  func undoSnapdUserServicesOnCore(s *snap.Info, inter interacter) error {
   461  	sysd := systemd.NewUnderRoot(dirs.GlobalRootDir, systemd.GlobalUserMode, inter)
   462  
   463  	// list user service and socket units present in the snapd snap
   464  	serviceUnits, err := filepath.Glob(filepath.Join(s.MountDir(), "usr/lib/systemd/user/*.service"))
   465  	if err != nil {
   466  		return err
   467  	}
   468  	socketUnits, err := filepath.Glob(filepath.Join(s.MountDir(), "usr/lib/systemd/user/*.socket"))
   469  	if err != nil {
   470  		return err
   471  	}
   472  	units := append(serviceUnits, socketUnits...)
   473  
   474  	for _, srcUnit := range units {
   475  		unit := filepath.Base(srcUnit)
   476  		writtenUnitPath := filepath.Join(dirs.SnapUserServicesDir, unit)
   477  		if !osutil.FileExists(writtenUnitPath) {
   478  			continue
   479  		}
   480  		coreUnit := filepath.Join(dirs.GlobalRootDir, "usr/lib/systemd/user", unit)
   481  		existsInCore := osutil.FileExists(coreUnit)
   482  
   483  		if err := sysd.Disable(unit); err != nil {
   484  			logger.Noticef("failed to disable %q: %v", unit, err)
   485  		}
   486  		if err := os.Remove(writtenUnitPath); err != nil {
   487  			return err
   488  		}
   489  		if !existsInCore {
   490  			// new unit that did not exist on core
   491  			continue
   492  		}
   493  		if err := sysd.Enable(unit); err != nil {
   494  			return err
   495  		}
   496  	}
   497  	return nil
   498  }
   499  
   500  func DeriveSnapdDBusConfig(s *snap.Info) (sessionContent, systemContent map[string]osutil.FileState, err error) {
   501  	sessionConfigs, err := filepath.Glob(filepath.Join(s.MountDir(), "usr/share/dbus-1/session.d/snapd.*.conf"))
   502  	if err != nil {
   503  		return nil, nil, err
   504  	}
   505  	sessionContent = make(map[string]osutil.FileState, len(sessionConfigs)+1)
   506  	for _, config := range sessionConfigs {
   507  		sessionContent[filepath.Base(config)] = &osutil.FileReference{
   508  			Path: config,
   509  		}
   510  	}
   511  
   512  	systemConfigs, err := filepath.Glob(filepath.Join(s.MountDir(), "usr/share/dbus-1/system.d/snapd.*.conf"))
   513  	if err != nil {
   514  		return nil, nil, err
   515  	}
   516  	systemContent = make(map[string]osutil.FileState, len(systemConfigs)+1)
   517  	for _, config := range systemConfigs {
   518  		systemContent[filepath.Base(config)] = &osutil.FileReference{
   519  			Path: config,
   520  		}
   521  	}
   522  
   523  	return sessionContent, systemContent, nil
   524  }
   525  
   526  func isReadOnlyFsError(err error) bool {
   527  	if err == nil {
   528  		return false
   529  	}
   530  	if e, ok := err.(*os.PathError); ok {
   531  		err = e.Err
   532  	}
   533  	if e, ok := err.(syscall.Errno); ok {
   534  		return e == syscall.EROFS
   535  	}
   536  	return false
   537  }
   538  
   539  var ensureDirState = osutil.EnsureDirState
   540  
   541  func writeSnapdDbusConfigOnCore(s *snap.Info) error {
   542  	sessionContent, systemContent, err := DeriveSnapdDBusConfig(s)
   543  	if err != nil {
   544  		return err
   545  	}
   546  
   547  	_, _, err = ensureDirState(dirs.SnapDBusSessionPolicyDir, "snapd.*.conf", sessionContent)
   548  	if err != nil {
   549  		if isReadOnlyFsError(err) {
   550  			// If /etc/dbus-1/session.d is read-only (which may be the case on very old core18), then
   551  			// err is os.PathError with syscall.Errno underneath. Hitting this prevents snapd refresh,
   552  			// so log the error but carry on. This fixes LP: 1899664.
   553  			// XXX: ideally we should regenerate session files elsewhere if we fail here (otherwise
   554  			// this will only happen on future snapd refresh), but realistically this
   555  			// is not relevant on core18 devices.
   556  			logger.Noticef("%s appears to be read-only, could not write snapd dbus config files", dirs.SnapDBusSessionPolicyDir)
   557  		} else {
   558  			return err
   559  		}
   560  	}
   561  
   562  	_, _, err = osutil.EnsureDirState(dirs.SnapDBusSystemPolicyDir, "snapd.*.conf", systemContent)
   563  	if err != nil {
   564  		return err
   565  	}
   566  
   567  	return nil
   568  }
   569  
   570  func undoSnapdDbusConfigOnCore(s *snap.Info) error {
   571  	_, _, err := osutil.EnsureDirState(dirs.SnapDBusSystemPolicyDir, "snapd.*.conf", nil)
   572  	if err != nil {
   573  		return err
   574  	}
   575  	_, _, err = osutil.EnsureDirState(dirs.SnapDBusSessionPolicyDir, "snapd.*.conf", nil)
   576  	return err
   577  }
   578  
   579  var dbusSessionServices = []string{
   580  	"io.snapcraft.Launcher.service",
   581  	"io.snapcraft.Settings.service",
   582  	"io.snapcraft.SessionAgent.service",
   583  }
   584  
   585  func writeSnapdDbusActivationOnCore(s *snap.Info) error {
   586  	if err := os.MkdirAll(dirs.SnapDBusSessionServicesDir, 0755); err != nil {
   587  		return err
   588  	}
   589  
   590  	content := make(map[string]osutil.FileState, len(dbusSessionServices)+1)
   591  	for _, service := range dbusSessionServices {
   592  		content[service] = &osutil.FileReference{
   593  			Path: filepath.Join(s.MountDir(), "usr/share/dbus-1/services", service),
   594  		}
   595  	}
   596  
   597  	_, _, err := osutil.EnsureDirStateGlobs(dirs.SnapDBusSessionServicesDir, dbusSessionServices, content)
   598  	return err
   599  }
   600  
   601  func undoSnapdDbusActivationOnCore(s *snap.Info) error {
   602  	_, _, err := osutil.EnsureDirStateGlobs(dirs.SnapDBusSessionServicesDir, dbusSessionServices, nil)
   603  	return err
   604  }
   605  
   606  // RemoveSnapdSnapServicesOnCore removes the snapd services generated by a prior
   607  // call to AddSnapdSnapServices. The core snap is used as the reference for
   608  // restoring the system state, making this undo helper suitable for use when
   609  // reverting the first installation of the snapd snap on a core device.
   610  func RemoveSnapdSnapServicesOnCore(s *snap.Info, inter interacter) error {
   611  	if snapType := s.Type(); snapType != snap.TypeSnapd {
   612  		return fmt.Errorf("internal error: removing explicit snapd services for snap %q type %q is unexpected", s.InstanceName(), snapType)
   613  	}
   614  
   615  	// snapd services are never written on classic, nothing to remove
   616  	if release.OnClassic {
   617  		return nil
   618  	}
   619  
   620  	sysd := systemd.NewUnderRoot(dirs.GlobalRootDir, systemd.SystemMode, inter)
   621  
   622  	if err := undoSnapdDbusActivationOnCore(s); err != nil {
   623  		return err
   624  	}
   625  	if err := undoSnapdDbusConfigOnCore(s); err != nil {
   626  		return err
   627  	}
   628  	if err := undoSnapdServicesOnCore(s, sysd); err != nil {
   629  		return err
   630  	}
   631  	if err := undoSnapdUserServicesOnCore(s, inter); err != nil {
   632  		return err
   633  	}
   634  	if err := undoSnapdToolingMountUnit(sysd); err != nil {
   635  		return err
   636  	}
   637  	// XXX: reload after all operations?
   638  	return nil
   639  }