1 /* acpi.c - show power state
2  *
3  * Written by Isaac Dunham, 2013
4  *
5  * No standard.
6 
7 USE_ACPI(NEWTOY(acpi, "abctV", TOYFLAG_USR|TOYFLAG_BIN))
8 
9 config ACPI
10   bool "acpi"
11   default y
12   help
13     usage: acpi [-abctV]
14 
15     Show status of power sources and thermal devices.
16 
17     -a	show power adapters
18     -b	show batteries
19     -c	show cooling device state
20     -t	show temperatures
21     -V	show everything
22 */
23 
24 #define FOR_acpi
25 #include "toys.h"
26 
GLOBALS(int ac;int bat;int therm;int cool;char * cpath;)27 GLOBALS(
28   int ac;
29   int bat;
30   int therm;
31   int cool;
32   char *cpath;
33 )
34 
35 int read_int_at(int dirfd, char *name)
36 {
37   int fd, ret=0;
38   FILE *fil;
39 
40   if ((fd = openat(dirfd, name, O_RDONLY)) < 0) return -1;
41   if (!fscanf(fil = xfdopen(fd, "r"), "%d", &ret)) perror_exit("%s", name);
42   fclose(fil);
43 
44   return ret;
45 }
46 
acpi_callback(struct dirtree * tree)47 int acpi_callback(struct dirtree *tree)
48 {
49   int dfd, fd, len, on;
50 
51   errno = 0;
52 
53   if (tree->name[0]=='.') return 0;
54 
55   if (!tree->parent)
56     return DIRTREE_RECURSE|DIRTREE_SYMFOLLOW;
57 
58   if (0 <= (dfd = open((TT.cpath=dirtree_path(tree, NULL)), O_RDONLY))) {
59     if ((fd = openat(dfd, "type", O_RDONLY)) < 0) goto done;
60     len = readall(fd, toybuf, sizeof(toybuf));
61     close(fd);
62     if (len < 1) goto done;
63 
64     if (!strncmp(toybuf, "Battery", 7)) {
65       if ((toys.optflags & FLAG_b) || (!toys.optflags)) {
66         int cap = 0, curr = 0, max = 0;
67 
68         if ((cap = read_int_at(dfd, "capacity")) < 0) {
69           if ((max = read_int_at(dfd, "charge_full")) > 0)
70             curr = read_int_at(dfd, "charge_now");
71           else if ((max = read_int_at(dfd, "energy_full")) > 0)
72             curr = read_int_at(dfd, "energy_now");
73           if (max > 0 && curr >= 0) cap = 100 * curr / max;
74         }
75         if (cap >= 0) printf("Battery %d: %d%%\n", TT.bat++, cap);
76       }
77     } else if (toys.optflags & FLAG_a) {
78       if ((on = read_int_at(dfd, "online")) >= 0)
79         printf("Adapter %d: %s-line\n", TT.ac++, (on ? "on" : "off"));
80     }
81 done:
82     close(dfd);
83   }
84   free(TT.cpath);
85   return 0;
86 }
87 
temp_callback(struct dirtree * tree)88 int temp_callback(struct dirtree *tree)
89 {
90   int dfd, temp;
91 
92   if (tree->name[0]=='.') return 0;
93   if (!tree->parent || !tree->parent->parent)
94     return DIRTREE_RECURSE|DIRTREE_SYMFOLLOW;
95   errno = 0;
96 
97   if (0 <= (dfd = open((TT.cpath=dirtree_path(tree, NULL)), O_RDONLY))) {
98     if ((0 < (temp = read_int_at(dfd, "temp"))) || !errno) {
99       //some tempertures are in milli-C, some in deci-C
100       //reputedly some are in deci-K, but I have not seen them
101       if (((temp >= 1000) || (temp <= -1000)) && (temp%100 == 0))
102         temp /= 100;
103       printf("Thermal %d: %d.%d degrees C\n", TT.therm++, temp/10, temp%10);
104     }
105     close(dfd);
106   }
107   free(TT.cpath);
108   return 0;
109 }
110 
cool_callback(struct dirtree * tree)111 int cool_callback(struct dirtree *tree)
112 {
113   int dfd=5, cur, max;
114 
115   errno = 0;
116   memset(toybuf, 0, sizeof(toybuf));
117 
118   if (*tree->name == '.') return 0;
119   if (!tree->parent) return DIRTREE_RECURSE|DIRTREE_SYMFOLLOW;
120 
121 
122   if (0 <= (dfd = open((TT.cpath=dirtree_path(tree, &dfd)), O_RDONLY))) {
123     TT.cpath = strcat(TT.cpath, "/type");
124     if (readfile(TT.cpath, toybuf, 256) && !errno) {
125       toybuf[strlen(toybuf) -1] = 0;
126       cur=read_int_at(dfd, "cur_state");
127       max=read_int_at(dfd, "max_state");
128       if (errno)
129         printf("Cooling %d: %s no state information\n", TT.cool++, toybuf);
130       else printf("Cooling %d: %s %d of %d\n", TT.cool++, toybuf, cur, max);
131     }
132     close(dfd);
133   }
134   free(TT.cpath);
135   return 0;
136 }
137 
acpi_main(void)138 void acpi_main(void)
139 {
140   if (toys.optflags & FLAG_V) toys.optflags = FLAG_a|FLAG_b|FLAG_c|FLAG_t;
141   if (!toys.optflags) toys.optflags = FLAG_b;
142   if (toys.optflags & (FLAG_a|FLAG_b))
143     dirtree_read("/sys/class/power_supply", acpi_callback);
144   if (toys.optflags & FLAG_t) dirtree_read("/sys/class", temp_callback);
145   if (toys.optflags & FLAG_c) dirtree_read("/sys/class/thermal", cool_callback);
146 
147 }
148