Xinqi Bao's Git

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