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