1 /* $OpenBSD: apm.c,v 1.43 2022/11/09 18:48:11 mbuhl Exp $ */
2
3 /*
4 * Copyright (c) 1996 John T. Kohl
5 * All rights reserved.
6 *
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
9 * are met:
10 * 1. Redistributions of source code must retain the above copyright
11 * notice, this list of conditions and the following disclaimer.
12 * 2. Redistributions in binary form must reproduce the above copyright
13 * notice, this list of conditions and the following disclaimer in the
14 * documentation and/or other materials provided with the distribution.
15 * 3. The name of the author may not be used to endorse or promote products
16 * derived from this software without specific prior written permission.
17 *
18 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR `AS IS'' AND ANY EXPRESS OR
19 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
20 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
21 * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
22 * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
23 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
24 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
25 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
26 * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
27 * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
28 * POSSIBILITY OF SUCH DAMAGE.
29 *
30 */
31
32 #include <sys/types.h>
33 #include <sys/sysctl.h>
34 #include <sys/socket.h>
35 #include <sys/un.h>
36 #include <sys/ioctl.h>
37 #include <machine/apmvar.h>
38 #include <stdio.h>
39 #include <stdlib.h>
40 #include <unistd.h>
41 #include <fcntl.h>
42 #include <errno.h>
43 #include <err.h>
44 #include <string.h>
45 #include "pathnames.h"
46 #include "apm-proto.h"
47
48 #define FALSE 0
49 #define TRUE 1
50
51 extern char *__progname;
52
53 static int do_zzz(int, enum apm_action);
54 static int open_socket(const char *);
55 static int send_command(int, struct apm_command *,
56 struct apm_reply *);
57 static __dead void usage(void);
58 static __dead void zzusage(void);
59
60 static __dead void
usage(void)61 usage(void)
62 {
63 fprintf(stderr,"usage: %s [-AabHLlmPSvZz] [-f sockname]\n",
64 __progname);
65 exit(1);
66 }
67
68 static __dead void
zzusage(void)69 zzusage(void)
70 {
71 fprintf(stderr,"usage: %s [-SZz] [-f sockname]\n",
72 __progname);
73 exit(1);
74 }
75
76 static int
send_command(int fd,struct apm_command * cmd,struct apm_reply * reply)77 send_command(int fd, struct apm_command *cmd, struct apm_reply *reply)
78 {
79 /* send a command to the apm daemon */
80 cmd->vno = APMD_VNO;
81
82 if (send(fd, cmd, sizeof(*cmd), 0) == sizeof(*cmd)) {
83 if (recv(fd, reply, sizeof(*reply), 0) != sizeof(*reply)) {
84 warn("invalid reply from APM daemon");
85 return (1);
86 }
87 } else {
88 warn("invalid send to APM daemon");
89 return (1);
90 }
91 return (0);
92 }
93
94 static int
do_zzz(int fd,enum apm_action action)95 do_zzz(int fd, enum apm_action action)
96 {
97 struct apm_command command;
98 struct apm_reply reply;
99 char *msg;
100 int ret;
101
102 bzero(&reply, sizeof reply);
103
104 switch (action) {
105 case NONE:
106 case SUSPEND:
107 command.action = SUSPEND;
108 msg = "Suspending system";
109 break;
110 case STANDBY:
111 command.action = STANDBY;
112 msg = "System standing by";
113 break;
114 case HIBERNATE:
115 command.action = HIBERNATE;
116 msg = "Hibernating system";
117 break;
118 default:
119 zzusage();
120 }
121
122 printf("%s...\n", msg);
123 ret = send_command(fd, &command, &reply);
124 if (ret == 0 && reply.error)
125 errx(1, "%s: %s", apm_state(reply.newstate), strerror(reply.error));
126 exit(ret);
127 }
128
129 static int
open_socket(const char * sockname)130 open_socket(const char *sockname)
131 {
132 int sock, errr;
133 struct sockaddr_un s_un;
134
135 sock = socket(AF_UNIX, SOCK_STREAM, 0);
136 if (sock == -1)
137 err(1, "cannot create local socket");
138
139 s_un.sun_family = AF_UNIX;
140 strlcpy(s_un.sun_path, sockname, sizeof(s_un.sun_path));
141 if (connect(sock, (struct sockaddr *)&s_un, sizeof(s_un)) == -1) {
142 errr = errno;
143 close(sock);
144 errno = errr;
145 sock = -1;
146 }
147 return (sock);
148 }
149
150 int
main(int argc,char * argv[])151 main(int argc, char *argv[])
152 {
153 const char *sockname = _PATH_APM_SOCKET;
154 int doac = FALSE;
155 int dopct = FALSE;
156 int dobstate = FALSE;
157 int domin = FALSE;
158 int doperf = FALSE;
159 int verbose = FALSE;
160 int ch, fd, rval;
161 enum apm_action action = NONE;
162 struct apm_command command;
163 struct apm_reply reply;
164 int cpuspeed_mib[] = { CTL_HW, HW_CPUSPEED }, cpuspeed;
165 size_t cpuspeed_sz = sizeof(cpuspeed);
166
167 if (sysctl(cpuspeed_mib, 2, &cpuspeed, &cpuspeed_sz, NULL, 0) == -1)
168 cpuspeed = 0;
169
170 while ((ch = getopt(argc, argv, "ACHLlmbvaPSzZf:")) != -1) {
171 switch (ch) {
172 case 'v':
173 verbose = TRUE;
174 break;
175 case 'f':
176 sockname = optarg;
177 break;
178 case 'z':
179 if (action != NONE)
180 usage();
181 action = SUSPEND;
182 break;
183 case 'S':
184 if (action != NONE)
185 usage();
186 action = STANDBY;
187 break;
188 case 'Z':
189 if (action != NONE)
190 usage();
191 action = HIBERNATE;
192 break;
193 case 'A':
194 case 'C':
195 if (action != NONE)
196 usage();
197 action = SETPERF_AUTO;
198 break;
199 case 'H':
200 if (action != NONE)
201 usage();
202 action = SETPERF_HIGH;
203 break;
204 case 'L':
205 if (action != NONE)
206 usage();
207 action = SETPERF_LOW;
208 break;
209 case 'b':
210 if (action != NONE && action != GETSTATUS)
211 usage();
212 dobstate = TRUE;
213 action = GETSTATUS;
214 break;
215 case 'l':
216 if (action != NONE && action != GETSTATUS)
217 usage();
218 dopct = TRUE;
219 action = GETSTATUS;
220 break;
221 case 'm':
222 if (action != NONE && action != GETSTATUS)
223 usage();
224 domin = TRUE;
225 action = GETSTATUS;
226 break;
227 case 'a':
228 if (action != NONE && action != GETSTATUS)
229 usage();
230 doac = TRUE;
231 action = GETSTATUS;
232 break;
233 case 'P':
234 if (action != NONE && action != GETSTATUS)
235 usage();
236 doperf = TRUE;
237 action = GETSTATUS;
238 break;
239 default:
240 if (!strcmp(__progname, "zzz") ||
241 !strcmp(__progname, "ZZZ"))
242 zzusage();
243 else
244 usage();
245 }
246 }
247 argc -= optind;
248 argv += optind;
249 if (argc)
250 usage();
251
252 fd = open_socket(sockname);
253
254 if (fd != -1) {
255 if (pledge("stdio", NULL) == -1)
256 err(1, "pledge");
257 }
258
259 if (!strcmp(__progname, "zzz")) {
260 if (fd < 0)
261 err(1, "cannot connect to apmd");
262 else
263 return (do_zzz(fd, action));
264 } else if (!strcmp(__progname, "ZZZ")) {
265 if (fd < 0)
266 err(1, "cannot connect to apmd");
267 else
268 return (do_zzz(fd, HIBERNATE));
269 }
270
271
272 bzero(&reply, sizeof reply);
273 reply.batterystate.battery_state = APM_BATT_UNKNOWN;
274 reply.batterystate.ac_state = APM_AC_UNKNOWN;
275 reply.perfmode = PERF_MANUAL;
276 reply.cpuspeed = cpuspeed;
277
278 switch (action) {
279 case SETPERF_LOW:
280 case SETPERF_HIGH:
281 case SETPERF_AUTO:
282 if (fd == -1)
283 errx(1, "cannot connect to apmd, "
284 "not changing performance adjustment mode");
285 goto balony;
286 case NONE:
287 action = GETSTATUS;
288 verbose = doac = dopct = dobstate = domin = doperf = TRUE;
289 /* FALLTHROUGH */
290 case GETSTATUS:
291 if (fd == -1) {
292 /* open the device directly and get status */
293 fd = open(_PATH_APM_NORMAL, O_RDONLY);
294 if (ioctl(fd, APM_IOC_GETPOWER,
295 &reply.batterystate) == 0) {
296 if (pledge("stdio", NULL) == -1)
297 err(1, "pledge");
298
299 goto printval;
300 }
301 }
302 /* FALLTHROUGH */
303 balony:
304 case SUSPEND:
305 case STANDBY:
306 case HIBERNATE:
307 command.action = action;
308 break;
309 default:
310 usage();
311 }
312
313 if (fd != -1 && (rval = send_command(fd, &command, &reply)) != 0)
314 errx(rval, "cannot get reply from APM daemon");
315
316 switch (action) {
317 case GETSTATUS:
318 printval:
319 if (!verbose) {
320 if (dobstate)
321 printf("%d\n",
322 reply.batterystate.battery_state);
323 if (dopct)
324 printf("%d\n",
325 reply.batterystate.battery_life);
326 if (domin) {
327 if (reply.batterystate.minutes_left ==
328 (u_int)-1)
329 printf("unknown\n");
330 else
331 printf("%d\n",
332 reply.batterystate.minutes_left);
333 }
334 if (doac)
335 printf("%d\n",
336 reply.batterystate.ac_state);
337 if (doperf)
338 printf("%d\n", reply.perfmode);
339 break;
340 }
341
342 if (dobstate) {
343 printf("Battery state: %s",
344 battstate(reply.batterystate.battery_state));
345 if (!dopct && !domin)
346 printf("\n");
347 }
348
349 if (dopct && !dobstate)
350 printf("Battery remaining: %d percent",
351 reply.batterystate.battery_life);
352 else if (dopct)
353 printf(", %d%% remaining",
354 reply.batterystate.battery_life);
355 if (dopct && !domin)
356 printf("\n");
357
358 if (domin && !dobstate && !dopct) {
359 if (reply.batterystate.battery_state ==
360 APM_BATT_CHARGING)
361 printf("Remaining battery recharge "
362 "time estimate: %d minutes\n",
363 reply.batterystate.minutes_left);
364 else if (reply.batterystate.minutes_left == 0 &&
365 reply.batterystate.battery_life > 10)
366 printf("Battery life estimate: "
367 "not available\n");
368 else
369 {
370 printf("Battery life estimate: ");
371 if (reply.batterystate.minutes_left ==
372 (u_int)-1)
373 printf("unknown\n");
374 else
375 printf("%d minutes\n",
376 reply.batterystate.minutes_left);
377 }
378 } else if (domin) {
379 if (reply.batterystate.battery_state ==
380 APM_BATT_CHARGING)
381 {
382 if (reply.batterystate.minutes_left ==
383 (u_int)-1)
384 printf(", unknown");
385 else
386 printf(", %d minutes",
387 reply.batterystate.minutes_left);
388 printf(" recharge time estimate\n");
389 }
390 else if (reply.batterystate.minutes_left == 0 &&
391 reply.batterystate.battery_life > 10)
392 printf(", unknown life estimate\n");
393 else
394 {
395 if (reply.batterystate.minutes_left ==
396 (u_int)-1)
397 printf(", unknown");
398 else
399 printf(", %d minutes",
400 reply.batterystate.minutes_left);
401 printf(" life estimate\n");
402 }
403 }
404
405 if (doac)
406 printf("AC adapter state: %s\n",
407 ac_state(reply.batterystate.ac_state));
408
409 if (doperf)
410 printf("Performance adjustment mode: %s (%d MHz)\n",
411 perf_mode(reply.perfmode), reply.cpuspeed);
412 break;
413 default:
414 break;
415 }
416
417 switch (reply.newstate) {
418 case SUSPEND:
419 printf("System will enter suspend mode momentarily.\n");
420 break;
421 case STANDBY:
422 printf("System will enter standby mode momentarily.\n");
423 break;
424 case HIBERNATE:
425 printf("System will enter hibernate mode momentarily.\n");
426 break;
427 default:
428 break;
429 }
430 if (reply.error)
431 errx(1, "%s: %s", apm_state(reply.newstate), strerror(reply.error));
432 return (0);
433 }
434