148730Siwasaki/*-
248730Siwasaki * APM (Advanced Power Management) Event Dispatcher
348730Siwasaki *
448730Siwasaki * Copyright (c) 1999 Mitsuru IWASAKI <iwasaki@FreeBSD.org>
548730Siwasaki * Copyright (c) 1999 KOIE Hidetaka <koie@suri.co.jp>
648730Siwasaki * All rights reserved.
748730Siwasaki *
848730Siwasaki * Redistribution and use in source and binary forms, with or without
948730Siwasaki * modification, are permitted provided that the following conditions
1048730Siwasaki * are met:
1148730Siwasaki * 1. Redistributions of source code must retain the above copyright
1248730Siwasaki *    notice, this list of conditions and the following disclaimer.
1348730Siwasaki * 2. Redistributions in binary form must reproduce the above copyright
1448730Siwasaki *    notice, this list of conditions and the following disclaimer in the
1548730Siwasaki *    documentation and/or other materials provided with the distribution.
1648730Siwasaki *
1748730Siwasaki * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
1848730Siwasaki * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
1948730Siwasaki * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
2048730Siwasaki * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
2148730Siwasaki * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
2248730Siwasaki * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
2348730Siwasaki * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
2448730Siwasaki * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
2548730Siwasaki * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
2648730Siwasaki * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
2748730Siwasaki * SUCH DAMAGE.
2848730Siwasaki */
2948730Siwasaki
3048730Siwasaki#ifndef lint
3148730Siwasakistatic const char rcsid[] =
3250479Speter  "$FreeBSD: releng/11.0/usr.sbin/apmd/apmd.c 300557 2016-05-24 03:15:46Z peter $";
3348730Siwasaki#endif /* not lint */
3448730Siwasaki
35300557Speter#include <sys/types.h>
3648730Siwasaki#include <assert.h>
3748730Siwasaki#include <bitstring.h>
3848730Siwasaki#include <err.h>
3948730Siwasaki#include <errno.h>
4048730Siwasaki#include <fcntl.h>
4148730Siwasaki#include <paths.h>
4248730Siwasaki#include <signal.h>
4348730Siwasaki#include <stdio.h>
4448730Siwasaki#include <stdlib.h>
4548730Siwasaki#include <string.h>
4648730Siwasaki#include <syslog.h>
4748730Siwasaki#include <unistd.h>
4848730Siwasaki#include <sys/ioctl.h>
4948730Siwasaki#include <sys/time.h>
5048730Siwasaki#include <sys/wait.h>
5148730Siwasaki#include <machine/apm_bios.h>
5248730Siwasaki
5348730Siwasaki#include "apmd.h"
5448730Siwasaki
5548730Siwasakiint		debug_level = 0;
5648730Siwasakiint		verbose = 0;
57116666Smdoddint		soft_power_state_change = 0;
5848730Siwasakiconst char	*apmd_configfile = APMD_CONFIGFILE;
5948730Siwasakiconst char	*apmd_pidfile = APMD_PIDFILE;
6076611Snsayerint             apmctl_fd = -1, apmnorm_fd = -1;
6148730Siwasaki
6248730Siwasaki/*
6348730Siwasaki * table of event handlers
6448730Siwasaki */
6548730Siwasaki#define EVENT_CONFIG_INITIALIZER(EV,R) { #EV, NULL, R },
6648730Siwasakistruct event_config events[EVENT_MAX] = {
6748730Siwasaki	EVENT_CONFIG_INITIALIZER(NOEVENT, 0)
6848730Siwasaki	EVENT_CONFIG_INITIALIZER(STANDBYREQ, 1)
6948730Siwasaki	EVENT_CONFIG_INITIALIZER(SUSPENDREQ, 1)
7048730Siwasaki	EVENT_CONFIG_INITIALIZER(NORMRESUME, 0)
7148730Siwasaki	EVENT_CONFIG_INITIALIZER(CRITRESUME, 0)
7248730Siwasaki	EVENT_CONFIG_INITIALIZER(BATTERYLOW, 0)
73144043Smdodd	EVENT_CONFIG_INITIALIZER(POWERSTATECHANGE, 0)
7448730Siwasaki	EVENT_CONFIG_INITIALIZER(UPDATETIME, 0)
7548730Siwasaki	EVENT_CONFIG_INITIALIZER(CRITSUSPEND, 1)
7648730Siwasaki	EVENT_CONFIG_INITIALIZER(USERSTANDBYREQ, 1)
7748730Siwasaki	EVENT_CONFIG_INITIALIZER(USERSUSPENDREQ, 1)
7848730Siwasaki	EVENT_CONFIG_INITIALIZER(STANDBYRESUME, 0)
7948730Siwasaki	EVENT_CONFIG_INITIALIZER(CAPABILITIESCHANGE, 0)
8048730Siwasaki};
8148730Siwasaki
8248730Siwasaki/*
8376611Snsayer * List of battery events
8476611Snsayer */
8576611Snsayerstruct battery_watch_event *battery_watch_list = NULL;
8676611Snsayer
8776611Snsayer#define BATT_CHK_INTV 10 /* how many seconds between battery state checks? */
8876611Snsayer
8976611Snsayer/*
9048730Siwasaki * default procedure
9148730Siwasaki */
9248730Siwasakistruct event_cmd *
9348730Siwasakievent_cmd_default_clone(void *this)
9448730Siwasaki{
9548730Siwasaki	struct event_cmd * oldone = this;
9648730Siwasaki	struct event_cmd * newone = malloc(oldone->len);
9748730Siwasaki
9848730Siwasaki	newone->next = NULL;
9948730Siwasaki	newone->len = oldone->len;
10048730Siwasaki	newone->name = oldone->name;
10148730Siwasaki	newone->op = oldone->op;
10248730Siwasaki	return newone;
10348730Siwasaki}
10448730Siwasaki
10548730Siwasaki/*
10648730Siwasaki * exec command
10748730Siwasaki */
10848730Siwasakiint
10948730Siwasakievent_cmd_exec_act(void *this)
11048730Siwasaki{
11148730Siwasaki	struct event_cmd_exec * p = this;
11248730Siwasaki	int status = -1;
11348730Siwasaki	pid_t pid;
11448730Siwasaki
11548730Siwasaki	switch ((pid = fork())) {
11648730Siwasaki	case -1:
117208075Suqs		warn("cannot fork");
118208289Suqs		break;
11948730Siwasaki	case 0:
12048730Siwasaki		/* child process */
121116668Smdodd		signal(SIGHUP, SIG_DFL);
122116668Smdodd		signal(SIGCHLD, SIG_DFL);
123116668Smdodd		signal(SIGTERM, SIG_DFL);
124208289Suqs		execl(_PATH_BSHELL, "sh", "-c", p->line, (char *)NULL);
12548730Siwasaki		_exit(127);
12648730Siwasaki	default:
12748730Siwasaki		/* parent process */
12848730Siwasaki		do {
12948730Siwasaki			pid = waitpid(pid, &status, 0);
13048730Siwasaki		} while (pid == -1 && errno == EINTR);
13148730Siwasaki		break;
13248730Siwasaki	}
13348730Siwasaki	return status;
13448730Siwasaki}
13548730Siwasakivoid
13648730Siwasakievent_cmd_exec_dump(void *this, FILE *fp)
13748730Siwasaki{
13848730Siwasaki	fprintf(fp, " \"%s\"", ((struct event_cmd_exec *)this)->line);
13948730Siwasaki}
14048730Siwasakistruct event_cmd *
14148730Siwasakievent_cmd_exec_clone(void *this)
14248730Siwasaki{
14348730Siwasaki	struct event_cmd_exec * newone = (struct event_cmd_exec *) event_cmd_default_clone(this);
14448730Siwasaki	struct event_cmd_exec * oldone = this;
14548730Siwasaki
14648730Siwasaki	newone->evcmd.next = NULL;
14748730Siwasaki	newone->evcmd.len = oldone->evcmd.len;
14848730Siwasaki	newone->evcmd.name = oldone->evcmd.name;
14948730Siwasaki	newone->evcmd.op = oldone->evcmd.op;
15071277Sjedgar	if ((newone->line = strdup(oldone->line)) == NULL)
15171277Sjedgar		err(1, "out of memory");
15248730Siwasaki	return (struct event_cmd *) newone;
15348730Siwasaki}
15448730Siwasakivoid
15548730Siwasakievent_cmd_exec_free(void *this)
15648730Siwasaki{
15748730Siwasaki	free(((struct event_cmd_exec *)this)->line);
15848730Siwasaki}
15948730Siwasakistruct event_cmd_op event_cmd_exec_ops = {
16048730Siwasaki	event_cmd_exec_act,
16148730Siwasaki	event_cmd_exec_dump,
16248730Siwasaki	event_cmd_exec_clone,
16348730Siwasaki	event_cmd_exec_free
16448730Siwasaki};
16548730Siwasaki
16648730Siwasaki/*
167208289Suqs * reject command
16848730Siwasaki */
16948730Siwasakiint
170208075Suqsevent_cmd_reject_act(void *this __unused)
17148730Siwasaki{
172208289Suqs	int rc = 0;
17348730Siwasaki
17448730Siwasaki	if (ioctl(apmctl_fd, APMIO_REJECTLASTREQ, NULL)) {
17548730Siwasaki		syslog(LOG_NOTICE, "fail to reject\n");
176208289Suqs		rc = -1;
17748730Siwasaki	}
17848730Siwasaki	return rc;
17948730Siwasaki}
18048730Siwasakistruct event_cmd_op event_cmd_reject_ops = {
18148730Siwasaki	event_cmd_reject_act,
18248730Siwasaki	NULL,
18348730Siwasaki	event_cmd_default_clone,
18448730Siwasaki	NULL
18548730Siwasaki};
18648730Siwasaki
18748730Siwasaki/*
18848730Siwasaki * manipulate event_config
18948730Siwasaki */
19048730Siwasakistruct event_cmd *
19148730Siwasakiclone_event_cmd_list(struct event_cmd *p)
19248730Siwasaki{
19348730Siwasaki	struct event_cmd dummy;
19448730Siwasaki	struct event_cmd *q = &dummy;
19548730Siwasaki	for ( ;p; p = p->next) {
19648730Siwasaki		assert(p->op->clone);
19748730Siwasaki		if ((q->next = p->op->clone(p)) == NULL)
198208075Suqs			err(1, "out of memory");
19948730Siwasaki		q = q->next;
20048730Siwasaki	}
20148730Siwasaki	q->next = NULL;
20248730Siwasaki	return dummy.next;
20348730Siwasaki}
20448730Siwasakivoid
20548730Siwasakifree_event_cmd_list(struct event_cmd *p)
20648730Siwasaki{
20748730Siwasaki	struct event_cmd * q;
20848730Siwasaki	for ( ; p ; p = q) {
20948730Siwasaki		q = p->next;
21048730Siwasaki		if (p->op->free)
21148730Siwasaki			p->op->free(p);
21248730Siwasaki		free(p);
21348730Siwasaki	}
21448730Siwasaki}
21548730Siwasakiint
21676611Snsayerregister_battery_handlers(
21776611Snsayer	int level, int direction,
21876611Snsayer	struct event_cmd *cmdlist)
21976611Snsayer{
22076611Snsayer	/*
22176611Snsayer	 * level is negative if it's in "minutes", non-negative if
22276611Snsayer	 * percentage.
22376611Snsayer	 *
22476611Snsayer	 * direction =1 means we care about this level when charging,
22576611Snsayer	 * direction =-1 means we care about it when discharging.
22676611Snsayer	 */
22776611Snsayer	if (level>100) /* percentage > 100 */
22876611Snsayer		return -1;
22976611Snsayer	if (abs(direction) != 1) /* nonsense direction value */
23076611Snsayer		return -1;
23176611Snsayer
23276611Snsayer	if (cmdlist) {
23376611Snsayer		struct battery_watch_event *we;
23476611Snsayer
23576611Snsayer		if ((we = malloc(sizeof(struct battery_watch_event))) == NULL)
236208075Suqs			err(1, "out of memory");
23776611Snsayer
23876611Snsayer		we->next = battery_watch_list; /* starts at NULL */
23976611Snsayer		battery_watch_list = we;
24076611Snsayer		we->level = abs(level);
24176611Snsayer		we->type = (level<0)?BATTERY_MINUTES:BATTERY_PERCENT;
24276611Snsayer		we->direction = (direction<0)?BATTERY_DISCHARGING:
24376611Snsayer			BATTERY_CHARGING;
24476611Snsayer		we->done = 0;
24576611Snsayer		we->cmdlist = clone_event_cmd_list(cmdlist);
24676611Snsayer	}
24776611Snsayer	return 0;
24876611Snsayer}
24976611Snsayerint
25048730Siwasakiregister_apm_event_handlers(
25148730Siwasaki	bitstr_t bit_decl(evlist, EVENT_MAX),
25248730Siwasaki	struct event_cmd *cmdlist)
25348730Siwasaki{
25448730Siwasaki	if (cmdlist) {
25548730Siwasaki		bitstr_t bit_decl(tmp, EVENT_MAX);
25648730Siwasaki		memcpy(&tmp, evlist, bitstr_size(EVENT_MAX));
25748730Siwasaki
25848730Siwasaki		for (;;) {
25948730Siwasaki			int n;
26048730Siwasaki			struct event_cmd *p;
26148730Siwasaki			struct event_cmd *q;
26248730Siwasaki			bit_ffs(tmp, EVENT_MAX, &n);
26348730Siwasaki			if (n < 0)
26448730Siwasaki				break;
26548730Siwasaki			p = events[n].cmdlist;
26648730Siwasaki			if ((q = clone_event_cmd_list(cmdlist)) == NULL)
267208075Suqs				err(1, "out of memory");
26848730Siwasaki			if (p) {
26948730Siwasaki				while (p->next != NULL)
27048730Siwasaki					p = p->next;
27148730Siwasaki				p->next = q;
27248730Siwasaki			} else {
27348730Siwasaki				events[n].cmdlist = q;
27448730Siwasaki			}
27548730Siwasaki			bit_clear(tmp, n);
27648730Siwasaki		}
27748730Siwasaki	}
27848730Siwasaki	return 0;
27948730Siwasaki}
28048730Siwasaki
28148730Siwasaki/*
28248730Siwasaki * execute command
28348730Siwasaki */
28448730Siwasakiint
28576611Snsayerexec_run_cmd(struct event_cmd *p)
28648730Siwasaki{
28748730Siwasaki	int status = 0;
28848730Siwasaki
28948730Siwasaki	for (; p; p = p->next) {
29048730Siwasaki		assert(p->op->act);
29148730Siwasaki		if (verbose)
29248730Siwasaki			syslog(LOG_INFO, "action: %s", p->name);
29348730Siwasaki		status = p->op->act(p);
29448730Siwasaki		if (status) {
29548730Siwasaki			syslog(LOG_NOTICE, "command finished with %d\n", status);
29648730Siwasaki			break;
29748730Siwasaki		}
29848730Siwasaki	}
29948730Siwasaki	return status;
30048730Siwasaki}
30148730Siwasaki
30248730Siwasaki/*
30376611Snsayer * execute command -- the event version
30476611Snsayer */
30576611Snsayerint
30676611Snsayerexec_event_cmd(struct event_config *ev)
30776611Snsayer{
30876611Snsayer	int status = 0;
30976611Snsayer
31076611Snsayer	status = exec_run_cmd(ev->cmdlist);
31176611Snsayer	if (status && ev->rejectable) {
31276611Snsayer		syslog(LOG_ERR, "canceled");
313208075Suqs		event_cmd_reject_act(NULL);
31476611Snsayer	}
31576611Snsayer	return status;
31676611Snsayer}
31776611Snsayer
31876611Snsayer/*
31948730Siwasaki * read config file
32048730Siwasaki */
32148730Siwasakiextern FILE * yyin;
32248730Siwasakiextern int yydebug;
32348730Siwasaki
32448730Siwasakivoid
32548730Siwasakiread_config(void)
32648730Siwasaki{
32748730Siwasaki	int i;
32848730Siwasaki
32948730Siwasaki	if ((yyin = fopen(apmd_configfile, "r")) == NULL) {
330208075Suqs		err(1, "cannot open config file");
33148730Siwasaki	}
33248730Siwasaki
33348730Siwasaki#ifdef DEBUG
33448730Siwasaki	yydebug = debug_level;
33548730Siwasaki#endif
33648730Siwasaki
33748730Siwasaki	if (yyparse() != 0)
338208075Suqs		err(1, "cannot parse config file");
33948730Siwasaki
34048730Siwasaki	fclose(yyin);
34148730Siwasaki
34248730Siwasaki	/* enable events */
34348730Siwasaki	for (i = 0; i < EVENT_MAX; i++) {
34448730Siwasaki		if (events[i].cmdlist) {
34548730Siwasaki			u_int event_type = i;
34648730Siwasaki			if (write(apmctl_fd, &event_type, sizeof(u_int)) == -1) {
347208075Suqs				err(1, "cannot enable event 0x%x", event_type);
34848730Siwasaki			}
34948730Siwasaki		}
35048730Siwasaki	}
35148730Siwasaki}
35248730Siwasaki
35348730Siwasakivoid
354208075Suqsdump_config(void)
35548730Siwasaki{
35648730Siwasaki	int i;
35776611Snsayer	struct battery_watch_event *q;
35848730Siwasaki
35948730Siwasaki	for (i = 0; i < EVENT_MAX; i++) {
36048730Siwasaki		struct event_cmd * p;
36148730Siwasaki		if ((p = events[i].cmdlist)) {
36248730Siwasaki			fprintf(stderr, "apm_event %s {\n", events[i].name);
36348730Siwasaki			for ( ; p ; p = p->next) {
36448730Siwasaki				fprintf(stderr, "\t%s", p->name);
36548730Siwasaki				if (p->op->dump)
36648730Siwasaki					p->op->dump(p, stderr);
36748730Siwasaki				fprintf(stderr, ";\n");
36848730Siwasaki			}
36948730Siwasaki			fprintf(stderr, "}\n");
37048730Siwasaki		}
37148730Siwasaki	}
37276611Snsayer	for (q = battery_watch_list ; q != NULL ; q = q -> next) {
37376611Snsayer		struct event_cmd * p;
37476611Snsayer		fprintf(stderr, "apm_battery %d%s %s {\n",
37576611Snsayer			q -> level,
37676611Snsayer			(q -> type == BATTERY_PERCENT)?"%":"m",
37776611Snsayer			(q -> direction == BATTERY_CHARGING)?"charging":
37876611Snsayer				"discharging");
37976611Snsayer		for ( p = q -> cmdlist; p ; p = p->next) {
38076611Snsayer			fprintf(stderr, "\t%s", p->name);
38176611Snsayer			if (p->op->dump)
38276611Snsayer				p->op->dump(p, stderr);
38376611Snsayer			fprintf(stderr, ";\n");
38476611Snsayer		}
38576611Snsayer		fprintf(stderr, "}\n");
38676611Snsayer	}
38748730Siwasaki}
38848730Siwasaki
38948730Siwasakivoid
390208075Suqsdestroy_config(void)
39148730Siwasaki{
39248730Siwasaki	int i;
39376611Snsayer	struct battery_watch_event *q;
39448730Siwasaki
39548730Siwasaki	/* disable events */
39648730Siwasaki	for (i = 0; i < EVENT_MAX; i++) {
39748730Siwasaki		if (events[i].cmdlist) {
39848730Siwasaki			u_int event_type = i;
39948730Siwasaki			if (write(apmctl_fd, &event_type, sizeof(u_int)) == -1) {
400208075Suqs				err(1, "cannot disable event 0x%x", event_type);
40148730Siwasaki			}
40248730Siwasaki		}
40348730Siwasaki	}
40448730Siwasaki
40548730Siwasaki	for (i = 0; i < EVENT_MAX; i++) {
40648730Siwasaki		struct event_cmd * p;
40748730Siwasaki		if ((p = events[i].cmdlist))
40848730Siwasaki			free_event_cmd_list(p);
40948730Siwasaki		events[i].cmdlist = NULL;
41048730Siwasaki	}
41176611Snsayer
41276611Snsayer	for( ; battery_watch_list; battery_watch_list = battery_watch_list -> next) {
41376611Snsayer		free_event_cmd_list(battery_watch_list->cmdlist);
41476611Snsayer		q = battery_watch_list->next;
41576611Snsayer		free(battery_watch_list);
41676611Snsayer		battery_watch_list = q;
41776611Snsayer	}
41848730Siwasaki}
41948730Siwasaki
42048730Siwasakivoid
421208075Suqsrestart(void)
42248730Siwasaki{
42348730Siwasaki	destroy_config();
42448730Siwasaki	read_config();
42548730Siwasaki	if (verbose)
42648730Siwasaki		dump_config();
42748730Siwasaki}
42848730Siwasaki
42948730Siwasaki/*
43048730Siwasaki * write pid file
43148730Siwasaki */
43248730Siwasakistatic void
433208075Suqswrite_pid(void)
43448730Siwasaki{
43548730Siwasaki	FILE *fp = fopen(apmd_pidfile, "w");
43648730Siwasaki
43748730Siwasaki	if (fp) {
438212048Skevlo		fprintf(fp, "%ld\n", (long)getpid());
43948730Siwasaki		fclose(fp);
44048730Siwasaki	}
44148730Siwasaki}
44248730Siwasaki
44348730Siwasaki/*
44448730Siwasaki * handle signals
44548730Siwasaki */
44648730Siwasakistatic int signal_fd[2];
44748730Siwasaki
44848730Siwasakivoid
44948730Siwasakienque_signal(int sig)
45048730Siwasaki{
45148730Siwasaki	if (write(signal_fd[1], &sig, sizeof sig) != sizeof sig)
452208075Suqs		err(1, "cannot process signal.");
45348730Siwasaki}
45448730Siwasaki
45548730Siwasakivoid
456208075Suqswait_child(void)
45748730Siwasaki{
45848730Siwasaki	int status;
45948730Siwasaki	while (waitpid(-1, &status, WNOHANG) > 0)
46048730Siwasaki		;
46148730Siwasaki}
46248730Siwasaki
46348730Siwasakiint
46448730Siwasakiproc_signal(int fd)
46548730Siwasaki{
466208289Suqs	int rc = 0;
46748730Siwasaki	int sig;
46848730Siwasaki
46948730Siwasaki	while (read(fd, &sig, sizeof sig) == sizeof sig) {
47048730Siwasaki		syslog(LOG_INFO, "caught signal: %d", sig);
47148730Siwasaki		switch (sig) {
47248730Siwasaki		case SIGHUP:
47348730Siwasaki			syslog(LOG_NOTICE, "restart by SIG");
47448730Siwasaki			restart();
47548730Siwasaki			break;
47648730Siwasaki		case SIGTERM:
47748730Siwasaki			syslog(LOG_NOTICE, "going down on signal %d", sig);
478116666Smdodd			rc = -1;
479208289Suqs			return rc;
48048730Siwasaki		case SIGCHLD:
48148730Siwasaki			wait_child();
48248730Siwasaki			break;
48348730Siwasaki		default:
484208075Suqs			warn("unexpected signal(%d) received.", sig);
48548730Siwasaki			break;
48648730Siwasaki		}
48748730Siwasaki	}
48848730Siwasaki	return rc;
48948730Siwasaki}
49048730Siwasakivoid
49148730Siwasakiproc_apmevent(int fd)
49248730Siwasaki{
49348730Siwasaki	struct apm_event_info apmevent;
49448730Siwasaki
49548730Siwasaki	while (ioctl(fd, APMIO_NEXTEVENT, &apmevent) == 0) {
49648730Siwasaki		int status;
49748730Siwasaki		syslog(LOG_NOTICE, "apmevent %04x index %d\n",
49848730Siwasaki			apmevent.type, apmevent.index);
49948730Siwasaki		syslog(LOG_INFO, "apm event: %s", events[apmevent.type].name);
50048730Siwasaki		if (fork() == 0) {
50148730Siwasaki			status = exec_event_cmd(&events[apmevent.type]);
50248730Siwasaki			exit(status);
50348730Siwasaki		}
50448730Siwasaki	}
50548730Siwasaki}
50676611Snsayer
50776611Snsayer#define AC_POWER_STATE ((pw_info.ai_acline == 1) ? BATTERY_CHARGING :\
50876611Snsayer	BATTERY_DISCHARGING)
50976611Snsayer
51048730Siwasakivoid
511208075Suqscheck_battery(void)
51276611Snsayer{
51376611Snsayer
51476611Snsayer	static int first_time=1, last_state;
515116666Smdodd	int status;
51676611Snsayer
51776611Snsayer	struct apm_info pw_info;
51876611Snsayer	struct battery_watch_event *p;
51976611Snsayer
52076611Snsayer	/* If we don't care, don't bother */
52176611Snsayer	if (battery_watch_list == NULL)
52276611Snsayer		return;
52376611Snsayer
52476611Snsayer	if (first_time) {
52576611Snsayer		if ( ioctl(apmnorm_fd, APMIO_GETINFO, &pw_info) < 0)
526208075Suqs			err(1, "cannot check battery state.");
52776611Snsayer/*
52876611Snsayer * This next statement isn't entirely true. The spec does not tie AC
52976611Snsayer * line state to battery charging or not, but this is a bit lazier to do.
53076611Snsayer */
53176611Snsayer		last_state = AC_POWER_STATE;
53276611Snsayer		first_time = 0;
53376611Snsayer		return; /* We can't process events, we have no baseline */
53476611Snsayer	}
53576611Snsayer
53676611Snsayer	/*
53776611Snsayer	 * XXX - should we do this a bunch of times and perform some sort
53876611Snsayer	 * of smoothing or correction?
53976611Snsayer	 */
54076611Snsayer	if ( ioctl(apmnorm_fd, APMIO_GETINFO, &pw_info) < 0)
541208075Suqs		err(1, "cannot check battery state.");
54276611Snsayer
54376611Snsayer	/*
54476611Snsayer	 * If we're not in the state now that we were in last time,
54576611Snsayer	 * then it's a transition, which means we must clean out
54676611Snsayer	 * the event-caught state.
54776611Snsayer	 */
54876611Snsayer	if (last_state != AC_POWER_STATE) {
549116666Smdodd		if (soft_power_state_change && fork() == 0) {
550116666Smdodd			status = exec_event_cmd(&events[PMEV_POWERSTATECHANGE]);
551116666Smdodd			exit(status);
552116666Smdodd		}
55376611Snsayer		last_state = AC_POWER_STATE;
55476611Snsayer		for (p = battery_watch_list ; p!=NULL ; p = p -> next)
55576611Snsayer			p->done = 0;
55676611Snsayer	}
55776611Snsayer	for (p = battery_watch_list ; p != NULL ; p = p -> next)
55876611Snsayer		if (p -> direction == AC_POWER_STATE &&
55976611Snsayer			!(p -> done) &&
56076611Snsayer			((p -> type == BATTERY_PERCENT &&
561208075Suqs				p -> level == (int)pw_info.ai_batt_life) ||
56276611Snsayer			(p -> type == BATTERY_MINUTES &&
56376611Snsayer				p -> level == (pw_info.ai_batt_time / 60)))) {
56476611Snsayer			p -> done++;
56576611Snsayer			if (verbose)
56676611Snsayer				syslog(LOG_NOTICE, "Caught battery event: %s, %d%s",
56776611Snsayer					(p -> direction == BATTERY_CHARGING)?"charging":"discharging",
56876611Snsayer					p -> level,
56976611Snsayer					(p -> type == BATTERY_PERCENT)?"%":" minutes");
57076611Snsayer			if (fork() == 0) {
57176611Snsayer				status = exec_run_cmd(p -> cmdlist);
57276611Snsayer				exit(status);
57376611Snsayer			}
57476611Snsayer		}
57576611Snsayer}
57676611Snsayervoid
57748730Siwasakievent_loop(void)
57848730Siwasaki{
57948730Siwasaki	int		fdmax = 0;
58048730Siwasaki	struct sigaction nsa;
58148730Siwasaki	fd_set          master_rfds;
58248730Siwasaki	sigset_t	sigmask, osigmask;
58348730Siwasaki
58448730Siwasaki	FD_ZERO(&master_rfds);
58548730Siwasaki	FD_SET(apmctl_fd, &master_rfds);
58648730Siwasaki	fdmax = apmctl_fd > fdmax ? apmctl_fd : fdmax;
58748730Siwasaki
58848730Siwasaki	FD_SET(signal_fd[0], &master_rfds);
58948730Siwasaki	fdmax = signal_fd[0] > fdmax ? signal_fd[0] : fdmax;
59048730Siwasaki
59148730Siwasaki	memset(&nsa, 0, sizeof nsa);
59248730Siwasaki	nsa.sa_handler = enque_signal;
59348730Siwasaki	sigfillset(&nsa.sa_mask);
59448730Siwasaki	nsa.sa_flags = SA_RESTART;
59548730Siwasaki	sigaction(SIGHUP, &nsa, NULL);
59648730Siwasaki	sigaction(SIGCHLD, &nsa, NULL);
59748730Siwasaki	sigaction(SIGTERM, &nsa, NULL);
59848730Siwasaki
59948730Siwasaki	sigemptyset(&sigmask);
60048730Siwasaki	sigaddset(&sigmask, SIGHUP);
60148730Siwasaki	sigaddset(&sigmask, SIGCHLD);
60248730Siwasaki	sigaddset(&sigmask, SIGTERM);
60348730Siwasaki	sigprocmask(SIG_SETMASK, &sigmask, &osigmask);
60448730Siwasaki
60548730Siwasaki	while (1) {
60648730Siwasaki		fd_set rfds;
60776611Snsayer		int res;
60876611Snsayer		struct timeval to;
60948730Siwasaki
61076611Snsayer		to.tv_sec = BATT_CHK_INTV;
61176611Snsayer		to.tv_usec = 0;
61276611Snsayer
61348730Siwasaki		memcpy(&rfds, &master_rfds, sizeof rfds);
61448730Siwasaki		sigprocmask(SIG_SETMASK, &osigmask, NULL);
61576611Snsayer		if ((res=select(fdmax + 1, &rfds, 0, 0, &to)) < 0) {
61648730Siwasaki			if (errno != EINTR)
617208075Suqs				err(1, "select");
61848730Siwasaki		}
61948730Siwasaki		sigprocmask(SIG_SETMASK, &sigmask, NULL);
62048730Siwasaki
62176611Snsayer		if (res == 0) { /* time to check the battery */
62276611Snsayer			check_battery();
62376611Snsayer			continue;
62476611Snsayer		}
62576611Snsayer
62648730Siwasaki		if (FD_ISSET(signal_fd[0], &rfds)) {
62748730Siwasaki			if (proc_signal(signal_fd[0]) < 0)
628208289Suqs				return;
62948730Siwasaki		}
63076611Snsayer
63148730Siwasaki		if (FD_ISSET(apmctl_fd, &rfds))
63248730Siwasaki			proc_apmevent(apmctl_fd);
63348730Siwasaki	}
63448730Siwasaki}
63548730Siwasaki
63651287Speterint
63748730Siwasakimain(int ac, char* av[])
63848730Siwasaki{
63948730Siwasaki	int	ch;
64048730Siwasaki	int	daemonize = 1;
64148730Siwasaki	char	*prog;
64248730Siwasaki	int	logopt = LOG_NDELAY | LOG_PID;
64348730Siwasaki
644166509Skevlo	while ((ch = getopt(ac, av, "df:sv")) != -1) {
64548730Siwasaki		switch (ch) {
64648730Siwasaki		case 'd':
64748730Siwasaki			daemonize = 0;
64848730Siwasaki			debug_level++;
64948730Siwasaki			break;
65048730Siwasaki		case 'f':
65148730Siwasaki			apmd_configfile = optarg;
65248730Siwasaki			break;
653116666Smdodd		case 's':
654116666Smdodd			soft_power_state_change = 1;
655116666Smdodd			break;
65648730Siwasaki		case 'v':
65748730Siwasaki			verbose = 1;
65848730Siwasaki			break;
65948730Siwasaki		default:
660208075Suqs			err(1, "unknown option `%c'", ch);
66148730Siwasaki		}
66248730Siwasaki	}
66348730Siwasaki
66448730Siwasaki	if (daemonize)
66548730Siwasaki		daemon(0, 0);
66648730Siwasaki
66748730Siwasaki#ifdef NICE_INCR
668208075Suqs	nice(NICE_INCR);
66948730Siwasaki#endif
67048730Siwasaki
67148730Siwasaki	if (!daemonize)
67248730Siwasaki		logopt |= LOG_PERROR;
67348730Siwasaki
67448730Siwasaki	prog = strrchr(av[0], '/');
67548730Siwasaki	openlog(prog ? prog+1 : av[0], logopt, LOG_DAEMON);
67648730Siwasaki
67748730Siwasaki	syslog(LOG_NOTICE, "start");
67848730Siwasaki
67948730Siwasaki	if (pipe(signal_fd) < 0)
680208075Suqs		err(1, "pipe");
68148730Siwasaki	if (fcntl(signal_fd[0], F_SETFL, O_NONBLOCK) < 0)
682208075Suqs		err(1, "fcntl");
68348730Siwasaki
68476611Snsayer	if ((apmnorm_fd = open(APM_NORM_DEVICEFILE, O_RDWR)) == -1) {
685208075Suqs		err(1, "cannot open device file `%s'", APM_NORM_DEVICEFILE);
68676611Snsayer	}
68776611Snsayer
688116668Smdodd	if (fcntl(apmnorm_fd, F_SETFD, 1) == -1) {
689208075Suqs		err(1, "cannot set close-on-exec flag for device file '%s'", APM_NORM_DEVICEFILE);
690116668Smdodd	}
691116668Smdodd
69248730Siwasaki	if ((apmctl_fd = open(APM_CTL_DEVICEFILE, O_RDWR)) == -1) {
693208075Suqs		err(1, "cannot open device file `%s'", APM_CTL_DEVICEFILE);
69448730Siwasaki	}
69548730Siwasaki
696116668Smdodd	if (fcntl(apmctl_fd, F_SETFD, 1) == -1) {
697208075Suqs		err(1, "cannot set close-on-exec flag for device file '%s'", APM_CTL_DEVICEFILE);
698208075Suqs	}
699116668Smdodd
70048730Siwasaki	restart();
70148730Siwasaki	write_pid();
70248730Siwasaki	event_loop();
703208075Suqs	exit(EXIT_SUCCESS);
70448730Siwasaki}
70548730Siwasaki
706