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