]> code.delx.au - monosys/blob - xfce4-genmon-script.c
3be3f174a4c28b0c0457e9b2296f6bb93055e4b2
[monosys] / xfce4-genmon-script.c
1 #include <errno.h>
2 #include <fcntl.h>
3 #include <math.h>
4 #include <inttypes.h>
5 #include <stdio.h>
6 #include <stdlib.h>
7 #include <string.h>
8 #include <sys/time.h>
9 #include <time.h>
10 #include <unistd.h>
11
12
13 void sleep_nanos(long nanos) {
14 struct timespec rqtp;
15 rqtp.tv_sec = 0;
16 rqtp.tv_nsec = nanos;
17 nanosleep(&rqtp, NULL);
18 }
19
20 long get_time_nanos() {
21 struct timeval t;
22 gettimeofday(&t, NULL);
23 return t.tv_sec * 1000000000L + t.tv_usec * 1000;
24 }
25
26 char* read_file(char* filename) {
27 static char buf[32768];
28
29 size_t pos = 0;
30 int fd = open(filename, 0);
31
32 if (fd < 0) {
33 return NULL;
34 }
35
36 for (;;) {
37 ssize_t result = read(fd, buf+pos, sizeof(buf)-pos);
38 if (result < 0) {
39 return NULL;
40 }
41 if (result == 0) {
42 return buf;
43 }
44 pos += result;
45 if (pos >= sizeof(buf)) {
46 fprintf(stderr, "Failed reading file, too much data: %s\n", filename);
47 return buf;
48 }
49 }
50 }
51
52 long parse_int(char* s) {
53 if (s == NULL) {
54 return -1;
55 }
56
57 errno = 0;
58 long value = strtol(s, NULL, 10);
59 if (errno != 0) {
60 fprintf(stderr, "Failed to parse string: %s -- %s\n", strerror(errno), s);
61 return -1;
62 }
63
64 return value;
65 }
66
67 char* read_next_line(char** s) {
68 char* end = index(*s, '\n');
69 if (end == NULL) {
70 return NULL;
71 }
72
73 char* line = *s;
74 *s = end+1;
75 *end = '\0';
76 return line;
77 }
78
79 int read_cpu_idle_jiffys() {
80 char* procstat = read_file("/proc/stat");
81 if (procstat == NULL) {
82 fprintf(stderr, "Failed reading file /proc/stat: %s\n", strerror(errno));
83 return -1;
84 }
85
86 char* line = read_next_line(&procstat);
87 if (line == NULL) {
88 return -1;
89 }
90
91 char* result = NULL;
92 for (int i = 0; i <= 4; ++i, line=NULL) {
93 result = strtok(line, " ");
94 }
95 return parse_int(result);
96 }
97
98 int count_cpus() {
99 char* procstat = read_file("/proc/stat");
100 if (procstat == NULL) {
101 fprintf(stderr, "Failed reading file /proc/stat: %s\n", strerror(errno));
102 return -1;
103 }
104
105 int count = -1;
106 while (*procstat) {
107 char* line = read_next_line(&procstat);
108 if (line == NULL) {
109 break;
110 }
111 if (strncmp("cpu", line, 3) == 0) {
112 ++count;
113 }
114 }
115 return count;
116 }
117
118 int read_cpu_percent() {
119 int num_cpus = count_cpus();
120
121 long tstart = get_time_nanos();
122 int idle_jiffy1 = read_cpu_idle_jiffys();
123 sleep_nanos(100000000L);
124 int idle_jiffy2 = read_cpu_idle_jiffys();
125 long tend = get_time_nanos();
126
127 double duration_sec = ((double)tend - (double)tstart) / 1000000000.0;
128 double idle_jiffys_per_second = (idle_jiffy2 - idle_jiffy1) / duration_sec;
129
130 double idle_jiffys_per_cpu_second = idle_jiffys_per_second / num_cpus;
131
132 // One jiffy is 10ms, so we can get the percentage very easily!
133 return 100 - (int)round(idle_jiffys_per_cpu_second);
134 }
135
136 int read_mem_free_mibis() {
137 char* meminfo = read_file("/proc/meminfo");
138 if (meminfo == NULL) {
139 fprintf(stderr, "Failed reading file /proc/meminfo: %s\n", strerror(errno));
140 return -1;
141 }
142
143 while (*meminfo) {
144 char* line = read_next_line(&meminfo);
145 if (line == NULL) {
146 break;
147 }
148
149 char* key = strtok(line, ": ");
150 char* value_str = strtok(NULL, ": ");
151
152 if (key == NULL || value_str == NULL) {
153 fprintf(stderr, "Failed to parse key/value token in /proc/meminfo\n");
154 return -1;
155 }
156
157 if (strcmp(key, "MemAvailable") == 0) {
158 int mem_available = parse_int(value_str);
159 return (int)round((double)mem_available / 1024);
160 }
161 }
162
163 fprintf(stderr, "Failed to find MemAvailable in /proc/meminfo\n");
164 return -1;
165 }
166
167 int read_zfs_arc_used_mibis() {
168 char* arcstats = read_file("/proc/spl/kstat/zfs/arcstats");
169 if (arcstats == NULL) {
170 return -1;
171 }
172
173 while (*arcstats) {
174 char* line = read_next_line(&arcstats);
175 if (line == NULL) {
176 break;
177 }
178
179 char* key = strtok(line, " ");
180 strtok(NULL, " ");
181 char* value_str = strtok(NULL, " ");
182
183 if (key == NULL || value_str == NULL) {
184 fprintf(stderr, "Failed to parse key/value token in /proc/spl/kstat/zfs/arcstats\n");
185 return -1;
186 }
187
188 if (strcmp(key, "size") == 0) {
189 long arc_used = parse_int(value_str);
190 return (int)round((double)arc_used / 1024 / 1024);
191 }
192 }
193
194 fprintf(stderr, "Failed to find 'c' in /proc/spl/kstat/zfs/arcstats\n");
195 return -1;
196 }
197
198 int read_battery_percent() {
199 char* percent_str = NULL;
200
201 if (percent_str == NULL) {
202 percent_str = read_file("/sys/class/power_supply/BAT0/capacity");
203 }
204
205 if (percent_str == NULL) {
206 percent_str = read_file("/sys/class/power_supply/BAT1/capacity");
207 }
208
209 if (percent_str == NULL) {
210 fprintf(stderr, "Failed reading file battery capacity file: %s\n", strerror(errno));
211 return -1;
212 }
213
214 return parse_int(percent_str);
215 }
216
217 void print_red_threshold(
218 char* name, char* units,
219 int value,
220 int red_low, int red_high
221 ) {
222 if (value < 0) {
223 return;
224 }
225
226 char* color = "black";
227 if (value >= red_low && value <= red_high) {
228 color = "red";
229 }
230
231 printf(
232 "%s <span color='%s'>%d</span>%s ",
233 name, color, value, units
234 );
235 }
236
237 int main(int argc, char** argv) {
238 char* show_flags = "cmb";
239 if (argc == 2) {
240 show_flags = argv[1];
241 }
242
243 printf("<txt>");
244
245 if (strchr(show_flags, 'c')) {
246 print_red_threshold(
247 "cpu", "%",
248 read_cpu_percent(),
249 50, 100
250 );
251 }
252
253 if (strchr(show_flags, 'm')) {
254 print_red_threshold(
255 "mem", " MiB",
256 read_mem_free_mibis() + read_zfs_arc_used_mibis(),
257 0, 512
258 );
259 }
260
261 if (strchr(show_flags, 'b')) {
262 print_red_threshold(
263 "batt", "%",
264 read_battery_percent(),
265 0, 25
266 );
267 }
268
269 printf("</txt>");
270
271 return 0;
272 }