-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathbatwarn.c
More file actions
154 lines (154 loc) · 4.51 KB
/
batwarn.c
File metadata and controls
154 lines (154 loc) · 4.51 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
// batwarn - (C) 2015-2018 Alisa Bedard
#include "BatwarnFlags.h"
#include "config.h"
#include "gamma.h"
#include <errno.h> // for errno, EWOULDBLOCK
#include <fcntl.h> // for open
#include <stdint.h> // for (u)intN*_t
#include <stdio.h> // for puts, fputs, fprintf
#include <stdlib.h> // for abort, exit
#include <sys/file.h> // for flock, LOCK_EX
#include <sys/wait.h> // for wait
#include <unistd.h> // for write, fork, execl, sleep
typedef int16_t fd_t;
static void check(int fail_condition, char const * restrict message)
{
if (fail_condition) {
puts(message);
exit(1);
}
}
static void sig_child_cb(int sig)
{
if (sig != SIGCHLD)
abort(); // attached to the wrong signal
int s;
// Loop through potentially multiple children
while (wait(&s) > 0) {
if (WIFEXITED(s)) {
if (WEXITSTATUS(s) != 0)
puts("Command exited abnormally");
else
puts("Command exited normally");
} else if (WIFSIGNALED(s))
puts("Terminated by a signal");
}
}
static void batwarn_execute(char const * restrict cmd)
{
pid_t fval = fork();
check(fval < 0, "fork failed");
if(fval) // in parent process
check(signal(SIGCHLD, sig_child_cb) == SIG_ERR,
"signal()"); // attach signal handler
else // in child process
check(execl("/bin/sh", "sh", "-c", cmd, NULL) < 0, "execl()");
}
/* Linux sets the charge to 257 if on a fully charged battery, so we need a
* 16 bit int here. */
static int16_t batwarn_get_value(char const * fn)
{
enum {READ_SZ = 4};
char buf[READ_SZ];
fd_t const fd = open(fn, O_RDONLY);
check(fd < 0, fn);
read(fd, buf, READ_SZ);
close(fd);
return atoi(buf);
}
static void perform_action_for_charge(int16_t const charge,
uint8_t const flags, uint8_t const critical) {
if (charge <= critical) {
if (flags & BATWARN_ENABLE_HIBERNATE)
batwarn_execute(BATWARN_HIBERNATE_COMMAND);
else if (flags & BATWARN_ENABLE_SUSPEND)
batwarn_execute(BATWARN_SUSPEND_COMMAND);
}
}
_Noreturn static void batwarn_start_checking(uint8_t const flags,
uint8_t const percent) {
uint8_t const critical = percent >> 1; // half
for (;;) {
int16_t const charge = batwarn_get_value(BATWARN_SYS_AC_FILE) ? 100
: batwarn_get_value(BATWARN_SYS_BATTERY_FILE);
if (flags & BATWARN_ENABLE_DEBUG)
fprintf(stderr, "charge: %d\n", charge);
batwarn_set_gamma(charge <= percent ? BATWARN_GAMMA_WARNING :
BATWARN_GAMMA_NORMAL);
perform_action_for_charge(charge, flags, critical);
sleep(1);
}
}
static void exit_cb(void)
{
batwarn_set_gamma(BATWARN_GAMMA_NORMAL);
}
_Noreturn static void usage(const int ec)
{
fputs(
"batwarn -dhHp:s\n"
"-d Do not fork a daemon; run in the foreground.\n"
"-h Show this usage information.\n"
"-H Enable hibernation at critical battery level.\n"
"-p PERCENT Set the warning percent for gamma change.\n"
"-s Enable suspend at critical battery level.\n"
"Copyright 2017-2020, Alisa Bedard <jefbed@gmail.com>\n"
"Version " BATWARN_VERSION, ec ? stderr : stdout
);
exit(ec);
}
static enum BatwarnFlags parse_argv(int argc, char ** argv,
uint8_t * percent)
{
enum BatwarnFlags flags;
int8_t opt;
char const optstr[] = "c:dhHp:s";
flags = 0;
while((opt = getopt(argc, argv, optstr)) != -1)
switch (opt) {
case 'd': // debug
flags |= BATWARN_ENABLE_DEBUG;
break;
case 'H': // enable hibernate at critical
flags |= BATWARN_ENABLE_HIBERNATE;
break;
case 'p': // warning percentage
*percent = atoi(optarg);
break;
case 's': // enable suspend
flags |= BATWARN_ENABLE_SUSPEND;
break;
case 'h': // help
usage(0);
default: // usage
usage(1);
}
return flags;
}
int main(int argc, char **argv)
{
/* Use flock to ensure only one instance is running. This makes
* it easier to put batwarn in .xinitrc without having to do
* process management. TODO: Allow one instance of batwarn per
* display. */
fd_t const lock_fd = open("/tmp/batwarn.lock", O_RDWR|O_CREAT, 0666);
flock(lock_fd, LOCK_EX | LOCK_NB);
if (errno == EWOULDBLOCK) {
fputs("batwarn already running!\n", stderr);
exit(1);
} else {
uint8_t percent;
uint8_t flags;
percent = BATWARN_PERCENT; // default
// possibly override percent
flags = parse_argv(argc, argv, &percent);
if ((flags & BATWARN_ENABLE_DEBUG) || !fork()) {
fputs("batwarn polling...\n", stderr);
signal(SIGINT, exit);
signal(SIGTERM, exit);
atexit(exit_cb);
batwarn_start_checking(flags, percent);
}
}
return 0;
}