func (m *InterfaceManager) setupAffectedSnaps(task *state.Task, affectingSnap string, affectedSnaps []string) error { st := task.State() // Setup security of the affected snaps. for _, affectedSnapName := range affectedSnaps { // the snap that triggered the change needs to be skipped if affectedSnapName == affectingSnap { continue } var snapst snapstate.SnapState if err := snapstate.Get(st, affectedSnapName, &snapst); err != nil { return err } affectedSnapInfo, err := snapst.CurrentInfo() if err != nil { return err } snap.AddImplicitSlots(affectedSnapInfo) opts := confinementOptions(snapst.Flags) if err := setupSnapSecurity(task, affectedSnapInfo, opts, m.repo); err != nil { return err } } return nil }
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 } var affectedConns []interfaces.ConnRef if plugRef.Snap != "" && plugRef.Name != "" && slotRef.Snap != "" && slotRef.Name != "" { if err := m.repo.Disconnect(plugRef.Snap, plugRef.Name, slotRef.Snap, slotRef.Name); err != nil { return err } affectedConns = []interfaces.ConnRef{{plugRef, slotRef}} } else if plugRef.Name != "" && slotRef.Snap == "" && slotRef.Name == "" { // NOTE: plugRef.Snap can be either empty or not, Connected handles both affectedConns, err = m.repo.Connected(plugRef.Snap, plugRef.Name) if err != nil { return err } m.repo.DisconnectAll(affectedConns) } else if plugRef.Snap == "" && plugRef.Name == "" && slotRef.Name != "" { // Symmetrically, slotRef.Snap can be either empty or not affectedConns, err = m.repo.Connected(slotRef.Snap, slotRef.Name) if err != nil { return err } m.repo.DisconnectAll(affectedConns) } else { return fmt.Errorf("internal error, unhandled disconnect case plug: %q, slot: %q", plugRef, slotRef) } affectedSnaps := snapNamesFromConns(affectedConns) for _, snapName := range affectedSnaps { var snapst snapstate.SnapState if err := snapstate.Get(st, snapName, &snapst); err != nil { return err } snapInfo, err := snapst.CurrentInfo() if err != nil { return err } opts := confinementOptions(snapst.Flags) if err := setupSnapSecurity(task, snapInfo, opts, m.repo); err != nil { return &state.Retry{} } } for _, conn := range affectedConns { delete(conns, conn.ID()) } setConns(st, conns) return nil }
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 := snapst.CurrentInfo() 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 }
func submitSerialRequest(t *state.Task, serialRequest string, client *http.Client) (*asserts.Serial, error) { st := t.State() st.Unlock() defer st.Lock() resp, err := client.Post(serialRequestURL, asserts.MediaType, bytes.NewBufferString(serialRequest)) if err != nil { return nil, retryErr(t, "cannot deliver device serial request: %v", err) } defer resp.Body.Close() switch resp.StatusCode { case 200, 201: case 202: return nil, errPoll default: return nil, retryErr(t, "cannot deliver device serial request: unexpected status %d", resp.StatusCode) } // decode body with serial assertion dec := asserts.NewDecoder(resp.Body) got, err := dec.Decode() if err != nil { // assume broken i/o return nil, retryErr(t, "cannot read response to request for a serial: %v", err) } serial, ok := got.(*asserts.Serial) if !ok { return nil, fmt.Errorf("cannot use device serial assertion of type %q", got.Type().Name) } return serial, nil }
func (m *SnapManager) doSetupAliases(t *state.Task, _ *tomb.Tomb) error { st := t.State() st.Lock() defer st.Unlock() snapsup, snapst, err := snapSetupAndState(t) 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 } var aliases []*backend.Alias for alias, aliasStatus := range aliasStatuses { if aliasStatus == "enabled" { aliasApp := curInfo.Aliases[alias] if aliasApp == nil { // not a known alias anymore, skip continue } aliases = append(aliases, &backend.Alias{ Name: alias, Target: filepath.Base(aliasApp.WrapperPath()), }) } } st.Unlock() defer st.Lock() return m.backend.UpdateAliases(aliases, nil) }
func (m *DeviceManager) doGenerateDeviceKey(t *state.Task, _ *tomb.Tomb) error { st := t.State() st.Lock() defer st.Unlock() device, err := auth.Device(st) if err != nil { return err } if device.KeyID != "" { // nothing to do return nil } keyPair, err := rsa.GenerateKey(rand.Reader, keyLength) if err != nil { return fmt.Errorf("cannot generate device key pair: %v", err) } privKey := asserts.RSAPrivateKey(keyPair) err = m.keypairMgr.Put(privKey) if err != nil { return fmt.Errorf("cannot store device key pair: %v", err) } device.KeyID = privKey.PublicKey().ID() err = auth.SetDevice(st, device) if err != nil { return err } t.SetStatus(state.DoneStatus) return nil }
func (p4 patch4T) mangle(task *state.Task) error { snapsup, snapst, err := p4.snapSetupAndState(task) if err != nil { return err } var hadCandidate bool if err := p4.getMaybe(task, "had-candidate", &hadCandidate); err != nil && err != state.ErrNoState { return err } if hadCandidate { change := task.Change() if change.Kind() != "revert-snap" { return fmt.Errorf("had-candidate true for task %s (%s) of non-revert change %s (%s)", task.ID(), task.Kind(), change.ID(), change.Kind()) } } task.Clear("had-candidate") task.Set("old-candidate-index", snapst.LastIndex(snapsup.SideInfo.Revision)) return nil }
func (m *InterfaceManager) undoSetupProfiles(task *state.Task, tomb *tomb.Tomb) error { st := task.State() st.Lock() defer st.Unlock() ss, err := snapstate.TaskSnapSetup(task) if err != nil { return err } snapName := ss.Name() // Get the name from SnapSetup and use it to find the current SideInfo // about the snap, if there is one. var snapst snapstate.SnapState err = snapstate.Get(st, snapName, &snapst) if err != nil && err != state.ErrNoState { return err } sideInfo := snapst.CurrentSideInfo() if sideInfo == nil { // The snap was not installed before so undo should remove security profiles. return m.removeProfilesForSnap(task, tomb, snapName) } else { // The snap was installed before so undo should setup the old security profiles. snapInfo, err := snap.ReadInfo(snapName, sideInfo) if err != nil { return err } return m.setupProfilesForSnap(task, tomb, snapInfo, snapst.DevMode) } }
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().Unset() { // Local revisions start at -1 and go down. revision := snapst.LocalRevision() if revision.Unset() || revision.N > 0 { revision = snap.R(-1) } else { revision.N-- } if !revision.Local() { panic("internal error: invalid local revision built: " + revision.String()) } ss.SideInfo.Revision = revision } st.Lock() t.Set("snap-setup", ss) st.Unlock() return nil }
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 }
func (m *SnapManager) doClearSnapData(t *state.Task, _ *tomb.Tomb) error { t.State().Lock() ss, snapst, err := snapSetupAndState(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 } if err = m.backend.RemoveSnapData(info); err != nil { return err } // Only remove data common between versions if this is the last version if len(snapst.Sequence) == 1 { if err = m.backend.RemoveSnapCommonData(info); err != nil { return err } } return nil }
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 := snapst.CurrentInfo() 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 }
func (m *SnapManager) cleanupCopySnapData(t *state.Task, _ *tomb.Tomb) error { st := t.State() st.Lock() defer st.Unlock() if t.Status() != state.DoneStatus { // it failed return nil } _, snapst, err := snapSetupAndState(t) if err != nil { return err } info, err := snapst.CurrentInfo() if err != nil { return err } m.backend.ClearTrashedData(info) return nil }
func (m *DeviceManager) doMarkSeeded(t *state.Task, _ *tomb.Tomb) error { st := t.State() st.Lock() defer st.Unlock() st.Set("seeded", true) return nil }
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 := snap.ReadInfo(ss.Name(), ss.SideInfo) if err != nil { return err } 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 { logger.Noticef("%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, ss.DevModeAllowed(), m.repo); err != nil { return err } return m.setupAffectedSnaps(task, snapName, affectedSnaps) }
func checkTaskLogContains(c *C, task *state.Task, pattern *regexp.Regexp) { found := false for _, message := range task.Log() { if pattern.MatchString(message) { found = true } } c.Check(found, Equals, true, Commentf("Expected to find regex %q in task log: %v", pattern, task.Log())) }
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 }
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 }
func (m *SnapManager) doRemoveAliases(t *state.Task, _ *tomb.Tomb) error { st := t.State() st.Lock() defer st.Unlock() snapsup, _, err := snapSetupAndState(t) if err != nil { return err } snapName := snapsup.Name() st.Unlock() defer st.Lock() return m.backend.RemoveSnapAliases(snapName) }
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 }
func prepareSerialRequest(t *state.Task, privKey asserts.PrivateKey, device *auth.DeviceState, client *http.Client, cfg *serialRequestConfig) (string, error) { st := t.State() st.Unlock() defer st.Lock() req, err := http.NewRequest("POST", cfg.requestIDURL, nil) if err != nil { return "", fmt.Errorf("internal error: cannot create request-id request %q", cfg.requestIDURL) } cfg.applyHeaders(req) resp, err := client.Do(req) if err != nil { return "", retryErr(t, "cannot retrieve request-id for making a request for a serial: %v", err) } defer resp.Body.Close() if resp.StatusCode != 200 { return "", retryErr(t, "cannot retrieve request-id for making a request for a serial: unexpected status %d", resp.StatusCode) } dec := json.NewDecoder(resp.Body) var requestID requestIDResp err = dec.Decode(&requestID) if err != nil { // assume broken i/o return "", retryErr(t, "cannot read response with request-id for making a request for a serial: %v", err) } encodedPubKey, err := asserts.EncodePublicKey(privKey.PublicKey()) if err != nil { return "", fmt.Errorf("internal error: cannot encode device public key: %v", err) } headers := map[string]interface{}{ "brand-id": device.Brand, "model": device.Model, "request-id": requestID.RequestID, "device-key": string(encodedPubKey), } if cfg.proposedSerial != "" { headers["serial"] = cfg.proposedSerial } serialReq, err := asserts.SignWithoutAuthority(asserts.SerialRequestType, headers, cfg.body, privKey) if err != nil { return "", err } return string(asserts.Encode(serialReq)), nil }
func (m *InterfaceManager) doRemoveProfiles(task *state.Task, tomb *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() return m.removeProfilesForSnap(task, tomb, snapName) }
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 }
func (p4 patch4T) addRevertFlag(task *state.Task) error { var snapsup patch4SnapSetup err := p4.getMaybe(task, "snap-setup", &snapsup) switch err { case nil: snapsup.Flags |= patch4FlagRevert // save it back task.Set("snap-setup", &snapsup) return nil case state.ErrNoState: return nil default: return err } }
func (m *InterfaceManager) doSetupProfiles(task *state.Task, tomb *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 := snap.ReadInfo(ss.Name(), ss.SideInfo) if err != nil { return err } return m.setupProfilesForSnap(task, tomb, snapInfo, ss.DevModeAllowed()) }
func (m *SnapManager) doDownloadSnap(t *state.Task, tomb *tomb.Tomb) error { st := t.State() st.Lock() snapsup, err := TaskSnapSetup(t) st.Unlock() if err != nil { return err } meter := &TaskProgressAdapter{task: t} st.Lock() theStore := Store(st) user, err := userFromUserID(st, snapsup.UserID) st.Unlock() if err != nil { return err } targetFn := snapsup.MountFile() if snapsup.DownloadInfo == nil { var storeInfo *snap.Info // COMPATIBILITY - this task was created from an older version // of snapd that did not store the DownloadInfo in the state // yet. storeInfo, err = theStore.Snap(snapsup.Name(), snapsup.Channel, snapsup.DevModeAllowed(), snapsup.Revision(), user) if err != nil { return err } err = theStore.Download(tomb.Context(nil), snapsup.Name(), targetFn, &storeInfo.DownloadInfo, meter, user) snapsup.SideInfo = &storeInfo.SideInfo } else { err = theStore.Download(tomb.Context(nil), snapsup.Name(), targetFn, snapsup.DownloadInfo, meter, user) } if err != nil { return err } snapsup.SnapPath = targetFn // update the snap setup for the follow up tasks st.Lock() t.Set("snap-setup", snapsup) st.Unlock() return nil }
// 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) }
func (m *SnapManager) doDownloadSnap(t *state.Task, _ *tomb.Tomb) error { st := t.State() st.Lock() ss, err := TaskSnapSetup(t) st.Unlock() if err != nil { return err } meter := &TaskProgressAdapter{task: t} st.Lock() theStore := Store(st) user, err := userFromUserID(st, ss.UserID) st.Unlock() if err != nil { return err } var downloadedSnapFile string if ss.DownloadInfo == nil { // COMPATIBILITY - this task was created from an older version // of snapd that did not store the DownloadInfo in the state // yet. storeInfo, err := theStore.Snap(ss.Name(), ss.Channel, ss.DevModeAllowed(), ss.Revision(), user) if err != nil { return err } downloadedSnapFile, err = theStore.Download(ss.Name(), &storeInfo.DownloadInfo, meter, user) ss.SideInfo = &storeInfo.SideInfo } else { downloadedSnapFile, err = theStore.Download(ss.Name(), ss.DownloadInfo, meter, user) } if err != nil { return err } ss.SnapPath = downloadedSnapFile // update the snap setup for the follow up tasks st.Lock() t.Set("snap-setup", ss) st.Unlock() return nil }
func (m *SnapManager) doSetAutoAliases(t *state.Task, _ *tomb.Tomb) error { st := t.State() st.Lock() defer st.Unlock() snapsup, snapst, err := snapSetupAndState(t) 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) } allNew, allRetired, err := AutoAliasesDelta(st, []string{snapName}) if err != nil { return err } for _, alias := range allRetired[snapName] { delete(aliasStatuses, alias) } for _, alias := range allNew[snapName] { aliasApp := curInfo.Aliases[alias] if aliasApp == nil { // not a known alias anymore or yet, skip continue } // TODO: only mark/log conflict if this is an update instead of an install? err := checkAliasConflict(st, snapName, alias) if err != nil { return err } aliasStatuses[alias] = "auto" } setAliases(st, snapName, aliasStatuses) return nil }
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 }