Xinqi Bao's Git

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