Xinqi Bao's Git

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