github.com/hugh712/snapd@v0.0.0-20200910133618-1a99902bd583/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 "github.com/snapcore/snapd/progress" 33 "github.com/snapcore/snapd/wrappers" 34 ) 35 36 func (m *DeviceManager) doMarkPreseeded(t *state.Task, _ *tomb.Tomb) error { 37 st := t.State() 38 st.Lock() 39 defer st.Unlock() 40 41 snaps, err := snapstate.All(st) 42 if err != nil { 43 return err 44 } 45 46 systemKey, err := interfaces.RecordedSystemKey() 47 if err != nil { 48 return fmt.Errorf("cannot get recorded system key: %v", err) 49 } 50 51 if m.preseed { 52 var preseeded bool 53 // the "preseeded" flag on this task is set to allow skipping the logic 54 // below in case this handler is retried in preseeding mode due to an 55 // EnsureBefore(0) done somewhere else. 56 // XXX: we should probably drop the flag from the task now that we have 57 // one on the state. 58 if err := t.Get("preseeded", &preseeded); err != nil && err != state.ErrNoState { 59 return err 60 } 61 if !preseeded { 62 preseeded = true 63 t.Set("preseeded", preseeded) 64 // unmount all snaps 65 // TODO: move to snapstate.UnmountAllSnaps. 66 for _, snapSt := range snaps { 67 info, err := snapSt.CurrentInfo() 68 if err != nil { 69 return err 70 } 71 logger.Debugf("unmounting snap %s at %s", info.InstanceName(), info.MountDir()) 72 if _, err := exec.Command("umount", "-d", "-l", info.MountDir()).CombinedOutput(); err != nil { 73 return err 74 } 75 } 76 77 st.Set("preseeded", preseeded) 78 st.Set("preseed-system-key", systemKey) 79 st.Set("preseed-time", timeNow()) 80 81 // do not mark this task done as this makes it racy against taskrunner tear down (the next task 82 // could start). Let this task finish after snapd restart when preseed mode is off. 83 st.RequestRestart(state.StopDaemon) 84 } 85 86 return &state.Retry{Reason: "mark-preseeded will be marked done when snapd is executed in normal mode"} 87 } 88 89 // normal snapd run after snapd restart (not in preseed mode anymore) 90 91 st.Set("seed-restart-system-key", systemKey) 92 if err := m.setTimeOnce("seed-restart-time", startTime); err != nil { 93 return err 94 } 95 96 // enable all services generated as part of preseeding, but not enabled 97 // XXX: this should go away once the problem of install & services is fixed. 98 for _, snapSt := range snaps { 99 info, err := snapSt.CurrentInfo() 100 if err != nil { 101 return err 102 } 103 if err := wrappers.EnableSnapServices(info, progress.Null); err != nil { 104 return err 105 } 106 } 107 108 return nil 109 } 110 111 type seededSystem struct { 112 // System carries the recovery system label that was used to seed the 113 // current system 114 System string `json:"system"` 115 Model string `json:"model"` 116 // BrandID is the brand account ID 117 BrandID string `json:"brand-id"` 118 // Revision of the model assertion 119 Revision int `json:"revision"` 120 // Timestamp of model assertion 121 Timestamp time.Time `json:"timestamp"` 122 // SeedTime holds the timestamp when the system was seeded 123 SeedTime time.Time `json:"seed-time"` 124 } 125 126 func (m *DeviceManager) recordSeededSystem(st *state.State, whatSeeded *seededSystem) error { 127 var seeded []seededSystem 128 if err := st.Get("seeded-systems", &seeded); err != nil && err != state.ErrNoState { 129 return err 130 } 131 seeded = append([]seededSystem{*whatSeeded}, seeded...) 132 st.Set("seeded-systems", seeded) 133 return nil 134 } 135 136 func (m *DeviceManager) doMarkSeeded(t *state.Task, _ *tomb.Tomb) error { 137 st := t.State() 138 st.Lock() 139 defer st.Unlock() 140 141 if m.preseed { 142 return fmt.Errorf("internal error: mark-seeded task not expected in pre-seeding mode") 143 } 144 145 deviceCtx, err := DeviceCtx(st, t, nil) 146 if err != nil { 147 return fmt.Errorf("cannot get device context: %v", err) 148 } 149 150 if deviceCtx.HasModeenv() && deviceCtx.RunMode() { 151 modeEnv, err := maybeReadModeenv() 152 if err != nil { 153 return err 154 } 155 if modeEnv == nil { 156 return fmt.Errorf("missing modeenv, cannot proceed") 157 } 158 // unset recovery_system because that is only needed during install mode 159 modeEnv.RecoverySystem = "" 160 err = modeEnv.Write() 161 if err != nil { 162 return err 163 } 164 } 165 166 now := time.Now() 167 var whatSeeded *seededSystem 168 if err := t.Get("seed-system", &whatSeeded); err != nil && err != state.ErrNoState { 169 return err 170 } 171 if whatSeeded != nil && deviceCtx.RunMode() { 172 // record what seeded in the state only when in run mode 173 174 whatSeeded.SeedTime = now 175 // TODO:UC20 what about remodels? 176 if err := m.recordSeededSystem(st, whatSeeded); err != nil { 177 return fmt.Errorf("cannot record the seeded system: %v", err) 178 } 179 } 180 st.Set("seed-time", now) 181 st.Set("seeded", true) 182 // avoid possibly recording the same system multiple times etc. 183 t.SetStatus(state.DoneStatus) 184 // make sure we setup a fallback model/consider the next phase 185 // (registration) timely 186 st.EnsureBefore(0) 187 return nil 188 }