github.com/hugh712/snapd@v0.0.0-20200910133618-1a99902bd583/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 after first boot on uc20, this is because
   693  			// even if the gadget has a cloud.conf configuring NoCloud, the
   694  			// config installed by cloud-init should not work differently for
   695  			// later boots, so it's sufficient that NoCloud runs on first-boot
   696  			// and never again
   697  			// note that the name DisableNoCloud is slightly misleading, it's
   698  			// more specifically "disable cloud-init after first boot if
   699  			// NoCloud, but just restrict after first boot if not NoCloud"
   700  			opts.DisableNoCloud = true
   701  		}
   702  
   703  		// now restrict/disable cloud-init
   704  		res, err := restrictCloudInit(cloudInitStatus, opts)
   705  		if err != nil {
   706  			return err
   707  		}
   708  
   709  		// log a message about what we did
   710  		actionMsg := ""
   711  		switch res.Action {
   712  		case "disable":
   713  			actionMsg = "disabled permanently"
   714  		case "restrict":
   715  			// log different messages depending on what datasource was used
   716  			if res.DataSource == "NoCloud" {
   717  				actionMsg = "set datasource_list to [ NoCloud ] and disabled auto-import by filesystem label"
   718  			} else {
   719  				// all other datasources just log that we limited it to that datasource
   720  				actionMsg = fmt.Sprintf("set datasource_list to [ %s ]", res.DataSource)
   721  			}
   722  		default:
   723  			return fmt.Errorf("internal error: unexpected action %s taken while restricting cloud-init", res.Action)
   724  		}
   725  		logger.Noticef("System initialized, cloud-init %s, %s", statusMsg, actionMsg)
   726  
   727  		m.cloudInitAlreadyRestricted = true
   728  	}
   729  
   730  	return nil
   731  }
   732  
   733  func (m *DeviceManager) ensureInstalled() error {
   734  	m.state.Lock()
   735  	defer m.state.Unlock()
   736  
   737  	if release.OnClassic {
   738  		return nil
   739  	}
   740  
   741  	if m.ensureInstalledRan {
   742  		return nil
   743  	}
   744  
   745  	if m.SystemMode() != "install" {
   746  		return nil
   747  	}
   748  
   749  	var seeded bool
   750  	err := m.state.Get("seeded", &seeded)
   751  	if err != nil && err != state.ErrNoState {
   752  		return err
   753  	}
   754  	if !seeded {
   755  		return nil
   756  	}
   757  
   758  	if m.changeInFlight("install-system") {
   759  		return nil
   760  	}
   761  
   762  	m.ensureInstalledRan = true
   763  
   764  	tasks := []*state.Task{}
   765  	setupRunSystem := m.state.NewTask("setup-run-system", i18n.G("Setup system for run mode"))
   766  	tasks = append(tasks, setupRunSystem)
   767  
   768  	chg := m.state.NewChange("install-system", i18n.G("Install the system"))
   769  	chg.AddAll(state.NewTaskSet(tasks...))
   770  
   771  	return nil
   772  }
   773  
   774  var timeNow = time.Now
   775  
   776  // StartOfOperationTime returns the time when snapd started operating,
   777  // and sets it in the state when called for the first time.
   778  // The StartOfOperationTime time is seed-time if available,
   779  // or current time otherwise.
   780  func (m *DeviceManager) StartOfOperationTime() (time.Time, error) {
   781  	var opTime time.Time
   782  	if m.preseed {
   783  		return opTime, fmt.Errorf("internal error: unexpected call to StartOfOperationTime in preseed mode")
   784  	}
   785  	err := m.state.Get("start-of-operation-time", &opTime)
   786  	if err == nil {
   787  		return opTime, nil
   788  	}
   789  	if err != nil && err != state.ErrNoState {
   790  		return opTime, err
   791  	}
   792  
   793  	// start-of-operation-time not set yet, use seed-time if available
   794  	var seedTime time.Time
   795  	err = m.state.Get("seed-time", &seedTime)
   796  	if err != nil && err != state.ErrNoState {
   797  		return opTime, err
   798  	}
   799  	if err == nil {
   800  		opTime = seedTime
   801  	} else {
   802  		opTime = timeNow()
   803  	}
   804  	m.state.Set("start-of-operation-time", opTime)
   805  	return opTime, nil
   806  }
   807  
   808  func markSeededInConfig(st *state.State) error {
   809  	var seedDone bool
   810  	tr := config.NewTransaction(st)
   811  	if err := tr.Get("core", "seed.loaded", &seedDone); err != nil && !config.IsNoOption(err) {
   812  		return err
   813  	}
   814  	if !seedDone {
   815  		if err := tr.Set("core", "seed.loaded", true); err != nil {
   816  			return err
   817  		}
   818  		tr.Commit()
   819  	}
   820  	return nil
   821  }
   822  
   823  func (m *DeviceManager) ensureSeedInConfig() error {
   824  	m.state.Lock()
   825  	defer m.state.Unlock()
   826  
   827  	if !m.ensureSeedInConfigRan {
   828  		// get global seeded option
   829  		var seeded bool
   830  		if err := m.state.Get("seeded", &seeded); err != nil && err != state.ErrNoState {
   831  			return err
   832  		}
   833  		if !seeded {
   834  			// wait for ensure again, this is fine because
   835  			// doMarkSeeded will run "EnsureBefore(0)"
   836  			return nil
   837  		}
   838  
   839  		// Sync seeding with the configuration state. We need to
   840  		// do this here to ensure that old systems which did not
   841  		// set the configuration on seeding get the configuration
   842  		// update too.
   843  		if err := markSeededInConfig(m.state); err != nil {
   844  			return err
   845  		}
   846  		m.ensureSeedInConfigRan = true
   847  	}
   848  
   849  	return nil
   850  
   851  }
   852  
   853  type ensureError struct {
   854  	errs []error
   855  }
   856  
   857  func (e *ensureError) Error() string {
   858  	if len(e.errs) == 1 {
   859  		return fmt.Sprintf("devicemgr: %v", e.errs[0])
   860  	}
   861  	parts := []string{"devicemgr:"}
   862  	for _, e := range e.errs {
   863  		parts = append(parts, e.Error())
   864  	}
   865  	return strings.Join(parts, "\n - ")
   866  }
   867  
   868  // no \n allowed in warnings
   869  var seedFailureFmt = `seeding failed with: %v. This indicates an error in your distribution, please see https://forum.snapcraft.io/t/16341 for more information.`
   870  
   871  // Ensure implements StateManager.Ensure.
   872  func (m *DeviceManager) Ensure() error {
   873  	var errs []error
   874  
   875  	if err := m.ensureSeeded(); err != nil {
   876  		m.state.Lock()
   877  		m.state.Warnf(seedFailureFmt, err)
   878  		m.state.Unlock()
   879  		errs = append(errs, fmt.Errorf("cannot seed: %v", err))
   880  	}
   881  
   882  	if !m.preseed {
   883  		if err := m.ensureCloudInitRestricted(); err != nil {
   884  			errs = append(errs, err)
   885  		}
   886  
   887  		if err := m.ensureOperational(); err != nil {
   888  			errs = append(errs, err)
   889  		}
   890  
   891  		if err := m.ensureBootOk(); err != nil {
   892  			errs = append(errs, err)
   893  		}
   894  
   895  		if err := m.ensureSeedInConfig(); err != nil {
   896  			errs = append(errs, err)
   897  		}
   898  
   899  		if err := m.ensureInstalled(); err != nil {
   900  			errs = append(errs, err)
   901  		}
   902  	}
   903  
   904  	if len(errs) > 0 {
   905  		return &ensureError{errs}
   906  	}
   907  
   908  	return nil
   909  }
   910  
   911  func (m *DeviceManager) keyPair() (asserts.PrivateKey, error) {
   912  	device, err := m.device()
   913  	if err != nil {
   914  		return nil, err
   915  	}
   916  
   917  	if device.KeyID == "" {
   918  		return nil, state.ErrNoState
   919  	}
   920  
   921  	privKey, err := m.keypairMgr.Get(device.KeyID)
   922  	if err != nil {
   923  		return nil, fmt.Errorf("cannot read device key pair: %v", err)
   924  	}
   925  	return privKey, nil
   926  }
   927  
   928  // Registered returns a channel that is closed when the device is known to have been registered.
   929  func (m *DeviceManager) Registered() <-chan struct{} {
   930  	return m.reg
   931  }
   932  
   933  // device returns current device state.
   934  func (m *DeviceManager) device() (*auth.DeviceState, error) {
   935  	return internal.Device(m.state)
   936  }
   937  
   938  // setDevice sets the device details in the state.
   939  func (m *DeviceManager) setDevice(device *auth.DeviceState) error {
   940  	return internal.SetDevice(m.state, device)
   941  }
   942  
   943  // Model returns the device model assertion.
   944  func (m *DeviceManager) Model() (*asserts.Model, error) {
   945  	return findModel(m.state)
   946  }
   947  
   948  // Serial returns the device serial assertion.
   949  func (m *DeviceManager) Serial() (*asserts.Serial, error) {
   950  	return findSerial(m.state, nil)
   951  }
   952  
   953  type SystemAction struct {
   954  	Title string
   955  	Mode  string
   956  }
   957  
   958  type System struct {
   959  	// Current is true when the system running now was installed from that
   960  	// seed
   961  	Current bool
   962  	// Label of the seed system
   963  	Label string
   964  	// Model assertion of the system
   965  	Model *asserts.Model
   966  	// Brand information
   967  	Brand *asserts.Account
   968  	// Actions available for this system
   969  	Actions []SystemAction
   970  }
   971  
   972  var defaultSystemActions = []SystemAction{
   973  	{Title: "Install", Mode: "install"},
   974  }
   975  var currentSystemActions = []SystemAction{
   976  	{Title: "Reinstall", Mode: "install"},
   977  	{Title: "Recover", Mode: "recover"},
   978  	{Title: "Run normally", Mode: "run"},
   979  }
   980  var recoverSystemActions = []SystemAction{
   981  	{Title: "Reinstall", Mode: "install"},
   982  	{Title: "Run normally", Mode: "run"},
   983  }
   984  
   985  var ErrNoSystems = errors.New("no systems seeds")
   986  
   987  // Systems list the available recovery/seeding systems. Returns the list of
   988  // systems, ErrNoSystems when no systems seeds were found or other error.
   989  func (m *DeviceManager) Systems() ([]*System, error) {
   990  	// it's tough luck when we cannot determine the current system seed
   991  	systemMode := m.SystemMode()
   992  	currentSys, _ := currentSystemForMode(m.state, systemMode)
   993  
   994  	systemLabels, err := filepath.Glob(filepath.Join(dirs.SnapSeedDir, "systems", "*"))
   995  	if err != nil && !os.IsNotExist(err) {
   996  		return nil, fmt.Errorf("cannot list available systems: %v", err)
   997  	}
   998  	if len(systemLabels) == 0 {
   999  		// maybe not a UC20 system
  1000  		return nil, ErrNoSystems
  1001  	}
  1002  
  1003  	var systems []*System
  1004  	for _, fpLabel := range systemLabels {
  1005  		label := filepath.Base(fpLabel)
  1006  		system, err := systemFromSeed(label, currentSys)
  1007  		if err != nil {
  1008  			// TODO:UC20 add a Broken field to the seed system like
  1009  			// we do for snap.Info
  1010  			logger.Noticef("cannot load system %q seed: %v", label, err)
  1011  			continue
  1012  		}
  1013  		systems = append(systems, system)
  1014  	}
  1015  	return systems, nil
  1016  }
  1017  
  1018  var ErrUnsupportedAction = errors.New("unsupported action")
  1019  
  1020  // Reboot triggers a reboot into the given systemLabel and mode.
  1021  //
  1022  // When called without a systemLabel and without a mode it will just
  1023  // trigger a regular reboot.
  1024  //
  1025  // When called without a systemLabel but with a mode it will use
  1026  // the current system to enter the given mode.
  1027  //
  1028  // Note that "recover" and "run" modes are only available for the
  1029  // current system.
  1030  func (m *DeviceManager) Reboot(systemLabel, mode string) error {
  1031  	rebootCurrent := func() {
  1032  		logger.Noticef("rebooting system")
  1033  		m.state.RequestRestart(state.RestartSystemNow)
  1034  	}
  1035  
  1036  	// most simple case: just reboot
  1037  	if systemLabel == "" && mode == "" {
  1038  		m.state.Lock()
  1039  		defer m.state.Unlock()
  1040  
  1041  		rebootCurrent()
  1042  		return nil
  1043  	}
  1044  
  1045  	// no systemLabel means "current" so get the current system label
  1046  	if systemLabel == "" {
  1047  		systemMode := m.SystemMode()
  1048  		currentSys, err := currentSystemForMode(m.state, systemMode)
  1049  		if err != nil {
  1050  			return fmt.Errorf("cannot get curent system: %v", err)
  1051  		}
  1052  		systemLabel = currentSys.System
  1053  	}
  1054  
  1055  	switched := func(systemLabel string, sysAction *SystemAction) {
  1056  		logger.Noticef("rebooting into system %q in %q mode", systemLabel, sysAction.Mode)
  1057  		m.state.RequestRestart(state.RestartSystemNow)
  1058  	}
  1059  	// even if we are already in the right mode we restart here by
  1060  	// passing rebootCurrent as this is what the user requested
  1061  	return m.switchToSystemAndMode(systemLabel, mode, rebootCurrent, switched)
  1062  }
  1063  
  1064  // RequestSystemAction requests the provided system to be run in a
  1065  // given mode as specified by action.
  1066  // A system reboot will be requested when the request can be
  1067  // successfully carried out.
  1068  func (m *DeviceManager) RequestSystemAction(systemLabel string, action SystemAction) error {
  1069  	if systemLabel == "" {
  1070  		return fmt.Errorf("internal error: system label is unset")
  1071  	}
  1072  
  1073  	nop := func() {}
  1074  	switched := func(systemLabel string, sysAction *SystemAction) {
  1075  		logger.Noticef("restarting into system %q for action %q", systemLabel, sysAction.Title)
  1076  		m.state.RequestRestart(state.RestartSystemNow)
  1077  	}
  1078  	// we do nothing (nop) if the mode and system are the same
  1079  	return m.switchToSystemAndMode(systemLabel, action.Mode, nop, switched)
  1080  }
  1081  
  1082  // switchToSystemAndMode switches to given systemLabel and mode.
  1083  // If the systemLabel and mode are the same as current, it calls
  1084  // sameSystemAndMode. If successful otherwise it calls switched. Both
  1085  // are called with the state lock held.
  1086  func (m *DeviceManager) switchToSystemAndMode(systemLabel, mode string, sameSystemAndMode func(), switched func(systemLabel string, sysAction *SystemAction)) error {
  1087  	if err := checkSystemRequestConflict(m.state, systemLabel); err != nil {
  1088  		return err
  1089  	}
  1090  
  1091  	systemMode := m.SystemMode()
  1092  	// ignore the error to be robust in scenarios that
  1093  	// dont' stricly require currentSys to be carried through.
  1094  	// make sure that currentSys == nil does not break
  1095  	// the code below!
  1096  	// TODO: should we log the error?
  1097  	currentSys, _ := currentSystemForMode(m.state, systemMode)
  1098  
  1099  	systemSeedDir := filepath.Join(dirs.SnapSeedDir, "systems", systemLabel)
  1100  	if _, err := os.Stat(systemSeedDir); err != nil {
  1101  		// XXX: should we wrap this instead return a naked stat error?
  1102  		return err
  1103  	}
  1104  	system, err := systemFromSeed(systemLabel, currentSys)
  1105  	if err != nil {
  1106  		return fmt.Errorf("cannot load seed system: %v", err)
  1107  	}
  1108  
  1109  	var sysAction *SystemAction
  1110  	for _, act := range system.Actions {
  1111  		if mode == act.Mode {
  1112  			sysAction = &act
  1113  			break
  1114  		}
  1115  	}
  1116  	if sysAction == nil {
  1117  		// XXX: provide more context here like what mode was requested?
  1118  		return ErrUnsupportedAction
  1119  	}
  1120  
  1121  	// XXX: requested mode is valid; only current system has 'run' and
  1122  	// recover 'actions'
  1123  
  1124  	switch systemMode {
  1125  	case "recover", "run":
  1126  		// if going from recover to recover or from run to run and the systems
  1127  		// are the same do nothing
  1128  		if systemMode == sysAction.Mode && currentSys != nil && systemLabel == currentSys.System {
  1129  			m.state.Lock()
  1130  			defer m.state.Unlock()
  1131  			sameSystemAndMode()
  1132  			return nil
  1133  		}
  1134  	case "install":
  1135  		// requesting system actions in install mode does not make sense atm
  1136  		//
  1137  		// TODO:UC20: maybe factory hooks will be able to something like
  1138  		// this?
  1139  		return ErrUnsupportedAction
  1140  	default:
  1141  		// probably test device manager mocking problem, or also potentially
  1142  		// missing modeenv
  1143  		return fmt.Errorf("internal error: unexpected manager system mode %q", systemMode)
  1144  	}
  1145  
  1146  	m.state.Lock()
  1147  	defer m.state.Unlock()
  1148  
  1149  	deviceCtx, err := DeviceCtx(m.state, nil, nil)
  1150  	if err != nil {
  1151  		return err
  1152  	}
  1153  	if err := boot.SetRecoveryBootSystemAndMode(deviceCtx, systemLabel, mode); err != nil {
  1154  		return fmt.Errorf("cannot set device to boot into system %q in mode %q: %v", systemLabel, mode, err)
  1155  	}
  1156  
  1157  	switched(systemLabel, sysAction)
  1158  	return nil
  1159  }
  1160  
  1161  // implement storecontext.Backend
  1162  
  1163  type storeContextBackend struct {
  1164  	*DeviceManager
  1165  }
  1166  
  1167  func (scb storeContextBackend) Device() (*auth.DeviceState, error) {
  1168  	return scb.DeviceManager.device()
  1169  }
  1170  
  1171  func (scb storeContextBackend) SetDevice(device *auth.DeviceState) error {
  1172  	return scb.DeviceManager.setDevice(device)
  1173  }
  1174  
  1175  func (scb storeContextBackend) ProxyStore() (*asserts.Store, error) {
  1176  	st := scb.DeviceManager.state
  1177  	return proxyStore(st, config.NewTransaction(st))
  1178  }
  1179  
  1180  // SignDeviceSessionRequest produces a signed device-session-request with for given serial assertion and nonce.
  1181  func (scb storeContextBackend) SignDeviceSessionRequest(serial *asserts.Serial, nonce string) (*asserts.DeviceSessionRequest, error) {
  1182  	if serial == nil {
  1183  		// shouldn't happen, but be safe
  1184  		return nil, fmt.Errorf("internal error: cannot sign a session request without a serial")
  1185  	}
  1186  
  1187  	privKey, err := scb.DeviceManager.keyPair()
  1188  	if err == state.ErrNoState {
  1189  		return nil, fmt.Errorf("internal error: inconsistent state with serial but no device key")
  1190  	}
  1191  	if err != nil {
  1192  		return nil, err
  1193  	}
  1194  
  1195  	a, err := asserts.SignWithoutAuthority(asserts.DeviceSessionRequestType, map[string]interface{}{
  1196  		"brand-id":  serial.BrandID(),
  1197  		"model":     serial.Model(),
  1198  		"serial":    serial.Serial(),
  1199  		"nonce":     nonce,
  1200  		"timestamp": time.Now().UTC().Format(time.RFC3339),
  1201  	}, nil, privKey)
  1202  	if err != nil {
  1203  		return nil, err
  1204  	}
  1205  
  1206  	return a.(*asserts.DeviceSessionRequest), nil
  1207  }
  1208  
  1209  func (m *DeviceManager) StoreContextBackend() storecontext.Backend {
  1210  	return storeContextBackend{m}
  1211  }