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
29 * $FreeBSD: src/usr.sbin/apmd/apmd.c,v 1.3.2.1 2001/08/13 17:30:30 nsayer Exp $
30 * $DragonFly: src/usr.sbin/apmd/apmd.c,v 1.7 2008/07/10 18:29:52 swildner Exp $
34 #include <bitstring.h>
46 #include <sys/ioctl.h>
47 #include <sys/types.h>
50 #include <machine/apm_bios.h>
54 extern int yyparse(void);
58 const char *apmd_configfile
= APMD_CONFIGFILE
;
59 int apmctl_fd
= -1, apmnorm_fd
= -1;
62 * table of event handlers
64 #define EVENT_CONFIG_INITIALIZER(EV,R) { #EV, NULL, R },
65 struct event_config events
[EVENT_MAX
] = {
66 EVENT_CONFIG_INITIALIZER(NOEVENT
, 0)
67 EVENT_CONFIG_INITIALIZER(STANDBYREQ
, 1)
68 EVENT_CONFIG_INITIALIZER(SUSPENDREQ
, 1)
69 EVENT_CONFIG_INITIALIZER(NORMRESUME
, 0)
70 EVENT_CONFIG_INITIALIZER(CRITRESUME
, 0)
71 EVENT_CONFIG_INITIALIZER(BATTERYLOW
, 0)
72 EVENT_CONFIG_INITIALIZER(POWERSTATECHANGE
, 0)
73 EVENT_CONFIG_INITIALIZER(UPDATETIME
, 0)
74 EVENT_CONFIG_INITIALIZER(CRITSUSPEND
, 1)
75 EVENT_CONFIG_INITIALIZER(USERSTANDBYREQ
, 1)
76 EVENT_CONFIG_INITIALIZER(USERSUSPENDREQ
, 1)
77 EVENT_CONFIG_INITIALIZER(STANDBYRESUME
, 0)
78 EVENT_CONFIG_INITIALIZER(CAPABILITIESCHANGE
, 0)
82 * List of battery events
84 struct battery_watch_event
*battery_watch_list
= NULL
;
86 #define BATT_CHK_INTV 10 /* how many seconds between battery state checks? */
92 event_cmd_default_clone(void *this)
94 struct event_cmd
* oldone
= this;
95 struct event_cmd
* newone
= malloc(oldone
->len
);
98 newone
->len
= oldone
->len
;
99 newone
->name
= oldone
->name
;
100 newone
->op
= oldone
->op
;
108 event_cmd_exec_act(void *this)
110 struct event_cmd_exec
* p
= this;
114 switch ((pid
= fork())) {
120 execl(_PATH_BSHELL
, "sh", "-c", p
->line
, (char *)NULL
);
125 pid
= waitpid(pid
, &status
, 0);
126 } while (pid
== -1 && errno
== EINTR
);
133 event_cmd_exec_dump(void *this, FILE *fp
)
135 fprintf(fp
, " \"%s\"", ((struct event_cmd_exec
*)this)->line
);
138 event_cmd_exec_clone(void *this)
140 struct event_cmd_exec
* newone
= (struct event_cmd_exec
*) event_cmd_default_clone(this);
141 struct event_cmd_exec
* oldone
= this;
143 newone
->evcmd
.next
= NULL
;
144 newone
->evcmd
.len
= oldone
->evcmd
.len
;
145 newone
->evcmd
.name
= oldone
->evcmd
.name
;
146 newone
->evcmd
.op
= oldone
->evcmd
.op
;
147 if ((newone
->line
= strdup(oldone
->line
)) == NULL
)
148 err(1, "out of memory");
149 return (struct event_cmd
*) newone
;
152 event_cmd_exec_free(void *this)
154 free(((struct event_cmd_exec
*)this)->line
);
156 struct event_cmd_op event_cmd_exec_ops
= {
159 event_cmd_exec_clone
,
167 event_cmd_reject_act(void *this)
171 if (ioctl(apmctl_fd
, APMIO_REJECTLASTREQ
, NULL
)) {
172 syslog(LOG_NOTICE
, "fail to reject\n");
179 struct event_cmd_op event_cmd_reject_ops
= {
180 event_cmd_reject_act
,
182 event_cmd_default_clone
,
187 * manipulate event_config
190 clone_event_cmd_list(struct event_cmd
*p
)
192 struct event_cmd dummy
;
193 struct event_cmd
*q
= &dummy
;
194 for ( ;p
; p
= p
->next
) {
195 assert(p
->op
->clone
);
196 if ((q
->next
= p
->op
->clone(p
)) == NULL
)
197 err(1, "out of memory");
204 free_event_cmd_list(struct event_cmd
*p
)
206 struct event_cmd
* q
;
215 register_battery_handlers(
216 int level
, int direction
,
217 struct event_cmd
*cmdlist
)
220 * level is negative if it's in "minutes", non-negative if
223 * direction =1 means we care about this level when charging,
224 * direction =-1 means we care about it when discharging.
226 if (level
>100) /* percentage > 100 */
228 if (abs(direction
) != 1) /* nonsense direction value */
232 struct battery_watch_event
*we
;
234 if ((we
= malloc(sizeof(struct battery_watch_event
))) == NULL
)
235 err(1, "out of memory");
237 we
->next
= battery_watch_list
; /* starts at NULL */
238 battery_watch_list
= we
;
239 we
->level
= abs(level
);
240 we
->type
= (level
<0)?BATTERY_MINUTES
:BATTERY_PERCENT
;
241 we
->direction
= (direction
<0)?BATTERY_DISCHARGING
:
244 we
->cmdlist
= clone_event_cmd_list(cmdlist
);
249 register_apm_event_handlers(
250 bitstr_t
bit_decl(evlist
, EVENT_MAX
),
251 struct event_cmd
*cmdlist
)
254 bitstr_t
bit_decl(tmp
, EVENT_MAX
);
255 memcpy(&tmp
, evlist
, bitstr_size(EVENT_MAX
));
261 bit_ffs(tmp
, EVENT_MAX
, &n
);
264 p
= events
[n
].cmdlist
;
265 if ((q
= clone_event_cmd_list(cmdlist
)) == NULL
)
266 err(1, "out of memory");
268 while (p
->next
!= NULL
)
272 events
[n
].cmdlist
= q
;
284 exec_run_cmd(struct event_cmd
*p
)
288 for (; p
; p
= p
->next
) {
291 syslog(LOG_INFO
, "action: %s", p
->name
);
292 status
= p
->op
->act(p
);
294 syslog(LOG_NOTICE
, "command finished with %d\n", status
);
302 * execute command -- the event version
305 exec_event_cmd(struct event_config
*ev
)
309 status
= exec_run_cmd(ev
->cmdlist
);
310 if (status
&& ev
->rejectable
) {
311 syslog(LOG_ERR
, "canceled");
312 event_cmd_reject_act(NULL
);
328 if ((yyin
= fopen(apmd_configfile
, "r")) == NULL
) {
329 err(1, "cannot open config file");
333 yydebug
= debug_level
;
337 err(1, "cannot parse config file");
342 for (i
= 0; i
< EVENT_MAX
; i
++) {
343 if (events
[i
].cmdlist
) {
344 u_int event_type
= i
;
345 if (write(apmctl_fd
, &event_type
, sizeof(u_int
)) == -1) {
346 err(1, "cannot enable event 0x%x", event_type
);
356 struct battery_watch_event
*q
;
358 for (i
= 0; i
< EVENT_MAX
; i
++) {
359 struct event_cmd
* p
;
360 if ((p
= events
[i
].cmdlist
)) {
361 fprintf(stderr
, "apm_event %s {\n", events
[i
].name
);
362 for ( ; p
; p
= p
->next
) {
363 fprintf(stderr
, "\t%s", p
->name
);
365 p
->op
->dump(p
, stderr
);
366 fprintf(stderr
, ";\n");
368 fprintf(stderr
, "}\n");
371 for (q
= battery_watch_list
; q
!= NULL
; q
= q
-> next
) {
372 struct event_cmd
* p
;
373 fprintf(stderr
, "apm_battery %d%s %s {\n",
375 (q
-> type
== BATTERY_PERCENT
)?"%":"m",
376 (q
-> direction
== BATTERY_CHARGING
)?"charging":
378 for ( p
= q
-> cmdlist
; p
; p
= p
->next
) {
379 fprintf(stderr
, "\t%s", p
->name
);
381 p
->op
->dump(p
, stderr
);
382 fprintf(stderr
, ";\n");
384 fprintf(stderr
, "}\n");
392 struct battery_watch_event
*q
;
395 for (i
= 0; i
< EVENT_MAX
; i
++) {
396 if (events
[i
].cmdlist
) {
397 u_int event_type
= i
;
398 if (write(apmctl_fd
, &event_type
, sizeof(u_int
)) == -1) {
399 err(1, "cannot disable event 0x%x", event_type
);
404 for (i
= 0; i
< EVENT_MAX
; i
++) {
405 struct event_cmd
* p
;
406 if ((p
= events
[i
].cmdlist
))
407 free_event_cmd_list(p
);
408 events
[i
].cmdlist
= NULL
;
411 for( ; battery_watch_list
; battery_watch_list
= battery_watch_list
-> next
) {
412 free_event_cmd_list(battery_watch_list
->cmdlist
);
413 q
= battery_watch_list
->next
;
414 free(battery_watch_list
);
415 battery_watch_list
= q
;
431 static int signal_fd
[2];
434 enque_signal(int sig
)
436 if (write(signal_fd
[1], &sig
, sizeof sig
) != sizeof sig
)
437 err(1, "cannot process signal.");
444 while (waitpid(-1, &status
, WNOHANG
) > 0)
454 while (read(fd
, &sig
, sizeof sig
) == sizeof sig
) {
455 syslog(LOG_INFO
, "caught signal: %d", sig
);
458 syslog(LOG_NOTICE
, "restart by SIG");
462 syslog(LOG_NOTICE
, "going down on signal %d", sig
);
469 warn("unexpected signal(%d) received.", sig
);
478 proc_apmevent(int fd
)
480 struct apm_event_info apmevent
;
482 while (ioctl(fd
, APMIO_NEXTEVENT
, &apmevent
) == 0) {
484 syslog(LOG_NOTICE
, "apmevent %04x index %d\n",
485 apmevent
.type
, apmevent
.index
);
486 syslog(LOG_INFO
, "apm event: %s", events
[apmevent
.type
].name
);
488 status
= exec_event_cmd(&events
[apmevent
.type
]);
494 #define AC_POWER_STATE ((pw_info.ai_acline == 1) ? BATTERY_CHARGING :\
501 static int first_time
=1, last_state
;
503 struct apm_info pw_info
;
504 struct battery_watch_event
*p
;
506 /* If we don't care, don't bother */
507 if (battery_watch_list
== NULL
)
511 if ( ioctl(apmnorm_fd
, APMIO_GETINFO
, &pw_info
) < 0)
512 err(1, "cannot check battery state.");
514 * This next statement isn't entirely true. The spec does not tie AC
515 * line state to battery charging or not, but this is a bit lazier to do.
517 last_state
= AC_POWER_STATE
;
519 return; /* We can't process events, we have no baseline */
523 * XXX - should we do this a bunch of times and perform some sort
524 * of smoothing or correction?
526 if ( ioctl(apmnorm_fd
, APMIO_GETINFO
, &pw_info
) < 0)
527 err(1, "cannot check battery state.");
530 * If we're not in the state now that we were in last time,
531 * then it's a transition, which means we must clean out
532 * the event-caught state.
534 if (last_state
!= AC_POWER_STATE
) {
535 last_state
= AC_POWER_STATE
;
536 for (p
= battery_watch_list
; p
!=NULL
; p
= p
-> next
)
539 for (p
= battery_watch_list
; p
!= NULL
; p
= p
-> next
)
540 if (p
-> direction
== AC_POWER_STATE
&&
542 ((p
-> type
== BATTERY_PERCENT
&&
543 p
-> level
== pw_info
.ai_batt_life
) ||
544 (p
-> type
== BATTERY_MINUTES
&&
545 p
-> level
== (pw_info
.ai_batt_time
/ 60)))) {
548 syslog(LOG_NOTICE
, "Caught battery event: %s, %d%s",
549 (p
-> direction
== BATTERY_CHARGING
)?"charging":"discharging",
551 (p
-> type
== BATTERY_PERCENT
)?"%":" minutes");
554 status
= exec_run_cmd(p
-> cmdlist
);
563 struct sigaction nsa
;
565 sigset_t sigmask
, osigmask
;
567 FD_ZERO(&master_rfds
);
568 FD_SET(apmctl_fd
, &master_rfds
);
569 fdmax
= apmctl_fd
> fdmax
? apmctl_fd
: fdmax
;
571 FD_SET(signal_fd
[0], &master_rfds
);
572 fdmax
= signal_fd
[0] > fdmax
? signal_fd
[0] : fdmax
;
574 memset(&nsa
, 0, sizeof nsa
);
575 nsa
.sa_handler
= enque_signal
;
576 sigfillset(&nsa
.sa_mask
);
577 nsa
.sa_flags
= SA_RESTART
;
578 sigaction(SIGHUP
, &nsa
, NULL
);
579 sigaction(SIGCHLD
, &nsa
, NULL
);
580 sigaction(SIGTERM
, &nsa
, NULL
);
582 sigemptyset(&sigmask
);
583 sigaddset(&sigmask
, SIGHUP
);
584 sigaddset(&sigmask
, SIGCHLD
);
585 sigaddset(&sigmask
, SIGTERM
);
586 sigprocmask(SIG_SETMASK
, &sigmask
, &osigmask
);
593 to
.tv_sec
= BATT_CHK_INTV
;
596 memcpy(&rfds
, &master_rfds
, sizeof rfds
);
597 sigprocmask(SIG_SETMASK
, &osigmask
, NULL
);
598 if ((res
=select(fdmax
+ 1, &rfds
, 0, 0, &to
)) < 0) {
602 sigprocmask(SIG_SETMASK
, &sigmask
, NULL
);
604 if (res
== 0) { /* time to check the battery */
609 if (FD_ISSET(signal_fd
[0], &rfds
)) {
610 if (proc_signal(signal_fd
[0]) < 0)
614 if (FD_ISSET(apmctl_fd
, &rfds
))
615 proc_apmevent(apmctl_fd
);
622 main(int ac
, char* av
[])
627 int logopt
= LOG_NDELAY
| LOG_PID
;
629 while ((ch
= getopt(ac
, av
, "df:v")) != -1) {
636 apmd_configfile
= optarg
;
642 err(1, "unknown option `%c'", ch
);
654 logopt
|= LOG_PERROR
;
656 prog
= strrchr(av
[0], '/');
657 openlog(prog
? prog
+1 : av
[0], logopt
, LOG_DAEMON
);
659 syslog(LOG_NOTICE
, "start");
661 if (pipe(signal_fd
) < 0)
663 if (fcntl(signal_fd
[0], F_SETFL
, O_NONBLOCK
) < 0)
666 if ((apmnorm_fd
= open(APM_NORM_DEVICEFILE
, O_RDWR
)) == -1) {
667 err(1, "cannot open device file `%s'", APM_NORM_DEVICEFILE
);
670 if ((apmctl_fd
= open(APM_CTL_DEVICEFILE
, O_RDWR
)) == -1) {
671 err(1, "cannot open device file `%s'", APM_CTL_DEVICEFILE
);