NUT is the "Network UPS Tools" one can use to monitor an uninterpretable power supply. In this exercise we will use NUT to monitor an APC ups unit on OpenBSD through the USB port.
The first task is to install NUT and the supporting USB libraries. Both are available from OpenBSB packages. When you install nut the supporting library package , libusb may be installed automatically.
pkg_add -i nut-2.2.1p0 pkg_add -i libusb-0.1.12
Notice that all of the config files and support script for NUT will be in /etc/nut/ . We will continue with this pattern for this example NUT setup.
Reboot your box and make sure that the usb ports are enabled. You can enable them all or just a few. Each BIOS is slightly different so you will need to check the motherboard manual if you have questions. We choose to enable 2 of the 8 usb ports on our box. The manual told us which USB ports were considered number 1 and 2.
Connect the UPS to the computer with the vendor supplied USB cable. Check /var/log/massages to make sure the computer sees that a new device has been attached. For example, this is the output when our OpenBSD box saw our APC 1300.
Jan 10 10:11:12 calomel /bsd: ugen0 at uhub1 Jan 10 10:11:12 calomel /bsd: port 1 "American Power Conversion Back-UPS XS 1300 LCD FW:888.88 .D USB FW:88" rev 1.88/1.89 addr 2
Use usbdevs -vd to list out all USB devices you box can see. You should see your UPS unit listed.
root@calomel: usbdevs -vd addr 1: EHCI root hub, VIA uhub0 addr 1: UHCI root hub, VIA uhub1 addr 2: Back-UPS XS 1300 LCD FW:888.88 .D USB FW:88, American Power Conversion ugen0
Notice that our UPS is using the device "ugen0". The first device listed on OpenBSD is /dev/ugen0.00 and that is what out UPS unit will be using.
Make sure that the user "_ups" that was added by the package "NUT" can read and write to the device /dev/ugen0.00 .
First, add the NUT group, "_nut" to the deamon group.root@calomel: cat /etc/group | grep nut daemon:*:1:daemon,_nut
Second, change the permissions on the usb device so the daemon group, which now includes the _nut group, can write to the device.root@calomel: ls -al /dev/ugen0.00 crw-rw---- 1 root wheel 63, 0 Jan 10 10:11 /dev/ugen0.00 root@calomel: chown root:daemon /dev/ugen0.00 root@calomel: ls -al /dev/ugen0.00 crw-rw---- 1 root daemon 63, 0 Jan 10 10:11 /dev/ugen0.00
The ups.conf is the device driver config file. It specifies what usb device to use (/dev/ugen0.00) and what usb driver. We are using the driver "usbhid-ups" because our ups, the Back-UPS XS 1300 LCD, is fully supported.
### Calomel.org ups.conf # user = _ups [apc] driver = usbhid-ups port = /dev/ugen0.00 pollfreq=60 desc = "Back-UPS XS 1300 LCD_USB"
Now that the config file is in place lets start the ups driver "usbhid-ups". You should see output like the following. If you see an error like the driver can not find the device then make sure you changed the permissions on the usb device "/dev/ugen0.00".
root@calomel: /usr/local/bin/upsdrvctl start Network UPS Tools - UPS driver controller Starting UPS: apc exec: /usr/local/bin/usbhid-ups -a apc Network UPS Tools: 0.29 USB communication driver - core 0.32 () Using subdriver: APC HID 0.92
The upsd.conf file is for the upsd daemon that will poll the USB communication driver (upsdrvctl). The upsd daemon will also allow the upsmon program to monitor the data output from the ups. Notice that we have setup an access control list to only allow localhost (127.0.0.1) to access the daemon. We will also be listening on localhost port 3493.
## Calomel.org upsd.conf # ACL all 0.0.0.0/0 ACL localhost 127.0.0.1/32 ACCEPT localhost REJECT all LISTEN 127.0.0.1 3493 MAXAGE 300
The upsd.users file specifies which users and passwords are allowed to access the upsd daemon. The admin account is for modifying setting on the ups and the monuser is used for polling the ups data.
## Calomel.org upsd.users # [admin] password = passwd1 allowfrom = localhost actions = SET instcmds = ALL [monuser] password = passwd2 allowfrom = localhost upsmon master
Now that the upsd.conf and upsd.users files are in place we can start the upsd daemon.
root@calomel: /usr/local/sbin/upsd Network UPS Tools upsd listening on 127.0.0.1 port 3493 Connected to UPS [apc]: usbhid-ups-apc
With the usb driver (usbhid-ups) and upsd daemon started you can verify client access to the apc unit. Simple use the command upsc apc@localhost to poll the upsd daemon. This is an example from our APC unit.
root@calomel: upsc apc@localhost battery.charge: 100 battery.charge.low: 10 battery.charge.warning: 50 battery.date: 2008/01/01 battery.mfr.date: 2008/08/20 battery.runtime: 1564 battery.runtime.low: 120 battery.type: PbAc battery.voltage: 26.9 battery.voltage.nominal: 24.0 driver.name: usbhid-ups driver.parameter.pollfreq: 30 driver.parameter.pollinterval: 2 driver.parameter.port: /dev/ugen0.00 driver.version: driver.version.data: APC HID 0.92 driver.version.internal: 0.32 input.transfer.high: 139 input.transfer.low: 88 input.voltage: 121.0 input.voltage.nominal: 120 ups.beeper.status: enabled ups.delay.shutdown: -1 ups.firmware: 836.H5 .D ups.firmware.aux: H5 ups.load: 23 ups.mfr: American Power Conversion ups.mfr.date: 2008/08/20 ups.model: Back-UPS XS 1300 LCD ups.productid: 0002 ups.serial: 8B0123R01234 ups.status: OL ups.test.result: No test initiated ups.vendorid: 012e
The upsmon.conf file will allow us to monitor the output of upsd without manually executing upsc. This config is setup to monitor the upsd daemon and if this is problem it will log the error to syslog and execute the script "/etc/nut/nut_notify.sh" which will send email with the same error.
## Calomel.org upsmon.conf # DEADTIME 300 FINALDELAY 0 HOSTSYNC 30 MINSUPPLIES 1 MONITOR apc@localhost 1 monuser passwd2 master NOCOMMWARNTIME 300 NOTIFYCMD /etc/nut/nut_notify.sh POLLFREQ 30 POLLFREQALERT 30 POWERDOWNFLAG /etc/nut/killpower RBWARNTIME 86400 SHUTDOWNCMD "/sbin/shutdown -p -h +0" ## Notify message format NOTIFYMSG ONLINE "UPS %s on line power" NOTIFYMSG ONBATT "UPS %s on battery" NOTIFYMSG LOWBATT "UPS %s battery is low" NOTIFYMSG FSD "UPS %s: forced shutdown in progress" NOTIFYMSG COMMOK "Communications with UPS %s established" NOTIFYMSG COMMBAD "Communications with UPS %s lost" NOTIFYMSG SHUTDOWN "Auto logout and shutdown proceeding" NOTIFYMSG REPLBATT "UPS %s battery needs to be replaced" NOTIFYMSG NOCOMM "UPS %s is unavailable" NOTIFYMSG NOPARENT "upsmon parent process died - shutdown impossible" ## Notify conditions NOTIFYFLAG ONLINE SYSLOG+EXEC NOTIFYFLAG ONBATT SYSLOG+EXEC NOTIFYFLAG LOWBATT SYSLOG+EXEC NOTIFYFLAG FSD SYSLOG+EXEC NOTIFYFLAG COMMOK SYSLOG+EXEC NOTIFYFLAG COMMBAD SYSLOG+EXEC NOTIFYFLAG SHUTDOWN SYSLOG+EXEC NOTIFYFLAG REPLBATT SYSLOG+EXEC NOTIFYFLAG NOCOMM SYSLOG+EXEC NOTIFYFLAG NOPARENT SYSLOG+EXEC
The /etc/nut/nut_notify.sh simple takes the argument sent by the upsmon daemon and sends an email to root. The argument upsmon sends is the error message.
## Calomel.org nut_notify.sh # echo $1 | mail -s "NUT $1" root
Add the following to you /etc/rc.conf . It will start all three daemons on the next reboot.
# Start NUT if [ -x /usr/local/bin/upsdrvctl ]; then echo -n ' nut' /usr/local/bin/upsdrvctl start > /dev/null 2>&1 /usr/local/sbin/upsd > /dev/null 2>&1 /usr/local/sbin/upsmon > /dev/null 2>&1 fi
Congratulations! All three NUT daemons are started and you now receive notifications if any events are triggered on the UPS unit. The status messages will be sent to the system in /var/log/messages and an email will be sent to root. You can verify the status of the daemons using "ps -aux | grep ups".
root@calomel: ps -aux | grep ups _ups 12345 Ss 12:00AM 0:00.71 /usr/local/bin/usbhid-ups -a apc _ups 67890 Ss 12:00AM 0:00.09 /usr/local/sbin/upsd _ups 2345 S 12:00AM 0:00.06 /usr/local/sbin/upsmon root 23456 Is 12:00AM 0:00.00 /usr/local/sbin/upsmon
Why are there two copies of upsmon running?
It's not really two complete copies if your OS forks efficiently. By default, upsmon runs the majority of the work as an unprivileged user and keeps a stub process around with root powers that can only shut down the system when necessary. This should make it much harder to gain root in the event a hole is ever discovered in upsmon.
If this really bothers you and you like running lots of code as root, start upsmon with -p and it will go back to being one big process. This is not recommended, so don't blame me if something bad happens in this mode.
The ups driver works as root, but not when run as _nut. Why?
This is most likely a permissions problem denying a user to the usb device. Try using systrace. Tedb of tedb.net suggested the following line which is handy in tracking down permission problems once you confirmed the Nut driver works running as root.systrace -A -d /tmp/st usbhid-ups -a UPS
Questions, comments, or suggestions? Contact Calomel.org