Xinqi Bao's Git

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