github.com/rigado/snapd@v2.42.5-go-mod+incompatible/overlord/devicestate/devicemgr.go (about) 1 // -*- Mode: Go; indent-tabs-mode: t -*- 2 3 /* 4 * Copyright (C) 2016-2019 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 "fmt" 24 "regexp" 25 "strings" 26 "time" 27 28 "github.com/snapcore/snapd/asserts" 29 "github.com/snapcore/snapd/asserts/sysdb" 30 "github.com/snapcore/snapd/boot" 31 "github.com/snapcore/snapd/dirs" 32 "github.com/snapcore/snapd/i18n" 33 "github.com/snapcore/snapd/overlord/assertstate" 34 "github.com/snapcore/snapd/overlord/auth" 35 "github.com/snapcore/snapd/overlord/configstate/config" 36 "github.com/snapcore/snapd/overlord/devicestate/internal" 37 "github.com/snapcore/snapd/overlord/hookstate" 38 "github.com/snapcore/snapd/overlord/snapstate" 39 "github.com/snapcore/snapd/overlord/state" 40 "github.com/snapcore/snapd/overlord/storecontext" 41 "github.com/snapcore/snapd/release" 42 "github.com/snapcore/snapd/timings" 43 ) 44 45 // DeviceManager is responsible for managing the device identity and device 46 // policies. 47 type DeviceManager struct { 48 state *state.State 49 keypairMgr asserts.KeypairManager 50 51 // newStore can make new stores for remodeling 52 newStore func(storecontext.DeviceBackend) snapstate.StoreService 53 54 bootOkRan bool 55 bootRevisionsUpdated bool 56 57 ensureSeedInConfigRan bool 58 59 lastBecomeOperationalAttempt time.Time 60 becomeOperationalBackoff time.Duration 61 registered bool 62 reg chan struct{} 63 } 64 65 // Manager returns a new device manager. 66 func Manager(s *state.State, hookManager *hookstate.HookManager, runner *state.TaskRunner, newStore func(storecontext.DeviceBackend) snapstate.StoreService) (*DeviceManager, error) { 67 delayedCrossMgrInit() 68 69 keypairMgr, err := asserts.OpenFSKeypairManager(dirs.SnapDeviceDir) 70 if err != nil { 71 return nil, err 72 73 } 74 75 m := &DeviceManager{ 76 state: s, 77 keypairMgr: keypairMgr, 78 newStore: newStore, 79 reg: make(chan struct{}), 80 } 81 82 s.Lock() 83 s.Cache(deviceMgrKey{}, m) 84 s.Unlock() 85 86 if err := m.confirmRegistered(); err != nil { 87 return nil, err 88 } 89 90 hookManager.Register(regexp.MustCompile("^prepare-device$"), newPrepareDeviceHandler) 91 92 runner.AddHandler("generate-device-key", m.doGenerateDeviceKey, nil) 93 runner.AddHandler("request-serial", m.doRequestSerial, nil) 94 runner.AddHandler("mark-seeded", m.doMarkSeeded, nil) 95 runner.AddHandler("prepare-remodeling", m.doPrepareRemodeling, nil) 96 runner.AddCleanup("prepare-remodeling", m.cleanupRemodel) 97 // this *must* always run last and finalizes a remodel 98 runner.AddHandler("set-model", m.doSetModel, nil) 99 runner.AddCleanup("set-model", m.cleanupRemodel) 100 // There is no undo for successful gadget updates. The system is 101 // rebooted during update, if it boots up to the point where snapd runs 102 // we deem the new assets (be it bootloader or firmware) functional. The 103 // deployed boot assets must be backward compatible with reverted kernel 104 // or gadget snaps. There are no further changes to the boot assets, 105 // unless a new gadget update is deployed. 106 runner.AddHandler("update-gadget-assets", m.doUpdateGadgetAssets, nil) 107 108 runner.AddBlocked(gadgetUpdateBlocked) 109 110 return m, nil 111 } 112 113 type deviceMgrKey struct{} 114 115 func deviceMgr(st *state.State) *DeviceManager { 116 mgr := st.Cached(deviceMgrKey{}) 117 if mgr == nil { 118 panic("internal error: device manager is not yet associated with state") 119 } 120 return mgr.(*DeviceManager) 121 } 122 123 func (m *DeviceManager) CanStandby() bool { 124 var seeded bool 125 if err := m.state.Get("seeded", &seeded); err != nil { 126 return false 127 } 128 return seeded 129 } 130 131 func (m *DeviceManager) confirmRegistered() error { 132 m.state.Lock() 133 defer m.state.Unlock() 134 135 device, err := m.device() 136 if err != nil { 137 return err 138 } 139 140 if device.Serial != "" { 141 m.markRegistered() 142 } 143 return nil 144 } 145 146 func (m *DeviceManager) markRegistered() { 147 if m.registered { 148 return 149 } 150 m.registered = true 151 close(m.reg) 152 } 153 154 func gadgetUpdateBlocked(cand *state.Task, running []*state.Task) bool { 155 if cand.Kind() == "update-gadget-assets" && len(running) != 0 { 156 // update-gadget-assets must be the only task running 157 return true 158 } else { 159 for _, other := range running { 160 if other.Kind() == "update-gadget-assets" { 161 // no other task can be started when 162 // update-gadget-assets is running 163 return true 164 } 165 } 166 } 167 168 return false 169 } 170 171 type prepareDeviceHandler struct{} 172 173 func newPrepareDeviceHandler(context *hookstate.Context) hookstate.Handler { 174 return prepareDeviceHandler{} 175 } 176 177 func (h prepareDeviceHandler) Before() error { 178 return nil 179 } 180 181 func (h prepareDeviceHandler) Done() error { 182 return nil 183 } 184 185 func (h prepareDeviceHandler) Error(err error) error { 186 return nil 187 } 188 189 func (m *DeviceManager) changeInFlight(kind string) bool { 190 for _, chg := range m.state.Changes() { 191 if chg.Kind() == kind && !chg.Status().Ready() { 192 // change already in motion 193 return true 194 } 195 } 196 return false 197 } 198 199 // helpers to keep count of attempts to get a serial, useful to decide 200 // to give up holding off trying to auto-refresh 201 202 type ensureOperationalAttemptsKey struct{} 203 204 func incEnsureOperationalAttempts(st *state.State) { 205 cur, _ := st.Cached(ensureOperationalAttemptsKey{}).(int) 206 st.Cache(ensureOperationalAttemptsKey{}, cur+1) 207 } 208 209 func ensureOperationalAttempts(st *state.State) int { 210 cur, _ := st.Cached(ensureOperationalAttemptsKey{}).(int) 211 return cur 212 } 213 214 // ensureOperationalShouldBackoff returns whether we should abstain from 215 // further become-operational tentatives while its backoff interval is 216 // not expired. 217 func (m *DeviceManager) ensureOperationalShouldBackoff(now time.Time) bool { 218 if !m.lastBecomeOperationalAttempt.IsZero() && m.lastBecomeOperationalAttempt.Add(m.becomeOperationalBackoff).After(now) { 219 return true 220 } 221 if m.becomeOperationalBackoff == 0 { 222 m.becomeOperationalBackoff = 5 * time.Minute 223 } else { 224 newBackoff := m.becomeOperationalBackoff * 2 225 if newBackoff > (12 * time.Hour) { 226 newBackoff = 24 * time.Hour 227 } 228 m.becomeOperationalBackoff = newBackoff 229 } 230 m.lastBecomeOperationalAttempt = now 231 return false 232 } 233 234 func setClassicFallbackModel(st *state.State, device *auth.DeviceState) error { 235 err := assertstate.Add(st, sysdb.GenericClassicModel()) 236 if err != nil && !asserts.IsUnaccceptedUpdate(err) { 237 return fmt.Errorf(`cannot install "generic-classic" fallback model assertion: %v`, err) 238 } 239 device.Brand = "generic" 240 device.Model = "generic-classic" 241 if err := internal.SetDevice(st, device); err != nil { 242 return err 243 } 244 return nil 245 } 246 247 func (m *DeviceManager) ensureOperational() error { 248 m.state.Lock() 249 defer m.state.Unlock() 250 251 perfTimings := timings.New(map[string]string{"ensure": "become-operational"}) 252 253 device, err := m.device() 254 if err != nil { 255 return err 256 } 257 258 if device.Serial != "" { 259 // serial is set, we are all set 260 return nil 261 } 262 263 // conditions to trigger device registration 264 // 265 // * have a model assertion with a gadget (core and 266 // device-like classic) in which case we need also to wait 267 // for the gadget to have been installed though 268 // TODO: consider a way to support lazy registration on classic 269 // even with a gadget and some preseeded snaps 270 // 271 // * classic with a model assertion with a non-default store specified 272 // * lazy classic case (might have a model with no gadget nor store 273 // or no model): we wait to have some snaps installed or be 274 // in the process to install some 275 276 var seeded bool 277 err = m.state.Get("seeded", &seeded) 278 if err != nil && err != state.ErrNoState { 279 return err 280 } 281 282 if device.Brand == "" || device.Model == "" { 283 if !release.OnClassic || !seeded { 284 return nil 285 } 286 // we are on classic and seeded but there is no model: 287 // use a fallback model! 288 err := setClassicFallbackModel(m.state, device) 289 if err != nil { 290 return err 291 } 292 } 293 294 if m.changeInFlight("become-operational") { 295 return nil 296 } 297 298 var storeID, gadget string 299 model, err := m.Model() 300 if err != nil && err != state.ErrNoState { 301 return err 302 } 303 if err == nil { 304 gadget = model.Gadget() 305 storeID = model.Store() 306 } else { 307 return fmt.Errorf("internal error: core device brand and model are set but there is no model assertion") 308 } 309 310 if gadget == "" && storeID == "" { 311 // classic: if we have no gadget and no non-default store 312 // wait to have snaps or snap installation 313 314 n, err := snapstate.NumSnaps(m.state) 315 if err != nil { 316 return err 317 } 318 if n == 0 && !snapstate.Installing(m.state) { 319 return nil 320 } 321 } 322 323 var hasPrepareDeviceHook bool 324 // if there's a gadget specified wait for it 325 if gadget != "" { 326 // if have a gadget wait until seeded to proceed 327 if !seeded { 328 // this will be run again, so eventually when the system is 329 // seeded the code below runs 330 return nil 331 332 } 333 334 gadgetInfo, err := snapstate.CurrentInfo(m.state, gadget) 335 if err != nil { 336 return err 337 } 338 hasPrepareDeviceHook = (gadgetInfo.Hooks["prepare-device"] != nil) 339 } 340 341 // have some backoff between full retries 342 if m.ensureOperationalShouldBackoff(time.Now()) { 343 return nil 344 } 345 // increment attempt count 346 incEnsureOperationalAttempts(m.state) 347 348 // XXX: some of these will need to be split and use hooks 349 // retries might need to embrace more than one "task" then, 350 // need to be careful 351 352 tasks := []*state.Task{} 353 354 var prepareDevice *state.Task 355 if hasPrepareDeviceHook { 356 summary := i18n.G("Run prepare-device hook") 357 hooksup := &hookstate.HookSetup{ 358 Snap: gadget, 359 Hook: "prepare-device", 360 } 361 prepareDevice = hookstate.HookTask(m.state, summary, hooksup, nil) 362 tasks = append(tasks, prepareDevice) 363 // hooks are under a different manager, make sure we consider 364 // it immediately 365 m.state.EnsureBefore(0) 366 } 367 368 genKey := m.state.NewTask("generate-device-key", i18n.G("Generate device key")) 369 if prepareDevice != nil { 370 genKey.WaitFor(prepareDevice) 371 } 372 tasks = append(tasks, genKey) 373 requestSerial := m.state.NewTask("request-serial", i18n.G("Request device serial")) 374 requestSerial.WaitFor(genKey) 375 tasks = append(tasks, requestSerial) 376 377 chg := m.state.NewChange("become-operational", i18n.G("Initialize device")) 378 chg.AddAll(state.NewTaskSet(tasks...)) 379 380 perfTimings.AddTag("change-id", chg.ID()) 381 perfTimings.Save(m.state) 382 383 return nil 384 } 385 386 var populateStateFromSeed = populateStateFromSeedImpl 387 388 // ensureSnaps makes sure that the snaps from seed.yaml get installed 389 // with the matching assertions 390 func (m *DeviceManager) ensureSeedYaml() error { 391 m.state.Lock() 392 defer m.state.Unlock() 393 394 perfTimings := timings.New(map[string]string{"ensure": "seed"}) 395 396 var seeded bool 397 err := m.state.Get("seeded", &seeded) 398 if err != nil && err != state.ErrNoState { 399 return err 400 } 401 if seeded { 402 return nil 403 } 404 405 if m.changeInFlight("seed") { 406 return nil 407 } 408 409 var tsAll []*state.TaskSet 410 timings.Run(perfTimings, "state-from-seed", "populate state from seed", func(tm timings.Measurer) { 411 tsAll, err = populateStateFromSeed(m.state, tm) 412 }) 413 if err != nil { 414 return err 415 } 416 if len(tsAll) == 0 { 417 return nil 418 } 419 420 msg := fmt.Sprintf("Initialize system state") 421 chg := m.state.NewChange("seed", msg) 422 for _, ts := range tsAll { 423 chg.AddAll(ts) 424 } 425 m.state.EnsureBefore(0) 426 427 perfTimings.AddTag("change-id", chg.ID()) 428 perfTimings.Save(m.state) 429 return nil 430 } 431 432 func (m *DeviceManager) ensureBootOk() error { 433 m.state.Lock() 434 defer m.state.Unlock() 435 436 if release.OnClassic { 437 return nil 438 } 439 440 if !m.bootOkRan { 441 if err := boot.MarkBootSuccessful(); err != nil { 442 return err 443 } 444 m.bootOkRan = true 445 } 446 447 if !m.bootRevisionsUpdated { 448 if err := snapstate.UpdateBootRevisions(m.state); err != nil { 449 return err 450 } 451 m.bootRevisionsUpdated = true 452 } 453 454 return nil 455 } 456 457 func markSeededInConfig(st *state.State) error { 458 var seedDone bool 459 tr := config.NewTransaction(st) 460 if err := tr.Get("core", "seed.loaded", &seedDone); err != nil && !config.IsNoOption(err) { 461 return err 462 } 463 if !seedDone { 464 if err := tr.Set("core", "seed.loaded", true); err != nil { 465 return err 466 } 467 tr.Commit() 468 } 469 return nil 470 } 471 472 func (m *DeviceManager) ensureSeedInConfig() error { 473 m.state.Lock() 474 defer m.state.Unlock() 475 476 if !m.ensureSeedInConfigRan { 477 // get global seeded option 478 var seeded bool 479 if err := m.state.Get("seeded", &seeded); err != nil && err != state.ErrNoState { 480 return err 481 } 482 if !seeded { 483 // wait for ensure again, this is fine because 484 // doMarkSeeded will run "EnsureBefore(0)" 485 return nil 486 } 487 488 // Sync seeding with the configuration state. We need to 489 // do this here to ensure that old systems which did not 490 // set the configuration on seeding get the configuration 491 // update too. 492 if err := markSeededInConfig(m.state); err != nil { 493 return err 494 } 495 m.ensureSeedInConfigRan = true 496 } 497 498 return nil 499 500 } 501 502 type ensureError struct { 503 errs []error 504 } 505 506 func (e *ensureError) Error() string { 507 if len(e.errs) == 1 { 508 return fmt.Sprintf("devicemgr: %v", e.errs[0]) 509 } 510 parts := []string{"devicemgr:"} 511 for _, e := range e.errs { 512 parts = append(parts, e.Error()) 513 } 514 return strings.Join(parts, "\n - ") 515 } 516 517 // Ensure implements StateManager.Ensure. 518 func (m *DeviceManager) Ensure() error { 519 var errs []error 520 521 if err := m.ensureSeedYaml(); err != nil { 522 errs = append(errs, err) 523 } 524 if err := m.ensureOperational(); err != nil { 525 errs = append(errs, err) 526 } 527 528 if err := m.ensureBootOk(); err != nil { 529 errs = append(errs, err) 530 } 531 532 if err := m.ensureSeedInConfig(); err != nil { 533 errs = append(errs, err) 534 } 535 536 if len(errs) > 0 { 537 return &ensureError{errs} 538 } 539 540 return nil 541 } 542 543 func (m *DeviceManager) keyPair() (asserts.PrivateKey, error) { 544 device, err := m.device() 545 if err != nil { 546 return nil, err 547 } 548 549 if device.KeyID == "" { 550 return nil, state.ErrNoState 551 } 552 553 privKey, err := m.keypairMgr.Get(device.KeyID) 554 if err != nil { 555 return nil, fmt.Errorf("cannot read device key pair: %v", err) 556 } 557 return privKey, nil 558 } 559 560 // Registered returns a channel that is closed when the device is known to have been registered. 561 func (m *DeviceManager) Registered() <-chan struct{} { 562 return m.reg 563 } 564 565 // device returns current device state. 566 func (m *DeviceManager) device() (*auth.DeviceState, error) { 567 return internal.Device(m.state) 568 } 569 570 // setDevice sets the device details in the state. 571 func (m *DeviceManager) setDevice(device *auth.DeviceState) error { 572 return internal.SetDevice(m.state, device) 573 } 574 575 // Model returns the device model assertion. 576 func (m *DeviceManager) Model() (*asserts.Model, error) { 577 return findModel(m.state) 578 } 579 580 // Serial returns the device serial assertion. 581 func (m *DeviceManager) Serial() (*asserts.Serial, error) { 582 return findSerial(m.state, nil) 583 } 584 585 // implement storecontext.Backend 586 587 type storeContextBackend struct { 588 *DeviceManager 589 } 590 591 func (scb storeContextBackend) Device() (*auth.DeviceState, error) { 592 return scb.DeviceManager.device() 593 } 594 595 func (scb storeContextBackend) SetDevice(device *auth.DeviceState) error { 596 return scb.DeviceManager.setDevice(device) 597 } 598 599 func (scb storeContextBackend) ProxyStore() (*asserts.Store, error) { 600 st := scb.DeviceManager.state 601 return proxyStore(st, config.NewTransaction(st)) 602 } 603 604 // SignDeviceSessionRequest produces a signed device-session-request with for given serial assertion and nonce. 605 func (scb storeContextBackend) SignDeviceSessionRequest(serial *asserts.Serial, nonce string) (*asserts.DeviceSessionRequest, error) { 606 if serial == nil { 607 // shouldn't happen, but be safe 608 return nil, fmt.Errorf("internal error: cannot sign a session request without a serial") 609 } 610 611 privKey, err := scb.DeviceManager.keyPair() 612 if err == state.ErrNoState { 613 return nil, fmt.Errorf("internal error: inconsistent state with serial but no device key") 614 } 615 if err != nil { 616 return nil, err 617 } 618 619 a, err := asserts.SignWithoutAuthority(asserts.DeviceSessionRequestType, map[string]interface{}{ 620 "brand-id": serial.BrandID(), 621 "model": serial.Model(), 622 "serial": serial.Serial(), 623 "nonce": nonce, 624 "timestamp": time.Now().UTC().Format(time.RFC3339), 625 }, nil, privKey) 626 if err != nil { 627 return nil, err 628 } 629 630 return a.(*asserts.DeviceSessionRequest), nil 631 } 632 633 func (m *DeviceManager) StoreContextBackend() storecontext.Backend { 634 return storeContextBackend{m} 635 }