diff options
| -rw-r--r-- | usr.sbin/apmd/apmd.c | 173 | ||||
| -rw-r--r-- | usr.sbin/apmd/apmd.h | 21 | ||||
| -rw-r--r-- | usr.sbin/apmd/apmdlex.l | 13 | ||||
| -rw-r--r-- | usr.sbin/apmd/apmdparse.y | 39 | 
4 files changed, 238 insertions, 8 deletions
diff --git a/usr.sbin/apmd/apmd.c b/usr.sbin/apmd/apmd.c index 470bcc6481e8..a285b9b2dad8 100644 --- a/usr.sbin/apmd/apmd.c +++ b/usr.sbin/apmd/apmd.c @@ -58,7 +58,7 @@ int		debug_level = 0;  int		verbose = 0;  const char	*apmd_configfile = APMD_CONFIGFILE;  const char	*apmd_pidfile = APMD_PIDFILE; -int             apmctl_fd = -1; +int             apmctl_fd = -1, apmnorm_fd = -1;  /*   * table of event handlers @@ -81,6 +81,13 @@ struct event_config events[EVENT_MAX] = {  };  /* + * List of battery events + */ +struct battery_watch_event *battery_watch_list = NULL; + +#define BATT_CHK_INTV 10 /* how many seconds between battery state checks? */ + +/*   * default procedure   */  struct event_cmd * @@ -207,6 +214,40 @@ free_event_cmd_list(struct event_cmd *p)  	}  }  int +register_battery_handlers( +	int level, int direction, +	struct event_cmd *cmdlist) +{ +	/* +	 * level is negative if it's in "minutes", non-negative if +	 * percentage. +	 * +	 * direction =1 means we care about this level when charging, +	 * direction =-1 means we care about it when discharging. +	 */ +	if (level>100) /* percentage > 100 */ +		return -1; +	if (abs(direction) != 1) /* nonsense direction value */ +		return -1; + +	if (cmdlist) { +		struct battery_watch_event *we; +		 +		if ((we = malloc(sizeof(struct battery_watch_event))) == NULL) +			(void) err(1, "out of memory"); + +		we->next = battery_watch_list; /* starts at NULL */ +		battery_watch_list = we; +		we->level = abs(level); +		we->type = (level<0)?BATTERY_MINUTES:BATTERY_PERCENT; +		we->direction = (direction<0)?BATTERY_DISCHARGING: +			BATTERY_CHARGING; +		we->done = 0; +		we->cmdlist = clone_event_cmd_list(cmdlist); +	} +	return 0; +} +int  register_apm_event_handlers(  	bitstr_t bit_decl(evlist, EVENT_MAX),  	struct event_cmd *cmdlist) @@ -242,11 +283,10 @@ register_apm_event_handlers(   * execute command   */  int -exec_event_cmd(struct event_config *ev) +exec_run_cmd(struct event_cmd *p)  {  	int status = 0; -	struct event_cmd *p = ev->cmdlist;  	for (; p; p = p->next) {  		assert(p->op->act);  		if (verbose) @@ -254,10 +294,6 @@ exec_event_cmd(struct event_config *ev)  		status = p->op->act(p);  		if (status) {  			syslog(LOG_NOTICE, "command finished with %d\n", status); -			if (ev->rejectable) { -				syslog(LOG_ERR, "canceled"); -				(void) event_cmd_reject_act(NULL); -			}  			break;  		}  	} @@ -265,6 +301,22 @@ exec_event_cmd(struct event_config *ev)  }  /* + * execute command -- the event version + */ +int +exec_event_cmd(struct event_config *ev) +{ +	int status = 0; + +	status = exec_run_cmd(ev->cmdlist); +	if (status && ev->rejectable) { +		syslog(LOG_ERR, "canceled"); +		(void) event_cmd_reject_act(NULL); +	} +	return status; +} + +/*   * read config file   */  extern FILE * yyin; @@ -303,6 +355,7 @@ void  dump_config()  {  	int i; +	struct battery_watch_event *q;  	for (i = 0; i < EVENT_MAX; i++) {  		struct event_cmd * p; @@ -317,12 +370,28 @@ dump_config()  			fprintf(stderr, "}\n");  		}  	} +	for (q = battery_watch_list ; q != NULL ; q = q -> next) { +		struct event_cmd * p; +		fprintf(stderr, "apm_battery %d%s %s {\n", +			q -> level, +			(q -> type == BATTERY_PERCENT)?"%":"m", +			(q -> direction == BATTERY_CHARGING)?"charging": +				"discharging"); +		for ( p = q -> cmdlist; p ; p = p->next) { +			fprintf(stderr, "\t%s", p->name); +			if (p->op->dump) +				p->op->dump(p, stderr); +			fprintf(stderr, ";\n"); +		} +		fprintf(stderr, "}\n"); +	}  }  void  destroy_config()  {  	int i; +	struct battery_watch_event *q;  	/* disable events */  	for (i = 0; i < EVENT_MAX; i++) { @@ -340,6 +409,13 @@ destroy_config()  			free_event_cmd_list(p);  		events[i].cmdlist = NULL;  	} + +	for( ; battery_watch_list; battery_watch_list = battery_watch_list -> next) { +		free_event_cmd_list(battery_watch_list->cmdlist); +		q = battery_watch_list->next; +		free(battery_watch_list); +		battery_watch_list = q; +	}  }  void @@ -430,6 +506,72 @@ proc_apmevent(int fd)  		}  	}  } + +#define AC_POWER_STATE ((pw_info.ai_acline == 1) ? BATTERY_CHARGING :\ +	BATTERY_DISCHARGING) + +void +check_battery() +{ + +	static int first_time=1, last_state; + +	struct apm_info pw_info; +	struct battery_watch_event *p; + +	/* If we don't care, don't bother */ +	if (battery_watch_list == NULL) +		return; + +	if (first_time) { +		if ( ioctl(apmnorm_fd, APMIO_GETINFO, &pw_info) < 0) +			(void) err(1, "cannot check battery state."); +/* + * This next statement isn't entirely true. The spec does not tie AC + * line state to battery charging or not, but this is a bit lazier to do. + */ +		last_state = AC_POWER_STATE; +		first_time = 0; +		return; /* We can't process events, we have no baseline */ +	} + +	/* +	 * XXX - should we do this a bunch of times and perform some sort +	 * of smoothing or correction? +	 */ +	if ( ioctl(apmnorm_fd, APMIO_GETINFO, &pw_info) < 0) +		(void) err(1, "cannot check battery state."); + +	/* +	 * If we're not in the state now that we were in last time, +	 * then it's a transition, which means we must clean out +	 * the event-caught state. +	 */ +	if (last_state != AC_POWER_STATE) { +		last_state = AC_POWER_STATE; +		for (p = battery_watch_list ; p!=NULL ; p = p -> next) +			p->done = 0; +	} +	for (p = battery_watch_list ; p != NULL ; p = p -> next) +		if (p -> direction == AC_POWER_STATE && +			!(p -> done) && +			((p -> type == BATTERY_PERCENT &&  +				p -> level == pw_info.ai_batt_life) || +			(p -> type == BATTERY_MINUTES && +				p -> level == (pw_info.ai_batt_time / 60)))) { +			p -> done++; +			if (verbose) +				syslog(LOG_NOTICE, "Caught battery event: %s, %d%s", +					(p -> direction == BATTERY_CHARGING)?"charging":"discharging", +					p -> level, +					(p -> type == BATTERY_PERCENT)?"%":" minutes"); +			if (fork() == 0) { +				int status; +				status = exec_run_cmd(p -> cmdlist); +				exit(status); +			} +		} +}  void  event_loop(void)  { @@ -461,19 +603,30 @@ event_loop(void)  	while (1) {  		fd_set rfds; +		int res; +		struct timeval to; + +		to.tv_sec = BATT_CHK_INTV; +		to.tv_usec = 0;  		memcpy(&rfds, &master_rfds, sizeof rfds);  		sigprocmask(SIG_SETMASK, &osigmask, NULL); -		if (select(fdmax + 1, &rfds, 0, 0, 0) < 0) { +		if ((res=select(fdmax + 1, &rfds, 0, 0, &to)) < 0) {  			if (errno != EINTR)  				(void) err(1, "select");  		}  		sigprocmask(SIG_SETMASK, &sigmask, NULL); +		if (res == 0) { /* time to check the battery */ +			check_battery(); +			continue; +		} +  		if (FD_ISSET(signal_fd[0], &rfds)) {  			if (proc_signal(signal_fd[0]) < 0)  				goto out;  		} +  		if (FD_ISSET(apmctl_fd, &rfds))  			proc_apmevent(apmctl_fd);  	} @@ -526,6 +679,10 @@ main(int ac, char* av[])  	if (fcntl(signal_fd[0], F_SETFL, O_NONBLOCK) < 0)  		(void) err(1, "fcntl"); +	if ((apmnorm_fd = open(APM_NORM_DEVICEFILE, O_RDWR)) == -1) { +		(void) err(1, "cannot open device file `%s'", APM_NORM_DEVICEFILE); +	} +  	if ((apmctl_fd = open(APM_CTL_DEVICEFILE, O_RDWR)) == -1) {  		(void) err(1, "cannot open device file `%s'", APM_CTL_DEVICEFILE);  	} diff --git a/usr.sbin/apmd/apmd.h b/usr.sbin/apmd/apmd.h index e45b84a2b17c..b716d0d15c18 100644 --- a/usr.sbin/apmd/apmd.h +++ b/usr.sbin/apmd/apmd.h @@ -31,6 +31,7 @@  #define APMD_CONFIGFILE		"/etc/apmd.conf"  #define APM_CTL_DEVICEFILE	"/dev/apmctl" +#define APM_NORM_DEVICEFILE	"/dev/apm"  #define APMD_PIDFILE		"/var/run/apmd.pid"  #define NICE_INCR		-20 @@ -77,10 +78,30 @@ struct event_config {  	int rejectable;  }; +struct battery_watch_event { +	struct battery_watch_event *next; +	int level; +	enum { +		BATTERY_CHARGING, +		BATTERY_DISCHARGING +	} direction; +	enum { +		BATTERY_MINUTES, +		BATTERY_PERCENT +	} type; +	int done; +	struct event_cmd *cmdlist; +}; + +	  extern struct event_cmd_op event_cmd_exec_ops;  extern struct event_cmd_op event_cmd_reject_ops;  extern struct event_config events[EVENT_MAX]; +extern struct battery_watch_event *battery_watch_list; +extern int register_battery_handlers( +	int level, int direction, +	struct event_cmd *cmdlist);  extern int register_apm_event_handlers(  	bitstr_t bit_decl(evlist, EVENT_MAX),  	struct event_cmd *cmdlist); diff --git a/usr.sbin/apmd/apmdlex.l b/usr.sbin/apmd/apmdlex.l index 34598bb927b6..0e5cff30036e 100644 --- a/usr.sbin/apmd/apmdlex.l +++ b/usr.sbin/apmd/apmdlex.l @@ -74,6 +74,19 @@ int first_time;  <TOP>STANDBYRESUME	{ yylval.ev = EVENT_STANDBYRESUME; return EVENT; }  <TOP>CAPABILITIESCHANGE	{ yylval.ev = EVENT_CAPABILITIESCHANGE; return EVENT; } +<TOP>apm_battery	{ return APMBATT; } + +<TOP>charging		{ return BATTCHARGE; } +<TOP>discharging	{ return BATTDISCHARGE; } +<TOP>[0-9]+%		{ +				yylval.i = atoi(yytext); +				return BATTPERCENT; +			} +<TOP>[0-9]+[Mm]		{ +				yylval.i = -atoi(yytext); +				return BATTTIME; +			} +  <TOP>exec		{ return EXECCMD; }  <TOP>reject		{ return REJECTCMD; } diff --git a/usr.sbin/apmd/apmdparse.y b/usr.sbin/apmd/apmdparse.y index ce9c9dcce341..f0acea32b508 100644 --- a/usr.sbin/apmd/apmdparse.y +++ b/usr.sbin/apmd/apmdparse.y @@ -32,6 +32,7 @@  #include <stdio.h>  #include <bitstring.h> +#include <stdlib.h>  #include "apmd.h"  #ifdef DEBUG @@ -47,15 +48,21 @@ extern int first_time;  	bitstr_t bit_decl(evlist, EVENT_MAX);  	int ev;  	struct event_cmd * evcmd; +	int i;  }  %token BEGINBLOCK ENDBLOCK  %token COMMA SEMICOLON  %token APMEVENT +%token APMBATT +%token BATTCHARGE BATTDISCHARGE +%token <str> BATTTIME BATTPERCENT  %token EXECCMD REJECTCMD  %token <ev> EVENT  %token <str> STRING UNKNOWN +%type <i> apm_battery_level +%type <i> apm_battery_direction  %type <str> string  %type <str> unknown  %type <evlist> event_list @@ -76,6 +83,7 @@ config_list  config  	: apm_event_statement +	| apm_battery_statement  	;  apm_event_statement @@ -87,6 +95,37 @@ apm_event_statement  		}  	; +apm_battery_level +	: BATTPERCENT +		{ +			$$ = $1; +		} +	| BATTTIME +		{ +			$$ = $1; +		} +	; + +apm_battery_direction +	: BATTCHARGE +		{ +			$$ = 1; +		} +	| BATTDISCHARGE +		{ +			$$ = -1; +		} +	; +apm_battery_statement +	: APMBATT apm_battery_level apm_battery_direction +		BEGINBLOCK cmd_list ENDBLOCK +		{ +			if (register_battery_handlers($2, $3, $5) < 0) +				abort(); /* XXX */ +			free_event_cmd_list($5); +		} +	; +  event_list  	: EVENT  		{  | 
