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 }