gitee.com/mysnapcore/mysnapd@v0.1.0/boot/bootstate16.go (about) 1 // -*- Mode: Go; indent-tabs-mode: t -*- 2 3 /* 4 * Copyright (C) 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 boot 21 22 import ( 23 "fmt" 24 25 "gitee.com/mysnapcore/mysnapd/bootloader" 26 "gitee.com/mysnapcore/mysnapd/snap" 27 ) 28 29 type bootState16 struct { 30 varSuffix string 31 errName string 32 } 33 34 func newBootState16(typ snap.Type, dev snap.Device) bootState { 35 var varSuffix, errName string 36 switch typ { 37 case snap.TypeKernel: 38 varSuffix = "kernel" 39 errName = "kernel" 40 case snap.TypeBase: 41 varSuffix = "core" 42 errName = "boot base" 43 default: 44 panic(fmt.Sprintf("cannot make a bootState16 for snap type %q", typ)) 45 } 46 return &bootState16{varSuffix: varSuffix, errName: errName} 47 } 48 49 func (s16 *bootState16) revisions() (s, tryS snap.PlaceInfo, status string, err error) { 50 bloader, err := bootloader.Find("", nil) 51 if err != nil { 52 return nil, nil, "", fmt.Errorf("cannot get boot settings: %s", err) 53 } 54 55 snapVar := "snap_" + s16.varSuffix 56 trySnapVar := "snap_try_" + s16.varSuffix 57 vars := []string{"snap_mode", snapVar, trySnapVar} 58 snaps := make(map[string]snap.PlaceInfo, 2) 59 60 m, err := bloader.GetBootVars(vars...) 61 if err != nil { 62 return nil, nil, "", fmt.Errorf("cannot get boot variables: %s", err) 63 } 64 65 for _, vName := range vars { 66 v := m[vName] 67 if v == "" && vName != snapVar { 68 // snap_mode & snap_try_<type> can be empty 69 // snap_<type> cannot be! and will fail parsing 70 // below 71 continue 72 } 73 74 if vName == "snap_mode" { 75 status = v 76 } else { 77 // TODO: use trySnapError here somehow? 78 if v == "" { 79 return nil, nil, "", fmt.Errorf("cannot get name and revision of %s (%s): boot variable unset", s16.errName, vName) 80 } 81 snap, err := snap.ParsePlaceInfoFromSnapFileName(v) 82 if err != nil { 83 return nil, nil, "", fmt.Errorf("cannot get name and revision of %s (%s): %v", s16.errName, vName, err) 84 } 85 snaps[vName] = snap 86 } 87 } 88 89 return snaps[snapVar], snaps[trySnapVar], status, nil 90 } 91 92 type bootStateUpdate16 struct { 93 bl bootloader.Bootloader 94 env map[string]string 95 toCommit map[string]string 96 } 97 98 func newBootStateUpdate16(u bootStateUpdate, names ...string) (*bootStateUpdate16, error) { 99 if u != nil { 100 u16, ok := u.(*bootStateUpdate16) 101 if !ok { 102 return nil, fmt.Errorf("internal error: threading unexpected boot state update on UC16/18: %T", u) 103 } 104 return u16, nil 105 } 106 bl, err := bootloader.Find("", nil) 107 if err != nil { 108 return nil, err 109 } 110 m, err := bl.GetBootVars(names...) 111 if err != nil { 112 return nil, err 113 } 114 return &bootStateUpdate16{bl: bl, env: m, toCommit: make(map[string]string)}, nil 115 } 116 117 func (u16 *bootStateUpdate16) commit() error { 118 if len(u16.toCommit) == 0 { 119 // nothing to do 120 return nil 121 } 122 env := u16.env 123 // TODO: we could just SetBootVars(toCommit) but it's not 124 // fully backward compatible with the preexisting behavior 125 for k, v := range u16.toCommit { 126 env[k] = v 127 } 128 return u16.bl.SetBootVars(env) 129 } 130 131 func (s16 *bootState16) markSuccessful(update bootStateUpdate) (bootStateUpdate, error) { 132 u16, err := newBootStateUpdate16(update, "snap_mode", "snap_try_core", "snap_try_kernel") 133 if err != nil { 134 return nil, err 135 } 136 137 env := u16.env 138 toCommit := u16.toCommit 139 140 tryBootVar := fmt.Sprintf("snap_try_%s", s16.varSuffix) 141 bootVar := fmt.Sprintf("snap_%s", s16.varSuffix) 142 143 // snap_mode goes from "" -> "try" -> "trying" -> "" 144 // so if we are not in "trying" mode, nothing to do here 145 if env["snap_mode"] != TryingStatus { 146 // clean the try var anyways in case it was leftover from a rollback, 147 // etc. 148 toCommit[tryBootVar] = "" 149 return u16, nil 150 } 151 152 // update the boot vars 153 if env[tryBootVar] != "" { 154 toCommit[bootVar] = env[tryBootVar] 155 toCommit[tryBootVar] = "" 156 } 157 toCommit["snap_mode"] = DefaultStatus 158 159 return u16, nil 160 } 161 162 func (s16 *bootState16) setNext(s snap.PlaceInfo, bootCtx NextBootContext) (rbi RebootInfo, u bootStateUpdate, err error) { 163 nextBootVar := fmt.Sprintf("snap_try_%s", s16.varSuffix) 164 goodBootVar := fmt.Sprintf("snap_%s", s16.varSuffix) 165 166 u16, err := newBootStateUpdate16(nil, "snap_mode", goodBootVar) 167 if err != nil { 168 return RebootInfo{RebootRequired: false}, nil, err 169 } 170 171 env := u16.env 172 toCommit := u16.toCommit 173 174 rbi.RebootRequired = true 175 snapMode := TryStatus 176 nextBoot := s.Filename() 177 if env[goodBootVar] == nextBoot { 178 // If we were in anything but default ("") mode before 179 // and switched to the good core/kernel again, make 180 // sure to clean the snap_mode here. This also 181 // mitigates https://forum.snapcraft.io/t/5253 182 if env["snap_mode"] == DefaultStatus { 183 // already clean 184 return RebootInfo{RebootRequired: false}, nil, nil 185 } 186 // clean 187 snapMode = DefaultStatus 188 nextBoot = "" 189 rbi.RebootRequired = false 190 } else if bootCtx.BootWithoutTry { 191 toCommit[goodBootVar] = nextBoot 192 snapMode = DefaultStatus 193 nextBoot = "" 194 } 195 196 toCommit["snap_mode"] = snapMode 197 toCommit[nextBootVar] = nextBoot 198 199 return rbi, u16, nil 200 }