UPS power failure daemon (long)

Daniel Ray norstar at tnl.UUCP
Sat Jul 22 16:23:25 AEST 1989


This is long. You are warned.

Hello again! I am posting this to comp.unix.questions instead of one of the
.sources groups, since the discussion for all this started here. I've inclu-
ded two source files, one for a bourne shell program, the other for a little
daemon that detects power failures. They both work in concert with each other.

I have recently purchased an uninterruptable power supply. Because I am sorta
poor, I opted for a cheapo unit that I bought thru mail order for $300. It
provides a clean 10 min. of battery protection, but no special feature to
tell the computer that a failure took place. There was a company offering such
a device/software package, costing $200. There are also several expensive
UPSs that have this capability. Having no extra money, I decided to create
my own.

It was surprisingly simple! Total cost $25. What I did was go to Radio Shack
and purchased:

	1. one 120VAC DPDT relay (can be attached directly to house current)
	2. one female DB-25 serial connector
	3. one small electronic experimenter's box
	4. one 6 foot wire with AC plug

You also need a serial cable, a little thin wire, a soldering iron, and a
drill if you want to mount the parts in the experimenters box. Once you
have the parts:

	a. drill an elongated hole in the experimenters box to mount the
	   serial connector. drill another hole for an extension cord-sized
	   wire for the plug-in. put the plug wire thru the hole and tie
	   a knot in it leaving about 4 inches of wire left for inside the
	   box.
	a. solder a wire onto pin 20 (DTR) of the serial connector.
	b. solder the other end onto a normally open (when power is applied
	   to the relay) contact of the relay.
	c. solder a very small piece of wire to pin 6 (DSR) of the serial
	   connector, and solder the other end of it to pin 8 (DCD).
	d. solder a longer wire to pin 8. solder the other end to the other
	   normally open contact on the relay. [This allows current to pass
	   from pin 20 to both pins 6 and 8 when AC power is lost to the
	   relay. When AC power is present, DCD is down on the port].
	e. solder the wires from the plug-in cable to each coil contact
	   on the relay.
	f. use super glue to glue the relay into the bottom of the box, so
	   that the exposed contacts don't touch anything. screw in the box
	   cover. you should have a serial port on the side, and a cable
	   coming out the top that plugs into the wall.
	g. then use a serial cable and attach to both the power sensor and
	   a modem-controllable serial port. this concludes the hardware
	   part.

Then, it comes down to a surprisingly simple software scheme. Similar to
the behavior of getty on a modem port, a compiled C program tries to open
the modem controled /dev/ttyXX file where the power sensor is cabled to.
Until DCD goes high on the port, the fopen() will freeze up and wait.
Once there is data carrier detect, it will proceed. The daemon will then
exec a shell program, which will either exec the daemon again, or start
a shutdown, depending on if the power outage is intermittent. On XENIX
systems you can use /dev/tty1A or /dev/tty2A for this. Included below
are the C and shell programs. Edit to suit your system, as lots of
system-specific things are in the shell program.

-------------------cut here for 'blackout.c' monitoring daemon -------------
/*
 *	@(#) blackout.c v1 21-July-1989
 *
 *	The Northern Lights (TNL v2)  Burlington, Vermont USA
 *	Dialins: 802-865-3614 at 300-2400 bps, login as 'new'
 *
 *	Author: Daniel Ray	uunet!uvm-gen!tnl!norstar
 *
 *	THIS PROGRAM IS FREELY RELEASED INTO THE PUBLIC DOMAIN.
 *	NO DISTRIBUTION RESTRICTIONS EXIST WHATSOEVER.
 *
 *
 *	Function: blackout is intended to be used with a UPS (uninterrup-
 *	table power supply or battery backup unit) and a free modem-controled
 *	serial port. If and when power fails, the UPS kicks in and provides
 *	battery power for a limited time. To inform the system of this
 *	problem, this program exists. When invoked from /etc/rc, blackout
 *	will fork to the background, and will test for a power failure
 *	by trying to open a /dev/tty?? file. It will sit and wait until
 *	the port's DCD goes high, when a blackout takes place. Then it
 *	execs a shell script. The script can either immediately shutdown
 *	or can sleep and fork another blackout daemon. When that 2nd
 *	daemon finds the port can be opened, it execs another shutdown script.
 *	The script can determine if it was recently invoked, and act
 *	accordingly.
 *
 *	Usage:  blackout [-d]		Use the -d option as a daemon, or
 *					invoke without it for a diagnostic
 *					of whether the power failed.
 *
 *	TAKE NOTICE: To use blackout, you must also construct a simple
 *	hardware device that attaches to a free modem-controled serial
 *	port on your machine. It consists of a 120 VAC relay, that is
 *	plugged into a wall outlet. A female DB-25 serial connector is
 *	connected to the relay as follows:
 *
 *	Solder a wire to Pin 20 (DTR) of the DB-25 connector. Attach it
 *	to a NO (normally open when power is on) contact on the relay.
 *	Connect a short wire between Pin 6 (DSR) and Pin 8 (DCD) on the
 *	serial connector. Then attach a wire from Pin 8 (DCD) to the other
 *	NC contact on the relay. Thus power not will flow from Pin 20 to Pins
 *	6 and 8 while power is available from the wall outlet.
 *
 *	Then connect a serial cable from this power detector to the serial
 *	connector for the port on your CPU. Now the /dev file for this
 *	tty can be opened when power fails. Until then the open is frozen
 *	and waits, similar to a getty process.
 */

static char Sccsid[] = "@(#) blackout v1 - created by daniel ray 07/21/89";

#include <stdio.h>
#include <signal.h>

/*		CONFIGURATION INFORMATION FOLLOWS			*/

/* POWERPORT is a link to the serial device that connects to the power
 * sensor device. It must be a modem-controlled port, i.e. the file can
 * only be opened if a data carrier detect is present. Root should do a
 * 'ln /dev/tty1A /dev/blackout' to link the port in question.
 */  

#define	POWERPORT	"/dev/blackout"

/* SHUTPROG is the name of a /bin/sh executable shell script, intended to
 * warn users and then shut the system down. If no script is found, the
 * program will terminate.
 */
 
#define SHUTPROG	"/etc/powerfail"

/*		END OF CONFIGURATION INFORMATION			*/


main(argc,argv)
	int argc;
	char *argv[];
{
	FILE	*file;
	int	pid, getuid();
	
	/* test to be sure only root executes the program, since root
	 * privileges are needed to shutdown the system.
	 */

	if(getuid() != 0){
		fprintf(stderr,"%s: must be invoked by root\n",argv[0]);
		exit(2);
	}

	/* routine below used for diagnostics on POWERPORT status.
	 * Will wait and wait until DCD goes high on POWERPORT, then
	 * will print a message and exit.
	 */

	if(argc < 2){
		if((file = fopen(POWERPORT, "r")) != NULL)   /* freezes! */
			fclose(POWERPORT);
		printf("power out\n");
		exit(1);
	}
	if(strcmp(argv[1],"-d") != 0){
		fprintf(stderr,"Usage: blackout [-d]\n");
		exit(3);
	}

	/* ensure safe and secure shell variables for root to use */

	putenv("IFS=' \t\n'");
	putenv("PATH=/bin:/usr/bin");

	/* program forks into background now. It waits until it can open
	 * the port, then execs SHUTPROG. If it is an intermittent failure
	 * it is up to the SHUTPROG to know the difference, and invoke
	 * blackout again in that event. Each invocation of blackout will
	 * ultimately end in the execing of SHUTPROG. (See example SHUTPROG).
	 */

	signal(SIGHUP, SIG_IGN);
		
	if ((pid = fork()) == 0){
		if((file = fopen(POWERPORT, "r")) != NULL)   /* freezes! */
			fclose(POWERPORT);
		execle ("/bin/sh", "-c", SHUTPROG, (char *) 0);
	} else {
		fprintf(stderr,"%s daemon is active\n",argv[0]);
		exit(0);
	}
}

-----------------------------cut here for 'powerfail' shell script -----------
:
# @(#) powerfail v1 07/21/89
# (X) The Northern Lights (TNL v2) Burlington, Vermont USA
# Author: Daniel Ray		uunet!uvm-gen!tnl!norstar
#
# Execd by /etc/blackout daemon to initiate system shutdown
#
# ------------------------------------------------------------------
# DEFINE THE BELOW VARIABLES TO MATCH YOUR STYLE
# the below file is used to check if the power failure is intermittent.
# This program will create it if it does not exist, wait a specified period,
# then exec /etc/blackout. If the power is still off, /etc/blackout will
# invoke this program again. If it finds the 'intermittent' file, it
# will initiate a shutdown. The intermittent file is erased slightly after
# the waiting period. Put this in a directory writable by only root.
intermittent=/systmp/intermittent
export intermittent
#
# the below variable is the duration, in seconds, between an initial invoca-
# tion and the invocation that would cause a shutdown. This guards against
# intermittent failures of only a few seconds to a couple minutes. Do not
# set it to be too long a time period.
duration=60
#
# the below file is the 'blackout' power failure daemon.
blackout=/etc/blackout
#
# the below file is a log file that is appended to for each detected failure
logfile=/usr/adm/blackout.log
# 						END OF CONFIGURATION SECTION
# --------------------------------------------------------------------------
#
PATH=/bin:/usr/bin:/etc ; export PATH
trap '' 1
#
# log invocation of this program for each detected failure
/bin/echo "POWER FAILURE `/bin/date`" >> $logfile
#
if /bin/test -f $intermittent ; then
# this is the second failure detection, so we shut down the system now
# PUT YOUR SHUTDOWN COMMANDS BELOW
 trap "continue" 2 3
 /usr/bin/oppost black hi_white K "Power failure shutdown!"
 /bin/echo "`date +'%D %T'` `logname` `tty` : blackout" >> /usr/adm/shut.log
 /bin/echo "`date`  `tty`  3o  root\tBlackout" >> /usr/oper/olog
 /bin/date > /etc/shutdate
 ORACLE_SID=Y
 ORACLE_HOME=/db
 export ORACLE_SID ORACLE_HOME
 /bin/su oracle -c '/db/bin/dbshut 1 -i'
 /bin/kill -3 1
 for sig in -15 -9 ; do
  pids=`/bin/ps -e | /bin/awk "NR > 3 && "'$2'" !~ /$tty/ { print "'$1'" }"`
  [ "$pids" ] || break
  /bin/kill $sig $pids
  /bin/sleep 5 ; done
 [ "$pids" ] && ps -e
 [ -f /etc/accton ] && /etc/accton
 /bin/sync
 mnt=`/etc/mount | /bin/grep -v root | /bin/sort +2r | /bin/sed "s/ on .*//"`
 for i in $mnt ; do
  /etc/umount $i ; done
 /bin/sync
 [ "$mnt" ] && /etc/mount
 /bin/sync
 /etc/haltsys
 /bin/echo "haltsys failed! going single user\007"
 /bin/kill -1 1
# END OF YOUR SHUTDOWN COMMANDS
#
# comes here if this is the first (and a possibly intermittent) power failure
else /bin/echo "`/bin/date`" > $intermittent
 /bin/echo "POSSIBLE POWER FAILURE SHUTDOWN, Logout now please" |
 /etc/wall 2>/dev/null
 /usr/bin/oppost black hi_white W "Power failure detected, possible shutdown"
 /bin/sleep $duration
 ( /bin/sleep 10 ; /bin/rm -f $intermittent ) & > /dev/null 2>&1
 exec $blackout -d 2>/dev/null
#
fi

------------------------end of programs --------------------------------------

Notes: be sure to read the config stuff in the C program and shell program to
set things up right. then test it. if the serial cable detaches, the system
will go on thinking it has power. if the plug comes loose from the detector,
however, it will shut down. the system will reboot if the power is out for
a length of time longer than the UPS holds out, or shorter than the test for
intermittent failure. if it is in between, the system STAYS down even if
power is restored (such as a 5 minute failure). the daemon is good for one
failure only, before it exits. the shell program must start up another if
it is an intermittent failure. logging and the like are left for the shell
program to do, to minimize overhead. the daemon detects a power failure
instantaneously, and does not need to periodically poll the port. the daemon
will place itself in background automatically when invoked. recommended for
use in /etc/rc.

Hope someone can use these!

norstar
The Northern Lights, Burlington Vermont             |      Give up.
tnl dialins: 802-865-3614 at 300-2400 bps.        ` | /    Surrender.
------------------------------------------      --- * ---  Lost in the
uucp: uunet!uvm-gen!tnl!norstar or                / | .    empty void.
{decvax,linus}!dartvax!uvm-gen!tnl!norstar          |      Not even dreaming.



More information about the Comp.unix.questions mailing list