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

     1  // -*- Mode: Go; indent-tabs-mode: t -*-
     2  
     3  /*
     4   * Copyright (C) 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  // https://www.kernel.org/doc/Documentation/pwm.txt
    34  const pwmSummary = `allows access to specific PWM channel`
    35  
    36  const pwmBaseDeclarationSlots = `
    37    pwm:
    38      allow-installation:
    39        slot-snap-type:
    40          - core
    41          - gadget
    42      deny-auto-connection: true
    43  `
    44  
    45  var pwmSysfsPwmChipBase = "/sys/class/pwm/pwmchip%d"
    46  
    47  // pwmInterface type
    48  type pwmInterface struct {
    49  	commonInterface
    50  }
    51  
    52  // BeforePrepareSlot checks the slot definition is valid
    53  func (iface *pwmInterface) BeforePrepareSlot(slot *snap.SlotInfo) error {
    54  	// must have a PWM channel
    55  	channel, ok := slot.Attrs["channel"]
    56  	if !ok {
    57  		return fmt.Errorf("pwm slot must have a channel attribute")
    58  	}
    59  
    60  	// valid values of channel
    61  	if _, ok := channel.(int64); !ok {
    62  		return fmt.Errorf("pwm slot channel attribute must be an int")
    63  	}
    64  
    65  	// must have a PWM chip number
    66  	chipNum, ok := slot.Attrs["chip-number"]
    67  	if !ok {
    68  		return fmt.Errorf("pwm slot must have a chip-number attribute")
    69  	}
    70  
    71  	// valid values of chip number
    72  	if _, ok := chipNum.(int64); !ok {
    73  		return fmt.Errorf("pwm slot chip-number attribute must be an int")
    74  	}
    75  
    76  	// slot is good
    77  	return nil
    78  }
    79  
    80  func (iface *pwmInterface) AppArmorConnectedPlug(spec *apparmor.Specification, plug *interfaces.ConnectedPlug, slot *interfaces.ConnectedSlot) error {
    81  	var chipNum int64
    82  	if err := slot.Attr("chip-number", &chipNum); err != nil {
    83  		return err
    84  	}
    85  
    86  	var channel int64
    87  	if err := slot.Attr("channel", &channel); err != nil {
    88  		return err
    89  	}
    90  	path := fmt.Sprintf(pwmSysfsPwmChipBase, chipNum)
    91  	// Entries in /sys/class/pwm for PWM chips are just symlinks
    92  	// to their correct device part in the sysfs tree. Given AppArmor
    93  	// requires symlinks to be dereferenced, evaluate the PWM
    94  	// path and add the correct absolute path to the AppArmor snippet.
    95  	dereferencedPath, err := evalSymlinks(path)
    96  	if err != nil && os.IsNotExist(err) {
    97  		// If the specific pwm is not available there is no point
    98  		// exporting it, we should also not fail because this
    99  		// will block snapd updates (LP: 1866424)
   100  		logger.Noticef("cannot find not existing pwm chipbase %s", path)
   101  		return nil
   102  	}
   103  	if err != nil {
   104  		return err
   105  	}
   106  	spec.AddSnippet(fmt.Sprintf("%s/pwm%d/* rwk,", dereferencedPath, channel))
   107  	return nil
   108  }
   109  
   110  func (iface *pwmInterface) SystemdConnectedSlot(spec *systemd.Specification, plug *interfaces.ConnectedPlug, slot *interfaces.ConnectedSlot) error {
   111  	var chipNum int64
   112  	if err := slot.Attr("chip-number", &chipNum); err != nil {
   113  		return err
   114  	}
   115  
   116  	var channel int64
   117  	if err := slot.Attr("channel", &channel); err != nil {
   118  		return err
   119  	}
   120  
   121  	serviceSuffix := fmt.Sprintf("pwmchip%d-pwm%d", chipNum, channel)
   122  	service := &systemd.Service{
   123  		Type:            "oneshot",
   124  		RemainAfterExit: true,
   125  		ExecStart:       fmt.Sprintf("/bin/sh -c 'test -e /sys/class/pwm/pwmchip%[1]d/pwm%[2]d || echo %[2]d > /sys/class/pwm/pwmchip%[1]d/export'", chipNum, channel),
   126  		ExecStop:        fmt.Sprintf("/bin/sh -c 'test ! -e /sys/class/pwm/pwmchip%[1]d/pwm%[2]d || echo %[2]d > /sys/class/pwm/pwmchip%[1]d/unexport'", chipNum, channel),
   127  	}
   128  	return spec.AddService(serviceSuffix, service)
   129  }
   130  
   131  func (iface *pwmInterface) AutoConnect(*snap.PlugInfo, *snap.SlotInfo) bool {
   132  	// allow what declarations allowed
   133  	return true
   134  }
   135  
   136  func init() {
   137  	registerIface(&pwmInterface{commonInterface{
   138  		name:                 "pwm",
   139  		summary:              pwmSummary,
   140  		baseDeclarationSlots: pwmBaseDeclarationSlots,
   141  	}})
   142  }