Xinqi Bao's Git

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