1 /* $OpenBSD: apm.c,v 1.38 2021/04/06 20:30:32 kn 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 61 usage(void) 62 { 63 fprintf(stderr,"usage: %s [-AabHLlmPSvZz] [-f sockname]\n", 64 __progname); 65 exit(1); 66 } 67 68 static __dead void 69 zzusage(void) 70 { 71 fprintf(stderr,"usage: %s [-SZz] [-f sockname]\n", 72 __progname); 73 exit(1); 74 } 75 76 static int 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 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 switch (action) { 103 case NONE: 104 case SUSPEND: 105 command.action = SUSPEND; 106 msg = "Suspending system"; 107 break; 108 case STANDBY: 109 command.action = STANDBY; 110 msg = "System standing by"; 111 break; 112 case HIBERNATE: 113 command.action = HIBERNATE; 114 msg = "Hibernating system"; 115 break; 116 default: 117 zzusage(); 118 } 119 120 printf("%s...\n", msg); 121 ret = send_command(fd, &command, &reply); 122 if (reply.error) 123 errx(1, "%s: %s", apm_state(reply.newstate), strerror(reply.error)); 124 exit(ret); 125 } 126 127 static int 128 open_socket(const char *sockname) 129 { 130 int sock, errr; 131 struct sockaddr_un s_un; 132 133 sock = socket(AF_UNIX, SOCK_STREAM, 0); 134 if (sock == -1) 135 err(1, "cannot create local socket"); 136 137 s_un.sun_family = AF_UNIX; 138 strlcpy(s_un.sun_path, sockname, sizeof(s_un.sun_path)); 139 if (connect(sock, (struct sockaddr *)&s_un, sizeof(s_un)) == -1) { 140 errr = errno; 141 close(sock); 142 errno = errr; 143 sock = -1; 144 } 145 return (sock); 146 } 147 148 int 149 main(int argc, char *argv[]) 150 { 151 const char *sockname = _PATH_APM_SOCKET; 152 int doac = FALSE; 153 int dopct = FALSE; 154 int dobstate = FALSE; 155 int domin = FALSE; 156 int doperf = FALSE; 157 int verbose = FALSE; 158 int ch, fd, rval; 159 enum apm_action action = NONE; 160 struct apm_command command; 161 struct apm_reply reply; 162 int cpuspeed_mib[] = { CTL_HW, HW_CPUSPEED }, cpuspeed; 163 size_t cpuspeed_sz = sizeof(cpuspeed); 164 165 if (sysctl(cpuspeed_mib, 2, &cpuspeed, &cpuspeed_sz, NULL, 0) == -1) 166 err(1, "sysctl hw.cpuspeed"); 167 168 while ((ch = getopt(argc, argv, "ACHLlmbvaPSzZf:")) != -1) { 169 switch (ch) { 170 case 'v': 171 verbose = TRUE; 172 break; 173 case 'f': 174 sockname = optarg; 175 break; 176 case 'z': 177 if (action != NONE) 178 usage(); 179 action = SUSPEND; 180 break; 181 case 'S': 182 if (action != NONE) 183 usage(); 184 action = STANDBY; 185 break; 186 case 'Z': 187 if (action != NONE) 188 usage(); 189 action = HIBERNATE; 190 break; 191 case 'A': 192 case 'C': 193 if (action != NONE) 194 usage(); 195 action = SETPERF_AUTO; 196 break; 197 case 'H': 198 if (action != NONE) 199 usage(); 200 action = SETPERF_HIGH; 201 break; 202 case 'L': 203 if (action != NONE) 204 usage(); 205 action = SETPERF_LOW; 206 break; 207 case 'b': 208 if (action != NONE && action != GETSTATUS) 209 usage(); 210 dobstate = TRUE; 211 action = GETSTATUS; 212 break; 213 case 'l': 214 if (action != NONE && action != GETSTATUS) 215 usage(); 216 dopct = TRUE; 217 action = GETSTATUS; 218 break; 219 case 'm': 220 if (action != NONE && action != GETSTATUS) 221 usage(); 222 domin = TRUE; 223 action = GETSTATUS; 224 break; 225 case 'a': 226 if (action != NONE && action != GETSTATUS) 227 usage(); 228 doac = TRUE; 229 action = GETSTATUS; 230 break; 231 case 'P': 232 if (action != NONE && action != GETSTATUS) 233 usage(); 234 doperf = TRUE; 235 action = GETSTATUS; 236 break; 237 default: 238 if (!strcmp(__progname, "zzz") || 239 !strcmp(__progname, "ZZZ")) 240 zzusage(); 241 else 242 usage(); 243 } 244 } 245 argc -= optind; 246 argv += optind; 247 if (argc) 248 usage(); 249 250 fd = open_socket(sockname); 251 252 if (fd != -1) { 253 if (pledge("stdio", NULL) == -1) 254 err(1, "pledge"); 255 } 256 257 if (!strcmp(__progname, "zzz")) { 258 if (fd < 0) 259 err(1, "cannot connect to apmd"); 260 else 261 return (do_zzz(fd, action)); 262 } else if (!strcmp(__progname, "ZZZ")) { 263 if (fd < 0) 264 err(1, "cannot connect to apmd"); 265 else 266 return (do_zzz(fd, HIBERNATE)); 267 } 268 269 270 bzero(&reply, sizeof reply); 271 reply.batterystate.battery_state = APM_BATT_UNKNOWN; 272 reply.batterystate.ac_state = APM_AC_UNKNOWN; 273 reply.perfmode = PERF_MANUAL; 274 reply.cpuspeed = cpuspeed; 275 276 switch (action) { 277 case SETPERF_LOW: 278 case SETPERF_HIGH: 279 case SETPERF_AUTO: 280 if (fd == -1) 281 errx(1, "cannot connect to apmd, " 282 "not changing performance adjustment mode"); 283 goto balony; 284 case NONE: 285 action = GETSTATUS; 286 verbose = doac = dopct = dobstate = domin = doperf = TRUE; 287 /* FALLTHROUGH */ 288 case GETSTATUS: 289 if (fd == -1) { 290 /* open the device directly and get status */ 291 fd = open(_PATH_APM_NORMAL, O_RDONLY); 292 if (ioctl(fd, APM_IOC_GETPOWER, 293 &reply.batterystate) == 0) { 294 if (pledge("stdio", NULL) == -1) 295 err(1, "pledge"); 296 297 goto printval; 298 } 299 } 300 /* FALLTHROUGH */ 301 balony: 302 case SUSPEND: 303 case STANDBY: 304 case HIBERNATE: 305 command.action = action; 306 break; 307 default: 308 usage(); 309 } 310 311 if (fd != -1 && (rval = send_command(fd, &command, &reply)) != 0) 312 errx(rval, "cannot get reply from APM daemon"); 313 314 switch (action) { 315 case GETSTATUS: 316 printval: 317 if (!verbose) { 318 if (dobstate) 319 printf("%d\n", 320 reply.batterystate.battery_state); 321 if (dopct) 322 printf("%d\n", 323 reply.batterystate.battery_life); 324 if (domin) { 325 if (reply.batterystate.minutes_left == 326 (u_int)-1) 327 printf("unknown\n"); 328 else 329 printf("%d\n", 330 reply.batterystate.minutes_left); 331 } 332 if (doac) 333 printf("%d\n", 334 reply.batterystate.ac_state); 335 if (doperf) 336 printf("%d\n", reply.perfmode); 337 break; 338 } 339 340 if (dobstate) { 341 printf("Battery state: %s", 342 battstate(reply.batterystate.battery_state)); 343 if (!dopct && !domin) 344 printf("\n"); 345 } 346 347 if (dopct && !dobstate) 348 printf("Battery remaining: %d percent", 349 reply.batterystate.battery_life); 350 else if (dopct) 351 printf(", %d%% remaining", 352 reply.batterystate.battery_life); 353 if (dopct && !domin) 354 printf("\n"); 355 356 if (domin && !dobstate && !dopct) { 357 #ifdef __powerpc__ 358 if (reply.batterystate.battery_state == 359 APM_BATT_CHARGING) 360 printf("Remaining battery recharge " 361 "time estimate: %d minutes\n", 362 reply.batterystate.minutes_left); 363 else if (reply.batterystate.minutes_left == 0 && 364 reply.batterystate.battery_life > 10) 365 printf("Battery life estimate: " 366 "not available\n"); 367 else 368 #endif 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 #ifdef __powerpc__ 380 if (reply.batterystate.battery_state == 381 APM_BATT_CHARGING) 382 printf(", %d minutes recharge time estimate\n", 383 reply.batterystate.minutes_left); 384 else if (reply.batterystate.minutes_left == 0 && 385 reply.batterystate.battery_life > 10) 386 printf(", unknown life estimate\n"); 387 else 388 #endif 389 { 390 if (reply.batterystate.minutes_left == 391 (u_int)-1) 392 printf(", unknown"); 393 else 394 printf(", %d minutes", 395 reply.batterystate.minutes_left); 396 printf(" life estimate\n"); 397 } 398 } 399 400 if (doac) 401 printf("A/C adapter state: %s\n", 402 ac_state(reply.batterystate.ac_state)); 403 404 if (doperf) 405 printf("Performance adjustment mode: %s (%d MHz)\n", 406 perf_mode(reply.perfmode), reply.cpuspeed); 407 break; 408 default: 409 break; 410 } 411 412 switch (reply.newstate) { 413 case SUSPEND: 414 printf("System will enter suspend mode momentarily.\n"); 415 break; 416 case STANDBY: 417 printf("System will enter standby mode momentarily.\n"); 418 break; 419 case HIBERNATE: 420 printf("System will enter hibernate mode momentarily.\n"); 421 break; 422 default: 423 break; 424 } 425 if (reply.error) 426 errx(1, "%s: %s", apm_state(reply.newstate), strerror(reply.error)); 427 return (0); 428 } 429