%{ /* * $Id: config.y,v 2.0.1.5 1996/06/26 18:39:38 alexis Exp alexis $ * * UPS Daemon * The Wild Wind Communications, 1995, 1996 * * See file LICENSE for the distribution terms of this software. */ #include "upsd.h" #include "apc.h" #include static FILE *config; static char *input; static char *pathname; static char *token; static int lineno; struct keyword { char *keyword; int len; int token; }; %} %union { char *string; double number; struct ups_reg *ups_reg; struct ups_trig *ups_trig; struct event *event; struct action *action; struct when *when; } %type condition after for every priority %type action actions %type event events %type trigger %type register %type when %token STRING %token NUMBER %token ERROR /* keywords */ %token AFTER BLOCK DELAY DEVICE DROP EQUAL EVERY EXEC FOR LESS LOG MORE %token NOT ON POLL PROTO QUEUE RAISE READ SIZE SLEEP SPEED THAN TIMEOUT %token TO TUNE UPS WRITE LEMERG LALERT LCRIT LERR LWARNING LNOTICE LINFO %token LDEBUG NOP %start config %% /* Top level. */ config : ups events { upsp->eventv = $2; } ; /* UPS specification. */ ups : UPS STRING '(' NUMBER ')' PROTO STRING '{' port_settings '}' { register struct ups_model **mp; register struct ups_reg *rp; register struct ups_val *vp; for(mp = upslist; *mp != NULL; mp++) { if(!strcmp((*mp)->type, $2) && ((*mp)->voltage == (int)$4) && !strcmp((*mp)->protocol, $7)) { break; } } if(*mp == NULL) { yyerror("uknown ups specification"); YYERROR; } upsp->model = *mp; xfree($2); xfree($7); if((zero_trig = TRIGGERID(0)) == NULL) { yyerror("cannot find the terminating trigger"); YYERROR; } /* Fill up upsp->state. */ if((upsp->state = xalloc((upsp->model->nregs) * sizeof(struct ups_val))) == NULL) { YYERROR; } bzero(upsp->state, sizeof(struct ups_val) * upsp->model->nregs); for(rp = upsp->model->registers, vp = upsp->state; rp->id; rp++, vp++) { vp->id = rp->id; if((rp->type & T_TYPE) == T_BINARY) { if((vp->val.binary = xalloc(rp->size)) == NULL) { YYERROR; } } } } ; /* Port specification. */ port_settings : port_setting | port_settings port_setting ; port_setting : /* empty */ | DEVICE STRING { if((upsp->port.device = $2) == NULL) { YYERROR; } } | SPEED NUMBER { upsp->port.ntty.c_ispeed = upsp->port.ntty.c_ospeed = (speed_t) $2; } | READ '-' TIMEOUT NUMBER '.' NUMBER { upsp->port.timeout.tv_sec = (long) $4; upsp->port.timeout.tv_usec = (long) $6; } | READ '-' TIMEOUT NUMBER { upsp->port.timeout.tv_sec = (long) $4; upsp->port.timeout.tv_usec = 0; } | WRITE '-' BLOCK '-' SIZE NUMBER { upsp->port.writeblksz = (int) $6; } | WRITE '-' BLOCK '-' DELAY NUMBER { upsp->port.writedelay.tv_sec = 0; upsp->port.writedelay.tv_usec = (long) $6; } | QUEUE '-' SIZE NUMBER { upsp->port.queue.size = (int) $4; } ; /* Event description. */ events : event { $$ = $1; $1->next = NULL; } | events event { register struct event *ep; for(ep = $1; ep->next != NULL; ep = ep->next); ep->next = $2; $2->next = NULL; $$ = $1; } ; event : ON trigger when '{' actions '}' { if(($$ = newevent($2, NULL, 0, $3, $5)) == NULL) { YYERROR; } } | ON register condition NUMBER when '{' actions '}' { if(($$ = newevent(NULL, newval($2, &($4), T_NUMBER), (int) $3, $5, $7)) == NULL) { YYERROR; } } | ON register condition STRING when '{' actions '}' { if(($$ = newevent(NULL, newval($2, &($4), T_BINARY), ((int) $3 | C_STRING), $5, $7)) == NULL) { YYERROR; } } | when '{' actions '}' { if(($$ = newevent(NULL, NULL, 0, $1, $3)) == NULL) { YYERROR; } } ; condition : morethan { $$ = C_MORE; } | lessthan { $$ = C_LESS; } | equalto { $$ = C_EQUAL; } | nequalto { $$ = C_NEQUAL; } ; morethan : MORE | MORE THAN | '>' ; lessthan : LESS | LESS THAN | '<' ; equalto : EQUAL | EQUAL TO | '=' '=' ; nequalto : NOT EQUAL | NOT EQUAL TO | '!' '=' ; when : after every for { if(($$ = xalloc(sizeof(struct when))) == NULL) { YYERROR; } $$->act_after = (time_t) $1; $$->act_every = (time_t) $2; $$->act_for = (time_t) $3; } ; after : /* empty */ { $$ = 0; } | AFTER NUMBER { $$ = $2; } ; every : /* empty */ { $$ = 0; } | EVERY NUMBER { $$ = $2; } ; for : /* empty */ { $$ = 0; } | FOR NUMBER { $$ = $2; } ; /* Action description. */ actions : action { $$ = $1; $1->next = NULL; } | actions action { register struct action *ap; for(ap = $1; ap->next != NULL; ap = ap->next); ap->next = $2; $2->next = NULL; $$ = $1; } ; action : DROP trigger { if(($$ = newaction(A_DROP, $2)) == NULL) { YYERROR; } } | EXEC STRING { if(($$ = newaction(A_EXEC, $2)) == NULL) { YYERROR; } } | RAISE trigger { if(($$ = newaction(A_RAISE, $2)) == NULL) { YYERROR; } } | POLL register { if(($$ = newaction(A_POLL, VALUEID($2->id))) == NULL) { YYERROR; } } | TUNE register STRING { register struct ups_val *val; if((val = newval($2, &($3), T_BINARY)) == NULL) { YYERROR; } if(illval(val, $2)) { yyerror("illegal value for this ups model"); YYERROR; } if(($$ = newaction(A_TUNE, val)) == NULL) { YYERROR; } } | TUNE register NUMBER { register struct ups_val *val; if((val = newval($2, &($3), T_NUMBER)) == NULL) { YYERROR; } if(illval(val, $2)) { yyerror("illegal value for this ups model"); YYERROR; } if(($$ = newaction(A_TUNE, val)) == NULL) { YYERROR; } } | LOG priority STRING { register struct message *m; if((m = xalloc(sizeof(struct message))) == NULL) { YYERROR; } m->priority = (int) $2; m->message = $3; if(($$ = newaction(A_LOG, m)) == NULL) { YYERROR; } } | SLEEP NUMBER { register struct timeval *t; if((t = xalloc(sizeof(struct timeval))) == NULL) { YYERROR; } t->tv_sec = (int) $2; t->tv_usec = 0; if(($$ = newaction(A_SLEEP, t)) == NULL) { YYERROR; } } | NOP { if(($$= newaction(A_NONE, NULL)) == NULL) { YYERROR; } } | event { if(($$ = newaction(A_EVENT, $1)) == NULL) { YYERROR; } } ; register : STRING { if(($$ = REGISTERNAME($1)) == NULL) { yyerror("incorrect register for this ups"); YYERROR; } } ; trigger : STRING { if(($$ = TRIGGERNAME($1)) == NULL) { yyerror("incorrect trigger for this ups"); YYERROR; } } ; priority : LEMERG { $$ = LOG_EMERG; } | LALERT { $$ = LOG_ALERT; } | LCRIT { $$ = LOG_CRIT; } | LERR { $$ = LOG_ERR; } | LWARNING { $$ = LOG_WARNING; } | LNOTICE { $$ = LOG_NOTICE; } | LINFO { $$ = LOG_INFO; } | LDEBUG { $$ = LOG_DEBUG; } | /* empty */ { $$ = DEFAULT_LOG_PRIORITY; } ; %% static struct keyword keywords[] = { {"after", 5, AFTER}, {"alert", 5, LALERT}, {"block", 5, BLOCK}, {"crit", 4, LCRIT}, {"debug", 5, LDEBUG}, {"delay", 5, DELAY}, {"device", 6, DEVICE}, {"drop", 4, DROP}, {"emerg", 5, LEMERG}, {"equal", 5, EQUAL}, {"err", 3, LERR}, {"every", 5, EVERY}, {"exec", 4, EXEC}, {"for", 3, FOR}, {"info", 4, LINFO}, {"less", 4, LESS}, {"log", 3, LOG}, {"more", 4, MORE}, {"nop", 3, NOP}, {"notice", 6, LNOTICE}, {"not", 3, NOT}, {"on", 2, ON}, {"poll", 4, POLL}, {"proto", 5, PROTO}, {"queue", 5, QUEUE}, {"raise", 5, RAISE}, {"read", 4, READ}, {"size", 4, SIZE}, {"sleep", 5, SLEEP}, {"speed", 5, SPEED}, {"than", 4, THAN}, {"timeout", 7, TIMEOUT}, {"to", 2, TO}, {"tune", 4, TUNE}, {"ups", 3, UPS}, {"warning", 7, LWARNING}, {"write", 5, WRITE}, {NULL, 0, 0} }; /* * This is the actual lexical parser. */ int yylex(void) { char *t; struct keyword *k; for(;;) { if((token == NULL) || (*token == '\0')) { lineno++; if((token = fgets(input, MAXINPUTLEN, config)) == NULL) { return 0; } if((t = index(token, '\n')) != NULL) { *t = '\0'; /* Chop the line. */ } } while(isspace(*token)) { /* Skip spaces. */ token++; } if(*token == '#' || *token == '\n' || *token == '\0') { token = NULL; continue; /* Keep reading the file. */ } if(*token == '"') { /* Is it a string? */ token++; if((t = index(token, '"')) == NULL) { /* An unterminated string is treated as */ /* a single double quote character. */ return *(token-1); } *t++ = '\0'; if((yylval.string = xstrdup(token)) == NULL) { return ERROR; } token = t; return STRING; } if(isdigit(*token)) { /* Is it a number? */ yylval.number = strtod(token, &t); token = t; return NUMBER; } for(k = keywords; k->keyword; k++) { if(!strncasecmp(token, k->keyword, k->len)) { token += k->len; return k->token; } } /* Let's return a character. */ t = token++; return *t; } } /* * Error reporting routine. */ void yyerror(string) char *string; { syslog(LOG_ERR, "parse error: %s in file %s, line %d", string, pathname, lineno); } /* * Main function. */ int configure(config_pathname) char *config_pathname; { extern int yyparse(); lineno = 0; pathname = config_pathname; token = NULL; if((input = xalloc(MAXINPUTLEN)) == NULL) { return -1; } if((config = fopen(pathname, "r")) == NULL) { syslog(LOG_ERR, "configure: cannot open %s: %m", pathname); return -1; }; if(yyparse()) { syslog(LOG_ERR, "configure: cannot parse configuration file %s", pathname); return -1; }; fclose(config); xfree(input); return 0; } /* * Allocates space for a new event and fills it according to the * arguments. Returns the pointer onto this newly allocated area * or NULL if failed. */ struct event * newevent(trigger, value, condition, when, actions) struct ups_trig *trigger; struct ups_val *value; int condition; struct when *when; struct action *actions; { register struct event *ep; if((ep = (struct event*) xalloc(sizeof(struct event))) == NULL) { return NULL; } bzero(ep, sizeof(struct event)); if(trigger != NULL) { ep->trigger = trigger->id; } else { ep->trigger = 0; } if(value == NULL && condition != 0) { return NULL; /* Already reported in newval(). */ } ep->v = value; ep->condition = condition; ep->when = when; ep->acts = actions; return ep; } /* * Allocates space for a new action and fills it according to the * arguments. Returns the pointer onto this newly allocated area * or NULL if failed. */ struct action * newaction(type, action) int type; void *action; { register struct action *ap; if((ap = (struct action*)xalloc(sizeof(struct action))) == NULL) { return NULL; } bzero(ap, sizeof(struct action)); ap->type = type; ap->action = action; return ap; } /* * Allocates space for a new value and fills it according to the * arguments. Returns the pointer onto the new value or NULL if * failed. */ struct ups_val * newval(reg, value, type) struct ups_reg *reg; void *value; int type; { register struct ups_val *vp; if(type != reg->type) { yyerror("incorrect value type for this register"); return NULL; } if((vp = xalloc(sizeof(struct ups_val))) == NULL) { return NULL; } bzero(vp, sizeof(struct ups_val)); vp->id = reg->id; if(value == NULL) { return vp; } switch((reg->type & T_TYPE)) { case T_BINARY: bcopy(value, (void *)&vp->val, sizeof(void *)); break; case T_NUMBER: bcopy(value, (void *)&vp->val, sizeof(double)); break; } return vp; } /* * Checks if the value is legal for this UPS. Zero is returned * if legal, non-zero otherwise. -1 is returned upon error. */ int illval(val, reg) struct ups_val *val; struct ups_reg *reg; { register struct ups_val *v; if(reg->id != val->id) { syslog(LOG_ERR, "illval: illegal register supplied for the value"); return -1; } for(v = upsp->model->values; v->id != 0; v++) { if(v->id == reg->id) { switch(reg->type & T_TYPE) { case T_NUMBER: if(val->val.number == v->val.number) { return 0; } break; case T_BINARY: if(v->val.binary == NULL) { return 0; } else if(!bcmp(val->val.binary, v->val.binary, reg->size - reg->prec)) { return 0; } break; default: syslog(LOG_ERR, "illval: incorrect type of legal value"); return -1; } } } return 1; }