/* 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][0] || 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; }