Desc: Network UPS Tools design document
File: design.txt
Date: 3 September 2002
Auth: Russell Kroll <rkroll@exploits.org>

Here is the general layout for the software.  It has a layered scheme,
in which clients, the server, and the drivers are separated.  Layers 
communicate with each other using an agreed-upon protocol.

The layering
============

  CLIENTS: upsmon, upsc, upsrw, upsstats, upsset, etc.
  
           < network: TCP or UDP sockets, typically on port 3493           >

   SERVER: upsd

           < local communications: state files typically in /var/state/ups >
	   < these files may be mmapped for performance gains		   >

           < *or* SysV shared memory attached by both driver and server    >
           < either way, they use the "info array" to share data           >

           < also sockets in the statepath for instant commands / "set"    >

  DRIVERS: apcsmart, bestups, fentonups, powercom, etc.

           < serial communications, SNMP, USB, etc.                        >

EQUIPMENT: Smart-UPS 700, Fenton PowerPal 660, etc. (actual UPS hardware)

How information gets around
===========================

From the equipment
------------------

DRIVERS talk to the EQUIPMENT and receive updates.  For most hardware this
is polled (DRIVER asks EQUIPMENT about a variable), but forced updates are
also possible.  The exact method is not important, as it is abstracted
by the driver.

From the driver
---------------

DRIVERS publish the data they obtain from the EQUIPMENT in the
"info array" - a standard C array of struct info.  The finer points of
this structure are documented in the new-drivers.txt document, and will
not be repeated here.

The actual array can exist in two forms.  The default form is to create
a file on the disk that simply contains the array.  This file is known as
a "state file" since it contains all the state information on the
EQUIPMENT.  In this mode, the DRIVER refreshes the data every few seconds
and the SERVER reads from it at similar intervals.  The server uses the
Unix ctime data to know when the file is "stale" - not being updated.
For this reason, the driver must keep this file fresh, even if nothing
changes.  This design introduces a slight lag in data propagation - the
time after the DRIVER writes out the array, but before the SERVER reads
it.

The other form uses shared memory.  This removes most of the disk I/O
since the data is being copied to and from memory segments.  There is still
a slight lag in data propagation due to the need to lock the state file
before copying data across.

Previously, upsd served data directly from the shared memory segment.
This presented the possibility of data corruption due to races during
updates.  Locking was introduced and a local copy of the data is now
used to avoid this problem.

In shared memory mode, there is still the need for a "state file", but
it is much smaller and only written once.  This stub file only contains
one entry, which serves to direct the SERVER at the appropriate shared
memory ID.  See the entry for INFO_SHMID in the new-drivers.txt for
additional information on this.

From the server
---------------

Regardless of the storage method of the state file (actual file or shared
memory struct), the SERVER will have an info array available for every
DRIVER being monitored.  When a request for data arrives from a CLIENT,
the SERVER will look through the right info array and return that data
if it exists.  The format for these requests is the same whether it comes
from a UDP or TCP socket, although there are restrictions on what may
happen over a UDP socket.  This is documented in protocol.txt.

Instant commands
================

Instant commands is the term given to a set of actions that result in
something happening to the UPS.  Some of the common ones are BTEST1 to
initiate a battery test and FPTEST to test the front panel of the UPS.

They are passed to the SERVER from a CLIENT using an authenticated
network connection.  The SERVER first checks to make sure that the instant
command is valid for the DRIVER.  If it's supported, a message is sent 
via a socket to the DRIVER containing the command and any auxiliary
information.

At this point, there is no confirmation to the SERVER of the command's
execution.  This is planned for a future release.  This has been delayed
since returning a response involves some potentially interesting timing
issues.  Remember that upsd services clients in a round-robin fashion,
so all queries must be lightweight and speedy.

Setting variables
=================

Some variables in the DRIVER can be changed, and carry the FLAG_RW flag.
Upon receiving a SET command from the CLIENT, the SERVER first verifies
that it is valid for that DRIVER in terms of writability and data type.
If those checks pass, it then sends the SET command through the socket,
much like the instant command design.

The DRIVER is expected to commit the value to the EQUIPMENT and update
its internal representation of that variable.

Like the instant commands, there is currently no acknowledgement of the
command's completion from the DRIVER.  This, too, is planned for a future
release.

Example data path
=================

Here's the path a piece of data might take through this architecture.
The event is a UPS going on battery, and the final result is a pager
delivering the alpha message to the admin.

1. EQUIPMENT reports on battery by setting flag in status register

2. DRIVER notices this flag and stores it in the INFO_STATUS entry as OB.
   The info struct is written to disk or merely updated in shared memory
   as appropriate.

3. SERVER upsd picks up the update via a routine read of the state file
   or immediately via shared memory.

4. CLIENT upsmon does a routine poll of SERVER for "STATUS" and gets "OB".

5. CLIENT upsmon then invokes its NOTIFYCMD which is upssched.

6. upssched starts up a daemon to handle a timer which will expire about
   30 seconds into the future.

7. 30 seconds later, the timer expires since the UPS is still on battery,
   and upssched calls the CMDSCRIPT upssched-cmd.

8. upssched-cmd parses the args and calls sendmail.

9. Avian carriers, smoke signals, SMTP, and some magic result in the
   message getting from the pager company's gateway to a transmitter
   and then to the admin's pager.

This scenario requires some configuration, obviously:

1. There's a UPS driver running.
   (Whatever applies for the hardware)

2. upsd has a valid UPS entry for this UPS.

	[myups]
		driver = upsdriver
		port = /dev/port

3. upsmon is set to monitor this UPS
   (MONITOR myups@localhost 1 mypass master)

4. upsmon is set to EXEC the NOTIFYCMD for the ONBATT condition.
   (NOTIFYFLAG ONBATT EXEC)

5. upsmon calls upssched as the NOTIFYCMD.
   (NOTIFYCMD /path/to/upssched)

6. upssched has a 30 second timer for ONBATT.
   (AT ONBATT * START-TIMER upsonbatt 30)

7. upssched calls upssched-cmd as the CMDSCRIPT
   (CMDSCRIPT /path/to/upssched-cmd)

8. upssched-cmd knows what to do with "upsonbatt" as its first argument
   (A quick case..esac construct, see the examples)
