xref: /openbsd/usr.sbin/apm/apm.c (revision 3abecac1)
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