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