1*3abecac1Smbuhl /* $OpenBSD: apm.c,v 1.43 2022/11/09 18:48:11 mbuhl Exp $ */
240bbec49Smillert
3054c7b81Shvozda /*
4054c7b81Shvozda * Copyright (c) 1996 John T. Kohl
5054c7b81Shvozda * All rights reserved.
6054c7b81Shvozda *
7054c7b81Shvozda * Redistribution and use in source and binary forms, with or without
8054c7b81Shvozda * modification, are permitted provided that the following conditions
9054c7b81Shvozda * are met:
10054c7b81Shvozda * 1. Redistributions of source code must retain the above copyright
11054c7b81Shvozda * notice, this list of conditions and the following disclaimer.
12054c7b81Shvozda * 2. Redistributions in binary form must reproduce the above copyright
13054c7b81Shvozda * notice, this list of conditions and the following disclaimer in the
14054c7b81Shvozda * documentation and/or other materials provided with the distribution.
15054c7b81Shvozda * 3. The name of the author may not be used to endorse or promote products
16054c7b81Shvozda * derived from this software without specific prior written permission.
17054c7b81Shvozda *
18054c7b81Shvozda * THIS SOFTWARE IS PROVIDED BY THE AUTHOR `AS IS'' AND ANY EXPRESS OR
19054c7b81Shvozda * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
20054c7b81Shvozda * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
21054c7b81Shvozda * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
22054c7b81Shvozda * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
23054c7b81Shvozda * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
24054c7b81Shvozda * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
25054c7b81Shvozda * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
26054c7b81Shvozda * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
27054c7b81Shvozda * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
28054c7b81Shvozda * POSSIBILITY OF SUCH DAMAGE.
29054c7b81Shvozda *
30054c7b81Shvozda */
31054c7b81Shvozda
32b9fc9a72Sderaadt #include <sys/types.h>
33a2123b9aSderaadt #include <sys/sysctl.h>
34a2123b9aSderaadt #include <sys/socket.h>
35a2123b9aSderaadt #include <sys/un.h>
36a2123b9aSderaadt #include <sys/ioctl.h>
37a2123b9aSderaadt #include <machine/apmvar.h>
38054c7b81Shvozda #include <stdio.h>
39054c7b81Shvozda #include <stdlib.h>
40054c7b81Shvozda #include <unistd.h>
41054c7b81Shvozda #include <fcntl.h>
42d246eb98Smickey #include <errno.h>
43054c7b81Shvozda #include <err.h>
44054c7b81Shvozda #include <string.h>
45054c7b81Shvozda #include "pathnames.h"
46054c7b81Shvozda #include "apm-proto.h"
47054c7b81Shvozda
48054c7b81Shvozda #define FALSE 0
49054c7b81Shvozda #define TRUE 1
50054c7b81Shvozda
51054c7b81Shvozda extern char *__progname;
52054c7b81Shvozda
53224a603cSanton static int do_zzz(int, enum apm_action);
54224a603cSanton static int open_socket(const char *);
55224a603cSanton static int send_command(int, struct apm_command *,
56224a603cSanton struct apm_reply *);
57224a603cSanton static __dead void usage(void);
58224a603cSanton static __dead void zzusage(void);
59054c7b81Shvozda
60224a603cSanton static __dead void
usage(void)61054c7b81Shvozda usage(void)
62054c7b81Shvozda {
6388362101Sjmc fprintf(stderr,"usage: %s [-AabHLlmPSvZz] [-f sockname]\n",
64c16dfee5Sderaadt __progname);
65054c7b81Shvozda exit(1);
66054c7b81Shvozda }
67054c7b81Shvozda
68224a603cSanton static __dead void
zzusage(void)69054c7b81Shvozda zzusage(void)
70054c7b81Shvozda {
715e2f88c9Sjmc fprintf(stderr,"usage: %s [-SZz] [-f sockname]\n",
72054c7b81Shvozda __progname);
73054c7b81Shvozda exit(1);
74054c7b81Shvozda }
75054c7b81Shvozda
76224a603cSanton static int
send_command(int fd,struct apm_command * cmd,struct apm_reply * reply)774b6e8b27Sderaadt send_command(int fd, struct apm_command *cmd, struct apm_reply *reply)
78054c7b81Shvozda {
79054c7b81Shvozda /* send a command to the apm daemon */
80054c7b81Shvozda cmd->vno = APMD_VNO;
81054c7b81Shvozda
82054c7b81Shvozda if (send(fd, cmd, sizeof(*cmd), 0) == sizeof(*cmd)) {
83054c7b81Shvozda if (recv(fd, reply, sizeof(*reply), 0) != sizeof(*reply)) {
8440bbec49Smillert warn("invalid reply from APM daemon");
856d78643eSderaadt return (1);
86054c7b81Shvozda }
87054c7b81Shvozda } else {
88054c7b81Shvozda warn("invalid send to APM daemon");
896d78643eSderaadt return (1);
90054c7b81Shvozda }
91c16dfee5Sderaadt return (0);
92054c7b81Shvozda }
93054c7b81Shvozda
94224a603cSanton static int
do_zzz(int fd,enum apm_action action)95d246eb98Smickey do_zzz(int fd, enum apm_action action)
96054c7b81Shvozda {
97054c7b81Shvozda struct apm_command command;
98054c7b81Shvozda struct apm_reply reply;
99d48e549cSderaadt char *msg;
100c7cff24eSkn int ret;
101054c7b81Shvozda
102*3abecac1Smbuhl bzero(&reply, sizeof reply);
103*3abecac1Smbuhl
104054c7b81Shvozda switch (action) {
105054c7b81Shvozda case NONE:
106054c7b81Shvozda case SUSPEND:
107054c7b81Shvozda command.action = SUSPEND;
108d48e549cSderaadt msg = "Suspending system";
109054c7b81Shvozda break;
110054c7b81Shvozda case STANDBY:
111054c7b81Shvozda command.action = STANDBY;
112d48e549cSderaadt msg = "System standing by";
113054c7b81Shvozda break;
11429a085afSderaadt case HIBERNATE:
11529a085afSderaadt command.action = HIBERNATE;
116d48e549cSderaadt msg = "Hibernating system";
11729a085afSderaadt break;
118054c7b81Shvozda default:
119054c7b81Shvozda zzusage();
120054c7b81Shvozda }
121054c7b81Shvozda
122d48e549cSderaadt printf("%s...\n", msg);
123c7cff24eSkn ret = send_command(fd, &command, &reply);
124*3abecac1Smbuhl if (ret == 0 && reply.error)
125c7cff24eSkn errx(1, "%s: %s", apm_state(reply.newstate), strerror(reply.error));
126c7cff24eSkn exit(ret);
127054c7b81Shvozda }
128054c7b81Shvozda
129224a603cSanton static int
open_socket(const char * sockname)130054c7b81Shvozda open_socket(const char *sockname)
131054c7b81Shvozda {
132054c7b81Shvozda int sock, errr;
133054c7b81Shvozda struct sockaddr_un s_un;
134054c7b81Shvozda
135054c7b81Shvozda sock = socket(AF_UNIX, SOCK_STREAM, 0);
136054c7b81Shvozda if (sock == -1)
137054c7b81Shvozda err(1, "cannot create local socket");
138054c7b81Shvozda
139054c7b81Shvozda s_un.sun_family = AF_UNIX;
140e9bd1503Sguenther strlcpy(s_un.sun_path, sockname, sizeof(s_un.sun_path));
141e9bd1503Sguenther if (connect(sock, (struct sockaddr *)&s_un, sizeof(s_un)) == -1) {
142054c7b81Shvozda errr = errno;
143054c7b81Shvozda close(sock);
144054c7b81Shvozda errno = errr;
145876a8cf6Smickey sock = -1;
146054c7b81Shvozda }
147c16dfee5Sderaadt return (sock);
148054c7b81Shvozda }
149054c7b81Shvozda
150a7f3a53aSmickey int
main(int argc,char * argv[])151054c7b81Shvozda main(int argc, char *argv[])
152054c7b81Shvozda {
153d246eb98Smickey const char *sockname = _PATH_APM_SOCKET;
154054c7b81Shvozda int doac = FALSE;
155054c7b81Shvozda int dopct = FALSE;
156054c7b81Shvozda int dobstate = FALSE;
157a7f3a53aSmickey int domin = FALSE;
158f6da2e61Ssturm int doperf = FALSE;
159054c7b81Shvozda int verbose = FALSE;
160d246eb98Smickey int ch, fd, rval;
161054c7b81Shvozda enum apm_action action = NONE;
162054c7b81Shvozda struct apm_command command;
163054c7b81Shvozda struct apm_reply reply;
164a2123b9aSderaadt int cpuspeed_mib[] = { CTL_HW, HW_CPUSPEED }, cpuspeed;
165a2123b9aSderaadt size_t cpuspeed_sz = sizeof(cpuspeed);
166054c7b81Shvozda
167df69c215Sderaadt if (sysctl(cpuspeed_mib, 2, &cpuspeed, &cpuspeed_sz, NULL, 0) == -1)
16873cd97e4Srobert cpuspeed = 0;
169372b8ca3Sderaadt
17029a085afSderaadt while ((ch = getopt(argc, argv, "ACHLlmbvaPSzZf:")) != -1) {
171054c7b81Shvozda switch (ch) {
172054c7b81Shvozda case 'v':
173054c7b81Shvozda verbose = TRUE;
174054c7b81Shvozda break;
175054c7b81Shvozda case 'f':
176054c7b81Shvozda sockname = optarg;
177054c7b81Shvozda break;
178054c7b81Shvozda case 'z':
179054c7b81Shvozda if (action != NONE)
180054c7b81Shvozda usage();
181054c7b81Shvozda action = SUSPEND;
182054c7b81Shvozda break;
183054c7b81Shvozda case 'S':
184054c7b81Shvozda if (action != NONE)
185054c7b81Shvozda usage();
186054c7b81Shvozda action = STANDBY;
187054c7b81Shvozda break;
18829a085afSderaadt case 'Z':
18929a085afSderaadt if (action != NONE)
19029a085afSderaadt usage();
19129a085afSderaadt action = HIBERNATE;
19229a085afSderaadt break;
193f6da2e61Ssturm case 'A':
194ab844ba2Sbeck case 'C':
195ab844ba2Sbeck if (action != NONE)
196ab844ba2Sbeck usage();
19759f67d8dSjca action = SETPERF_AUTO;
198ab844ba2Sbeck break;
199f6da2e61Ssturm case 'H':
200f6da2e61Ssturm if (action != NONE)
201f6da2e61Ssturm usage();
202f6da2e61Ssturm action = SETPERF_HIGH;
203f6da2e61Ssturm break;
204f6da2e61Ssturm case 'L':
205f6da2e61Ssturm if (action != NONE)
206f6da2e61Ssturm usage();
207f6da2e61Ssturm action = SETPERF_LOW;
208f6da2e61Ssturm break;
209054c7b81Shvozda case 'b':
210054c7b81Shvozda if (action != NONE && action != GETSTATUS)
211054c7b81Shvozda usage();
212054c7b81Shvozda dobstate = TRUE;
213054c7b81Shvozda action = GETSTATUS;
214054c7b81Shvozda break;
215054c7b81Shvozda case 'l':
216054c7b81Shvozda if (action != NONE && action != GETSTATUS)
217054c7b81Shvozda usage();
218054c7b81Shvozda dopct = TRUE;
219054c7b81Shvozda action = GETSTATUS;
220054c7b81Shvozda break;
221a7f3a53aSmickey case 'm':
222a7f3a53aSmickey if (action != NONE && action != GETSTATUS)
223a7f3a53aSmickey usage();
224a7f3a53aSmickey domin = TRUE;
225a7f3a53aSmickey action = GETSTATUS;
226a7f3a53aSmickey break;
227054c7b81Shvozda case 'a':
228054c7b81Shvozda if (action != NONE && action != GETSTATUS)
229054c7b81Shvozda usage();
230054c7b81Shvozda doac = TRUE;
231054c7b81Shvozda action = GETSTATUS;
232054c7b81Shvozda break;
233f6da2e61Ssturm case 'P':
234f6da2e61Ssturm if (action != NONE && action != GETSTATUS)
235f6da2e61Ssturm usage();
236f6da2e61Ssturm doperf = TRUE;
237f6da2e61Ssturm action = GETSTATUS;
238f6da2e61Ssturm break;
239054c7b81Shvozda default:
240224a603cSanton if (!strcmp(__progname, "zzz") ||
241224a603cSanton !strcmp(__progname, "ZZZ"))
242b2c4cc1eSsobrado zzusage();
243b2c4cc1eSsobrado else
244054c7b81Shvozda usage();
245054c7b81Shvozda }
2466d78643eSderaadt }
247b15928b2Stedu argc -= optind;
248b15928b2Stedu argv += optind;
249b15928b2Stedu if (argc)
250b15928b2Stedu usage();
251054c7b81Shvozda
252054c7b81Shvozda fd = open_socket(sockname);
253054c7b81Shvozda
254cef29278Sderaadt if (fd != -1) {
2551c33ef21Smestre if (pledge("stdio", NULL) == -1)
256cef29278Sderaadt err(1, "pledge");
257cef29278Sderaadt }
258cef29278Sderaadt
259ef1540bfSmickey if (!strcmp(__progname, "zzz")) {
260ef1540bfSmickey if (fd < 0)
261ef1540bfSmickey err(1, "cannot connect to apmd");
262ef1540bfSmickey else
263d246eb98Smickey return (do_zzz(fd, action));
26429a085afSderaadt } else if (!strcmp(__progname, "ZZZ")) {
26529a085afSderaadt if (fd < 0)
26629a085afSderaadt err(1, "cannot connect to apmd");
26729a085afSderaadt else
26829a085afSderaadt return (do_zzz(fd, HIBERNATE));
269ef1540bfSmickey }
270d246eb98Smickey
27129a085afSderaadt
272a2123b9aSderaadt bzero(&reply, sizeof reply);
273a2123b9aSderaadt reply.batterystate.battery_state = APM_BATT_UNKNOWN;
274a2123b9aSderaadt reply.batterystate.ac_state = APM_AC_UNKNOWN;
275a2123b9aSderaadt reply.perfmode = PERF_MANUAL;
276a2123b9aSderaadt reply.cpuspeed = cpuspeed;
277a2123b9aSderaadt
278054c7b81Shvozda switch (action) {
279a2123b9aSderaadt case SETPERF_LOW:
280a2123b9aSderaadt case SETPERF_HIGH:
281a2123b9aSderaadt case SETPERF_AUTO:
282a2123b9aSderaadt if (fd == -1)
28357bc3fecSsturm errx(1, "cannot connect to apmd, "
28457bc3fecSsturm "not changing performance adjustment mode");
285a2123b9aSderaadt goto balony;
286054c7b81Shvozda case NONE:
287054c7b81Shvozda action = GETSTATUS;
288afc54a00Ssturm verbose = doac = dopct = dobstate = domin = doperf = TRUE;
289cc5d5fa6Ssturm /* FALLTHROUGH */
290054c7b81Shvozda case GETSTATUS:
291054c7b81Shvozda if (fd == -1) {
292054c7b81Shvozda /* open the device directly and get status */
293054c7b81Shvozda fd = open(_PATH_APM_NORMAL, O_RDONLY);
294d246eb98Smickey if (ioctl(fd, APM_IOC_GETPOWER,
2951c33ef21Smestre &reply.batterystate) == 0) {
2961c33ef21Smestre if (pledge("stdio", NULL) == -1)
2971c33ef21Smestre err(1, "pledge");
2981c33ef21Smestre
299054c7b81Shvozda goto printval;
300054c7b81Shvozda }
3011c33ef21Smestre }
302cc5d5fa6Ssturm /* FALLTHROUGH */
303a2123b9aSderaadt balony:
304054c7b81Shvozda case SUSPEND:
305054c7b81Shvozda case STANDBY:
30629a085afSderaadt case HIBERNATE:
307054c7b81Shvozda command.action = action;
308054c7b81Shvozda break;
309054c7b81Shvozda default:
310054c7b81Shvozda usage();
311054c7b81Shvozda }
312054c7b81Shvozda
313a2123b9aSderaadt if (fd != -1 && (rval = send_command(fd, &command, &reply)) != 0)
3146d78643eSderaadt errx(rval, "cannot get reply from APM daemon");
3156d78643eSderaadt
316054c7b81Shvozda switch (action) {
317054c7b81Shvozda case GETSTATUS:
318054c7b81Shvozda printval:
3196d78643eSderaadt if (!verbose) {
320054c7b81Shvozda if (dobstate)
321d246eb98Smickey printf("%d\n",
322d246eb98Smickey reply.batterystate.battery_state);
323054c7b81Shvozda if (dopct)
324d246eb98Smickey printf("%d\n",
325d246eb98Smickey reply.batterystate.battery_life);
326e4613c9eSmiod if (domin) {
327e4613c9eSmiod if (reply.batterystate.minutes_left ==
328e4613c9eSmiod (u_int)-1)
329e4613c9eSmiod printf("unknown\n");
330e4613c9eSmiod else
331d246eb98Smickey printf("%d\n",
332d246eb98Smickey reply.batterystate.minutes_left);
333e4613c9eSmiod }
334054c7b81Shvozda if (doac)
335d246eb98Smickey printf("%d\n",
336d246eb98Smickey reply.batterystate.ac_state);
337f6da2e61Ssturm if (doperf)
338a99a7ef6Ssturm printf("%d\n", reply.perfmode);
3396d78643eSderaadt break;
340054c7b81Shvozda }
341afc54a00Ssturm
342afc54a00Ssturm if (dobstate) {
343afc54a00Ssturm printf("Battery state: %s",
3446d78643eSderaadt battstate(reply.batterystate.battery_state));
345afc54a00Ssturm if (!dopct && !domin)
346afc54a00Ssturm printf("\n");
347afc54a00Ssturm }
348afc54a00Ssturm
349afc54a00Ssturm if (dopct && !dobstate)
350afc54a00Ssturm printf("Battery remaining: %d percent",
3516d78643eSderaadt reply.batterystate.battery_life);
352afc54a00Ssturm else if (dopct)
353afc54a00Ssturm printf(", %d%% remaining",
354afc54a00Ssturm reply.batterystate.battery_life);
355afc54a00Ssturm if (dopct && !domin)
356afc54a00Ssturm printf("\n");
357afc54a00Ssturm
358afc54a00Ssturm if (domin && !dobstate && !dopct) {
3596d78643eSderaadt if (reply.batterystate.battery_state ==
3606d78643eSderaadt APM_BATT_CHARGING)
3616d78643eSderaadt printf("Remaining battery recharge "
3626d78643eSderaadt "time estimate: %d minutes\n",
3636d78643eSderaadt reply.batterystate.minutes_left);
3646d78643eSderaadt else if (reply.batterystate.minutes_left == 0 &&
3656d78643eSderaadt reply.batterystate.battery_life > 10)
3666d78643eSderaadt printf("Battery life estimate: "
3676d78643eSderaadt "not available\n");
3686d78643eSderaadt else
369e4613c9eSmiod {
370e4613c9eSmiod printf("Battery life estimate: ");
371e4613c9eSmiod if (reply.batterystate.minutes_left ==
372e4613c9eSmiod (u_int)-1)
373e4613c9eSmiod printf("unknown\n");
374e4613c9eSmiod else
375e4613c9eSmiod printf("%d minutes\n",
3766d78643eSderaadt reply.batterystate.minutes_left);
3776d78643eSderaadt }
378afc54a00Ssturm } else if (domin) {
379afc54a00Ssturm if (reply.batterystate.battery_state ==
380afc54a00Ssturm APM_BATT_CHARGING)
38106f42f13Ssdk {
38206f42f13Ssdk if (reply.batterystate.minutes_left ==
38306f42f13Ssdk (u_int)-1)
38406f42f13Ssdk printf(", unknown");
38506f42f13Ssdk else
38606f42f13Ssdk printf(", %d minutes",
387afc54a00Ssturm reply.batterystate.minutes_left);
38806f42f13Ssdk printf(" recharge time estimate\n");
38906f42f13Ssdk }
390afc54a00Ssturm else if (reply.batterystate.minutes_left == 0 &&
391afc54a00Ssturm reply.batterystate.battery_life > 10)
392afc54a00Ssturm printf(", unknown life estimate\n");
393afc54a00Ssturm else
394afc54a00Ssturm {
395afc54a00Ssturm if (reply.batterystate.minutes_left ==
396afc54a00Ssturm (u_int)-1)
397afc54a00Ssturm printf(", unknown");
398afc54a00Ssturm else
399afc54a00Ssturm printf(", %d minutes",
400afc54a00Ssturm reply.batterystate.minutes_left);
401afc54a00Ssturm printf(" life estimate\n");
402e4613c9eSmiod }
403afc54a00Ssturm }
404afc54a00Ssturm
4056d78643eSderaadt if (doac)
406ac50edb2Sjmc printf("AC adapter state: %s\n",
4076d78643eSderaadt ac_state(reply.batterystate.ac_state));
408afc54a00Ssturm
409f6da2e61Ssturm if (doperf)
410a99a7ef6Ssturm printf("Performance adjustment mode: %s (%d MHz)\n",
411a99a7ef6Ssturm perf_mode(reply.perfmode), reply.cpuspeed);
412054c7b81Shvozda break;
413054c7b81Shvozda default:
414dc8cf138Sderaadt break;
415054c7b81Shvozda }
4166d78643eSderaadt
417054c7b81Shvozda switch (reply.newstate) {
418054c7b81Shvozda case SUSPEND:
419054c7b81Shvozda printf("System will enter suspend mode momentarily.\n");
420054c7b81Shvozda break;
421054c7b81Shvozda case STANDBY:
422054c7b81Shvozda printf("System will enter standby mode momentarily.\n");
423054c7b81Shvozda break;
42429a085afSderaadt case HIBERNATE:
42529a085afSderaadt printf("System will enter hibernate mode momentarily.\n");
42629a085afSderaadt break;
427054c7b81Shvozda default:
428dc8cf138Sderaadt break;
429054c7b81Shvozda }
430c7cff24eSkn if (reply.error)
431c7cff24eSkn errx(1, "%s: %s", apm_state(reply.newstate), strerror(reply.error));
432a7f3a53aSmickey return (0);
433054c7b81Shvozda }
434