rtld.1: Staticize the variable in the _rtld_functrace example.
[dragonfly.git] / usr.sbin / apmd / apmd.c
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  * $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 $
31  */
32
33 #include <assert.h>
34 #include <bitstring.h>
35 #include <err.h>
36 #include <errno.h>
37 #include <fcntl.h>
38 #include <libutil.h>
39 #include <paths.h>
40 #include <signal.h>
41 #include <stdio.h>
42 #include <stdlib.h>
43 #include <string.h>
44 #include <syslog.h>
45 #include <unistd.h>
46 #include <sys/ioctl.h>
47 #include <sys/types.h>
48 #include <sys/time.h>
49 #include <sys/wait.h>
50 #include <machine/apm_bios.h>
51
52 #include "apmd.h"
53
54
55 int             debug_level = 0;
56 int             verbose = 0;
57 const char      *apmd_configfile = APMD_CONFIGFILE;
58 int             apmctl_fd = -1, apmnorm_fd = -1;
59
60 /*
61  * table of event handlers
62  */
63 #define EVENT_CONFIG_INITIALIZER(EV,R) { #EV, NULL, R },
64 struct event_config events[EVENT_MAX] = {
65         EVENT_CONFIG_INITIALIZER(NOEVENT, 0)
66         EVENT_CONFIG_INITIALIZER(STANDBYREQ, 1)
67         EVENT_CONFIG_INITIALIZER(SUSPENDREQ, 1)
68         EVENT_CONFIG_INITIALIZER(NORMRESUME, 0)
69         EVENT_CONFIG_INITIALIZER(CRITRESUME, 0)
70         EVENT_CONFIG_INITIALIZER(BATTERYLOW, 0)
71         EVENT_CONFIG_INITIALIZER(POWERSTATECHANGE, 0)
72         EVENT_CONFIG_INITIALIZER(UPDATETIME, 0)
73         EVENT_CONFIG_INITIALIZER(CRITSUSPEND, 1)
74         EVENT_CONFIG_INITIALIZER(USERSTANDBYREQ, 1)
75         EVENT_CONFIG_INITIALIZER(USERSUSPENDREQ, 1)
76         EVENT_CONFIG_INITIALIZER(STANDBYRESUME, 0)
77         EVENT_CONFIG_INITIALIZER(CAPABILITIESCHANGE, 0)
78 };
79
80 /*
81  * List of battery events
82  */
83 struct battery_watch_event *battery_watch_list = NULL;
84
85 #define BATT_CHK_INTV 10 /* how many seconds between battery state checks? */
86
87 /*
88  * default procedure
89  */
90 struct event_cmd *
91 event_cmd_default_clone(void *this)
92 {
93         struct event_cmd * oldone = this;
94         struct event_cmd * newone = malloc(oldone->len);
95
96         newone->next = NULL;
97         newone->len = oldone->len;
98         newone->name = oldone->name;
99         newone->op = oldone->op;
100         return newone;
101 }
102
103 /*
104  * exec command
105  */
106 int
107 event_cmd_exec_act(void *this)
108 {
109         struct event_cmd_exec * p = this;
110         int status = -1;
111         pid_t pid;
112
113         switch ((pid = fork())) {
114         case -1:
115                 warn("cannot fork");
116                 goto out;
117         case 0:
118                 /* child process */
119                 execl(_PATH_BSHELL, "sh", "-c", p->line, NULL);
120                 _exit(127);
121         default:
122                 /* parent process */
123                 do {
124                         pid = waitpid(pid, &status, 0);
125                 } while (pid == -1 && errno == EINTR);
126                 break;
127         }
128  out:
129         return status;
130 }
131 void
132 event_cmd_exec_dump(void *this, FILE *fp)
133 {
134         fprintf(fp, " \"%s\"", ((struct event_cmd_exec *)this)->line);
135 }
136 struct event_cmd *
137 event_cmd_exec_clone(void *this)
138 {
139         struct event_cmd_exec * newone = (struct event_cmd_exec *) event_cmd_default_clone(this);
140         struct event_cmd_exec * oldone = this;
141
142         newone->evcmd.next = NULL;
143         newone->evcmd.len = oldone->evcmd.len;
144         newone->evcmd.name = oldone->evcmd.name;
145         newone->evcmd.op = oldone->evcmd.op;
146         if ((newone->line = strdup(oldone->line)) == NULL)
147                 err(1, "out of memory");
148         return (struct event_cmd *) newone;
149 }
150 void
151 event_cmd_exec_free(void *this)
152 {
153         free(((struct event_cmd_exec *)this)->line);
154 }
155 struct event_cmd_op event_cmd_exec_ops = {
156         event_cmd_exec_act,
157         event_cmd_exec_dump,
158         event_cmd_exec_clone,
159         event_cmd_exec_free
160 };
161
162 /*
163  * reject commad
164  */
165 int
166 event_cmd_reject_act(void *this)
167 {
168         int rc = -1;
169
170         if (ioctl(apmctl_fd, APMIO_REJECTLASTREQ, NULL)) {
171                 syslog(LOG_NOTICE, "fail to reject\n");
172                 goto out;
173         }
174         rc = 0;
175  out:
176         return rc;
177 }
178 struct event_cmd_op event_cmd_reject_ops = {
179         event_cmd_reject_act,
180         NULL,
181         event_cmd_default_clone,
182         NULL
183 };
184
185 /*
186  * manipulate event_config
187  */
188 struct event_cmd *
189 clone_event_cmd_list(struct event_cmd *p)
190 {
191         struct event_cmd dummy;
192         struct event_cmd *q = &dummy;
193         for ( ;p; p = p->next) {
194                 assert(p->op->clone);
195                 if ((q->next = p->op->clone(p)) == NULL)
196                         err(1, "out of memory");
197                 q = q->next;
198         }
199         q->next = NULL;
200         return dummy.next;
201 }
202 void
203 free_event_cmd_list(struct event_cmd *p)
204 {
205         struct event_cmd * q;
206         for ( ; p ; p = q) {
207                 q = p->next;
208                 if (p->op->free)
209                         p->op->free(p);
210                 free(p);
211         }
212 }
213 int
214 register_battery_handlers(
215         int level, int direction,
216         struct event_cmd *cmdlist)
217 {
218         /*
219          * level is negative if it's in "minutes", non-negative if
220          * percentage.
221          *
222          * direction =1 means we care about this level when charging,
223          * direction =-1 means we care about it when discharging.
224          */
225         if (level>100) /* percentage > 100 */
226                 return -1;
227         if (abs(direction) != 1) /* nonsense direction value */
228                 return -1;
229
230         if (cmdlist) {
231                 struct battery_watch_event *we;
232                 
233                 if ((we = malloc(sizeof(struct battery_watch_event))) == NULL)
234                         err(1, "out of memory");
235
236                 we->next = battery_watch_list; /* starts at NULL */
237                 battery_watch_list = we;
238                 we->level = abs(level);
239                 we->type = (level<0)?BATTERY_MINUTES:BATTERY_PERCENT;
240                 we->direction = (direction<0)?BATTERY_DISCHARGING:
241                         BATTERY_CHARGING;
242                 we->done = 0;
243                 we->cmdlist = clone_event_cmd_list(cmdlist);
244         }
245         return 0;
246 }
247 int
248 register_apm_event_handlers(
249         bitstr_t bit_decl(evlist, EVENT_MAX),
250         struct event_cmd *cmdlist)
251 {
252         if (cmdlist) {
253                 bitstr_t bit_decl(tmp, EVENT_MAX);
254                 memcpy(&tmp, evlist, bitstr_size(EVENT_MAX));
255
256                 for (;;) {
257                         int n;
258                         struct event_cmd *p;
259                         struct event_cmd *q;
260                         bit_ffs(tmp, EVENT_MAX, &n);
261                         if (n < 0)
262                                 break;
263                         p = events[n].cmdlist;
264                         if ((q = clone_event_cmd_list(cmdlist)) == NULL)
265                                 err(1, "out of memory");
266                         if (p) {
267                                 while (p->next != NULL)
268                                         p = p->next;
269                                 p->next = q;
270                         } else {
271                                 events[n].cmdlist = q;
272                         }
273                         bit_clear(tmp, n);
274                 }
275         }
276         return 0;
277 }
278
279 /*
280  * execute command
281  */
282 int
283 exec_run_cmd(struct event_cmd *p)
284 {
285         int status = 0;
286
287         for (; p; p = p->next) {
288                 assert(p->op->act);
289                 if (verbose)
290                         syslog(LOG_INFO, "action: %s", p->name);
291                 status = p->op->act(p);
292                 if (status) {
293                         syslog(LOG_NOTICE, "command finished with %d\n", status);
294                         break;
295                 }
296         }
297         return status;
298 }
299
300 /*
301  * execute command -- the event version
302  */
303 int
304 exec_event_cmd(struct event_config *ev)
305 {
306         int status = 0;
307
308         status = exec_run_cmd(ev->cmdlist);
309         if (status && ev->rejectable) {
310                 syslog(LOG_ERR, "canceled");
311                 event_cmd_reject_act(NULL);
312         }
313         return status;
314 }
315
316 /*
317  * read config file
318  */
319 extern FILE * yyin;
320 extern int yydebug;
321
322 void
323 read_config(void)
324 {
325         int i;
326
327         if ((yyin = fopen(apmd_configfile, "r")) == NULL) {
328                 err(1, "cannot open config file");
329         }
330
331 #ifdef DEBUG
332         yydebug = debug_level;
333 #endif
334
335         if (yyparse() != 0)
336                 err(1, "cannot parse config file");
337
338         fclose(yyin);
339
340         /* enable events */
341         for (i = 0; i < EVENT_MAX; i++) {
342                 if (events[i].cmdlist) {
343                         u_int event_type = i;
344                         if (write(apmctl_fd, &event_type, sizeof(u_int)) == -1) {
345                                 err(1, "cannot enable event 0x%x", event_type);
346                         }
347                 }
348         }
349 }
350
351 void
352 dump_config(void)
353 {
354         int i;
355         struct battery_watch_event *q;
356
357         for (i = 0; i < EVENT_MAX; i++) {
358                 struct event_cmd * p;
359                 if ((p = events[i].cmdlist)) {
360                         fprintf(stderr, "apm_event %s {\n", events[i].name);
361                         for ( ; p ; p = p->next) {
362                                 fprintf(stderr, "\t%s", p->name);
363                                 if (p->op->dump)
364                                         p->op->dump(p, stderr);
365                                 fprintf(stderr, ";\n");
366                         }
367                         fprintf(stderr, "}\n");
368                 }
369         }
370         for (q = battery_watch_list ; q != NULL ; q = q -> next) {
371                 struct event_cmd * p;
372                 fprintf(stderr, "apm_battery %d%s %s {\n",
373                         q -> level,
374                         (q -> type == BATTERY_PERCENT)?"%":"m",
375                         (q -> direction == BATTERY_CHARGING)?"charging":
376                                 "discharging");
377                 for ( p = q -> cmdlist; p ; p = p->next) {
378                         fprintf(stderr, "\t%s", p->name);
379                         if (p->op->dump)
380                                 p->op->dump(p, stderr);
381                         fprintf(stderr, ";\n");
382                 }
383                 fprintf(stderr, "}\n");
384         }
385 }
386
387 void
388 destroy_config(void)
389 {
390         int i;
391         struct battery_watch_event *q;
392
393         /* disable events */
394         for (i = 0; i < EVENT_MAX; i++) {
395                 if (events[i].cmdlist) {
396                         u_int event_type = i;
397                         if (write(apmctl_fd, &event_type, sizeof(u_int)) == -1) {
398                                 err(1, "cannot disable event 0x%x", event_type);
399                         }
400                 }
401         }
402
403         for (i = 0; i < EVENT_MAX; i++) {
404                 struct event_cmd * p;
405                 if ((p = events[i].cmdlist))
406                         free_event_cmd_list(p);
407                 events[i].cmdlist = NULL;
408         }
409
410         for( ; battery_watch_list; battery_watch_list = battery_watch_list -> next) {
411                 free_event_cmd_list(battery_watch_list->cmdlist);
412                 q = battery_watch_list->next;
413                 free(battery_watch_list);
414                 battery_watch_list = q;
415         }
416 }
417
418 void
419 restart(void)
420 {
421         destroy_config();
422         read_config();
423         if (verbose)
424                 dump_config();
425 }
426
427 /*
428  * handle signals
429  */
430 static int signal_fd[2];
431
432 void
433 enque_signal(int sig)
434 {
435         if (write(signal_fd[1], &sig, sizeof sig) != sizeof sig)
436                 err(1, "cannot process signal.");
437 }
438
439 void
440 wait_child(void)
441 {
442         int status;
443         while (waitpid(-1, &status, WNOHANG) > 0)
444                 ;
445 }
446
447 int
448 proc_signal(int fd)
449 {
450         int rc = -1;
451         int sig;
452
453         while (read(fd, &sig, sizeof sig) == sizeof sig) {
454                 syslog(LOG_INFO, "caught signal: %d", sig);
455                 switch (sig) {
456                 case SIGHUP:
457                         syslog(LOG_NOTICE, "restart by SIG");
458                         restart();
459                         break;
460                 case SIGTERM:
461                         syslog(LOG_NOTICE, "going down on signal %d", sig);
462                         rc = 1;
463                         goto out;
464                 case SIGCHLD:
465                         wait_child();
466                         break;
467                 default:
468                         warn("unexpected signal(%d) received.", sig);
469                         break;
470                 }
471         }
472         rc = 0;
473  out:
474         return rc;
475 }
476 void
477 proc_apmevent(int fd)
478 {
479         struct apm_event_info apmevent;
480
481         while (ioctl(fd, APMIO_NEXTEVENT, &apmevent) == 0) {
482                 int status;
483                 syslog(LOG_NOTICE, "apmevent %04x index %d\n",
484                         apmevent.type, apmevent.index);
485                 syslog(LOG_INFO, "apm event: %s", events[apmevent.type].name);
486                 if (fork() == 0) {
487                         status = exec_event_cmd(&events[apmevent.type]);
488                         exit(status);
489                 }
490         }
491 }
492
493 #define AC_POWER_STATE ((pw_info.ai_acline == 1) ? BATTERY_CHARGING :\
494         BATTERY_DISCHARGING)
495
496 void
497 check_battery(void)
498 {
499
500         static int first_time=1, last_state;
501
502         struct apm_info pw_info;
503         struct battery_watch_event *p;
504
505         /* If we don't care, don't bother */
506         if (battery_watch_list == NULL)
507                 return;
508
509         if (first_time) {
510                 if ( ioctl(apmnorm_fd, APMIO_GETINFO, &pw_info) < 0)
511                         err(1, "cannot check battery state.");
512 /*
513  * This next statement isn't entirely true. The spec does not tie AC
514  * line state to battery charging or not, but this is a bit lazier to do.
515  */
516                 last_state = AC_POWER_STATE;
517                 first_time = 0;
518                 return; /* We can't process events, we have no baseline */
519         }
520
521         /*
522          * XXX - should we do this a bunch of times and perform some sort
523          * of smoothing or correction?
524          */
525         if ( ioctl(apmnorm_fd, APMIO_GETINFO, &pw_info) < 0)
526                 err(1, "cannot check battery state.");
527
528         /*
529          * If we're not in the state now that we were in last time,
530          * then it's a transition, which means we must clean out
531          * the event-caught state.
532          */
533         if (last_state != AC_POWER_STATE) {
534                 last_state = AC_POWER_STATE;
535                 for (p = battery_watch_list ; p!=NULL ; p = p -> next)
536                         p->done = 0;
537         }
538         for (p = battery_watch_list ; p != NULL ; p = p -> next)
539                 if (p -> direction == AC_POWER_STATE &&
540                         !(p -> done) &&
541                         ((p -> type == BATTERY_PERCENT && 
542                                 p -> level == (int)pw_info.ai_batt_life) ||
543                         (p -> type == BATTERY_MINUTES &&
544                                 p -> level == (pw_info.ai_batt_time / 60)))) {
545                         p -> done++;
546                         if (verbose)
547                                 syslog(LOG_NOTICE, "Caught battery event: %s, %d%s",
548                                         (p -> direction == BATTERY_CHARGING)?"charging":"discharging",
549                                         p -> level,
550                                         (p -> type == BATTERY_PERCENT)?"%":" minutes");
551                         if (fork() == 0) {
552                                 int status;
553                                 status = exec_run_cmd(p -> cmdlist);
554                                 exit(status);
555                         }
556                 }
557 }
558 void
559 event_loop(void)
560 {
561         int             fdmax = 0;
562         struct sigaction nsa;
563         fd_set          master_rfds;
564         sigset_t        sigmask, osigmask;
565
566         FD_ZERO(&master_rfds);
567         FD_SET(apmctl_fd, &master_rfds);
568         fdmax = apmctl_fd > fdmax ? apmctl_fd : fdmax;
569
570         FD_SET(signal_fd[0], &master_rfds);
571         fdmax = signal_fd[0] > fdmax ? signal_fd[0] : fdmax;
572
573         memset(&nsa, 0, sizeof nsa);
574         nsa.sa_handler = enque_signal;
575         sigfillset(&nsa.sa_mask);
576         nsa.sa_flags = SA_RESTART;
577         sigaction(SIGHUP, &nsa, NULL);
578         sigaction(SIGCHLD, &nsa, NULL);
579         sigaction(SIGTERM, &nsa, NULL);
580
581         sigemptyset(&sigmask);
582         sigaddset(&sigmask, SIGHUP);
583         sigaddset(&sigmask, SIGCHLD);
584         sigaddset(&sigmask, SIGTERM);
585         sigprocmask(SIG_SETMASK, &sigmask, &osigmask);
586
587         while (1) {
588                 fd_set rfds;
589                 int res;
590                 struct timeval to;
591
592                 to.tv_sec = BATT_CHK_INTV;
593                 to.tv_usec = 0;
594
595                 memcpy(&rfds, &master_rfds, sizeof rfds);
596                 sigprocmask(SIG_SETMASK, &osigmask, NULL);
597                 if ((res=select(fdmax + 1, &rfds, 0, 0, &to)) < 0) {
598                         if (errno != EINTR)
599                                 err(1, "select");
600                 }
601                 sigprocmask(SIG_SETMASK, &sigmask, NULL);
602
603                 if (res == 0) { /* time to check the battery */
604                         check_battery();
605                         continue;
606                 }
607
608                 if (FD_ISSET(signal_fd[0], &rfds)) {
609                         if (proc_signal(signal_fd[0]) < 0)
610                                 goto out;
611                 }
612
613                 if (FD_ISSET(apmctl_fd, &rfds))
614                         proc_apmevent(apmctl_fd);
615         }
616 out:
617         return;
618 }
619
620 int
621 main(int ac, char* av[])
622 {
623         int     ch;
624         int     daemonize = 1;
625         char    *prog;
626         int     logopt = LOG_NDELAY | LOG_PID;
627
628         while ((ch = getopt(ac, av, "df:v")) != -1) {
629                 switch (ch) {
630                 case 'd':
631                         daemonize = 0;
632                         debug_level++;
633                         break;
634                 case 'f':
635                         apmd_configfile = optarg;
636                         break;
637                 case 'v':
638                         verbose = 1;
639                         break;
640                 default:
641                         err(1, "unknown option `%c'", ch);
642                 }
643         }
644
645         if (daemonize)
646                 daemon(0, 0);
647
648 #ifdef NICE_INCR
649         nice(NICE_INCR);
650 #endif
651
652         if (!daemonize)
653                 logopt |= LOG_PERROR;
654
655         prog = strrchr(av[0], '/');
656         openlog(prog ? prog+1 : av[0], logopt, LOG_DAEMON);
657
658         syslog(LOG_NOTICE, "start");
659
660         if (pipe(signal_fd) < 0)
661                 err(1, "pipe");
662         if (fcntl(signal_fd[0], F_SETFL, O_NONBLOCK) < 0)
663                 err(1, "fcntl");
664
665         if ((apmnorm_fd = open(APM_NORM_DEVICEFILE, O_RDWR)) == -1) {
666                 err(1, "cannot open device file `%s'", APM_NORM_DEVICEFILE);
667         }
668
669         if ((apmctl_fd = open(APM_CTL_DEVICEFILE, O_RDWR)) == -1) {
670                 err(1, "cannot open device file `%s'", APM_CTL_DEVICEFILE);
671         }
672
673         restart();
674         pidfile(NULL);
675         event_loop();
676         exit(EXIT_SUCCESS);
677 }
678