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