Xinqi Bao's Git

opened the file before the path concatenates, what an idiot
[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: %s\n",
256 strerror(errno));
257 return smprintf(UNKNOWN_STR);
258 }
259
260 fscanf(fp, "%d", &entropy);
261 fclose(fp);
262 return smprintf("%d", entropy);
263 }
264
265 static char *
266 gid(void)
267 {
268 gid_t gid = getgid();
269 return smprintf("%d", gid);
270 }
271
272 static char *
273 hostname(void)
274 {
275 char hostname[HOST_NAME_MAX];
276 FILE *fp = fopen("/proc/sys/kernel/hostname", "r");
277
278 if (fp == NULL) {
279 fprintf(stderr, "Could not open hostname file: %s\n",
280 strerror(errno));
281 return smprintf(UNKNOWN_STR);
282 }
283
284 fgets(hostname, sizeof(hostname), fp);
285 /* FIXME: needs improvement */
286 memset(&hostname[strlen(hostname)-1], '\0',
287 sizeof(hostname) - strlen(hostname));
288 fclose(fp);
289 return smprintf("%s", hostname);
290 }
291
292 static char *
293 ip(const char *interface)
294 {
295 struct ifaddrs *ifaddr, *ifa;
296 int s;
297 char host[NI_MAXHOST];
298
299 if (getifaddrs(&ifaddr) == -1) {
300 fprintf(stderr, "Error getting IP address: %s\n",
301 strerror(errno));
302 return smprintf(UNKNOWN_STR);
303 }
304
305 /* get the ip address */
306 for (ifa = ifaddr; ifa != NULL; ifa = ifa->ifa_next) {
307 if (ifa->ifa_addr == NULL)
308 continue;
309
310 s = getnameinfo(ifa->ifa_addr, sizeof(struct sockaddr_in), host, NI_MAXHOST,
311 NULL, 0, NI_NUMERICHOST);
312
313 if ((strcmp(ifa->ifa_name, interface) == 0) && (ifa->ifa_addr->sa_family == AF_INET)) {
314 if (s != 0) {
315 fprintf(stderr, "Error getting IP address.\n");
316 return smprintf(UNKNOWN_STR);
317 }
318 return smprintf("%s", host);
319 }
320 }
321
322 /* free the address */
323 freeifaddrs(ifaddr);
324
325 return smprintf(UNKNOWN_STR);
326 }
327
328 static char *
329 load_avg(void)
330 {
331 double avgs[3];
332
333 if (getloadavg(avgs, 3) < 0) {
334 fprintf(stderr, "Error getting load avg.\n");
335 return smprintf(UNKNOWN_STR);
336 }
337
338 return smprintf("%.2f %.2f %.2f", avgs[0], avgs[1], avgs[2]);
339 }
340
341 static char *
342 ram_free(void)
343 {
344 long free;
345 FILE *fp = fopen("/proc/meminfo", "r");
346
347 if (fp == NULL) {
348 fprintf(stderr, "Error opening meminfo file: %s\n",
349 strerror(errno));
350 return smprintf(UNKNOWN_STR);
351 }
352
353 fscanf(fp, "MemFree: %ld kB\n", &free);
354 fclose(fp);
355 return smprintf("%f", (float)free / 1024 / 1024);
356 }
357
358 static char *
359 ram_perc(void)
360 {
361 int perc;
362 long total, free, buffers, cached;
363 FILE *fp = fopen("/proc/meminfo", "r");
364
365 if (fp == NULL) {
366 fprintf(stderr, "Error opening meminfo file: %s\n",
367 strerror(errno));
368 return smprintf(UNKNOWN_STR);
369 }
370
371 fscanf(fp, "MemTotal: %ld kB\n", &total);
372 fscanf(fp, "MemFree: %ld kB\n", &free);
373 fscanf(fp, "MemAvailable: %ld kB\nBuffers: %ld kB\n", &buffers, &buffers);
374 fscanf(fp, "Cached: %ld kB\n", &cached);
375
376 fclose(fp);
377 perc = 100 * ((total - free) - (buffers + cached)) / total;
378 return smprintf("%d%%", perc);
379 }
380
381 static char *
382 ram_total(void)
383 {
384 long total;
385 FILE *fp = fopen("/proc/meminfo", "r");
386
387 if (fp == NULL) {
388 fprintf(stderr, "Error opening meminfo file: %s\n",
389 strerror(errno));
390 return smprintf(UNKNOWN_STR);
391 }
392
393 fscanf(fp, "MemTotal: %ld kB\n", &total);
394 fclose(fp);
395 return smprintf("%f", (float)total / 1024 / 1024);
396 }
397
398 static char *
399 ram_used(void)
400 {
401 long free, total, buffers, cached, used;
402 FILE *fp = fopen("/proc/meminfo", "r");
403
404 if (fp == NULL) {
405 fprintf(stderr, "Error opening meminfo file: %s\n",
406 strerror(errno));
407 return smprintf(UNKNOWN_STR);
408 }
409
410 fscanf(fp, "MemTotal: %ld kB\n", &total);
411 fscanf(fp, "MemFree: %ld kB\n", &free);
412 fscanf(fp, "MemAvailable: %ld kB\nBuffers: %ld kB\n", &buffers, &buffers);
413 fscanf(fp, "Cached: %ld kB\n", &cached);
414
415 fclose(fp);
416 used = total - free - buffers - cached;
417 return smprintf("%f", (float)used / 1024 / 1024);
418 }
419
420 static char *
421 run_command(const char* command)
422 {
423 int good;
424 FILE *fp = popen(command, "r");
425 char buffer[64];
426
427 if (fp == NULL) {
428 fprintf(stderr, "Could not get command output for: %s: %s\n",
429 command, strerror(errno));
430 return smprintf(UNKNOWN_STR);
431 }
432
433 fgets(buffer, sizeof(buffer)-1, fp);
434 pclose(fp);
435 for (int i = 0 ; i != sizeof(buffer); i++) {
436 if (buffer[i] == '\0') {
437 good = 1;
438 break;
439 }
440 }
441 if (good)
442 buffer[strlen(buffer)-1] = '\0';
443 return smprintf("%s", buffer);
444 }
445
446 static char *
447 temp(const char *file)
448 {
449 int temperature;
450 FILE *fp = fopen(file, "r");
451
452 if (fp == NULL) {
453 fprintf(stderr, "Could not open temperature file: %s\n",
454 strerror(errno));
455 return smprintf(UNKNOWN_STR);
456 }
457
458 fscanf(fp, "%d", &temperature);
459 fclose(fp);
460 return smprintf("%d°C", temperature / 1000);
461 }
462
463 static char *
464 uptime(void)
465 {
466 struct sysinfo info;
467 int hours = 0;
468 int minutes = 0;
469
470 sysinfo(&info);
471 hours = info.uptime / 3600;
472 minutes = (info.uptime - hours * 3600 ) / 60;
473
474 return smprintf("%dh %dm", hours, minutes);
475 }
476
477 static char *
478 username(void)
479 {
480 register struct passwd *pw;
481 register uid_t uid;
482
483 uid = geteuid();
484 pw = getpwuid(uid);
485
486 if (pw)
487 return smprintf("%s", pw->pw_name);
488 else {
489 fprintf(stderr, "Could not get username: %s\n",
490 strerror(errno));
491 return smprintf(UNKNOWN_STR);
492 }
493
494 return smprintf(UNKNOWN_STR);
495 }
496
497 static char *
498 uid(void)
499 {
500 /* FIXME: WHY USE register modifier? */
501 register uid_t uid;
502
503 uid = geteuid();
504
505 if (uid)
506 return smprintf("%d", uid);
507 else {
508 fprintf(stderr, "Could not get uid.\n");
509 return smprintf(UNKNOWN_STR);
510 }
511
512 return smprintf(UNKNOWN_STR);
513 }
514
515
516 static char *
517 vol_perc(const char *soundcard)
518 {
519 int mute = 0;
520 long vol = 0, max = 0, min = 0;
521 snd_mixer_t *handle;
522 snd_mixer_elem_t *pcm_mixer, *mas_mixer;
523 snd_mixer_selem_id_t *vol_info, *mute_info;
524
525 snd_mixer_open(&handle, 0);
526 snd_mixer_attach(handle, soundcard);
527 snd_mixer_selem_register(handle, NULL, NULL);
528 snd_mixer_load(handle);
529
530 snd_mixer_selem_id_malloc(&vol_info);
531 snd_mixer_selem_id_malloc(&mute_info);
532 if (vol_info == NULL || mute_info == NULL) {
533 fprintf(stderr, "Could not get alsa volume.\n");
534 return smprintf(UNKNOWN_STR);
535 }
536 snd_mixer_selem_id_set_name(vol_info, ALSA_CHANNEL);
537 snd_mixer_selem_id_set_name(mute_info, ALSA_CHANNEL);
538 pcm_mixer = snd_mixer_find_selem(handle, vol_info);
539 mas_mixer = snd_mixer_find_selem(handle, mute_info);
540
541 snd_mixer_selem_get_playback_volume_range((snd_mixer_elem_t *)pcm_mixer, &min, &max);
542 snd_mixer_selem_get_playback_volume((snd_mixer_elem_t *)pcm_mixer, SND_MIXER_SCHN_MONO, &vol);
543 snd_mixer_selem_get_playback_switch(mas_mixer, SND_MIXER_SCHN_MONO, &mute);
544
545 if (vol_info)
546 snd_mixer_selem_id_free(vol_info);
547 if (mute_info)
548 snd_mixer_selem_id_free(mute_info);
549 if (handle)
550 snd_mixer_close(handle);
551
552 if (!mute)
553 return smprintf("mute");
554 else
555 return smprintf("%d%%", (vol * 100) / max);
556 }
557
558 static char *
559 wifi_perc(const char *wificard)
560 {
561 int strength;
562 char buf[255];
563 char *datastart;
564 char path[64];
565 char status[5];
566 char needle[strlen(wificard)+2];
567 FILE *fp;
568
569 strlcpy(path, "/sys/class/net/", sizeof(path));
570 strlcat(path, wificard, sizeof(path));
571 strlcat(path, "/operstate", sizeof(path));
572
573 fp = fopen(path, "r");
574
575 if(fp == NULL) {
576 fprintf(stderr, "Error opening wifi operstate file: %s\n",
577 strerror(errno));
578 return smprintf(UNKNOWN_STR);
579 }
580
581 fgets(status, 5, fp);
582 fclose(fp);
583 if(strcmp(status, "up\n") != 0)
584 return smprintf(UNKNOWN_STR);
585
586 fp = fopen("/proc/net/wireless", "r");
587 if (fp == NULL) {
588 fprintf(stderr, "Error opening wireless file: %s\n",
589 strerror(errno));
590 return smprintf(UNKNOWN_STR);
591 }
592
593 strlcpy(needle, wificard, sizeof(needle));
594 strlcat(needle, ":", sizeof(needle));
595 fgets(buf, sizeof(buf), fp);
596 fgets(buf, sizeof(buf), fp);
597 fgets(buf, sizeof(buf), fp);
598
599 datastart = strstr(buf, needle);
600 if (datastart != NULL) {
601 datastart = strstr(buf, ":");
602 sscanf(datastart + 1, " %*d %d %*d %*d %*d %*d %*d %*d %*d %*d", &strength);
603 }
604
605 fclose(fp);
606 return smprintf("%d%%", strength);
607 }
608
609 static char *
610 wifi_essid(const char *wificard)
611 {
612 char id[IW_ESSID_MAX_SIZE+1];
613 int sockfd = socket(AF_INET, SOCK_DGRAM, 0);
614 struct iwreq wreq;
615
616 memset(&wreq, 0, sizeof(struct iwreq));
617 wreq.u.essid.length = IW_ESSID_MAX_SIZE+1;
618 sprintf(wreq.ifr_name, wificard);
619 if(sockfd == -1) {
620 fprintf(stderr, "Cannot open socket for interface: %s: %s\n",
621 wificard, strerror(errno));
622 return smprintf(UNKNOWN_STR);
623 }
624 wreq.u.essid.pointer = id;
625 if (ioctl(sockfd,SIOCGIWESSID, &wreq) == -1) {
626 fprintf(stderr, "Get ESSID ioctl failed for interface %s: %s\n",
627 wificard, strerror(errno));
628 return smprintf(UNKNOWN_STR);
629 }
630
631 if (strcmp((char *)wreq.u.essid.pointer, "") == 0)
632 return smprintf(UNKNOWN_STR);
633 else
634 return smprintf("%s", (char *)wreq.u.essid.pointer);
635 }
636
637 int
638 main(void)
639 {
640 size_t i;
641 char status_string[4096];
642 char *res, *element;
643 struct arg argument;
644
645 dpy = XOpenDisplay(0x0);
646 if (!dpy) {
647 fprintf(stderr, "Cannot open display!\n");
648 exit(1);
649 }
650
651 for (;;) {
652 memset(status_string, 0, sizeof(status_string));
653 for (i = 0; i < sizeof(args) / sizeof(args[0]); ++i) {
654 argument = args[i];
655 if (argument.args == NULL)
656 res = argument.func();
657 else
658 res = argument.func(argument.args);
659 element = smprintf(argument.format, res);
660 if (element == NULL) {
661 element = smprintf(UNKNOWN_STR);
662 fprintf(stderr, "Failed to format output.\n");
663 }
664 strlcat(status_string, element, sizeof(status_string));
665 free(res);
666 free(element);
667 }
668
669 setstatus(status_string);
670 sleep(UPDATE_INTERVAL -1);
671 }
672
673 XCloseDisplay(dpy);
674 return 0;
675 }