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