Xinqi Bao's Git

f72f99445f7c2e52d42a1ef5fd18cced6c9ac593
[slstatus.git] / slstatus.c
1 /* See LICENSE file for copyright and license details. */
2 #include <dirent.h>
3 #include <err.h>
4 #include <errno.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/soundcard.h>
21 #include <sys/sysinfo.h>
22 #include <sys/types.h>
23 #include <sys/utsname.h>
24 #include <time.h>
25 #include <unistd.h>
26 #include <X11/Xlib.h>
27
28 #include "arg.h"
29
30 #define LEN(x) (sizeof (x) / sizeof *(x))
31
32 struct arg {
33 const char *(*func)();
34 const char *fmt;
35 const char *args;
36 };
37
38 static const char *battery_perc(const char *bat);
39 static const char *battery_power(const char *bat);
40 static const char *battery_state(const char *bat);
41 static const char *cpu_freq(void);
42 static const char *cpu_perc(void);
43 static const char *datetime(const char *fmt);
44 static const char *disk_free(const char *mnt);
45 static const char *disk_perc(const char *mnt);
46 static const char *disk_total(const char *mnt);
47 static const char *disk_used(const char *mnt);
48 static const char *entropy(void);
49 static const char *gid(void);
50 static const char *hostname(void);
51 static const char *ip(const char *iface);
52 static const char *kernel_release(void);
53 static const char *keyboard_indicators(void);
54 static const char *load_avg(void);
55 static const char *num_files(const char *dir);
56 static const char *ram_free(void);
57 static const char *ram_perc(void);
58 static const char *ram_used(void);
59 static const char *ram_total(void);
60 static const char *run_command(const char *cmd);
61 static const char *swap_free(void);
62 static const char *swap_perc(void);
63 static const char *swap_used(void);
64 static const char *swap_total(void);
65 static const char *temp(const char *file);
66 static const char *uid(void);
67 static const char *uptime(void);
68 static const char *username(void);
69 static const char *vol_perc(const char *card);
70 static const char *wifi_perc(const char *iface);
71 static const char *wifi_essid(const char *iface);
72
73 char *argv0;
74 static unsigned short int done;
75 static Display *dpy;
76
77 #include "config.h"
78
79 static char buf[MAXLEN];
80
81 static const char *
82 bprintf(const char *fmt, ...)
83 {
84 va_list ap;
85 size_t len;
86
87 va_start(ap, fmt);
88 len = vsnprintf(buf, sizeof(buf) - 1, fmt, ap);
89 va_end(ap);
90
91 if (len >= sizeof(buf))
92 buf[sizeof(buf)-1] = '\0';
93
94 return buf;
95 }
96
97 int
98 pscanf(const char *path, const char *fmt, ...)
99 {
100 FILE *fp;
101 va_list ap;
102 int n;
103
104 if (!(fp = fopen(path, "r"))) {
105 warn("fopen %s: %s\n", path, strerror(errno));
106 return -1;
107 }
108 va_start(ap, fmt);
109 n = vfscanf(fp, fmt, ap);
110 va_end(ap);
111 fclose(fp);
112
113 return (n == EOF) ? -1 : n;
114 }
115
116 static const char *
117 battery_perc(const char *bat)
118 {
119 int perc;
120 char path[PATH_MAX];
121
122 snprintf(path, sizeof(path), "%s%s%s", "/sys/class/power_supply/", bat, "/capacity");
123 return (pscanf(path, "%i", &perc) == 1) ?
124 bprintf("%d", perc) : unknown_str;
125 }
126
127 static const char *
128 battery_power(const char *bat)
129 {
130 int watts;
131 char path[PATH_MAX];
132
133 snprintf(path, sizeof(path), "%s%s%s", "/sys/class/power_supply/", bat, "/power_now");
134 return (pscanf(path, "%i", &watts) == 1) ?
135 bprintf("%d", (watts + 500000) / 1000000) : unknown_str;
136 }
137
138 static const char *
139 battery_state(const char *bat)
140 {
141 struct {
142 char *state;
143 char *symbol;
144 } map[] = {
145 { "Charging", "+" },
146 { "Discharging", "-" },
147 { "Full", "=" },
148 { "Unknown", "/" },
149 };
150 size_t i;
151 char path[PATH_MAX], state[12];
152
153 snprintf(path, sizeof(path), "%s%s%s", "/sys/class/power_supply/", bat, "/status");
154 if (pscanf(path, "%12s", state) != 1) {
155 return unknown_str;
156 }
157
158 for (i = 0; i < LEN(map); i++) {
159 if (!strcmp(map[i].state, state)) {
160 break;
161 }
162 }
163 return (i == LEN(map)) ? "?" : map[i].symbol;
164 }
165
166 static const char *
167 cpu_freq(void)
168 {
169 int freq;
170
171 return (pscanf("/sys/devices/system/cpu/cpu0/cpufreq/scaling_cur_freq",
172 "%i", &freq) == 1) ?
173 bprintf("%d", (freq + 500) / 1000) : unknown_str;
174 }
175
176 static const char *
177 cpu_perc(void)
178 {
179 struct timespec delay;
180 int perc;
181 long double a[4], b[4];
182
183 if (pscanf("/proc/stat", "%*s %Lf %Lf %Lf %Lf", &a[0], &a[1], &a[2],
184 &a[3]) != 4) {
185 return unknown_str;
186 }
187
188 delay.tv_sec = (interval / 2) / 1000;
189 delay.tv_nsec = ((interval / 2) % 1000) * 1000000;
190 nanosleep(&delay, NULL);
191
192 if (pscanf("/proc/stat", "%*s %Lf %Lf %Lf %Lf", &b[0], &b[1], &b[2],
193 &b[3]) != 4) {
194 return unknown_str;
195 }
196
197 perc = 100 * ((b[0]+b[1]+b[2]) - (a[0]+a[1]+a[2])) /
198 ((b[0]+b[1]+b[2]+b[3]) - (a[0]+a[1]+a[2]+a[3]));
199
200 return bprintf("%d", perc);
201 }
202
203 static const char *
204 datetime(const char *fmt)
205 {
206 time_t t;
207
208 t = time(NULL);
209 if (strftime(buf, sizeof(buf), fmt, localtime(&t)) == 0)
210 return unknown_str;
211
212 return buf;
213 }
214
215 static const char *
216 disk_free(const char *mnt)
217 {
218 struct statvfs fs;
219
220 if (statvfs(mnt, &fs) < 0) {
221 warn("Failed to get filesystem info");
222 return unknown_str;
223 }
224
225 return bprintf("%f", (float)fs.f_bsize * (float)fs.f_bfree / 1024 / 1024 / 1024);
226 }
227
228 static const char *
229 disk_perc(const char *mnt)
230 {
231 int perc;
232 struct statvfs fs;
233
234 if (statvfs(mnt, &fs) < 0) {
235 warn("Failed to get filesystem info");
236 return unknown_str;
237 }
238
239 perc = 100 * (1.0f - ((float)fs.f_bfree / (float)fs.f_blocks));
240
241 return bprintf("%d", perc);
242 }
243
244 static const char *
245 disk_total(const char *mnt)
246 {
247 struct statvfs fs;
248
249 if (statvfs(mnt, &fs) < 0) {
250 warn("Failed to get filesystem info");
251 return unknown_str;
252 }
253
254 return bprintf("%f", (float)fs.f_bsize * (float)fs.f_blocks / 1024 / 1024 / 1024);
255 }
256
257 static const char *
258 disk_used(const char *mnt)
259 {
260 struct statvfs fs;
261
262 if (statvfs(mnt, &fs) < 0) {
263 warn("Failed to get filesystem info");
264 return unknown_str;
265 }
266
267 return bprintf("%f", (float)fs.f_bsize * ((float)fs.f_blocks - (float)fs.f_bfree) / 1024 / 1024 / 1024);
268 }
269
270 static const char *
271 entropy(void)
272 {
273 int num;
274
275 return (pscanf("/proc/sys/kernel/random/entropy_avail", "%d", &num) == 1) ?
276 bprintf("%d", num) : unknown_str;
277 }
278
279 static const char *
280 gid(void)
281 {
282 return bprintf("%d", getgid());
283 }
284
285 static const char *
286 hostname(void)
287 {
288 if (gethostname(buf, sizeof(buf)) == -1) {
289 warn("hostname");
290 return unknown_str;
291 }
292
293 return buf;
294 }
295
296 static const char *
297 ip(const char *iface)
298 {
299 struct ifaddrs *ifaddr, *ifa;
300 int s;
301 char host[NI_MAXHOST];
302
303 if (getifaddrs(&ifaddr) == -1) {
304 warn("Failed to get IP address for interface %s", iface);
305 return unknown_str;
306 }
307
308 for (ifa = ifaddr; ifa != NULL; ifa = ifa->ifa_next) {
309 if (ifa->ifa_addr == NULL) {
310 continue;
311 }
312 s = getnameinfo(ifa->ifa_addr, sizeof(struct sockaddr_in), host, NI_MAXHOST, NULL, 0, NI_NUMERICHOST);
313 if ((strcmp(ifa->ifa_name, iface) == 0) && (ifa->ifa_addr->sa_family == AF_INET)) {
314 if (s != 0) {
315 warnx("Failed to get IP address for interface %s", iface);
316 return unknown_str;
317 }
318 return bprintf("%s", host);
319 }
320 }
321
322 freeifaddrs(ifaddr);
323
324 return unknown_str;
325 }
326
327 static const char *
328 kernel_release(void)
329 {
330 struct utsname udata;
331
332 if (uname(&udata) < 0) {
333 return unknown_str;
334 }
335
336 return bprintf("%s", udata.release);
337 }
338
339 static const char *
340 keyboard_indicators(void)
341 {
342 Display *dpy = XOpenDisplay(NULL);
343 XKeyboardState state;
344
345 if (dpy == NULL) {
346 warnx("XOpenDisplay failed");
347 return unknown_str;
348 }
349 XGetKeyboardControl(dpy, &state);
350 XCloseDisplay(dpy);
351
352 switch (state.led_mask) {
353 case 1:
354 return "c";
355 case 2:
356 return "n";
357 case 3:
358 return "cn";
359 default:
360 return "";
361 }
362 }
363
364 static const char *
365 load_avg(void)
366 {
367 double avgs[3];
368
369 if (getloadavg(avgs, 3) < 0) {
370 warnx("Failed to get the load avg");
371 return unknown_str;
372 }
373
374 return bprintf("%.2f %.2f %.2f", avgs[0], avgs[1], avgs[2]);
375 }
376
377 static const char *
378 num_files(const char *dir)
379 {
380 struct dirent *dp;
381 DIR *fd;
382 int num = 0;
383
384 if ((fd = opendir(dir)) == NULL) {
385 warn("Failed to get number of files in directory %s", dir);
386 return unknown_str;
387 }
388
389 while ((dp = readdir(fd)) != NULL) {
390 if (!strcmp(dp->d_name, ".") || !strcmp(dp->d_name, ".."))
391 continue; /* skip self and parent */
392 num++;
393 }
394
395 closedir(fd);
396
397 return bprintf("%d", num);
398 }
399
400 static const char *
401 ram_free(void)
402 {
403 long free;
404
405 return (pscanf("/proc/meminfo", "MemFree: %ld kB\n", &free) == 1) ?
406 bprintf("%f", (float)free / 1024 / 1024) : unknown_str;
407 }
408
409 static const char *
410 ram_perc(void)
411 {
412 long total, free, buffers, cached;
413
414 return (pscanf("/proc/meminfo",
415 "MemTotal: %ld kB\n"
416 "MemFree: %ld kB\n"
417 "MemAvailable: %ld kB\nBuffers: %ld kB\n"
418 "Cached: %ld kB\n",
419 &total, &free, &buffers, &buffers, &cached) == 5) ?
420 bprintf("%d", 100 * ((total - free) - (buffers + cached)) / total) :
421 unknown_str;
422 }
423
424 static const char *
425 ram_total(void)
426 {
427 long total;
428
429 return (pscanf("/proc/meminfo", "MemTotal: %ld kB\n", &total) == 1) ?
430 bprintf("%f", (float)total / 1024 / 1024) : unknown_str;
431 }
432
433 static const char *
434 ram_used(void)
435 {
436 long total, free, buffers, cached;
437
438 return (pscanf("/proc/meminfo",
439 "MemTotal: %ld kB\n"
440 "MemFree: %ld kB\n"
441 "MemAvailable: %ld kB\nBuffers: %ld kB\n"
442 "Cached: %ld kB\n",
443 &total, &free, &buffers, &buffers, &cached) == 5) ?
444 bprintf("%f", (float)(total - free - buffers - cached) / 1024 / 1024) :
445 unknown_str;
446 }
447
448 static const char *
449 run_command(const char *cmd)
450 {
451 char *p;
452 FILE *fp;
453
454 fp = popen(cmd, "r");
455 if (fp == NULL) {
456 warn("Failed to get command output for %s", cmd);
457 return unknown_str;
458 }
459 p = fgets(buf, sizeof(buf) - 1, fp);
460 pclose(fp);
461 if (!p)
462 return unknown_str;
463 if ((p = strrchr(buf, '\n')) != NULL)
464 p[0] = '\0';
465
466 return buf[0] ? buf : unknown_str;
467 }
468
469 static const char *
470 swap_free(void)
471 {
472 long total, free;
473 FILE *fp;
474 size_t bytes_read;
475 char *match;
476
477 fp = fopen("/proc/meminfo", "r");
478 if (fp == NULL) {
479 warn("Failed to open file /proc/meminfo");
480 return unknown_str;
481 }
482
483 if ((bytes_read = fread(buf, sizeof(char), sizeof(buf) - 1, fp)) == 0) {
484 warn("swap_free: read error");
485 fclose(fp);
486 return unknown_str;
487 }
488 fclose(fp);
489
490 if ((match = strstr(buf, "SwapTotal")) == NULL)
491 return unknown_str;
492 sscanf(match, "SwapTotal: %ld kB\n", &total);
493
494 if ((match = strstr(buf, "SwapFree")) == NULL)
495 return unknown_str;
496 sscanf(match, "SwapFree: %ld kB\n", &free);
497
498 return bprintf("%f", (float)free / 1024 / 1024);
499 }
500
501 static const char *
502 swap_perc(void)
503 {
504 long total, free, cached;
505 FILE *fp;
506 size_t bytes_read;
507 char *match;
508
509 fp = fopen("/proc/meminfo", "r");
510 if (fp == NULL) {
511 warn("Failed to open file /proc/meminfo");
512 return unknown_str;
513 }
514
515 if ((bytes_read = fread(buf, sizeof(char), sizeof(buf) - 1, fp)) == 0) {
516 warn("swap_perc: read error");
517 fclose(fp);
518 return unknown_str;
519 }
520 fclose(fp);
521
522 if ((match = strstr(buf, "SwapTotal")) == NULL)
523 return unknown_str;
524 sscanf(match, "SwapTotal: %ld kB\n", &total);
525
526 if ((match = strstr(buf, "SwapCached")) == NULL)
527 return unknown_str;
528 sscanf(match, "SwapCached: %ld kB\n", &cached);
529
530 if ((match = strstr(buf, "SwapFree")) == NULL)
531 return unknown_str;
532 sscanf(match, "SwapFree: %ld kB\n", &free);
533
534 return bprintf("%d", 100 * (total - free - cached) / total);
535 }
536
537 static const char *
538 swap_total(void)
539 {
540 long total;
541 FILE *fp;
542 size_t bytes_read;
543 char *match;
544
545 fp = fopen("/proc/meminfo", "r");
546 if (fp == NULL) {
547 warn("Failed to open file /proc/meminfo");
548 return unknown_str;
549 }
550 if ((bytes_read = fread(buf, sizeof(char), sizeof(buf) - 1, fp)) == 0) {
551 warn("swap_total: read error");
552 fclose(fp);
553 return unknown_str;
554 }
555 fclose(fp);
556
557 if ((match = strstr(buf, "SwapTotal")) == NULL)
558 return unknown_str;
559 sscanf(match, "SwapTotal: %ld kB\n", &total);
560
561 return bprintf("%f", (float)total / 1024 / 1024);
562 }
563
564 static const char *
565 swap_used(void)
566 {
567 long total, free, cached;
568 FILE *fp;
569 size_t bytes_read;
570 char *match;
571
572 fp = fopen("/proc/meminfo", "r");
573 if (fp == NULL) {
574 warn("Failed to open file /proc/meminfo");
575 return unknown_str;
576 }
577 if ((bytes_read = fread(buf, sizeof(char), sizeof(buf) - 1, fp)) == 0) {
578 warn("swap_used: read error");
579 fclose(fp);
580 return unknown_str;
581 }
582 fclose(fp);
583
584 if ((match = strstr(buf, "SwapTotal")) == NULL)
585 return unknown_str;
586 sscanf(match, "SwapTotal: %ld kB\n", &total);
587
588 if ((match = strstr(buf, "SwapCached")) == NULL)
589 return unknown_str;
590 sscanf(match, "SwapCached: %ld kB\n", &cached);
591
592 if ((match = strstr(buf, "SwapFree")) == NULL)
593 return unknown_str;
594 sscanf(match, "SwapFree: %ld kB\n", &free);
595
596 return bprintf("%f", (float)(total - free - cached) / 1024 / 1024);
597 }
598
599 static const char *
600 temp(const char *file)
601 {
602 int temp;
603
604 return (pscanf(file, "%d", &temp) == 1) ?
605 bprintf("%d", temp / 1000) : unknown_str;
606 }
607
608 static const char *
609 uptime(void)
610 {
611 struct sysinfo info;
612 int h = 0;
613 int m = 0;
614
615 sysinfo(&info);
616 h = info.uptime / 3600;
617 m = (info.uptime - h * 3600 ) / 60;
618
619 return bprintf("%dh %dm", h, m);
620 }
621
622 static const char *
623 username(void)
624 {
625 struct passwd *pw = getpwuid(geteuid());
626
627 if (pw == NULL) {
628 warn("Failed to get username");
629 return unknown_str;
630 }
631
632 return bprintf("%s", pw->pw_name);
633 }
634
635 static const char *
636 uid(void)
637 {
638 return bprintf("%d", geteuid());
639 }
640
641
642 static const char *
643 vol_perc(const char *card)
644 {
645 unsigned int i;
646 int v, afd, devmask;
647 char *vnames[] = SOUND_DEVICE_NAMES;
648
649 afd = open(card, O_RDONLY | O_NONBLOCK);
650 if (afd == -1) {
651 warn("Cannot open %s", card);
652 return unknown_str;
653 }
654
655 if (ioctl(afd, SOUND_MIXER_READ_DEVMASK, &devmask) == -1) {
656 warn("Cannot get volume for %s", card);
657 close(afd);
658 return unknown_str;
659 }
660 for (i = 0; i < LEN(vnames); i++) {
661 if (devmask & (1 << i) && !strcmp("vol", vnames[i])) {
662 if (ioctl(afd, MIXER_READ(i), &v) == -1) {
663 warn("vol_perc: ioctl");
664 close(afd);
665 return unknown_str;
666 }
667 }
668 }
669
670 close(afd);
671
672 return bprintf("%d", v & 0xff);
673 }
674
675 static const char *
676 wifi_perc(const char *iface)
677 {
678 int i, perc;
679 char *p, *datastart;
680 char path[PATH_MAX];
681 char status[5];
682 FILE *fp;
683
684 snprintf(path, sizeof(path), "%s%s%s", "/sys/class/net/", iface, "/operstate");
685 fp = fopen(path, "r");
686 if (fp == NULL) {
687 warn("Failed to open file %s", path);
688 return unknown_str;
689 }
690 p = fgets(status, 5, fp);
691 fclose(fp);
692 if(!p || strcmp(status, "up\n") != 0) {
693 return unknown_str;
694 }
695
696 fp = fopen("/proc/net/wireless", "r");
697 if (fp == NULL) {
698 warn("Failed to open file /proc/net/wireless");
699 return unknown_str;
700 }
701
702 for (i = 0; i < 3; i++) {
703 if (!(p = fgets(buf, sizeof(buf) - 1, fp)))
704 break;
705 }
706 fclose(fp);
707 if (i < 2 || !p)
708 return unknown_str;
709
710 if ((datastart = strstr(buf, iface)) == NULL)
711 return unknown_str;
712
713 datastart = (datastart+(strlen(iface)+1));
714 sscanf(datastart + 1, " %*d %d %*d %*d %*d %*d %*d %*d %*d %*d", &perc);
715
716 return bprintf("%d", perc);
717 }
718
719 static const char *
720 wifi_essid(const char *iface)
721 {
722 static char id[IW_ESSID_MAX_SIZE+1];
723 int sockfd = socket(AF_INET, SOCK_DGRAM, 0);
724 struct iwreq wreq;
725
726 memset(&wreq, 0, sizeof(struct iwreq));
727 wreq.u.essid.length = IW_ESSID_MAX_SIZE+1;
728 snprintf(wreq.ifr_name, sizeof(wreq.ifr_name), "%s", iface);
729
730 if (sockfd == -1) {
731 warn("Failed to get ESSID for interface %s", iface);
732 return unknown_str;
733 }
734 wreq.u.essid.pointer = id;
735 if (ioctl(sockfd,SIOCGIWESSID, &wreq) == -1) {
736 warn("Failed to get ESSID for interface %s", iface);
737 return unknown_str;
738 }
739
740 close(sockfd);
741
742 if (strcmp(id, "") == 0)
743 return unknown_str;
744 else
745 return id;
746 }
747
748 static void
749 terminate(const int signo)
750 {
751 done = 1;
752 }
753
754 static void
755 difftimespec(struct timespec *res, struct timespec *a, struct timespec *b)
756 {
757 res->tv_sec = a->tv_sec - b->tv_sec - (a->tv_nsec < b->tv_nsec);
758 res->tv_nsec = a->tv_nsec - b->tv_nsec +
759 (a->tv_nsec < b->tv_nsec) * 1000000000;
760 }
761
762 static void
763 usage(void)
764 {
765 fprintf(stderr, "usage: %s [-s]\n", argv0);
766 exit(1);
767 }
768
769 int
770 main(int argc, char *argv[])
771 {
772 struct sigaction act;
773 struct timespec start, current, diff, intspec, wait;
774 size_t i, len;
775 int sflag = 0;
776 char status[MAXLEN];
777
778 ARGBEGIN {
779 case 's':
780 sflag = 1;
781 break;
782 default:
783 usage();
784 } ARGEND
785
786 if (argc) {
787 usage();
788 }
789
790 memset(&act, 0, sizeof(act));
791 act.sa_handler = terminate;
792 sigaction(SIGINT, &act, NULL);
793 sigaction(SIGTERM, &act, NULL);
794
795 if (!sflag && !(dpy = XOpenDisplay(NULL))) {
796 fprintf(stderr, "slstatus: cannot open display");
797 return 1;
798 }
799
800 while (!done) {
801 clock_gettime(CLOCK_MONOTONIC, &start);
802
803 status[0] = '\0';
804 for (i = len = 0; i < LEN(args); i++) {
805 len += snprintf(status + len, sizeof(status) - len,
806 args[i].fmt, args[i].func(args[i].args));
807
808 if (len >= sizeof(status)) {
809 status[sizeof(status) - 1] = '\0';
810 }
811 }
812
813 if (sflag) {
814 printf("%s\n", status);
815 } else {
816 XStoreName(dpy, DefaultRootWindow(dpy), status);
817 XSync(dpy, False);
818 }
819
820 if (!done) {
821 clock_gettime(CLOCK_MONOTONIC, &current);
822 difftimespec(&diff, &current, &start);
823
824 intspec.tv_sec = interval / 1000;
825 intspec.tv_nsec = (interval % 1000) * 1000000;
826 difftimespec(&wait, &intspec, &diff);
827
828 if (wait.tv_sec >= 0) {
829 nanosleep(&wait, NULL);
830 }
831 }
832 }
833
834 if (!sflag) {
835 XStoreName(dpy, DefaultRootWindow(dpy), NULL);
836 XCloseDisplay(dpy);
837 }
838
839 return 0;
840 }