Xinqi Bao's Git

505f25d7dab88203c36caf2505fae75e8824e246
[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, "MemTotal: %*d kB\n");
363 fscanf(fp, "MemFree: %ld kB\n", &free);
364
365 /* close meminfo file */
366 fclose(fp);
367
368 /* return free ram as string */
369 return smprintf("%f", (float)free / 1024 / 1024);
370 }
371
372 /* ram percentage */
373 char *
374 ram_perc(const char *null)
375 {
376 int perc;
377 long total, free, buffers, cached;
378 FILE *fp;
379
380 /* open meminfo file */
381 if (!(fp = fopen("/proc/meminfo", "r"))) {
382 fprintf(stderr, "Error opening meminfo file.");
383 return smprintf("n/a");
384 }
385
386 /* read the values */
387 fscanf(fp, "MemTotal: %ld kB\n", &total);
388 fscanf(fp, "MemFree: %ld kB\n", &free);
389 fscanf(fp, "MemAvailable: %ld kB\nBuffers: %ld kB\n", &buffers, &buffers);
390 fscanf(fp, "Cached: %ld kB\n", &cached);
391
392 /* close meminfo file */
393 fclose(fp);
394
395 /* calculate percentage */
396 perc = 100 * ((total - free) - (buffers + cached)) / total;
397
398 /* return perc as string */
399 return smprintf("%d%%", perc);
400 }
401
402 /* ram total */
403 char *
404 ram_total(const char *null)
405 {
406 long total;
407 FILE *fp;
408
409 /* open meminfo file */
410 if (!(fp = fopen("/proc/meminfo", "r"))) {
411 fprintf(stderr, "Error opening meminfo file.");
412 return smprintf("n/a");
413 }
414
415 /* read the values */
416 fscanf(fp, "MemTotal: %ld kB\n", &total);
417
418 /* close meminfo file */
419 fclose(fp);
420
421 /* return total ram as string */
422 return smprintf("%f", (float)total / 1024 / 1024);
423 }
424
425 /* ram used */
426 char *
427 ram_used(const char *null)
428 {
429 long free, total, buffers, cached, used;
430 FILE *fp;
431
432 /* open meminfo file */
433 if (!(fp = fopen("/proc/meminfo", "r"))) {
434 fprintf(stderr, "Error opening meminfo file.");
435 return smprintf("n/a");
436 }
437
438 /* read the values */
439 fscanf(fp, "MemTotal: %ld kB\n", &total);
440 fscanf(fp, "MemFree: %ld kB\n", &free);
441 fscanf(fp, "MemAvailable: %ld kB\nBuffers: %ld kB\n", &buffers, &buffers);
442 fscanf(fp, "Cached: %ld kB\n", &cached);
443
444 /* close meminfo file */
445 fclose(fp);
446
447 /* calculate used */
448 used = total - free - buffers - cached;
449
450 /* return used ram as string */
451 return smprintf("%f", (float)used / 1024 / 1024);
452 }
453
454 /* custom shell command */
455 char *
456 run_command(const char* command)
457 {
458 int good;
459 FILE *fp;
460 char buffer[64];
461
462 /* try to open the command output */
463 if (!(fp = popen(command, "r"))) {
464 fprintf(stderr, "Could not get command output for: %s.\n", command);
465 return smprintf("n/a");
466 }
467
468 /* get command output text, save it to buffer */
469 fgets(buffer, sizeof(buffer)-1, fp);
470
471 /* close it again */
472 pclose(fp);
473
474 /* add nullchar at the end */
475 for (int i = 0 ; i != sizeof(buffer) ; i++) {
476 if (buffer[i] == '\0') {
477 good = 1;
478 break;
479 }
480 }
481 if (good) {
482 buffer[strlen(buffer) - 1] = '\0';
483 }
484
485 /* return the output */
486 return smprintf("%s", buffer);
487 }
488
489 /* temperature */
490 char *
491 temp(const char *file)
492 {
493 int temperature;
494 FILE *fp;
495
496 /* open temperature file */
497 if (!(fp = fopen(file, "r"))) {
498 fprintf(stderr, "Could not open temperature file.\n");
499 return smprintf("n/a");
500 }
501
502 /* extract temperature */
503 fscanf(fp, "%d", &temperature);
504
505 /* close temperature file */
506 fclose(fp);
507
508 /* return temperature in degrees */
509 return smprintf("%d°C", temperature / 1000);
510 }
511
512 /* username */
513 char *
514 username(const char *null)
515 {
516 register struct passwd *pw;
517 register uid_t uid;
518
519 /* get the values */
520 uid = geteuid ();
521 pw = getpwuid (uid);
522
523 /* if it worked, return */
524 if (pw) {
525 return smprintf("%s", pw->pw_name);
526 }
527 else {
528 fprintf(stderr, "Could not get username.\n");
529 return smprintf("n/a");
530 }
531
532 return smprintf("n/a");
533 }
534
535 /* uid */
536 char *
537 uid(const char *null)
538 {
539 register uid_t uid;
540
541 /* get the values */
542 uid = geteuid ();
543
544 /* if it worked, return */
545 if (uid) {
546 return smprintf("%d", uid);
547 }
548 else {
549 fprintf(stderr, "Could not get uid.\n");
550 return smprintf("n/a");
551 }
552
553 return smprintf("n/a");
554 }
555
556
557 /* alsa volume percentage */
558 char *
559 vol_perc(const char *soundcard)
560 {
561 int mute = 0;
562 long vol = 0, max = 0, min = 0;
563 snd_mixer_t *handle;
564 snd_mixer_elem_t *pcm_mixer, *mas_mixer;
565 snd_mixer_selem_id_t *vol_info, *mute_info;
566
567 /* open everything */
568 snd_mixer_open(&handle, 0);
569 snd_mixer_attach(handle, soundcard);
570 snd_mixer_selem_register(handle, NULL, NULL);
571 snd_mixer_load(handle);
572
573 /* prepare everything */
574 snd_mixer_selem_id_malloc(&vol_info);
575 snd_mixer_selem_id_malloc(&mute_info);
576 /* check */
577 if (vol_info == NULL || mute_info == NULL) {
578 fprintf(stderr, "Could not get alsa volume");
579 return smprintf("n/a");
580 }
581 snd_mixer_selem_id_set_name(vol_info, channel);
582 snd_mixer_selem_id_set_name(mute_info, channel);
583 pcm_mixer = snd_mixer_find_selem(handle, vol_info);
584 mas_mixer = snd_mixer_find_selem(handle, mute_info);
585
586 /* get the info */
587 snd_mixer_selem_get_playback_volume_range((snd_mixer_elem_t *)pcm_mixer, &min, &max);
588 snd_mixer_selem_get_playback_volume((snd_mixer_elem_t *)pcm_mixer, SND_MIXER_SCHN_MONO, &vol);
589 snd_mixer_selem_get_playback_switch(mas_mixer, SND_MIXER_SCHN_MONO, &mute);
590
591 /* clean up */
592 if (vol_info) {
593 snd_mixer_selem_id_free(vol_info);
594 }
595 if (mute_info) {
596 snd_mixer_selem_id_free(mute_info);
597 }
598 if (handle) {
599 snd_mixer_close(handle);
600 }
601
602 /* return the string (mute) */
603 if (!mute) {
604 return smprintf("mute");
605 }
606 else {
607 return smprintf("%d%%", (vol * 100) / max);
608 }
609 }
610
611 /* wifi percentage */
612 char *
613 wifi_perc(const char *wificard)
614 {
615 int bufsize = 255;
616 int strength;
617 char buf[bufsize];
618 char *datastart;
619 char path[64];
620 char status[5];
621 char needle[sizeof wificard + 1];
622 FILE *fp;
623
624 /* generate the path name */
625 memset(path, 0, sizeof path);
626 strcat(path, "/sys/class/net/");
627 strcat(path, wificard);
628 strcat(path, "/operstate");
629
630 /* open wifi file */
631 if(!(fp = fopen(path, "r"))) {
632 fprintf(stderr, "Error opening wifi operstate file.");
633 return smprintf("n/a");
634 }
635
636 /* read the status */
637 fgets(status, 5, fp);
638
639 /* close wifi file */
640 fclose(fp);
641
642 /* check if interface down */
643 if(strcmp(status, "up\n") != 0){
644 return smprintf("n/a");
645 }
646
647 /* open wifi file */
648 if (!(fp = fopen("/proc/net/wireless", "r"))) {
649 fprintf(stderr, "Error opening wireless file.");
650 return smprintf("n/a");
651 }
652
653 /* extract the signal strength */
654 strcpy(needle, wificard);
655 strcat(needle, ":");
656 fgets(buf, bufsize, fp);
657 fgets(buf, bufsize, fp);
658 fgets(buf, bufsize, fp);
659 if ((datastart = strstr(buf, needle)) != NULL) {
660 datastart = strstr(buf, ":");
661 sscanf(datastart + 1, " %*d %d %*d %*d %*d %*d %*d %*d %*d %*d", &strength);
662 }
663
664 /* close wifi file */
665 fclose(fp);
666
667 /* return strength in percent */
668 return smprintf("%d%%", strength);
669 }
670
671 /* wifi essid */
672 char *
673 wifi_essid(const char *wificard)
674 {
675 char *id = malloc(IW_ESSID_MAX_SIZE+1);
676 if (id == NULL) {
677 fprintf(stderr, "Cannot get ESSID.");
678 return smprintf("n/a");
679 }
680 int sockfd;
681 struct iwreq wreq;
682
683 /* prepare */
684 memset(&wreq, 0, sizeof(struct iwreq));
685 wreq.u.essid.length = IW_ESSID_MAX_SIZE+1;
686
687 /* set the interface */
688 sprintf(wreq.ifr_name, wificard);
689
690 /* check */
691 if((sockfd = socket(AF_INET, SOCK_DGRAM, 0)) == -1) {
692 fprintf(stderr, "Cannot open socket for interface: %s\n", wificard);
693 return smprintf("n/a");
694 }
695 wreq.u.essid.pointer = id;
696 if (ioctl(sockfd,SIOCGIWESSID, &wreq) == -1) {
697 fprintf(stderr, "Get ESSID ioctl failed for interface %s\n", wificard);
698 return smprintf("n/a");
699 }
700
701 /* return the essid */
702 if (strcmp((char *)wreq.u.essid.pointer, "") == 0) {
703 return smprintf("n/a");
704 }
705 else {
706 return smprintf("%s", (char *)wreq.u.essid.pointer);
707 }
708 }
709
710 /* main function */
711 int
712 main(void)
713 {
714 char status_string[1024];
715 struct arg argument;
716
717 /* try to open display */
718 if (!(dpy = XOpenDisplay(0x0))) {
719 fprintf(stderr, "Cannot open display!\n");
720 exit(1);
721 }
722
723 /* return status every interval */
724 for (;;) {
725 /* clear the string */
726 memset(status_string, 0, sizeof(status_string));
727
728 /* generate status_string */
729 for (size_t i = 0; i < sizeof(args) / sizeof(args[0]); ++i) {
730 argument = args[i];
731 char *res = argument.func(argument.args);
732 char *element = smprintf(argument.format, res);
733 if (element == NULL) {
734 element = smprintf("n/a");
735 fprintf(stderr, "Failed to format output.");
736 }
737 strcat(status_string, element);
738 free(res);
739 free(element);
740 }
741
742 /* return the statusbar */
743 setstatus(status_string);
744
745 /* wait, "update_interval - 1" because of get_cpu_usage() which uses 1 second */
746 sleep(update_interval -1);
747 }
748
749 /* close display */
750 XCloseDisplay(dpy);
751
752 /* exit successfully */
753 return 0;
754 }