github.com/chipaca/snappy@v0.0.0-20210104084008-1f06296fe8ad/overlord/snapstate/snapmgr.go (about)

     1  // -*- Mode: Go; indent-tabs-mode: t -*-
     2  
     3  /*
     4   * Copyright (C) 2016-2017 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 snapstate
    21  
    22  import (
    23  	"context"
    24  	"errors"
    25  	"fmt"
    26  	"io"
    27  	"os"
    28  	"strings"
    29  	"time"
    30  
    31  	"gopkg.in/tomb.v2"
    32  
    33  	"github.com/snapcore/snapd/dirs"
    34  	"github.com/snapcore/snapd/errtracker"
    35  	"github.com/snapcore/snapd/i18n"
    36  	"github.com/snapcore/snapd/logger"
    37  	"github.com/snapcore/snapd/osutil"
    38  	"github.com/snapcore/snapd/overlord/snapstate/backend"
    39  	"github.com/snapcore/snapd/overlord/state"
    40  	"github.com/snapcore/snapd/randutil"
    41  	"github.com/snapcore/snapd/release"
    42  	"github.com/snapcore/snapd/sandbox"
    43  	"github.com/snapcore/snapd/snap"
    44  	"github.com/snapcore/snapd/snap/channel"
    45  	"github.com/snapcore/snapd/snapdenv"
    46  	"github.com/snapcore/snapd/store"
    47  )
    48  
    49  var (
    50  	snapdTransitionDelayWithRandomess = 3*time.Hour + randutil.RandomDuration(4*time.Hour)
    51  )
    52  
    53  // overridden in the tests
    54  var errtrackerReport = errtracker.Report
    55  
    56  // SnapManager is responsible for the installation and removal of snaps.
    57  type SnapManager struct {
    58  	state   *state.State
    59  	backend managerBackend
    60  
    61  	autoRefresh    *autoRefresh
    62  	refreshHints   *refreshHints
    63  	catalogRefresh *catalogRefresh
    64  
    65  	lastUbuntuCoreTransitionAttempt time.Time
    66  
    67  	preseed bool
    68  }
    69  
    70  // SnapSetup holds the necessary snap details to perform most snap manager tasks.
    71  type SnapSetup struct {
    72  	// FIXME: rename to RequestedChannel to convey the meaning better
    73  	Channel string    `json:"channel,omitempty"`
    74  	UserID  int       `json:"user-id,omitempty"`
    75  	Base    string    `json:"base,omitempty"`
    76  	Type    snap.Type `json:"type,omitempty"`
    77  	// PlugsOnly indicates whether the relevant revisions for the
    78  	// operation have only plugs (#plugs >= 0), and absolutely no
    79  	// slots (#slots == 0).
    80  	PlugsOnly bool `json:"plugs-only,omitempty"`
    81  
    82  	CohortKey string `json:"cohort-key,omitempty"`
    83  
    84  	// FIXME: implement rename of this as suggested in
    85  	//  https://github.com/snapcore/snapd/pull/4103#discussion_r169569717
    86  	//
    87  	// Prereq is a list of snap-names that need to get installed
    88  	// together with this snap. Typically used when installing
    89  	// content-snaps with default-providers.
    90  	Prereq []string `json:"prereq,omitempty"`
    91  
    92  	Flags
    93  
    94  	SnapPath string `json:"snap-path,omitempty"`
    95  
    96  	DownloadInfo *snap.DownloadInfo `json:"download-info,omitempty"`
    97  	SideInfo     *snap.SideInfo     `json:"side-info,omitempty"`
    98  	auxStoreInfo
    99  
   100  	// InstanceKey is set by the user during installation and differs for
   101  	// each instance of given snap
   102  	InstanceKey string `json:"instance-key,omitempty"`
   103  }
   104  
   105  func (snapsup *SnapSetup) InstanceName() string {
   106  	return snap.InstanceName(snapsup.SnapName(), snapsup.InstanceKey)
   107  }
   108  
   109  func (snapsup *SnapSetup) SnapName() string {
   110  	if snapsup.SideInfo.RealName == "" {
   111  		panic("SnapSetup.SideInfo.RealName not set")
   112  	}
   113  	return snapsup.SideInfo.RealName
   114  }
   115  
   116  func (snapsup *SnapSetup) Revision() snap.Revision {
   117  	return snapsup.SideInfo.Revision
   118  }
   119  
   120  func (snapsup *SnapSetup) placeInfo() snap.PlaceInfo {
   121  	return snap.MinimalPlaceInfo(snapsup.InstanceName(), snapsup.Revision())
   122  }
   123  
   124  func (snapsup *SnapSetup) MountDir() string {
   125  	return snap.MountDir(snapsup.InstanceName(), snapsup.Revision())
   126  }
   127  
   128  func (snapsup *SnapSetup) MountFile() string {
   129  	return snap.MountFile(snapsup.InstanceName(), snapsup.Revision())
   130  }
   131  
   132  // SnapState holds the state for a snap installed in the system.
   133  type SnapState struct {
   134  	SnapType string           `json:"type"` // Use Type and SetType
   135  	Sequence []*snap.SideInfo `json:"sequence"`
   136  	Active   bool             `json:"active,omitempty"`
   137  
   138  	// LastActiveDisabledServices is a list of services that were disabled in
   139  	// this snap when it was last active - i.e. when it was disabled, before
   140  	// it was reverted, or before a refresh happens.
   141  	// It is set during unlink-snap and unlink-current-snap and reset during
   142  	// link-snap since it is only meant to be saved when snapd needs to remove
   143  	// systemd units.
   144  	// Note that to handle potential service renames, only services that exist
   145  	// in the snap are removed from this list on link-snap, so that we can
   146  	// remember services that were disabled in another revision and then renamed
   147  	// or otherwise removed from the snap in a future refresh.
   148  	LastActiveDisabledServices []string `json:"last-active-disabled-services,omitempty"`
   149  
   150  	// tracking services enabled and disabled by hooks
   151  	ServicesEnabledByHooks  []string `json:"services-enabled-by-hooks,omitempty"`
   152  	ServicesDisabledByHooks []string `json:"services-disabled-by-hooks,omitempty"`
   153  
   154  	// Current indicates the current active revision if Active is
   155  	// true or the last active revision if Active is false
   156  	// (usually while a snap is being operated on or disabled)
   157  	Current         snap.Revision `json:"current"`
   158  	TrackingChannel string        `json:"channel,omitempty"`
   159  	Flags
   160  	// aliases, see aliasesv2.go
   161  	Aliases             map[string]*AliasTarget `json:"aliases,omitempty"`
   162  	AutoAliasesDisabled bool                    `json:"auto-aliases-disabled,omitempty"`
   163  	AliasesPending      bool                    `json:"aliases-pending,omitempty"`
   164  
   165  	// UserID of the user requesting the install
   166  	UserID int `json:"user-id,omitempty"`
   167  
   168  	// InstanceKey is set by the user during installation and differs for
   169  	// each instance of given snap
   170  	InstanceKey string `json:"instance-key,omitempty"`
   171  	CohortKey   string `json:"cohort-key,omitempty"`
   172  
   173  	// RefreshInhibitedime records the time when the refresh was first
   174  	// attempted but inhibited because the snap was busy. This value is
   175  	// reset on each successful refresh.
   176  	RefreshInhibitedTime *time.Time `json:"refresh-inhibited-time,omitempty"`
   177  }
   178  
   179  func (snapst *SnapState) SetTrackingChannel(s string) error {
   180  	s, err := channel.Full(s)
   181  	if err != nil {
   182  		return err
   183  	}
   184  	snapst.TrackingChannel = s
   185  	return nil
   186  }
   187  
   188  // Type returns the type of the snap or an error.
   189  // Should never error if Current is not nil.
   190  func (snapst *SnapState) Type() (snap.Type, error) {
   191  	if snapst.SnapType == "" {
   192  		return snap.Type(""), fmt.Errorf("snap type unset")
   193  	}
   194  	return snap.Type(snapst.SnapType), nil
   195  }
   196  
   197  // SetType records the type of the snap.
   198  func (snapst *SnapState) SetType(typ snap.Type) {
   199  	snapst.SnapType = string(typ)
   200  }
   201  
   202  // IsInstalled returns whether the snap is installed, i.e. snapst represents an installed snap with Current revision set.
   203  func (snapst *SnapState) IsInstalled() bool {
   204  	if snapst.Current.Unset() {
   205  		if len(snapst.Sequence) > 0 {
   206  			panic(fmt.Sprintf("snapst.Current and snapst.Sequence out of sync: %#v %#v", snapst.Current, snapst.Sequence))
   207  		}
   208  
   209  		return false
   210  	}
   211  	return true
   212  }
   213  
   214  // LocalRevision returns the "latest" local revision. Local revisions
   215  // start at -1 and are counted down.
   216  func (snapst *SnapState) LocalRevision() snap.Revision {
   217  	var local snap.Revision
   218  	for _, si := range snapst.Sequence {
   219  		if si.Revision.Local() && si.Revision.N < local.N {
   220  			local = si.Revision
   221  		}
   222  	}
   223  	return local
   224  }
   225  
   226  // CurrentSideInfo returns the side info for the revision indicated by snapst.Current in the snap revision sequence if there is one.
   227  func (snapst *SnapState) CurrentSideInfo() *snap.SideInfo {
   228  	if !snapst.IsInstalled() {
   229  		return nil
   230  	}
   231  	if idx := snapst.LastIndex(snapst.Current); idx >= 0 {
   232  		return snapst.Sequence[idx]
   233  	}
   234  	panic("cannot find snapst.Current in the snapst.Sequence")
   235  }
   236  
   237  func (snapst *SnapState) previousSideInfo() *snap.SideInfo {
   238  	n := len(snapst.Sequence)
   239  	if n < 2 {
   240  		return nil
   241  	}
   242  	// find "current" and return the one before that
   243  	currentIndex := snapst.LastIndex(snapst.Current)
   244  	if currentIndex <= 0 {
   245  		return nil
   246  	}
   247  	return snapst.Sequence[currentIndex-1]
   248  }
   249  
   250  // LastIndex returns the last index of the given revision in the
   251  // snapst.Sequence
   252  func (snapst *SnapState) LastIndex(revision snap.Revision) int {
   253  	for i := len(snapst.Sequence) - 1; i >= 0; i-- {
   254  		if snapst.Sequence[i].Revision == revision {
   255  			return i
   256  		}
   257  	}
   258  	return -1
   259  }
   260  
   261  // Block returns revisions that should be blocked on refreshes,
   262  // computed from Sequence[currentRevisionIndex+1:].
   263  func (snapst *SnapState) Block() []snap.Revision {
   264  	// return revisions from Sequence[currentIndex:]
   265  	currentIndex := snapst.LastIndex(snapst.Current)
   266  	if currentIndex < 0 || currentIndex+1 == len(snapst.Sequence) {
   267  		return nil
   268  	}
   269  	out := make([]snap.Revision, len(snapst.Sequence)-currentIndex-1)
   270  	for i, si := range snapst.Sequence[currentIndex+1:] {
   271  		out[i] = si.Revision
   272  	}
   273  	return out
   274  }
   275  
   276  var ErrNoCurrent = errors.New("snap has no current revision")
   277  
   278  // Retrieval functions
   279  
   280  const (
   281  	errorOnBroken = 1 << iota
   282  	withAuxStoreInfo
   283  )
   284  
   285  var snapReadInfo = snap.ReadInfo
   286  
   287  // AutomaticSnapshot allows to hook snapshot manager's AutomaticSnapshot.
   288  var AutomaticSnapshot func(st *state.State, instanceName string) (ts *state.TaskSet, err error)
   289  var AutomaticSnapshotExpiration func(st *state.State) (time.Duration, error)
   290  var EstimateSnapshotSize func(st *state.State, instanceName string, users []string) (uint64, error)
   291  
   292  func readInfo(name string, si *snap.SideInfo, flags int) (*snap.Info, error) {
   293  	info, err := snapReadInfo(name, si)
   294  	if err != nil && flags&errorOnBroken != 0 {
   295  		return nil, err
   296  	}
   297  	if err != nil {
   298  		logger.Noticef("cannot read snap info of snap %q at revision %s: %s", name, si.Revision, err)
   299  	}
   300  	if bse, ok := err.(snap.BrokenSnapError); ok {
   301  		_, instanceKey := snap.SplitInstanceName(name)
   302  		info = &snap.Info{
   303  			SuggestedName: name,
   304  			Broken:        bse.Broken(),
   305  			InstanceKey:   instanceKey,
   306  		}
   307  		info.Apps = snap.GuessAppsForBroken(info)
   308  		if si != nil {
   309  			info.SideInfo = *si
   310  		}
   311  		err = nil
   312  	}
   313  	if err == nil && flags&withAuxStoreInfo != 0 {
   314  		if err := retrieveAuxStoreInfo(info); err != nil {
   315  			logger.Debugf("cannot read auxiliary store info for snap %q: %v", name, err)
   316  		}
   317  	}
   318  	return info, err
   319  }
   320  
   321  var revisionDate = revisionDateImpl
   322  
   323  // revisionDate returns a good approximation of when a revision reached the system.
   324  func revisionDateImpl(info *snap.Info) time.Time {
   325  	fi, err := os.Lstat(info.MountFile())
   326  	if err != nil {
   327  		return time.Time{}
   328  	}
   329  	return fi.ModTime()
   330  }
   331  
   332  // CurrentInfo returns the information about the current active revision or the last active revision (if the snap is inactive). It returns the ErrNoCurrent error if snapst.Current is unset.
   333  func (snapst *SnapState) CurrentInfo() (*snap.Info, error) {
   334  	cur := snapst.CurrentSideInfo()
   335  	if cur == nil {
   336  		return nil, ErrNoCurrent
   337  	}
   338  
   339  	name := snap.InstanceName(cur.RealName, snapst.InstanceKey)
   340  	return readInfo(name, cur, withAuxStoreInfo)
   341  }
   342  
   343  func (snapst *SnapState) InstanceName() string {
   344  	cur := snapst.CurrentSideInfo()
   345  	if cur == nil {
   346  		return ""
   347  	}
   348  	return snap.InstanceName(cur.RealName, snapst.InstanceKey)
   349  }
   350  
   351  func revisionInSequence(snapst *SnapState, needle snap.Revision) bool {
   352  	for _, si := range snapst.Sequence {
   353  		if si.Revision == needle {
   354  			return true
   355  		}
   356  	}
   357  	return false
   358  }
   359  
   360  type cachedStoreKey struct{}
   361  
   362  // ReplaceStore replaces the store used by the manager.
   363  func ReplaceStore(state *state.State, store StoreService) {
   364  	state.Cache(cachedStoreKey{}, store)
   365  }
   366  
   367  func cachedStore(st *state.State) StoreService {
   368  	ubuntuStore := st.Cached(cachedStoreKey{})
   369  	if ubuntuStore == nil {
   370  		return nil
   371  	}
   372  	return ubuntuStore.(StoreService)
   373  }
   374  
   375  // the store implementation has the interface consumed here
   376  var _ StoreService = (*store.Store)(nil)
   377  
   378  // Store returns the store service provided by the optional device context or
   379  // the one used by the snapstate package if the former has no
   380  // override.
   381  func Store(st *state.State, deviceCtx DeviceContext) StoreService {
   382  	if deviceCtx != nil {
   383  		sto := deviceCtx.Store()
   384  		if sto != nil {
   385  			return sto
   386  		}
   387  	}
   388  	if cachedStore := cachedStore(st); cachedStore != nil {
   389  		return cachedStore
   390  	}
   391  	panic("internal error: needing the store before managers have initialized it")
   392  }
   393  
   394  // Manager returns a new snap manager.
   395  func Manager(st *state.State, runner *state.TaskRunner) (*SnapManager, error) {
   396  	preseed := snapdenv.Preseeding()
   397  	m := &SnapManager{
   398  		state:          st,
   399  		autoRefresh:    newAutoRefresh(st),
   400  		refreshHints:   newRefreshHints(st),
   401  		catalogRefresh: newCatalogRefresh(st),
   402  		preseed:        preseed,
   403  	}
   404  	if preseed {
   405  		m.backend = backend.NewForPreseedMode()
   406  	} else {
   407  		m.backend = backend.Backend{}
   408  	}
   409  
   410  	if err := os.MkdirAll(dirs.SnapCookieDir, 0700); err != nil {
   411  		return nil, fmt.Errorf("cannot create directory %q: %v", dirs.SnapCookieDir, err)
   412  	}
   413  
   414  	if err := genRefreshRequestSalt(st); err != nil {
   415  		return nil, fmt.Errorf("cannot generate request salt: %v", err)
   416  	}
   417  
   418  	// this handler does nothing
   419  	runner.AddHandler("nop", func(t *state.Task, _ *tomb.Tomb) error {
   420  		return nil
   421  	}, nil)
   422  
   423  	// install/update related
   424  
   425  	// TODO: no undo handler here, we may use the GC for this and just
   426  	// remove anything that is not referenced anymore
   427  	runner.AddHandler("prerequisites", m.doPrerequisites, nil)
   428  	runner.AddHandler("prepare-snap", m.doPrepareSnap, m.undoPrepareSnap)
   429  	runner.AddHandler("download-snap", m.doDownloadSnap, m.undoPrepareSnap)
   430  	runner.AddHandler("mount-snap", m.doMountSnap, m.undoMountSnap)
   431  	runner.AddHandler("unlink-current-snap", m.doUnlinkCurrentSnap, m.undoUnlinkCurrentSnap)
   432  	runner.AddHandler("copy-snap-data", m.doCopySnapData, m.undoCopySnapData)
   433  	runner.AddCleanup("copy-snap-data", m.cleanupCopySnapData)
   434  	runner.AddHandler("link-snap", m.doLinkSnap, m.undoLinkSnap)
   435  	runner.AddHandler("start-snap-services", m.startSnapServices, m.undoStartSnapServices)
   436  	runner.AddHandler("switch-snap-channel", m.doSwitchSnapChannel, nil)
   437  	runner.AddHandler("toggle-snap-flags", m.doToggleSnapFlags, nil)
   438  	runner.AddHandler("check-rerefresh", m.doCheckReRefresh, nil)
   439  
   440  	// FIXME: drop the task entirely after a while
   441  	// (having this wart here avoids yet-another-patch)
   442  	runner.AddHandler("cleanup", func(*state.Task, *tomb.Tomb) error { return nil }, nil)
   443  
   444  	// remove related
   445  	runner.AddHandler("stop-snap-services", m.stopSnapServices, m.undoStopSnapServices)
   446  	runner.AddHandler("unlink-snap", m.doUnlinkSnap, m.undoUnlinkSnap)
   447  	runner.AddHandler("clear-snap", m.doClearSnapData, nil)
   448  	runner.AddHandler("discard-snap", m.doDiscardSnap, nil)
   449  
   450  	// alias related
   451  	// FIXME: drop the task entirely after a while
   452  	runner.AddHandler("clear-aliases", func(*state.Task, *tomb.Tomb) error { return nil }, nil)
   453  	runner.AddHandler("set-auto-aliases", m.doSetAutoAliases, m.undoRefreshAliases)
   454  	runner.AddHandler("setup-aliases", m.doSetupAliases, m.doRemoveAliases)
   455  	runner.AddHandler("refresh-aliases", m.doRefreshAliases, m.undoRefreshAliases)
   456  	runner.AddHandler("prune-auto-aliases", m.doPruneAutoAliases, m.undoRefreshAliases)
   457  	runner.AddHandler("remove-aliases", m.doRemoveAliases, m.doSetupAliases)
   458  	runner.AddHandler("alias", m.doAlias, m.undoRefreshAliases)
   459  	runner.AddHandler("unalias", m.doUnalias, m.undoRefreshAliases)
   460  	runner.AddHandler("disable-aliases", m.doDisableAliases, m.undoRefreshAliases)
   461  	runner.AddHandler("prefer-aliases", m.doPreferAliases, m.undoRefreshAliases)
   462  
   463  	// misc
   464  	runner.AddHandler("switch-snap", m.doSwitchSnap, nil)
   465  
   466  	// control serialisation
   467  	runner.AddBlocked(m.blockedTask)
   468  
   469  	return m, nil
   470  }
   471  
   472  // StartUp implements StateStarterUp.Startup.
   473  func (m *SnapManager) StartUp() error {
   474  	writeSnapReadme()
   475  
   476  	m.state.Lock()
   477  	defer m.state.Unlock()
   478  	if err := m.SyncCookies(m.state); err != nil {
   479  		return fmt.Errorf("failed to generate cookies: %q", err)
   480  	}
   481  	return nil
   482  }
   483  
   484  func (m *SnapManager) CanStandby() bool {
   485  	if n, err := NumSnaps(m.state); err == nil && n == 0 {
   486  		return true
   487  	}
   488  	return false
   489  }
   490  
   491  func genRefreshRequestSalt(st *state.State) error {
   492  	var refreshPrivacyKey string
   493  
   494  	st.Lock()
   495  	defer st.Unlock()
   496  
   497  	if err := st.Get("refresh-privacy-key", &refreshPrivacyKey); err != nil && err != state.ErrNoState {
   498  		return err
   499  	}
   500  	if refreshPrivacyKey != "" {
   501  		// nothing to do
   502  		return nil
   503  	}
   504  
   505  	refreshPrivacyKey = randutil.RandomString(16)
   506  	st.Set("refresh-privacy-key", refreshPrivacyKey)
   507  
   508  	return nil
   509  }
   510  
   511  func (m *SnapManager) blockedTask(cand *state.Task, running []*state.Task) bool {
   512  	// Serialize "prerequisites", the state lock is not enough as
   513  	// Install() inside doPrerequisites() will unlock to talk to
   514  	// the store.
   515  	if cand.Kind() == "prerequisites" {
   516  		for _, t := range running {
   517  			if t.Kind() == "prerequisites" {
   518  				return true
   519  			}
   520  		}
   521  	}
   522  
   523  	return false
   524  }
   525  
   526  // NextRefresh returns the time the next update of the system's snaps
   527  // will be attempted.
   528  // The caller should be holding the state lock.
   529  func (m *SnapManager) NextRefresh() time.Time {
   530  	return m.autoRefresh.NextRefresh()
   531  }
   532  
   533  // EffectiveRefreshHold returns the time until to which refreshes are
   534  // held if refresh.hold configuration is set and accounting for the
   535  // max postponement since the last refresh.
   536  // The caller should be holding the state lock.
   537  func (m *SnapManager) EffectiveRefreshHold() (time.Time, error) {
   538  	return m.autoRefresh.EffectiveRefreshHold()
   539  }
   540  
   541  // LastRefresh returns the time the last snap update.
   542  // The caller should be holding the state lock.
   543  func (m *SnapManager) LastRefresh() (time.Time, error) {
   544  	return m.autoRefresh.LastRefresh()
   545  }
   546  
   547  // RefreshSchedule returns the current refresh schedule as a string suitable for
   548  // display to a user and a flag indicating whether the schedule is a legacy one.
   549  // The caller should be holding the state lock.
   550  func (m *SnapManager) RefreshSchedule() (string, bool, error) {
   551  	return m.autoRefresh.RefreshSchedule()
   552  }
   553  
   554  // EnsureAutoRefreshesAreDelayed will delay refreshes for the specified amount
   555  // of time, as well as return any active auto-refresh changes that are currently
   556  // not ready so that the client can wait for those.
   557  func (m *SnapManager) EnsureAutoRefreshesAreDelayed(delay time.Duration) ([]*state.Change, error) {
   558  	// always delay for at least the specified time, this ensures that even if
   559  	// there are active refreshes right now, there won't be more auto-refreshes
   560  	// that happen after the current set finish
   561  	err := m.autoRefresh.ensureRefreshHoldAtLeast(delay)
   562  	if err != nil {
   563  		return nil, err
   564  	}
   565  
   566  	// look for auto refresh changes in progress
   567  	autoRefreshChgsInFlight := []*state.Change{}
   568  	for _, chg := range m.state.Changes() {
   569  		if chg.Kind() == "auto-refresh" && !chg.Status().Ready() {
   570  			autoRefreshChgsInFlight = append(autoRefreshChgsInFlight, chg)
   571  		}
   572  	}
   573  
   574  	return autoRefreshChgsInFlight, nil
   575  }
   576  
   577  // ensureForceDevmodeDropsDevmodeFromState undoes the forced devmode
   578  // in snapstate for forced devmode distros.
   579  func (m *SnapManager) ensureForceDevmodeDropsDevmodeFromState() error {
   580  	if !sandbox.ForceDevMode() {
   581  		return nil
   582  	}
   583  
   584  	m.state.Lock()
   585  	defer m.state.Unlock()
   586  
   587  	// int because we might want to come back and do a second pass at cleanup
   588  	var fixed int
   589  	if err := m.state.Get("fix-forced-devmode", &fixed); err != nil && err != state.ErrNoState {
   590  		return err
   591  	}
   592  
   593  	if fixed > 0 {
   594  		return nil
   595  	}
   596  
   597  	for _, name := range []string{"core", "ubuntu-core"} {
   598  		var snapst SnapState
   599  		if err := Get(m.state, name, &snapst); err == state.ErrNoState {
   600  			// nothing to see here
   601  			continue
   602  		} else if err != nil {
   603  			// bad
   604  			return err
   605  		}
   606  		if info := snapst.CurrentSideInfo(); info == nil || info.SnapID == "" {
   607  			continue
   608  		}
   609  		snapst.DevMode = false
   610  		Set(m.state, name, &snapst)
   611  	}
   612  	m.state.Set("fix-forced-devmode", 1)
   613  
   614  	return nil
   615  }
   616  
   617  // changeInFlight returns true if there is any change in the state
   618  // in non-ready state.
   619  func changeInFlight(st *state.State) bool {
   620  	for _, chg := range st.Changes() {
   621  		if !chg.IsReady() {
   622  			// another change already in motion
   623  			return true
   624  		}
   625  	}
   626  	return false
   627  }
   628  
   629  // ensureSnapdSnapTransition will migrate systems to use the "snapd" snap
   630  func (m *SnapManager) ensureSnapdSnapTransition() error {
   631  	m.state.Lock()
   632  	defer m.state.Unlock()
   633  
   634  	// we only auto-transition people on classic systems, for core we
   635  	// will need to do a proper re-model
   636  	if !release.OnClassic {
   637  		return nil
   638  	}
   639  
   640  	// check if snapd snap is installed
   641  	var snapst SnapState
   642  	err := Get(m.state, "snapd", &snapst)
   643  	if err != nil && err != state.ErrNoState {
   644  		return err
   645  	}
   646  	// nothing to do
   647  	if snapst.IsInstalled() {
   648  		return nil
   649  	}
   650  
   651  	// check if the user opts into the snapd snap
   652  	optedIntoSnapdTransition, err := optedIntoSnapdSnap(m.state)
   653  	if err != nil {
   654  		return err
   655  	}
   656  	// nothing to do: the user does not want the snapd snap yet
   657  	if !optedIntoSnapdTransition {
   658  		return nil
   659  	}
   660  
   661  	// ensure we only transition systems that have snaps already
   662  	installedSnaps, err := NumSnaps(m.state)
   663  	if err != nil {
   664  		return err
   665  	}
   666  	// no installed snaps (yet): do nothing (fresh classic install)
   667  	if installedSnaps == 0 {
   668  		return nil
   669  	}
   670  
   671  	// get current core snap and use same channel/user for the snapd snap
   672  	err = Get(m.state, "core", &snapst)
   673  	// Note that state.ErrNoState should never happen in practise. However
   674  	// if it *does* happen we still want to fix those systems by installing
   675  	// the snapd snap.
   676  	if err != nil && err != state.ErrNoState {
   677  		return err
   678  	}
   679  	coreChannel := snapst.TrackingChannel
   680  	// snapd/core are never blocked on auth so we don't need to copy
   681  	// the userID from the snapst here
   682  	userID := 0
   683  
   684  	if changeInFlight(m.state) {
   685  		// check that there is no change in flight already, this is a
   686  		// precaution to ensure the snapd transition is safe
   687  		return nil
   688  	}
   689  
   690  	// ensure we limit the retries in case something goes wrong
   691  	var lastSnapdTransitionAttempt time.Time
   692  	err = m.state.Get("snapd-transition-last-retry-time", &lastSnapdTransitionAttempt)
   693  	if err != nil && err != state.ErrNoState {
   694  		return err
   695  	}
   696  	now := time.Now()
   697  	if !lastSnapdTransitionAttempt.IsZero() && lastSnapdTransitionAttempt.Add(snapdTransitionDelayWithRandomess).After(now) {
   698  		return nil
   699  	}
   700  	m.state.Set("snapd-transition-last-retry-time", now)
   701  
   702  	var retryCount int
   703  	err = m.state.Get("snapd-transition-retry", &retryCount)
   704  	if err != nil && err != state.ErrNoState {
   705  		return err
   706  	}
   707  	m.state.Set("snapd-transition-retry", retryCount+1)
   708  
   709  	ts, err := Install(context.Background(), m.state, "snapd", &RevisionOptions{Channel: coreChannel}, userID, Flags{})
   710  	if err != nil {
   711  		return err
   712  	}
   713  
   714  	msg := i18n.G("Transition to the snapd snap")
   715  	chg := m.state.NewChange("transition-to-snapd-snap", msg)
   716  	chg.AddAll(ts)
   717  
   718  	return nil
   719  }
   720  
   721  // ensureUbuntuCoreTransition will migrate systems that use "ubuntu-core"
   722  // to the new "core" snap
   723  func (m *SnapManager) ensureUbuntuCoreTransition() error {
   724  	m.state.Lock()
   725  	defer m.state.Unlock()
   726  
   727  	var snapst SnapState
   728  	err := Get(m.state, "ubuntu-core", &snapst)
   729  	if err == state.ErrNoState {
   730  		return nil
   731  	}
   732  	if err != nil && err != state.ErrNoState {
   733  		return err
   734  	}
   735  
   736  	// check that there is no change in flight already, this is a
   737  	// precaution to ensure the core transition is safe
   738  	if changeInFlight(m.state) {
   739  		// another change already in motion
   740  		return nil
   741  	}
   742  
   743  	// ensure we limit the retries in case something goes wrong
   744  	var lastUbuntuCoreTransitionAttempt time.Time
   745  	err = m.state.Get("ubuntu-core-transition-last-retry-time", &lastUbuntuCoreTransitionAttempt)
   746  	if err != nil && err != state.ErrNoState {
   747  		return err
   748  	}
   749  	now := time.Now()
   750  	if !lastUbuntuCoreTransitionAttempt.IsZero() && lastUbuntuCoreTransitionAttempt.Add(6*time.Hour).After(now) {
   751  		return nil
   752  	}
   753  
   754  	tss, trErr := TransitionCore(m.state, "ubuntu-core", "core")
   755  	if _, ok := trErr.(*ChangeConflictError); ok {
   756  		// likely just too early, retry at next Ensure
   757  		return nil
   758  	}
   759  
   760  	m.state.Set("ubuntu-core-transition-last-retry-time", now)
   761  
   762  	var retryCount int
   763  	err = m.state.Get("ubuntu-core-transition-retry", &retryCount)
   764  	if err != nil && err != state.ErrNoState {
   765  		return err
   766  	}
   767  	m.state.Set("ubuntu-core-transition-retry", retryCount+1)
   768  
   769  	if trErr != nil {
   770  		return trErr
   771  	}
   772  
   773  	msg := i18n.G("Transition ubuntu-core to core")
   774  	chg := m.state.NewChange("transition-ubuntu-core", msg)
   775  	for _, ts := range tss {
   776  		chg.AddAll(ts)
   777  	}
   778  
   779  	return nil
   780  }
   781  
   782  // atSeed implements at seeding policy for refreshes.
   783  func (m *SnapManager) atSeed() error {
   784  	m.state.Lock()
   785  	defer m.state.Unlock()
   786  	var seeded bool
   787  	err := m.state.Get("seeded", &seeded)
   788  	if err != state.ErrNoState {
   789  		// already seeded or other error
   790  		return err
   791  	}
   792  	if err := m.autoRefresh.AtSeed(); err != nil {
   793  		return err
   794  	}
   795  	if err := m.refreshHints.AtSeed(); err != nil {
   796  		return err
   797  	}
   798  	return nil
   799  }
   800  
   801  var (
   802  	localInstallCleanupWait = time.Duration(24 * time.Hour)
   803  	localInstallLastCleanup time.Time
   804  )
   805  
   806  // localInstallCleanup removes files that might've been left behind by an
   807  // old aborted local install.
   808  //
   809  // They're usually cleaned up, but if they're created and then snapd
   810  // stops before writing the change to disk (killed, light cut, etc)
   811  // it'll be left behind.
   812  //
   813  // The code that creates the files is in daemon/api.go's postSnaps
   814  func (m *SnapManager) localInstallCleanup() error {
   815  	m.state.Lock()
   816  	defer m.state.Unlock()
   817  
   818  	now := time.Now()
   819  	cutoff := now.Add(-localInstallCleanupWait)
   820  	if localInstallLastCleanup.After(cutoff) {
   821  		return nil
   822  	}
   823  	localInstallLastCleanup = now
   824  
   825  	d, err := os.Open(dirs.SnapBlobDir)
   826  	if err != nil {
   827  		if os.IsNotExist(err) {
   828  			return nil
   829  		}
   830  		return err
   831  	}
   832  	defer d.Close()
   833  
   834  	var filenames []string
   835  	var fis []os.FileInfo
   836  	for err == nil {
   837  		// TODO: if we had fstatat we could avoid a bunch of stats
   838  		fis, err = d.Readdir(100)
   839  		// fis is nil if err isn't
   840  		for _, fi := range fis {
   841  			name := fi.Name()
   842  			if !strings.HasPrefix(name, dirs.LocalInstallBlobTempPrefix) {
   843  				continue
   844  			}
   845  			if fi.ModTime().After(cutoff) {
   846  				continue
   847  			}
   848  			filenames = append(filenames, name)
   849  		}
   850  	}
   851  	if err != io.EOF {
   852  		return err
   853  	}
   854  	return osutil.UnlinkManyAt(d, filenames)
   855  }
   856  
   857  // Ensure implements StateManager.Ensure.
   858  func (m *SnapManager) Ensure() error {
   859  	if m.preseed {
   860  		return nil
   861  	}
   862  
   863  	// do not exit right away on error
   864  	errs := []error{
   865  		m.atSeed(),
   866  		m.ensureAliasesV2(),
   867  		m.ensureForceDevmodeDropsDevmodeFromState(),
   868  		m.ensureUbuntuCoreTransition(),
   869  		m.ensureSnapdSnapTransition(),
   870  		// we should check for full regular refreshes before
   871  		// considering issuing a hint only refresh request
   872  		m.autoRefresh.Ensure(),
   873  		m.refreshHints.Ensure(),
   874  		m.catalogRefresh.Ensure(),
   875  		m.localInstallCleanup(),
   876  	}
   877  
   878  	//FIXME: use firstErr helper
   879  	for _, e := range errs {
   880  		if e != nil {
   881  			return e
   882  		}
   883  	}
   884  
   885  	return nil
   886  }