/*
 * $Id: event.c,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"

/*
 * Acts. Intended to be called with traverse().
 *
 * This function returns no value.
 */
void
act(action)
	struct action *action;
{
	switch(action->type) {
	case A_NONE:
		break;
	case A_EVENT:
		process((struct event *)action->action);
		break;
	case A_EXEC:
		execute(expand((char *)action->action));
		break;
	case A_RAISE:
		traverse2((struct chain *)upsp->eventv, (void *)trig, action->action);
		break;
	case A_DROP:
		traverse2((struct chain *)upsp->eventv, (void *)drop, action->action);
		break;
	case A_POLL:
		upsp->model->poll((struct ups_val *)action->action);
		break;
	case A_TUNE:
		upsp->model->tune((struct ups_val *)action->action);
		break;
	case A_LOG:
		syslog(((struct message *)action->action)->priority,
		    expand(((struct message *)action->action)->message));
		break;
	case A_SLEEP:
		(void) select(0, NULL, NULL, NULL, (struct timeval *)action->action);
		break;
	default:
		syslog(LOG_ERR, "act: incorrect action type");
	}
}

/*
 * Processes the event.
 */
void
process(event)
	struct event *event;
{

	if(checkcondition(event)) {
		if(event->first == 0) {
			event->nextrun = 0;
			event->first = event->last = current_time;
		} else {
			event->last = current_time;
		}
	}

	/* The event is inactive || expires. */
	if((event->first == 0) ||
	   (event->when->act_for != 0 &&
	   (current_time - event->last > event->when->act_for))) {
		event->first = event->last = event->nextrun = 0;
		return;
	}
	if(event->nextrun == 0) {
		event->nextrun = event->first + event->when->act_after;
	}
	/* Act if the time has come. */
	if(event->nextrun <= current_time) {
		/* XXX: we may want to move the next line into act() */
		traverse2((struct chain *)event->acts, (void *)atrig, (void *)zero_trig);
		traverse((struct chain *)event->acts, (void *)act);
		event->nextrun += event->when->act_every;
		/* The event already acted once. */
		if(event->when->act_every == 0) {
			event->first = event->last = event->nextrun = 0;
			return;
		}
	}
	if(((next_time == 0) || ((event->nextrun < next_time) &&
	    ((event->first + event->when->act_for > event->nextrun)
	    || (event->when->act_for == 0))))) {
		next_time = event->nextrun;
	}
}

/*
 * Trig an event.
 */
void
trig(event, trigger)
	struct event *event;
	struct ups_trig *trigger;
{
	if((event->condition == 0) && (event->v == NULL)) {
		if(event->trigger == trigger->id) {
			if(event->first == 0) {
				event->nextrun = 0;
				event->first = event->last = current_time;
			} else {
				event->last = current_time;
			}
		} else if(event->trigger == trigger->drop) {
			event->nextrun = event->first = event->last = 0;
		}
	}
}

/*
 * Drops an event.
 */
void
drop(event, trigger)
	struct event *event;
	struct ups_trig *trigger;
{
	if((event->condition == 0) && (event->v == NULL) &&
	    (event->trigger == trigger->id)) {
		event->nextrun = event->first = event->last = 0;
	}
}

/*
 * Returns the trigger id if any or zero if none.
 */
struct ups_trig *
checktrigger(buffer, size)
	char *buffer;
	size_t size;
{
	register struct ups_trig *tp;

	for(;size > 0; buffer++, size--) {
		for(tp = upsp->model->triggers; tp->id != 0; tp++) {
			if(size < tp->size) {
				continue;
			}
			if(!bcmp(tp->trigger, buffer, tp->size)) {
				return tp;
			}
		}
	}
	return NULL;
}

/*
 * Checks if condition occured.
 */
int
checkcondition(event)
	struct event *event;
{
	register struct ups_val *v;
	register struct ups_reg *r;

	if((event->trigger == 0) && (event->condition != 0) &&
	    (event->v != NULL)) {
		if((r = REGISTERID(event->v->id)) == NULL) {
			syslog(LOG_ERR, "check: incorrect register id");
			return 0;
		}
		if((v = VALUEID(event->v->id)) == NULL) {
			syslog(LOG_ERR, "check: value is not in the state");
			return 0;
		}
		if((event->condition & C_STRING) == 0) {
			switch(event->condition & C_COND) {
			case C_LESS:
				return (v->val.number < event->v->val.number);
			case C_MORE:
				return (v->val.number > event->v->val.number);
			case C_EQUAL:
				return (v->val.number == event->v->val.number);
			case C_NEQUAL:
				return (v->val.number != event->v->val.number);
			default:
				return 0;
			}
		} else {
			/* XXX: strcmp() is not applicable. Should be bcmp(). */
			switch(event->condition & C_COND) {
			case C_LESS:
				return (strcmp(v->val.binary, event->v->val.binary) < 0);
			case C_MORE:
				return (strcmp(v->val.binary, event->v->val.binary) > 0);
			case C_EQUAL:
				return (strcmp(v->val.binary, event->v->val.binary) == 0);
			case C_NEQUAL:
				return (strcmp(v->val.binary, event->v->val.binary) != 0);
			default:
				return 0;
			}
			/* NOTREACHED */
		}
		/* NOTREACHED */
	}
	return 0;
}

/*
 * Trigs an event given an action pointer if the action is the evenet.
 */
void
atrig(action, trigger)
	struct action *action;
	struct ups_trig *trigger;
{
	if(action->type == A_EVENT) {
		trig((struct event *)action->action, trigger);
	}
}


syntax highlighted by Code2HTML, v. 0.9.1