github.com/meulengracht/snapd@v0.0.0-20210719210640-8bde69bcc84e/overlord/ifacestate/helpers.go (about)

     1  // -*- Mode: Go; indent-tabs-mode: t -*-
     2  
     3  /*
     4   * Copyright (C) 2016-2020 Canonical Ltd
     5   *
     6   * This program is free software: you can redistribute it and/or modify
     7   * it under the terms of the GNU General Public License version 3 as
     8   * published by the Free Software Foundation.
     9   *
    10   * This program is distributed in the hope that it will be useful,
    11   * but WITHOUT ANY WARRANTY; without even the implied warranty of
    12   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    13   * GNU General Public License for more details.
    14   *
    15   * You should have received a copy of the GNU General Public License
    16   * along with this program.  If not, see <http://www.gnu.org/licenses/>.
    17   *
    18   */
    19  
    20  package ifacestate
    21  
    22  import (
    23  	"bytes"
    24  	"encoding/json"
    25  	"fmt"
    26  	"os"
    27  	"sort"
    28  	"strings"
    29  
    30  	"github.com/snapcore/snapd/asserts"
    31  	"github.com/snapcore/snapd/dirs"
    32  	"github.com/snapcore/snapd/interfaces"
    33  	"github.com/snapcore/snapd/interfaces/backends"
    34  	"github.com/snapcore/snapd/interfaces/builtin"
    35  	"github.com/snapcore/snapd/interfaces/policy"
    36  	"github.com/snapcore/snapd/interfaces/utils"
    37  	"github.com/snapcore/snapd/jsonutil"
    38  	"github.com/snapcore/snapd/logger"
    39  	"github.com/snapcore/snapd/overlord/assertstate"
    40  	"github.com/snapcore/snapd/overlord/snapstate"
    41  	"github.com/snapcore/snapd/overlord/state"
    42  	"github.com/snapcore/snapd/snap"
    43  	"github.com/snapcore/snapd/timings"
    44  )
    45  
    46  func (m *InterfaceManager) selectInterfaceMapper(snaps []*snap.Info) {
    47  	for _, snapInfo := range snaps {
    48  		if snapInfo.Type() == snap.TypeSnapd {
    49  			mapper = &CoreSnapdSystemMapper{}
    50  			break
    51  		}
    52  	}
    53  }
    54  
    55  func (m *InterfaceManager) addInterfaces(extra []interfaces.Interface) error {
    56  	for _, iface := range builtin.Interfaces() {
    57  		if err := m.repo.AddInterface(iface); err != nil {
    58  			return err
    59  		}
    60  	}
    61  	for _, iface := range extra {
    62  		if err := m.repo.AddInterface(iface); err != nil {
    63  			return err
    64  		}
    65  	}
    66  	return nil
    67  }
    68  
    69  func (m *InterfaceManager) addBackends(extra []interfaces.SecurityBackend) error {
    70  	opts := interfaces.SecurityBackendOptions{Preseed: m.preseed}
    71  	for _, backend := range backends.All {
    72  		if err := backend.Initialize(&opts); err != nil {
    73  			return err
    74  		}
    75  		if err := m.repo.AddBackend(backend); err != nil {
    76  			return err
    77  		}
    78  	}
    79  	for _, backend := range extra {
    80  		if err := backend.Initialize(&opts); err != nil {
    81  			return err
    82  		}
    83  		if err := m.repo.AddBackend(backend); err != nil {
    84  			return err
    85  		}
    86  	}
    87  	return nil
    88  }
    89  
    90  func (m *InterfaceManager) addSnaps(snaps []*snap.Info) error {
    91  	for _, snapInfo := range snaps {
    92  		if err := addImplicitSlots(m.state, snapInfo); err != nil {
    93  			return err
    94  		}
    95  		if err := m.repo.AddSnap(snapInfo); err != nil {
    96  			logger.Noticef("cannot add snap %q to interface repository: %s", snapInfo.InstanceName(), err)
    97  		}
    98  	}
    99  	return nil
   100  }
   101  
   102  func profilesNeedRegenerationImpl() bool {
   103  	mismatch, err := interfaces.SystemKeyMismatch()
   104  	if err != nil {
   105  		logger.Noticef("error trying to compare the snap system key: %v", err)
   106  		return true
   107  	}
   108  	return mismatch
   109  }
   110  
   111  var profilesNeedRegeneration = profilesNeedRegenerationImpl
   112  var writeSystemKey = interfaces.WriteSystemKey
   113  
   114  // regenerateAllSecurityProfiles will regenerate all security profiles.
   115  func (m *InterfaceManager) regenerateAllSecurityProfiles(tm timings.Measurer) error {
   116  	// Get all the security backends
   117  	securityBackends := m.repo.Backends()
   118  
   119  	// Get all the snap infos
   120  	snaps, err := snapsWithSecurityProfiles(m.state)
   121  	if err != nil {
   122  		return err
   123  	}
   124  
   125  	// Add implicit slots to all snaps
   126  	for _, snapInfo := range snaps {
   127  		if err := addImplicitSlots(m.state, snapInfo); err != nil {
   128  			return err
   129  		}
   130  	}
   131  
   132  	// The reason the system key is unlinked is to prevent snapd from believing
   133  	// that an old system key is valid and represents security setup
   134  	// established in the system. If snapd is reverted following a failed
   135  	// startup then system key may match the system key that used to be on disk
   136  	// but some of the system security may have been changed by the new snapd,
   137  	// the one that was reverted. Unlinking avoids such possibility, forcing
   138  	// old snapd to re-establish proper security view.
   139  	shouldWriteSystemKey := true
   140  	os.Remove(dirs.SnapSystemKeyFile)
   141  
   142  	confinementOpts := func(snapName string) interfaces.ConfinementOptions {
   143  		var snapst snapstate.SnapState
   144  		if err := snapstate.Get(m.state, snapName, &snapst); err != nil {
   145  			logger.Noticef("cannot get state of snap %q: %s", snapName, err)
   146  		}
   147  		return confinementOptions(snapst.Flags)
   148  	}
   149  
   150  	// For each backend:
   151  	for _, backend := range securityBackends {
   152  		if backend.Name() == "" {
   153  			continue // Test backends have no name, skip them to simplify testing.
   154  		}
   155  		if errors := interfaces.SetupMany(m.repo, backend, snaps, confinementOpts, tm); len(errors) > 0 {
   156  			logger.Noticef("cannot regenerate %s profiles", backend.Name())
   157  			for _, err := range errors {
   158  				logger.Noticef(err.Error())
   159  			}
   160  			shouldWriteSystemKey = false
   161  		}
   162  	}
   163  
   164  	if shouldWriteSystemKey {
   165  		if err := writeSystemKey(); err != nil {
   166  			logger.Noticef("cannot write system key: %v", err)
   167  		}
   168  	}
   169  	return nil
   170  }
   171  
   172  // renameCorePlugConnection renames one connection from "core-support" plug to
   173  // slot so that the plug name is "core-support-plug" while the slot is
   174  // unchanged. This matches a change introduced in 2.24, where the core snap no
   175  // longer has the "core-support" plug as that was clashing with the slot with
   176  // the same name.
   177  func (m *InterfaceManager) renameCorePlugConnection() error {
   178  	conns, err := getConns(m.state)
   179  	if err != nil {
   180  		return err
   181  	}
   182  	const oldPlugName = "core-support"
   183  	const newPlugName = "core-support-plug"
   184  	// old connection, note that slotRef is the same in both
   185  	slotRef := interfaces.SlotRef{Snap: "core", Name: oldPlugName}
   186  	oldPlugRef := interfaces.PlugRef{Snap: "core", Name: oldPlugName}
   187  	oldConnRef := interfaces.ConnRef{PlugRef: oldPlugRef, SlotRef: slotRef}
   188  	oldID := oldConnRef.ID()
   189  	// if the old connection is saved, replace it with the new connection
   190  	if cState, ok := conns[oldID]; ok {
   191  		newPlugRef := interfaces.PlugRef{Snap: "core", Name: newPlugName}
   192  		newConnRef := interfaces.ConnRef{PlugRef: newPlugRef, SlotRef: slotRef}
   193  		newID := newConnRef.ID()
   194  		delete(conns, oldID)
   195  		conns[newID] = cState
   196  		setConns(m.state, conns)
   197  	}
   198  	return nil
   199  }
   200  
   201  // removeStaleConnections removes stale connections left by some older versions of snapd.
   202  // Connection is considered stale if the snap on either end of the connection doesn't exist anymore.
   203  // XXX: this code should eventually go away.
   204  var removeStaleConnections = func(st *state.State) error {
   205  	conns, err := getConns(st)
   206  	if err != nil {
   207  		return err
   208  	}
   209  	var staleConns []string
   210  	for id := range conns {
   211  		connRef, err := interfaces.ParseConnRef(id)
   212  		if err != nil {
   213  			return err
   214  		}
   215  		var snapst snapstate.SnapState
   216  		if err := snapstate.Get(st, connRef.PlugRef.Snap, &snapst); err != nil {
   217  			if err != state.ErrNoState {
   218  				return err
   219  			}
   220  			staleConns = append(staleConns, id)
   221  			continue
   222  		}
   223  		if err := snapstate.Get(st, connRef.SlotRef.Snap, &snapst); err != nil {
   224  			if err != state.ErrNoState {
   225  				return err
   226  			}
   227  			staleConns = append(staleConns, id)
   228  			continue
   229  		}
   230  	}
   231  	if len(staleConns) > 0 {
   232  		for _, id := range staleConns {
   233  			delete(conns, id)
   234  		}
   235  		setConns(st, conns)
   236  		logger.Noticef("removed stale connections: %s", strings.Join(staleConns, ", "))
   237  	}
   238  	return nil
   239  }
   240  
   241  // reloadConnections reloads connections stored in the state in the repository.
   242  // Using non-empty snapName the operation can be scoped to connections
   243  // affecting a given snap.
   244  //
   245  // The return value is the list of affected snap names.
   246  func (m *InterfaceManager) reloadConnections(snapName string) ([]string, error) {
   247  	conns, err := getConns(m.state)
   248  	if err != nil {
   249  		return nil, err
   250  	}
   251  
   252  	connStateChanged := false
   253  	affected := make(map[string]bool)
   254  	for connId, connState := range conns {
   255  		// Skip entries that just mark a connection as undesired. Those don't
   256  		// carry attributes that can go stale. In the same spirit, skip
   257  		// information about hotplug connections that don't have the associated
   258  		// hotplug hardware.
   259  		if connState.Undesired || connState.HotplugGone {
   260  			continue
   261  		}
   262  		connRef, err := interfaces.ParseConnRef(connId)
   263  		if err != nil {
   264  			return nil, err
   265  		}
   266  		// Apply filtering, this allows us to reload only a subset of
   267  		// connections (and similarly, refresh the static attributes of only a
   268  		// subset of connections).
   269  		if snapName != "" && connRef.PlugRef.Snap != snapName && connRef.SlotRef.Snap != snapName {
   270  			continue
   271  		}
   272  
   273  		plugInfo := m.repo.Plug(connRef.PlugRef.Snap, connRef.PlugRef.Name)
   274  		slotInfo := m.repo.Slot(connRef.SlotRef.Snap, connRef.SlotRef.Name)
   275  
   276  		// The connection refers to a plug or slot that doesn't exist anymore, e.g. because of a refresh
   277  		// to a new snap revision that doesn't have the given plug/slot.
   278  		if plugInfo == nil || slotInfo == nil {
   279  			// automatic connection can simply be removed (it will be re-created automatically if needed)
   280  			// as long as it wasn't disconnected manually; note that undesired flag is taken care of at
   281  			// the beginning of the loop.
   282  			if connState.Auto && !connState.ByGadget && connState.Interface != "core-support" {
   283  				delete(conns, connId)
   284  				connStateChanged = true
   285  			}
   286  			// otherwise keep it and silently ignore, e.g. in case of a revert.
   287  			continue
   288  		}
   289  
   290  		var updateStaticAttrs bool
   291  		staticPlugAttrs := connState.StaticPlugAttrs
   292  		staticSlotAttrs := connState.StaticSlotAttrs
   293  
   294  		// XXX: Refresh the copy of the static connection attributes for "content" interface as long
   295  		// as its "content" attribute
   296  		// This is a partial and temporary solution to https://bugs.launchpad.net/snapd/+bug/1825883
   297  		if plugInfo.Interface == "content" {
   298  			var plugContent, slotContent string
   299  			plugInfo.Attr("content", &plugContent)
   300  			slotInfo.Attr("content", &slotContent)
   301  
   302  			if plugContent != "" && plugContent == slotContent {
   303  				staticPlugAttrs = utils.NormalizeInterfaceAttributes(plugInfo.Attrs).(map[string]interface{})
   304  				staticSlotAttrs = utils.NormalizeInterfaceAttributes(slotInfo.Attrs).(map[string]interface{})
   305  				updateStaticAttrs = true
   306  			} else {
   307  				logger.Noticef("cannot refresh static attributes of the connection %q", connId)
   308  			}
   309  		}
   310  
   311  		// Note: reloaded connections are not checked against policy again, and also we don't call BeforeConnect* methods on them.
   312  		if _, err := m.repo.Connect(connRef, staticPlugAttrs, connState.DynamicPlugAttrs, staticSlotAttrs, connState.DynamicSlotAttrs, nil); err != nil {
   313  			logger.Noticef("%s", err)
   314  		} else {
   315  			// If the connection succeeded update the connection state and keep
   316  			// track of the snaps that were affected.
   317  			affected[connRef.PlugRef.Snap] = true
   318  			affected[connRef.SlotRef.Snap] = true
   319  
   320  			if updateStaticAttrs {
   321  				connState.StaticPlugAttrs = staticPlugAttrs
   322  				connState.StaticSlotAttrs = staticSlotAttrs
   323  				connStateChanged = true
   324  			}
   325  		}
   326  	}
   327  	if connStateChanged {
   328  		setConns(m.state, conns)
   329  	}
   330  
   331  	result := make([]string, 0, len(affected))
   332  	for name := range affected {
   333  		result = append(result, name)
   334  	}
   335  	return result, nil
   336  }
   337  
   338  // removeConnections disconnects all connections of the snap in the repo. It should only be used if the snap
   339  // has no connections in the state. State must be locked by the caller.
   340  func (m *InterfaceManager) removeConnections(snapName string) error {
   341  	conns, err := getConns(m.state)
   342  	if err != nil {
   343  		return err
   344  	}
   345  	for id := range conns {
   346  		connRef, err := interfaces.ParseConnRef(id)
   347  		if err != nil {
   348  			return err
   349  		}
   350  		if connRef.PlugRef.Snap == snapName || connRef.SlotRef.Snap == snapName {
   351  			return fmt.Errorf("internal error: cannot remove connections of snap %s from the repository while its connections are present in the state", snapName)
   352  		}
   353  	}
   354  
   355  	repoConns, err := m.repo.Connections(snapName)
   356  	if err != nil {
   357  		return fmt.Errorf("internal error: %v", err)
   358  	}
   359  	for _, conn := range repoConns {
   360  		if err := m.repo.Disconnect(conn.PlugRef.Snap, conn.PlugRef.Name, conn.SlotRef.Snap, conn.SlotRef.Name); err != nil {
   361  			return fmt.Errorf("internal error: %v", err)
   362  		}
   363  	}
   364  	return nil
   365  }
   366  
   367  func (m *InterfaceManager) setupSecurityByBackend(task *state.Task, snaps []*snap.Info, opts []interfaces.ConfinementOptions, tm timings.Measurer) error {
   368  	if len(snaps) != len(opts) {
   369  		return fmt.Errorf("internal error: setupSecurityByBackend received an unexpected number of snaps (expected: %d, got %d)", len(opts), len(snaps))
   370  	}
   371  	confOpts := make(map[string]interfaces.ConfinementOptions, len(snaps))
   372  	for i, snapInfo := range snaps {
   373  		confOpts[snapInfo.InstanceName()] = opts[i]
   374  	}
   375  
   376  	st := task.State()
   377  	st.Unlock()
   378  	defer st.Lock()
   379  
   380  	// Setup all affected snaps, start with the most important security
   381  	// backend and run it for all snaps. See LP: 1802581
   382  	for _, backend := range m.repo.Backends() {
   383  		errs := interfaces.SetupMany(m.repo, backend, snaps, func(snapName string) interfaces.ConfinementOptions {
   384  			return confOpts[snapName]
   385  		}, tm)
   386  		if len(errs) > 0 {
   387  			// SetupMany processes all profiles and returns all encountered errors; report just the first one
   388  			return errs[0]
   389  		}
   390  	}
   391  
   392  	return nil
   393  }
   394  
   395  func (m *InterfaceManager) setupSnapSecurity(task *state.Task, snapInfo *snap.Info, opts interfaces.ConfinementOptions, tm timings.Measurer) error {
   396  	return m.setupSecurityByBackend(task, []*snap.Info{snapInfo}, []interfaces.ConfinementOptions{opts}, tm)
   397  }
   398  
   399  func (m *InterfaceManager) removeSnapSecurity(task *state.Task, instanceName string) error {
   400  	st := task.State()
   401  	for _, backend := range m.repo.Backends() {
   402  		st.Unlock()
   403  		err := backend.Remove(instanceName)
   404  		st.Lock()
   405  		if err != nil {
   406  			task.Errorf("cannot setup %s for snap %q: %s", backend.Name(), instanceName, err)
   407  			return err
   408  		}
   409  	}
   410  	return nil
   411  }
   412  
   413  func addHotplugSlot(st *state.State, repo *interfaces.Repository, stateSlots map[string]*HotplugSlotInfo, iface interfaces.Interface, slot *snap.SlotInfo) error {
   414  	if slot.HotplugKey == "" {
   415  		return fmt.Errorf("internal error: cannot store slot %q, not a hotplug slot", slot.Name)
   416  	}
   417  	if iface, ok := iface.(interfaces.SlotSanitizer); ok {
   418  		if err := iface.BeforePrepareSlot(slot); err != nil {
   419  			return fmt.Errorf("cannot sanitize hotplug slot %q for interface %s: %s", slot.Name, slot.Interface, err)
   420  		}
   421  	}
   422  
   423  	if err := repo.AddSlot(slot); err != nil {
   424  		return fmt.Errorf("cannot add hotplug slot %q for interface %s: %s", slot.Name, slot.Interface, err)
   425  	}
   426  
   427  	stateSlots[slot.Name] = &HotplugSlotInfo{
   428  		Name:        slot.Name,
   429  		Interface:   slot.Interface,
   430  		StaticAttrs: slot.Attrs,
   431  		HotplugKey:  slot.HotplugKey,
   432  		HotplugGone: false,
   433  	}
   434  	setHotplugSlots(st, stateSlots)
   435  	logger.Debugf("added hotplug slot %s:%s of interface %s, hotplug key %q", slot.Snap.InstanceName(), slot.Name, slot.Interface, slot.HotplugKey)
   436  	return nil
   437  }
   438  
   439  type connState struct {
   440  	Auto      bool   `json:"auto,omitempty"`
   441  	ByGadget  bool   `json:"by-gadget,omitempty"`
   442  	Interface string `json:"interface,omitempty"`
   443  	// Undesired tracks connections that were manually disconnected after being auto-connected,
   444  	// so that they are not automatically reconnected again in the future.
   445  	Undesired        bool                   `json:"undesired,omitempty"`
   446  	StaticPlugAttrs  map[string]interface{} `json:"plug-static,omitempty"`
   447  	DynamicPlugAttrs map[string]interface{} `json:"plug-dynamic,omitempty"`
   448  	StaticSlotAttrs  map[string]interface{} `json:"slot-static,omitempty"`
   449  	DynamicSlotAttrs map[string]interface{} `json:"slot-dynamic,omitempty"`
   450  	// Hotplug-related attributes: HotplugGone indicates a connection that
   451  	// disappeared because the device was removed, but may potentially be
   452  	// restored in the future if we see the device again. HotplugKey is the
   453  	// key of the associated device; it's empty for connections of regular
   454  	// slots.
   455  	HotplugGone bool            `json:"hotplug-gone,omitempty"`
   456  	HotplugKey  snap.HotplugKey `json:"hotplug-key,omitempty"`
   457  }
   458  
   459  type gadgetConnect struct {
   460  	st   *state.State
   461  	task *state.Task
   462  	repo *interfaces.Repository
   463  
   464  	instanceName string
   465  
   466  	deviceCtx snapstate.DeviceContext
   467  }
   468  
   469  func newGadgetConnect(s *state.State, task *state.Task, repo *interfaces.Repository, instanceName string, deviceCtx snapstate.DeviceContext) *gadgetConnect {
   470  	return &gadgetConnect{
   471  		st:           s,
   472  		task:         task,
   473  		repo:         repo,
   474  		instanceName: instanceName,
   475  		deviceCtx:    deviceCtx,
   476  	}
   477  }
   478  
   479  // addGadgetConnections adds to newconns any applicable connections
   480  // from the gadget connections stanza.
   481  // conflictError is called to handle checkAutoconnectConflicts errors.
   482  func (gc *gadgetConnect) addGadgetConnections(newconns map[string]*interfaces.ConnRef, conns map[string]*connState, conflictError func(*state.Retry, error) error) error {
   483  	var seeded bool
   484  	err := gc.st.Get("seeded", &seeded)
   485  	if err != nil && err != state.ErrNoState {
   486  		return err
   487  	}
   488  	// we apply gadget connections only during seeding or a remodeling
   489  	if seeded && !gc.deviceCtx.ForRemodeling() {
   490  		return nil
   491  	}
   492  
   493  	task := gc.task
   494  	snapName := gc.instanceName
   495  
   496  	var snapst snapstate.SnapState
   497  	if err := snapstate.Get(gc.st, snapName, &snapst); err != nil {
   498  		return err
   499  	}
   500  
   501  	snapInfo, err := snapst.CurrentInfo()
   502  	if err != nil {
   503  		return err
   504  	}
   505  	snapID := snapInfo.SnapID
   506  	if snapID == "" {
   507  		// not a snap-id identifiable snap, skip
   508  		return nil
   509  	}
   510  
   511  	gconns, err := snapstate.GadgetConnections(gc.st, gc.deviceCtx)
   512  	if err != nil {
   513  		if err == state.ErrNoState {
   514  			// no gadget yet, nothing to do
   515  			return nil
   516  		}
   517  		return err
   518  	}
   519  
   520  	// consider the gadget connect instructions
   521  	for _, gconn := range gconns {
   522  		var plugSnapName, slotSnapName string
   523  		if gconn.Plug.SnapID == snapID {
   524  			plugSnapName = snapName
   525  		}
   526  		if gconn.Slot.SnapID == snapID {
   527  			slotSnapName = snapName
   528  		}
   529  
   530  		if plugSnapName == "" && slotSnapName == "" {
   531  			// no match, nothing to do
   532  			continue
   533  		}
   534  
   535  		if plugSnapName == "" {
   536  			var err error
   537  			plugSnapName, err = resolveSnapIDToName(gc.st, gconn.Plug.SnapID)
   538  			if err != nil {
   539  				return err
   540  			}
   541  		}
   542  		plug := gc.repo.Plug(plugSnapName, gconn.Plug.Plug)
   543  		if plug == nil {
   544  			task.Logf("gadget connections: ignoring missing plug %s:%s", gconn.Plug.SnapID, gconn.Plug.Plug)
   545  			continue
   546  		}
   547  
   548  		if slotSnapName == "" {
   549  			var err error
   550  			slotSnapName, err = resolveSnapIDToName(gc.st, gconn.Slot.SnapID)
   551  			if err != nil {
   552  				return err
   553  			}
   554  		}
   555  		slot := gc.repo.Slot(slotSnapName, gconn.Slot.Slot)
   556  		if slot == nil {
   557  			task.Logf("gadget connections: ignoring missing slot %s:%s", gconn.Slot.SnapID, gconn.Slot.Slot)
   558  			continue
   559  		}
   560  
   561  		if err := addNewConnection(gc.st, task, newconns, conns, plug, slot, conflictError); err != nil {
   562  			return err
   563  		}
   564  	}
   565  
   566  	return nil
   567  }
   568  
   569  func addNewConnection(st *state.State, task *state.Task, newconns map[string]*interfaces.ConnRef, conns map[string]*connState, plug *snap.PlugInfo, slot *snap.SlotInfo, conflictError func(*state.Retry, error) error) error {
   570  	connRef := interfaces.NewConnRef(plug, slot)
   571  	key := connRef.ID()
   572  	if _, ok := conns[key]; ok {
   573  		// Suggested connection already exist (or has
   574  		// Undesired flag set) so don't clobber it.
   575  		// NOTE: we don't log anything here as this is
   576  		// a normal and common condition.
   577  		return nil
   578  	}
   579  	if _, ok := newconns[key]; ok {
   580  		return nil
   581  	}
   582  
   583  	if task.Kind() == "auto-connect" {
   584  		ignore, err := findSymmetricAutoconnectTask(st, plug.Snap.InstanceName(), slot.Snap.InstanceName(), task)
   585  		if err != nil {
   586  			return err
   587  		}
   588  
   589  		if ignore {
   590  			return nil
   591  		}
   592  	}
   593  
   594  	if err := checkAutoconnectConflicts(st, task, plug.Snap.InstanceName(), slot.Snap.InstanceName()); err != nil {
   595  		retry, _ := err.(*state.Retry)
   596  		return conflictError(retry, err)
   597  	}
   598  
   599  	newconns[key] = connRef
   600  	return nil
   601  }
   602  
   603  type autoConnectChecker struct {
   604  	st   *state.State
   605  	task *state.Task
   606  	repo *interfaces.Repository
   607  
   608  	deviceCtx snapstate.DeviceContext
   609  	cache     map[string]*asserts.SnapDeclaration
   610  	baseDecl  *asserts.BaseDeclaration
   611  }
   612  
   613  func newAutoConnectChecker(s *state.State, task *state.Task, repo *interfaces.Repository, deviceCtx snapstate.DeviceContext) (*autoConnectChecker, error) {
   614  	baseDecl, err := assertstate.BaseDeclaration(s)
   615  	if err != nil {
   616  		return nil, fmt.Errorf("internal error: cannot find base declaration: %v", err)
   617  	}
   618  	return &autoConnectChecker{
   619  		st:        s,
   620  		task:      task,
   621  		repo:      repo,
   622  		deviceCtx: deviceCtx,
   623  		cache:     make(map[string]*asserts.SnapDeclaration),
   624  		baseDecl:  baseDecl,
   625  	}, nil
   626  }
   627  
   628  func (c *autoConnectChecker) snapDeclaration(snapID string) (*asserts.SnapDeclaration, error) {
   629  	snapDecl := c.cache[snapID]
   630  	if snapDecl != nil {
   631  		return snapDecl, nil
   632  	}
   633  	snapDecl, err := assertstate.SnapDeclaration(c.st, snapID)
   634  	if err != nil {
   635  		return nil, err
   636  	}
   637  	c.cache[snapID] = snapDecl
   638  	return snapDecl, nil
   639  }
   640  
   641  func (c *autoConnectChecker) check(plug *interfaces.ConnectedPlug, slot *interfaces.ConnectedSlot) (bool, interfaces.SideArity, error) {
   642  	modelAs := c.deviceCtx.Model()
   643  
   644  	var storeAs *asserts.Store
   645  	if modelAs.Store() != "" {
   646  		var err error
   647  		storeAs, err = assertstate.Store(c.st, modelAs.Store())
   648  		if err != nil && !asserts.IsNotFound(err) {
   649  			return false, nil, err
   650  		}
   651  	}
   652  
   653  	var plugDecl *asserts.SnapDeclaration
   654  	if plug.Snap().SnapID != "" {
   655  		var err error
   656  		plugDecl, err = c.snapDeclaration(plug.Snap().SnapID)
   657  		if err != nil {
   658  			logger.Noticef("error: cannot find snap declaration for %q: %v", plug.Snap().InstanceName(), err)
   659  			return false, nil, nil
   660  		}
   661  	}
   662  
   663  	var slotDecl *asserts.SnapDeclaration
   664  	if slot.Snap().SnapID != "" {
   665  		var err error
   666  		slotDecl, err = c.snapDeclaration(slot.Snap().SnapID)
   667  		if err != nil {
   668  			logger.Noticef("error: cannot find snap declaration for %q: %v", slot.Snap().InstanceName(), err)
   669  			return false, nil, nil
   670  		}
   671  	}
   672  
   673  	// check the connection against the declarations' rules
   674  	ic := policy.ConnectCandidate{
   675  		Plug:                plug,
   676  		PlugSnapDeclaration: plugDecl,
   677  		Slot:                slot,
   678  		SlotSnapDeclaration: slotDecl,
   679  		BaseDeclaration:     c.baseDecl,
   680  		Model:               modelAs,
   681  		Store:               storeAs,
   682  	}
   683  
   684  	arity, err := ic.CheckAutoConnect()
   685  	if err == nil {
   686  		return true, arity, nil
   687  	}
   688  
   689  	return false, nil, nil
   690  }
   691  
   692  // filterUbuntuCoreSlots filters out any ubuntu-core slots,
   693  // if there are both ubuntu-core and core slots. This would occur
   694  // during a ubuntu-core -> core transition.
   695  func filterUbuntuCoreSlots(candidates []*snap.SlotInfo, arities []interfaces.SideArity) ([]*snap.SlotInfo, []interfaces.SideArity) {
   696  	hasCore := false
   697  	hasUbuntuCore := false
   698  	var withoutUbuntuCore []*snap.SlotInfo
   699  	var withoutUbuntuCoreArities []interfaces.SideArity
   700  	for i, candSlot := range candidates {
   701  		switch candSlot.Snap.InstanceName() {
   702  		case "ubuntu-core":
   703  			if !hasUbuntuCore {
   704  				hasUbuntuCore = true
   705  				withoutUbuntuCore = append(withoutUbuntuCore, candidates[:i]...)
   706  				withoutUbuntuCoreArities = append(withoutUbuntuCoreArities, arities[:i]...)
   707  			}
   708  		case "core":
   709  			hasCore = true
   710  			fallthrough
   711  		default:
   712  			if hasUbuntuCore {
   713  				withoutUbuntuCore = append(withoutUbuntuCore, candSlot)
   714  				withoutUbuntuCoreArities = append(withoutUbuntuCoreArities, arities[i])
   715  			}
   716  		}
   717  	}
   718  	if hasCore && hasUbuntuCore {
   719  		candidates = withoutUbuntuCore
   720  		arities = withoutUbuntuCoreArities
   721  	}
   722  	return candidates, arities
   723  }
   724  
   725  // addAutoConnections adds to newconns any applicable auto-connections
   726  // from the given plugs to corresponding candidates slots after
   727  // filtering them with optional filter and against preexisting
   728  // conns. cannotAutoConnectLog is called to build a log message in
   729  // case no applicable pair was found. conflictError is called
   730  // to handle checkAutoconnectConflicts errors.
   731  func (c *autoConnectChecker) addAutoConnections(newconns map[string]*interfaces.ConnRef, plugs []*snap.PlugInfo, filter func([]*snap.SlotInfo) []*snap.SlotInfo, conns map[string]*connState, cannotAutoConnectLog func(plug *snap.PlugInfo, candRefs []string) string, conflictError func(*state.Retry, error) error) error {
   732  	for _, plug := range plugs {
   733  		candSlots, arities := c.repo.AutoConnectCandidateSlots(plug.Snap.InstanceName(), plug.Name, c.check)
   734  
   735  		if len(candSlots) == 0 {
   736  			continue
   737  		}
   738  
   739  		// If we are in a core transition we may have both the
   740  		// old ubuntu-core snap and the new core snap
   741  		// providing the same interface. In that situation we
   742  		// want to ignore any candidates in ubuntu-core and
   743  		// simply go with those from the new core snap.
   744  		candSlots, arities = filterUbuntuCoreSlots(candSlots, arities)
   745  
   746  		applicable := candSlots
   747  		// candidate arity check
   748  		for _, arity := range arities {
   749  			if !arity.SlotsPerPlugAny() {
   750  				// ATM not any (*) => none or exactly one
   751  				if len(candSlots) != 1 {
   752  					applicable = nil
   753  				}
   754  				break
   755  			}
   756  		}
   757  
   758  		if filter != nil {
   759  			applicable = filter(applicable)
   760  		}
   761  
   762  		if len(applicable) == 0 {
   763  			crefs := make([]string, len(candSlots))
   764  			for i, candidate := range candSlots {
   765  				crefs[i] = candidate.String()
   766  			}
   767  			c.task.Logf(cannotAutoConnectLog(plug, crefs))
   768  			continue
   769  		}
   770  
   771  		for _, slot := range applicable {
   772  			if err := addNewConnection(c.st, c.task, newconns, conns, plug, slot, conflictError); err != nil {
   773  				return err
   774  			}
   775  		}
   776  	}
   777  
   778  	return nil
   779  }
   780  
   781  type connectChecker struct {
   782  	st        *state.State
   783  	deviceCtx snapstate.DeviceContext
   784  	baseDecl  *asserts.BaseDeclaration
   785  }
   786  
   787  func newConnectChecker(s *state.State, deviceCtx snapstate.DeviceContext) (*connectChecker, error) {
   788  	baseDecl, err := assertstate.BaseDeclaration(s)
   789  	if err != nil {
   790  		return nil, fmt.Errorf("internal error: cannot find base declaration: %v", err)
   791  	}
   792  	return &connectChecker{
   793  		st:        s,
   794  		deviceCtx: deviceCtx,
   795  		baseDecl:  baseDecl,
   796  	}, nil
   797  }
   798  
   799  func (c *connectChecker) check(plug *interfaces.ConnectedPlug, slot *interfaces.ConnectedSlot) (bool, error) {
   800  	modelAs := c.deviceCtx.Model()
   801  
   802  	var storeAs *asserts.Store
   803  	if modelAs.Store() != "" {
   804  		var err error
   805  		storeAs, err = assertstate.Store(c.st, modelAs.Store())
   806  		if err != nil && !asserts.IsNotFound(err) {
   807  			return false, err
   808  		}
   809  	}
   810  
   811  	var plugDecl *asserts.SnapDeclaration
   812  	if plug.Snap().SnapID != "" {
   813  		var err error
   814  		plugDecl, err = assertstate.SnapDeclaration(c.st, plug.Snap().SnapID)
   815  		if err != nil {
   816  			return false, fmt.Errorf("cannot find snap declaration for %q: %v", plug.Snap().InstanceName(), err)
   817  		}
   818  	}
   819  
   820  	var slotDecl *asserts.SnapDeclaration
   821  	if slot.Snap().SnapID != "" {
   822  		var err error
   823  		slotDecl, err = assertstate.SnapDeclaration(c.st, slot.Snap().SnapID)
   824  		if err != nil {
   825  			return false, fmt.Errorf("cannot find snap declaration for %q: %v", slot.Snap().InstanceName(), err)
   826  		}
   827  	}
   828  
   829  	// check the connection against the declarations' rules
   830  	ic := policy.ConnectCandidate{
   831  		Plug:                plug,
   832  		PlugSnapDeclaration: plugDecl,
   833  		Slot:                slot,
   834  		SlotSnapDeclaration: slotDecl,
   835  		BaseDeclaration:     c.baseDecl,
   836  		Model:               modelAs,
   837  		Store:               storeAs,
   838  	}
   839  
   840  	// if either of plug or slot snaps don't have a declaration it
   841  	// means they were installed with "dangerous", so the security
   842  	// check should be skipped at this point.
   843  	if plugDecl != nil && slotDecl != nil {
   844  		if err := ic.Check(); err != nil {
   845  			return false, err
   846  		}
   847  	}
   848  	return true, nil
   849  }
   850  
   851  func getPlugAndSlotRefs(task *state.Task) (interfaces.PlugRef, interfaces.SlotRef, error) {
   852  	var plugRef interfaces.PlugRef
   853  	var slotRef interfaces.SlotRef
   854  	if err := task.Get("plug", &plugRef); err != nil {
   855  		return plugRef, slotRef, err
   856  	}
   857  	if err := task.Get("slot", &slotRef); err != nil {
   858  		return plugRef, slotRef, err
   859  	}
   860  	return plugRef, slotRef, nil
   861  }
   862  
   863  // getConns returns information about connections from the state.
   864  //
   865  // Connections are transparently re-mapped according to remapIncomingConnRef
   866  func getConns(st *state.State) (conns map[string]*connState, err error) {
   867  	var raw *json.RawMessage
   868  	err = st.Get("conns", &raw)
   869  	if err != nil && err != state.ErrNoState {
   870  		return nil, fmt.Errorf("cannot obtain raw data about existing connections: %s", err)
   871  	}
   872  	if raw != nil {
   873  		err = jsonutil.DecodeWithNumber(bytes.NewReader(*raw), &conns)
   874  		if err != nil {
   875  			return nil, fmt.Errorf("cannot decode data about existing connections: %s", err)
   876  		}
   877  	}
   878  	if conns == nil {
   879  		conns = make(map[string]*connState)
   880  	}
   881  	remapped := make(map[string]*connState, len(conns))
   882  	for id, cstate := range conns {
   883  		cref, err := interfaces.ParseConnRef(id)
   884  		if err != nil {
   885  			return nil, err
   886  		}
   887  		cref.PlugRef.Snap = RemapSnapFromState(cref.PlugRef.Snap)
   888  		cref.SlotRef.Snap = RemapSnapFromState(cref.SlotRef.Snap)
   889  		cstate.StaticSlotAttrs = utils.NormalizeInterfaceAttributes(cstate.StaticSlotAttrs).(map[string]interface{})
   890  		cstate.DynamicSlotAttrs = utils.NormalizeInterfaceAttributes(cstate.DynamicSlotAttrs).(map[string]interface{})
   891  		cstate.StaticPlugAttrs = utils.NormalizeInterfaceAttributes(cstate.StaticPlugAttrs).(map[string]interface{})
   892  		cstate.DynamicPlugAttrs = utils.NormalizeInterfaceAttributes(cstate.DynamicPlugAttrs).(map[string]interface{})
   893  		remapped[cref.ID()] = cstate
   894  	}
   895  	return remapped, nil
   896  }
   897  
   898  // setConns sets information about connections in the state.
   899  //
   900  // Connections are transparently re-mapped according to remapOutgoingConnRef
   901  func setConns(st *state.State, conns map[string]*connState) {
   902  	remapped := make(map[string]*connState, len(conns))
   903  	for id, cstate := range conns {
   904  		cref, err := interfaces.ParseConnRef(id)
   905  		if err != nil {
   906  			// We cannot fail here
   907  			panic(err)
   908  		}
   909  		cref.PlugRef.Snap = RemapSnapToState(cref.PlugRef.Snap)
   910  		cref.SlotRef.Snap = RemapSnapToState(cref.SlotRef.Snap)
   911  		remapped[cref.ID()] = cstate
   912  	}
   913  	st.Set("conns", remapped)
   914  }
   915  
   916  // snapsWithSecurityProfiles returns all snaps that have active
   917  // security profiles: these are either snaps that are active, or about
   918  // to be active (pending link-snap) with a done setup-profiles
   919  func snapsWithSecurityProfiles(st *state.State) ([]*snap.Info, error) {
   920  	infos, err := snapstate.ActiveInfos(st)
   921  	if err != nil {
   922  		return nil, err
   923  	}
   924  	seen := make(map[string]bool, len(infos))
   925  	for _, info := range infos {
   926  		seen[info.InstanceName()] = true
   927  	}
   928  	for _, t := range st.Tasks() {
   929  		if t.Kind() != "link-snap" || t.Status().Ready() {
   930  			continue
   931  		}
   932  		snapsup, err := snapstate.TaskSnapSetup(t)
   933  		if err != nil {
   934  			return nil, err
   935  		}
   936  		instanceName := snapsup.InstanceName()
   937  		if seen[instanceName] {
   938  			continue
   939  		}
   940  
   941  		doneProfiles := false
   942  		for _, t1 := range t.WaitTasks() {
   943  			if t1.Kind() == "setup-profiles" && t1.Status() == state.DoneStatus {
   944  				snapsup1, err := snapstate.TaskSnapSetup(t1)
   945  				if err != nil {
   946  					return nil, err
   947  				}
   948  				if snapsup1.InstanceName() == instanceName {
   949  					doneProfiles = true
   950  					break
   951  				}
   952  			}
   953  		}
   954  		if !doneProfiles {
   955  			continue
   956  		}
   957  
   958  		seen[instanceName] = true
   959  		snapInfo, err := snap.ReadInfo(instanceName, snapsup.SideInfo)
   960  		if err != nil {
   961  			logger.Noticef("cannot retrieve info for snap %q: %s", instanceName, err)
   962  			continue
   963  		}
   964  		infos = append(infos, snapInfo)
   965  	}
   966  
   967  	return infos, nil
   968  }
   969  
   970  func resolveSnapIDToName(st *state.State, snapID string) (name string, err error) {
   971  	if snapID == "system" {
   972  		// Resolve the system nickname to a concrete snap.
   973  		return mapper.RemapSnapFromRequest(snapID), nil
   974  	}
   975  	decl, err := assertstate.SnapDeclaration(st, snapID)
   976  	if asserts.IsNotFound(err) {
   977  		return "", nil
   978  	}
   979  	if err != nil {
   980  		return "", err
   981  	}
   982  	return decl.SnapName(), nil
   983  }
   984  
   985  // SnapMapper offers APIs for re-mapping snap names in interfaces and the
   986  // configuration system. The mapper is designed to apply transformations around
   987  // the edges of snapd (state interactions and API interactions) to offer one
   988  // view on the inside of snapd and another view on the outside.
   989  type SnapMapper interface {
   990  	// re-map functions for loading and saving objects in the state.
   991  	RemapSnapFromState(snapName string) string
   992  	RemapSnapToState(snapName string) string
   993  	// RamapSnapFromRequest can replace snap names in API requests.
   994  	// There is no corresponding mapping function for API responses anymore.
   995  	// The API responses always reflect the real system state.
   996  	RemapSnapFromRequest(snapName string) string
   997  	// Returns actual name of the system snap.
   998  	SystemSnapName() string
   999  }
  1000  
  1001  // IdentityMapper implements SnapMapper and performs no transformations at all.
  1002  type IdentityMapper struct{}
  1003  
  1004  // RemapSnapFromState doesn't change the snap name in any way.
  1005  func (m *IdentityMapper) RemapSnapFromState(snapName string) string {
  1006  	return snapName
  1007  }
  1008  
  1009  // RemapSnapToState doesn't change the snap name in any way.
  1010  func (m *IdentityMapper) RemapSnapToState(snapName string) string {
  1011  	return snapName
  1012  }
  1013  
  1014  // RemapSnapFromRequest  doesn't change the snap name in any way.
  1015  func (m *IdentityMapper) RemapSnapFromRequest(snapName string) string {
  1016  	return snapName
  1017  }
  1018  
  1019  // CoreCoreSystemMapper implements SnapMapper and makes implicit slots
  1020  // appear to be on "core" in the state and in memory but as "system" in the API.
  1021  //
  1022  // NOTE: This mapper can be used to prepare, as an intermediate step, for the
  1023  // transition to "snapd" mapper. Using it the state and API layer will look
  1024  // exactly the same as with the "snapd" mapper. This can be used to make any
  1025  // necessary adjustments the test suite.
  1026  type CoreCoreSystemMapper struct {
  1027  	IdentityMapper // Embedding the identity mapper allows us to cut on boilerplate.
  1028  }
  1029  
  1030  // RemapSnapFromRequest renames the "system" snap to the "core" snap.
  1031  //
  1032  // This allows us to accept connection and disconnection requests that
  1033  // explicitly refer to "core" or using the "system" nickname.
  1034  func (m *CoreCoreSystemMapper) RemapSnapFromRequest(snapName string) string {
  1035  	if snapName == "system" {
  1036  		return m.SystemSnapName()
  1037  	}
  1038  	return snapName
  1039  }
  1040  
  1041  // SystemSnapName returns actual name of the system snap.
  1042  func (m *CoreCoreSystemMapper) SystemSnapName() string {
  1043  	return "core"
  1044  }
  1045  
  1046  // CoreSnapdSystemMapper implements SnapMapper and makes implicit slots
  1047  // appear to be on "core" in the state and on "system" in the API while they
  1048  // are on "snapd" in memory.
  1049  type CoreSnapdSystemMapper struct {
  1050  	IdentityMapper // Embedding the identity mapper allows us to cut on boilerplate.
  1051  }
  1052  
  1053  // RemapSnapFromState renames the "core" snap to the "snapd" snap.
  1054  //
  1055  // This allows modern snapd to load an old state that remembers connections
  1056  // between slots on the "core" snap and other snaps. In memory we are actually
  1057  // using "snapd" snap for hosting those slots and this lets us stay compatible.
  1058  func (m *CoreSnapdSystemMapper) RemapSnapFromState(snapName string) string {
  1059  	if snapName == "core" {
  1060  		return m.SystemSnapName()
  1061  	}
  1062  	return snapName
  1063  }
  1064  
  1065  // RemapSnapToState renames the "snapd" snap to the "core" snap.
  1066  //
  1067  // This allows the state to stay backwards compatible as all the connections
  1068  // seem to refer to the "core" snap, as in pre core{16,18} days where there was
  1069  // only one core snap.
  1070  func (m *CoreSnapdSystemMapper) RemapSnapToState(snapName string) string {
  1071  	if snapName == m.SystemSnapName() {
  1072  		return "core"
  1073  	}
  1074  	return snapName
  1075  }
  1076  
  1077  // RemapSnapFromRequest renames the "core" or "system" snaps to the "snapd" snap.
  1078  //
  1079  // This allows us to accept connection and disconnection requests that
  1080  // explicitly refer to "core" or "system" even though we really want them to
  1081  // refer to "snapd". Note that this is not fully symmetric with
  1082  // RemapSnapToResponse as we explicitly always talk about "system" snap,
  1083  // even if the request used "core".
  1084  func (m *CoreSnapdSystemMapper) RemapSnapFromRequest(snapName string) string {
  1085  	if snapName == "system" || snapName == "core" {
  1086  		return m.SystemSnapName()
  1087  	}
  1088  	return snapName
  1089  }
  1090  
  1091  // SystemSnapName returns actual name of the system snap.
  1092  func (m *CoreSnapdSystemMapper) SystemSnapName() string {
  1093  	return "snapd"
  1094  }
  1095  
  1096  // mapper contains the currently active snap mapper.
  1097  var mapper SnapMapper = &CoreCoreSystemMapper{}
  1098  
  1099  // MockSnapMapper mocks the currently used snap mapper.
  1100  func MockSnapMapper(new SnapMapper) (restore func()) {
  1101  	old := mapper
  1102  	mapper = new
  1103  	return func() { mapper = old }
  1104  }
  1105  
  1106  // RemapSnapFromState renames a snap when loaded from state according to the current mapper.
  1107  func RemapSnapFromState(snapName string) string {
  1108  	return mapper.RemapSnapFromState(snapName)
  1109  }
  1110  
  1111  // RemapSnapToState renames a snap when saving to state according to the current mapper.
  1112  func RemapSnapToState(snapName string) string {
  1113  	return mapper.RemapSnapToState(snapName)
  1114  }
  1115  
  1116  // RemapSnapFromRequest renames a snap as received from an API request according to the current mapper.
  1117  func RemapSnapFromRequest(snapName string) string {
  1118  	return mapper.RemapSnapFromRequest(snapName)
  1119  }
  1120  
  1121  // SystemSnapName returns actual name of the system snap.
  1122  func SystemSnapName() string {
  1123  	return mapper.SystemSnapName()
  1124  }
  1125  
  1126  // systemSnapInfo returns current info for system snap.
  1127  func systemSnapInfo(st *state.State) (*snap.Info, error) {
  1128  	return snapstate.CurrentInfo(st, SystemSnapName())
  1129  }
  1130  
  1131  func connectDisconnectAffectedSnaps(t *state.Task) ([]string, error) {
  1132  	plugRef, slotRef, err := getPlugAndSlotRefs(t)
  1133  	if err != nil {
  1134  		return nil, fmt.Errorf("internal error: cannot obtain plug/slot data from task: %s", t.Summary())
  1135  	}
  1136  	return []string{plugRef.Snap, slotRef.Snap}, nil
  1137  }
  1138  
  1139  func checkSystemSnapIsPresent(st *state.State) bool {
  1140  	st.Lock()
  1141  	defer st.Unlock()
  1142  	_, err := systemSnapInfo(st)
  1143  	return err == nil
  1144  }
  1145  
  1146  func setHotplugAttrs(task *state.Task, ifaceName string, hotplugKey snap.HotplugKey) {
  1147  	task.Set("interface", ifaceName)
  1148  	task.Set("hotplug-key", hotplugKey)
  1149  }
  1150  
  1151  func getHotplugAttrs(task *state.Task) (ifaceName string, hotplugKey snap.HotplugKey, err error) {
  1152  	if err = task.Get("interface", &ifaceName); err != nil {
  1153  		return "", "", fmt.Errorf("internal error: cannot get interface name from hotplug task: %s", err)
  1154  	}
  1155  	if err = task.Get("hotplug-key", &hotplugKey); err != nil {
  1156  		return "", "", fmt.Errorf("internal error: cannot get hotplug key from hotplug task: %s", err)
  1157  	}
  1158  	return ifaceName, hotplugKey, err
  1159  }
  1160  
  1161  func allocHotplugSeq(st *state.State) (int, error) {
  1162  	var seq int
  1163  	if err := st.Get("hotplug-seq", &seq); err != nil && err != state.ErrNoState {
  1164  		return 0, fmt.Errorf("internal error: cannot allocate hotplug sequence number: %s", err)
  1165  	}
  1166  	seq++
  1167  	st.Set("hotplug-seq", seq)
  1168  	return seq, nil
  1169  }
  1170  
  1171  func isHotplugChange(chg *state.Change) bool {
  1172  	return strings.HasPrefix(chg.Kind(), "hotplug-")
  1173  }
  1174  
  1175  func getHotplugChangeAttrs(chg *state.Change) (seq int, hotplugKey snap.HotplugKey, err error) {
  1176  	if err = chg.Get("hotplug-key", &hotplugKey); err != nil {
  1177  		return 0, "", fmt.Errorf("internal error: hotplug-key not set on change %q", chg.Kind())
  1178  	}
  1179  	if err = chg.Get("hotplug-seq", &seq); err != nil {
  1180  		return 0, "", fmt.Errorf("internal error: hotplug-seq not set on change %q", chg.Kind())
  1181  	}
  1182  	return seq, hotplugKey, nil
  1183  }
  1184  
  1185  func setHotplugChangeAttrs(chg *state.Change, seq int, hotplugKey snap.HotplugKey) {
  1186  	chg.Set("hotplug-seq", seq)
  1187  	chg.Set("hotplug-key", hotplugKey)
  1188  }
  1189  
  1190  // addHotplugSeqWaitTask sets mandatory hotplug attributes on the hotplug change, adds "hotplug-seq-wait" task
  1191  // and makes all existing tasks of the change wait for it.
  1192  func addHotplugSeqWaitTask(hotplugChange *state.Change, hotplugKey snap.HotplugKey, hotplugSeq int) {
  1193  	st := hotplugChange.State()
  1194  	setHotplugChangeAttrs(hotplugChange, hotplugSeq, hotplugKey)
  1195  	seqControl := st.NewTask("hotplug-seq-wait", fmt.Sprintf("Serialize hotplug change for hotplug key %q", hotplugKey))
  1196  	tss := state.NewTaskSet(hotplugChange.Tasks()...)
  1197  	tss.WaitFor(seqControl)
  1198  	hotplugChange.AddTask(seqControl)
  1199  }
  1200  
  1201  type HotplugSlotInfo struct {
  1202  	Name        string                 `json:"name"`
  1203  	Interface   string                 `json:"interface"`
  1204  	StaticAttrs map[string]interface{} `json:"static-attrs,omitempty"`
  1205  	HotplugKey  snap.HotplugKey        `json:"hotplug-key"`
  1206  
  1207  	// device was unplugged but has connections, so slot is remembered
  1208  	HotplugGone bool `json:"hotplug-gone"`
  1209  }
  1210  
  1211  func getHotplugSlots(st *state.State) (map[string]*HotplugSlotInfo, error) {
  1212  	var slots map[string]*HotplugSlotInfo
  1213  	err := st.Get("hotplug-slots", &slots)
  1214  	if err != nil {
  1215  		if err != state.ErrNoState {
  1216  			return nil, err
  1217  		}
  1218  		slots = make(map[string]*HotplugSlotInfo)
  1219  	}
  1220  	return slots, nil
  1221  }
  1222  
  1223  func setHotplugSlots(st *state.State, slots map[string]*HotplugSlotInfo) {
  1224  	st.Set("hotplug-slots", slots)
  1225  }
  1226  
  1227  func findHotplugSlot(stateSlots map[string]*HotplugSlotInfo, ifaceName string, hotplugKey snap.HotplugKey) *HotplugSlotInfo {
  1228  	for _, slot := range stateSlots {
  1229  		if slot.HotplugKey == hotplugKey && slot.Interface == ifaceName {
  1230  			return slot
  1231  		}
  1232  	}
  1233  	return nil
  1234  }
  1235  
  1236  func findConnsForHotplugKey(conns map[string]*connState, ifaceName string, hotplugKey snap.HotplugKey) []string {
  1237  	var connsForDevice []string
  1238  	for id, connSt := range conns {
  1239  		if connSt.Interface != ifaceName || connSt.HotplugKey != hotplugKey {
  1240  			continue
  1241  		}
  1242  		connsForDevice = append(connsForDevice, id)
  1243  	}
  1244  	sort.Strings(connsForDevice)
  1245  	return connsForDevice
  1246  }
  1247  
  1248  func (m *InterfaceManager) discardSecurityProfilesLate(name string, rev snap.Revision, typ snap.Type) error {
  1249  	for _, backend := range m.repo.Backends() {
  1250  		lateDiscardBackend, ok := backend.(interfaces.SecurityBackendDiscardingLate)
  1251  		if !ok {
  1252  			continue
  1253  		}
  1254  		if err := lateDiscardBackend.RemoveLate(name, rev, typ); err != nil {
  1255  			return err
  1256  		}
  1257  	}
  1258  	return nil
  1259  }