github.com/chipaca/snappy@v0.0.0-20210104084008-1f06296fe8ad/overlord/snapstate/handlers.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
    21  
    22  import (
    23  	"context"
    24  	"encoding/json"
    25  	"fmt"
    26  	"os"
    27  	"path/filepath"
    28  	"sort"
    29  	"strconv"
    30  	"strings"
    31  	"time"
    32  
    33  	"gopkg.in/tomb.v2"
    34  
    35  	"github.com/snapcore/snapd/boot"
    36  	"github.com/snapcore/snapd/cmd/snaplock/runinhibit"
    37  	"github.com/snapcore/snapd/dirs"
    38  	"github.com/snapcore/snapd/features"
    39  	"github.com/snapcore/snapd/i18n"
    40  	"github.com/snapcore/snapd/logger"
    41  	"github.com/snapcore/snapd/osutil"
    42  	"github.com/snapcore/snapd/overlord/auth"
    43  	"github.com/snapcore/snapd/overlord/configstate/config"
    44  	"github.com/snapcore/snapd/overlord/configstate/settings"
    45  	"github.com/snapcore/snapd/overlord/snapstate/backend"
    46  	"github.com/snapcore/snapd/overlord/state"
    47  	"github.com/snapcore/snapd/progress"
    48  	"github.com/snapcore/snapd/release"
    49  	"github.com/snapcore/snapd/snap"
    50  	"github.com/snapcore/snapd/store"
    51  	"github.com/snapcore/snapd/strutil"
    52  	"github.com/snapcore/snapd/timings"
    53  )
    54  
    55  // TaskSnapSetup returns the SnapSetup with task params hold by or referred to by the task.
    56  func TaskSnapSetup(t *state.Task) (*SnapSetup, error) {
    57  	var snapsup SnapSetup
    58  
    59  	err := t.Get("snap-setup", &snapsup)
    60  	if err != nil && err != state.ErrNoState {
    61  		return nil, err
    62  	}
    63  	if err == nil {
    64  		return &snapsup, nil
    65  	}
    66  
    67  	var id string
    68  	err = t.Get("snap-setup-task", &id)
    69  	if err != nil {
    70  		return nil, err
    71  	}
    72  
    73  	ts := t.State().Task(id)
    74  	if ts == nil {
    75  		return nil, fmt.Errorf("internal error: tasks are being pruned")
    76  	}
    77  	if err := ts.Get("snap-setup", &snapsup); err != nil {
    78  		return nil, err
    79  	}
    80  	return &snapsup, nil
    81  }
    82  
    83  // SetTaskSnapSetup writes the given SnapSetup to the provided task's
    84  // snap-setup-task Task, or to the task itself if the task does not have a
    85  // snap-setup-task (i.e. it _is_ the snap-setup-task)
    86  func SetTaskSnapSetup(t *state.Task, snapsup *SnapSetup) error {
    87  	if t.Has("snap-setup") {
    88  		// this is the snap-setup-task so just write to the task directly
    89  		t.Set("snap-setup", snapsup)
    90  	} else {
    91  		// this task isn't the snap-setup-task, so go get that and write to that
    92  		// one
    93  		var id string
    94  		err := t.Get("snap-setup-task", &id)
    95  		if err != nil {
    96  			return err
    97  		}
    98  
    99  		ts := t.State().Task(id)
   100  		if ts == nil {
   101  			return fmt.Errorf("internal error: tasks are being pruned")
   102  		}
   103  		ts.Set("snap-setup", snapsup)
   104  	}
   105  
   106  	return nil
   107  }
   108  
   109  func snapSetupAndState(t *state.Task) (*SnapSetup, *SnapState, error) {
   110  	snapsup, err := TaskSnapSetup(t)
   111  	if err != nil {
   112  		return nil, nil, err
   113  	}
   114  	var snapst SnapState
   115  	err = Get(t.State(), snapsup.InstanceName(), &snapst)
   116  	if err != nil && err != state.ErrNoState {
   117  		return nil, nil, err
   118  	}
   119  	return snapsup, &snapst, nil
   120  }
   121  
   122  /* State Locking
   123  
   124     do* / undo* handlers should usually lock the state just once with:
   125  
   126  	st.Lock()
   127  	defer st.Unlock()
   128  
   129     For tasks doing slow operations (long i/o, networking operations) it's OK
   130     to unlock the state temporarily:
   131  
   132          st.Unlock()
   133          err := slowIOOp()
   134          st.Lock()
   135          if err != nil {
   136             ...
   137          }
   138  
   139      but if a task Get and then Set the SnapState of a snap it must avoid
   140      releasing the state lock in between, other tasks might have
   141      reasons to update the SnapState independently:
   142  
   143          // DO NOT DO THIS!:
   144          snapst := ...
   145          snapst.Attr = ...
   146          st.Unlock()
   147          ...
   148          st.Lock()
   149          Set(st, snapName, snapst)
   150  
   151      if a task really needs to mix mutating a SnapState and releasing the state
   152      lock it should be serialized at the task runner level, see
   153      SnapManger.blockedTask and TaskRunner.SetBlocked
   154  
   155  */
   156  
   157  const defaultCoreSnapName = "core"
   158  
   159  func defaultBaseSnapsChannel() string {
   160  	channel := os.Getenv("SNAPD_BASES_CHANNEL")
   161  	if channel == "" {
   162  		return "stable"
   163  	}
   164  	return channel
   165  }
   166  
   167  func defaultSnapdSnapsChannel() string {
   168  	channel := os.Getenv("SNAPD_SNAPD_CHANNEL")
   169  	if channel == "" {
   170  		return "stable"
   171  	}
   172  	return channel
   173  }
   174  
   175  func defaultPrereqSnapsChannel() string {
   176  	channel := os.Getenv("SNAPD_PREREQS_CHANNEL")
   177  	if channel == "" {
   178  		return "stable"
   179  	}
   180  	return channel
   181  }
   182  
   183  func linkSnapInFlight(st *state.State, snapName string) (bool, error) {
   184  	for _, chg := range st.Changes() {
   185  		if chg.Status().Ready() {
   186  			continue
   187  		}
   188  		for _, tc := range chg.Tasks() {
   189  			if tc.Status().Ready() {
   190  				continue
   191  			}
   192  			if tc.Kind() == "link-snap" {
   193  				snapsup, err := TaskSnapSetup(tc)
   194  				if err != nil {
   195  					return false, err
   196  				}
   197  				if snapsup.InstanceName() == snapName {
   198  					return true, nil
   199  				}
   200  			}
   201  		}
   202  	}
   203  
   204  	return false, nil
   205  }
   206  
   207  func isInstalled(st *state.State, snapName string) (bool, error) {
   208  	var snapState SnapState
   209  	err := Get(st, snapName, &snapState)
   210  	if err != nil && err != state.ErrNoState {
   211  		return false, err
   212  	}
   213  	return snapState.IsInstalled(), nil
   214  }
   215  
   216  // timeout for tasks to check if the prerequisites are ready
   217  var prerequisitesRetryTimeout = 30 * time.Second
   218  
   219  func (m *SnapManager) doPrerequisites(t *state.Task, _ *tomb.Tomb) error {
   220  	st := t.State()
   221  	st.Lock()
   222  	defer st.Unlock()
   223  
   224  	perfTimings := state.TimingsForTask(t)
   225  	defer perfTimings.Save(st)
   226  
   227  	// check if we need to inject tasks to install core
   228  	snapsup, _, err := snapSetupAndState(t)
   229  	if err != nil {
   230  		return err
   231  	}
   232  
   233  	// os/base/kernel/gadget cannot have prerequisites other
   234  	// than the models default base (or core) which is installed anyway
   235  	switch snapsup.Type {
   236  	case snap.TypeOS, snap.TypeBase, snap.TypeKernel, snap.TypeGadget:
   237  		return nil
   238  	}
   239  	// snapd is special and has no prereqs
   240  	if snapsup.Type == snap.TypeSnapd {
   241  		return nil
   242  	}
   243  
   244  	// we need to make sure we install all prereqs together in one
   245  	// operation
   246  	base := defaultCoreSnapName
   247  	if snapsup.Base != "" {
   248  		base = snapsup.Base
   249  	}
   250  
   251  	if err := m.installPrereqs(t, base, snapsup.Prereq, snapsup.UserID, perfTimings); err != nil {
   252  		return err
   253  	}
   254  
   255  	return nil
   256  }
   257  
   258  func (m *SnapManager) installOneBaseOrRequired(st *state.State, snapName string, requireTypeBase bool, channel string, onInFlight error, userID int) (*state.TaskSet, error) {
   259  	// The core snap provides everything we need for core16.
   260  	coreInstalled, err := isInstalled(st, "core")
   261  	if err != nil {
   262  		return nil, err
   263  	}
   264  	if snapName == "core16" && coreInstalled {
   265  		return nil, nil
   266  	}
   267  
   268  	// installed already?
   269  	isInstalled, err := isInstalled(st, snapName)
   270  	if err != nil {
   271  		return nil, err
   272  	}
   273  	if isInstalled {
   274  		return nil, nil
   275  	}
   276  	// in progress?
   277  	inFlight, err := linkSnapInFlight(st, snapName)
   278  	if err != nil {
   279  		return nil, err
   280  	}
   281  	if inFlight {
   282  		return nil, onInFlight
   283  	}
   284  
   285  	// not installed, nor queued for install -> install it
   286  	ts, err := Install(context.TODO(), st, snapName, &RevisionOptions{Channel: channel}, userID, Flags{RequireTypeBase: requireTypeBase})
   287  
   288  	// something might have triggered an explicit install while
   289  	// the state was unlocked -> deal with that here by simply
   290  	// retrying the operation.
   291  	if _, ok := err.(*ChangeConflictError); ok {
   292  		return nil, &state.Retry{After: prerequisitesRetryTimeout}
   293  	}
   294  	return ts, err
   295  }
   296  
   297  func (m *SnapManager) installPrereqs(t *state.Task, base string, prereq []string, userID int, tm timings.Measurer) error {
   298  	st := t.State()
   299  
   300  	// We try to install all wanted snaps. If one snap cannot be installed
   301  	// because of change conflicts or similar we retry. Only if all snaps
   302  	// can be installed together we add the tasks to the change.
   303  	var tss []*state.TaskSet
   304  	for _, prereqName := range prereq {
   305  		var onInFlightErr error = nil
   306  		var err error
   307  		var ts *state.TaskSet
   308  		timings.Run(tm, "install-prereq", fmt.Sprintf("install %q", prereqName), func(timings.Measurer) {
   309  			noTypeBaseCheck := false
   310  			ts, err = m.installOneBaseOrRequired(st, prereqName, noTypeBaseCheck, defaultPrereqSnapsChannel(), onInFlightErr, userID)
   311  		})
   312  		if err != nil {
   313  			return prereqError("prerequisite", prereqName, err)
   314  		}
   315  		if ts == nil {
   316  			continue
   317  		}
   318  		tss = append(tss, ts)
   319  	}
   320  
   321  	// for base snaps we need to wait until the change is done
   322  	// (either finished or failed)
   323  	onInFlightErr := &state.Retry{After: prerequisitesRetryTimeout}
   324  
   325  	var tsBase *state.TaskSet
   326  	var err error
   327  	if base != "none" {
   328  		timings.Run(tm, "install-prereq", fmt.Sprintf("install base %q", base), func(timings.Measurer) {
   329  			requireTypeBase := true
   330  			tsBase, err = m.installOneBaseOrRequired(st, base, requireTypeBase, defaultBaseSnapsChannel(), onInFlightErr, userID)
   331  		})
   332  		if err != nil {
   333  			return prereqError("snap base", base, err)
   334  		}
   335  	}
   336  
   337  	// on systems without core or snapd need to install snapd to
   338  	// make interfaces work - LP: 1819318
   339  	var tsSnapd *state.TaskSet
   340  	snapdSnapInstalled, err := isInstalled(st, "snapd")
   341  	if err != nil {
   342  		return err
   343  	}
   344  	coreSnapInstalled, err := isInstalled(st, "core")
   345  	if err != nil {
   346  		return err
   347  	}
   348  	if base != "core" && !snapdSnapInstalled && !coreSnapInstalled {
   349  		timings.Run(tm, "install-prereq", "install snapd", func(timings.Measurer) {
   350  			noTypeBaseCheck := false
   351  			tsSnapd, err = m.installOneBaseOrRequired(st, "snapd", noTypeBaseCheck, defaultSnapdSnapsChannel(), onInFlightErr, userID)
   352  		})
   353  		if err != nil {
   354  			return prereqError("system snap", "snapd", err)
   355  		}
   356  	}
   357  
   358  	chg := t.Change()
   359  	// add all required snaps, no ordering, this will be done in the
   360  	// auto-connect task handler
   361  	for _, ts := range tss {
   362  		ts.JoinLane(st.NewLane())
   363  		chg.AddAll(ts)
   364  	}
   365  	// add the base if needed, prereqs else must wait on this
   366  	if tsBase != nil {
   367  		tsBase.JoinLane(st.NewLane())
   368  		for _, t := range chg.Tasks() {
   369  			t.WaitAll(tsBase)
   370  		}
   371  		chg.AddAll(tsBase)
   372  	}
   373  	// add snapd if needed, everything must wait on this
   374  	if tsSnapd != nil {
   375  		tsSnapd.JoinLane(st.NewLane())
   376  		for _, t := range chg.Tasks() {
   377  			t.WaitAll(tsSnapd)
   378  		}
   379  		chg.AddAll(tsSnapd)
   380  	}
   381  
   382  	// make sure that the new change is committed to the state
   383  	// together with marking this task done
   384  	t.SetStatus(state.DoneStatus)
   385  
   386  	return nil
   387  }
   388  
   389  func prereqError(what, snapName string, err error) error {
   390  	if _, ok := err.(*state.Retry); ok {
   391  		return err
   392  	}
   393  	return fmt.Errorf("cannot install %s %q: %v", what, snapName, err)
   394  }
   395  
   396  func (m *SnapManager) doPrepareSnap(t *state.Task, _ *tomb.Tomb) error {
   397  	st := t.State()
   398  	st.Lock()
   399  	defer st.Unlock()
   400  	snapsup, snapst, err := snapSetupAndState(t)
   401  	if err != nil {
   402  		return err
   403  	}
   404  
   405  	if snapsup.Revision().Unset() {
   406  		// Local revisions start at -1 and go down.
   407  		revision := snapst.LocalRevision()
   408  		if revision.Unset() || revision.N > 0 {
   409  			revision = snap.R(-1)
   410  		} else {
   411  			revision.N--
   412  		}
   413  		if !revision.Local() {
   414  			panic("internal error: invalid local revision built: " + revision.String())
   415  		}
   416  		snapsup.SideInfo.Revision = revision
   417  	}
   418  
   419  	t.Set("snap-setup", snapsup)
   420  	return nil
   421  }
   422  
   423  func (m *SnapManager) undoPrepareSnap(t *state.Task, _ *tomb.Tomb) error {
   424  	st := t.State()
   425  	st.Lock()
   426  	defer st.Unlock()
   427  
   428  	snapsup, err := TaskSnapSetup(t)
   429  	if err != nil {
   430  		return err
   431  	}
   432  
   433  	if snapsup.SideInfo == nil || snapsup.SideInfo.RealName == "" {
   434  		return nil
   435  	}
   436  
   437  	var logMsg []string
   438  	var snapSetup string
   439  	dupSig := []string{"snap-install:"}
   440  	chg := t.Change()
   441  	logMsg = append(logMsg, fmt.Sprintf("change %q: %q", chg.Kind(), chg.Summary()))
   442  	for _, t := range chg.Tasks() {
   443  		// TODO: report only tasks in intersecting lanes?
   444  		tintro := fmt.Sprintf("%s: %s", t.Kind(), t.Status())
   445  		logMsg = append(logMsg, tintro)
   446  		dupSig = append(dupSig, tintro)
   447  		if snapsup, err := TaskSnapSetup(t); err == nil && snapsup.SideInfo != nil {
   448  			snapSetup1 := fmt.Sprintf(" snap-setup: %q (%v) %q", snapsup.SideInfo.RealName, snapsup.SideInfo.Revision, snapsup.SideInfo.Channel)
   449  			if snapSetup1 != snapSetup {
   450  				snapSetup = snapSetup1
   451  				logMsg = append(logMsg, snapSetup)
   452  				dupSig = append(dupSig, fmt.Sprintf(" snap-setup: %q", snapsup.SideInfo.RealName))
   453  			}
   454  		}
   455  		for _, l := range t.Log() {
   456  			// cut of the rfc339 timestamp to ensure duplicate
   457  			// detection works in daisy
   458  			tStampLen := strings.Index(l, " ")
   459  			if tStampLen < 0 {
   460  				continue
   461  			}
   462  			// not tStampLen+1 because the indent is nice
   463  			entry := l[tStampLen:]
   464  			logMsg = append(logMsg, entry)
   465  			dupSig = append(dupSig, entry)
   466  		}
   467  	}
   468  
   469  	var ubuntuCoreTransitionCount int
   470  	err = st.Get("ubuntu-core-transition-retry", &ubuntuCoreTransitionCount)
   471  	if err != nil && err != state.ErrNoState {
   472  		return err
   473  	}
   474  	extra := map[string]string{
   475  		"Channel":  snapsup.Channel,
   476  		"Revision": snapsup.SideInfo.Revision.String(),
   477  	}
   478  	if ubuntuCoreTransitionCount > 0 {
   479  		extra["UbuntuCoreTransitionCount"] = strconv.Itoa(ubuntuCoreTransitionCount)
   480  	}
   481  
   482  	// Only report and error if there is an actual error in the change,
   483  	// we could undo things because the user canceled the change.
   484  	var isErr bool
   485  	for _, tt := range t.Change().Tasks() {
   486  		if tt.Status() == state.ErrorStatus {
   487  			isErr = true
   488  			break
   489  		}
   490  	}
   491  	if isErr && !settings.ProblemReportsDisabled(st) {
   492  		st.Unlock()
   493  		oopsid, err := errtrackerReport(snapsup.SideInfo.RealName, strings.Join(logMsg, "\n"), strings.Join(dupSig, "\n"), extra)
   494  		st.Lock()
   495  		if err == nil {
   496  			logger.Noticef("Reported install problem for %q as %s", snapsup.SideInfo.RealName, oopsid)
   497  		} else {
   498  			logger.Debugf("Cannot report problem: %s", err)
   499  		}
   500  	}
   501  
   502  	return nil
   503  }
   504  
   505  func installInfoUnlocked(st *state.State, snapsup *SnapSetup, deviceCtx DeviceContext) (store.SnapActionResult, error) {
   506  	st.Lock()
   507  	defer st.Unlock()
   508  	opts := &RevisionOptions{Channel: snapsup.Channel, CohortKey: snapsup.CohortKey, Revision: snapsup.Revision()}
   509  	return installInfo(context.TODO(), st, snapsup.InstanceName(), opts, snapsup.UserID, deviceCtx)
   510  }
   511  
   512  // autoRefreshRateLimited returns the rate limit of auto-refreshes or 0 if
   513  // there is no limit.
   514  func autoRefreshRateLimited(st *state.State) (rate int64) {
   515  	tr := config.NewTransaction(st)
   516  
   517  	var rateLimit string
   518  	err := tr.Get("core", "refresh.rate-limit", &rateLimit)
   519  	if err != nil {
   520  		return 0
   521  	}
   522  	// NOTE ParseByteSize errors on negative rates
   523  	val, err := strutil.ParseByteSize(rateLimit)
   524  	if err != nil {
   525  		return 0
   526  	}
   527  	return val
   528  }
   529  
   530  func downloadSnapParams(st *state.State, t *state.Task) (*SnapSetup, StoreService, *auth.UserState, error) {
   531  	snapsup, err := TaskSnapSetup(t)
   532  	if err != nil {
   533  		return nil, nil, nil, err
   534  	}
   535  
   536  	deviceCtx, err := DeviceCtx(st, t, nil)
   537  	if err != nil {
   538  		return nil, nil, nil, err
   539  	}
   540  
   541  	sto := Store(st, deviceCtx)
   542  
   543  	user, err := userFromUserID(st, snapsup.UserID)
   544  	if err != nil {
   545  		return nil, nil, nil, err
   546  	}
   547  
   548  	return snapsup, sto, user, nil
   549  }
   550  
   551  func (m *SnapManager) doDownloadSnap(t *state.Task, tomb *tomb.Tomb) error {
   552  	st := t.State()
   553  	var rate int64
   554  
   555  	st.Lock()
   556  	perfTimings := state.TimingsForTask(t)
   557  	snapsup, theStore, user, err := downloadSnapParams(st, t)
   558  	if snapsup != nil && snapsup.IsAutoRefresh {
   559  		// NOTE rate is never negative
   560  		rate = autoRefreshRateLimited(st)
   561  	}
   562  	st.Unlock()
   563  	if err != nil {
   564  		return err
   565  	}
   566  
   567  	meter := NewTaskProgressAdapterUnlocked(t)
   568  	targetFn := snapsup.MountFile()
   569  
   570  	dlOpts := &store.DownloadOptions{
   571  		IsAutoRefresh: snapsup.IsAutoRefresh,
   572  		RateLimit:     rate,
   573  	}
   574  	if snapsup.DownloadInfo == nil {
   575  		var storeInfo store.SnapActionResult
   576  		// COMPATIBILITY - this task was created from an older version
   577  		// of snapd that did not store the DownloadInfo in the state
   578  		// yet. Therefore do not worry about DeviceContext.
   579  		storeInfo, err = installInfoUnlocked(st, snapsup, nil)
   580  		if err != nil {
   581  			return err
   582  		}
   583  		timings.Run(perfTimings, "download", fmt.Sprintf("download snap %q", snapsup.SnapName()), func(timings.Measurer) {
   584  			err = theStore.Download(tomb.Context(nil), snapsup.SnapName(), targetFn, &storeInfo.DownloadInfo, meter, user, dlOpts)
   585  		})
   586  		snapsup.SideInfo = &storeInfo.SideInfo
   587  	} else {
   588  		timings.Run(perfTimings, "download", fmt.Sprintf("download snap %q", snapsup.SnapName()), func(timings.Measurer) {
   589  			err = theStore.Download(tomb.Context(nil), snapsup.SnapName(), targetFn, snapsup.DownloadInfo, meter, user, dlOpts)
   590  		})
   591  	}
   592  	if err != nil {
   593  		return err
   594  	}
   595  
   596  	snapsup.SnapPath = targetFn
   597  
   598  	// update the snap setup for the follow up tasks
   599  	st.Lock()
   600  	t.Set("snap-setup", snapsup)
   601  	perfTimings.Save(st)
   602  	st.Unlock()
   603  
   604  	return nil
   605  }
   606  
   607  var (
   608  	mountPollInterval = 1 * time.Second
   609  )
   610  
   611  // hasOtherInstances checks whether there are other instances of the snap, be it
   612  // instance keyed or not
   613  func hasOtherInstances(st *state.State, instanceName string) (bool, error) {
   614  	snapName, _ := snap.SplitInstanceName(instanceName)
   615  	var all map[string]*json.RawMessage
   616  	if err := st.Get("snaps", &all); err != nil && err != state.ErrNoState {
   617  		return false, err
   618  	}
   619  	for otherName := range all {
   620  		if otherName == instanceName {
   621  			continue
   622  		}
   623  		if otherSnapName, _ := snap.SplitInstanceName(otherName); otherSnapName == snapName {
   624  			return true, nil
   625  		}
   626  	}
   627  	return false, nil
   628  }
   629  
   630  func (m *SnapManager) doMountSnap(t *state.Task, _ *tomb.Tomb) error {
   631  	st := t.State()
   632  	st.Lock()
   633  	perfTimings := state.TimingsForTask(t)
   634  	snapsup, snapst, err := snapSetupAndState(t)
   635  	st.Unlock()
   636  	if err != nil {
   637  		return err
   638  	}
   639  
   640  	curInfo, err := snapst.CurrentInfo()
   641  	if err != nil && err != ErrNoCurrent {
   642  		return err
   643  	}
   644  
   645  	m.backend.CurrentInfo(curInfo)
   646  
   647  	st.Lock()
   648  	deviceCtx, err := DeviceCtx(t.State(), t, nil)
   649  	st.Unlock()
   650  	if err != nil {
   651  		return err
   652  	}
   653  
   654  	timings.Run(perfTimings, "check-snap", fmt.Sprintf("check snap %q", snapsup.InstanceName()), func(timings.Measurer) {
   655  		err = checkSnap(st, snapsup.SnapPath, snapsup.InstanceName(), snapsup.SideInfo, curInfo, snapsup.Flags, deviceCtx)
   656  	})
   657  	if err != nil {
   658  		return err
   659  	}
   660  
   661  	cleanup := func() {
   662  		st.Lock()
   663  		defer st.Unlock()
   664  
   665  		otherInstances, err := hasOtherInstances(st, snapsup.InstanceName())
   666  		if err != nil {
   667  			t.Errorf("cannot cleanup partial setup snap %q: %v", snapsup.InstanceName(), err)
   668  			return
   669  		}
   670  
   671  		// remove snap dir is idempotent so it's ok to always call it in the cleanup path
   672  		if err := m.backend.RemoveSnapDir(snapsup.placeInfo(), otherInstances); err != nil {
   673  			t.Errorf("cannot cleanup partial setup snap %q: %v", snapsup.InstanceName(), err)
   674  		}
   675  
   676  	}
   677  
   678  	pb := NewTaskProgressAdapterUnlocked(t)
   679  	// TODO Use snapsup.Revision() to obtain the right info to mount
   680  	//      instead of assuming the candidate is the right one.
   681  	var snapType snap.Type
   682  	var installRecord *backend.InstallRecord
   683  	timings.Run(perfTimings, "setup-snap", fmt.Sprintf("setup snap %q", snapsup.InstanceName()), func(timings.Measurer) {
   684  		snapType, installRecord, err = m.backend.SetupSnap(snapsup.SnapPath, snapsup.InstanceName(), snapsup.SideInfo, deviceCtx, pb)
   685  	})
   686  	if err != nil {
   687  		cleanup()
   688  		return err
   689  	}
   690  
   691  	// double check that the snap is mounted
   692  	var readInfoErr error
   693  	for i := 0; i < 10; i++ {
   694  		_, readInfoErr = readInfo(snapsup.InstanceName(), snapsup.SideInfo, errorOnBroken)
   695  		if readInfoErr == nil {
   696  			break
   697  		}
   698  		if _, ok := readInfoErr.(*snap.NotFoundError); !ok {
   699  			break
   700  		}
   701  		// snap not found, seems is not mounted yet
   702  		msg := fmt.Sprintf("expected snap %q revision %v to be mounted but is not", snapsup.InstanceName(), snapsup.Revision())
   703  		readInfoErr = fmt.Errorf("cannot proceed, %s", msg)
   704  		if i == 0 {
   705  			logger.Noticef(msg)
   706  		}
   707  		time.Sleep(mountPollInterval)
   708  	}
   709  	if readInfoErr != nil {
   710  		timings.Run(perfTimings, "undo-setup-snap", fmt.Sprintf("Undo setup of snap %q", snapsup.InstanceName()), func(timings.Measurer) {
   711  			err = m.backend.UndoSetupSnap(snapsup.placeInfo(), snapType, installRecord, deviceCtx, pb)
   712  		})
   713  		if err != nil {
   714  			st.Lock()
   715  			t.Errorf("cannot undo partial setup snap %q: %v", snapsup.InstanceName(), err)
   716  			st.Unlock()
   717  		}
   718  
   719  		cleanup()
   720  		return readInfoErr
   721  	}
   722  
   723  	st.Lock()
   724  	// set snapst type for undoMountSnap
   725  	t.Set("snap-type", snapType)
   726  	if installRecord != nil {
   727  		t.Set("install-record", installRecord)
   728  	}
   729  	st.Unlock()
   730  
   731  	if snapsup.Flags.RemoveSnapPath {
   732  		if err := os.Remove(snapsup.SnapPath); err != nil {
   733  			logger.Noticef("Failed to cleanup %s: %s", snapsup.SnapPath, err)
   734  		}
   735  	}
   736  
   737  	st.Lock()
   738  	perfTimings.Save(st)
   739  	st.Unlock()
   740  
   741  	return nil
   742  }
   743  
   744  func (m *SnapManager) undoMountSnap(t *state.Task, _ *tomb.Tomb) error {
   745  	st := t.State()
   746  	st.Lock()
   747  	snapsup, err := TaskSnapSetup(t)
   748  	st.Unlock()
   749  	if err != nil {
   750  		return err
   751  	}
   752  
   753  	st.Lock()
   754  	deviceCtx, err := DeviceCtx(t.State(), t, nil)
   755  	st.Unlock()
   756  	if err != nil {
   757  		return err
   758  	}
   759  
   760  	st.Lock()
   761  	var typ snap.Type
   762  	err = t.Get("snap-type", &typ)
   763  	st.Unlock()
   764  	// backward compatibility
   765  	if err == state.ErrNoState {
   766  		typ = "app"
   767  	} else if err != nil {
   768  		return err
   769  	}
   770  
   771  	var installRecord backend.InstallRecord
   772  	st.Lock()
   773  	// install-record is optional (e.g. not present in tasks from older snapd)
   774  	err = t.Get("install-record", &installRecord)
   775  	st.Unlock()
   776  	if err != nil && err != state.ErrNoState {
   777  		return err
   778  	}
   779  
   780  	pb := NewTaskProgressAdapterUnlocked(t)
   781  	if err := m.backend.UndoSetupSnap(snapsup.placeInfo(), typ, &installRecord, deviceCtx, pb); err != nil {
   782  		return err
   783  	}
   784  
   785  	st.Lock()
   786  	defer st.Unlock()
   787  
   788  	otherInstances, err := hasOtherInstances(st, snapsup.InstanceName())
   789  	if err != nil {
   790  		return err
   791  	}
   792  
   793  	return m.backend.RemoveSnapDir(snapsup.placeInfo(), otherInstances)
   794  }
   795  
   796  // queryDisabledServices uses wrappers.QueryDisabledServices()
   797  //
   798  // Note this function takes a snap info rather than snapst because there are
   799  // situations where we want to call this on non-current snap infos, i.e. in the
   800  // undo handlers, see undoLinkSnap for an example.
   801  func (m *SnapManager) queryDisabledServices(info *snap.Info, pb progress.Meter) ([]string, error) {
   802  	return m.backend.QueryDisabledServices(info, pb)
   803  }
   804  
   805  func (m *SnapManager) doUnlinkCurrentSnap(t *state.Task, _ *tomb.Tomb) error {
   806  	st := t.State()
   807  	st.Lock()
   808  	defer st.Unlock()
   809  
   810  	snapsup, snapst, err := snapSetupAndState(t)
   811  	if err != nil {
   812  		return err
   813  	}
   814  
   815  	oldInfo, err := snapst.CurrentInfo()
   816  	if err != nil {
   817  		return err
   818  	}
   819  
   820  	tr := config.NewTransaction(st)
   821  	experimentalRefreshAppAwareness, err := features.Flag(tr, features.RefreshAppAwareness)
   822  	if err != nil && !config.IsNoOption(err) {
   823  		return err
   824  	}
   825  
   826  	if experimentalRefreshAppAwareness && !snapsup.Flags.IgnoreRunning {
   827  		// Invoke the hard refresh flow. Upon success the returned lock will be
   828  		// held to prevent snap-run from advancing until UnlinkSnap, executed
   829  		// below, completes.
   830  		lock, err := hardEnsureNothingRunningDuringRefresh(m.backend, st, snapst, oldInfo)
   831  		if err != nil {
   832  			return err
   833  		}
   834  		defer lock.Close()
   835  	}
   836  
   837  	snapst.Active = false
   838  
   839  	// do the final unlink
   840  	linkCtx := backend.LinkContext{
   841  		FirstInstall: false,
   842  		// This task is only used for unlinking a snap during refreshes so we
   843  		// can safely hard-code this condition here.
   844  		RunInhibitHint: runinhibit.HintInhibitedForRefresh,
   845  	}
   846  	err = m.backend.UnlinkSnap(oldInfo, linkCtx, NewTaskProgressAdapterLocked(t))
   847  	if err != nil {
   848  		return err
   849  	}
   850  
   851  	// mark as inactive
   852  	Set(st, snapsup.InstanceName(), snapst)
   853  
   854  	// Notify link snap participants about link changes.
   855  	notifyLinkParticipants(t, snapsup.InstanceName())
   856  	return nil
   857  }
   858  
   859  func (m *SnapManager) undoUnlinkCurrentSnap(t *state.Task, _ *tomb.Tomb) error {
   860  	st := t.State()
   861  	st.Lock()
   862  	defer st.Unlock()
   863  
   864  	perfTimings := state.TimingsForTask(t)
   865  	defer perfTimings.Save(st)
   866  
   867  	snapsup, snapst, err := snapSetupAndState(t)
   868  	if err != nil {
   869  		return err
   870  	}
   871  
   872  	oldInfo, err := snapst.CurrentInfo()
   873  	if err != nil {
   874  		return err
   875  	}
   876  
   877  	deviceCtx, err := DeviceCtx(st, t, nil)
   878  	if err != nil {
   879  		return err
   880  	}
   881  
   882  	snapst.Active = true
   883  	vitalityRank, err := vitalityRank(st, snapsup.InstanceName())
   884  	if err != nil {
   885  		return err
   886  	}
   887  	linkCtx := backend.LinkContext{
   888  		FirstInstall: false,
   889  		VitalityRank: vitalityRank,
   890  	}
   891  	reboot, err := m.backend.LinkSnap(oldInfo, deviceCtx, linkCtx, perfTimings)
   892  	if err != nil {
   893  		return err
   894  	}
   895  
   896  	// mark as active again
   897  	Set(st, snapsup.InstanceName(), snapst)
   898  
   899  	// Notify link snap participants about link changes.
   900  	notifyLinkParticipants(t, snapsup.InstanceName())
   901  
   902  	// if we just put back a previous a core snap, request a restart
   903  	// so that we switch executing its snapd
   904  	m.maybeRestart(t, oldInfo, reboot, deviceCtx)
   905  	return nil
   906  }
   907  
   908  func (m *SnapManager) doCopySnapData(t *state.Task, _ *tomb.Tomb) error {
   909  	st := t.State()
   910  	st.Lock()
   911  	snapsup, snapst, err := snapSetupAndState(t)
   912  	st.Unlock()
   913  	if err != nil {
   914  		return err
   915  	}
   916  
   917  	newInfo, err := readInfo(snapsup.InstanceName(), snapsup.SideInfo, 0)
   918  	if err != nil {
   919  		return err
   920  	}
   921  
   922  	oldInfo, err := snapst.CurrentInfo()
   923  	if err != nil && err != ErrNoCurrent {
   924  		return err
   925  	}
   926  
   927  	pb := NewTaskProgressAdapterUnlocked(t)
   928  	if copyDataErr := m.backend.CopySnapData(newInfo, oldInfo, pb); copyDataErr != nil {
   929  		if oldInfo != nil {
   930  			// there is another revision of the snap, cannot remove
   931  			// shared data directory
   932  			return copyDataErr
   933  		}
   934  
   935  		// cleanup shared snap data directory
   936  		st.Lock()
   937  		defer st.Unlock()
   938  
   939  		otherInstances, err := hasOtherInstances(st, snapsup.InstanceName())
   940  		if err != nil {
   941  			t.Errorf("cannot undo partial snap %q data copy: %v", snapsup.InstanceName(), err)
   942  			return copyDataErr
   943  		}
   944  		// no other instances of this snap, shared data directory can be
   945  		// removed now too
   946  		if err := m.backend.RemoveSnapDataDir(newInfo, otherInstances); err != nil {
   947  			t.Errorf("cannot undo partial snap %q data copy, failed removing shared directory: %v", snapsup.InstanceName(), err)
   948  		}
   949  		return copyDataErr
   950  	}
   951  	return nil
   952  }
   953  
   954  func (m *SnapManager) undoCopySnapData(t *state.Task, _ *tomb.Tomb) error {
   955  	st := t.State()
   956  	st.Lock()
   957  	snapsup, snapst, err := snapSetupAndState(t)
   958  	st.Unlock()
   959  	if err != nil {
   960  		return err
   961  	}
   962  
   963  	newInfo, err := readInfo(snapsup.InstanceName(), snapsup.SideInfo, 0)
   964  	if err != nil {
   965  		return err
   966  	}
   967  
   968  	oldInfo, err := snapst.CurrentInfo()
   969  	if err != nil && err != ErrNoCurrent {
   970  		return err
   971  	}
   972  
   973  	pb := NewTaskProgressAdapterUnlocked(t)
   974  	if err := m.backend.UndoCopySnapData(newInfo, oldInfo, pb); err != nil {
   975  		return err
   976  	}
   977  
   978  	if oldInfo != nil {
   979  		// there is other revision of this snap, cannot remove shared
   980  		// directory anyway
   981  		return nil
   982  	}
   983  
   984  	st.Lock()
   985  	defer st.Unlock()
   986  
   987  	otherInstances, err := hasOtherInstances(st, snapsup.InstanceName())
   988  	if err != nil {
   989  		return err
   990  	}
   991  	// no other instances of this snap and no other revisions, shared data
   992  	// directory can be removed
   993  	if err := m.backend.RemoveSnapDataDir(newInfo, otherInstances); err != nil {
   994  		return err
   995  	}
   996  	return nil
   997  }
   998  
   999  func (m *SnapManager) cleanupCopySnapData(t *state.Task, _ *tomb.Tomb) error {
  1000  	st := t.State()
  1001  	st.Lock()
  1002  	defer st.Unlock()
  1003  
  1004  	if t.Status() != state.DoneStatus {
  1005  		// it failed
  1006  		return nil
  1007  	}
  1008  
  1009  	_, snapst, err := snapSetupAndState(t)
  1010  	if err != nil {
  1011  		return err
  1012  	}
  1013  
  1014  	info, err := snapst.CurrentInfo()
  1015  	if err != nil {
  1016  		return err
  1017  	}
  1018  
  1019  	m.backend.ClearTrashedData(info)
  1020  
  1021  	return nil
  1022  }
  1023  
  1024  // writeSeqFile writes the sequence file for failover handling
  1025  func writeSeqFile(name string, snapst *SnapState) error {
  1026  	p := filepath.Join(dirs.SnapSeqDir, name+".json")
  1027  	if err := os.MkdirAll(filepath.Dir(p), 0755); err != nil {
  1028  		return err
  1029  	}
  1030  
  1031  	b, err := json.Marshal(&struct {
  1032  		Sequence []*snap.SideInfo `json:"sequence"`
  1033  		Current  string           `json:"current"`
  1034  	}{
  1035  		Sequence: snapst.Sequence,
  1036  		Current:  snapst.Current.String(),
  1037  	})
  1038  	if err != nil {
  1039  		return err
  1040  	}
  1041  
  1042  	return osutil.AtomicWriteFile(p, b, 0644, 0)
  1043  }
  1044  
  1045  // missingDisabledServices returns a list of services that are present in
  1046  // this snap info and should be disabled as well as a list of disabled
  1047  // services that are currently missing (i.e. they were renamed).
  1048  // present in this snap info.
  1049  // the first arg is the disabled services when the snap was last active
  1050  func missingDisabledServices(svcs []string, info *snap.Info) ([]string, []string, error) {
  1051  	// make a copy of all the previously disabled services that we will remove
  1052  	// from, as well as an empty list to add to for the found services
  1053  	missingSvcs := []string{}
  1054  	foundSvcs := []string{}
  1055  
  1056  	// for all the previously disabled services, check if they are in the
  1057  	// current snap info revision as services or not
  1058  	for _, disabledSvcName := range svcs {
  1059  		// check if the service is an app _and_ is a service
  1060  		if app, ok := info.Apps[disabledSvcName]; ok && app.IsService() {
  1061  			foundSvcs = append(foundSvcs, disabledSvcName)
  1062  		} else {
  1063  			missingSvcs = append(missingSvcs, disabledSvcName)
  1064  		}
  1065  	}
  1066  
  1067  	// sort the lists for easier testing
  1068  	sort.Strings(missingSvcs)
  1069  	sort.Strings(foundSvcs)
  1070  
  1071  	return foundSvcs, missingSvcs, nil
  1072  }
  1073  
  1074  func vitalityRank(st *state.State, instanceName string) (rank int, err error) {
  1075  	tr := config.NewTransaction(st)
  1076  
  1077  	var vitalityStr string
  1078  	err = tr.GetMaybe("core", "resilience.vitality-hint", &vitalityStr)
  1079  	if err != nil {
  1080  		return 0, err
  1081  	}
  1082  	for i, s := range strings.Split(vitalityStr, ",") {
  1083  		if s == instanceName {
  1084  			return i + 1, nil
  1085  		}
  1086  	}
  1087  	return 0, nil
  1088  }
  1089  
  1090  // LinkSnapParticipant is an interface for interacting with snap link/unlink
  1091  // operations.
  1092  //
  1093  // Unlike the interface for a task handler, only one notification method is
  1094  // used. The method notifies a participant that linkage of a snap has changed.
  1095  // This method is invoked in link-snap, unlink-snap, the undo path of those
  1096  // methods and the undo handler for link-snap.
  1097  //
  1098  // In all cases it is invoked after all other operations are completed but
  1099  // before the task completes.
  1100  type LinkSnapParticipant interface {
  1101  	// SnapLinkageChanged is called when a snap is linked or unlinked.
  1102  	// The error is only logged and does not stop the task it is used from.
  1103  	SnapLinkageChanged(st *state.State, instanceName string) error
  1104  }
  1105  
  1106  var linkSnapParticipants []LinkSnapParticipant
  1107  
  1108  // AddLinkSnapParticipant adds a participant in the link/unlink operations.
  1109  func AddLinkSnapParticipant(p LinkSnapParticipant) {
  1110  	linkSnapParticipants = append(linkSnapParticipants, p)
  1111  }
  1112  
  1113  // MockLinkSnapParticipants replaces the list of link snap participants for testing.
  1114  func MockLinkSnapParticipants(ps []LinkSnapParticipant) (restore func()) {
  1115  	old := linkSnapParticipants
  1116  	linkSnapParticipants = ps
  1117  	return func() {
  1118  		linkSnapParticipants = old
  1119  	}
  1120  }
  1121  
  1122  func notifyLinkParticipants(t *state.Task, instanceName string) {
  1123  	st := t.State()
  1124  	for _, p := range linkSnapParticipants {
  1125  		if err := p.SnapLinkageChanged(st, instanceName); err != nil {
  1126  			t.Errorf("%v", err)
  1127  		}
  1128  	}
  1129  }
  1130  
  1131  func (m *SnapManager) doLinkSnap(t *state.Task, _ *tomb.Tomb) (err error) {
  1132  	st := t.State()
  1133  	st.Lock()
  1134  	defer st.Unlock()
  1135  
  1136  	perfTimings := state.TimingsForTask(t)
  1137  	defer perfTimings.Save(st)
  1138  
  1139  	snapsup, snapst, err := snapSetupAndState(t)
  1140  	if err != nil {
  1141  		return err
  1142  	}
  1143  
  1144  	deviceCtx, err := DeviceCtx(st, t, nil)
  1145  	if err != nil {
  1146  		return err
  1147  	}
  1148  
  1149  	// find if the snap is already installed before we modify snapst below
  1150  	isInstalled := snapst.IsInstalled()
  1151  
  1152  	cand := snapsup.SideInfo
  1153  	m.backend.Candidate(cand)
  1154  
  1155  	oldCandidateIndex := snapst.LastIndex(cand.Revision)
  1156  
  1157  	if oldCandidateIndex < 0 {
  1158  		snapst.Sequence = append(snapst.Sequence, cand)
  1159  	} else if !snapsup.Revert {
  1160  		// remove the old candidate from the sequence, add it at the end
  1161  		copy(snapst.Sequence[oldCandidateIndex:len(snapst.Sequence)-1], snapst.Sequence[oldCandidateIndex+1:])
  1162  		snapst.Sequence[len(snapst.Sequence)-1] = cand
  1163  	}
  1164  
  1165  	oldCurrent := snapst.Current
  1166  	snapst.Current = cand.Revision
  1167  	snapst.Active = true
  1168  	oldChannel := snapst.TrackingChannel
  1169  	if snapsup.Channel != "" {
  1170  		err := snapst.SetTrackingChannel(snapsup.Channel)
  1171  		if err != nil {
  1172  			return err
  1173  		}
  1174  	}
  1175  	oldIgnoreValidation := snapst.IgnoreValidation
  1176  	snapst.IgnoreValidation = snapsup.IgnoreValidation
  1177  	oldTryMode := snapst.TryMode
  1178  	snapst.TryMode = snapsup.TryMode
  1179  	oldDevMode := snapst.DevMode
  1180  	snapst.DevMode = snapsup.DevMode
  1181  	oldJailMode := snapst.JailMode
  1182  	snapst.JailMode = snapsup.JailMode
  1183  	oldClassic := snapst.Classic
  1184  	snapst.Classic = snapsup.Classic
  1185  	oldCohortKey := snapst.CohortKey
  1186  	snapst.CohortKey = snapsup.CohortKey
  1187  	if snapsup.Required { // set only on install and left alone on refresh
  1188  		snapst.Required = true
  1189  	}
  1190  	oldRefreshInhibitedTime := snapst.RefreshInhibitedTime
  1191  	// only set userID if unset or logged out in snapst and if we
  1192  	// actually have an associated user
  1193  	if snapsup.UserID > 0 {
  1194  		var user *auth.UserState
  1195  		if snapst.UserID != 0 {
  1196  			user, err = auth.User(st, snapst.UserID)
  1197  			if err != nil && err != auth.ErrInvalidUser {
  1198  				return err
  1199  			}
  1200  		}
  1201  		if user == nil {
  1202  			// if the original user installing the snap is
  1203  			// no longer available transfer to user who
  1204  			// triggered this change
  1205  			snapst.UserID = snapsup.UserID
  1206  		}
  1207  	}
  1208  	// keep instance key
  1209  	snapst.InstanceKey = snapsup.InstanceKey
  1210  
  1211  	newInfo, err := readInfo(snapsup.InstanceName(), cand, 0)
  1212  	if err != nil {
  1213  		return err
  1214  	}
  1215  
  1216  	// record type
  1217  	snapst.SetType(newInfo.Type())
  1218  
  1219  	pb := NewTaskProgressAdapterLocked(t)
  1220  
  1221  	// Check for D-Bus service conflicts a second time to detect
  1222  	// conflicts within a transaction.
  1223  	if err := checkDBusServiceConflicts(st, newInfo); err != nil {
  1224  		return err
  1225  	}
  1226  
  1227  	vitalityRank, err := vitalityRank(st, snapsup.InstanceName())
  1228  	if err != nil {
  1229  		return err
  1230  	}
  1231  	linkCtx := backend.LinkContext{
  1232  		FirstInstall: oldCurrent.Unset(),
  1233  		VitalityRank: vitalityRank,
  1234  	}
  1235  	reboot, err := m.backend.LinkSnap(newInfo, deviceCtx, linkCtx, perfTimings)
  1236  	// defer a cleanup helper which will unlink the snap if anything fails after
  1237  	// this point
  1238  	defer func() {
  1239  		if err == nil {
  1240  			return
  1241  		}
  1242  		// err is not nil, we need to try and unlink the snap to cleanup after
  1243  		// ourselves
  1244  		var unlinkErr error
  1245  		unlinkErr = m.backend.UnlinkSnap(newInfo, linkCtx, pb)
  1246  		if unlinkErr != nil {
  1247  			t.Errorf("cannot cleanup failed attempt at making snap %q available to the system: %v", snapsup.InstanceName(), unlinkErr)
  1248  		}
  1249  		notifyLinkParticipants(t, snapsup.InstanceName())
  1250  	}()
  1251  	if err != nil {
  1252  		return err
  1253  	}
  1254  
  1255  	// Restore configuration of the target revision (if available) on revert
  1256  	if isInstalled {
  1257  		// Make a copy of configuration of current snap revision
  1258  		if err = config.SaveRevisionConfig(st, snapsup.InstanceName(), oldCurrent); err != nil {
  1259  			return err
  1260  		}
  1261  	}
  1262  
  1263  	// Restore configuration of the target revision (if available; nothing happens if it's not).
  1264  	// We only do this on reverts (and not on refreshes).
  1265  	if snapsup.Revert {
  1266  		if err = config.RestoreRevisionConfig(st, snapsup.InstanceName(), snapsup.Revision()); err != nil {
  1267  			return err
  1268  		}
  1269  	}
  1270  
  1271  	if len(snapst.Sequence) == 1 {
  1272  		if err := m.createSnapCookie(st, snapsup.InstanceName()); err != nil {
  1273  			return fmt.Errorf("cannot create snap cookie: %v", err)
  1274  		}
  1275  	}
  1276  	// save for undoLinkSnap
  1277  	t.Set("old-trymode", oldTryMode)
  1278  	t.Set("old-devmode", oldDevMode)
  1279  	t.Set("old-jailmode", oldJailMode)
  1280  	t.Set("old-classic", oldClassic)
  1281  	t.Set("old-ignore-validation", oldIgnoreValidation)
  1282  	t.Set("old-channel", oldChannel)
  1283  	t.Set("old-current", oldCurrent)
  1284  	t.Set("old-candidate-index", oldCandidateIndex)
  1285  	t.Set("old-refresh-inhibited-time", oldRefreshInhibitedTime)
  1286  	t.Set("old-cohort-key", oldCohortKey)
  1287  
  1288  	// Record the fact that the snap was refreshed successfully.
  1289  	snapst.RefreshInhibitedTime = nil
  1290  
  1291  	if cand.SnapID != "" {
  1292  		// write the auxiliary store info
  1293  		aux := &auxStoreInfo{
  1294  			Media:   snapsup.Media,
  1295  			Website: snapsup.Website,
  1296  		}
  1297  		if err := keepAuxStoreInfo(cand.SnapID, aux); err != nil {
  1298  			return err
  1299  		}
  1300  		if len(snapst.Sequence) == 1 {
  1301  			defer func() {
  1302  				if err != nil {
  1303  					// the install is getting undone, and there are no more of this snap
  1304  					// try to remove the aux info we just created
  1305  					discardAuxStoreInfo(cand.SnapID)
  1306  				}
  1307  			}()
  1308  		}
  1309  	}
  1310  
  1311  	// write sequence file for failover helpers
  1312  	if err := writeSeqFile(snapsup.InstanceName(), snapst); err != nil {
  1313  		return err
  1314  	}
  1315  
  1316  	// Compatibility with old snapd: check if we have auto-connect task and
  1317  	// if not, inject it after self (link-snap) for snaps that are not core
  1318  	if newInfo.Type() != snap.TypeOS {
  1319  		var hasAutoConnect, hasSetupProfiles bool
  1320  		for _, other := range t.Change().Tasks() {
  1321  			// Check if this is auto-connect task for same snap and we it's part of the change with setup-profiles task
  1322  			if other.Kind() == "auto-connect" || other.Kind() == "setup-profiles" {
  1323  				otherSnapsup, err := TaskSnapSetup(other)
  1324  				if err != nil {
  1325  					return err
  1326  				}
  1327  				if snapsup.InstanceName() == otherSnapsup.InstanceName() {
  1328  					if other.Kind() == "auto-connect" {
  1329  						hasAutoConnect = true
  1330  					} else {
  1331  						hasSetupProfiles = true
  1332  					}
  1333  				}
  1334  			}
  1335  		}
  1336  		if !hasAutoConnect && hasSetupProfiles {
  1337  			InjectAutoConnect(t, snapsup)
  1338  		}
  1339  	}
  1340  
  1341  	// Do at the end so we only preserve the new state if it worked.
  1342  	Set(st, snapsup.InstanceName(), snapst)
  1343  
  1344  	// Notify link snap participants about link changes.
  1345  	notifyLinkParticipants(t, snapsup.InstanceName())
  1346  
  1347  	// Make sure if state commits and snapst is mutated we won't be rerun
  1348  	t.SetStatus(state.DoneStatus)
  1349  
  1350  	// if we just installed a core snap, request a restart
  1351  	// so that we switch executing its snapd.
  1352  	m.maybeRestart(t, newInfo, reboot, deviceCtx)
  1353  
  1354  	return nil
  1355  }
  1356  
  1357  // maybeRestart will schedule a reboot or restart as needed for the
  1358  // just linked snap with info if it's a core or snapd or kernel snap.
  1359  func (m *SnapManager) maybeRestart(t *state.Task, info *snap.Info, rebootRequired bool, deviceCtx DeviceContext) {
  1360  	// Don't restart when preseeding - we will switch to new snapd on
  1361  	// first boot.
  1362  	if m.preseed {
  1363  		return
  1364  	}
  1365  
  1366  	st := t.State()
  1367  
  1368  	if rebootRequired {
  1369  		t.Logf("Requested system restart.")
  1370  		st.RequestRestart(state.RestartSystem)
  1371  		return
  1372  	}
  1373  
  1374  	typ := info.Type()
  1375  
  1376  	// if bp is non-trivial then either we're not on classic, or the snap is
  1377  	// snapd. So daemonRestartReason will always return "" which is what we
  1378  	// want. If that combination stops being true and there's a situation
  1379  	// where a non-trivial bp could return a non-empty reason, use IsTrivial
  1380  	// to check and bail before reaching this far.
  1381  
  1382  	restartReason := daemonRestartReason(st, typ)
  1383  	if restartReason == "" {
  1384  		// no message -> no restart
  1385  		return
  1386  	}
  1387  
  1388  	t.Logf(restartReason)
  1389  	st.RequestRestart(state.RestartDaemon)
  1390  }
  1391  
  1392  func daemonRestartReason(st *state.State, typ snap.Type) string {
  1393  	if !((release.OnClassic && typ == snap.TypeOS) || typ == snap.TypeSnapd) {
  1394  		// not interesting
  1395  		return ""
  1396  	}
  1397  
  1398  	if typ == snap.TypeOS {
  1399  		// ignore error here as we have no way to return to caller
  1400  		snapdSnapInstalled, _ := isInstalled(st, "snapd")
  1401  		if snapdSnapInstalled {
  1402  			// this snap is the base, but snapd is running from the snapd snap
  1403  			return ""
  1404  		}
  1405  		return "Requested daemon restart."
  1406  	}
  1407  
  1408  	return "Requested daemon restart (snapd snap)."
  1409  }
  1410  
  1411  // maybeUndoRemodelBootChanges will check if an undo needs to update the
  1412  // bootloader. This can happen if e.g. a new kernel gets installed. This
  1413  // will switch the bootloader to the new kernel but if the change is later
  1414  // undone we need to switch back to the kernel of the old model.
  1415  func (m *SnapManager) maybeUndoRemodelBootChanges(t *state.Task) error {
  1416  	// get the new and the old model
  1417  	deviceCtx, err := DeviceCtx(t.State(), t, nil)
  1418  	if err != nil {
  1419  		return err
  1420  	}
  1421  	// we only have an old model if we are in a remodel situation
  1422  	if !deviceCtx.ForRemodeling() {
  1423  		return nil
  1424  	}
  1425  	groundDeviceCtx := deviceCtx.GroundContext()
  1426  	oldModel := groundDeviceCtx.Model()
  1427  	newModel := deviceCtx.Model()
  1428  
  1429  	// check type of the snap we are undoing, only kernel/base/core are
  1430  	// relevant
  1431  	snapsup, _, err := snapSetupAndState(t)
  1432  	if err != nil {
  1433  		return err
  1434  	}
  1435  	var newSnapName, snapName string
  1436  	switch snapsup.Type {
  1437  	case snap.TypeKernel:
  1438  		snapName = oldModel.Kernel()
  1439  		newSnapName = newModel.Kernel()
  1440  	case snap.TypeOS, snap.TypeBase:
  1441  		// XXX: add support for "core"
  1442  		snapName = oldModel.Base()
  1443  		newSnapName = newModel.Base()
  1444  	default:
  1445  		return nil
  1446  	}
  1447  	// we can stop if the kernel/base has not changed
  1448  	if snapName == newSnapName {
  1449  		return nil
  1450  	}
  1451  	// we can stop if the snap we are looking at is not a kernel/base
  1452  	// of the new model
  1453  	if snapsup.InstanceName() != newSnapName {
  1454  		return nil
  1455  	}
  1456  	// get info for *old* kernel/base/core and see if we need to reboot
  1457  	// TODO: we may need something like infoForDeviceSnap here
  1458  	var snapst SnapState
  1459  	if err = Get(t.State(), snapName, &snapst); err != nil {
  1460  		return err
  1461  	}
  1462  	info, err := snapst.CurrentInfo()
  1463  	if err != nil && err != ErrNoCurrent {
  1464  		return err
  1465  	}
  1466  	bp := boot.Participant(info, info.Type(), groundDeviceCtx)
  1467  	reboot, err := bp.SetNextBoot()
  1468  	if err != nil {
  1469  		return err
  1470  	}
  1471  
  1472  	// we may just have switch back to the old kernel/base/core so
  1473  	// we may need to restart
  1474  	m.maybeRestart(t, info, reboot, groundDeviceCtx)
  1475  
  1476  	return nil
  1477  }
  1478  
  1479  func (m *SnapManager) undoLinkSnap(t *state.Task, _ *tomb.Tomb) error {
  1480  	st := t.State()
  1481  	st.Lock()
  1482  	defer st.Unlock()
  1483  
  1484  	perfTimings := state.TimingsForTask(t)
  1485  	defer perfTimings.Save(st)
  1486  
  1487  	snapsup, snapst, err := snapSetupAndState(t)
  1488  	if err != nil {
  1489  		return err
  1490  	}
  1491  
  1492  	var oldChannel string
  1493  	err = t.Get("old-channel", &oldChannel)
  1494  	if err != nil {
  1495  		return err
  1496  	}
  1497  	var oldIgnoreValidation bool
  1498  	err = t.Get("old-ignore-validation", &oldIgnoreValidation)
  1499  	if err != nil && err != state.ErrNoState {
  1500  		return err
  1501  	}
  1502  	var oldTryMode bool
  1503  	err = t.Get("old-trymode", &oldTryMode)
  1504  	if err != nil {
  1505  		return err
  1506  	}
  1507  	var oldDevMode bool
  1508  	err = t.Get("old-devmode", &oldDevMode)
  1509  	if err != nil {
  1510  		return err
  1511  	}
  1512  	var oldJailMode bool
  1513  	err = t.Get("old-jailmode", &oldJailMode)
  1514  	if err != nil {
  1515  		return err
  1516  	}
  1517  	var oldClassic bool
  1518  	err = t.Get("old-classic", &oldClassic)
  1519  	if err != nil {
  1520  		return err
  1521  	}
  1522  	var oldCurrent snap.Revision
  1523  	err = t.Get("old-current", &oldCurrent)
  1524  	if err != nil {
  1525  		return err
  1526  	}
  1527  	var oldCandidateIndex int
  1528  	if err := t.Get("old-candidate-index", &oldCandidateIndex); err != nil {
  1529  		return err
  1530  	}
  1531  	var oldRefreshInhibitedTime *time.Time
  1532  	if err := t.Get("old-refresh-inhibited-time", &oldRefreshInhibitedTime); err != nil && err != state.ErrNoState {
  1533  		return err
  1534  	}
  1535  	var oldCohortKey string
  1536  	if err := t.Get("old-cohort-key", &oldCohortKey); err != nil && err != state.ErrNoState {
  1537  		return err
  1538  	}
  1539  
  1540  	if len(snapst.Sequence) == 1 {
  1541  		// XXX: shouldn't these two just log and carry on? this is an undo handler...
  1542  		timings.Run(perfTimings, "discard-snap-namespace", fmt.Sprintf("discard the namespace of snap %q", snapsup.InstanceName()), func(tm timings.Measurer) {
  1543  			err = m.backend.DiscardSnapNamespace(snapsup.InstanceName())
  1544  		})
  1545  		if err != nil {
  1546  			t.Errorf("cannot discard snap namespace %q, will retry in 3 mins: %s", snapsup.InstanceName(), err)
  1547  			return &state.Retry{After: 3 * time.Minute}
  1548  		}
  1549  		if err := m.removeSnapCookie(st, snapsup.InstanceName()); err != nil {
  1550  			return fmt.Errorf("cannot remove snap cookie: %v", err)
  1551  		}
  1552  		// try to remove the auxiliary store info
  1553  		if err := discardAuxStoreInfo(snapsup.SideInfo.SnapID); err != nil {
  1554  			return fmt.Errorf("cannot remove auxiliary store info: %v", err)
  1555  		}
  1556  	}
  1557  
  1558  	isRevert := snapsup.Revert
  1559  
  1560  	// relinking of the old snap is done in the undo of unlink-current-snap
  1561  	currentIndex := snapst.LastIndex(snapst.Current)
  1562  	if currentIndex < 0 {
  1563  		return fmt.Errorf("internal error: cannot find revision %d in %v for undoing the added revision", snapsup.SideInfo.Revision, snapst.Sequence)
  1564  	}
  1565  
  1566  	if oldCandidateIndex < 0 {
  1567  		snapst.Sequence = append(snapst.Sequence[:currentIndex], snapst.Sequence[currentIndex+1:]...)
  1568  	} else if !isRevert {
  1569  		oldCand := snapst.Sequence[currentIndex]
  1570  		copy(snapst.Sequence[oldCandidateIndex+1:], snapst.Sequence[oldCandidateIndex:])
  1571  		snapst.Sequence[oldCandidateIndex] = oldCand
  1572  	}
  1573  	snapst.Current = oldCurrent
  1574  	snapst.Active = false
  1575  	snapst.TrackingChannel = oldChannel
  1576  	snapst.IgnoreValidation = oldIgnoreValidation
  1577  	snapst.TryMode = oldTryMode
  1578  	snapst.DevMode = oldDevMode
  1579  	snapst.JailMode = oldJailMode
  1580  	snapst.Classic = oldClassic
  1581  	snapst.RefreshInhibitedTime = oldRefreshInhibitedTime
  1582  	snapst.CohortKey = oldCohortKey
  1583  
  1584  	newInfo, err := readInfo(snapsup.InstanceName(), snapsup.SideInfo, 0)
  1585  	if err != nil {
  1586  		return err
  1587  	}
  1588  
  1589  	// we need to undo potential changes to current snap configuration (e.g. if
  1590  	// modified by post-refresh/install/configure hooks as part of failed
  1591  	// refresh/install) by restoring the configuration of "old current".
  1592  	// similarly, we need to re-save the disabled services if there is a
  1593  	// revision for us to go back to, see comment below for full explanation
  1594  	if len(snapst.Sequence) > 0 {
  1595  		if err = config.RestoreRevisionConfig(st, snapsup.InstanceName(), oldCurrent); err != nil {
  1596  			return err
  1597  		}
  1598  	} else {
  1599  		// in the case of an install we need to clear any config
  1600  		err = config.DeleteSnapConfig(st, snapsup.InstanceName())
  1601  		if err != nil {
  1602  			return err
  1603  		}
  1604  	}
  1605  
  1606  	pb := NewTaskProgressAdapterLocked(t)
  1607  	linkCtx := backend.LinkContext{
  1608  		FirstInstall: oldCurrent.Unset(),
  1609  	}
  1610  	err = m.backend.UnlinkSnap(newInfo, linkCtx, pb)
  1611  	if err != nil {
  1612  		return err
  1613  	}
  1614  
  1615  	if err := m.maybeUndoRemodelBootChanges(t); err != nil {
  1616  		return err
  1617  	}
  1618  
  1619  	// restart only when snapd was installed for the first time and the rest of
  1620  	// the cleanup is performed by snapd from core;
  1621  	// when reverting a subsequent snapd revision, the restart happens in
  1622  	// undoLinkCurrentSnap() instead
  1623  	if linkCtx.FirstInstall && newInfo.Type() == snap.TypeSnapd {
  1624  		// only way to get
  1625  		deviceCtx, err := DeviceCtx(st, t, nil)
  1626  		if err != nil {
  1627  			return err
  1628  		}
  1629  		const rebootRequired = false
  1630  		m.maybeRestart(t, newInfo, rebootRequired, deviceCtx)
  1631  	}
  1632  
  1633  	// write sequence file for failover helpers
  1634  	if err := writeSeqFile(snapsup.InstanceName(), snapst); err != nil {
  1635  		return err
  1636  	}
  1637  	// mark as inactive
  1638  	Set(st, snapsup.InstanceName(), snapst)
  1639  
  1640  	// Notify link snap participants about link changes.
  1641  	notifyLinkParticipants(t, snapsup.InstanceName())
  1642  
  1643  	// Make sure if state commits and snapst is mutated we won't be rerun
  1644  	t.SetStatus(state.UndoneStatus)
  1645  
  1646  	// If we are on classic and have no previous version of core
  1647  	// we may have restarted from a distro package into the core
  1648  	// snap. We need to undo that restart here. Instead of in
  1649  	// doUnlinkCurrentSnap() like we usually do when going from
  1650  	// core snap -> next core snap
  1651  	if release.OnClassic && newInfo.Type() == snap.TypeOS && oldCurrent.Unset() {
  1652  		t.Logf("Requested daemon restart (undo classic initial core install)")
  1653  		st.RequestRestart(state.RestartDaemon)
  1654  	}
  1655  	return nil
  1656  }
  1657  
  1658  type doSwitchFlags struct {
  1659  	switchCurrentChannel bool
  1660  }
  1661  
  1662  // doSwitchSnapChannel switches the snap's tracking channel and/or cohort. It
  1663  // also switches the current channel if appropriate. For use from 'Update'.
  1664  func (m *SnapManager) doSwitchSnapChannel(t *state.Task, _ *tomb.Tomb) error {
  1665  	return m.genericDoSwitchSnap(t, doSwitchFlags{switchCurrentChannel: true})
  1666  }
  1667  
  1668  // doSwitchSnap switches the snap's tracking channel and/or cohort, *without*
  1669  // switching the current snap channel. For use from 'Switch'.
  1670  func (m *SnapManager) doSwitchSnap(t *state.Task, _ *tomb.Tomb) error {
  1671  	return m.genericDoSwitchSnap(t, doSwitchFlags{})
  1672  }
  1673  
  1674  func (m *SnapManager) genericDoSwitchSnap(t *state.Task, flags doSwitchFlags) error {
  1675  	st := t.State()
  1676  	st.Lock()
  1677  	defer st.Unlock()
  1678  
  1679  	snapsup, snapst, err := snapSetupAndState(t)
  1680  	if err != nil {
  1681  		return err
  1682  	}
  1683  
  1684  	// switched the tracked channel
  1685  	if err := snapst.SetTrackingChannel(snapsup.Channel); err != nil {
  1686  		return err
  1687  	}
  1688  	snapst.CohortKey = snapsup.CohortKey
  1689  	if flags.switchCurrentChannel {
  1690  		// optionally support switching the current snap channel too, e.g.
  1691  		// if a snap is in both stable and candidate with the same revision
  1692  		// we can update it here and it will be displayed correctly in the UI
  1693  		if snapsup.SideInfo.Channel != "" {
  1694  			snapst.CurrentSideInfo().Channel = snapsup.Channel
  1695  		}
  1696  	}
  1697  
  1698  	Set(st, snapsup.InstanceName(), snapst)
  1699  	return nil
  1700  }
  1701  
  1702  func (m *SnapManager) doToggleSnapFlags(t *state.Task, _ *tomb.Tomb) error {
  1703  	st := t.State()
  1704  	st.Lock()
  1705  	defer st.Unlock()
  1706  
  1707  	snapsup, snapst, err := snapSetupAndState(t)
  1708  	if err != nil {
  1709  		return err
  1710  	}
  1711  
  1712  	// for now we support toggling only ignore-validation
  1713  	snapst.IgnoreValidation = snapsup.IgnoreValidation
  1714  
  1715  	Set(st, snapsup.InstanceName(), snapst)
  1716  	return nil
  1717  }
  1718  
  1719  // installModeDisabledServices returns what services with
  1720  // "install-mode: disabled" should be disabled. Only services
  1721  // seen for the first time are considered.
  1722  func installModeDisabledServices(st *state.State, snapst *SnapState, currentInfo *snap.Info) (svcsToDisable []string, err error) {
  1723  	enabledByHookSvcs := map[string]bool{}
  1724  	for _, svcName := range snapst.ServicesEnabledByHooks {
  1725  		enabledByHookSvcs[svcName] = true
  1726  	}
  1727  
  1728  	// find what servies the previous snap had
  1729  	prevCurrentSvcs := map[string]bool{}
  1730  	if psi := snapst.previousSideInfo(); psi != nil {
  1731  		var prevCurrentInfo *snap.Info
  1732  		if prevCurrentInfo, err = Info(st, snapst.InstanceName(), psi.Revision); prevCurrentInfo != nil {
  1733  			for _, prevSvc := range prevCurrentInfo.Services() {
  1734  				prevCurrentSvcs[prevSvc.Name] = true
  1735  			}
  1736  		}
  1737  	}
  1738  	// and deal with "install-mode: disable" for all new services
  1739  	// (i.e. not present in previous snap).
  1740  	//
  1741  	// Services that are not new but have "install-mode: disable"
  1742  	// do not need special handling. They are either still disabled
  1743  	// or something has enabled them and then they should stay enabled.
  1744  	for _, svc := range currentInfo.Services() {
  1745  		if svc.InstallMode == "disable" && !enabledByHookSvcs[svc.Name] {
  1746  			if !prevCurrentSvcs[svc.Name] {
  1747  				svcsToDisable = append(svcsToDisable, svc.Name)
  1748  			}
  1749  		}
  1750  	}
  1751  	return svcsToDisable, nil
  1752  }
  1753  
  1754  func (m *SnapManager) startSnapServices(t *state.Task, _ *tomb.Tomb) error {
  1755  	st := t.State()
  1756  	st.Lock()
  1757  	defer st.Unlock()
  1758  
  1759  	perfTimings := state.TimingsForTask(t)
  1760  	defer perfTimings.Save(st)
  1761  
  1762  	snapsup, snapst, err := snapSetupAndState(t)
  1763  	if err != nil {
  1764  		return err
  1765  	}
  1766  	currentInfo, err := snapst.CurrentInfo()
  1767  	if err != nil {
  1768  		return err
  1769  	}
  1770  
  1771  	// check if any previously disabled services are now no longer services and
  1772  	// log messages about that
  1773  	for _, svc := range snapst.LastActiveDisabledServices {
  1774  		app, ok := currentInfo.Apps[svc]
  1775  		if !ok {
  1776  			logger.Noticef("previously disabled service %s no longer exists", svc)
  1777  		} else if !app.IsService() {
  1778  			logger.Noticef("previously disabled service %s is now an app and not a service", svc)
  1779  		}
  1780  	}
  1781  
  1782  	// get the services which should be disabled (not started),
  1783  	// as well as the services which are not present in this revision, but were
  1784  	// present and disabled in a previous one and as such should be kept inside
  1785  	// snapst for persistent storage
  1786  	svcsToDisable, svcsToSave, err := missingDisabledServices(snapst.LastActiveDisabledServices, currentInfo)
  1787  	if err != nil {
  1788  		return err
  1789  	}
  1790  
  1791  	// check what services with "InstallMode: disable" need to be disabled
  1792  	svcsToDisableFromInstallMode, err := installModeDisabledServices(st, snapst, currentInfo)
  1793  	if err != nil {
  1794  		return err
  1795  	}
  1796  	svcsToDisable = append(svcsToDisable, svcsToDisableFromInstallMode...)
  1797  
  1798  	// append services that were disabled by hooks (they should not get re-enabled)
  1799  	svcsToDisable = append(svcsToDisable, snapst.ServicesDisabledByHooks...)
  1800  
  1801  	// save the current last-active-disabled-services before we re-write it in case we
  1802  	// need to undo this
  1803  	t.Set("old-last-active-disabled-services", snapst.LastActiveDisabledServices)
  1804  
  1805  	// commit the missing services to state so when we unlink this revision and
  1806  	// go to a different revision with potentially different service names, the
  1807  	// currently missing service names will be re-disabled if they exist later
  1808  	snapst.LastActiveDisabledServices = svcsToSave
  1809  
  1810  	// reset services tracked by operations from hooks
  1811  	snapst.ServicesDisabledByHooks = nil
  1812  	snapst.ServicesEnabledByHooks = nil
  1813  	Set(st, snapsup.InstanceName(), snapst)
  1814  
  1815  	svcs := currentInfo.Services()
  1816  	if len(svcs) == 0 {
  1817  		return nil
  1818  	}
  1819  
  1820  	startupOrdered, err := snap.SortServices(svcs)
  1821  	if err != nil {
  1822  		return err
  1823  	}
  1824  
  1825  	pb := NewTaskProgressAdapterUnlocked(t)
  1826  
  1827  	st.Unlock()
  1828  	err = m.backend.StartServices(startupOrdered, svcsToDisable, pb, perfTimings)
  1829  	st.Lock()
  1830  
  1831  	return err
  1832  }
  1833  
  1834  func (m *SnapManager) undoStartSnapServices(t *state.Task, _ *tomb.Tomb) error {
  1835  	st := t.State()
  1836  	st.Lock()
  1837  	defer st.Unlock()
  1838  
  1839  	perfTimings := state.TimingsForTask(t)
  1840  	defer perfTimings.Save(st)
  1841  
  1842  	snapsup, snapst, err := snapSetupAndState(t)
  1843  	if err != nil {
  1844  		return err
  1845  	}
  1846  
  1847  	currentInfo, err := snapst.CurrentInfo()
  1848  	if err != nil {
  1849  		return err
  1850  	}
  1851  
  1852  	var oldLastActiveDisabledServices []string
  1853  	if err := t.Get("old-last-active-disabled-services", &oldLastActiveDisabledServices); err != nil && err != state.ErrNoState {
  1854  		return err
  1855  	}
  1856  	snapst.LastActiveDisabledServices = oldLastActiveDisabledServices
  1857  	Set(st, snapsup.InstanceName(), snapst)
  1858  
  1859  	svcs := currentInfo.Services()
  1860  	if len(svcs) == 0 {
  1861  		return nil
  1862  	}
  1863  
  1864  	// XXX: stop reason not set on start task, should we have a new reason for undo?
  1865  	var stopReason snap.ServiceStopReason
  1866  
  1867  	// stop the services
  1868  	err = m.backend.StopServices(svcs, stopReason, progress.Null, perfTimings)
  1869  	if err != nil {
  1870  		return err
  1871  	}
  1872  
  1873  	return nil
  1874  }
  1875  
  1876  func (m *SnapManager) stopSnapServices(t *state.Task, _ *tomb.Tomb) error {
  1877  	st := t.State()
  1878  	st.Lock()
  1879  	defer st.Unlock()
  1880  
  1881  	perfTimings := state.TimingsForTask(t)
  1882  	defer perfTimings.Save(st)
  1883  
  1884  	snapsup, snapst, err := snapSetupAndState(t)
  1885  	if err != nil {
  1886  		return err
  1887  	}
  1888  
  1889  	currentInfo, err := snapst.CurrentInfo()
  1890  	if err != nil {
  1891  		return err
  1892  	}
  1893  	svcs := currentInfo.Services()
  1894  	if len(svcs) == 0 {
  1895  		return nil
  1896  	}
  1897  
  1898  	var stopReason snap.ServiceStopReason
  1899  	if err := t.Get("stop-reason", &stopReason); err != nil && err != state.ErrNoState {
  1900  		return err
  1901  	}
  1902  
  1903  	pb := NewTaskProgressAdapterUnlocked(t)
  1904  	st.Unlock()
  1905  	defer st.Lock()
  1906  
  1907  	// stop the services
  1908  	err = m.backend.StopServices(svcs, stopReason, pb, perfTimings)
  1909  	if err != nil {
  1910  		return err
  1911  	}
  1912  
  1913  	// get the disabled services after we stopped all the services.
  1914  	// this list is not meant to save what services are disabled at any given
  1915  	// time, specifically just what services are disabled while systemd loses
  1916  	// track of the services. this list is also used to determine what services are enabled
  1917  	// when we start services of a new revision of the snap in
  1918  	// start-snap-services handler.
  1919  	disabledServices, err := m.queryDisabledServices(currentInfo, pb)
  1920  	if err != nil {
  1921  		return err
  1922  	}
  1923  
  1924  	st.Lock()
  1925  	defer st.Unlock()
  1926  
  1927  	// for undo
  1928  	t.Set("old-last-active-disabled-services", snapst.LastActiveDisabledServices)
  1929  	// undo could queryDisabledServices, but this avoids it
  1930  	t.Set("disabled-services", disabledServices)
  1931  
  1932  	// add to the disabled services list in snapst services which were disabled
  1933  	// for usage across changes like in reverting and enabling after being
  1934  	// disabled.
  1935  	// we keep what's already in the list in snapst because that list is
  1936  	// services which were previously present in the snap and disabled, but are
  1937  	// no longer present.
  1938  	snapst.LastActiveDisabledServices = append(
  1939  		snapst.LastActiveDisabledServices,
  1940  		disabledServices...,
  1941  	)
  1942  
  1943  	// reset services tracked by operations from hooks
  1944  	snapst.ServicesDisabledByHooks = nil
  1945  	snapst.ServicesEnabledByHooks = nil
  1946  
  1947  	Set(st, snapsup.InstanceName(), snapst)
  1948  
  1949  	return nil
  1950  }
  1951  
  1952  func (m *SnapManager) undoStopSnapServices(t *state.Task, _ *tomb.Tomb) error {
  1953  	st := t.State()
  1954  	st.Lock()
  1955  	defer st.Unlock()
  1956  
  1957  	perfTimings := state.TimingsForTask(t)
  1958  	defer perfTimings.Save(st)
  1959  
  1960  	snapsup, snapst, err := snapSetupAndState(t)
  1961  	if err != nil {
  1962  		return err
  1963  	}
  1964  	currentInfo, err := snapst.CurrentInfo()
  1965  	if err != nil {
  1966  		return err
  1967  	}
  1968  
  1969  	svcs := currentInfo.Services()
  1970  	if len(svcs) == 0 {
  1971  		return nil
  1972  	}
  1973  
  1974  	startupOrdered, err := snap.SortServices(svcs)
  1975  	if err != nil {
  1976  		return err
  1977  	}
  1978  
  1979  	var lastActiveDisabled []string
  1980  	if err := t.Get("old-last-active-disabled-services", &lastActiveDisabled); err != nil && err != state.ErrNoState {
  1981  		return err
  1982  	}
  1983  	snapst.LastActiveDisabledServices = lastActiveDisabled
  1984  	Set(st, snapsup.InstanceName(), snapst)
  1985  
  1986  	var disabledServices []string
  1987  	if err := t.Get("disabled-services", &disabledServices); err != nil && err != state.ErrNoState {
  1988  		return err
  1989  	}
  1990  
  1991  	st.Unlock()
  1992  	err = m.backend.StartServices(startupOrdered, disabledServices, progress.Null, perfTimings)
  1993  	if err != nil {
  1994  		return err
  1995  	}
  1996  
  1997  	st.Lock()
  1998  	return nil
  1999  }
  2000  
  2001  func (m *SnapManager) doUnlinkSnap(t *state.Task, _ *tomb.Tomb) error {
  2002  	// invoked only if snap has a current active revision
  2003  	st := t.State()
  2004  	st.Lock()
  2005  	defer st.Unlock()
  2006  
  2007  	snapsup, snapst, err := snapSetupAndState(t)
  2008  	if err != nil {
  2009  		return err
  2010  	}
  2011  
  2012  	info, err := Info(t.State(), snapsup.InstanceName(), snapsup.Revision())
  2013  	if err != nil {
  2014  		return err
  2015  	}
  2016  
  2017  	// do the final unlink
  2018  	linkCtx := backend.LinkContext{
  2019  		FirstInstall: false,
  2020  	}
  2021  	err = m.backend.UnlinkSnap(info, linkCtx, NewTaskProgressAdapterLocked(t))
  2022  	if err != nil {
  2023  		return err
  2024  	}
  2025  
  2026  	// mark as inactive
  2027  	snapst.Active = false
  2028  	Set(st, snapsup.InstanceName(), snapst)
  2029  
  2030  	// Notify link snap participants about link changes.
  2031  	notifyLinkParticipants(t, snapsup.InstanceName())
  2032  
  2033  	return err
  2034  }
  2035  
  2036  func (m *SnapManager) undoUnlinkSnap(t *state.Task, _ *tomb.Tomb) error {
  2037  	st := t.State()
  2038  	st.Lock()
  2039  	defer st.Unlock()
  2040  
  2041  	perfTimings := state.TimingsForTask(t)
  2042  	defer perfTimings.Save(st)
  2043  
  2044  	snapsup, snapst, err := snapSetupAndState(t)
  2045  	if err != nil {
  2046  		return err
  2047  	}
  2048  
  2049  	isInstalled := snapst.IsInstalled()
  2050  	if !isInstalled {
  2051  		return fmt.Errorf("internal error: snap %q not installed anymore", snapsup.InstanceName())
  2052  	}
  2053  
  2054  	info, err := snapst.CurrentInfo()
  2055  	if err != nil {
  2056  		return err
  2057  	}
  2058  
  2059  	deviceCtx, err := DeviceCtx(st, t, nil)
  2060  	if err != nil {
  2061  		return err
  2062  	}
  2063  
  2064  	// undo here may be part of failed snap remove change, in which case a later
  2065  	// "clear-snap" task could have been executed and some or all of the
  2066  	// data of this snap could be lost. If that's the case, then we should not
  2067  	// enable the snap back.
  2068  	// XXX: should make an exception for snapd/core?
  2069  	place := snapsup.placeInfo()
  2070  	for _, dir := range []string{place.DataDir(), place.CommonDataDir()} {
  2071  		if exists, _, _ := osutil.DirExists(dir); !exists {
  2072  			t.Logf("cannot link snap %q back, some of its data has already been removed", snapsup.InstanceName())
  2073  			// TODO: mark the snap broken at the SnapState level when we have
  2074  			// such concept.
  2075  			return nil
  2076  		}
  2077  	}
  2078  
  2079  	snapst.Active = true
  2080  	Set(st, snapsup.InstanceName(), snapst)
  2081  
  2082  	vitalityRank, err := vitalityRank(st, snapsup.InstanceName())
  2083  	if err != nil {
  2084  		return err
  2085  	}
  2086  	linkCtx := backend.LinkContext{
  2087  		FirstInstall: false,
  2088  		VitalityRank: vitalityRank,
  2089  	}
  2090  	reboot, err := m.backend.LinkSnap(info, deviceCtx, linkCtx, perfTimings)
  2091  	if err != nil {
  2092  		return err
  2093  	}
  2094  
  2095  	// Notify link snap participants about link changes.
  2096  	notifyLinkParticipants(t, snapsup.InstanceName())
  2097  
  2098  	// if we just linked back a core snap, request a restart
  2099  	// so that we switch executing its snapd.
  2100  	m.maybeRestart(t, info, reboot, deviceCtx)
  2101  
  2102  	return nil
  2103  }
  2104  
  2105  func (m *SnapManager) doClearSnapData(t *state.Task, _ *tomb.Tomb) error {
  2106  	st := t.State()
  2107  	st.Lock()
  2108  	snapsup, snapst, err := snapSetupAndState(t)
  2109  	st.Unlock()
  2110  	if err != nil {
  2111  		return err
  2112  	}
  2113  
  2114  	st.Lock()
  2115  	info, err := Info(t.State(), snapsup.InstanceName(), snapsup.Revision())
  2116  	st.Unlock()
  2117  	if err != nil {
  2118  		return err
  2119  	}
  2120  
  2121  	if err = m.backend.RemoveSnapData(info); err != nil {
  2122  		return err
  2123  	}
  2124  
  2125  	if len(snapst.Sequence) == 1 {
  2126  		// Only remove data common between versions if this is the last version
  2127  		if err = m.backend.RemoveSnapCommonData(info); err != nil {
  2128  			return err
  2129  		}
  2130  
  2131  		st.Lock()
  2132  		defer st.Unlock()
  2133  
  2134  		otherInstances, err := hasOtherInstances(st, snapsup.InstanceName())
  2135  		if err != nil {
  2136  			return err
  2137  		}
  2138  		// Snap data directory can be removed now too
  2139  		if err := m.backend.RemoveSnapDataDir(info, otherInstances); err != nil {
  2140  			return err
  2141  		}
  2142  	}
  2143  
  2144  	return nil
  2145  }
  2146  
  2147  func (m *SnapManager) doDiscardSnap(t *state.Task, _ *tomb.Tomb) error {
  2148  	st := t.State()
  2149  	st.Lock()
  2150  	defer st.Unlock()
  2151  
  2152  	snapsup, snapst, err := snapSetupAndState(t)
  2153  	if err != nil {
  2154  		return err
  2155  	}
  2156  
  2157  	deviceCtx, err := DeviceCtx(st, t, nil)
  2158  	if err != nil {
  2159  		return err
  2160  	}
  2161  
  2162  	if snapst.Current == snapsup.Revision() && snapst.Active {
  2163  		return fmt.Errorf("internal error: cannot discard snap %q: still active", snapsup.InstanceName())
  2164  	}
  2165  
  2166  	if len(snapst.Sequence) == 1 {
  2167  		snapst.Sequence = nil
  2168  		snapst.Current = snap.Revision{}
  2169  	} else {
  2170  		newSeq := make([]*snap.SideInfo, 0, len(snapst.Sequence))
  2171  		for _, si := range snapst.Sequence {
  2172  			if si.Revision == snapsup.Revision() {
  2173  				// leave out
  2174  				continue
  2175  			}
  2176  			newSeq = append(newSeq, si)
  2177  		}
  2178  		snapst.Sequence = newSeq
  2179  		if snapst.Current == snapsup.Revision() {
  2180  			snapst.Current = newSeq[len(newSeq)-1].Revision
  2181  		}
  2182  	}
  2183  
  2184  	pb := NewTaskProgressAdapterLocked(t)
  2185  	typ, err := snapst.Type()
  2186  	if err != nil {
  2187  		return err
  2188  	}
  2189  	err = m.backend.RemoveSnapFiles(snapsup.placeInfo(), typ, nil, deviceCtx, pb)
  2190  	if err != nil {
  2191  		t.Errorf("cannot remove snap file %q, will retry in 3 mins: %s", snapsup.InstanceName(), err)
  2192  		return &state.Retry{After: 3 * time.Minute}
  2193  	}
  2194  	if len(snapst.Sequence) == 0 {
  2195  		// Remove configuration associated with this snap.
  2196  		err = config.DeleteSnapConfig(st, snapsup.InstanceName())
  2197  		if err != nil {
  2198  			return err
  2199  		}
  2200  		err = m.backend.DiscardSnapNamespace(snapsup.InstanceName())
  2201  		if err != nil {
  2202  			t.Errorf("cannot discard snap namespace %q, will retry in 3 mins: %s", snapsup.InstanceName(), err)
  2203  			return &state.Retry{After: 3 * time.Minute}
  2204  		}
  2205  		err = m.backend.RemoveSnapInhibitLock(snapsup.InstanceName())
  2206  		if err != nil {
  2207  			return err
  2208  		}
  2209  		if err := m.removeSnapCookie(st, snapsup.InstanceName()); err != nil {
  2210  			return fmt.Errorf("cannot remove snap cookie: %v", err)
  2211  		}
  2212  
  2213  		otherInstances, err := hasOtherInstances(st, snapsup.InstanceName())
  2214  		if err != nil {
  2215  			return err
  2216  		}
  2217  
  2218  		if err := m.backend.RemoveSnapDir(snapsup.placeInfo(), otherInstances); err != nil {
  2219  			return fmt.Errorf("cannot remove snap directory: %v", err)
  2220  		}
  2221  
  2222  		// try to remove the auxiliary store info
  2223  		if err := discardAuxStoreInfo(snapsup.SideInfo.SnapID); err != nil {
  2224  			logger.Noticef("Cannot remove auxiliary store info for %q: %v", snapsup.InstanceName(), err)
  2225  		}
  2226  
  2227  		// XXX: also remove sequence files?
  2228  	}
  2229  	if err = config.DiscardRevisionConfig(st, snapsup.InstanceName(), snapsup.Revision()); err != nil {
  2230  		return err
  2231  	}
  2232  	Set(st, snapsup.InstanceName(), snapst)
  2233  	return nil
  2234  }
  2235  
  2236  /* aliases v2
  2237  
  2238  aliases v2 implementation uses the following tasks:
  2239  
  2240    * for install/refresh/remove/enable/disable etc
  2241  
  2242      - remove-aliases: remove aliases of a snap from disk and mark them pending
  2243  
  2244      - setup-aliases: (re)creates aliases from snap state, mark them as
  2245        not pending
  2246  
  2247      - set-auto-aliases: updates aliases snap state based on the
  2248        snap-declaration and current revision info of the snap
  2249  
  2250    * for refresh & when the snap-declaration aliases change without a
  2251      new revision
  2252  
  2253      - refresh-aliases: updates aliases snap state and updates them on disk too;
  2254        its undo is used generically by other tasks as well
  2255  
  2256      - prune-auto-aliases: used for the special case of automatic
  2257        aliases transferred from one snap to another to prune them from
  2258        the source snaps to avoid conflicts in later operations
  2259  
  2260    * for alias/unalias/prefer:
  2261  
  2262      - alias: creates a manual alias
  2263  
  2264      - unalias: removes a manual alias
  2265  
  2266      - disable-aliases: disable the automatic aliases of a snap and
  2267        removes all manual ones as well
  2268  
  2269      - prefer-aliases: enables the automatic aliases of a snap after
  2270        disabling any other snap conflicting aliases
  2271  
  2272  */
  2273  
  2274  func (m *SnapManager) doSetAutoAliases(t *state.Task, _ *tomb.Tomb) error {
  2275  	st := t.State()
  2276  	st.Lock()
  2277  	defer st.Unlock()
  2278  	snapsup, snapst, err := snapSetupAndState(t)
  2279  	if err != nil {
  2280  		return err
  2281  	}
  2282  	snapName := snapsup.InstanceName()
  2283  	curInfo, err := snapst.CurrentInfo()
  2284  	if err != nil {
  2285  		return err
  2286  	}
  2287  
  2288  	// --unaliased
  2289  	if snapsup.Unaliased {
  2290  		t.Set("old-auto-aliases-disabled", snapst.AutoAliasesDisabled)
  2291  		snapst.AutoAliasesDisabled = true
  2292  	}
  2293  
  2294  	curAliases := snapst.Aliases
  2295  	// TODO: implement --prefer logic
  2296  	newAliases, err := refreshAliases(st, curInfo, curAliases)
  2297  	if err != nil {
  2298  		return err
  2299  	}
  2300  	_, err = checkAliasesConflicts(st, snapName, snapst.AutoAliasesDisabled, newAliases, nil)
  2301  	if err != nil {
  2302  		return err
  2303  	}
  2304  
  2305  	t.Set("old-aliases-v2", curAliases)
  2306  	// noop, except on first install where we need to set this here
  2307  	snapst.AliasesPending = true
  2308  	snapst.Aliases = newAliases
  2309  	Set(st, snapName, snapst)
  2310  	return nil
  2311  }
  2312  
  2313  func (m *SnapManager) doRemoveAliases(t *state.Task, _ *tomb.Tomb) error {
  2314  	st := t.State()
  2315  	st.Lock()
  2316  	defer st.Unlock()
  2317  	snapsup, snapst, err := snapSetupAndState(t)
  2318  	if err != nil {
  2319  		return err
  2320  	}
  2321  	snapName := snapsup.InstanceName()
  2322  
  2323  	err = m.backend.RemoveSnapAliases(snapName)
  2324  	if err != nil {
  2325  		return err
  2326  	}
  2327  
  2328  	snapst.AliasesPending = true
  2329  	Set(st, snapName, snapst)
  2330  	return nil
  2331  }
  2332  
  2333  func (m *SnapManager) doSetupAliases(t *state.Task, _ *tomb.Tomb) error {
  2334  	st := t.State()
  2335  	st.Lock()
  2336  	defer st.Unlock()
  2337  	snapsup, snapst, err := snapSetupAndState(t)
  2338  	if err != nil {
  2339  		return err
  2340  	}
  2341  	snapName := snapsup.InstanceName()
  2342  	curAliases := snapst.Aliases
  2343  
  2344  	_, _, err = applyAliasesChange(snapName, autoDis, nil, snapst.AutoAliasesDisabled, curAliases, m.backend, doApply)
  2345  	if err != nil {
  2346  		return err
  2347  	}
  2348  
  2349  	snapst.AliasesPending = false
  2350  	Set(st, snapName, snapst)
  2351  	return nil
  2352  }
  2353  
  2354  func (m *SnapManager) doRefreshAliases(t *state.Task, _ *tomb.Tomb) error {
  2355  	st := t.State()
  2356  	st.Lock()
  2357  	defer st.Unlock()
  2358  	snapsup, snapst, err := snapSetupAndState(t)
  2359  	if err != nil {
  2360  		return err
  2361  	}
  2362  	snapName := snapsup.InstanceName()
  2363  	curInfo, err := snapst.CurrentInfo()
  2364  	if err != nil {
  2365  		return err
  2366  	}
  2367  
  2368  	autoDisabled := snapst.AutoAliasesDisabled
  2369  	curAliases := snapst.Aliases
  2370  	newAliases, err := refreshAliases(st, curInfo, curAliases)
  2371  	if err != nil {
  2372  		return err
  2373  	}
  2374  	_, err = checkAliasesConflicts(st, snapName, autoDisabled, newAliases, nil)
  2375  	if err != nil {
  2376  		return err
  2377  	}
  2378  
  2379  	if !snapst.AliasesPending {
  2380  		if _, _, err := applyAliasesChange(snapName, autoDisabled, curAliases, autoDisabled, newAliases, m.backend, doApply); err != nil {
  2381  			return err
  2382  		}
  2383  	}
  2384  
  2385  	t.Set("old-aliases-v2", curAliases)
  2386  	snapst.Aliases = newAliases
  2387  	Set(st, snapName, snapst)
  2388  	return nil
  2389  }
  2390  
  2391  func (m *SnapManager) undoRefreshAliases(t *state.Task, _ *tomb.Tomb) error {
  2392  	st := t.State()
  2393  	st.Lock()
  2394  	defer st.Unlock()
  2395  	var oldAliases map[string]*AliasTarget
  2396  	err := t.Get("old-aliases-v2", &oldAliases)
  2397  	if err == state.ErrNoState {
  2398  		// nothing to do
  2399  		return nil
  2400  	}
  2401  	if err != nil {
  2402  		return err
  2403  	}
  2404  	snapsup, snapst, err := snapSetupAndState(t)
  2405  	if err != nil {
  2406  		return err
  2407  	}
  2408  	snapName := snapsup.InstanceName()
  2409  	curAutoDisabled := snapst.AutoAliasesDisabled
  2410  	autoDisabled := curAutoDisabled
  2411  	if err = t.Get("old-auto-aliases-disabled", &autoDisabled); err != nil && err != state.ErrNoState {
  2412  		return err
  2413  	}
  2414  
  2415  	var otherSnapDisabled map[string]*otherDisabledAliases
  2416  	if err = t.Get("other-disabled-aliases", &otherSnapDisabled); err != nil && err != state.ErrNoState {
  2417  		return err
  2418  	}
  2419  
  2420  	// check if the old states creates conflicts now
  2421  	_, err = checkAliasesConflicts(st, snapName, autoDisabled, oldAliases, nil)
  2422  	if _, ok := err.(*AliasConflictError); ok {
  2423  		// best we can do is reinstate with all aliases disabled
  2424  		t.Errorf("cannot reinstate alias state because of conflicts, disabling: %v", err)
  2425  		oldAliases, _ = disableAliases(oldAliases)
  2426  		autoDisabled = true
  2427  	} else if err != nil {
  2428  		return err
  2429  	}
  2430  
  2431  	if !snapst.AliasesPending {
  2432  		curAliases := snapst.Aliases
  2433  		if _, _, err := applyAliasesChange(snapName, curAutoDisabled, curAliases, autoDisabled, oldAliases, m.backend, doApply); err != nil {
  2434  			return err
  2435  		}
  2436  	}
  2437  
  2438  	snapst.AutoAliasesDisabled = autoDisabled
  2439  	snapst.Aliases = oldAliases
  2440  	newSnapStates := make(map[string]*SnapState, 1+len(otherSnapDisabled))
  2441  	newSnapStates[snapName] = snapst
  2442  
  2443  	// if we disabled other snap aliases try to undo that
  2444  	conflicting := make(map[string]bool, len(otherSnapDisabled))
  2445  	otherCurSnapStates := make(map[string]*SnapState, len(otherSnapDisabled))
  2446  	for otherSnap, otherDisabled := range otherSnapDisabled {
  2447  		var otherSnapState SnapState
  2448  		err := Get(st, otherSnap, &otherSnapState)
  2449  		if err != nil {
  2450  			return err
  2451  		}
  2452  		otherCurInfo, err := otherSnapState.CurrentInfo()
  2453  		if err != nil {
  2454  			return err
  2455  		}
  2456  
  2457  		otherCurSnapStates[otherSnap] = &otherSnapState
  2458  
  2459  		autoDisabled := otherSnapState.AutoAliasesDisabled
  2460  		if otherDisabled.Auto {
  2461  			// automatic aliases of other were disabled, undo that
  2462  			autoDisabled = false
  2463  		}
  2464  		otherAliases := reenableAliases(otherCurInfo, otherSnapState.Aliases, otherDisabled.Manual)
  2465  		// check for conflicts taking into account
  2466  		// re-enabled aliases
  2467  		conflicts, err := checkAliasesConflicts(st, otherSnap, autoDisabled, otherAliases, newSnapStates)
  2468  		if _, ok := err.(*AliasConflictError); ok {
  2469  			conflicting[otherSnap] = true
  2470  			for conflictSnap := range conflicts {
  2471  				conflicting[conflictSnap] = true
  2472  			}
  2473  		} else if err != nil {
  2474  			return err
  2475  		}
  2476  
  2477  		newSnapState := otherSnapState
  2478  		newSnapState.Aliases = otherAliases
  2479  		newSnapState.AutoAliasesDisabled = autoDisabled
  2480  		newSnapStates[otherSnap] = &newSnapState
  2481  	}
  2482  
  2483  	// apply non-conflicting other
  2484  	for otherSnap, otherSnapState := range otherCurSnapStates {
  2485  		if conflicting[otherSnap] {
  2486  			// keep as it was
  2487  			continue
  2488  		}
  2489  		newSnapSt := newSnapStates[otherSnap]
  2490  		if !otherSnapState.AliasesPending {
  2491  			if _, _, err := applyAliasesChange(otherSnap, otherSnapState.AutoAliasesDisabled, otherSnapState.Aliases, newSnapSt.AutoAliasesDisabled, newSnapSt.Aliases, m.backend, doApply); err != nil {
  2492  				return err
  2493  			}
  2494  		}
  2495  	}
  2496  
  2497  	for instanceName, snapst := range newSnapStates {
  2498  		if conflicting[instanceName] {
  2499  			// keep as it was
  2500  			continue
  2501  		}
  2502  		Set(st, instanceName, snapst)
  2503  	}
  2504  	return nil
  2505  }
  2506  
  2507  func (m *SnapManager) doPruneAutoAliases(t *state.Task, _ *tomb.Tomb) error {
  2508  	st := t.State()
  2509  	st.Lock()
  2510  	defer st.Unlock()
  2511  	snapsup, snapst, err := snapSetupAndState(t)
  2512  	if err != nil {
  2513  		return err
  2514  	}
  2515  	var which []string
  2516  	err = t.Get("aliases", &which)
  2517  	if err != nil {
  2518  		return err
  2519  	}
  2520  	snapName := snapsup.InstanceName()
  2521  	autoDisabled := snapst.AutoAliasesDisabled
  2522  	curAliases := snapst.Aliases
  2523  
  2524  	newAliases := pruneAutoAliases(curAliases, which)
  2525  
  2526  	if !snapst.AliasesPending {
  2527  		if _, _, err := applyAliasesChange(snapName, autoDisabled, curAliases, autoDisabled, newAliases, m.backend, doApply); err != nil {
  2528  			return err
  2529  		}
  2530  	}
  2531  
  2532  	t.Set("old-aliases-v2", curAliases)
  2533  	snapst.Aliases = newAliases
  2534  	Set(st, snapName, snapst)
  2535  	return nil
  2536  }
  2537  
  2538  type changedAlias struct {
  2539  	Snap  string `json:"snap"`
  2540  	App   string `json:"app"`
  2541  	Alias string `json:"alias"`
  2542  }
  2543  
  2544  func aliasesTrace(t *state.Task, added, removed []*backend.Alias) error {
  2545  	chg := t.Change()
  2546  	var data map[string]interface{}
  2547  	err := chg.Get("api-data", &data)
  2548  	if err != nil && err != state.ErrNoState {
  2549  		return err
  2550  	}
  2551  	if len(data) == 0 {
  2552  		data = make(map[string]interface{})
  2553  	}
  2554  
  2555  	curAdded, _ := data["aliases-added"].([]interface{})
  2556  	for _, a := range added {
  2557  		snap, app := snap.SplitSnapApp(a.Target)
  2558  		curAdded = append(curAdded, &changedAlias{
  2559  			Snap:  snap,
  2560  			App:   app,
  2561  			Alias: a.Name,
  2562  		})
  2563  	}
  2564  	data["aliases-added"] = curAdded
  2565  
  2566  	curRemoved, _ := data["aliases-removed"].([]interface{})
  2567  	for _, a := range removed {
  2568  		snap, app := snap.SplitSnapApp(a.Target)
  2569  		curRemoved = append(curRemoved, &changedAlias{
  2570  			Snap:  snap,
  2571  			App:   app,
  2572  			Alias: a.Name,
  2573  		})
  2574  	}
  2575  	data["aliases-removed"] = curRemoved
  2576  
  2577  	chg.Set("api-data", data)
  2578  	return nil
  2579  }
  2580  
  2581  func (m *SnapManager) doAlias(t *state.Task, _ *tomb.Tomb) error {
  2582  	st := t.State()
  2583  	st.Lock()
  2584  	defer st.Unlock()
  2585  	snapsup, snapst, err := snapSetupAndState(t)
  2586  	if err != nil {
  2587  		return err
  2588  	}
  2589  	var target, alias string
  2590  	err = t.Get("target", &target)
  2591  	if err != nil {
  2592  		return err
  2593  	}
  2594  	err = t.Get("alias", &alias)
  2595  	if err != nil {
  2596  		return err
  2597  	}
  2598  
  2599  	snapName := snapsup.InstanceName()
  2600  	curInfo, err := snapst.CurrentInfo()
  2601  	if err != nil {
  2602  		return err
  2603  	}
  2604  
  2605  	autoDisabled := snapst.AutoAliasesDisabled
  2606  	curAliases := snapst.Aliases
  2607  	newAliases, err := manualAlias(curInfo, curAliases, target, alias)
  2608  	if err != nil {
  2609  		return err
  2610  	}
  2611  	_, err = checkAliasesConflicts(st, snapName, autoDisabled, newAliases, nil)
  2612  	if err != nil {
  2613  		return err
  2614  	}
  2615  
  2616  	added, removed, err := applyAliasesChange(snapName, autoDisabled, curAliases, autoDisabled, newAliases, m.backend, snapst.AliasesPending)
  2617  	if err != nil {
  2618  		return err
  2619  	}
  2620  	if err := aliasesTrace(t, added, removed); err != nil {
  2621  		return err
  2622  	}
  2623  
  2624  	t.Set("old-aliases-v2", curAliases)
  2625  	snapst.Aliases = newAliases
  2626  	Set(st, snapName, snapst)
  2627  	return nil
  2628  }
  2629  
  2630  func (m *SnapManager) doDisableAliases(t *state.Task, _ *tomb.Tomb) error {
  2631  	st := t.State()
  2632  	st.Lock()
  2633  	defer st.Unlock()
  2634  	snapsup, snapst, err := snapSetupAndState(t)
  2635  	if err != nil {
  2636  		return err
  2637  	}
  2638  	snapName := snapsup.InstanceName()
  2639  
  2640  	oldAutoDisabled := snapst.AutoAliasesDisabled
  2641  	oldAliases := snapst.Aliases
  2642  	newAliases, _ := disableAliases(oldAliases)
  2643  
  2644  	added, removed, err := applyAliasesChange(snapName, oldAutoDisabled, oldAliases, autoDis, newAliases, m.backend, snapst.AliasesPending)
  2645  	if err != nil {
  2646  		return err
  2647  	}
  2648  	if err := aliasesTrace(t, added, removed); err != nil {
  2649  		return err
  2650  	}
  2651  
  2652  	t.Set("old-auto-aliases-disabled", oldAutoDisabled)
  2653  	snapst.AutoAliasesDisabled = true
  2654  	t.Set("old-aliases-v2", oldAliases)
  2655  	snapst.Aliases = newAliases
  2656  	Set(st, snapName, snapst)
  2657  	return nil
  2658  }
  2659  
  2660  func (m *SnapManager) doUnalias(t *state.Task, _ *tomb.Tomb) error {
  2661  	st := t.State()
  2662  	st.Lock()
  2663  	defer st.Unlock()
  2664  	snapsup, snapst, err := snapSetupAndState(t)
  2665  	if err != nil {
  2666  		return err
  2667  	}
  2668  	var alias string
  2669  	err = t.Get("alias", &alias)
  2670  	if err != nil {
  2671  		return err
  2672  	}
  2673  	snapName := snapsup.InstanceName()
  2674  
  2675  	autoDisabled := snapst.AutoAliasesDisabled
  2676  	oldAliases := snapst.Aliases
  2677  	newAliases, err := manualUnalias(oldAliases, alias)
  2678  	if err != nil {
  2679  		return err
  2680  	}
  2681  
  2682  	added, removed, err := applyAliasesChange(snapName, autoDisabled, oldAliases, autoDisabled, newAliases, m.backend, snapst.AliasesPending)
  2683  	if err != nil {
  2684  		return err
  2685  	}
  2686  	if err := aliasesTrace(t, added, removed); err != nil {
  2687  		return err
  2688  	}
  2689  
  2690  	t.Set("old-aliases-v2", oldAliases)
  2691  	snapst.Aliases = newAliases
  2692  	Set(st, snapName, snapst)
  2693  	return nil
  2694  }
  2695  
  2696  // otherDisabledAliases is used to track for the benefit of undo what
  2697  // changes were made aka what aliases were disabled of another
  2698  // conflicting snap by prefer logic
  2699  type otherDisabledAliases struct {
  2700  	// Auto records whether prefer had to disable automatic aliases
  2701  	Auto bool `json:"auto,omitempty"`
  2702  	// Manual records which manual aliases were removed by prefer
  2703  	Manual map[string]string `json:"manual,omitempty"`
  2704  }
  2705  
  2706  func (m *SnapManager) doPreferAliases(t *state.Task, _ *tomb.Tomb) error {
  2707  	st := t.State()
  2708  	st.Lock()
  2709  	defer st.Unlock()
  2710  	snapsup, snapst, err := snapSetupAndState(t)
  2711  	if err != nil {
  2712  		return err
  2713  	}
  2714  	instanceName := snapsup.InstanceName()
  2715  
  2716  	if !snapst.AutoAliasesDisabled {
  2717  		// already enabled, nothing to do
  2718  		return nil
  2719  	}
  2720  
  2721  	curAliases := snapst.Aliases
  2722  	aliasConflicts, err := checkAliasesConflicts(st, instanceName, autoEn, curAliases, nil)
  2723  	conflErr, isConflErr := err.(*AliasConflictError)
  2724  	if err != nil && !isConflErr {
  2725  		return err
  2726  	}
  2727  	if isConflErr && conflErr.Conflicts == nil {
  2728  		// it's a snap command namespace conflict, we cannot remedy it
  2729  		return conflErr
  2730  	}
  2731  	// proceed to disable conflicting aliases as needed
  2732  	// before re-enabling instanceName aliases
  2733  
  2734  	otherSnapStates := make(map[string]*SnapState, len(aliasConflicts))
  2735  	otherSnapDisabled := make(map[string]*otherDisabledAliases, len(aliasConflicts))
  2736  	for otherSnap := range aliasConflicts {
  2737  		var otherSnapState SnapState
  2738  		err := Get(st, otherSnap, &otherSnapState)
  2739  		if err != nil {
  2740  			return err
  2741  		}
  2742  
  2743  		otherAliases, disabledManual := disableAliases(otherSnapState.Aliases)
  2744  
  2745  		added, removed, err := applyAliasesChange(otherSnap, otherSnapState.AutoAliasesDisabled, otherSnapState.Aliases, autoDis, otherAliases, m.backend, otherSnapState.AliasesPending)
  2746  		if err != nil {
  2747  			return err
  2748  		}
  2749  		if err := aliasesTrace(t, added, removed); err != nil {
  2750  			return err
  2751  		}
  2752  
  2753  		var otherDisabled otherDisabledAliases
  2754  		otherDisabled.Manual = disabledManual
  2755  		otherSnapState.Aliases = otherAliases
  2756  		// disable automatic aliases as needed
  2757  		if !otherSnapState.AutoAliasesDisabled && len(otherAliases) != 0 {
  2758  			// record that we did disable automatic aliases
  2759  			otherDisabled.Auto = true
  2760  			otherSnapState.AutoAliasesDisabled = true
  2761  		}
  2762  		otherSnapDisabled[otherSnap] = &otherDisabled
  2763  		otherSnapStates[otherSnap] = &otherSnapState
  2764  	}
  2765  
  2766  	added, removed, err := applyAliasesChange(instanceName, autoDis, curAliases, autoEn, curAliases, m.backend, snapst.AliasesPending)
  2767  	if err != nil {
  2768  		return err
  2769  	}
  2770  	if err := aliasesTrace(t, added, removed); err != nil {
  2771  		return err
  2772  	}
  2773  
  2774  	for otherSnap, otherSnapState := range otherSnapStates {
  2775  		Set(st, otherSnap, otherSnapState)
  2776  	}
  2777  	if len(otherSnapDisabled) != 0 {
  2778  		t.Set("other-disabled-aliases", otherSnapDisabled)
  2779  	}
  2780  	t.Set("old-auto-aliases-disabled", true)
  2781  	t.Set("old-aliases-v2", curAliases)
  2782  	snapst.AutoAliasesDisabled = false
  2783  	Set(st, instanceName, snapst)
  2784  	return nil
  2785  }
  2786  
  2787  // changeReadyUpToTask returns whether all other change's tasks are Ready.
  2788  func changeReadyUpToTask(task *state.Task) bool {
  2789  	me := task.ID()
  2790  	change := task.Change()
  2791  	for _, task := range change.Tasks() {
  2792  		if me == task.ID() {
  2793  			// ignore self
  2794  			continue
  2795  		}
  2796  		if !task.Status().Ready() {
  2797  			return false
  2798  		}
  2799  	}
  2800  	return true
  2801  }
  2802  
  2803  // refreshedSnaps returns the instance names of the snaps successfully refreshed
  2804  // in the last batch of refreshes before the given (re-refresh) task.
  2805  //
  2806  // It does this by advancing through the given task's change's tasks, keeping
  2807  // track of the instance names from the first SnapSetup in every lane, stopping
  2808  // when finding the given task, and resetting things when finding a different
  2809  // re-refresh task (that indicates the end of a batch that isn't the given one).
  2810  func refreshedSnaps(reTask *state.Task) []string {
  2811  	// NOTE nothing requires reTask to be a check-rerefresh task, nor even to be in
  2812  	// a refresh-ish change, but it doesn't make much sense to call this otherwise.
  2813  	tid := reTask.ID()
  2814  	laneSnaps := map[int]string{}
  2815  	// change.Tasks() preserves the order tasks were added, otherwise it all falls apart
  2816  	for _, task := range reTask.Change().Tasks() {
  2817  		if task.ID() == tid {
  2818  			// we've reached ourselves; we don't care about anything beyond this
  2819  			break
  2820  		}
  2821  		if task.Kind() == "check-rerefresh" {
  2822  			// we've reached a previous check-rerefresh (but not ourselves).
  2823  			// Only snaps in tasks after this point are of interest.
  2824  			laneSnaps = map[int]string{}
  2825  		}
  2826  		lanes := task.Lanes()
  2827  		if len(lanes) != 1 {
  2828  			// can't happen, really
  2829  			continue
  2830  		}
  2831  		lane := lanes[0]
  2832  		if lane == 0 {
  2833  			// not really a lane
  2834  			continue
  2835  		}
  2836  		if task.Status() != state.DoneStatus {
  2837  			// ignore non-successful lane (1)
  2838  			laneSnaps[lane] = ""
  2839  			continue
  2840  		}
  2841  		if _, ok := laneSnaps[lane]; ok {
  2842  			// ignore lanes we've already seen (including ones explicitly ignored in (1))
  2843  			continue
  2844  		}
  2845  		var snapsup SnapSetup
  2846  		if err := task.Get("snap-setup", &snapsup); err != nil {
  2847  			continue
  2848  		}
  2849  		laneSnaps[lane] = snapsup.InstanceName()
  2850  	}
  2851  
  2852  	snapNames := make([]string, 0, len(laneSnaps))
  2853  	for _, name := range laneSnaps {
  2854  		if name == "" {
  2855  			// the lane was unsuccessful
  2856  			continue
  2857  		}
  2858  		snapNames = append(snapNames, name)
  2859  	}
  2860  	return snapNames
  2861  }
  2862  
  2863  // reRefreshSetup holds the necessary details to re-refresh snaps that need it
  2864  type reRefreshSetup struct {
  2865  	UserID int `json:"user-id,omitempty"`
  2866  	*Flags
  2867  }
  2868  
  2869  // reRefreshUpdateMany exists just to make testing simpler
  2870  var reRefreshUpdateMany = updateManyFiltered
  2871  
  2872  // reRefreshFilter is an updateFilter that returns whether the given update
  2873  // needs a re-refresh because of further epoch transitions available.
  2874  func reRefreshFilter(update *snap.Info, snapst *SnapState) bool {
  2875  	cur, err := snapst.CurrentInfo()
  2876  	if err != nil {
  2877  		return false
  2878  	}
  2879  	return !update.Epoch.Equal(&cur.Epoch)
  2880  }
  2881  
  2882  var reRefreshRetryTimeout = time.Second / 10
  2883  
  2884  func (m *SnapManager) doCheckReRefresh(t *state.Task, tomb *tomb.Tomb) error {
  2885  	st := t.State()
  2886  	st.Lock()
  2887  	defer st.Unlock()
  2888  
  2889  	if numHaltTasks := t.NumHaltTasks(); numHaltTasks > 0 {
  2890  		logger.Panicf("Re-refresh task has %d tasks waiting for it.", numHaltTasks)
  2891  	}
  2892  
  2893  	if !changeReadyUpToTask(t) {
  2894  		return &state.Retry{After: reRefreshRetryTimeout, Reason: "pending refreshes"}
  2895  	}
  2896  	snaps := refreshedSnaps(t)
  2897  	if len(snaps) == 0 {
  2898  		// nothing to do (maybe everything failed)
  2899  		return nil
  2900  	}
  2901  
  2902  	var re reRefreshSetup
  2903  	if err := t.Get("rerefresh-setup", &re); err != nil {
  2904  		return err
  2905  	}
  2906  	chg := t.Change()
  2907  	updated, tasksets, err := reRefreshUpdateMany(tomb.Context(nil), st, snaps, re.UserID, reRefreshFilter, re.Flags, chg.ID())
  2908  	if err != nil {
  2909  		return err
  2910  	}
  2911  
  2912  	if len(updated) == 0 {
  2913  		t.Logf("No re-refreshes found.")
  2914  	} else {
  2915  		t.Logf("Found re-refresh for %s.", strutil.Quoted(updated))
  2916  
  2917  		for _, taskset := range tasksets {
  2918  			chg.AddAll(taskset)
  2919  		}
  2920  		st.EnsureBefore(0)
  2921  	}
  2922  	t.SetStatus(state.DoneStatus)
  2923  
  2924  	return nil
  2925  }
  2926  
  2927  // InjectTasks makes all the halt tasks of the mainTask wait for extraTasks;
  2928  // extraTasks join the same lane and change as the mainTask.
  2929  func InjectTasks(mainTask *state.Task, extraTasks *state.TaskSet) {
  2930  	lanes := mainTask.Lanes()
  2931  	if len(lanes) == 1 && lanes[0] == 0 {
  2932  		lanes = nil
  2933  	}
  2934  	for _, l := range lanes {
  2935  		extraTasks.JoinLane(l)
  2936  	}
  2937  
  2938  	chg := mainTask.Change()
  2939  	// Change shouldn't normally be nil, except for cases where
  2940  	// this helper is used before tasks are added to a change.
  2941  	if chg != nil {
  2942  		chg.AddAll(extraTasks)
  2943  	}
  2944  
  2945  	// make all halt tasks of the mainTask wait on extraTasks
  2946  	ht := mainTask.HaltTasks()
  2947  	for _, t := range ht {
  2948  		t.WaitAll(extraTasks)
  2949  	}
  2950  
  2951  	// make the extra tasks wait for main task
  2952  	extraTasks.WaitFor(mainTask)
  2953  }
  2954  
  2955  func InjectAutoConnect(mainTask *state.Task, snapsup *SnapSetup) {
  2956  	st := mainTask.State()
  2957  	autoConnect := st.NewTask("auto-connect", fmt.Sprintf(i18n.G("Automatically connect eligible plugs and slots of snap %q"), snapsup.InstanceName()))
  2958  	autoConnect.Set("snap-setup", snapsup)
  2959  	InjectTasks(mainTask, state.NewTaskSet(autoConnect))
  2960  	mainTask.Logf("added auto-connect task")
  2961  }