github.com/rigado/snapd@v2.42.5-go-mod+incompatible/interfaces/builtin/gpio.go (about) 1 // -*- Mode: Go; indent-tabs-mode: t -*- 2 3 /* 4 * Copyright (C) 2016-2017 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 builtin 21 22 import ( 23 "fmt" 24 25 "github.com/snapcore/snapd/interfaces" 26 "github.com/snapcore/snapd/interfaces/apparmor" 27 "github.com/snapcore/snapd/interfaces/systemd" 28 "github.com/snapcore/snapd/snap" 29 ) 30 31 const gpioSummary = `allows access to specifc GPIO pin` 32 33 const gpioBaseDeclarationSlots = ` 34 gpio: 35 allow-installation: 36 slot-snap-type: 37 - core 38 - gadget 39 deny-auto-connection: true 40 ` 41 42 var gpioSysfsGpioBase = "/sys/class/gpio/gpio" 43 44 // gpioInterface type 45 type gpioInterface struct{} 46 47 // String returns the same value as Name(). 48 func (iface *gpioInterface) String() string { 49 return iface.Name() 50 } 51 52 // Name of the gpioInterface 53 func (iface *gpioInterface) Name() string { 54 return "gpio" 55 } 56 57 func (iface *gpioInterface) StaticInfo() interfaces.StaticInfo { 58 return interfaces.StaticInfo{ 59 Summary: gpioSummary, 60 BaseDeclarationSlots: gpioBaseDeclarationSlots, 61 } 62 } 63 64 // BeforePrepareSlot checks the slot definition is valid 65 func (iface *gpioInterface) BeforePrepareSlot(slot *snap.SlotInfo) error { 66 // Must have a GPIO number 67 number, ok := slot.Attrs["number"] 68 if !ok { 69 return fmt.Errorf("gpio slot must have a number attribute") 70 } 71 72 // Valid values of number 73 if _, ok := number.(int64); !ok { 74 return fmt.Errorf("gpio slot number attribute must be an int") 75 } 76 77 // Slot is good 78 return nil 79 } 80 81 func (iface *gpioInterface) AppArmorConnectedPlug(spec *apparmor.Specification, plug *interfaces.ConnectedPlug, slot *interfaces.ConnectedSlot) error { 82 var number int64 83 if err := slot.Attr("number", &number); err != nil { 84 return err 85 } 86 path := fmt.Sprint(gpioSysfsGpioBase, number) 87 // Entries in /sys/class/gpio for single GPIO's are just symlinks 88 // to their correct device part in the sysfs tree. Given AppArmor 89 // requires symlinks to be dereferenced, evaluate the GPIO 90 // path and add the correct absolute path to the AppArmor snippet. 91 dereferencedPath, err := evalSymlinks(path) 92 if err != nil { 93 return err 94 } 95 spec.AddSnippet(fmt.Sprintf("%s/* rwk,", dereferencedPath)) 96 return nil 97 98 } 99 100 func (iface *gpioInterface) SystemdConnectedSlot(spec *systemd.Specification, plug *interfaces.ConnectedPlug, slot *interfaces.ConnectedSlot) error { 101 var gpioNum int64 102 if err := slot.Attr("number", &gpioNum); err != nil { 103 return err 104 } 105 106 serviceName := interfaces.InterfaceServiceName(slot.Snap().InstanceName(), fmt.Sprintf("gpio-%d", gpioNum)) 107 service := &systemd.Service{ 108 Type: "oneshot", 109 RemainAfterExit: true, 110 ExecStart: fmt.Sprintf("/bin/sh -c 'test -e /sys/class/gpio/gpio%d || echo %d > /sys/class/gpio/export'", gpioNum, gpioNum), 111 ExecStop: fmt.Sprintf("/bin/sh -c 'test ! -e /sys/class/gpio/gpio%d || echo %d > /sys/class/gpio/unexport'", gpioNum, gpioNum), 112 } 113 return spec.AddService(serviceName, service) 114 } 115 116 func (iface *gpioInterface) AutoConnect(*snap.PlugInfo, *snap.SlotInfo) bool { 117 // allow what declarations allowed 118 return true 119 } 120 121 func init() { 122 registerIface(&gpioInterface{}) 123 }