github.com/rigado/snapd@v2.42.5-go-mod+incompatible/interfaces/core.go (about)

     1  // -*- Mode: Go; indent-tabs-mode: t -*-
     2  
     3  /*
     4   * Copyright (C) 2016 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 interfaces
    21  
    22  import (
    23  	"fmt"
    24  	"regexp"
    25  	"strings"
    26  
    27  	"github.com/snapcore/snapd/snap"
    28  )
    29  
    30  // Sanitize plug with a given snapd interface.
    31  func BeforePreparePlug(iface Interface, plugInfo *snap.PlugInfo) error {
    32  	if iface.Name() != plugInfo.Interface {
    33  		return fmt.Errorf("cannot sanitize plug %q (interface %q) using interface %q",
    34  			PlugRef{Snap: plugInfo.Snap.InstanceName(), Name: plugInfo.Name}, plugInfo.Interface, iface.Name())
    35  	}
    36  	var err error
    37  	if iface, ok := iface.(PlugSanitizer); ok {
    38  		err = iface.BeforePreparePlug(plugInfo)
    39  	}
    40  	return err
    41  }
    42  
    43  // PlugRef is a reference to a plug.
    44  type PlugRef struct {
    45  	Snap string `json:"snap"`
    46  	Name string `json:"plug"`
    47  }
    48  
    49  // String returns the "snap:plug" representation of a plug reference.
    50  func (ref PlugRef) String() string {
    51  	return fmt.Sprintf("%s:%s", ref.Snap, ref.Name)
    52  }
    53  
    54  // SortsBefore returns true when plug should be sorted before the other
    55  func (ref PlugRef) SortsBefore(other PlugRef) bool {
    56  	if ref.Snap != other.Snap {
    57  		return ref.Snap < other.Snap
    58  	}
    59  	return ref.Name < other.Name
    60  }
    61  
    62  // Sanitize slot with a given snapd interface.
    63  func BeforePrepareSlot(iface Interface, slotInfo *snap.SlotInfo) error {
    64  	if iface.Name() != slotInfo.Interface {
    65  		return fmt.Errorf("cannot sanitize slot %q (interface %q) using interface %q",
    66  			SlotRef{Snap: slotInfo.Snap.InstanceName(), Name: slotInfo.Name}, slotInfo.Interface, iface.Name())
    67  	}
    68  	var err error
    69  	if iface, ok := iface.(SlotSanitizer); ok {
    70  		err = iface.BeforePrepareSlot(slotInfo)
    71  	}
    72  	return err
    73  }
    74  
    75  // SlotRef is a reference to a slot.
    76  type SlotRef struct {
    77  	Snap string `json:"snap"`
    78  	Name string `json:"slot"`
    79  }
    80  
    81  // String returns the "snap:slot" representation of a slot reference.
    82  func (ref SlotRef) String() string {
    83  	return fmt.Sprintf("%s:%s", ref.Snap, ref.Name)
    84  }
    85  
    86  // SortsBefore returns true when slot should be sorted before the other
    87  func (ref SlotRef) SortsBefore(other SlotRef) bool {
    88  	if ref.Snap != other.Snap {
    89  		return ref.Snap < other.Snap
    90  	}
    91  	return ref.Name < other.Name
    92  }
    93  
    94  // Interfaces holds information about a list of plugs, slots and their connections.
    95  type Interfaces struct {
    96  	Plugs       []*snap.PlugInfo
    97  	Slots       []*snap.SlotInfo
    98  	Connections []*ConnRef
    99  }
   100  
   101  // Info holds information about a given interface and its instances.
   102  type Info struct {
   103  	Name    string
   104  	Summary string
   105  	DocURL  string
   106  	Plugs   []*snap.PlugInfo
   107  	Slots   []*snap.SlotInfo
   108  }
   109  
   110  // ConnRef holds information about plug and slot reference that form a particular connection.
   111  type ConnRef struct {
   112  	PlugRef PlugRef
   113  	SlotRef SlotRef
   114  }
   115  
   116  // NewConnRef creates a connection reference for given plug and slot
   117  func NewConnRef(plug *snap.PlugInfo, slot *snap.SlotInfo) *ConnRef {
   118  	return &ConnRef{
   119  		PlugRef: PlugRef{Snap: plug.Snap.InstanceName(), Name: plug.Name},
   120  		SlotRef: SlotRef{Snap: slot.Snap.InstanceName(), Name: slot.Name},
   121  	}
   122  }
   123  
   124  // ID returns a string identifying a given connection.
   125  func (conn *ConnRef) ID() string {
   126  	return fmt.Sprintf("%s:%s %s:%s", conn.PlugRef.Snap, conn.PlugRef.Name, conn.SlotRef.Snap, conn.SlotRef.Name)
   127  }
   128  
   129  // SortsBefore returns true when connection should be sorted before the other
   130  func (conn *ConnRef) SortsBefore(other *ConnRef) bool {
   131  	if conn.PlugRef != other.PlugRef {
   132  		return conn.PlugRef.SortsBefore(other.PlugRef)
   133  	}
   134  	return conn.SlotRef.SortsBefore(other.SlotRef)
   135  }
   136  
   137  // ParseConnRef parses an ID string
   138  func ParseConnRef(id string) (*ConnRef, error) {
   139  	var conn ConnRef
   140  	parts := strings.SplitN(id, " ", 2)
   141  	if len(parts) != 2 {
   142  		return nil, fmt.Errorf("malformed connection identifier: %q", id)
   143  	}
   144  	plugParts := strings.Split(parts[0], ":")
   145  	slotParts := strings.Split(parts[1], ":")
   146  	if len(plugParts) != 2 || len(slotParts) != 2 {
   147  		return nil, fmt.Errorf("malformed connection identifier: %q", id)
   148  	}
   149  	conn.PlugRef.Snap = plugParts[0]
   150  	conn.PlugRef.Name = plugParts[1]
   151  	conn.SlotRef.Snap = slotParts[0]
   152  	conn.SlotRef.Name = slotParts[1]
   153  	return &conn, nil
   154  }
   155  
   156  // Interface describes a group of interchangeable capabilities with common features.
   157  // Interfaces act as a contract between system builders, application developers
   158  // and end users.
   159  type Interface interface {
   160  	// Unique and public name of this interface.
   161  	Name() string
   162  
   163  	// AutoConnect returns whether plug and slot should be
   164  	// implicitly auto-connected assuming they will be an
   165  	// unambiguous connection candidate and declaration-based checks
   166  	// allow.
   167  	AutoConnect(plug *snap.PlugInfo, slot *snap.SlotInfo) bool
   168  }
   169  
   170  // PlugSanitizer can be implemented by Interfaces that have reasons to sanitize their plugs.
   171  type PlugSanitizer interface {
   172  	BeforePreparePlug(plug *snap.PlugInfo) error
   173  }
   174  
   175  // SlotSanitizer can be implemented by Interfaces that have reasons to sanitize their slots.
   176  type SlotSanitizer interface {
   177  	BeforePrepareSlot(slot *snap.SlotInfo) error
   178  }
   179  
   180  // StaticInfo describes various static-info of a given interface.
   181  //
   182  // The Summary must be a one-line string of length suitable for listing views.
   183  // The DocsURL can point to website (e.g. a forum thread) that goes into more
   184  // depth and documents the interface in detail.
   185  type StaticInfo struct {
   186  	Summary string `json:"summary,omitempty"`
   187  	DocURL  string `json:"doc-url,omitempty"`
   188  
   189  	// ImplicitOnCore controls if a slot is automatically added to core (non-classic) systems.
   190  	ImplicitOnCore bool `json:"implicit-on-core,omitempty"`
   191  	// ImplicitOnClassic controls if a slot is automatically added to classic systems.
   192  	ImplicitOnClassic bool `json:"implicit-on-classic,omitempty"`
   193  
   194  	// BaseDeclarationPlugs defines an optional extension to the base-declaration assertion relevant for this interface.
   195  	BaseDeclarationPlugs string
   196  	// BaseDeclarationSlots defines an optional extension to the base-declaration assertion relevant for this interface.
   197  	BaseDeclarationSlots string
   198  }
   199  
   200  // StaticInfoOf returns the static-info of the given interface.
   201  func StaticInfoOf(iface Interface) (si StaticInfo) {
   202  	type metaDataProvider interface {
   203  		StaticInfo() StaticInfo
   204  	}
   205  	if iface, ok := iface.(metaDataProvider); ok {
   206  		si = iface.StaticInfo()
   207  	}
   208  	return si
   209  }
   210  
   211  // Specification describes interactions between backends and interfaces.
   212  type Specification interface {
   213  	// AddPermanentSlot records side-effects of having a slot.
   214  	AddPermanentSlot(iface Interface, slot *snap.SlotInfo) error
   215  	// AddPermanentPlug records side-effects of having a plug.
   216  	AddPermanentPlug(iface Interface, plug *snap.PlugInfo) error
   217  	// AddConnectedSlot records side-effects of having a connected slot.
   218  	AddConnectedSlot(iface Interface, plug *ConnectedPlug, slot *ConnectedSlot) error
   219  	// AddConnectedPlug records side-effects of having a connected plug.
   220  	AddConnectedPlug(iface Interface, plug *ConnectedPlug, slot *ConnectedSlot) error
   221  }
   222  
   223  // SecuritySystem is a name of a security system.
   224  type SecuritySystem string
   225  
   226  const (
   227  	// SecurityAppArmor identifies the apparmor security system.
   228  	SecurityAppArmor SecuritySystem = "apparmor"
   229  	// SecuritySecComp identifies the seccomp security system.
   230  	SecuritySecComp SecuritySystem = "seccomp"
   231  	// SecurityDBus identifies the DBus security system.
   232  	SecurityDBus SecuritySystem = "dbus"
   233  	// SecurityUDev identifies the UDev security system.
   234  	SecurityUDev SecuritySystem = "udev"
   235  	// SecurityMount identifies the mount security system.
   236  	SecurityMount SecuritySystem = "mount"
   237  	// SecurityKMod identifies the kernel modules security system
   238  	SecurityKMod SecuritySystem = "kmod"
   239  	// SecuritySystemd identifies the systemd services security system
   240  	SecuritySystemd SecuritySystem = "systemd"
   241  )
   242  
   243  var isValidBusName = regexp.MustCompile(`^[a-zA-Z_-][a-zA-Z0-9_-]*(\.[a-zA-Z_-][a-zA-Z0-9_-]*)+$`).MatchString
   244  
   245  // ValidateDBusBusName checks if a string conforms to
   246  // https://dbus.freedesktop.org/doc/dbus-specification.html#message-protocol-names
   247  func ValidateDBusBusName(busName string) error {
   248  	if len(busName) == 0 {
   249  		return fmt.Errorf("DBus bus name must be set")
   250  	} else if len(busName) > 255 {
   251  		return fmt.Errorf("DBus bus name is too long (must be <= 255)")
   252  	}
   253  
   254  	if !isValidBusName(busName) {
   255  		return fmt.Errorf("invalid DBus bus name: %q", busName)
   256  	}
   257  	return nil
   258  }
   259  
   260  // UnknownPlugSlotError is an error reported when plug or slot cannot be found.
   261  type UnknownPlugSlotError struct {
   262  	Msg string
   263  }
   264  
   265  // Error returns the message associated with unknown plug or slot error.
   266  func (e *UnknownPlugSlotError) Error() string {
   267  	return e.Msg
   268  }