gopkg.in/ubuntu-core/snappy.v0@v0.0.0-20210902073436-25a8614f10a6/overlord/snapstate/snapstate.go (about)

     1  // -*- Mode: Go; indent-tabs-mode: t -*-
     2  
     3  /*
     4   * Copyright (C) 2016-2021 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 implements the manager and state aspects responsible for the installation and removal of snaps.
    21  package snapstate
    22  
    23  import (
    24  	"context"
    25  	"encoding/json"
    26  	"errors"
    27  	"fmt"
    28  	"os"
    29  	"sort"
    30  	"strings"
    31  	"time"
    32  
    33  	"github.com/snapcore/snapd/asserts"
    34  	"github.com/snapcore/snapd/boot"
    35  	"github.com/snapcore/snapd/dirs"
    36  	"github.com/snapcore/snapd/features"
    37  	"github.com/snapcore/snapd/gadget"
    38  	"github.com/snapcore/snapd/i18n"
    39  	"github.com/snapcore/snapd/interfaces"
    40  	"github.com/snapcore/snapd/logger"
    41  	"github.com/snapcore/snapd/osutil"
    42  	"github.com/snapcore/snapd/overlord/auth"
    43  	"github.com/snapcore/snapd/overlord/configstate/config"
    44  	"github.com/snapcore/snapd/overlord/ifacestate/ifacerepo"
    45  	"github.com/snapcore/snapd/overlord/snapstate/backend"
    46  	"github.com/snapcore/snapd/overlord/state"
    47  	"github.com/snapcore/snapd/release"
    48  	"github.com/snapcore/snapd/snap"
    49  	"github.com/snapcore/snapd/snap/channel"
    50  	"github.com/snapcore/snapd/store"
    51  	"github.com/snapcore/snapd/strutil"
    52  )
    53  
    54  // control flags for doInstall
    55  const (
    56  	skipConfigure = 1 << iota
    57  )
    58  
    59  // control flags for "Configure()"
    60  const (
    61  	IgnoreHookError = 1 << iota
    62  	TrackHookError
    63  	UseConfigDefaults
    64  )
    65  
    66  const (
    67  	DownloadAndChecksDoneEdge = state.TaskSetEdge("download-and-checks-done")
    68  	BeginEdge                 = state.TaskSetEdge("begin")
    69  	BeforeHooksEdge           = state.TaskSetEdge("before-hooks")
    70  	HooksEdge                 = state.TaskSetEdge("hooks")
    71  )
    72  
    73  var ErrNothingToDo = errors.New("nothing to do")
    74  
    75  var osutilCheckFreeSpace = osutil.CheckFreeSpace
    76  
    77  // TestingLeaveOutKernelUpdateGadgetAssets can be used to simulate an upgrade
    78  // from a broken snapd that does not generate a "update-gadget-assets" task.
    79  // See LP:#1940553
    80  var TestingLeaveOutKernelUpdateGadgetAssets bool = false
    81  
    82  type minimalInstallInfo interface {
    83  	InstanceName() string
    84  	Type() snap.Type
    85  	SnapBase() string
    86  	DownloadSize() int64
    87  	Prereq(st *state.State) []string
    88  }
    89  
    90  type updateParamsFunc func(*snap.Info) (*RevisionOptions, Flags, *SnapState)
    91  
    92  type readyUpdateInfo interface {
    93  	minimalInstallInfo
    94  
    95  	SnapSetupForUpdate(st *state.State, params updateParamsFunc, userID int, globalFlags *Flags) (*SnapSetup, *SnapState, error)
    96  }
    97  
    98  // ByType supports sorting by snap type. The most important types come first.
    99  type byType []minimalInstallInfo
   100  
   101  func (r byType) Len() int      { return len(r) }
   102  func (r byType) Swap(i, j int) { r[i], r[j] = r[j], r[i] }
   103  func (r byType) Less(i, j int) bool {
   104  	return r[i].Type().SortsBefore(r[j].Type())
   105  }
   106  
   107  type installSnapInfo struct {
   108  	*snap.Info
   109  }
   110  
   111  func (ins installSnapInfo) DownloadSize() int64 {
   112  	return ins.DownloadInfo.Size
   113  }
   114  
   115  // SnapBase returns the base snap of the snap.
   116  func (ins installSnapInfo) SnapBase() string {
   117  	return ins.Base
   118  }
   119  
   120  func (ins installSnapInfo) Prereq(st *state.State) []string {
   121  	return defaultContentPlugProviders(st, ins.Info)
   122  }
   123  
   124  func (ins installSnapInfo) SnapSetupForUpdate(st *state.State, params updateParamsFunc, userID int, globalFlags *Flags) (*SnapSetup, *SnapState, error) {
   125  	update := ins.Info
   126  
   127  	revnoOpts, flags, snapst := params(update)
   128  	flags.IsAutoRefresh = globalFlags.IsAutoRefresh
   129  
   130  	flags, err := earlyChecks(st, snapst, update, flags)
   131  	if err != nil {
   132  		return nil, nil, err
   133  	}
   134  
   135  	snapUserID, err := userIDForSnap(st, snapst, userID)
   136  	if err != nil {
   137  		return nil, nil, err
   138  	}
   139  
   140  	snapsup := SnapSetup{
   141  		Base:         update.Base,
   142  		Prereq:       defaultContentPlugProviders(st, update),
   143  		Channel:      revnoOpts.Channel,
   144  		CohortKey:    revnoOpts.CohortKey,
   145  		UserID:       snapUserID,
   146  		Flags:        flags.ForSnapSetup(),
   147  		DownloadInfo: &update.DownloadInfo,
   148  		SideInfo:     &update.SideInfo,
   149  		Type:         update.Type(),
   150  		PlugsOnly:    len(update.Slots) == 0,
   151  		InstanceKey:  update.InstanceKey,
   152  		auxStoreInfo: auxStoreInfo{
   153  			Website: update.Website,
   154  			Media:   update.Media,
   155  		},
   156  	}
   157  	return &snapsup, snapst, nil
   158  }
   159  
   160  // soundness check
   161  var _ readyUpdateInfo = installSnapInfo{}
   162  
   163  // InsufficientSpaceError represents an error where there is not enough disk
   164  // space to perform an operation.
   165  type InsufficientSpaceError struct {
   166  	// Path is the filesystem path checked for available disk space
   167  	Path string
   168  	// Snaps affected by the failing operation
   169  	Snaps []string
   170  	// Kind of the change that failed
   171  	ChangeKind string
   172  	// Message is optional, otherwise one is composed from the other information
   173  	Message string
   174  }
   175  
   176  func (e *InsufficientSpaceError) Error() string {
   177  	if e.Message != "" {
   178  		return e.Message
   179  	}
   180  	if len(e.Snaps) > 0 {
   181  		snaps := strings.Join(e.Snaps, ", ")
   182  		return fmt.Sprintf("insufficient space in %q to perform %q change for the following snaps: %s", e.Path, e.ChangeKind, snaps)
   183  	}
   184  	return fmt.Sprintf("insufficient space in %q", e.Path)
   185  }
   186  
   187  // safetyMarginDiskSpace returns size plus a safety margin (5Mb)
   188  func safetyMarginDiskSpace(size uint64) uint64 {
   189  	return size + 5*1024*1024
   190  }
   191  
   192  func isParallelInstallable(snapsup *SnapSetup) error {
   193  	if snapsup.InstanceKey == "" {
   194  		return nil
   195  	}
   196  	if snapsup.Type == snap.TypeApp {
   197  		return nil
   198  	}
   199  	return fmt.Errorf("cannot install snap of type %v as %q", snapsup.Type, snapsup.InstanceName())
   200  }
   201  
   202  func optedIntoSnapdSnap(st *state.State) (bool, error) {
   203  	tr := config.NewTransaction(st)
   204  	experimentalAllowSnapd, err := features.Flag(tr, features.SnapdSnap)
   205  	if err != nil && !config.IsNoOption(err) {
   206  		return false, err
   207  	}
   208  	return experimentalAllowSnapd, nil
   209  }
   210  
   211  func doInstall(st *state.State, snapst *SnapState, snapsup *SnapSetup, flags int, fromChange string, inUseCheck func(snap.Type) (boot.InUseFunc, error)) (*state.TaskSet, error) {
   212  	// NB: we should strive not to need or propagate deviceCtx
   213  	// here, the resulting effects/changes were not pleasant at
   214  	// one point
   215  	tr := config.NewTransaction(st)
   216  	experimentalRefreshAppAwareness, err := features.Flag(tr, features.RefreshAppAwareness)
   217  	if err != nil && !config.IsNoOption(err) {
   218  		return nil, err
   219  	}
   220  	experimentalGateAutoRefreshHook, err := features.Flag(tr, features.GateAutoRefreshHook)
   221  	if err != nil && !config.IsNoOption(err) {
   222  		return nil, err
   223  	}
   224  
   225  	if snapsup.InstanceName() == "system" {
   226  		return nil, fmt.Errorf("cannot install reserved snap name 'system'")
   227  	}
   228  	if snapst.IsInstalled() && !snapst.Active {
   229  		return nil, fmt.Errorf("cannot update disabled snap %q", snapsup.InstanceName())
   230  	}
   231  	if snapst.IsInstalled() && !snapsup.Flags.Revert {
   232  		if inUseCheck == nil {
   233  			return nil, fmt.Errorf("internal error: doInstall: inUseCheck not provided for refresh")
   234  		}
   235  	}
   236  
   237  	if snapsup.Flags.Classic {
   238  		if !release.OnClassic {
   239  			return nil, fmt.Errorf("classic confinement is only supported on classic systems")
   240  		} else if !dirs.SupportsClassicConfinement() {
   241  			return nil, fmt.Errorf(i18n.G("classic confinement requires snaps under /snap or symlink from /snap to %s"), dirs.SnapMountDir)
   242  		}
   243  	}
   244  	if !snapst.IsInstalled() { // install?
   245  		// check that the snap command namespace doesn't conflict with an enabled alias
   246  		if err := checkSnapAliasConflict(st, snapsup.InstanceName()); err != nil {
   247  			return nil, err
   248  		}
   249  	}
   250  
   251  	if err := isParallelInstallable(snapsup); err != nil {
   252  		return nil, err
   253  	}
   254  
   255  	if err := checkChangeConflictIgnoringOneChange(st, snapsup.InstanceName(), snapst, fromChange); err != nil {
   256  		return nil, err
   257  	}
   258  
   259  	if snapst.IsInstalled() {
   260  		// consider also the current revision to set plugs-only hint
   261  		info, err := snapst.CurrentInfo()
   262  		if err != nil {
   263  			return nil, err
   264  		}
   265  		snapsup.PlugsOnly = snapsup.PlugsOnly && (len(info.Slots) == 0)
   266  
   267  		if experimentalRefreshAppAwareness && !snapsup.Flags.IgnoreRunning {
   268  			// Note that because we are modifying the snap state inside
   269  			// softCheckNothingRunningForRefresh, this block must be located
   270  			// after the conflict check done above.
   271  			if err := softCheckNothingRunningForRefresh(st, snapst, info); err != nil {
   272  				return nil, err
   273  			}
   274  		}
   275  
   276  		if experimentalGateAutoRefreshHook {
   277  			// If this snap was held, then remove it from snaps-hold.
   278  			if err := resetGatingForRefreshed(st, snapsup.InstanceName()); err != nil {
   279  				return nil, err
   280  			}
   281  		}
   282  	}
   283  
   284  	ts := state.NewTaskSet()
   285  
   286  	targetRevision := snapsup.Revision()
   287  	revisionStr := ""
   288  	if snapsup.SideInfo != nil {
   289  		revisionStr = fmt.Sprintf(" (%s)", targetRevision)
   290  	}
   291  
   292  	// check if we already have the revision locally (alters tasks)
   293  	revisionIsLocal := snapst.LastIndex(targetRevision) >= 0
   294  
   295  	prereq := st.NewTask("prerequisites", fmt.Sprintf(i18n.G("Ensure prerequisites for %q are available"), snapsup.InstanceName()))
   296  	prereq.Set("snap-setup", snapsup)
   297  
   298  	var prepare, prev *state.Task
   299  	fromStore := false
   300  	// if we have a local revision here we go back to that
   301  	if snapsup.SnapPath != "" || revisionIsLocal {
   302  		prepare = st.NewTask("prepare-snap", fmt.Sprintf(i18n.G("Prepare snap %q%s"), snapsup.SnapPath, revisionStr))
   303  	} else {
   304  		fromStore = true
   305  		prepare = st.NewTask("download-snap", fmt.Sprintf(i18n.G("Download snap %q%s from channel %q"), snapsup.InstanceName(), revisionStr, snapsup.Channel))
   306  	}
   307  	prepare.Set("snap-setup", snapsup)
   308  	prepare.WaitFor(prereq)
   309  
   310  	tasks := []*state.Task{prereq, prepare}
   311  	addTask := func(t *state.Task) {
   312  		t.Set("snap-setup-task", prepare.ID())
   313  		t.WaitFor(prev)
   314  		tasks = append(tasks, t)
   315  	}
   316  	prev = prepare
   317  
   318  	var checkAsserts *state.Task
   319  	if fromStore {
   320  		// fetch and check assertions
   321  		checkAsserts = st.NewTask("validate-snap", fmt.Sprintf(i18n.G("Fetch and check assertions for snap %q%s"), snapsup.InstanceName(), revisionStr))
   322  		addTask(checkAsserts)
   323  		prev = checkAsserts
   324  	}
   325  
   326  	// mount
   327  	if !revisionIsLocal {
   328  		mount := st.NewTask("mount-snap", fmt.Sprintf(i18n.G("Mount snap %q%s"), snapsup.InstanceName(), revisionStr))
   329  		addTask(mount)
   330  		prev = mount
   331  	} else {
   332  		if snapsup.Flags.RemoveSnapPath {
   333  			// If the revision is local, we will not need the
   334  			// temporary snap.  This can happen when
   335  			// e.g. side-loading a local revision again.  The
   336  			// SnapPath is only needed in the "mount-snap" handler
   337  			// and that is skipped for local revisions.
   338  			if err := os.Remove(snapsup.SnapPath); err != nil {
   339  				return nil, err
   340  			}
   341  		}
   342  	}
   343  
   344  	// run refresh hooks when updating existing snap, otherwise run install hook further down.
   345  	runRefreshHooks := (snapst.IsInstalled() && !snapsup.Flags.Revert)
   346  	if runRefreshHooks {
   347  		preRefreshHook := SetupPreRefreshHook(st, snapsup.InstanceName())
   348  		addTask(preRefreshHook)
   349  		prev = preRefreshHook
   350  	}
   351  
   352  	if snapst.IsInstalled() {
   353  		// unlink-current-snap (will stop services for copy-data)
   354  		stop := st.NewTask("stop-snap-services", fmt.Sprintf(i18n.G("Stop snap %q services"), snapsup.InstanceName()))
   355  		stop.Set("stop-reason", snap.StopReasonRefresh)
   356  		addTask(stop)
   357  		prev = stop
   358  
   359  		removeAliases := st.NewTask("remove-aliases", fmt.Sprintf(i18n.G("Remove aliases for snap %q"), snapsup.InstanceName()))
   360  		addTask(removeAliases)
   361  		prev = removeAliases
   362  
   363  		unlink := st.NewTask("unlink-current-snap", fmt.Sprintf(i18n.G("Make current revision for snap %q unavailable"), snapsup.InstanceName()))
   364  		addTask(unlink)
   365  		prev = unlink
   366  	}
   367  
   368  	if !release.OnClassic && (snapsup.Type == snap.TypeGadget || (snapsup.Type == snap.TypeKernel && !TestingLeaveOutKernelUpdateGadgetAssets)) {
   369  		// XXX: gadget update currently for core systems only
   370  		gadgetUpdate := st.NewTask("update-gadget-assets", fmt.Sprintf(i18n.G("Update assets from %s %q%s"), snapsup.Type, snapsup.InstanceName(), revisionStr))
   371  		addTask(gadgetUpdate)
   372  		prev = gadgetUpdate
   373  	}
   374  	if !release.OnClassic && snapsup.Type == snap.TypeGadget {
   375  		// kernel command line from gadget is for core systems only
   376  		gadgetCmdline := st.NewTask("update-gadget-cmdline", fmt.Sprintf(i18n.G("Update kernel command line from gadget %q%s"), snapsup.InstanceName(), revisionStr))
   377  		addTask(gadgetCmdline)
   378  		prev = gadgetCmdline
   379  	}
   380  
   381  	// copy-data (needs stopped services by unlink)
   382  	if !snapsup.Flags.Revert {
   383  		copyData := st.NewTask("copy-snap-data", fmt.Sprintf(i18n.G("Copy snap %q data"), snapsup.InstanceName()))
   384  		addTask(copyData)
   385  		prev = copyData
   386  	}
   387  
   388  	// security
   389  	setupSecurity := st.NewTask("setup-profiles", fmt.Sprintf(i18n.G("Setup snap %q%s security profiles"), snapsup.InstanceName(), revisionStr))
   390  	addTask(setupSecurity)
   391  	prev = setupSecurity
   392  
   393  	// finalize (wrappers+current symlink)
   394  	linkSnap := st.NewTask("link-snap", fmt.Sprintf(i18n.G("Make snap %q%s available to the system"), snapsup.InstanceName(), revisionStr))
   395  	addTask(linkSnap)
   396  	prev = linkSnap
   397  
   398  	// auto-connections
   399  	autoConnect := st.NewTask("auto-connect", fmt.Sprintf(i18n.G("Automatically connect eligible plugs and slots of snap %q"), snapsup.InstanceName()))
   400  	addTask(autoConnect)
   401  	prev = autoConnect
   402  
   403  	// setup aliases
   404  	setAutoAliases := st.NewTask("set-auto-aliases", fmt.Sprintf(i18n.G("Set automatic aliases for snap %q"), snapsup.InstanceName()))
   405  	addTask(setAutoAliases)
   406  	prev = setAutoAliases
   407  
   408  	setupAliases := st.NewTask("setup-aliases", fmt.Sprintf(i18n.G("Setup snap %q aliases"), snapsup.InstanceName()))
   409  	addTask(setupAliases)
   410  	prev = setupAliases
   411  
   412  	if !release.OnClassic && snapsup.Type == snap.TypeSnapd {
   413  		// only run for core devices and the snapd snap, run late enough
   414  		// so that the task is executed by the new snapd
   415  		bootConfigUpdate := st.NewTask("update-managed-boot-config", fmt.Sprintf(i18n.G("Update managed boot config assets from %q%s"), snapsup.InstanceName(), revisionStr))
   416  		addTask(bootConfigUpdate)
   417  		prev = bootConfigUpdate
   418  	}
   419  
   420  	if runRefreshHooks {
   421  		postRefreshHook := SetupPostRefreshHook(st, snapsup.InstanceName())
   422  		addTask(postRefreshHook)
   423  		prev = postRefreshHook
   424  	}
   425  
   426  	var installHook *state.Task
   427  	// only run install hook if installing the snap for the first time
   428  	if !snapst.IsInstalled() {
   429  		installHook = SetupInstallHook(st, snapsup.InstanceName())
   430  		addTask(installHook)
   431  		prev = installHook
   432  	}
   433  
   434  	// run new services
   435  	startSnapServices := st.NewTask("start-snap-services", fmt.Sprintf(i18n.G("Start snap %q%s services"), snapsup.InstanceName(), revisionStr))
   436  	addTask(startSnapServices)
   437  	prev = startSnapServices
   438  
   439  	// Do not do that if we are reverting to a local revision
   440  	if snapst.IsInstalled() && !snapsup.Flags.Revert {
   441  		var retain int
   442  		if err := config.NewTransaction(st).Get("core", "refresh.retain", &retain); err != nil {
   443  			// on classic we only keep 2 copies by default
   444  			if release.OnClassic {
   445  				retain = 2
   446  			} else {
   447  				retain = 3
   448  			}
   449  		}
   450  		retain-- //  we're adding one
   451  
   452  		seq := snapst.Sequence
   453  		currentIndex := snapst.LastIndex(snapst.Current)
   454  
   455  		// discard everything after "current" (we may have reverted to
   456  		// a previous versions earlier)
   457  		for i := currentIndex + 1; i < len(seq); i++ {
   458  			si := seq[i]
   459  			if si.Revision == targetRevision {
   460  				// but don't discard this one; its' the thing we're switching to!
   461  				continue
   462  			}
   463  			ts := removeInactiveRevision(st, snapsup.InstanceName(), si.SnapID, si.Revision, snapsup.Type)
   464  			ts.WaitFor(prev)
   465  			tasks = append(tasks, ts.Tasks()...)
   466  			prev = tasks[len(tasks)-1]
   467  		}
   468  
   469  		// make sure we're not scheduling the removal of the target
   470  		// revision in the case where the target revision is already in
   471  		// the sequence.
   472  		for i := 0; i < currentIndex; i++ {
   473  			si := seq[i]
   474  			if si.Revision == targetRevision {
   475  				// we do *not* want to removeInactiveRevision of this one
   476  				copy(seq[i:], seq[i+1:])
   477  				seq = seq[:len(seq)-1]
   478  				currentIndex--
   479  			}
   480  		}
   481  
   482  		// normal garbage collect
   483  		var inUse boot.InUseFunc
   484  		for i := 0; i <= currentIndex-retain; i++ {
   485  			if inUse == nil {
   486  				var err error
   487  				inUse, err = inUseCheck(snapsup.Type)
   488  				if err != nil {
   489  					return nil, err
   490  				}
   491  			}
   492  
   493  			si := seq[i]
   494  			if inUse(snapsup.InstanceName(), si.Revision) {
   495  				continue
   496  			}
   497  			ts := removeInactiveRevision(st, snapsup.InstanceName(), si.SnapID, si.Revision, snapsup.Type)
   498  			ts.WaitFor(prev)
   499  			tasks = append(tasks, ts.Tasks()...)
   500  			prev = tasks[len(tasks)-1]
   501  		}
   502  
   503  		addTask(st.NewTask("cleanup", fmt.Sprintf("Clean up %q%s install", snapsup.InstanceName(), revisionStr)))
   504  	}
   505  
   506  	installSet := state.NewTaskSet(tasks...)
   507  	installSet.WaitAll(ts)
   508  	installSet.MarkEdge(prereq, BeginEdge)
   509  	installSet.MarkEdge(setupAliases, BeforeHooksEdge)
   510  	if installHook != nil {
   511  		installSet.MarkEdge(installHook, HooksEdge)
   512  	}
   513  	ts.AddAllWithEdges(installSet)
   514  	if checkAsserts != nil {
   515  		ts.MarkEdge(checkAsserts, DownloadAndChecksDoneEdge)
   516  	}
   517  
   518  	if flags&skipConfigure != 0 {
   519  		return installSet, nil
   520  	}
   521  
   522  	// we do not support configuration for bases or the "snapd" snap yet
   523  	if snapsup.Type != snap.TypeBase && snapsup.Type != snap.TypeSnapd {
   524  		confFlags := 0
   525  		notCore := snapsup.InstanceName() != "core"
   526  		hasSnapID := snapsup.SideInfo != nil && snapsup.SideInfo.SnapID != ""
   527  		if !snapst.IsInstalled() && hasSnapID && notCore {
   528  			// installation, run configure using the gadget defaults
   529  			// if available, system config defaults (attached to
   530  			// "core") are consumed only during seeding, via an
   531  			// explicit configure step separate from installing
   532  			confFlags |= UseConfigDefaults
   533  		}
   534  		configSet := ConfigureSnap(st, snapsup.InstanceName(), confFlags)
   535  		configSet.WaitAll(ts)
   536  		ts.AddAll(configSet)
   537  	}
   538  
   539  	healthCheck := CheckHealthHook(st, snapsup.InstanceName(), snapsup.Revision())
   540  	healthCheck.WaitAll(ts)
   541  	ts.AddTask(healthCheck)
   542  
   543  	return ts, nil
   544  }
   545  
   546  // ConfigureSnap returns a set of tasks to configure snapName as done during installation/refresh.
   547  func ConfigureSnap(st *state.State, snapName string, confFlags int) *state.TaskSet {
   548  	// This is slightly ugly, ideally we would check the type instead
   549  	// of hardcoding the name here. Unfortunately we do not have the
   550  	// type until we actually run the change.
   551  	if snapName == defaultCoreSnapName {
   552  		confFlags |= IgnoreHookError
   553  		confFlags |= TrackHookError
   554  	}
   555  	return Configure(st, snapName, nil, confFlags)
   556  }
   557  
   558  var Configure = func(st *state.State, snapName string, patch map[string]interface{}, flags int) *state.TaskSet {
   559  	panic("internal error: snapstate.Configure is unset")
   560  }
   561  
   562  var SetupInstallHook = func(st *state.State, snapName string) *state.Task {
   563  	panic("internal error: snapstate.SetupInstallHook is unset")
   564  }
   565  
   566  var SetupPreRefreshHook = func(st *state.State, snapName string) *state.Task {
   567  	panic("internal error: snapstate.SetupPreRefreshHook is unset")
   568  }
   569  
   570  var SetupPostRefreshHook = func(st *state.State, snapName string) *state.Task {
   571  	panic("internal error: snapstate.SetupPostRefreshHook is unset")
   572  }
   573  
   574  var SetupRemoveHook = func(st *state.State, snapName string) *state.Task {
   575  	panic("internal error: snapstate.SetupRemoveHook is unset")
   576  }
   577  
   578  var CheckHealthHook = func(st *state.State, snapName string, rev snap.Revision) *state.Task {
   579  	panic("internal error: snapstate.CheckHealthHook is unset")
   580  }
   581  
   582  var SetupGateAutoRefreshHook = func(st *state.State, snapName string) *state.Task {
   583  	panic("internal error: snapstate.SetupAutoRefreshGatingHook is unset")
   584  }
   585  
   586  var generateSnapdWrappers = backend.GenerateSnapdWrappers
   587  
   588  // FinishRestart will return a Retry error if there is a pending restart
   589  // and a real error if anything went wrong (like a rollback across
   590  // restarts).
   591  // For snapd snap updates this will also rerun wrappers generation to fully
   592  // catch up with any change.
   593  func FinishRestart(task *state.Task, snapsup *SnapSetup) (err error) {
   594  	if ok, _ := task.State().Restarting(); ok {
   595  		// don't continue until we are in the restarted snapd
   596  		task.Logf("Waiting for automatic snapd restart...")
   597  		return &state.Retry{}
   598  	}
   599  
   600  	if snapsup.Type == snap.TypeSnapd {
   601  		if os.Getenv("SNAPD_REVERT_TO_REV") != "" {
   602  			return fmt.Errorf("there was a snapd rollback across the restart")
   603  		}
   604  
   605  		// if we have restarted and snapd was refreshed, then we need to generate
   606  		// snapd wrappers again with current snapd, as the logic of generating
   607  		// wrappers may have changed between previous and new snapd code.
   608  		if !release.OnClassic {
   609  			snapdInfo, err := snap.ReadCurrentInfo(snapsup.SnapName())
   610  			if err != nil {
   611  				return fmt.Errorf("cannot get current snapd snap info: %v", err)
   612  			}
   613  			// TODO: if future changes to wrappers need one more snapd restart,
   614  			// then it should be handled here as well.
   615  			if err := generateSnapdWrappers(snapdInfo); err != nil {
   616  				return err
   617  			}
   618  		}
   619  	}
   620  
   621  	// consider kernel and base
   622  
   623  	deviceCtx, err := DeviceCtx(task.State(), task, nil)
   624  	if err != nil {
   625  		return err
   626  	}
   627  
   628  	// Check if there was a rollback. A reboot can be triggered by:
   629  	// - core (old core16 world, system-reboot)
   630  	// - bootable base snap (new core18 world, system-reboot)
   631  	// - kernel
   632  	//
   633  	// On classic and in ephemeral run modes (like install, recover)
   634  	// there can never be a rollback so we can skip the check there.
   635  	//
   636  	// TODO: Detect "snapd" snap daemon-restarts here that
   637  	//       fallback into the old version (once we have
   638  	//       better snapd rollback support in core18).
   639  	if deviceCtx.RunMode() && !release.OnClassic {
   640  		// get the name of the name relevant for booting
   641  		// based on the given type
   642  		model := deviceCtx.Model()
   643  		var bootName string
   644  		switch snapsup.Type {
   645  		case snap.TypeKernel:
   646  			bootName = model.Kernel()
   647  		case snap.TypeOS, snap.TypeBase:
   648  			bootName = "core"
   649  			if model.Base() != "" {
   650  				bootName = model.Base()
   651  			}
   652  		default:
   653  			return nil
   654  		}
   655  		// if it is not a snap related to our booting we are not
   656  		// interested
   657  		if snapsup.InstanceName() != bootName {
   658  			return nil
   659  		}
   660  
   661  		// compare what we think is "current" for snapd with what
   662  		// actually booted. The bootloader may revert on a failed
   663  		// boot from a bad os/base/kernel to a good one and in this
   664  		// case we need to catch this and error accordingly
   665  		current, err := boot.GetCurrentBoot(snapsup.Type, deviceCtx)
   666  		if err == boot.ErrBootNameAndRevisionNotReady {
   667  			return &state.Retry{After: 5 * time.Second}
   668  		}
   669  		if err != nil {
   670  			return err
   671  		}
   672  		if snapsup.InstanceName() != current.SnapName() || snapsup.SideInfo.Revision != current.SnapRevision() {
   673  			// TODO: make sure this revision gets ignored for
   674  			//       automatic refreshes
   675  			return fmt.Errorf("cannot finish %s installation, there was a rollback across reboot", snapsup.InstanceName())
   676  		}
   677  	}
   678  
   679  	return nil
   680  }
   681  
   682  func contentAttr(attrer interfaces.Attrer) string {
   683  	var s string
   684  	err := attrer.Attr("content", &s)
   685  	if err != nil {
   686  		return ""
   687  	}
   688  	return s
   689  }
   690  
   691  func contentIfaceAvailable(st *state.State) map[string]bool {
   692  	repo := ifacerepo.Get(st)
   693  	contentSlots := repo.AllSlots("content")
   694  	avail := make(map[string]bool, len(contentSlots))
   695  	for _, slot := range contentSlots {
   696  		contentTag := contentAttr(slot)
   697  		if contentTag == "" {
   698  			continue
   699  		}
   700  		avail[contentTag] = true
   701  	}
   702  	return avail
   703  }
   704  
   705  // defaultContentPlugProviders takes a snap.Info and returns what
   706  // default providers there are.
   707  func defaultContentPlugProviders(st *state.State, info *snap.Info) []string {
   708  	needed := snap.NeededDefaultProviders(info)
   709  	if len(needed) == 0 {
   710  		return nil
   711  	}
   712  	avail := contentIfaceAvailable(st)
   713  	out := []string{}
   714  	for snapInstance, contentTags := range needed {
   715  		for _, contentTag := range contentTags {
   716  			if !avail[contentTag] {
   717  				out = append(out, snapInstance)
   718  				break
   719  			}
   720  		}
   721  	}
   722  	return out
   723  }
   724  
   725  // validateFeatureFlags validates the given snap only uses experimental
   726  // features that are enabled by the user.
   727  func validateFeatureFlags(st *state.State, info *snap.Info) error {
   728  	tr := config.NewTransaction(st)
   729  
   730  	if len(info.Layout) > 0 {
   731  		flag, err := features.Flag(tr, features.Layouts)
   732  		if err != nil {
   733  			return err
   734  		}
   735  		if !flag {
   736  			return fmt.Errorf("experimental feature disabled - test it by setting 'experimental.layouts' to true")
   737  		}
   738  	}
   739  
   740  	if info.InstanceKey != "" {
   741  		flag, err := features.Flag(tr, features.ParallelInstances)
   742  		if err != nil {
   743  			return err
   744  		}
   745  		if !flag {
   746  			return fmt.Errorf("experimental feature disabled - test it by setting 'experimental.parallel-instances' to true")
   747  		}
   748  	}
   749  
   750  	var hasUserService, usesDbusActivation bool
   751  	for _, app := range info.Apps {
   752  		if app.IsService() && app.DaemonScope == snap.UserDaemon {
   753  			hasUserService = true
   754  		}
   755  		if len(app.ActivatesOn) != 0 {
   756  			usesDbusActivation = true
   757  		}
   758  	}
   759  
   760  	if hasUserService {
   761  		flag, err := features.Flag(tr, features.UserDaemons)
   762  		if err != nil {
   763  			return err
   764  		}
   765  		if !flag {
   766  			return fmt.Errorf("experimental feature disabled - test it by setting 'experimental.user-daemons' to true")
   767  		}
   768  		if !release.SystemctlSupportsUserUnits() {
   769  			return fmt.Errorf("user session daemons are not supported on this release")
   770  		}
   771  	}
   772  
   773  	if usesDbusActivation {
   774  		flag, err := features.Flag(tr, features.DbusActivation)
   775  		if err != nil {
   776  			return err
   777  		}
   778  		if !flag {
   779  			return fmt.Errorf("experimental feature disabled - test it by setting 'experimental.dbus-activation' to true")
   780  		}
   781  	}
   782  
   783  	return nil
   784  }
   785  
   786  func ensureInstallPreconditions(st *state.State, info *snap.Info, flags Flags, snapst *SnapState) (Flags, error) {
   787  	// if snap is allowed to be devmode via the dangerous model and it's
   788  	// confinement is indeed devmode, promote the flags.DevMode to true
   789  	if flags.ApplySnapDevMode && info.NeedsDevMode() {
   790  		// TODO: what about jail-mode? will we also allow putting devmode
   791  		// snaps (i.e. snaps with snap.yaml with confinement: devmode) into
   792  		// strict confinement via the model assertion?
   793  		flags.DevMode = true
   794  	}
   795  
   796  	if flags.Classic && !info.NeedsClassic() {
   797  		// snap does not require classic confinement, silently drop the flag
   798  		flags.Classic = false
   799  	}
   800  
   801  	if err := validateInfoAndFlags(info, snapst, flags); err != nil {
   802  		return flags, err
   803  	}
   804  	if err := validateFeatureFlags(st, info); err != nil {
   805  		return flags, err
   806  	}
   807  	if err := checkDBusServiceConflicts(st, info); err != nil {
   808  		return flags, err
   809  	}
   810  	return flags, nil
   811  }
   812  
   813  // InstallPath returns a set of tasks for installing a snap from a file path
   814  // and the snap.Info for the given snap.
   815  //
   816  // Note that the state must be locked by the caller.
   817  // The provided SideInfo can contain just a name which results in a
   818  // local revision and sideloading, or full metadata in which case it
   819  // the snap will appear as installed from the store.
   820  func InstallPath(st *state.State, si *snap.SideInfo, path, instanceName, channel string, flags Flags) (*state.TaskSet, *snap.Info, error) {
   821  	if si.RealName == "" {
   822  		return nil, nil, fmt.Errorf("internal error: snap name to install %q not provided", path)
   823  	}
   824  
   825  	if instanceName == "" {
   826  		instanceName = si.RealName
   827  	}
   828  
   829  	deviceCtx, err := DeviceCtxFromState(st, nil)
   830  	if err != nil {
   831  		return nil, nil, err
   832  	}
   833  
   834  	var snapst SnapState
   835  	err = Get(st, instanceName, &snapst)
   836  	if err != nil && err != state.ErrNoState {
   837  		return nil, nil, err
   838  	}
   839  
   840  	if si.SnapID != "" {
   841  		if si.Revision.Unset() {
   842  			return nil, nil, fmt.Errorf("internal error: snap id set to install %q but revision is unset", path)
   843  		}
   844  	}
   845  
   846  	channel, err = resolveChannel(st, instanceName, snapst.TrackingChannel, channel, deviceCtx)
   847  	if err != nil {
   848  		return nil, nil, err
   849  	}
   850  
   851  	var instFlags int
   852  	if flags.SkipConfigure {
   853  		// extract it as a doInstall flag, this is not passed
   854  		// into SnapSetup
   855  		instFlags |= skipConfigure
   856  	}
   857  
   858  	// It is ok do open the snap file here because we either
   859  	// have side info or the user passed --dangerous
   860  	info, container, err := backend.OpenSnapFile(path, si)
   861  	if err != nil {
   862  		return nil, nil, err
   863  	}
   864  
   865  	if err := validateContainer(container, info, logger.Noticef); err != nil {
   866  		return nil, nil, err
   867  	}
   868  	if err := snap.ValidateInstanceName(instanceName); err != nil {
   869  		return nil, nil, fmt.Errorf("invalid instance name: %v", err)
   870  	}
   871  
   872  	snapName, instanceKey := snap.SplitInstanceName(instanceName)
   873  	if info.SnapName() != snapName {
   874  		return nil, nil, fmt.Errorf("cannot install snap %q, the name does not match the metadata %q", instanceName, info.SnapName())
   875  	}
   876  	info.InstanceKey = instanceKey
   877  
   878  	flags, err = ensureInstallPreconditions(st, info, flags, &snapst)
   879  	if err != nil {
   880  		return nil, nil, err
   881  	}
   882  	// this might be a refresh; check the epoch before proceeding
   883  	if err := earlyEpochCheck(info, &snapst); err != nil {
   884  		return nil, nil, err
   885  	}
   886  
   887  	snapsup := &SnapSetup{
   888  		Base:        info.Base,
   889  		Prereq:      defaultContentPlugProviders(st, info),
   890  		SideInfo:    si,
   891  		SnapPath:    path,
   892  		Channel:     channel,
   893  		Flags:       flags.ForSnapSetup(),
   894  		Type:        info.Type(),
   895  		PlugsOnly:   len(info.Slots) == 0,
   896  		InstanceKey: info.InstanceKey,
   897  	}
   898  
   899  	ts, err := doInstall(st, &snapst, snapsup, instFlags, "", inUseFor(deviceCtx))
   900  	return ts, info, err
   901  }
   902  
   903  // TryPath returns a set of tasks for trying a snap from a file path.
   904  // Note that the state must be locked by the caller.
   905  func TryPath(st *state.State, name, path string, flags Flags) (*state.TaskSet, error) {
   906  	flags.TryMode = true
   907  
   908  	ts, _, err := InstallPath(st, &snap.SideInfo{RealName: name}, path, "", "", flags)
   909  	return ts, err
   910  }
   911  
   912  // Install returns a set of tasks for installing a snap.
   913  // Note that the state must be locked by the caller.
   914  //
   915  // The returned TaskSet will contain a DownloadAndChecksDoneEdge.
   916  func Install(ctx context.Context, st *state.State, name string, opts *RevisionOptions, userID int, flags Flags) (*state.TaskSet, error) {
   917  	return InstallWithDeviceContext(ctx, st, name, opts, userID, flags, nil, "")
   918  }
   919  
   920  // InstallWithDeviceContext returns a set of tasks for installing a snap.
   921  // It will query for the snap with the given deviceCtx.
   922  // Note that the state must be locked by the caller.
   923  //
   924  // The returned TaskSet will contain a DownloadAndChecksDoneEdge.
   925  func InstallWithDeviceContext(ctx context.Context, st *state.State, name string, opts *RevisionOptions, userID int, flags Flags, deviceCtx DeviceContext, fromChange string) (*state.TaskSet, error) {
   926  	if opts == nil {
   927  		opts = &RevisionOptions{}
   928  	}
   929  	if opts.CohortKey != "" && !opts.Revision.Unset() {
   930  		return nil, errors.New("cannot specify revision and cohort")
   931  	}
   932  
   933  	if opts.Channel == "" {
   934  		opts.Channel = "stable"
   935  	}
   936  
   937  	var snapst SnapState
   938  	err := Get(st, name, &snapst)
   939  	if err != nil && err != state.ErrNoState {
   940  		return nil, err
   941  	}
   942  	if snapst.IsInstalled() {
   943  		return nil, &snap.AlreadyInstalledError{Snap: name}
   944  	}
   945  	// need to have a model set before trying to talk the store
   946  	deviceCtx, err = DevicePastSeeding(st, deviceCtx)
   947  	if err != nil {
   948  		return nil, err
   949  	}
   950  
   951  	if err := snap.ValidateInstanceName(name); err != nil {
   952  		return nil, fmt.Errorf("invalid instance name: %v", err)
   953  	}
   954  
   955  	sar, err := installInfo(ctx, st, name, opts, userID, flags, deviceCtx)
   956  	if err != nil {
   957  		return nil, err
   958  	}
   959  	info := sar.Info
   960  
   961  	if flags.RequireTypeBase && info.Type() != snap.TypeBase && info.Type() != snap.TypeOS {
   962  		return nil, fmt.Errorf("unexpected snap type %q, instead of 'base'", info.Type())
   963  	}
   964  
   965  	flags, err = ensureInstallPreconditions(st, info, flags, &snapst)
   966  	if err != nil {
   967  		return nil, err
   968  	}
   969  
   970  	tr := config.NewTransaction(st)
   971  	checkDiskSpaceInstall, err := features.Flag(tr, features.CheckDiskSpaceInstall)
   972  	if err != nil && !config.IsNoOption(err) {
   973  		return nil, err
   974  	}
   975  	if checkDiskSpaceInstall {
   976  		// check if there is enough disk space for requested snap and its
   977  		// prerequisites.
   978  		totalSize, err := installSize(st, []minimalInstallInfo{installSnapInfo{info}}, userID)
   979  		if err != nil {
   980  			return nil, err
   981  		}
   982  		requiredSpace := safetyMarginDiskSpace(totalSize)
   983  		path := dirs.SnapdStateDir(dirs.GlobalRootDir)
   984  		if err := osutilCheckFreeSpace(path, requiredSpace); err != nil {
   985  			if _, ok := err.(*osutil.NotEnoughDiskSpaceError); ok {
   986  				return nil, &InsufficientSpaceError{
   987  					Path:       path,
   988  					Snaps:      []string{info.InstanceName()},
   989  					ChangeKind: "install",
   990  				}
   991  			}
   992  			return nil, err
   993  		}
   994  	}
   995  
   996  	snapsup := &SnapSetup{
   997  		Channel:      opts.Channel,
   998  		Base:         info.Base,
   999  		Prereq:       defaultContentPlugProviders(st, info),
  1000  		UserID:       userID,
  1001  		Flags:        flags.ForSnapSetup(),
  1002  		DownloadInfo: &info.DownloadInfo,
  1003  		SideInfo:     &info.SideInfo,
  1004  		Type:         info.Type(),
  1005  		PlugsOnly:    len(info.Slots) == 0,
  1006  		InstanceKey:  info.InstanceKey,
  1007  		auxStoreInfo: auxStoreInfo{
  1008  			Media:   info.Media,
  1009  			Website: info.Website,
  1010  		},
  1011  		CohortKey: opts.CohortKey,
  1012  	}
  1013  
  1014  	if sar.RedirectChannel != "" {
  1015  		snapsup.Channel = sar.RedirectChannel
  1016  	}
  1017  
  1018  	return doInstall(st, &snapst, snapsup, 0, fromChange, nil)
  1019  }
  1020  
  1021  // InstallMany installs everything from the given list of names.
  1022  // Note that the state must be locked by the caller.
  1023  func InstallMany(st *state.State, names []string, userID int) ([]string, []*state.TaskSet, error) {
  1024  	// need to have a model set before trying to talk the store
  1025  	deviceCtx, err := DevicePastSeeding(st, nil)
  1026  	if err != nil {
  1027  		return nil, nil, err
  1028  	}
  1029  
  1030  	toInstall := make([]string, 0, len(names))
  1031  	for _, name := range names {
  1032  		var snapst SnapState
  1033  		err := Get(st, name, &snapst)
  1034  		if err != nil && err != state.ErrNoState {
  1035  			return nil, nil, err
  1036  		}
  1037  		if snapst.IsInstalled() {
  1038  			continue
  1039  		}
  1040  
  1041  		if err := snap.ValidateInstanceName(name); err != nil {
  1042  			return nil, nil, fmt.Errorf("invalid instance name: %v", err)
  1043  		}
  1044  
  1045  		toInstall = append(toInstall, name)
  1046  	}
  1047  
  1048  	user, err := userFromUserID(st, userID)
  1049  	if err != nil {
  1050  		return nil, nil, err
  1051  	}
  1052  
  1053  	installs, err := installCandidates(st, toInstall, "stable", user)
  1054  	if err != nil {
  1055  		return nil, nil, err
  1056  	}
  1057  
  1058  	tr := config.NewTransaction(st)
  1059  	checkDiskSpaceInstall, err := features.Flag(tr, features.CheckDiskSpaceInstall)
  1060  	if err != nil && !config.IsNoOption(err) {
  1061  		return nil, nil, err
  1062  	}
  1063  	if checkDiskSpaceInstall {
  1064  		// check if there is enough disk space for requested snaps and their
  1065  		// prerequisites.
  1066  		snapInfos := make([]minimalInstallInfo, len(installs))
  1067  		for i, sar := range installs {
  1068  			snapInfos[i] = installSnapInfo{sar.Info}
  1069  		}
  1070  		totalSize, err := installSize(st, snapInfos, userID)
  1071  		if err != nil {
  1072  			return nil, nil, err
  1073  		}
  1074  
  1075  		requiredSpace := safetyMarginDiskSpace(totalSize)
  1076  		path := dirs.SnapdStateDir(dirs.GlobalRootDir)
  1077  		if err := osutilCheckFreeSpace(path, requiredSpace); err != nil {
  1078  			if _, ok := err.(*osutil.NotEnoughDiskSpaceError); ok {
  1079  				return nil, nil, &InsufficientSpaceError{
  1080  					Path:       path,
  1081  					Snaps:      toInstall,
  1082  					ChangeKind: "install",
  1083  				}
  1084  			}
  1085  			return nil, nil, err
  1086  		}
  1087  	}
  1088  
  1089  	tasksets := make([]*state.TaskSet, 0, len(installs))
  1090  	for _, sar := range installs {
  1091  		info := sar.Info
  1092  		var snapst SnapState
  1093  		var flags Flags
  1094  
  1095  		flags, err := ensureInstallPreconditions(st, info, flags, &snapst)
  1096  		if err != nil {
  1097  			return nil, nil, err
  1098  		}
  1099  
  1100  		channel := "stable"
  1101  		if sar.RedirectChannel != "" {
  1102  			channel = sar.RedirectChannel
  1103  		}
  1104  
  1105  		snapsup := &SnapSetup{
  1106  			Channel:      channel,
  1107  			Base:         info.Base,
  1108  			Prereq:       defaultContentPlugProviders(st, info),
  1109  			UserID:       userID,
  1110  			Flags:        flags.ForSnapSetup(),
  1111  			DownloadInfo: &info.DownloadInfo,
  1112  			SideInfo:     &info.SideInfo,
  1113  			Type:         info.Type(),
  1114  			PlugsOnly:    len(info.Slots) == 0,
  1115  			InstanceKey:  info.InstanceKey,
  1116  		}
  1117  
  1118  		ts, err := doInstall(st, &snapst, snapsup, 0, "", inUseFor(deviceCtx))
  1119  		if err != nil {
  1120  			return nil, nil, err
  1121  		}
  1122  		ts.JoinLane(st.NewLane())
  1123  		tasksets = append(tasksets, ts)
  1124  	}
  1125  
  1126  	return toInstall, tasksets, nil
  1127  }
  1128  
  1129  // RefreshCandidates gets a list of candidates for update
  1130  // Note that the state must be locked by the caller.
  1131  func RefreshCandidates(st *state.State, user *auth.UserState) ([]*snap.Info, error) {
  1132  	updates, _, _, err := refreshCandidates(context.TODO(), st, nil, user, nil)
  1133  	return updates, err
  1134  }
  1135  
  1136  // ValidateRefreshes allows to hook validation into the handling of refresh candidates.
  1137  var ValidateRefreshes func(st *state.State, refreshes []*snap.Info, ignoreValidation map[string]bool, userID int, deviceCtx DeviceContext) (validated []*snap.Info, err error)
  1138  
  1139  // UpdateMany updates everything from the given list of names that the
  1140  // store says is updateable. If the list is empty, update everything.
  1141  // Note that the state must be locked by the caller.
  1142  func UpdateMany(ctx context.Context, st *state.State, names []string, userID int, flags *Flags) ([]string, []*state.TaskSet, error) {
  1143  	return updateManyFiltered(ctx, st, names, userID, nil, flags, "")
  1144  }
  1145  
  1146  // updateFilter is the type of function that can be passed to
  1147  // updateManyFromChange so it filters the updates.
  1148  //
  1149  // If the filter returns true, the update for that snap proceeds. If
  1150  // it returns false, the snap is removed from the list of updates to
  1151  // consider.
  1152  type updateFilter func(*snap.Info, *SnapState) bool
  1153  
  1154  func updateManyFiltered(ctx context.Context, st *state.State, names []string, userID int, filter updateFilter, flags *Flags, fromChange string) ([]string, []*state.TaskSet, error) {
  1155  	if flags == nil {
  1156  		flags = &Flags{}
  1157  	}
  1158  	user, err := userFromUserID(st, userID)
  1159  	if err != nil {
  1160  		return nil, nil, err
  1161  	}
  1162  
  1163  	// need to have a model set before trying to talk the store
  1164  	deviceCtx, err := DevicePastSeeding(st, nil)
  1165  	if err != nil {
  1166  		return nil, nil, err
  1167  	}
  1168  
  1169  	refreshOpts := &store.RefreshOptions{IsAutoRefresh: flags.IsAutoRefresh}
  1170  	updates, stateByInstanceName, ignoreValidation, err := refreshCandidates(ctx, st, names, user, refreshOpts)
  1171  	if err != nil {
  1172  		return nil, nil, err
  1173  	}
  1174  
  1175  	if filter != nil {
  1176  		actual := updates[:0]
  1177  		for _, update := range updates {
  1178  			if filter(update, stateByInstanceName[update.InstanceName()]) {
  1179  				actual = append(actual, update)
  1180  			}
  1181  		}
  1182  		updates = actual
  1183  	}
  1184  
  1185  	if ValidateRefreshes != nil && len(updates) != 0 {
  1186  		updates, err = ValidateRefreshes(st, updates, ignoreValidation, userID, deviceCtx)
  1187  		if err != nil {
  1188  			// not doing "refresh all" report the error
  1189  			if len(names) != 0 {
  1190  				return nil, nil, err
  1191  			}
  1192  			// doing "refresh all", log the problems
  1193  			logger.Noticef("cannot refresh some snaps: %v", err)
  1194  		}
  1195  	}
  1196  
  1197  	params := func(update *snap.Info) (*RevisionOptions, Flags, *SnapState) {
  1198  		snapst := stateByInstanceName[update.InstanceName()]
  1199  		// setting options to what's in state as multi-refresh doesn't let you change these
  1200  		opts := &RevisionOptions{
  1201  			Channel:   snapst.TrackingChannel,
  1202  			CohortKey: snapst.CohortKey,
  1203  		}
  1204  		return opts, snapst.Flags, snapst
  1205  
  1206  	}
  1207  
  1208  	toUpdate := make([]minimalInstallInfo, len(updates))
  1209  	for i, up := range updates {
  1210  		toUpdate[i] = installSnapInfo{up}
  1211  	}
  1212  
  1213  	tr := config.NewTransaction(st)
  1214  	checkDiskSpaceRefresh, err := features.Flag(tr, features.CheckDiskSpaceRefresh)
  1215  	if err != nil && !config.IsNoOption(err) {
  1216  		return nil, nil, err
  1217  	}
  1218  	if checkDiskSpaceRefresh {
  1219  		// check if there is enough disk space for requested snap and its
  1220  		// prerequisites.
  1221  		totalSize, err := installSize(st, toUpdate, userID)
  1222  		if err != nil {
  1223  			return nil, nil, err
  1224  		}
  1225  		requiredSpace := safetyMarginDiskSpace(totalSize)
  1226  		path := dirs.SnapdStateDir(dirs.GlobalRootDir)
  1227  		if err := osutilCheckFreeSpace(path, requiredSpace); err != nil {
  1228  			snaps := make([]string, len(updates))
  1229  			for i, up := range updates {
  1230  				snaps[i] = up.InstanceName()
  1231  			}
  1232  			if _, ok := err.(*osutil.NotEnoughDiskSpaceError); ok {
  1233  				return nil, nil, &InsufficientSpaceError{
  1234  					Path:       path,
  1235  					Snaps:      snaps,
  1236  					ChangeKind: "refresh",
  1237  				}
  1238  			}
  1239  			return nil, nil, err
  1240  		}
  1241  	}
  1242  
  1243  	updated, tasksets, err := doUpdate(ctx, st, names, toUpdate, params, userID, flags, deviceCtx, fromChange)
  1244  	if err != nil {
  1245  		return nil, nil, err
  1246  	}
  1247  	tasksets = finalizeUpdate(st, tasksets, len(updates) > 0, updated, userID, flags)
  1248  	return updated, tasksets, nil
  1249  }
  1250  
  1251  func doUpdate(ctx context.Context, st *state.State, names []string, updates []minimalInstallInfo, params updateParamsFunc, userID int, globalFlags *Flags, deviceCtx DeviceContext, fromChange string) ([]string, []*state.TaskSet, error) {
  1252  	if globalFlags == nil {
  1253  		globalFlags = &Flags{}
  1254  	}
  1255  
  1256  	tasksets := make([]*state.TaskSet, 0, len(updates)+2) // 1 for auto-aliases, 1 for re-refresh
  1257  
  1258  	refreshAll := len(names) == 0
  1259  	var nameSet map[string]bool
  1260  	if len(names) != 0 {
  1261  		nameSet = make(map[string]bool, len(names))
  1262  		for _, name := range names {
  1263  			nameSet[name] = true
  1264  		}
  1265  	}
  1266  
  1267  	newAutoAliases, mustPruneAutoAliases, transferTargets, err := autoAliasesUpdate(st, names, updates)
  1268  	if err != nil {
  1269  		return nil, nil, err
  1270  	}
  1271  
  1272  	reportUpdated := make(map[string]bool, len(updates))
  1273  	var pruningAutoAliasesTs *state.TaskSet
  1274  
  1275  	if len(mustPruneAutoAliases) != 0 {
  1276  		var err error
  1277  		pruningAutoAliasesTs, err = applyAutoAliasesDelta(st, mustPruneAutoAliases, "prune", refreshAll, fromChange, func(snapName string, _ *state.TaskSet) {
  1278  			if nameSet[snapName] {
  1279  				reportUpdated[snapName] = true
  1280  			}
  1281  		})
  1282  		if err != nil {
  1283  			return nil, nil, err
  1284  		}
  1285  		tasksets = append(tasksets, pruningAutoAliasesTs)
  1286  	}
  1287  
  1288  	// wait for the auto-alias prune tasks as needed
  1289  	scheduleUpdate := func(snapName string, ts *state.TaskSet) {
  1290  		if pruningAutoAliasesTs != nil && (mustPruneAutoAliases[snapName] != nil || transferTargets[snapName]) {
  1291  			ts.WaitAll(pruningAutoAliasesTs)
  1292  		}
  1293  		reportUpdated[snapName] = true
  1294  	}
  1295  
  1296  	// first snapd, core, bases, then rest
  1297  	sort.Stable(byType(updates))
  1298  	prereqs := make(map[string]*state.TaskSet)
  1299  	waitPrereq := func(ts *state.TaskSet, prereqName string) {
  1300  		preTs := prereqs[prereqName]
  1301  		if preTs != nil {
  1302  			ts.WaitAll(preTs)
  1303  		}
  1304  	}
  1305  	var kernelTs, gadgetTs *state.TaskSet
  1306  
  1307  	// updates is sorted by kind so this will process first core
  1308  	// and bases and then other snaps
  1309  	for _, update := range updates {
  1310  		snapsup, snapst, err := update.(readyUpdateInfo).SnapSetupForUpdate(st, params, userID, globalFlags)
  1311  		if err != nil {
  1312  			if refreshAll {
  1313  				logger.Noticef("cannot update %q: %v", update.InstanceName(), err)
  1314  				continue
  1315  			}
  1316  			return nil, nil, err
  1317  		}
  1318  
  1319  		ts, err := doInstall(st, snapst, snapsup, 0, fromChange, inUseFor(deviceCtx))
  1320  		if err != nil {
  1321  			if refreshAll {
  1322  				// doing "refresh all", just skip this snap
  1323  				logger.Noticef("cannot refresh snap %q: %v", update.InstanceName(), err)
  1324  				continue
  1325  			}
  1326  			return nil, nil, err
  1327  		}
  1328  		ts.JoinLane(st.NewLane())
  1329  
  1330  		// because of the sorting of updates we fill prereqs
  1331  		// first (if branch) and only then use it to setup
  1332  		// waits (else branch)
  1333  		if t := update.Type(); t == snap.TypeOS || t == snap.TypeBase || t == snap.TypeSnapd {
  1334  			// prereq types come first in updates, we
  1335  			// also assume bases don't have hooks, otherwise
  1336  			// they would need to wait on core or snapd
  1337  			prereqs[update.InstanceName()] = ts
  1338  		} else {
  1339  			// prereqs were processed already, wait for
  1340  			// them as necessary for the other kind of
  1341  			// snaps
  1342  			waitPrereq(ts, defaultCoreSnapName)
  1343  			waitPrereq(ts, "snapd")
  1344  			if update.SnapBase() != "" {
  1345  				waitPrereq(ts, update.SnapBase())
  1346  			}
  1347  		}
  1348  		// keep track of kernel/gadget udpates
  1349  		switch update.Type() {
  1350  		case snap.TypeKernel:
  1351  			kernelTs = ts
  1352  		case snap.TypeGadget:
  1353  			gadgetTs = ts
  1354  		}
  1355  
  1356  		scheduleUpdate(update.InstanceName(), ts)
  1357  		tasksets = append(tasksets, ts)
  1358  	}
  1359  	// Kernel must wait for gadget because the gadget may define
  1360  	// new "$kernel:refs". Sorting the other way is impossible
  1361  	// because a kernel with new kernel-assets would never refresh
  1362  	// because the matching gadget could never get installed
  1363  	// because the gadget always waits for the kernel and if the
  1364  	// kernel aborts the wait tasks (the gadget) is put on "Hold".
  1365  	if kernelTs != nil && gadgetTs != nil {
  1366  		kernelTs.WaitAll(gadgetTs)
  1367  	}
  1368  
  1369  	if len(newAutoAliases) != 0 {
  1370  		addAutoAliasesTs, err := applyAutoAliasesDelta(st, newAutoAliases, "refresh", refreshAll, fromChange, scheduleUpdate)
  1371  		if err != nil {
  1372  			return nil, nil, err
  1373  		}
  1374  		tasksets = append(tasksets, addAutoAliasesTs)
  1375  	}
  1376  
  1377  	updated := make([]string, 0, len(reportUpdated))
  1378  	for name := range reportUpdated {
  1379  		updated = append(updated, name)
  1380  	}
  1381  
  1382  	return updated, tasksets, nil
  1383  }
  1384  
  1385  func finalizeUpdate(st *state.State, tasksets []*state.TaskSet, hasUpdates bool, updated []string, userID int, globalFlags *Flags) []*state.TaskSet {
  1386  	if hasUpdates && !globalFlags.NoReRefresh {
  1387  		// re-refresh will check the lanes to decide what to
  1388  		// _actually_ re-refresh, but it'll be a subset of updated
  1389  		// (and equal to updated if nothing goes wrong)
  1390  		sort.Strings(updated)
  1391  		rerefresh := st.NewTask("check-rerefresh", fmt.Sprintf("Handling re-refresh of %s as needed", strutil.Quoted(updated)))
  1392  		rerefresh.Set("rerefresh-setup", reRefreshSetup{
  1393  			UserID: userID,
  1394  			Flags:  globalFlags,
  1395  		})
  1396  		tasksets = append(tasksets, state.NewTaskSet(rerefresh))
  1397  	}
  1398  	return tasksets
  1399  }
  1400  
  1401  func applyAutoAliasesDelta(st *state.State, delta map[string][]string, op string, refreshAll bool, fromChange string, linkTs func(instanceName string, ts *state.TaskSet)) (*state.TaskSet, error) {
  1402  	applyTs := state.NewTaskSet()
  1403  	kind := "refresh-aliases"
  1404  	msg := i18n.G("Refresh aliases for snap %q")
  1405  	if op == "prune" {
  1406  		kind = "prune-auto-aliases"
  1407  		msg = i18n.G("Prune automatic aliases for snap %q")
  1408  	}
  1409  	for instanceName, aliases := range delta {
  1410  		if err := checkChangeConflictIgnoringOneChange(st, instanceName, nil, fromChange); err != nil {
  1411  			if refreshAll {
  1412  				// doing "refresh all", just skip this snap
  1413  				logger.Noticef("cannot %s automatic aliases for snap %q: %v", op, instanceName, err)
  1414  				continue
  1415  			}
  1416  			return nil, err
  1417  		}
  1418  
  1419  		snapName, instanceKey := snap.SplitInstanceName(instanceName)
  1420  		snapsup := &SnapSetup{
  1421  			SideInfo:    &snap.SideInfo{RealName: snapName},
  1422  			InstanceKey: instanceKey,
  1423  		}
  1424  		alias := st.NewTask(kind, fmt.Sprintf(msg, snapsup.InstanceName()))
  1425  		alias.Set("snap-setup", &snapsup)
  1426  		if op == "prune" {
  1427  			alias.Set("aliases", aliases)
  1428  		}
  1429  		ts := state.NewTaskSet(alias)
  1430  		linkTs(instanceName, ts)
  1431  		applyTs.AddAll(ts)
  1432  	}
  1433  	return applyTs, nil
  1434  }
  1435  
  1436  func autoAliasesUpdate(st *state.State, names []string, updates []minimalInstallInfo) (changed map[string][]string, mustPrune map[string][]string, transferTargets map[string]bool, err error) {
  1437  	changed, dropped, err := autoAliasesDelta(st, nil)
  1438  	if err != nil {
  1439  		if len(names) != 0 {
  1440  			// not "refresh all", error
  1441  			return nil, nil, nil, err
  1442  		}
  1443  		// log and continue
  1444  		logger.Noticef("cannot find the delta for automatic aliases for some snaps: %v", err)
  1445  	}
  1446  
  1447  	refreshAll := len(names) == 0
  1448  
  1449  	// dropped alias -> snapName
  1450  	droppedAliases := make(map[string][]string, len(dropped))
  1451  	for instanceName, aliases := range dropped {
  1452  		for _, alias := range aliases {
  1453  			droppedAliases[alias] = append(droppedAliases[alias], instanceName)
  1454  		}
  1455  	}
  1456  
  1457  	// filter changed considering only names if set:
  1458  	// we add auto-aliases only for mentioned snaps
  1459  	if !refreshAll && len(changed) != 0 {
  1460  		filteredChanged := make(map[string][]string, len(changed))
  1461  		for _, name := range names {
  1462  			if changed[name] != nil {
  1463  				filteredChanged[name] = changed[name]
  1464  			}
  1465  		}
  1466  		changed = filteredChanged
  1467  	}
  1468  
  1469  	// mark snaps that are sources or target of transfers
  1470  	transferSources := make(map[string]bool, len(dropped))
  1471  	transferTargets = make(map[string]bool, len(changed))
  1472  	for instanceName, aliases := range changed {
  1473  		for _, alias := range aliases {
  1474  			if sources := droppedAliases[alias]; len(sources) != 0 {
  1475  				transferTargets[instanceName] = true
  1476  				for _, source := range sources {
  1477  					transferSources[source] = true
  1478  				}
  1479  			}
  1480  		}
  1481  	}
  1482  
  1483  	// snaps with updates
  1484  	updating := make(map[string]bool, len(updates))
  1485  	for _, info := range updates {
  1486  		updating[info.InstanceName()] = true
  1487  	}
  1488  
  1489  	// add explicitly auto-aliases only for snaps that are not updated
  1490  	for instanceName := range changed {
  1491  		if updating[instanceName] {
  1492  			delete(changed, instanceName)
  1493  		}
  1494  	}
  1495  
  1496  	// prune explicitly auto-aliases only for snaps that are mentioned
  1497  	// and not updated OR the source of transfers
  1498  	mustPrune = make(map[string][]string, len(dropped))
  1499  	for instanceName := range transferSources {
  1500  		mustPrune[instanceName] = dropped[instanceName]
  1501  	}
  1502  	if refreshAll {
  1503  		for instanceName, aliases := range dropped {
  1504  			if !updating[instanceName] {
  1505  				mustPrune[instanceName] = aliases
  1506  			}
  1507  		}
  1508  	} else {
  1509  		for _, name := range names {
  1510  			if !updating[name] && dropped[name] != nil {
  1511  				mustPrune[name] = dropped[name]
  1512  			}
  1513  		}
  1514  	}
  1515  
  1516  	return changed, mustPrune, transferTargets, nil
  1517  }
  1518  
  1519  // resolveChannel returns the effective channel to use, based on the requested
  1520  // channel and constrains set by device model, or an error if switching to
  1521  // requested channel is forbidden.
  1522  func resolveChannel(st *state.State, snapName, oldChannel, newChannel string, deviceCtx DeviceContext) (effectiveChannel string, err error) {
  1523  	if newChannel == "" {
  1524  		return "", nil
  1525  	}
  1526  
  1527  	// ensure we do not switch away from the kernel-track in the model
  1528  	model := deviceCtx.Model()
  1529  
  1530  	var pinnedTrack, which string
  1531  	if snapName == model.Kernel() && model.KernelTrack() != "" {
  1532  		pinnedTrack, which = model.KernelTrack(), "kernel"
  1533  	}
  1534  	if snapName == model.Gadget() && model.GadgetTrack() != "" {
  1535  		pinnedTrack, which = model.GadgetTrack(), "gadget"
  1536  	}
  1537  
  1538  	if pinnedTrack == "" {
  1539  		// no pinned track
  1540  		return channel.Resolve(oldChannel, newChannel)
  1541  	}
  1542  
  1543  	// channel name is valid and consist of risk level or
  1544  	// risk/branch only, do the right thing and default to risk (or
  1545  	// risk/branch) within the pinned track
  1546  	resChannel, err := channel.ResolvePinned(pinnedTrack, newChannel)
  1547  	if err == channel.ErrPinnedTrackSwitch {
  1548  		// switching to a different track is not allowed
  1549  		return "", fmt.Errorf("cannot switch from %s track %q as specified for the (device) model to %q", which, pinnedTrack, newChannel)
  1550  
  1551  	}
  1552  	if err != nil {
  1553  		return "", err
  1554  	}
  1555  	return resChannel, nil
  1556  }
  1557  
  1558  var errRevisionSwitch = errors.New("cannot switch revision")
  1559  
  1560  func switchSummary(snap, chanFrom, chanTo, cohFrom, cohTo string) string {
  1561  	if cohFrom != cohTo {
  1562  		if cohTo == "" {
  1563  			// leave cohort
  1564  			if chanFrom == chanTo {
  1565  				return fmt.Sprintf(i18n.G("Switch snap %q away from cohort %q"),
  1566  					snap, strutil.ElliptLeft(cohFrom, 10))
  1567  			}
  1568  			if chanFrom == "" {
  1569  				return fmt.Sprintf(i18n.G("Switch snap %q to channel %q and away from cohort %q"),
  1570  					snap, chanTo, strutil.ElliptLeft(cohFrom, 10),
  1571  				)
  1572  			}
  1573  			return fmt.Sprintf(i18n.G("Switch snap %q from channel %q to %q and away from cohort %q"),
  1574  				snap, chanFrom, chanTo, strutil.ElliptLeft(cohFrom, 10),
  1575  			)
  1576  		}
  1577  		if cohFrom == "" {
  1578  			// moving into a cohort
  1579  			if chanFrom == chanTo {
  1580  				return fmt.Sprintf(i18n.G("Switch snap %q from no cohort to %q"),
  1581  					snap, strutil.ElliptLeft(cohTo, 10))
  1582  			}
  1583  			if chanFrom == "" {
  1584  				return fmt.Sprintf(i18n.G("Switch snap %q to channel %q and from no cohort to %q"),
  1585  					snap, chanTo, strutil.ElliptLeft(cohTo, 10),
  1586  				)
  1587  			}
  1588  			// chanTo == "" is not interesting
  1589  			return fmt.Sprintf(i18n.G("Switch snap %q from channel %q to %q and from no cohort to %q"),
  1590  				snap, chanFrom, chanTo, strutil.ElliptLeft(cohTo, 10),
  1591  			)
  1592  		}
  1593  		if chanFrom == chanTo {
  1594  			return fmt.Sprintf(i18n.G("Switch snap %q from cohort %q to %q"),
  1595  				snap, strutil.ElliptLeft(cohFrom, 10), strutil.ElliptLeft(cohTo, 10))
  1596  		}
  1597  		if chanFrom == "" {
  1598  			return fmt.Sprintf(i18n.G("Switch snap %q to channel %q and from cohort %q to %q"),
  1599  				snap, chanTo, strutil.ElliptLeft(cohFrom, 10), strutil.ElliptLeft(cohTo, 10),
  1600  			)
  1601  		}
  1602  		return fmt.Sprintf(i18n.G("Switch snap %q from channel %q to %q and from cohort %q to %q"),
  1603  			snap, chanFrom, chanTo,
  1604  			strutil.ElliptLeft(cohFrom, 10), strutil.ElliptLeft(cohTo, 10),
  1605  		)
  1606  	}
  1607  
  1608  	if chanFrom == "" {
  1609  		return fmt.Sprintf(i18n.G("Switch snap %q to channel %q"),
  1610  			snap, chanTo)
  1611  	}
  1612  	if chanFrom != chanTo {
  1613  		return fmt.Sprintf(i18n.G("Switch snap %q from channel %q to %q"),
  1614  			snap, chanFrom, chanTo)
  1615  	}
  1616  	// a no-change switch is accepted for idempotency
  1617  	return "No change switch (no-op)"
  1618  }
  1619  
  1620  // Switch switches a snap to a new channel and/or cohort
  1621  func Switch(st *state.State, name string, opts *RevisionOptions) (*state.TaskSet, error) {
  1622  	if opts == nil {
  1623  		opts = &RevisionOptions{}
  1624  	}
  1625  	if !opts.Revision.Unset() {
  1626  		return nil, errRevisionSwitch
  1627  	}
  1628  	var snapst SnapState
  1629  	err := Get(st, name, &snapst)
  1630  	if err != nil && err != state.ErrNoState {
  1631  		return nil, err
  1632  	}
  1633  	if !snapst.IsInstalled() {
  1634  		return nil, &snap.NotInstalledError{Snap: name}
  1635  	}
  1636  
  1637  	if err := CheckChangeConflict(st, name, nil); err != nil {
  1638  		return nil, err
  1639  	}
  1640  
  1641  	deviceCtx, err := DeviceCtxFromState(st, nil)
  1642  	if err != nil {
  1643  		return nil, err
  1644  	}
  1645  
  1646  	opts.Channel, err = resolveChannel(st, name, snapst.TrackingChannel, opts.Channel, deviceCtx)
  1647  	if err != nil {
  1648  		return nil, err
  1649  	}
  1650  
  1651  	snapsup := &SnapSetup{
  1652  		SideInfo:    snapst.CurrentSideInfo(),
  1653  		InstanceKey: snapst.InstanceKey,
  1654  		// set the from state (i.e. no change), they are overridden from opts as needed below
  1655  		CohortKey: snapst.CohortKey,
  1656  		Channel:   snapst.TrackingChannel,
  1657  	}
  1658  
  1659  	if opts.Channel != "" {
  1660  		snapsup.Channel = opts.Channel
  1661  	}
  1662  	if opts.CohortKey != "" {
  1663  		snapsup.CohortKey = opts.CohortKey
  1664  	}
  1665  	if opts.LeaveCohort {
  1666  		snapsup.CohortKey = ""
  1667  	}
  1668  
  1669  	summary := switchSummary(snapsup.InstanceName(), snapst.TrackingChannel, snapsup.Channel, snapst.CohortKey, snapsup.CohortKey)
  1670  	switchSnap := st.NewTask("switch-snap", summary)
  1671  	switchSnap.Set("snap-setup", &snapsup)
  1672  
  1673  	return state.NewTaskSet(switchSnap), nil
  1674  }
  1675  
  1676  // RevisionOptions control the selection of a snap revision.
  1677  type RevisionOptions struct {
  1678  	Channel     string
  1679  	Revision    snap.Revision
  1680  	CohortKey   string
  1681  	LeaveCohort bool
  1682  }
  1683  
  1684  // Update initiates a change updating a snap.
  1685  // Note that the state must be locked by the caller.
  1686  //
  1687  // The returned TaskSet will contain a DownloadAndChecksDoneEdge.
  1688  func Update(st *state.State, name string, opts *RevisionOptions, userID int, flags Flags) (*state.TaskSet, error) {
  1689  	return UpdateWithDeviceContext(st, name, opts, userID, flags, nil, "")
  1690  }
  1691  
  1692  // UpdateWithDeviceContext initiates a change updating a snap.
  1693  // It will query for the snap with the given deviceCtx.
  1694  // Note that the state must be locked by the caller.
  1695  //
  1696  // The returned TaskSet will contain a DownloadAndChecksDoneEdge.
  1697  func UpdateWithDeviceContext(st *state.State, name string, opts *RevisionOptions, userID int, flags Flags, deviceCtx DeviceContext, fromChange string) (*state.TaskSet, error) {
  1698  	if opts == nil {
  1699  		opts = &RevisionOptions{}
  1700  	}
  1701  	var snapst SnapState
  1702  	err := Get(st, name, &snapst)
  1703  	if err != nil && err != state.ErrNoState {
  1704  		return nil, err
  1705  	}
  1706  	if !snapst.IsInstalled() {
  1707  		return nil, &snap.NotInstalledError{Snap: name}
  1708  	}
  1709  
  1710  	// FIXME: snaps that are not active are skipped for now
  1711  	//        until we know what we want to do
  1712  	if !snapst.Active {
  1713  		return nil, fmt.Errorf("refreshing disabled snap %q not supported", name)
  1714  	}
  1715  
  1716  	// need to have a model set before trying to talk the store
  1717  	deviceCtx, err = DevicePastSeeding(st, deviceCtx)
  1718  	if err != nil {
  1719  		return nil, err
  1720  	}
  1721  
  1722  	opts.Channel, err = resolveChannel(st, name, snapst.TrackingChannel, opts.Channel, deviceCtx)
  1723  	if err != nil {
  1724  		return nil, err
  1725  	}
  1726  
  1727  	if opts.Channel == "" {
  1728  		// default to tracking the same channel
  1729  		opts.Channel = snapst.TrackingChannel
  1730  	}
  1731  	if opts.CohortKey == "" {
  1732  		// default to being in the same cohort
  1733  		opts.CohortKey = snapst.CohortKey
  1734  	}
  1735  	if opts.LeaveCohort {
  1736  		opts.CohortKey = ""
  1737  	}
  1738  
  1739  	// TODO: make flags be per revision to avoid this logic (that
  1740  	//       leaves corner cases all over the place)
  1741  	if !(flags.JailMode || flags.DevMode) {
  1742  		flags.Classic = flags.Classic || snapst.Flags.Classic
  1743  	}
  1744  
  1745  	var updates []*snap.Info
  1746  	info, infoErr := infoForUpdate(st, &snapst, name, opts, userID, flags, deviceCtx)
  1747  	switch infoErr {
  1748  	case nil:
  1749  		updates = append(updates, info)
  1750  	case store.ErrNoUpdateAvailable:
  1751  		// there may be some new auto-aliases
  1752  	default:
  1753  		return nil, infoErr
  1754  	}
  1755  
  1756  	toUpdate := make([]minimalInstallInfo, len(updates))
  1757  	for i, up := range updates {
  1758  		toUpdate[i] = installSnapInfo{up}
  1759  	}
  1760  
  1761  	tr := config.NewTransaction(st)
  1762  	checkDiskSpaceRefresh, err := features.Flag(tr, features.CheckDiskSpaceRefresh)
  1763  	if err != nil && !config.IsNoOption(err) {
  1764  		return nil, err
  1765  	}
  1766  	if checkDiskSpaceRefresh {
  1767  		// check if there is enough disk space for requested snap and its
  1768  		// prerequisites.
  1769  		totalSize, err := installSize(st, toUpdate, userID)
  1770  		if err != nil {
  1771  			return nil, err
  1772  		}
  1773  		requiredSpace := safetyMarginDiskSpace(totalSize)
  1774  		path := dirs.SnapdStateDir(dirs.GlobalRootDir)
  1775  		if err := osutilCheckFreeSpace(path, requiredSpace); err != nil {
  1776  			snaps := make([]string, len(updates))
  1777  			for i, up := range updates {
  1778  				snaps[i] = up.InstanceName()
  1779  			}
  1780  			if _, ok := err.(*osutil.NotEnoughDiskSpaceError); ok {
  1781  				return nil, &InsufficientSpaceError{
  1782  					Path:       path,
  1783  					Snaps:      snaps,
  1784  					ChangeKind: "refresh",
  1785  				}
  1786  			}
  1787  			return nil, err
  1788  		}
  1789  	}
  1790  
  1791  	params := func(update *snap.Info) (*RevisionOptions, Flags, *SnapState) {
  1792  		return opts, flags, &snapst
  1793  	}
  1794  
  1795  	_, tts, err := doUpdate(context.TODO(), st, []string{name}, toUpdate, params, userID, &flags, deviceCtx, fromChange)
  1796  	if err != nil {
  1797  		return nil, err
  1798  	}
  1799  
  1800  	// see if we need to switch the channel or cohort, or toggle ignore-validation
  1801  	switchChannel := snapst.TrackingChannel != opts.Channel
  1802  	switchCohortKey := snapst.CohortKey != opts.CohortKey
  1803  	toggleIgnoreValidation := snapst.IgnoreValidation != flags.IgnoreValidation
  1804  	if infoErr == store.ErrNoUpdateAvailable && (switchChannel || switchCohortKey || toggleIgnoreValidation) {
  1805  		if err := checkChangeConflictIgnoringOneChange(st, name, nil, fromChange); err != nil {
  1806  			return nil, err
  1807  		}
  1808  
  1809  		snapsup := &SnapSetup{
  1810  			SideInfo:    snapst.CurrentSideInfo(),
  1811  			Flags:       snapst.Flags.ForSnapSetup(),
  1812  			InstanceKey: snapst.InstanceKey,
  1813  			CohortKey:   opts.CohortKey,
  1814  		}
  1815  
  1816  		if switchChannel || switchCohortKey {
  1817  			// update the tracked channel and cohort
  1818  			snapsup.Channel = opts.Channel
  1819  			snapsup.CohortKey = opts.CohortKey
  1820  			// Update the current snap channel as well. This ensures that
  1821  			// the UI displays the right values.
  1822  			snapsup.SideInfo.Channel = opts.Channel
  1823  
  1824  			summary := switchSummary(snapsup.InstanceName(), snapst.TrackingChannel, opts.Channel, snapst.CohortKey, opts.CohortKey)
  1825  			switchSnap := st.NewTask("switch-snap-channel", summary)
  1826  			switchSnap.Set("snap-setup", &snapsup)
  1827  
  1828  			switchSnapTs := state.NewTaskSet(switchSnap)
  1829  			for _, ts := range tts {
  1830  				switchSnapTs.WaitAll(ts)
  1831  			}
  1832  			tts = append(tts, switchSnapTs)
  1833  		}
  1834  
  1835  		if toggleIgnoreValidation {
  1836  			snapsup.IgnoreValidation = flags.IgnoreValidation
  1837  			toggle := st.NewTask("toggle-snap-flags", fmt.Sprintf(i18n.G("Toggle snap %q flags"), snapsup.InstanceName()))
  1838  			toggle.Set("snap-setup", &snapsup)
  1839  
  1840  			toggleTs := state.NewTaskSet(toggle)
  1841  			for _, ts := range tts {
  1842  				toggleTs.WaitAll(ts)
  1843  			}
  1844  			tts = append(tts, toggleTs)
  1845  		}
  1846  	}
  1847  
  1848  	if len(tts) == 0 && len(updates) == 0 {
  1849  		// really nothing to do, return the original no-update-available error
  1850  		return nil, infoErr
  1851  	}
  1852  
  1853  	tts = finalizeUpdate(st, tts, len(updates) > 0, []string{name}, userID, &flags)
  1854  
  1855  	flat := state.NewTaskSet()
  1856  	for _, ts := range tts {
  1857  		// The tasksets we get from "doUpdate" contain important
  1858  		// "TaskEdge" information that is needed for "Remodel".
  1859  		// To preserve those we need to use "AddAllWithEdges()".
  1860  		if err := flat.AddAllWithEdges(ts); err != nil {
  1861  			return nil, err
  1862  		}
  1863  	}
  1864  	return flat, nil
  1865  }
  1866  
  1867  func infoForUpdate(st *state.State, snapst *SnapState, name string, opts *RevisionOptions, userID int, flags Flags, deviceCtx DeviceContext) (*snap.Info, error) {
  1868  	if opts.Revision.Unset() {
  1869  		// good ol' refresh
  1870  		info, err := updateInfo(st, snapst, opts, userID, flags, deviceCtx)
  1871  		if err != nil {
  1872  			return nil, err
  1873  		}
  1874  		if ValidateRefreshes != nil && !flags.IgnoreValidation {
  1875  			_, err := ValidateRefreshes(st, []*snap.Info{info}, nil, userID, deviceCtx)
  1876  			if err != nil {
  1877  				return nil, err
  1878  			}
  1879  		}
  1880  		return info, nil
  1881  	}
  1882  	var sideInfo *snap.SideInfo
  1883  	for _, si := range snapst.Sequence {
  1884  		if si.Revision == opts.Revision {
  1885  			sideInfo = si
  1886  			break
  1887  		}
  1888  	}
  1889  	if sideInfo == nil {
  1890  		// refresh from given revision from store
  1891  		return updateToRevisionInfo(st, snapst, opts.Revision, userID, deviceCtx)
  1892  	}
  1893  
  1894  	// refresh-to-local, this assumes the snap revision is mounted
  1895  	return readInfo(name, sideInfo, errorOnBroken)
  1896  }
  1897  
  1898  // AutoRefreshAssertions allows to hook fetching of important assertions
  1899  // into the Autorefresh function.
  1900  var AutoRefreshAssertions func(st *state.State, userID int) error
  1901  
  1902  // AutoRefresh is the wrapper that will do a refresh of all the installed
  1903  // snaps on the system. In addition to that it will also refresh important
  1904  // assertions.
  1905  func AutoRefresh(ctx context.Context, st *state.State) ([]string, []*state.TaskSet, error) {
  1906  	userID := 0
  1907  
  1908  	if AutoRefreshAssertions != nil {
  1909  		if err := AutoRefreshAssertions(st, userID); err != nil {
  1910  			return nil, nil, err
  1911  		}
  1912  	}
  1913  
  1914  	tr := config.NewTransaction(st)
  1915  	gateAutoRefreshHook, err := features.Flag(tr, features.GateAutoRefreshHook)
  1916  	if err != nil && !config.IsNoOption(err) {
  1917  		return nil, nil, err
  1918  	}
  1919  	if !gateAutoRefreshHook {
  1920  		// old-style refresh (gate-auto-refresh-hook feature disabled)
  1921  		return UpdateMany(ctx, st, nil, userID, &Flags{IsAutoRefresh: true})
  1922  	}
  1923  
  1924  	// TODO: rename to autoRefreshTasks when old auto refresh logic gets removed.
  1925  	return autoRefreshPhase1(ctx, st, "")
  1926  }
  1927  
  1928  // autoRefreshPhase1 creates gate-auto-refresh hooks and conditional-auto-refresh
  1929  // task that initiates actual refresh. forGatingSnap is optional and limits auto-refresh
  1930  // to the snaps affecting the given snap only; it defaults to all snaps if nil.
  1931  // The state needs to be locked by the caller.
  1932  func autoRefreshPhase1(ctx context.Context, st *state.State, forGatingSnap string) ([]string, []*state.TaskSet, error) {
  1933  	user, err := userFromUserID(st, 0)
  1934  	if err != nil {
  1935  		return nil, nil, err
  1936  	}
  1937  
  1938  	refreshOpts := &store.RefreshOptions{IsAutoRefresh: true}
  1939  	// XXX: should we skip refreshCandidates if forGatingSnap isn't empty (meaning we're handling proceed from a snap)?
  1940  	candidates, snapstateByInstance, ignoreValidationByInstanceName, err := refreshCandidates(ctx, st, nil, user, refreshOpts)
  1941  	if err != nil {
  1942  		// XXX: should we reset "refresh-candidates" to nil in state for some types
  1943  		// of errors?
  1944  		return nil, nil, err
  1945  	}
  1946  	deviceCtx, err := DeviceCtxFromState(st, nil)
  1947  	if err != nil {
  1948  		return nil, nil, err
  1949  	}
  1950  	hints, err := refreshHintsFromCandidates(st, candidates, ignoreValidationByInstanceName, deviceCtx)
  1951  	if err != nil {
  1952  		return nil, nil, err
  1953  	}
  1954  	st.Set("refresh-candidates", hints)
  1955  
  1956  	// prune affecting snaps that are not in refresh candidates from hold state.
  1957  	if err := pruneGating(st, hints); err != nil {
  1958  		return nil, nil, err
  1959  	}
  1960  
  1961  	updates := make([]string, 0, len(hints))
  1962  
  1963  	// check conflicts
  1964  	fromChange := ""
  1965  	for _, up := range candidates {
  1966  		if _, ok := hints[up.InstanceName()]; !ok {
  1967  			// filtered out by refreshHintsFromCandidates
  1968  			continue
  1969  		}
  1970  
  1971  		snapst := snapstateByInstance[up.InstanceName()]
  1972  		if err := checkChangeConflictIgnoringOneChange(st, up.InstanceName(), snapst, fromChange); err != nil {
  1973  			logger.Noticef("cannot refresh snap %q: %v", up.InstanceName(), err)
  1974  		} else {
  1975  			updates = append(updates, up.InstanceName())
  1976  		}
  1977  	}
  1978  
  1979  	if forGatingSnap != "" {
  1980  		var gatingSnapHasUpdate bool
  1981  		for _, up := range updates {
  1982  			if up == forGatingSnap {
  1983  				gatingSnapHasUpdate = true
  1984  				break
  1985  			}
  1986  		}
  1987  		if !gatingSnapHasUpdate {
  1988  			return nil, nil, nil
  1989  		}
  1990  	}
  1991  
  1992  	if len(updates) == 0 {
  1993  		return nil, nil, nil
  1994  	}
  1995  
  1996  	// all snaps in updates are now considered to be operated on and should
  1997  	// provoke conflicts until updated or until we know (after running
  1998  	// gate-auto-refresh hooks) that some are not going to be updated
  1999  	// and can stop conflicting.
  2000  
  2001  	affectedSnaps, err := affectedByRefresh(st, updates)
  2002  	if err != nil {
  2003  		return nil, nil, err
  2004  	}
  2005  
  2006  	// only used if forGatingSnap != ""
  2007  	var snapsAffectingGatingSnap map[string]bool
  2008  
  2009  	// if specific gating snap was given, drop other affected snaps unless
  2010  	// they are affected by same updates as forGatingSnap.
  2011  	if forGatingSnap != "" {
  2012  		snapsAffectingGatingSnap = affectedSnaps[forGatingSnap].AffectingSnaps
  2013  
  2014  		// check if there is an intersection between affecting snaps of this
  2015  		// forGatingSnap and other gating snaps. If so, we need to run
  2016  		// their gate-auto-refresh hooks as well.
  2017  		for gatingSnap, affectedInfo := range affectedSnaps {
  2018  			if gatingSnap == forGatingSnap {
  2019  				continue
  2020  			}
  2021  			var found bool
  2022  			for affectingSnap := range affectedInfo.AffectingSnaps {
  2023  				if snapsAffectingGatingSnap[affectingSnap] {
  2024  					found = true
  2025  					break
  2026  				}
  2027  			}
  2028  			if !found {
  2029  				delete(affectedSnaps, gatingSnap)
  2030  			}
  2031  		}
  2032  	}
  2033  
  2034  	var hooks *state.TaskSet
  2035  	if len(affectedSnaps) > 0 {
  2036  		affected := make([]string, 0, len(affectedSnaps))
  2037  		for snapName := range affectedSnaps {
  2038  			affected = append(affected, snapName)
  2039  		}
  2040  		sort.Strings(affected)
  2041  		hooks = createGateAutoRefreshHooks(st, affected)
  2042  	}
  2043  
  2044  	// gate-auto-refresh hooks, followed by conditional-auto-refresh task waiting
  2045  	// for all hooks.
  2046  	ar := st.NewTask("conditional-auto-refresh", "Run auto-refresh for ready snaps")
  2047  	tss := []*state.TaskSet{state.NewTaskSet(ar)}
  2048  	if hooks != nil {
  2049  		ar.WaitAll(hooks)
  2050  		tss = append(tss, hooks)
  2051  	}
  2052  
  2053  	// return all names as potentially getting updated even though some may be
  2054  	// held.
  2055  	names := make([]string, 0, len(updates))
  2056  	toUpdate := make(map[string]*refreshCandidate, len(updates))
  2057  	for _, up := range updates {
  2058  		// if specific gating snap was requested, filter out updates.
  2059  		if forGatingSnap != "" && forGatingSnap != up {
  2060  			if !snapsAffectingGatingSnap[up] {
  2061  				continue
  2062  			}
  2063  		}
  2064  		names = append(names, up)
  2065  		toUpdate[up] = hints[up]
  2066  	}
  2067  
  2068  	// store the list of snaps to update on the conditional-auto-refresh task
  2069  	// (this may be a subset of refresh-candidates due to conflicts).
  2070  	ar.Set("snaps", toUpdate)
  2071  
  2072  	// return all names as potentially getting updated even though some may be
  2073  	// held.
  2074  	sort.Strings(names)
  2075  	return names, tss, nil
  2076  }
  2077  
  2078  // autoRefreshPhase2 creates tasks for refreshing snaps from updates.
  2079  func autoRefreshPhase2(ctx context.Context, st *state.State, updates []*refreshCandidate, fromChange string) ([]*state.TaskSet, error) {
  2080  	flags := &Flags{IsAutoRefresh: true}
  2081  	userID := 0
  2082  
  2083  	deviceCtx, err := DeviceCtx(st, nil, nil)
  2084  	if err != nil {
  2085  		return nil, err
  2086  	}
  2087  
  2088  	toUpdate := make([]minimalInstallInfo, len(updates))
  2089  	for i, up := range updates {
  2090  		toUpdate[i] = up
  2091  	}
  2092  
  2093  	tr := config.NewTransaction(st)
  2094  	checkDiskSpaceRefresh, err := features.Flag(tr, features.CheckDiskSpaceRefresh)
  2095  	if err != nil && !config.IsNoOption(err) {
  2096  		return nil, err
  2097  	}
  2098  	if checkDiskSpaceRefresh {
  2099  		// check if there is enough disk space for requested snaps and their
  2100  		// prerequisites.
  2101  		totalSize, err := installSize(st, toUpdate, 0)
  2102  		if err != nil {
  2103  			return nil, err
  2104  		}
  2105  		requiredSpace := safetyMarginDiskSpace(totalSize)
  2106  		path := dirs.SnapdStateDir(dirs.GlobalRootDir)
  2107  		if err := osutilCheckFreeSpace(path, requiredSpace); err != nil {
  2108  			snaps := make([]string, len(updates))
  2109  			for i, up := range updates {
  2110  				snaps[i] = up.InstanceName()
  2111  			}
  2112  			if _, ok := err.(*osutil.NotEnoughDiskSpaceError); ok {
  2113  				return nil, &InsufficientSpaceError{
  2114  					Path:       path,
  2115  					Snaps:      snaps,
  2116  					ChangeKind: "refresh",
  2117  				}
  2118  			}
  2119  			return nil, err
  2120  		}
  2121  	}
  2122  
  2123  	updated, tasksets, err := doUpdate(ctx, st, nil, toUpdate, nil, userID, flags, deviceCtx, fromChange)
  2124  	if err != nil {
  2125  		return nil, err
  2126  	}
  2127  
  2128  	tasksets = finalizeUpdate(st, tasksets, len(updates) > 0, updated, userID, flags)
  2129  	return tasksets, nil
  2130  }
  2131  
  2132  // LinkNewBaseOrKernel will create prepare/link-snap tasks for a remodel
  2133  func LinkNewBaseOrKernel(st *state.State, name string) (*state.TaskSet, error) {
  2134  	var snapst SnapState
  2135  	err := Get(st, name, &snapst)
  2136  	if err == state.ErrNoState {
  2137  		return nil, &snap.NotInstalledError{Snap: name}
  2138  	}
  2139  	if err != nil {
  2140  		return nil, err
  2141  	}
  2142  
  2143  	if err := CheckChangeConflict(st, name, nil); err != nil {
  2144  		return nil, err
  2145  	}
  2146  
  2147  	info, err := snapst.CurrentInfo()
  2148  	if err != nil {
  2149  		return nil, err
  2150  	}
  2151  
  2152  	switch info.Type() {
  2153  	case snap.TypeOS, snap.TypeBase, snap.TypeKernel:
  2154  		// good
  2155  	default:
  2156  		// bad
  2157  		return nil, fmt.Errorf("cannot link type %v", info.Type())
  2158  	}
  2159  
  2160  	snapsup := &SnapSetup{
  2161  		SideInfo:    snapst.CurrentSideInfo(),
  2162  		Flags:       snapst.Flags.ForSnapSetup(),
  2163  		Type:        info.Type(),
  2164  		PlugsOnly:   len(info.Slots) == 0,
  2165  		InstanceKey: snapst.InstanceKey,
  2166  	}
  2167  
  2168  	prepareSnap := st.NewTask("prepare-snap", fmt.Sprintf(i18n.G("Prepare snap %q (%s) for remodel"), snapsup.InstanceName(), snapst.Current))
  2169  	prepareSnap.Set("snap-setup", &snapsup)
  2170  
  2171  	linkSnap := st.NewTask("link-snap", fmt.Sprintf(i18n.G("Make snap %q (%s) available to the system during remodel"), snapsup.InstanceName(), snapst.Current))
  2172  	linkSnap.Set("snap-setup-task", prepareSnap.ID())
  2173  	linkSnap.WaitFor(prepareSnap)
  2174  
  2175  	// we need this for remodel
  2176  	ts := state.NewTaskSet(prepareSnap, linkSnap)
  2177  	ts.MarkEdge(prepareSnap, DownloadAndChecksDoneEdge)
  2178  	return ts, nil
  2179  }
  2180  
  2181  // Enable sets a snap to the active state
  2182  func Enable(st *state.State, name string) (*state.TaskSet, error) {
  2183  	var snapst SnapState
  2184  	err := Get(st, name, &snapst)
  2185  	if err == state.ErrNoState {
  2186  		return nil, &snap.NotInstalledError{Snap: name}
  2187  	}
  2188  	if err != nil {
  2189  		return nil, err
  2190  	}
  2191  
  2192  	if snapst.Active {
  2193  		return nil, fmt.Errorf("snap %q already enabled", name)
  2194  	}
  2195  
  2196  	if err := CheckChangeConflict(st, name, nil); err != nil {
  2197  		return nil, err
  2198  	}
  2199  
  2200  	info, err := snapst.CurrentInfo()
  2201  	if err != nil {
  2202  		return nil, err
  2203  	}
  2204  
  2205  	snapsup := &SnapSetup{
  2206  		SideInfo:    snapst.CurrentSideInfo(),
  2207  		Flags:       snapst.Flags.ForSnapSetup(),
  2208  		Type:        info.Type(),
  2209  		PlugsOnly:   len(info.Slots) == 0,
  2210  		InstanceKey: snapst.InstanceKey,
  2211  	}
  2212  
  2213  	prepareSnap := st.NewTask("prepare-snap", fmt.Sprintf(i18n.G("Prepare snap %q (%s)"), snapsup.InstanceName(), snapst.Current))
  2214  	prepareSnap.Set("snap-setup", &snapsup)
  2215  
  2216  	setupProfiles := st.NewTask("setup-profiles", fmt.Sprintf(i18n.G("Setup snap %q (%s) security profiles"), snapsup.InstanceName(), snapst.Current))
  2217  	setupProfiles.Set("snap-setup-task", prepareSnap.ID())
  2218  	setupProfiles.WaitFor(prepareSnap)
  2219  
  2220  	linkSnap := st.NewTask("link-snap", fmt.Sprintf(i18n.G("Make snap %q (%s) available to the system"), snapsup.InstanceName(), snapst.Current))
  2221  	linkSnap.Set("snap-setup-task", prepareSnap.ID())
  2222  	linkSnap.WaitFor(setupProfiles)
  2223  
  2224  	// setup aliases
  2225  	setupAliases := st.NewTask("setup-aliases", fmt.Sprintf(i18n.G("Setup snap %q aliases"), snapsup.InstanceName()))
  2226  	setupAliases.Set("snap-setup-task", prepareSnap.ID())
  2227  	setupAliases.WaitFor(linkSnap)
  2228  
  2229  	startSnapServices := st.NewTask("start-snap-services", fmt.Sprintf(i18n.G("Start snap %q (%s) services"), snapsup.InstanceName(), snapst.Current))
  2230  	startSnapServices.Set("snap-setup-task", prepareSnap.ID())
  2231  	startSnapServices.WaitFor(setupAliases)
  2232  
  2233  	return state.NewTaskSet(prepareSnap, setupProfiles, linkSnap, setupAliases, startSnapServices), nil
  2234  }
  2235  
  2236  // Disable sets a snap to the inactive state
  2237  func Disable(st *state.State, name string) (*state.TaskSet, error) {
  2238  	var snapst SnapState
  2239  	err := Get(st, name, &snapst)
  2240  	if err == state.ErrNoState {
  2241  		return nil, &snap.NotInstalledError{Snap: name}
  2242  	}
  2243  	if err != nil {
  2244  		return nil, err
  2245  	}
  2246  	if !snapst.Active {
  2247  		return nil, fmt.Errorf("snap %q already disabled", name)
  2248  	}
  2249  
  2250  	info, err := Info(st, name, snapst.Current)
  2251  	if err != nil {
  2252  		return nil, err
  2253  	}
  2254  	if !canDisable(info) {
  2255  		return nil, fmt.Errorf("snap %q cannot be disabled", name)
  2256  	}
  2257  
  2258  	if err := CheckChangeConflict(st, name, nil); err != nil {
  2259  		return nil, err
  2260  	}
  2261  
  2262  	snapsup := &SnapSetup{
  2263  		SideInfo: &snap.SideInfo{
  2264  			RealName: snap.InstanceSnap(name),
  2265  			Revision: snapst.Current,
  2266  		},
  2267  		Type:        info.Type(),
  2268  		PlugsOnly:   len(info.Slots) == 0,
  2269  		InstanceKey: snapst.InstanceKey,
  2270  	}
  2271  
  2272  	stopSnapServices := st.NewTask("stop-snap-services", fmt.Sprintf(i18n.G("Stop snap %q (%s) services"), snapsup.InstanceName(), snapst.Current))
  2273  	stopSnapServices.Set("snap-setup", &snapsup)
  2274  	stopSnapServices.Set("stop-reason", snap.StopReasonDisable)
  2275  
  2276  	removeAliases := st.NewTask("remove-aliases", fmt.Sprintf(i18n.G("Remove aliases for snap %q"), snapsup.InstanceName()))
  2277  	removeAliases.Set("snap-setup-task", stopSnapServices.ID())
  2278  	removeAliases.WaitFor(stopSnapServices)
  2279  
  2280  	unlinkSnap := st.NewTask("unlink-snap", fmt.Sprintf(i18n.G("Make snap %q (%s) unavailable to the system"), snapsup.InstanceName(), snapst.Current))
  2281  	unlinkSnap.Set("snap-setup-task", stopSnapServices.ID())
  2282  	unlinkSnap.WaitFor(removeAliases)
  2283  
  2284  	removeProfiles := st.NewTask("remove-profiles", fmt.Sprintf(i18n.G("Remove security profiles of snap %q"), snapsup.InstanceName()))
  2285  	removeProfiles.Set("snap-setup-task", stopSnapServices.ID())
  2286  	removeProfiles.WaitFor(unlinkSnap)
  2287  
  2288  	return state.NewTaskSet(stopSnapServices, removeAliases, unlinkSnap, removeProfiles), nil
  2289  }
  2290  
  2291  // canDisable verifies that a snap can be deactivated.
  2292  func canDisable(si *snap.Info) bool {
  2293  	for _, importantSnapType := range []snap.Type{snap.TypeGadget, snap.TypeKernel, snap.TypeOS} {
  2294  		if importantSnapType == si.Type() {
  2295  			return false
  2296  		}
  2297  	}
  2298  
  2299  	return true
  2300  }
  2301  
  2302  // canRemove verifies that a snap can be removed.
  2303  func canRemove(st *state.State, si *snap.Info, snapst *SnapState, removeAll bool, deviceCtx DeviceContext) error {
  2304  	rev := snap.Revision{}
  2305  	if !removeAll {
  2306  		rev = si.Revision
  2307  	}
  2308  
  2309  	return PolicyFor(si.Type(), deviceCtx.Model()).CanRemove(st, snapst, rev, deviceCtx)
  2310  }
  2311  
  2312  // RemoveFlags are used to pass additional flags to the Remove operation.
  2313  type RemoveFlags struct {
  2314  	// Remove the snap without creating snapshot data
  2315  	Purge bool
  2316  }
  2317  
  2318  // Remove returns a set of tasks for removing snap.
  2319  // Note that the state must be locked by the caller.
  2320  func Remove(st *state.State, name string, revision snap.Revision, flags *RemoveFlags) (*state.TaskSet, error) {
  2321  	ts, snapshotSize, err := removeTasks(st, name, revision, flags)
  2322  	// removeTasks() checks check-disk-space-remove feature flag, so snapshotSize
  2323  	// will only be greater than 0 if the feature is enabled.
  2324  	if snapshotSize > 0 {
  2325  		requiredSpace := safetyMarginDiskSpace(snapshotSize)
  2326  		path := dirs.SnapdStateDir(dirs.GlobalRootDir)
  2327  		if err := osutilCheckFreeSpace(path, requiredSpace); err != nil {
  2328  			if _, ok := err.(*osutil.NotEnoughDiskSpaceError); ok {
  2329  				return nil, &InsufficientSpaceError{
  2330  					Path:       path,
  2331  					Snaps:      []string{name},
  2332  					ChangeKind: "remove",
  2333  					Message:    fmt.Sprintf("cannot create automatic snapshot when removing last revision of the snap: %v", err)}
  2334  			}
  2335  			return nil, err
  2336  		}
  2337  	}
  2338  	return ts, err
  2339  }
  2340  
  2341  // removeTasks provides the task set to remove snap name after taking a snapshot
  2342  // if flags.Purge is not true, it also computes an estimate of the latter size.
  2343  func removeTasks(st *state.State, name string, revision snap.Revision, flags *RemoveFlags) (removeTs *state.TaskSet, snapshotSize uint64, err error) {
  2344  	var snapst SnapState
  2345  	err = Get(st, name, &snapst)
  2346  	if err != nil && err != state.ErrNoState {
  2347  		return nil, 0, err
  2348  	}
  2349  
  2350  	if !snapst.IsInstalled() {
  2351  		return nil, 0, &snap.NotInstalledError{Snap: name, Rev: snap.R(0)}
  2352  	}
  2353  
  2354  	if err := CheckChangeConflict(st, name, nil); err != nil {
  2355  		return nil, 0, err
  2356  	}
  2357  
  2358  	deviceCtx, err := DeviceCtxFromState(st, nil)
  2359  	if err != nil {
  2360  		return nil, 0, err
  2361  	}
  2362  
  2363  	active := snapst.Active
  2364  	var removeAll bool
  2365  	if revision.Unset() {
  2366  		revision = snapst.Current
  2367  		removeAll = true
  2368  	} else {
  2369  		if active {
  2370  			if revision == snapst.Current {
  2371  				msg := "cannot remove active revision %s of snap %q"
  2372  				if len(snapst.Sequence) > 1 {
  2373  					msg += " (revert first?)"
  2374  				}
  2375  				return nil, 0, fmt.Errorf(msg, revision, name)
  2376  			}
  2377  			active = false
  2378  		}
  2379  
  2380  		if !revisionInSequence(&snapst, revision) {
  2381  			return nil, 0, &snap.NotInstalledError{Snap: name, Rev: revision}
  2382  		}
  2383  
  2384  		removeAll = len(snapst.Sequence) == 1
  2385  	}
  2386  
  2387  	info, err := Info(st, name, revision)
  2388  	if err != nil {
  2389  		return nil, 0, err
  2390  	}
  2391  
  2392  	// check if this is something that can be removed
  2393  	if err := canRemove(st, info, &snapst, removeAll, deviceCtx); err != nil {
  2394  		return nil, 0, fmt.Errorf("snap %q is not removable: %v", name, err)
  2395  	}
  2396  
  2397  	// main/current SnapSetup
  2398  	snapsup := SnapSetup{
  2399  		SideInfo: &snap.SideInfo{
  2400  			SnapID:   info.SnapID,
  2401  			RealName: snap.InstanceSnap(name),
  2402  			Revision: revision,
  2403  		},
  2404  		Type:        info.Type(),
  2405  		PlugsOnly:   len(info.Slots) == 0,
  2406  		InstanceKey: snapst.InstanceKey,
  2407  	}
  2408  
  2409  	// trigger remove
  2410  
  2411  	removeTs = state.NewTaskSet()
  2412  	var chain *state.TaskSet
  2413  
  2414  	addNext := func(ts *state.TaskSet) {
  2415  		if chain != nil {
  2416  			ts.WaitAll(chain)
  2417  		}
  2418  		removeTs.AddAll(ts)
  2419  		chain = ts
  2420  	}
  2421  
  2422  	var prev *state.Task
  2423  	var stopSnapServices *state.Task
  2424  	if active {
  2425  		stopSnapServices = st.NewTask("stop-snap-services", fmt.Sprintf(i18n.G("Stop snap %q services"), name))
  2426  		stopSnapServices.Set("snap-setup", snapsup)
  2427  		stopSnapServices.Set("stop-reason", snap.StopReasonRemove)
  2428  		addNext(state.NewTaskSet(stopSnapServices))
  2429  		prev = stopSnapServices
  2430  	}
  2431  
  2432  	// only run remove hook if uninstalling the snap completely
  2433  	if removeAll {
  2434  		removeHook := SetupRemoveHook(st, snapsup.InstanceName())
  2435  		addNext(state.NewTaskSet(removeHook))
  2436  		prev = removeHook
  2437  
  2438  		// run disconnect hooks
  2439  		disconnect := st.NewTask("auto-disconnect", fmt.Sprintf(i18n.G("Disconnect interfaces of snap %q"), snapsup.InstanceName()))
  2440  		disconnect.Set("snap-setup", snapsup)
  2441  		if prev != nil {
  2442  			disconnect.WaitFor(prev)
  2443  		}
  2444  		addNext(state.NewTaskSet(disconnect))
  2445  		prev = disconnect
  2446  	}
  2447  
  2448  	// 'purge' flag disables automatic snapshot for given remove op
  2449  	if flags == nil || !flags.Purge {
  2450  		if tp, _ := snapst.Type(); tp == snap.TypeApp && removeAll {
  2451  			ts, err := AutomaticSnapshot(st, name)
  2452  			if err == nil {
  2453  				tr := config.NewTransaction(st)
  2454  				checkDiskSpaceRemove, err := features.Flag(tr, features.CheckDiskSpaceRemove)
  2455  				if err != nil && !config.IsNoOption(err) {
  2456  					return nil, 0, err
  2457  				}
  2458  				if checkDiskSpaceRemove {
  2459  					snapshotSize, err = EstimateSnapshotSize(st, name, nil)
  2460  					if err != nil {
  2461  						return nil, 0, err
  2462  					}
  2463  				}
  2464  				addNext(ts)
  2465  			} else {
  2466  				if err != ErrNothingToDo {
  2467  					return nil, 0, err
  2468  				}
  2469  			}
  2470  		}
  2471  	}
  2472  
  2473  	if active { // unlink
  2474  		var tasks []*state.Task
  2475  
  2476  		removeAliases := st.NewTask("remove-aliases", fmt.Sprintf(i18n.G("Remove aliases for snap %q"), name))
  2477  		removeAliases.WaitFor(prev) // prev is not needed beyond here
  2478  		removeAliases.Set("snap-setup-task", stopSnapServices.ID())
  2479  
  2480  		unlink := st.NewTask("unlink-snap", fmt.Sprintf(i18n.G("Make snap %q unavailable to the system"), name))
  2481  		unlink.Set("snap-setup-task", stopSnapServices.ID())
  2482  		unlink.WaitFor(removeAliases)
  2483  
  2484  		removeSecurity := st.NewTask("remove-profiles", fmt.Sprintf(i18n.G("Remove security profile for snap %q (%s)"), name, revision))
  2485  		removeSecurity.WaitFor(unlink)
  2486  		removeSecurity.Set("snap-setup-task", stopSnapServices.ID())
  2487  
  2488  		tasks = append(tasks, removeAliases, unlink, removeSecurity)
  2489  		addNext(state.NewTaskSet(tasks...))
  2490  	}
  2491  
  2492  	if removeAll {
  2493  		seq := snapst.Sequence
  2494  		currentIndex := snapst.LastIndex(snapst.Current)
  2495  		for i := len(seq) - 1; i >= 0; i-- {
  2496  			if i != currentIndex {
  2497  				si := seq[i]
  2498  				addNext(removeInactiveRevision(st, name, info.SnapID, si.Revision, snapsup.Type))
  2499  			}
  2500  		}
  2501  		// add tasks for removing the current revision last,
  2502  		// this is then also when common data will be removed
  2503  		if currentIndex >= 0 {
  2504  			addNext(removeInactiveRevision(st, name, info.SnapID, seq[currentIndex].Revision, snapsup.Type))
  2505  		}
  2506  	} else {
  2507  		addNext(removeInactiveRevision(st, name, info.SnapID, revision, snapsup.Type))
  2508  	}
  2509  
  2510  	return removeTs, snapshotSize, nil
  2511  }
  2512  
  2513  func removeInactiveRevision(st *state.State, name, snapID string, revision snap.Revision, typ snap.Type) *state.TaskSet {
  2514  	snapName, instanceKey := snap.SplitInstanceName(name)
  2515  	snapsup := SnapSetup{
  2516  		SideInfo: &snap.SideInfo{
  2517  			RealName: snapName,
  2518  			SnapID:   snapID,
  2519  			Revision: revision,
  2520  		},
  2521  		InstanceKey: instanceKey,
  2522  		Type:        typ,
  2523  	}
  2524  
  2525  	clearData := st.NewTask("clear-snap", fmt.Sprintf(i18n.G("Remove data for snap %q (%s)"), name, revision))
  2526  	clearData.Set("snap-setup", snapsup)
  2527  
  2528  	discardSnap := st.NewTask("discard-snap", fmt.Sprintf(i18n.G("Remove snap %q (%s) from the system"), name, revision))
  2529  	discardSnap.WaitFor(clearData)
  2530  	discardSnap.Set("snap-setup-task", clearData.ID())
  2531  
  2532  	return state.NewTaskSet(clearData, discardSnap)
  2533  }
  2534  
  2535  // RemoveMany removes everything from the given list of names.
  2536  // Note that the state must be locked by the caller.
  2537  func RemoveMany(st *state.State, names []string) ([]string, []*state.TaskSet, error) {
  2538  	if err := validateSnapNames(names); err != nil {
  2539  		return nil, nil, err
  2540  	}
  2541  
  2542  	removed := make([]string, 0, len(names))
  2543  	tasksets := make([]*state.TaskSet, 0, len(names))
  2544  
  2545  	var totalSnapshotsSize uint64
  2546  	path := dirs.SnapdStateDir(dirs.GlobalRootDir)
  2547  
  2548  	for _, name := range names {
  2549  		ts, snapshotSize, err := removeTasks(st, name, snap.R(0), nil)
  2550  		// FIXME: is this expected behavior?
  2551  		if _, ok := err.(*snap.NotInstalledError); ok {
  2552  			continue
  2553  		}
  2554  		if err != nil {
  2555  			return nil, nil, err
  2556  		}
  2557  		totalSnapshotsSize += snapshotSize
  2558  		removed = append(removed, name)
  2559  		ts.JoinLane(st.NewLane())
  2560  		tasksets = append(tasksets, ts)
  2561  	}
  2562  
  2563  	// removeTasks() checks check-disk-space-remove feature flag, so totalSnapshotsSize
  2564  	// will only be greater than 0 if the feature is enabled.
  2565  	if totalSnapshotsSize > 0 {
  2566  		requiredSpace := safetyMarginDiskSpace(totalSnapshotsSize)
  2567  		if err := osutilCheckFreeSpace(path, requiredSpace); err != nil {
  2568  			if _, ok := err.(*osutil.NotEnoughDiskSpaceError); ok {
  2569  				return nil, nil, &InsufficientSpaceError{
  2570  					Path:       path,
  2571  					Snaps:      names,
  2572  					ChangeKind: "remove",
  2573  				}
  2574  			}
  2575  			return nil, nil, err
  2576  		}
  2577  	}
  2578  
  2579  	return removed, tasksets, nil
  2580  }
  2581  
  2582  func validateSnapNames(names []string) error {
  2583  	var invalidNames []string
  2584  
  2585  	for _, name := range names {
  2586  		if err := snap.ValidateInstanceName(name); err != nil {
  2587  			invalidNames = append(invalidNames, name)
  2588  		}
  2589  	}
  2590  
  2591  	if len(invalidNames) > 0 {
  2592  		return fmt.Errorf("cannot remove invalid snap names: %v", strings.Join(invalidNames, ", "))
  2593  	}
  2594  
  2595  	return nil
  2596  }
  2597  
  2598  // Revert returns a set of tasks for reverting to the previous version of the snap.
  2599  // Note that the state must be locked by the caller.
  2600  func Revert(st *state.State, name string, flags Flags) (*state.TaskSet, error) {
  2601  	var snapst SnapState
  2602  	err := Get(st, name, &snapst)
  2603  	if err != nil && err != state.ErrNoState {
  2604  		return nil, err
  2605  	}
  2606  
  2607  	pi := snapst.previousSideInfo()
  2608  	if pi == nil {
  2609  		return nil, fmt.Errorf("no revision to revert to")
  2610  	}
  2611  
  2612  	return RevertToRevision(st, name, pi.Revision, flags)
  2613  }
  2614  
  2615  func RevertToRevision(st *state.State, name string, rev snap.Revision, flags Flags) (*state.TaskSet, error) {
  2616  	var snapst SnapState
  2617  	err := Get(st, name, &snapst)
  2618  	if err != nil && err != state.ErrNoState {
  2619  		return nil, err
  2620  	}
  2621  
  2622  	if snapst.Current == rev {
  2623  		return nil, fmt.Errorf("already on requested revision")
  2624  	}
  2625  
  2626  	if !snapst.Active {
  2627  		return nil, fmt.Errorf("cannot revert inactive snaps")
  2628  	}
  2629  	i := snapst.LastIndex(rev)
  2630  	if i < 0 {
  2631  		return nil, fmt.Errorf("cannot find revision %s for snap %q", rev, name)
  2632  	}
  2633  
  2634  	flags.Revert = true
  2635  	// TODO: make flags be per revision to avoid this logic (that
  2636  	//       leaves corner cases all over the place)
  2637  	if !(flags.JailMode || flags.DevMode || flags.Classic) {
  2638  		if snapst.Flags.DevMode {
  2639  			flags.DevMode = true
  2640  		}
  2641  		if snapst.Flags.JailMode {
  2642  			flags.JailMode = true
  2643  		}
  2644  		if snapst.Flags.Classic {
  2645  			flags.Classic = true
  2646  		}
  2647  	}
  2648  
  2649  	info, err := Info(st, name, rev)
  2650  	if err != nil {
  2651  		return nil, err
  2652  	}
  2653  
  2654  	snapsup := &SnapSetup{
  2655  		Base:        info.Base,
  2656  		SideInfo:    snapst.Sequence[i],
  2657  		Flags:       flags.ForSnapSetup(),
  2658  		Type:        info.Type(),
  2659  		PlugsOnly:   len(info.Slots) == 0,
  2660  		InstanceKey: snapst.InstanceKey,
  2661  	}
  2662  	return doInstall(st, &snapst, snapsup, 0, "", nil)
  2663  }
  2664  
  2665  // TransitionCore transitions from an old core snap name to a new core
  2666  // snap name. It is used for the ubuntu-core -> core transition (that
  2667  // is not just a rename because the two snaps have different snapIDs)
  2668  //
  2669  // Note that this function makes some assumptions like:
  2670  // - no aliases setup for both snaps
  2671  // - no data needs to be copied
  2672  // - all interfaces are absolutely identical on both new and old
  2673  // Do not use this as a general way to transition from snap A to snap B.
  2674  func TransitionCore(st *state.State, oldName, newName string) ([]*state.TaskSet, error) {
  2675  	var oldSnapst, newSnapst SnapState
  2676  	err := Get(st, oldName, &oldSnapst)
  2677  	if err != nil && err != state.ErrNoState {
  2678  		return nil, err
  2679  	}
  2680  	if !oldSnapst.IsInstalled() {
  2681  		return nil, fmt.Errorf("cannot transition snap %q: not installed", oldName)
  2682  	}
  2683  
  2684  	var all []*state.TaskSet
  2685  	// install new core (if not already installed)
  2686  	err = Get(st, newName, &newSnapst)
  2687  	if err != nil && err != state.ErrNoState {
  2688  		return nil, err
  2689  	}
  2690  	if !newSnapst.IsInstalled() {
  2691  		var userID int
  2692  		newInfo, err := installInfo(context.TODO(), st, newName, &RevisionOptions{Channel: oldSnapst.TrackingChannel}, userID, Flags{}, nil)
  2693  		if err != nil {
  2694  			return nil, err
  2695  		}
  2696  
  2697  		// start by installing the new snap
  2698  		tsInst, err := doInstall(st, &newSnapst, &SnapSetup{
  2699  			Channel:      oldSnapst.TrackingChannel,
  2700  			DownloadInfo: &newInfo.DownloadInfo,
  2701  			SideInfo:     &newInfo.SideInfo,
  2702  			Type:         newInfo.Type(),
  2703  		}, 0, "", nil)
  2704  		if err != nil {
  2705  			return nil, err
  2706  		}
  2707  		all = append(all, tsInst)
  2708  	}
  2709  
  2710  	// then transition the interface connections over
  2711  	transIf := st.NewTask("transition-ubuntu-core", fmt.Sprintf(i18n.G("Transition security profiles from %q to %q"), oldName, newName))
  2712  	transIf.Set("old-name", oldName)
  2713  	transIf.Set("new-name", newName)
  2714  	if len(all) > 0 {
  2715  		transIf.WaitAll(all[0])
  2716  	}
  2717  	tsTrans := state.NewTaskSet(transIf)
  2718  	all = append(all, tsTrans)
  2719  
  2720  	// FIXME: this is just here for the tests
  2721  	transIf.Set("snap-setup", &SnapSetup{
  2722  		SideInfo: &snap.SideInfo{
  2723  			RealName: oldName,
  2724  		},
  2725  	})
  2726  
  2727  	// then remove the old snap
  2728  	tsRm, err := Remove(st, oldName, snap.R(0), nil)
  2729  	if err != nil {
  2730  		return nil, err
  2731  	}
  2732  	tsRm.WaitFor(transIf)
  2733  	all = append(all, tsRm)
  2734  
  2735  	return all, nil
  2736  }
  2737  
  2738  // State/info accessors
  2739  
  2740  // Installing returns whether there's an in-progress installation.
  2741  func Installing(st *state.State) bool {
  2742  	for _, task := range st.Tasks() {
  2743  		k := task.Kind()
  2744  		chg := task.Change()
  2745  		if k == "mount-snap" && chg != nil && !chg.Status().Ready() {
  2746  			return true
  2747  		}
  2748  	}
  2749  	return false
  2750  }
  2751  
  2752  // Info returns the information about the snap with given name and revision.
  2753  // Works also for a mounted candidate snap in the process of being installed.
  2754  func Info(st *state.State, name string, revision snap.Revision) (*snap.Info, error) {
  2755  	var snapst SnapState
  2756  	err := Get(st, name, &snapst)
  2757  	if err == state.ErrNoState {
  2758  		return nil, &snap.NotInstalledError{Snap: name}
  2759  	}
  2760  	if err != nil {
  2761  		return nil, err
  2762  	}
  2763  
  2764  	for i := len(snapst.Sequence) - 1; i >= 0; i-- {
  2765  		if si := snapst.Sequence[i]; si.Revision == revision {
  2766  			return readInfo(name, si, 0)
  2767  		}
  2768  	}
  2769  
  2770  	return nil, fmt.Errorf("cannot find snap %q at revision %s", name, revision.String())
  2771  }
  2772  
  2773  // CurrentInfo returns the information about the current revision of a snap with the given name.
  2774  func CurrentInfo(st *state.State, name string) (*snap.Info, error) {
  2775  	var snapst SnapState
  2776  	err := Get(st, name, &snapst)
  2777  	if err != nil && err != state.ErrNoState {
  2778  		return nil, err
  2779  	}
  2780  	info, err := snapst.CurrentInfo()
  2781  	if err == ErrNoCurrent {
  2782  		return nil, &snap.NotInstalledError{Snap: name}
  2783  	}
  2784  	return info, err
  2785  }
  2786  
  2787  // Get retrieves the SnapState of the given snap.
  2788  func Get(st *state.State, name string, snapst *SnapState) error {
  2789  	if snapst == nil {
  2790  		return fmt.Errorf("internal error: snapst is nil")
  2791  	}
  2792  	// SnapState is (un-)marshalled from/to JSON, fields having omitempty
  2793  	// tag will not appear in the output (if empty) and subsequently will
  2794  	// not be unmarshalled to (or cleared); if the caller reuses the same
  2795  	// struct though subsequent calls, it is possible that they end up with
  2796  	// garbage inside, clear the destination struct so that we always
  2797  	// unmarshal to a clean state
  2798  	*snapst = SnapState{}
  2799  
  2800  	var snaps map[string]*json.RawMessage
  2801  	err := st.Get("snaps", &snaps)
  2802  	if err != nil {
  2803  		return err
  2804  	}
  2805  	raw, ok := snaps[name]
  2806  	if !ok {
  2807  		return state.ErrNoState
  2808  	}
  2809  
  2810  	// XXX: &snapst pointer isn't needed here but it is likely historical
  2811  	// (a bug in old JSON marshaling probably).
  2812  	err = json.Unmarshal([]byte(*raw), &snapst)
  2813  	if err != nil {
  2814  		return fmt.Errorf("cannot unmarshal snap state: %v", err)
  2815  	}
  2816  	return nil
  2817  }
  2818  
  2819  // All retrieves return a map from name to SnapState for all current snaps in the system state.
  2820  func All(st *state.State) (map[string]*SnapState, error) {
  2821  	// XXX: result is a map because sideloaded snaps carry no name
  2822  	// atm in their sideinfos
  2823  	var stateMap map[string]*SnapState
  2824  	if err := st.Get("snaps", &stateMap); err != nil && err != state.ErrNoState {
  2825  		return nil, err
  2826  	}
  2827  	curStates := make(map[string]*SnapState, len(stateMap))
  2828  	for instanceName, snapst := range stateMap {
  2829  		curStates[instanceName] = snapst
  2830  	}
  2831  	return curStates, nil
  2832  }
  2833  
  2834  // NumSnaps returns the number of installed snaps.
  2835  func NumSnaps(st *state.State) (int, error) {
  2836  	var snaps map[string]*json.RawMessage
  2837  	if err := st.Get("snaps", &snaps); err != nil && err != state.ErrNoState {
  2838  		return -1, err
  2839  	}
  2840  	return len(snaps), nil
  2841  }
  2842  
  2843  // Set sets the SnapState of the given snap, overwriting any earlier state.
  2844  // Note that a SnapState with an empty Sequence will be treated as if snapst was
  2845  // nil and name will be deleted from the state.
  2846  func Set(st *state.State, name string, snapst *SnapState) {
  2847  	var snaps map[string]*json.RawMessage
  2848  	err := st.Get("snaps", &snaps)
  2849  	if err != nil && err != state.ErrNoState {
  2850  		panic("internal error: cannot unmarshal snaps state: " + err.Error())
  2851  	}
  2852  	if snaps == nil {
  2853  		snaps = make(map[string]*json.RawMessage)
  2854  	}
  2855  	if snapst == nil || (len(snapst.Sequence) == 0) {
  2856  		delete(snaps, name)
  2857  	} else {
  2858  		data, err := json.Marshal(snapst)
  2859  		if err != nil {
  2860  			panic("internal error: cannot marshal snap state: " + err.Error())
  2861  		}
  2862  		raw := json.RawMessage(data)
  2863  		snaps[name] = &raw
  2864  	}
  2865  	st.Set("snaps", snaps)
  2866  }
  2867  
  2868  // ActiveInfos returns information about all active snaps.
  2869  func ActiveInfos(st *state.State) ([]*snap.Info, error) {
  2870  	var stateMap map[string]*SnapState
  2871  	var infos []*snap.Info
  2872  	if err := st.Get("snaps", &stateMap); err != nil && err != state.ErrNoState {
  2873  		return nil, err
  2874  	}
  2875  	for instanceName, snapst := range stateMap {
  2876  		if !snapst.Active {
  2877  			continue
  2878  		}
  2879  		snapInfo, err := snapst.CurrentInfo()
  2880  		if err != nil {
  2881  			logger.Noticef("cannot retrieve info for snap %q: %s", instanceName, err)
  2882  			continue
  2883  		}
  2884  		infos = append(infos, snapInfo)
  2885  	}
  2886  	return infos, nil
  2887  }
  2888  
  2889  func HasSnapOfType(st *state.State, snapType snap.Type) (bool, error) {
  2890  	var stateMap map[string]*SnapState
  2891  	if err := st.Get("snaps", &stateMap); err != nil && err != state.ErrNoState {
  2892  		return false, err
  2893  	}
  2894  
  2895  	for _, snapst := range stateMap {
  2896  		typ, err := snapst.Type()
  2897  		if err != nil {
  2898  			return false, err
  2899  		}
  2900  		if typ == snapType {
  2901  			return true, nil
  2902  		}
  2903  	}
  2904  
  2905  	return false, nil
  2906  }
  2907  
  2908  func infosForType(st *state.State, snapType snap.Type) ([]*snap.Info, error) {
  2909  	var stateMap map[string]*SnapState
  2910  	if err := st.Get("snaps", &stateMap); err != nil && err != state.ErrNoState {
  2911  		return nil, err
  2912  	}
  2913  
  2914  	var res []*snap.Info
  2915  	for _, snapst := range stateMap {
  2916  		if !snapst.IsInstalled() {
  2917  			continue
  2918  		}
  2919  		typ, err := snapst.Type()
  2920  		if err != nil {
  2921  			return nil, err
  2922  		}
  2923  		if typ != snapType {
  2924  			continue
  2925  		}
  2926  		si, err := snapst.CurrentInfo()
  2927  		if err != nil {
  2928  			return nil, err
  2929  		}
  2930  		res = append(res, si)
  2931  	}
  2932  
  2933  	if len(res) == 0 {
  2934  		return nil, state.ErrNoState
  2935  	}
  2936  
  2937  	return res, nil
  2938  }
  2939  
  2940  func infoForDeviceSnap(st *state.State, deviceCtx DeviceContext, which string, whichName func(*asserts.Model) string) (*snap.Info, error) {
  2941  	if deviceCtx == nil {
  2942  		return nil, fmt.Errorf("internal error: unset deviceCtx")
  2943  	}
  2944  	model := deviceCtx.Model()
  2945  	snapName := whichName(model)
  2946  	if snapName == "" {
  2947  		return nil, state.ErrNoState
  2948  	}
  2949  	var snapst SnapState
  2950  	err := Get(st, snapName, &snapst)
  2951  	if err != nil {
  2952  		return nil, err
  2953  	}
  2954  	return snapst.CurrentInfo()
  2955  }
  2956  
  2957  // GadgetInfo finds the gadget snap's info for the given device context.
  2958  func GadgetInfo(st *state.State, deviceCtx DeviceContext) (*snap.Info, error) {
  2959  	return infoForDeviceSnap(st, deviceCtx, "gadget", (*asserts.Model).Gadget)
  2960  }
  2961  
  2962  // KernelInfo finds the kernel snap's info for the given device context.
  2963  func KernelInfo(st *state.State, deviceCtx DeviceContext) (*snap.Info, error) {
  2964  	return infoForDeviceSnap(st, deviceCtx, "kernel", (*asserts.Model).Kernel)
  2965  }
  2966  
  2967  // BootBaseInfo finds the boot base snap's info for the given device context.
  2968  func BootBaseInfo(st *state.State, deviceCtx DeviceContext) (*snap.Info, error) {
  2969  	baseName := func(mod *asserts.Model) string {
  2970  		base := mod.Base()
  2971  		if base == "" {
  2972  			return "core"
  2973  		}
  2974  		return base
  2975  	}
  2976  	return infoForDeviceSnap(st, deviceCtx, "boot base", baseName)
  2977  }
  2978  
  2979  // TODO: reintroduce a KernelInfo(state.State, DeviceContext) if needed
  2980  // KernelInfo finds the current kernel snap's info.
  2981  
  2982  // coreInfo finds the current OS snap's info. If both
  2983  // "core" and "ubuntu-core" is installed then "core"
  2984  // is preferred. Different core names are not supported
  2985  // currently and will result in an error.
  2986  func coreInfo(st *state.State) (*snap.Info, error) {
  2987  	res, err := infosForType(st, snap.TypeOS)
  2988  	if err != nil {
  2989  		return nil, err
  2990  	}
  2991  
  2992  	// a single core: just return it
  2993  	if len(res) == 1 {
  2994  		return res[0], nil
  2995  	}
  2996  
  2997  	// some systems have two cores: ubuntu-core/core
  2998  	// we always return "core" in this case
  2999  	if len(res) == 2 {
  3000  		if res[0].InstanceName() == defaultCoreSnapName && res[1].InstanceName() == "ubuntu-core" {
  3001  			return res[0], nil
  3002  		}
  3003  		if res[0].InstanceName() == "ubuntu-core" && res[1].InstanceName() == defaultCoreSnapName {
  3004  			return res[1], nil
  3005  		}
  3006  		return nil, fmt.Errorf("unexpected cores %q and %q", res[0].InstanceName(), res[1].InstanceName())
  3007  	}
  3008  
  3009  	return nil, fmt.Errorf("unexpected number of cores, got %d", len(res))
  3010  }
  3011  
  3012  // ConfigDefaults returns the configuration defaults for the snap as
  3013  // specified in the gadget for the given device context.
  3014  // If gadget is absent or the snap has no snap-id it returns
  3015  // ErrNoState.
  3016  func ConfigDefaults(st *state.State, deviceCtx DeviceContext, snapName string) (map[string]interface{}, error) {
  3017  	info, err := GadgetInfo(st, deviceCtx)
  3018  	if err != nil {
  3019  		return nil, err
  3020  	}
  3021  
  3022  	// system configuration is kept under "core" so apply its defaults when
  3023  	// configuring "core"
  3024  	isSystemDefaults := snapName == defaultCoreSnapName
  3025  	var snapst SnapState
  3026  	if err := Get(st, snapName, &snapst); err != nil && err != state.ErrNoState {
  3027  		return nil, err
  3028  	}
  3029  
  3030  	var snapID string
  3031  	if snapst.IsInstalled() {
  3032  		snapID = snapst.CurrentSideInfo().SnapID
  3033  	}
  3034  	// system snaps (core and snapd) snaps can be addressed even without a
  3035  	// snap-id via the special "system" value in the config; first-boot
  3036  	// always configures the core snap with UseConfigDefaults
  3037  	if snapID == "" && !isSystemDefaults {
  3038  		return nil, state.ErrNoState
  3039  	}
  3040  
  3041  	// no constraints enforced: those should have been checked before already
  3042  	gadgetInfo, err := gadget.ReadInfo(info.MountDir(), nil)
  3043  	if err != nil {
  3044  		return nil, err
  3045  	}
  3046  
  3047  	// we support setting core defaults via "system"
  3048  	if isSystemDefaults {
  3049  		if defaults, ok := gadgetInfo.Defaults["system"]; ok {
  3050  			if _, ok := gadgetInfo.Defaults[snapID]; ok && snapID != "" {
  3051  				logger.Noticef("core snap configuration defaults found under both 'system' key and core-snap-id, preferring 'system'")
  3052  			}
  3053  
  3054  			return defaults, nil
  3055  		}
  3056  	}
  3057  
  3058  	defaults, ok := gadgetInfo.Defaults[snapID]
  3059  	if !ok {
  3060  		return nil, state.ErrNoState
  3061  	}
  3062  
  3063  	return defaults, nil
  3064  }
  3065  
  3066  // GadgetConnections returns the interface connection instructions
  3067  // specified in the gadget for the given device context.
  3068  // If gadget is absent it returns ErrNoState.
  3069  func GadgetConnections(st *state.State, deviceCtx DeviceContext) ([]gadget.Connection, error) {
  3070  	info, err := GadgetInfo(st, deviceCtx)
  3071  	if err != nil {
  3072  		return nil, err
  3073  	}
  3074  
  3075  	// no constraints enforced: those should have been checked before already
  3076  	gadgetInfo, err := gadget.ReadInfo(info.MountDir(), nil)
  3077  	if err != nil {
  3078  		return nil, err
  3079  	}
  3080  
  3081  	return gadgetInfo.Connections, nil
  3082  }
  3083  
  3084  func MockOsutilCheckFreeSpace(mock func(path string, minSize uint64) error) (restore func()) {
  3085  	old := osutilCheckFreeSpace
  3086  	osutilCheckFreeSpace = mock
  3087  	return func() { osutilCheckFreeSpace = old }
  3088  }