/* Copyright (C) 2023 Aiden Gall
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see . */
#include
#include
#include
#include
#include
#include
#include
#include
static const char VIDEO_GROUP[] = "video";
static const char BRIGHTNESS[] =
"/sys/class/backlight/intel_backlight/brightness";
static const char MAX_BRIGHTNESS[] =
"/sys/class/backlight/intel_backlight/max_brightness";
static void die(const char *fmt, ...);
static long strtonum(const char *arg, long minval, long maxval);
static int check_group(void);
static long read_sys_file_num(const char *path);
static void write_sys_file_num(const char *path, long val);
static void
die(const char *const fmt, ...)
{
va_list ap;
va_start(ap, fmt);
vfprintf(stderr, fmt, ap);
va_end(ap);
exit(EXIT_FAILURE);
}
static long
strtonum(const char *const arg, const long minval, const long maxval)
{
long ret;
char *endptr;
if (minval > maxval)
die("strtonum: Invalid range\n");
if (!arg)
die("strtonum: Missing argument\n");
errno = 0;
ret = strtol(arg, &endptr, 10);
if (errno)
die("strtol: %s\n", strerror(errno));
if (arg == endptr || *endptr)
die("strtol: Invalid integer\n");
if (ret < minval || ret > maxval)
die("strtonum: Argument out of range\n");
return ret;
}
static int
check_group(void)
{
uid_t uid, euid;
gid_t video_gid;
gid_t *glist;
int glist_size;
struct group *grp;
int i;
uid = getuid();
euid = geteuid();
if (uid == 0)
return 1;
if (euid != 0)
die("check_group: setuid is not set\n");
errno = 0;
grp = getgrnam(VIDEO_GROUP);
if (errno)
die("getgrnam: %s\n", strerror(errno));
if (!grp)
die("getgrnam: Group '%s' not found\n", VIDEO_GROUP);
video_gid = grp->gr_gid;
errno = 0;
glist_size = getgroups(0, NULL);
if (errno)
die("getgroups: %s\n", strerror(errno));
glist = calloc(glist_size, sizeof(*glist));
if (!glist)
die("check_group: Out of memory\n");
errno = 0;
if (getgroups(glist_size, glist) != glist_size)
die("getgroups: Inconsistent number of groups\n");
if (errno)
die("getgroups: %s\n", strerror(errno));
for (i = 0; i < glist_size; i++) {
if (glist[i] == video_gid) {
free(glist);
return 1;
}
}
free(glist);
return 0;
}
static long
read_sys_file_num(const char *const path)
{
FILE *fd;
char buf[64];
errno = 0;
fd = fopen(path, "r");
if (errno)
die("fopen: %s\n", strerror(errno));
if (!fd)
die("fopen: Returned NULL\n");
if (!fgets(buf, sizeof(buf), fd))
die("fgets: Returned NULL\n");
fclose(fd);
buf[strcspn(buf, "\n")] = '\0';
return strtonum(buf, 0, LONG_MAX/2);
}
static void
write_sys_file_num(const char *const path, const long val)
{
FILE *fd;
errno = 0;
fd = fopen(path, "w");
if (errno)
die("fopen: %s\n", strerror(errno));
if (!fd)
die("fopen: Returned NULL\n");
if (!fprintf(fd, "%ld\n", val))
die("fprintf: Failed to write\n");
fclose(fd);
}
int
main(int argc, char *argv[])
{
long brightness, max_brightness;
long val;
if (!check_group())
die("%s: User not in '%s' group\n", argv[0], VIDEO_GROUP);
if (argc != 3)
die("%s: Invalid number of arguments\n", argv[0]);
val = strtonum(argv[2], 0, LONG_MAX/2);
if (argv[1][1])
goto invalid_mode;
switch (argv[1][0]) {
case '-':
val = -val;
/* FALLTHROUGH */
case '+':
brightness = read_sys_file_num(BRIGHTNESS);
val += brightness;
/* FALLTHROUGH */
case '=':
max_brightness = read_sys_file_num(MAX_BRIGHTNESS);
if (val < 0)
val = 0;
if (val > max_brightness)
val = max_brightness;
write_sys_file_num(BRIGHTNESS, val);
break;
default:
invalid_mode:
die("%s: Invalid mode\n", argv[0]);
}
return 0;
}