Пример #1
0
func (m *SnapManager) undoUnlinkCurrentSnap(t *state.Task, _ *tomb.Tomb) error {
	st := t.State()

	st.Lock()
	defer st.Unlock()

	ss, snapst, err := snapSetupAndState(t)
	if err != nil {
		return err
	}

	oldInfo, err := readInfo(ss.Name, snapst.Current())
	if err != nil {
		return err
	}

	snapst.Active = true
	st.Unlock()
	err = m.backend.LinkSnap(oldInfo)
	st.Lock()
	if err != nil {
		return err
	}

	// mark as active again
	Set(st, ss.Name, snapst)
	return nil

}
Пример #2
0
func (m *InterfaceManager) doDisconnect(task *state.Task, _ *tomb.Tomb) error {
	st := task.State()
	st.Lock()
	defer st.Unlock()

	plugRef, slotRef, err := getPlugAndSlotRefs(task)
	if err != nil {
		return err
	}

	conns, err := getConns(st)
	if err != nil {
		return err
	}

	err = m.repo.Disconnect(plugRef.Snap, plugRef.Name, slotRef.Snap, slotRef.Name)
	if err != nil {
		return err
	}

	plug := m.repo.Plug(plugRef.Snap, plugRef.Name)
	slot := m.repo.Slot(slotRef.Snap, slotRef.Name)
	if err := setupSnapSecurity(task, plug.Snap, m.repo); err != nil {
		return state.Retry
	}
	if err := setupSnapSecurity(task, slot.Snap, m.repo); err != nil {
		return state.Retry
	}

	delete(conns, connID(plugRef, slotRef))
	setConns(st, conns)
	return nil
}
Пример #3
0
func (m *SnapManager) doCopySnapData(t *state.Task, _ *tomb.Tomb) error {
	t.State().Lock()
	ss, snapst, err := snapSetupAndState(t)
	t.State().Unlock()
	if err != nil {
		return err
	}

	newInfo, err := readInfo(ss.Name, snapst.Candidate)
	if err != nil {
		return err
	}

	var oldInfo *snap.Info
	if cur := snapst.Current(); cur != nil {
		var err error
		oldInfo, err = readInfo(ss.Name, cur)
		if err != nil {
			return err
		}

	}

	return m.backend.CopySnapData(newInfo, oldInfo, ss.Flags)
}
Пример #4
0
func (m *SnapManager) doMountSnap(t *state.Task, _ *tomb.Tomb) error {
	t.State().Lock()
	ss, snapst, err := snapSetupAndState(t)
	t.State().Unlock()
	if err != nil {
		return err
	}

	var curInfo *snap.Info
	if cur := snapst.Current(); cur != nil {
		var err error
		curInfo, err = readInfo(ss.Name, cur)
		if err != nil {
			return err
		}

	}

	if err := m.backend.CheckSnap(ss.SnapPath, curInfo, ss.Flags); err != nil {
		return err
	}

	// TODO Use ss.Revision to obtain the right info to mount
	//      instead of assuming the candidate is the right one.
	return m.backend.SetupSnap(ss.SnapPath, snapst.Candidate, ss.Flags)
}
Пример #5
0
func (m *SnapManager) doUnlinkSnap(t *state.Task, _ *tomb.Tomb) error {
	// invoked only if snap has a current active revision

	st := t.State()

	st.Lock()
	defer st.Unlock()

	ss, snapst, err := snapSetupAndState(t)
	if err != nil {
		return err
	}

	info, err := Info(t.State(), ss.Name, ss.Revision)
	if err != nil {
		return err
	}

	pb := &TaskProgressAdapter{task: t}
	st.Unlock() // pb itself will ask for locking
	err = m.backend.UnlinkSnap(info, pb)
	st.Lock()
	if err != nil {
		return err
	}

	// mark as inactive
	snapst.Active = false
	Set(st, ss.Name, snapst)
	return nil
}
Пример #6
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 len(snapst.Sequence) == 1 {
		snapst.Sequence = nil
	} 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
	}

	pb := &TaskProgressAdapter{task: t}
	err = m.backend.RemoveSnapFiles(ss.placeInfo(), pb)
	if err != nil {
		return err
	}

	st.Lock()
	Set(st, ss.Name, snapst)
	st.Unlock()
	return nil
}
Пример #7
0
func (m *SnapManager) doPrepareSnap(t *state.Task, _ *tomb.Tomb) error {
	st := t.State()
	st.Lock()
	ss, snapst, err := snapSetupAndState(t)
	st.Unlock()
	if err != nil {
		return err
	}

	if ss.Revision == 0 { // sideloading
		// to not clash with not sideload installs
		// and to not have clashes between them
		// use incremental revisions starting at 100001
		// for sideloads
		revision := snapst.LocalRevision
		if revision == 0 {
			revision = firstLocalRevision
		} else {
			revision++
		}
		snapst.LocalRevision = revision
		ss.Revision = revision
	} else {
		if err := checkRevisionIsNew(ss.Name, snapst, ss.Revision); err != nil {
			return err
		}
	}

	st.Lock()
	t.Set("snap-setup", ss)
	snapst.Candidate = &snap.SideInfo{Revision: ss.Revision}
	Set(st, ss.Name, snapst)
	st.Unlock()
	return nil
}
Пример #8
0
func (m *SnapManager) doUnlinkCurrentSnap(t *state.Task, _ *tomb.Tomb) error {
	st := t.State()

	st.Lock()
	defer st.Unlock()

	ss, snapst, err := snapSetupAndState(t)
	if err != nil {
		return err
	}

	oldInfo, err := readInfo(ss.Name, snapst.Current())
	if err != nil {
		return err
	}

	snapst.Active = false

	pb := &TaskProgressAdapter{task: t}
	st.Unlock() // pb itself will ask for locking
	err = m.backend.UnlinkSnap(oldInfo, pb)
	st.Lock()
	if err != nil {
		return err
	}

	// mark as inactive
	Set(st, ss.Name, snapst)
	return nil
}
Пример #9
0
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
	}

	return m.backend.UndoSetupSnap(ss.placeInfo())
}
Пример #10
0
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 nil, nil, err
	}
	if err := task.Get("slot", &slotRef); err != nil {
		return nil, nil, err
	}
	return &plugRef, &slotRef, nil
}
Пример #11
0
func snapSetupAndState(t *state.Task) (*SnapSetup, *SnapState, error) {
	ss, err := TaskSnapSetup(t)
	if err != nil {
		return nil, nil, err
	}
	var snapst SnapState
	err = Get(t.State(), ss.Name, &snapst)
	if err != nil && err != state.ErrNoState {
		return nil, nil, err
	}
	return ss, &snapst, nil
}
Пример #12
0
func removeSnapSecurity(task *state.Task, snapName string) error {
	st := task.State()
	for _, backend := range securityBackends {
		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
}
Пример #13
0
func (m *SnapManager) undoPrepareSnap(t *state.Task, _ *tomb.Tomb) error {
	st := t.State()
	st.Lock()
	defer st.Unlock()

	ss, snapst, err := snapSetupAndState(t)
	if err != nil {
		return err
	}
	snapst.Candidate = nil
	Set(st, ss.Name, snapst)
	return nil
}
Пример #14
0
func (m *SnapManager) undoCopySnapData(t *state.Task, _ *tomb.Tomb) error {
	t.State().Lock()
	ss, snapst, err := snapSetupAndState(t)
	t.State().Unlock()
	if err != nil {
		return err
	}

	newInfo, err := readInfo(ss.Name, snapst.Candidate)
	if err != nil {
		return err
	}

	return m.backend.UndoCopySnapData(newInfo, ss.Flags)
}
Пример #15
0
func (m *SnapManager) doClearSnapData(t *state.Task, _ *tomb.Tomb) error {
	t.State().Lock()
	ss, err := TaskSnapSetup(t)
	t.State().Unlock()
	if err != nil {
		return err
	}

	t.State().Lock()
	info, err := Info(t.State(), ss.Name, ss.Revision)
	t.State().Unlock()
	if err != nil {
		return err
	}

	return m.backend.RemoveSnapData(info)
}
Пример #16
0
func (m *SnapManager) doDownloadSnap(t *state.Task, _ *tomb.Tomb) error {
	st := t.State()
	st.Lock()
	ss, snapst, err := snapSetupAndState(t)
	st.Unlock()
	if err != nil {
		return err
	}

	checker := func(info *snap.Info) error {
		return checkRevisionIsNew(ss.Name, snapst, info.Revision)
	}

	pb := &TaskProgressAdapter{task: t}

	var auther store.Authenticator
	if ss.UserID > 0 {
		st.Lock()
		user, err := auth.User(st, ss.UserID)
		st.Unlock()
		if err != nil {
			return err
		}
		auther = user.Authenticator()
	}

	storeInfo, downloadedSnapFile, err := m.backend.Download(ss.Name, ss.Channel, checker, pb, auther)
	if err != nil {
		return err
	}
	ss.SnapPath = downloadedSnapFile
	ss.Revision = storeInfo.Revision

	// update the snap setup and state for the follow up tasks
	st.Lock()
	t.Set("snap-setup", ss)
	snapst.Candidate = &storeInfo.SideInfo
	Set(st, ss.Name, snapst)
	st.Unlock()

	return nil
}
Пример #17
0
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
	}

	// relinking of the old snap is done in the undo of unlink-current-snap

	snapst.Candidate = snapst.Sequence[len(snapst.Sequence)-1]
	snapst.Sequence = snapst.Sequence[:len(snapst.Sequence)-1]
	snapst.Active = false
	snapst.Channel = oldChannel

	newInfo, err := readInfo(ss.Name, snapst.Candidate)
	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)
	return nil
}
Пример #18
0
func (m *InterfaceManager) doDiscardConns(task *state.Task, _ *tomb.Tomb) error {
	st := task.State()
	st.Lock()
	defer st.Unlock()

	snapSetup, err := snapstate.TaskSnapSetup(task)
	if err != nil {
		return err
	}

	snapName := snapSetup.Name

	var snapState snapstate.SnapState
	err = snapstate.Get(st, snapName, &snapState)
	if err != nil && err != state.ErrNoState {
		return err
	}

	if err == nil && len(snapState.Sequence) != 0 {
		return fmt.Errorf("cannot discard connections for snap %q while it is present", snapName)
	}
	conns, err := getConns(st)
	if err != nil {
		return err
	}
	removed := make(map[string]connState)
	for id := range conns {
		plugRef, slotRef, err := parseConnID(id)
		if err != nil {
			return err
		}
		if plugRef.Snap == snapName || slotRef.Snap == snapName {
			removed[id] = conns[id]
			delete(conns, id)
		}
	}
	task.Set("removed", removed)
	setConns(st, conns)
	return nil
}
Пример #19
0
func (m *InterfaceManager) autoConnect(task *state.Task, snapName string, blacklist map[string]bool) error {
	var conns map[string]connState
	err := task.State().Get("conns", &conns)
	if err != nil && err != state.ErrNoState {
		return err
	}
	if conns == nil {
		conns = make(map[string]connState)
	}
	// XXX: quick hack, auto-connect everything
	for _, plug := range m.repo.Plugs(snapName) {
		if blacklist[plug.Name] {
			continue
		}
		candidates := m.repo.AutoConnectCandidates(snapName, plug.Name)
		if len(candidates) != 1 {
			continue
		}
		slot := candidates[0]
		if err := m.repo.Connect(snapName, plug.Name, slot.Snap.Name(), slot.Name); err != nil {
			task.Logf("cannot auto connect %s:%s to %s:%s: %s",
				snapName, plug.Name, slot.Snap.Name(), slot.Name, err)
		}
		key := fmt.Sprintf("%s:%s %s:%s", snapName, plug.Name, slot.Snap.Name(), slot.Name)
		conns[key] = connState{Interface: plug.Interface, Auto: true}
	}
	task.State().Set("conns", conns)
	return nil
}
Пример #20
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 := snapst.Candidate

	m.backend.Candidate(snapst.Candidate)
	snapst.Sequence = append(snapst.Sequence, snapst.Candidate)
	snapst.Candidate = nil
	snapst.Active = true
	oldChannel := snapst.Channel
	if ss.Channel != "" {
		snapst.Channel = ss.Channel
	}

	newInfo, err := readInfo(ss.Name, cand)
	if err != nil {
		return err
	}

	st.Unlock()
	err = m.backend.LinkSnap(newInfo)
	st.Lock()
	if err != nil {
		return err
	}

	t.Set("old-channel", oldChannel)
	// Do at the end so we only preserve the new state if it worked.
	Set(st, ss.Name, snapst)
	return nil
}
Пример #21
0
func setupSnapSecurity(task *state.Task, snapInfo *snap.Info, repo *interfaces.Repository) error {
	st := task.State()
	var snapState snapstate.SnapState
	snapName := snapInfo.Name()
	if err := snapstate.Get(st, snapName, &snapState); err != nil {
		task.Errorf("cannot get state of snap %q: %s", snapName, err)
		return err
	}
	for _, backend := range securityBackends {
		st.Unlock()
		err := backend.Setup(snapInfo, snapState.DevMode(), repo)
		st.Lock()
		if err != nil {
			task.Errorf("cannot setup %s for snap %q: %s", backend.Name(), snapName, err)
			return err
		}
	}
	return nil
}
Пример #22
0
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
}
Пример #23
0
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
}
Пример #24
0
func (m *InterfaceManager) doRemoveProfiles(task *state.Task, _ *tomb.Tomb) error {
	st := task.State()
	st.Lock()
	defer st.Unlock()

	// Get SnapSetup for this snap. This is gives us the name of the snap.
	snapSetup, err := snapstate.TaskSnapSetup(task)
	if err != nil {
		return err
	}
	snapName := snapSetup.Name

	// Get SnapState for this snap
	var snapState snapstate.SnapState
	err = snapstate.Get(st, snapName, &snapState)
	if err != nil && err != state.ErrNoState {
		return err
	}

	// Get the old-devmode flag from the task.
	// This flag is set by setup-profiles in case we have to undo.
	var oldDevMode bool
	err = task.Get("old-devmode", &oldDevMode)
	if err != nil && err != state.ErrNoState {
		return err
	}
	// Restore the state of DevMode flag if old-devmode was saved in the task.
	if err == nil {
		if oldDevMode {
			snapState.Flags |= snapstate.DevMode
		} else {
			snapState.Flags &= ^snapstate.DevMode
		}
		snapstate.Set(st, snapName, &snapState)
	}

	// Disconnect the snap entirely.
	// This is required to remove the snap from the interface repository.
	// The returned list of affected snaps will need to have its security setup
	// to reflect the change.
	affectedSnaps, err := m.repo.DisconnectSnap(snapName)
	if err != nil {
		return err
	}

	// Setup security of the affected snaps.
	for _, snapInfo := range affectedSnaps {
		if snapInfo.Name() == snapName {
			// Skip setup for the snap being removed as this is handled below.
			continue
		}
		if err := setupSnapSecurity(task, snapInfo, m.repo); err != nil {
			return state.Retry
		}
	}

	// Remove the snap from the interface repository.
	// This discards all the plugs and slots belonging to that snap.
	if err := m.repo.RemoveSnap(snapName); err != nil {
		return err
	}

	// Remove security artefacts of the snap.
	if err := removeSnapSecurity(task, snapName); err != nil {
		return state.Retry
	}

	return nil
}
Пример #25
0
func (m *InterfaceManager) doSetupProfiles(task *state.Task, _ *tomb.Tomb) error {
	task.State().Lock()
	defer task.State().Unlock()

	// Get snap.Info from bits handed by the snap manager.
	ss, err := snapstate.TaskSnapSetup(task)
	if err != nil {
		return err
	}
	snapInfo, err := snapstate.Info(task.State(), ss.Name, ss.Revision)
	if err != nil {
		return err
	}
	snap.AddImplicitSlots(snapInfo)
	snapName := snapInfo.Name()
	var snapState snapstate.SnapState
	if err := snapstate.Get(task.State(), snapName, &snapState); err != nil {
		task.Errorf("cannot get state of snap %q: %s", snapName, err)
		return err
	}

	// Set DevMode flag if SnapSetup.Flags indicates it should be done
	// but remember the old value in the task in case we undo.
	task.Set("old-devmode", snapState.DevMode())
	if ss.DevMode() {
		snapState.Flags |= snapstate.DevMode
	} else {
		snapState.Flags &= ^snapstate.DevMode
	}
	snapstate.Set(task.State(), snapName, &snapState)

	// The snap may have been updated so perform the following operation to
	// ensure that we are always working on the correct state:
	//
	// - disconnect all connections to/from the given snap
	//   - remembering the snaps that were affected by this operation
	// - remove the (old) snap from the interfaces repository
	// - add the (new) snap to the interfaces repository
	// - restore connections based on what is kept in the state
	//   - if a connection cannot be restored then remove it from the state
	// - setup the security of all the affected snaps
	blacklist := m.repo.AutoConnectBlacklist(snapName)
	affectedSnaps, err := m.repo.DisconnectSnap(snapName)
	if err != nil {
		return err
	}
	// XXX: what about snap renames? We should remove the old name (or switch
	// to IDs in the interfaces repository)
	if err := m.repo.RemoveSnap(snapName); err != nil {
		return err
	}
	if err := m.repo.AddSnap(snapInfo); err != nil {
		if _, ok := err.(*interfaces.BadInterfacesError); ok {
			logger.Noticef("%s", err)
		} else {
			return err
		}
	}
	if err := m.reloadConnections(snapName); err != nil {
		return err
	}
	if err := m.autoConnect(task, snapName, blacklist); err != nil {
		return err
	}
	if len(affectedSnaps) == 0 {
		affectedSnaps = append(affectedSnaps, snapInfo)
	}
	for _, snapInfo := range affectedSnaps {
		if err := setupSnapSecurity(task, snapInfo, m.repo); err != nil {
			return state.Retry
		}
	}
	return nil
}
Пример #26
0
func doInstall(s *state.State, curActive bool, snapName, snapPath, channel string, userID int, flags snappy.InstallFlags) (*state.TaskSet, error) {
	if err := checkChangeConflict(s, snapName); err != nil {
		return nil, err
	}

	if snapPath == "" && channel == "" {
		channel = "stable"
	}

	var prepare *state.Task
	ss := SnapSetup{
		Channel: channel,
		UserID:  userID,
		Flags:   int(flags),
	}
	ss.Name = snapName
	ss.SnapPath = snapPath
	if snapPath != "" {
		prepare = s.NewTask("prepare-snap", fmt.Sprintf(i18n.G("Prepare snap %q"), snapPath))
	} else {
		prepare = s.NewTask("download-snap", fmt.Sprintf(i18n.G("Download snap %q from channel %q"), snapName, channel))
	}
	prepare.Set("snap-setup", ss)

	tasks := []*state.Task{prepare}
	addTask := func(t *state.Task) {
		t.Set("snap-setup-task", prepare.ID())
		tasks = append(tasks, t)
	}

	// mount
	mount := s.NewTask("mount-snap", fmt.Sprintf(i18n.G("Mount snap %q"), snapName))
	addTask(mount)
	mount.WaitFor(prepare)
	precopy := mount

	if curActive {
		// unlink-current-snap (will stop services for copy-data)
		unlink := s.NewTask("unlink-current-snap", fmt.Sprintf(i18n.G("Make current revision for snap %q unavailable"), snapName))
		addTask(unlink)
		unlink.WaitFor(mount)
		precopy = unlink
	}

	// copy-data (needs stopped services by unlink)
	copyData := s.NewTask("copy-snap-data", fmt.Sprintf(i18n.G("Copy snap %q data"), snapName))
	addTask(copyData)
	copyData.WaitFor(precopy)

	// security
	setupSecurity := s.NewTask("setup-profiles", fmt.Sprintf(i18n.G("Setup snap %q security profiles"), snapName))
	addTask(setupSecurity)
	setupSecurity.WaitFor(copyData)

	// finalize (wrappers+current symlink)
	linkSnap := s.NewTask("link-snap", fmt.Sprintf(i18n.G("Make snap %q available to the system"), snapName))
	addTask(linkSnap)
	linkSnap.WaitFor(setupSecurity)

	return state.NewTaskSet(tasks...), nil
}