powstatd-1.5.1/Makefile0100644000175000017500000001327407272360073014035 0ustar segresegre# powstatd: a configurable UPS monitor. # Copyright (C) 1999-2001 The University of Iowa # Author: Alberto Maria Segre # segre@cs.uiowa.edu # S378 Pappajohn Building # The University of Iowa # Iowa City, IA 52242-1000 # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License as # published by the Free Software Foundation; either version 2 of the # License, or (at your option) any later version. # # This program is distributed in the hope that it will be useful, but # WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU # General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 # USA #====================================================================== # Local configuration information. # Select compilation architecture. ARCH = i386 #ARCH = alpha # To compile the secure version of powstatd, first obtain the source # code for TEA, the Tiny Encryption Algorithm, from # ftp://vader.eeng.brad.ac.uk/pub/crypto/xtea.c # and then make sure the line below is not commented out. # # On the other hand, if you intend to run powstatd on a single machine # (i.e., no slaves), or simply do not wish to bother with encrypted # communication, make sure the following line is commented out. #CFLGS += -DSECURE # You probably shouldn't have to edit these variable definitions, # although some inits look for status in /var/run/powerstatus rather # than /etc/powerstatus. If your init seems to ignore signals from UPS # monitoring daemon, try switching. Be sure to escape string # delimiters! STATUS = \"/etc/powerstatus\" CFGDIR = /etc DESTDIR = /sbin MANDIR = /usr/man/man8 # Debian Linux (also SPARC/Solaris and SGI/IRIX) INITDIR = /etc/init.d # RedHat and Slackware #INITDIR = /etc/rc.d/init.d #====================================================================== # Stop: no user configurable items below this point. #====================================================================== VERSION = 1.5.1 DATE = "April 27 2001" SRC = powstatd.c CRYPT = xtea.c ACRYPT = xtea.alpha.c INC = LIB = BIN = powstatd SCR = powstatd.ok powstatd.fail powstatd.low INIT = powstatd.init CFG = powstatd.conf MAN = powstatd MSEC = 8 DOC = {License,History,QuickStart,ReadMe,ReadMe.sparc,ReadMe.sgi} ALTCFG = {powstatd.conf.{apc,cps,tpl,master,slave}} XTAR = {Makefile,powstatd.{c,init,ok,fail,dumb,low,man},$(DOC),$(ALTCFG)} USTAR = {$(XTAR),$(CRYPT)} CFLGS += -Wall -g -DDEBUG #====================================================================== # Targets i386: $(SRC) $(INC) $(CRYPT) Makefile $(CC) $(CFLGS) -DSTATFILE=$(STATUS) -DCONFIG="\"$(CFGDIR)/$(CFG)\"" $(SRC) $(CRYPT) -o $(BIN) $(LIB) alpha: $(SRC) $(INC) $(ACRYPT) Makefile $(CC) $(CFLGS) -DALPHA -DSTATFILE=$(STATUS) -DCONFIG="\"$(CFGDIR)/$(CFG)\"" $(SRC) $(ACRYPT) -o $(BIN) $(LIB) sparc: $(SRC) $(INC) $(ACRYPT) Makefile $(CC) $(CFLGS) -DSPARC -DSTATFILE=$(STATUS) -DCONFIG="\"$(CFGDIR)/$(CFG)\"" $(SRC) $(ACRYPT) -lxnet -o $(BIN) $(LIB) sgi: $(SRC) $(INC) $(ACRYPT) Makefile $(CC) $(CFLGS) -DSGI -DSTATFILE=$(STATUS) -DCONFIG="\"$(CFGDIR)/$(CFG)\"" $(SRC) $(ACRYPT) -o $(BIN) $(LIB) install: $(ARCH) $(MAN).$(MSEC) uninstall strip $(BIN) cp $(BIN) $(DESTDIR) # powstatd cp $(SCR) $(DESTDIR) # powstatd.{ok,fail,low} (cd $(DESTDIR); chmod 744 $(BIN) $(SCR)) cp $(MAN).$(MSEC) $(MANDIR) # powstatd.8 cp $(INIT) $(INITDIR)/$(BIN) # powstatd.init chmod 774 $(INITDIR)/$(BIN) ln -s $(INITDIR)/$(BIN) $(INITDIR)/../rc0.d/K91$(BIN) ln -s $(INITDIR)/$(BIN) $(INITDIR)/../rc1.d/K91$(BIN) ln -s $(INITDIR)/$(BIN) $(INITDIR)/../rc3.d/S91$(BIN) ln -s $(INITDIR)/$(BIN) $(INITDIR)/../rc5.d/S91$(BIN) ln -s $(INITDIR)/$(BIN) $(INITDIR)/../rc6.d/K91$(BIN) uninstall: (cd $(DESTDIR); rm -f $(BIN) $(SCR)) rm -f $(MANDIR)/$(MAN).$(MSEC) rm -f $(INITDIR)/$(BIN) rm -f $(INITDIR)/../rc?.d/?91$(BIN) $(MAN).$(MSEC): powstatd.man sed -e 's/%%VERSION%%/\"Version $(VERSION)\"/' -e 's/%%DATE%%/$(DATE)/' < $(MAN).man > $(MAN).$(MSEC) doc: $(MAN).man $(MAN).$(MSEC) gtbl $(MAN).$(MSEC) | groff -man -Tps > $(MAN).ps # gtbl $(MAN).$(MSEC) | groff -man -Tdvi > $(MAN).dvi # A hack for those not having access to TEA. Will create an empty file # to satisfy make. You'll still need to remove -DSECURE from the CFLGS # variable. $(CRYPT): echo "/* Obtain xtea.c from ftp://vader.eeng.brad.ac.uk/pub/crypto/xtea.c */" > $(CRYPT) # This is a major hack. We can't include TEA in the source because of # export control rules, but we need to modify it for the Alpha, which # has 64 bit unsigned long ints. Sigh. $(ACRYPT): $(CRYPT) sed -e"s%unsigned long%unsigned int%g" < $(CRYPT) > $(ACRYPT) config: cp $(CFG) $(CFGDIR) # powstatd.conf clean: rm -f $(BIN) *~ $(BIN)*-$(VERSION).{tgz,tar,tar.Z,sh} $(MAN).{$(MSEC),dvi,ps} $(ACRYPT) tar: @sed -e"s%^CFLGS += -DSECURE%#CFLGS += -DSECURE%g" < Makefile > Makefile.tmp @mv Makefile.tmp Makefile tar -cf $(BIN)-$(VERSION).tar -C .. $(BIN)-$(VERSION)/$(XTAR) tgz: @sed -e"s%^CFLGS += -DSECURE%#CFLGS += -DSECURE%g" < Makefile > Makefile.tmp @mv Makefile.tmp Makefile tar -czf $(BIN)-$(VERSION).tgz -C .. $(BIN)-$(VERSION)/$(XTAR) shar: @sed -e"s%^CFLGS += -DSECURE%#CFLGS += -DSECURE%g" < Makefile > Makefile.tmp @mv Makefile.tmp Makefile shar $(XTAR) > $(BIN)-$(VERSION).sh dist: @sed -e"s%^#CFLGS += -DSECURE%CFLGS += -DSECURE%g" < Makefile > Makefile.tmp @mv Makefile.tmp Makefile tar -czf $(BIN)-crypt-$(VERSION).tgz -C .. $(BIN)-$(VERSION)/$(USTAR) powstatd-1.5.1/powstatd.c0100644000175000017500000011317307272357720014412 0ustar segresegre/********************************************************************** * powstatd: a configurable UPS monitor. * Copyright (C) 1999-2001 The University of Iowa * Author: Alberto Maria Segre * segre@cs.uiowa.edu * S378 Pappajohn Building * The University of Iowa * Iowa City, IA 52242-1000 * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 * USA **********************************************************************/ #include /* Needed for fprintf */ #include #include /* Needed for va_list */ #include /* Needed for open */ #include /* Needed for unbuffered write */ #include #include /* Needed for time(). */ #include #include #include #include #include #ifdef DEBUG #include /* Needed for inet_ntoa when debugging. */ #endif #include #include #include #include #ifndef NULL #define NULL 0 #endif #ifndef FALSE #define FALSE 0 #define TRUE !FALSE #endif #ifndef ERROR #define ERROR -1 #endif #ifndef MAX #define MAX(m,n) ((m)>=(n)?(m):(n)) #endif #ifdef SECURE /* Time stamp (as "seconds after 1970"). */ #ifdef ALPHA #define TSTAMP() ((unsigned int) time (NULL)) #else #define TSTAMP() ((unsigned long int) time (NULL)) #endif /* Maximum acceptable difference in master/slave clocks, in seconds. */ #define MAXCLOCKDRIFT 5 #endif /* Some defaults. */ #define TESTINT 1 /* Test UPS polling interval, in seconds. */ #define POLLINT 10 /* UPS polling interval, in seconds. */ #define POLLCNT 5 /* State change debouncing counter. */ #define KILLINT 10 /* UPS kill interval, in seconds. */ #define KILLWAIT 30 /* UPS master delay at kill. */ #define PPORT 1111 /* Port to watch for UPS information. */ #define MAXLINELEN 128 /* Configuration file maximum line length. */ #ifdef SECURE #define MAXSTATLEN 56 /* Max length at least 2xULONG_MAX+1 digits. */ #else #define MAXSTATLEN 8 /* Max chars in {POK,PLOW,PFAIL}. */ #endif /* Defines for UPS status. */ #define POK 0 #define PFAIL 1 #define PLOW 2 #define PSHOW(s) ((s==POK)?"OK":(s==PFAIL)?"FAIL":(s==PLOW)?"LOW":"ERROR") /* Defines for UPS mode. */ #define MASTER 0 #define SLAVE 1 #define SOLO 2 #define MSHOW(s) ((s==MASTER)?"master":(s==SLAVE)?"slave":(s==SOLO)?"standalone":"ERROR") /* Slaves. */ #define MAXSLAVES 2 /* No more than 2 machines on one UPS. */ int nslaves = 0; /* Number of "extra" machines on this UPS. */ char slaves[MAXSLAVES][MAXLINELEN + 1]; /* Slave names. */ /* Master/slave Internet host data. Let hosts[MASTER] represent the * master, if any, and hosts[1] through hosts[MAXSLAVES+1] represent * the slaves, if any. */ #define IPADDRLEN 4 /* IPv4 requires 32 bits. */ typedef struct host { char address[IPADDRLEN]; int length; /* Length of the address. */ } Host; Host hosts[MAXSLAVES + 1]; /* Global configuration variables. */ int testMode = FALSE; /* Test mode. */ int killMode = FALSE; /* Kill mode. */ uint failmask = 0, failtest = 0; uint battmask = 0, batttest = 0; uint setmask = ~0, setbits = 0; uint killmask = ~0, killbits = 0; /* Some prototypes. */ #ifdef SECURE #ifdef ALPHA void readConfigFile (char *monitor, int *mode, unsigned int *key); #else void readConfigFile (char *monitor, int *mode, unsigned long int *key); #endif #else void readConfigFile (char *monitor, int *mode); #endif void daemonize (); int openSock (unsigned short port); int initLine (char *device); int readLine (int line); #ifdef SECURE #ifdef ALPHA int readSock (int socket, unsigned int *key); void tellSlaves (int socket, unsigned short int port, int status, unsigned int *key); #else int readSock (int socket, unsigned long int *key); void tellSlaves (int socket, unsigned short int port, int status, unsigned long int *key); #endif #else int readSock (int socket); void tellSlaves (int socket, unsigned short port, int status); #endif #ifdef SECURE #ifdef ALPHA void encipher(unsigned *const v, unsigned *const w, const unsigned *const k); void decipher(unsigned *const v, unsigned *const w, const unsigned *const k); #else void encipher(unsigned long *const v, unsigned long *const w, const unsigned long *const k); void decipher(unsigned long *const v, unsigned long *const w, const unsigned long *const k); #endif #endif /********************************************************************** * UPS monitor. * * Configured mostly from the configuration file, which is usually * found as /etc/powstatd.conf. * * Only two command line options are supported: * -t: test mode; don't turn into daemon, don't actually signal init. * -k: kill mode; send kill signal to turn off UPS. **********************************************************************/ int main (int argc, char *argv[]) { int i; int mode = ERROR; /* UPS mode. */ char monitor[MAXLINELEN + 1]; /* Device to monitor. */ int line; /* Line to monitor. */ int socket; /* Socket to monitor (slave), send (master). */ unsigned int bits; /* For serial communication. */ int lastStatus = POK; /* UPS status. */ int newStatus; int bounceCnt; /* Debouncing counter. */ char sfile[MAXLINELEN + 1] = STATFILE; /* Usually /etc/powerstatus. */ FILE *sfp; #ifdef SECURE #ifdef ALPHA unsigned int key[5]; /* 128 bits of password; key[4] for \0. */ #else unsigned long int key[5]; /* 128 bits of password; key[4] for \0. */ #endif #endif /* Open system logfile for output. Tag output as coming from current * program and indicate the PID of the process as well. */ if (!testMode) openlog (argv[0], LOG_PID, LOG_DAEMON); /* Parse input arguments. */ for (i = 1; i < argc; i++) { if (strncmp (argv[i], "-h", 2) == 0) { printf ("Usage: %s [ -t | -k ]\n", argv[0]); printf ("where -t : test mode\n"); printf (" -k : kill mode\n"); printf ("Unless -t or -k are specified, %s runs as daemon.\n", argv[0]); exit (ERROR); } else if (strncmp (argv[i], "-t", 2) == 0) /* test mode. */ testMode = TRUE; else if (strncmp (argv[i], "-k", 2) == 0) /* kill mode. */ killMode = TRUE; else { if (!testMode) syslog (LOG_ERR, "unrecognized argument %s; quitting.\n", argv[i]); else printf ("powstatd: unrecognized argument %s; quitting.\n", argv[i]); exit (ERROR); } } #ifdef SECURE /* If you're running a secure system, make sure the key is initially * zero filled. */ #ifdef ALPHA memset (key, 0, (5 * sizeof (unsigned int))); #else memset (key, 0, (5 * sizeof (unsigned long int))); #endif #endif /* Read configuration file. Testing of the serial line is actually * carried out at the bit level. The configuration file is used to * set up the appropriate bit conditions which can then be used to * detect the power supply's condition. */ #ifdef SECURE readConfigFile (monitor, &mode, key); #else readConfigFile (monitor, &mode); #endif if (mode == ERROR) { if (!testMode) syslog (LOG_ERR, "no UPS to watch; quitting.\n"); else printf ("powstatd: no UPS to watch; quitting.\n"); exit (ERROR); } #ifdef SECURE else if ((mode != SOLO) && (key[0] == 0 && key[1] == 0 && key[2] == 0 && key[3] == 0)) { if (!testMode) syslog (LOG_ERR, "missing password; quitting.\n"); else printf ("powstatd: missing password; quitting.\n"); exit (ERROR); } #endif /* Slaves are not responsible for killing their power supplies, so * just exit. */ if (mode == SLAVE && killMode) exit (EXIT_FAILURE); /* Turn yourself into a daemon. */ if (!(testMode || killMode)) daemonize (); /* Establish port slaves watch for incoming reports, and the master * uses to broadcast status information. If you can't get the port, * abort (we're assuming another powstatd process is running and has * already grabbed the port; only one powstatd process is * allowed). Grabbing the port is always attempted, as this is a * convenient mechanism to enforce the one CPU/one powstatd * constraint. If we are slaves, then this will actually be the * device we monitor for status information broadcast via UDP from * our master (through this socket). Solo machines simply ignore the * socket. */ if ((socket = openSock (PPORT)) == ERROR) { if (!testMode) syslog (LOG_ERR, "socket %d unavailable; quitting.\n", PPORT); else printf ("powstatd: socket %d unavailable; quitting.\n", PPORT); exit (ERROR); } /* Get rid of any stray status file that may confuse init. */ unlink (sfile); /* Set up your connection to your UPS via serial line (if standalone * or master). If you are a slave, then the socket you will be * watching has already been opened. */ if (mode != SLAVE) line = initLine (monitor); /* All systems go. */ if (!killMode) { if (!testMode) syslog (LOG_NOTICE, "online (%s), watching %s.\n", MSHOW (mode), monitor); else printf ("powstatd: online (%s), watching %s.\n", MSHOW (mode), monitor); } /* Start monitoring loop; but first print out headers if you are in * testMode. */ if (testMode && mode != SLAVE) printf ("CTS DSR DCD RNG DTR RTS STATUS\n"); else if (testMode && mode == SLAVE) printf ("STATUS\n"); while (TRUE) { /* Get new status. If you're a slave reading from a socket and * you get nonsense back, try again (don't worry, it blocks if * there's nothing to process). */ if (mode == SLAVE) { #ifdef SECURE while ((newStatus = readSock (socket, key)) == ERROR); #else while ((newStatus = readSock (socket)) == ERROR); #endif } else newStatus = readLine (line); /* To insulate ourselves from power glitches, we require that a * new status persist POLLCNT samples (using shortened sampling * interval TESTINT) before certifying the state change. Slave * machines needn't debounce, of course, since their master will * have debounced before issuing a change in status. */ if (mode != SLAVE) { bounceCnt = POLLCNT; while ((lastStatus != newStatus) && bounceCnt-- > 0) { newStatus = readLine (line); /* Sleep for reduced polling interval. */ sleep (TESTINT); } } /* Service kill mode, then exit. Need to do this after * debouncing, because sometimes startup effects can cause a * perfectly good UPS to appear to be failing. */ if (killMode) { if (newStatus != POK && mode != SLAVE) { if (!testMode) { syslog (LOG_NOTICE, "killing UPS.\n"); closelog (); } else printf ("powstatd: killing UPS.\n"); /* If you have slave machines, wait a prespecified * interval to ensure they catch up before yanking the * UPS out from under them. */ if (nslaves > 0) sleep (KILLWAIT); if (killmask == ~0) { /* This is the default case when killmask is all * 1's, indicating the UPS is expecting you to send * a break signal (for KILLINT/10 seconds) along on * TxD instead of signaling on another * line. Wouldn't normally be my choice for default * behavior, as its use seems confined to some older * APC units with homebrew cabling, but it should be * harmless enough.*/ ioctl (line, TCSBRKP, KILLINT); if (testMode) printf ("[sending BREAK signal]\n"); } else { /* Some killbits have been specified in the * configuration file: drop them momentarily. Make * sure you hold them down long enough for the UPS * to notice. */ ioctl (line, TIOCMGET, &bits); bits = (bits & killmask) | (~killbits & ~killmask); if (testMode) printf (" %i %i %i %i %i %i DROP\n", (bits & TIOCM_CTS) != 0, (bits & TIOCM_DSR) != 0, (bits & TIOCM_CD) != 0, (bits & TIOCM_RNG) != 0, (bits & TIOCM_DTR) != 0, (bits & TIOCM_RTS) != 0); ioctl (line, TIOCMSET, &bits); sleep (TESTINT); /* Now set the killbits. */ ioctl (line, TIOCMGET, &bits); bits = (bits & killmask) | killbits; if (testMode) printf (" %i %i %i %i %i %i KILL\n", (bits & TIOCM_CTS) != 0, (bits & TIOCM_DSR) != 0, (bits & TIOCM_CD) != 0, (bits & TIOCM_RNG) != 0, (bits & TIOCM_DTR) != 0, (bits & TIOCM_RTS) != 0); ioctl (line, TIOCMSET, &bits); sleep (KILLINT); #if FALSE /* Finally, clear the killbits again. Probably isn't * needed, as the UPS should have already understood. */ ioctl (line, TIOCMGET, &bits); bits = (bits & killmask) | (~killbits & ~killmask); if (testMode) printf (" %i %i %i %i %i %i DEAD\n", (bits & TIOCM_CTS) != 0, (bits & TIOCM_DSR) != 0, (bits & TIOCM_CD) != 0, (bits & TIOCM_RNG) != 0, (bits & TIOCM_DTR) != 0, (bits & TIOCM_RTS) != 0); ioctl (line, TIOCMSET, &bits); sleep (TESTINT); #endif } /* If you're in test mode, watch the output forever. */ while (testMode) { ioctl (line, TIOCMGET, &bits); printf (" %i %i %i %i %i %i DEAD\n", (bits & TIOCM_CTS) != 0, (bits & TIOCM_DSR) != 0, (bits & TIOCM_CD) != 0, (bits & TIOCM_RNG) != 0, (bits & TIOCM_DTR) != 0, (bits & TIOCM_RTS) != 0); sleep (TESTINT); } exit (EXIT_SUCCESS); } else if (!testMode) { syslog (LOG_NOTICE, "ignoring kill signal.\n"); closelog (); exit (EXIT_FAILURE); } else { printf ("powstatd: ignoring kill signal.\n"); exit (EXIT_FAILURE); } /* Should never reach here... */ exit (ERROR); } /* OK, status has in fact changed (and survived debouncing). */ if (lastStatus != newStatus) { /* Accept state change and enter change into logfile. */ if (newStatus == POK) { if (!testMode) syslog (LOG_NOTICE, "power restored.\n"); else printf ("powstatd: power restored.\n"); } else if (newStatus == PFAIL) { if (!testMode) syslog (LOG_NOTICE, "power is failing.\n"); else printf ("powstatd: power is failing.\n"); } else if (newStatus == PLOW) { if (!testMode) syslog (LOG_NOTICE, "low battery.\n"); else printf ("powstatd: low battery.\n"); } /* Next write appropriate status indicator to status file * (usually /etc/powerstatus) and then signal init. Note * that init must already know where to look! Also, if you * are running in test mode, don't actually signal init. */ if (!testMode) { sfp = fopen (sfile, "w"); fprintf (sfp, "%s", PSHOW (newStatus)); fclose (sfp); kill (1, SIGPWR); } /* Retain new status as current status. */ lastStatus = newStatus; } /* Broadcast your status, then sleep for the polling * interval. */ #ifdef SECURE if (mode == MASTER) tellSlaves (socket, PPORT, lastStatus, key); #else if (mode == MASTER) tellSlaves (socket, PPORT, lastStatus); #endif sleep (testMode ? TESTINT : POLLINT); } /* Should never execute. */ return (ERROR); } /********************************************************************** * Read in configuration file. We have two basic tasks to * accomplish. First, determine our mode (solo, master, or slave), * that is, if we are to watch a serial line (and which serial line) * or instead prod another machine for status. Second, if we are * monitoring a serial line, what bits correspond to OK, FAIL, LOW, * and KILL. We return the mode of the daemon as UNKNOWN, SOLO, * MASTER, or SLAVE. **********************************************************************/ #ifdef SECURE #ifdef ALPHA void readConfigFile (char *monitor, int *mode, unsigned int *key) #else void readConfigFile (char *monitor, int *mode, unsigned long int *key) #endif #else void readConfigFile (char *monitor, int *mode) #endif { char filename[MAXLINELEN + 1] = CONFIG; char line[MAXLINELEN + 1]; char temp[MAXLINELEN + 1]; struct hostent *temphost; int n; FILE *cfp; #ifdef SECURE struct stat fstatus; char *kbuffer; #endif #ifdef SECURE /* Make sure the configuration file is available, else punt. Note * that the secure version requires we first stat the file, so the * error given when configuration file is not found will differ from * the non-secure version, which won't fail until it attempts to * open the configuration file for reading. */ if (stat (filename, &fstatus) != 0) { if (!testMode) syslog (LOG_ERR, "can't stat configuration file (%d); quitting.\n", errno); else printf ("powstatd: can't stat configuration file (%d); quitting.\n", errno); exit (ERROR); } else if ((fstatus.st_mode & S_IRGRP) || (fstatus.st_mode & S_IWGRP) || (fstatus.st_mode & S_IXGRP) || (fstatus.st_mode & S_IROTH) || (fstatus.st_mode & S_IWOTH) || (fstatus.st_mode & S_IXOTH)) { /* As a security precaution, ensure that the configuration file * has appropriate permissions set. If not, issue a message and * punt, as the password could be compromised. */ if (!testMode) syslog (LOG_ERR, "configuration file permission error (must be go-rwx); quitting.\n"); else printf ("powstatd: configuration file permission error (must be go-rwx); quitting.\n"); exit (ERROR); } #endif /* Open the configuration file. If you can't find it, punt. */ if (!(cfp = fopen (filename, "r"))) { if (!testMode) syslog (LOG_ERR, "can't open %s for read; quitting.\n", filename); else printf ("powstatd: can't open %s for read; quitting.\n", filename); exit (ERROR); } /* While not EOF, read in the configuration file line by line. */ while (fgets (line, MAXLINELEN, cfp)) { if ((strlen (line) == strspn (line, " \t\n\r")) || (strncmp (&line[strspn (line, " \t")], "#", 1) == 0)) { /* Flush blank lines or lines starting with the comment * character (ignore tabs and spaces before the #). */ continue; } else if (sscanf (line, "watch %s", temp) == 1) { /* What to watch: serial line or master host? */ if (*mode == ERROR) { if ((snprintf (monitor, MAXLINELEN, "/dev/%s", temp) > 0) && (access (monitor, R_OK) != ERROR)) { /* OK, you're supposed to watch the serial line. We'll * assume you're in stand alone mode unless and until we * encounter any slave definitions. */ *mode = SOLO; } else if (access (monitor, F_OK) != ERROR) { /* Tried to specify a device to monitor for which * you do not have access. Generate an error, but * keep looking just in case there is some * redundancy in the configuration file. */ if (!testMode) syslog (LOG_NOTICE, "can't watch %s; access denied.\n", monitor); else printf ("powstatd: can't watch %s; access denied.\n", monitor); } else if ((temphost = gethostbyname (temp)) != NULL) { /* This is an internet host, so we must be a slave. */ *mode = SLAVE; /* Copy master host name into monitor string. */ snprintf (monitor, MAXLINELEN, "%s", temp); /* Set up the master host record. We can't rely on * the hostent returned by gethostbyname, since its * contents will change if gethostbyname is * reinvoked. */ memcpy (&hosts[MASTER].address, temphost->h_addr, temphost->h_length); hosts[MASTER].length = temphost->h_length; #ifdef DEBUG if (testMode) printf ("powstatd: slave of %s\n", inet_ntoa(*((struct in_addr *)temphost->h_addr))); #endif } else if (!testMode) syslog (LOG_NOTICE, "can't watch %s; no such host.\n", temp); else printf ("powstatd: can't watch %s; no such host.\n", temp); } else { /* This is the second "watch" line encountered. Issue a * warning and ignore it. */ if (!testMode) syslog (LOG_NOTICE, "multiple `watch' statments, ignoring %s.\n", temp); else printf ("powstatd: multiple `watch' statements, ignoring %s.\n", temp); } continue; } else if (sscanf (line, "slave %s", temp) == 1) { if (*mode == ERROR) { if (!testMode) syslog (LOG_NOTICE, "no line to watch yet; ignoring %s.\n", temp); else printf ("powstatd: no line to watch yet; ignoring %s.\n", temp); } else if (*mode == SLAVE) { if (!testMode) syslog (LOG_NOTICE, "slave can't watch serial line; ignoring %s.\n", temp); else printf ("powstatd: slave can't watch serial line; ignoring %s.\n", temp); } else if (nslaves >= MAXSLAVES) { if (!testMode) syslog (LOG_NOTICE, "too many slaves; ignoring %s.\n", temp); else printf ("powstatd: too many slaves; ignoring %s.\n", temp); } else if ((temphost = gethostbyname (temp)) != NULL) { /* Specifying a legal slave. There can be at most MAXSLAVES * of these. */ *mode = MASTER; /* Copy slave host name into appropriate slave string. */ snprintf (slaves[nslaves], MAXLINELEN, "%s", temp); /* Increment slave count; note slaves[] is 0-based while * hosts[] is 1-based as far as slaves are concerned. */ nslaves++; /* Set up the slave host record. We can't rely on the * hostent returned by gethostbyname, since its contents * will change if gethostbyname is reinvoked. */ memcpy (&hosts[nslaves].address, temphost->h_addr, temphost->h_length); hosts[nslaves].length = temphost->h_length; #ifdef DEBUG if (testMode) printf ("powstatd: master of %s\n", inet_ntoa(*((struct in_addr *)temphost->h_addr))); #endif } else if (!testMode) syslog (LOG_ERR, "can't find slave %s; no such host.\n", temp); else printf ("powstatd: can't find slave %s; no such host.\n", temp); continue; } #ifdef SECURE else if (sscanf (line, "password %s", temp) == 1) { /* Copy the first 128 bits of the password into the global * key variable, key[0] through key[4]. Note that key[5] * exists only so that the trailing '\0' has someplace to * go; we never really treat the password as a string after * this point. */ kbuffer = (char *) &key[0]; snprintf (kbuffer, 17, "%s", temp); } #endif else if (sscanf (line, "fail %s %d", temp, &n) == 2) { /* Power failure line and condition. */ if (strncmp (temp, "cts", 3) == 0) { failtest = ((n == 0) ? TIOCM_CTS : 0); failmask = TIOCM_CTS; } else if (strncmp (temp, "dsr", 3) == 0) { failtest = ((n == 0) ? TIOCM_DSR : 0); failmask = TIOCM_DSR; } else if (strncmp (temp, "dcd", 3) == 0) { failtest = ((n == 0) ? TIOCM_CD : 0); failmask = TIOCM_CD; } else if (strncmp (temp, "rng", 3) == 0) { failtest = ((n == 0) ? TIOCM_RNG : 0); failmask = TIOCM_RNG; } else if (!testMode) syslog (LOG_ERR, "ignoring bad `fail' specification %s.\n", temp); else printf ("powstatd: ignoring bad `fail' specification %s.\n", temp); continue; } else if (sscanf (line, "low %s %d", temp, &n) == 2) { /* Low battery line and condition. */ if (strncmp (temp, "cts", 3) == 0) { batttest = ((n == 0) ? TIOCM_CTS : 0); battmask = TIOCM_CTS; } else if (strncmp (temp, "dsr", 3) == 0) { batttest = ((n == 0) ? TIOCM_DSR : 0); battmask = TIOCM_DSR; } else if (strncmp (temp, "dcd", 3) == 0) { batttest = ((n == 0) ? TIOCM_CD : 0); battmask = TIOCM_CD; } else if (strncmp (temp, "rng", 3) == 0) { batttest = ((n == 0) ? TIOCM_RNG : 0); battmask = TIOCM_RNG; } else if (!testMode) syslog (LOG_ERR, "ignoring bad `low' specification %s.\n", temp); else printf ("powstatd: ignoring bad `low' specification %s.\n", temp); continue; } else if (sscanf (line, "kill %s %d", temp, &n) == 2) { /* UPS kill line and condition. */ if (strncmp (temp, "rts", 3) == 0) { killmask &= ~TIOCM_RTS; killbits |= ((n == 1) ? TIOCM_RTS : 0); } else if (strncmp (temp, "dtr", 3) == 0) { killmask &= ~TIOCM_DTR; killbits |= ((n == 1) ? TIOCM_DTR : 0); } else if (!testMode) syslog (LOG_ERR, "ignoring bad `kill' specification %s.\n", temp); else printf ("powstatd: ignoring bad `kill' specification %s.\n", temp); continue; } else if (strncmp (line, "kill break", 10) == 0) { /* UPS kill line special case: send break on TxD. We'll * indicate the special case by setting killmask to all 1's * and killbits to 0, as if there were no kill bits in use * at all. In effect, this makes sending the break signal * the default behavior if no kill specification is given in * the configuration file (should be harmless enough). */ killmask = ~0; killbits = 0; continue; } else if (sscanf (line, "init %s %d", temp, &n) == 2) { /* Initialize line and condition. */ if (strncmp (temp, "rts", 3) == 0) { setmask &= ~TIOCM_RTS; setbits |= ((n == 1) ? TIOCM_RTS : 0); } else if (strncmp (temp, "dtr", 3) == 0) { setmask &= ~TIOCM_DTR; setbits |= ((n == 1) ? TIOCM_DTR : 0); } else if (!testMode) syslog (LOG_ERR, "ignoring bad `init' specification %s.\n", temp); else printf ("powstatd: ignoring bad `init' specification %s.\n", temp); continue; } else if (!testMode) syslog (LOG_ERR, "ignoring unrecognized configuration line `%s'.\n", line); else printf ("powstatd: ignoring unrecognized configuration line `%s'.\n", line); } /* Close config file and return. */ fclose (cfp); } /********************************************************************** * Call this procedure to turn yourself into a daemon. See p418 of * Steven's book "Advanced Programming in the UNIX Environment" for * more details. **********************************************************************/ void daemonize () { int pid; /* Fork a process and have parent exit. Returns control to the shell * if this was called from the shell, and also makes sure the daemon * is not a process group leader. */ if ((pid = fork ()) < 0) { /* Oops! Can't fork. Abort. */ syslog (LOG_ERR, "can't fork daemon process; quitting.\n"); exit (ERROR); } else if (pid != 0) { /* Let the parent die. */ exit (EXIT_SUCCESS); } else { /* Create a new session and become the session leader. */ setsid (); /* Clear file mode creation mask. This allows daemon to open * files without any a priori constraints on file access. */ umask (0); /* OK, we're done. */ return; } } /********************************************************************** * Initialize serial line. Returns a file descriptor, suitable for ioctl. **********************************************************************/ int initLine (char *device) { int line; unsigned int bits; /* Open serial line. If unsuccessful, exit after logging the * error. */ if ((line = open (device, O_RDWR | O_NDELAY)) == ERROR) { if (!testMode) syslog (LOG_ERR, "can't open %s; quitting.\n", device); else printf ("powstatd: can't open %s; quitting.\n", device); exit (ERROR); } /* Set initial conditions if any were specified. */ if (setmask != ~0) { ioctl (line, TIOCMGET, &bits); bits = (bits & setmask) | setbits; ioctl (line, TIOCMSET, &bits); } /* Return file descriptor for open serial line. */ return (line); } /********************************************************************** * Read UPS status. Return one of POK, PFAIL, or PLOW. **********************************************************************/ int readLine (int line) { unsigned int bits; int status = POK; /* Obtain bits from serial line. */ ioctl (line, TIOCMGET, &bits); /* Interpret bits according to configuration. Note that the low * battery signal is orthogonal to power failure. If the power is * off, we want to signal PLOW, but if the power is on we want to * signal POK. */ if ((bits & failmask) ^ failtest) { if ((bits & battmask) ^ batttest) status = PLOW; else status = PFAIL; } if (testMode) printf (" %i %i %i %i %i %i %s\n", (bits & TIOCM_CTS) != 0, (bits & TIOCM_DSR) != 0, (bits & TIOCM_CD) != 0, (bits & TIOCM_RNG) != 0, (bits & TIOCM_DTR) != 0, (bits & TIOCM_RTS) != 0, PSHOW (status)); /* Return status. */ return (status); } /********************************************************************** * Open socket at a specified port address. Every daemon does this, if * only to ensure that only one daemon is running per machine. Solo * daemons then simply ignore the socket; slave daemons monitor the * socket for incoming UDP packets from their master, who uses the * socket to send its status to its slaves. **********************************************************************/ int openSock (unsigned short port) { int sock; struct sockaddr_in server; /* First make sure that the address structure is clear. */ memset (&server, 0, sizeof (server)); /* Create the socket. Note that we don't bother fetching the * protocol index number for UDP; instead, we use 0 as the third * argument on the assumption that UDP is indeed the only DGRAM * protocol available. */ if ((sock = socket (AF_INET, SOCK_DGRAM, 0)) < 0) { if (!testMode) syslog (LOG_ERR, "can't create socket; quitting.\n"); else printf ("powstatd: can't create socket; quitting.\n"); exit (ERROR); } /* Set up the address structure and bind to the socket. */ server.sin_family = AF_INET; /* Input address. */ server.sin_addr.s_addr = INADDR_ANY; /* Port number. */ server.sin_port = htons (port); /* We need to cast the second argument to bind to avoid a * warning. Note that we are relying on the fact that sockaddr and * sockaddr_in have the same size, which is enforced in the * appropriate system include files. */ if (bind (sock, (struct sockaddr *) &server, sizeof (server)) == ERROR) { if (!testMode) syslog (LOG_ERR, "socket bind error; quitting.\n"); else printf ("powstatd: socket bind error; quitting.\n"); exit (ERROR); } /* Success. No need to put the socket in listen mode, since we'll * just be watching for UDP packets. */ return (sock); } /********************************************************************** * Wait for a report from your master. **********************************************************************/ #ifdef SECURE #ifdef ALPHA int readSock (int socket, unsigned int *key) #else int readSock (int socket, unsigned long int *key) #endif #else int readSock (int socket) #endif { char message[MAXSTATLEN + 1]; struct sockaddr_in masteraddr; socklen_t masterlen = sizeof (masteraddr); #ifdef SECURE #ifdef ALPHA /* If you're running on a DEC Alpha, unsigned longs will be 64 * bits instead of 32 bits; use unsigned int instead. */ unsigned int timenow; unsigned int plain[2], cipher[2]; #else /* Use 32 bit words for XTEA. */ unsigned long int timenow; unsigned long int plain[2], cipher[2]; #endif #endif /* First make sure that the address structure is clear. */ memset (&masteraddr, 0, masterlen); /* Wait for a UDP packet (this blocks until you get one). For some * reason, MSG_WAITALL is not defined on linux systems; use 0 * instead. The cast to (struct sockaddr *) is just to keep the * compiler from complaining. */ if (recvfrom (socket, message, MAXSTATLEN, 0, (struct sockaddr *) &masteraddr, &masterlen) >= 0) { #ifndef SECURE /* OK, got a message. Make sure the address of the packet * received matches the address of your master. Note that this * may not work properly if your master has multiple NICs. */ #ifdef DEBUG if (testMode) printf ("Packet from %s", inet_ntoa(masteraddr.sin_addr)); #endif if (memcmp (&masteraddr.sin_addr, &hosts[MASTER].address, hosts[MASTER].length) != 0) { #ifdef DEBUG if (testMode) printf (" -> rejected, %s != %s (%s)\n", inet_ntoa(masteraddr.sin_addr), inet_ntoa(*((struct in_addr *) hosts[MASTER].address)), message); #endif return (ERROR); } #ifdef DEBUG /* Echo status to screen. */ if (testMode) printf (" -> %s\n", message); #else /* Echo status to screen. */ if (testMode) printf ("%s\n", message); #endif /* Parse the message string received and return appropriate * status. */ if (strncmp (message, "OK", 2) == 0) return (POK); else if (strncmp (message, "FAIL", 4) == 0) return (PFAIL); else if (strncmp (message, "LOW", 3) == 0) return (PLOW); #else /* OK, got a message. Don't worry too much about packet source * address since we'll rely on encryption for * authentication. Start by fetching current time for comparison. */ timenow = TSTAMP(); /* Now decrypt the message you received. */ #ifdef ALPHA sscanf (message, "%u:%u", &cipher[0], &cipher[1]); #else sscanf (message, "%lu:%lu", &cipher[0], &cipher[1]); #endif decipher (cipher, plain, key); #ifdef DEBUG if (testMode) printf ("Packet from %s [abs(%lu-%lu)=%lu]", inet_ntoa(masteraddr.sin_addr), plain[1], timenow, ((plain[1]>timenow)?plain[1]-timenow:timenow-plain[1])); #endif /* Compare timestamps. If you are within allowable clock drift, * accept the message, else exit and return an ERROR. Careful; * these are unsigned numbers, so taking abs of the difference * will not work as you might first think! */ if (((plain[1]>timenow)?plain[1]-timenow:timenow-plain[1]) > MAXCLOCKDRIFT) { if (!testMode) syslog (LOG_NOTICE, "rejecting expired packet.\n"); else #ifdef DEBUG printf (" -> rejected\n"); #else printf ("powstatd: rejecting expired packet.\n"); #endif return (ERROR); } /* Make sure the given status is legal (i.e., one of POK, PLOW, * or PFAIL). Otherwise, exit and return ERROR. */ if ((plain[0] != POK) && (plain[0] != PLOW) && (plain[0] != PFAIL)) { if (!testMode) syslog (LOG_NOTICE, "ignoring illformed packet.\n"); else #ifdef DEBUG printf (" -> ignored\n"); #else printf ("powstatd: ignoring illformed packet.\n"); #endif return (ERROR); } #ifdef DEBUG /* Echo status to screen. */ if (testMode) printf (" -> %s\n", PSHOW(plain[0])); #else /* Echo status to screen. */ if (testMode) printf ("%s\n", PSHOW(plain[0])); #endif /* Return the status; make sure you cast plain[0] to int when * returning to avoid compiler complaints. */ return ((int) plain[0]); #endif } return (ERROR); } /********************************************************************** * Send your status to your slaves. **********************************************************************/ #ifdef SECURE #ifdef ALPHA void tellSlaves (int socket, unsigned short port, int status, unsigned int *key) #else void tellSlaves (int socket, unsigned short port, int status, unsigned long int *key) #endif #else void tellSlaves (int socket, unsigned short port, int status) #endif { int i; char message[MAXSTATLEN + 1]; struct sockaddr_in slaveaddr; #ifdef SECURE #ifdef ALPHA /* If you're running on a DEC Alpha, unsigned longs will be 64 * bits instead of 32 bits; use unsigned int instead. */ unsigned int plain[2], cipher[2]; #else /* Use 32 bit words for XTEA. */ unsigned long int plain[2], cipher[2]; #endif #endif /* Make sure that the address structure is initially clear. */ memset (&slaveaddr, 0, sizeof (slaveaddr)); /* Set the unchanging parts of the address structure. */ slaveaddr.sin_family = AF_INET; slaveaddr.sin_port = htons (port); #ifdef SECURE /* Write your status and the current timestamp to plain text * variables. */ plain[0] = status; plain[1] = TSTAMP (); /* Encrypt the message. */ encipher (plain, cipher, key); /* Write your encrypted status and timestamp to the message * buffer. */ #ifdef ALPHA snprintf (message, MAXSTATLEN, "%u:%u", cipher[0], cipher[1]); #else snprintf (message, MAXSTATLEN, "%lu:%lu", cipher[0], cipher[1]); #endif #else /* Write your status to the message buffer. */ snprintf (message, MAXSTATLEN, "%s", PSHOW (status)); #endif /* Loop through the slaves and send one packet to each. */ for (i = 0; i < nslaves; i++) { /* Set up the host part of the address structure. */ memcpy (&slaveaddr.sin_addr, &hosts[i + 1].address, hosts[i + 1].length); /* Send the message. Only local errors will be caught; there is * no confirmation of actual receipt from slave. The cast to * (struct sockaddr *) is just to keep the compiler from * complaining. */ if (sendto (socket, message, sizeof (message), 0, (struct sockaddr *) &slaveaddr, sizeof (slaveaddr)) == ERROR) { if (!testMode) syslog (LOG_NOTICE, "can't inform %s [%d].\n", slaves[i], errno); else printf ("powstatd: can't inform %s [%d].\n", slaves[i], errno); } } } powstatd-1.5.1/powstatd.init0100644000175000017500000000307407245250512015120 0ustar segresegre#!/bin/sh # powstatd: a configurable UPS monitor. # Copyright (C) 1999-2001 The University of Iowa # Author: Alberto Maria Segre # segre@cs.uiowa.edu # S378 Pappajohn Building # The University of Iowa # Iowa City, IA 52242-1000 # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License as # published by the Free Software Foundation; either version 2 of the # License, or (at your option) any later version. # # This program is distributed in the hope that it will be useful, but # WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU # General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 # USA # Place this script in /etc/rc.d/init.d and make appropriate soft links to # /etc/rc.d/rc?.d # Works for RedHat Linux 5.2. # Source function library. . /etc/rc.d/init.d/functions # See how we were called. case "$1" in start) echo -n "Starting UPS services: " daemon powstatd touch /var/lock/subsys/powstatd echo "" ;; stop) echo -n "Shutting down UPS services: " killproc powstatd rm -f /var/lock/subsys/powstatd # Shut off UPS if you are on battery power (ignored otherwise). powstatd -k echo "" ;; status) /sbin/pidof powstatd ;; *) echo "Usage: powstatd {start|stop|status}" exit 1 esac powstatd-1.5.1/powstatd.ok0100644000175000017500000000210707245250512014562 0ustar segresegre#!/bin/sh # powstatd: a configurable UPS monitor. # Copyright (C) 1999-2001 The University of Iowa # Author: Alberto Maria Segre # segre@cs.uiowa.edu # S378 Pappajohn Building # The University of Iowa # Iowa City, IA 52242-1000 # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License as # published by the Free Software Foundation; either version 2 of the # License, or (at your option) any later version. # # This program is distributed in the hope that it will be useful, but # WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU # General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 # USA # Called by init when powstatd detects that power has been restored. /sbin/shutdown -c "Power restored: going off battery power." powstatd-1.5.1/powstatd.fail0100644000175000017500000000210707245250511015063 0ustar segresegre#!/bin/sh # powstatd: a configurable UPS monitor. # Copyright (C) 1999-2001 The University of Iowa # Author: Alberto Maria Segre # segre@cs.uiowa.edu # S378 Pappajohn Building # The University of Iowa # Iowa City, IA 52242-1000 # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License as # published by the Free Software Foundation; either version 2 of the # License, or (at your option) any later version. # # This program is distributed in the hope that it will be useful, but # WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU # General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 # USA # Called by init when powstatd detects a power failure. /sbin/shutdown -f -h +5 "Power failure: system operating on batteries." & powstatd-1.5.1/powstatd.dumb0100644000175000017500000000364707245250511015111 0ustar segresegre#!/bin/sh ## Solaris and IRIX do not have the "robust" amount of power status ## notifications that Linux is "blessed" with, they only have two: ## powerfail and powerwait. As such, init on these platforms can only ## run one script when it recieves a SIGPWR, so the script will have to ## perform the checks that Linux's init normally does. ## ## To implement, add/change the "powerfail" entry in /etc/inittab to read: ## ## p3:s1234:powerfail:/etc/powstatd.nonlinux # System commands.. awk=/bin/awk cat=/bin/cat grep=/bin/grep kill='/usr/bin/kill -9' ## trap(1) on Solaris doesn't seem to work.. ps=/bin/ps test=/bin/test uname=/bin/uname STATUS=`$cat /etc/powerstatus` || exit 1; ## Solaris uses runlevel 5 for powerdown, everybody else uses 0.. SHUTDOWN='/etc/shutdown -y -i0' $test `$uname -s` = 'SunOS' && SHUTDOWN='/etc/shutdown -y -i5' # killall(1M) on Solaris is really, really, really stupid, so use this # search-and-destroy pipeline to find shutdown processes instead. SHUTDOWNPID=`$ps -ef | $grep shutdown | $grep -v grep | $awk '{print $2}'` case $STATUS in 'FAIL') # When powstatd detects a power failure. # Assume that there's some life in the batteries still, and give it # five minutes to get the power restored. Check for existing shutdown # process to avoid superfluous SIGPWR triggers.. test "$SHUTDOWNPID" = "" && \ $SHUTDOWN -g300 "Power failure: system operating on batteries." ;; 'OK') # When powstatd detects that power is restored. # "shutdown -c" is a foreign notion to Solaris/IRIX, so you have to # find and kill the shutdown process to cancel a shutdown. Also, # removing the /etc/nologin file is always beneficial to users # wishing to login. test "$SHUTDOWNPID" != "" ] && $kill $SHUTDOWNPID rm -f /etc/nologin ;; 'LOW') # When powstatd detects that backup battery power is low. test "$SHUTDOWNPID" = "" && $kill $SHUTDOWNPID $SHUTDOWN -g0 "Battery failure: no juice left!" ;; esac powstatd-1.5.1/powstatd.low0100644000175000017500000000220507245250512014751 0ustar segresegre#!/bin/sh # powstatd: a configurable UPS monitor. # Copyright (C) 1999-2001 The University of Iowa # Author: Alberto Maria Segre # segre@cs.uiowa.edu # S378 Pappajohn Building # The University of Iowa # Iowa City, IA 52242-1000 # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License as # published by the Free Software Foundation; either version 2 of the # License, or (at your option) any later version. # # This program is distributed in the hope that it will be useful, but # WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU # General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 # USA # Called by init when powstatd detects that backup battery power is low. /sbin/shutdown -c "Battery failure: rescheduling shutdown." /sbin/shutdown -f -h now "Battery failure: no juice left!" powstatd-1.5.1/powstatd.man0100644000175000017500000004301007247220711014722 0ustar segresegre'\" t .\" powstatd: a configurable UPS monitor. .\" Copyright (C) 1999-2001 The University of Iowa .\" Author: Alberto Maria Segre .\" segre@cs.uiowa.edu .\" S378 Pappajohn Building .\" The University of Iowa .\" Iowa City, IA 52242-1000 .\" .\" This program is free software; you can redistribute it and/or .\" modify it under the terms of the GNU General Public License as .\" published by the Free Software Foundation; either version 2 of the .\" License, or (at your option) any later version. .\" .\" This program is distributed in the hope that it will be useful, but .\" WITHOUT ANY WARRANTY; without even the implied warranty of .\" MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU .\" General Public License for more details. .\" .\" You should have received a copy of the GNU General Public License .\" along with this program; if not, write to the Free Software .\" Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 .\" USA .TH POWSTATD 8 %%DATE%% %%VERSION%% "Linux System Administrator's Manual" .SH NAME powstatd \- a configurable UPS monitor. .SH SYNOPSIS .B powstatd [ .BR \-t \ | .BR \-k ] .SH DESCRIPTION .I powstatd is a configurable UPS monitoring daemon designed to work with SysVinit (e.g., most Linux distributions). .PP .I powstatd monitors a serial connection from a "dumb" or "relay" UPS for power failures and shuts the machine down gracefully if the power remains off for more than a prespecified interval. .I powstatd can also be configured to allow a master machine to control several slave machines connected to the same UPS via a network connection. This allows you to run several machines off the same UPS, with only one of the machines actually reading the UPS status over the serial line. When compiled with appropriate options enabled, .I powstatd also provides security by means of fast encryption of master/slave communication to prevent malicious shutdown of slave systems. .PP .I powstatd has two options: .PP .TP .B \-t Test mode. Used to explore what a given UPS signals under various failure modes as an aid to constructing an appropriate configuration file. When operating in test mode, .I powstatd doesn't actually signal init, so while changes in UPS status are detected, they are not acted upon. See configuration information for more details. .TP .BR \-k Kill mode. Directs .I powstatd to attempt to shut down the UPS. Most UPS systems will ignore the shutdown signal unless the power is actually off. By shutting down the UPS, .I powstatd ensures that once the main power returns the UPS will automatically turn on and cause the machine to reboot. .SH CAVEATS .I powstatd uses the connectionless UDP protocol to communicate UPS status from master to slave. To keep just anyone from generating a UDP packet that will shut your slave machine down when encrypted master/slave communication is not in use, the slave checks the IP address of origin in the UDP packet it receives. If your master has more than one network card, a master with multiple addresses may provide an unexpected IP address, which results in the slave ignoring (legitimate) status reports. So when you have a machine with multiple network cards, you must either ensure that the master has a unique IP address (try connecting the single-NIC slave to the UPS and demoting the dual-NIC master to slave instead), or use encrypted master/slave communication, which does not need to check IP source addresses thanks to the cryptographic measures used. .\" FIXED in v1.5? .\" .\" Note also that there is a bug in some versions of the Linux kernel .\" that may keep the receiving end of a UDP connection from properly .\" setting the source IP address in the call to recvfrom. This would also .\" cause the slave to reject legitimate packets sent from the master; .\" if this is the behavior you observe, use the secure version of .\" .I powstatd .\" which skips the IP address check altogether. When using encrypted master/slave communication, .I powstatd also uses a timestamp to foil replay attacks. Outdated status messages (as defined by a compile-time constant) are simply rejected; thus master and slave clocks should be reasonably synchronized (using, for example, the NTP protocol) or valid status messages may be rejected. .SH CONFIGURATION .I powstatd is configured via a configuration file, .I /etc/powstatd.conf by default, that specifies the serial line behavior of the UPS under its various failure modes (note that, if compiled with appropriate security option enabled, .I powstatd will require that file permissions on the configuration file deny rwx access to "group" or "other"). To configure your UPS, follow these steps: .B 0. Edit the Makefile to suit your installation. In particular, the secure master/slave communication protocol is selected/deselected by making the appropriate change to the definition of CFLGS in the Makefile. Note that users outside the United States wishing to use the secure communication protocol will first need to download the public-domain ANSI C implementation of TEA, the .I Tiny Encryption Algorithm, from .I ftp://vader.eeng.brad.ac.uk/pub/crypto/xtea.c .B 1. Compile .I powstatd: .in +3 % make .in -3 or, if you are compiling on a DEC Alpha platform: .in +3 % make alpha .in -3 .B 2. Make sure your new UPS is completely charged and is connected to your machine via the serial monitoring line provided by the UPS manufacturer to your machine (if you did not get such a monitoring line with your UPS, see the the UPS-HOWTO for information on how to make one). .B 3. In order to configure .I powstatd, make sure your machine .B is not powered from the UPS but is instead plugged directly into the wall outlet (your UPS should still, however, be connected to the machine via the serial monitoring line). Instead, plug a desk light or radio into the UPS. .B 4. Determine what serial line is connected to your UPS and create an initial copy of .I /etc/powstatd.conf (you can look at the sample powstatd.conf.* files included in the distribution) containing a single line specifying the serial line, e.g.: .in +3 watch ttyS0 .in -3 .B 5. As root, run .I powstatd in test mode (you'll have to be root to have rw access to /dev/ttyS0 or whatever device corresponds to your UPS monitoring line). .in +3 % ./powstatd -t .in -3 Hit ^C to stop after you see something like: .in +3 .TS tab(#); l2 l2 l2 l4 l2 l4 l2 l4 l. CTS#DSR#DCD#RNG#RxD#TxD#DTR#RTS#STATUS 1#0#1#1#0#0#0#1#OK 1#0#1#1#0#0#0#1#OK 1#0#1#1#0#0#0#1#OK 1#0#1#1#0#0#0#1#OK .TE .in -3 The output describes the state of the serial connection between the UPS and the computer. Since the signaling needs of a UPS/computer connection are really quite low (just a few bits), we aren't really using the serial connection as it was designed to be used for higher-throughput applications. Instead, we'll use the individual control and transmission lines in the nine wire standard connector a bit differently. Of the nine wires, six are of particular interest: four "input" control lines (CTS, DSR, DCD, RNG) and two "output" control lines (DTR, RTS). The remaining three lines consist of an electrical ground and the two transmission lines (TxD and RxD, one running in each direction); the latter are typically used for asynchronous serial information rather than for control. Most "dumb" UPS systems ignore the transmission lines and operate only by toggling combinations of the four "input" wires to indicate the status of the UPS and the two "output" wires to send commands back to the UPS from the computer (for now, we'll assume that the current UPS is one of these). Since the UPS is fully charged and the power is on, we want to play with the configuration in .I /etc/powstatd.conf until STATUS reads OK, and not LOW or FAIL. Provided we are actually watching the correct serial line, getting STATUS to read OK entails setting initial values for the output lines DTR and RTS (of course, it makes no sense to try to set initial values on the input lines). Try adding permutations of init values like: .in +3 .nf init dtr 1 init rts 1 .fi .in -3 until you get the OK status reading. .B 6. Repeat step 4, but this time pull the plug on the UPS and then reinsert it after a few seconds. Observe changes in the values for the input lines; it should be easy to determine what line corresponds to power failure. For example: .in +3 .TS tab(#); l2 l2 l2 l4 l2 l4 l1 l. CTS#DSR#DCD#RNG#DTR#RTS#STATUS# 1#0#1#1#0#1#OK# 1#0#1#1#0#1#OK# 1#0#1#1#0#1#OK# 1#0#1#1#0#1#OK# 0#0#1#1#0#1#FAIL#<- plug pulled 0#0#1#1#0#1#FAIL# 0#0#1#1#0#1#FAIL# 0#0#1#1#0#1#FAIL# 1#0#1#1#0#1#OK#<- plug reinserted 1#0#1#1#0#1#OK# 1#0#1#1#0#1#OK# .TE .in -3 would indicate that CTS going to 0 corresponds to a power failure. If you leave the UPS unplugged long enough to discharge (this may take quite a while even with a good size light bulb!), the UPS should signal a battery failure in a similar fashion, e.g., .in +3 .TS tab(#); l2 l2 l2 l4 l2 l4 l. CTS#DSR#DCD#RNG#DTR#RTS#STATUS# 0#0#0#1#0#1#LOW 0#0#0#1#0#1#LOW 0#0#0#1#0#1#LOW 0#0#0#1#0#1#LOW .TE .in -3 .B 7. At this point, you should know the power failure and low battery signals. Now we must determine the appropriate UPS shutdown signal. Fortunately, for most UPS systems, there are only 4 possible simple signals. With the UPS on battery power (e.g., unplugged), try adding each of the following lines into .I /etc/powstatd.conf in turn: .in +3 .nf kill dtr 0 kill dtr 1 kill rts 0 kill rts 1 .fi .in -3 for each trial, try issuing the command: .in +3 % ./powstatd -k .in -3 one of the line specifications should cause the desk lamp or radio to turn off; that's the one you want. Important: be aware that many UPS have a "dead time" after the signal is sent before the UPS turns itself off; this "dead time" can be as long as 30-45 seconds! So don't be too impatient here or you won't know which signal is responsible for actually turning the UPS off. .B 8. At this point configuration should be complete. For example, for the Cyberpower Power99 325/385/450/500VA models, a reasonable .I /etc/powstatd.conf configuration file reads: .in +3 .nf watch ttyS0 fail cts 0 low dcd 0 init rts 1 init dtr 0 kill dtr 1 .fi .in -3 .B 9. If you have other machines running off the same UPS, include one or more slave entries specifying their names in the master's configuration file: .in +3 .nf slave slave1.domain.edu slave slave2.domain.edu .fi .in -3 The slave machines' configuration files .I /etc/powstatd.conf should contain a single line specifying the name of the master machine that is actually monitoring the UPS that also powers the slave: .in +3 .nf watch master.domain.edu .fi .in -3 If you intend to run more than 2 slaves off a very large UPS, you will need to adjust the MAXSLAVES parameter in the source code accordingly and then recompile. If .I powstatd is compiled with the appropriate security option enabled, encryption is used to protect slaves from malicious shutdown messages. An identical password directive should therefore appear in both master and slave configuration files: .in +3 .nf password MyPasswordHere .fi .in -3 In addition to encrypting status information, .I powstatd will encrypt generate and check timestamps in order to foil replay attacks. .B 10. Make sure your inittab file contains appropriate lines to invoke .I powstatd.fail, powstatd.low, and .I powstatd.ok: .in +3 .nf # UPS signals a power outage. pf:12345:powerfail:/sbin/powstatd.fail # UPS signals power restored before the shutdown kicks in. pr:12345:powerokwait:/sbin/powstatd.ok # UPS signals low battery power: emergency shutdown. pn:12345:powerfailnow:/sbin/powstatd.low .fi .in -3 .B 11. Edit scripts .I powstatd.ok, powstatd.fail, and .I powstatd.low to adjust time parameters, if desired. .B 12. "make install", then reboot the machine. If you don't want to reboot, issue instead: .in +3 .nf % /etc/rc.d/init.d/powstatd start % /sbin/init q .fi .in -3 .SS How It Works: .I powstatd is initiated as a daemon in runlevels 3 and 5. A UPS can only be in one of three states; OK, FAIL, or LOW. Usually, when the main power is on, the UPS is operating in the OK state; when power fails, the UPS changes to FAIL mode, from which it can either recover and return to OK (if the power is restored) or can move to LOW (if the battery starts running out of juice before the power returns). .I powstatd monitors the UPS condition. When the state changes, .I powstatd writes the new state of the UPS in .I /etc/powerstatus and then signals init (the mother of all processes) of the change in the UPS condition. The init process receives this notice (a SIGPWR interrupt) and checks .I /etc/powerstatus to see if it contains "OK", "FAIL" or "LOW". The contents of .I /etc/powerstatus tells init (which is configured by the .I /etc/inittab file) to run one of three scripts: .I powstatd.fail, powstatd.ok, or .I powstatd.low The init process then removes .I /etc/powerstatus so as not to be confused on subsequent interrupts. .B powstatd.fail .RS initiates a timed .I shutdown -h (halt) in background, on the assumption that if power is restored the shutdown can be cancelled. .RE .B powstatd.ok .RS cancels the running shutdown and notifies all users that power is restored and no shutdown is imminent. .RE .B powstatd.low .RS cancels the running shutdown and initiates an immediate .I shutdown -h in foreground; this means once the UPS tells you the battery is low, you will indeed shutdown (there is no recovery). .RE Note that as you halt the machine, the shutdown sequence invokes .I powstatd one last time, but this time with the kill flag (-k), forcing the UPS to turn off, but only if the UPS is indeed in either the FAIL or LOW state (in any case, most supplies will ignore the kill signal if power is still available). In this fashion, once the power eventually returns (even after a day or two), the system should automatically restart without intervention. .SS Troubleshooting: .B 1. If your machine doesn't seem to notice power status changes even when the UPS daemon is signalling them, try adjusting the location of the powerstatus file by changing the value of STATUS in the Makefile and recompiling. Some versions of the init process look in .I /var/log/powerstatus rather than the default .I /etc/powerstatus. .B 2. If your machine keeps shutting down even when the power is on, you're probably watching the wrong serial line. To recover, try rebooting in single user mode (issue "linux 1" at the LILO prompt) and disable .I powstatd by renaming .I /etc/powstatd.conf to something else. Reboot and you should be able to fix the configuration. .\" FIXED in v1.5? .\" If your slave machines keep rejecting seemingly valid status messages .\" from the master when not using encrypted master/slave communication, .\" you may be dealing with a kernel problem, where recvfrom fails to .\" properly report the source IP address of a UDP packet. .\" Try using the secure version of .\" .I powstatd .\" instead. .B 3. Some older UPS systems as well as some homebuilt cable connections (see the UPS HowTo) may require that UPS shutdown signals be sent on the transmission line rather than on one of the signaling lines. Usually, this implies that a serial break signal (e.g., a long series of zeros) is required for the UPS to shut down. For these situations, you can use the special configuration file command .in +3 .nf kill break .fi .in -3 to obtain the appropriate behavior. .B 4. If your slave machines keep rejecting seemingly valid status messages from the master when using encrypted master/slave communication, first make sure they are running the same version of the software. Otherwise, try changing the definition of "outdated" at compile time (MAXCLOCKDRIFT) and recompiling. If you are not using NTP or some other mechanism to ensure that master and slave clocks are reasonably synchronized, you may well be better off running powstatd compiled without -DSECURE. .B 5. Some older versions of init are not capable of invoking one of several different scripts (e.g., powstatd.ok, powstatd.fail, powstatd.low) on receipt of SIGPWR under different circumstances. Instead, they always invoke the same script, which must then handle the control logic (i.e., deciding whether to shutdown gracefully, abort a shutdown, or shutdown immediately) internally (SPARC/Solaris and SGI/Irix apparently fall in this catagory). If you have such a version of init, look at the sample script .I powstatd.dumb included in the distribution for an example of how to go about handling this case. .SH ACKNOWLEDGEMENTS .P I learned a lot from reading the publically-available source code for other UPS monitoring packages, like .I genpower, .I powerd and .I upsd. I wrote .I powstatd primarily because the exact combination of features or configuration options I required were not available with these other packages. .P Peter Galbraith (galbraithp@dfo-mpo.gc.ca), the .I powstatd Debian package maintainer, provided numerous suggestions and bug fixes, as did Philippe Troin (phil@fifi.org), who was the first to suggest using, e.g., MD5-based digital signatures to avoid malicious shutdowns. Nick Holgate (holgate@debian.org) suggested an extension to .I powstatd signaling capabilities in order to support UPS systems that require a break signal for shutdown. Nicolas Simonds (nic@bridge.com) provided information about changes to the code for both SPARC/Solaris and SGI/Irix. TEA, or the \fITiny Encription Algorithm\fP, is due to David Wheeler and Roger Needham of the Cambridge Computer Laboratory. Their implementation, used here, is in the public domain. .nf Alberto Maria Segre segre@cs.uiowa.edu S378 Pappajohn Building The University of Iowa Iowa City, IA 52242-1000. .fi powstatd-1.5.1/License0100644000175000017500000003542707002634311013674 0ustar segresegre GNU GENERAL PUBLIC LICENSE Version 2, June 1991 Copyright (C) 1989, 1991 Free Software Foundation, Inc. 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. Preamble The licenses for most software are designed to take away your freedom to share and change it. By contrast, the GNU General Public License is intended to guarantee your freedom to share and change free software--to make sure the software is free for all its users. This General Public License applies to most of the Free Software Foundation's software and to any other program whose authors commit to using it. (Some other Free Software Foundation software is covered by the GNU Library General Public License instead.) You can apply it to your programs, too. When we speak of free software, we are referring to freedom, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for this service if you wish), that you receive source code or can get it if you want it, that you can change the software or use pieces of it in new free programs; and that you know you can do these things. To protect your rights, we need to make restrictions that forbid anyone to deny you these rights or to ask you to surrender the rights. These restrictions translate to certain responsibilities for you if you distribute copies of the software, or if you modify it. For example, if you distribute copies of such a program, whether gratis or for a fee, you must give the recipients all the rights that you have. You must make sure that they, too, receive or can get the source code. And you must show them these terms so they know their rights. We protect your rights with two steps: (1) copyright the software, and (2) offer you this license which gives you legal permission to copy, distribute and/or modify the software. Also, for each author's protection and ours, we want to make certain that everyone understands that there is no warranty for this free software. If the software is modified by someone else and passed on, we want its recipients to know that what they have is not the original, so that any problems introduced by others will not reflect on the original authors' reputations. Finally, any free program is threatened constantly by software patents. We wish to avoid the danger that redistributors of a free program will individually obtain patent licenses, in effect making the program proprietary. To prevent this, we have made it clear that any patent must be licensed for everyone's free use or not licensed at all. The precise terms and conditions for copying, distribution and modification follow. GNU GENERAL PUBLIC LICENSE TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 0. This License applies to any program or other work which contains a notice placed by the copyright holder saying it may be distributed under the terms of this General Public License. The "Program", below, refers to any such program or work, and a "work based on the Program" means either the Program or any derivative work under copyright law: that is to say, a work containing the Program or a portion of it, either verbatim or with modifications and/or translated into another language. (Hereinafter, translation is included without limitation in the term "modification".) Each licensee is addressed as "you". Activities other than copying, distribution and modification are not covered by this License; they are outside its scope. The act of running the Program is not restricted, and the output from the Program is covered only if its contents constitute a work based on the Program (independent of having been made by running the Program). Whether that is true depends on what the Program does. 1. You may copy and distribute verbatim copies of the Program's source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice and disclaimer of warranty; keep intact all the notices that refer to this License and to the absence of any warranty; and give any other recipients of the Program a copy of this License along with the Program. You may charge a fee for the physical act of transferring a copy, and you may at your option offer warranty protection in exchange for a fee. 2. You may modify your copy or copies of the Program or any portion of it, thus forming a work based on the Program, and copy and distribute such modifications or work under the terms of Section 1 above, provided that you also meet all of these conditions: a) You must cause the modified files to carry prominent notices stating that you changed the files and the date of any change. b) You must cause any work that you distribute or publish, that in whole or in part contains or is derived from the Program or any part thereof, to be licensed as a whole at no charge to all third parties under the terms of this License. c) If the modified program normally reads commands interactively when run, you must cause it, when started running for such interactive use in the most ordinary way, to print or display an announcement including an appropriate copyright notice and a notice that there is no warranty (or else, saying that you provide a warranty) and that users may redistribute the program under these conditions, and telling the user how to view a copy of this License. (Exception: if the Program itself is interactive but does not normally print such an announcement, your work based on the Program is not required to print an announcement.) These requirements apply to the modified work as a whole. If identifiable sections of that work are not derived from the Program, and can be reasonably considered independent and separate works in themselves, then this License, and its terms, do not apply to those sections when you distribute them as separate works. But when you distribute the same sections as part of a whole which is a work based on the Program, the distribution of the whole must be on the terms of this License, whose permissions for other licensees extend to the entire whole, and thus to each and every part regardless of who wrote it. Thus, it is not the intent of this section to claim rights or contest your rights to work written entirely by you; rather, the intent is to exercise the right to control the distribution of derivative or collective works based on the Program. In addition, mere aggregation of another work not based on the Program with the Program (or with a work based on the Program) on a volume of a storage or distribution medium does not bring the other work under the scope of this License. 3. You may copy and distribute the Program (or a work based on it, under Section 2) in object code or executable form under the terms of Sections 1 and 2 above provided that you also do one of the following: a) Accompany it with the complete corresponding machine-readable source code, which must be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or, b) Accompany it with a written offer, valid for at least three years, to give any third party, for a charge no more than your cost of physically performing source distribution, a complete machine-readable copy of the corresponding source code, to be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or, c) Accompany it with the information you received as to the offer to distribute corresponding source code. (This alternative is allowed only for noncommercial distribution and only if you received the program in object code or executable form with such an offer, in accord with Subsection b above.) The source code for a work means the preferred form of the work for making modifications to it. For an executable work, complete source code means all the source code for all modules it contains, plus any associated interface definition files, plus the scripts used to control compilation and installation of the executable. However, as a special exception, the source code distributed need not include anything that is normally distributed (in either source or binary form) with the major components (compiler, kernel, and so on) of the operating system on which the executable runs, unless that component itself accompanies the executable. If distribution of executable or object code is made by offering access to copy from a designated place, then offering equivalent access to copy the source code from the same place counts as distribution of the source code, even though third parties are not compelled to copy the source along with the object code. 4. You may not copy, modify, sublicense, or distribute the Program except as expressly provided under this License. Any attempt otherwise to copy, modify, sublicense or distribute the Program is void, and will automatically terminate your rights under this License. However, parties who have received copies, or rights, from you under this License will not have their licenses terminated so long as such parties remain in full compliance. 5. You are not required to accept this License, since you have not signed it. However, nothing else grants you permission to modify or distribute the Program or its derivative works. These actions are prohibited by law if you do not accept this License. Therefore, by modifying or distributing the Program (or any work based on the Program), you indicate your acceptance of this License to do so, and all its terms and conditions for copying, distributing or modifying the Program or works based on it. 6. Each time you redistribute the Program (or any work based on the Program), the recipient automatically receives a license from the original licensor to copy, distribute or modify the Program subject to these terms and conditions. You may not impose any further restrictions on the recipients' exercise of the rights granted herein. You are not responsible for enforcing compliance by third parties to this License. 7. If, as a consequence of a court judgment or allegation of patent infringement or for any other reason (not limited to patent issues), conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot distribute so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not distribute the Program at all. For example, if a patent license would not permit royalty-free redistribution of the Program by all those who receive copies directly or indirectly through you, then the only way you could satisfy both it and this License would be to refrain entirely from distribution of the Program. If any portion of this section is held invalid or unenforceable under any particular circumstance, the balance of the section is intended to apply and the section as a whole is intended to apply in other circumstances. It is not the purpose of this section to induce you to infringe any patents or other property right claims or to contest validity of any such claims; this section has the sole purpose of protecting the integrity of the free software distribution system, which is implemented by public license practices. Many people have made generous contributions to the wide range of software distributed through that system in reliance on consistent application of that system; it is up to the author/donor to decide if he or she is willing to distribute software through any other system and a licensee cannot impose that choice. This section is intended to make thoroughly clear what is believed to be a consequence of the rest of this License. 8. If the distribution and/or use of the Program is restricted in certain countries either by patents or by copyrighted interfaces, the original copyright holder who places the Program under this License may add an explicit geographical distribution limitation excluding those countries, so that distribution is permitted only in or among countries not thus excluded. In such case, this License incorporates the limitation as if written in the body of this License. 9. The Free Software Foundation may publish revised and/or new versions of the General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. Each version is given a distinguishing version number. If the Program specifies a version number of this License which applies to it and "any later version", you have the option of following the terms and conditions either of that version or of any later version published by the Free Software Foundation. If the Program does not specify a version number of this License, you may choose any version ever published by the Free Software Foundation. 10. If you wish to incorporate parts of the Program into other free programs whose distribution conditions are different, write to the author to ask for permission. For software which is copyrighted by the Free Software Foundation, write to the Free Software Foundation; we sometimes make exceptions for this. Our decision will be guided by the two goals of preserving the free status of all derivatives of our free software and of promoting the sharing and reuse of software generally. NO WARRANTY 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. END OF TERMS AND CONDITIONS powstatd-1.5.1/History0100644000175000017500000000127607247220032013750 0ustar segresegrev1.5 (March 1, 2001) added signaling capability to handle some older UPS fixed timestamping bug (WARNING: master/slave must run same version!) fixed security problem with sleeping slave fixed recvfrom initialization bug v1.4.1 (December 16, 1999) fixed bug with secure mode timestamping v1.4 (December 4, 1999) fixed bug in multiple-slave configurations added (optional) TEA-based encryption to avoid malicious shutdowns cleaned up parsing of config file v1.3 (September 17, 1999) low battery indication bug fixed v1.2 (September 14, 1999) improved man page fixed return status v1.1 (August 23, 1999) rereleased, with permission, under GPL v1.0 (August 9, 1999) first public release powstatd-1.5.1/QuickStart0100644000175000017500000000064307247220160014400 0ustar segresegreQuickstart installation instructions: 0. Unpack the sources (if you're looking at this, you've already done so). 1. Produce powstatd.ps (a postscript copy of the man page): % make doc 2. Print powstatd.ps for full installation and configuration instructions. === Note: see notes for SPARC/Solaris and SGI/Irix. I don't have access to either kind of system, but have been told it can be made to work. powstatd-1.5.1/ReadMe0100644000175000017500000000253307012514672013447 0ustar segresegreDescription: Configurable UPS monitoring daemon Assuming you have a relay-based "dumb" UPS that signals your computer via a serial connection, you should be able to configure powstatd in just a few minutes. Powstatd is known to run with a variety of Cyberpower UPS supplies (Power99 325VA, 400VA, 500VA and 720VA, Power2000 1500VA, and some older 385VA and 450VA models) and various older APC units. Powstatd is easily configured, and can be expected to support most "dumb" UPS supplies. Powstatd can also be configured to allow a master machine to control (via a network connection) up to 2 (by default) additional slave machines connected to the same UPS. This allows you to run several machines off the same UPS, with only one of the machines actually reading the UPS status over the serial line. When compiled with security option enabled, powstatd will timestamp and encrypt all master/slave communication to protect slave machines from malicious shutdowns. Note: In order to use encrypted master/slave communication, users outside the United States will need to download the public-domain ANSI C implementation of TEA, the Tiny Encryption Algorithm, from ftp://vader.eeng.brad.ac.uk/pub/crypto/xtea.c before attempting to compile powstatd. ==== Alberto Maria Segre segre@cs.uiowa.edu S378 Pappajohn Building The University of Iowa Iowa City, IA 52242-1000 powstatd-1.5.1/ReadMe.sparc0100644000175000017500000000222607245250711014554 0ustar segresegreThanks to Nicolas Simonds (nic@bridge.com) for this information. Solaris: * First, you'll need to include the TTY defs for Solaris in powstatd.c: #ifdef SPARC #include /* TTY defs for Solaris */ #endif # Second, you'll need to define the type for the length arguments in socket calls (also in powstatd.c): #ifdef SPARC typdef int socklen_t; #endif * Finally, you'll need to add -lxnet to the compile in the Makefile along with the -DSPARC flag; assuming you are adding a new target "sparc" it would look something like this: sparc: $(SRC) $(INC) $(CRYPT) Makefile $(CC) $(CFLGS) -DSPARC -DSTATFILE=$(STATUS) -DCONFIG="\"$(CFGDIR)/$(CFG)\"" $(SRC) $(ACRYPT) -lxnet -o $(BIN) $(LIB) * Both Sun make and GNU make work fine. * SunWPro CC and GCC both issue warnings, but the code builds/runs fine. * Sun init issues only powerfail or powerwait on receipt of SIGPWR. Since you can only associate a single script to this signal, you'll need a "smarter" script that blends elements of powstatd.fail, powstatd.ok and powstatd.low into a single script. Check powstatd.dumb (its the init process that's dumb, not the script) for an example. powstatd-1.5.1/ReadMe.sgi0100644000175000017500000000315207245250723014230 0ustar segresegreThanks to Nicolas Simonds (nic@bridge.com) for this information. Irix: * First, you'll need to define the type for the length arguments in socket calls in powstatd.c: #ifdef SGI typdef int socklen_t; #endif * You'll also need to redefine inet_ntoa() for GCC. Apparently, the native inet_ntoa() always returns 0xFFFFFFFF regardless of the input address. This alternate won't work for IPv6 or on 64-bit machines, but is fine for Irix. Here's an alternative version (unremoursefully copied from "Dave" whoever that is): #ifdef SGI char *inet_ntoa (struct in_addr rcvd_addr) { unsigned int i, quad[4]; char *ipaddress; ipaddress = (char *) malloc(16); for ( i=0; i<4; i++ ) quad[i] = (rcvd_addr.s_addr << (i*8)) >> 24; sprintf(ipaddress, "%d.%d.%d.%d", quad[0], quad[1], quad[2], quad[3]); return ipaddress; } #endif * Don't forget to pass the -DSGI flag to the compiler in the Makefile; assuming you are adding a new target "sgi" it would look something like this: sgi: $(SRC) $(INC) $(ACRYPT) Makefile libdave.o $(CC) $(CFLGS) -DSGI -DSTATFILE=$(STATUS) -DCONFIG="\"$(CFGDIR)/$(CFG)\"" $(SRC) $(ACRYPT) -o $(BIN) $(LIB) * Use GNU make: SGI make is too stupid to cope with the Makefile. * GCC builds without complaint, MIPSPro CC not tested. * Irix init issues only powerfail or powerwait on receipt of SIGPWR. Since you can only associate a single script to this signal, you'll need a "smarter" script that blends elements of powstatd.fail, powstatd.ok and powstatd.low into a single script. Check powstatd.dumb (its the init process that's dumb, not the script) for an example. powstatd-1.5.1/powstatd.conf.apc0100644000175000017500000000244207245250511015641 0ustar segresegre# powstatd: a configurable UPS monitor. # Copyright (C) 1999-2001 The University of Iowa # Author: Alberto Maria Segre # segre@cs.uiowa.edu # S378 Pappajohn Building # The University of Iowa # Iowa City, IA 52242-1000 # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License as # published by the Free Software Foundation; either version 2 of the # License, or (at your option) any later version. # # This program is distributed in the hope that it will be useful, but # WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU # General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 # USA # Sample configuration file; appropriate for some older APC UPS. # Watch /dev/ttyS0 watch ttyS0 # DSR goes low when mains fail. fail dsr 0 # CTS goes low when battery is too low. low cts 0 # DTR must be set high at initialization. init dtr 1 # RTS is initially set low; pulling it high causes UPS to turn off if main # power is gone. init rts 0 kill rts 1 powstatd-1.5.1/powstatd.conf.cps0100644000175000017500000000247707245250511015673 0ustar segresegre# powstatd: a configurable UPS monitor. # Copyright (C) 1999-2001 The University of Iowa # Author: Alberto Maria Segre # segre@cs.uiowa.edu # S378 Pappajohn Building # The University of Iowa # Iowa City, IA 52242-1000 # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License as # published by the Free Software Foundation; either version 2 of the # License, or (at your option) any later version. # # This program is distributed in the hope that it will be useful, but # WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU # General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 # USA # Sample configuration file; appropriate for Cyber Power Systems Power99 and # Power2000 UPS. # Watch /dev/ttyS0 watch ttyS0 # CTS goes low when mains fail. fail cts 0 # DCD goes low when battery is too low. low dcd 0 # RTS must be set high at initialization. init rts 1 # DTR is initially set low; pulling it high causes UPS to turn off if main # power is gone. init dtr 0 kill dtr 1 powstatd-1.5.1/powstatd.conf.tpl0100644000175000017500000000245507246370244015710 0ustar segresegre# powstatd: a configurable UPS monitor. # Copyright (C) 1999 The University of Iowa # Author: Alberto Maria Segre # segre@cs.uiowa.edu # S378 Pappajohn Building # The University of Iowa # Iowa City, IA 52242-1000 # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License as # published by the Free Software Foundation; either version 2 of the # License, or (at your option) any later version. # # This program is distributed in the hope that it will be useful, but # WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU # General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 # USA # Sample configuration file; appropriate for TrippLite Internet Office 500. # Watch /dev/ttyS0 watch ttyS0 # CTS goes low when mains fail. fail cts 0 # DCD goes low when battery is too low. low dcd 0 # DTR must be set high at initialization. init dtr 1 # RTS is initially set low; pulling it high causes UPS to turn off if main # power is gone. init rts 0 kill rts 1 powstatd-1.5.1/powstatd.conf.master0100644000175000017500000000271407245250511016373 0ustar segresegre# powstatd: a configurable UPS monitor. # Copyright (C) 1999-2001 The University of Iowa # Author: Alberto Maria Segre # segre@cs.uiowa.edu # S378 Pappajohn Building # The University of Iowa # Iowa City, IA 52242-1000 # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License as # published by the Free Software Foundation; either version 2 of the # License, or (at your option) any later version. # # This program is distributed in the hope that it will be useful, but # WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU # General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 # USA # Sample configuration file; appropriate for Cyber Power Systems Power99 and # Power2000 UPS running as master. # Watch /dev/ttyS0 watch ttyS0 # CTS goes low when mains fail. fail cts 0 # DCD goes low when battery is too low. low dcd 0 # RTS must be set high at initialization. init rts 1 # DTR is initially set low; pulling it high causes UPS to turn off if main # power is gone. init dtr 0 kill dtr 1 # Slave specifications slave slave1.domain.edu slave slave2.domain.edu # Password for encryption password MyPasswordHere powstatd-1.5.1/powstatd.conf.slave0100644000175000017500000000211507245250511016205 0ustar segresegre# powstatd: a configurable UPS monitor. # Copyright (C) 1999-2001 The University of Iowa # Author: Alberto Maria Segre # segre@cs.uiowa.edu # S378 Pappajohn Building # The University of Iowa # Iowa City, IA 52242-1000 # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License as # published by the Free Software Foundation; either version 2 of the # License, or (at your option) any later version. # # This program is distributed in the hope that it will be useful, but # WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU # General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 # USA # Sample configuration file; appropriate for slave UPS. # Watch master watch master.domain.edu # Password for encryption password MyPasswordHere