github.com/anonymouse64/snapd@v0.0.0-20210824153203-04c4c42d842d/overlord/devicestate/handlers.go (about) 1 // -*- Mode: Go; indent-tabs-mode: t -*- 2 /* 3 * Copyright (C) 2016-2020 Canonical Ltd 4 * 5 * This program is free software: you can redistribute it and/or modify 6 * it under the terms of the GNU General Public License version 3 as 7 * published by the Free Software Foundation. 8 * 9 * This program is distributed in the hope that it will be useful, 10 * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 * GNU General Public License for more details. 13 * 14 * You should have received a copy of the GNU General Public License 15 * along with this program. If not, see <http://www.gnu.org/licenses/>. 16 * 17 */ 18 19 package devicestate 20 21 import ( 22 "fmt" 23 "os/exec" 24 "time" 25 26 "gopkg.in/tomb.v2" 27 28 "github.com/snapcore/snapd/interfaces" 29 "github.com/snapcore/snapd/logger" 30 "github.com/snapcore/snapd/overlord/snapstate" 31 "github.com/snapcore/snapd/overlord/state" 32 ) 33 34 func (m *DeviceManager) doMarkPreseeded(t *state.Task, _ *tomb.Tomb) error { 35 st := t.State() 36 st.Lock() 37 defer st.Unlock() 38 39 snaps, err := snapstate.All(st) 40 if err != nil { 41 return err 42 } 43 44 systemKey, err := interfaces.RecordedSystemKey() 45 if err != nil { 46 return fmt.Errorf("cannot get recorded system key: %v", err) 47 } 48 49 if m.preseed { 50 var preseeded bool 51 // the "preseeded" flag on this task is set to allow skipping the logic 52 // below in case this handler is retried in preseeding mode due to an 53 // EnsureBefore(0) done somewhere else. 54 // XXX: we should probably drop the flag from the task now that we have 55 // one on the state. 56 if err := t.Get("preseeded", &preseeded); err != nil && err != state.ErrNoState { 57 return err 58 } 59 if !preseeded { 60 preseeded = true 61 t.Set("preseeded", preseeded) 62 // unmount all snaps 63 // TODO: move to snapstate.UnmountAllSnaps. 64 for _, snapSt := range snaps { 65 info, err := snapSt.CurrentInfo() 66 if err != nil { 67 return err 68 } 69 logger.Debugf("unmounting snap %s at %s", info.InstanceName(), info.MountDir()) 70 if _, err := exec.Command("umount", "-d", "-l", info.MountDir()).CombinedOutput(); err != nil { 71 return err 72 } 73 } 74 75 st.Set("preseeded", preseeded) 76 st.Set("preseed-system-key", systemKey) 77 st.Set("preseed-time", timeNow()) 78 79 // do not mark this task done as this makes it racy against taskrunner tear down (the next task 80 // could start). Let this task finish after snapd restart when preseed mode is off. 81 st.RequestRestart(state.StopDaemon) 82 } 83 84 return &state.Retry{Reason: "mark-preseeded will be marked done when snapd is executed in normal mode"} 85 } 86 87 // normal snapd run after snapd restart (not in preseed mode anymore) 88 89 st.Set("seed-restart-system-key", systemKey) 90 if err := m.setTimeOnce("seed-restart-time", startTime); err != nil { 91 return err 92 } 93 94 return nil 95 } 96 97 type seededSystem struct { 98 // System carries the recovery system label that was used to seed the 99 // current system 100 System string `json:"system"` 101 Model string `json:"model"` 102 // BrandID is the brand account ID 103 BrandID string `json:"brand-id"` 104 // Revision of the model assertion 105 Revision int `json:"revision"` 106 // Timestamp of model assertion 107 Timestamp time.Time `json:"timestamp"` 108 // SeedTime holds the timestamp when the system was seeded 109 SeedTime time.Time `json:"seed-time"` 110 } 111 112 func (s *seededSystem) sameAs(other *seededSystem) bool { 113 // in theory the system labels are unique, however be extra paranoid and 114 // check all model related fields too 115 return s.System == other.System && 116 s.Model == other.Model && 117 s.BrandID == other.BrandID && 118 s.Revision == other.Revision 119 } 120 121 func (m *DeviceManager) recordSeededSystem(st *state.State, whatSeeded *seededSystem) error { 122 var seeded []seededSystem 123 if err := st.Get("seeded-systems", &seeded); err != nil && err != state.ErrNoState { 124 return err 125 } 126 for _, sys := range seeded { 127 if sys.sameAs(whatSeeded) { 128 return nil 129 } 130 } 131 // contrary to the usual approach of appending new entries to the list 132 // like we do with modeenv, the recently seeded system is added at the 133 // front, as it is not considered candidate like for the other entries, 134 // but rather it describes the currently existing 135 seeded = append([]seededSystem{*whatSeeded}, seeded...) 136 st.Set("seeded-systems", seeded) 137 return nil 138 } 139 140 func (m *DeviceManager) doMarkSeeded(t *state.Task, _ *tomb.Tomb) error { 141 st := t.State() 142 st.Lock() 143 defer st.Unlock() 144 145 if m.preseed { 146 return fmt.Errorf("internal error: mark-seeded task not expected in pre-seeding mode") 147 } 148 149 deviceCtx, err := DeviceCtx(st, t, nil) 150 if err != nil { 151 return fmt.Errorf("cannot get device context: %v", err) 152 } 153 154 if deviceCtx.HasModeenv() && deviceCtx.RunMode() { 155 modeEnv, err := maybeReadModeenv() 156 if err != nil { 157 return err 158 } 159 if modeEnv == nil { 160 return fmt.Errorf("missing modeenv, cannot proceed") 161 } 162 // unset recovery_system because that is only needed during install mode 163 modeEnv.RecoverySystem = "" 164 err = modeEnv.Write() 165 if err != nil { 166 return err 167 } 168 } 169 170 now := time.Now() 171 var whatSeeded *seededSystem 172 if err := t.Get("seed-system", &whatSeeded); err != nil && err != state.ErrNoState { 173 return err 174 } 175 if whatSeeded != nil && deviceCtx.RunMode() { 176 // record what seeded in the state only when in run mode 177 whatSeeded.SeedTime = now 178 if err := m.recordSeededSystem(st, whatSeeded); err != nil { 179 return fmt.Errorf("cannot record the seeded system: %v", err) 180 } 181 } 182 st.Set("seed-time", now) 183 st.Set("seeded", true) 184 // avoid possibly recording the same system multiple times etc. 185 t.SetStatus(state.DoneStatus) 186 // make sure we setup a fallback model/consider the next phase 187 // (registration) timely 188 st.EnsureBefore(0) 189 return nil 190 }