func getSerial(t *state.Task, privKey asserts.PrivateKey, device *auth.DeviceState, cfg *serialRequestConfig) (*asserts.Serial, error) { var serialSup serialSetup err := t.Get("serial-setup", &serialSup) if err != nil && err != state.ErrNoState { return nil, err } if serialSup.Serial != "" { // we got a serial, just haven't managed to save its info yet a, err := asserts.Decode([]byte(serialSup.Serial)) if err != nil { return nil, fmt.Errorf("internal error: cannot decode previously saved serial: %v", err) } return a.(*asserts.Serial), nil } client := &http.Client{Timeout: 30 * time.Second} // NB: until we get at least an Accepted (202) we need to // retry from scratch creating a new request-id because the // previous one used could have expired if serialSup.SerialRequest == "" { serialRequest, err := prepareSerialRequest(t, privKey, device, client, cfg) if err != nil { // errors & retries return nil, err } serialSup.SerialRequest = serialRequest } serial, err := submitSerialRequest(t, serialSup.SerialRequest, client, cfg) if err == errPoll { // we can/should reuse the serial-request t.Set("serial-setup", serialSup) return nil, errPoll } if err != nil { // errors & retries return nil, err } keyID := privKey.PublicKey().ID() if serial.BrandID() != device.Brand || serial.Model() != device.Model || serial.DeviceKey().ID() != keyID { return nil, fmt.Errorf("obtained serial assertion does not match provided device identity information (brand, model, key id): %s / %s / %s != %s / %s / %s", serial.BrandID(), serial.Model(), serial.DeviceKey().ID(), device.Brand, device.Model, keyID) } serialSup.Serial = string(asserts.Encode(serial)) t.Set("serial-setup", serialSup) if repeatRequestSerial == "after-got-serial" { // For testing purposes, ensure a crash in this state works. return nil, &state.Retry{} } return serial, nil }
// gget does the actual work of get and getMaybe func (patch4T) gget(task *state.Task, key string, passThroughMissing bool, value interface{}) error { err := task.Get(key, value) if err == nil || (passThroughMissing && err == state.ErrNoState) { return err } change := task.Change() return fmt.Errorf("cannot get %q from task %s (%s) of change %s (%s): %v", key, task.ID(), task.Kind(), change.ID(), change.Kind(), err) }
func getPlugAndSlotRefs(task *state.Task) (interfaces.PlugRef, interfaces.SlotRef, error) { var plugRef interfaces.PlugRef var slotRef interfaces.SlotRef if err := task.Get("plug", &plugRef); err != nil { return plugRef, slotRef, err } if err := task.Get("slot", &slotRef); err != nil { return plugRef, slotRef, err } return plugRef, slotRef, nil }
func hookSetup(task *state.Task) (*HookSetup, *snapstate.SnapState, error) { var hooksup HookSetup err := task.Get("hook-setup", &hooksup) if err != nil { return nil, nil, fmt.Errorf("cannot extract hook setup from task: %s", err) } var snapst snapstate.SnapState err = snapstate.Get(task.State(), hooksup.Snap, &snapst) if err == state.ErrNoState { return nil, nil, fmt.Errorf("cannot find %q snap", hooksup.Snap) } if err != nil { return nil, nil, fmt.Errorf("cannot handle %q snap: %v", hooksup.Snap, err) } return &hooksup, &snapst, nil }
func (m *SnapManager) undoMountSnap(t *state.Task, _ *tomb.Tomb) error { t.State().Lock() ss, err := TaskSnapSetup(t) t.State().Unlock() if err != nil { return err } t.State().Lock() var typ snap.Type err = t.Get("snap-type", &typ) t.State().Unlock() // backward compatibility if err == state.ErrNoState { typ = "app" } else if err != nil { return err } pb := &TaskProgressAdapter{task: t} return m.backend.UndoSetupSnap(ss.placeInfo(), typ, pb) }
// TaskSnapSetup returns the SnapSetup with task params hold by or referred to by the the task. func TaskSnapSetup(t *state.Task) (*SnapSetup, error) { var ss SnapSetup err := t.Get("snap-setup", &ss) if err != nil && err != state.ErrNoState { return nil, err } if err == nil { return &ss, nil } var id string err = t.Get("snap-setup-task", &id) if err != nil { return nil, err } ts := t.State().Task(id) if err := ts.Get("snap-setup", &ss); err != nil { return nil, err } return &ss, nil }
func (m *InterfaceManager) undoDiscardConns(task *state.Task, _ *tomb.Tomb) error { st := task.State() st.Lock() defer st.Unlock() var removed map[string]connState err := task.Get("removed", &removed) if err != nil && err != state.ErrNoState { return err } conns, err := getConns(st) if err != nil { return err } for id, connState := range removed { conns[id] = connState } setConns(st, conns) task.Set("removed", nil) 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) undoLinkSnap(t *state.Task, _ *tomb.Tomb) error { st := t.State() st.Lock() defer st.Unlock() ss, snapst, err := snapSetupAndState(t) if err != nil { return err } var oldChannel string err = t.Get("old-channel", &oldChannel) if err != nil { return err } var oldTryMode bool err = t.Get("old-trymode", &oldTryMode) if err != nil { return err } var oldDevMode bool err = t.Get("old-devmode", &oldDevMode) if err != nil { return err } var oldJailMode bool err = t.Get("old-jailmode", &oldJailMode) if err != nil { return err } var oldCurrent snap.Revision err = t.Get("old-current", &oldCurrent) if err != nil { return err } var oldCandidateIndex int if err := t.Get("old-candidate-index", &oldCandidateIndex); err != nil { return err } isRevert := ss.Flags.Revert() // relinking of the old snap is done in the undo of unlink-current-snap currentIndex := snapst.LastIndex(snapst.Current) if currentIndex < 0 { return fmt.Errorf("internal error: cannot find revision %d in %v for undoing the added revision", ss.SideInfo.Revision, snapst.Sequence) } if oldCandidateIndex < 0 { snapst.Sequence = append(snapst.Sequence[:currentIndex], snapst.Sequence[currentIndex+1:]...) } else if !isRevert { oldCand := snapst.Sequence[currentIndex] copy(snapst.Sequence[oldCandidateIndex+1:], snapst.Sequence[oldCandidateIndex:]) snapst.Sequence[oldCandidateIndex] = oldCand } snapst.Current = oldCurrent snapst.Active = false snapst.Channel = oldChannel snapst.SetTryMode(oldTryMode) snapst.SetDevMode(oldDevMode) snapst.SetJailMode(oldJailMode) newInfo, err := readInfo(ss.Name(), ss.SideInfo) if err != nil { return err } pb := &TaskProgressAdapter{task: t} st.Unlock() // pb itself will ask for locking err = m.backend.UnlinkSnap(newInfo, pb) st.Lock() if err != nil { return err } // mark as inactive Set(st, ss.Name(), snapst) // Make sure if state commits and snapst is mutated we won't be rerun t.SetStatus(state.UndoneStatus) return nil }
func Patch6SnapSetup(task *state.Task) (patch6SnapSetup, error) { var snapsup patch6SnapSetup err := task.Get("snap-setup", &snapsup) return snapsup, err }
// doRunHook actually runs the hook that was requested. // // Note that this method is synchronous, as the task is already running in a // goroutine. func (m *HookManager) doRunHook(task *state.Task, tomb *tomb.Tomb) error { task.State().Lock() setup := &HookSetup{} err := task.Get("hook-setup", setup) task.State().Unlock() if err != nil { return fmt.Errorf("cannot extract hook setup from task: %s", err) } context, err := NewContext(task, setup, nil) if err != nil { return err } // Obtain a handler for this hook. The repository returns a list since it's // possible for regular expressions to overlap, but multiple handlers is an // error (as is no handler). handlers := m.repository.generateHandlers(context) handlersCount := len(handlers) if handlersCount == 0 { return fmt.Errorf("no registered handlers for hook %q", setup.Hook) } if handlersCount > 1 { return fmt.Errorf("%d handlers registered for hook %q, expected 1", handlersCount, setup.Hook) } context.handler = handlers[0] contextID := context.ID() m.contextsMutex.Lock() m.contexts[contextID] = context m.contextsMutex.Unlock() defer func() { m.contextsMutex.Lock() delete(m.contexts, contextID) m.contextsMutex.Unlock() }() // About to run the hook-- notify the handler if err = context.Handler().Before(); err != nil { return err } // Actually run the hook output, err := runHookAndWait(setup.Snap, setup.Revision, setup.Hook, contextID, tomb) if err != nil { err = osutil.OutputErr(output, err) if handlerErr := context.Handler().Error(err); handlerErr != nil { return handlerErr } return err } // Assuming no error occurred, notify the handler that the hook has // finished. if err = context.Handler().Done(); err != nil { return err } // Let the context know we're finished with it. context.Lock() defer context.Unlock() if err = context.Done(); err != nil { return err } return nil }
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 }
func (m *SnapManager) doAlias(t *state.Task, _ *tomb.Tomb) error { st := t.State() st.Lock() defer st.Unlock() 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 } aliasStatuses, err := getAliases(st, snapName) if err != nil && err != state.ErrNoState { return err } t.Set("old-aliases", aliasStatuses) if aliasStatuses == nil { aliasStatuses = make(map[string]string) } var add []*backend.Alias var remove []*backend.Alias for alias, newStatus := range changes { aliasApp := curInfo.Aliases[alias] if aliasApp == nil { var action string switch newStatus { case "enabled": action = "enable" case "disabled": action = "disable" } return fmt.Errorf("cannot %s alias %q for %q, no such alias", action, alias, snapName) } if aliasStatuses[alias] == newStatus { // nothing to do continue } beAlias := &backend.Alias{ Name: alias, Target: filepath.Base(aliasApp.WrapperPath()), } switch newStatus { case "enabled": err := checkAliasConflict(st, snapName, alias) if err != nil { return err } add = append(add, beAlias) case "disabled": if aliasStatuses[alias] != "" { remove = append(remove, beAlias) } } aliasStatuses[alias] = newStatus } st.Unlock() err = m.backend.UpdateAliases(add, remove) st.Lock() if err != nil { return err } setAliases(st, snapName, aliasStatuses) return nil }
func (m *SnapManager) doAlias(t *state.Task, _ *tomb.Tomb) error { st := t.State() st.Lock() defer st.Unlock() 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 } autoAliases, err := AutoAliases(st, curInfo) if err != nil { return err } autoSet := make(map[string]bool, len(autoAliases)) for _, alias := range autoAliases { autoSet[alias] = true } aliasStatuses, err := getAliases(st, snapName) if err != nil && err != state.ErrNoState { return err } t.Set("old-aliases", aliasStatuses) if aliasStatuses == nil { aliasStatuses = make(map[string]string) } var add []*backend.Alias var remove []*backend.Alias for alias, newStatus := range changes { if aliasStatuses[alias] == newStatus { // nothing to do continue } aliasApp := curInfo.Aliases[alias] if aliasApp == nil { if newStatus == "auto" { // reset to default disabled status delete(aliasStatuses, alias) continue } var action string switch newStatus { case "enabled": action = "enable" case "disabled": action = "disable" } return fmt.Errorf("cannot %s alias %q for %q, no such alias", action, alias, snapName) } beAlias := &backend.Alias{ Name: alias, Target: filepath.Base(aliasApp.WrapperPath()), } if newStatus == "auto" { if !autoSet[alias] { newStatus = "-" // default disabled status, not stored! } } switch newStatus { case "enabled", "auto": if !enabledAlias(aliasStatuses[alias]) { err := checkAliasConflict(st, snapName, alias) if err != nil { return err } add = append(add, beAlias) } case "disabled", "-": if enabledAlias(aliasStatuses[alias]) { remove = append(remove, beAlias) } } if newStatus != "-" { aliasStatuses[alias] = newStatus } else { delete(aliasStatuses, alias) } } st.Unlock() err = m.backend.UpdateAliases(add, remove) st.Lock() if err != nil { return err } setAliases(st, snapName, aliasStatuses) return nil }