From ae88d2ff59eb851d7badbe5ff8eb11ed7040aeb9 Mon Sep 17 00:00:00 2001 From: Bartosz Golaszewski Date: Tue, 10 Dec 2024 13:32:43 +0100 Subject: gpiod-sysfs-proxy: new recipe 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 Signed-off-by: Khem Raj --- meta-filesystems/conf/layer.conf | 4 + .../gpiod-sysfs-proxy/gpiod-sysfs-proxy.init.in | 84 +++++++++++++++++++++ .../gpiod-sysfs-proxy/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/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 --git a/meta-filesystems/conf/layer.conf b/meta-filesystems/conf/layer.conf index 8a0c831e2b..5323913d58 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 0000000000..a9cf5e4075 --- /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 + +# 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 0000000000..313523268c --- /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 + +[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 0000000000..a924cb9b64 --- /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 + +[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 0000000000..aab3a5ce3e --- /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 0000000000..e3e3ce8e74 --- /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 + +[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 0000000000..4d466d30f0 --- /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" -- cgit v1.2.3-54-g00ecf