Index: head/usr.sbin/apmd/apmd.c =================================================================== --- head/usr.sbin/apmd/apmd.c (revision 300556) +++ head/usr.sbin/apmd/apmd.c (revision 300557) @@ -1,705 +1,705 @@ /*- * APM (Advanced Power Management) Event Dispatcher * * Copyright (c) 1999 Mitsuru IWASAKI * Copyright (c) 1999 KOIE Hidetaka * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #ifndef lint static const char rcsid[] = "$FreeBSD$"; #endif /* not lint */ +#include #include #include #include #include #include #include #include #include #include #include #include #include #include -#include #include #include #include #include "apmd.h" int debug_level = 0; int verbose = 0; int soft_power_state_change = 0; const char *apmd_configfile = APMD_CONFIGFILE; const char *apmd_pidfile = APMD_PIDFILE; int apmctl_fd = -1, apmnorm_fd = -1; /* * table of event handlers */ #define EVENT_CONFIG_INITIALIZER(EV,R) { #EV, NULL, R }, struct event_config events[EVENT_MAX] = { EVENT_CONFIG_INITIALIZER(NOEVENT, 0) EVENT_CONFIG_INITIALIZER(STANDBYREQ, 1) EVENT_CONFIG_INITIALIZER(SUSPENDREQ, 1) EVENT_CONFIG_INITIALIZER(NORMRESUME, 0) EVENT_CONFIG_INITIALIZER(CRITRESUME, 0) EVENT_CONFIG_INITIALIZER(BATTERYLOW, 0) EVENT_CONFIG_INITIALIZER(POWERSTATECHANGE, 0) EVENT_CONFIG_INITIALIZER(UPDATETIME, 0) EVENT_CONFIG_INITIALIZER(CRITSUSPEND, 1) EVENT_CONFIG_INITIALIZER(USERSTANDBYREQ, 1) EVENT_CONFIG_INITIALIZER(USERSUSPENDREQ, 1) EVENT_CONFIG_INITIALIZER(STANDBYRESUME, 0) EVENT_CONFIG_INITIALIZER(CAPABILITIESCHANGE, 0) }; /* * 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 * event_cmd_default_clone(void *this) { struct event_cmd * oldone = this; struct event_cmd * newone = malloc(oldone->len); newone->next = NULL; newone->len = oldone->len; newone->name = oldone->name; newone->op = oldone->op; return newone; } /* * exec command */ int event_cmd_exec_act(void *this) { struct event_cmd_exec * p = this; int status = -1; pid_t pid; switch ((pid = fork())) { case -1: warn("cannot fork"); break; case 0: /* child process */ signal(SIGHUP, SIG_DFL); signal(SIGCHLD, SIG_DFL); signal(SIGTERM, SIG_DFL); execl(_PATH_BSHELL, "sh", "-c", p->line, (char *)NULL); _exit(127); default: /* parent process */ do { pid = waitpid(pid, &status, 0); } while (pid == -1 && errno == EINTR); break; } return status; } void event_cmd_exec_dump(void *this, FILE *fp) { fprintf(fp, " \"%s\"", ((struct event_cmd_exec *)this)->line); } struct event_cmd * event_cmd_exec_clone(void *this) { struct event_cmd_exec * newone = (struct event_cmd_exec *) event_cmd_default_clone(this); struct event_cmd_exec * oldone = this; newone->evcmd.next = NULL; newone->evcmd.len = oldone->evcmd.len; newone->evcmd.name = oldone->evcmd.name; newone->evcmd.op = oldone->evcmd.op; if ((newone->line = strdup(oldone->line)) == NULL) err(1, "out of memory"); return (struct event_cmd *) newone; } void event_cmd_exec_free(void *this) { free(((struct event_cmd_exec *)this)->line); } struct event_cmd_op event_cmd_exec_ops = { event_cmd_exec_act, event_cmd_exec_dump, event_cmd_exec_clone, event_cmd_exec_free }; /* * reject command */ int event_cmd_reject_act(void *this __unused) { int rc = 0; if (ioctl(apmctl_fd, APMIO_REJECTLASTREQ, NULL)) { syslog(LOG_NOTICE, "fail to reject\n"); rc = -1; } return rc; } struct event_cmd_op event_cmd_reject_ops = { event_cmd_reject_act, NULL, event_cmd_default_clone, NULL }; /* * manipulate event_config */ struct event_cmd * clone_event_cmd_list(struct event_cmd *p) { struct event_cmd dummy; struct event_cmd *q = &dummy; for ( ;p; p = p->next) { assert(p->op->clone); if ((q->next = p->op->clone(p)) == NULL) err(1, "out of memory"); q = q->next; } q->next = NULL; return dummy.next; } void free_event_cmd_list(struct event_cmd *p) { struct event_cmd * q; for ( ; p ; p = q) { q = p->next; if (p->op->free) p->op->free(p); free(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) 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) { if (cmdlist) { bitstr_t bit_decl(tmp, EVENT_MAX); memcpy(&tmp, evlist, bitstr_size(EVENT_MAX)); for (;;) { int n; struct event_cmd *p; struct event_cmd *q; bit_ffs(tmp, EVENT_MAX, &n); if (n < 0) break; p = events[n].cmdlist; if ((q = clone_event_cmd_list(cmdlist)) == NULL) err(1, "out of memory"); if (p) { while (p->next != NULL) p = p->next; p->next = q; } else { events[n].cmdlist = q; } bit_clear(tmp, n); } } return 0; } /* * execute command */ int exec_run_cmd(struct event_cmd *p) { int status = 0; for (; p; p = p->next) { assert(p->op->act); if (verbose) syslog(LOG_INFO, "action: %s", p->name); status = p->op->act(p); if (status) { syslog(LOG_NOTICE, "command finished with %d\n", status); break; } } return status; } /* * 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"); event_cmd_reject_act(NULL); } return status; } /* * read config file */ extern FILE * yyin; extern int yydebug; void read_config(void) { int i; if ((yyin = fopen(apmd_configfile, "r")) == NULL) { err(1, "cannot open config file"); } #ifdef DEBUG yydebug = debug_level; #endif if (yyparse() != 0) err(1, "cannot parse config file"); fclose(yyin); /* enable events */ for (i = 0; i < EVENT_MAX; i++) { if (events[i].cmdlist) { u_int event_type = i; if (write(apmctl_fd, &event_type, sizeof(u_int)) == -1) { err(1, "cannot enable event 0x%x", event_type); } } } } void dump_config(void) { int i; struct battery_watch_event *q; for (i = 0; i < EVENT_MAX; i++) { struct event_cmd * p; if ((p = events[i].cmdlist)) { fprintf(stderr, "apm_event %s {\n", events[i].name); for ( ; 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"); } } 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(void) { int i; struct battery_watch_event *q; /* disable events */ for (i = 0; i < EVENT_MAX; i++) { if (events[i].cmdlist) { u_int event_type = i; if (write(apmctl_fd, &event_type, sizeof(u_int)) == -1) { err(1, "cannot disable event 0x%x", event_type); } } } for (i = 0; i < EVENT_MAX; i++) { struct event_cmd * p; if ((p = events[i].cmdlist)) 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 restart(void) { destroy_config(); read_config(); if (verbose) dump_config(); } /* * write pid file */ static void write_pid(void) { FILE *fp = fopen(apmd_pidfile, "w"); if (fp) { fprintf(fp, "%ld\n", (long)getpid()); fclose(fp); } } /* * handle signals */ static int signal_fd[2]; void enque_signal(int sig) { if (write(signal_fd[1], &sig, sizeof sig) != sizeof sig) err(1, "cannot process signal."); } void wait_child(void) { int status; while (waitpid(-1, &status, WNOHANG) > 0) ; } int proc_signal(int fd) { int rc = 0; int sig; while (read(fd, &sig, sizeof sig) == sizeof sig) { syslog(LOG_INFO, "caught signal: %d", sig); switch (sig) { case SIGHUP: syslog(LOG_NOTICE, "restart by SIG"); restart(); break; case SIGTERM: syslog(LOG_NOTICE, "going down on signal %d", sig); rc = -1; return rc; case SIGCHLD: wait_child(); break; default: warn("unexpected signal(%d) received.", sig); break; } } return rc; } void proc_apmevent(int fd) { struct apm_event_info apmevent; while (ioctl(fd, APMIO_NEXTEVENT, &apmevent) == 0) { int status; syslog(LOG_NOTICE, "apmevent %04x index %d\n", apmevent.type, apmevent.index); syslog(LOG_INFO, "apm event: %s", events[apmevent.type].name); if (fork() == 0) { status = exec_event_cmd(&events[apmevent.type]); exit(status); } } } #define AC_POWER_STATE ((pw_info.ai_acline == 1) ? BATTERY_CHARGING :\ BATTERY_DISCHARGING) void check_battery(void) { static int first_time=1, last_state; int status; 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) 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) 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) { if (soft_power_state_change && fork() == 0) { status = exec_event_cmd(&events[PMEV_POWERSTATECHANGE]); exit(status); } 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 == (int)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) { status = exec_run_cmd(p -> cmdlist); exit(status); } } } void event_loop(void) { int fdmax = 0; struct sigaction nsa; fd_set master_rfds; sigset_t sigmask, osigmask; FD_ZERO(&master_rfds); FD_SET(apmctl_fd, &master_rfds); fdmax = apmctl_fd > fdmax ? apmctl_fd : fdmax; FD_SET(signal_fd[0], &master_rfds); fdmax = signal_fd[0] > fdmax ? signal_fd[0] : fdmax; memset(&nsa, 0, sizeof nsa); nsa.sa_handler = enque_signal; sigfillset(&nsa.sa_mask); nsa.sa_flags = SA_RESTART; sigaction(SIGHUP, &nsa, NULL); sigaction(SIGCHLD, &nsa, NULL); sigaction(SIGTERM, &nsa, NULL); sigemptyset(&sigmask); sigaddset(&sigmask, SIGHUP); sigaddset(&sigmask, SIGCHLD); sigaddset(&sigmask, SIGTERM); sigprocmask(SIG_SETMASK, &sigmask, &osigmask); 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 ((res=select(fdmax + 1, &rfds, 0, 0, &to)) < 0) { if (errno != EINTR) 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) return; } if (FD_ISSET(apmctl_fd, &rfds)) proc_apmevent(apmctl_fd); } } int main(int ac, char* av[]) { int ch; int daemonize = 1; char *prog; int logopt = LOG_NDELAY | LOG_PID; while ((ch = getopt(ac, av, "df:sv")) != -1) { switch (ch) { case 'd': daemonize = 0; debug_level++; break; case 'f': apmd_configfile = optarg; break; case 's': soft_power_state_change = 1; break; case 'v': verbose = 1; break; default: err(1, "unknown option `%c'", ch); } } if (daemonize) daemon(0, 0); #ifdef NICE_INCR nice(NICE_INCR); #endif if (!daemonize) logopt |= LOG_PERROR; prog = strrchr(av[0], '/'); openlog(prog ? prog+1 : av[0], logopt, LOG_DAEMON); syslog(LOG_NOTICE, "start"); if (pipe(signal_fd) < 0) err(1, "pipe"); if (fcntl(signal_fd[0], F_SETFL, O_NONBLOCK) < 0) err(1, "fcntl"); if ((apmnorm_fd = open(APM_NORM_DEVICEFILE, O_RDWR)) == -1) { err(1, "cannot open device file `%s'", APM_NORM_DEVICEFILE); } if (fcntl(apmnorm_fd, F_SETFD, 1) == -1) { err(1, "cannot set close-on-exec flag for device file '%s'", APM_NORM_DEVICEFILE); } if ((apmctl_fd = open(APM_CTL_DEVICEFILE, O_RDWR)) == -1) { err(1, "cannot open device file `%s'", APM_CTL_DEVICEFILE); } if (fcntl(apmctl_fd, F_SETFD, 1) == -1) { err(1, "cannot set close-on-exec flag for device file '%s'", APM_CTL_DEVICEFILE); } restart(); write_pid(); event_loop(); exit(EXIT_SUCCESS); } Index: head/usr.sbin/apmd/apmdlex.l =================================================================== --- head/usr.sbin/apmd/apmdlex.l (revision 300556) +++ head/usr.sbin/apmd/apmdlex.l (revision 300557) @@ -1,116 +1,117 @@ %{ /*- * APM (Advanced Power Management) Event Dispatcher * * Copyright (c) 1999 Mitsuru IWASAKI * Copyright (c) 1999 KOIE Hidetaka * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * $FreeBSD$ */ +#include #include #include #include #include "apmd.h" #include "y.tab.h" int lineno; int first_time; %} /* We don't need it, avoid the warning. */ %option nounput %option noinput %s TOP %% %{ if (first_time) { BEGIN TOP; lineno = 1; first_time = 0; } %} [ \t]+ ; \n lineno++; , { return COMMA; } ; { return SEMICOLON; } #.*$ ; apm_event { return APMEVENT; } NOEVENT { yylval.ev = EVENT_NOEVENT; return EVENT; } STANDBYREQ { yylval.ev = EVENT_STANDBYREQ; return EVENT; } SUSPENDREQ { yylval.ev = EVENT_SUSPENDREQ; return EVENT; } NORMRESUME { yylval.ev = EVENT_NORMRESUME; return EVENT; } CRITRESUME { yylval.ev = EVENT_CRITRESUME; return EVENT; } BATTERYLOW { yylval.ev = EVENT_BATTERYLOW; return EVENT; } POWERSTATECHANGE { yylval.ev = EVENT_POWERSTATECHANGE; return EVENT; } UPDATETIME { yylval.ev = EVENT_UPDATETIME; return EVENT; } CRITSUSPEND { yylval.ev = EVENT_CRITSUSPEND; return EVENT; } USERSTANDBYREQ { yylval.ev = EVENT_USERSTANDBYREQ; return EVENT; } USERSUSPENDREQ { yylval.ev = EVENT_USERSUSPENDREQ; return EVENT; } STANDBYRESUME { yylval.ev = EVENT_STANDBYRESUME; return EVENT; } CAPABILITIESCHANGE { yylval.ev = EVENT_CAPABILITIESCHANGE; return EVENT; } apm_battery { return APMBATT; } charging { return BATTCHARGE; } discharging { return BATTDISCHARGE; } [0-9]+% { yylval.i = atoi(yytext); return BATTPERCENT; } [0-9]+[Mm] { yylval.i = -atoi(yytext); return BATTTIME; } exec { return EXECCMD; } reject { return REJECTCMD; } \{ { return BEGINBLOCK; } \} { return ENDBLOCK; } \"[^"]+\" { int len = strlen(yytext) - 2; if ((yylval.str = (char *) malloc(len + 1)) == NULL) goto out; memcpy(yylval.str, yytext + 1, len); yylval.str[len] = '\0'; out: return STRING; } [^"{},;#\n\t ]+ { yylval.str = strdup(yytext); return UNKNOWN; } %% void yyerror(const char *s) { syslog(LOG_ERR, "line %d: %s%s %s.\n", lineno, yytext, yytext?":":"", s); } Index: head/usr.sbin/apmd/apmdparse.y =================================================================== --- head/usr.sbin/apmd/apmdparse.y (revision 300556) +++ head/usr.sbin/apmd/apmdparse.y (revision 300557) @@ -1,205 +1,206 @@ %{ /*- * APM (Advanced Power Management) Event Dispatcher * * Copyright (c) 1999 Mitsuru IWASAKI * Copyright (c) 1999 KOIE Hidetaka * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * $FreeBSD$ */ +#include #include #include #include #include #include "apmd.h" #ifdef DEBUG #define YYDEBUG 1 #endif extern int first_time; %} %union { char *str; 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 BATTTIME BATTPERCENT %token EXECCMD REJECTCMD %token EVENT %token STRING UNKNOWN %type apm_battery_level %type apm_battery_direction %type string %type unknown %type event_list %type cmd_list %type cmd %type exec_cmd reject_cmd %% config_file : { first_time = 1; } config_list ; config_list : config | config_list config ; config : apm_event_statement | apm_battery_statement ; apm_event_statement : APMEVENT event_list BEGINBLOCK cmd_list ENDBLOCK { if (register_apm_event_handlers($2, $4) < 0) abort(); /* XXX */ free_event_cmd_list($4); } ; 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 { bit_nclear($$, 0, EVENT_MAX - 1); bit_set($$, $1); } | event_list COMMA EVENT { memcpy(&($$), &($1), bitstr_size(EVENT_MAX)); bit_set($$, $3); } ; cmd_list : /* empty */ { $$ = NULL; } | cmd_list cmd { struct event_cmd * p = $1; if (p) { while (p->next != NULL) p = p->next; p->next = $2; $$ = $1; } else { $$ = $2; } } ; cmd : exec_cmd SEMICOLON { $$ = $1; } | reject_cmd SEMICOLON { $$ = $1; } ; exec_cmd : EXECCMD string { size_t len = sizeof (struct event_cmd_exec); struct event_cmd_exec *cmd = malloc(len); cmd->evcmd.next = NULL; cmd->evcmd.len = len; cmd->evcmd.name = "exec"; cmd->evcmd.op = &event_cmd_exec_ops; cmd->line = $2; $$ = (struct event_cmd *) cmd; } ; reject_cmd : REJECTCMD { size_t len = sizeof (struct event_cmd_reject); struct event_cmd_reject *cmd = malloc(len); cmd->evcmd.next = NULL; cmd->evcmd.len = len; cmd->evcmd.name = "reject"; cmd->evcmd.op = &event_cmd_reject_ops; $$ = (struct event_cmd *) cmd; } ; string : STRING { $$ = $1; } ; unknown : UNKNOWN { $$ = $1; } ; %%