dhess / hpio

Perform GPIO from Haskell
BSD 3-Clause "New" or "Revised" License
21 stars 4 forks source link

Do I need root permissions for accessing GPIO? #73

Closed amigalemming closed 6 years ago

amigalemming commented 6 years ago

I wrote a simple program that switches a light on and plays a sound if a motion sensor triggers: https://hub.darcs.net/thielema/deep-thought/browse/src/DeepThought.hs

However, when I start the program it fails with a Permission error. It only works when run as root, in contrast to the Python programs I tested before. Is this due to hpio's use of sysfs?

Running as root is problematic. I like to test the program in GHCi but as root I am missing the user packages. When running as root, I can no longer abort the program with CTRL-C. If I terminate it with kill then the finalization code (switching off LED) is skipped. How can I cleanup properly at the end?

dhess commented 6 years ago

By default, writes to Linux's sysfs's GPIO filesystem can only be performed by root. There are ways around that, but I think they're outside the scope of what a package like hpio should be doing.

Regardless, this is a relevant issue because it will come up for anyone who wants to use hpio in its current form [1]. At least one solution to the problem is to:

  1. Create a gpio group.
  2. Run your hpio program as a user who is a member of that group.
  3. Use udev to ensure that the pseudo-files and pseudo-directories created by sysfss GPIO subsystem are writable by the gpio group. This is the solution I use on my own systems where I run hpio programs.

There are numerous ways to accomplish this in udev, but here are the rules I use:

SUBSYSTEM=="gpio", KERNEL=="gpiochip*", ACTION=="add", PROGRAM="/bin/sh -c 'chown root:gpio /sys/class/gpio/export /sys/class/gpio/unexport ; chmod 220 /sys/class/gpio/export /sys/class/gpio/unexport'"
SUBSYSTEM=="gpio", KERNEL=="gpio*", ACTION=="add", PROGRAM="/bin/sh -c 'chown root:gpio /sys%p/active_low /sys%p/direction /sys%p/edge /sys%p/value ; chmod 660 /sys%p/active_low /sys%p/direction /sys%p/edge /sys%p/value'"

A Google search will turn up quite a few alternative ways to accomplish more or less the same thing:

https://www.google.com/search?hl=en&q=linux%20gpio%20udev%20rules

One flaw with any udev-based approach is that there is a slight delay between when the kernel creates the pseudo-devices and when the udev script runs. This can cause a race where, if your program tries to write a file before its permissions have been fixed, the program will fail. The way I solve that is to run a shell script which exports the devices my program needs before I run the program, so that by the time the program runs, the permissions will have been fixed; but a more robust solution would be to build a delay into in hpio's sysfs monad to accommodate this race condition. There is another race condition issue (#70) that I need to address, and the fix for that should probably handle any udev delay, as well. I will try to fix that in the next couple of weeks (pretty busy here at the moment), but a workaround script like the one I mentioned should work in the meantime.


[1] Eventually I want to support libgpiod (see #71) and deprecate the sysfs interface, which I presume will have its own way of dealing with privileged operations.

amigalemming commented 6 years ago

Thank you for the detailed explanation! The default user on the Raspberry Pi is already in the gpio group. Setting sysfs groups is the missing part for me. How does the Python interface avoid these problems? Does it use libgpio?

dhess commented 6 years ago

I'm not that familiar with GPIO in Python, and it obviously depends on which Python module you were using, but I believe that at least some of the Python RPi modules wrap the bcm2835 RPi-specific C library (see http://www.airspayce.com/mikem/bcm2835/). According to the home page for that project, recent versions of Raspbian allow non-root access to the /dev/gpiomem device used by that library.

Anyway, I'm glad this helped. I will close this issue if you don't object.