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; 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 * default procedure 85 */ 86 struct event_cmd * 87 event_cmd_default_clone(void *this) 88 { 89 struct event_cmd * oldone = this; 90 struct event_cmd * newone = malloc(oldone->len); 91 92 newone->next = NULL; 93 newone->len = oldone->len; 94 newone->name = oldone->name; 95 newone->op = oldone->op; 96 return newone; 97 } 98 99 /* 100 * exec command 101 */ 102 int 103 event_cmd_exec_act(void *this) 104 { 105 struct event_cmd_exec * p = this; 106 int status = -1; 107 pid_t pid; 108 109 switch ((pid = fork())) { 110 case -1: 111 (void) warn("cannot fork"); 112 goto out; 113 case 0: 114 /* child process */ 115 execl(_PATH_BSHELL, "sh", "-c", p->line, (char *)NULL); 116 _exit(127); 117 default: 118 /* parent process */ 119 do { 120 pid = waitpid(pid, &status, 0); 121 } while (pid == -1 && errno == EINTR); 122 break; 123 } 124 out: 125 return status; 126 } 127 void 128 event_cmd_exec_dump(void *this, FILE *fp) 129 { 130 fprintf(fp, " \"%s\"", ((struct event_cmd_exec *)this)->line); 131 } 132 struct event_cmd * 133 event_cmd_exec_clone(void *this) 134 { 135 struct event_cmd_exec * newone = (struct event_cmd_exec *) event_cmd_default_clone(this); 136 struct event_cmd_exec * oldone = this; 137 138 newone->evcmd.next = NULL; 139 newone->evcmd.len = oldone->evcmd.len; 140 newone->evcmd.name = oldone->evcmd.name; 141 newone->evcmd.op = oldone->evcmd.op; 142 if ((newone->line = strdup(oldone->line)) == NULL) 143 err(1, "out of memory"); 144 return (struct event_cmd *) newone; 145 } 146 void 147 event_cmd_exec_free(void *this) 148 { 149 free(((struct event_cmd_exec *)this)->line); 150 } 151 struct event_cmd_op event_cmd_exec_ops = { 152 event_cmd_exec_act, 153 event_cmd_exec_dump, 154 event_cmd_exec_clone, 155 event_cmd_exec_free 156 }; 157 158 /* 159 * reject commad 160 */ 161 int 162 event_cmd_reject_act(void *this) 163 { 164 int rc = -1; 165 166 if (ioctl(apmctl_fd, APMIO_REJECTLASTREQ, NULL)) { 167 syslog(LOG_NOTICE, "fail to reject\n"); 168 goto out; 169 } 170 rc = 0; 171 out: 172 return rc; 173 } 174 struct event_cmd_op event_cmd_reject_ops = { 175 event_cmd_reject_act, 176 NULL, 177 event_cmd_default_clone, 178 NULL 179 }; 180 181 /* 182 * manipulate event_config 183 */ 184 struct event_cmd * 185 clone_event_cmd_list(struct event_cmd *p) 186 { 187 struct event_cmd dummy; 188 struct event_cmd *q = &dummy; 189 for ( ;p; p = p->next) { 190 assert(p->op->clone); 191 if ((q->next = p->op->clone(p)) == NULL) 192 (void) err(1, "out of memory"); 193 q = q->next; 194 } 195 q->next = NULL; 196 return dummy.next; 197 } 198 void 199 free_event_cmd_list(struct event_cmd *p) 200 { 201 struct event_cmd * q; 202 for ( ; p ; p = q) { 203 q = p->next; 204 if (p->op->free) 205 p->op->free(p); 206 free(p); 207 } 208 } 209 int 210 register_apm_event_handlers( 211 bitstr_t bit_decl(evlist, EVENT_MAX), 212 struct event_cmd *cmdlist) 213 { 214 if (cmdlist) { 215 bitstr_t bit_decl(tmp, EVENT_MAX); 216 memcpy(&tmp, evlist, bitstr_size(EVENT_MAX)); 217 218 for (;;) { 219 int n; 220 struct event_cmd *p; 221 struct event_cmd *q; 222 bit_ffs(tmp, EVENT_MAX, &n); 223 if (n < 0) 224 break; 225 p = events[n].cmdlist; 226 if ((q = clone_event_cmd_list(cmdlist)) == NULL) 227 (void) err(1, "out of memory"); 228 if (p) { 229 while (p->next != NULL) 230 p = p->next; 231 p->next = q; 232 } else { 233 events[n].cmdlist = q; 234 } 235 bit_clear(tmp, n); 236 } 237 } 238 return 0; 239 } 240 241 /* 242 * execute command 243 */ 244 int 245 exec_event_cmd(struct event_config *ev) 246 { 247 int status = 0; 248 249 struct event_cmd *p = ev->cmdlist; 250 for (; p; p = p->next) { 251 assert(p->op->act); 252 if (verbose) 253 syslog(LOG_INFO, "action: %s", p->name); 254 status = p->op->act(p); 255 if (status) { 256 syslog(LOG_NOTICE, "command finished with %d\n", status); 257 if (ev->rejectable) { 258 syslog(LOG_ERR, "canceled"); 259 (void) event_cmd_reject_act(NULL); 260 } 261 break; 262 } 263 } 264 return status; 265 } 266 267 /* 268 * read config file 269 */ 270 extern FILE * yyin; 271 extern int yydebug; 272 273 void 274 read_config(void) 275 { 276 int i; 277 278 if ((yyin = fopen(apmd_configfile, "r")) == NULL) { 279 (void) err(1, "cannot open config file"); 280 } 281 282 #ifdef DEBUG 283 yydebug = debug_level; 284 #endif 285 286 if (yyparse() != 0) 287 (void) err(1, "cannot parse config file"); 288 289 fclose(yyin); 290 291 /* enable events */ 292 for (i = 0; i < EVENT_MAX; i++) { 293 if (events[i].cmdlist) { 294 u_int event_type = i; 295 if (write(apmctl_fd, &event_type, sizeof(u_int)) == -1) { 296 (void) err(1, "cannot enable event 0x%x", event_type); 297 } 298 } 299 } 300 } 301 302 void 303 dump_config() 304 { 305 int i; 306 307 for (i = 0; i < EVENT_MAX; i++) { 308 struct event_cmd * p; 309 if ((p = events[i].cmdlist)) { 310 fprintf(stderr, "apm_event %s {\n", events[i].name); 311 for ( ; p ; p = p->next) { 312 fprintf(stderr, "\t%s", p->name); 313 if (p->op->dump) 314 p->op->dump(p, stderr); 315 fprintf(stderr, ";\n"); 316 } 317 fprintf(stderr, "}\n"); 318 } 319 } 320 } 321 322 void 323 destroy_config() 324 { 325 int i; 326 327 /* disable events */ 328 for (i = 0; i < EVENT_MAX; i++) { 329 if (events[i].cmdlist) { 330 u_int event_type = i; 331 if (write(apmctl_fd, &event_type, sizeof(u_int)) == -1) { 332 (void) err(1, "cannot disable event 0x%x", event_type); 333 } 334 } 335 } 336 337 for (i = 0; i < EVENT_MAX; i++) { 338 struct event_cmd * p; 339 if ((p = events[i].cmdlist)) 340 free_event_cmd_list(p); 341 events[i].cmdlist = NULL; 342 } 343 } 344 345 void 346 restart() 347 { 348 destroy_config(); 349 read_config(); 350 if (verbose) 351 dump_config(); 352 } 353 354 /* 355 * write pid file 356 */ 357 static void 358 write_pid() 359 { 360 FILE *fp = fopen(apmd_pidfile, "w"); 361 362 if (fp) { 363 fprintf(fp, "%d\n", getpid()); 364 fclose(fp); 365 } 366 } 367 368 /* 369 * handle signals 370 */ 371 static int signal_fd[2]; 372 373 void 374 enque_signal(int sig) 375 { 376 if (write(signal_fd[1], &sig, sizeof sig) != sizeof sig) 377 (void) err(1, "cannot process signal."); 378 } 379 380 void 381 wait_child() 382 { 383 int status; 384 while (waitpid(-1, &status, WNOHANG) > 0) 385 ; 386 } 387 388 int 389 proc_signal(int fd) 390 { 391 int rc = -1; 392 int sig; 393 394 while (read(fd, &sig, sizeof sig) == sizeof sig) { 395 syslog(LOG_INFO, "caught signal: %d", sig); 396 switch (sig) { 397 case SIGHUP: 398 syslog(LOG_NOTICE, "restart by SIG"); 399 restart(); 400 break; 401 case SIGTERM: 402 syslog(LOG_NOTICE, "going down on signal %d", sig); 403 rc = 1; 404 goto out; 405 case SIGCHLD: 406 wait_child(); 407 break; 408 default: 409 (void) warn("unexpected signal(%d) received.", sig); 410 break; 411 } 412 } 413 rc = 0; 414 out: 415 return rc; 416 } 417 void 418 proc_apmevent(int fd) 419 { 420 struct apm_event_info apmevent; 421 422 while (ioctl(fd, APMIO_NEXTEVENT, &apmevent) == 0) { 423 int status; 424 syslog(LOG_NOTICE, "apmevent %04x index %d\n", 425 apmevent.type, apmevent.index); 426 syslog(LOG_INFO, "apm event: %s", events[apmevent.type].name); 427 if (fork() == 0) { 428 status = exec_event_cmd(&events[apmevent.type]); 429 exit(status); 430 } 431 } 432 } 433 void 434 event_loop(void) 435 { 436 int fdmax = 0; 437 struct sigaction nsa; 438 fd_set master_rfds; 439 sigset_t sigmask, osigmask; 440 441 FD_ZERO(&master_rfds); 442 FD_SET(apmctl_fd, &master_rfds); 443 fdmax = apmctl_fd > fdmax ? apmctl_fd : fdmax; 444 445 FD_SET(signal_fd[0], &master_rfds); 446 fdmax = signal_fd[0] > fdmax ? signal_fd[0] : fdmax; 447 448 memset(&nsa, 0, sizeof nsa); 449 nsa.sa_handler = enque_signal; 450 sigfillset(&nsa.sa_mask); 451 nsa.sa_flags = SA_RESTART; 452 sigaction(SIGHUP, &nsa, NULL); 453 sigaction(SIGCHLD, &nsa, NULL); 454 sigaction(SIGTERM, &nsa, NULL); 455 456 sigemptyset(&sigmask); 457 sigaddset(&sigmask, SIGHUP); 458 sigaddset(&sigmask, SIGCHLD); 459 sigaddset(&sigmask, SIGTERM); 460 sigprocmask(SIG_SETMASK, &sigmask, &osigmask); 461 462 while (1) { 463 fd_set rfds; 464 465 memcpy(&rfds, &master_rfds, sizeof rfds); 466 sigprocmask(SIG_SETMASK, &osigmask, NULL); 467 if (select(fdmax + 1, &rfds, 0, 0, 0) < 0) { 468 if (errno != EINTR) 469 (void) err(1, "select"); 470 } 471 sigprocmask(SIG_SETMASK, &sigmask, NULL); 472 473 if (FD_ISSET(signal_fd[0], &rfds)) { 474 if (proc_signal(signal_fd[0]) < 0) 475 goto out; 476 } 477 if (FD_ISSET(apmctl_fd, &rfds)) 478 proc_apmevent(apmctl_fd); 479 } 480 out: 481 return; 482 } 483 484 int 485 main(int ac, char* av[]) 486 { 487 int ch; 488 int daemonize = 1; 489 char *prog; 490 int logopt = LOG_NDELAY | LOG_PID; 491 492 while ((ch = getopt(ac, av, "df:v")) != EOF) { 493 switch (ch) { 494 case 'd': 495 daemonize = 0; 496 debug_level++; 497 break; 498 case 'f': 499 apmd_configfile = optarg; 500 break; 501 case 'v': 502 verbose = 1; 503 break; 504 default: 505 (void) err(1, "unknown option `%c'", ch); 506 } 507 } 508 509 if (daemonize) 510 daemon(0, 0); 511 512 #ifdef NICE_INCR 513 (void) nice(NICE_INCR); 514 #endif 515 516 if (!daemonize) 517 logopt |= LOG_PERROR; 518 519 prog = strrchr(av[0], '/'); 520 openlog(prog ? prog+1 : av[0], logopt, LOG_DAEMON); 521 522 syslog(LOG_NOTICE, "start"); 523 524 if (pipe(signal_fd) < 0) 525 (void) err(1, "pipe"); 526 if (fcntl(signal_fd[0], F_SETFL, O_NONBLOCK) < 0) 527 (void) err(1, "fcntl"); 528 529 if ((apmctl_fd = open(APM_CTL_DEVICEFILE, O_RDWR)) == -1) { 530 (void) err(1, "cannot open device file `%s'", APM_CTL_DEVICEFILE); 531 } 532 533 restart(); 534 write_pid(); 535 event_loop(); 536 exit(EXIT_SUCCESS); 537 } 538 539