Xinqi Bao's Git

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