github.com/kubiko/snapd@v0.0.0-20201013125620-d4f3094d9ddf/overlord/devicestate/devicemgr.go (about)

     1  // -*- Mode: Go; indent-tabs-mode: t -*-
     2  
     3  /*
     4   * Copyright (C) 2016-2020 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  	"errors"
    24  	"fmt"
    25  	"os"
    26  	"path/filepath"
    27  	"regexp"
    28  	"strings"
    29  	"time"
    30  
    31  	"github.com/snapcore/snapd/asserts"
    32  	"github.com/snapcore/snapd/asserts/sysdb"
    33  	"github.com/snapcore/snapd/boot"
    34  	"github.com/snapcore/snapd/dirs"
    35  	"github.com/snapcore/snapd/i18n"
    36  	"github.com/snapcore/snapd/logger"
    37  	"github.com/snapcore/snapd/overlord/assertstate"
    38  	"github.com/snapcore/snapd/overlord/auth"
    39  	"github.com/snapcore/snapd/overlord/configstate/config"
    40  	"github.com/snapcore/snapd/overlord/devicestate/internal"
    41  	"github.com/snapcore/snapd/overlord/hookstate"
    42  	"github.com/snapcore/snapd/overlord/snapstate"
    43  	"github.com/snapcore/snapd/overlord/state"
    44  	"github.com/snapcore/snapd/overlord/storecontext"
    45  	"github.com/snapcore/snapd/release"
    46  	"github.com/snapcore/snapd/snapdenv"
    47  	"github.com/snapcore/snapd/sysconfig"
    48  	"github.com/snapcore/snapd/timings"
    49  )
    50  
    51  var (
    52  	cloudInitStatus   = sysconfig.CloudInitStatus
    53  	restrictCloudInit = sysconfig.RestrictCloudInit
    54  )
    55  
    56  // DeviceManager is responsible for managing the device identity and device
    57  // policies.
    58  type DeviceManager struct {
    59  	systemMode string
    60  
    61  	state      *state.State
    62  	keypairMgr asserts.KeypairManager
    63  
    64  	// newStore can make new stores for remodeling
    65  	newStore func(storecontext.DeviceBackend) snapstate.StoreService
    66  
    67  	bootOkRan            bool
    68  	bootRevisionsUpdated bool
    69  
    70  	ensureSeedInConfigRan bool
    71  
    72  	ensureInstalledRan bool
    73  
    74  	cloudInitAlreadyRestricted           bool
    75  	cloudInitErrorAttemptStart           *time.Time
    76  	cloudInitEnabledInactiveAttemptStart *time.Time
    77  
    78  	lastBecomeOperationalAttempt time.Time
    79  	becomeOperationalBackoff     time.Duration
    80  	registered                   bool
    81  	reg                          chan struct{}
    82  
    83  	preseed bool
    84  }
    85  
    86  // Manager returns a new device manager.
    87  func Manager(s *state.State, hookManager *hookstate.HookManager, runner *state.TaskRunner, newStore func(storecontext.DeviceBackend) snapstate.StoreService) (*DeviceManager, error) {
    88  	delayedCrossMgrInit()
    89  
    90  	keypairMgr, err := asserts.OpenFSKeypairManager(dirs.SnapDeviceDir)
    91  	if err != nil {
    92  		return nil, err
    93  	}
    94  
    95  	m := &DeviceManager{
    96  		state:      s,
    97  		keypairMgr: keypairMgr,
    98  		newStore:   newStore,
    99  		reg:        make(chan struct{}),
   100  		preseed:    snapdenv.Preseeding(),
   101  	}
   102  
   103  	modeEnv, err := maybeReadModeenv()
   104  	if err != nil {
   105  		return nil, err
   106  	}
   107  	if modeEnv != nil {
   108  		m.systemMode = modeEnv.Mode
   109  	}
   110  
   111  	s.Lock()
   112  	s.Cache(deviceMgrKey{}, m)
   113  	s.Unlock()
   114  
   115  	if err := m.confirmRegistered(); err != nil {
   116  		return nil, err
   117  	}
   118  
   119  	hookManager.Register(regexp.MustCompile("^prepare-device$"), newPrepareDeviceHandler)
   120  
   121  	runner.AddHandler("generate-device-key", m.doGenerateDeviceKey, nil)
   122  	runner.AddHandler("request-serial", m.doRequestSerial, nil)
   123  	runner.AddHandler("mark-preseeded", m.doMarkPreseeded, nil)
   124  	runner.AddHandler("mark-seeded", m.doMarkSeeded, nil)
   125  	runner.AddHandler("setup-run-system", m.doSetupRunSystem, nil)
   126  	runner.AddHandler("prepare-remodeling", m.doPrepareRemodeling, nil)
   127  	runner.AddCleanup("prepare-remodeling", m.cleanupRemodel)
   128  	// this *must* always run last and finalizes a remodel
   129  	runner.AddHandler("set-model", m.doSetModel, nil)
   130  	runner.AddCleanup("set-model", m.cleanupRemodel)
   131  	// There is no undo for successful gadget updates. The system is
   132  	// rebooted during update, if it boots up to the point where snapd runs
   133  	// we deem the new assets (be it bootloader or firmware) functional. The
   134  	// deployed boot assets must be backward compatible with reverted kernel
   135  	// or gadget snaps. There are no further changes to the boot assets,
   136  	// unless a new gadget update is deployed.
   137  	runner.AddHandler("update-gadget-assets", m.doUpdateGadgetAssets, nil)
   138  
   139  	runner.AddBlocked(gadgetUpdateBlocked)
   140  
   141  	return m, nil
   142  }
   143  
   144  func maybeReadModeenv() (*boot.Modeenv, error) {
   145  	modeEnv, err := boot.ReadModeenv("")
   146  	if err != nil && !os.IsNotExist(err) {
   147  		return nil, fmt.Errorf("cannot read modeenv: %v", err)
   148  	}
   149  	return modeEnv, nil
   150  }
   151  
   152  type deviceMgrKey struct{}
   153  
   154  func deviceMgr(st *state.State) *DeviceManager {
   155  	mgr := st.Cached(deviceMgrKey{})
   156  	if mgr == nil {
   157  		panic("internal error: device manager is not yet associated with state")
   158  	}
   159  	return mgr.(*DeviceManager)
   160  }
   161  
   162  func (m *DeviceManager) CanStandby() bool {
   163  	var seeded bool
   164  	if err := m.state.Get("seeded", &seeded); err != nil {
   165  		return false
   166  	}
   167  	return seeded
   168  }
   169  
   170  func (m *DeviceManager) confirmRegistered() error {
   171  	m.state.Lock()
   172  	defer m.state.Unlock()
   173  
   174  	device, err := m.device()
   175  	if err != nil {
   176  		return err
   177  	}
   178  
   179  	if device.Serial != "" {
   180  		m.markRegistered()
   181  	}
   182  	return nil
   183  }
   184  
   185  func (m *DeviceManager) markRegistered() {
   186  	if m.registered {
   187  		return
   188  	}
   189  	m.registered = true
   190  	close(m.reg)
   191  }
   192  
   193  func gadgetUpdateBlocked(cand *state.Task, running []*state.Task) bool {
   194  	if cand.Kind() == "update-gadget-assets" && len(running) != 0 {
   195  		// update-gadget-assets must be the only task running
   196  		return true
   197  	} else {
   198  		for _, other := range running {
   199  			if other.Kind() == "update-gadget-assets" {
   200  				// no other task can be started when
   201  				// update-gadget-assets is running
   202  				return true
   203  			}
   204  		}
   205  	}
   206  
   207  	return false
   208  }
   209  
   210  type prepareDeviceHandler struct{}
   211  
   212  func newPrepareDeviceHandler(context *hookstate.Context) hookstate.Handler {
   213  	return prepareDeviceHandler{}
   214  }
   215  
   216  func (h prepareDeviceHandler) Before() error {
   217  	return nil
   218  }
   219  
   220  func (h prepareDeviceHandler) Done() error {
   221  	return nil
   222  }
   223  
   224  func (h prepareDeviceHandler) Error(err error) error {
   225  	return nil
   226  }
   227  
   228  func (m *DeviceManager) changeInFlight(kind string) bool {
   229  	for _, chg := range m.state.Changes() {
   230  		if chg.Kind() == kind && !chg.Status().Ready() {
   231  			// change already in motion
   232  			return true
   233  		}
   234  	}
   235  	return false
   236  }
   237  
   238  // helpers to keep count of attempts to get a serial, useful to decide
   239  // to give up holding off trying to auto-refresh
   240  
   241  type ensureOperationalAttemptsKey struct{}
   242  
   243  func incEnsureOperationalAttempts(st *state.State) {
   244  	cur, _ := st.Cached(ensureOperationalAttemptsKey{}).(int)
   245  	st.Cache(ensureOperationalAttemptsKey{}, cur+1)
   246  }
   247  
   248  func ensureOperationalAttempts(st *state.State) int {
   249  	cur, _ := st.Cached(ensureOperationalAttemptsKey{}).(int)
   250  	return cur
   251  }
   252  
   253  // ensureOperationalShouldBackoff returns whether we should abstain from
   254  // further become-operational tentatives while its backoff interval is
   255  // not expired.
   256  func (m *DeviceManager) ensureOperationalShouldBackoff(now time.Time) bool {
   257  	if !m.lastBecomeOperationalAttempt.IsZero() && m.lastBecomeOperationalAttempt.Add(m.becomeOperationalBackoff).After(now) {
   258  		return true
   259  	}
   260  	if m.becomeOperationalBackoff == 0 {
   261  		m.becomeOperationalBackoff = 5 * time.Minute
   262  	} else {
   263  		newBackoff := m.becomeOperationalBackoff * 2
   264  		if newBackoff > (12 * time.Hour) {
   265  			newBackoff = 24 * time.Hour
   266  		}
   267  		m.becomeOperationalBackoff = newBackoff
   268  	}
   269  	m.lastBecomeOperationalAttempt = now
   270  	return false
   271  }
   272  
   273  func setClassicFallbackModel(st *state.State, device *auth.DeviceState) error {
   274  	err := assertstate.Add(st, sysdb.GenericClassicModel())
   275  	if err != nil && !asserts.IsUnaccceptedUpdate(err) {
   276  		return fmt.Errorf(`cannot install "generic-classic" fallback model assertion: %v`, err)
   277  	}
   278  	device.Brand = "generic"
   279  	device.Model = "generic-classic"
   280  	if err := internal.SetDevice(st, device); err != nil {
   281  		return err
   282  	}
   283  	return nil
   284  }
   285  
   286  func (m *DeviceManager) SystemMode() string {
   287  	if m.systemMode == "" {
   288  		return "run"
   289  	}
   290  	return m.systemMode
   291  }
   292  
   293  func (m *DeviceManager) ensureOperational() error {
   294  	m.state.Lock()
   295  	defer m.state.Unlock()
   296  
   297  	if m.SystemMode() != "run" {
   298  		// avoid doing registration in ephemeral mode
   299  		// note: this also stop auto-refreshes indirectly
   300  		return nil
   301  	}
   302  
   303  	device, err := m.device()
   304  	if err != nil {
   305  		return err
   306  	}
   307  
   308  	if device.Serial != "" {
   309  		// serial is set, we are all set
   310  		return nil
   311  	}
   312  
   313  	perfTimings := timings.New(map[string]string{"ensure": "become-operational"})
   314  
   315  	// conditions to trigger device registration
   316  	//
   317  	// * have a model assertion with a gadget (core and
   318  	//   device-like classic) in which case we need also to wait
   319  	//   for the gadget to have been installed though
   320  	// TODO: consider a way to support lazy registration on classic
   321  	// even with a gadget and some preseeded snaps
   322  	//
   323  	// * classic with a model assertion with a non-default store specified
   324  	// * lazy classic case (might have a model with no gadget nor store
   325  	//   or no model): we wait to have some snaps installed or be
   326  	//   in the process to install some
   327  
   328  	var seeded bool
   329  	err = m.state.Get("seeded", &seeded)
   330  	if err != nil && err != state.ErrNoState {
   331  		return err
   332  	}
   333  
   334  	if device.Brand == "" || device.Model == "" {
   335  		if !release.OnClassic || !seeded {
   336  			return nil
   337  		}
   338  		// we are on classic and seeded but there is no model:
   339  		// use a fallback model!
   340  		err := setClassicFallbackModel(m.state, device)
   341  		if err != nil {
   342  			return err
   343  		}
   344  	}
   345  
   346  	if m.changeInFlight("become-operational") {
   347  		return nil
   348  	}
   349  
   350  	var storeID, gadget string
   351  	model, err := m.Model()
   352  	if err != nil && err != state.ErrNoState {
   353  		return err
   354  	}
   355  	if err == nil {
   356  		gadget = model.Gadget()
   357  		storeID = model.Store()
   358  	} else {
   359  		return fmt.Errorf("internal error: core device brand and model are set but there is no model assertion")
   360  	}
   361  
   362  	if gadget == "" && storeID == "" {
   363  		// classic: if we have no gadget and no non-default store
   364  		// wait to have snaps or snap installation
   365  
   366  		n, err := snapstate.NumSnaps(m.state)
   367  		if err != nil {
   368  			return err
   369  		}
   370  		if n == 0 && !snapstate.Installing(m.state) {
   371  			return nil
   372  		}
   373  	}
   374  
   375  	var hasPrepareDeviceHook bool
   376  	// if there's a gadget specified wait for it
   377  	if gadget != "" {
   378  		// if have a gadget wait until seeded to proceed
   379  		if !seeded {
   380  			// this will be run again, so eventually when the system is
   381  			// seeded the code below runs
   382  			return nil
   383  
   384  		}
   385  
   386  		gadgetInfo, err := snapstate.CurrentInfo(m.state, gadget)
   387  		if err != nil {
   388  			return err
   389  		}
   390  		hasPrepareDeviceHook = (gadgetInfo.Hooks["prepare-device"] != nil)
   391  	}
   392  
   393  	// have some backoff between full retries
   394  	if m.ensureOperationalShouldBackoff(time.Now()) {
   395  		return nil
   396  	}
   397  	// increment attempt count
   398  	incEnsureOperationalAttempts(m.state)
   399  
   400  	// XXX: some of these will need to be split and use hooks
   401  	// retries might need to embrace more than one "task" then,
   402  	// need to be careful
   403  
   404  	tasks := []*state.Task{}
   405  
   406  	var prepareDevice *state.Task
   407  	if hasPrepareDeviceHook {
   408  		summary := i18n.G("Run prepare-device hook")
   409  		hooksup := &hookstate.HookSetup{
   410  			Snap: gadget,
   411  			Hook: "prepare-device",
   412  		}
   413  		prepareDevice = hookstate.HookTask(m.state, summary, hooksup, nil)
   414  		tasks = append(tasks, prepareDevice)
   415  		// hooks are under a different manager, make sure we consider
   416  		// it immediately
   417  		m.state.EnsureBefore(0)
   418  	}
   419  
   420  	genKey := m.state.NewTask("generate-device-key", i18n.G("Generate device key"))
   421  	if prepareDevice != nil {
   422  		genKey.WaitFor(prepareDevice)
   423  	}
   424  	tasks = append(tasks, genKey)
   425  	requestSerial := m.state.NewTask("request-serial", i18n.G("Request device serial"))
   426  	requestSerial.WaitFor(genKey)
   427  	tasks = append(tasks, requestSerial)
   428  
   429  	chg := m.state.NewChange("become-operational", i18n.G("Initialize device"))
   430  	chg.AddAll(state.NewTaskSet(tasks...))
   431  
   432  	state.TagTimingsWithChange(perfTimings, chg)
   433  	perfTimings.Save(m.state)
   434  
   435  	return nil
   436  }
   437  
   438  var startTime time.Time
   439  
   440  func init() {
   441  	startTime = time.Now()
   442  }
   443  
   444  func (m *DeviceManager) setTimeOnce(name string, t time.Time) error {
   445  	var prev time.Time
   446  	err := m.state.Get(name, &prev)
   447  	if err != nil && err != state.ErrNoState {
   448  		return err
   449  	}
   450  	if !prev.IsZero() {
   451  		// already set
   452  		return nil
   453  	}
   454  	m.state.Set(name, t)
   455  	return nil
   456  }
   457  
   458  var populateStateFromSeed = populateStateFromSeedImpl
   459  
   460  // ensureSeeded makes sure that the snaps from seed.yaml get installed
   461  // with the matching assertions
   462  func (m *DeviceManager) ensureSeeded() error {
   463  	m.state.Lock()
   464  	defer m.state.Unlock()
   465  
   466  	var seeded bool
   467  	err := m.state.Get("seeded", &seeded)
   468  	if err != nil && err != state.ErrNoState {
   469  		return err
   470  	}
   471  	if seeded {
   472  		return nil
   473  	}
   474  
   475  	perfTimings := timings.New(map[string]string{"ensure": "seed"})
   476  
   477  	if m.changeInFlight("seed") {
   478  		return nil
   479  	}
   480  
   481  	var recordedStart string
   482  	var start time.Time
   483  	if m.preseed {
   484  		recordedStart = "preseed-start-time"
   485  		start = timeNow()
   486  	} else {
   487  		recordedStart = "seed-start-time"
   488  		start = startTime
   489  	}
   490  	if err := m.setTimeOnce(recordedStart, start); err != nil {
   491  		return err
   492  	}
   493  
   494  	var opts *populateStateFromSeedOptions
   495  	if m.preseed {
   496  		opts = &populateStateFromSeedOptions{Preseed: true}
   497  	} else {
   498  		modeEnv, err := maybeReadModeenv()
   499  		if err != nil {
   500  			return err
   501  		}
   502  		if modeEnv != nil {
   503  			opts = &populateStateFromSeedOptions{
   504  				Mode:  m.systemMode,
   505  				Label: modeEnv.RecoverySystem,
   506  			}
   507  		}
   508  	}
   509  
   510  	var tsAll []*state.TaskSet
   511  	timings.Run(perfTimings, "state-from-seed", "populate state from seed", func(tm timings.Measurer) {
   512  		tsAll, err = populateStateFromSeed(m.state, opts, tm)
   513  	})
   514  	if err != nil {
   515  		return err
   516  	}
   517  	if len(tsAll) == 0 {
   518  		return nil
   519  	}
   520  
   521  	chg := m.state.NewChange("seed", "Initialize system state")
   522  	for _, ts := range tsAll {
   523  		chg.AddAll(ts)
   524  	}
   525  	m.state.EnsureBefore(0)
   526  
   527  	state.TagTimingsWithChange(perfTimings, chg)
   528  	perfTimings.Save(m.state)
   529  	return nil
   530  }
   531  
   532  // ResetBootOk is only useful for integration testing
   533  func (m *DeviceManager) ResetBootOk() {
   534  	m.bootOkRan = false
   535  	m.bootRevisionsUpdated = false
   536  }
   537  
   538  func (m *DeviceManager) ensureBootOk() error {
   539  	m.state.Lock()
   540  	defer m.state.Unlock()
   541  
   542  	if release.OnClassic {
   543  		return nil
   544  	}
   545  
   546  	// boot-ok/update-boot-revision is only relevant in run-mode
   547  	if m.SystemMode() != "run" {
   548  		return nil
   549  	}
   550  
   551  	if !m.bootOkRan {
   552  		deviceCtx, err := DeviceCtx(m.state, nil, nil)
   553  		if err != nil && err != state.ErrNoState {
   554  			return err
   555  		}
   556  		if err == nil {
   557  			if err := boot.MarkBootSuccessful(deviceCtx); err != nil {
   558  				return err
   559  			}
   560  		}
   561  		m.bootOkRan = true
   562  	}
   563  
   564  	if !m.bootRevisionsUpdated {
   565  		if err := snapstate.UpdateBootRevisions(m.state); err != nil {
   566  			return err
   567  		}
   568  		m.bootRevisionsUpdated = true
   569  	}
   570  
   571  	return nil
   572  }
   573  
   574  func (m *DeviceManager) ensureCloudInitRestricted() error {
   575  	m.state.Lock()
   576  	defer m.state.Unlock()
   577  
   578  	if m.cloudInitAlreadyRestricted {
   579  		return nil
   580  	}
   581  
   582  	var seeded bool
   583  	err := m.state.Get("seeded", &seeded)
   584  	if err != nil && err != state.ErrNoState {
   585  		return err
   586  	}
   587  
   588  	// On Ubuntu Core devices that have been seeded, we want to restrict
   589  	// cloud-init so that its more dangerous (for an IoT device at least)
   590  	// features are not exploitable after a device has been seeded. This allows
   591  	// device administrators and other tools (such as multipass) to still
   592  	// configure an Ubuntu Core device on first boot, and also allows cloud
   593  	// vendors to run cloud-init with only a specific data-source on subsequent
   594  	// boots but disallows arbitrary cloud-init {user,meta,vendor}-data to be
   595  	// attached to a device via a USB drive and inject code onto the device.
   596  
   597  	if seeded && !release.OnClassic {
   598  		opts := &sysconfig.CloudInitRestrictOptions{}
   599  
   600  		// check the current state of cloud-init, if it is disabled or already
   601  		// restricted then we have nothing to do
   602  		cloudInitStatus, err := cloudInitStatus()
   603  		if err != nil {
   604  			return err
   605  		}
   606  		statusMsg := ""
   607  
   608  		switch cloudInitStatus {
   609  		case sysconfig.CloudInitDisabledPermanently, sysconfig.CloudInitRestrictedBySnapd:
   610  			// already been permanently disabled, nothing to do
   611  			m.cloudInitAlreadyRestricted = true
   612  			return nil
   613  		case sysconfig.CloudInitUntriggered:
   614  			// hasn't been used
   615  			statusMsg = "reported to be in disabled state"
   616  		case sysconfig.CloudInitDone:
   617  			// is done being used
   618  			statusMsg = "reported to be done"
   619  		case sysconfig.CloudInitErrored:
   620  			// cloud-init errored, so we give the device admin / developer a few
   621  			// minutes to reboot the machine to re-run cloud-init and try again,
   622  			// otherwise we will disable cloud-init permanently
   623  
   624  			// initialize the time we first saw cloud-init in error state
   625  			if m.cloudInitErrorAttemptStart == nil {
   626  				// save the time we started the attempt to restrict
   627  				now := timeNow()
   628  				m.cloudInitErrorAttemptStart = &now
   629  				logger.Noticef("System initialized, cloud-init reported to be in error state, will disable in 3 minutes")
   630  			}
   631  
   632  			// check if 3 minutes have elapsed since we first saw cloud-init in
   633  			// error state
   634  			timeSinceFirstAttempt := timeNow().Sub(*m.cloudInitErrorAttemptStart)
   635  			if timeSinceFirstAttempt <= 3*time.Minute {
   636  				// we need to keep waiting for cloud-init, up to 3 minutes
   637  				nextCheck := 3*time.Minute - timeSinceFirstAttempt
   638  				m.state.EnsureBefore(nextCheck)
   639  				return nil
   640  			}
   641  			// otherwise, we timed out waiting for cloud-init to be fixed or
   642  			// rebooted and should restrict cloud-init
   643  			// we will restrict cloud-init below, but we need to force the
   644  			// disable, as by default RestrictCloudInit will error on state
   645  			// CloudInitErrored
   646  			opts.ForceDisable = true
   647  			statusMsg = "reported to be in error state after 3 minutes"
   648  		default:
   649  			// in unknown states we are conservative and let the device run for
   650  			// a while to see if it transitions to a known state, but eventually
   651  			// will disable anyways
   652  			fallthrough
   653  		case sysconfig.CloudInitEnabled:
   654  			// we will give cloud-init up to 5 minutes to try and run, if it
   655  			// still has not transitioned to some other known state, then we
   656  			// will give up waiting for it and disable it anyways
   657  
   658  			// initialize the first time we saw cloud-init in enabled state
   659  			if m.cloudInitEnabledInactiveAttemptStart == nil {
   660  				// save the time we started the attempt to restrict
   661  				now := timeNow()
   662  				m.cloudInitEnabledInactiveAttemptStart = &now
   663  			}
   664  
   665  			// keep re-scheduling again in 10 seconds until we hit 5 minutes
   666  			timeSinceFirstAttempt := timeNow().Sub(*m.cloudInitEnabledInactiveAttemptStart)
   667  			if timeSinceFirstAttempt <= 5*time.Minute {
   668  				// TODO: should we log a message here about waiting for cloud-init
   669  				//       to be in a "known state"?
   670  				m.state.EnsureBefore(10 * time.Second)
   671  				return nil
   672  			}
   673  
   674  			// otherwise, we gave cloud-init 5 minutes to run, if it's still not
   675  			// done disable it anyways
   676  			// note we we need to force the disable, as by default
   677  			// RestrictCloudInit will error on state CloudInitEnabled
   678  			opts.ForceDisable = true
   679  			statusMsg = "failed to transition to done or error state after 5 minutes"
   680  		}
   681  
   682  		// we should always have a model if we are seeded and are not on classic
   683  		model, err := m.Model()
   684  		if err != nil {
   685  			return err
   686  		}
   687  
   688  		// For UC20, we want to always disable cloud-init after it has run on
   689  		// first boot unless we are in a "real cloud", i.e. not using NoCloud,
   690  		// or if we installed cloud-init configuration from the gadget
   691  		if model.Grade() != asserts.ModelGradeUnset {
   692  			// always disable NoCloud/local datasources after first boot on
   693  			// uc20, this is because even if the gadget has a cloud.conf
   694  			// configuring NoCloud, the config installed by cloud-init should
   695  			// not work differently for later boots, so it's sufficient that
   696  			// NoCloud runs on first-boot and never again
   697  			opts.DisableAfterLocalDatasourcesRun = true
   698  		}
   699  
   700  		// now restrict/disable cloud-init
   701  		res, err := restrictCloudInit(cloudInitStatus, opts)
   702  		if err != nil {
   703  			return err
   704  		}
   705  
   706  		// log a message about what we did
   707  		actionMsg := ""
   708  		switch res.Action {
   709  		case "disable":
   710  			actionMsg = "disabled permanently"
   711  		case "restrict":
   712  			// log different messages depending on what datasource was used
   713  			if res.DataSource == "NoCloud" {
   714  				actionMsg = "set datasource_list to [ NoCloud ] and disabled auto-import by filesystem label"
   715  			} else {
   716  				// all other datasources just log that we limited it to that datasource
   717  				actionMsg = fmt.Sprintf("set datasource_list to [ %s ]", res.DataSource)
   718  			}
   719  		default:
   720  			return fmt.Errorf("internal error: unexpected action %s taken while restricting cloud-init", res.Action)
   721  		}
   722  		logger.Noticef("System initialized, cloud-init %s, %s", statusMsg, actionMsg)
   723  
   724  		m.cloudInitAlreadyRestricted = true
   725  	}
   726  
   727  	return nil
   728  }
   729  
   730  func (m *DeviceManager) ensureInstalled() error {
   731  	m.state.Lock()
   732  	defer m.state.Unlock()
   733  
   734  	if release.OnClassic {
   735  		return nil
   736  	}
   737  
   738  	if m.ensureInstalledRan {
   739  		return nil
   740  	}
   741  
   742  	if m.SystemMode() != "install" {
   743  		return nil
   744  	}
   745  
   746  	var seeded bool
   747  	err := m.state.Get("seeded", &seeded)
   748  	if err != nil && err != state.ErrNoState {
   749  		return err
   750  	}
   751  	if !seeded {
   752  		return nil
   753  	}
   754  
   755  	if m.changeInFlight("install-system") {
   756  		return nil
   757  	}
   758  
   759  	m.ensureInstalledRan = true
   760  
   761  	tasks := []*state.Task{}
   762  	setupRunSystem := m.state.NewTask("setup-run-system", i18n.G("Setup system for run mode"))
   763  	tasks = append(tasks, setupRunSystem)
   764  
   765  	chg := m.state.NewChange("install-system", i18n.G("Install the system"))
   766  	chg.AddAll(state.NewTaskSet(tasks...))
   767  
   768  	return nil
   769  }
   770  
   771  var timeNow = time.Now
   772  
   773  // StartOfOperationTime returns the time when snapd started operating,
   774  // and sets it in the state when called for the first time.
   775  // The StartOfOperationTime time is seed-time if available,
   776  // or current time otherwise.
   777  func (m *DeviceManager) StartOfOperationTime() (time.Time, error) {
   778  	var opTime time.Time
   779  	if m.preseed {
   780  		return opTime, fmt.Errorf("internal error: unexpected call to StartOfOperationTime in preseed mode")
   781  	}
   782  	err := m.state.Get("start-of-operation-time", &opTime)
   783  	if err == nil {
   784  		return opTime, nil
   785  	}
   786  	if err != nil && err != state.ErrNoState {
   787  		return opTime, err
   788  	}
   789  
   790  	// start-of-operation-time not set yet, use seed-time if available
   791  	var seedTime time.Time
   792  	err = m.state.Get("seed-time", &seedTime)
   793  	if err != nil && err != state.ErrNoState {
   794  		return opTime, err
   795  	}
   796  	if err == nil {
   797  		opTime = seedTime
   798  	} else {
   799  		opTime = timeNow()
   800  	}
   801  	m.state.Set("start-of-operation-time", opTime)
   802  	return opTime, nil
   803  }
   804  
   805  func markSeededInConfig(st *state.State) error {
   806  	var seedDone bool
   807  	tr := config.NewTransaction(st)
   808  	if err := tr.Get("core", "seed.loaded", &seedDone); err != nil && !config.IsNoOption(err) {
   809  		return err
   810  	}
   811  	if !seedDone {
   812  		if err := tr.Set("core", "seed.loaded", true); err != nil {
   813  			return err
   814  		}
   815  		tr.Commit()
   816  	}
   817  	return nil
   818  }
   819  
   820  func (m *DeviceManager) ensureSeedInConfig() error {
   821  	m.state.Lock()
   822  	defer m.state.Unlock()
   823  
   824  	if !m.ensureSeedInConfigRan {
   825  		// get global seeded option
   826  		var seeded bool
   827  		if err := m.state.Get("seeded", &seeded); err != nil && err != state.ErrNoState {
   828  			return err
   829  		}
   830  		if !seeded {
   831  			// wait for ensure again, this is fine because
   832  			// doMarkSeeded will run "EnsureBefore(0)"
   833  			return nil
   834  		}
   835  
   836  		// Sync seeding with the configuration state. We need to
   837  		// do this here to ensure that old systems which did not
   838  		// set the configuration on seeding get the configuration
   839  		// update too.
   840  		if err := markSeededInConfig(m.state); err != nil {
   841  			return err
   842  		}
   843  		m.ensureSeedInConfigRan = true
   844  	}
   845  
   846  	return nil
   847  
   848  }
   849  
   850  type ensureError struct {
   851  	errs []error
   852  }
   853  
   854  func (e *ensureError) Error() string {
   855  	if len(e.errs) == 1 {
   856  		return fmt.Sprintf("devicemgr: %v", e.errs[0])
   857  	}
   858  	parts := []string{"devicemgr:"}
   859  	for _, e := range e.errs {
   860  		parts = append(parts, e.Error())
   861  	}
   862  	return strings.Join(parts, "\n - ")
   863  }
   864  
   865  // no \n allowed in warnings
   866  var seedFailureFmt = `seeding failed with: %v. This indicates an error in your distribution, please see https://forum.snapcraft.io/t/16341 for more information.`
   867  
   868  // Ensure implements StateManager.Ensure.
   869  func (m *DeviceManager) Ensure() error {
   870  	var errs []error
   871  
   872  	if err := m.ensureSeeded(); err != nil {
   873  		m.state.Lock()
   874  		m.state.Warnf(seedFailureFmt, err)
   875  		m.state.Unlock()
   876  		errs = append(errs, fmt.Errorf("cannot seed: %v", err))
   877  	}
   878  
   879  	if !m.preseed {
   880  		if err := m.ensureCloudInitRestricted(); err != nil {
   881  			errs = append(errs, err)
   882  		}
   883  
   884  		if err := m.ensureOperational(); err != nil {
   885  			errs = append(errs, err)
   886  		}
   887  
   888  		if err := m.ensureBootOk(); err != nil {
   889  			errs = append(errs, err)
   890  		}
   891  
   892  		if err := m.ensureSeedInConfig(); err != nil {
   893  			errs = append(errs, err)
   894  		}
   895  
   896  		if err := m.ensureInstalled(); err != nil {
   897  			errs = append(errs, err)
   898  		}
   899  	}
   900  
   901  	if len(errs) > 0 {
   902  		return &ensureError{errs}
   903  	}
   904  
   905  	return nil
   906  }
   907  
   908  func (m *DeviceManager) keyPair() (asserts.PrivateKey, error) {
   909  	device, err := m.device()
   910  	if err != nil {
   911  		return nil, err
   912  	}
   913  
   914  	if device.KeyID == "" {
   915  		return nil, state.ErrNoState
   916  	}
   917  
   918  	privKey, err := m.keypairMgr.Get(device.KeyID)
   919  	if err != nil {
   920  		return nil, fmt.Errorf("cannot read device key pair: %v", err)
   921  	}
   922  	return privKey, nil
   923  }
   924  
   925  // Registered returns a channel that is closed when the device is known to have been registered.
   926  func (m *DeviceManager) Registered() <-chan struct{} {
   927  	return m.reg
   928  }
   929  
   930  // device returns current device state.
   931  func (m *DeviceManager) device() (*auth.DeviceState, error) {
   932  	return internal.Device(m.state)
   933  }
   934  
   935  // setDevice sets the device details in the state.
   936  func (m *DeviceManager) setDevice(device *auth.DeviceState) error {
   937  	return internal.SetDevice(m.state, device)
   938  }
   939  
   940  // Model returns the device model assertion.
   941  func (m *DeviceManager) Model() (*asserts.Model, error) {
   942  	return findModel(m.state)
   943  }
   944  
   945  // Serial returns the device serial assertion.
   946  func (m *DeviceManager) Serial() (*asserts.Serial, error) {
   947  	return findSerial(m.state, nil)
   948  }
   949  
   950  type SystemAction struct {
   951  	Title string
   952  	Mode  string
   953  }
   954  
   955  type System struct {
   956  	// Current is true when the system running now was installed from that
   957  	// seed
   958  	Current bool
   959  	// Label of the seed system
   960  	Label string
   961  	// Model assertion of the system
   962  	Model *asserts.Model
   963  	// Brand information
   964  	Brand *asserts.Account
   965  	// Actions available for this system
   966  	Actions []SystemAction
   967  }
   968  
   969  var defaultSystemActions = []SystemAction{
   970  	{Title: "Install", Mode: "install"},
   971  }
   972  var currentSystemActions = []SystemAction{
   973  	{Title: "Reinstall", Mode: "install"},
   974  	{Title: "Recover", Mode: "recover"},
   975  	{Title: "Run normally", Mode: "run"},
   976  }
   977  var recoverSystemActions = []SystemAction{
   978  	{Title: "Reinstall", Mode: "install"},
   979  	{Title: "Run normally", Mode: "run"},
   980  }
   981  
   982  var ErrNoSystems = errors.New("no systems seeds")
   983  
   984  // Systems list the available recovery/seeding systems. Returns the list of
   985  // systems, ErrNoSystems when no systems seeds were found or other error.
   986  func (m *DeviceManager) Systems() ([]*System, error) {
   987  	// it's tough luck when we cannot determine the current system seed
   988  	systemMode := m.SystemMode()
   989  	currentSys, _ := currentSystemForMode(m.state, systemMode)
   990  
   991  	systemLabels, err := filepath.Glob(filepath.Join(dirs.SnapSeedDir, "systems", "*"))
   992  	if err != nil && !os.IsNotExist(err) {
   993  		return nil, fmt.Errorf("cannot list available systems: %v", err)
   994  	}
   995  	if len(systemLabels) == 0 {
   996  		// maybe not a UC20 system
   997  		return nil, ErrNoSystems
   998  	}
   999  
  1000  	var systems []*System
  1001  	for _, fpLabel := range systemLabels {
  1002  		label := filepath.Base(fpLabel)
  1003  		system, err := systemFromSeed(label, currentSys)
  1004  		if err != nil {
  1005  			// TODO:UC20 add a Broken field to the seed system like
  1006  			// we do for snap.Info
  1007  			logger.Noticef("cannot load system %q seed: %v", label, err)
  1008  			continue
  1009  		}
  1010  		systems = append(systems, system)
  1011  	}
  1012  	return systems, nil
  1013  }
  1014  
  1015  var ErrUnsupportedAction = errors.New("unsupported action")
  1016  
  1017  // Reboot triggers a reboot into the given systemLabel and mode.
  1018  //
  1019  // When called without a systemLabel and without a mode it will just
  1020  // trigger a regular reboot.
  1021  //
  1022  // When called without a systemLabel but with a mode it will use
  1023  // the current system to enter the given mode.
  1024  //
  1025  // Note that "recover" and "run" modes are only available for the
  1026  // current system.
  1027  func (m *DeviceManager) Reboot(systemLabel, mode string) error {
  1028  	rebootCurrent := func() {
  1029  		logger.Noticef("rebooting system")
  1030  		m.state.RequestRestart(state.RestartSystemNow)
  1031  	}
  1032  
  1033  	// most simple case: just reboot
  1034  	if systemLabel == "" && mode == "" {
  1035  		m.state.Lock()
  1036  		defer m.state.Unlock()
  1037  
  1038  		rebootCurrent()
  1039  		return nil
  1040  	}
  1041  
  1042  	// no systemLabel means "current" so get the current system label
  1043  	if systemLabel == "" {
  1044  		systemMode := m.SystemMode()
  1045  		currentSys, err := currentSystemForMode(m.state, systemMode)
  1046  		if err != nil {
  1047  			return fmt.Errorf("cannot get curent system: %v", err)
  1048  		}
  1049  		systemLabel = currentSys.System
  1050  	}
  1051  
  1052  	switched := func(systemLabel string, sysAction *SystemAction) {
  1053  		logger.Noticef("rebooting into system %q in %q mode", systemLabel, sysAction.Mode)
  1054  		m.state.RequestRestart(state.RestartSystemNow)
  1055  	}
  1056  	// even if we are already in the right mode we restart here by
  1057  	// passing rebootCurrent as this is what the user requested
  1058  	return m.switchToSystemAndMode(systemLabel, mode, rebootCurrent, switched)
  1059  }
  1060  
  1061  // RequestSystemAction requests the provided system to be run in a
  1062  // given mode as specified by action.
  1063  // A system reboot will be requested when the request can be
  1064  // successfully carried out.
  1065  func (m *DeviceManager) RequestSystemAction(systemLabel string, action SystemAction) error {
  1066  	if systemLabel == "" {
  1067  		return fmt.Errorf("internal error: system label is unset")
  1068  	}
  1069  
  1070  	nop := func() {}
  1071  	switched := func(systemLabel string, sysAction *SystemAction) {
  1072  		logger.Noticef("restarting into system %q for action %q", systemLabel, sysAction.Title)
  1073  		m.state.RequestRestart(state.RestartSystemNow)
  1074  	}
  1075  	// we do nothing (nop) if the mode and system are the same
  1076  	return m.switchToSystemAndMode(systemLabel, action.Mode, nop, switched)
  1077  }
  1078  
  1079  // switchToSystemAndMode switches to given systemLabel and mode.
  1080  // If the systemLabel and mode are the same as current, it calls
  1081  // sameSystemAndMode. If successful otherwise it calls switched. Both
  1082  // are called with the state lock held.
  1083  func (m *DeviceManager) switchToSystemAndMode(systemLabel, mode string, sameSystemAndMode func(), switched func(systemLabel string, sysAction *SystemAction)) error {
  1084  	if err := checkSystemRequestConflict(m.state, systemLabel); err != nil {
  1085  		return err
  1086  	}
  1087  
  1088  	systemMode := m.SystemMode()
  1089  	// ignore the error to be robust in scenarios that
  1090  	// dont' stricly require currentSys to be carried through.
  1091  	// make sure that currentSys == nil does not break
  1092  	// the code below!
  1093  	// TODO: should we log the error?
  1094  	currentSys, _ := currentSystemForMode(m.state, systemMode)
  1095  
  1096  	systemSeedDir := filepath.Join(dirs.SnapSeedDir, "systems", systemLabel)
  1097  	if _, err := os.Stat(systemSeedDir); err != nil {
  1098  		// XXX: should we wrap this instead return a naked stat error?
  1099  		return err
  1100  	}
  1101  	system, err := systemFromSeed(systemLabel, currentSys)
  1102  	if err != nil {
  1103  		return fmt.Errorf("cannot load seed system: %v", err)
  1104  	}
  1105  
  1106  	var sysAction *SystemAction
  1107  	for _, act := range system.Actions {
  1108  		if mode == act.Mode {
  1109  			sysAction = &act
  1110  			break
  1111  		}
  1112  	}
  1113  	if sysAction == nil {
  1114  		// XXX: provide more context here like what mode was requested?
  1115  		return ErrUnsupportedAction
  1116  	}
  1117  
  1118  	// XXX: requested mode is valid; only current system has 'run' and
  1119  	// recover 'actions'
  1120  
  1121  	switch systemMode {
  1122  	case "recover", "run":
  1123  		// if going from recover to recover or from run to run and the systems
  1124  		// are the same do nothing
  1125  		if systemMode == sysAction.Mode && currentSys != nil && systemLabel == currentSys.System {
  1126  			m.state.Lock()
  1127  			defer m.state.Unlock()
  1128  			sameSystemAndMode()
  1129  			return nil
  1130  		}
  1131  	case "install":
  1132  		// requesting system actions in install mode does not make sense atm
  1133  		//
  1134  		// TODO:UC20: maybe factory hooks will be able to something like
  1135  		// this?
  1136  		return ErrUnsupportedAction
  1137  	default:
  1138  		// probably test device manager mocking problem, or also potentially
  1139  		// missing modeenv
  1140  		return fmt.Errorf("internal error: unexpected manager system mode %q", systemMode)
  1141  	}
  1142  
  1143  	m.state.Lock()
  1144  	defer m.state.Unlock()
  1145  
  1146  	deviceCtx, err := DeviceCtx(m.state, nil, nil)
  1147  	if err != nil {
  1148  		return err
  1149  	}
  1150  	if err := boot.SetRecoveryBootSystemAndMode(deviceCtx, systemLabel, mode); err != nil {
  1151  		return fmt.Errorf("cannot set device to boot into system %q in mode %q: %v", systemLabel, mode, err)
  1152  	}
  1153  
  1154  	switched(systemLabel, sysAction)
  1155  	return nil
  1156  }
  1157  
  1158  // implement storecontext.Backend
  1159  
  1160  type storeContextBackend struct {
  1161  	*DeviceManager
  1162  }
  1163  
  1164  func (scb storeContextBackend) Device() (*auth.DeviceState, error) {
  1165  	return scb.DeviceManager.device()
  1166  }
  1167  
  1168  func (scb storeContextBackend) SetDevice(device *auth.DeviceState) error {
  1169  	return scb.DeviceManager.setDevice(device)
  1170  }
  1171  
  1172  func (scb storeContextBackend) ProxyStore() (*asserts.Store, error) {
  1173  	st := scb.DeviceManager.state
  1174  	return proxyStore(st, config.NewTransaction(st))
  1175  }
  1176  
  1177  // SignDeviceSessionRequest produces a signed device-session-request with for given serial assertion and nonce.
  1178  func (scb storeContextBackend) SignDeviceSessionRequest(serial *asserts.Serial, nonce string) (*asserts.DeviceSessionRequest, error) {
  1179  	if serial == nil {
  1180  		// shouldn't happen, but be safe
  1181  		return nil, fmt.Errorf("internal error: cannot sign a session request without a serial")
  1182  	}
  1183  
  1184  	privKey, err := scb.DeviceManager.keyPair()
  1185  	if err == state.ErrNoState {
  1186  		return nil, fmt.Errorf("internal error: inconsistent state with serial but no device key")
  1187  	}
  1188  	if err != nil {
  1189  		return nil, err
  1190  	}
  1191  
  1192  	a, err := asserts.SignWithoutAuthority(asserts.DeviceSessionRequestType, map[string]interface{}{
  1193  		"brand-id":  serial.BrandID(),
  1194  		"model":     serial.Model(),
  1195  		"serial":    serial.Serial(),
  1196  		"nonce":     nonce,
  1197  		"timestamp": time.Now().UTC().Format(time.RFC3339),
  1198  	}, nil, privKey)
  1199  	if err != nil {
  1200  		return nil, err
  1201  	}
  1202  
  1203  	return a.(*asserts.DeviceSessionRequest), nil
  1204  }
  1205  
  1206  func (m *DeviceManager) StoreContextBackend() storecontext.Backend {
  1207  	return storeContextBackend{m}
  1208  }