RPi3 GPIO confinement from a SNAP called from another SNAP

What I am trying to do: Use a chatbot to control the RPi3 gpio.

My setup:
Ubuntu Core 16.04
rocketchat-server snap - I’m hosting a chat service.
botswana snap - deploys a hubot chatbot and hooks it up to rocketchat.

I have modified the botswana snap by adding a statically compiled c program “gpio” that accesses the GPIO through physical-memory-control and physical-memory-observe. I call “gpio” from the chatbot scripts. This allows me to control the RPi3 gpio remotely with the chatbot.

Problem: If I snap install botswana with confinement “strict”, I can not access /dev/mem even with the botswana plugs connected to physical-memory-control and physical-memory-observe core slots. With --devmode, everything works: I can trigger the chatbot scripts to control the gpio program and see the gpio toggle. This works even though the rocketchat-server is installed with “strict”.

I am controlling the chatbot from within rocketchat. rocketchat-server does not have physical-memory-control and physical-memory-observe plugs.

Everything works if rocketchat-server is “strict” and botswana is “devmode”. I can access /dev/mem by calling gpio from the chatbot inside rocketchat.

snap run botswana.gpio (just calling the gpio program) works on the command line when “strict” as long as I connect the physical memory plugs.

But I am denied access to /dev/mem within rocketchat if botswana is “strict”, even with the botswana plugs hooked up to the core physical memory slots.

I don’t know how to get access to /dev/mem from within rocketchat “strict” when botswana is also “strict”. This is what I would like to do.

Any advice would be appreciated.

physical-memory-control should be enough to get read and write access to /dev/mem. Are there any messages output to the log when you call botswana in strict mode? Use snappy-debug in a separate terminal, while you run your snap, to print the AppArmor messages:

sudo snap install snappy-debug
sudo snappy-debug.security scanlog

Try /dev/gpiomem and the gpio-memory-control interface instead. In addition to this, you might need to verify that /dev/gpiomem is accessible to your user.

Thanks @lucyllewy for telling me about snappy-debug. This is very helpful. I am a novice. :slight_smile:

I am trying to execute “sudo snap run botswana.gpio” from within a coffee script that my chatbot is linked to. snappy-debug reports this:

= AppArmor = Time: Jan 19 08:42:57 Log: apparmor=“DENIED” operation=“exec” profile=“snap.botswana.letloose” name="/usr/bin/sudo" pid=16578 comm=“node” requested_mask=“x” denied_mask=“x” fsuid=1000 ouid=0 File: /usr/bin/sudo (exec) Suggestions:

  • adjust snap to ship ‘sudo’
  • adjust program to use relative paths if the snap already ships ‘sudo’

If I run “sudo snap run botswana.gpio” from the command line it works. But I get the error log above if I try to access from the coffee script using the chatbot in rocketchat.

If I run “snap run botswana.gpio” (removing sudo) on the command line I get:

can’t open /dev/mem

But if I try to access this same command (without sudo) from the coffee script, snappy-debug reports:

= AppArmor = Time: Jan 19 08:47:47 Log: apparmor=“DENIED” operation=“exec” profile=“snap.botswana.letloose” name="/usr/bin/snap" pid=16896 comm=“node” requested_mask=“x” denied_mask=“x” fsuid=1000 ouid=0 File: /usr/bin/snap (exec) Suggestions:

  • adjust snap to ship ‘snap’
  • adjust program to use relative paths if the snap already ships ‘snap’

So it’s telling me I need to include “sudo” and “snap” in my package if I try to run botswana from a coffee script, but not if I run it from the command line.

Not sure what to do…

Thanks for the suggestion @jdstrand.

When I run “snap interfaces” I get this:

fad0@localhost:~/dubot$ snap interfaces
Slot Plug
:account-control -
:alsa -
:autopilot-introspection -
:bluetooth-control -
:broadcom-asic-control -
:browser-support -
:camera -
:classic-support classic
:core-support core:core-support-plug
:dcdbas-control -
:docker-support -
:firewall-control -
:framebuffer -
:fuse-support -
:gpg-keys -
:gpg-public-keys -
:gpio-memory-control -
:greengrass-support -
:hardware-observe -
:hardware-random-control -
:hardware-random-observe -
:home botswana
:io-ports-control -
:joystick -
:kernel-module-control -
:kubernetes-support -
:kvm -
:log-observe snappy-debug
:lxd-support -
:mount-observe -
:netlink-audit -
:netlink-connector -
:network botswana,rocketchat-server
:network-bind botswana,rocketchat-server
:network-control -
:network-observe -
:network-setup-control -
:network-setup-observe -
:opengl -
:openvswitch-support -
:physical-memory-control botswana
:physical-memory-observe botswana
:ppp -
:process-control botswana
:raw-usb -
:removable-media -
:shutdown -
:snapd-control -
:ssh-keys -
:ssh-public-keys -
:system-observe botswana
:system-trace -
:time-control -
:timeserver-control -
:timezone-control -
:tpm -
:uhid -
pi3:bcm-gpio-0 -
pi3:bcm-gpio-1 -
pi3:bcm-gpio-10 -
pi3:bcm-gpio-11 -
pi3:bcm-gpio-12 -
pi3:bcm-gpio-13 -
pi3:bcm-gpio-14 -
pi3:bcm-gpio-15 -
pi3:bcm-gpio-16 -
pi3:bcm-gpio-17 -
pi3:bcm-gpio-18 -
pi3:bcm-gpio-19 -
pi3:bcm-gpio-2 -
pi3:bcm-gpio-20 -
pi3:bcm-gpio-21 -
pi3:bcm-gpio-22 -
pi3:bcm-gpio-23 -
pi3:bcm-gpio-24 -
pi3:bcm-gpio-25 -
pi3:bcm-gpio-26 -
pi3:bcm-gpio-27 -
pi3:bcm-gpio-3 -
pi3:bcm-gpio-4 botswana:gpio
pi3:bcm-gpio-5 -
pi3:bcm-gpio-6 -
pi3:bcm-gpio-7 -
pi3:bcm-gpio-8 -
pi3:bcm-gpio-9 -
pi3:bt-serial -
pi3:i2c-0 -
pi3:i2c-1 -
pi3:i2c-2 -

This is after I have connected all botswana plugs. I am using an edge version of ubuntu core 16 and that is why the pi3 slots exists. The stable version does not have them. I do not see the two interfaces you suggest: /dev/gpiomem and the gpio-memory-control interface. What version of ubuntu core has these? My c program access /dev/mem which is mapped to the physical-memory and I’m not having an issue with it. I only have issues when I try to install the snap with “strict” confinement.

The issue is not straight forward because I am calling the gpio program from a coffee script that is linked to a chatbot running in rocketchat. Rocketchat is one snap. The chatbot and gpio are in another snap. I can get gpio to work on the command line, but not when called from the chatbot if the snap is in strict confinement…

/dev/gpiomem is a device only on kernels that provide it. It is available via the bcm2835-gpiomem module which allows non-root processes to access gpio memory. /dev/gpiomem is the subset of /dev/mem set aside for gpio memory. My understanding is that rpi kernels should have this module available. Perhaps use modprobe to load the module?

If that doesn’t work for you, you must necessarily use /dev/mem since, as you say, /dev/gpiomem does not exist. To write to this file, your snap needs physical-memory-control connected, and the process accessing /dev/mem must also be root.

I noticed in your previous comments that you are trying to raise privileges with sudo from inside your botswana snap. This is not allowed. I suggest removing the use of the sudo from the snap and instead use sudo botswana.letloose to have the required privileges to access /dev/mem. Alternatively, you could create a daemon command in your snap to access /dev/mem, since daemons from snaps run as root.

I verified that the pi2-kernel snap (the one used for both rpi2 and rpi3) has the bcm2835_gpiomem module loaded and the /dev/gpiomem device exists on my Ubuntu Core device. I also noticed on this device that /dev/gpiomem only has 0600 permissions. If you want non-root users to access this, you’ll need to adjust the permissions to 0660 and then chgrp /dev/gpiomem to a group your user is in.

What kernel are you using?

while we indeed ship the module and have a /dev/gpiomem device file with our official kernels, a “gpio-memory-control” interface does not exist on any of my Pi’s here … is there a bug or did something not land yet ?

@jdstrand
Here’s the output of “snap list”:

fad0@localhost:~$ snap list
Name Version Rev Developer Notes
botswana 0.0.1 x1 try
classic 16.04 26 canonical devmode
core 16-2.30+git477.ccba80a 3852 canonical core
pi2-kernel 4.4.0-1082.90 49 canonical kernel
pi3 16.04-0.5 22 canonical gadget
rocketchat-server 0.60.3 1211 rocketchat -
snappy-debug 0.31.7-snapd2.28.5 222 canonical -

I will update after I have tried some of the suggestions. Thanks!

@ogra I’m new to snaps, so most likely my issue is not a bug. I’m probably violating some rules… :slight_smile:

well, @jdstrand mentioned that interface so i would expect to see it if it is really supposed to be there :slight_smile:

I do see /dev/gpiomem but I have not seen a gpio-memory-control interface in any of the ubuntu core images I have used.

Your snap will not be allowed to use sudo even if you shipped the binary in the package. The command you want to run must be inside the same snap as the caller and you should use direct execution. i.e. do not use snap run. In your coffee script use the full path or relative path to the actual executable inside your snap. You can reference the environment for a variable called SNAP which has the full path to your snap’s filesystem enabling you to just worry about the relative path inside your snap:

exec = require('child_process').exec
snap_dir = process.env['SNAP']
exec '#{ snap_dir }/usr/bin/my-gpio-command', (error, stdout, stderr) ->
  ....

@lucyllewy This again is very helpful. I changed the coffee script to point directly to the gpio executable in the /snap/botswana/current/bin directory.

However, the gpio executable uses /dev/mem, which requires sudo access. But, as you point out, I can’t use “sudo” in strict mode. So I think I need to change the gpio program so that a non-root user can access the RPi3 gpio.

@jdstrand suggests using /dev/gpiomem, (which I guess can be accessed by non-root users), but I don’t see a gpio-memory-control interfaces slot when I do “snap interfaces”.

Can I hook it to physical-memory-control instead when accessing /dev/gpiomem?

I have updated my executable to access /dev/gpiomem and verified it works on the command line (not in the snap), but it still requires sudo…

According to https://github.com/snapcore/snapd/wiki/Interfaces it was added in 2.30, but looking at the sources for the core snap in https://launchpad.net/~snappy-dev/+archive/ubuntu/image/+packages, gpio-memory-control is not in there. I thought I saw it list with snap interfaces, but I rechecked and it isn’t on my rpi2 (sorry). I then checked origin/release/2.30 and it is not in there, but it is in master and will therefore be in 2.31 (I’ve adjusted the wiki). Sorry for the confusion.

You can’t call sudo from your snap in strict mode, but you can use sudo to call your snap just fine. Note you can also create a daemon in your snap to access /dev/mem and it will run as root and can therefore access the file.

That’s not how interfaces work, no, but snapd 2.31 will have the gpio-memory-control interface.

@jdstrand I changed the chatbot launch script to allow sudo when launching the bot. And when launched, I see a process for the bot, owned by root. However, it fails to connect to the chat-service. So I think I am stuck using --devmode for now.

Also, since my edge version has rpi2-bmc-gpio# slots, I could use the “gpio” plug, and change my program to access the gpio through /sys/class/gpio/gpioN, but even ‘sudo echo “4” > /sys/class/gpio/export’ fails…

I also tried to give my username sudo privileges, but even as root I couldn’t make it happen.

Once ubuntu core is available with gpio-memory-control, will my c-program that is memory mapped to /dev/gpiomem work without sudo?

The command above won’t work as you expect, by design.

To correctly echo a value into a file which needs elevated/different privileges you need to sudo the writing step not the echo step. Think of it like this: bash in your command above runs sudo echo '4' which runs echo 4 as root, and you tell bash running as your user to pipe the output of that to /sys/class/gpio/export via your redirection >; the pipe is coordinated by the shell you’re running as yourself so the write will be as you not the user who ran the echo.

This will achieve a write to a root-only file as a non-root user:

echo '4' | sudo tee /sys/class/gpio/export

Here the tee command does the writing and is executed as root via sudo. The echo runs as you, and the pipe redirects the input to sudo which forwards it onto the tee after gaining root privilege, and finally tee running as root then writes to the file.

You could also spawn an entirely new shell and run the whole thing as root: echo, pipe and write. You need to be careful with quotes here though:

sudo bash -c 'echo "4" > /sys/class/gpio/export'

@lucyllewy Great explanation. This is helpful.

Two questions:

  1. Once ubuntu core is available with gpio-memory-control, will it allow read/write access to /dev/gpiomem without sudo?

  2. If not, how can any snap with confinement = strict work without the snap programs being called with sudo? (Because sudo can not be included in a snap, it must be used when doing "snap run snapname.program…)