Beispiel #1
0
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
}
Beispiel #2
0
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
}
Beispiel #3
0
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
}
Beispiel #4
0
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
}
Beispiel #5
0
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
}
Beispiel #6
0
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
}
Beispiel #7
0
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
}
Beispiel #8
0
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}
}
Beispiel #9
0
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

}