github.com/stolowski/snapd@v0.0.0-20210407085831-115137ce5a22/overlord/devicestate/systems.go (about) 1 // -*- Mode: Go; indent-tabs-mode: t -*- 2 3 /* 4 * Copyright (C) 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 "fmt" 24 25 "github.com/snapcore/snapd/dirs" 26 "github.com/snapcore/snapd/overlord/snapstate" 27 "github.com/snapcore/snapd/overlord/state" 28 "github.com/snapcore/snapd/seed" 29 ) 30 31 func checkSystemRequestConflict(st *state.State, systemLabel string) error { 32 st.Lock() 33 defer st.Unlock() 34 35 var seeded bool 36 if err := st.Get("seeded", &seeded); err != nil && err != state.ErrNoState { 37 return err 38 } 39 if seeded { 40 // the system is fully seeded already 41 return nil 42 } 43 44 // inspect the current system which is stored in modeenv, note we are 45 // holding the state lock so there is no race against mark-seeded 46 // clearing recovery system; recovery system is not cleared when seeding 47 // fails 48 modeEnv, err := maybeReadModeenv() 49 if err != nil { 50 return err 51 } 52 if modeEnv == nil { 53 // non UC20 systems do not support actions, no conflict can 54 // happen 55 return nil 56 } 57 58 // not yet fully seeded, hold off requests for the system that is being 59 // seeded, but allow requests for other systems 60 if modeEnv.RecoverySystem == systemLabel { 61 return &snapstate.ChangeConflictError{ 62 ChangeKind: "seed", 63 Message: "cannot request system action, system is seeding", 64 } 65 } 66 return nil 67 } 68 69 func systemFromSeed(label string, current *currentSystem) (*System, error) { 70 s, err := seed.Open(dirs.SnapSeedDir, label) 71 if err != nil { 72 return nil, fmt.Errorf("cannot open: %v", err) 73 } 74 if err := s.LoadAssertions(nil, nil); err != nil { 75 return nil, fmt.Errorf("cannot load assertions: %v", err) 76 } 77 // get the model 78 model := s.Model() 79 brand, err := s.Brand() 80 if err != nil { 81 return nil, fmt.Errorf("cannot obtain brand: %v", err) 82 } 83 system := &System{ 84 Current: false, 85 Label: label, 86 Model: model, 87 Brand: brand, 88 Actions: defaultSystemActions, 89 } 90 if current.sameAs(system) { 91 system.Current = true 92 system.Actions = current.actions 93 } 94 return system, nil 95 } 96 97 type currentSystem struct { 98 *seededSystem 99 actions []SystemAction 100 } 101 102 func (c *currentSystem) sameAs(other *System) bool { 103 return c != nil && 104 c.System == other.Label && 105 c.Model == other.Model.Model() && 106 c.BrandID == other.Brand.AccountID() 107 } 108 109 func currentSystemForMode(st *state.State, mode string) (*currentSystem, error) { 110 var system *seededSystem 111 var actions []SystemAction 112 var err error 113 114 switch mode { 115 case "run": 116 actions = currentSystemActions 117 system, err = currentSeededSystem(st) 118 case "install": 119 // there is no current system for install mode 120 return nil, nil 121 case "recover": 122 actions = recoverSystemActions 123 // recover mode uses modeenv for reference 124 system, err = seededSystemFromModeenv() 125 default: 126 return nil, fmt.Errorf("internal error: cannot identify current system for unsupported mode %q", mode) 127 } 128 if err != nil { 129 return nil, err 130 } 131 currentSys := ¤tSystem{ 132 seededSystem: system, 133 actions: actions, 134 } 135 return currentSys, nil 136 } 137 138 func currentSeededSystem(st *state.State) (*seededSystem, error) { 139 st.Lock() 140 defer st.Unlock() 141 142 var whatseeded []seededSystem 143 if err := st.Get("seeded-systems", &whatseeded); err != nil { 144 return nil, err 145 } 146 if len(whatseeded) == 0 { 147 // unexpected 148 return nil, state.ErrNoState 149 } 150 return &whatseeded[0], nil 151 } 152 153 func seededSystemFromModeenv() (*seededSystem, error) { 154 modeEnv, err := maybeReadModeenv() 155 if err != nil { 156 return nil, err 157 } 158 if modeEnv == nil { 159 return nil, fmt.Errorf("internal error: modeenv does not exist") 160 } 161 if modeEnv.RecoverySystem == "" { 162 return nil, fmt.Errorf("internal error: recovery system is unset") 163 } 164 165 system, err := systemFromSeed(modeEnv.RecoverySystem, nil) 166 if err != nil { 167 return nil, err 168 } 169 seededSys := &seededSystem{ 170 System: modeEnv.RecoverySystem, 171 Model: system.Model.Model(), 172 BrandID: system.Model.BrandID(), 173 Revision: system.Model.Revision(), 174 Timestamp: system.Model.Timestamp(), 175 // SeedTime is intentionally left unset 176 } 177 return seededSys, nil 178 }