Xinqi Bao's Git

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