/*
 * $Id: upsd.c,v 2.0.1.6 1996/08/02 21:43:58 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"

/*
 * Actual UPS pre-initialized handler with reasonable defaults.
 * The initialization is *very* GNU CC specific. Sigh.
 */
static struct ups ups = {
	NULL,						/* model */
	{NULL, 0, {0, 0},				/* port */
		{0, 0, 0, 0, {[0 ... NCCS-1] = 0},
		    0, 0},				/* otty */
		{0, 0, CS8 | CREAD | CLOCAL,
		    0, {[0 ... NCCS-1] = 0},
		    B2400, B2400},			/* ntty */
		1, {0, 0},				/* block size and delay */
		{QUEUE_SIZE, NULL, NULL, NULL}},	/* queue */
	NULL,						/* status */
	NULL						/* events */
};

/* All supported UPS models. */
struct ups_model *upslist[] = {
	&apc_SmartUPS_230,
	&apc_SmartUPS_vs_420,
	NULL
};

/* Signal list to handle. */
static int sig_handle_list[] = {
	SIGABRT,
	SIGBUS,
	SIGEMT,
	SIGFPE,
	SIGILL,
	SIGIO,
	SIGQUIT,
	SIGSEGV,
	SIGSYS,
	0
};

/* Signal list to ignore. */
static int sig_ignore_list[] = {
	SIGALRM,
	SIGCHLD,
	SIGCONT,
	SIGHUP,
	SIGINFO,
	SIGINT,
	SIGPIPE,
	SIGPROF,
	SIGTERM,
	SIGTRAP,
	SIGTSTP,
	SIGTTIN,
	SIGTTOU,
	SIGURG,
	SIGUSR1,
	SIGUSR2,
	SIGVTALRM,
	SIGWINCH,
	SIGXCPU,
	SIGXFSZ,
	0
};

static char *triggerb;
struct ups *upsp = &ups;
struct ups_trig *zero_trig;
time_t current_time, next_time;

/*
 * This is a daemon for monitoring an Uninterruptable Power Source
 * or as usually referenced UPS. The daemon is intended to support
 * several protocols but currently only APC SmartUPS protocol is
 * implemented.
 */
int
main(argc, argv)
	int argc;
	char **argv;
{
	extern char *optarg;
	extern int optind;

	register int *sp;
	register int c;
	struct sigaction act;
	struct timeval tv;

	/* Parse the command line first. */
	while((c = getopt(argc, argv, "")) != EOF) {
		switch(c) {
		default:
			usage();
			/* NOTREACHED */
		}
	}
	argc -= optind;
	argv += optind;

	/* Set up the system log. */
	openlog(NULL, LOG_CONS | LOG_PID, LOG_DAEMON);

	/* Configure the daemon. */
	if(configure(_PATH_UPSD_CONF) == -1) {
		exit(1);
	}

	if((triggerb = xalloc(upsp->port.queue.size)) == NULL) {
		exit(1);
	}

	/* Set up signal handling. */
	sigemptyset(&act.sa_mask);
	act.sa_handler = SIG_IGN;
	act.sa_flags = 0;
	for(sp = sig_ignore_list; *sp; sp++) {
		if(sigaction(*sp, &act, NULL) == -1) {
			syslog(LOG_ERR, "cannot install signal handler for signal %d: %m", *sp);
			exit(1);
		}
	}
	act.sa_handler = signal_handler;
	act.sa_flags = SA_RESTART;
	for(sp = sig_handle_list; *sp; sp++) {
		if(sigaction(*sp, &act, NULL) == -1) {
			syslog(LOG_ERR, "cannot install signal handler for signal %d: %m", *sp);
			exit(1);
		}
	}

	/* Detach and become a daemon. */
#ifndef DEBUG
	if(daemon(0, 1) == -1) {
#else
	if(daemon(1, 1) == -1) {
#endif
		syslog(LOG_ERR, "cannot become a daemon: %m");
		exit(1);
	}

	/* Open the port. */
	if(openport() == -1) {
		exit(1);
	}

	(void)mkpid(_PATH_UPSD_PID);

	/* Stamp the current time for top-level conditionless events. */
	if(getcurrenttime()) {
		exit(1);
	}
	traverse2((struct chain *)upsp->eventv, (void *)trig, (void *)zero_trig);

	syslog(LOG_INFO, "ups daemon started");
	
	/* The main loop. */
	for(;;) {
		if(getcurrenttime()) {
			TERMINATE;	/* XXX: Or continue? */
		}
		next_time = 0;
		traverse((struct chain *)upsp->eventv, (void *)process);
		if(getcurrenttime()) {
			TERMINATE;	/* XXX: Or continue? */
		}
		if(next_time != 0) {
			tv.tv_sec = next_time - current_time;
			tv.tv_usec = 0;
			(void) select(0, NULL, NULL, NULL, &tv);
		} else {
			(void) select(0, NULL, NULL, NULL, NULL);
		}
	}
	/* NOTREACHED */
	exit(1);
}

/*
 * Handles every signal received.
 */
void
signal_handler(sig, code, scp)
	int sig;
	int code;
	struct sigcontext *scp;
{
	register int s;
	register struct ups_trig *tp;

	if(sig == SIGIO || sig == SIGURG) {
		if(listenport() == -1) {
			exit(1);	/* XXX: Wrong... */
		}
		s = readport(triggerb, upsp->port.queue.size, 0);
		if(s > 0) {
			if((tp = checktrigger(triggerb, s))) {
				flushport();	/* XXX: Is it correct? */
				traverse2((struct chain *)upsp->eventv, (void *)trig, tp);
				/* XXX: the trigger could be processed after all the */
				/* XXX: actions currently executing... */
			}
		}
		return;
	}
	syslog(LOG_ERR, "signal_handler: %s", sys_siglist[sig]);
	closeport();
	(void)rmpid(_PATH_UPSD_PID);
	exit(1);
}

/*
 * Prints usage message and leaves the program with exit code
 * equal to 1.
 */
void
usage(void)
{

	fprintf(stderr, "usage: upsd\n");
	exit(1);
}


syntax highlighted by Code2HTML, v. 0.9.1