1 /*- 2 * SPDX-License-Identifier: BSD-2-Clause 3 * 4 * APM (Advanced Power Management) Event Dispatcher 5 * 6 * Copyright (c) 1999 Mitsuru IWASAKI <iwasaki@FreeBSD.org> 7 * Copyright (c) 1999 KOIE Hidetaka <koie@suri.co.jp> 8 * All rights reserved. 9 * 10 * Redistribution and use in source and binary forms, with or without 11 * modification, are permitted provided that the following conditions 12 * are met: 13 * 1. Redistributions of source code must retain the above copyright 14 * notice, this list of conditions and the following disclaimer. 15 * 2. Redistributions in binary form must reproduce the above copyright 16 * notice, this list of conditions and the following disclaimer in the 17 * documentation and/or other materials provided with the distribution. 18 * 19 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 20 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 21 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 22 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 23 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 24 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 25 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 26 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 27 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 28 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 29 * SUCH DAMAGE. 30 */ 31 32 #include <sys/types.h> 33 #include <assert.h> 34 #include <bitstring.h> 35 #include <err.h> 36 #include <errno.h> 37 #include <fcntl.h> 38 #include <paths.h> 39 #include <signal.h> 40 #include <stdio.h> 41 #include <stdlib.h> 42 #include <string.h> 43 #include <syslog.h> 44 #include <unistd.h> 45 #include <sys/ioctl.h> 46 #include <sys/time.h> 47 #include <sys/wait.h> 48 #include <machine/apm_bios.h> 49 50 #include "apmd.h" 51 52 int debug_level = 0; 53 int verbose = 0; 54 int soft_power_state_change = 0; 55 const char *apmd_configfile = APMD_CONFIGFILE; 56 const char *apmd_pidfile = APMD_PIDFILE; 57 int apmctl_fd = -1, apmnorm_fd = -1; 58 59 /* 60 * table of event handlers 61 */ 62 #define EVENT_CONFIG_INITIALIZER(EV,R) { #EV, NULL, R }, 63 struct event_config events[EVENT_MAX] = { 64 EVENT_CONFIG_INITIALIZER(NOEVENT, 0) 65 EVENT_CONFIG_INITIALIZER(STANDBYREQ, 1) 66 EVENT_CONFIG_INITIALIZER(SUSPENDREQ, 1) 67 EVENT_CONFIG_INITIALIZER(NORMRESUME, 0) 68 EVENT_CONFIG_INITIALIZER(CRITRESUME, 0) 69 EVENT_CONFIG_INITIALIZER(BATTERYLOW, 0) 70 EVENT_CONFIG_INITIALIZER(POWERSTATECHANGE, 0) 71 EVENT_CONFIG_INITIALIZER(UPDATETIME, 0) 72 EVENT_CONFIG_INITIALIZER(CRITSUSPEND, 1) 73 EVENT_CONFIG_INITIALIZER(USERSTANDBYREQ, 1) 74 EVENT_CONFIG_INITIALIZER(USERSUSPENDREQ, 1) 75 EVENT_CONFIG_INITIALIZER(STANDBYRESUME, 0) 76 EVENT_CONFIG_INITIALIZER(CAPABILITIESCHANGE, 0) 77 }; 78 79 /* 80 * List of battery events 81 */ 82 struct battery_watch_event *battery_watch_list = NULL; 83 84 #define BATT_CHK_INTV 10 /* how many seconds between battery state checks? */ 85 86 /* 87 * default procedure 88 */ 89 struct event_cmd * 90 event_cmd_default_clone(void *this) 91 { 92 struct event_cmd * oldone = this; 93 struct event_cmd * newone = malloc(oldone->len); 94 95 newone->next = NULL; 96 newone->len = oldone->len; 97 newone->name = oldone->name; 98 newone->op = oldone->op; 99 return newone; 100 } 101 102 /* 103 * exec command 104 */ 105 int 106 event_cmd_exec_act(void *this) 107 { 108 struct event_cmd_exec * p = this; 109 int status = -1; 110 pid_t pid; 111 112 switch ((pid = fork())) { 113 case -1: 114 warn("cannot fork"); 115 break; 116 case 0: 117 /* child process */ 118 signal(SIGHUP, SIG_DFL); 119 signal(SIGCHLD, SIG_DFL); 120 signal(SIGTERM, SIG_DFL); 121 execl(_PATH_BSHELL, "sh", "-c", p->line, (char *)NULL); 122 _exit(127); 123 default: 124 /* parent process */ 125 do { 126 pid = waitpid(pid, &status, 0); 127 } while (pid == -1 && errno == EINTR); 128 break; 129 } 130 return status; 131 } 132 void 133 event_cmd_exec_dump(void *this, FILE *fp) 134 { 135 fprintf(fp, " \"%s\"", ((struct event_cmd_exec *)this)->line); 136 } 137 struct event_cmd * 138 event_cmd_exec_clone(void *this) 139 { 140 struct event_cmd_exec * newone = (struct event_cmd_exec *) event_cmd_default_clone(this); 141 struct event_cmd_exec * oldone = this; 142 143 newone->evcmd.next = NULL; 144 newone->evcmd.len = oldone->evcmd.len; 145 newone->evcmd.name = oldone->evcmd.name; 146 newone->evcmd.op = oldone->evcmd.op; 147 if ((newone->line = strdup(oldone->line)) == NULL) 148 err(1, "out of memory"); 149 return (struct event_cmd *) newone; 150 } 151 void 152 event_cmd_exec_free(void *this) 153 { 154 free(((struct event_cmd_exec *)this)->line); 155 } 156 struct event_cmd_op event_cmd_exec_ops = { 157 event_cmd_exec_act, 158 event_cmd_exec_dump, 159 event_cmd_exec_clone, 160 event_cmd_exec_free 161 }; 162 163 /* 164 * reject command 165 */ 166 int 167 event_cmd_reject_act(void *this __unused) 168 { 169 int rc = 0; 170 171 if (ioctl(apmctl_fd, APMIO_REJECTLASTREQ, NULL)) { 172 syslog(LOG_NOTICE, "fail to reject\n"); 173 rc = -1; 174 } 175 return rc; 176 } 177 struct event_cmd_op event_cmd_reject_ops = { 178 event_cmd_reject_act, 179 NULL, 180 event_cmd_default_clone, 181 NULL 182 }; 183 184 /* 185 * manipulate event_config 186 */ 187 struct event_cmd * 188 clone_event_cmd_list(struct event_cmd *p) 189 { 190 struct event_cmd dummy; 191 struct event_cmd *q = &dummy; 192 for ( ;p; p = p->next) { 193 assert(p->op->clone); 194 if ((q->next = p->op->clone(p)) == NULL) 195 err(1, "out of memory"); 196 q = q->next; 197 } 198 q->next = NULL; 199 return dummy.next; 200 } 201 void 202 free_event_cmd_list(struct event_cmd *p) 203 { 204 struct event_cmd * q; 205 for ( ; p ; p = q) { 206 q = p->next; 207 if (p->op->free) 208 p->op->free(p); 209 free(p); 210 } 211 } 212 int 213 register_battery_handlers( 214 int level, int direction, 215 struct event_cmd *cmdlist) 216 { 217 /* 218 * level is negative if it's in "minutes", non-negative if 219 * percentage. 220 * 221 * direction =1 means we care about this level when charging, 222 * direction =-1 means we care about it when discharging. 223 */ 224 if (level>100) /* percentage > 100 */ 225 return -1; 226 if (abs(direction) != 1) /* nonsense direction value */ 227 return -1; 228 229 if (cmdlist) { 230 struct battery_watch_event *we; 231 232 if ((we = malloc(sizeof(struct battery_watch_event))) == NULL) 233 err(1, "out of memory"); 234 235 we->next = battery_watch_list; /* starts at NULL */ 236 battery_watch_list = we; 237 we->level = abs(level); 238 we->type = (level<0)?BATTERY_MINUTES:BATTERY_PERCENT; 239 we->direction = (direction<0)?BATTERY_DISCHARGING: 240 BATTERY_CHARGING; 241 we->done = 0; 242 we->cmdlist = clone_event_cmd_list(cmdlist); 243 } 244 return 0; 245 } 246 int 247 register_apm_event_handlers( 248 bitstr_t bit_decl(evlist, EVENT_MAX), 249 struct event_cmd *cmdlist) 250 { 251 if (cmdlist) { 252 bitstr_t bit_decl(tmp, EVENT_MAX); 253 memcpy(&tmp, evlist, bitstr_size(EVENT_MAX)); 254 255 for (;;) { 256 int n; 257 struct event_cmd *p; 258 struct event_cmd *q; 259 bit_ffs(tmp, EVENT_MAX, &n); 260 if (n < 0) 261 break; 262 p = events[n].cmdlist; 263 if ((q = clone_event_cmd_list(cmdlist)) == NULL) 264 err(1, "out of memory"); 265 if (p) { 266 while (p->next != NULL) 267 p = p->next; 268 p->next = q; 269 } else { 270 events[n].cmdlist = q; 271 } 272 bit_clear(tmp, n); 273 } 274 } 275 return 0; 276 } 277 278 /* 279 * execute command 280 */ 281 int 282 exec_run_cmd(struct event_cmd *p) 283 { 284 int status = 0; 285 286 for (; p; p = p->next) { 287 assert(p->op->act); 288 if (verbose) 289 syslog(LOG_INFO, "action: %s", p->name); 290 status = p->op->act(p); 291 if (status) { 292 syslog(LOG_NOTICE, "command finished with %d\n", status); 293 break; 294 } 295 } 296 return status; 297 } 298 299 /* 300 * execute command -- the event version 301 */ 302 int 303 exec_event_cmd(struct event_config *ev) 304 { 305 int status = 0; 306 307 status = exec_run_cmd(ev->cmdlist); 308 if (status && ev->rejectable) { 309 syslog(LOG_ERR, "canceled"); 310 event_cmd_reject_act(NULL); 311 } 312 return status; 313 } 314 315 /* 316 * read config file 317 */ 318 extern FILE * yyin; 319 extern int yydebug; 320 321 void 322 read_config(void) 323 { 324 int i; 325 326 if ((yyin = fopen(apmd_configfile, "r")) == NULL) { 327 err(1, "cannot open config file"); 328 } 329 330 #ifdef DEBUG 331 yydebug = debug_level; 332 #endif 333 334 if (yyparse() != 0) 335 err(1, "cannot parse config file"); 336 337 fclose(yyin); 338 339 /* enable events */ 340 for (i = 0; i < EVENT_MAX; i++) { 341 if (events[i].cmdlist) { 342 u_int event_type = i; 343 if (write(apmctl_fd, &event_type, sizeof(u_int)) == -1) { 344 err(1, "cannot enable event 0x%x", event_type); 345 } 346 } 347 } 348 } 349 350 void 351 dump_config(void) 352 { 353 int i; 354 struct battery_watch_event *q; 355 356 for (i = 0; i < EVENT_MAX; i++) { 357 struct event_cmd * p; 358 if ((p = events[i].cmdlist)) { 359 fprintf(stderr, "apm_event %s {\n", events[i].name); 360 for ( ; p ; p = p->next) { 361 fprintf(stderr, "\t%s", p->name); 362 if (p->op->dump) 363 p->op->dump(p, stderr); 364 fprintf(stderr, ";\n"); 365 } 366 fprintf(stderr, "}\n"); 367 } 368 } 369 for (q = battery_watch_list ; q != NULL ; q = q -> next) { 370 struct event_cmd * p; 371 fprintf(stderr, "apm_battery %d%s %s {\n", 372 q -> level, 373 (q -> type == BATTERY_PERCENT)?"%":"m", 374 (q -> direction == BATTERY_CHARGING)?"charging": 375 "discharging"); 376 for ( p = q -> cmdlist; p ; p = p->next) { 377 fprintf(stderr, "\t%s", p->name); 378 if (p->op->dump) 379 p->op->dump(p, stderr); 380 fprintf(stderr, ";\n"); 381 } 382 fprintf(stderr, "}\n"); 383 } 384 } 385 386 void 387 destroy_config(void) 388 { 389 int i; 390 struct battery_watch_event *q; 391 392 /* disable events */ 393 for (i = 0; i < EVENT_MAX; i++) { 394 if (events[i].cmdlist) { 395 u_int event_type = i; 396 if (write(apmctl_fd, &event_type, sizeof(u_int)) == -1) { 397 err(1, "cannot disable event 0x%x", event_type); 398 } 399 } 400 } 401 402 for (i = 0; i < EVENT_MAX; i++) { 403 struct event_cmd * p; 404 if ((p = events[i].cmdlist)) 405 free_event_cmd_list(p); 406 events[i].cmdlist = NULL; 407 } 408 409 for( ; battery_watch_list; battery_watch_list = battery_watch_list -> next) { 410 free_event_cmd_list(battery_watch_list->cmdlist); 411 q = battery_watch_list->next; 412 free(battery_watch_list); 413 battery_watch_list = q; 414 } 415 } 416 417 void 418 restart(void) 419 { 420 destroy_config(); 421 read_config(); 422 if (verbose) 423 dump_config(); 424 } 425 426 /* 427 * write pid file 428 */ 429 static void 430 write_pid(void) 431 { 432 FILE *fp = fopen(apmd_pidfile, "w"); 433 434 if (fp) { 435 fprintf(fp, "%ld\n", (long)getpid()); 436 fclose(fp); 437 } 438 } 439 440 /* 441 * handle signals 442 */ 443 static int signal_fd[2]; 444 445 void 446 enque_signal(int sig) 447 { 448 if (write(signal_fd[1], &sig, sizeof sig) != sizeof sig) 449 err(1, "cannot process signal."); 450 } 451 452 void 453 wait_child(void) 454 { 455 int status; 456 while (waitpid(-1, &status, WNOHANG) > 0) 457 ; 458 } 459 460 int 461 proc_signal(int fd) 462 { 463 int rc = 0; 464 int sig; 465 466 while (read(fd, &sig, sizeof sig) == sizeof sig) { 467 syslog(LOG_INFO, "caught signal: %d", sig); 468 switch (sig) { 469 case SIGHUP: 470 syslog(LOG_NOTICE, "restart by SIG"); 471 restart(); 472 break; 473 case SIGTERM: 474 syslog(LOG_NOTICE, "going down on signal %d", sig); 475 rc = -1; 476 return rc; 477 case SIGCHLD: 478 wait_child(); 479 break; 480 default: 481 warn("unexpected signal(%d) received.", sig); 482 break; 483 } 484 } 485 return rc; 486 } 487 void 488 proc_apmevent(int fd) 489 { 490 struct apm_event_info apmevent; 491 492 while (ioctl(fd, APMIO_NEXTEVENT, &apmevent) == 0) { 493 int status; 494 syslog(LOG_NOTICE, "apmevent %04x index %d\n", 495 apmevent.type, apmevent.index); 496 syslog(LOG_INFO, "apm event: %s", events[apmevent.type].name); 497 if (fork() == 0) { 498 status = exec_event_cmd(&events[apmevent.type]); 499 exit(status); 500 } 501 } 502 } 503 504 #define AC_POWER_STATE ((pw_info.ai_acline == 1) ? BATTERY_CHARGING :\ 505 BATTERY_DISCHARGING) 506 507 void 508 check_battery(void) 509 { 510 511 static int first_time=1, last_state; 512 int status; 513 514 struct apm_info pw_info; 515 struct battery_watch_event *p; 516 517 /* If we don't care, don't bother */ 518 if (battery_watch_list == NULL) 519 return; 520 521 if (first_time) { 522 if ( ioctl(apmnorm_fd, APMIO_GETINFO, &pw_info) < 0) 523 err(1, "cannot check battery state."); 524 /* 525 * This next statement isn't entirely true. The spec does not tie AC 526 * line state to battery charging or not, but this is a bit lazier to do. 527 */ 528 last_state = AC_POWER_STATE; 529 first_time = 0; 530 return; /* We can't process events, we have no baseline */ 531 } 532 533 /* 534 * XXX - should we do this a bunch of times and perform some sort 535 * of smoothing or correction? 536 */ 537 if ( ioctl(apmnorm_fd, APMIO_GETINFO, &pw_info) < 0) 538 err(1, "cannot check battery state."); 539 540 /* 541 * If we're not in the state now that we were in last time, 542 * then it's a transition, which means we must clean out 543 * the event-caught state. 544 */ 545 if (last_state != AC_POWER_STATE) { 546 if (soft_power_state_change && fork() == 0) { 547 status = exec_event_cmd(&events[PMEV_POWERSTATECHANGE]); 548 exit(status); 549 } 550 last_state = AC_POWER_STATE; 551 for (p = battery_watch_list ; p!=NULL ; p = p -> next) 552 p->done = 0; 553 } 554 for (p = battery_watch_list ; p != NULL ; p = p -> next) 555 if (p -> direction == AC_POWER_STATE && 556 !(p -> done) && 557 ((p -> type == BATTERY_PERCENT && 558 p -> level == (int)pw_info.ai_batt_life) || 559 (p -> type == BATTERY_MINUTES && 560 p -> level == (pw_info.ai_batt_time / 60)))) { 561 p -> done++; 562 if (verbose) 563 syslog(LOG_NOTICE, "Caught battery event: %s, %d%s", 564 (p -> direction == BATTERY_CHARGING)?"charging":"discharging", 565 p -> level, 566 (p -> type == BATTERY_PERCENT)?"%":" minutes"); 567 if (fork() == 0) { 568 status = exec_run_cmd(p -> cmdlist); 569 exit(status); 570 } 571 } 572 } 573 void 574 event_loop(void) 575 { 576 int fdmax = 0; 577 struct sigaction nsa; 578 fd_set master_rfds; 579 sigset_t sigmask, osigmask; 580 581 FD_ZERO(&master_rfds); 582 FD_SET(apmctl_fd, &master_rfds); 583 fdmax = apmctl_fd > fdmax ? apmctl_fd : fdmax; 584 585 FD_SET(signal_fd[0], &master_rfds); 586 fdmax = signal_fd[0] > fdmax ? signal_fd[0] : fdmax; 587 588 memset(&nsa, 0, sizeof nsa); 589 nsa.sa_handler = enque_signal; 590 sigfillset(&nsa.sa_mask); 591 nsa.sa_flags = SA_RESTART; 592 sigaction(SIGHUP, &nsa, NULL); 593 sigaction(SIGCHLD, &nsa, NULL); 594 sigaction(SIGTERM, &nsa, NULL); 595 596 sigemptyset(&sigmask); 597 sigaddset(&sigmask, SIGHUP); 598 sigaddset(&sigmask, SIGCHLD); 599 sigaddset(&sigmask, SIGTERM); 600 sigprocmask(SIG_SETMASK, &sigmask, &osigmask); 601 602 while (1) { 603 fd_set rfds; 604 int res; 605 struct timeval to; 606 607 to.tv_sec = BATT_CHK_INTV; 608 to.tv_usec = 0; 609 610 memcpy(&rfds, &master_rfds, sizeof rfds); 611 sigprocmask(SIG_SETMASK, &osigmask, NULL); 612 if ((res=select(fdmax + 1, &rfds, 0, 0, &to)) < 0) { 613 if (errno != EINTR) 614 err(1, "select"); 615 } 616 sigprocmask(SIG_SETMASK, &sigmask, NULL); 617 618 if (res == 0) { /* time to check the battery */ 619 check_battery(); 620 continue; 621 } 622 623 if (FD_ISSET(signal_fd[0], &rfds)) { 624 if (proc_signal(signal_fd[0]) < 0) 625 return; 626 } 627 628 if (FD_ISSET(apmctl_fd, &rfds)) 629 proc_apmevent(apmctl_fd); 630 } 631 } 632 633 int 634 main(int ac, char* av[]) 635 { 636 int ch; 637 int daemonize = 1; 638 char *prog; 639 int logopt = LOG_NDELAY | LOG_PID; 640 641 while ((ch = getopt(ac, av, "df:sv")) != -1) { 642 switch (ch) { 643 case 'd': 644 daemonize = 0; 645 debug_level++; 646 break; 647 case 'f': 648 apmd_configfile = optarg; 649 break; 650 case 's': 651 soft_power_state_change = 1; 652 break; 653 case 'v': 654 verbose = 1; 655 break; 656 default: 657 err(1, "unknown option `%c'", ch); 658 } 659 } 660 661 if (daemonize) 662 daemon(0, 0); 663 664 #ifdef NICE_INCR 665 nice(NICE_INCR); 666 #endif 667 668 if (!daemonize) 669 logopt |= LOG_PERROR; 670 671 prog = strrchr(av[0], '/'); 672 openlog(prog ? prog+1 : av[0], logopt, LOG_DAEMON); 673 674 syslog(LOG_NOTICE, "start"); 675 676 if (pipe(signal_fd) < 0) 677 err(1, "pipe"); 678 if (fcntl(signal_fd[0], F_SETFL, O_NONBLOCK) < 0) 679 err(1, "fcntl"); 680 681 if ((apmnorm_fd = open(APM_NORM_DEVICEFILE, O_RDWR)) == -1) { 682 err(1, "cannot open device file `%s'", APM_NORM_DEVICEFILE); 683 } 684 685 if (fcntl(apmnorm_fd, F_SETFD, 1) == -1) { 686 err(1, "cannot set close-on-exec flag for device file '%s'", APM_NORM_DEVICEFILE); 687 } 688 689 if ((apmctl_fd = open(APM_CTL_DEVICEFILE, O_RDWR)) == -1) { 690 err(1, "cannot open device file `%s'", APM_CTL_DEVICEFILE); 691 } 692 693 if (fcntl(apmctl_fd, F_SETFD, 1) == -1) { 694 err(1, "cannot set close-on-exec flag for device file '%s'", APM_CTL_DEVICEFILE); 695 } 696 697 restart(); 698 write_pid(); 699 event_loop(); 700 exit(EXIT_SUCCESS); 701 } 702 703