func (m *SnapManager) doDiscardSnap(t *state.Task, _ *tomb.Tomb) error { st := t.State() st.Lock() ss, snapst, err := snapSetupAndState(t) st.Unlock() if err != nil { return err } if snapst.Current == ss.Revision() && snapst.Active { return fmt.Errorf("internal error: cannot discard snap %q: still active", ss.Name()) } if len(snapst.Sequence) == 1 { snapst.Sequence = nil snapst.Current = snap.Revision{} } else { newSeq := make([]*snap.SideInfo, 0, len(snapst.Sequence)) for _, si := range snapst.Sequence { if si.Revision == ss.Revision() { // leave out continue } newSeq = append(newSeq, si) } snapst.Sequence = newSeq if snapst.Current == ss.Revision() { snapst.Current = newSeq[len(newSeq)-1].Revision } } pb := &TaskProgressAdapter{task: t} typ, err := snapst.Type() if err != nil { return err } err = m.backend.RemoveSnapFiles(ss.placeInfo(), typ, pb) if err != nil { st.Lock() t.Errorf("cannot remove snap file %q, will retry in 3 mins: %s", ss.Name(), err) st.Unlock() return &state.Retry{After: 3 * time.Minute} } if len(snapst.Sequence) == 0 { err = m.backend.DiscardSnapNamespace(ss.Name()) if err != nil { st.Lock() t.Errorf("cannot discard snap namespace %q, will retry in 3 mins: %s", ss.Name(), err) st.Unlock() return &state.Retry{After: 3 * time.Minute} } } st.Lock() Set(st, ss.Name(), snapst) st.Unlock() return nil }
func removeSnapSecurity(task *state.Task, snapName string) error { st := task.State() for _, backend := range backends.All { st.Unlock() err := backend.Remove(snapName) st.Lock() if err != nil { task.Errorf("cannot setup %s for snap %q: %s", backend.Name(), snapName, err) return err } } return nil }
func setupSnapSecurity(task *state.Task, snapInfo *snap.Info, devMode bool, repo *interfaces.Repository) error { st := task.State() snapName := snapInfo.Name() for _, backend := range backends.All { st.Unlock() err := backend.Setup(snapInfo, devMode, repo) st.Lock() if err != nil { task.Errorf("cannot setup %s for snap %q: %s", backend.Name(), snapName, err) return err } } return nil }
func (m *SnapManager) cleanup(t *state.Task, _ *tomb.Tomb) error { st := t.State() st.Lock() defer st.Unlock() _, snapst, err := snapSetupAndState(t) if err != nil { t.Errorf("cannot clean up: %v", err) return nil // cleanup should not return error } info, err := snapst.CurrentInfo() if err != nil { t.Errorf("cannot clean up: %v", err) return nil } m.backend.ClearTrashedData(info) return nil }
func (m *SnapManager) undoClearAliases(t *state.Task, _ *tomb.Tomb) error { st := t.State() st.Lock() defer st.Unlock() var oldStatuses map[string]string err := t.Get("old-aliases", &oldStatuses) if err == state.ErrNoState { // nothing to do return nil } if err != nil { return err } snapsup, _, err := snapSetupAndState(t) if err != nil { return err } snapName := snapsup.Name() for alias, status := range oldStatuses { if enabledAlias(status) { // can actually be reinstated only if it doesn't conflict err := checkAliasConflict(st, snapName, alias) if err != nil { if _, ok := err.(*aliasConflictError); ok { // TODO mark the conflict if it was auto? delete(oldStatuses, alias) t.Errorf("%v", err) continue } return err } } } setAliases(st, snapName, oldStatuses) return nil }
func (m *SnapManager) doLinkSnap(t *state.Task, _ *tomb.Tomb) error { st := t.State() st.Lock() defer st.Unlock() ss, snapst, err := snapSetupAndState(t) if err != nil { return err } cand := ss.SideInfo m.backend.Candidate(cand) oldCandidateIndex := snapst.LastIndex(cand.Revision) if oldCandidateIndex < 0 { snapst.Sequence = append(snapst.Sequence, cand) } else if !ss.Flags.Revert() { // remove the old candidate from the sequence, add it at the end copy(snapst.Sequence[oldCandidateIndex:len(snapst.Sequence)-1], snapst.Sequence[oldCandidateIndex+1:]) snapst.Sequence[len(snapst.Sequence)-1] = cand } oldCurrent := snapst.Current snapst.Current = cand.Revision snapst.Active = true oldChannel := snapst.Channel if ss.Channel != "" { snapst.Channel = ss.Channel } oldTryMode := snapst.TryMode() snapst.SetTryMode(ss.TryMode()) oldDevMode := snapst.DevMode() snapst.SetDevMode(ss.DevMode()) oldJailMode := snapst.JailMode() snapst.SetJailMode(ss.JailMode()) newInfo, err := readInfo(ss.Name(), cand) if err != nil { return err } // record type snapst.SetType(newInfo.Type) st.Unlock() // XXX: this block is slightly ugly, find a pattern when we have more examples err = m.backend.LinkSnap(newInfo) if err != nil { pb := &TaskProgressAdapter{task: t} err := m.backend.UnlinkSnap(newInfo, pb) if err != nil { st.Lock() t.Errorf("cannot cleanup failed attempt at making snap %q available to the system: %v", ss.Name(), err) st.Unlock() } } st.Lock() if err != nil { return err } // save for undoLinkSnap t.Set("old-trymode", oldTryMode) t.Set("old-devmode", oldDevMode) t.Set("old-jailmode", oldJailMode) t.Set("old-channel", oldChannel) t.Set("old-current", oldCurrent) t.Set("old-candidate-index", oldCandidateIndex) // Do at the end so we only preserve the new state if it worked. Set(st, ss.Name(), snapst) // Make sure if state commits and snapst is mutated we won't be rerun t.SetStatus(state.DoneStatus) // if we just installed a core snap, request a restart // so that we switch executing its snapd if release.OnClassic && newInfo.Type == snap.TypeOS { t.Logf("Requested daemon restart.") st.Unlock() st.RequestRestart(state.RestartDaemon) st.Lock() } if !release.OnClassic && (newInfo.Type == snap.TypeOS || newInfo.Type == snap.TypeKernel) { t.Logf("Requested system restart.") st.Unlock() st.RequestRestart(state.RestartSystem) st.Lock() } return nil }
func (m *DeviceManager) doRequestSerial(t *state.Task, _ *tomb.Tomb) error { st := t.State() st.Lock() defer st.Unlock() cfg, err := getSerialRequestConfig(t) if err != nil { return err } device, err := auth.Device(st) if err != nil { return err } privKey, err := m.keyPair() if err == state.ErrNoState { return fmt.Errorf("internal error: cannot find device key pair") } if err != nil { return err } // make this idempotent, look if we have already a serial assertion // for privKey serials, err := assertstate.DB(st).FindMany(asserts.SerialType, map[string]string{ "brand-id": device.Brand, "model": device.Model, "device-key-sha3-384": privKey.PublicKey().ID(), }) if err != nil && err != asserts.ErrNotFound { return err } if len(serials) == 1 { // means we saved the assertion but didn't get to the end of the task device.Serial = serials[0].(*asserts.Serial).Serial() err := auth.SetDevice(st, device) if err != nil { return err } t.SetStatus(state.DoneStatus) return nil } if len(serials) > 1 { return fmt.Errorf("internal error: multiple serial assertions for the same device key") } serial, err := getSerial(t, privKey, device, cfg) if err == errPoll { t.Logf("Will poll for device serial assertion in 60 seconds") return &state.Retry{After: retryInterval} } if err != nil { // errors & retries return err } sto := snapstate.Store(st) // try to fetch the signing key of the serial st.Unlock() a, errAcctKey := sto.Assertion(asserts.AccountKeyType, []string{serial.SignKeyID()}, nil) st.Lock() if errAcctKey == nil { err := assertstate.Add(st, a) if err != nil { if !asserts.IsUnaccceptedUpdate(err) { return err } } } // add the serial assertion to the system assertion db err = assertstate.Add(st, serial) if err != nil { // if we had failed to fetch the signing key, retry in a bit if errAcctKey != nil { t.Errorf("cannot fetch signing key for the serial: %v", errAcctKey) return &state.Retry{After: retryInterval} } return err } if repeatRequestSerial == "after-add-serial" { // For testing purposes, ensure a crash in this state works. return &state.Retry{} } device.Serial = serial.Serial() err = auth.SetDevice(st, device) if err != nil { return err } t.SetStatus(state.DoneStatus) return nil }
func retryErr(t *state.Task, reason string, a ...interface{}) error { t.State().Lock() defer t.State().Unlock() t.Errorf(reason, a...) return &state.Retry{After: retryInterval} }
func (m *SnapManager) undoAlias(t *state.Task, _ *tomb.Tomb) error { st := t.State() st.Lock() defer st.Unlock() var oldStatuses map[string]string err := t.Get("old-aliases", &oldStatuses) if err != nil { return err } snapsup, snapst, err := snapSetupAndState(t) if err != nil { return err } var changes map[string]string err = t.Get("aliases", &changes) if err != nil { return err } snapName := snapsup.Name() curInfo, err := snapst.CurrentInfo() if err != nil { return err } var add []*backend.Alias var remove []*backend.Alias Next: for alias, newStatus := range changes { if oldStatuses[alias] == newStatus { // nothing to undo continue } aliasApp := curInfo.Aliases[alias] if aliasApp == nil { // unexpected return fmt.Errorf("internal error: cannot re-toggle alias %q for %q, no such alias", alias, snapName) } beAlias := &backend.Alias{ Name: alias, Target: filepath.Base(aliasApp.WrapperPath()), } switch newStatus { case "enabled": remove = append(remove, beAlias) case "disabled": if oldStatuses[alias] != "" { // can actually be reinstated only if it doesn't conflict err := checkAliasConflict(st, snapName, alias) if err != nil { if _, ok := err.(*aliasConflictError); ok { delete(oldStatuses, alias) t.Errorf("%v", err) continue Next } return err } add = append(add, beAlias) } } } st.Unlock() remove, err = m.backend.MatchingAliases(remove) st.Lock() if err != nil { return fmt.Errorf("cannot list aliases for snap %q: %v", snapName, err) } st.Unlock() add, err = m.backend.MissingAliases(add) st.Lock() if err != nil { return fmt.Errorf("cannot list aliases for snap %q: %v", snapName, err) } st.Unlock() err = m.backend.UpdateAliases(add, remove) st.Lock() if err != nil { return err } setAliases(st, snapName, oldStatuses) return nil }