canonical / matter-pi-gpio-commander

Matter Raspberry Pi GPIO Commander - Turn your Pi into a Matter lighting device!
Apache License 2.0
90 stars 3 forks source link

Replace `gpio` snap interface plug with `gpio-memory-control` #8

Closed farshidtz closed 1 year ago

farshidtz commented 1 year ago

The test app (and the actual service) can't access the GPIO in confinement on Ubuntu Core:

$ sudo matter-pi-gpio-commander.test-blink
WiringPi pin: 8
wiringPiSetup: Unable to open /dev/mem or /dev/gpiomem: Permission denied.
  Aborting your program because if it can not access the GPIO
  hardware then it most certianly won't work
  Try running with sudo?

This is a confinement issue because on Ubuntu Core and other distros, the app and service can access the GPIO when installing the snap in developer mode (--devmode flag). Here is the output of snappy-debug:

= AppArmor =
Time: Feb 28 09:51:40
Log: apparmor="DENIED" operation="open" profile="snap.matter-pi-gpio-commander.test-blink" name="/dev/mem" pid=2535 comm="test-blink" requested_mask="wr" denied_mask="wr" fsuid=0 ouid=0
File: /dev/mem (write)
Suggestion:
* add 'physical-memory-control' to 'plugs'

= AppArmor =
Time: Feb 28 09:51:40
Log: apparmor="DENIED" operation="open" profile="snap.matter-pi-gpio-commander.test-blink" name="/dev/gpiomem" pid=2535 comm="test-blink" requested_mask="wr" denied_mask="wr" fsuid=0 ouid=0
File: /dev/gpiomem (write)
Suggestion:
* add 'gpio-memory-control' to 'plugs'

The currently used setup function wiringPiSetupGpio(void) uses the low-level gpiomem interface, rather than sysfs. The wiringPiSetupSys(void) function setup function uses the sysfs (/sys/class/gpio), however it has the following limitations:

  1. It does not export the gpio
  2. The GPIO direction setting functions will have no effect

The gpio snap interface exports the GPIO on connection but the direction isn't set. The direction needs to be set manually:

sudo snap connect matter-pi-gpio-commander:gpio pi:bcm-gpio-4
sudo bash -c 'echo out > /sys/class/gpio/gpio4/direction'
sudo matter-pi-gpio-commander.test-blink

If the direction is left as in, the application neither shows errors nor performs the expected GPIO value change. A GPIO connect hook (connect-plug-gpio) may be used to set the direction upon connection.

Relevant snippet from the WiringPi documentation, supporting these findings:

wiringPiSetupSys (void) ;

This initialises wiringPi but uses the /sys/class/gpio interface rather than accessing the hardware directly. This can be called as a non-root user provided the GPIO pins have been exported before-hand using the gpio program. Pin numbering in this mode is the native Broadcom GPIO numbers – the same as wiringPiSetupGpio() above, so be aware of the differences between Rev 1 and Rev 2 boards.

Note: In this mode you can only use the pins which have been exported via the /sys/class/gpio interface before you run your program. You can do this in a separate shell-script, or by using the system() function from inside your program to call the gpio program.

Also note that some functions have no effect when using this mode as they’re not currently possible to action unless called with root privileges. (although you can use system() to call gpio to set/change modes if needed)

Given that the gpio snap interface is missing by default from Ubuntu Server and other confined distros, it makes sense to use the gpiomem interface and grant that access via the snap's gpio-memory-control interface. This is less secure than the gpio interface because it gives access to all GPIO memory. However this is an acceptable compromise for the sake of usability. The gpio-memory-control interface is available on both Ubuntu Core and classic Ubuntu so we no longer need to install the snap in developer mode on classic.

As such, this PR makes the following changes:

Test

$ sudo snap install matter-pi-gpio-commander --channel=edge/gpiomem
matter-pi-gpio-commander (edge/gpiomem) 0.3.0 from Farshid Tavakolizadeh (farshidtz) installed

$ sudo snap set matter-pi-gpio-commander gpio=4

$ sudo matter-pi-gpio-commander.test-blink 
GPIO: 4
wiringPiSetup: Unable to open /dev/mem or /dev/gpiomem: Permission denied.
  Aborting your program because if it can not access the GPIO
  hardware then it most certianly won't work
  Try running with sudo?

$ sudo snap connect matter-pi-gpio-commander:gpio-memory-control 

$ sudo matter-pi-gpio-commander.test-blink 
GPIO: 4
On
Off
On
^C