apmd.c revision 48731
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
30#ifndef lint
31static const char rcsid[] =
32	"$Id: apmd.c,v 1.1.3.13 1999/06/18 04:07:05 koie Exp $";
33#endif /* not lint */
34
35#include <assert.h>
36#include <bitstring.h>
37#include <err.h>
38#include <errno.h>
39#include <fcntl.h>
40#include <paths.h>
41#include <signal.h>
42#include <stdio.h>
43#include <stdlib.h>
44#include <string.h>
45#include <syslog.h>
46#include <unistd.h>
47#include <sys/ioctl.h>
48#include <sys/types.h>
49#include <sys/time.h>
50#include <sys/wait.h>
51#include <machine/apm_bios.h>
52
53#include "apmd.h"
54
55extern int	yyparse(void);
56
57int		debug_level = 0;
58int		verbose = 0;
59const char	*apmd_configfile = APMD_CONFIGFILE;
60const char	*apmd_pidfile = APMD_PIDFILE;
61int             apmctl_fd = -1;
62
63/*
64 * table of event handlers
65 */
66#define EVENT_CONFIG_INITIALIZER(EV,R) { #EV, NULL, R },
67struct event_config events[EVENT_MAX] = {
68	EVENT_CONFIG_INITIALIZER(NOEVENT, 0)
69	EVENT_CONFIG_INITIALIZER(STANDBYREQ, 1)
70	EVENT_CONFIG_INITIALIZER(SUSPENDREQ, 1)
71	EVENT_CONFIG_INITIALIZER(NORMRESUME, 0)
72	EVENT_CONFIG_INITIALIZER(CRITRESUME, 0)
73	EVENT_CONFIG_INITIALIZER(BATTERYLOW, 0)
74	EVENT_CONFIG_INITIALIZER(POWERSTATECHANGE, 0)
75	EVENT_CONFIG_INITIALIZER(UPDATETIME, 0)
76	EVENT_CONFIG_INITIALIZER(CRITSUSPEND, 1)
77	EVENT_CONFIG_INITIALIZER(USERSTANDBYREQ, 1)
78	EVENT_CONFIG_INITIALIZER(USERSUSPENDREQ, 1)
79	EVENT_CONFIG_INITIALIZER(STANDBYRESUME, 0)
80	EVENT_CONFIG_INITIALIZER(CAPABILITIESCHANGE, 0)
81};
82
83/*
84 * default procedure
85 */
86struct event_cmd *
87event_cmd_default_clone(void *this)
88{
89	struct event_cmd * oldone = this;
90	struct event_cmd * newone = malloc(oldone->len);
91
92	newone->next = NULL;
93	newone->len = oldone->len;
94	newone->name = oldone->name;
95	newone->op = oldone->op;
96	return newone;
97}
98
99/*
100 * exec command
101 */
102int
103event_cmd_exec_act(void *this)
104{
105	struct event_cmd_exec * p = this;
106	int status = -1;
107	pid_t pid;
108
109	switch ((pid = fork())) {
110	case -1:
111		(void) warn("cannot fork");
112		goto out;
113	case 0:
114		/* child process */
115		execl(_PATH_BSHELL, "sh", "-c", p->line, (char *)NULL);
116		_exit(127);
117	default:
118		/* parent process */
119		do {
120			pid = waitpid(pid, &status, 0);
121		} while (pid == -1 && errno == EINTR);
122		break;
123	}
124 out:
125	return status;
126}
127void
128event_cmd_exec_dump(void *this, FILE *fp)
129{
130	fprintf(fp, " \"%s\"", ((struct event_cmd_exec *)this)->line);
131}
132struct event_cmd *
133event_cmd_exec_clone(void *this)
134{
135	struct event_cmd_exec * newone = (struct event_cmd_exec *) event_cmd_default_clone(this);
136	struct event_cmd_exec * oldone = this;
137
138	newone->evcmd.next = NULL;
139	newone->evcmd.len = oldone->evcmd.len;
140	newone->evcmd.name = oldone->evcmd.name;
141	newone->evcmd.op = oldone->evcmd.op;
142	newone->line = strdup(oldone->line);
143	return (struct event_cmd *) newone;
144}
145void
146event_cmd_exec_free(void *this)
147{
148	free(((struct event_cmd_exec *)this)->line);
149}
150struct event_cmd_op event_cmd_exec_ops = {
151	event_cmd_exec_act,
152	event_cmd_exec_dump,
153	event_cmd_exec_clone,
154	event_cmd_exec_free
155};
156
157/*
158 * reject commad
159 */
160int
161event_cmd_reject_act(void *this)
162{
163	int rc = -1;
164
165	if (ioctl(apmctl_fd, APMIO_REJECTLASTREQ, NULL)) {
166		syslog(LOG_NOTICE, "fail to reject\n");
167		goto out;
168	}
169	rc = 0;
170 out:
171	return rc;
172}
173struct event_cmd_op event_cmd_reject_ops = {
174	event_cmd_reject_act,
175	NULL,
176	event_cmd_default_clone,
177	NULL
178};
179
180/*
181 * manipulate event_config
182 */
183struct event_cmd *
184clone_event_cmd_list(struct event_cmd *p)
185{
186	struct event_cmd dummy;
187	struct event_cmd *q = &dummy;
188	for ( ;p; p = p->next) {
189		assert(p->op->clone);
190		if ((q->next = p->op->clone(p)) == NULL)
191			(void) err(1, "out of memory");
192		q = q->next;
193	}
194	q->next = NULL;
195	return dummy.next;
196}
197void
198free_event_cmd_list(struct event_cmd *p)
199{
200	struct event_cmd * q;
201	for ( ; p ; p = q) {
202		q = p->next;
203		if (p->op->free)
204			p->op->free(p);
205		free(p);
206	}
207}
208int
209register_apm_event_handlers(
210	bitstr_t bit_decl(evlist, EVENT_MAX),
211	struct event_cmd *cmdlist)
212{
213	if (cmdlist) {
214		bitstr_t bit_decl(tmp, EVENT_MAX);
215		memcpy(&tmp, evlist, bitstr_size(EVENT_MAX));
216
217		for (;;) {
218			int n;
219			struct event_cmd *p;
220			struct event_cmd *q;
221			bit_ffs(tmp, EVENT_MAX, &n);
222			if (n < 0)
223				break;
224			p = events[n].cmdlist;
225			if ((q = clone_event_cmd_list(cmdlist)) == NULL)
226				(void) err(1, "out of memory");
227			if (p) {
228				while (p->next != NULL)
229					p = p->next;
230				p->next = q;
231			} else {
232				events[n].cmdlist = q;
233			}
234			bit_clear(tmp, n);
235		}
236	}
237	return 0;
238}
239
240/*
241 * execute command
242 */
243int
244exec_event_cmd(struct event_config *ev)
245{
246	int status = 0;
247
248	struct event_cmd *p = ev->cmdlist;
249	for (; p; p = p->next) {
250		assert(p->op->act);
251		if (verbose)
252			syslog(LOG_INFO, "action: %s", p->name);
253		status = p->op->act(p);
254		if (status) {
255			syslog(LOG_NOTICE, "command finished with %d\n", status);
256			if (ev->rejectable) {
257				syslog(LOG_ERR, "canceled");
258				(void) event_cmd_reject_act(NULL);
259			}
260			break;
261		}
262	}
263	return status;
264}
265
266/*
267 * read config file
268 */
269extern FILE * yyin;
270extern int yydebug;
271
272void
273read_config(void)
274{
275	int i;
276
277	if ((yyin = fopen(apmd_configfile, "r")) == NULL) {
278		(void) err(1, "cannot open config file");
279	}
280
281#ifdef DEBUG
282	yydebug = debug_level;
283#endif
284
285	if (yyparse() != 0)
286		(void) err(1, "cannot parse config file");
287
288	fclose(yyin);
289
290	/* enable events */
291	for (i = 0; i < EVENT_MAX; i++) {
292		if (events[i].cmdlist) {
293			u_int event_type = i;
294			if (write(apmctl_fd, &event_type, sizeof(u_int)) == -1) {
295				(void) err(1, "cannot enable event 0x%x", event_type);
296			}
297		}
298	}
299}
300
301void
302dump_config()
303{
304	int i;
305
306	for (i = 0; i < EVENT_MAX; i++) {
307		struct event_cmd * p;
308		if ((p = events[i].cmdlist)) {
309			fprintf(stderr, "apm_event %s {\n", events[i].name);
310			for ( ; p ; p = p->next) {
311				fprintf(stderr, "\t%s", p->name);
312				if (p->op->dump)
313					p->op->dump(p, stderr);
314				fprintf(stderr, ";\n");
315			}
316			fprintf(stderr, "}\n");
317		}
318	}
319}
320
321void
322destroy_config()
323{
324	int i;
325
326	/* disable events */
327	for (i = 0; i < EVENT_MAX; i++) {
328		if (events[i].cmdlist) {
329			u_int event_type = i;
330			if (write(apmctl_fd, &event_type, sizeof(u_int)) == -1) {
331				(void) err(1, "cannot disable event 0x%x", event_type);
332			}
333		}
334	}
335
336	for (i = 0; i < EVENT_MAX; i++) {
337		struct event_cmd * p;
338		if ((p = events[i].cmdlist))
339			free_event_cmd_list(p);
340		events[i].cmdlist = NULL;
341	}
342}
343
344void
345restart()
346{
347	destroy_config();
348	read_config();
349	if (verbose)
350		dump_config();
351}
352
353/*
354 * write pid file
355 */
356static void
357write_pid()
358{
359	FILE *fp = fopen(apmd_pidfile, "w");
360
361	if (fp) {
362		fprintf(fp, "%d\n", getpid());
363		fclose(fp);
364	}
365}
366
367/*
368 * handle signals
369 */
370static int signal_fd[2];
371
372void
373enque_signal(int sig)
374{
375	if (write(signal_fd[1], &sig, sizeof sig) != sizeof sig)
376		(void) err(1, "cannot process signal.");
377}
378
379void
380wait_child()
381{
382	int status;
383	while (waitpid(-1, &status, WNOHANG) > 0)
384		;
385}
386
387int
388proc_signal(int fd)
389{
390	int rc = -1;
391	int sig;
392
393	while (read(fd, &sig, sizeof sig) == sizeof sig) {
394		syslog(LOG_INFO, "caught signal: %d", sig);
395		switch (sig) {
396		case SIGHUP:
397			syslog(LOG_NOTICE, "restart by SIG");
398			restart();
399			break;
400		case SIGTERM:
401			syslog(LOG_NOTICE, "going down on signal %d", sig);
402			rc = 1;
403			goto out;
404		case SIGCHLD:
405			wait_child();
406			break;
407		default:
408			(void) warn("unexpected signal(%d) received.", sig);
409			break;
410		}
411	}
412	rc = 0;
413 out:
414	return rc;
415}
416void
417proc_apmevent(int fd)
418{
419	struct apm_event_info apmevent;
420
421	while (ioctl(fd, APMIO_NEXTEVENT, &apmevent) == 0) {
422		int status;
423		syslog(LOG_NOTICE, "apmevent %04x index %d\n",
424			apmevent.type, apmevent.index);
425		syslog(LOG_INFO, "apm event: %s", events[apmevent.type].name);
426		if (fork() == 0) {
427			status = exec_event_cmd(&events[apmevent.type]);
428			exit(status);
429		}
430	}
431}
432void
433event_loop(void)
434{
435	int		fdmax = 0;
436	struct sigaction nsa;
437	fd_set          master_rfds;
438	sigset_t	sigmask, osigmask;
439
440	FD_ZERO(&master_rfds);
441	FD_SET(apmctl_fd, &master_rfds);
442	fdmax = apmctl_fd > fdmax ? apmctl_fd : fdmax;
443
444	FD_SET(signal_fd[0], &master_rfds);
445	fdmax = signal_fd[0] > fdmax ? signal_fd[0] : fdmax;
446
447	memset(&nsa, 0, sizeof nsa);
448	nsa.sa_handler = enque_signal;
449	sigfillset(&nsa.sa_mask);
450	nsa.sa_flags = SA_RESTART;
451	sigaction(SIGHUP, &nsa, NULL);
452	sigaction(SIGCHLD, &nsa, NULL);
453	sigaction(SIGTERM, &nsa, NULL);
454
455	sigemptyset(&sigmask);
456	sigaddset(&sigmask, SIGHUP);
457	sigaddset(&sigmask, SIGCHLD);
458	sigaddset(&sigmask, SIGTERM);
459	sigprocmask(SIG_SETMASK, &sigmask, &osigmask);
460
461	while (1) {
462		fd_set rfds;
463
464		memcpy(&rfds, &master_rfds, sizeof rfds);
465		sigprocmask(SIG_SETMASK, &osigmask, NULL);
466		if (select(fdmax + 1, &rfds, 0, 0, 0) < 0) {
467			if (errno != EINTR)
468				(void) err(1, "select");
469		}
470		sigprocmask(SIG_SETMASK, &sigmask, NULL);
471
472		if (FD_ISSET(signal_fd[0], &rfds)) {
473			if (proc_signal(signal_fd[0]) < 0)
474				goto out;
475		}
476		if (FD_ISSET(apmctl_fd, &rfds))
477			proc_apmevent(apmctl_fd);
478	}
479out:
480	return;
481}
482
483void
484main(int ac, char* av[])
485{
486	int	ch;
487	int	daemonize = 1;
488	char	*prog;
489	int	logopt = LOG_NDELAY | LOG_PID;
490
491	while ((ch = getopt(ac, av, "df:v")) != EOF) {
492		switch (ch) {
493		case 'd':
494			daemonize = 0;
495			debug_level++;
496			break;
497		case 'f':
498			apmd_configfile = optarg;
499			break;
500		case 'v':
501			verbose = 1;
502			break;
503		default:
504			(void) err(1, "unknown option `%c'", ch);
505		}
506	}
507
508	if (daemonize)
509		daemon(0, 0);
510
511#ifdef NICE_INCR
512	(void) nice(NICE_INCR);
513#endif
514
515	if (!daemonize)
516		logopt |= LOG_PERROR;
517
518	prog = strrchr(av[0], '/');
519	openlog(prog ? prog+1 : av[0], logopt, LOG_DAEMON);
520
521	syslog(LOG_NOTICE, "start");
522
523	if (pipe(signal_fd) < 0)
524		(void) err(1, "pipe");
525	if (fcntl(signal_fd[0], F_SETFL, O_NONBLOCK) < 0)
526		(void) err(1, "fcntl");
527
528	if ((apmctl_fd = open(APM_CTL_DEVICEFILE, O_RDWR)) == -1) {
529		(void) err(1, "cannot open device file `%s'", APM_CTL_DEVICEFILE);
530	}
531
532	restart();
533	write_pid();
534	event_loop();
535 	exit(EXIT_SUCCESS);
536}
537
538