Technical notes on my code submission of 05 Nov 99 Kern Sibbald General: - Since I did not complete my submission notes of yesterday, this file contains the missing parts. Additional documenatation of kes03Nov99: - I changed the default location of the status file to be /etc/apcupsd/apcupsd.status (previously /etc/apcupsd.status). - Added new function codes for the functions and codes used to access the UPS. This code will be described more in detail in a later submission, but essential it replaces the UPS capability flag word (limited to 32 bits or capabilities) with a character array, allowing us to add additional functions. - Removed the old defines for LI, LW and LD. - Removed the old defines for error_abort and error_exit. They are now simple subroutines. - Removed the subroutine remove_file() as it is better written simply as unlink(). - Removed the killpwr variable from several subroutines, and created a new subroutine to actually do the work, called kill_power(). - Renamed procfs to be status - Cleaned up the text in apccontrol to be a bit easier to understand, and slightly more explicit (at least for me). Changes submitted this submission: - added code for LOGSTATS <on/off> in apcconfig.c - removed all logprintf() from apcnet.c replacing them with log_event(LOG_DEBUG, ...). - removed logprintf() subroutine. - Completed STATUS logging code. - Added a first step at documenting the program flow to the end of apcupsd.c as comments. - put an updated version of apcupsd.conf into the /etc directory. - put a dumb readfifo.c test program into the examples directory. If you point this program to a named pipe that writes the STATUS data, it will print it out. - The Makefiles in the cgi and cgi/gd1.2 directories were missing in the snapshot. I suspect that they were deleted by the make distclean command. To prevent their loss in the future, I also created a Makefile.in in each of these directories. Work to complete: - Automatically call cgi make from our make. - Documentation. - Automatic conversion of old apcupsd.config files to the new format? - Eliminate the rest of the printfs(). - Examine all configuration messages, and eliminate all "Someone will have to explain this to me" messages. - Check and double check killpwr changes. - Correct problems pointed out by Riccardo in integrating my submission. - Complete writing of status logging. - Rework the cgi program to correct errors and add other display modes (e.g. remaining run time). - Add the full status display to the cgi program (same as currently printed by "apcaccess status" - Remove remaining traces of old log file. Suggestion: - I suggest we begin changing names of our program from apc... to ups... to be more generic, especially if we are intending to support UPSes other than APC UPSes. New syslog() code: Our basic philosophy is that all logging should be done through the syslog() facility. This is now implemented with one exception that I will explain below concerning the status file logging. All logging functions and all error reporting are now done through the log_event() subroutine call. Exceptions to this are: initialization code where printf's are done, and writing to the status file. Once the initialization code has completed and the fork() to become a daemon is done, no printf's are used. log_event() has exactly the same format as syslog(). In fact, the subroutine consists of only a syslog() call. If anyone really wishes to log to a file, the code to do so can easily be done by adding code to log_event() in apclog.c. I have broken logging into four separate types that I call: 1. DEBUG 2. DATA 3. STATUS 4. EVENTS DEBUG logging: Debug logging consists of debug messages. Normally these are turned on only by developers, and currently there exist very few of these debug messages. DATA logging: Data logging consists of periodically logging important data concerning the operation of the UPS. For the definitive definition of the format, see log_data() in apcreports.c. The format varies according to the UPS model and hence the information available. However, for UPS models, configured with the UPSTYPE statement as newbackupspro, backupspropnp, smartups, matrixups, and sharesmart will be written in a format very similar to what PowerChute writes. logging output will be the same for all the following models: Configured UPSTYPE Long name =========== ================= newbackupspro Smarter BackUPS Pro backupspropnp Smarter BackUPS Pro smartups SmartUPS matrixups MatrixUPS sharesmart ShareUPS Advanced Port That is: MinLineVoltage, MaxLineVoltage, OutputVoltage, BatteryVoltage, LineFrequency, UPSTemperature,AmbientTemperature,Humidity,LineVoltage where AmbientTemperature and Humity are always blank in the current implementation. An actual example from the log file is: Nov 2 12:43:05 matou apcupsd[23439]: 224.9,227.5,226.2,27.74,50.00,100.0,30.6,,,226.2 STATUS logging: Status logging consists of periodically logging ALL available information concerning the UPS. In this case, the volume of data is rather large, consequently, this information is not automatically sent to the system log file, but rather written as a series of data records to a specific file. After each write, the file is rewound so that the size of the file remains constant. At the current time, this file is 763 bytes. The format of this file is very similar to the old apcupsd procfs file. Once networking is implemented, we can consider eliminating this file. However, in the mean time. It is essential so that users can obtain all desired information. In fact, the current CGI brower user interface uses this file to graphically display information. If specified in the configuration file, this data will also be written to the system log file. Please note, that it would not normally be wise to write this data to a normal system log file as there is no mechanism in syslog() to rewind the file and hence the log file would quickly become enormous. However, in two cases, it can be very useful to use syslog() to write this information. The first case is to setup your syslog.conf file so that the data is written to a named pipe. In this case, normally not more than about 8192 bytes of data will be kept before it is discarded by the system. The second case is to setup your syslog.conf file so that the status data is sent to another machine, which presumably then writes it to a named pipe. Consequently, with this mechanism, we have a very simple means of networking apcupsd data. To make reading the status data reliable via a named pipe, the first record written contains a version number, the number of records that follow the first record, and the total number of bytes in those subsequent records. An actual example of such a status file (/etc/apcupsd/apcupsd.status) is: APC : 001,028,0675 DATE : Nov 03 11:56:32 RELEASE : 3.6.3 CABLE : Custom Cable Smart UPSMODEL : SMART-UPS 1000 UPSMODE : Stand Alone UPSNAME : UPS_IDEN ULINE : 228.8 Volts MLINE : 230.1 Volts NLINE : 227.5 Volts FLINE : 50.0 Hz VOUTP : 228.8 Volts LOUTP : 011.4 Percent Load Capacity BOUTP : 27.7 Volts BCHAR : 100.0 Batt. Charge TIME : 112.0 Minutes SENSE : LOW WAKEUP : 060 Cycles SLEEP : 180 Cycles LOTRANS : 204.0 Volts HITRANS : 253.0 Volts CHARGE : 050.0 Percent BFAIL : 0x08 Status Flag UTEMP : 30.1 C Internal ALARM : Low Batt LASTEVNT : LINE VOLTAGE DECREASE LOWBATT : 02 Minutes DIPSW : 0x0000 END APC : Nov 03 11:56:32 Consequently, the first record always consists of 24 bytes (23 characters followed by a newline). This record starts with APC and as indicated in the example above is followed by 28 records consisting of 675 bytes. The last record begins with END APC and contains the date and time matching the DATE record. Documentation of each record needs to be written. In the coming weeks, I plan to add additional records and possibly change the names of some of the fields. When this data is written to a file, it is written as two records, the first record, and all the other records together. In reading the file, it can be either be read a record at a time, or in one big read. When this data is written to syslog(), it is written a record at a time. The first record is the first 24 bytes. By having the number of records and the size in the first record, the complete status can be reliably reassembled. EVENTS logging: Events logging consists of logging events as they happen. For example, successful startup, power fail, battery failure, system shutdown, Implementation details: In order to ensure that the data logged to syslog() can be directed to different files, I have assigned syslog() levels to each of our four types of data as follows: 1. DEBUG logging has level LOG_DEBUG 2. DATA logging has level LOG_INFO 3. STATUS logging has level LOG_NOTICE 4. EVENTS logging has levels LOG_WARNING, LOG_ERR, LOG_CRIT, and LOG_ALERT It should be noted that more work needs to be done on the precise definitions of each of the levels for EVENTS logging. Currently, it is roughly broken down as follows: LOG_WARNING general information such as startup, etc. LOG_ERR an error condition detected, e.g. communications problem with the UPS. LOG_CRIT a serious problem has occurred such as power failure, running on UPS batteries, LOG_ALERT a condition that needs immediate attention such as pending system shutdown, More work needs to be done to the code to ensure that it corresponds to the above levels. As a practical example of how to setup your syslog() to use the new logging feature, suppose you wish to direct all DATA logging to a file named /var/log/apcupsd.data, all EVENTS to the standard /var/log/messages file (to be mixed with other system messages), and at the same time send all EVENTS to /var/log/apcupsd.events, and finally, you want to send all STATUS logging to the named pipe /var/log/apcupsd.status First as root, you create the named pipe: mkfifo /var/log/apcupsd.status change its permissions as necessary or use the -m option to set them when creating the pipe. Then you modify your /etc/syslog.conf file to direct the appropriate levels of messages where you want them. To accomplish the above, my syslog.conf file looks like: # exclude all apcupsd info by default *.info;local0.none /var/log/messages # Everything for apcupsd goes here local0.info;local0.!notice /var/log/apcupsd.data local0.notice;local0.!warn |/var/log/apcupsd.status local0.warn /var/log/apcupsd.events local0.warn /var/log/messages