Xinqi Bao's Git

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