From pa.dec.com!decwrl!sdd.hp.com!cs.utexas.edu!uunet!sparky!kent Tue Apr 9 08:02:13 PDT 1991 Article: 2173 of comp.sources.misc Path: pa.dec.com!decwrl!sdd.hp.com!cs.utexas.edu!uunet!sparky!kent From: art@pilikia.pegasus.com (Authur W. Neilson III) Newsgroups: comp.sources.misc Subject: v18i005: upsd - UPS monitor daemon, Part01/01 Message-ID: <1991Apr9.044203.8382@sparky.IMD.Sterling.COM> Date: 9 Apr 91 04:42:03 GMT Sender: kent@sparky.IMD.Sterling.COM (Kent Landfield) Organization: Sterling Software, IMD Lines: 686 Approved: kent@sparky.imd.sterling.com X-Checksum-Snefru: 93736b6d fe6ddccc 02693e2a a5e6707a Submitted-by: Authur W. Neilson III Posting-number: Volume 18, Issue 5 Archive-name: upsd/part01 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. Art -----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 '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 common.h 1 Common program header X funcs.c 1 Functions X main.c 1 Main program X ups 1 Init.d startup script END_OF_FILE if test 422 -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'\" \(942 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@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 XINITDIR = /etc/init.d XRC2DIR = /etc/rc2.d XRC2NUM = 22 X Xupsd: $(OFILES) X $(CC) $(CFLAGS) $(OFILES) -o $@ $(LIBES) X @strip $@ X @mcs -d $@ X Xinstall: upsd 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 ups $(INITDIR)/ups X chown root $(INITDIR)/ups X chgrp sys $(INITDIR)/ups X chmod 744 $(INITDIR)/ups X ln $(INITDIR)/ups $(RC2DIR)/S$(RC2NUM)ups 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 $(INITDIR)/ups $(RC2DIR)/S$(RC2NUM)ups X X# dependencies Xmain.o: main.c $(HFILES) Xfuncs.o: funcs.c $(HFILES) END_OF_FILE if test 942 -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@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@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@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@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 '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@pilikia.pegasus.com X** Sat Mar 30 1991 X*/ X X#include X#include X#include X#include X#include X#include X#include X#include 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'\" \(4629 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@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 time_t ticks; X char *p, *ct; X char msg_buf[80]; X X time(&ticks); X ct = ctime(&ticks); X X /* find newline in buffer */ X if ((p = strrchr(ct, '\n')) != NULL) X *p = NULL; /* and zap it */ X X sprintf(msg_buf, "%s -- %s\n", ct, 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 4629 -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@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 if test -f 'ups' -a "${1}" != "-c" ; then echo shar: Will not clobber existing file \"'ups'\" else echo shar: Extracting \"'ups'\" \(296 characters\) sed "s/^X//" >'ups' <<'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 <'ups'`; then echo shar: \"'ups'\" unpacked with wrong size! fi chmod +x 'ups' # end of 'ups' 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@pilikia.pegasus.com Bank of Hawaii Tech Support | UUCP: uunet!ucsd!nosc!pilikia!art exit 0 # Just in case... -- Kent Landfield INTERNET: kent@sparky.IMD.Sterling.COM Sterling Software, IMD UUCP: uunet!sparky!kent Phone: (402) 291-8300 FAX: (402) 291-4362 Please send comp.sources.misc-related mail to kent@uunet.uu.net.