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