gopkg.in/ubuntu-core/snappy.v0@v0.0.0-20210902073436-25a8614f10a6/overlord/devicestate/devicemgr.go (about)

     1  // -*- Mode: Go; indent-tabs-mode: t -*-
     2  
     3  /*
     4   * Copyright (C) 2016-2021 Canonical Ltd
     5   *
     6   * This program is free software: you can redistribute it and/or modify
     7   * it under the terms of the GNU General Public License version 3 as
     8   * published by the Free Software Foundation.
     9   *
    10   * This program is distributed in the hope that it will be useful,
    11   * but WITHOUT ANY WARRANTY; without even the implied warranty of
    12   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    13   * GNU General Public License for more details.
    14   *
    15   * You should have received a copy of the GNU General Public License
    16   * along with this program.  If not, see <http://www.gnu.org/licenses/>.
    17   *
    18   */
    19  
    20  package devicestate
    21  
    22  import (
    23  	"context"
    24  	"encoding/json"
    25  	"errors"
    26  	"fmt"
    27  	"os"
    28  	"path/filepath"
    29  	"regexp"
    30  	"strings"
    31  	"time"
    32  
    33  	"github.com/snapcore/snapd/asserts"
    34  	"github.com/snapcore/snapd/asserts/sysdb"
    35  	"github.com/snapcore/snapd/boot"
    36  	"github.com/snapcore/snapd/dirs"
    37  	"github.com/snapcore/snapd/gadget"
    38  	"github.com/snapcore/snapd/i18n"
    39  	"github.com/snapcore/snapd/kernel/fde"
    40  	"github.com/snapcore/snapd/logger"
    41  	"github.com/snapcore/snapd/osutil"
    42  	"github.com/snapcore/snapd/overlord/assertstate"
    43  	"github.com/snapcore/snapd/overlord/auth"
    44  	"github.com/snapcore/snapd/overlord/configstate/config"
    45  	"github.com/snapcore/snapd/overlord/devicestate/internal"
    46  	"github.com/snapcore/snapd/overlord/hookstate"
    47  	"github.com/snapcore/snapd/overlord/snapstate"
    48  	"github.com/snapcore/snapd/overlord/state"
    49  	"github.com/snapcore/snapd/overlord/storecontext"
    50  	"github.com/snapcore/snapd/progress"
    51  	"github.com/snapcore/snapd/release"
    52  	"github.com/snapcore/snapd/seed"
    53  	"github.com/snapcore/snapd/snap"
    54  	"github.com/snapcore/snapd/snap/snapfile"
    55  	"github.com/snapcore/snapd/snapdenv"
    56  	"github.com/snapcore/snapd/strutil"
    57  	"github.com/snapcore/snapd/sysconfig"
    58  	"github.com/snapcore/snapd/systemd"
    59  	"github.com/snapcore/snapd/timings"
    60  )
    61  
    62  var (
    63  	cloudInitStatus   = sysconfig.CloudInitStatus
    64  	restrictCloudInit = sysconfig.RestrictCloudInit
    65  )
    66  
    67  // EarlyConfig is a hook set by configstate that can process early configuration
    68  // during managers' startup.
    69  var EarlyConfig func(st *state.State, preloadGadget func() (sysconfig.Device, *gadget.Info, error)) error
    70  
    71  // DeviceManager is responsible for managing the device identity and device
    72  // policies.
    73  type DeviceManager struct {
    74  	// sysMode is the system mode from modeenv or "" on pre-UC20,
    75  	// use SystemMode instead
    76  	sysMode string
    77  	// saveAvailable keeps track whether /var/lib/snapd/save
    78  	// is available, i.e. exists and is mounted from ubuntu-save
    79  	// if the latter exists.
    80  	// TODO: evolve this to state to track things if we start mounting
    81  	// save as rw vs ro, or mount/umount it fully on demand
    82  	saveAvailable bool
    83  
    84  	state   *state.State
    85  	hookMgr *hookstate.HookManager
    86  
    87  	cachedKeypairMgr asserts.KeypairManager
    88  
    89  	// newStore can make new stores for remodeling
    90  	newStore func(storecontext.DeviceBackend) snapstate.StoreService
    91  
    92  	bootOkRan            bool
    93  	bootRevisionsUpdated bool
    94  
    95  	seedTimings *timings.Timings
    96  
    97  	ensureSeedInConfigRan bool
    98  
    99  	ensureInstalledRan bool
   100  
   101  	ensureTriedRecoverySystemRan bool
   102  
   103  	cloudInitAlreadyRestricted           bool
   104  	cloudInitErrorAttemptStart           *time.Time
   105  	cloudInitEnabledInactiveAttemptStart *time.Time
   106  
   107  	lastBecomeOperationalAttempt time.Time
   108  	becomeOperationalBackoff     time.Duration
   109  	registered                   bool
   110  	reg                          chan struct{}
   111  
   112  	preseed bool
   113  }
   114  
   115  // Manager returns a new device manager.
   116  func Manager(s *state.State, hookManager *hookstate.HookManager, runner *state.TaskRunner, newStore func(storecontext.DeviceBackend) snapstate.StoreService) (*DeviceManager, error) {
   117  	delayedCrossMgrInit()
   118  
   119  	m := &DeviceManager{
   120  		state:    s,
   121  		hookMgr:  hookManager,
   122  		newStore: newStore,
   123  		reg:      make(chan struct{}),
   124  		preseed:  snapdenv.Preseeding(),
   125  	}
   126  
   127  	modeEnv, err := maybeReadModeenv()
   128  	if err != nil {
   129  		return nil, err
   130  	}
   131  	if modeEnv != nil {
   132  		m.sysMode = modeEnv.Mode
   133  	}
   134  
   135  	s.Lock()
   136  	s.Cache(deviceMgrKey{}, m)
   137  	s.Unlock()
   138  
   139  	if err := m.confirmRegistered(); err != nil {
   140  		return nil, err
   141  	}
   142  
   143  	hookManager.Register(regexp.MustCompile("^prepare-device$"), newBasicHookStateHandler)
   144  	hookManager.Register(regexp.MustCompile("^install-device$"), newBasicHookStateHandler)
   145  
   146  	runner.AddHandler("generate-device-key", m.doGenerateDeviceKey, nil)
   147  	runner.AddHandler("request-serial", m.doRequestSerial, nil)
   148  	runner.AddHandler("mark-preseeded", m.doMarkPreseeded, nil)
   149  	runner.AddHandler("mark-seeded", m.doMarkSeeded, nil)
   150  	runner.AddHandler("setup-run-system", m.doSetupRunSystem, nil)
   151  	runner.AddHandler("restart-system-to-run-mode", m.doRestartSystemToRunMode, nil)
   152  	runner.AddHandler("prepare-remodeling", m.doPrepareRemodeling, nil)
   153  	runner.AddCleanup("prepare-remodeling", m.cleanupRemodel)
   154  	// this *must* always run last and finalizes a remodel
   155  	runner.AddHandler("set-model", m.doSetModel, nil)
   156  	runner.AddCleanup("set-model", m.cleanupRemodel)
   157  	// There is no undo for successful gadget updates. The system is
   158  	// rebooted during update, if it boots up to the point where snapd runs
   159  	// we deem the new assets (be it bootloader or firmware) functional. The
   160  	// deployed boot assets must be backward compatible with reverted kernel
   161  	// or gadget snaps. There are no further changes to the boot assets,
   162  	// unless a new gadget update is deployed.
   163  	runner.AddHandler("update-gadget-assets", m.doUpdateGadgetAssets, nil)
   164  	// There is no undo handler for successful boot config update. The
   165  	// config assets are assumed to be always backwards compatible.
   166  	runner.AddHandler("update-managed-boot-config", m.doUpdateManagedBootConfig, nil)
   167  	// kernel command line updates from a gadget supplied file
   168  	runner.AddHandler("update-gadget-cmdline", m.doUpdateGadgetCommandLine, m.undoUpdateGadgetCommandLine)
   169  	// recovery systems
   170  	runner.AddHandler("create-recovery-system", m.doCreateRecoverySystem, m.undoCreateRecoverySystem)
   171  	runner.AddHandler("finalize-recovery-system", m.doFinalizeTriedRecoverySystem, m.undoFinalizeTriedRecoverySystem)
   172  	runner.AddCleanup("finalize-recovery-system", m.cleanupRecoverySystem)
   173  
   174  	runner.AddBlocked(gadgetUpdateBlocked)
   175  
   176  	// wire FDE kernel hook support into boot
   177  	boot.HasFDESetupHook = m.hasFDESetupHook
   178  	boot.RunFDESetupHook = m.runFDESetupHook
   179  	hookManager.Register(regexp.MustCompile("^fde-setup$"), newFdeSetupHandler)
   180  
   181  	return m, nil
   182  }
   183  
   184  type genericHook struct{}
   185  
   186  func (h genericHook) Before() error                 { return nil }
   187  func (h genericHook) Done() error                   { return nil }
   188  func (h genericHook) Error(err error) (bool, error) { return false, nil }
   189  
   190  func newBasicHookStateHandler(context *hookstate.Context) hookstate.Handler {
   191  	return genericHook{}
   192  }
   193  
   194  func maybeReadModeenv() (*boot.Modeenv, error) {
   195  	modeEnv, err := boot.ReadModeenv("")
   196  	if err != nil && !os.IsNotExist(err) {
   197  		return nil, fmt.Errorf("cannot read modeenv: %v", err)
   198  	}
   199  	return modeEnv, nil
   200  }
   201  
   202  // ReloadModeenv is only useful for integration testing
   203  func (m *DeviceManager) ReloadModeenv() error {
   204  	osutil.MustBeTestBinary("ReloadModeenv can only be called from tests")
   205  	modeEnv, err := maybeReadModeenv()
   206  	if err != nil {
   207  		return err
   208  	}
   209  	if modeEnv != nil {
   210  		m.sysMode = modeEnv.Mode
   211  	}
   212  	return nil
   213  }
   214  
   215  type SysExpectation int
   216  
   217  const (
   218  	// SysAny indicates any system is appropriate.
   219  	SysAny SysExpectation = iota
   220  	// SysHasModeenv indicates only systems with modeenv are appropriate.
   221  	SysHasModeenv
   222  )
   223  
   224  // SystemMode returns the current mode of the system.
   225  // An expectation about the system controls the returned mode when
   226  // none is set explicitly, as it's the case on pre-UC20 systems. In
   227  // which case, with SysAny, the mode defaults to implicit "run", thus
   228  // covering pre-UC20 systems. With SysHasModeeenv, as there is always
   229  // an explicit mode in systems that use modeenv, no implicit default
   230  // is used and thus "" is returned for pre-UC20 systems.
   231  func (m *DeviceManager) SystemMode(sysExpect SysExpectation) string {
   232  	if m.sysMode == "" {
   233  		if sysExpect == SysHasModeenv {
   234  			return ""
   235  		}
   236  		return "run"
   237  	}
   238  	return m.sysMode
   239  }
   240  
   241  // StartUp implements StateStarterUp.Startup.
   242  func (m *DeviceManager) StartUp() error {
   243  	// TODO:UC20: ubuntu-save needs to be mounted for recover too
   244  	if !release.OnClassic && m.SystemMode(SysHasModeenv) == "run" {
   245  		// UC20 and run mode => setup /var/lib/snapd/save
   246  		if err := m.maybeSetupUbuntuSave(); err != nil {
   247  			return fmt.Errorf("cannot set up ubuntu-save: %v", err)
   248  		}
   249  	}
   250  
   251  	// TODO: setup proper timings measurements for this
   252  
   253  	m.state.Lock()
   254  	defer m.state.Unlock()
   255  	return EarlyConfig(m.state, m.preloadGadget)
   256  }
   257  
   258  func (m *DeviceManager) maybeSetupUbuntuSave() error {
   259  	// only called for UC20
   260  
   261  	saveMounted, err := osutil.IsMounted(dirs.SnapSaveDir)
   262  	if err != nil {
   263  		return err
   264  	}
   265  	if saveMounted {
   266  		logger.Noticef("save already mounted under %v", dirs.SnapSaveDir)
   267  		m.saveAvailable = true
   268  		return nil
   269  	}
   270  
   271  	runMntSaveMounted, err := osutil.IsMounted(boot.InitramfsUbuntuSaveDir)
   272  	if err != nil {
   273  		return err
   274  	}
   275  	if !runMntSaveMounted {
   276  		// we don't have ubuntu-save, save will be used directly
   277  		logger.Noticef("no ubuntu-save mount")
   278  		m.saveAvailable = true
   279  		return nil
   280  	}
   281  
   282  	logger.Noticef("bind-mounting ubuntu-save under %v", dirs.SnapSaveDir)
   283  
   284  	err = systemd.New(systemd.SystemMode, progress.Null).Mount(boot.InitramfsUbuntuSaveDir,
   285  		dirs.SnapSaveDir, "-o", "bind")
   286  	if err != nil {
   287  		logger.Noticef("bind-mounting ubuntu-save failed %v", err)
   288  		return fmt.Errorf("cannot bind mount %v under %v: %v", boot.InitramfsUbuntuSaveDir, dirs.SnapSaveDir, err)
   289  	}
   290  	m.saveAvailable = true
   291  	return nil
   292  }
   293  
   294  type deviceMgrKey struct{}
   295  
   296  func deviceMgr(st *state.State) *DeviceManager {
   297  	mgr := st.Cached(deviceMgrKey{})
   298  	if mgr == nil {
   299  		panic("internal error: device manager is not yet associated with state")
   300  	}
   301  	return mgr.(*DeviceManager)
   302  }
   303  
   304  func (m *DeviceManager) CanStandby() bool {
   305  	var seeded bool
   306  	if err := m.state.Get("seeded", &seeded); err != nil {
   307  		return false
   308  	}
   309  	return seeded
   310  }
   311  
   312  func (m *DeviceManager) confirmRegistered() error {
   313  	m.state.Lock()
   314  	defer m.state.Unlock()
   315  
   316  	device, err := m.device()
   317  	if err != nil {
   318  		return err
   319  	}
   320  
   321  	if device.Serial != "" {
   322  		m.markRegistered()
   323  	}
   324  	return nil
   325  }
   326  
   327  func (m *DeviceManager) markRegistered() {
   328  	if m.registered {
   329  		return
   330  	}
   331  	m.registered = true
   332  	close(m.reg)
   333  }
   334  
   335  func gadgetUpdateBlocked(cand *state.Task, running []*state.Task) bool {
   336  	if cand.Kind() == "update-gadget-assets" && len(running) != 0 {
   337  		// update-gadget-assets must be the only task running
   338  		return true
   339  	}
   340  	for _, other := range running {
   341  		if other.Kind() == "update-gadget-assets" {
   342  			// no other task can be started when
   343  			// update-gadget-assets is running
   344  			return true
   345  		}
   346  	}
   347  
   348  	return false
   349  }
   350  
   351  func (m *DeviceManager) changeInFlight(kind string) bool {
   352  	for _, chg := range m.state.Changes() {
   353  		if chg.Kind() == kind && !chg.Status().Ready() {
   354  			// change already in motion
   355  			return true
   356  		}
   357  	}
   358  	return false
   359  }
   360  
   361  // helpers to keep count of attempts to get a serial, useful to decide
   362  // to give up holding off trying to auto-refresh
   363  
   364  type ensureOperationalAttemptsKey struct{}
   365  
   366  func incEnsureOperationalAttempts(st *state.State) {
   367  	cur, _ := st.Cached(ensureOperationalAttemptsKey{}).(int)
   368  	st.Cache(ensureOperationalAttemptsKey{}, cur+1)
   369  }
   370  
   371  func ensureOperationalAttempts(st *state.State) int {
   372  	cur, _ := st.Cached(ensureOperationalAttemptsKey{}).(int)
   373  	return cur
   374  }
   375  
   376  // ensureOperationalShouldBackoff returns whether we should abstain from
   377  // further become-operational tentatives while its backoff interval is
   378  // not expired.
   379  func (m *DeviceManager) ensureOperationalShouldBackoff(now time.Time) bool {
   380  	if !m.lastBecomeOperationalAttempt.IsZero() && m.lastBecomeOperationalAttempt.Add(m.becomeOperationalBackoff).After(now) {
   381  		return true
   382  	}
   383  	if m.becomeOperationalBackoff == 0 {
   384  		m.becomeOperationalBackoff = 5 * time.Minute
   385  	} else {
   386  		newBackoff := m.becomeOperationalBackoff * 2
   387  		if newBackoff > (12 * time.Hour) {
   388  			newBackoff = 24 * time.Hour
   389  		}
   390  		m.becomeOperationalBackoff = newBackoff
   391  	}
   392  	m.lastBecomeOperationalAttempt = now
   393  	return false
   394  }
   395  
   396  func setClassicFallbackModel(st *state.State, device *auth.DeviceState) error {
   397  	err := assertstate.Add(st, sysdb.GenericClassicModel())
   398  	if err != nil && !asserts.IsUnaccceptedUpdate(err) {
   399  		return fmt.Errorf(`cannot install "generic-classic" fallback model assertion: %v`, err)
   400  	}
   401  	device.Brand = "generic"
   402  	device.Model = "generic-classic"
   403  	if err := internal.SetDevice(st, device); err != nil {
   404  		return err
   405  	}
   406  	return nil
   407  }
   408  
   409  func (m *DeviceManager) ensureOperational() error {
   410  	m.state.Lock()
   411  	defer m.state.Unlock()
   412  
   413  	if m.SystemMode(SysAny) != "run" {
   414  		// avoid doing registration in ephemeral mode
   415  		// note: this also stop auto-refreshes indirectly
   416  		return nil
   417  	}
   418  
   419  	device, err := m.device()
   420  	if err != nil {
   421  		return err
   422  	}
   423  
   424  	if device.Serial != "" {
   425  		// serial is set, we are all set
   426  		return nil
   427  	}
   428  
   429  	perfTimings := timings.New(map[string]string{"ensure": "become-operational"})
   430  
   431  	// conditions to trigger device registration
   432  	//
   433  	// * have a model assertion with a gadget (core and
   434  	//   device-like classic) in which case we need also to wait
   435  	//   for the gadget to have been installed though
   436  	// TODO: consider a way to support lazy registration on classic
   437  	// even with a gadget and some preseeded snaps
   438  	//
   439  	// * classic with a model assertion with a non-default store specified
   440  	// * lazy classic case (might have a model with no gadget nor store
   441  	//   or no model): we wait to have some snaps installed or be
   442  	//   in the process to install some
   443  
   444  	var seeded bool
   445  	err = m.state.Get("seeded", &seeded)
   446  	if err != nil && err != state.ErrNoState {
   447  		return err
   448  	}
   449  
   450  	if device.Brand == "" || device.Model == "" {
   451  		if !release.OnClassic || !seeded {
   452  			return nil
   453  		}
   454  		// we are on classic and seeded but there is no model:
   455  		// use a fallback model!
   456  		err := setClassicFallbackModel(m.state, device)
   457  		if err != nil {
   458  			return err
   459  		}
   460  	}
   461  
   462  	if m.changeInFlight("become-operational") {
   463  		return nil
   464  	}
   465  
   466  	var storeID, gadget string
   467  	model, err := m.Model()
   468  	if err != nil && err != state.ErrNoState {
   469  		return err
   470  	}
   471  	if err == nil {
   472  		gadget = model.Gadget()
   473  		storeID = model.Store()
   474  	} else {
   475  		return fmt.Errorf("internal error: core device brand and model are set but there is no model assertion")
   476  	}
   477  
   478  	if gadget == "" && storeID == "" {
   479  		// classic: if we have no gadget and no non-default store
   480  		// wait to have snaps or snap installation
   481  
   482  		n, err := snapstate.NumSnaps(m.state)
   483  		if err != nil {
   484  			return err
   485  		}
   486  		if n == 0 && !snapstate.Installing(m.state) {
   487  			return nil
   488  		}
   489  	}
   490  
   491  	var hasPrepareDeviceHook bool
   492  	// if there's a gadget specified wait for it
   493  	if gadget != "" {
   494  		// if have a gadget wait until seeded to proceed
   495  		if !seeded {
   496  			// this will be run again, so eventually when the system is
   497  			// seeded the code below runs
   498  			return nil
   499  
   500  		}
   501  
   502  		gadgetInfo, err := snapstate.CurrentInfo(m.state, gadget)
   503  		if err != nil {
   504  			return err
   505  		}
   506  		hasPrepareDeviceHook = (gadgetInfo.Hooks["prepare-device"] != nil)
   507  	}
   508  
   509  	// have some backoff between full retries
   510  	if m.ensureOperationalShouldBackoff(time.Now()) {
   511  		return nil
   512  	}
   513  	// increment attempt count
   514  	incEnsureOperationalAttempts(m.state)
   515  
   516  	// XXX: some of these will need to be split and use hooks
   517  	// retries might need to embrace more than one "task" then,
   518  	// need to be careful
   519  
   520  	tasks := []*state.Task{}
   521  
   522  	var prepareDevice *state.Task
   523  	if hasPrepareDeviceHook {
   524  		summary := i18n.G("Run prepare-device hook")
   525  		hooksup := &hookstate.HookSetup{
   526  			Snap: gadget,
   527  			Hook: "prepare-device",
   528  		}
   529  		prepareDevice = hookstate.HookTask(m.state, summary, hooksup, nil)
   530  		tasks = append(tasks, prepareDevice)
   531  	}
   532  
   533  	genKey := m.state.NewTask("generate-device-key", i18n.G("Generate device key"))
   534  	if prepareDevice != nil {
   535  		genKey.WaitFor(prepareDevice)
   536  	}
   537  	tasks = append(tasks, genKey)
   538  	requestSerial := m.state.NewTask("request-serial", i18n.G("Request device serial"))
   539  	requestSerial.WaitFor(genKey)
   540  	tasks = append(tasks, requestSerial)
   541  
   542  	chg := m.state.NewChange("become-operational", i18n.G("Initialize device"))
   543  	chg.AddAll(state.NewTaskSet(tasks...))
   544  
   545  	state.TagTimingsWithChange(perfTimings, chg)
   546  	perfTimings.Save(m.state)
   547  
   548  	return nil
   549  }
   550  
   551  var startTime time.Time
   552  
   553  func init() {
   554  	startTime = time.Now()
   555  }
   556  
   557  func (m *DeviceManager) setTimeOnce(name string, t time.Time) error {
   558  	var prev time.Time
   559  	err := m.state.Get(name, &prev)
   560  	if err != nil && err != state.ErrNoState {
   561  		return err
   562  	}
   563  	if !prev.IsZero() {
   564  		// already set
   565  		return nil
   566  	}
   567  	m.state.Set(name, t)
   568  	return nil
   569  }
   570  
   571  func (m *DeviceManager) seedStart() (*timings.Timings, error) {
   572  	if m.seedTimings != nil {
   573  		// reuse the early cached one
   574  		return m.seedTimings, nil
   575  	}
   576  	perfTimings := timings.New(map[string]string{"ensure": "seed"})
   577  
   578  	var recordedStart string
   579  	var start time.Time
   580  	if m.preseed {
   581  		recordedStart = "preseed-start-time"
   582  		start = timeNow()
   583  	} else {
   584  		recordedStart = "seed-start-time"
   585  		start = startTime
   586  	}
   587  	if err := m.setTimeOnce(recordedStart, start); err != nil {
   588  		return nil, err
   589  	}
   590  	return perfTimings, nil
   591  }
   592  
   593  func (m *DeviceManager) preloadGadget() (sysconfig.Device, *gadget.Info, error) {
   594  	var sysLabel string
   595  	modeEnv, err := maybeReadModeenv()
   596  	if err != nil {
   597  		return nil, nil, err
   598  	}
   599  	if modeEnv != nil {
   600  		sysLabel = modeEnv.RecoverySystem
   601  	}
   602  
   603  	// we time preloadGadget + first ensureSeeded together
   604  	// under --ensure=seed
   605  	tm, err := m.seedStart()
   606  	if err != nil {
   607  		return nil, nil, err
   608  	}
   609  	// cached for first ensureSeeded
   610  	m.seedTimings = tm
   611  
   612  	// Here we behave as if there was no gadget if we encounter
   613  	// errors, under the assumption that those will be resurfaced
   614  	// in ensureSeed. This preserves having a failing to seed
   615  	// snapd continuing running.
   616  	//
   617  	// TODO: consider changing that again but we need consider the
   618  	// effect of the different failure mode.
   619  	//
   620  	// We also assume that anything sensitive will not be guarded
   621  	// just by option flags. For example automatic user creation
   622  	// also requires the model to be known/set. Otherwise ignoring
   623  	// errors here would be problematic.
   624  	var deviceSeed seed.Seed
   625  	timings.Run(tm, "import-assertions[early]", "early import assertions from seed", func(nested timings.Measurer) {
   626  		deviceSeed, err = loadDeviceSeed(m.state, sysLabel)
   627  	})
   628  	if err != nil {
   629  		// this same error will be resurfaced in ensureSeed later
   630  		if err != seed.ErrNoAssertions {
   631  			logger.Debugf("early import assertions from seed failed: %v", err)
   632  		}
   633  		return nil, nil, state.ErrNoState
   634  	}
   635  	model := deviceSeed.Model()
   636  	if model.Gadget() == "" {
   637  		// no gadget
   638  		return nil, nil, state.ErrNoState
   639  	}
   640  	var gi *gadget.Info
   641  	timings.Run(tm, "preload-verified-gadget-metadata", "preload verified gadget metadata from seed", func(nested timings.Measurer) {
   642  		gi, err = func() (*gadget.Info, error) {
   643  			if err := deviceSeed.LoadEssentialMeta([]snap.Type{snap.TypeGadget}, nested); err != nil {
   644  				return nil, err
   645  			}
   646  			essGadget := deviceSeed.EssentialSnaps()
   647  			if len(essGadget) != 1 {
   648  				return nil, fmt.Errorf("multiple gadgets among essential snaps are unexpected")
   649  			}
   650  			snapf, err := snapfile.Open(essGadget[0].Path)
   651  			if err != nil {
   652  				return nil, err
   653  			}
   654  			return gadget.ReadInfoFromSnapFile(snapf, model)
   655  		}()
   656  	})
   657  	if err != nil {
   658  		logger.Noticef("preload verified gadget metadata from seed failed: %v", err)
   659  		return nil, nil, state.ErrNoState
   660  	}
   661  
   662  	dev := newModelDeviceContext(m, model)
   663  	return dev, gi, nil
   664  }
   665  
   666  var populateStateFromSeed = populateStateFromSeedImpl
   667  
   668  // ensureSeeded makes sure that the snaps from seed.yaml get installed
   669  // with the matching assertions
   670  func (m *DeviceManager) ensureSeeded() error {
   671  	m.state.Lock()
   672  	defer m.state.Unlock()
   673  
   674  	var seeded bool
   675  	err := m.state.Get("seeded", &seeded)
   676  	if err != nil && err != state.ErrNoState {
   677  		return err
   678  	}
   679  	if seeded {
   680  		return nil
   681  	}
   682  
   683  	if m.changeInFlight("seed") {
   684  		return nil
   685  	}
   686  
   687  	perfTimings, err := m.seedStart()
   688  	if err != nil {
   689  		return err
   690  	}
   691  	// we time preloadGadget + first ensureSeeded together
   692  	// succcessive ensureSeeded should be timed separately
   693  	m.seedTimings = nil
   694  
   695  	var opts *populateStateFromSeedOptions
   696  	if m.preseed {
   697  		opts = &populateStateFromSeedOptions{Preseed: true}
   698  	} else {
   699  		modeEnv, err := maybeReadModeenv()
   700  		if err != nil {
   701  			return err
   702  		}
   703  		if modeEnv != nil {
   704  			opts = &populateStateFromSeedOptions{
   705  				Mode:  modeEnv.Mode,
   706  				Label: modeEnv.RecoverySystem,
   707  			}
   708  		}
   709  	}
   710  
   711  	var tsAll []*state.TaskSet
   712  	timings.Run(perfTimings, "state-from-seed", "populate state from seed", func(tm timings.Measurer) {
   713  		tsAll, err = populateStateFromSeed(m.state, opts, tm)
   714  	})
   715  	if err != nil {
   716  		return err
   717  	}
   718  	if len(tsAll) == 0 {
   719  		return nil
   720  	}
   721  
   722  	chg := m.state.NewChange("seed", "Initialize system state")
   723  	for _, ts := range tsAll {
   724  		chg.AddAll(ts)
   725  	}
   726  	m.state.EnsureBefore(0)
   727  
   728  	state.TagTimingsWithChange(perfTimings, chg)
   729  	perfTimings.Save(m.state)
   730  	return nil
   731  }
   732  
   733  func (m *DeviceManager) ensureBootOk() error {
   734  	m.state.Lock()
   735  	defer m.state.Unlock()
   736  
   737  	if release.OnClassic {
   738  		return nil
   739  	}
   740  
   741  	// boot-ok/update-boot-revision is only relevant in run-mode
   742  	if m.SystemMode(SysAny) != "run" {
   743  		return nil
   744  	}
   745  
   746  	if !m.bootOkRan {
   747  		deviceCtx, err := DeviceCtx(m.state, nil, nil)
   748  		if err != nil && err != state.ErrNoState {
   749  			return err
   750  		}
   751  		if err == nil {
   752  			if err := boot.MarkBootSuccessful(deviceCtx); err != nil {
   753  				return err
   754  			}
   755  		}
   756  		m.bootOkRan = true
   757  	}
   758  
   759  	if !m.bootRevisionsUpdated {
   760  		if err := snapstate.UpdateBootRevisions(m.state); err != nil {
   761  			return err
   762  		}
   763  		m.bootRevisionsUpdated = true
   764  	}
   765  
   766  	return nil
   767  }
   768  
   769  func (m *DeviceManager) ensureCloudInitRestricted() error {
   770  	m.state.Lock()
   771  	defer m.state.Unlock()
   772  
   773  	if m.cloudInitAlreadyRestricted {
   774  		return nil
   775  	}
   776  
   777  	var seeded bool
   778  	err := m.state.Get("seeded", &seeded)
   779  	if err != nil && err != state.ErrNoState {
   780  		return err
   781  	}
   782  
   783  	if !seeded {
   784  		// we need to wait until we are seeded
   785  		return nil
   786  	}
   787  
   788  	if release.OnClassic {
   789  		// don't re-run on classic since classic devices don't get subject to
   790  		// the cloud-init restricting that core devices do
   791  		m.cloudInitAlreadyRestricted = true
   792  		return nil
   793  	}
   794  
   795  	// On Ubuntu Core devices that have been seeded, we want to restrict
   796  	// cloud-init so that its more dangerous (for an IoT device at least)
   797  	// features are not exploitable after a device has been seeded. This allows
   798  	// device administrators and other tools (such as multipass) to still
   799  	// configure an Ubuntu Core device on first boot, and also allows cloud
   800  	// vendors to run cloud-init with only a specific data-source on subsequent
   801  	// boots but disallows arbitrary cloud-init {user,meta,vendor}-data to be
   802  	// attached to a device via a USB drive and inject code onto the device.
   803  
   804  	opts := &sysconfig.CloudInitRestrictOptions{}
   805  
   806  	// check the current state of cloud-init, if it is disabled or already
   807  	// restricted then we have nothing to do
   808  	cloudInitStatus, err := cloudInitStatus()
   809  	if err != nil {
   810  		return err
   811  	}
   812  	statusMsg := ""
   813  
   814  	switch cloudInitStatus {
   815  	case sysconfig.CloudInitDisabledPermanently, sysconfig.CloudInitRestrictedBySnapd:
   816  		// already been permanently disabled, nothing to do
   817  		m.cloudInitAlreadyRestricted = true
   818  		return nil
   819  	case sysconfig.CloudInitNotFound:
   820  		// no cloud init at all
   821  		statusMsg = "not found"
   822  	case sysconfig.CloudInitUntriggered:
   823  		// hasn't been used
   824  		statusMsg = "reported to be in disabled state"
   825  	case sysconfig.CloudInitDone:
   826  		// is done being used
   827  		statusMsg = "reported to be done"
   828  	case sysconfig.CloudInitErrored:
   829  		// cloud-init errored, so we give the device admin / developer a few
   830  		// minutes to reboot the machine to re-run cloud-init and try again,
   831  		// otherwise we will disable cloud-init permanently
   832  
   833  		// initialize the time we first saw cloud-init in error state
   834  		if m.cloudInitErrorAttemptStart == nil {
   835  			// save the time we started the attempt to restrict
   836  			now := timeNow()
   837  			m.cloudInitErrorAttemptStart = &now
   838  			logger.Noticef("System initialized, cloud-init reported to be in error state, will disable in 3 minutes")
   839  		}
   840  
   841  		// check if 3 minutes have elapsed since we first saw cloud-init in
   842  		// error state
   843  		timeSinceFirstAttempt := timeNow().Sub(*m.cloudInitErrorAttemptStart)
   844  		if timeSinceFirstAttempt <= 3*time.Minute {
   845  			// we need to keep waiting for cloud-init, up to 3 minutes
   846  			nextCheck := 3*time.Minute - timeSinceFirstAttempt
   847  			m.state.EnsureBefore(nextCheck)
   848  			return nil
   849  		}
   850  		// otherwise, we timed out waiting for cloud-init to be fixed or
   851  		// rebooted and should restrict cloud-init
   852  		// we will restrict cloud-init below, but we need to force the
   853  		// disable, as by default RestrictCloudInit will error on state
   854  		// CloudInitErrored
   855  		opts.ForceDisable = true
   856  		statusMsg = "reported to be in error state after 3 minutes"
   857  	default:
   858  		// in unknown states we are conservative and let the device run for
   859  		// a while to see if it transitions to a known state, but eventually
   860  		// will disable anyways
   861  		fallthrough
   862  	case sysconfig.CloudInitEnabled:
   863  		// we will give cloud-init up to 5 minutes to try and run, if it
   864  		// still has not transitioned to some other known state, then we
   865  		// will give up waiting for it and disable it anyways
   866  
   867  		// initialize the first time we saw cloud-init in enabled state
   868  		if m.cloudInitEnabledInactiveAttemptStart == nil {
   869  			// save the time we started the attempt to restrict
   870  			now := timeNow()
   871  			m.cloudInitEnabledInactiveAttemptStart = &now
   872  		}
   873  
   874  		// keep re-scheduling again in 10 seconds until we hit 5 minutes
   875  		timeSinceFirstAttempt := timeNow().Sub(*m.cloudInitEnabledInactiveAttemptStart)
   876  		if timeSinceFirstAttempt <= 5*time.Minute {
   877  			// TODO: should we log a message here about waiting for cloud-init
   878  			//       to be in a "known state"?
   879  			m.state.EnsureBefore(10 * time.Second)
   880  			return nil
   881  		}
   882  
   883  		// otherwise, we gave cloud-init 5 minutes to run, if it's still not
   884  		// done disable it anyways
   885  		// note we we need to force the disable, as by default
   886  		// RestrictCloudInit will error on state CloudInitEnabled
   887  		opts.ForceDisable = true
   888  		statusMsg = "failed to transition to done or error state after 5 minutes"
   889  	}
   890  
   891  	// we should always have a model if we are seeded and are not on classic
   892  	model, err := m.Model()
   893  	if err != nil {
   894  		return err
   895  	}
   896  
   897  	// For UC20, we want to always disable cloud-init after it has run on
   898  	// first boot unless we are in a "real cloud", i.e. not using NoCloud,
   899  	// or if we installed cloud-init configuration from the gadget
   900  	if model.Grade() != asserts.ModelGradeUnset {
   901  		// always disable NoCloud/local datasources after first boot on
   902  		// uc20, this is because even if the gadget has a cloud.conf
   903  		// configuring NoCloud, the config installed by cloud-init should
   904  		// not work differently for later boots, so it's sufficient that
   905  		// NoCloud runs on first-boot and never again
   906  		opts.DisableAfterLocalDatasourcesRun = true
   907  	}
   908  
   909  	// now restrict/disable cloud-init
   910  	res, err := restrictCloudInit(cloudInitStatus, opts)
   911  	if err != nil {
   912  		return err
   913  	}
   914  
   915  	// log a message about what we did
   916  	actionMsg := ""
   917  	switch res.Action {
   918  	case "disable":
   919  		actionMsg = "disabled permanently"
   920  	case "restrict":
   921  		// log different messages depending on what datasource was used
   922  		if res.DataSource == "NoCloud" {
   923  			actionMsg = "set datasource_list to [ NoCloud ] and disabled auto-import by filesystem label"
   924  		} else {
   925  			// all other datasources just log that we limited it to that datasource
   926  			actionMsg = fmt.Sprintf("set datasource_list to [ %s ]", res.DataSource)
   927  		}
   928  	default:
   929  		return fmt.Errorf("internal error: unexpected action %s taken while restricting cloud-init", res.Action)
   930  	}
   931  	logger.Noticef("System initialized, cloud-init %s, %s", statusMsg, actionMsg)
   932  
   933  	m.cloudInitAlreadyRestricted = true
   934  
   935  	return nil
   936  }
   937  
   938  func (m *DeviceManager) ensureInstalled() error {
   939  	m.state.Lock()
   940  	defer m.state.Unlock()
   941  
   942  	if release.OnClassic {
   943  		return nil
   944  	}
   945  
   946  	if m.ensureInstalledRan {
   947  		return nil
   948  	}
   949  
   950  	if m.SystemMode(SysHasModeenv) != "install" {
   951  		return nil
   952  	}
   953  
   954  	var seeded bool
   955  	err := m.state.Get("seeded", &seeded)
   956  	if err != nil && err != state.ErrNoState {
   957  		return err
   958  	}
   959  	if !seeded {
   960  		return nil
   961  	}
   962  
   963  	if m.changeInFlight("install-system") {
   964  		return nil
   965  	}
   966  
   967  	perfTimings := timings.New(map[string]string{"ensure": "install-system"})
   968  
   969  	model, err := m.Model()
   970  	if err != nil && err != state.ErrNoState {
   971  		return err
   972  	}
   973  	if err != nil {
   974  		return fmt.Errorf("internal error: core device brand and model are set but there is no model assertion")
   975  	}
   976  
   977  	// check if the gadget has an install-device hook
   978  	var hasInstallDeviceHook bool
   979  
   980  	gadgetInfo, err := snapstate.CurrentInfo(m.state, model.Gadget())
   981  	if err != nil {
   982  		return fmt.Errorf("internal error: device is seeded in install mode but has no gadget snap: %v", err)
   983  	}
   984  	hasInstallDeviceHook = (gadgetInfo.Hooks["install-device"] != nil)
   985  
   986  	m.ensureInstalledRan = true
   987  
   988  	var prev *state.Task
   989  	setupRunSystem := m.state.NewTask("setup-run-system", i18n.G("Setup system for run mode"))
   990  
   991  	tasks := []*state.Task{setupRunSystem}
   992  	addTask := func(t *state.Task) {
   993  		t.WaitFor(prev)
   994  		tasks = append(tasks, t)
   995  		prev = t
   996  	}
   997  	prev = setupRunSystem
   998  
   999  	// add the install-device hook before ensure-next-boot-to-run-mode if it
  1000  	// exists in the snap
  1001  	var installDevice *state.Task
  1002  	if hasInstallDeviceHook {
  1003  		summary := i18n.G("Run install-device hook")
  1004  		hooksup := &hookstate.HookSetup{
  1005  			// TODO: what's a reasonable timeout for the install-device hook?
  1006  			Snap: model.Gadget(),
  1007  			Hook: "install-device",
  1008  		}
  1009  		installDevice = hookstate.HookTask(m.state, summary, hooksup, nil)
  1010  		addTask(installDevice)
  1011  	}
  1012  
  1013  	restartSystem := m.state.NewTask("restart-system-to-run-mode", i18n.G("Ensure next boot to run mode"))
  1014  	addTask(restartSystem)
  1015  
  1016  	if installDevice != nil {
  1017  		// reference used by snapctl reboot
  1018  		installDevice.Set("restart-task", restartSystem.ID())
  1019  	}
  1020  
  1021  	chg := m.state.NewChange("install-system", i18n.G("Install the system"))
  1022  	chg.AddAll(state.NewTaskSet(tasks...))
  1023  
  1024  	state.TagTimingsWithChange(perfTimings, chg)
  1025  	perfTimings.Save(m.state)
  1026  
  1027  	return nil
  1028  }
  1029  
  1030  var timeNow = time.Now
  1031  
  1032  // StartOfOperationTime returns the time when snapd started operating,
  1033  // and sets it in the state when called for the first time.
  1034  // The StartOfOperationTime time is seed-time if available,
  1035  // or current time otherwise.
  1036  func (m *DeviceManager) StartOfOperationTime() (time.Time, error) {
  1037  	var opTime time.Time
  1038  	if m.preseed {
  1039  		return opTime, fmt.Errorf("internal error: unexpected call to StartOfOperationTime in preseed mode")
  1040  	}
  1041  	err := m.state.Get("start-of-operation-time", &opTime)
  1042  	if err == nil {
  1043  		return opTime, nil
  1044  	}
  1045  	if err != nil && err != state.ErrNoState {
  1046  		return opTime, err
  1047  	}
  1048  
  1049  	// start-of-operation-time not set yet, use seed-time if available
  1050  	var seedTime time.Time
  1051  	err = m.state.Get("seed-time", &seedTime)
  1052  	if err != nil && err != state.ErrNoState {
  1053  		return opTime, err
  1054  	}
  1055  	if err == nil {
  1056  		opTime = seedTime
  1057  	} else {
  1058  		opTime = timeNow()
  1059  	}
  1060  	m.state.Set("start-of-operation-time", opTime)
  1061  	return opTime, nil
  1062  }
  1063  
  1064  func markSeededInConfig(st *state.State) error {
  1065  	var seedDone bool
  1066  	tr := config.NewTransaction(st)
  1067  	if err := tr.Get("core", "seed.loaded", &seedDone); err != nil && !config.IsNoOption(err) {
  1068  		return err
  1069  	}
  1070  	if !seedDone {
  1071  		if err := tr.Set("core", "seed.loaded", true); err != nil {
  1072  			return err
  1073  		}
  1074  		tr.Commit()
  1075  	}
  1076  	return nil
  1077  }
  1078  
  1079  func (m *DeviceManager) ensureSeedInConfig() error {
  1080  	m.state.Lock()
  1081  	defer m.state.Unlock()
  1082  
  1083  	if !m.ensureSeedInConfigRan {
  1084  		// get global seeded option
  1085  		var seeded bool
  1086  		if err := m.state.Get("seeded", &seeded); err != nil && err != state.ErrNoState {
  1087  			return err
  1088  		}
  1089  		if !seeded {
  1090  			// wait for ensure again, this is fine because
  1091  			// doMarkSeeded will run "EnsureBefore(0)"
  1092  			return nil
  1093  		}
  1094  
  1095  		// Sync seeding with the configuration state. We need to
  1096  		// do this here to ensure that old systems which did not
  1097  		// set the configuration on seeding get the configuration
  1098  		// update too.
  1099  		if err := markSeededInConfig(m.state); err != nil {
  1100  			return err
  1101  		}
  1102  		m.ensureSeedInConfigRan = true
  1103  	}
  1104  
  1105  	return nil
  1106  
  1107  }
  1108  
  1109  func (m *DeviceManager) appendTriedRecoverySystem(label string) error {
  1110  	// state is locked by the caller
  1111  
  1112  	var triedSystems []string
  1113  	if err := m.state.Get("tried-systems", &triedSystems); err != nil && err != state.ErrNoState {
  1114  		return err
  1115  	}
  1116  	if strutil.ListContains(triedSystems, label) {
  1117  		// system already recorded as tried?
  1118  		return nil
  1119  	}
  1120  	triedSystems = append(triedSystems, label)
  1121  	m.state.Set("tried-systems", triedSystems)
  1122  	return nil
  1123  }
  1124  
  1125  func (m *DeviceManager) ensureTriedRecoverySystem() error {
  1126  	if release.OnClassic {
  1127  		return nil
  1128  	}
  1129  	// nothing to do if not UC20 and run mode
  1130  	if m.SystemMode(SysHasModeenv) != "run" {
  1131  		return nil
  1132  	}
  1133  	if m.ensureTriedRecoverySystemRan {
  1134  		return nil
  1135  	}
  1136  
  1137  	m.state.Lock()
  1138  	defer m.state.Unlock()
  1139  
  1140  	deviceCtx, err := DeviceCtx(m.state, nil, nil)
  1141  	if err != nil && err != state.ErrNoState {
  1142  		return err
  1143  	}
  1144  	outcome, label, err := boot.InspectTryRecoverySystemOutcome(deviceCtx)
  1145  	if err != nil {
  1146  		if !boot.IsInconsistentRecoverySystemState(err) {
  1147  			return err
  1148  		}
  1149  		// boot variables state was inconsistent
  1150  		logger.Noticef("tried recovery system outcome error: %v", err)
  1151  	}
  1152  	switch outcome {
  1153  	case boot.TryRecoverySystemOutcomeSuccess:
  1154  		logger.Noticef("tried recovery system %q was successful", label)
  1155  		if err := m.appendTriedRecoverySystem(label); err != nil {
  1156  			return err
  1157  		}
  1158  	case boot.TryRecoverySystemOutcomeFailure:
  1159  		logger.Noticef("tried recovery system %q failed", label)
  1160  	case boot.TryRecoverySystemOutcomeInconsistent:
  1161  		logger.Noticef("inconsistent outcome of a tried recovery system")
  1162  	case boot.TryRecoverySystemOutcomeNoneTried:
  1163  		// no system was tried
  1164  	}
  1165  	if outcome != boot.TryRecoverySystemOutcomeNoneTried {
  1166  		if err := boot.ClearTryRecoverySystem(deviceCtx, label); err != nil {
  1167  			logger.Noticef("cannot clear tried recovery system status: %v", err)
  1168  			return err
  1169  		}
  1170  	}
  1171  
  1172  	m.ensureTriedRecoverySystemRan = true
  1173  	return nil
  1174  }
  1175  
  1176  type ensureError struct {
  1177  	errs []error
  1178  }
  1179  
  1180  func (e *ensureError) Error() string {
  1181  	if len(e.errs) == 1 {
  1182  		return fmt.Sprintf("devicemgr: %v", e.errs[0])
  1183  	}
  1184  	parts := []string{"devicemgr:"}
  1185  	for _, e := range e.errs {
  1186  		parts = append(parts, e.Error())
  1187  	}
  1188  	return strings.Join(parts, "\n - ")
  1189  }
  1190  
  1191  // no \n allowed in warnings
  1192  var seedFailureFmt = `seeding failed with: %v. This indicates an error in your distribution, please see https://forum.snapcraft.io/t/16341 for more information.`
  1193  
  1194  // Ensure implements StateManager.Ensure.
  1195  func (m *DeviceManager) Ensure() error {
  1196  	var errs []error
  1197  
  1198  	if err := m.ensureSeeded(); err != nil {
  1199  		m.state.Lock()
  1200  		m.state.Warnf(seedFailureFmt, err)
  1201  		m.state.Unlock()
  1202  		errs = append(errs, fmt.Errorf("cannot seed: %v", err))
  1203  	}
  1204  
  1205  	if !m.preseed {
  1206  		if err := m.ensureCloudInitRestricted(); err != nil {
  1207  			errs = append(errs, err)
  1208  		}
  1209  
  1210  		if err := m.ensureOperational(); err != nil {
  1211  			errs = append(errs, err)
  1212  		}
  1213  
  1214  		if err := m.ensureBootOk(); err != nil {
  1215  			errs = append(errs, err)
  1216  		}
  1217  
  1218  		if err := m.ensureSeedInConfig(); err != nil {
  1219  			errs = append(errs, err)
  1220  		}
  1221  
  1222  		if err := m.ensureInstalled(); err != nil {
  1223  			errs = append(errs, err)
  1224  		}
  1225  
  1226  		if err := m.ensureTriedRecoverySystem(); err != nil {
  1227  			errs = append(errs, err)
  1228  		}
  1229  	}
  1230  
  1231  	if len(errs) > 0 {
  1232  		return &ensureError{errs}
  1233  	}
  1234  
  1235  	return nil
  1236  }
  1237  
  1238  // ResetToPostBootState is only useful for integration testing.
  1239  func (m *DeviceManager) ResetToPostBootState() {
  1240  	osutil.MustBeTestBinary("ResetToPostBootState can only be called from tests")
  1241  	m.bootOkRan = false
  1242  	m.bootRevisionsUpdated = false
  1243  	m.ensureTriedRecoverySystemRan = false
  1244  }
  1245  
  1246  var errNoSaveSupport = errors.New("no save directory before UC20")
  1247  
  1248  // withSaveDir invokes a function making sure save dir is available.
  1249  // Under UC16/18 it returns errNoSaveSupport
  1250  // For UC20 it also checks that ubuntu-save is available/mounted.
  1251  func (m *DeviceManager) withSaveDir(f func() error) error {
  1252  	// we use the model to check whether this is a UC20 device
  1253  	model, err := m.Model()
  1254  	if err == state.ErrNoState {
  1255  		return fmt.Errorf("internal error: cannot access save dir before a model is set")
  1256  	}
  1257  	if err != nil {
  1258  		return err
  1259  	}
  1260  	if model.Grade() == asserts.ModelGradeUnset {
  1261  		return errNoSaveSupport
  1262  	}
  1263  	// at this point we need save available
  1264  	if !m.saveAvailable {
  1265  		return fmt.Errorf("internal error: save dir is unavailable")
  1266  	}
  1267  
  1268  	return f()
  1269  }
  1270  
  1271  // withSaveAssertDB invokes a function making the save device assertion
  1272  // backup database available to it.
  1273  // Under UC16/18 it returns errNoSaveSupport
  1274  // For UC20 it also checks that ubuntu-save is available/mounted.
  1275  func (m *DeviceManager) withSaveAssertDB(f func(*asserts.Database) error) error {
  1276  	return m.withSaveDir(func() error {
  1277  		// open an ancillary backup assertion database in save/device
  1278  		assertDB, err := sysdb.OpenAt(dirs.SnapDeviceSaveDir)
  1279  		if err != nil {
  1280  			return err
  1281  		}
  1282  		return f(assertDB)
  1283  	})
  1284  }
  1285  
  1286  // withKeypairMgr invokes a function making the device KeypairManager
  1287  // available to it.
  1288  // It uses the right location for the manager depending on UC16/18 vs 20,
  1289  // the latter uses ubuntu-save.
  1290  // For UC20 it also checks that ubuntu-save is available/mounted.
  1291  func (m *DeviceManager) withKeypairMgr(f func(asserts.KeypairManager) error) error {
  1292  	// we use the model to check whether this is a UC20 device
  1293  	// TODO: during a theoretical UC18->20 remodel the location of
  1294  	// keypair manager keys would move, we will need dedicated code
  1295  	// to deal with that, this code typically will return the old location
  1296  	// until a restart
  1297  	model, err := m.Model()
  1298  	if err == state.ErrNoState {
  1299  		return fmt.Errorf("internal error: cannot access device keypair manager before a model is set")
  1300  	}
  1301  	if err != nil {
  1302  		return err
  1303  	}
  1304  	underSave := false
  1305  	if model.Grade() != asserts.ModelGradeUnset {
  1306  		// on UC20 the keys are kept under the save dir
  1307  		underSave = true
  1308  	}
  1309  	where := dirs.SnapDeviceDir
  1310  	if underSave {
  1311  		// at this point we need save available
  1312  		if !m.saveAvailable {
  1313  			return fmt.Errorf("internal error: cannot access device keypair manager if ubuntu-save is unavailable")
  1314  		}
  1315  		where = dirs.SnapDeviceSaveDir
  1316  	}
  1317  	keypairMgr := m.cachedKeypairMgr
  1318  	if keypairMgr == nil {
  1319  		var err error
  1320  		keypairMgr, err = asserts.OpenFSKeypairManager(where)
  1321  		if err != nil {
  1322  			return err
  1323  		}
  1324  		m.cachedKeypairMgr = keypairMgr
  1325  	}
  1326  	return f(keypairMgr)
  1327  }
  1328  
  1329  // TODO:UC20: we need proper encapsulated support to read
  1330  // tpm-policy-auth-key from save if the latter can get unmounted on
  1331  // demand
  1332  
  1333  func (m *DeviceManager) keyPair() (asserts.PrivateKey, error) {
  1334  	device, err := m.device()
  1335  	if err != nil {
  1336  		return nil, err
  1337  	}
  1338  
  1339  	if device.KeyID == "" {
  1340  		return nil, state.ErrNoState
  1341  	}
  1342  
  1343  	var privKey asserts.PrivateKey
  1344  	err = m.withKeypairMgr(func(keypairMgr asserts.KeypairManager) (err error) {
  1345  		privKey, err = keypairMgr.Get(device.KeyID)
  1346  		if err != nil {
  1347  			return fmt.Errorf("cannot read device key pair: %v", err)
  1348  		}
  1349  		return nil
  1350  	})
  1351  	if err != nil {
  1352  		return nil, err
  1353  	}
  1354  	return privKey, nil
  1355  }
  1356  
  1357  // Registered returns a channel that is closed when the device is known to have been registered.
  1358  func (m *DeviceManager) Registered() <-chan struct{} {
  1359  	return m.reg
  1360  }
  1361  
  1362  // device returns current device state.
  1363  func (m *DeviceManager) device() (*auth.DeviceState, error) {
  1364  	return internal.Device(m.state)
  1365  }
  1366  
  1367  // setDevice sets the device details in the state.
  1368  func (m *DeviceManager) setDevice(device *auth.DeviceState) error {
  1369  	return internal.SetDevice(m.state, device)
  1370  }
  1371  
  1372  // Model returns the device model assertion.
  1373  func (m *DeviceManager) Model() (*asserts.Model, error) {
  1374  	return findModel(m.state)
  1375  }
  1376  
  1377  // Serial returns the device serial assertion.
  1378  func (m *DeviceManager) Serial() (*asserts.Serial, error) {
  1379  	return findSerial(m.state, nil)
  1380  }
  1381  
  1382  type SystemModeInfo struct {
  1383  	Mode              string
  1384  	HasModeenv        bool
  1385  	Seeded            bool
  1386  	BootFlags         []string
  1387  	HostDataLocations []string
  1388  }
  1389  
  1390  // SystemModeInfo returns details about the current system mode the device is in.
  1391  func (m *DeviceManager) SystemModeInfo() (*SystemModeInfo, error) {
  1392  	deviceCtx, err := DeviceCtx(m.state, nil, nil)
  1393  	if err == state.ErrNoState {
  1394  		return nil, fmt.Errorf("cannot report system mode information before device model is acknowledged")
  1395  	}
  1396  	if err != nil {
  1397  		return nil, err
  1398  	}
  1399  
  1400  	var seeded bool
  1401  	err = m.state.Get("seeded", &seeded)
  1402  	if err != nil && err != state.ErrNoState {
  1403  		return nil, err
  1404  	}
  1405  
  1406  	mode := deviceCtx.SystemMode()
  1407  	smi := SystemModeInfo{
  1408  		Mode:       mode,
  1409  		HasModeenv: deviceCtx.HasModeenv(),
  1410  		Seeded:     seeded,
  1411  	}
  1412  	if smi.HasModeenv {
  1413  		bootFlags, err := boot.BootFlags(deviceCtx)
  1414  		if err != nil {
  1415  			return nil, err
  1416  		}
  1417  		smi.BootFlags = bootFlags
  1418  
  1419  		hostDataLocs, err := boot.HostUbuntuDataForMode(mode)
  1420  		if err != nil {
  1421  			return nil, err
  1422  		}
  1423  		smi.HostDataLocations = hostDataLocs
  1424  	}
  1425  	return &smi, nil
  1426  }
  1427  
  1428  type SystemAction struct {
  1429  	Title string
  1430  	Mode  string
  1431  }
  1432  
  1433  type System struct {
  1434  	// Current is true when the system running now was installed from that
  1435  	// seed
  1436  	Current bool
  1437  	// Label of the seed system
  1438  	Label string
  1439  	// Model assertion of the system
  1440  	Model *asserts.Model
  1441  	// Brand information
  1442  	Brand *asserts.Account
  1443  	// Actions available for this system
  1444  	Actions []SystemAction
  1445  }
  1446  
  1447  var defaultSystemActions = []SystemAction{
  1448  	{Title: "Install", Mode: "install"},
  1449  }
  1450  var currentSystemActions = []SystemAction{
  1451  	{Title: "Reinstall", Mode: "install"},
  1452  	{Title: "Recover", Mode: "recover"},
  1453  	{Title: "Run normally", Mode: "run"},
  1454  }
  1455  var recoverSystemActions = []SystemAction{
  1456  	{Title: "Reinstall", Mode: "install"},
  1457  	{Title: "Run normally", Mode: "run"},
  1458  }
  1459  
  1460  var ErrNoSystems = errors.New("no systems seeds")
  1461  
  1462  // Systems list the available recovery/seeding systems. Returns the list of
  1463  // systems, ErrNoSystems when no systems seeds were found or other error.
  1464  func (m *DeviceManager) Systems() ([]*System, error) {
  1465  	// it's tough luck when we cannot determine the current system seed
  1466  	systemMode := m.SystemMode(SysAny)
  1467  	currentSys, _ := currentSystemForMode(m.state, systemMode)
  1468  
  1469  	systemLabels, err := filepath.Glob(filepath.Join(dirs.SnapSeedDir, "systems", "*"))
  1470  	if err != nil && !os.IsNotExist(err) {
  1471  		return nil, fmt.Errorf("cannot list available systems: %v", err)
  1472  	}
  1473  	if len(systemLabels) == 0 {
  1474  		// maybe not a UC20 system
  1475  		return nil, ErrNoSystems
  1476  	}
  1477  
  1478  	var systems []*System
  1479  	for _, fpLabel := range systemLabels {
  1480  		label := filepath.Base(fpLabel)
  1481  		system, err := systemFromSeed(label, currentSys)
  1482  		if err != nil {
  1483  			// TODO:UC20 add a Broken field to the seed system like
  1484  			// we do for snap.Info
  1485  			logger.Noticef("cannot load system %q seed: %v", label, err)
  1486  			continue
  1487  		}
  1488  		systems = append(systems, system)
  1489  	}
  1490  	return systems, nil
  1491  }
  1492  
  1493  var ErrUnsupportedAction = errors.New("unsupported action")
  1494  
  1495  // Reboot triggers a reboot into the given systemLabel and mode.
  1496  //
  1497  // When called without a systemLabel and without a mode it will just
  1498  // trigger a regular reboot.
  1499  //
  1500  // When called without a systemLabel but with a mode it will use
  1501  // the current system to enter the given mode.
  1502  //
  1503  // Note that "recover" and "run" modes are only available for the
  1504  // current system.
  1505  func (m *DeviceManager) Reboot(systemLabel, mode string) error {
  1506  	rebootCurrent := func() {
  1507  		logger.Noticef("rebooting system")
  1508  		m.state.RequestRestart(state.RestartSystemNow)
  1509  	}
  1510  
  1511  	// most simple case: just reboot
  1512  	if systemLabel == "" && mode == "" {
  1513  		m.state.Lock()
  1514  		defer m.state.Unlock()
  1515  
  1516  		rebootCurrent()
  1517  		return nil
  1518  	}
  1519  
  1520  	// no systemLabel means "current" so get the current system label
  1521  	if systemLabel == "" {
  1522  		systemMode := m.SystemMode(SysAny)
  1523  		currentSys, err := currentSystemForMode(m.state, systemMode)
  1524  		if err != nil {
  1525  			return fmt.Errorf("cannot get current system: %v", err)
  1526  		}
  1527  		systemLabel = currentSys.System
  1528  	}
  1529  
  1530  	switched := func(systemLabel string, sysAction *SystemAction) {
  1531  		logger.Noticef("rebooting into system %q in %q mode", systemLabel, sysAction.Mode)
  1532  		m.state.RequestRestart(state.RestartSystemNow)
  1533  	}
  1534  	// even if we are already in the right mode we restart here by
  1535  	// passing rebootCurrent as this is what the user requested
  1536  	return m.switchToSystemAndMode(systemLabel, mode, rebootCurrent, switched)
  1537  }
  1538  
  1539  // RequestSystemAction requests the provided system to be run in a
  1540  // given mode as specified by action.
  1541  // A system reboot will be requested when the request can be
  1542  // successfully carried out.
  1543  func (m *DeviceManager) RequestSystemAction(systemLabel string, action SystemAction) error {
  1544  	if systemLabel == "" {
  1545  		return fmt.Errorf("internal error: system label is unset")
  1546  	}
  1547  
  1548  	nop := func() {}
  1549  	switched := func(systemLabel string, sysAction *SystemAction) {
  1550  		logger.Noticef("restarting into system %q for action %q", systemLabel, sysAction.Title)
  1551  		m.state.RequestRestart(state.RestartSystemNow)
  1552  	}
  1553  	// we do nothing (nop) if the mode and system are the same
  1554  	return m.switchToSystemAndMode(systemLabel, action.Mode, nop, switched)
  1555  }
  1556  
  1557  // switchToSystemAndMode switches to given systemLabel and mode.
  1558  // If the systemLabel and mode are the same as current, it calls
  1559  // sameSystemAndMode. If successful otherwise it calls switched. Both
  1560  // are called with the state lock held.
  1561  func (m *DeviceManager) switchToSystemAndMode(systemLabel, mode string, sameSystemAndMode func(), switched func(systemLabel string, sysAction *SystemAction)) error {
  1562  	if err := checkSystemRequestConflict(m.state, systemLabel); err != nil {
  1563  		return err
  1564  	}
  1565  
  1566  	systemMode := m.SystemMode(SysAny)
  1567  	// ignore the error to be robust in scenarios that
  1568  	// dont' stricly require currentSys to be carried through.
  1569  	// make sure that currentSys == nil does not break
  1570  	// the code below!
  1571  	// TODO: should we log the error?
  1572  	currentSys, _ := currentSystemForMode(m.state, systemMode)
  1573  
  1574  	systemSeedDir := filepath.Join(dirs.SnapSeedDir, "systems", systemLabel)
  1575  	if _, err := os.Stat(systemSeedDir); err != nil {
  1576  		// XXX: should we wrap this instead return a naked stat error?
  1577  		return err
  1578  	}
  1579  	system, err := systemFromSeed(systemLabel, currentSys)
  1580  	if err != nil {
  1581  		return fmt.Errorf("cannot load seed system: %v", err)
  1582  	}
  1583  
  1584  	var sysAction *SystemAction
  1585  	for _, act := range system.Actions {
  1586  		if mode == act.Mode {
  1587  			sysAction = &act
  1588  			break
  1589  		}
  1590  	}
  1591  	if sysAction == nil {
  1592  		// XXX: provide more context here like what mode was requested?
  1593  		return ErrUnsupportedAction
  1594  	}
  1595  
  1596  	// XXX: requested mode is valid; only current system has 'run' and
  1597  	// recover 'actions'
  1598  
  1599  	switch systemMode {
  1600  	case "recover", "run":
  1601  		// if going from recover to recover or from run to run and the systems
  1602  		// are the same do nothing
  1603  		if systemMode == sysAction.Mode && currentSys != nil && systemLabel == currentSys.System {
  1604  			m.state.Lock()
  1605  			defer m.state.Unlock()
  1606  			sameSystemAndMode()
  1607  			return nil
  1608  		}
  1609  	case "install":
  1610  		// requesting system actions in install mode does not make sense atm
  1611  		//
  1612  		// TODO:UC20: maybe factory hooks will be able to something like
  1613  		// this?
  1614  		return ErrUnsupportedAction
  1615  	default:
  1616  		// probably test device manager mocking problem, or also potentially
  1617  		// missing modeenv
  1618  		return fmt.Errorf("internal error: unexpected manager system mode %q", systemMode)
  1619  	}
  1620  
  1621  	m.state.Lock()
  1622  	defer m.state.Unlock()
  1623  
  1624  	deviceCtx, err := DeviceCtx(m.state, nil, nil)
  1625  	if err != nil {
  1626  		return err
  1627  	}
  1628  	if err := boot.SetRecoveryBootSystemAndMode(deviceCtx, systemLabel, mode); err != nil {
  1629  		return fmt.Errorf("cannot set device to boot into system %q in mode %q: %v", systemLabel, mode, err)
  1630  	}
  1631  
  1632  	switched(systemLabel, sysAction)
  1633  	return nil
  1634  }
  1635  
  1636  // implement storecontext.Backend
  1637  
  1638  type storeContextBackend struct {
  1639  	*DeviceManager
  1640  }
  1641  
  1642  func (scb storeContextBackend) Device() (*auth.DeviceState, error) {
  1643  	return scb.DeviceManager.device()
  1644  }
  1645  
  1646  func (scb storeContextBackend) SetDevice(device *auth.DeviceState) error {
  1647  	return scb.DeviceManager.setDevice(device)
  1648  }
  1649  
  1650  func (scb storeContextBackend) ProxyStore() (*asserts.Store, error) {
  1651  	st := scb.DeviceManager.state
  1652  	return proxyStore(st, config.NewTransaction(st))
  1653  }
  1654  
  1655  // SignDeviceSessionRequest produces a signed device-session-request with for given serial assertion and nonce.
  1656  func (scb storeContextBackend) SignDeviceSessionRequest(serial *asserts.Serial, nonce string) (*asserts.DeviceSessionRequest, error) {
  1657  	if serial == nil {
  1658  		// shouldn't happen, but be safe
  1659  		return nil, fmt.Errorf("internal error: cannot sign a session request without a serial")
  1660  	}
  1661  
  1662  	privKey, err := scb.DeviceManager.keyPair()
  1663  	if err == state.ErrNoState {
  1664  		return nil, fmt.Errorf("internal error: inconsistent state with serial but no device key")
  1665  	}
  1666  	if err != nil {
  1667  		return nil, err
  1668  	}
  1669  
  1670  	a, err := asserts.SignWithoutAuthority(asserts.DeviceSessionRequestType, map[string]interface{}{
  1671  		"brand-id":  serial.BrandID(),
  1672  		"model":     serial.Model(),
  1673  		"serial":    serial.Serial(),
  1674  		"nonce":     nonce,
  1675  		"timestamp": time.Now().UTC().Format(time.RFC3339),
  1676  	}, nil, privKey)
  1677  	if err != nil {
  1678  		return nil, err
  1679  	}
  1680  
  1681  	return a.(*asserts.DeviceSessionRequest), nil
  1682  }
  1683  
  1684  func (m *DeviceManager) StoreContextBackend() storecontext.Backend {
  1685  	return storeContextBackend{m}
  1686  }
  1687  
  1688  func (m *DeviceManager) hasFDESetupHook() (bool, error) {
  1689  	// state must be locked
  1690  	st := m.state
  1691  
  1692  	deviceCtx, err := DeviceCtx(st, nil, nil)
  1693  	if err != nil {
  1694  		return false, fmt.Errorf("cannot get device context: %v", err)
  1695  	}
  1696  
  1697  	kernelInfo, err := snapstate.KernelInfo(st, deviceCtx)
  1698  	if err != nil {
  1699  		return false, fmt.Errorf("cannot get kernel info: %v", err)
  1700  	}
  1701  	return hasFDESetupHookInKernel(kernelInfo), nil
  1702  }
  1703  
  1704  func (m *DeviceManager) runFDESetupHook(req *fde.SetupRequest) ([]byte, error) {
  1705  	// TODO:UC20: when this runs on refresh we need to be very careful
  1706  	// that we never run this when the kernel is not fully configured
  1707  	// i.e. when there are no security profiles for the hook
  1708  
  1709  	// state must be locked
  1710  	st := m.state
  1711  
  1712  	deviceCtx, err := DeviceCtx(st, nil, nil)
  1713  	if err != nil {
  1714  		return nil, fmt.Errorf("cannot get device context to run fde-setup hook: %v", err)
  1715  	}
  1716  	kernelInfo, err := snapstate.KernelInfo(st, deviceCtx)
  1717  	if err != nil {
  1718  		return nil, fmt.Errorf("cannot get kernel info to run fde-setup hook: %v", err)
  1719  	}
  1720  	hooksup := &hookstate.HookSetup{
  1721  		Snap:     kernelInfo.InstanceName(),
  1722  		Revision: kernelInfo.Revision,
  1723  		Hook:     "fde-setup",
  1724  		// XXX: should this be configurable somehow?
  1725  		Timeout: 5 * time.Minute,
  1726  	}
  1727  	contextData := map[string]interface{}{
  1728  		"fde-setup-request": req,
  1729  	}
  1730  	st.Unlock()
  1731  	defer st.Lock()
  1732  	context, err := m.hookMgr.EphemeralRunHook(context.Background(), hooksup, contextData)
  1733  	if err != nil {
  1734  		return nil, fmt.Errorf("cannot run hook for %q: %v", req.Op, err)
  1735  	}
  1736  	// the hook is expected to call "snapctl fde-setup-result" which
  1737  	// will set the "fde-setup-result" value on the task
  1738  	var hookOutput []byte
  1739  	context.Lock()
  1740  	err = context.Get("fde-setup-result", &hookOutput)
  1741  	context.Unlock()
  1742  	if err != nil {
  1743  		return nil, fmt.Errorf("cannot get result from fde-setup hook %q: %v", req.Op, err)
  1744  	}
  1745  	return hookOutput, nil
  1746  }
  1747  
  1748  func (m *DeviceManager) checkFDEFeatures(st *state.State) error {
  1749  	// TODO: move most of this to kernel/fde.Features
  1750  	// Run fde-setup hook with "op":"features". If the hook
  1751  	// returns any {"features":[...]} reply we consider the
  1752  	// hardware supported. If the hook errors or if it returns
  1753  	// {"error":"hardware-unsupported"} we don't.
  1754  	req := &fde.SetupRequest{
  1755  		Op: "features",
  1756  	}
  1757  	output, err := m.runFDESetupHook(req)
  1758  	if err != nil {
  1759  		return err
  1760  	}
  1761  	var res struct {
  1762  		Features []string `json:"features"`
  1763  		Error    string   `json:"error"`
  1764  	}
  1765  	if err := json.Unmarshal(output, &res); err != nil {
  1766  		return fmt.Errorf("cannot parse hook output %q: %v", output, err)
  1767  	}
  1768  	if res.Features == nil && res.Error == "" {
  1769  		return fmt.Errorf(`cannot use hook: neither "features" nor "error" returned`)
  1770  	}
  1771  	if res.Error != "" {
  1772  		return fmt.Errorf("cannot use hook: it returned error: %v", res.Error)
  1773  	}
  1774  	return nil
  1775  }
  1776  
  1777  func hasFDESetupHookInKernel(kernelInfo *snap.Info) bool {
  1778  	_, ok := kernelInfo.Hooks["fde-setup"]
  1779  	return ok
  1780  }
  1781  
  1782  type fdeSetupHandler struct {
  1783  	context *hookstate.Context
  1784  }
  1785  
  1786  func newFdeSetupHandler(ctx *hookstate.Context) hookstate.Handler {
  1787  	return fdeSetupHandler{context: ctx}
  1788  }
  1789  
  1790  func (h fdeSetupHandler) Before() error {
  1791  	return nil
  1792  }
  1793  
  1794  func (h fdeSetupHandler) Done() error {
  1795  	return nil
  1796  }
  1797  
  1798  func (h fdeSetupHandler) Error(err error) (bool, error) {
  1799  	return false, nil
  1800  }