dwmstat.c 4.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235
  1. #include <sys/ioctl.h>
  2. #include <sys/param.h>
  3. #include <sys/audioio.h>
  4. #include <sys/sensors.h>
  5. #include <sys/socket.h>
  6. #include <sys/sysctl.h>
  7. /*
  8. * XXX: All this for IFA_IN6(x) seems hackish
  9. */
  10. #include <net/if.h> /* IFNAMISZ */
  11. #include <netinet/in.h>
  12. #include <netinet6/in6_var.h> /* IFA_IN6(x) */
  13. #include <netdb.h>
  14. #include <err.h>
  15. #include <errno.h>
  16. #include <fcntl.h>
  17. #include <ifaddrs.h>
  18. #include <machine/apmvar.h>
  19. #include <signal.h>
  20. #include <stdarg.h>
  21. #include <stdio.h>
  22. #include <stdlib.h>
  23. #include <string.h>
  24. #include <time.h>
  25. #include <unistd.h>
  26. #include <X11/Xlib.h>
  27. #include "config.h"
  28. #define WRET(s, r) do { warn(s); return (r); } while(0)
  29. static Display *dpy;
  30. static char stat[MAX_LEN];
  31. static char battery(void);
  32. static char cputemp(void);
  33. static const char *ip(const char *);
  34. static const char *timedate(void);
  35. static char volume(void);
  36. static void handler(int);
  37. static char
  38. battery(void)
  39. {
  40. static int fd;
  41. static struct apm_power_info pi;
  42. if ((fd = open("/dev/apm", O_RDONLY)) == -1 ||
  43. ioctl(fd, APM_IOC_GETPOWER, &pi) == -1 ||
  44. close(fd) == -1)
  45. WRET("battery", -1);
  46. if (pi.battery_state == APM_BATT_UNKNOWN ||
  47. pi.battery_state == APM_BATTERY_ABSENT)
  48. WRET("unknown/missing battery", -1);
  49. return (pi.battery_life);
  50. }
  51. static char
  52. cputemp(void)
  53. {
  54. static struct sensor sn;
  55. static size_t sn_sz = sizeof(sn);
  56. static const int mib[5] = {CTL_HW, HW_SENSORS,
  57. 0, SENSOR_TEMP, 0};
  58. if (sysctl(mib, 5, &sn, &sn_sz, NULL, 0) == -1)
  59. WRET("cputemp", -1);
  60. return ((sn.value - 273150000LL) / 1000000LL);
  61. }
  62. static const char *
  63. ip(const char *name)
  64. {
  65. static struct ifaddrs *ifap, *ifa;
  66. static sa_family_t af;
  67. static char addr[NI_MAXHOST];
  68. if (getifaddrs(&ifap) == -1) {
  69. warn("getifaddrs");
  70. addr[0] = '!';
  71. addr[1] = '\0';
  72. return (addr);
  73. }
  74. for (ifa = ifap; ifa != NULL; ifa = ifa->ifa_next) {
  75. if (ifa->ifa_addr == NULL ||
  76. strcmp(ifa->ifa_name, name) != 0 ||
  77. (((af = ifa->ifa_addr->sa_family) == AF_INET6 &&
  78. IN6_IS_ADDR_LINKLOCAL(IFA_IN6(ifa))) ||
  79. af != AF_INET))
  80. continue;
  81. if (getnameinfo(ifa->ifa_addr, sizeof(ifa->ifa_addr),
  82. addr, sizeof(addr), NULL, 0, NI_NUMERICHOST) != 0) {
  83. warn("getnameinfo");
  84. addr[0] = '!';
  85. addr[1] = '\0';
  86. }
  87. break;
  88. }
  89. freeifaddrs(ifap);
  90. if (ifa == NULL) {
  91. addr[0] = '-';
  92. addr[1] = '\0';
  93. }
  94. return (addr);
  95. }
  96. static const char *
  97. timedate(void)
  98. {
  99. static time_t t;
  100. static struct tm *tm;
  101. static char s[64];
  102. if ((t = time(NULL)) == (time_t) - 1 ||
  103. (tm = localtime(&t)) == NULL ||
  104. strftime(s, sizeof(s), TIMEFMT, tm) == 0)
  105. WRET("timedate", "!");
  106. return (s);
  107. }
  108. static char
  109. volume(void)
  110. {
  111. static int cls = -1, fd, v;
  112. static struct mixer_devinfo mdi;
  113. static struct mixer_ctrl mc;
  114. if ((fd = open("/dev/mixer", O_RDONLY)) == -1)
  115. WRET("volume", -1);
  116. for (mdi.index = 0; cls == -1; ++mdi.index) {
  117. if (ioctl(fd, AUDIO_MIXER_DEVINFO, &mdi) == -1)
  118. goto fail;
  119. if (mdi.type == AUDIO_MIXER_CLASS &&
  120. strcmp(mdi.label.name, AudioCoutputs) == 0)
  121. cls = mdi.index;
  122. }
  123. for (mdi.index = 0, v = -1; v == -1; ++mdi.index) {
  124. if (ioctl(fd, AUDIO_MIXER_DEVINFO, &mdi) == -1)
  125. goto fail;
  126. if (mdi.type == AUDIO_MIXER_VALUE &&
  127. mdi.prev == AUDIO_MIXER_LAST &&
  128. mdi.mixer_class == cls &&
  129. strcmp(mdi.label.name, AudioNmaster) == 0) {
  130. mc.dev = mdi.index;
  131. if (ioctl(fd, AUDIO_MIXER_READ, &mc) == -1)
  132. goto fail;
  133. v = MAX(mc.un.value.level[AUDIO_MIXER_LEVEL_MONO],
  134. mc.un.value.level[AUDIO_MIXER_LEVEL_RIGHT]);
  135. }
  136. }
  137. if (v == -1 || close(fd) == -1)
  138. WRET("volume", -1);
  139. return (v * 100 / 255);
  140. fail:
  141. (void)close(fd);
  142. WRET("volume", -1);
  143. }
  144. static void
  145. handler(const int sig)
  146. {
  147. const int save_errno = errno;
  148. psignal((unsigned int)sig, "caught signal");
  149. switch (sig) {
  150. case SIGINT:
  151. case SIGTERM:
  152. (void)XCloseDisplay(dpy);
  153. exit(0);
  154. case SIGALRM:
  155. case SIGHUP:
  156. break;
  157. case SIGINFO:
  158. if (puts(stat) == EOF)
  159. warn("puts");
  160. break;
  161. }
  162. errno = save_errno;
  163. }
  164. int
  165. main(int argc, char **argv)
  166. {
  167. static int dflag = 0, r;
  168. static const int s[] = {SIGHUP, SIGINT, SIGABRT,
  169. SIGTERM, SIGINFO};
  170. if (argc > 1) {
  171. if (argc == 2 && strcmp(argv[1], "-d") == 0)
  172. dflag = 1;
  173. else {
  174. fprintf(stderr, "usage: %s [-d]\n", getprogname());
  175. return (1);
  176. }
  177. }
  178. if (!dflag && daemon(1, 1) == -1)
  179. err(1, "daemon");
  180. for (r = 0; r < (int)nitems(s); ++r)
  181. if (signal(s[r], handler) == SIG_ERR)
  182. err(1, "signal");
  183. if ((dpy = XOpenDisplay(NULL)) == NULL)
  184. errx(1, "cannot open display");
  185. loop:
  186. if ((r = snprintf(stat, MAX_LEN, OUTFMT, ip(INTERFACE), battery(),
  187. cputemp(), volume(), timedate())) == -1)
  188. warn("snprintf");
  189. else if (r >= MAX_LEN)
  190. warnx("status exceeds MAX_LEN");
  191. else {
  192. (void)XStoreName(dpy, DefaultRootWindow(dpy), stat);
  193. (void)XSync(dpy, False);
  194. }
  195. (void)sleep(INTERVAL);
  196. goto loop;
  197. /* unreachable */
  198. return (0);
  199. }