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