xref: /freebsd/usr.sbin/apmd/apmd.c (revision ee41f1b1cf5e3d4f586cb85b46123b416275862c)
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
31 static const char rcsid[] =
32   "$FreeBSD$";
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 
55 extern int	yyparse(void);
56 
57 int		debug_level = 0;
58 int		verbose = 0;
59 const char	*apmd_configfile = APMD_CONFIGFILE;
60 const char	*apmd_pidfile = APMD_PIDFILE;
61 int             apmctl_fd = -1;
62 
63 /*
64  * table of event handlers
65  */
66 #define EVENT_CONFIG_INITIALIZER(EV,R) { #EV, NULL, R },
67 struct 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  */
86 struct event_cmd *
87 event_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  */
102 int
103 event_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 }
127 void
128 event_cmd_exec_dump(void *this, FILE *fp)
129 {
130 	fprintf(fp, " \"%s\"", ((struct event_cmd_exec *)this)->line);
131 }
132 struct event_cmd *
133 event_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 	if ((newone->line = strdup(oldone->line)) == NULL)
143 		err(1, "out of memory");
144 	return (struct event_cmd *) newone;
145 }
146 void
147 event_cmd_exec_free(void *this)
148 {
149 	free(((struct event_cmd_exec *)this)->line);
150 }
151 struct event_cmd_op event_cmd_exec_ops = {
152 	event_cmd_exec_act,
153 	event_cmd_exec_dump,
154 	event_cmd_exec_clone,
155 	event_cmd_exec_free
156 };
157 
158 /*
159  * reject commad
160  */
161 int
162 event_cmd_reject_act(void *this)
163 {
164 	int rc = -1;
165 
166 	if (ioctl(apmctl_fd, APMIO_REJECTLASTREQ, NULL)) {
167 		syslog(LOG_NOTICE, "fail to reject\n");
168 		goto out;
169 	}
170 	rc = 0;
171  out:
172 	return rc;
173 }
174 struct event_cmd_op event_cmd_reject_ops = {
175 	event_cmd_reject_act,
176 	NULL,
177 	event_cmd_default_clone,
178 	NULL
179 };
180 
181 /*
182  * manipulate event_config
183  */
184 struct event_cmd *
185 clone_event_cmd_list(struct event_cmd *p)
186 {
187 	struct event_cmd dummy;
188 	struct event_cmd *q = &dummy;
189 	for ( ;p; p = p->next) {
190 		assert(p->op->clone);
191 		if ((q->next = p->op->clone(p)) == NULL)
192 			(void) err(1, "out of memory");
193 		q = q->next;
194 	}
195 	q->next = NULL;
196 	return dummy.next;
197 }
198 void
199 free_event_cmd_list(struct event_cmd *p)
200 {
201 	struct event_cmd * q;
202 	for ( ; p ; p = q) {
203 		q = p->next;
204 		if (p->op->free)
205 			p->op->free(p);
206 		free(p);
207 	}
208 }
209 int
210 register_apm_event_handlers(
211 	bitstr_t bit_decl(evlist, EVENT_MAX),
212 	struct event_cmd *cmdlist)
213 {
214 	if (cmdlist) {
215 		bitstr_t bit_decl(tmp, EVENT_MAX);
216 		memcpy(&tmp, evlist, bitstr_size(EVENT_MAX));
217 
218 		for (;;) {
219 			int n;
220 			struct event_cmd *p;
221 			struct event_cmd *q;
222 			bit_ffs(tmp, EVENT_MAX, &n);
223 			if (n < 0)
224 				break;
225 			p = events[n].cmdlist;
226 			if ((q = clone_event_cmd_list(cmdlist)) == NULL)
227 				(void) err(1, "out of memory");
228 			if (p) {
229 				while (p->next != NULL)
230 					p = p->next;
231 				p->next = q;
232 			} else {
233 				events[n].cmdlist = q;
234 			}
235 			bit_clear(tmp, n);
236 		}
237 	}
238 	return 0;
239 }
240 
241 /*
242  * execute command
243  */
244 int
245 exec_event_cmd(struct event_config *ev)
246 {
247 	int status = 0;
248 
249 	struct event_cmd *p = ev->cmdlist;
250 	for (; p; p = p->next) {
251 		assert(p->op->act);
252 		if (verbose)
253 			syslog(LOG_INFO, "action: %s", p->name);
254 		status = p->op->act(p);
255 		if (status) {
256 			syslog(LOG_NOTICE, "command finished with %d\n", status);
257 			if (ev->rejectable) {
258 				syslog(LOG_ERR, "canceled");
259 				(void) event_cmd_reject_act(NULL);
260 			}
261 			break;
262 		}
263 	}
264 	return status;
265 }
266 
267 /*
268  * read config file
269  */
270 extern FILE * yyin;
271 extern int yydebug;
272 
273 void
274 read_config(void)
275 {
276 	int i;
277 
278 	if ((yyin = fopen(apmd_configfile, "r")) == NULL) {
279 		(void) err(1, "cannot open config file");
280 	}
281 
282 #ifdef DEBUG
283 	yydebug = debug_level;
284 #endif
285 
286 	if (yyparse() != 0)
287 		(void) err(1, "cannot parse config file");
288 
289 	fclose(yyin);
290 
291 	/* enable events */
292 	for (i = 0; i < EVENT_MAX; i++) {
293 		if (events[i].cmdlist) {
294 			u_int event_type = i;
295 			if (write(apmctl_fd, &event_type, sizeof(u_int)) == -1) {
296 				(void) err(1, "cannot enable event 0x%x", event_type);
297 			}
298 		}
299 	}
300 }
301 
302 void
303 dump_config()
304 {
305 	int i;
306 
307 	for (i = 0; i < EVENT_MAX; i++) {
308 		struct event_cmd * p;
309 		if ((p = events[i].cmdlist)) {
310 			fprintf(stderr, "apm_event %s {\n", events[i].name);
311 			for ( ; p ; p = p->next) {
312 				fprintf(stderr, "\t%s", p->name);
313 				if (p->op->dump)
314 					p->op->dump(p, stderr);
315 				fprintf(stderr, ";\n");
316 			}
317 			fprintf(stderr, "}\n");
318 		}
319 	}
320 }
321 
322 void
323 destroy_config()
324 {
325 	int i;
326 
327 	/* disable events */
328 	for (i = 0; i < EVENT_MAX; i++) {
329 		if (events[i].cmdlist) {
330 			u_int event_type = i;
331 			if (write(apmctl_fd, &event_type, sizeof(u_int)) == -1) {
332 				(void) err(1, "cannot disable event 0x%x", event_type);
333 			}
334 		}
335 	}
336 
337 	for (i = 0; i < EVENT_MAX; i++) {
338 		struct event_cmd * p;
339 		if ((p = events[i].cmdlist))
340 			free_event_cmd_list(p);
341 		events[i].cmdlist = NULL;
342 	}
343 }
344 
345 void
346 restart()
347 {
348 	destroy_config();
349 	read_config();
350 	if (verbose)
351 		dump_config();
352 }
353 
354 /*
355  * write pid file
356  */
357 static void
358 write_pid()
359 {
360 	FILE *fp = fopen(apmd_pidfile, "w");
361 
362 	if (fp) {
363 		fprintf(fp, "%d\n", getpid());
364 		fclose(fp);
365 	}
366 }
367 
368 /*
369  * handle signals
370  */
371 static int signal_fd[2];
372 
373 void
374 enque_signal(int sig)
375 {
376 	if (write(signal_fd[1], &sig, sizeof sig) != sizeof sig)
377 		(void) err(1, "cannot process signal.");
378 }
379 
380 void
381 wait_child()
382 {
383 	int status;
384 	while (waitpid(-1, &status, WNOHANG) > 0)
385 		;
386 }
387 
388 int
389 proc_signal(int fd)
390 {
391 	int rc = -1;
392 	int sig;
393 
394 	while (read(fd, &sig, sizeof sig) == sizeof sig) {
395 		syslog(LOG_INFO, "caught signal: %d", sig);
396 		switch (sig) {
397 		case SIGHUP:
398 			syslog(LOG_NOTICE, "restart by SIG");
399 			restart();
400 			break;
401 		case SIGTERM:
402 			syslog(LOG_NOTICE, "going down on signal %d", sig);
403 			rc = 1;
404 			goto out;
405 		case SIGCHLD:
406 			wait_child();
407 			break;
408 		default:
409 			(void) warn("unexpected signal(%d) received.", sig);
410 			break;
411 		}
412 	}
413 	rc = 0;
414  out:
415 	return rc;
416 }
417 void
418 proc_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 }
433 void
434 event_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 	}
480 out:
481 	return;
482 }
483 
484 int
485 main(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