Desc: How to make a new module to support another UPS
File: new-modules.txt
Date: 28 November 2000
Auth: Russell Kroll <rkroll@exploits.org>

Smart vs. Contact-closure
=========================

If your UPS only does contact closure readings, then go straight to the
genericups.txt document for information on adding support.  It's a lot
easier to add a few lines to a header file than it is to create a whole
new module.

Design principles
-----------------

The basic premise used to pass data between a module and the upsd is a 
state file that usually lives in /var/state/ups.  In shared memory mode,
this file only contains a pointer to the shared memory structure, and
is not updated after the driver starts up.  On other systems, this file
must be kept fresh or upsd will detect "staleness" and flag it unusable.

If you are using the upscommon library, just call writeinfo() whenever
things have been updated, and it will take care of it for you.  

The info array
--------------

The data itself lives in an array of type "itype" as defined in shared.h.
This array has no set upper limit, but must have as many elements as
there are variables to store.  The first member of this array MUST be type
INFO_MEMBERS and the value MUST be equal to the maximum number of
elements that can ever be stored in there.  If there is ever a possibility
that you could store 15 items in that struct, then set the number to 15.
This is done for you when you call create_info().

All of the other types are optional.  Don't include them unless the UPS
you are monitoring actually supports that value in some way.  Some things
can't be determined by the UPS itself but should still be reported if you
can.  For example, the generic UPS driver has no idea what's out there
due to the nature of contact closure hookups, so it just hardcodes a
value for "model" and "mfr".  This lets it look nice in multimon and other
programs that use those fields.

So, if you have a reasonable idea of what is out there, hardcoding the
value is probably better than not including it at all, especially for
something fairly important like the model string.

The general logic for your module should be as follows:

 1. Create the array - createinfo()
 2. Setup the array with all your intended types - addinfo()
 3. Fill it with data from the UPS in the proper format - setinfo()
 4. Commit it to disk or update the mtime for the SHM segment - writeinfo()
 5. Goto 3

It's that easy.

Helpful functions
-----------------

There are a few functions provided by upscommon which will make
manipulation of the info array easier for you.

 - itype *create_info (int numinfo, int shmok)

   This will create info[] of size numinfo for you in shared memory or in
   normal memory depending on what's available and the value of shmok.
   It returns a pointer to the array.

 - void addinfo (int type, char *value, int flags, int auxdata)

   This is a better way to add new entries to the info array.  Don't add
   them by hand - it's messy and prone to breakage.

   type is a value from shared.h - look for the #define INFO_* section.

   value is a string that is the initial setting for this entry.
   Sometimes you call addinfo just to put it in the array in preparation
   for a setinfo() later.  That's OK.  Just set it to "" in that case.

   flags again come from shared.h.  See the #define FLAG_* entries.

   auxdata depends on what the type is.  This can be a length in the case
   of a read-write string, a ENUM_* value for the INFO_ENUM type, and
   more.

Info array member formatting
============================

The data contained in the value for each member of the array must conform
to the standard or it will be impossible for client programs to parse.  
All values are stored in string form and should be null terminated.

Occasionally there are numeric values shown as nnn.n.  These can actually
be less (nn.n or even n.n).  The format shown is the usual one that will
occur.

The basics
----------

 Name         | Format details 
--------------+--------------------------------------------------------------
INFO_MEMBERS  | Defines how many members of the array exist including the
              | one containing INFO_MEMBERS.  Must be stored in position 0.
              | Must exist unless using shared memory mode - see INFO_SHMID.
              |
              | The easiest way to handle this is to define #INFOMAX to
              | some value and then use it for all references to this count.
              | Many modules have a variable number of variables - leave
              | unused entries set to the type of INFO_UNUSED.
--------------+--------------------------------------------------------------
INFO_SHMID    | Defines the shared memory ID where the information is
              | really found when using shared memory mode.  Must be
              | stored in position 0 with no other members.  The array
              | must start with either INFO_MEMBERS or INFO_SHMID.
--------------+--------------------------------------------------------------
INFO_MSGID    | Defines the SysV message queue ID.  This is used internally
              | by upscommon and upsd.  Normally never accessed directly by
              | models.  Stick to the createmsgq() interface to handle this.
--------------+--------------------------------------------------------------
INFO_MFR      | Manufacturer's name.                   Example: APC
--------------+--------------------------------------------------------------
INFO_MODEL    | UPS model name.                        Example: SMART-UPS 700
--------------+--------------------------------------------------------------
INFO_UTILITY  | Voltage that UPS is receiving - nnn[.n] format  Ex: 116.7
--------------+--------------------------------------------------------------
INFO_BATTPCT  | UPS battery charge in percent - nnn[.n] format  Ex: 056.5
--------------+--------------------------------------------------------------
INFO_STATUS   | Current internal UPS power status - contains abbreviations
              | separated by spaces when necessary
              |
              | OFF   - UPS is off (not supplying power)
              | OL    - UPS is online (supplying power from the line/mains)
              | OB    - UPS is on battery
              | LB    - UPS battery is low (with OB = shutdown situation)
              | CAL   - UPS is performing calibration
              | TRIM  - UPS is trimming incoming voltage (APC "SmartTrim")
              | BOOST - UPS is boosting incoming voltage (APC "SmartBoost")
              | OVER  - UPS is overloaded
              | RB    - UPS battery needs to be replaced
              | FSD   - UPS is in forced shutdown state (slaves take note)
              |
              | Things like "OL" and "OB" should not ever occur together.
              | Likewise, "OFF" and "OL" or "OB" are also mutually exclusive.
              |
              | Example for on battery + low battery: OB LB
--------------+--------------------------------------------------------------
INFO_UPSTEMP  | UPS internal temperature in degrees C - nnn[.n] format
              | Example: 037.0
--------------+--------------------------------------------------------------
INFO_ACFREQ   | Incoming frequency in Hz - nn[.nn] format   Example: 60.00
--------------+--------------------------------------------------------------
INFO_LOADPCT  | Load on UPS in percent - nnn[.n] format     Example: 023.4
--------------+--------------------------------------------------------------
INFO_LOWXFER  | UPS transfers to battery when incoming voltage drops below
              | this value - nnn[.n] format                 Example: 103
--------------+--------------------------------------------------------------
INFO_HIGHXFER | UPS transfers to battery when incoming voltage rises above
              | this value - nnn[.n] format                 Example: 132
--------------+--------------------------------------------------------------
INFO_AMBTEMP  | Current temperature outside the UPS.  Not to be confused
              | with INFO_UPSTEMP which is the _internal_ UPS temperature.
              | Degrees C - nnn[.n] format                  Example: 050.8
--------------+--------------------------------------------------------------
INFO_AMBHUMID | Current humidity outside the UPS.  Percentage.
              | nn.nn format                                Example: 25.40
--------------+--------------------------------------------------------------
INFO_CONTACTS | Current status of dry contacts on the UPS.  Hex chars.
              | hh format                                   Example: F0
--------------+--------------------------------------------------------------
INFO_UPSIDENT | Internal UPS identification.                Example: EXPLOITS
--------------+--------------------------------------------------------------
INFO_WAKEDELAY| Seconds that the UPS will wait before powering up.
              | nnn format                                  Example: 020
--------------+--------------------------------------------------------------
INFO_LINESENS | Defines the UPS's sensitivity to line fluctuations.
              | char - L = low, M = medium, H = high, A = auto   Ex: H
--------------+--------------------------------------------------------------
INFO_BATTVOLT | Voltage of the UPS's battery or batteries.  Example: 27.87
--------------+--------------------------------------------------------------
INFO_OUTVOLT  | AC voltage being supplied by the UPS to the load.
              |                                             Example: 114.4
--------------+--------------------------------------------------------------
INFO_BATTDATE | Text string: last date the battery was replaced.
              | Date format may be forced by the UPS hardware.
              |                                             Example: 11/24/00
--------------+--------------------------------------------------------------
INFO_WAKETHRSH| Defines percentage of battery charge required before UPS
              | will power up after powerdown. ie 015 025 035 etc
--------------+--------------------------------------------------------------
INFO_OUTVOLT  | Output voltage of UPS.  Theoretically this can be changed
              | but implemented as RO.  3 digit voltage - ie 230
--------------+--------------------------------------------------------------
INFO_LOBATTIME| Remaining run time when UPS switches to LB.  UPS will set
              | this up when calibrated - ie 005 etc
--------------+--------------------------------------------------------------
INFO_PDNGRACE | Grace time after UPS is ordered to shutdown until it does
              | so.  In seconds - 020, 060, 120 etc
--------------+--------------------------------------------------------------
INFO_ALRMDELAY| Delay between power outage and UPS alerting you (to prevent
              | glitches causing alarms).  Seconds ie 005
--------------+--------------------------------------------------------------
INFO_SLFTSTINT| Intervals between selftests.  In hours
              | 
--------------+--------------------------------------------------------------

System level INFO values
------------------------

These are special members of the info array that define some "behind the
scenes" data used by the model driver and upsd.

--------------+--------------------------------------------------------------
INFO_INSTCMD  | Indicates support for an instant command in auxdata.
              | Set auxdata to a CMD_* value from shared.h.
--------------+--------------------------------------------------------------
INFO_ENUM     | Indicates a possible enumerated value for one of the
              | variables elsewhere in the array.
              | 
              | auxdata is the INFO_ value for that variable.
              | value should be the actual enumerated value possibility.
--------------+--------------------------------------------------------------
INFO_MSGID    | Internal value used to keep track of the SysV message ID
              | used by upsd and the model driver to send things like SET
              | and INSTCMD around.
--------------+--------------------------------------------------------------

New variable support
====================

To add support for a new variable, the following changes must happen:

1. Edit shared.h to give it an INFO_* identifier for the itype array

2. Edit upsd.h to match the INFO_* identifier to a text name

3. Make your module supply data for that variable

For sanity's sake, all variables have INFO_ as a prefix as their itype
array identifier.  A variable called FOO would be type INFO_FOO in the
array.

To keep things together, new variables should be submitted for inclusion
into the main source tree.

Shared memory support
---------------------

For speed and efficiency, this array will be stored in a shared memory
segment and upsd will attach to it for reading whenever possible.  upsd
monitors the number of attaches and the ctime info on the structure to
make sure it's staying fresh.  If it goes stale, upsd drops it and starts
rereading the state file for updates - either a new SHM id or the data
itself.

In shared memory mode, the state file should contain *ONE* element of type
INFO_SHMID.  The value for this element should be the shared memory ID.
upsd will detect this and switch into shared memory mode if possible.
If you manage to compile it without shared memory support and somehow give
it a shared memory state file, it will complain.

This is all handled for you by the routines in upscommon - create_info and
writeinfo take care of the details.  Making a driver use this shared
memory code is just a matter of calling those functions instead of local
ones that do similar things.

Message passing support
-----------------------

See commands.txt.

Enumerated types
----------------

Enumerated types consume more than one entry in the array.  The first
position is just a normal variable like any other, with the exception
of the ENUM flag.  To create one of these, use addinfo as follows:

	addinfo (INFO_LOWXFER, "", FLAG_RW | FLAG_ENUM, 4);

Here we're adding a LOWXFER type that's read/write, enumerated, and has
4 possibilities.  Those 4 possibilities must then be added too.

	addinfo (INFO_ENUM, somevalue, 0, INFO_HIGHXFER);

Just call addinfo using that example and it will store the possible
values for you.  Don't worry about flagging which one is "SELECTED" -
upsd handles that by itself when the clients ask about it.

Strings
-------

Add these like any other variable, only you tack on the RW and STRING
flags, along with the length.

	addinfo (INFO_UPSIDENT, "", FLAG_RW | FLAG_STRING, 8);

Easy.

Instant commands
----------------

These work just like enumerated variables seen above.

	addinfo (INFO_INSTCMD, "", 0, CMD_FPTEST);
