rexbacklight/rexbacklight.c
2018-01-12 16:06:48 -08:00

245 lines
6.0 KiB
C

/**
rexbacklight
Copyright (C) 2018 rexy712
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 <http://www.gnu.org/licenses/>.
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <dirent.h>
#include <errno.h>
static const char* backlight_dir = "/sys/class/backlight/";
void usage(int exit_val){
fprintf(stderr, "Usage: rexbacklight [option]\n\n");
fprintf(stderr, "Options:\n");
fprintf(stderr, " =<percentage>\n");
fprintf(stderr, " -<percentage>\n");
fprintf(stderr, " +<percentage>\n");
fprintf(stderr, " off\n");
fprintf(stderr, " max\n");
fprintf(stderr, " min\n");
if(!exit_val){
fprintf(stderr, "\nrexbacklight Copyright (C) 2018 rexy712\n");
fprintf(stderr, "This program comes with ABSOLUTELY NO WARRANTY.\n");
fprintf(stderr, "This is free software, and you are welcome to redistribute it\n");
fprintf(stderr, "under certain conditions; see the GNU GPLv3 for details.\n");
fprintf(stderr, "A copy of the GPLv3 is available with the source in the file 'LICENSE'\n");
}
exit(exit_val);
}
int check_valid_files(const char* f1, const char* f2){
struct stat file1, file2;
if(stat(f1, &file1)){
printf("failed to open '%s'\n", f1);
return -1;
}
if(stat(f2, &file2)){
printf("failed to open '%s'\n", f2);
return -1;
}
//Same device and same inode means same file
//device id
if(file1.st_dev != file2.st_dev)
return -2;
//inode number
if(file1.st_ino != file2.st_ino)
return -2;
return 0;
}
struct string_array{
char** list;
int size;
};
//Get a list of backlight devices in sysfs
struct string_array get_backlight_sources(void){
DIR* fd;
struct dirent* dir;
fd = opendir(backlight_dir);
if(!fd){
fprintf(stderr, "Unable to open '%s' for reading!\n", backlight_dir);
return (struct string_array){.list = NULL, .size = 0};
}
char** list = malloc(sizeof(const char*) * 8);
int i;
for(i = 0;(dir = readdir(fd));){
if(!strcmp(dir->d_name, ".") || !strcmp(dir->d_name, "..")){
continue;
}
list[i] = malloc(strlen(dir->d_name) + 1);
strcpy(list[i], dir->d_name);
i++;
}
closedir(fd);
return (struct string_array){.list = list, .size = i};
}
void free_string_array(struct string_array* s){
for(int i = 0;i < s->size;i++)
free(s->list[i]);
free(s->list);
}
int get_brightness(const char* file){
FILE* bright = fopen(file, "r");
char buff[127];
if(!bright){
fprintf(stderr, "Unable to open brightness file \"%s\"!\n", file);
return 0;
}
if(!fgets(buff, sizeof(buff), bright)){
fprintf(stderr, "Unable to read file \"%s\"!\n", file);
fclose(bright);
return 0;
}
fclose(bright);
return atof(buff);
}
int process_arg(char* arg, float min, float current, float max){
//Amount to inc/dec
int delta = delta = max * atof(arg + 1) / 100.0;
//Increment
if(arg[0] == '+'){
delta = delta + current;
if(delta >= max)
return max;
else{
return delta;
}
//Decrement
}else if(arg[0] == '-'){
delta = current - delta;
if(delta <= min)
return min;
else
return delta;
//Directly set
}else if(arg[0] == '='){
if(delta >= max)
return max;
else if(delta <= min)
return min;
else
return delta;
//Set to minimum plus one (so backlight is actually on!)
}else if(!strcmp("min", arg)){
return min + 1;
//Turn off backlight
}else if(!strcmp("off", arg)){
return min;
//Set to maximum
}else if(!strcmp("max", arg)){
return max;
//Unknown option
}else{
usage(-1);
}
return current;
}
//argv[1] shall be [+-=]<percentage>|min|max|off
int main(int argc, char** argv){
const char* backlight_file = "brightness";
const char* max_backlight_file = "max_brightness";
//Make sure we have an argument and it is at least 2 characters long
if(argc < 2 || !strcmp(argv[1], "--help"))
usage(0);
else if(strlen(argv[1]) < 2)
usage(-1);
//Gain access to sysfs files
if(setuid(0)){
fprintf(stderr, "Unable to setuid to 0!\n");
return -3;
}
//Make sure we aren't in a chroot environment (prevents writing to a file we don't want to!)
if(check_valid_files("/", "/proc/1/root")){
fprintf(stderr, "No chroot today\n");
return -4;
}
//Now that we've verified we're in a safe environment to do the work,
//Let's get everything set up!
//Acquire a list of backlight devices in sysfs
struct string_array backlight_names = get_backlight_sources();
if(backlight_names.size == 0){
fprintf(stderr, "No backlights devices found!\n");
return -1;
}
//save our starting directory so we can change to each backlight directory
const char* starting_dir = getcwd(NULL, 0);
if(chdir(backlight_dir)){
fprintf(stderr, "Unable to read backlight sysfs directory!\n");
return -2;
}
//Set all backlight sources to the requested percentage
for(int i = 0;i < backlight_names.size;i++){
if(chdir(backlight_names.list[i])){
fprintf(stderr, "Unable to open backlight directory \"%s%s\"!\n", backlight_dir, backlight_names.list[i]);
continue;
}
//Open brightness file for writing
int out = process_arg(argv[1], 0, get_brightness(backlight_file), get_brightness(max_backlight_file));
FILE* bright = fopen(backlight_file, "w+");
if(!bright){
fprintf(stderr, "Unable to open brightness file \"%s\" for writing!\n", backlight_file);
continue;
}
fprintf(bright, "%d", out);
fclose(bright);
}
free_string_array(&backlight_names);
if(chdir(starting_dir)){
fprintf(stderr, "Could not return to starting directory!\nWas the directory moved/deleted?\n");
if(chdir(getenv("HOME")) || chdir("/")){
return -5;
}
}
return 0;
}