sysfsgpio: give time to udev to change gpio permission
authorAntonio Borneo <borneo.antonio@gmail.com>
Thu, 5 Sep 2019 08:48:12 +0000 (10:48 +0200)
committerTomas Vanek <vanekt@fbl.cz>
Thu, 19 Dec 2019 20:42:01 +0000 (20:42 +0000)
When a gpio is exported by writing in /sys/class/gpio/export, the
corresponding gpio control files appear immediately in sysfs but
with default access permission for root user only. The daemon udev
requires some time to get notified of the new files before it can
change the permissions to allow access to unprivileged users.
Due to this race condition, sysfsgpio can fail with EACCES error
if OpenOCD is executed by any unprivileged user.

Give 0.5 seconds to udev to identify the new files and change the
permission.

Tested with udev rules:
SUBSYSTEM=="gpio*", PROGRAM="/bin/sh -c 'find -L /sys/class/gpio/ -maxdepth 2 -exec chown root:uucp {} \; -exec chmod g=u {} \; || true'"

Change-Id: I1316c66ff103ffe23e5e4720f33372dc272a3766
Signed-off-by: Antonio Borneo <borneo.antonio@gmail.com>
Reviewed-on: http://openocd.zylin.com/5302
Tested-by: jenkins
Reviewed-by: Tomas Vanek <vanekt@fbl.cz>
src/jtag/drivers/sysfsgpio.c

index eb4941e6b241a3852dab87f12ade519116eb85ff..c6ffa3205a15ad6a6d7699c629cb9b8a717c942d 100644 (file)
@@ -52,6 +52,7 @@
 #include "config.h"
 #endif
 
+#include <helper/time_support.h>
 #include <jtag/interface.h>
 #include "bitbang.h"
 
@@ -112,6 +113,7 @@ static void unexport_sysfs_gpio(int gpio)
  */
 static int setup_sysfs_gpio(int gpio, int is_output, int init_high)
 {
+       struct timeval timeout, now;
        char buf[40];
        char gpiostr[5];
        int ret;
@@ -131,8 +133,19 @@ static int setup_sysfs_gpio(int gpio, int is_output, int init_high)
                }
        }
 
+       gettimeofday(&timeout, NULL);
+       timeval_add_time(&timeout, 0, 500000);
+
        snprintf(buf, sizeof(buf), "/sys/class/gpio/gpio%d/direction", gpio);
-       ret = open_write_close(buf, is_output ? (init_high ? "high" : "low") : "in");
+       for (;;) {
+               ret = open_write_close(buf, is_output ? (init_high ? "high" : "low") : "in");
+               if (ret >= 0 || errno != EACCES)
+                       break;
+               gettimeofday(&now, NULL);
+               if (timeval_compare(&now, &timeout) >= 0)
+                       break;
+               jtag_sleep(10000);
+       }
        if (ret < 0) {
                LOG_ERROR("Couldn't set direction for gpio %d", gpio);
                perror("sysfsgpio: ");
@@ -141,7 +154,15 @@ static int setup_sysfs_gpio(int gpio, int is_output, int init_high)
        }
 
        snprintf(buf, sizeof(buf), "/sys/class/gpio/gpio%d/value", gpio);
-       ret = open(buf, O_RDWR | O_NONBLOCK | O_SYNC);
+       for (;;) {
+               ret = open(buf, O_RDWR | O_NONBLOCK | O_SYNC);
+               if (ret >= 0 || errno != EACCES)
+                       break;
+               gettimeofday(&now, NULL);
+               if (timeval_compare(&now, &timeout) >= 0)
+                       break;
+               jtag_sleep(10000);
+       }
        if (ret < 0) {
                LOG_ERROR("Couldn't open value for gpio %d", gpio);
                perror("sysfsgpio: ");