[ale] APC UPS
Bob's ALE Mail
transam at cavu.com
Fri Aug 15 23:12:57 EDT 1997
This works for me, and IMHO is better than what is in the HOWTOs. I have
extensively tested it.
Bob Toxen
bob at cavu.com
transam at cavu.com [ALE & linux-ppp]
http://www.mindspring.com/~cavu
Fly-By-Day Consulting, Inc.
"Venimus, Vidimus, Dolavimus" (We came, we saw, we hacked)
------------------------------- start of ups.c -------------------------------
/*
* ups.c: program to shut down a Linux System on an Intel 486, etc. when
* the UPS indicates that the battery is running low during a power failure.
* My UPS is an APC Back-UPS 1250.
*
* TO INSTALL:
* 1. Search the source for AUINPUT and change as appropriate.
* 2. Compile and install in /usr/local/bin (or wherever).
* 3. Start in background near the end of /etc/rc.d/rc.local:
* /usr/local/bin/ups&
* 4. Reboot your system.
*
* This program is smart enough so that if your cable breaks or is removed
* it will not shut your system down shortly after booting.
*
* Unless modified, it will voice a message to your sound board saying that
* it is up. You will need to supply the sound clip grab mine from
*
* ftp:ftp.mindspring.com/~cavu/ups_low.au
*
* This program can be tweaked to work with any UNIX system, including most
* System V systems, where init catches the SIGPWR signal and shuts the system
* down.
*
* On other systems, the kill(1, SIGPWR) call can be replaced with
* system("/etc/init 0") or similar. Adjustments to the other notifica-
* tions, such as the wall or email notification may be desirable.
*
* Copyright 1994, 1995, 1996, 1997 Robert M. Toxen. All rights reserved,
* except as noted. Unlimited use on Linux and adhering to the GNU Copyleft
* agreement is hereby granted so long as this copyright notice remains in
* the source. (Other low volume usage frequently will be granted on
* request.)
*
* You may want to enhance to shut off the UPS after the system has shut down.
*
* Connections between UPS and Computer (plugs sold at computer stores)
* UPS 9-pin pin number Linux serial DB25
* -------------------- --------------------
* 4 Ground connect to 7 Ground
* 5 2/5 minute warning (N/O) connect to 8 & 20 CD & DTR
*
* On Linux (standard serial DTE line running the monitor program):
*
* pin signal color of my cable wires
* --- ------ -----------------------
* 7 GND Black (one switch contact)
* 8 CD Green (other switch contact)
* 20 DTR Blue (other switch contact)
*
* Notes:
* If your computer can shut down in about 90 seconds then set the UPS
* to a 2 minute warning. Otherwise set it to a 5 minute warning.
* I estimate that this UPS will power my system for about 6 minutes.
*
* The Linux FAQs have an alternate way of doing an automatic
* shutdown that is much more complicated.
*
* For further information contact:
* Bob Toxen
* Fly-By-Day Consulting, Inc.
* 4642 Bentley Place
* Duluth, GA 30096 USA
* bob at cavu.com
* +1 770-662-8321
*/
#include <stdio.h>
#include <signal.h>
#include <string.h>
#include <sys/ioctl.h>
#include <termios.h>
#include <syslog.h>
/*
* System must be up at least 5 minutes
* in order for shutdown sequence to begin.
*
* This is to guard against bugs, bad cables,
* etc. by preventing the program from being
* killed within 5 minutes in case of problems.
* Adjust for your system.
*/
#define MINUP (5*60)
/* Email path, for notification. */
#define MAILPATH "/usr/spool/mail/bob"
/* Email user, for notification. */
#define MAILUSER "bob"
#define AUINPUT "/home/bob/sounds/ups_low.au"
#define AUDEV "/dev/audio"
#define AUSIZE (8*1024)
/* Serial device to use: change yours! */
/* #define DEVICE "/dev/ttyS3" */
#define DEVICE "/dev/cua3"
char *dev = DEVICE;
int active;
extern int errno;
extern long time();
char aubuf[AUSIZE];
main(argc, argv)
int argc;
char **argv;
{
int fd;
FILE *fp;
int fdi;
int fdo;
char chr;
int i;
char *s;
int was;
long start;
long lowbat;
long delta;
char *when;
char *ctime();
argv[0][0] = 'B';
argc--;
argv++;
close(0);
close(1);
close(2);
open("/dev/null", 2);
dup(0);
dup(0);
signal(SIGCLD, SIG_IGN);
signal(SIGHUP, SIG_IGN);
signal(SIGINT, SIG_IGN);
signal(SIGQUIT, SIG_IGN);
signal(SIGTERM, SIG_IGN);
openlog("ups", LOG_PID, LOG_DAEMON);
syslog(LOG_INFO, "Starting");
fd = open(dev, 0);
if (fd < 0) {
syslog(LOG_ALERT, "OPEN FAILED on %s: errno=%d", dev, errno);
syslog(LOG_ALERT, "UPS exiting");
exit(1);
}
errno = 0;
if (ioctl(fd, TIOCMGET, &was) < 0) {
syslog(LOG_ALERT, "initial TIOCMGET FAILED on %s: errno=%d",
dev, errno);
syslog(LOG_ALERT, "UPS exiting");
exit(2);
}
if (!(was & TIOCM_CAR)) {
syslog(LOG_ALERT, "CD (Carrier Detect) is not initially on");
syslog(LOG_ALERT, "UPS exiting");
exit(3);
}
syslog(LOG_INFO, "Starting initial %d minute warmup", MINUP/60);
start = time(0);
for (;;) {
if (!active && time(0) > start + MINUP) {
syslog(LOG_INFO, "Ready for power failure");
active = 1;
}
if (ioctl(fd, TIOCMGET, &i) < 0) {
syslog(LOG_INFO, "ioctl failed: errno=%d", errno);
break;
}
#ifdef notdef
if (i != was)
printf(
"RTS=%s DTR=%s CD=%s RI=%s DSR=%s CTS=%s\7\n",
(i & TIOCM_RTS) ? "YES" : "no ",
(i & TIOCM_DTR) ? "YES" : "no ",
(i & TIOCM_CAR) ? "YES" : "no ",
(i & TIOCM_RNG) ? "YES" : "no ",
(i & TIOCM_DSR) ? "YES" : "no ",
(i & TIOCM_CTS) ? "YES" : "no ");
#endif /* notdef */
if (!(i & TIOCM_CAR))
break;
was = i;
sleep(5);
}
lowbat = time(0);
delta = lowbat - start;
when = ctime(&lowbat);
if (delta > 0 && delta < MINUP) {
syslog(LOG_INFO, "UPS on battery and running low");
syslog(LOG_INFO, "The system has only been up %ld seconds", delta);
syslog(LOG_INFO,
"It must be up at least %d seconds to guard against bugs",
MINUP);
syslog(LOG_INFO,
"We will assume a bug or cable problem and exit");
syslog(LOG_ALERT, "UPS on battery and running low");
syslog(LOG_ALERT, "The system has only been up %ld seconds", delta);
syslog(LOG_ALERT,
"It must be up at least %d seconds to guard against bugs",
MINUP);
syslog(LOG_ALERT,
"We will assume a bug or cable problem and exit");
exit(4);
}
syslog(LOG_INFO, "UPS on battery and running low");
syslog(LOG_ALERT, "UPS on battery and running low");
/* kill(1, SIGPWR); /* Possibly change for your system. */
fp = fopen(MAILPATH, "a");
if (fp) {
fprintf(fp,
"From UPS %sFrom: UPS\nDate: %sTo: %s\nSubject: Power Failure\n\nLow battery during Power Failure @ %s\n",
when, when, MAILUSER, when);
fclose(fp);
}
fdi = open(AUINPUT, 0);
if (fdi < 0)
goto didaudio;
fdo = open(AUDEV, 1);
if (fdo < 0) {
close(fdi);
goto didaudio;
}
while ((i = read(fdi, aubuf, AUSIZE)) > 0)
write(fdo, aubuf, i);
close(fdi);
close(fdo);
didaudio:;
sync();
sleep(1);
sync();
system("echo 'POWER FAILURE: exit IMMEDIATELY' | /etc/wall&");
sleep(10);
s = strchr(when, '\n');
if (s)
*s = '\0';
syslog(LOG_INFO, "Low battery during power failure at %s", when);
syslog(LOG_INFO,
"The system has been up for %ld seconds (%ld minutes) min=%ld",
delta, delta / 60L, MINUP);
syslog(LOG_INFO, "Shutting down gracefully");
close(fd);
/*
* Alternate shutdown methods if kill(1, SIGPWR) does not work on your system.
*/
syslog(LOG_ALERT, "Starting shutdown sequence now");
chdir("/");
/* execl("/etc/init", "/etc/init", "0", 0); */
execl("/sbin/shutdown", "shutdown", "-t", "10", "-r", "now",
"Power Failure And UPS Battery Is Now Low", 0);
syslog(LOG_ALERT, "Cannot \"/sbin/shutdown -t 10 -r now reason\": exiting");
exit(5);
}
------------------------------- end of ups.c -------------------------------
More information about the Ale
mailing list