Commit b5611148 authored by Guido Gunther's avatar Guido Gunther Committed by Sebastian Krzyszkowiak
Browse files

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.
parent 20ba06a1
/*
* Copyright (C) 2020 Purism SPC
* SPDX-License-Identifier: GPL-3.0+
* Author: Guido Günther <agx@sigxcpu.org>
*/
#define G_LOG_DOMAIN "fbd-ledctrl"
#include <glib.h>
#include <errno.h>
#include <fcntl.h>
#include <grp.h>
#include <stdio.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>
#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;
}
......@@ -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
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment