Xinqi Bao's Git

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