Пример #1
0
// allLocalSnapInfos returns the information about the all current snaps and their SnapStates.
func allLocalSnapInfos(st *state.State) ([]aboutSnap, error) {
	st.Lock()
	defer st.Unlock()

	snapStates, err := snapstate.All(st)
	if err != nil {
		return nil, err
	}
	about := make([]aboutSnap, 0, len(snapStates))

	var firstErr error
	for _, snapState := range snapStates {
		info, err := snapState.CurrentInfo()
		if err != nil {
			// XXX: aggregate instead?
			if firstErr == nil {
				firstErr = err
			}
			continue
		}
		about = append(about, aboutSnap{info, snapState})
	}

	return about, firstErr
}
Пример #2
0
func updateInfo(st *state.State, snapst *SnapState, channel string, userID int, flags Flags) (*snap.Info, error) {
	user, err := userFromUserID(st, userID)
	if err != nil {
		return nil, err
	}
	curInfo, err := snapst.CurrentInfo()
	if err != nil {
		return nil, err
	}

	if curInfo.SnapID == "" { // covers also trymode
		return nil, fmt.Errorf("cannot refresh local snap %q", curInfo.Name())
	}

	refreshCand := &store.RefreshCandidate{
		// the desired channel
		Channel: channel,
		DevMode: flags.DevModeAllowed(),
		Block:   snapst.Block(),

		SnapID:   curInfo.SnapID,
		Revision: curInfo.Revision,
		Epoch:    curInfo.Epoch,
	}

	theStore := Store(st)
	st.Unlock() // calls to the store should be done without holding the state lock
	res, err := theStore.ListRefresh([]*store.RefreshCandidate{refreshCand}, user)
	st.Lock()
	if len(res) == 0 {
		return nil, fmt.Errorf("snap %q has no updates available", curInfo.Name())
	}
	return res[0], nil
}
Пример #3
0
func doFetch(s *state.State, userID int, fetching func(asserts.Fetcher) error) error {
	// TODO: once we have a bulk assertion retrieval endpoint this approach will change

	user, err := userFromUserID(s, userID)
	if err != nil {
		return err
	}

	sto := snapstate.Store(s)

	retrieve := func(ref *asserts.Ref) (asserts.Assertion, error) {
		// TODO: ignore errors if already in db?
		return sto.Assertion(ref.Type, ref.PrimaryKey, user)
	}

	f := newFetcher(s, retrieve)

	s.Unlock()
	err = fetching(f)
	s.Lock()
	if err != nil {
		return err
	}

	// TODO: trigger w. caller a global sanity check if a is revoked
	// (but try to save as much possible still),
	// or err is a check error
	return f.commit()
}
Пример #4
0
// Apply applies any necessary patches to update the provided state to
// conventions required by the current patch level of the system.
func Apply(s *state.State) error {
	var stateLevel int
	s.Lock()
	err := s.Get("patch-level", &stateLevel)
	s.Unlock()
	if err != nil && err != state.ErrNoState {
		return err
	}
	if stateLevel == Level {
		// already at right level, nothing to do
		return nil
	}
	if stateLevel > Level {
		return fmt.Errorf("cannot downgrade: snapd is too old for the current system state (patch level %d)", stateLevel)
	}

	level := stateLevel
	for level < Level {
		logger.Noticef("Patching system state from level %d to %d", level, level+1)
		patch := patches[level+1]
		if patch == nil {
			return fmt.Errorf("cannot upgrade: snapd is too new for the current system state (patch level %d)", level)
		}
		err := applyOne(patch, s, level)
		if err != nil {
			logger.Noticef("Cannnot patch: %v", err)
			return fmt.Errorf("cannot patch system state from level %d to %d: %v", level, level+1, err)
		}
		level++
	}

	return nil
}
Пример #5
0
// checkSnap ensures that the snap can be installed.
func checkSnap(st *state.State, snapFilePath string, si *snap.SideInfo, curInfo *snap.Info, flags Flags) error {
	// This assumes that the snap was already verified or --dangerous was used.

	s, _, err := openSnapFile(snapFilePath, si)
	if err != nil {
		return err
	}

	if s.NeedsDevMode() && !flags.DevModeAllowed() {
		return fmt.Errorf("snap %q requires devmode or confinement override", s.Name())
	}

	// verify we have a valid architecture
	if !arch.IsSupportedArchitecture(s.Architectures) {
		return fmt.Errorf("snap %q supported architectures (%s) are incompatible with this system (%s)", s.Name(), strings.Join(s.Architectures, ", "), arch.UbuntuArchitecture())
	}

	// check assumes
	err = checkAssumes(s)
	if err != nil {
		return err
	}

	st.Lock()
	defer st.Unlock()

	for _, check := range checkSnapCallbacks {
		err := check(st, s, curInfo, flags)
		if err != nil {
			return err
		}
	}

	return nil
}
Пример #6
0
// Init initializes an empty state to the current implemented patch level.
func Init(s *state.State) {
	s.Lock()
	defer s.Unlock()
	if s.Get("patch-level", new(int)) != state.ErrNoState {
		panic("internal error: expected empty state, attempting to override patch-level without actual patching")
	}
	s.Set("patch-level", Level)
}
Пример #7
0
func importAssertionsFromSeed(st *state.State) error {
	st.Lock()
	defer st.Unlock()

	assertSeedDir := filepath.Join(dirs.SnapSeedDir, "assertions")
	dc, err := ioutil.ReadDir(assertSeedDir)
	if err != nil {
		return fmt.Errorf("cannot read assert seed dir: %s", err)
	}

	// FIXME: remove this check once asserts are mandatory
	if len(dc) == 0 {
		return nil
	}

	// collect
	var modelRef *asserts.Ref
	batch := assertstate.NewBatch()
	for _, fi := range dc {
		fn := filepath.Join(assertSeedDir, fi.Name())
		refs, err := readAsserts(fn, batch)
		if err != nil {
			return fmt.Errorf("cannot read assertions: %s", err)
		}
		for _, ref := range refs {
			if ref.Type == asserts.ModelType {
				if modelRef != nil && modelRef.Unique() != ref.Unique() {
					return fmt.Errorf("cannot add more than one model assertion")
				}
				modelRef = ref
			}
		}
	}
	// verify we have one model assertion
	if modelRef == nil {
		return fmt.Errorf("need a model assertion")
	}

	if err := batch.Commit(st); err != nil {
		return err
	}

	a, err := modelRef.Resolve(assertstate.DB(st).Find)
	if err != nil {
		return fmt.Errorf("internal error: cannot find just added assertion %v: %v", modelRef, err)
	}
	modelAssertion := a.(*asserts.Model)

	// set device,model from the model assertion
	auth.SetDevice(st, &auth.DeviceState{
		Brand: modelAssertion.BrandID(),
		Model: modelAssertion.Model(),
	})

	return nil
}
Пример #8
0
func snapInfo(st *state.State, name, channel string, revision snap.Revision, userID int, flags Flags) (*snap.Info, error) {
	user, err := userFromUserID(st, userID)
	if err != nil {
		return nil, err
	}
	theStore := Store(st)
	st.Unlock() // calls to the store should be done without holding the state lock
	snap, err := theStore.Snap(name, channel, flags.DevModeAllowed(), revision, user)
	st.Lock()
	return snap, err
}
Пример #9
0
func applyOne(patch func(s *state.State) error, s *state.State, level int) error {
	s.Lock()
	defer s.Unlock()

	err := patch(s)
	if err != nil {
		return err
	}

	s.Set("patch-level", level+1)
	return nil
}
Пример #10
0
// checkSnap ensures that the snap can be installed.
func checkSnap(st *state.State, snapFilePath string, curInfo *snap.Info, flags Flags) error {
	// This assumes that the snap was already verified or --dangerous was used.

	s, _, err := openSnapFile(snapFilePath, nil)
	if err != nil {
		return err
	}

	if s.NeedsDevMode() && !flags.DevModeAllowed() {
		return fmt.Errorf("snap %q requires devmode or confinement override", s.Name())
	}

	// verify we have a valid architecture
	if !arch.IsSupportedArchitecture(s.Architectures) {
		return fmt.Errorf("snap %q supported architectures (%s) are incompatible with this system (%s)", s.Name(), strings.Join(s.Architectures, ", "), arch.UbuntuArchitecture())
	}

	// check assumes
	err = checkAssumes(s)
	if err != nil {
		return err
	}

	if s.Type != snap.TypeGadget {
		return nil
	}

	// gadget specific checks
	if release.OnClassic {
		// for the time being
		return fmt.Errorf("cannot install a gadget snap on classic")
	}

	st.Lock()
	defer st.Unlock()
	currentGadget, err := GadgetInfo(st)
	// in firstboot we have no gadget yet - that is ok
	if err == state.ErrNoState && !firstboot.HasRun() {
		return nil
	}
	if err != nil {
		return fmt.Errorf("cannot find original gadget snap")
	}

	// TODO: actually compare snap ids, from current gadget and candidate
	if currentGadget.Name() != s.Name() {
		return fmt.Errorf("cannot replace gadget snap with a different one")
	}

	return nil
}
Пример #11
0
// Manager returns a new assertion manager.
func Manager(s *state.State) (*AssertManager, error) {
	runner := state.NewTaskRunner(s)

	runner.AddHandler("validate-snap", doValidateSnap, nil)

	db, err := sysdb.Open()
	if err != nil {
		return nil, err
	}

	s.Lock()
	ReplaceDB(s, db)
	s.Unlock()

	return &AssertManager{runner: runner}, nil
}
Пример #12
0
// allLocalSnapInfos returns the information about the all current snaps and their SnapStates.
func allLocalSnapInfos(st *state.State, all bool) ([]aboutSnap, error) {
	st.Lock()
	defer st.Unlock()

	snapStates, err := snapstate.All(st)
	if err != nil {
		return nil, err
	}
	about := make([]aboutSnap, 0, len(snapStates))

	var firstErr error
	for _, snapst := range snapStates {
		var infos []*snap.Info
		var info *snap.Info
		var err error
		if all {
			for _, seq := range snapst.Sequence {
				info, err = snap.ReadInfo(seq.RealName, seq)
				if err != nil {
					break
				}
				infos = append(infos, info)
			}
		} else {
			info, err = snapst.CurrentInfo()
			infos = append(infos, info)
		}

		if err != nil {
			// XXX: aggregate instead?
			if firstErr == nil {
				firstErr = err
			}
			continue
		}
		for _, info := range infos {
			about = append(about, aboutSnap{info, snapst})
		}
	}

	return about, firstErr
}
Пример #13
0
// localSnapInfo returns the information about the current snap for the given name plus the SnapState with the active flag and other snap revisions.
func localSnapInfo(st *state.State, name string) (*snap.Info, *snapstate.SnapState, error) {
	st.Lock()
	defer st.Unlock()

	var snapst snapstate.SnapState
	err := snapstate.Get(st, name, &snapst)
	if err != nil && err != state.ErrNoState {
		return nil, nil, fmt.Errorf("cannot consult state: %v", err)
	}

	info, err := snapst.CurrentInfo()
	if err == snapstate.ErrNoCurrent {
		return nil, nil, errNoSnap
	}
	if err != nil {
		return nil, nil, fmt.Errorf("cannot read snap details: %v", err)
	}

	return info, &snapst, nil
}
Пример #14
0
func newRunnerManager(s *state.State) *runnerManager {
	rm := &runnerManager{
		runner: state.NewTaskRunner(s),
	}

	rm.runner.AddHandler("runMgr1", func(t *state.Task, _ *tomb.Tomb) error {
		s := t.State()
		s.Lock()
		defer s.Unlock()
		s.Set("runMgr1Mark", 1)
		return nil
	}, nil)
	rm.runner.AddHandler("runMgr2", func(t *state.Task, _ *tomb.Tomb) error {
		s := t.State()
		s.Lock()
		defer s.Unlock()
		s.Set("runMgr2Mark", 1)
		return nil
	}, nil)
	rm.runner.AddHandler("runMgrEnsureBefore", func(t *state.Task, _ *tomb.Tomb) error {
		s := t.State()
		s.Lock()
		defer s.Unlock()
		s.EnsureBefore(20 * time.Millisecond)
		return nil
	}, nil)

	return rm
}
Пример #15
0
func (bs *bootedSuite) makeInstalledKernelOS(c *C, st *state.State) {
	st.Lock()
	defer st.Unlock()

	snaptest.MockSnap(c, "name: ubuntu-core\ntype: os\nversion: 1", osSI1)
	snaptest.MockSnap(c, "name: ubuntu-core\ntype: os\nversion: 2", osSI2)
	snapstate.Set(st, "ubuntu-core", &snapstate.SnapState{
		SnapType: "os",
		Active:   true,
		Sequence: []*snap.SideInfo{osSI1, osSI2},
		Current:  snap.R(2),
	})

	snaptest.MockSnap(c, "name: canonical-pc-linux\ntype: os\nversion: 1", kernelSI1)
	snaptest.MockSnap(c, "name: canonical-pc-linux\ntype: os\nversion: 2", kernelSI2)
	snapstate.Set(st, "canonical-pc-linux", &snapstate.SnapState{
		SnapType: "kernel",
		Active:   true,
		Sequence: []*snap.SideInfo{kernelSI1, kernelSI2},
		Current:  snap.R(2),
	})

}
Пример #16
0
func refreshCandidates(st *state.State, names []string, user *auth.UserState) ([]*snap.Info, map[string]*SnapState, error) {
	snapStates, err := All(st)
	if err != nil {
		return nil, nil, err
	}

	sort.Strings(names)

	stateByID := make(map[string]*SnapState, len(snapStates))
	candidatesInfo := make([]*store.RefreshCandidate, 0, len(snapStates))
	for _, snapst := range snapStates {
		if len(names) == 0 && (snapst.TryMode || snapst.DevMode) {
			// no auto-refresh for trymode nor devmode
			continue
		}

		// FIXME: snaps that are not active are skipped for now
		//        until we know what we want to do
		if !snapst.Active {
			continue
		}

		snapInfo, err := snapst.CurrentInfo()
		if err != nil {
			// log something maybe?
			continue
		}

		if snapInfo.SnapID == "" {
			// no refresh for sideloaded
			continue
		}

		if len(names) > 0 && !contains(names, snapInfo.Name()) {
			continue
		}

		stateByID[snapInfo.SnapID] = snapst

		// get confinement preference from the snapstate
		candidateInfo := &store.RefreshCandidate{
			// the desired channel (not info.Channel!)
			Channel: snapst.Channel,
			DevMode: snapst.DevModeAllowed(),

			SnapID:   snapInfo.SnapID,
			Revision: snapInfo.Revision,
			Epoch:    snapInfo.Epoch,
		}

		if len(names) == 0 {
			candidateInfo.Block = snapst.Block()
		}

		candidatesInfo = append(candidatesInfo, candidateInfo)
	}

	theStore := Store(st)

	st.Unlock()
	updates, err := theStore.ListRefresh(candidatesInfo, user)
	st.Lock()
	if err != nil {
		return nil, nil, err
	}

	return updates, stateByID, nil
}