Network UPS Tools (NUT)

The quick command to set the UPS battery date, after changing batteries is (note that /usr/local/ups/bin may just be /bin on some installations):

     /usr/local/ups/bin/upsrw -s battery.date=mm/dd/yy -u username -p password \
       upsname@localhost

This presupposes that the username and password are set in upsd.users and that "actions = set" are specified for the user. Also, upsd and the appropriate UPS driver must be up and running for the UPS in question.

If you are running a version of NUT that does not set the battery.date variable correctly for an APC SmartUPS, you can do it manually.

First you must make sure that you have a copy of "screen" installed on the system in question (for RedHat/CentOS use "yum install screen" or for Debian/Ubuntu us "apt-get install screen"). You must also make sure that the UPS driver is not running. Then, assuming that you have the UPS connected to /dev/ttyS0, do the following (note that there are no carriage returns used for any of the commands sent to the UPS):

     su
     screen /dev/ttyS0 2400,cs8
     Y
     (UPS echoes "SM")
     x
     (UPS echoes current date)
     -
     (UPS echoes nothing)
     mm/dd/yy
     (after about 5 seconds, UPS echoes "|", indicating EEPROM is changed)
     R
     (UPS echoes "BYE")
     <Ctrl-a>\
     (answer yes to exit screen)

Similarly, if you are running a version of NUT that does not set the UPS identifier correctly for the APC SmartUPS, you can set an up-to-eight-character identifier manually, using "screen" and assuming that you have the UPS connected to /dev/ttyS0, as follows:

     su
     screen /dev/ttyS0 2400,cs8
     Y
     (UPS echoes "SM")
     c
     (UPS echoes current UPS ID, probably UPS_IDEN for a new UPS)
     -
     (UPS echoes nothing, possibly including the "-", i.e. really nothing)
     upsname<Enter>
     (after about 5 seconds, UPS echoes "|", indicating EEPROM is changed;
      some versions echo "OK", instead)
     (on some UPS, you may have to press <Enter> twice)
     c
     (UPS echoes the new UPS ID)
     R
     (UPS echoes "BYE")
     <Ctrl-a>\
     (answer yes to exit screen)

Also, the NUT drivers may not return the UPS serial number so that upsrw can display it. If that's the case, and you need to find the serial number of an installed APC UPS (e.g. one whose back panel is in a hard to read spot), you can do so in the same fashion as described above for setting the date. Again, assuming that you have the UPS connected to /dev/ttyS0, do the following (note that there are no carriage returns used for any of the commands sent to the UPS):

     su
     screen /dev/ttyS0 2400,cs8
     Y
     (UPS echoes "SM")
     n
     (UPS echoes the serial number)
     R
     (UPS echoes "BYE")
     <Ctrl-a>\
     (answer yes to exit screen)

A full description of the APC SmartUPS protocol can be found at:

     http://www.apcupsd.com/manual/manual.html

If you want to get rid of the "Change the battery" message and the red light on the UPS panel, you can force the battery test to be rerun immediately. However, before you do this, wait for three or four hours after the new battery is installed to give the UPS time to charge it first. Then, run the battery test, which will reset everything, if the new battery is OK. To do this, use:

     /usr/local/ups/bin/upscmd -u username -p password upsname@localhost \
       test.battery.start

The battery test should run (you'll see all of the messages it usually generates) for a few seconds and all will be well.

The quick list of steps to take when setting up a new UPS on an existing platform is as follows.

Change the group ownership of the UPS port(s) to the ups user:

     chgrp ups /dev/ttyS2

If you have one of the later versions of Linux that use udev for dynamically creating the "/dev" device space, you will, as well, need to hack the udev rules for the tty device. These can be found in the /etc/udev/rules.d dirctory, typically in the file /etc/udev/rules.d/50-udev.rules. Add something to the effect of:

     # Special case for the UPS devices.
     KERNEL=="ttyS2",                NAME="%k", GROUP="ups", MODE="0660",
                                     OPTIONS="last_rule"

Add these lines before the general case rules for tty devices.

Alternately, later installs of NUT add a set of rules for the USB drivers in a file named 52_nut-usbups.rules. You could add the following to that file instead of hacking the existing 50-udev.rules:

     # Special case for the UPS devices
     KERNEL=="ttyS1", group="nut", MODE="0660"

Some serial ports will come up with the correct baud rate and interrupt address set automagically. After you've made the changes to the new serial port, as described above, to make it accessible to the UPS userid, you should check that the serial port is configured correctly (reboot the machine first, if you had to change the rules in /etc/udev). To do this use:

     su
     setserial /dev/ttySx

If the system figures things out and sets the serial ports correctly, all on its own, go with its settings because this will cause a lot less grief, in the long run. However, if the type of UART, port address and interrupt vector, along with the baud rate, aren't set correctly (usually looking at /dev/ttyS0 will give some clues), you should add some lines to /etc/rc.serial to set the serial port up at boot time. If it already exists, you can edit file but, if it doesn't already exist, you can just create it with your text editor.

To determine what interrupt numbers and port addresses to use, you should list the PCI devices like this:

     /sbin/lspci -v

For each serial port, figure out from the information given by lspci what values to use and then add something like this to /etc/rc.serial:

     setserial /dev/ttyS2 port 0xdf88 UART 16550A irq 225 Baud_base 115200

When you're done, the permissions on /etc/rc.serial should be:

     -rw-r--r--    1 root     root          138 Aug  6  2005 /etc/rc.serial

Add your new UPS to the file /etc/ups/ups.conf. For an APC SmartUPS, the following config information applies:

     [bevatron]
          driver = apcsmart
          port = /dev/ttyS2

Begin monitoring your new UPS by adding it to /etc/ups/upsmon.conf:

     MONITOR bevatron@localhost 0 lmmonitor ItsASecret master

If you like to be able to monitor the UPS from the Web, add its information to /etc/ups/hosts.conf:

     MONITOR bevatron@localhost "stargate UPS"
     LOGFILE /var/log/upslog.bevatron

If you monitor the UPS via a Web server on another machine, add its information to the /etc/ups/hosts.conf file there, too.

Add your new UPS to the list of UPS boxes that the startup script must start, in /etc/sysconfig/upsd:

     UPS_BOXES="fusion-reactor bevatron"

If the logs from this UPS are pushed to another machine for saving or for other reasons, make sure the following is in /etc/crontab:

     05,20,35,50 * * * * root /etc/ups/ftplogs

Pick some suitable times that won't collide with other machines sending their UPS files to the central server (if you care). Also, if the receiving server is rotating the copied logs (this need not be the case, since the sending server is rotating its logs and the copied log always replaces the log on the receiving server, thereby ensuring that rotation on the sending server is sufficient to guarantee that log never grows too big), you should time the push to be a few minutes after the receiving server rotates its logs so that it can get a good, rotated copy of the last log before the new log is sent over top of it. Typically, logrotate runs out of /etc/cron.daily which is usually run at 04:02 each day. Consequently, the value of five minutes past the hour (chosen above) is a good choice.

On the central server, make sure that the "ups" user exists and that its password is "ItsASecret". Create empty logfiles in /var/log, named after the new UPS and change their ownership to "ups":

     touch /var/log/upslog.bevatron /var/log/upslog.bevatron.1
     chown root:ups /var/log/upslog.bevatron
     chmod g+w /var/log/upslog.bevatron

If you want the UPS to be shown in the graph of UPS activity, add its name and full load power to /etc/ups/upsplot.pl:

     my @UPSNames = (                        # Names of UPS boxes                    
         "Bevatron",
              .
              .
              .
     my @UPSWatts = (                        # Full load wattages of UPS boxes       
         466,
              .
              .
              .

If you'd like the UPS to test the battery at periodic intervals (see the notes below about battery life), add the following to /etc/crontab:

     # Every two months, have the UPS check its battery.
     00 10 4 1,3,5,7,9,11 * root /etc/ups/batterytest bevatron

Once again, if you care, schedule the times for the battery test on days when the other UPS are not also testing their batteries.

Note that the UPS will usually come with automatic self-test turned on and set to every 14 days. This means that the UPS will run the battery test itself every 14 days, exactly 1209600 seconds after it is turned on. How convenient is that? Not to mention how hard is that on the batteries (see the notes below about battery life). If you'd rather the test was done when you decide and under your control, you should turn off this dubious feature like so:

     /usr/local/ups/bin/upsrw -s ups.test.interval=0 -u username -p password \
       upsname@localhost

To take care of the logfiles, add the new UPS' logfile to the logrotate config file (either /etc/logrotate.conf or /etc/logrotate.d/ups):

     /var/log/upslog.bevatron {
         notifempty
         missingok
         create 0664 root ups
         copytruncate
         postrotate
             echo HEADER: Bevatron >/var/log/upslog.bevatron
             /etc/ups/ftplogs bevatron.1
         endscript
     }

Finally, not all UPS come with their control parameters set correctly (especially if you've bought a refurbished one) so you may want to check that they are what you expect them to be:

     /usr/local/ups/bin/upsc upsname@localhost

To find out which parameters you can set, run upsrw with no options:

     /usr/local/ups/bin/upsrw upsname@localhost

If any of the settable parameters are set to values that you don't like, you can change them with upsrw, like this:

     /usr/local/ups/bin/upsrw -s ups.test.interval=0 -u username \
       -p password upsname@localhost

For a typical APC SmartUPS, you may wish to set the following:

     battery.alarm.threshold     L         (the SmartUPS 750 doesn't support
                                           "L".  The default is "0" which alarms
                                           when on battery.  You might want "N",
                                           which produces no alarms).
     battery.charge.restart 00
     battery.date                [batdate]
     battery.runtime.low 120
     input.sensitivity           H
     input.transfer.high 132       (for SmartUPS 750, use 133)
     input.transfer.low 103
     output.voltage.nominal 115       (only necessary on 240V units)
     ups.delay.shutdown 020       (for SmartUPS 750, 090 is the min)
     ups.delay.start 000
     ups.id                      [upsname]
     ups.test.interval           0         (see notes on periodic test, above)

To do a complete install, start by obtaining the latest release of NUT from http://www.networkupstools.org/. Unzip and untar the distro to a directory of your choice:

     tar -xvzf nut-2.6.5.tar.gz

Create a new user, under which the UPS tools can run (otherwise they run as "nobody"). For example:

     su
     /usr/sbin/useradd -c "UPS Monitoring Tools" -M -s /sbin/nologin ups

If you are using RedHat or one of its derivatives (e.g. CentOS), the useradd command will also create a group with the same name as the UPS user, by default. If this isn't the case, you can create the group like this:

     su
     /usr/sbin/groupadd ups

Later on in these notes, we'll describe how to protect the config files that contain secret passwords, etc. For now, if there are any users who should have access to the UPS files (such as apache, which may need access to display UPS statistics on a Web page), you should add them to the ups group in /etc/group, with your favorite editor.

Incidentally, if you have to create the group directly (as opposed to indirectly, when the userid is created -- e.g. on SuSE), you may have to modify the new userid so that its primary group is the new group. If this isn't done, setting group permissions on the various config files and executables (as described below) will have no effect. Do the following:

     su
     /usr/sbin/groupmod -g ups ups

If this will be the centralized server where all log files from other servers will be copied, you should set a password on the ups userid so that FTP from the other servers will work:

     su
     passwd ups ItsASecret

Also note that, although no shell logins are allowed (by using /sbin/nologin as the userid's shell), FTP will be doing logins (hence the need for a password). And, FTP will gratuitously try to CD to the userid's home directory when it does the login so a home directory must be provided and be set in /etc/passwd. The permissions on that directory must be set so that the ups userid can access it. If this directory is not created by useradd (i.e. because you picked the -M flag, to avoid copying all the crap from the new user template), do the following:

     su
     mkdir /home/ups
     chown ups:ups /home/ups
     chmod u=rwx,go= /home/ups

Add a directory where the UPS tools can store state information and make the new user the owner:

     mkdir /var/state  (if necessary)
     mkdir /var/state/ups
     chown ups:ups /var/state/ups
     chmod u=rwx,g=rx,o= /var/state/ups

Change the directory to the distribution directory and configure the build scripts:

     cd nut-2.6.5
     ./configure --with-user=ups --with-group=ups --sysconfdir=/etc/ups \
                 --mandir=/usr/share/man --with-cgi

Note that, if you want to monitor an SNMP UPS over the network, you must first make sure that the net-snmp development package (www.net-snmp.org) is installed (either via your system's package manager, probably by picking the net-snmp-devel package, or by downloading and installing the library from the Web site).

You can check that net-snmp is properly installed and available for NUT to use in building the SNMP drivers by issuing this command:

     net-snmp-config --version --base-cflags --libs

If the library is properly installed, you'll see something like this:

     5.3.0.1
     -DINET6 -O2 -fmessage-length=0 -Wall -D_FORTIFY_SOURCE=2 -g \
       -fno-strict-aliasing -fstack-protector-all -Dlinux \
       -I/usr/include/rpm -I/usr/include
     -L/usr/lib64 -lnetsnmp -lcrypto -lm -L/usr/lib -lwrap

Once you've installed the net-snmp library, go back to the cofigure step and add this option to the "./configure" line:

     ../configure ... --with-snmp ...

Note that, even if you do see apparently proper results from net-snmp-config, the configure step may fail, claiming that the library doesn't exist. This is because the code in the configure step that checks for the presence of the "init_snmp" entry point fails to compile properly, most likely due to a problem with the link libraries returned by net-snmp-config.

Even if the net-snmp library is properly installed, the requirement for other libraries (e.g. "-lwrap") in the link library parameters returned by net-snmp-config can cause the compile to fail and configure to assume that the library isn't present (even though it is).

If you experience this problem, "-lwrap" is most likely the cause. The net-snmp-config command returned a lib string that included "-lwrap" but libwrap has not been installed. You can test this fact with the following command:

     su
     find / -name libwrap\*

If the find doesn't locate any libwrap modules, that's the problem. You can get rid of libwrap (it doesn't seem to be required) by rerunning "./configure" without the "--with-snmp" parameter and, instead, repeating all of the parameters returned by net-snmp library, minus "-L/usr/lib -lwrap", using the "--with-snmp-libs" configure option, something like this:

     ../configure ... --with-snmp-libs="-L/usr/lib64 -lnetsnmp -lcrypto -lm"

Apply the patches for local source code changes or copy the local source code, if any, to the install directory before building. Currently, there are hacks to upsplot.pl, upsstats.c and upsmon.c (including upsmon.h). Put upsplot.pl in /etc/ups, after the "make install" is run (see below). Put the hacked ".c" and ".h" files in ./clients before doing the build.

Compile the source from the build directory, according to the INSTALL file and then install the build. Basically, you do the following, in the top level directory (but read the next two paragraphs first):

     make
     su
     make install

This should put most things in:

     /etc/ups
     /usr/local/ups/bin
     /usr/local/ups/sbin
     /usr/share/man

If you wish to use the upsimage.cgi, upsset.cgi and/or upsstats.cgi modules anywhere, perhaps in your UPS Status Web pages, make sure that you either install the dynamic link library modules libupsclient.so* somewhere in your load library tree (possibly user local lib), run ldconfig and then relink the .cgi modules, or use the statically-linked .cgi modules.

If you copy the dynamically-linked .cgi modules (from the nut-2.6.5/client directory) to your Web site tree (e.g. your cgi-bin directory), you will likely get an error that reads something like this:

     /usr/bin/ld: cannot open output file /var/www/MySite/cgi-bin/\
       .libs/2764-lt-upsstats.cgi: No such file or directory

As the loader tries to find the libupsclient.so module which was linked to upsstats.cgi from the .libs directory, relative to the client directory.

A good clue that you've gotten the dynamically-linked .cgi modules is their size. The dynamically-linked modules are 8K or 9K in size. Once again, we repeat that these modules won't work unless they are run from the exact directory in which they were built, the .libs directory is copied to the Web tree in the same relative location as they are to the .cgi modules in the build tree, or the dynamic link library libupsclient.so is copied to a public dynamic link directory and ldconfig is run before the .cgi modules are built.

We much prefer to use the statically-linked .cgi modules, mainly because using them is much less of a pain in the butt and the increase in size (another 75K) is essentially chump change for the three modules in question. These can be fround in the nut-2.6.5/client/.libs directory itself and are identified by their size (e.g. 95K to 105K). You can copy these three modules anywhere and they will run just fine. For example:

     cp nut-2.6.5/client/.libs /var/www/MySite/cgi-bin

Incidentally, the upsmon command can be used to force a shutdown of all of the master UPSs connected to the system. The way it is installed by the "make install" command, above, it is given permissions that allow anyone to execute it. In other words, any user could theoretically shut off all of the UPSs. Probably not what you intend. So, you might want to do this:

     su
     chgrp ups /usr/local/ups/sbin/upsmon
     chmod o= /usr/local/ups/sbin/upsmon

If you receive errors about "gd" during configure, go get it and install it before continuing. On some systems, you can do:

     su
     yum install gd gd-devel php-gd

Otherwise, you can get the source here:

     http://www.libgd.org/

In the event that you need libpng or zlib in order to compile gd, they can be found at these URLs:

     http://www.libpng.org/pub/png/pngcode.html
     http://www.gzip.org/zlib/

Also, if you're going to be using upsplot.pl, you will need to make sure that GD.pm is available. If not, you should install it from CPAN:

     perl -MCPAN -e shell
     install GD

After the build and install are successful, if you are using a serially-connected UPS, change the group ownership of the UPS serial port(s) to the ups user:

     chgrp ups /dev/ttyS0

If you have one of the versions of Linux that use udev for dynamically creating the "/dev" device space, you will, instead, need to hack the udev rules for the tty device. On earlier versions of Linux (e g. RHEL 5.x/CentOS 5.x), these can be found in the /etc/udev/rules.d directory, typically in the file /etc/udev/rules.d/50-udev.rules. For this file, add something to the effect of:

     # Special case for the UPS devices.
     KERNEL=="ttyS2",                NAME="%k", GROUP="ups", MODE="0660",
                                     OPTIONS="last_rule"

Add these lines before the general case rules for the tty devices.

Alternately, on later versions of Linux (e.g. RHEL 6.x/CentOS 6.x or Ubuntu) the best place to add your udev rules for NUT is in a separate rules file that you make up just for this purpose (that way, subsequent releases of NUT or the OS won't monkey with your rules for the serial port and you also won't be concerned by the fact that there isn't even a 50-udev.rules file in the first place). We suggest calling the file "52-nut-ttyups.rules. It should be created in the /etc/udev/rules.d directory. You should add something like the following to that file, depending on which serial port you need to use:

     # udev rules for NUT serial drivers
     # Special case for the UPS devices
     KERNEL=="ttyS1", group="ups", MODE="0660"

If you will be managing your UPS via SNMP, now is the time to install the network management card into the UPS, if required, and get the UPS set up. The default setup, according to the manufacturer's instructions, should be OK. However, you will need to take the necessary steps to assign a static IP address to the UPS/Network Card, since many network management interfaces come with DHCP as their default.

/etc/ups/ups.conf:

Copy the file ups.conf.sample and hack it to set up the machine/UPS configuration, per the instructions in the INSTALL file. For an APC SmartUPS, the following config information applies:

     [fusion-reactor]
          driver = apcsmart
          port = /dev/ttyS0

For an APC UPS with a Network Management Card (e.g. AP9606, AP9617, AP9618, AP9619) installed, this configuration should work:

     [fusion-reactor]
          driver = snmp-ups
          port = 192.168.1.20
          mibs = apcc
          community = private
          snmp_version = v1
          pollfreq = 10          (or whatever number of seconds you prefer)

/etc/ups/upsd.conf:

On older versions of NUT, copy the file upsd.conf.sample. It is probably OK as is but, if you'd like to be able to monitor this machine's UPS from your network, add something like:

     ACL homeworld 192.168.1.0/24
     ACCESS grant monitor homeworld

Or, if you don't want to be messing about and you want to be able to do anything from the local network (e.g. run battery tests), try replacing the existing rules with:

     ACL all 0.0.0.0/0
     ACL localhost 127.0.0.1/32
     ACL homeworld 192.168.1.0/24
     ACCEPT homeworld
     ACCEPT localhost
     REJECT all

On newer versions of NUT (e.g. version 2.4 and up), copy the upsd.conf.sample file and replace the LISTEN directives with:

     LISTEN 127.0.0.1
     LISTEN 192.168.1.123 3493

You should use the actual IP address of the machine where NUT is being installed in place of 192.168.1.123 (above). If the machine has two or more NICs and you want NUT to listen on all of them, add additional LISTEN directives for the IP address of each of them.

Note that, if you have an older config file and are upgrading to a newer version of NUT, you will need to remove all of the ACL, ACCEPT and REJECT rules and simply use LISTEN. Apparently, the designers of NUT have decided that it should get out of the security business and leave everything to the firewall. Consequently, the ACL, ACCEPT, etc. rules are no longer accepted and LISTEN is simply used to tell NUT which port to listen on.

After you've made all of your changes, the permissions on the file need to be altered, since the device driver will windge about them if they aren't correct. Set the permissions as follows:

     chmod g=r,o= /etc/ups/upsd.conf

/etc/ups/upsd.users:

Copy the file upsd.users.sample. Since this file contains the secret UPS password, it needs to be protected from prying eyes as follows:

     chgrp ups /etc/ups/upsd.users
     chmod g=r,o= /etc/ups/upsd.users

Add a user that will allow upsmon on the local host to shut down the machine, if the power goes south, as well as do periodic battery tests:

     [lmmonitor]
         password = ItsASecret
         allowfrom = localhost               (deprecated in later versions)
         instcmds = shutdown.return
         instcmds = test.battery.start
         actions = set
         upsmon master

/etc/ups/upsmon.conf:

Copy the file upsmon.conf.sample. Change the group ownership to the ups user, to keep the file safe from the riff raff, and give it group permissions:

     chgrp ups /etc/ups/upsmon.conf
     chmod g=r,o= /etc/ups/upsmon.conf

Make the following changes to the file to begin monitoring the UPS:

     NOTIFYCMD /etc/ups/notify
     RUN_AS_USER ups
     MONITOR fusion-reactor@localhost 1 lmmonitor ItsASecret master
     NOTIFYFLAG COMMBAD  SYSLOG+EXEC
     NOTIFYFLAG COMMOK   SYSLOG+EXEC
     NOTIFYFLAG FSD      SYSLOG+EXEC
     NOTIFYFLAG LOWBATT  SYSLOG+EXEC
     NOTIFYFLAG NOCOMM   SYSLOG+EXEC
     NOTIFYFLAG ONBATT   SYSLOG+WALL+EXEC
     NOTIFYFLAG ONLINE   SYSLOG+WALL+EXEC
     NOTIFYFLAG REPLBATT SYSLOG+EXEC
     NOTIFYFLAG SHUTDOWN SYSLOG+WALL+EXEC
     NOTIFYFLAG OVERTEMP SYSLOG+EXEC

If you want to monitor UPS temperature and you've applied the proper hacks (or the temperature hacks are included in your version of the source), add the following:

     # --------------------------------------------------------------------------
     # UPSOVERTEMP - Temperature (in Celcius) which is too high for operation
     #
     # upsmon will check all UPS that return temperature information against this
     # value.  If the UPS temperature exceeds this value, an OVERTEMP notification
     # will be generated.
     #
     # Note that certain UPS are renown for cooking and even burning up batteries
     # (some reports of spectacular battery fires have been received).  From
     # actual observed log data, it appears that prior to burning up the
     # batteries, the UPS internal temperature rises significantly.  Hence,
     # monitoring the UPS temperature can be a valuable tool towards detecting
     # battery cooking, before the UPS burns the place down (the UPS is supposed
     # to solve problems, not cause them, isn't it).
     #
     # Once again, typical observed internal temperatures are in the 40 to 50
     # degree Celcius range.  Observed temperatures of 80 degrees Celcius prior
     # to an actual battery failure are indicative of pending failure.  Thus, to
     # be safe, the the UPSOVERTEMP value should be set in the 60-70 degree
     # range.
     UPSOVERTEMP 60.0
     # --------------------------------------------------------------------------
     # OTWARNTIME - Time (in seconds) between each over temperature warning
     #
     # upsmon will check all UPS that return temperature information against the
     # UPSOVERTEMP value, above.  If the UPS temperature exceeds the UPSOVERTEMP
     # value, an OVERTEMP notification will be generated.  In addition, a
     # notification will be generated every OTWARNTIME seconds until the over
     # temperature situation is remedied.
     #
     # Since a UPS over temperature situation is a critical one (the batteries
     # could be destroyed, as could the UPS), an OVERTEMP notification will be
     # generated at regular intervals, as specified by OTWARNTIME.  The default
     # is every 3600 seconds.
     OTWARNTIME 3600

/etc/sysconfig/upsd:

If you use any of the following scripts, you will need to either create or update /etc/sysconfig/upsd. This file contains the options that tell the UPS scripts which UPS boxes to monitor, what to do about logging, etc. Here is an example:

     #
     # Configuration for the NUT UPS monitoring and power down daemons.
     #
     # The list of UPS boxes to be controlled are set by UPS_BOXES, for example:
     #
     #      "My_UPS"
     #      "UPS-1 UPS-2"
     #
     # For clustering support, the list of UPS boxes on the primary server is set
     # by UPS_BOXES_PRIMARY and the list of UPS boxes on the secondary server is
     # set by UPS_BOXES_SECONDARY.
     #
     # You may define all three parameters in this config file and the startup
     # script will choose the appropriate one, based on whether clustering is
     # enabled and, if so, which role the server is playing.
     #
     UPS_BOXES=""
     UPS_BOXES_PRIMARY="fusion-reactor"
     UPS_BOXES_SECONDARY="nuke-plant"
     #
     # If you would like to start logging of UPS statistics at regular intervals
     # to a log file too, define the location of the log file and the logging
     # interval.  Otherwise, if the LOG_PATH is set to an empty string, the
     # logging daemon isn't started.
     #
     # A separate file is created for each UPS that is monitored.  The name of the
     # file is created by appending the UPS name to the LOG_PATH value, separated
     # by a period.  For example:
     #
     #      LOG_PATH="/var/log/upslog"
     #      UPS_BOXES="UPS1 UPS1"
     #
     #      Log files = /var/log/upslog.UPS1, /var/log/upslog.UPS2
     #
     # The log interval is given in seconds by LOG_INTERVAL.
     #
     # The group that will be used to create log files is set by UPS_GROUP.  You
     # may find it useful to have your logfiles set to a different group and
     # given read/write permissions so that they can be accessed by UPS users.
     # Otherwise, root permissions are used.
     #
     LOG_PATH="/var/log/upslog"
     LOG_INTERVAL=30
     UPS_GROUP="ups"
     #
     # For secondary and/or standalone servers, if you'd like the UPS logs to be
     # copied to the primary server for consolidation purposes, define the
     # server's name here and the logging directory where they will be copied to.
     #
     # PRI_SERVER="stargate"
     # PRI_LOG_PATH="/var/log/upslog"

While we're at it, we'd like the logfiles to be created with the correct permissions, so let's create an empty one for each UPS before we proceed any further. Logrotate will take care of this when it rotates the log files but let's start with:

     su
     touch /var/log/upslog.fusion-reactor
     touch /var/log/upslog.nuke-plant
     chgrp ups /var/log/upslog.
     chmod g+w /var/log/upslog.

/etc/ups/batterytest:

Set up a script that will test a UPS' battery at regular intervals. This script can be scheduled from crontab:

     #! /bin/sh
     #
     # batterytest - Script to test the battery of a UPS at regular intervals.
     #
     # This script is used by cron to periodically test the battery of a UPS.
     #
     #
     # Define the install path for the UPS binaries.
     #
     INSTALL_PATH="/usr/local/ups"
     #
     # Source the UPS configuration.
     #
     if [ -f /etc/sysconfig/upsd ] ; then
        . /etc/sysconfig/upsd
     fi
     #
     # Source the clustering configuration.
     #
     if [ -f /etc/sysconfig/clustering ] ; then
         . /etc/sysconfig/clustering
     else
         SERVERROLE=Standalone
     fi
     if [ x"$SERVERROLE" == x ] ; then
         SERVERROLE=Standalone
     fi
     #
     # Determine which UPS boxes we can test.
     #
     if [ ${SERVERROLE} = "Primary" ] ; then
         UPS_BOXES=$UPS_BOXES_PRIMARY
     else
         if [ ${SERVERROLE} = "Secondary" ] ; then
             UPS_BOXES=$UPS_BOXES_SECONDARY
         fi
     fi
     #
     # For all of the UPS on this system, see which one we're testing.
     #
     for UPSBox in $UPS_BOXES; do
      if [ x"$UPSBox" == x"$1" ] ; then
          $INSTALL_PATH/bin/upscmd -u jgmonitor -p ItsASecret \
              ${1}@localhost test.battery.start >/dev/null 2>&1
      fi
     done

Change the permissions of the script, after you create it, to add execute permission and to hide it from regular users, since it contains the secret UPS password:

     su
     chgrp ups /etc/ups/batterytest
     chmod ug+x,o= /etc/ups/batterytest

/etc/ups/notify:

Create the following script to send event notifications to root, via email, whenever the UPS has something important to say. Also, if the event scheduling configuration file upssched.conf is defined, the upssched program will be invoked to start/stop the timers and execute the event handlers described in upssched.conf.

Note that any indentation in front of "ENDMSG" can only be tabs. If your lame-butt text editor sticks spaces at the front of any lines between "<<-ENDMSG" and "ENDMSG", the shell script will get a syntax error. Here is the script:

     #!/bin/sh
     #
     # A shell script that can be used by the UPS power monitor to send messages
     # to root when UPS events occur.  This script can also be used to
     # start/stop the timers and execute the event handlers described in the
     # upssched.conf config file.
     #
     #
     # Define the install path for the UPS binaries, etc.
     #
     INSTALL_PATH="/usr/local/ups"
     CONFIG_DIR="/etc/ups"
     #
     # Source the UPS configuration.
     #
     if [ -f /etc/sysconfig/upsd ] ; then
        . /etc/sysconfig/upsd
     fi
     #
     # Source the clustering configuration.
     #
     if [ -f /etc/sysconfig/clustering ] ; then
         . /etc/sysconfig/clustering
     else
         SERVERROLE=Standalone
     fi
     if [ x"$SERVERROLE" == x ] ; then
         SERVERROLE=Standalone
     fi
     #
     # Determine which UPS boxes we are logging to.
     #
     if [ ${SERVERROLE} = "Primary" ] ; then
         UPS_BOXES=$UPS_BOXES_PRIMARY
     else
         if [ ${SERVERROLE} = "Secondary" ] ; then
             UPS_BOXES=$UPS_BOXES_SECONDARY
         fi
     fi
     #
     # Write the event to the log, if there is one.  Also, start/stop any
     # associated timers and execute any event handlers that are defined in
     # uspssched.conf.
     #
     if [ x"$LOG_PATH" != x ] ; then
      timestamp=`date "+%Y/%m/%d %H:%M:%S"`
      for LogBox in $UPS_BOXES; do
          echo $UPSNAME | grep -q $LogBox
          matval=$?
          if [ $matval = 0 ] ; then
              echo $timestamp EVENT: $1 >> ${LOG_PATH}.$LogBox
              #
              # If there's a schedule config file, run the UPS scheduler.
              # The scheduler uses the $UPSNAME and $NOTIFYTYPE environment
              # variables to figure out what to do with this event, based on
              # the configuration in upssched.conf.
              #
              if [ -f ${CONFIG_DIR}/upssched.conf ] ; then
                  ${INSTALL_PATH}/sbin/upssched
              fi
          fi
      done

fi
#
# Some events we only log.
#
if ([ x"$NOTIFYTYPE" != xONBATT ]) && ([ x"$NOTIFYTYPE" != xONLINE ]) \

      && ([ x"$NOTIFYTYPE" != xREPLBATT ]) \
      && ([ x"$NOTIFYTYPE" != xSHUTDOWN ]) \
      && ([ x"$NOTIFYTYPE" != xOVERTEMP ]) ; then
      exit 0
     fi
     #
     # Email the message to root so that they can see everything that's going on.
     # After all, if you're omnipotent, you need to know everything.
     #
     HostName=`hostname`
     if [ x"$NOTIFYTYPE" != xSHUTDOWN ] ; then
      /bin/mail -s "Message from the UPS" root <<-ENDMSG
          The power monitor on $HostName has generated the following message:
          $1
          ENDMSG
     else
      /bin/mail -s "Urgent message from the UPS" root <<-ENDMSG
          The power monitor on $HostName has generated the following message:
          $1
          ENDMSG
     fi

Note that, in the above script, the two sets of lines between "/bin/mail -s ... <<-ENDMSG" and up to and including the line with "ENDMSG" must either not be indented at all or only indented with actual tab characters (not blanks). If they are not, the script will fail.

Also, don't forget to add execute permissions to the script after you create it:

     su
     chmod ugo+x /etc/ups/notify

But, be warned that, if a upssched.conf file exists, the notify script will run upssched. If the upssched config file defines an event handler that shuts down the system or the UPS, a malicious user could run the script to shut down your system, if the above permissions are applied to the script. It is better to use the following permissions:

     su
     chgrp ups /etc/ups/notify
     chmod ug+x,o= /etc/ups/notify

Once you've set up the notify script and set whatever permissions you choose, it wouldn't hurt try it out. For example:

     UPSNAME=bevatron; export UPSNAME
     NOTIFYTYPE=ONBATT; export NOTIFYTYPE
     /etc/ups/notify "This is a test"

Check that root receives the message and that the message gets logged to bevatron's log file.

If you'd like to schedule or directly execute other actions, in conjunction with any of the events associated with a UPS, the above notify script checks for the existence of the upssched.conf file in the UPS configuration directory (typically /etc/ups). If this config file exists, the notify script assumes that you've defined some actions therein and that they need to be executed by the upssched program.

The primary reason for creating a upssched.conf file is to implement a set of pessimistic shutdown rules that will shut down the system shortly after the UPS goes on battery, as a result of a power failure, well before the battery is fully depleted, thereby providing a reserve of battery capacity that can be used to ensure an orderly shutdown a second time around, if a second power failure occurs during startup, after an initial power failure. The pessimism stems from the belief that power failures will be frequent and closely spaced and, therefore, startup after a power failure is a dangerous time. The batteries will be depleted from the first failure. If a second failure happens during the subsequent restart, it is possible that the batteries could give out before the system has a chance to execute an orderly shutdown, thereby causing a loss of data, etc. In that case, shutting down early, while there's still plenty of juice left in the batteries can be a hedge against this situation.

/etc/ups/upssched.conf:

You can optionally create the upssched.conf to define actions to be taken when certain UPS events occur. If this file exists, the notify script (above) will invoke upssched to schedule and/or execute the event handlers defined therein.

If you do use upssched, it needs to create several files used for locking and synchronization. Typically, files of this sort are created in an application-specific subdirectory off of /var/lib. So, we'll create such a directory that upssched can use:

     su
     mkdir /var/lib/ups
     chown ups:ups /var/lib/ups
     chmod ug=rw,o= /var/lib/ups

Now, copy the sample upssched.conf file and then hack it with your favorite text editor. You need to set the following options:

     CMDSCRIPT /etc/ups/upssched-cmd
     PIPEFN /var/state/ups/upssched.pipe
     LOCKFN /var/state/ups/upssched.lock

The rest of the file specifies which events should be handled by which event handlers. The event handlers are defined by the "AT" command, which specifies which event type is to be handled for which UPS. When an event type and UPS name is matched, the "AT" command specifies what action to take.

Possible actions include: starting a timer which will eventually fire the event handler if and when the timer runs down; stopping a running timer; executing the event handler immediately.

Naturally, you can schedule or execute whatever event handler you want, in conjunction with each and every UPS event but, by way of example, our goal herein is to implement a pessimistic shutdown policy. So, here goes. Add the following event handlers:

     # As soon as the UPS goes on battery, give the users a message.
     AT ONBATT * EXECUTE UPSOnBattery
     # If the UPS stays on battery for 60 seconds, give the users a second
     # message.
     AT ONBATT * START-TIMER UPSOnBattery60 60
     # If the UPS stays on battery for 120 seconds, shut the system down.
     AT ONBATT * START-TIMER UPSOnBattery120 120
     # If the power returns, cancel all of the timers.
     AT ONLINE * CANCEL-TIMER UPSOnBattery60
     AT ONLINE * CANCEL-TIMER UPSOnBattery120
     # If the power returns, give the users a message that, "He's baaaaak!"
     AT ONLINE * EXECUTE UPSOnLinePower

/etc/ups/upssched-cmd:

This script is executed by upssched whenever a timer counts down or an event handler is directly executed. The script is passed the name of the timer or the name of the directly-executed command so that it may act accordingly. Typically, the script uses a switch statement to select the block of code, based on the timer/event being handled.

Since, in the above configuration file, we defined three events (one directly-executed and two timed) for power failure and one event for power restoration, our command script must handle them. We'll use them to implement the pessimistic shutdown policy.

Begin by copying the sample script from the clients subdirectory of the build tree:

     su
     cp .../clients/upssched-cmd /etc/ups
     chgrp ups /etc/ups/upssched-cmd
     chmod ug+x,o= /etc/ups/upssched-cmd

Note especially that we removed execute permission for "other" from the script. If we didn't do this, a malicious user could run the script to shut down your system. Also, it is possible that the script may contain instant commands, which require a username/password, so making it non-readable too is a good idea.

Edit the script to add handlers for all of the events. Here is the full script that we use:

     #! /bin/sh
     #
     # This script is called by upssched, via the CMDSCRIPT directive.  Its
     # purpose is to handle UPS events that are dispatched by upssched.
     #
     # In conjunction with the events defined in upssched.conf, this script
     # implements a pessimistic shutdown policy to gracefully shut down the
     # attached system after the UPS has been on battery for only two minutes.
     # This allows the UPS to retain enough charge in the batteries to allow
     # a second orderly shutdown, should a power failure occur as soon as the
     # system restarts.
     #
     # The first argument passed to this script is the name of the timer that
     # is being fired or the name of the immediately-executed event that is
     # being signalled.
     #
     #
     # UPS connection type.
     #
     # If the UPS is directly connected to the system (i.e. via a serial or USB
     # cable), set the connection type to "Direct".  If the UPS is
     # network-connected, via SNMP, set the connection type to "SNMP".
     #
     UPSConnection="SNMP"
     if [ x"$UPSConnection" == x ]; then
         UPSConnection="Direct"
     fi
     if [ "$UPSConnection" != SNMP ]; then
         UPSConnection="Direct"
     fi
     #
     # UPS shutdown command.  If this command is defined, it is sent to the UPS
     # before the system shutdown command (below).
     #
     # If the UPS is directly connected to the system (i.e. via a serial or USB
     # cable), there is probably no need for this command (although you could
     # use it to set the "battery.charge.restart" or "ups.delay.shutdown"
     # variables via the upsrw command, as opposed to just setting them once,
     # ahead of time).
     #
     # On the other hand, if the UPS is network-connected, via SNMP, this
     # command is needed to cause the UPS to shut down, with a delay.  This is
     # because the SNMP-connected UPS drivers do not support shutdown commands
     # (with good reason).  Consequently, the UPS must be sent an instant
     # command that shuts it off, while the system is still up and the network
     # connected.  The delay is necessary so that the system will have time
     # (hopefully enough) to be able to shut down gracefully, before the UPS
     # actually powers off.
     #
     # If you have a network-connected UPS, you may want to do some timing
     # tests to ensure that the shutdown will be given enough run time, by the
     # UPS, before it powers off the load.  There is no real harm in giving
     # the UPS a little extra time, since the system will hopefully have shut
     # itself off and the battery drain will be minimal.  Better to be safe
     # than sorry.
     #
     if [ "$UPSConnection" != SNMP ]; then
         UPSCmd=""
     else
         UPSCmd="/usr/local/ups/sbin/upscmd -u lmmonitor -p ItsASecret"
         InstCmd="shutdown.return"
     fi
     #
     # System shutdown command.
     #
     # If the UPS is directly connected to the system (i.e. via a serial or USB
     # cable), you can probably have the UPS shut the system down (i.e. via
     # "/usr/local/ups/sbin/upsmon -c fsd").
     #
     # If the UPS is network-connected, via SNMP, there is no UPS shutdown
     # command.  Consequently, you'll probably want to shut the system down
     # directly (i.e. via "/sbin/shutdown -h now").
     #
     if [ "$UPSConnection" != SNMP ]; then
         ShutdownCmd="/usr/local/ups/sbin/upsmon -c fsd"
     else
         ShutdownCmd="/sbin/shutdown -h now"
     fi
     #
     # Execute the appropriate event handling code.
     #
     case $1 in
         #
         # The UPS just went on battery.
         #
         UPSOnBattery)
             echo Save your work. System has lost power and may be shut down \
                     in 2 minutes. | \
                 wall
             ;;
         #
         # The UPS has been on battery for 60 seconds.
         #
         UPSOnBattery60)
             echo Save your work now! System has had no power for 1 minute. \
                     Shutdown is immenent. | \
                 wall
             ;;
         #
         # The UPS has been on battery for 120 seconds.  Time to shut 'er down.
         #
         UPSOnBattery120)
             #
             # If we need to shut down the UPS separately, do that now.
             #
             if [ x"$UPSCmd" != x ]; then
                 $UPSCmd $UPSNAME "\"$InstCmd\""
             fi
             #
             # Now, its time to shut down the system.
             #
             $ShutdownCmd
             ;;
         #
         # He's baaaaak!
         #
         UPSOnLinePower)
             echo System power has returned. Shutdown has been aborted. Carry \
                     on as if normal. | \
                 wall
             ;;
         #
         # Uh oooh!  Somebody made a boo boo.
         #
         *)
             logger -t upssched-cmd "Unrecognized command: $1"
             ;;
     esac

If your script uses one of the delayed shutdown commands (e.g. shutdown.return), you will need to set up the shutdown delay of the UPS. As noted in the upssched-cmd script (above), you should do some timing tests to determine how long the system takes to shut down. Give yourself a margin for safety and set that value into the UPS like this:

     /usr/local/ups/bin/upsrw -s ups.delay.shutdown=90 \
          -u lmmonitor -p ItsASecret bevatron

If you wish the UPS to delay startup after the power returns, you have two choices. You can delay startup for a fixed interval like this:

     /usr/local/ups/bin/upsrw -s ups.delay.start=600 \
          -u lmmonitor -p ItsASecret bevatron

You can delay startup until a sufficient amount of battery charge (in percent) has been reached. Try something like this:

     /usr/local/ups/bin/upsrw -s battery.charge.restart=50 \
          -u lmmonitor -p ItsASecret bevatron

You can find out if your UPS supports any or all of these variables by listing all of the variables supported, like this:

     /usr/local/ups/bin/upsrw bevatron

/etc/ups/hosts.conf:

If you chose the "--with-cgi" option, when you built NUT, you can define which UPS boxes can be monitored from the Web. Copy the file hosts.conf.sample to hosts.conf. Add all of the ups boxes that you want to be able to monitor from the Web. Also add the name of the logfile, if logged events will be displayed:

     MONITOR fusion-reactor@localhost "stargate UPS"
     LOGFILE /var/log/upslog.fusion-reactor

/etc/ups/ftplogs:

If you'd like to build a consolidated graph of all your UPS activity and have a centralized server where logfiles can be copied for this purpose, you should create the file /etc/ups/ftplogs:

     #! /bin/sh
     #
     # ftplogs - Script to ftp the UPS logs to the primary server at regular
     #           intervals.
     #
     # This script is used both by cron, to send the current UPS logs to the
     # primary server, every 15 minutes, and by logrotate, to send the freshly
     # rotated UPS logs to the primary server whenever UPS logs are rotated.
     #
     #
     # Source the UPS configuration.
     #
     if [ -f /etc/sysconfig/upsd ] ; then
        . /etc/sysconfig/upsd
     fi
     #
     # Source the clustering configuration.
     #
     if [ -f /etc/sysconfig/clustering ] ; then
         . /etc/sysconfig/clustering
     else
         SERVERROLE=Standalone
     fi
     if [ x"$SERVERROLE" == x ] ; then
         SERVERROLE=Standalone
     fi
     #
     # Determine which UPS boxes we are logging to.  If we aren't the secondary
     # or a standalone system, there are no logfiles to FTP.
     #
     if [ x"$SERVERROLE" == xSecondary ] ; then
         UPS_BOXES=$UPS_BOXES_SECONDARY
     else
         if [ x"$SERVERROLE" != xStandalone ] ; then
             exit 0
         fi
     fi
     #
     # If there's no primary server defined, we're all done.
     #
     if [ x"$PRI_SERVER" == x ] ; then
         exit 0
     fi
     #
     # If we were passed a logfile name, send it to the primary server directly.
     #
     if test x"$1" != x; then
         echo -e "user ups ItsASecret\\nbin\\nput ${LOG_PATH}.$1 \
             ${PRI_LOG_PATH}.$1" | ftp -n $PRI_SERVER
     #
     # For all of the UPS on this system, send the logs to the primary.
     #
     else
         for LogBox in $UPS_BOXES; do
             echo -e "user ups ItsASecret\\nbin\\nput ${LOG_PATH}.$LogBox \
                 ${PRI_LOG_PATH}.$LogBox" | ftp -n $PRI_SERVER
         done
     fi

In the FTP commands in the script, you should pick a user that is valid on the primary server and use the right password for that user. Once you've saved the script, don't forget to add execute permissions and to hide it from regular users, since it contains the secret UPS password:

     su
     chgrp ups /etc/ups/ftplogs
     chmod ug+x,o= /etc/ups/ftplogs

Next, on the primary server, you should create a couple of empty log files with the correct permissions so that FTP can overwrite them without getting permission denied:

     su
     touch /var/log/upslog.bevatron /var/log/upslog.bevatron.1
     chown root:ups /var/log/upslog.bevatron
     chmod g+w /var/log/upslog.bevatron

Then, it wouldn't hurt try out the script, now that you've got it all set up. For example:

     /etc/ups/ftplogs

Check on the primary server that the log files get copied over.

Now, you can add the script to the cron table on the secondary server so that it runs at regular intervals (e.g. every 15 minutes), as shown in the next section.

/etc/crontab:

To schedule all of the UPS-related activities, add the following to your crontab:

     # Push the UPS logs over to the primary server every 15 minutes.
     05,20,35,50 * * * * root /etc/ups/ftplogs
     # Every two months, have the UPS check its battery.
     00 10 3 1,3,5,7,9,11 * root /etc/ups/batterytest fusion-reactor
     00 10 4 2,4,6,8,10,12 * * root /etc/ups/batterytest bevatron

You should probably schedule the times for each UPS' battery test on days when the other UPS are not also testing their batteries so that, if there are any bad batteries, all of your machines won't be down at once.

Furthermore, you should consider the effect that battery testing will have on battery life. Each time the battery is tested it experiences a fairly deep discharge cycle. Most of the cheaper UPS replacement batteries that are sold by replacement battery vendors, such as the UB1280 and UB12120, come from China and do not have long lifetimes. They are warrantied for a year and, if you discharge them every two weeks, you'll be lucky to have them last any more than that. We don't see a compelling reason for testing batteries every two weeks. Instead, we test them every two months. Mind you, if you are running a computer center and you want high-reliability, you should probably buy better quality batteries, in which case you can test your UPS more often.

Note that most UPS will usually come with automatic self-test turned on and set to every 14 days (your UPS vendor sells batteries too). This means that the UPS will run its battery test itself every 14 days, exactly 1209600 seconds after it is turned on. Isn't that convenient, not to mention good for the batteries? If you'd rather the test was done when you decide, under the control of your crontab, you should turn off this dubious feature like so:

     /usr/local/ups/bin/upsrw -s ups.test.interval=0 -u username -p password \
       upsname@localhost

/etc/logrotate.conf, /etc/logrotate.d/ups:

Add the new UPS' logfile to either the global logrotate config file (/etc/logrotate.conf) or the specific UPS config file (/etc/logrotate.d/ups):

     /var/log/upslog.bevatron {
         notifempty
         missingok
         create 0664 root ups
         copytruncate
         postrotate
             echo HEADER: Bevatron >/var/log/upslog.bevatron
             /etc/ups/ftplogs bevatron.1
         endscript
     }

/etc/rc.d/init.d/upsd:

Install the following script using "chkconfig --add upsd" and "chkconfig upsd on". Don't forget to set its permissions to "ugo+x":

     #! /bin/sh
     #
     # upsd - Script to start/stop the NUT UPS monitoring and power down daemons.
     #
     # chkconfig: 2345 19 81
     # description: Uninterruptable Power Supply monitoring and power down daemons
     #
     # pidfile: /var/lock/subsys/upsd
     # pidfile: /var/lock/subsys/upsmon
     # pidfile: /var/lock/subsys/upsdrivers.upsname
     # pidfile: /var/lock/subsys/upslog.upsname
     # config:  /etc/ups/*
     #
     #
     # Define the install path for the UPS binaries, etc.
     #
     INSTALL_PATH="/usr/local/ups"
     PID_UPSDAEMON="/var/lock/subsys/upsd"
     PID_UPSMONITOR="/var/lock/subsys/upsmon"
     PID_UPSDRIVER="/var/lock/subsys/upsdrivers"
     PID_UPSLOG="/var/lock/subsys/upslog"
     LOCK_UPSDOWN="/var/lock/subsys/upsdown"
     #
     # Load the RedHat functions.
     #
     if [ -f /etc/redhat-release ] ; then
         . /etc/rc.d/init.d/functions
     fi
     #
     # Source the UPS configuration.
     #
     if [ -f /etc/sysconfig/upsd ] ; then
        . /etc/sysconfig/upsd
     fi
     #
     # Source the clustering configuration.
     #
     if [ -f /etc/sysconfig/clustering ] ; then
         . /etc/sysconfig/clustering
     else
         SERVERROLE=Standalone
     fi
     if [ x"$SERVERROLE" == x ] ; then
         SERVERROLE=Standalone
     fi
     #
     # Determine which UPS boxes we are starting up.
     #
     if [ ${SERVERROLE} = "Primary" ] ; then
         UPS_BOXES=$UPS_BOXES_PRIMARY
     else
         if [ ${SERVERROLE} = "Secondary" ] ; then
             UPS_BOXES=$UPS_BOXES_SECONDARY
         fi
     fi
     #
     # Upon startup, start all the UPS drivers that are configured in
     # /etc/ups/ups.conf (or wherever you put it).
     #
     # Next, start the UPS daemon to begin collecting information about all of
     # the connected UPS'.
     #
     # Then, start the power monitoring daemon to watch for low power conditions
     # and shut down the machine, if found.
     #
     start()
         {
         #
         # Remove the shutdown lock, if we're starting up.
         #
         if [ -f $LOCK_UPSDOWN ] ; then
             rm -f $LOCK_UPSDOWN
         fi
         #
         # Start a driver for each of our UPS.
         #
         for UPSBox in $UPS_BOXES; do
          echo -n "NUT starting UPS driver for ${UPSBox}: "
          $INSTALL_PATH/bin/upsdrvctl start ${UPSBox} >/dev/null 2>&1
          startval=$?
          if [ $startval = 0 ] ; then
              touch ${PID_UPSDRIVER}.${UPSBox}
              echo_success
          else
              echo_failure
          fi
          echo
      done
      #
      # Start the UPS daemon to process service requests.
      #
      echo -n "NUT starting UPS daemon: "
      $INSTALL_PATH/sbin/upsd >/dev/null 2>&1
      startval=$?
      if [ $startval = 0 ] ; then
          touch ${PID_UPSDAEMON}
          echo_success
          echo
      else
          echo_failure
          echo
          return $startval
      fi
      #
      # Start power monitoring.
      #
      echo -n "NUT starting power monitor: "
      $INSTALL_PATH/sbin/upsmon >/dev/null 2>&1
      startval=$?
      if [ $startval = 0 ] ; then
          touch ${PID_UPSMONITOR}
          echo_success
          echo
      else
          echo_failure
          echo
          return $startval
      fi
      #
      # If the user has given us a logging directory, start up logging.
      #
      if [ x"${LOG_PATH}" != x ] ; then
          echo -n "NUT starting UPS log: "
          timestamp=`date "+%Y/%m/%d %H:%M:%S"`
          #
          # Start up logging for each UPS box.
          #
          # Note that, above, we could have failed to start one or more UPS
          # and ignored the failure.  Here, since it is the last step, we
          # abort upon the failure of any log.
          #
          if [ x"$LOG_INTERVAL" == x ] ; then
              LOG_INTERVAL=30
          fi
          for UPSBox in $UPS_BOXES; do
              if [ ! -f ${LOG_PATH}.${UPSBox} ] ; then
                  echo HEADER: ${UPSBox} > ${LOG_PATH}.${UPSBox}
              fi
              echo $timestamp EVENT: Starting UPS logging \
                  >> ${LOG_PATH}.${UPSBox}
              if [ x"$UPS_GROUP" != x ] ; then
                  chgrp $UPS_GROUP ${LOG_PATH}.${UPSBox}
                  chmod g=rw ${LOG_PATH}.${UPSBox}
              fi
              $INSTALL_PATH/bin/upslog ${UPSBox}@localhost \
                  ${LOG_PATH}.${UPSBox} $LOG_INTERVAL \
                  "%TIME @Y/@m/@d @H:@M:@S% %VAR input.voltage% \
                      %VAR output.voltage% %VAR input.frequency% \
                      %VAR battery.charge% %VAR ups.load% [%VAR ups.status%] \
                      %VAR ups.temperature%" \
                  >/dev/null 2>&1
              startval=$?
              if [ $startval = 0 ] ; then
                  touch ${PID_UPSLOG}.${UPSBox}
              else
                  echo_failure
                  echo
                  return $startval
              fi
          done
          echo_success
          echo
      fi
      #
      # We're all done with startup.
      #
      return $startval
      }

#
# Upon shutdown, do all of the startups in reverse: logging; power monitor; # UPS daemon; UPS drivers.
#
stop()

      {
      #
      # Shutdown logging for any log that we started.
      #
      echo -n "NUT stoping UPS log: "
      timestamp=`date "+%Y/%m/%d %H:%M:%S"`
      for UPSBox in $UPS_BOXES; do
          if [ -f ${PID_UPSLOG}.${UPSBox} ] ; then
              LogPID=`ps -eo pid,args | grep upslog | grep ${UPSBox} | \
                  sed -n 's/^ \([0-9]\).*/\1/p'`
              if [ x"$LogPID" != x ] ; then
                  kill -9 $LogPID
                  stopval=$?
                  echo $timestamp EVENT: Stopping UPS logging \
                      >> ${LOG_PATH}.${UPSBox}
                  [ $stopval = 0 ] && rm -f ${PID_UPSLOG}.${UPSBox}
              fi
          fi
      done
      echo_success
      echo
      #
      # Stop power monitoring.
      #
      if [ -f ${PID_UPSMONITOR} ] ; then
          echo -n "NUT stoping power monitor: "
          $INSTALL_PATH/sbin/upsmon -c stop >/dev/null 2>&1
          stopval=$?
          if [ $stopval = 0 ] ; then
              rm -f ${PID_UPSMONITOR}
              echo_success
          else
              echo_failure
          fi
          echo
      fi
      #
      # Stop the UPS daemon.
      #
      if [ -f ${PID_UPSDAEMON} ] ; then
          echo -n "NUT stoping UPS daemon: "
          $INSTALL_PATH/sbin/upsd -c stop >/dev/null 2>&1
          stopval=$?
          if [ $stopval = 0 ] ; then
              rm -f ${PID_UPSDAEMON}
              echo_success
          else
              echo_failure
          fi
          echo
      fi
      #
      # Stop all of our UPS drivers.
      #
      for UPSBox in $UPS_BOXES; do
          echo -n "NUT stoping UPS driver for ${UPSBox}: "
          $INSTALL_PATH/bin/upsdrvctl stop ${UPSBox} >/dev/null 2>&1
          stopval=$?
          if [ $stopval = 0 ] ; then
              rm -f ${PID_UPSDRIVER}.${UPSBox}
              echo_success
          else
              echo_failure
          fi
          echo
      done
      return 0
      }

#
# See how we were called.
#
case "$1" in

      #
      # Start.
      #
      start)
          start
          RETVAL=$?
          ;;
      #
      # Stop.
      #
      stop)
          stop
          RETVAL=$?
          ;;
      #
      # Restart or reload (whatever).
      #
      restart|reload)
          stop
          start
          RETVAL=$?
          ;;
      #
      # Conditional restart.
      #
      condrestart)
          if [ -f ${PID_UPSDRIVER} ] ; then
              stop
              start
              RETVAL=$?
          fi
          ;;
      #
      # Give the status of all of the NUT components that are running.
      #
      status)
          #
          # If none of the UPS are running, assume that NUT is down.
          #
          if [ ! -f ${PID_UPSDRIVER} ] ; then
              echo NUT is down
              exit 1
          fi
          #
          # Let's check each UPS.
          #
          for UPSBox in $UPS_BOXES; do
              $INSTALL_PATH/bin/upsc ${UPSBox} output.voltage >/dev/null 2>&1
              RETVAL=$?
              if [ $RETVAL -eq 0 ] ; then
                  echo UPS ${UPSBox} is functioning normally
              else
                  echo UPS ${UPSBox} is down
              fi
          done
          #
          # Check the UPS daemon.
          #
          status upsd
          #
          # Check the power monitor.
          #
          status upsmon
          #
          # If we're logging then let's see what we're logging.
          #
          if [ x"${LOG_PATH}" != x ] ; then
              status upslog
              RETVAL=$?
              if [ $RETVAL -eq 0 ] ; then
                  for UPSBox in $UPS_BOXES; do
                      if [ -f ${PID_UPSLOG}.${UPSBox} ] ; then
                          echo UPS ${UPSBox} is being logged to ${LOG_PATH}.${UPSBox}
                      fi
                  done
              fi
          fi
          ;;
      #
      # Help text.
      #
      *)
          echo $"Usage: $0 {start|stop|restart|condrestart|status}"
          exit 1

esac

     exit $RETVAL

If you are running on SuSE, the UPS startup script needs to be slightly different. Here's the modified script:

     #! /bin/sh
     #
     # Author: Eric Wilde
     #
     # /etc/init.d/upsd
     #   and its symbolic link
     # /usr/sbin/rcupsd
     #
     ### BEGIN INIT INFO
     # Provides:       upsd
     # Required-Start: $network $syslog
     # Required-Stop:
     # Default-Start:  2 3 5
     # Default-Stop:   0 1 6
     # Description:    Start/stop the NUT UPS monitoring and power down daemons.
     ### END INIT INFO
     #
     # Return values acc. to LSB for all commands but status:
     # 0 - success
     # 1 - generic or unspecified error
     # 2 - invalid or excess argument(s)
     # 3 - unimplemented feature (e.g. "reload")
     # 4 - insufficient privilege
     # 5 - program is not installed
     # 6 - program is not configured
     # 7 - program is not running
     #
     ############################################################################
     #
     # Define the install path for the UPS binaries, etc.
     #
     INSTALL_PATH="/usr/local/ups"
     CONFIG_DIR="/etc/ups"
     PID_UPSDAEMON="/var/lock/subsys/upsd"
     PID_UPSMONITOR="/var/lock/subsys/upsmon"
     PID_UPSDRIVER="/var/lock/subsys/upsdrivers"
     PID_UPSLOG="/var/lock/subsys/upslog"
     LOCK_UPSDOWN="/var/lock/subsys/upsdown"
     #
     # First reset status of this service.
     #
     . /etc/rc.status
     rc_reset
     #
     # Check for missing binary.
     #
     if [ ! -x ${INSTALL_PATH}/bin/upsdrvctl ]; then
         echo -n "NUT daemon, ${INSTALL_PATH}/bin/upsdrvctl is not installed. "
         rc_status -s
         exit 5
     fi
     #
     # Check that the configuration directory exists.
     #
     if [ ! -d ${CONFIG_DIR} ]; then
         echo -n "NUT configuration directory ${CONFIG_DIR} does not exist. "
         rc_status -s
         exit 6
     fi
     #
     # Source the UPS configuration.
     #
     if [ -f /etc/sysconfig/upsd ]; then
         . /etc/sysconfig/upsd
     fi
     #
     # Source the clustering configuration.
     #
     if [ -f /etc/sysconfig/clustering ] ; then
         . /etc/sysconfig/clustering
     else
         SERVERROLE=Standalone
     fi
     if [ x"$SERVERROLE" == x ]; then
         SERVERROLE=Standalone
     fi
     #
     # Determine which UPS boxes we are starting up.
     #
     if [ ${SERVERROLE} = "Primary" ] ; then
         UPS_BOXES=$UPS_BOXES_PRIMARY
     else
         if [ ${SERVERROLE} = "Secondary" ] ; then
             UPS_BOXES=$UPS_BOXES_SECONDARY
         fi
     fi
     #
     # Upon startup, start all the UPS drivers that are configured in
     # /etc/ups/ups.conf (or wherever you put it).
     #
     # Next, start the UPS daemon to begin collecting information about all of
     # the connected UPS'.
     #
     # Then, start the power monitoring daemon to watch for low power conditions
     # and shut down the machine, if found.
     #
     start()
         {
         #
         # Remove the shutdown lock, if we're starting up.
         #
         if [ -f $LOCK_UPSDOWN ] ; then
             rm -f $LOCK_UPSDOWN
         fi
         #
         # Start a driver for each of our UPS.
         #
         for UPSBox in $UPS_BOXES; do
          echo -n "NUT starting UPS ${UPSBox} "
          if [ -f ${PID_UPSDRIVER}.${UPSBox} ]; then
              echo -n "- Warning: driver already running. "
          fi
          ${INSTALL_PATH}/bin/upsdrvctl start ${UPSBox} >/dev/null 2>&1
          startval=$?
          rc_status -v
          if [ $startval = 0 ] ; then
              touch ${PID_UPSDRIVER}.${UPSBox}
          fi
      done
      #
      # Start the UPS daemon to process service requests.
      #
      echo -n "NUT starting UPS daemon "
      if [ -f ${PID_UPSDAEMON} ]; then
          echo -n "- Warning: daemon already running. "
      fi
      ${INSTALL_PATH}/sbin/upsd >/dev/null 2>&1
      startval=$?
      rc_status -v
      if [ $startval = 0 ] ; then
          touch ${PID_UPSDAEMON}
      else
          return $startval
      fi
      #
      # Start power monitoring.
      #
      echo -n "NUT starting power monitor "
      if [ -f ${PID_UPSMONITOR} ]; then
          echo -n "- Warning: monitor already running. "
      fi
      ${INSTALL_PATH}/sbin/upsmon >/dev/null 2>&1
      startval=$?
      rc_status -v
      if [ $startval = 0 ] ; then
          touch ${PID_UPSMONITOR}
      else
          return $startval
      fi
      #
      # If the user has given us a logging directory, start up logging.
      #
      if [ x"${LOG_PATH}" != x ]; then
          echo -n "NUT starting UPS log "
          timestamp=`date "+%Y/%m/%d %H:%M:%S"`
          #
          # Start up logging for each UPS box.
          #
          # Note that, above, we could have failed to start one or more UPS and
          # ignored the failure.  Here, since it is the last step, we abort upon
          # the failure of any log.
          #
          if [ x"$LOG_INTERVAL" == x ]; then
              LOG_INTERVAL=30
          fi
          for UPSBox in $UPS_BOXES; do
              if [ ! -f ${LOG_PATH}.${UPSBox} ]; then
                  echo HEADER: ${UPSBox} > ${LOG_PATH}.${UPSBox}
              fi
              echo $timestamp EVENT: Starting UPS logging \
                  >> ${LOG_PATH}.${UPSBox}
              if [ x"$UPS_GROUP" != x ]; then
                  chgrp $UPS_GROUP ${LOG_PATH}.${UPSBox}
                  chmod g=rw ${LOG_PATH}.${UPSBox}
              fi
              if [ -f ${PID_UPSLOG}.${UPSBox} ]; then
                  echo -n "- Warning: logging already started. "
              fi
              ${INSTALL_PATH}/bin/upslog \
                  ${UPSBox}@localhost ${LOG_PATH}.${UPSBox} \
                  $LOG_INTERVAL \
                  "%TIME @Y/@m/@d @H:@M:@S% %VAR input.voltage% %VAR output.voltage% %VAR input.frequency% %VAR battery.charge% %VAR ups.load% [%VAR ups.status%] %VAR ups.temperature%" \
                  >/dev/null 2>&1
              startval=$?
              rc_status -v
              if [ $startval = 0 ] ; then
                  touch ${PID_UPSLOG}.${UPSBox}
              else
                  return $startval
              fi
          done
      fi
      #
      # We're all done with startup.
      #
      return $startval
      }

#
# Upon shutdown, do all of the startups in reverse: logging; power monitor; # UPS daemon; UPS drivers.
#
stop()

      {
      #
      # Shutdown logging for any log that we started.
      #
      echo -n "NUT stoping UPS log "
      timestamp=`date "+%Y/%m/%d %H:%M:%S"`
      for UPSBox in $UPS_BOXES; do
          if [ -f ${PID_UPSLOG}.${UPSBox} ]; then
              LogPID=`ps -eo pid,args | grep upslog | grep ${UPSBox} | \
                  sed -n 's/^ \([0-9]\).*/\1/p'`
              if [ x"$LogPID" != x ]; then
                  kill -9 $LogPID
                  stopval=$?
                  echo $timestamp EVENT: Stopping UPS logging \
                      >> ${LOG_PATH}.${UPSBox}
                  [ $stopval = 0 ] && rm -f ${PID_UPSLOG}.${UPSBox}
              fi
          fi
      done
      rc_status -v
      #
      # Stop power monitoring.
      #
      if [ -f ${PID_UPSMONITOR} ]; then
          echo -n "NUT stoping power monitor "
          ${INSTALL_PATH}/sbin/upsmon -c stop >/dev/null 2>&1
          stopval=$?
          rc_status -v
          if [ $stopval = 0 ] ; then
              rm -f ${PID_UPSMONITOR}
          fi
      fi
      #
      # Stop the UPS daemon.
      #
      if [ -f ${PID_UPSDAEMON} ]; then
          echo -n "NUT stoping UPS daemon "
          ${INSTALL_PATH}/sbin/upsd -c stop >/dev/null 2>&1
          stopval=$?
          rc_status -v
          if [ $stopval = 0 ] ; then
              rm -f ${PID_UPSDAEMON}
          fi
      fi
      #
      # Stop all of our UPS drivers.
      #
      for UPSBox in $UPS_BOXES; do
          echo -n "NUT stoping UPS ${UPSBox} "
          ${INSTALL_PATH}/bin/upsdrvctl stop ${UPSBox} >/dev/null 2>&1
          stopval=$?
          rc_status -v
          if [ $stopval = 0 ] ; then
              rm -f ${PID_UPSDRIVER}.${UPSBox}
          fi
      done
      return 0
      }

#
# See how we were called.
#
case "$1" in

      #
      # Start.
      #
      start)
          start
          RETVAL=$?
          if [ $RETVAL != 0 ]; then
              RETVAL=1
          fi
          ;;
      #
      # Stop.
      #
      stop)
          stop
          RETVAL=$?
          ;;
      #
      # Restart or reload (whatever).
      #
      restart|reload)
          stop
          start
          RETVAL=$?
          if [ $RETVAL != 0 ]; then
              RETVAL=1
          fi
        ;;
      #
      # Conditional restart.
      #
      condrestart)
          if [ -f ${PID_UPSDRIVER} ]; then
              stop
              start
              RETVAL=$?
              if [ $RETVAL != 0 ]; then
                  RETVAL=1
              fi
          fi
          ;;
      #
      # Give the status of all of the NUT components that are running.
      #
      status)
          #
          # If none of the UPS are running, assume that NUT is down.
          #
          if [ ! -f ${PID_UPSDRIVER} ]; then
              echo NUT is down
              exit 1
          fi
          #
          # Let's check each UPS.
          #
          for UPSBox in $UPS_BOXES; do
              ${INSTALL_PATH}/bin/upsc ${UPSBox} output.voltage >/dev/null 2>&1
              RETVAL=$?
              if [ $RETVAL -eq 0 ]; then
                  echo UPS ${UPSBox} is functioning normally
              else
                  echo UPS ${UPSBox} is down
              fi
          done
          #
          # Check the UPS daemon.
          #
          status upsd
          #
          # Check the power monitor.
          #
          status upsmon
          #
          # If we're logging then let's see what we're logging.
          #
          if [ x"${LOG_PATH}" != x ]; then
              status upslog
              RETVAL=$?
              if [ $RETVAL -eq 0 ]; then
                  for UPSBox in $UPS_BOXES; do
                      if [ -f ${PID_UPSLOG}.${UPSBox} ]; then
                          echo UPS ${UPSBox} is being logged to ${LOG_PATH}.${UPSBox}
                      fi
                  done
              fi
          fi
          ;;
      #
      # Help text.
      #
      *)
          echo $"Usage: $0 {start|stop|restart|condrestart|status}"
          exit 1

esac

     exit $RETVAL

After you create the startup script, give it execute permissions, put it into service and add a symlink to it, from the expected directory:

     su
     chmod ugo+x /etc/init.d/upsd
     insserv upsd
     ln -s /etc/init.d/upsd /usr/sbin/rcupsd

/etc/rc.d/init.d/upsdown:

If you use upssched.conf, upssched and upssched-cmd to shutdown your UPS, you might not need this script. You most likely will need this script, if you just let upsmon shut down your system in its default mode, and you probably will need this script, if your upssched-cmd script uses upsmon to shut down the UPS, for example like this:

     /usr/local/ups/sbin/upsmon -c fsd

Basically, this script waits for the system to go through all of the steps in an orderly shutdown and then, when it gets to the last step (runlevel 0), it powers off the UPS.

However, a better scheme is undoubtedly to use the "shutdown.return" instant command, like this:

     /usr/local/ups/sbin/upscmd -u lmmonitor -p ItsASecret bevatron \
         "shutdown.return"

This is what the enclosed upssched-cmd does. This gives the system a chance to actually power itself off before the UPS shuts down. And, if the line power comes back, the UPS will restart itself and hopefully bring your system back up, which doesn't happen if you use this upsdown script.

Also, many UPS models have a hardware setting that prevents the UPS from powering up until a specified time has elapsed or, better yet, a specified battery charge has been reached. If you have one of those UPS models (e.g. APC SmartUPS), you won't need to use this script to delay startup so, if you do need it for shutdown, leave the START_SLEEP value empty.

To facillitate physical shutdown of the UPS power, when the system is shut down, install the following script using "chkconfig --add upsdown" and "chkconfig upsdown on". Don't forget to set its permissions to "ugo+x":

     #! /bin/sh
     #
     # upsd - Script to shut down the UPS upon system shutdown due to \
     #        power failure.
     #
     # Revision History:
     # ewilde      2003Apr06  Initial coding.
     #
     # chkconfig: 12345 01 99
     # description: Shut down Uninteruptable Power Supply on power fail shutdown
     #
     # This script runs on startup, right after new microcode has been installed
     # on the machine but before any file system activity.  It can sleep for a
     # while until the UPS batteries have a chance to recharge to a sufficient
     # level to ensure an orderly shutdown, should power fail again.  If you wish
     # to do this, set the START_SLEEP value to a valid "sleep(1)" value (e.g.
     # "5m") and make sure POWERDOWNFLAG points to the same file as found in the
     # upsmon.conf file.
     #
     # It also runs on shutdown, as the system cleans up after entering runlevel
     # 0 (shutdown) or 6 (reboot).  If the UPS POWERDOWNFLAG is set, it physically
     # powers off the UPS.  This should only happen in runlevel 0.
     # Define the install path for the UPS binaries, etc.
     INSTALL_PATH="/usr/local/ups"
     POWERDOWNFLAG="/etc/killpower"
     START_SLEEP=""
     if [ -f /etc/redhat-release ] ; then
         . /etc/rc.d/init.d/functions
     fi
     # See how we were called.
     case "$1" in
      # Start.
      start)
          if [ -f $POWERDOWNFLAG ] ; then
              echo -n "NUT waiting for UPS batteries to recharge: "
              if [ x"$START_SLEEP" != x ] ; then
                sleep $START_SLEEP
              fi
              echo_success
              echo
          fi
          ;;
      # Stop.
      stop)
          if [ -f $POWERDOWNFLAG ] ; then
              echo -n "NUT shutting down the UPS: "
              echo_success
              echo
              $INSTALL_PATH/bin/upsdrvctl shutdown
          fi
          ;;
      # Help text (pushing it, I know).
      *)
          echo $"Usage: $0 {start|stop}"
          exit 1

esac

     exit 0

Go back to the begining of this section and look at the steps for the quick add of a new UPS. You may need to do some of those steps too, to complete your installation of any UPS that are on your machine.