[ale] ups.c
Bob Toxen
bob at cavu.com
Wed Dec 18 20:52:37 EST 1996
Here is the program I wrote that will shut down my Linux box when my
UPS is running low on power. I hate busy-loops but the trick that I've
used before, of getting the open() to hang until the UPS signals it is
running low on power doesn't work. I'm sure I could tweak the tty driver.
In any case it uses virtually no CPU time so it doesn't matter.
This program needs to be run as root. I installed it in /usr/local/bin
and start it with the following at the end of /etc/rc.d/rc.local:
/usr/local/bin/ups&
cp /home/bob/sounds/ups_up.au /dev/audio
ups_up.au says "UPS watch is up".
ups_low.au says "UPS battery is low. System is shutting down now."
Enjoy,
Bob Toxen
bob at cavu.com
http://www.mindspring.com/~cavu
http://www.mindspring.com/~cavu/sunset.html [Sunset Computer]
Fly-By-Day Consulting, Inc.
N79879 C-172 based PDK, Atlanta, GA
"Venimus, Vidimus, Dolavimus" (We came, we saw, we hacked)
--------------- ups.c follows -------------------------------
/*
* 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.
*
* 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 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 30136 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);
}
More information about the Ale
mailing list