Xinqi Bao's Git

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