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