apmd.c revision 71277
1115013Smarcel/*- 2160157Smarcel * APM (Advanced Power Management) Event Dispatcher 3121642Smarcel * 4121642Smarcel * Copyright (c) 1999 Mitsuru IWASAKI <iwasaki@FreeBSD.org> 5121642Smarcel * Copyright (c) 1999 KOIE Hidetaka <koie@suri.co.jp> 6121642Smarcel * All rights reserved. 7121642Smarcel * 8121642Smarcel * Redistribution and use in source and binary forms, with or without 9121642Smarcel * modification, are permitted provided that the following conditions 10121642Smarcel * are met: 11115013Smarcel * 1. Redistributions of source code must retain the above copyright 12121642Smarcel * notice, this list of conditions and the following disclaimer. 13121642Smarcel * 2. Redistributions in binary form must reproduce the above copyright 14121642Smarcel * notice, this list of conditions and the following disclaimer in the 15121642Smarcel * documentation and/or other materials provided with the distribution. 16121642Smarcel * 17121642Smarcel * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 18121642Smarcel * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 19121642Smarcel * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 20121642Smarcel * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 21121642Smarcel * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 22121642Smarcel * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 23121642Smarcel * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 24121642Smarcel * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 25115013Smarcel * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 26115013Smarcel * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 27120925Smarcel * SUCH DAMAGE. 28115013Smarcel */ 29115013Smarcel 30115013Smarcel#ifndef lint 31115013Smarcelstatic const char rcsid[] = 32115013Smarcel "$FreeBSD: head/usr.sbin/apmd/apmd.c 71277 2001-01-20 01:22:31Z jedgar $"; 33115013Smarcel#endif /* not lint */ 34115013Smarcel 35115013Smarcel#include <assert.h> 36115013Smarcel#include <bitstring.h> 37115013Smarcel#include <err.h> 38115013Smarcel#include <errno.h> 39115013Smarcel#include <fcntl.h> 40115013Smarcel#include <paths.h> 41115013Smarcel#include <signal.h> 42115013Smarcel#include <stdio.h> 43115013Smarcel#include <stdlib.h> 44115013Smarcel#include <string.h> 45115013Smarcel#include <syslog.h> 46115013Smarcel#include <unistd.h> 47115013Smarcel#include <sys/ioctl.h> 48115013Smarcel#include <sys/types.h> 49115013Smarcel#include <sys/time.h> 50115013Smarcel#include <sys/wait.h> 51120925Smarcel#include <machine/apm_bios.h> 52115013Smarcel 53115013Smarcel#include "apmd.h" 54115013Smarcel 55115013Smarcelextern int yyparse(void); 56115013Smarcel 57115013Smarcelint debug_level = 0; 58115013Smarcelint verbose = 0; 59115013Smarcelconst char *apmd_configfile = APMD_CONFIGFILE; 60115013Smarcelconst char *apmd_pidfile = APMD_PIDFILE; 61115013Smarcelint apmctl_fd = -1; 62115013Smarcel 63115013Smarcel/* 64115013Smarcel * table of event handlers 65115013Smarcel */ 66115013Smarcel#define EVENT_CONFIG_INITIALIZER(EV,R) { #EV, NULL, R }, 67115013Smarcelstruct event_config events[EVENT_MAX] = { 68115013Smarcel EVENT_CONFIG_INITIALIZER(NOEVENT, 0) 69115013Smarcel EVENT_CONFIG_INITIALIZER(STANDBYREQ, 1) 70120925Smarcel EVENT_CONFIG_INITIALIZER(SUSPENDREQ, 1) 71120925Smarcel EVENT_CONFIG_INITIALIZER(NORMRESUME, 0) 72115013Smarcel EVENT_CONFIG_INITIALIZER(CRITRESUME, 0) 73115013Smarcel EVENT_CONFIG_INITIALIZER(BATTERYLOW, 0) 74120925Smarcel EVENT_CONFIG_INITIALIZER(POWERSTATECHANGE, 0) 75120925Smarcel EVENT_CONFIG_INITIALIZER(UPDATETIME, 0) 76120925Smarcel EVENT_CONFIG_INITIALIZER(CRITSUSPEND, 1) 77120925Smarcel EVENT_CONFIG_INITIALIZER(USERSTANDBYREQ, 1) 78120925Smarcel EVENT_CONFIG_INITIALIZER(USERSUSPENDREQ, 1) 79120925Smarcel EVENT_CONFIG_INITIALIZER(STANDBYRESUME, 0) 80120925Smarcel EVENT_CONFIG_INITIALIZER(CAPABILITIESCHANGE, 0) 81115013Smarcel}; 82115013Smarcel 83115013Smarcel/* 84115013Smarcel * default procedure 85115013Smarcel */ 86115013Smarcelstruct event_cmd * 87115013Smarcelevent_cmd_default_clone(void *this) 88115013Smarcel{ 89115013Smarcel struct event_cmd * oldone = this; 90115013Smarcel struct event_cmd * newone = malloc(oldone->len); 91115013Smarcel 92115013Smarcel newone->next = NULL; 93115013Smarcel newone->len = oldone->len; 94115013Smarcel newone->name = oldone->name; 95115013Smarcel newone->op = oldone->op; 96115013Smarcel return newone; 97115013Smarcel} 98115013Smarcel 99115013Smarcel/* 100115013Smarcel * exec command 101115013Smarcel */ 102115013Smarcelint 103115013Smarcelevent_cmd_exec_act(void *this) 104115013Smarcel{ 105115013Smarcel struct event_cmd_exec * p = this; 106115013Smarcel int status = -1; 107115013Smarcel pid_t pid; 108115013Smarcel 109120925Smarcel switch ((pid = fork())) { 110120925Smarcel case -1: 111115013Smarcel (void) warn("cannot fork"); 112115013Smarcel goto out; 113115013Smarcel case 0: 114120925Smarcel /* child process */ 115120925Smarcel execl(_PATH_BSHELL, "sh", "-c", p->line, (char *)NULL); 116115013Smarcel _exit(127); 117115013Smarcel default: 118115013Smarcel /* parent process */ 119115013Smarcel do { 120115013Smarcel pid = waitpid(pid, &status, 0); 121115013Smarcel } while (pid == -1 && errno == EINTR); 122115013Smarcel break; 123115013Smarcel } 124115013Smarcel out: 125115013Smarcel return status; 126115013Smarcel} 127115013Smarcelvoid 128115013Smarcelevent_cmd_exec_dump(void *this, FILE *fp) 129120925Smarcel{ 130115013Smarcel fprintf(fp, " \"%s\"", ((struct event_cmd_exec *)this)->line); 131115013Smarcel} 132115013Smarcelstruct event_cmd * 133115013Smarcelevent_cmd_exec_clone(void *this) 134115013Smarcel{ 135115013Smarcel struct event_cmd_exec * newone = (struct event_cmd_exec *) event_cmd_default_clone(this); 136115013Smarcel struct event_cmd_exec * oldone = this; 137115013Smarcel 138115013Smarcel newone->evcmd.next = NULL; 139115013Smarcel newone->evcmd.len = oldone->evcmd.len; 140115013Smarcel newone->evcmd.name = oldone->evcmd.name; 141115013Smarcel newone->evcmd.op = oldone->evcmd.op; 142115013Smarcel if ((newone->line = strdup(oldone->line)) == NULL) 143115013Smarcel err(1, "out of memory"); 144115013Smarcel return (struct event_cmd *) newone; 145115013Smarcel} 146115013Smarcelvoid 147115013Smarcelevent_cmd_exec_free(void *this) 148115013Smarcel{ 149115013Smarcel free(((struct event_cmd_exec *)this)->line); 150115013Smarcel} 151115013Smarcelstruct event_cmd_op event_cmd_exec_ops = { 152115013Smarcel event_cmd_exec_act, 153115013Smarcel event_cmd_exec_dump, 154115013Smarcel event_cmd_exec_clone, 155115013Smarcel event_cmd_exec_free 156115013Smarcel}; 157115013Smarcel 158115013Smarcel/* 159115013Smarcel * reject commad 160115013Smarcel */ 161115013Smarcelint 162115013Smarcelevent_cmd_reject_act(void *this) 163115013Smarcel{ 164115013Smarcel int rc = -1; 165115013Smarcel 166115013Smarcel if (ioctl(apmctl_fd, APMIO_REJECTLASTREQ, NULL)) { 167115013Smarcel syslog(LOG_NOTICE, "fail to reject\n"); 168115013Smarcel goto out; 169115013Smarcel } 170115013Smarcel rc = 0; 171115013Smarcel out: 172115013Smarcel return rc; 173115013Smarcel} 174115013Smarcelstruct event_cmd_op event_cmd_reject_ops = { 175115013Smarcel event_cmd_reject_act, 176115013Smarcel NULL, 177115013Smarcel event_cmd_default_clone, 178115013Smarcel NULL 179115013Smarcel}; 180115013Smarcel 181115013Smarcel/* 182115013Smarcel * manipulate event_config 183129059Smarcel */ 184160157Smarcelstruct event_cmd * 185115013Smarcelclone_event_cmd_list(struct event_cmd *p) 186115013Smarcel{ 187115013Smarcel struct event_cmd dummy; 188115013Smarcel struct event_cmd *q = &dummy; 189115013Smarcel for ( ;p; p = p->next) { 190115013Smarcel assert(p->op->clone); 191115013Smarcel if ((q->next = p->op->clone(p)) == NULL) 192115013Smarcel (void) err(1, "out of memory"); 193115013Smarcel q = q->next; 194115013Smarcel } 195115013Smarcel q->next = NULL; 196115013Smarcel return dummy.next; 197115013Smarcel} 198115013Smarcelvoid 199115013Smarcelfree_event_cmd_list(struct event_cmd *p) 200115013Smarcel{ 201115013Smarcel struct event_cmd * q; 202115013Smarcel for ( ; p ; p = q) { 203115013Smarcel q = p->next; 204115013Smarcel if (p->op->free) 205115013Smarcel p->op->free(p); 206115013Smarcel free(p); 207115013Smarcel } 208120925Smarcel} 209120925Smarcelint 210160157Smarcelregister_apm_event_handlers( 211160157Smarcel bitstr_t bit_decl(evlist, EVENT_MAX), 212160157Smarcel struct event_cmd *cmdlist) 213160157Smarcel{ 214160157Smarcel if (cmdlist) { 215160157Smarcel bitstr_t bit_decl(tmp, EVENT_MAX); 216160157Smarcel memcpy(&tmp, evlist, bitstr_size(EVENT_MAX)); 217160157Smarcel 218160157Smarcel for (;;) { 219115013Smarcel int n; 220160157Smarcel struct event_cmd *p; 221115013Smarcel struct event_cmd *q; 222115013Smarcel bit_ffs(tmp, EVENT_MAX, &n); 223115013Smarcel if (n < 0) 224115013Smarcel break; 225115013Smarcel p = events[n].cmdlist; 226115013Smarcel if ((q = clone_event_cmd_list(cmdlist)) == NULL) 227115013Smarcel (void) err(1, "out of memory"); 228115013Smarcel if (p) { 229115013Smarcel while (p->next != NULL) 230115013Smarcel p = p->next; 231115013Smarcel p->next = q; 232115013Smarcel } else { 233115013Smarcel events[n].cmdlist = q; 234115013Smarcel } 235115013Smarcel bit_clear(tmp, n); 236115013Smarcel } 237115013Smarcel } 238115013Smarcel return 0; 239115013Smarcel} 240115013Smarcel 241115013Smarcel/* 242115013Smarcel * execute command 243115013Smarcel */ 244115013Smarcelint 245115013Smarcelexec_event_cmd(struct event_config *ev) 246115013Smarcel{ 247115013Smarcel int status = 0; 248115013Smarcel 249115013Smarcel struct event_cmd *p = ev->cmdlist; 250115013Smarcel for (; p; p = p->next) { 251115013Smarcel assert(p->op->act); 252115013Smarcel if (verbose) 253115013Smarcel syslog(LOG_INFO, "action: %s", p->name); 254115013Smarcel status = p->op->act(p); 255120925Smarcel if (status) { 256120925Smarcel syslog(LOG_NOTICE, "command finished with %d\n", status); 257115013Smarcel if (ev->rejectable) { 258115013Smarcel syslog(LOG_ERR, "canceled"); 259115013Smarcel (void) event_cmd_reject_act(NULL); 260115013Smarcel } 261115013Smarcel break; 262115013Smarcel } 263115013Smarcel } 264115013Smarcel return status; 265115013Smarcel} 266115013Smarcel 267115013Smarcel/* 268115013Smarcel * read config file 269115013Smarcel */ 270115013Smarcelextern FILE * yyin; 271115013Smarcelextern int yydebug; 272115013Smarcel 273115013Smarcelvoid 274115013Smarcelread_config(void) 275115013Smarcel{ 276115013Smarcel int i; 277115013Smarcel 278115013Smarcel if ((yyin = fopen(apmd_configfile, "r")) == NULL) { 279115013Smarcel (void) err(1, "cannot open config file"); 280115013Smarcel } 281115013Smarcel 282115013Smarcel#ifdef DEBUG 283115013Smarcel yydebug = debug_level; 284115013Smarcel#endif 285115013Smarcel 286115013Smarcel if (yyparse() != 0) 287115013Smarcel (void) err(1, "cannot parse config file"); 288115013Smarcel 289115013Smarcel fclose(yyin); 290115013Smarcel 291115013Smarcel /* enable events */ 292115013Smarcel for (i = 0; i < EVENT_MAX; i++) { 293115013Smarcel if (events[i].cmdlist) { 294115013Smarcel u_int event_type = i; 295115013Smarcel if (write(apmctl_fd, &event_type, sizeof(u_int)) == -1) { 296115013Smarcel (void) err(1, "cannot enable event 0x%x", event_type); 297115013Smarcel } 298115013Smarcel } 299115013Smarcel } 300115013Smarcel} 301115013Smarcel 302115013Smarcelvoid 303115013Smarceldump_config() 304115013Smarcel{ 305115013Smarcel int i; 306115013Smarcel 307115013Smarcel for (i = 0; i < EVENT_MAX; i++) { 308115013Smarcel struct event_cmd * p; 309115013Smarcel if ((p = events[i].cmdlist)) { 310115013Smarcel fprintf(stderr, "apm_event %s {\n", events[i].name); 311115013Smarcel for ( ; p ; p = p->next) { 312115013Smarcel fprintf(stderr, "\t%s", p->name); 313115013Smarcel if (p->op->dump) 314115013Smarcel p->op->dump(p, stderr); 315115013Smarcel fprintf(stderr, ";\n"); 316115013Smarcel } 317115013Smarcel fprintf(stderr, "}\n"); 318115013Smarcel } 319115013Smarcel } 320115013Smarcel} 321115013Smarcel 322115013Smarcelvoid 323115013Smarceldestroy_config() 324160157Smarcel{ 325115013Smarcel int i; 326129059Smarcel 327129059Smarcel /* disable events */ 328129059Smarcel for (i = 0; i < EVENT_MAX; i++) { 329129059Smarcel if (events[i].cmdlist) { 330115013Smarcel u_int event_type = i; 331115013Smarcel if (write(apmctl_fd, &event_type, sizeof(u_int)) == -1) { 332115013Smarcel (void) err(1, "cannot disable event 0x%x", event_type); 333115013Smarcel } 334115013Smarcel } 335129059Smarcel } 336160157Smarcel 337129059Smarcel for (i = 0; i < EVENT_MAX; i++) { 338129059Smarcel struct event_cmd * p; 339129059Smarcel if ((p = events[i].cmdlist)) 340129059Smarcel free_event_cmd_list(p); 341160157Smarcel events[i].cmdlist = NULL; 342115013Smarcel } 343115013Smarcel} 344129059Smarcel 345129059Smarcelvoid 346129059Smarcelrestart() 347129059Smarcel{ 348129059Smarcel destroy_config(); 349160163Smarcel read_config(); 350129059Smarcel if (verbose) 351129059Smarcel dump_config(); 352129059Smarcel} 353129059Smarcel 354129059Smarcel/* 355129059Smarcel * write pid file 356129059Smarcel */ 357129059Smarcelstatic void 358129059Smarcelwrite_pid() 359129059Smarcel{ 360129059Smarcel FILE *fp = fopen(apmd_pidfile, "w"); 361129059Smarcel 362129059Smarcel if (fp) { 363129059Smarcel fprintf(fp, "%d\n", getpid()); 364129059Smarcel fclose(fp); 365129059Smarcel } 366129059Smarcel} 367129059Smarcel 368115013Smarcel/* 369129059Smarcel * handle signals 370129059Smarcel */ 371129059Smarcelstatic int signal_fd[2]; 372129059Smarcel 373129059Smarcelvoid 374129059Smarcelenque_signal(int sig) 375129059Smarcel{ 376129059Smarcel if (write(signal_fd[1], &sig, sizeof sig) != sizeof sig) 377160157Smarcel (void) err(1, "cannot process signal."); 378129059Smarcel} 379129059Smarcel 380129059Smarcelvoid 381129059Smarcelwait_child() 382129059Smarcel{ 383129059Smarcel int status; 384129059Smarcel while (waitpid(-1, &status, WNOHANG) > 0) 385129059Smarcel ; 386115013Smarcel} 387129059Smarcel 388129059Smarcelint 389129059Smarcelproc_signal(int fd) 390129059Smarcel{ 391129059Smarcel int rc = -1; 392129059Smarcel int sig; 393129059Smarcel 394129059Smarcel while (read(fd, &sig, sizeof sig) == sizeof sig) { 395129059Smarcel syslog(LOG_INFO, "caught signal: %d", sig); 396160157Smarcel switch (sig) { 397129059Smarcel case SIGHUP: 398129059Smarcel syslog(LOG_NOTICE, "restart by SIG"); 399129059Smarcel restart(); 400129059Smarcel break; 401129059Smarcel case SIGTERM: 402129059Smarcel syslog(LOG_NOTICE, "going down on signal %d", sig); 403129059Smarcel rc = 1; 404129059Smarcel goto out; 405129059Smarcel case SIGCHLD: 406129059Smarcel wait_child(); 407115013Smarcel break; 408160163Smarcel default: 409 (void) warn("unexpected signal(%d) received.", sig); 410 break; 411 } 412 } 413 rc = 0; 414 out: 415 return rc; 416} 417void 418proc_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} 433void 434event_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 } 480out: 481 return; 482} 483 484int 485main(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