My slstatus configuration
25개 이상의 토픽을 선택하실 수 없습니다. Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 

697 lines
15 KiB

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