]> code.delx.au - monosys/blob - xfce4-genmon-script.c
split-mvimg
[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_meminfo(int* mem_free_mibis, int* mem_total_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 *mem_free_mibis = -1;
144 *mem_total_mibis = -1;
145
146 while (*meminfo) {
147 char* line = read_next_line(&meminfo);
148 if (line == NULL) {
149 break;
150 }
151
152 char* key = strtok(line, ": ");
153 char* value_str = strtok(NULL, ": ");
154
155 if (key == NULL || value_str == NULL) {
156 fprintf(stderr, "Failed to parse key/value token in /proc/meminfo\n");
157 return -1;
158 }
159
160 if (strcmp(key, "MemAvailable") == 0) {
161 int mem_available = parse_int(value_str);
162 *mem_free_mibis = (int)round((double)mem_available / 1024);
163 }
164
165 if (strcmp(key, "MemTotal") == 0) {
166 int mem_available = parse_int(value_str);
167 *mem_total_mibis = (int)round((double)mem_available / 1024);
168 }
169 }
170
171 if (*mem_free_mibis < 0 || *mem_total_mibis < 0) {
172 fprintf(stderr, "Failed to find field in /proc/meminfo\n");
173 return -1;
174 } else {
175 return 0;
176 }
177 }
178
179 int read_zfs_arc_used_mibis() {
180 char* arcstats = read_file("/proc/spl/kstat/zfs/arcstats");
181 if (arcstats == NULL) {
182 return -1;
183 }
184
185 while (*arcstats) {
186 char* line = read_next_line(&arcstats);
187 if (line == NULL) {
188 break;
189 }
190
191 char* key = strtok(line, " ");
192 strtok(NULL, " ");
193 char* value_str = strtok(NULL, " ");
194
195 if (key == NULL || value_str == NULL) {
196 fprintf(stderr, "Failed to parse key/value token in /proc/spl/kstat/zfs/arcstats\n");
197 return -1;
198 }
199
200 if (strcmp(key, "size") == 0) {
201 long arc_used = parse_int(value_str);
202 return (int)round((double)arc_used / 1024 / 1024);
203 }
204 }
205
206 fprintf(stderr, "Failed to find 'c' in /proc/spl/kstat/zfs/arcstats\n");
207 return -1;
208 }
209
210 int read_battery_percent() {
211 char* percent_str = NULL;
212
213 if (percent_str == NULL) {
214 percent_str = read_file("/sys/class/power_supply/BAT0/capacity");
215 }
216
217 if (percent_str == NULL) {
218 percent_str = read_file("/sys/class/power_supply/BAT1/capacity");
219 }
220
221 if (percent_str == NULL) {
222 fprintf(stderr, "Failed reading file battery capacity file: %s\n", strerror(errno));
223 return -1;
224 }
225
226 return parse_int(percent_str);
227 }
228
229 void print_red_threshold(
230 char* name, char* units,
231 int value,
232 int red_low, int red_high
233 ) {
234 if (value < 0) {
235 return;
236 }
237
238 char* color = "black";
239 if (value >= red_low && value <= red_high) {
240 color = "red";
241 }
242
243 printf(
244 "%s <span color='%s'>%d</span>%s\n",
245 name, color, value, units
246 );
247 }
248
249 int main(int argc, char** argv) {
250 char* show_flags = "cmb";
251 char* top_padding = "0";
252 if (argc >= 2) {
253 show_flags = argv[1];
254 }
255 if (argc >= 3) {
256 top_padding = argv[2];
257 }
258
259 printf("<txt><span size=\"%s\">\n</span>", top_padding);
260
261 if (strchr(show_flags, 'c')) {
262 print_red_threshold(
263 "cpu", "%",
264 read_cpu_percent(),
265 50, 100
266 );
267 }
268
269 if (strchr(show_flags, 'm')) {
270 int mem_free_mibis, mem_total_mibis;
271 read_meminfo(&mem_free_mibis, &mem_total_mibis);
272 print_red_threshold(
273 "mem", " MiB",
274 mem_free_mibis + read_zfs_arc_used_mibis(),
275 0, mem_total_mibis / 10
276 );
277 }
278
279 if (strchr(show_flags, 'b')) {
280 print_red_threshold(
281 "batt", "%",
282 read_battery_percent(),
283 0, 25
284 );
285 }
286
287 printf("</txt>");
288
289 return 0;
290 }