UPS monitor daemon part 1 of 1
Art Neilson
root at pilikia.pegasus.com
Wed Apr 3 19:50:11 AEST 1991
The UPS monitor daemon or "upsd" watches the serial port
connected to an UPS and will perform an unattended shutdown
of the system if the UPS is on battery longer than a specified
number of minutes. Upsd needs to watch a tty with modem control
properties, and expects the UPS to raise DCD when it switches
to battery backup and drop DCD when it goes back to online.
Upsd was developed and tested under ISC with the FAS 2.08 driver
and an American Power Conversion SmartUPS 600, your milage may
vary on other OSes and UPSes.
-----8<----- cut here -----8<----- cut here -----8<----- cut here -----8<-----
#! /bin/sh
# This is a shell archive. Remove anything before this line, then unpack
# it by saving it into a file and typing "sh file". To overwrite existing
# files, type "sh file -c". You can also feed this as standard input via
# unshar, or by typing "sh <file", e.g.. If this archive is complete, you
# will see the following message at the end:
# "End of archive 1 (of 1)."
# Contents: MANIFEST Makefile README S22ups common.h funcs.c main.c
# Wrapped by root at pilikia on Tue Apr 2 23:40:57 1991
PATH=/bin:/usr/bin:/usr/ucb ; export PATH
if test -f 'MANIFEST' -a "${1}" != "-c" ; then
echo shar: Will not clobber existing file \"'MANIFEST'\"
else
echo shar: Extracting \"'MANIFEST'\" \(421 characters\)
sed "s/^X//" >'MANIFEST' <<'END_OF_FILE'
X File Name Archive # Description
X-----------------------------------------------------------
X MANIFEST 1 This shipping list
X Makefile 1 Upsd makefile
X README 1 Documentation
X S22ups 1 Rc2.d startup script
X common.h 1 Common program header
X funcs.c 1 Functions
X main.c 1 Main program
END_OF_FILE
if test 421 -ne `wc -c <'MANIFEST'`; then
echo shar: \"'MANIFEST'\" unpacked with wrong size!
fi
# end of 'MANIFEST'
fi
if test -f 'Makefile' -a "${1}" != "-c" ; then
echo shar: Will not clobber existing file \"'Makefile'\"
else
echo shar: Extracting \"'Makefile'\" \(816 characters\)
sed "s/^X//" >'Makefile' <<'END_OF_FILE'
X##
X# M a k e f i l e
X#
X# makefile for UPS monitor daemon
X#
X# Arthur W. Neilson III
X# art at pilikia.pegasus.com
X# Sat Mar 30 1991
X#
X
XCC = cc
XDEFS =
XCFLAGS = -O $(DEFS)
XCFILES = main.c funcs.c
XOFILES = main.o funcs.o
XHFILES = common.h
XLIBES =
XDESTDIR = /etc
X
Xupsd: $(OFILES)
X $(CC) $(CFLAGS) $(OFILES) -o $@ $(LIBES)
X @strip $@
X @mcs -d $@
X
Xinstall:
X cp upsd $(DESTDIR)/upsd
X chown root $(DESTDIR)/upsd
X chgrp sys $(DESTDIR)/upsd
X chmod 550 $(DESTDIR)/upsd
X
Xinstall_rc: install
X cp S22ups $(DESTDIR)/S22ups
X chown root $(DESTDIR)/S22ups
X chgrp sys $(DESTDIR)/S22ups
X chmod 744 $(DESTDIR)/S22ups
X
Xindent:
X @for f in $(CFILES); do \
X indent $$f; \
X done
X
Xkit:
X makekit -m
X
Xclean:
X rm -f upsd core *.o *.BAK Part*
X
Xclobber: clean
X rm -f $(DESTDIR)/upsd
X
X# dependencies
Xmain.o: main.c $(HFILES)
Xfuncs.o: funcs.c $(HFILES)
END_OF_FILE
if test 816 -ne `wc -c <'Makefile'`; then
echo shar: \"'Makefile'\" unpacked with wrong size!
fi
# end of 'Makefile'
fi
if test -f 'README' -a "${1}" != "-c" ; then
echo shar: Will not clobber existing file \"'README'\"
else
echo shar: Extracting \"'README'\" \(3910 characters\)
sed "s/^X//" >'README' <<'END_OF_FILE'
X Tue Apr 02 23:24:20 HST 1991 art at pilikia.pegasus.com Pg. 1
X
X
X INTRODUCTION
X
X The UPS monitor daemon or "upsd" watches the serial port
X connected to an UPS and will perform an unattended shutdown
X of the system if the UPS is on battery longer than a specified
X number of minutes. Upsd needs to watch a tty with modem control
X properties, and expects the UPS to raise DCD when it switches
X to battery backup and drop DCD when it goes back to online.
X Upsd was developed and tested under ISC with the FAS 2.08 driver
X and an American Power Conversion SmartUPS 600, your milage may
X vary on other OSes and UPSes.
X
X The C source was written for system V and hence will require
X some work to get it working under BSD or other UNIXes, the
X program is built via the Makefile. A number of configurable
X defaults are in common.h, you may want to hack that file before
X making upsd. Although the program can be run manually from the
X command line, users will most likely want the program to start
X automatically from their /etc/rc script (SYS5R2) or a script
X in /etc/rc2.d (SYS5R3). The Makefile by default installs upsd
X in the /etc directory when the install target is made.
X
X
X Tue Apr 02 23:24:20 HST 1991 art at pilikia.pegasus.com Pg. 2
X
X
X COMMAND LINE OPTIONS
X
X Upsd runtime behavior can be configured either on the command
X line or via environment variables. The command line options
X take precedence to the environment variable settings, and are
X as follows:
X
X usage: upsd [-d tty][-c cmd][-l log][-t min]
X -d tty pathname of UPS device
X -c cmd pathname of shutdown command
X -l log pathname of UPS log file
X -t min delay time in minutes
X
X The -d tty option must specify the full pathname (including the
X /dev/ prefix) to the tty device the UPS is on.
X
X Example:
X
X upsd -d /dev/ttyFM00
X
X The -c cmd option specifies the full pathname of the command
X to be executed to shut down the system. This command must
X be enclosed in quotes if it consists of 2 or more words.
X
X Example:
X
X upsd -d /dev/ttyFM00 -c "/etc/shutdown -y -g1 -i0"
X
X The -l option specified the logfile upsd will write it's event
X messages to, these messages give the date and time that the UPS
X daemon started, switched to battery, switched back to online,
X executed the shutdown command, or was terminated via SIGTERM.
X
X Example:
X
X upsd -d /dev/ttyFM00 -c "/etc/shutdown -y -g1 -i0" -l /etc/upslog
X
X Finally, the -t option specifies the number of minutes to allow
X the UPS to be on battery backup before executing the shutdown
X sequence. This number must be between 1 and 30. Be careful not
X to choose a value greater than the number of minutes of battery
X time your UPS supports with your current load. A value in the
X 5 to 10 minute range is probably sufficient.
X
X Example:
X
X upsd -d /dev/ttyFM00 -c "/etc/shutdown -y -g1 -i0" -l /etc/upslog -t 10
X
X Tue Apr 02 23:24:20 HST 1991 art at pilikia.pegasus.com Pg. 3
X
X
X ENVIRONMENT VARIABLES
X
X The following environment variables can be set if that interface
X is preferred to the command line options. Note again that command
X line options override the environment variable settings.
X
X
X Environment Equivalent Default
X Variable Command line option Value
X UPSPORT -p /dev/ttyFM00
X UPSSHUT -c "/etc/shutdown -y -g1 -i0"
X UPSLOG -l /etc/upslog
X UPSTIME -t 10
X
X Note that the compiled in default values can be altered in common.h
X and the program can be recompiled. If no command line options or
X environment variables exist, the defaults will be used. The table
X above gives the environment variables looked for by the program and
X their command line option counterparts. All the rules applying to
X the command line options apply to the environment variables as well.
X
X If you have any comments or suggestions regarding my program,
X send email to the following address:
X
X Arthur W. Neilson III
X INET: art at pilikia.pegasus.com
X UUCP: uunet!ucsd!nosc!pilikia!art
X
END_OF_FILE
if test 3910 -ne `wc -c <'README'`; then
echo shar: \"'README'\" unpacked with wrong size!
fi
# end of 'README'
fi
if test -f 'S22ups' -a "${1}" != "-c" ; then
echo shar: Will not clobber existing file \"'S22ups'\"
else
echo shar: Extracting \"'S22ups'\" \(296 characters\)
sed "s/^X//" >'S22ups' <<'END_OF_FILE'
X#ident "@(#)ups 1.0 - 91/03/30"
X# UPS monitor daemon
X
X# configure UPS daemon runtime
Xexport UPSPORT; UPSPORT=/dev/ttyFM00
Xexport UPSSHUT; UPSSHUT="/etc/shutdown -y -i0"
Xexport UPSLOG; UPSLOG=/etc/upslog
Xexport UPSTIME; UPSTIME=10
X
Xset `who -r`
Xif [ $9 = "S" -a -x /etc/upsd ]
Xthen
X /etc/upsd
Xfi
END_OF_FILE
if test 296 -ne `wc -c <'S22ups'`; then
echo shar: \"'S22ups'\" unpacked with wrong size!
fi
chmod +x 'S22ups'
# end of 'S22ups'
fi
if test -f 'common.h' -a "${1}" != "-c" ; then
echo shar: Will not clobber existing file \"'common.h'\"
else
echo shar: Extracting \"'common.h'\" \(1885 characters\)
sed "s/^X//" >'common.h' <<'END_OF_FILE'
X/*
X** c o m m o n . h
X**
X** common header file for UPS monitor daemon
X**
X** Arthur W. Neilson III
X** art at pilikia.pegasus.com
X** Sat Mar 30 1991
X*/
X
X#include <stdio.h>
X#include <string.h>
X#include <fcntl.h>
X#include <time.h>
X#include <sys/types.h>
X#include <sys/termio.h>
X#include <sys/signal.h>
X#include <sys/stat.h>
X
X#define ERR -1 /* error return value */
X#define SH "/bin/sh" /* shell to exec shutdown command */
X#define ROOT "/" /* root directory for chdir */
X#define CONSOLE "/dev/console" /* console device */
X#define SECS_PER_MIN 60 /* number of secs in a minute */
X#define MAX_TIME 30 /* maximum delay time allowed */
X#define SHUT_PERMS 0500 /* shutdown command permissions mask */
X#define LOG_PERMS 0600 /* logfile permissions on create */
X#define LOG_FLAGS O_WRONLY | O_CREAT | O_APPEND /* logfile open flags */
X
X/* default tuneable parameter values */
X#define UPS_PORT "/dev/ttyFM00" /* UPS port device name */
X#define UPS_SHUT "/etc/shutdown -y -g0" /* shutdown command */
X#define UPS_LOG "/etc/upslog" /* UPS log file pathname */
X#define UPS_TIME 10 /* default delay time (minutes) */
X
X/* environment variable names */
X#define UPSPORT "UPSPORT"
X#define UPSSHUT "UPSSHUT"
X#define UPSLOG "UPSLOG"
X#define UPSTIME "UPSTIME"
X
X/* UPS log messages */
X#define START_MSG "UPS daemon started"
X#define OPEN_MSG "Error opening UPS port"
X#define BATTERY_MSG "UPS switch to battery backup"
X#define ONLINE_MSG "UPS switch to online power"
X#define SHUTDOWN_MSG "Shutdown command executed"
X#define TERM_MSG "Caught termination signal"
X
X/* common globals */
Xextern char *ups_port; /* UPS device name */
Xextern char *ups_shut; /* system shutdown command */
Xextern char *ups_log; /* UPS log file name */
Xextern int ups_time; /* delay time before shutdown */
Xextern int ups_fd; /* UPS port descriptor */
Xextern int log_fd; /* log file descriptor */
END_OF_FILE
if test 1885 -ne `wc -c <'common.h'`; then
echo shar: \"'common.h'\" unpacked with wrong size!
fi
# end of 'common.h'
fi
if test -f 'funcs.c' -a "${1}" != "-c" ; then
echo shar: Will not clobber existing file \"'funcs.c'\"
else
echo shar: Extracting \"'funcs.c'\" \(4682 characters\)
sed "s/^X//" >'funcs.c' <<'END_OF_FILE'
X/*
X** f u n c s . c
X**
X** functions for UPS monitor daemon
X**
X** Arthur W. Neilson III
X** art at pilikia.pegasus.com
X** Sat Mar 30 1991
X*/
X
X#include "common.h"
X
X/*
X** g e t v a r s
X**
X** retrieve environment variables
X*/
Xvoid
Xgetvars()
X{
X char *s, *getenv();
X
X if ((s = getenv(UPSPORT)) != NULL)
X ups_port = s;
X if ((s = getenv(UPSSHUT)) != NULL)
X ups_shut = s;
X if ((s = getenv(UPSLOG)) != NULL)
X ups_log = s;
X if ((s = getenv(UPSTIME)) != NULL)
X ups_time = atoi(s) * SECS_PER_MIN;
X}
X
X/*
X** g e t o p t i o n s
X**
X** retrieve and process command line options
X*/
Xvoid
Xgetoptions(ac, av)
Xint ac;
Xchar *av[];
X{
X int c;
X
X void usage();
X
X extern char *optarg;
X extern int optind, opterr;
X
X /* parse the command line */
X while ((c = getopt(ac, av, "d:c:l:t:")) != EOF)
X switch (c) {
X case 'd': /* device option */
X ups_port = optarg;
X break;
X case 'c': /* command option */
X ups_shut = optarg;
X break;
X case 'l': /* logfile option */
X ups_log = optarg;
X break;
X case 't': /* time option */
X ups_time = atoi(optarg) * SECS_PER_MIN;
X break;
X case '\?': /* illegal option */
X usage(av[0]);
X exit(1);
X }
X}
X
X/*
X** c h k o p t i o n s
X**
X** check runtime options
X*/
Xvoid
Xchkoptions()
X{
X struct stat st;
X char *p, buf[64];
X
X /* UPS port must exist */
X if (stat(ups_port, &st) == ERR) {
X perror(ups_port);
X exit(1);
X }
X /* and must be character special */
X if ((st.st_mode & S_IFMT) != S_IFCHR) {
X fprintf(stderr, "%s not character special\n", ups_port);
X exit(1);
X }
X /* get command name out of shutdown command */
X strcpy(buf, ups_shut);
X if ((p = strtok(buf, " ")) == NULL)
X p = buf;
X
X /* shutdown command must exist */
X if (stat(p, &st) == ERR) {
X perror(ups_shut);
X exit(1);
X }
X /* and must be readable/executable by owner */
X if (!(st.st_mode & SHUT_PERMS)) {
X fprintf(stderr, "%s must be readable/executable by owner\n", ups_port);
X exit(1);
X }
X /* delay time must be > 0 and <= MAX_TIME */
X if (ups_time < 1 || ups_time > MAX_TIME) {
X fprintf(stderr, "time must be between 1 and %d\n", MAX_TIME);
X exit(1);
X }
X}
X
X/*
X** m k d a e m o n
X**
X** create daemon process
X*/
Xvoid
Xmkdaemon()
X{
X char c;
X
X void sigcatch();
X void writelog();
X void shutdown();
X
X if (!fork()) {
X
X /* close standard files */
X close(0); /* stdin */
X close(1); /* stdout */
X close(2); /* stderr */
X
X setpgrp(); /* disassociate from terminal */
X
X /* ignore interrupts */
X signal(SIGHUP, SIG_IGN);
X signal(SIGINT, SIG_IGN);
X signal(SIGQUIT, SIG_IGN);
X
X /* catch termination signal */
X signal(SIGTERM, sigcatch);
X
X /* and shutdown on alarm */
X signal(SIGALRM, shutdown);
X
X /* open log file for append */
X if ((log_fd = open(ups_log, LOG_FLAGS, LOG_PERMS)) == ERR)
X exit(1);
X
X writelog(START_MSG);
X
X /* open blocks on UPS switch to battery */
X if ((ups_fd = open(ups_port, O_RDWR)) == ERR) {
X writelog(OPEN_MSG);
X exit(1);
X }
X writelog(BATTERY_MSG);
X
X alarm(ups_time);/* set the alarm clock */
X
X /* read blocks on UPS switch to online */
X if (read(ups_fd, &c, 1) == ERR)
X exit(1);
X
X writelog(ONLINE_MSG);
X
X close(log_fd);
X close(ups_fd);
X
X mkdaemon();
X }
X}
X
X/*
X** s i g c a t c h
X**
X** catch termination signal
X*/
Xvoid
Xsigcatch()
X{
X writelog(TERM_MSG);
X close(log_fd);
X close(ups_fd);
X exit(1);
X}
X
X/*
X** w r i t e l o g
X**
X** write message to the UPS log file
X*/
Xvoid
Xwritelog(msg)
Xchar *msg;
X{
X struct tm *t;
X time_t ticks;
X char *p, ctime_buf[26];
X char msg_buf[80];
X
X time(&ticks);
X strcpy(ctime_buf, ctime(&ticks));
X
X /* find newline in buffer */
X if ((p = strrchr(ctime_buf, '\n')) != NULL)
X *p = NULL; /* and zap it */
X
X sprintf(msg_buf, "%s -- %s\n", ctime_buf, msg);
X write(log_fd, msg_buf, strlen(msg_buf));
X}
X
X/*
X** s h u t d o w n
X**
X** shutdown the system
X*/
Xvoid
Xshutdown()
X{
X void attach();
X
X writelog(SHUTDOWN_MSG);
X
X close(log_fd);
X close(ups_fd);
X
X attach(CONSOLE);
X
X chdir(ROOT);
X
X /* execute shutdown command */
X execlp(SH, SH, "-c", ups_shut, NULL);
X}
X
X/*
X** a t t a c h
X**
X** attach standard i/o to a device
X*/
Xvoid
Xattach(dev)
Xchar *dev;
X{
X int fd;
X
X /* close standard files */
X close(0); /* stdin */
X close(1); /* stdout */
X close(2); /* stderr */
X
X /* attach stdin to named device */
X if ((fd = open(dev, O_RDWR)) == ERR)
X exit(1);
X dup(fd); /* and stdout */
X dup(fd); /* and stderr */
X}
X
X/*
X** u s a g e
X**
X** display program usage
X*/
Xvoid
Xusage(s)
Xchar *s;
X{
X fprintf(stderr, "usage: %s [-d tty][-c cmd][-l log][-t min]\n", s);
X fprintf(stderr, "\t-d tty\t\tpathname of UPS device\n");
X fprintf(stderr, "\t-c cmd\t\tpathname of shutdown command\n");
X fprintf(stderr, "\t-l log\t\tpathname of UPS log file\n");
X fprintf(stderr, "\t-t min\t\tdelay time in minutes\n");
X}
END_OF_FILE
if test 4682 -ne `wc -c <'funcs.c'`; then
echo shar: \"'funcs.c'\" unpacked with wrong size!
fi
# end of 'funcs.c'
fi
if test -f 'main.c' -a "${1}" != "-c" ; then
echo shar: Will not clobber existing file \"'main.c'\"
else
echo shar: Extracting \"'main.c'\" \(692 characters\)
sed "s/^X//" >'main.c' <<'END_OF_FILE'
X/*
X** m a i n . c
X**
X** main program for UPS monitor daemon
X**
X** Arthur W. Neilson III
X** art at pilikia.pegasus.com
X** Sat Mar 30 1991
X*/
X
X#include "common.h"
X
X/* default tuneables */
Xchar *ups_port = UPS_PORT;
Xchar *ups_shut = UPS_SHUT;
Xchar *ups_log = UPS_LOG;
Xint ups_time = UPS_TIME;
X
X/* global descriptors */
Xint ups_fd;
Xint log_fd;
X
Xmain(argc, argv)
Xint argc;
Xchar *argv[];
X{
X void getvars();
X void getoptions();
X void chkoptions();
X void mkdaemon();
X
X getvars(); /* retrieve environment vars */
X getoptions(argc, argv); /* process command line options */
X chkoptions(); /* validate command line options */
X mkdaemon(); /* fork daemon process */
X}
END_OF_FILE
if test 692 -ne `wc -c <'main.c'`; then
echo shar: \"'main.c'\" unpacked with wrong size!
fi
# end of 'main.c'
fi
echo shar: End of archive 1 \(of 1\).
cp /dev/null ark1isdone
MISSING=""
for I in 1 ; do
if test ! -f ark${I}isdone ; then
MISSING="${MISSING} ${I}"
fi
done
if test "${MISSING}" = "" ; then
echo You have the archive.
rm -f ark[1-9]isdone
else
echo You still need to unpack the following archives:
echo " " ${MISSING}
fi
## End of shell archive.
exit 0
-----8<----- cut here -----8<----- cut here -----8<----- cut here -----8<-----
--
Arthur W. Neilson III | INET: art at pilikia.pegasus.com
Bank of Hawaii Tech Support | UUCP: uunet!ucsd!nosc!pilikia!art
More information about the Alt.sources
mailing list