apmd.c revision 71277
1115013Smarcel/*-
2160157Smarcel * APM (Advanced Power Management) Event Dispatcher
3121642Smarcel *
4121642Smarcel * Copyright (c) 1999 Mitsuru IWASAKI <iwasaki@FreeBSD.org>
5121642Smarcel * Copyright (c) 1999 KOIE Hidetaka <koie@suri.co.jp>
6121642Smarcel * All rights reserved.
7121642Smarcel *
8121642Smarcel * Redistribution and use in source and binary forms, with or without
9121642Smarcel * modification, are permitted provided that the following conditions
10121642Smarcel * are met:
11115013Smarcel * 1. Redistributions of source code must retain the above copyright
12121642Smarcel *    notice, this list of conditions and the following disclaimer.
13121642Smarcel * 2. Redistributions in binary form must reproduce the above copyright
14121642Smarcel *    notice, this list of conditions and the following disclaimer in the
15121642Smarcel *    documentation and/or other materials provided with the distribution.
16121642Smarcel *
17121642Smarcel * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
18121642Smarcel * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19121642Smarcel * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20121642Smarcel * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
21121642Smarcel * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22121642Smarcel * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
23121642Smarcel * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
24121642Smarcel * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25115013Smarcel * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
26115013Smarcel * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
27120925Smarcel * SUCH DAMAGE.
28115013Smarcel */
29115013Smarcel
30115013Smarcel#ifndef lint
31115013Smarcelstatic const char rcsid[] =
32115013Smarcel  "$FreeBSD: head/usr.sbin/apmd/apmd.c 71277 2001-01-20 01:22:31Z jedgar $";
33115013Smarcel#endif /* not lint */
34115013Smarcel
35115013Smarcel#include <assert.h>
36115013Smarcel#include <bitstring.h>
37115013Smarcel#include <err.h>
38115013Smarcel#include <errno.h>
39115013Smarcel#include <fcntl.h>
40115013Smarcel#include <paths.h>
41115013Smarcel#include <signal.h>
42115013Smarcel#include <stdio.h>
43115013Smarcel#include <stdlib.h>
44115013Smarcel#include <string.h>
45115013Smarcel#include <syslog.h>
46115013Smarcel#include <unistd.h>
47115013Smarcel#include <sys/ioctl.h>
48115013Smarcel#include <sys/types.h>
49115013Smarcel#include <sys/time.h>
50115013Smarcel#include <sys/wait.h>
51120925Smarcel#include <machine/apm_bios.h>
52115013Smarcel
53115013Smarcel#include "apmd.h"
54115013Smarcel
55115013Smarcelextern int	yyparse(void);
56115013Smarcel
57115013Smarcelint		debug_level = 0;
58115013Smarcelint		verbose = 0;
59115013Smarcelconst char	*apmd_configfile = APMD_CONFIGFILE;
60115013Smarcelconst char	*apmd_pidfile = APMD_PIDFILE;
61115013Smarcelint             apmctl_fd = -1;
62115013Smarcel
63115013Smarcel/*
64115013Smarcel * table of event handlers
65115013Smarcel */
66115013Smarcel#define EVENT_CONFIG_INITIALIZER(EV,R) { #EV, NULL, R },
67115013Smarcelstruct event_config events[EVENT_MAX] = {
68115013Smarcel	EVENT_CONFIG_INITIALIZER(NOEVENT, 0)
69115013Smarcel	EVENT_CONFIG_INITIALIZER(STANDBYREQ, 1)
70120925Smarcel	EVENT_CONFIG_INITIALIZER(SUSPENDREQ, 1)
71120925Smarcel	EVENT_CONFIG_INITIALIZER(NORMRESUME, 0)
72115013Smarcel	EVENT_CONFIG_INITIALIZER(CRITRESUME, 0)
73115013Smarcel	EVENT_CONFIG_INITIALIZER(BATTERYLOW, 0)
74120925Smarcel	EVENT_CONFIG_INITIALIZER(POWERSTATECHANGE, 0)
75120925Smarcel	EVENT_CONFIG_INITIALIZER(UPDATETIME, 0)
76120925Smarcel	EVENT_CONFIG_INITIALIZER(CRITSUSPEND, 1)
77120925Smarcel	EVENT_CONFIG_INITIALIZER(USERSTANDBYREQ, 1)
78120925Smarcel	EVENT_CONFIG_INITIALIZER(USERSUSPENDREQ, 1)
79120925Smarcel	EVENT_CONFIG_INITIALIZER(STANDBYRESUME, 0)
80120925Smarcel	EVENT_CONFIG_INITIALIZER(CAPABILITIESCHANGE, 0)
81115013Smarcel};
82115013Smarcel
83115013Smarcel/*
84115013Smarcel * default procedure
85115013Smarcel */
86115013Smarcelstruct event_cmd *
87115013Smarcelevent_cmd_default_clone(void *this)
88115013Smarcel{
89115013Smarcel	struct event_cmd * oldone = this;
90115013Smarcel	struct event_cmd * newone = malloc(oldone->len);
91115013Smarcel
92115013Smarcel	newone->next = NULL;
93115013Smarcel	newone->len = oldone->len;
94115013Smarcel	newone->name = oldone->name;
95115013Smarcel	newone->op = oldone->op;
96115013Smarcel	return newone;
97115013Smarcel}
98115013Smarcel
99115013Smarcel/*
100115013Smarcel * exec command
101115013Smarcel */
102115013Smarcelint
103115013Smarcelevent_cmd_exec_act(void *this)
104115013Smarcel{
105115013Smarcel	struct event_cmd_exec * p = this;
106115013Smarcel	int status = -1;
107115013Smarcel	pid_t pid;
108115013Smarcel
109120925Smarcel	switch ((pid = fork())) {
110120925Smarcel	case -1:
111115013Smarcel		(void) warn("cannot fork");
112115013Smarcel		goto out;
113115013Smarcel	case 0:
114120925Smarcel		/* child process */
115120925Smarcel		execl(_PATH_BSHELL, "sh", "-c", p->line, (char *)NULL);
116115013Smarcel		_exit(127);
117115013Smarcel	default:
118115013Smarcel		/* parent process */
119115013Smarcel		do {
120115013Smarcel			pid = waitpid(pid, &status, 0);
121115013Smarcel		} while (pid == -1 && errno == EINTR);
122115013Smarcel		break;
123115013Smarcel	}
124115013Smarcel out:
125115013Smarcel	return status;
126115013Smarcel}
127115013Smarcelvoid
128115013Smarcelevent_cmd_exec_dump(void *this, FILE *fp)
129120925Smarcel{
130115013Smarcel	fprintf(fp, " \"%s\"", ((struct event_cmd_exec *)this)->line);
131115013Smarcel}
132115013Smarcelstruct event_cmd *
133115013Smarcelevent_cmd_exec_clone(void *this)
134115013Smarcel{
135115013Smarcel	struct event_cmd_exec * newone = (struct event_cmd_exec *) event_cmd_default_clone(this);
136115013Smarcel	struct event_cmd_exec * oldone = this;
137115013Smarcel
138115013Smarcel	newone->evcmd.next = NULL;
139115013Smarcel	newone->evcmd.len = oldone->evcmd.len;
140115013Smarcel	newone->evcmd.name = oldone->evcmd.name;
141115013Smarcel	newone->evcmd.op = oldone->evcmd.op;
142115013Smarcel	if ((newone->line = strdup(oldone->line)) == NULL)
143115013Smarcel		err(1, "out of memory");
144115013Smarcel	return (struct event_cmd *) newone;
145115013Smarcel}
146115013Smarcelvoid
147115013Smarcelevent_cmd_exec_free(void *this)
148115013Smarcel{
149115013Smarcel	free(((struct event_cmd_exec *)this)->line);
150115013Smarcel}
151115013Smarcelstruct event_cmd_op event_cmd_exec_ops = {
152115013Smarcel	event_cmd_exec_act,
153115013Smarcel	event_cmd_exec_dump,
154115013Smarcel	event_cmd_exec_clone,
155115013Smarcel	event_cmd_exec_free
156115013Smarcel};
157115013Smarcel
158115013Smarcel/*
159115013Smarcel * reject commad
160115013Smarcel */
161115013Smarcelint
162115013Smarcelevent_cmd_reject_act(void *this)
163115013Smarcel{
164115013Smarcel	int rc = -1;
165115013Smarcel
166115013Smarcel	if (ioctl(apmctl_fd, APMIO_REJECTLASTREQ, NULL)) {
167115013Smarcel		syslog(LOG_NOTICE, "fail to reject\n");
168115013Smarcel		goto out;
169115013Smarcel	}
170115013Smarcel	rc = 0;
171115013Smarcel out:
172115013Smarcel	return rc;
173115013Smarcel}
174115013Smarcelstruct event_cmd_op event_cmd_reject_ops = {
175115013Smarcel	event_cmd_reject_act,
176115013Smarcel	NULL,
177115013Smarcel	event_cmd_default_clone,
178115013Smarcel	NULL
179115013Smarcel};
180115013Smarcel
181115013Smarcel/*
182115013Smarcel * manipulate event_config
183129059Smarcel */
184160157Smarcelstruct event_cmd *
185115013Smarcelclone_event_cmd_list(struct event_cmd *p)
186115013Smarcel{
187115013Smarcel	struct event_cmd dummy;
188115013Smarcel	struct event_cmd *q = &dummy;
189115013Smarcel	for ( ;p; p = p->next) {
190115013Smarcel		assert(p->op->clone);
191115013Smarcel		if ((q->next = p->op->clone(p)) == NULL)
192115013Smarcel			(void) err(1, "out of memory");
193115013Smarcel		q = q->next;
194115013Smarcel	}
195115013Smarcel	q->next = NULL;
196115013Smarcel	return dummy.next;
197115013Smarcel}
198115013Smarcelvoid
199115013Smarcelfree_event_cmd_list(struct event_cmd *p)
200115013Smarcel{
201115013Smarcel	struct event_cmd * q;
202115013Smarcel	for ( ; p ; p = q) {
203115013Smarcel		q = p->next;
204115013Smarcel		if (p->op->free)
205115013Smarcel			p->op->free(p);
206115013Smarcel		free(p);
207115013Smarcel	}
208120925Smarcel}
209120925Smarcelint
210160157Smarcelregister_apm_event_handlers(
211160157Smarcel	bitstr_t bit_decl(evlist, EVENT_MAX),
212160157Smarcel	struct event_cmd *cmdlist)
213160157Smarcel{
214160157Smarcel	if (cmdlist) {
215160157Smarcel		bitstr_t bit_decl(tmp, EVENT_MAX);
216160157Smarcel		memcpy(&tmp, evlist, bitstr_size(EVENT_MAX));
217160157Smarcel
218160157Smarcel		for (;;) {
219115013Smarcel			int n;
220160157Smarcel			struct event_cmd *p;
221115013Smarcel			struct event_cmd *q;
222115013Smarcel			bit_ffs(tmp, EVENT_MAX, &n);
223115013Smarcel			if (n < 0)
224115013Smarcel				break;
225115013Smarcel			p = events[n].cmdlist;
226115013Smarcel			if ((q = clone_event_cmd_list(cmdlist)) == NULL)
227115013Smarcel				(void) err(1, "out of memory");
228115013Smarcel			if (p) {
229115013Smarcel				while (p->next != NULL)
230115013Smarcel					p = p->next;
231115013Smarcel				p->next = q;
232115013Smarcel			} else {
233115013Smarcel				events[n].cmdlist = q;
234115013Smarcel			}
235115013Smarcel			bit_clear(tmp, n);
236115013Smarcel		}
237115013Smarcel	}
238115013Smarcel	return 0;
239115013Smarcel}
240115013Smarcel
241115013Smarcel/*
242115013Smarcel * execute command
243115013Smarcel */
244115013Smarcelint
245115013Smarcelexec_event_cmd(struct event_config *ev)
246115013Smarcel{
247115013Smarcel	int status = 0;
248115013Smarcel
249115013Smarcel	struct event_cmd *p = ev->cmdlist;
250115013Smarcel	for (; p; p = p->next) {
251115013Smarcel		assert(p->op->act);
252115013Smarcel		if (verbose)
253115013Smarcel			syslog(LOG_INFO, "action: %s", p->name);
254115013Smarcel		status = p->op->act(p);
255120925Smarcel		if (status) {
256120925Smarcel			syslog(LOG_NOTICE, "command finished with %d\n", status);
257115013Smarcel			if (ev->rejectable) {
258115013Smarcel				syslog(LOG_ERR, "canceled");
259115013Smarcel				(void) event_cmd_reject_act(NULL);
260115013Smarcel			}
261115013Smarcel			break;
262115013Smarcel		}
263115013Smarcel	}
264115013Smarcel	return status;
265115013Smarcel}
266115013Smarcel
267115013Smarcel/*
268115013Smarcel * read config file
269115013Smarcel */
270115013Smarcelextern FILE * yyin;
271115013Smarcelextern int yydebug;
272115013Smarcel
273115013Smarcelvoid
274115013Smarcelread_config(void)
275115013Smarcel{
276115013Smarcel	int i;
277115013Smarcel
278115013Smarcel	if ((yyin = fopen(apmd_configfile, "r")) == NULL) {
279115013Smarcel		(void) err(1, "cannot open config file");
280115013Smarcel	}
281115013Smarcel
282115013Smarcel#ifdef DEBUG
283115013Smarcel	yydebug = debug_level;
284115013Smarcel#endif
285115013Smarcel
286115013Smarcel	if (yyparse() != 0)
287115013Smarcel		(void) err(1, "cannot parse config file");
288115013Smarcel
289115013Smarcel	fclose(yyin);
290115013Smarcel
291115013Smarcel	/* enable events */
292115013Smarcel	for (i = 0; i < EVENT_MAX; i++) {
293115013Smarcel		if (events[i].cmdlist) {
294115013Smarcel			u_int event_type = i;
295115013Smarcel			if (write(apmctl_fd, &event_type, sizeof(u_int)) == -1) {
296115013Smarcel				(void) err(1, "cannot enable event 0x%x", event_type);
297115013Smarcel			}
298115013Smarcel		}
299115013Smarcel	}
300115013Smarcel}
301115013Smarcel
302115013Smarcelvoid
303115013Smarceldump_config()
304115013Smarcel{
305115013Smarcel	int i;
306115013Smarcel
307115013Smarcel	for (i = 0; i < EVENT_MAX; i++) {
308115013Smarcel		struct event_cmd * p;
309115013Smarcel		if ((p = events[i].cmdlist)) {
310115013Smarcel			fprintf(stderr, "apm_event %s {\n", events[i].name);
311115013Smarcel			for ( ; p ; p = p->next) {
312115013Smarcel				fprintf(stderr, "\t%s", p->name);
313115013Smarcel				if (p->op->dump)
314115013Smarcel					p->op->dump(p, stderr);
315115013Smarcel				fprintf(stderr, ";\n");
316115013Smarcel			}
317115013Smarcel			fprintf(stderr, "}\n");
318115013Smarcel		}
319115013Smarcel	}
320115013Smarcel}
321115013Smarcel
322115013Smarcelvoid
323115013Smarceldestroy_config()
324160157Smarcel{
325115013Smarcel	int i;
326129059Smarcel
327129059Smarcel	/* disable events */
328129059Smarcel	for (i = 0; i < EVENT_MAX; i++) {
329129059Smarcel		if (events[i].cmdlist) {
330115013Smarcel			u_int event_type = i;
331115013Smarcel			if (write(apmctl_fd, &event_type, sizeof(u_int)) == -1) {
332115013Smarcel				(void) err(1, "cannot disable event 0x%x", event_type);
333115013Smarcel			}
334115013Smarcel		}
335129059Smarcel	}
336160157Smarcel
337129059Smarcel	for (i = 0; i < EVENT_MAX; i++) {
338129059Smarcel		struct event_cmd * p;
339129059Smarcel		if ((p = events[i].cmdlist))
340129059Smarcel			free_event_cmd_list(p);
341160157Smarcel		events[i].cmdlist = NULL;
342115013Smarcel	}
343115013Smarcel}
344129059Smarcel
345129059Smarcelvoid
346129059Smarcelrestart()
347129059Smarcel{
348129059Smarcel	destroy_config();
349160163Smarcel	read_config();
350129059Smarcel	if (verbose)
351129059Smarcel		dump_config();
352129059Smarcel}
353129059Smarcel
354129059Smarcel/*
355129059Smarcel * write pid file
356129059Smarcel */
357129059Smarcelstatic void
358129059Smarcelwrite_pid()
359129059Smarcel{
360129059Smarcel	FILE *fp = fopen(apmd_pidfile, "w");
361129059Smarcel
362129059Smarcel	if (fp) {
363129059Smarcel		fprintf(fp, "%d\n", getpid());
364129059Smarcel		fclose(fp);
365129059Smarcel	}
366129059Smarcel}
367129059Smarcel
368115013Smarcel/*
369129059Smarcel * handle signals
370129059Smarcel */
371129059Smarcelstatic int signal_fd[2];
372129059Smarcel
373129059Smarcelvoid
374129059Smarcelenque_signal(int sig)
375129059Smarcel{
376129059Smarcel	if (write(signal_fd[1], &sig, sizeof sig) != sizeof sig)
377160157Smarcel		(void) err(1, "cannot process signal.");
378129059Smarcel}
379129059Smarcel
380129059Smarcelvoid
381129059Smarcelwait_child()
382129059Smarcel{
383129059Smarcel	int status;
384129059Smarcel	while (waitpid(-1, &status, WNOHANG) > 0)
385129059Smarcel		;
386115013Smarcel}
387129059Smarcel
388129059Smarcelint
389129059Smarcelproc_signal(int fd)
390129059Smarcel{
391129059Smarcel	int rc = -1;
392129059Smarcel	int sig;
393129059Smarcel
394129059Smarcel	while (read(fd, &sig, sizeof sig) == sizeof sig) {
395129059Smarcel		syslog(LOG_INFO, "caught signal: %d", sig);
396160157Smarcel		switch (sig) {
397129059Smarcel		case SIGHUP:
398129059Smarcel			syslog(LOG_NOTICE, "restart by SIG");
399129059Smarcel			restart();
400129059Smarcel			break;
401129059Smarcel		case SIGTERM:
402129059Smarcel			syslog(LOG_NOTICE, "going down on signal %d", sig);
403129059Smarcel			rc = 1;
404129059Smarcel			goto out;
405129059Smarcel		case SIGCHLD:
406129059Smarcel			wait_child();
407115013Smarcel			break;
408160163Smarcel		default:
409			(void) warn("unexpected signal(%d) received.", sig);
410			break;
411		}
412	}
413	rc = 0;
414 out:
415	return rc;
416}
417void
418proc_apmevent(int fd)
419{
420	struct apm_event_info apmevent;
421
422	while (ioctl(fd, APMIO_NEXTEVENT, &apmevent) == 0) {
423		int status;
424		syslog(LOG_NOTICE, "apmevent %04x index %d\n",
425			apmevent.type, apmevent.index);
426		syslog(LOG_INFO, "apm event: %s", events[apmevent.type].name);
427		if (fork() == 0) {
428			status = exec_event_cmd(&events[apmevent.type]);
429			exit(status);
430		}
431	}
432}
433void
434event_loop(void)
435{
436	int		fdmax = 0;
437	struct sigaction nsa;
438	fd_set          master_rfds;
439	sigset_t	sigmask, osigmask;
440
441	FD_ZERO(&master_rfds);
442	FD_SET(apmctl_fd, &master_rfds);
443	fdmax = apmctl_fd > fdmax ? apmctl_fd : fdmax;
444
445	FD_SET(signal_fd[0], &master_rfds);
446	fdmax = signal_fd[0] > fdmax ? signal_fd[0] : fdmax;
447
448	memset(&nsa, 0, sizeof nsa);
449	nsa.sa_handler = enque_signal;
450	sigfillset(&nsa.sa_mask);
451	nsa.sa_flags = SA_RESTART;
452	sigaction(SIGHUP, &nsa, NULL);
453	sigaction(SIGCHLD, &nsa, NULL);
454	sigaction(SIGTERM, &nsa, NULL);
455
456	sigemptyset(&sigmask);
457	sigaddset(&sigmask, SIGHUP);
458	sigaddset(&sigmask, SIGCHLD);
459	sigaddset(&sigmask, SIGTERM);
460	sigprocmask(SIG_SETMASK, &sigmask, &osigmask);
461
462	while (1) {
463		fd_set rfds;
464
465		memcpy(&rfds, &master_rfds, sizeof rfds);
466		sigprocmask(SIG_SETMASK, &osigmask, NULL);
467		if (select(fdmax + 1, &rfds, 0, 0, 0) < 0) {
468			if (errno != EINTR)
469				(void) err(1, "select");
470		}
471		sigprocmask(SIG_SETMASK, &sigmask, NULL);
472
473		if (FD_ISSET(signal_fd[0], &rfds)) {
474			if (proc_signal(signal_fd[0]) < 0)
475				goto out;
476		}
477		if (FD_ISSET(apmctl_fd, &rfds))
478			proc_apmevent(apmctl_fd);
479	}
480out:
481	return;
482}
483
484int
485main(int ac, char* av[])
486{
487	int	ch;
488	int	daemonize = 1;
489	char	*prog;
490	int	logopt = LOG_NDELAY | LOG_PID;
491
492	while ((ch = getopt(ac, av, "df:v")) != EOF) {
493		switch (ch) {
494		case 'd':
495			daemonize = 0;
496			debug_level++;
497			break;
498		case 'f':
499			apmd_configfile = optarg;
500			break;
501		case 'v':
502			verbose = 1;
503			break;
504		default:
505			(void) err(1, "unknown option `%c'", ch);
506		}
507	}
508
509	if (daemonize)
510		daemon(0, 0);
511
512#ifdef NICE_INCR
513	(void) nice(NICE_INCR);
514#endif
515
516	if (!daemonize)
517		logopt |= LOG_PERROR;
518
519	prog = strrchr(av[0], '/');
520	openlog(prog ? prog+1 : av[0], logopt, LOG_DAEMON);
521
522	syslog(LOG_NOTICE, "start");
523
524	if (pipe(signal_fd) < 0)
525		(void) err(1, "pipe");
526	if (fcntl(signal_fd[0], F_SETFL, O_NONBLOCK) < 0)
527		(void) err(1, "fcntl");
528
529	if ((apmctl_fd = open(APM_CTL_DEVICEFILE, O_RDWR)) == -1) {
530		(void) err(1, "cannot open device file `%s'", APM_CTL_DEVICEFILE);
531	}
532
533	restart();
534	write_pid();
535	event_loop();
536 	exit(EXIT_SUCCESS);
537}
538
539