From b561114800a304850a9d1e2d2e4f17a72469d057 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Guido=20G=C3=BCnther?= Date: Tue, 2 Jun 2020 18:37:40 +0200 Subject: [PATCH] feedbackd: Add helper to set up permissions for LEDs This helper knows about the tiggers and necessary permission. It's intended to be run via udev. --- src/fbd-ledctrl.c | 186 ++++++++++++++++++++++++++++++++++++++++++++++ src/meson.build | 9 +++ 2 files changed, 195 insertions(+) create mode 100644 src/fbd-ledctrl.c diff --git a/src/fbd-ledctrl.c b/src/fbd-ledctrl.c new file mode 100644 index 0000000..b31ab43 --- /dev/null +++ b/src/fbd-ledctrl.c @@ -0,0 +1,186 @@ +/* + * Copyright (C) 2020 Purism SPC + * SPDX-License-Identifier: GPL-3.0+ + * Author: Guido Günther + */ + +#define G_LOG_DOMAIN "fbd-ledctrl" + +#include + +#include +#include +#include +#include +#include +#include +#include + +#define LED_BRIGHTNESS_ATTR "brightness" +#define LED_PATTERN_ATTR "pattern" +#define LED_REPEAT_ATTR "repeat" +#define LED_TRIGGER_ATTR "trigger" +#define LED_TRIGGER_PATTERN "pattern" + +enum { + FBD_LEDCTRL_ERR_CMDLINE = 1, + FBD_LEDCTRL_ERR_TRIGGER = 2, + FBD_LEDCTRL_ERR_PERMS = 3, +}; + +static gboolean +write_sysfs_attr (const char *sysfs_path, const char *attr, const char *val) +{ + gint fd; + int len; + g_autofree gchar *path = g_strjoin ("/", sysfs_path, attr, NULL); + + fd = open (path, O_WRONLY|O_TRUNC, 0666); + if (fd < 0) { + fprintf (stderr, "Failed to open %s: %s\n", path, strerror (errno)); + return FALSE; + } + + len = strlen (val); + if (write (fd, val, len) < 0) { + fprintf (stderr, "Failed to write %s to %s: %s\n", val, path, strerror (errno)); + close (fd); + return FALSE; + } + + if (close (fd) < 0) { + fprintf (stderr, "Failed to close %s: %s\n", path, strerror (errno)); + return FALSE; + } + + return TRUE; +} + +static gchar * +read_sysfs_attr (const char *sysfs_path, const char *attr) +{ + gchar *content = NULL; + g_autoptr (GError) err = NULL; + g_autofree gchar *path = g_strjoin ("/", sysfs_path, attr, NULL); + + + if (!g_file_get_contents (path, &content, NULL, &err)) { + fprintf (stderr, "Failed to read %s: %s\n", path, err->message); + return NULL; + } + + return content; +} + +static gboolean +set_sysfs_attr_perm (const char *sysfs_path, const char *attr, gid_t gid) +{ + g_autofree gchar *path = g_strjoin ("/", sysfs_path, attr, NULL); + + if (chown(path, -1, gid) < 0) { + fprintf (stderr, "Failed to set perms of %s to %d: %s\n", path, gid, strerror (errno)); + return FALSE; + } + + if (chmod(path, 0664) < 0) { + fprintf (stderr, "Failed to set mode of %s: %s\n", path, strerror (errno)); + return FALSE; + } + + return TRUE; +} + +static gboolean +set_trigger (const char *sysfs_path, const char *trigger) +{ + g_autofree gchar *val = NULL; + g_auto(GStrv) triggers = NULL; + + /* + * Check the current trigger, don't do anything if it's already + * set. This prevents a change event storm when setting the + * same trigger over and over again in a udev rule. + */ + val = read_sysfs_attr (sysfs_path, LED_TRIGGER_ATTR); + triggers = g_strsplit (val, " ", 0); + + /* + * The active trigger is marked with [...] so if we don't find the + * trigger verbatim it's already the active one: + */ + if (!g_strv_contains ((const gchar * const *)triggers, trigger)) + return TRUE; + + return write_sysfs_attr (sysfs_path, LED_TRIGGER_ATTR, trigger); +} + +static gboolean +set_perms (const char *sysfs_path, const char *trigger, const char *gname) +{ + struct group *group; + gboolean success = TRUE; + + group = getgrnam (gname); + + if (!set_sysfs_attr_perm (sysfs_path, LED_BRIGHTNESS_ATTR, group->gr_gid)) + return FALSE; + + if (!g_strcmp0 (trigger, LED_TRIGGER_PATTERN)) { + if (!set_sysfs_attr_perm (sysfs_path, LED_PATTERN_ATTR, group->gr_gid)) + success = FALSE; + if (!set_sysfs_attr_perm (sysfs_path, LED_REPEAT_ATTR, group->gr_gid)) + success = FALSE; + } + + /* specific setup for other triggers goes here */ + return success; +} + +int main(int argc, char *argv[]) +{ + g_autoptr(GError) err = NULL; + g_autoptr(GOptionContext) opt_context = NULL; + g_autofree gchar *sysfs_path = NULL; + g_autofree gchar *group = NULL; + g_autofree gchar *trigger = NULL; + + const GOptionEntry options [] = { + {"path", 'p', 0, G_OPTION_ARG_STRING, &sysfs_path, + "Path to LEDs sysfs dir", NULL}, + {"group", 'G', 0, G_OPTION_ARG_STRING, &group, + "Group to set permissions to", NULL}, + {"trigger", 't', 0, G_OPTION_ARG_STRING, &trigger, + "LED trigger to configure", NULL}, + { NULL, 0, 0, G_OPTION_ARG_NONE, NULL, NULL, NULL } + }; + + opt_context = g_option_context_new ("- helper to prepare sysfs for feedbackd"); + g_option_context_add_main_entries (opt_context, options, NULL); + if (!g_option_context_parse (opt_context, &argc, &argv, &err)) { + fprintf (stderr, "%s", err->message); + g_clear_error (&err); + return 1; + } + + if (!sysfs_path) { + fprintf (stderr, "No sysfs path given\n"); + return FBD_LEDCTRL_ERR_CMDLINE; + } + + if (!trigger) { + fprintf (stderr, "No trigger specified\n"); + return FBD_LEDCTRL_ERR_CMDLINE;; + } + g_debug ("Configuring LED at %s for trigger %s", sysfs_path, trigger); + + if (!set_trigger (sysfs_path, trigger)) + return FBD_LEDCTRL_ERR_TRIGGER; + + if (group) { + g_debug ("Setting permision of %s to %s", sysfs_path, group); + if (!set_perms (sysfs_path, trigger, group)) + return FBD_LEDCTRL_ERR_PERMS; + } + + return 0; +} diff --git a/src/meson.build b/src/meson.build index b9f4e03..128d873 100644 --- a/src/meson.build +++ b/src/meson.build @@ -65,4 +65,13 @@ executable( install_dir: libexecdir, ) +executable( + 'fbd-ledctrl', + sources : ['fbd-ledctrl.c'], + include_directories : fbd_inc, + dependencies : [glib], + install : true, + install_dir: libexecdir, +) + endif