gitee.com/mysnapcore/mysnapd@v0.1.0/interfaces/builtin/gpio.go (about)

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