2 * APM (Advanced Power Management) Event Dispatcher
4 * Copyright (c) 1999 Mitsuru IWASAKI <iwasaki@FreeBSD.org>
5 * Copyright (c) 1999 KOIE Hidetaka <koie@suri.co.jp>
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted provided that the following conditions
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.
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
31 static const char rcsid[] =
36 #include <bitstring.h>
47 #include <sys/ioctl.h>
48 #include <sys/types.h>
51 #include <machine/apm_bios.h>
55 extern int yyparse(void);
59 int soft_power_state_change = 0;
60 const char *apmd_configfile = APMD_CONFIGFILE;
61 const char *apmd_pidfile = APMD_PIDFILE;
62 int apmctl_fd = -1, apmnorm_fd = -1;
65 * table of event handlers
67 #define EVENT_CONFIG_INITIALIZER(EV,R) { #EV, NULL, R },
68 struct event_config events[EVENT_MAX] = {
69 EVENT_CONFIG_INITIALIZER(NOEVENT, 0)
70 EVENT_CONFIG_INITIALIZER(STANDBYREQ, 1)
71 EVENT_CONFIG_INITIALIZER(SUSPENDREQ, 1)
72 EVENT_CONFIG_INITIALIZER(NORMRESUME, 0)
73 EVENT_CONFIG_INITIALIZER(CRITRESUME, 0)
74 EVENT_CONFIG_INITIALIZER(BATTERYLOW, 0)
75 EVENT_CONFIG_INITIALIZER(POWERSTATECHANGE, 0)
76 EVENT_CONFIG_INITIALIZER(UPDATETIME, 0)
77 EVENT_CONFIG_INITIALIZER(CRITSUSPEND, 1)
78 EVENT_CONFIG_INITIALIZER(USERSTANDBYREQ, 1)
79 EVENT_CONFIG_INITIALIZER(USERSUSPENDREQ, 1)
80 EVENT_CONFIG_INITIALIZER(STANDBYRESUME, 0)
81 EVENT_CONFIG_INITIALIZER(CAPABILITIESCHANGE, 0)
85 * List of battery events
87 struct battery_watch_event *battery_watch_list = NULL;
89 #define BATT_CHK_INTV 10 /* how many seconds between battery state checks? */
95 event_cmd_default_clone(void *this)
97 struct event_cmd * oldone = this;
98 struct event_cmd * newone = malloc(oldone->len);
101 newone->len = oldone->len;
102 newone->name = oldone->name;
103 newone->op = oldone->op;
111 event_cmd_exec_act(void *this)
113 struct event_cmd_exec * p = this;
117 switch ((pid = fork())) {
119 (void) warn("cannot fork");
123 signal(SIGHUP, SIG_DFL);
124 signal(SIGCHLD, SIG_DFL);
125 signal(SIGTERM, SIG_DFL);
126 execl(_PATH_BSHELL, "sh", "-c", p->line, (char *)NULL);
131 pid = waitpid(pid, &status, 0);
132 } while (pid == -1 && errno == EINTR);
139 event_cmd_exec_dump(void *this, FILE *fp)
141 fprintf(fp, " \"%s\"", ((struct event_cmd_exec *)this)->line);
144 event_cmd_exec_clone(void *this)
146 struct event_cmd_exec * newone = (struct event_cmd_exec *) event_cmd_default_clone(this);
147 struct event_cmd_exec * oldone = this;
149 newone->evcmd.next = NULL;
150 newone->evcmd.len = oldone->evcmd.len;
151 newone->evcmd.name = oldone->evcmd.name;
152 newone->evcmd.op = oldone->evcmd.op;
153 if ((newone->line = strdup(oldone->line)) == NULL)
154 err(1, "out of memory");
155 return (struct event_cmd *) newone;
158 event_cmd_exec_free(void *this)
160 free(((struct event_cmd_exec *)this)->line);
162 struct event_cmd_op event_cmd_exec_ops = {
165 event_cmd_exec_clone,
173 event_cmd_reject_act(void *this)
177 if (ioctl(apmctl_fd, APMIO_REJECTLASTREQ, NULL)) {
178 syslog(LOG_NOTICE, "fail to reject\n");
185 struct event_cmd_op event_cmd_reject_ops = {
186 event_cmd_reject_act,
188 event_cmd_default_clone,
193 * manipulate event_config
196 clone_event_cmd_list(struct event_cmd *p)
198 struct event_cmd dummy;
199 struct event_cmd *q = &dummy;
200 for ( ;p; p = p->next) {
201 assert(p->op->clone);
202 if ((q->next = p->op->clone(p)) == NULL)
203 (void) err(1, "out of memory");
210 free_event_cmd_list(struct event_cmd *p)
212 struct event_cmd * q;
221 register_battery_handlers(
222 int level, int direction,
223 struct event_cmd *cmdlist)
226 * level is negative if it's in "minutes", non-negative if
229 * direction =1 means we care about this level when charging,
230 * direction =-1 means we care about it when discharging.
232 if (level>100) /* percentage > 100 */
234 if (abs(direction) != 1) /* nonsense direction value */
238 struct battery_watch_event *we;
240 if ((we = malloc(sizeof(struct battery_watch_event))) == NULL)
241 (void) err(1, "out of memory");
243 we->next = battery_watch_list; /* starts at NULL */
244 battery_watch_list = we;
245 we->level = abs(level);
246 we->type = (level<0)?BATTERY_MINUTES:BATTERY_PERCENT;
247 we->direction = (direction<0)?BATTERY_DISCHARGING:
250 we->cmdlist = clone_event_cmd_list(cmdlist);
255 register_apm_event_handlers(
256 bitstr_t bit_decl(evlist, EVENT_MAX),
257 struct event_cmd *cmdlist)
260 bitstr_t bit_decl(tmp, EVENT_MAX);
261 memcpy(&tmp, evlist, bitstr_size(EVENT_MAX));
267 bit_ffs(tmp, EVENT_MAX, &n);
270 p = events[n].cmdlist;
271 if ((q = clone_event_cmd_list(cmdlist)) == NULL)
272 (void) err(1, "out of memory");
274 while (p->next != NULL)
278 events[n].cmdlist = q;
290 exec_run_cmd(struct event_cmd *p)
294 for (; p; p = p->next) {
297 syslog(LOG_INFO, "action: %s", p->name);
298 status = p->op->act(p);
300 syslog(LOG_NOTICE, "command finished with %d\n", status);
308 * execute command -- the event version
311 exec_event_cmd(struct event_config *ev)
315 status = exec_run_cmd(ev->cmdlist);
316 if (status && ev->rejectable) {
317 syslog(LOG_ERR, "canceled");
318 (void) event_cmd_reject_act(NULL);
334 if ((yyin = fopen(apmd_configfile, "r")) == NULL) {
335 (void) err(1, "cannot open config file");
339 yydebug = debug_level;
343 (void) err(1, "cannot parse config file");
348 for (i = 0; i < EVENT_MAX; i++) {
349 if (events[i].cmdlist) {
350 u_int event_type = i;
351 if (write(apmctl_fd, &event_type, sizeof(u_int)) == -1) {
352 (void) err(1, "cannot enable event 0x%x", event_type);
362 struct battery_watch_event *q;
364 for (i = 0; i < EVENT_MAX; i++) {
365 struct event_cmd * p;
366 if ((p = events[i].cmdlist)) {
367 fprintf(stderr, "apm_event %s {\n", events[i].name);
368 for ( ; p ; p = p->next) {
369 fprintf(stderr, "\t%s", p->name);
371 p->op->dump(p, stderr);
372 fprintf(stderr, ";\n");
374 fprintf(stderr, "}\n");
377 for (q = battery_watch_list ; q != NULL ; q = q -> next) {
378 struct event_cmd * p;
379 fprintf(stderr, "apm_battery %d%s %s {\n",
381 (q -> type == BATTERY_PERCENT)?"%":"m",
382 (q -> direction == BATTERY_CHARGING)?"charging":
384 for ( p = q -> cmdlist; p ; p = p->next) {
385 fprintf(stderr, "\t%s", p->name);
387 p->op->dump(p, stderr);
388 fprintf(stderr, ";\n");
390 fprintf(stderr, "}\n");
398 struct battery_watch_event *q;
401 for (i = 0; i < EVENT_MAX; i++) {
402 if (events[i].cmdlist) {
403 u_int event_type = i;
404 if (write(apmctl_fd, &event_type, sizeof(u_int)) == -1) {
405 (void) err(1, "cannot disable event 0x%x", event_type);
410 for (i = 0; i < EVENT_MAX; i++) {
411 struct event_cmd * p;
412 if ((p = events[i].cmdlist))
413 free_event_cmd_list(p);
414 events[i].cmdlist = NULL;
417 for( ; battery_watch_list; battery_watch_list = battery_watch_list -> next) {
418 free_event_cmd_list(battery_watch_list->cmdlist);
419 q = battery_watch_list->next;
420 free(battery_watch_list);
421 battery_watch_list = q;
440 FILE *fp = fopen(apmd_pidfile, "w");
443 fprintf(fp, "%d\n", getpid());
451 static int signal_fd[2];
454 enque_signal(int sig)
456 if (write(signal_fd[1], &sig, sizeof sig) != sizeof sig)
457 (void) err(1, "cannot process signal.");
464 while (waitpid(-1, &status, WNOHANG) > 0)
474 while (read(fd, &sig, sizeof sig) == sizeof sig) {
475 syslog(LOG_INFO, "caught signal: %d", sig);
478 syslog(LOG_NOTICE, "restart by SIG");
482 syslog(LOG_NOTICE, "going down on signal %d", sig);
489 (void) warn("unexpected signal(%d) received.", sig);
498 proc_apmevent(int fd)
500 struct apm_event_info apmevent;
502 while (ioctl(fd, APMIO_NEXTEVENT, &apmevent) == 0) {
504 syslog(LOG_NOTICE, "apmevent %04x index %d\n",
505 apmevent.type, apmevent.index);
506 syslog(LOG_INFO, "apm event: %s", events[apmevent.type].name);
508 status = exec_event_cmd(&events[apmevent.type]);
514 #define AC_POWER_STATE ((pw_info.ai_acline == 1) ? BATTERY_CHARGING :\
521 static int first_time=1, last_state;
524 struct apm_info pw_info;
525 struct battery_watch_event *p;
527 /* If we don't care, don't bother */
528 if (battery_watch_list == NULL)
532 if ( ioctl(apmnorm_fd, APMIO_GETINFO, &pw_info) < 0)
533 (void) err(1, "cannot check battery state.");
535 * This next statement isn't entirely true. The spec does not tie AC
536 * line state to battery charging or not, but this is a bit lazier to do.
538 last_state = AC_POWER_STATE;
540 return; /* We can't process events, we have no baseline */
544 * XXX - should we do this a bunch of times and perform some sort
545 * of smoothing or correction?
547 if ( ioctl(apmnorm_fd, APMIO_GETINFO, &pw_info) < 0)
548 (void) err(1, "cannot check battery state.");
551 * If we're not in the state now that we were in last time,
552 * then it's a transition, which means we must clean out
553 * the event-caught state.
555 if (last_state != AC_POWER_STATE) {
556 if (soft_power_state_change && fork() == 0) {
557 status = exec_event_cmd(&events[PMEV_POWERSTATECHANGE]);
560 last_state = AC_POWER_STATE;
561 for (p = battery_watch_list ; p!=NULL ; p = p -> next)
564 for (p = battery_watch_list ; p != NULL ; p = p -> next)
565 if (p -> direction == AC_POWER_STATE &&
567 ((p -> type == BATTERY_PERCENT &&
568 p -> level == pw_info.ai_batt_life) ||
569 (p -> type == BATTERY_MINUTES &&
570 p -> level == (pw_info.ai_batt_time / 60)))) {
573 syslog(LOG_NOTICE, "Caught battery event: %s, %d%s",
574 (p -> direction == BATTERY_CHARGING)?"charging":"discharging",
576 (p -> type == BATTERY_PERCENT)?"%":" minutes");
578 status = exec_run_cmd(p -> cmdlist);
587 struct sigaction nsa;
589 sigset_t sigmask, osigmask;
591 FD_ZERO(&master_rfds);
592 FD_SET(apmctl_fd, &master_rfds);
593 fdmax = apmctl_fd > fdmax ? apmctl_fd : fdmax;
595 FD_SET(signal_fd[0], &master_rfds);
596 fdmax = signal_fd[0] > fdmax ? signal_fd[0] : fdmax;
598 memset(&nsa, 0, sizeof nsa);
599 nsa.sa_handler = enque_signal;
600 sigfillset(&nsa.sa_mask);
601 nsa.sa_flags = SA_RESTART;
602 sigaction(SIGHUP, &nsa, NULL);
603 sigaction(SIGCHLD, &nsa, NULL);
604 sigaction(SIGTERM, &nsa, NULL);
606 sigemptyset(&sigmask);
607 sigaddset(&sigmask, SIGHUP);
608 sigaddset(&sigmask, SIGCHLD);
609 sigaddset(&sigmask, SIGTERM);
610 sigprocmask(SIG_SETMASK, &sigmask, &osigmask);
617 to.tv_sec = BATT_CHK_INTV;
620 memcpy(&rfds, &master_rfds, sizeof rfds);
621 sigprocmask(SIG_SETMASK, &osigmask, NULL);
622 if ((res=select(fdmax + 1, &rfds, 0, 0, &to)) < 0) {
624 (void) err(1, "select");
626 sigprocmask(SIG_SETMASK, &sigmask, NULL);
628 if (res == 0) { /* time to check the battery */
633 if (FD_ISSET(signal_fd[0], &rfds)) {
634 if (proc_signal(signal_fd[0]) < 0)
638 if (FD_ISSET(apmctl_fd, &rfds))
639 proc_apmevent(apmctl_fd);
646 main(int ac, char* av[])
651 int logopt = LOG_NDELAY | LOG_PID;
653 while ((ch = getopt(ac, av, "df:sv")) != -1) {
660 apmd_configfile = optarg;
663 soft_power_state_change = 1;
669 (void) err(1, "unknown option `%c'", ch);
677 (void) nice(NICE_INCR);
681 logopt |= LOG_PERROR;
683 prog = strrchr(av[0], '/');
684 openlog(prog ? prog+1 : av[0], logopt, LOG_DAEMON);
686 syslog(LOG_NOTICE, "start");
688 if (pipe(signal_fd) < 0)
689 (void) err(1, "pipe");
690 if (fcntl(signal_fd[0], F_SETFL, O_NONBLOCK) < 0)
691 (void) err(1, "fcntl");
693 if ((apmnorm_fd = open(APM_NORM_DEVICEFILE, O_RDWR)) == -1) {
694 (void) err(1, "cannot open device file `%s'", APM_NORM_DEVICEFILE);
697 if (fcntl(apmnorm_fd, F_SETFD, 1) == -1) {
698 (void) err(1, "cannot set close-on-exec flag for device file '%s'", APM_NORM_DEVICEFILE);
701 if ((apmctl_fd = open(APM_CTL_DEVICEFILE, O_RDWR)) == -1) {
702 (void) err(1, "cannot open device file `%s'", APM_CTL_DEVICEFILE);
705 if (fcntl(apmctl_fd, F_SETFD, 1) == -1) {
706 (void) err(1, "cannot set close-on-exec flag for device file '%s'", APM_CTL_DEVICEFILE);