Xinqi Bao's Git

fixed cpu_perc() stupidity && added some useful comment for drkh5h
[slstatus.git] / slstatus.c
1 /* See LICENSE file for copyright and license details. */
2
3 #include <alsa/asoundlib.h>
4 #include <err.h>
5 #include <fcntl.h>
6 #include <ifaddrs.h>
7 #include <limits.h>
8 #include <linux/wireless.h>
9 #include <netdb.h>
10 #include <pwd.h>
11 #include <stdarg.h>
12 #include <stdio.h>
13 #include <stdlib.h>
14 #include <string.h>
15 #include <sys/ioctl.h>
16 #include <sys/stat.h>
17 #include <sys/statvfs.h>
18 #include <sys/socket.h>
19 #include <sys/sysinfo.h>
20 #include <sys/types.h>
21 #include <time.h>
22 #include <unistd.h>
23 #include <X11/Xlib.h>
24
25 #undef strlcat
26 #undef strlcpy
27
28 #include "strlcat.h"
29 #include "strlcpy.h"
30 #include "concat.h"
31
32 char concat[];
33
34 struct arg {
35 char *(*func)();
36 const char *format;
37 const char *args;
38 };
39
40 static unsigned short int delay;
41
42 static char *smprintf(const char *, ...);
43 static char *battery_perc(const char *);
44 static char *cpu_perc(void);
45 static char *datetime(const char *);
46 static char *disk_free(const char *);
47 static char *disk_perc(const char *);
48 static char *disk_total(const char *);
49 static char *disk_used(const char *);
50 static char *entropy(void);
51 static char *gid(void);
52 static char *hostname(void);
53 static char *ip(const char *);
54 static char *load_avg(void);
55 static char *ram_free(void);
56 static char *ram_perc(void);
57 static char *ram_used(void);
58 static char *ram_total(void);
59 static char *run_command(const char *);
60 static char *temp(const char *);
61 static char *uid(void);
62 static char *uptime(void);
63 static char *username(void);
64 static char *vol_perc(const char *);
65 static char *wifi_perc(const char *);
66 static char *wifi_essid(const char *);
67
68 static Display *dpy;
69
70 #include "config.h"
71
72 static char *
73 smprintf(const char *fmt, ...)
74 {
75 va_list ap;
76 char *ret;
77 int len;
78
79 va_start(ap, fmt);
80 len = vsnprintf(NULL, 0, fmt, ap);
81 va_end(ap);
82
83 ret = malloc(++len);
84 if (ret == NULL) {
85 perror("malloc");
86 exit(1);
87 }
88
89 va_start(ap, fmt);
90 vsnprintf(ret, len, fmt, ap);
91 va_end(ap);
92
93 return ret;
94 }
95
96 static char *
97 battery_perc(const char *battery)
98 {
99 int now, full, perc;
100 FILE *fp;
101
102 ccat(4, BATTERY_PATH, battery, "/", BATTERY_NOW);
103
104 fp = fopen(concat, "r");
105 if (fp == NULL) {
106 warn("Error opening battery file: %s", concat);
107 return smprintf(UNKNOWN_STR);
108 }
109
110 fscanf(fp, "%i", &now);
111 fclose(fp);
112
113 ccat(4, BATTERY_PATH, battery, "/", BATTERY_FULL);
114
115 fp = fopen(concat, "r");
116 if (fp == NULL) {
117 warn("Error opening battery file: %s", concat);
118 return smprintf(UNKNOWN_STR);
119 }
120
121 fscanf(fp, "%i", &full);
122 fclose(fp);
123
124 perc = now / (full / 100);
125
126 return smprintf("%d%%", perc);
127 }
128
129 static char *
130 cpu_perc(void)
131 {
132 int perc;
133 long double a[4], b[4];
134 FILE *fp = fopen("/proc/stat","r");
135
136 if (fp == NULL) {
137 warn("Error opening stat file");
138 return smprintf(UNKNOWN_STR);
139 }
140
141 fscanf(fp, "%*s %Lf %Lf %Lf %Lf", &a[0], &a[1], &a[2], &a[3]);
142 fclose(fp);
143
144 delay = (UPDATE_INTERVAL - (UPDATE_INTERVAL - 1));
145 sleep(delay);
146
147 fp = fopen("/proc/stat","r");
148 if (fp == NULL) {
149 warn("Error opening stat file");
150 return smprintf(UNKNOWN_STR);
151 }
152
153 fscanf(fp, "%*s %Lf %Lf %Lf %Lf", &b[0], &b[1], &b[2], &b[3]);
154 fclose(fp);
155 perc = 100 * ((b[0]+b[1]+b[2]) - (a[0]+a[1]+a[2])) / ((b[0]+b[1]+b[2]+b[3]) - (a[0]+a[1]+a[2]+a[3]));
156
157 return smprintf("%d%%", perc);
158 }
159
160 static char *
161 datetime(const char *timeformat)
162 {
163 time_t t;
164 char timestr[80];
165
166 t = time(NULL);
167 if (strftime(timestr, sizeof(timestr), timeformat, localtime(&t)) == 0)
168 return smprintf(UNKNOWN_STR);
169
170 return smprintf("%s", timestr);
171 }
172
173 static char *
174 disk_free(const char *mountpoint)
175 {
176 struct statvfs fs;
177
178 if (statvfs(mountpoint, &fs) < 0) {
179 warn("Could not get filesystem info");
180 return smprintf(UNKNOWN_STR);
181 }
182
183 return smprintf("%f", (float)fs.f_bsize * (float)fs.f_bfree / 1024 / 1024 / 1024);
184 }
185
186 static char *
187 disk_perc(const char *mountpoint)
188 {
189 int perc = 0;
190 struct statvfs fs;
191
192 if (statvfs(mountpoint, &fs) < 0) {
193 warn("Could not get filesystem info");
194 return smprintf(UNKNOWN_STR);
195 }
196
197 perc = 100 * (1.0f - ((float)fs.f_bfree / (float)fs.f_blocks));
198
199 return smprintf("%d%%", perc);
200 }
201
202 static char *
203 disk_total(const char *mountpoint)
204 {
205 struct statvfs fs;
206
207 if (statvfs(mountpoint, &fs) < 0) {
208 warn("Could not get filesystem info");
209 return smprintf(UNKNOWN_STR);
210 }
211
212 return smprintf("%f", (float)fs.f_bsize * (float)fs.f_blocks / 1024 / 1024 / 1024);
213 }
214
215 static char *
216 disk_used(const char *mountpoint)
217 {
218 struct statvfs fs;
219
220 if (statvfs(mountpoint, &fs) < 0) {
221 warn("Could not get filesystem info");
222 return smprintf(UNKNOWN_STR);
223 }
224
225 return smprintf("%f", (float)fs.f_bsize * ((float)fs.f_blocks - (float)fs.f_bfree) / 1024 / 1024 / 1024);
226 }
227
228 static char *
229 entropy(void)
230 {
231 int entropy = 0;
232 FILE *fp = fopen("/proc/sys/kernel/random/entropy_avail", "r");
233
234 if (fp == NULL) {
235 warn("Could not open entropy file");
236 return smprintf(UNKNOWN_STR);
237 }
238
239 fscanf(fp, "%d", &entropy);
240 fclose(fp);
241
242 return smprintf("%d", entropy);
243 }
244
245 static char *
246 gid(void)
247 {
248 return smprintf("%d", getgid());
249 }
250
251 static char *
252 hostname(void)
253 {
254 char hostname[HOST_NAME_MAX];
255 FILE *fp = fopen("/proc/sys/kernel/hostname", "r");
256
257 if (fp == NULL) {
258 warn("Could not open hostname file");
259 return smprintf(UNKNOWN_STR);
260 }
261
262 fgets(hostname, sizeof(hostname), fp);
263 /* FIXME: needs improvement */
264 memset(&hostname[strlen(hostname)-1], '\0',
265 sizeof(hostname) - strlen(hostname));
266 fclose(fp);
267
268 return smprintf("%s", hostname);
269 }
270
271 static char *
272 ip(const char *interface)
273 {
274 struct ifaddrs *ifaddr, *ifa;
275 int s;
276 char host[NI_MAXHOST];
277
278 if (getifaddrs(&ifaddr) == -1) {
279 warn("Error getting IP address");
280 return smprintf(UNKNOWN_STR);
281 }
282
283 for (ifa = ifaddr; ifa != NULL; ifa = ifa->ifa_next) {
284 if (ifa->ifa_addr == NULL)
285 continue;
286
287 s = getnameinfo(ifa->ifa_addr, sizeof(struct sockaddr_in), host, NI_MAXHOST,
288 NULL, 0, NI_NUMERICHOST);
289
290 if ((strcmp(ifa->ifa_name, interface) == 0) && (ifa->ifa_addr->sa_family == AF_INET)) {
291 if (s != 0) {
292 warnx("Error getting IP address.");
293 return smprintf(UNKNOWN_STR);
294 }
295 return smprintf("%s", host);
296 }
297 }
298
299 freeifaddrs(ifaddr);
300
301 return smprintf(UNKNOWN_STR);
302 }
303
304 static char *
305 load_avg(void)
306 {
307 double avgs[3];
308
309 if (getloadavg(avgs, 3) < 0) {
310 warnx("Error getting load avg.");
311 return smprintf(UNKNOWN_STR);
312 }
313
314 return smprintf("%.2f %.2f %.2f", avgs[0], avgs[1], avgs[2]);
315 }
316
317 static char *
318 ram_free(void)
319 {
320 long free;
321 FILE *fp = fopen("/proc/meminfo", "r");
322
323 if (fp == NULL) {
324 warn("Error opening meminfo file");
325 return smprintf(UNKNOWN_STR);
326 }
327
328 fscanf(fp, "MemFree: %ld kB\n", &free);
329 fclose(fp);
330
331 return smprintf("%f", (float)free / 1024 / 1024);
332 }
333
334 static char *
335 ram_perc(void)
336 {
337 int perc;
338 long total, free, buffers, cached;
339 FILE *fp = fopen("/proc/meminfo", "r");
340
341 if (fp == NULL) {
342 warn("Error opening meminfo file");
343 return smprintf(UNKNOWN_STR);
344 }
345
346 fscanf(fp, "MemTotal: %ld kB\n", &total);
347 fscanf(fp, "MemFree: %ld kB\n", &free);
348 fscanf(fp, "MemAvailable: %ld kB\nBuffers: %ld kB\n", &buffers, &buffers);
349 fscanf(fp, "Cached: %ld kB\n", &cached);
350
351 fclose(fp);
352 perc = 100 * ((total - free) - (buffers + cached)) / total;
353
354 return smprintf("%d%%", perc);
355 }
356
357 static char *
358 ram_total(void)
359 {
360 long total;
361 FILE *fp = fopen("/proc/meminfo", "r");
362
363 if (fp == NULL) {
364 warn("Error opening meminfo file");
365 return smprintf(UNKNOWN_STR);
366 }
367
368 fscanf(fp, "MemTotal: %ld kB\n", &total);
369 fclose(fp);
370
371 return smprintf("%f", (float)total / 1024 / 1024);
372 }
373
374 static char *
375 ram_used(void)
376 {
377 long free, total, buffers, cached, used;
378 FILE *fp = fopen("/proc/meminfo", "r");
379
380 if (fp == NULL) {
381 warn("Error opening meminfo file");
382 return smprintf(UNKNOWN_STR);
383 }
384
385 fscanf(fp, "MemTotal: %ld kB\n", &total);
386 fscanf(fp, "MemFree: %ld kB\n", &free);
387 fscanf(fp, "MemAvailable: %ld kB\nBuffers: %ld kB\n", &buffers, &buffers);
388 fscanf(fp, "Cached: %ld kB\n", &cached);
389
390 fclose(fp);
391 used = total - free - buffers - cached;
392
393 return smprintf("%f", (float)used / 1024 / 1024);
394 }
395
396 static char *
397 run_command(const char* command)
398 {
399 int good;
400 FILE *fp = popen(command, "r");
401 char buffer[64] = "";
402
403 if (fp == NULL) {
404 warn("Could not get command output for: %s", command);
405 return smprintf(UNKNOWN_STR);
406 }
407
408 fgets(buffer, sizeof(buffer)-1, fp);
409 pclose(fp);
410 for (int i = 0 ; i != sizeof(buffer); i++) {
411 if (buffer[i] == '\0') {
412 good = 1;
413 break;
414 }
415 }
416 if (good)
417 buffer[strlen(buffer)-1] = '\0';
418
419 return smprintf("%s", buffer);
420 }
421
422 static char *
423 temp(const char *file)
424 {
425 int temperature;
426 FILE *fp = fopen(file, "r");
427
428 if (fp == NULL) {
429 warn("Could not open temperature file");
430 return smprintf(UNKNOWN_STR);
431 }
432
433 fscanf(fp, "%d", &temperature);
434 fclose(fp);
435
436 return smprintf("%d°C", temperature / 1000);
437 }
438
439 static char *
440 uptime(void)
441 {
442 struct sysinfo info;
443 int hours = 0;
444 int minutes = 0;
445
446 sysinfo(&info);
447 hours = info.uptime / 3600;
448 minutes = (info.uptime - hours * 3600 ) / 60;
449
450 return smprintf("%dh %dm", hours, minutes);
451 }
452
453 static char *
454 username(void)
455 {
456 uid_t uid = geteuid();
457 struct passwd *pw = getpwuid(uid);
458
459 if (pw == NULL) {
460 warn("Could not get username");
461 return smprintf(UNKNOWN_STR);
462 }
463
464 return smprintf("%s", pw->pw_name);
465 }
466
467 static char *
468 uid(void)
469 {
470 return smprintf("%d", geteuid());
471 }
472
473
474 static char *
475 vol_perc(const char *snd_card)
476 { /* FIX THIS SHIT! */
477 long int vol, max, min;
478 snd_mixer_t *handle;
479 snd_mixer_elem_t *elem;
480 snd_mixer_selem_id_t *s_elem;
481
482 snd_mixer_open(&handle, 0);
483 snd_mixer_attach(handle, "default");
484 snd_mixer_selem_register(handle, NULL, NULL);
485 snd_mixer_load(handle);
486 snd_mixer_selem_id_malloc(&s_elem);
487 snd_mixer_selem_id_set_name(s_elem, snd_card);
488 elem = snd_mixer_find_selem(handle, s_elem);
489
490 if (elem == NULL) {
491 snd_mixer_selem_id_free(s_elem);
492 snd_mixer_close(handle);
493 perror("alsa error");
494 return smprintf(UNKNOWN_STR);
495 }
496
497 snd_mixer_handle_events(handle);
498 snd_mixer_selem_get_playback_volume_range(elem, &min, &max);
499 snd_mixer_selem_get_playback_volume(elem, 0, &vol);
500
501 snd_mixer_selem_id_free(s_elem);
502 snd_mixer_close(handle);
503
504 return smprintf("%d", (vol * 100) / max);
505 }
506
507 static char *
508 wifi_perc(const char *wificard)
509 {
510 int strength;
511 char buf[255];
512 char *datastart;
513 char status[5];
514 FILE *fp;
515
516 ccat(3, "/sys/class/net", wificard, "/operstate");
517
518 fp = fopen(concat, "r");
519
520 if(fp == NULL) {
521 warn("Error opening wifi operstate file");
522 return smprintf(UNKNOWN_STR);
523 }
524
525 fgets(status, 5, fp);
526 fclose(fp);
527 if(strcmp(status, "up\n") != 0)
528 return smprintf(UNKNOWN_STR);
529
530 fp = fopen("/proc/net/wireless", "r");
531 if (fp == NULL) {
532 warn("Error opening wireless file");
533 return smprintf(UNKNOWN_STR);
534 }
535
536 ccat(2, wificard, ":");
537 fgets(buf, sizeof(buf), fp);
538 fgets(buf, sizeof(buf), fp);
539 fgets(buf, sizeof(buf), fp);
540
541 datastart = strstr(buf, concat);
542 if (datastart != NULL) {
543 datastart = strstr(buf, ":");
544 sscanf(datastart + 1, " %*d %d %*d %*d %*d %*d %*d %*d %*d %*d", &strength);
545 }
546
547 fclose(fp);
548
549 return smprintf("%d%%", strength);
550 }
551
552 static char *
553 wifi_essid(const char *wificard)
554 {
555 char id[IW_ESSID_MAX_SIZE+1];
556 int sockfd = socket(AF_INET, SOCK_DGRAM, 0);
557 struct iwreq wreq;
558
559 memset(&wreq, 0, sizeof(struct iwreq));
560 wreq.u.essid.length = IW_ESSID_MAX_SIZE+1;
561 sprintf(wreq.ifr_name, wificard);
562 if(sockfd == -1) {
563 warn("Cannot open socket for interface: %s", wificard);
564 return smprintf(UNKNOWN_STR);
565 }
566 wreq.u.essid.pointer = id;
567 if (ioctl(sockfd,SIOCGIWESSID, &wreq) == -1) {
568 warn("Get ESSID ioctl failed for interface %s", wificard);
569 return smprintf(UNKNOWN_STR);
570 }
571
572 if (strcmp((char *)wreq.u.essid.pointer, "") == 0)
573 return smprintf(UNKNOWN_STR);
574 else
575 return smprintf("%s", (char *)wreq.u.essid.pointer);
576 }
577
578 int
579 main(void)
580 {
581 size_t i;
582 char status_string[4096];
583 char *res, *element;
584 struct arg argument;
585
586 dpy = XOpenDisplay(NULL);
587
588 for (;;) {
589 memset(status_string, 0, sizeof(status_string));
590 for (i = 0; i < sizeof(args) / sizeof(args[0]); ++i) {
591 argument = args[i];
592 if (argument.args == NULL)
593 res = argument.func();
594 else
595 res = argument.func(argument.args);
596 element = smprintf(argument.format, res);
597 if (element == NULL) {
598 element = smprintf(UNKNOWN_STR);
599 warnx("Failed to format output.");
600 }
601 strlcat(status_string, element, sizeof(status_string));
602 free(res);
603 free(element);
604 }
605 XStoreName(dpy, DefaultRootWindow(dpy), status_string);
606 XSync(dpy, False);
607 /*
608 * subtract delay time spend in function
609 * calls from the actual global delay time
610 */
611 sleep(UPDATE_INTERVAL - delay);
612 delay = 0;
613 }
614
615 XCloseDisplay(dpy);
616
617 return 0;
618 }