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