예제 #1
0
파일: devicemgr.go 프로젝트: niemeyer/snapd
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
}
예제 #2
0
파일: patch4.go 프로젝트: pedronis/snappy
// 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)
}
예제 #3
0
파일: helpers.go 프로젝트: niemeyer/snapd
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
}
예제 #4
0
파일: hookmgr.go 프로젝트: niemeyer/snapd
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
}
예제 #5
0
파일: snapmgr.go 프로젝트: clobrano/snappy
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)
}
예제 #6
0
파일: snapmgr.go 프로젝트: clobrano/snappy
// 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
}
예제 #7
0
파일: handlers.go 프로젝트: niemeyer/snapd
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
}
예제 #8
0
파일: aliases.go 프로젝트: pedronis/snappy
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
}
예제 #9
0
파일: snapmgr.go 프로젝트: clobrano/snappy
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
}
예제 #10
0
func Patch6SnapSetup(task *state.Task) (patch6SnapSetup, error) {
	var snapsup patch6SnapSetup
	err := task.Get("snap-setup", &snapsup)
	return snapsup, err
}
예제 #11
0
파일: hookmgr.go 프로젝트: clobrano/snappy
// 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
}
예제 #12
0
파일: aliases.go 프로젝트: chipaca/snappy
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

}
예제 #13
0
파일: aliases.go 프로젝트: chipaca/snappy
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
}
예제 #14
0
파일: aliases.go 프로젝트: pedronis/snappy
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
}