diff mbox series

[meta-filesystems] gpiod-sysfs-proxy: new recipe

Message ID 20241210123243.58809-1-brgl@bgdev.pl
State Accepted
Headers show
Series [meta-filesystems] gpiod-sysfs-proxy: new recipe | expand

Commit Message

Bartosz Golaszewski Dec. 10, 2024, 12:32 p.m. UTC
From: Bartosz Golaszewski <bartosz.golaszewski@linaro.org>

Many users are reluctant to use libgpiod instead of the deprecated
/sys/class/gpio interface. The gpiod-sysfs-proxy project aims at making
the transition easier by implementing a compatibility layer in
user-space using FUSE and python3-gpiod. This way we can eat the cookie
by disabling the sysfs ABI and have the users have it too by sticking to
their existing scripts.

The project itself is a very simple setuptools-based python package but
the recipe is quite complex due to comprehensive distro integration.

By default we use /run/gpio as mountpoint. For full backward
compatibility with the kernel interface, the user must explicitly add
the 'sys-class-mount' switch to PACKAGECONFIG. We do this because,
depending on whether CONFIG_GPIO_SYSFS Kconfig option is enabled,
/sys/class/gpio will either be non-empty or not exist at all. In the
latter case, we need to somehow create the /sys/class/gpio and, since
user-space is not allowed to mkdir() inside sysfs, we use overlayfs for
that. As this is rather non-standard, we want the user to be aware of
this.

We support both systemd and sys V init managers.

We also provide a ptest package which uses an external
gpio-sysfs-compat-tests script.

Signed-off-by: Bartosz Golaszewski <bartosz.golaszewski@linaro.org>
---
 meta-filesystems/conf/layer.conf              |  4 +
 .../gpiod-sysfs-proxy.init.in                 | 84 ++++++++++++++++++
 .../gpiod-sysfs-proxy.service.in              | 15 ++++
 .../gpiod-sysfs-proxy/run-gpio-sys.mount      | 13 +++
 .../gpiod-sysfs-proxy/run-ptest.in            | 21 +++++
 .../gpiod-sysfs-proxy/sys-class.mount         | 16 ++++
 .../gpiod-sysfs-proxy_0.1.1.bb                | 85 +++++++++++++++++++
 7 files changed, 238 insertions(+)
 create mode 100644 meta-filesystems/dynamic-layers/meta-python/recipes-support/gpiod-sysfs-proxy/gpiod-sysfs-proxy/gpiod-sysfs-proxy.init.in
 create mode 100644 meta-filesystems/dynamic-layers/meta-python/recipes-support/gpiod-sysfs-proxy/gpiod-sysfs-proxy/gpiod-sysfs-proxy.service.in
 create mode 100644 meta-filesystems/dynamic-layers/meta-python/recipes-support/gpiod-sysfs-proxy/gpiod-sysfs-proxy/run-gpio-sys.mount
 create mode 100644 meta-filesystems/dynamic-layers/meta-python/recipes-support/gpiod-sysfs-proxy/gpiod-sysfs-proxy/run-ptest.in
 create mode 100644 meta-filesystems/dynamic-layers/meta-python/recipes-support/gpiod-sysfs-proxy/gpiod-sysfs-proxy/sys-class.mount
 create mode 100644 meta-filesystems/dynamic-layers/meta-python/recipes-support/gpiod-sysfs-proxy/gpiod-sysfs-proxy_0.1.1.bb
diff mbox series

Patch

diff --git a/meta-filesystems/conf/layer.conf b/meta-filesystems/conf/layer.conf
index 8a0c831e2..5323913d5 100644
--- a/meta-filesystems/conf/layer.conf
+++ b/meta-filesystems/conf/layer.conf
@@ -16,3 +16,7 @@  LAYERVERSION_filesystems-layer = "1"
 LAYERDEPENDS_filesystems-layer = "core openembedded-layer networking-layer"
 
 LAYERSERIES_COMPAT_filesystems-layer = "styhead walnascar"
+
+BBFILES_DYNAMIC += " \
+    meta-python:${LAYERDIR}/dynamic-layers/meta-python/recipes-*/*/*.bb \
+"
diff --git a/meta-filesystems/dynamic-layers/meta-python/recipes-support/gpiod-sysfs-proxy/gpiod-sysfs-proxy/gpiod-sysfs-proxy.init.in b/meta-filesystems/dynamic-layers/meta-python/recipes-support/gpiod-sysfs-proxy/gpiod-sysfs-proxy/gpiod-sysfs-proxy.init.in
new file mode 100644
index 000000000..a9cf5e407
--- /dev/null
+++ b/meta-filesystems/dynamic-layers/meta-python/recipes-support/gpiod-sysfs-proxy/gpiod-sysfs-proxy/gpiod-sysfs-proxy.init.in
@@ -0,0 +1,84 @@ 
+#! /bin/sh
+### BEGIN INIT INFO
+# Provides:             gpiod-sysfs-proxy
+# Required-Start:       $remote_fs $syslog
+# Required-Stop:        $remote_fs $syslog
+# Default-Start:        2 3 4 5
+# Default-Stop:         1
+# Short-Description:    User-space, libgpiod-based compatibility layer for linux GPIO sysfs interface.
+### END INIT INFO
+#
+# -*- coding: utf-8 -*-
+# Debian init.d script for gpiod-sysfs-proxy
+# Copyright (c) 2024 Bartosz Golaszewski <bartosz.golaszewski@linaro.org>
+
+# set -e
+
+# Source function library.
+. /etc/init.d/functions
+
+PROG="/usr/bin/gpiod-sysfs-proxy"
+NAME="gpiod-sysfs-proxy"
+DESC="/sys/class/gpio compatibility layer"
+MOUNTPOINT="@mountpoint@"
+
+test -x $PROG || exit 0
+
+do_start()
+{
+	echo -n "Starting $DESC: "
+
+	if [ "$MOUNTPOINT" = "/sys/class/gpio" ] && [ ! -e /sys/class/gpio ]; then
+		mkdir -p /run/gpio/sys /run/gpio/class/gpio /run/gpio/work
+		mount -t sysfs sysfs /run/gpio/sys -o nosuid,nodev,noexec
+		# Bail out if overlayfs is not available
+		set -e
+		mount -t overlay overlay /sys/class \
+-o upperdir=/run/gpio/class,lowerdir=/run/gpio/sys/class,workdir=/run/gpio/work,nosuid,nodev,noexec,relatime,ro
+		set +e
+	else
+		mkdir -p $MOUNTPOINT
+	fi
+
+	$PROG $MOUNTPOINT -o nonempty -o allow_other -o default_permissions -o entry_timeout=0 -f | logger -i $NAME &
+	echo "done"
+}
+
+do_stop()
+{
+	echo -n "Stopping $DESC: "
+
+	umount $MOUNTPOINT
+
+	mountpoint -q /sys/class
+	if [ "$?" = "0" ]; then
+		umount /sys/class
+		umount /run/gpio/sys
+		rm -rf /run/gpio
+	fi
+	echo "done"
+}
+
+case "$1" in
+	start)
+		do_start
+	;;
+	stop)
+		do_stop
+	;;
+	status)
+		status $PROG
+		exit $?
+	;;
+	restart)
+		do_stop
+		sleep 1
+		do_start
+	;;
+	*)
+		echo "Usage: /etc/init.d/$NAME {start|stop|status|restart}" >&2
+		exit 1
+	;;
+esac
+
+exit 0
diff --git a/meta-filesystems/dynamic-layers/meta-python/recipes-support/gpiod-sysfs-proxy/gpiod-sysfs-proxy/gpiod-sysfs-proxy.service.in b/meta-filesystems/dynamic-layers/meta-python/recipes-support/gpiod-sysfs-proxy/gpiod-sysfs-proxy/gpiod-sysfs-proxy.service.in
new file mode 100644
index 000000000..313523268
--- /dev/null
+++ b/meta-filesystems/dynamic-layers/meta-python/recipes-support/gpiod-sysfs-proxy/gpiod-sysfs-proxy/gpiod-sysfs-proxy.service.in
@@ -0,0 +1,15 @@ 
+# SPDX-License-Identifier: CC0-1.0
+# SPDX-FileCopyrightText: 2024 Bartosz Golaszewski <bartosz.golaszewski@linaro.org>
+
+[Unit]
+Description=User-space, libgpiod-based compatibility layer for linux GPIO sysfs interface
+
+[Service]
+RuntimeDirectory=gpio
+Type=simple
+ExecStart=/usr/bin/gpiod-sysfs-proxy @mountpoint@ -f -o nonempty -o allow_other -o default_permissions -o entry_timeout=0
+ExecStop=/bin/umount @mountpoint@
+Restart=always
+
+[Install]
+WantedBy=multi-user.target
diff --git a/meta-filesystems/dynamic-layers/meta-python/recipes-support/gpiod-sysfs-proxy/gpiod-sysfs-proxy/run-gpio-sys.mount b/meta-filesystems/dynamic-layers/meta-python/recipes-support/gpiod-sysfs-proxy/gpiod-sysfs-proxy/run-gpio-sys.mount
new file mode 100644
index 000000000..a924cb9b6
--- /dev/null
+++ b/meta-filesystems/dynamic-layers/meta-python/recipes-support/gpiod-sysfs-proxy/gpiod-sysfs-proxy/run-gpio-sys.mount
@@ -0,0 +1,13 @@ 
+# SPDX-License-Identifier: CC0-1.0
+# SPDX-FileCopyrightText: 2024 Bartosz Golaszewski <bartosz.golaszewski@linaro.org>
+
+[Unit]
+Description=Remount of sysfs for gpiod-sysfs-proxy
+ConditionPathExists=!/sys/class/gpio
+
+[Mount]
+DirectoryMode=0700
+What=sysfs
+Where=/run/gpio/sys
+Type=sysfs
+Options=nosuid,nodev,noexec
diff --git a/meta-filesystems/dynamic-layers/meta-python/recipes-support/gpiod-sysfs-proxy/gpiod-sysfs-proxy/run-ptest.in b/meta-filesystems/dynamic-layers/meta-python/recipes-support/gpiod-sysfs-proxy/gpiod-sysfs-proxy/run-ptest.in
new file mode 100644
index 000000000..aab3a5ce3
--- /dev/null
+++ b/meta-filesystems/dynamic-layers/meta-python/recipes-support/gpiod-sysfs-proxy/gpiod-sysfs-proxy/run-ptest.in
@@ -0,0 +1,21 @@ 
+#!/bin/sh
+
+ptestdir=$(dirname "$(readlink -f "$0")")
+testbin="gpio-sysfs-compat-tests"
+
+modprobe gpio-sim
+modprobe configfs
+
+mountpoint -q /sys/kernel/config
+if [ "$?" -ne "0" ]; then
+	mount -t configfs configfs /sys/kernel/config
+fi
+
+cd $ptestdir/tests
+
+./$testbin -v --gpio-class @mountpoint@ --chown-user gpio-test > ./$testbin.out 2>&1
+if [ $? -ne 0 ]; then
+	echo "FAIL: $testbin"
+else
+	echo "PASS: $testbin"
+fi
diff --git a/meta-filesystems/dynamic-layers/meta-python/recipes-support/gpiod-sysfs-proxy/gpiod-sysfs-proxy/sys-class.mount b/meta-filesystems/dynamic-layers/meta-python/recipes-support/gpiod-sysfs-proxy/gpiod-sysfs-proxy/sys-class.mount
new file mode 100644
index 000000000..e3e3ce8e7
--- /dev/null
+++ b/meta-filesystems/dynamic-layers/meta-python/recipes-support/gpiod-sysfs-proxy/gpiod-sysfs-proxy/sys-class.mount
@@ -0,0 +1,16 @@ 
+# SPDX-License-Identifier: CC0-1.0
+# SPDX-FileCopyrightText: 2024 Bartosz Golaszewski <bartosz.golaszewski@linaro.org>
+
+[Unit]
+Description=Overlay on top of /sys/class adding the gpio class directory
+Before=gpiod-sysfs-proxy.service
+After=run-gpio-sys.mount
+ConditionPathExists=!/sys/class/gpio
+
+[Mount]
+RuntimeDirectory=gpio/class/gpio
+DirectoryMode=0755
+What=overlay
+Where=/sys/class
+Type=overlay
+Options=upperdir=/run/gpio/class,lowerdir=/run/gpio/sys/class,workdir=/run/gpio/work,ro,nosuid,nodev,noexec,relatime
diff --git a/meta-filesystems/dynamic-layers/meta-python/recipes-support/gpiod-sysfs-proxy/gpiod-sysfs-proxy_0.1.1.bb b/meta-filesystems/dynamic-layers/meta-python/recipes-support/gpiod-sysfs-proxy/gpiod-sysfs-proxy_0.1.1.bb
new file mode 100644
index 000000000..4d466d30f
--- /dev/null
+++ b/meta-filesystems/dynamic-layers/meta-python/recipes-support/gpiod-sysfs-proxy/gpiod-sysfs-proxy_0.1.1.bb
@@ -0,0 +1,85 @@ 
+SUMMARY = "User-space, libgpiod-based compatibility layer for linux GPIO sysfs interface."
+
+LICENSE = "MIT"
+LIC_FILES_CHKSUM = "file://COPYING;md5=0dcf8b702b5c96178978c7223f64a73b"
+
+inherit systemd update-rc.d ptest pypi python_pep517 python_setuptools_build_meta useradd
+
+PYPI_PACKAGE = "gpiod_sysfs_proxy"
+
+SRC_URI += " \
+    file://gpiod-sysfs-proxy.service.in \
+    file://run-gpio-sys.mount \
+    file://sys-class.mount \
+    file://gpiod-sysfs-proxy.init.in \
+    file://run-ptest.in \
+"
+
+SRC_URI[sha256sum] = "c7830cb6a2c01914df2bc0549aef2dcfcb955520d400f65b3b50fb7a6f77f1b4"
+
+# For full backward compatibility with the kernel sysfs interface, this option
+# must be selected. However, we don't make it the default as - with kernel sysfs
+# disabled - it plays a silly game with /sys/class, where it mounts a read-only
+# overlay containing the missing /sys/class/gpio directory. This is a rather
+# non-standard behavior so make sure the user actually wants it.
+PACKAGECONFIG[sys-class-mount] = ""
+
+export MOUNTPOINT="${@bb.utils.contains('PACKAGECONFIG', 'sys-class-mount', '\/sys\/class\/gpio', '\/run\/gpio', d)}"
+
+do_install:append() {
+    if ${@bb.utils.contains('DISTRO_FEATURES','systemd','true','false',d)}; then
+        install -d ${D}${systemd_system_unitdir}
+        install -m 0644 ${UNPACKDIR}/gpiod-sysfs-proxy.service.in ${D}${systemd_system_unitdir}/gpiod-sysfs-proxy.service
+
+        if ${@bb.utils.contains('PACKAGECONFIG', 'sys-class-mount', 'true', 'false', d)}; then
+            install -d ${D}${systemd_system_unitdir}/sysinit.target.wants/
+
+            install -m 0644 ${UNPACKDIR}/run-gpio-sys.mount ${D}${systemd_system_unitdir}/run-gpio-sys.mount
+            install -m 0644 ${UNPACKDIR}/sys-class.mount ${D}${systemd_system_unitdir}/sys-class.mount
+
+            ln -sf ../run-gpio-sys.mount ${D}${systemd_system_unitdir}/sysinit.target.wants/run-gpio-sys.mount
+            ln -sf ../sys-class.mount ${D}${systemd_system_unitdir}/sysinit.target.wants/sys-class.mount
+        fi
+
+        sed -i "s/@mountpoint@/$MOUNTPOINT/g" ${D}${systemd_system_unitdir}/gpiod-sysfs-proxy.service
+    elif ${@bb.utils.contains('DISTRO_FEATURES', 'sysvinit', 'true', 'false', d)}; then
+        install -d ${D}${sysconfdir}/init.d
+        install -m 0755 ${UNPACKDIR}/gpiod-sysfs-proxy.init.in ${D}${sysconfdir}/init.d/gpiod-sysfs-proxy
+        sed -i "s/@mountpoint@/$MOUNTPOINT/g" ${D}${sysconfdir}/init.d/gpiod-sysfs-proxy
+    fi
+}
+
+SYSTEMD_SERVICE:${PN} = "gpiod-sysfs-proxy.service"
+SYSTEMD_AUTO_ENABLE = "enable"
+
+INITSCRIPT_NAME = "gpiod-sysfs-proxy"
+INITSCRIPT_PARAMS = "start 20 2 3 4 5 . stop 20 0 1 6 ."
+
+FILES:${PN} += "/usr/lib/systemd/system"
+
+RDEPENDS:${PN} += " \
+    python3-fuse \
+    python3-gpiod \
+    python3-pyudev \
+"
+
+python __anonymous() {
+    if d.getVar("PTEST_ENABLED") == "1":
+        d.appendVar("SRC_URI", "git://github.com/brgl/gpio-sysfs-compat-tests;protocol=https;branch=main;destsuffix=tests;name=tests")
+        d.setVar("SRCREV_tests", "a3c9daa4650dd1e8d7fd8972db68d9c2c204263d")
+}
+
+do_install_ptest() {
+    install -d ${D}${PTEST_PATH}/tests/
+    install -m 0755 ${UNPACKDIR}/run-ptest.in ${D}${PTEST_PATH}/run-ptest
+    sed -i "s/@mountpoint@/$MOUNTPOINT/g" ${D}${PTEST_PATH}/run-ptest
+    install -m 0755 ${UNPACKDIR}/tests/gpio-sysfs-compat-tests ${D}${PTEST_PATH}/tests/gpio-sysfs-compat-tests
+}
+
+# Test user is created for verifying chown() and chmod() operations.
+USERADD_PACKAGES = "${PN}-ptest"
+GROUPADD_PARAM:${PN}-ptest = "--system gpio-test"
+USERADD_PARAM:${PN}-ptest = "--system -M -s /bin/nologin -g gpio-test gpio-test"
+
+RDEPENDS:${PN}-ptest += "kmod"
+RRECOMMENDS:${PN}-ptest += "kernel-module-gpio-sim kernel-module-configfs"