github.com/hugh712/snapd@v0.0.0-20200910133618-1a99902bd583/overlord/snapstate/snapstate.go (about)

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