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