Xinqi Bao's Git

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