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 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 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 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 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