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 }
func (m *InterfaceManager) setupProfilesForSnap(task *state.Task, _ *tomb.Tomb, snapInfo *snap.Info, opts interfaces.ConfinementOptions) error { snap.AddImplicitSlots(snapInfo) snapName := snapInfo.Name() // 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 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 { task.Logf("%s", err) } else { return err } } if err := m.reloadConnections(snapName); err != nil { return err } // FIXME: here we should not reconnect auto-connect plug/slot // pairs that were explicitly disconnected by the user if err := m.autoConnect(task, snapName, nil); err != nil { return err } if err := setupSnapSecurity(task, snapInfo, opts, m.repo); err != nil { return err } return m.setupAffectedSnaps(task, snapName, affectedSnaps) }
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 }
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 }