Package: nut-server
Version: 2.7.2-4
Severity: normal
Justification: breaks the forced shutdown functionality


Dear maintainer,

Here goes the description of a small bug in the driver for the Riello
IDG-800 UPS (riello_ser), perhaps affecting other Riello UPS models.


Environment
-----------
Computer with Debian Jessie 64 bit - USB cable - Riello IDG 800 UPS

$ uname -a
Linux debian 3.16.0-4-amd64 #1 SMP Debian 3.16.7-ckt11-1+deb8u3 (2015-08-04) 
x86_64 GNU/Linux

$ lsusb | grep "Cypress"
Bus 001 Device 004: ID 04b4:5500 Cypress Semiconductor Corp. HID->COM RS232 
Adapter

$ ls -l /dev/serial/by-id | grep "Cypress"
lrwxrwxrwx 1 root root 13 sep 17 11:23 
usb-Cypress_Semiconductor_USB_to_Serial-if00-port0 -> ../../ttyUSB0

$ sudo tail -n1 /etc/nut/nut.conf
MODE=standalone

$ sudo tail -n8 /etc/nut/ups.conf 
[riello]
        driver = riello_ser
        #port = /dev/ttyUSB0
        port = 
/dev/serial/by-id/usb-Cypress_Semiconductor_USB_to_Serial-if00-port0
        desc = "Riello IDG-800"
        pollinterval = 5
        default.ups.delay.shutdown = 20
        default.ups.delay.reboot = 30

$ sudo tail -n1 /etc/nut/upsd.conf 
LISTEN 0.0.0.0

$ sudo tail -n7 /etc/nut/upsd.users 
[jose]
        password = samplepass1
        actions = SET
        instcmds = ALL
[upsmon]
        password = samplepass2
        upsmon master

$ sudo tail -n1 /etc/nut/upsmon.conf 
MONITOR riello@localhost 1 upsmon samplepass2 master



What led up to the situation
----------------------------
I installed nut and configured it to use the riello_ser driver for my
Riello IDG-800 UPS.

I configured it to run in standalone mode (nut.conf), added the [riello]
section to ups.conf, added a couple of users for the UPS daemon (me and
the upsmon), and finally configured upsmon to monitor the UPS.

Everything works fine, in principle. I can issue the "upsc riello"
command on a terminal window and it displays UPS variables properly. In
case of a power outage, everything turns off in an orderly manner
(computer and UPS power). When the mains power comes back, the UPS
powers on the computer and it boots normally.

I then tried to test the "forced shutdown" functionality as described in
the NUT documentation at chapter 6.3:
http://www.networkupstools.org/docs/user-manual.chunked/ar01s06.html

$ sudo /sbin/upsmon -c fsd


What was expected to happen
---------------------------
"If everything works correctly, the computer will be forcibly powered
off, may remain off for a few seconds to a few minutes (depending on the
driver and UPS type), then will power on again."


What happens
------------
The computer shuts down in an orderly manner, but after that, the UPS
never turns off the power output. The computer remains shut down.


What leads me to suspect of the driver
--------------------------------------
NUT docs also say:
"If your UPS just sits there and never resets the load, you are
vulnerable to a power race and should add the "reboot after timeout"
hack at the very least."

Before investigating any power race issues, I first tried to test if the
driver was doing its job.

$ sudo systemctl stop nut-driver
$ sudo sudo /lib/nut/riello_ser -a riello -DD -k
---cut---
Network UPS Tools - Riello serial driver 0.02 (2.7.2)
Warning: This is an experimental driver.
Some features may not function correctly.

   0.000000     debug level is '2'
   0.001003     entering upsdrv_initups()
   0.003530     set baudrate 9600
   0.005303     try to detect SENTR
   5.009680     try to detect GPSER
   5.691630     Connected to UPS GPSER on 
/dev/serial/by-id/usb-Cypress_Semiconductor_USB_to_Serial-if00-port0 with 
baudrate 9600
   5.691665     Initiating UPS shutdown
   5.691673     upsdrv Shutdown execute
   5.907707     instcmd: unknown command [shutdown.return]
   6.123608     instcmd: unknown command [shutdown.return]
   6.339637     instcmd: unknown command [shutdown.return]
   6.339673     Shutdown failed!
---cut---

So, it seems the driver doesn't recognize the shutdown.return command?


Looking at the code (apt-get source nut) I found this piece of code in
the riello_ser driver:

drivers/riello_ser.c lines 357+:
---cut---
    if (!riello_test_bit(&DevData.StatusCode[0], 1)) {
        ...
    }
    else {
        if (!strcasecmp(cmdname, "shutdown.return")) {
            issue the command to the UPS
            ...
            return STAT_INSTCMD_HANDLED;
        }
    }
---cut---

The meaning of StatusCode[0], bit 1 is clarified later:

drivers/riello_ser.c lines 811+:
---cut---
    /* AC Fail */
    if (riello_test_bit(&DevData.StatusCode[0], 1))
        status_set("OB");
    else
        status_set("OL");
---cut---


Conclusion
----------
So, the driver only allows the shutdown.return when on battery (OB
status), not in online mode (OL). This disallows any system
administrator to test the forced shutdown functionality described in
NUT's documentation, which is supposed to be done in online mode.

If on battery, ITOH, the shutdown.return is recognized and does its job.



Solution that works for me
--------------------------
* Get the package source (apt-get source nut).
* Install the build dependencies (sudo apt-get build-dep nut).
* Apply the patch described below.
* Recompile (fakeroot debian/rules binary).
* Install the generated DEB (nut-server_2.7.2-4_amd64.deb) or just copy
the generated binary overwriting the old file:

$ cd ~/dev/nut-2.7.2-riello-mod/src/debian/nut-2.7.2
$ sudo cp debian/nut-server/lib/nut /lib/nut

The patch just removes the else clause to allow the shutdown.return
command regardless of the OL/OB status.

Patch contents (maybe misformatted due to the 80-char width limit):
---cut---
--- a/drivers/riello_ser.c      2014-02-25 16:39:34.000000000 +0100
+++ b/drivers/riello_ser.c      2015-09-17 13:14:32.306018237 +0200
@@ -498,7 +498,7 @@ int riello_instcmd(const char *cmdname,
                        }
                }
        }
-       else {
+       //else {
                if (!strcasecmp(cmdname, "shutdown.return")) {
                        delay_char = dstate_getinfo("ups.delay.shutdown");      
        
                        delay = atoi(delay_char);
@@ -526,7 +526,7 @@ int riello_instcmd(const char *cmdname,
                                return STAT_INSTCMD_HANDLED;
                        }
                }
-       }
+       //}
 
        if (!strcasecmp(cmdname, "shutdown.stop")) {
                riello_init_serial();
---cut---


Working patched driver
----------------------
$ sudo systemctl stop nut-driver
$ sudo sudo /lib/nut/riello_ser -a riello -DD -k
---cut---
Network UPS Tools - Riello serial driver 0.02-JAP (2.7.2)
Warning: This is an experimental driver.
Some features may not function correctly.

   0.000000     debug level is '2'
   0.000748     entering upsdrv_initups()
   0.003084     set baudrate 9600
   0.004311     try to detect SENTR
   5.008864     try to detect GPSER
   5.682851     Connected to UPS GPSER on 
/dev/serial/by-id/usb-Cypress_Semiconductor_USB_to_Serial-if00-port0 with 
baudrate 9600
   5.682880     Initiating UPS shutdown
   5.682887     upsdrv Shutdown execute
   6.154917     Shutting down
---cut---

NOTE: this cuts off power inmediately. Do this driver test with the
computer plugged directly into the mains socket. Use a desktop lamp or
similar connected to the UPS out socket.


Result
------
With the driver now patched, issueing the forced shutdown procedure, it
does what is expected.

For this test, I now start back the nut-driver service since I had
stopped it (for launching it manually in debug mode) for the test.

$ sudo systemctl start nut-driver
$ sudo /sbin/upsmon -c fsd

Network UPS Tools upsmon 2.7.2

Broadcast message from nut@debian (somewhere) (Fri Sep 18 00:41:41 2015):   
Executing automatic power-fail shutdown

Broadcast message from nut@debian (somewhere) (Fri Sep 18 00:41:41 2015):   
Auto logout and shutdown proceeding


Relevant syslog entries:

---cut---
$ grep ups /var/log/syslog
Sep 18 00:41:41 debian upsmon[1308]: Signal 10: User requested FSD
Sep 18 00:41:41 debian upsd[1305]: Client [email protected] set FSD on UPS 
[riello]
Sep 18 00:41:41 debian upsmon[1308]: Executing automatic power-fail shutdown
Sep 18 00:41:41 debian upsmon[1308]: Auto logout and shutdown proceeding
Sep 18 00:41:46 debian upsd[1305]: mainloop: Interrupted system call
Sep 18 00:41:46 debian upsd[1305]: Signal 15: exiting
Power turns off after a few seconds
...
Power turns on after a while
Sep 18 00:43:07 debian upsdrvctl[749]: Connected to UPS GPSER on 
/dev/serial/by-id/usb-Cypress_Semiconductor_USB_to_Serial-if00-port0 with 
baudrate 1200
Sep 18 00:43:08 debian upsdrvctl[749]: Network UPS Tools - UPS driver 
controller 2.7.2
Sep 18 00:43:08 debian upsd[1310]: fopen /var/run/nut/upsd.pid: No such file or 
directory
Sep 18 00:43:08 debian upsd[1310]: listening on 0.0.0.0 port 3493
Sep 18 00:43:08 debian upsd[1310]: Connected to UPS [riello]: riello_ser-riello
Sep 18 00:43:08 debian upsd[1311]: Startup successful
Sep 18 00:43:08 debian upsmon[1312]: fopen /var/run/nut/upsmon.pid: No such 
file or directory
Sep 18 00:43:08 debian upsmon[1312]: Using power down flag file /etc/killpower
Sep 18 00:43:08 debian upsmon[1312]: UPS: riello@localhost (master) (power 
value 1)
Sep 18 00:43:08 debian upsmon[1313]: Startup successful
Sep 18 00:43:08 debian upsmon[1314]: Init SSL without certificate database
Sep 18 00:43:08 debian upsd[1311]: User [email protected] logged into UPS 
[riello]
---cut---

Reply via email to