Permalink
Cannot retrieve contributors at this time
Fetching contributors…
| // -*- Mode: Go; indent-tabs-mode: t -*- | |
| /* | |
| * Copyright (C) 2016 Canonical Ltd | |
| * | |
| * This program is free software: you can redistribute it and/or modify | |
| * it under the terms of the GNU General Public License version 3 as | |
| * published by the Free Software Foundation. | |
| * | |
| * This program is distributed in the hope that it will be useful, | |
| * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
| * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
| * GNU General Public License for more details. | |
| * | |
| * You should have received a copy of the GNU General Public License | |
| * along with this program. If not, see <http://www.gnu.org/licenses/>. | |
| * | |
| */ | |
| package interfaces | |
| import ( | |
| "fmt" | |
| "regexp" | |
| "strings" | |
| "github.com/snapcore/snapd/snap" | |
| ) | |
| // Plug represents the potential of a given snap to connect to a slot. | |
| type Plug struct { | |
| *snap.PlugInfo | |
| Connections []SlotRef `json:"connections,omitempty"` | |
| } | |
| // Ref returns reference to a plug | |
| func (plug *Plug) Ref() PlugRef { | |
| return PlugRef{Snap: plug.Snap.Name(), Name: plug.Name} | |
| } | |
| // Sanitize plug with a given snapd interface. | |
| func BeforePreparePlug(iface Interface, plugInfo *snap.PlugInfo) error { | |
| if iface.Name() != plugInfo.Interface { | |
| return fmt.Errorf("cannot sanitize plug %q (interface %q) using interface %q", | |
| PlugRef{Snap: plugInfo.Snap.Name(), Name: plugInfo.Name}, plugInfo.Interface, iface.Name()) | |
| } | |
| var err error | |
| if iface, ok := iface.(PlugSanitizer); ok { | |
| err = iface.BeforePreparePlug(plugInfo) | |
| } | |
| return err | |
| } | |
| // PlugRef is a reference to a plug. | |
| type PlugRef struct { | |
| Snap string `json:"snap"` | |
| Name string `json:"plug"` | |
| } | |
| // String returns the "snap:plug" representation of a plug reference. | |
| func (ref PlugRef) String() string { | |
| return fmt.Sprintf("%s:%s", ref.Snap, ref.Name) | |
| } | |
| // Slot represents a capacity offered by a snap. | |
| type Slot struct { | |
| *snap.SlotInfo | |
| Connections []PlugRef `json:"connections,omitempty"` | |
| } | |
| // Ref returns reference to a slot | |
| func (slot *Slot) Ref() SlotRef { | |
| return SlotRef{Snap: slot.Snap.Name(), Name: slot.Name} | |
| } | |
| // Sanitize slot with a given snapd interface. | |
| func BeforePrepareSlot(iface Interface, slotInfo *snap.SlotInfo) error { | |
| if iface.Name() != slotInfo.Interface { | |
| return fmt.Errorf("cannot sanitize slot %q (interface %q) using interface %q", | |
| SlotRef{Snap: slotInfo.Snap.Name(), Name: slotInfo.Name}, slotInfo.Interface, iface.Name()) | |
| } | |
| var err error | |
| if iface, ok := iface.(SlotSanitizer); ok { | |
| err = iface.BeforePrepareSlot(slotInfo) | |
| } | |
| return err | |
| } | |
| // SlotRef is a reference to a slot. | |
| type SlotRef struct { | |
| Snap string `json:"snap"` | |
| Name string `json:"slot"` | |
| } | |
| // String returns the "snap:slot" representation of a slot reference. | |
| func (ref SlotRef) String() string { | |
| return fmt.Sprintf("%s:%s", ref.Snap, ref.Name) | |
| } | |
| // Interfaces holds information about a list of plugs, slots and their connections. | |
| type Interfaces struct { | |
| Plugs []*snap.PlugInfo | |
| Slots []*snap.SlotInfo | |
| Connections []*ConnRef | |
| } | |
| // Info holds information about a given interface and its instances. | |
| type Info struct { | |
| Name string | |
| Summary string | |
| DocURL string | |
| Plugs []*snap.PlugInfo | |
| Slots []*snap.SlotInfo | |
| } | |
| // ConnRef holds information about plug and slot reference that form a particular connection. | |
| type ConnRef struct { | |
| PlugRef PlugRef | |
| SlotRef SlotRef | |
| } | |
| // NewConnRef creates a connection reference for given plug and slot | |
| func NewConnRef(plug *snap.PlugInfo, slot *snap.SlotInfo) *ConnRef { | |
| return &ConnRef{ | |
| PlugRef: PlugRef{Snap: plug.Snap.Name(), Name: plug.Name}, | |
| SlotRef: SlotRef{Snap: slot.Snap.Name(), Name: slot.Name}, | |
| } | |
| } | |
| // ID returns a string identifying a given connection. | |
| func (conn *ConnRef) ID() string { | |
| return fmt.Sprintf("%s:%s %s:%s", conn.PlugRef.Snap, conn.PlugRef.Name, conn.SlotRef.Snap, conn.SlotRef.Name) | |
| } | |
| // ParseConnRef parses an ID string | |
| func ParseConnRef(id string) (ConnRef, error) { | |
| var conn ConnRef | |
| parts := strings.SplitN(id, " ", 2) | |
| if len(parts) != 2 { | |
| return conn, fmt.Errorf("malformed connection identifier: %q", id) | |
| } | |
| plugParts := strings.Split(parts[0], ":") | |
| slotParts := strings.Split(parts[1], ":") | |
| if len(plugParts) != 2 || len(slotParts) != 2 { | |
| return conn, fmt.Errorf("malformed connection identifier: %q", id) | |
| } | |
| conn.PlugRef.Snap = plugParts[0] | |
| conn.PlugRef.Name = plugParts[1] | |
| conn.SlotRef.Snap = slotParts[0] | |
| conn.SlotRef.Name = slotParts[1] | |
| return conn, nil | |
| } | |
| // Interface describes a group of interchangeable capabilities with common features. | |
| // Interfaces act as a contract between system builders, application developers | |
| // and end users. | |
| type Interface interface { | |
| // Unique and public name of this interface. | |
| Name() string | |
| // AutoConnect returns whether plug and slot should be | |
| // implicitly auto-connected assuming they will be an | |
| // unambiguous connection candidate and declaration-based checks | |
| // allow. | |
| AutoConnect(plug *Plug, slot *Slot) bool | |
| } | |
| // PlugSanitizer can be implemented by Interfaces that have reasons to sanitize their plugs. | |
| type PlugSanitizer interface { | |
| BeforePreparePlug(plug *snap.PlugInfo) error | |
| } | |
| // SlotSanitizer can be implemented by Interfaces that have reasons to sanitize their slots. | |
| type SlotSanitizer interface { | |
| BeforePrepareSlot(slot *snap.SlotInfo) error | |
| } | |
| // StaticInfo describes various static-info of a given interface. | |
| // | |
| // The Summary must be a one-line string of length suitable for listing views. | |
| // The DocsURL can point to website (e.g. a forum thread) that goes into more | |
| // depth and documents the interface in detail. | |
| type StaticInfo struct { | |
| Summary string `json:"summary,omitempty"` | |
| DocURL string `json:"doc-url,omitempty"` | |
| // ImplicitOnCore controls if a slot is automatically added to core (non-classic) systems. | |
| ImplicitOnCore bool `json:"implicit-on-core,omitempty"` | |
| // ImplicitOnClassic controls if a slot is automatically added to classic systems. | |
| ImplicitOnClassic bool `json:"implicit-on-classic,omitempty"` | |
| // BaseDeclarationPlugs defines an optional extension to the base-declaration assertion relevant for this interface. | |
| BaseDeclarationPlugs string | |
| // BaseDeclarationSlots defines an optional extension to the base-declaration assertion relevant for this interface. | |
| BaseDeclarationSlots string | |
| } | |
| // StaticInfoOf returns the static-info of the given interface. | |
| func StaticInfoOf(iface Interface) (si StaticInfo) { | |
| type metaDataProvider interface { | |
| StaticInfo() StaticInfo | |
| } | |
| if iface, ok := iface.(metaDataProvider); ok { | |
| si = iface.StaticInfo() | |
| } | |
| return si | |
| } | |
| // Specification describes interactions between backends and interfaces. | |
| type Specification interface { | |
| // AddPermanentSlot records side-effects of having a slot. | |
| AddPermanentSlot(iface Interface, slot *snap.SlotInfo) error | |
| // AddPermanentPlug records side-effects of having a plug. | |
| AddPermanentPlug(iface Interface, plug *snap.PlugInfo) error | |
| // AddConnectedSlot records side-effects of having a connected slot. | |
| AddConnectedSlot(iface Interface, plug *ConnectedPlug, slot *ConnectedSlot) error | |
| // AddConnectedPlug records side-effects of having a connected plug. | |
| AddConnectedPlug(iface Interface, plug *ConnectedPlug, slot *ConnectedSlot) error | |
| } | |
| // SecuritySystem is a name of a security system. | |
| type SecuritySystem string | |
| const ( | |
| // SecurityAppArmor identifies the apparmor security system. | |
| SecurityAppArmor SecuritySystem = "apparmor" | |
| // SecuritySecComp identifies the seccomp security system. | |
| SecuritySecComp SecuritySystem = "seccomp" | |
| // SecurityDBus identifies the DBus security system. | |
| SecurityDBus SecuritySystem = "dbus" | |
| // SecurityUDev identifies the UDev security system. | |
| SecurityUDev SecuritySystem = "udev" | |
| // SecurityMount identifies the mount security system. | |
| SecurityMount SecuritySystem = "mount" | |
| // SecurityKMod identifies the kernel modules security system | |
| SecurityKMod SecuritySystem = "kmod" | |
| // SecuritySystemd identifies the systemd services security system | |
| SecuritySystemd SecuritySystem = "systemd" | |
| ) | |
| // Regular expression describing correct identifiers. | |
| var validName = regexp.MustCompile("^[a-z](?:-?[a-z0-9])*$") | |
| // ValidateName checks if a string can be used as a plug or slot name. | |
| func ValidateName(name string) error { | |
| valid := validName.MatchString(name) | |
| if !valid { | |
| return fmt.Errorf("invalid interface name: %q", name) | |
| } | |
| return nil | |
| } | |
| // ValidateDBusBusName checks if a string conforms to | |
| // https://dbus.freedesktop.org/doc/dbus-specification.html#message-protocol-names | |
| func ValidateDBusBusName(busName string) error { | |
| if len(busName) == 0 { | |
| return fmt.Errorf("DBus bus name must be set") | |
| } else if len(busName) > 255 { | |
| return fmt.Errorf("DBus bus name is too long (must be <= 255)") | |
| } | |
| validBusName := regexp.MustCompile("^[a-zA-Z_-][a-zA-Z0-9_-]*(\\.[a-zA-Z_-][a-zA-Z0-9_-]*)+$") | |
| if !validBusName.MatchString(busName) { | |
| return fmt.Errorf("invalid DBus bus name: %q", busName) | |
| } | |
| return nil | |
| } |