Xinqi Bao's Git

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