Xinqi Bao's Git

removed setstatus() && simplified main()
[slstatus.git] / slstatus.c
1 /* See LICENSE file for copyright and license details. */
2
3 #include <alsa/asoundlib.h>
4 #include <fcntl.h>
5 #include <ifaddrs.h>
6 #include <limits.h>
7 #include <linux/wireless.h>
8 #include <netdb.h>
9 #include <pwd.h>
10 #include <stdarg.h>
11 #include <stdio.h>
12 #include <stdlib.h>
13 #include <string.h>
14 #include <sys/ioctl.h>
15 #include <sys/stat.h>
16 #include <sys/statvfs.h>
17 #include <sys/socket.h>
18 #include <sys/sysinfo.h>
19 #include <sys/types.h>
20 #include <time.h>
21 #include <unistd.h>
22 #include <X11/Xlib.h>
23
24 #undef strlcat
25 #undef strlcpy
26
27 #include "strlcat.h"
28 #include "strlcpy.h"
29
30 struct arg {
31 char *(*func)();
32 const char *format;
33 const char *args;
34 };
35
36 static char *smprintf(const char *, ...);
37 static char *battery_perc(const char *);
38 static char *cpu_perc(void);
39 static char *datetime(const char *);
40 static char *disk_free(const char *);
41 static char *disk_perc(const char *);
42 static char *disk_total(const char *);
43 static char *disk_used(const char *);
44 static char *entropy(void);
45 static char *gid(void);
46 static char *hostname(void);
47 static char *ip(const char *);
48 static char *load_avg(void);
49 static char *ram_free(void);
50 static char *ram_perc(void);
51 static char *ram_used(void);
52 static char *ram_total(void);
53 static char *run_command(const char *);
54 static char *temp(const char *);
55 static char *uid(void);
56 static char *uptime(void);
57 static char *username(void);
58 static char *vol_perc(const char *);
59 static char *wifi_perc(const char *);
60 static char *wifi_essid(const char *);
61
62 static Display *dpy;
63
64 #include "config.h"
65
66 static char *
67 smprintf(const char *fmt, ...)
68 {
69 va_list ap;
70 char *ret;
71 int len;
72
73 va_start(ap, fmt);
74 len = vsnprintf(NULL, 0, fmt, ap);
75 va_end(ap);
76
77 ret = malloc(++len);
78 if (ret == NULL) {
79 perror("malloc");
80 exit(1);
81 }
82
83 va_start(ap, fmt);
84 vsnprintf(ret, len, fmt, ap);
85 va_end(ap);
86
87 return ret;
88 }
89
90 static char *
91 battery_perc(const char *battery)
92 {
93 int now, full, perc;
94 char batterynowfile[64];
95 char batteryfullfile[64];
96 FILE *fp;
97
98 strlcpy(batterynowfile, BATTERY_PATH, sizeof(batterynowfile));
99 strlcat(batterynowfile, battery, sizeof(batterynowfile));
100 strlcat(batterynowfile, "/", sizeof(batterynowfile));
101 strlcat(batterynowfile, BATTERY_NOW, sizeof(batterynowfile));
102
103 strlcpy(batteryfullfile, BATTERY_PATH, sizeof(batteryfullfile));
104 strlcat(batteryfullfile, battery, sizeof(batteryfullfile));
105 strlcat(batteryfullfile, "/", sizeof(batteryfullfile));
106 strlcat(batteryfullfile, BATTERY_FULL, sizeof(batteryfullfile));
107
108 fp = fopen(batterynowfile, "r");
109 if (fp == NULL ) {
110 fprintf(stderr, "Error opening battery file: %s: %s\n",
111 batterynowfile,
112 strerror(errno));
113 return smprintf(UNKNOWN_STR);
114 }
115
116 fscanf(fp, "%i", &now);
117 fclose(fp);
118
119 fp = fopen(batteryfullfile, "r");
120 if (fp == NULL) {
121 fprintf(stderr, "Error opening battery file: %s\n",
122 strerror(errno));
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 = fopen("/proc/stat","r");
140
141 if (fp == NULL) {
142 fprintf(stderr, "Error opening stat file: %s\n",
143 strerror(errno));
144 return smprintf(UNKNOWN_STR);
145 }
146
147 fscanf(fp, "%*s %Lf %Lf %Lf %Lf", &a[0], &a[1], &a[2], &a[3]);
148 fclose(fp);
149
150 /* wait a second (for avg values) */
151 sleep(1);
152
153 fp = fopen("/proc/stat","r");
154 if (fp == NULL) {
155 fprintf(stderr, "Error opening stat file: %s\n",
156 strerror(errno));
157 return smprintf(UNKNOWN_STR);
158 }
159
160 fscanf(fp, "%*s %Lf %Lf %Lf %Lf", &b[0], &b[1], &b[2], &b[3]);
161 fclose(fp);
162 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]));
163 return smprintf("%d%%", perc);
164 }
165
166 static char *
167 datetime(const char *timeformat)
168 {
169 time_t t;
170 char timestr[80];
171
172 t = time(NULL);
173 if (strftime(timestr, sizeof(timestr), timeformat, localtime(&t)) == 0)
174 return smprintf(UNKNOWN_STR);
175
176 return smprintf("%s", timestr);
177 }
178
179 static char *
180 disk_free(const char *mountpoint)
181 {
182 struct statvfs fs;
183
184 if (statvfs(mountpoint, &fs) < 0) {
185 fprintf(stderr, "Could not get filesystem info: %s\n",
186 strerror(errno));
187 return smprintf(UNKNOWN_STR);
188 }
189 return smprintf("%f", (float)fs.f_bsize * (float)fs.f_bfree / 1024 / 1024 / 1024);
190 }
191
192 static char *
193 disk_perc(const char *mountpoint)
194 {
195 int perc = 0;
196 struct statvfs fs;
197
198 if (statvfs(mountpoint, &fs) < 0) {
199 fprintf(stderr, "Could not get filesystem info: %s\n",
200 strerror(errno));
201 return smprintf(UNKNOWN_STR);
202 }
203
204 perc = 100 * (1.0f - ((float)fs.f_bfree / (float)fs.f_blocks));
205 return smprintf("%d%%", perc);
206 }
207
208 static char *
209 disk_total(const char *mountpoint)
210 {
211 struct statvfs fs;
212
213 if (statvfs(mountpoint, &fs) < 0) {
214 fprintf(stderr, "Could not get filesystem info: %s\n",
215 strerror(errno));
216 return smprintf(UNKNOWN_STR);
217 }
218
219 return smprintf("%f", (float)fs.f_bsize * (float)fs.f_blocks / 1024 / 1024 / 1024);
220 }
221
222 static char *
223 disk_used(const char *mountpoint)
224 {
225 struct statvfs fs;
226
227 if (statvfs(mountpoint, &fs) < 0) {
228 fprintf(stderr, "Could not get filesystem info: %s\n",
229 strerror(errno));
230 return smprintf(UNKNOWN_STR);
231 }
232
233 return smprintf("%f", (float)fs.f_bsize * ((float)fs.f_blocks - (float)fs.f_bfree) / 1024 / 1024 / 1024);
234 }
235
236 static char *
237 entropy(void)
238 {
239 int entropy = 0;
240 FILE *fp = fopen("/proc/sys/kernel/random/entropy_avail", "r");
241
242 if (fp == NULL) {
243 fprintf(stderr, "Could not open entropy file: %s\n",
244 strerror(errno));
245 return smprintf(UNKNOWN_STR);
246 }
247
248 fscanf(fp, "%d", &entropy);
249 fclose(fp);
250 return smprintf("%d", entropy);
251 }
252
253 static char *
254 gid(void)
255 {
256 return smprintf("%d", getgid());
257 }
258
259 static char *
260 hostname(void)
261 {
262 char hostname[HOST_NAME_MAX];
263 FILE *fp = fopen("/proc/sys/kernel/hostname", "r");
264
265 if (fp == NULL) {
266 fprintf(stderr, "Could not open hostname file: %s\n",
267 strerror(errno));
268 return smprintf(UNKNOWN_STR);
269 }
270
271 fgets(hostname, sizeof(hostname), fp);
272 /* FIXME: needs improvement */
273 memset(&hostname[strlen(hostname)-1], '\0',
274 sizeof(hostname) - strlen(hostname));
275 fclose(fp);
276 return smprintf("%s", hostname);
277 }
278
279 static char *
280 ip(const char *interface)
281 {
282 struct ifaddrs *ifaddr, *ifa;
283 int s;
284 char host[NI_MAXHOST];
285
286 if (getifaddrs(&ifaddr) == -1) {
287 fprintf(stderr, "Error getting IP address: %s\n",
288 strerror(errno));
289 return smprintf(UNKNOWN_STR);
290 }
291
292 /* get the ip address */
293 for (ifa = ifaddr; ifa != NULL; ifa = ifa->ifa_next) {
294 if (ifa->ifa_addr == NULL)
295 continue;
296
297 s = getnameinfo(ifa->ifa_addr, sizeof(struct sockaddr_in), host, NI_MAXHOST,
298 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 = fopen("/proc/meminfo", "r");
333
334 if (fp == NULL) {
335 fprintf(stderr, "Error opening meminfo file: %s\n",
336 strerror(errno));
337 return smprintf(UNKNOWN_STR);
338 }
339
340 fscanf(fp, "MemFree: %ld kB\n", &free);
341 fclose(fp);
342 return smprintf("%f", (float)free / 1024 / 1024);
343 }
344
345 static char *
346 ram_perc(void)
347 {
348 int perc;
349 long total, free, buffers, cached;
350 FILE *fp = fopen("/proc/meminfo", "r");
351
352 if (fp == NULL) {
353 fprintf(stderr, "Error opening meminfo file: %s\n",
354 strerror(errno));
355 return smprintf(UNKNOWN_STR);
356 }
357
358 fscanf(fp, "MemTotal: %ld kB\n", &total);
359 fscanf(fp, "MemFree: %ld kB\n", &free);
360 fscanf(fp, "MemAvailable: %ld kB\nBuffers: %ld kB\n", &buffers, &buffers);
361 fscanf(fp, "Cached: %ld kB\n", &cached);
362
363 fclose(fp);
364 perc = 100 * ((total - free) - (buffers + cached)) / total;
365 return smprintf("%d%%", perc);
366 }
367
368 static char *
369 ram_total(void)
370 {
371 long total;
372 FILE *fp = fopen("/proc/meminfo", "r");
373
374 if (fp == NULL) {
375 fprintf(stderr, "Error opening meminfo file: %s\n",
376 strerror(errno));
377 return smprintf(UNKNOWN_STR);
378 }
379
380 fscanf(fp, "MemTotal: %ld kB\n", &total);
381 fclose(fp);
382 return smprintf("%f", (float)total / 1024 / 1024);
383 }
384
385 static char *
386 ram_used(void)
387 {
388 long free, total, buffers, cached, used;
389 FILE *fp = fopen("/proc/meminfo", "r");
390
391 if (fp == NULL) {
392 fprintf(stderr, "Error opening meminfo file: %s\n",
393 strerror(errno));
394 return smprintf(UNKNOWN_STR);
395 }
396
397 fscanf(fp, "MemTotal: %ld kB\n", &total);
398 fscanf(fp, "MemFree: %ld kB\n", &free);
399 fscanf(fp, "MemAvailable: %ld kB\nBuffers: %ld kB\n", &buffers, &buffers);
400 fscanf(fp, "Cached: %ld kB\n", &cached);
401
402 fclose(fp);
403 used = total - free - buffers - cached;
404 return smprintf("%f", (float)used / 1024 / 1024);
405 }
406
407 static char *
408 run_command(const char* command)
409 {
410 int good;
411 FILE *fp = popen(command, "r");
412 char buffer[64];
413
414 if (fp == NULL) {
415 fprintf(stderr, "Could not get command output for: %s: %s\n",
416 command, strerror(errno));
417 return smprintf(UNKNOWN_STR);
418 }
419
420 fgets(buffer, sizeof(buffer)-1, fp);
421 pclose(fp);
422 for (int i = 0 ; i != sizeof(buffer); i++) {
423 if (buffer[i] == '\0') {
424 good = 1;
425 break;
426 }
427 }
428 if (good)
429 buffer[strlen(buffer)-1] = '\0';
430 return smprintf("%s", buffer);
431 }
432
433 static char *
434 temp(const char *file)
435 {
436 int temperature;
437 FILE *fp = fopen(file, "r");
438
439 if (fp == NULL) {
440 fprintf(stderr, "Could not open temperature file: %s\n",
441 strerror(errno));
442 return smprintf(UNKNOWN_STR);
443 }
444
445 fscanf(fp, "%d", &temperature);
446 fclose(fp);
447 return smprintf("%d°C", temperature / 1000);
448 }
449
450 static char *
451 uptime(void)
452 {
453 struct sysinfo info;
454 int hours = 0;
455 int minutes = 0;
456
457 sysinfo(&info);
458 hours = info.uptime / 3600;
459 minutes = (info.uptime - hours * 3600 ) / 60;
460
461 return smprintf("%dh %dm", hours, minutes);
462 }
463
464 static char *
465 username(void)
466 {
467 uid_t uid = geteuid();
468 struct passwd *pw = getpwuid(uid);
469
470 if (pw == NULL)
471 return smprintf("%s", pw->pw_name);
472
473 fprintf(stderr, "Could not get username: %s\n",
474 strerror(errno));
475 return smprintf(UNKNOWN_STR);
476 }
477
478 static char *
479 uid(void)
480 {
481 return smprintf("%d", geteuid());
482 }
483
484
485 static char *
486 vol_perc(const char *soundcard)
487 {
488 /*
489 * TODO: FIXME:
490 * https://github.com/drkh5h/slstatus/issues/12
491 */
492 int mute = 0;
493 long vol = 0, max = 0, min = 0;
494 snd_mixer_t *handle;
495 snd_mixer_elem_t *pcm_mixer, *mas_mixer;
496 snd_mixer_selem_id_t *vol_info, *mute_info;
497
498 snd_mixer_open(&handle, 0);
499 snd_mixer_attach(handle, soundcard);
500 snd_mixer_selem_register(handle, NULL, NULL);
501 snd_mixer_load(handle);
502
503 snd_mixer_selem_id_malloc(&vol_info);
504 snd_mixer_selem_id_malloc(&mute_info);
505 if (vol_info == NULL || mute_info == NULL) {
506 fprintf(stderr, "Could not get alsa volume.\n");
507 return smprintf(UNKNOWN_STR);
508 }
509 snd_mixer_selem_id_set_name(vol_info, ALSA_CHANNEL);
510 snd_mixer_selem_id_set_name(mute_info, ALSA_CHANNEL);
511 pcm_mixer = snd_mixer_find_selem(handle, vol_info);
512 mas_mixer = snd_mixer_find_selem(handle, mute_info);
513
514 snd_mixer_selem_get_playback_volume_range((snd_mixer_elem_t *)pcm_mixer, &min, &max);
515 snd_mixer_selem_get_playback_volume((snd_mixer_elem_t *)pcm_mixer, SND_MIXER_SCHN_MONO, &vol);
516 snd_mixer_selem_get_playback_switch(mas_mixer, SND_MIXER_SCHN_MONO, &mute);
517
518 if (vol_info)
519 snd_mixer_selem_id_free(vol_info);
520 if (mute_info)
521 snd_mixer_selem_id_free(mute_info);
522 if (handle)
523 snd_mixer_close(handle);
524
525 if (!mute)
526 return smprintf("mute");
527 else
528 return smprintf("%d%%", (vol * 100) / max);
529 }
530
531 static char *
532 wifi_perc(const char *wificard)
533 {
534 int strength;
535 char buf[255];
536 char *datastart;
537 char path[64];
538 char status[5];
539 char needle[strlen(wificard)+2];
540 FILE *fp;
541
542 strlcpy(path, "/sys/class/net/", sizeof(path));
543 strlcat(path, wificard, sizeof(path));
544 strlcat(path, "/operstate", sizeof(path));
545
546 fp = fopen(path, "r");
547
548 if(fp == NULL) {
549 fprintf(stderr, "Error opening wifi operstate file: %s\n",
550 strerror(errno));
551 return smprintf(UNKNOWN_STR);
552 }
553
554 fgets(status, 5, fp);
555 fclose(fp);
556 if(strcmp(status, "up\n") != 0)
557 return smprintf(UNKNOWN_STR);
558
559 fp = fopen("/proc/net/wireless", "r");
560 if (fp == NULL) {
561 fprintf(stderr, "Error opening wireless file: %s\n",
562 strerror(errno));
563 return smprintf(UNKNOWN_STR);
564 }
565
566 strlcpy(needle, wificard, sizeof(needle));
567 strlcat(needle, ":", sizeof(needle));
568 fgets(buf, sizeof(buf), fp);
569 fgets(buf, sizeof(buf), fp);
570 fgets(buf, sizeof(buf), fp);
571
572 datastart = strstr(buf, needle);
573 if (datastart != NULL) {
574 datastart = strstr(buf, ":");
575 sscanf(datastart + 1, " %*d %d %*d %*d %*d %*d %*d %*d %*d %*d", &strength);
576 }
577
578 fclose(fp);
579 return smprintf("%d%%", strength);
580 }
581
582 static char *
583 wifi_essid(const char *wificard)
584 {
585 char id[IW_ESSID_MAX_SIZE+1];
586 int sockfd = socket(AF_INET, SOCK_DGRAM, 0);
587 struct iwreq wreq;
588
589 memset(&wreq, 0, sizeof(struct iwreq));
590 wreq.u.essid.length = IW_ESSID_MAX_SIZE+1;
591 sprintf(wreq.ifr_name, wificard);
592 if(sockfd == -1) {
593 fprintf(stderr, "Cannot open socket for interface: %s: %s\n",
594 wificard, strerror(errno));
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: %s\n",
600 wificard, strerror(errno));
601 return smprintf(UNKNOWN_STR);
602 }
603
604 if (strcmp((char *)wreq.u.essid.pointer, "") == 0)
605 return smprintf(UNKNOWN_STR);
606 else
607 return smprintf("%s", (char *)wreq.u.essid.pointer);
608 }
609
610 int
611 main(void)
612 {
613 size_t i;
614 char status_string[4096];
615 char *res, *element;
616 struct arg argument;
617
618 dpy = XOpenDisplay(0x0);
619
620 for (;;) {
621 memset(status_string, 0, sizeof(status_string));
622 for (i = 0; i < sizeof(args) / sizeof(args[0]); ++i) {
623 argument = args[i];
624 if (argument.args == NULL)
625 res = argument.func();
626 else
627 res = argument.func(argument.args);
628 element = smprintf(argument.format, res);
629 if (element == NULL) {
630 element = smprintf(UNKNOWN_STR);
631 fprintf(stderr, "Failed to format output.\n");
632 }
633 strlcat(status_string, element, sizeof(status_string));
634 free(res);
635 free(element);
636 }
637
638 XStoreName(dpy, DefaultRootWindow(dpy), status_string);
639 XSync(dpy, False);
640 sleep(UPDATE_INTERVAL -1);
641 }
642
643 /* NOT REACHED */
644 /*
645 * TODO: find out a way to exit successfully
646 * to prevent memory leaks
647 */
648 XCloseDisplay(dpy);
649 return 0;
650 }