Xinqi Bao's Git

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