Wednesday, January 02, 2013

Rebuilding APCUPSd RPM to work around ESXi(5) USB Arbitrator Disconnections

If you're like me, you have a home lab with ESXi and a UPS;  probably an APC UPS, for now, although they sometimes seem like they want to exclude their best customers and marginalize themselves out of the market at times.

If you want a VM (like a VMA) to manage your startup and shutdown, you need to port a USB connection into that VM, and there's two options:

  1. Use VM Directpath, and port ALL your USB connections in to the one VM, which may not be what you want if you're also doing things like monitoring USB Cams, synching USB Sticks or doing iPhone/iPad syncs via the same USB bus.
  2. let the ESXi (5, here) host dynamically connect the USB device to the proper VM, which is hit-and-miss and right now causes a number of errors.
I got fed up with the former, and went with the latter.  Here's the error messages I was getting:
Dec 30 04:21:29 kibo apcupsd[3825]: Battery reattached.
Dec 30 04:21:32 kibo apcupsd[3825]: Battery disconnected.
Dec 30 04:35:50 kibo apcupsd[3825]: Battery reattached.
Dec 30 04:35:52 kibo apcupsd[3825]: Battery disconnected.
Dec 30 04:38:57 kibo apcupsd[3825]: Battery reattached.
Dec 30 04:39:00 kibo apcupsd[3825]: Battery disconnected.
Dec 30 04:48:47 kibo apcupsd[3825]: Battery reattached.
It's a little surprising, as bugs go, but someone else has fixed this before, and it's a simple rebuild of the apcupsd RPM to (temporarily) fix it.  Here's what I did:

  1. move to a build account (and/or box) with a trivial RPM build environment set up on RHEL5/Centos5  (comment and I'll show how this can be done )
  2. grab the SRPM:
    apt-get source apcupsd
  3. trivially modify the spec file:
    --- -   2013-01-02 16:32:42.902789000 -0800
    +++ apcupsd.spec        2013-01-02 16:31:46.000000000 -0800
    @@ -79,6 +81,7 @@
             --enable-nls \
             --enable-gapcmon \
             --enable-pcnet \
    +       --with-generic-usb \
             --with-libwrap=%{_libdir} \
             --with-serial-dev= \
             --with-upstype=usb \
    
  4. grab dependencies (as root, or else in sudo if you need training wheels / normally run ubuntu)
    apt-get -y build-dep apcupsd-*.src.rpm
  5. and build (as non-root in case anything's been susefied)
    rpmbuild -ba apcupsd.spec
  6. remove the stock apcupsd and install this modified one.
One Caveat:  You have replaced a vendor package with one of your own.  You now support that package fully, which includes checking for exploits, fixing them as required, rolling the patch for the fix into a new RPM, testing and distributing the product.  You are on the hook for incompatibilities.  If you have more than one machine, if the machine is accessible to the world (ie, no airgap exists between this machine's LANs and the world) and it's therefore a security concern, don't do this!  Okay, that's it -- you're on your own, whether you understand the ramifications or not.

In my case, I built an alt-package, of sorts.  Here's the full  Diff:

--- -   2013-01-02 16:32:42.902789000 -0800
+++ apcupsd.spec        2013-01-02 16:31:46.000000000 -0800
@@ -1,4 +1,6 @@
-Name:         apcupsd
+%define        realnam apcupsd
+
+Name:         usb-%{realnam}
 Version:      3.14.10
 Release:      1%{?dist}
 Summary:      APC UPS Power Control Daemon for Linux
@@ -6,13 +8,13 @@
 Group:        System Environment/Daemons
 License:      GPL
 URL:          http://www.apcupsd.com
-Source0:      http://download.sourceforge.net/apcupsd/%{name}-%{version}.tar.gz
+Source0:      http://download.sourceforge.net/apcupsd/%{realnam}-%{version}.tar.gz
 Source1:      apcupsd.logrotate
 Source2:      apcupsd-httpd.conf
 Source3:      README.Red_Hat
 Patch0:       apcupsd-3.14.0-init.patch
 Patch1:       apcupsd-3.14.8-cxxld.patch
-BuildRoot:    %{_tmppath}/%{name}-%{version}-%{release}-root-%(%{__id_u} -n)
+BuildRoot:    %{_tmppath}/%{realnam}-%{version}-%{release}-root-%(%{__id_u} -n)

 BuildRequires: glibc-devel >= 2.3, gd-devel > 2.0
 BuildRequires: net-snmp-devel, gettext-devel, ncurses-devel, tcp_wrappers
@@ -53,7 +55,7 @@


 %prep
-%setup -q
+%setup -q -n %{realnam}-%{version}
 %patch0 -p1 -b .init
 %patch1 -p1 -b .cxxld
 # Don't strip binaries
@@ -79,6 +81,7 @@
         --enable-nls \
         --enable-gapcmon \
         --enable-pcnet \
+       --with-generic-usb \
         --with-libwrap=%{_libdir} \
         --with-serial-dev= \
         --with-upstype=usb \
@@ -101,9 +104,9 @@
 install -m755 platforms/redhat/apcupsd $RPM_BUILD_ROOT%{_initrddir}

 install -d %{buildroot}%{_sysconfdir}/logrotate.d
-install -m0644 %{SOURCE1} %{buildroot}%{_sysconfdir}/logrotate.d/%{name}
+install -m0644 %{SOURCE1} %{buildroot}%{_sysconfdir}/logrotate.d/%{realnam}
 install -d %{buildroot}%{_sysconfdir}/httpd/conf.d
-install -m0644 %{SOURCE2} %{buildroot}%{_sysconfdir}/httpd/conf.d/%{name}.conf
+install -m0644 %{SOURCE2} %{buildroot}%{_sysconfdir}/httpd/conf.d/%{realnam}.conf

 desktop-file-install --vendor="fedora" \
         --dir=${RPM_BUILD_ROOT}%{_datadir}/applications \

(watch the patch above -- Blogger input sanitizer doesn't know not to clean out some marks within PRE sections yet)

I built the trivial RPM and dumped it onto my own repo.  Now, if another apcupsd is released, I do have the diff handy for a re-patch, but I do understand that I am on the hook for this.  Moo.